From 077d085ea9a4da9c306da2aac086879652264c91 Mon Sep 17 00:00:00 2001 From: Kazunori Kojima Date: Sun, 6 Mar 2016 21:57:06 +0900 Subject: [PATCH 01/13] Implement aws_s3_bucket_notification resource --- builtin/providers/aws/provider.go | 1 + .../resource_aws_s3_bucket_notification.go | 453 ++++++++++++++++++ ...esource_aws_s3_bucket_notification_test.go | 429 +++++++++++++++++ .../providers/aws/resource_aws_sqs_queue.go | 8 +- 4 files changed, 889 insertions(+), 2 deletions(-) create mode 100644 builtin/providers/aws/resource_aws_s3_bucket_notification.go create mode 100644 builtin/providers/aws/resource_aws_s3_bucket_notification_test.go diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index e469c6756943..1feb8ea9d615 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -226,6 +226,7 @@ func Provider() terraform.ResourceProvider { "aws_route_table_association": resourceAwsRouteTableAssociation(), "aws_s3_bucket": resourceAwsS3Bucket(), "aws_s3_bucket_object": resourceAwsS3BucketObject(), + "aws_s3_bucket_notification": resourceAwsS3BucketNotification(), "aws_security_group": resourceAwsSecurityGroup(), "aws_security_group_rule": resourceAwsSecurityGroupRule(), "aws_spot_instance_request": resourceAwsSpotInstanceRequest(), diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification.go b/builtin/providers/aws/resource_aws_s3_bucket_notification.go new file mode 100644 index 000000000000..bb7f5ea4a3af --- /dev/null +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification.go @@ -0,0 +1,453 @@ +package aws + +import ( + "bytes" + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/schema" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/s3" +) + +func resourceAwsS3BucketNotification() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsS3BucketNotificationPut, + Read: resourceAwsS3BucketNotificationRead, + Update: resourceAwsS3BucketNotificationPut, + Delete: resourceAwsS3BucketNotificationDelete, + + Schema: map[string]*schema.Schema{ + "bucket": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "topic": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "filter_prefix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "filter_suffix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "topic": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "events": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + }, + }, + Set: func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%t-", m["filter_prefix"].(string))) + buf.WriteString(fmt.Sprintf("%t-", m["filter_suffix"].(string))) + return hashcode.String(buf.String()) + }, + }, + + "queue": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "filter_prefix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "filter_suffix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "queue": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "events": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + }, + }, + Set: func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%t-", m["filter_prefix"].(string))) + buf.WriteString(fmt.Sprintf("%t-", m["filter_suffix"].(string))) + return hashcode.String(buf.String()) + }, + }, + + "lambda_function": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "filter_prefix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "filter_suffix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "lambda_function": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "events": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + }, + }, + Set: func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%t-", m["filter_prefix"].(string))) + buf.WriteString(fmt.Sprintf("%t-", m["filter_suffix"].(string))) + return hashcode.String(buf.String()) + }, + }, + }, + } +} + +func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{}) error { + s3conn := meta.(*AWSClient).s3conn + bucket := d.Get("bucket").(string) + + // TopicNotifications + topicNotifications := d.Get("topic").(*schema.Set).List() + topicConfigs := make([]*s3.TopicConfiguration, 0, len(topicNotifications)) + for _, c := range topicNotifications { + tc := &s3.TopicConfiguration{} + + c := c.(map[string]interface{}) + + // Id + if val, ok := c["id"].(string); ok { + tc.Id = aws.String(val) + } + + // TopicArn + if val, ok := c["topic"].(string); ok { + tc.TopicArn = aws.String(val) + } + + // Events + if v := c["events"].(*schema.Set); v.Len() > 0 { + tc.Events = make([]*string, 0, v.Len()) + for _, val := range v.List() { + tc.Events = append(tc.Events, aws.String(val.(string))) + } + } + + // Filter + filterRules := make([]*s3.FilterRule, 0, 2) + if val, ok := c["filter_prefix"].(string); ok && val != "" { + filterRule := &s3.FilterRule{ + Name: aws.String("prefix"), + Value: aws.String(val), + } + filterRules = append(filterRules, filterRule) + } + if val, ok := c["filter_suffix"].(string); ok && val != "" { + filterRule := &s3.FilterRule{ + Name: aws.String("suffix"), + Value: aws.String(val), + } + filterRules = append(filterRules, filterRule) + } + tc.Filter = &s3.NotificationConfigurationFilter{ + Key: &s3.KeyFilter{ + FilterRules: filterRules, + }, + } + topicConfigs = append(topicConfigs, tc) + } + + // Lambda + lambdaFunctionNotifications := d.Get("lambda_function").(*schema.Set).List() + lambdaConfigs := make([]*s3.LambdaFunctionConfiguration, 0, len(lambdaFunctionNotifications)) + for _, c := range lambdaFunctionNotifications { + lc := &s3.LambdaFunctionConfiguration{} + + c := c.(map[string]interface{}) + + // Id + if val, ok := c["id"].(string); ok { + lc.Id = aws.String(val) + } + + // LambdaFunctionArn + if val, ok := c["lambda_function"].(string); ok { + lc.LambdaFunctionArn = aws.String(val) + } + + // Events + if v := c["events"].(*schema.Set); v.Len() > 0 { + lc.Events = make([]*string, 0, v.Len()) + for _, val := range v.List() { + lc.Events = append(lc.Events, aws.String(val.(string))) + } + } + + // Filter + filterRules := make([]*s3.FilterRule, 0, 2) + if val, ok := c["filter_prefix"].(string); ok && val != "" { + filterRule := &s3.FilterRule{ + Name: aws.String("prefix"), + Value: aws.String(val), + } + filterRules = append(filterRules, filterRule) + } + if val, ok := c["filter_suffix"].(string); ok && val != "" { + filterRule := &s3.FilterRule{ + Name: aws.String("suffix"), + Value: aws.String(val), + } + filterRules = append(filterRules, filterRule) + } + lc.Filter = &s3.NotificationConfigurationFilter{ + Key: &s3.KeyFilter{ + FilterRules: filterRules, + }, + } + lambdaConfigs = append(lambdaConfigs, lc) + } + + // SQS + queueNotifications := d.Get("queue").(*schema.Set).List() + queueConfigs := make([]*s3.QueueConfiguration, 0, len(queueNotifications)) + for _, c := range queueNotifications { + qc := &s3.QueueConfiguration{} + + c := c.(map[string]interface{}) + + // Id + if val, ok := c["id"].(string); ok { + qc.Id = aws.String(val) + } + + // QueueArn + if val, ok := c["queue"].(string); ok { + qc.QueueArn = aws.String(val) + } + + // Events + if v := c["events"].(*schema.Set); v.Len() > 0 { + qc.Events = make([]*string, 0, v.Len()) + for _, val := range v.List() { + qc.Events = append(qc.Events, aws.String(val.(string))) + } + } + + // Filter + filterRules := make([]*s3.FilterRule, 0, 2) + if val, ok := c["filter_prefix"].(string); ok && val != "" { + filterRule := &s3.FilterRule{ + Name: aws.String("prefix"), + Value: aws.String(val), + } + filterRules = append(filterRules, filterRule) + } + if val, ok := c["filter_suffix"].(string); ok && val != "" { + filterRule := &s3.FilterRule{ + Name: aws.String("suffix"), + Value: aws.String(val), + } + filterRules = append(filterRules, filterRule) + } + qc.Filter = &s3.NotificationConfigurationFilter{ + Key: &s3.KeyFilter{ + FilterRules: filterRules, + }, + } + queueConfigs = append(queueConfigs, qc) + } + + notificationConfiguration := &s3.NotificationConfiguration{} + if len(lambdaConfigs) > 0 { + notificationConfiguration.LambdaFunctionConfigurations = lambdaConfigs + } + if len(queueConfigs) > 0 { + notificationConfiguration.QueueConfigurations = queueConfigs + } + if len(topicConfigs) > 0 { + notificationConfiguration.TopicConfigurations = topicConfigs + } + i := &s3.PutBucketNotificationConfigurationInput{ + Bucket: aws.String(bucket), + NotificationConfiguration: notificationConfiguration, + } + + log.Printf("[DEBUG] S3 bucket: %s, Putting notification: %v", bucket, i) + _, err := s3conn.PutBucketNotificationConfiguration(i) + if err != nil { + return fmt.Errorf("Error putting S3 notification configuration: %s", err) + } + + d.SetId(bucket) + + return resourceAwsS3BucketNotificationRead(d, meta) +} + +func resourceAwsS3BucketNotificationDelete(d *schema.ResourceData, meta interface{}) error { + s3conn := meta.(*AWSClient).s3conn + + i := &s3.PutBucketNotificationConfigurationInput{ + Bucket: aws.String(d.Id()), + NotificationConfiguration: &s3.NotificationConfiguration{}, + } + + log.Printf("[DEBUG] S3 bucket: %s, Deleting notification: %v", d.Id(), i) + _, err := s3conn.PutBucketNotificationConfiguration(i) + if err != nil { + return fmt.Errorf("Error deleting S3 notification configuration: %s", err) + } + + d.SetId("") + + return nil +} + +func resourceAwsS3BucketNotificationRead(d *schema.ResourceData, meta interface{}) error { + s3conn := meta.(*AWSClient).s3conn + + var err error + _, err = s3conn.HeadBucket(&s3.HeadBucketInput{ + Bucket: aws.String(d.Id()), + }) + if err != nil { + if awsError, ok := err.(awserr.RequestFailure); ok && awsError.StatusCode() == 404 { + log.Printf("[WARN] S3 Bucket (%s) not found, error code (404)", d.Id()) + d.SetId("") + return nil + } else { + // some of the AWS SDK's errors can be empty strings, so let's add + // some additional context. + return fmt.Errorf("error reading S3 bucket \"%s\": %s", d.Id(), err) + } + } + + // Read the notification configuration + notificationConfigs, err := s3conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String(d.Id()), + }) + if err != nil { + return err + } + log.Printf("[DEBUG] S3 Bucket: %s, get notification: %v", d.Id(), notificationConfigs) + // Topic Notification + topicNotifications := make([]map[string]interface{}, 0, len(notificationConfigs.TopicConfigurations)) + for _, notification := range notificationConfigs.TopicConfigurations { + conf := map[string]interface{}{} + + if notification.Id != nil { + conf["id"] = *notification.Id + } + + for _, f := range notification.Filter.Key.FilterRules { + if strings.ToLower(*f.Name) == "prefix" { + conf["filter_prefix"] = *f.Value + } + if strings.ToLower(*f.Name) == "suffix" { + conf["filter_suffix"] = *f.Value + } + } + + conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) + conf["topic"] = *notification.TopicArn + topicNotifications = append(topicNotifications, conf) + } + if err := d.Set("topic", topicNotifications); err != nil { + return fmt.Errorf("error reading S3 bucket \"%s\" topic notification: %s", d.Id(), err) + } + + // Lambda Notification + lambdaFunctionNotifications := make([]map[string]interface{}, 0, len(notificationConfigs.LambdaFunctionConfigurations)) + for _, notification := range notificationConfigs.LambdaFunctionConfigurations { + conf := map[string]interface{}{} + + if notification.Id != nil { + conf["id"] = *notification.Id + } + + for _, f := range notification.Filter.Key.FilterRules { + if strings.ToLower(*f.Name) == "prefix" { + conf["filter_prefix"] = *f.Value + } + if strings.ToLower(*f.Name) == "suffix" { + conf["filter_suffix"] = *f.Value + } + } + + conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) + conf["lambda_function"] = *notification.LambdaFunctionArn + lambdaFunctionNotifications = append(lambdaFunctionNotifications, conf) + } + if err := d.Set("lambda_function", lambdaFunctionNotifications); err != nil { + return fmt.Errorf("error reading S3 bucket \"%s\" lambda function notification: %s", d.Id(), err) + } + + // SQS Notification + queueNotifications := make([]map[string]interface{}, 0, len(notificationConfigs.QueueConfigurations)) + for _, notification := range notificationConfigs.QueueConfigurations { + conf := map[string]interface{}{} + + if notification.Id != nil { + conf["id"] = *notification.Id + } + + for _, f := range notification.Filter.Key.FilterRules { + if strings.ToLower(*f.Name) == "prefix" { + conf["filter_prefix"] = *f.Value + } + if strings.ToLower(*f.Name) == "suffix" { + conf["filter_suffix"] = *f.Value + } + } + + conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) + conf["queue"] = *notification.QueueArn + queueNotifications = append(queueNotifications, conf) + } + if err := d.Set("queue", queueNotifications); err != nil { + return fmt.Errorf("error reading S3 bucket \"%s\" queue notification: %s", d.Id(), err) + } + + return nil +} diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go b/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go new file mode 100644 index 000000000000..37a53618b518 --- /dev/null +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go @@ -0,0 +1,429 @@ +package aws + +import ( + "fmt" + "reflect" + "sort" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/s3" +) + +func TestAccAWSS3Bucket_Notification(t *testing.T) { + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3BucketNotificationDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSS3BucketConfigWithTopicNotification(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketTopicNotification( + "aws_s3_bucket.bucket", + "notification-sns1", + "aws_sns_topic.topic", + []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:Delete"}, + &s3.KeyFilter{ + FilterRules: []*s3.FilterRule{ + &s3.FilterRule{ + Name: aws.String("Prefix"), + Value: aws.String(fmt.Sprintf("%d/", rInt)), + }, + &s3.FilterRule{ + Name: aws.String("Suffix"), + Value: aws.String(".txt"), + }, + }, + }, + ), + testAccCheckAWSS3BucketTopicNotification( + "aws_s3_bucket.bucket", + "notification-sns2", + "aws_sns_topic.topic", + []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:Delete"}, + &s3.KeyFilter{ + FilterRules: []*s3.FilterRule{ + &s3.FilterRule{ + Name: aws.String("Suffix"), + Value: aws.String(".log"), + }, + }, + }, + ), + ), + }, + resource.TestStep{ + Config: testAccAWSS3BucketConfigWithQueueNotification(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketQueueNotification( + "aws_s3_bucket.bucket", + "notification-sqs", + "aws_sqs_queue.queue", + []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:Delete"}, + &s3.KeyFilter{ + FilterRules: []*s3.FilterRule{ + &s3.FilterRule{ + Name: aws.String("Prefix"), + Value: aws.String(fmt.Sprintf("%d/", rInt)), + }, + &s3.FilterRule{ + Name: aws.String("Suffix"), + Value: aws.String(".mp4"), + }, + }, + }, + ), + ), + }, + resource.TestStep{ + Config: testAccAWSS3BucketConfigWithLambdaNotification(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketLambdaFunctionConfiguration( + "aws_s3_bucket.bucket", + "notification-lambda", + "aws_lambda_function.func", + []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:Delete"}, + &s3.KeyFilter{ + FilterRules: []*s3.FilterRule{ + &s3.FilterRule{ + Name: aws.String("Prefix"), + Value: aws.String(fmt.Sprintf("%d/", rInt)), + }, + &s3.FilterRule{ + Name: aws.String("Suffix"), + Value: aws.String(".png"), + }, + }, + }, + ), + ), + }, + }, + }) +} + +func testAccCheckAWSS3BucketNotificationDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).s3conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_s3_bucket_notification" { + continue + } + out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String(rs.Primary.ID), + }) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoSuchBucket" { + return nil + } + return err + } + if len(out.TopicConfigurations) > 0 { + return fmt.Errorf("TopicConfigurations is exists: %v", out) + } + if len(out.LambdaFunctionConfigurations) > 0 { + return fmt.Errorf("LambdaFunctionConfigurations is exists: %v", out) + } + if len(out.QueueConfigurations) > 0 { + return fmt.Errorf("QueueConfigurations is exists: %v", out) + } + } + return nil +} + +func testAccCheckAWSS3BucketTopicNotification(n, i, t string, events []string, filters *s3.KeyFilter) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, _ := s.RootModule().Resources[n] + topicArn := s.RootModule().Resources[t].Primary.ID + conn := testAccProvider.Meta().(*AWSClient).s3conn + + out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String(rs.Primary.ID), + }) + + if err != nil { + return fmt.Errorf("GetBucketNotification error: %v", err) + } + + eventSlice := sort.StringSlice(events) + eventSlice.Sort() + + outputTopics := out.TopicConfigurations + matched := false + for _, outputTopic := range outputTopics { + if *outputTopic.Id == i { + matched = true + + if *outputTopic.TopicArn != topicArn { + return fmt.Errorf("bad topic arn, expected: %s, got %#v", topicArn, *outputTopic.TopicArn) + } + if !reflect.DeepEqual(filters, outputTopic.Filter.Key) { + return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputTopic.Filter.Key) + } + + outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputTopic.Events)) + outputEventSlice.Sort() + if !reflect.DeepEqual(eventSlice, outputEventSlice) { + return fmt.Errorf("bad notification events, expected: %#v, got %#v", events, outputEventSlice) + } + } + } + + if !matched { + return fmt.Errorf("No match topic configurations: %#v", out) + } + + return nil + } +} + +func testAccCheckAWSS3BucketQueueNotification(n, i, t string, events []string, filters *s3.KeyFilter) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, _ := s.RootModule().Resources[n] + queueArn := s.RootModule().Resources[t].Primary.Attributes["arn"] + conn := testAccProvider.Meta().(*AWSClient).s3conn + + out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String(rs.Primary.ID), + }) + + if err != nil { + return fmt.Errorf("GetBucketNotification error: %v", err) + } + + eventSlice := sort.StringSlice(events) + eventSlice.Sort() + + outputQueues := out.QueueConfigurations + matched := false + for _, outputQueue := range outputQueues { + if *outputQueue.Id == i { + matched = true + + if *outputQueue.QueueArn != queueArn { + return fmt.Errorf("bad queue arn, expected: %s, got %#v", queueArn, *outputQueue.QueueArn) + } + if !reflect.DeepEqual(filters, outputQueue.Filter.Key) { + return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputQueue.Filter.Key) + } + + outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputQueue.Events)) + outputEventSlice.Sort() + if !reflect.DeepEqual(eventSlice, outputEventSlice) { + return fmt.Errorf("bad notification events, expected: %#v, got %#v", events, outputEventSlice) + } + } + } + + if !matched { + return fmt.Errorf("No match queue configurations: %#v", out) + } + + return nil + } +} + +func testAccCheckAWSS3BucketLambdaFunctionConfiguration(n, i, t string, events []string, filters *s3.KeyFilter) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, _ := s.RootModule().Resources[n] + funcArn := s.RootModule().Resources[t].Primary.Attributes["arn"] + conn := testAccProvider.Meta().(*AWSClient).s3conn + + out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String(rs.Primary.ID), + }) + + if err != nil { + return fmt.Errorf("GetBucketNotification error: %v", err) + } + + eventSlice := sort.StringSlice(events) + eventSlice.Sort() + + outputFunctions := out.LambdaFunctionConfigurations + matched := false + for _, outputFunc := range outputFunctions { + if *outputFunc.Id == i { + matched = true + + if *outputFunc.LambdaFunctionArn != funcArn { + return fmt.Errorf("bad lambda function arn, expected: %s, got %#v", funcArn, *outputFunc.LambdaFunctionArn) + } + if !reflect.DeepEqual(filters, outputFunc.Filter.Key) { + return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputFunc.Filter.Key) + } + + outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputFunc.Events)) + outputEventSlice.Sort() + if !reflect.DeepEqual(eventSlice, outputEventSlice) { + return fmt.Errorf("bad notification events, expected: %#v, got %#v", events, outputEventSlice) + } + } + } + + if !matched { + return fmt.Errorf("No match lambda function configurations: %#v", out) + } + + return nil + } +} + +func testAccAWSS3BucketConfigWithTopicNotification(randInt int) string { + return fmt.Sprintf(` +resource "aws_sns_topic" "topic" { + name = "terraform-test-topic" + policy = < Date: Sun, 6 Mar 2016 22:34:01 +0900 Subject: [PATCH 02/13] Add s3_bucket_notification documentation --- .../r/s3_bucket_notification.html.markdown | 170 ++++++++++++++++++ website/source/layouts/aws.erb | 4 + 2 files changed, 174 insertions(+) create mode 100644 website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown diff --git a/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown b/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown new file mode 100644 index 000000000000..28f351c3babc --- /dev/null +++ b/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown @@ -0,0 +1,170 @@ +--- +layout: "aws" +page_title: "AWS: aws_s3_bucket_notification" +side_bar_current: "docs-aws-resource-s3-bucket-notification" +description: |- + Provides a S3 bucket notification resource. +--- + +# aws\_s3\_bucket\_notification + +Provides a S3 bucket notification resource. + +## Example Usage + +### Add notification configuration to SNS Topic + +``` +resource "aws_sns_topic" "topic" { + name = "s3-event-notification-topic" + policy = <aws_s3_bucket_object + > + aws_s3_bucket_notification + + From 3e61be95cd6c3df7b42ada9703f4c8a7e936dba3 Mon Sep 17 00:00:00 2001 From: Kazunori Kojima Date: Fri, 11 Mar 2016 10:09:57 +0900 Subject: [PATCH 03/13] Fix mismatch topic name and policy resource in document. --- .../docs/providers/aws/r/s3_bucket_notification.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown b/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown index 28f351c3babc..bc95b39d5d6c 100644 --- a/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown +++ b/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown @@ -24,7 +24,7 @@ resource "aws_sns_topic" "topic" { "Effect": "Allow", "Principal": {"AWS":"*"}, "Action": "SNS:Publish", - "Resource": "arn:aws:sns:*:*:terraform-test-topic", + "Resource": "arn:aws:sns:*:*:s3-event-notification-topic", "Condition":{ "ArnLike":{"aws:SourceArn":"${aws_s3_bucket.bucket.arn}"} } From 2362199506cfcd95fd9fe08b47b0e886a5e87bc5 Mon Sep 17 00:00:00 2001 From: Kazunori Kojima Date: Fri, 11 Mar 2016 10:14:59 +0900 Subject: [PATCH 04/13] Revert sqs policy normalize json commit --- builtin/providers/aws/resource_aws_sqs_queue.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/builtin/providers/aws/resource_aws_sqs_queue.go b/builtin/providers/aws/resource_aws_sqs_queue.go index d07bd26ad7cd..2ce67d9179cc 100644 --- a/builtin/providers/aws/resource_aws_sqs_queue.go +++ b/builtin/providers/aws/resource_aws_sqs_queue.go @@ -64,9 +64,8 @@ func resourceAwsSqsQueue() *schema.Resource { Computed: true, }, "policy": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - StateFunc: normalizeJson, + Type: schema.TypeString, + Optional: true, }, "redrive_policy": &schema.Schema{ Type: schema.TypeString, @@ -178,9 +177,6 @@ func resourceAwsSqsQueueRead(d *schema.ResourceData, meta interface{}) error { } d.Set(iKey, value) } else { - if iKey == "policy" { - *attrmap[oKey] = normalizeJson(*attrmap[oKey]) - } d.Set(iKey, *attrmap[oKey]) } } From 69bf108a7bc02210beebdf5651b3742b0b13ca45 Mon Sep 17 00:00:00 2001 From: Kazunori Kojima Date: Fri, 11 Mar 2016 10:15:29 +0900 Subject: [PATCH 05/13] Stop use here document sqs policy --- .../resource_aws_s3_bucket_notification_test.go | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go b/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go index 37a53618b518..b628708f7341 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go @@ -331,22 +331,7 @@ func testAccAWSS3BucketConfigWithQueueNotification(randInt int) string { return fmt.Sprintf(` resource "aws_sqs_queue" "queue" { name = "terraform-test-queue-%d" - policy = < Date: Sat, 12 Mar 2016 21:09:28 +0900 Subject: [PATCH 06/13] Fix can not detect the change configuration --- .../resource_aws_s3_bucket_notification.go | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification.go b/builtin/providers/aws/resource_aws_s3_bucket_notification.go index bb7f5ea4a3af..b5657c387372 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification.go @@ -1,12 +1,10 @@ package aws import ( - "bytes" "fmt" "log" "strings" - "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" "github.com/aws/aws-sdk-go/aws" @@ -57,13 +55,6 @@ func resourceAwsS3BucketNotification() *schema.Resource { }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%t-", m["filter_prefix"].(string))) - buf.WriteString(fmt.Sprintf("%t-", m["filter_suffix"].(string))) - return hashcode.String(buf.String()) - }, }, "queue": &schema.Schema{ @@ -95,13 +86,6 @@ func resourceAwsS3BucketNotification() *schema.Resource { }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%t-", m["filter_prefix"].(string))) - buf.WriteString(fmt.Sprintf("%t-", m["filter_suffix"].(string))) - return hashcode.String(buf.String()) - }, }, "lambda_function": &schema.Schema{ @@ -133,13 +117,6 @@ func resourceAwsS3BucketNotification() *schema.Resource { }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%t-", m["filter_prefix"].(string))) - buf.WriteString(fmt.Sprintf("%t-", m["filter_suffix"].(string))) - return hashcode.String(buf.String()) - }, }, }, } From 6bb8954dcb49329b6789509c3a6954a8d4ee7463 Mon Sep 17 00:00:00 2001 From: Kazunori Kojima Date: Sun, 13 Mar 2016 02:14:41 +0900 Subject: [PATCH 07/13] Fix MalformedXML error when not specific filter rules --- .../resource_aws_s3_bucket_notification.go | 72 ++++++++------ ...esource_aws_s3_bucket_notification_test.go | 96 +++++++++++++++++-- 2 files changed, 132 insertions(+), 36 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification.go b/builtin/providers/aws/resource_aws_s3_bucket_notification.go index b5657c387372..d569fe0d9c90 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification.go @@ -168,10 +168,12 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } filterRules = append(filterRules, filterRule) } - tc.Filter = &s3.NotificationConfigurationFilter{ - Key: &s3.KeyFilter{ - FilterRules: filterRules, - }, + if len(filterRules) > 0 { + tc.Filter = &s3.NotificationConfigurationFilter{ + Key: &s3.KeyFilter{ + FilterRules: filterRules, + }, + } } topicConfigs = append(topicConfigs, tc) } @@ -218,10 +220,12 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } filterRules = append(filterRules, filterRule) } - lc.Filter = &s3.NotificationConfigurationFilter{ - Key: &s3.KeyFilter{ - FilterRules: filterRules, - }, + if len(filterRules) > 0 { + lc.Filter = &s3.NotificationConfigurationFilter{ + Key: &s3.KeyFilter{ + FilterRules: filterRules, + }, + } } lambdaConfigs = append(lambdaConfigs, lc) } @@ -268,10 +272,12 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } filterRules = append(filterRules, filterRule) } - qc.Filter = &s3.NotificationConfigurationFilter{ - Key: &s3.KeyFilter{ - FilterRules: filterRules, - }, + if len(filterRules) > 0 { + qc.Filter = &s3.NotificationConfigurationFilter{ + Key: &s3.KeyFilter{ + FilterRules: filterRules, + }, + } } queueConfigs = append(queueConfigs, qc) } @@ -357,12 +363,14 @@ func resourceAwsS3BucketNotificationRead(d *schema.ResourceData, meta interface{ conf["id"] = *notification.Id } - for _, f := range notification.Filter.Key.FilterRules { - if strings.ToLower(*f.Name) == "prefix" { - conf["filter_prefix"] = *f.Value - } - if strings.ToLower(*f.Name) == "suffix" { - conf["filter_suffix"] = *f.Value + if filter := notification.Filter; filter != nil { + for _, f := range filter.Key.FilterRules { + if strings.ToLower(*f.Name) == "prefix" { + conf["filter_prefix"] = *f.Value + } + if strings.ToLower(*f.Name) == "suffix" { + conf["filter_suffix"] = *f.Value + } } } @@ -383,12 +391,14 @@ func resourceAwsS3BucketNotificationRead(d *schema.ResourceData, meta interface{ conf["id"] = *notification.Id } - for _, f := range notification.Filter.Key.FilterRules { - if strings.ToLower(*f.Name) == "prefix" { - conf["filter_prefix"] = *f.Value - } - if strings.ToLower(*f.Name) == "suffix" { - conf["filter_suffix"] = *f.Value + if filter := notification.Filter; filter != nil { + for _, f := range filter.Key.FilterRules { + if strings.ToLower(*f.Name) == "prefix" { + conf["filter_prefix"] = *f.Value + } + if strings.ToLower(*f.Name) == "suffix" { + conf["filter_suffix"] = *f.Value + } } } @@ -409,12 +419,14 @@ func resourceAwsS3BucketNotificationRead(d *schema.ResourceData, meta interface{ conf["id"] = *notification.Id } - for _, f := range notification.Filter.Key.FilterRules { - if strings.ToLower(*f.Name) == "prefix" { - conf["filter_prefix"] = *f.Value - } - if strings.ToLower(*f.Name) == "suffix" { - conf["filter_suffix"] = *f.Value + if filter := notification.Filter; filter != nil { + for _, f := range filter.Key.FilterRules { + if strings.ToLower(*f.Name) == "prefix" { + conf["filter_prefix"] = *f.Value + } + if strings.ToLower(*f.Name) == "suffix" { + conf["filter_suffix"] = *f.Value + } } } diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go b/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go index b628708f7341..b35108bedbd1 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go @@ -109,6 +109,29 @@ func TestAccAWSS3Bucket_Notification(t *testing.T) { }) } +func TestAccAWSS3Bucket_NotificationWithoutFilter(t *testing.T) { + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3BucketNotificationDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSS3BucketConfigWithTopicNotificationWithoutFilter(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketTopicNotification( + "aws_s3_bucket.bucket", + "notification-sns1", + "aws_sns_topic.topic", + []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:Delete"}, + nil, + ), + ), + }, + }, + }) +} + func testAccCheckAWSS3BucketNotificationDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).s3conn @@ -164,8 +187,15 @@ func testAccCheckAWSS3BucketTopicNotification(n, i, t string, events []string, f if *outputTopic.TopicArn != topicArn { return fmt.Errorf("bad topic arn, expected: %s, got %#v", topicArn, *outputTopic.TopicArn) } - if !reflect.DeepEqual(filters, outputTopic.Filter.Key) { - return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputTopic.Filter.Key) + + if filters != nil { + if !reflect.DeepEqual(filters, outputTopic.Filter.Key) { + return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputTopic.Filter.Key) + } + } else { + if outputTopic.Filter != nil { + return fmt.Errorf("bad notification filters, expected: nil, got %#v", outputTopic.Filter) + } } outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputTopic.Events)) @@ -210,8 +240,15 @@ func testAccCheckAWSS3BucketQueueNotification(n, i, t string, events []string, f if *outputQueue.QueueArn != queueArn { return fmt.Errorf("bad queue arn, expected: %s, got %#v", queueArn, *outputQueue.QueueArn) } - if !reflect.DeepEqual(filters, outputQueue.Filter.Key) { - return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputQueue.Filter.Key) + + if filters != nil { + if !reflect.DeepEqual(filters, outputQueue.Filter.Key) { + return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputQueue.Filter.Key) + } + } else { + if outputQueue.Filter != nil { + return fmt.Errorf("bad notification filters, expected: nil, got %#v", outputQueue.Filter) + } } outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputQueue.Events)) @@ -256,8 +293,15 @@ func testAccCheckAWSS3BucketLambdaFunctionConfiguration(n, i, t string, events [ if *outputFunc.LambdaFunctionArn != funcArn { return fmt.Errorf("bad lambda function arn, expected: %s, got %#v", funcArn, *outputFunc.LambdaFunctionArn) } - if !reflect.DeepEqual(filters, outputFunc.Filter.Key) { - return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputFunc.Filter.Key) + + if filters != nil { + if !reflect.DeepEqual(filters, outputFunc.Filter.Key) { + return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputFunc.Filter.Key) + } + } else { + if outputFunc.Filter != nil { + return fmt.Errorf("bad notification filters, expected: nil, got %#v", outputFunc.Filter) + } } outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputFunc.Events)) @@ -412,3 +456,43 @@ resource "aws_s3_bucket_notification" "notification" { } `, randInt, randInt) } + +func testAccAWSS3BucketConfigWithTopicNotificationWithoutFilter(randInt int) string { + return fmt.Sprintf(` +resource "aws_sns_topic" "topic" { + name = "terraform-test-topic" + policy = < Date: Sun, 13 Mar 2016 02:21:42 +0900 Subject: [PATCH 08/13] Use resource.Retry when put configuration --- .../aws/resource_aws_s3_bucket_notification.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification.go b/builtin/providers/aws/resource_aws_s3_bucket_notification.go index d569fe0d9c90..13e813ac148f 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification.go @@ -4,7 +4,9 @@ import ( "fmt" "log" "strings" + "time" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/aws/aws-sdk-go/aws" @@ -298,7 +300,20 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } log.Printf("[DEBUG] S3 bucket: %s, Putting notification: %v", bucket, i) - _, err := s3conn.PutBucketNotificationConfiguration(i) + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + if _, err := s3conn.PutBucketNotificationConfiguration(i); err != nil { + if awserr, ok := err.(awserr.Error); ok { + switch awserr.Message() { + case "Unable to validate the following destination configurations": + return resource.RetryableError(awserr) + } + } + // Didn't recognize the error, so shouldn't retry. + return resource.NonRetryableError(err) + } + // Successful put configuration + return nil + }) if err != nil { return fmt.Errorf("Error putting S3 notification configuration: %s", err) } From f3830d50680962a177e4381afe77227aebf97b06 Mon Sep 17 00:00:00 2001 From: Kazunori Kojima Date: Sun, 20 Mar 2016 00:14:39 +0900 Subject: [PATCH 09/13] Auto generate notification id when empty --- .../resource_aws_s3_bucket_notification.go | 170 +++++++++--------- 1 file changed, 87 insertions(+), 83 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification.go b/builtin/providers/aws/resource_aws_s3_bucket_notification.go index 13e813ac148f..9ec07f27cb59 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification.go @@ -29,13 +29,14 @@ func resourceAwsS3BucketNotification() *schema.Resource { }, "topic": &schema.Schema{ - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, }, "filter_prefix": &schema.Schema{ Type: schema.TypeString, @@ -60,13 +61,14 @@ func resourceAwsS3BucketNotification() *schema.Resource { }, "queue": &schema.Schema{ - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, }, "filter_prefix": &schema.Schema{ Type: schema.TypeString, @@ -91,13 +93,14 @@ func resourceAwsS3BucketNotification() *schema.Resource { }, "lambda_function": &schema.Schema{ - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, }, "filter_prefix": &schema.Schema{ Type: schema.TypeString, @@ -129,7 +132,7 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} bucket := d.Get("bucket").(string) // TopicNotifications - topicNotifications := d.Get("topic").(*schema.Set).List() + topicNotifications := d.Get("topic").([]interface{}) topicConfigs := make([]*s3.TopicConfiguration, 0, len(topicNotifications)) for _, c := range topicNotifications { tc := &s3.TopicConfiguration{} @@ -137,8 +140,10 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} c := c.(map[string]interface{}) // Id - if val, ok := c["id"].(string); ok { + if val, ok := c["id"].(string); ok && val != "" { tc.Id = aws.String(val) + } else { + tc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-topic-")) } // TopicArn @@ -147,11 +152,9 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // Events - if v := c["events"].(*schema.Set); v.Len() > 0 { - tc.Events = make([]*string, 0, v.Len()) - for _, val := range v.List() { - tc.Events = append(tc.Events, aws.String(val.(string))) - } + tc.Events = make([]*string, 0, len(c["events"].(*schema.Set).List())) + for _, e := range c["events"].(*schema.Set).List() { + tc.Events = append(tc.Events, aws.String(e.(string))) } // Filter @@ -181,7 +184,7 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // Lambda - lambdaFunctionNotifications := d.Get("lambda_function").(*schema.Set).List() + lambdaFunctionNotifications := d.Get("lambda_function").([]interface{}) lambdaConfigs := make([]*s3.LambdaFunctionConfiguration, 0, len(lambdaFunctionNotifications)) for _, c := range lambdaFunctionNotifications { lc := &s3.LambdaFunctionConfiguration{} @@ -189,8 +192,10 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} c := c.(map[string]interface{}) // Id - if val, ok := c["id"].(string); ok { + if val, ok := c["id"].(string); ok && val != "" { lc.Id = aws.String(val) + } else { + lc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-lambda-")) } // LambdaFunctionArn @@ -199,11 +204,9 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // Events - if v := c["events"].(*schema.Set); v.Len() > 0 { - lc.Events = make([]*string, 0, v.Len()) - for _, val := range v.List() { - lc.Events = append(lc.Events, aws.String(val.(string))) - } + lc.Events = make([]*string, 0, len(c["events"].(*schema.Set).List())) + for _, e := range c["events"].(*schema.Set).List() { + lc.Events = append(lc.Events, aws.String(e.(string))) } // Filter @@ -233,7 +236,7 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // SQS - queueNotifications := d.Get("queue").(*schema.Set).List() + queueNotifications := d.Get("queue").([]interface{}) queueConfigs := make([]*s3.QueueConfiguration, 0, len(queueNotifications)) for _, c := range queueNotifications { qc := &s3.QueueConfiguration{} @@ -241,8 +244,10 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} c := c.(map[string]interface{}) // Id - if val, ok := c["id"].(string); ok { + if val, ok := c["id"].(string); ok && val != "" { qc.Id = aws.String(val) + } else { + qc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-queue-")) } // QueueArn @@ -251,11 +256,9 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // Events - if v := c["events"].(*schema.Set); v.Len() > 0 { - qc.Events = make([]*string, 0, v.Len()) - for _, val := range v.List() { - qc.Events = append(qc.Events, aws.String(val.(string))) - } + qc.Events = make([]*string, 0, len(c["events"].(*schema.Set).List())) + for _, e := range c["events"].(*schema.Set).List() { + qc.Events = append(qc.Events, aws.String(e.(string))) } // Filter @@ -370,88 +373,89 @@ func resourceAwsS3BucketNotificationRead(d *schema.ResourceData, meta interface{ } log.Printf("[DEBUG] S3 Bucket: %s, get notification: %v", d.Id(), notificationConfigs) // Topic Notification - topicNotifications := make([]map[string]interface{}, 0, len(notificationConfigs.TopicConfigurations)) - for _, notification := range notificationConfigs.TopicConfigurations { - conf := map[string]interface{}{} + if err := d.Set("topic", flattenTopicConfigurations(notificationConfigs.TopicConfigurations)); err != nil { + return fmt.Errorf("error reading S3 bucket \"%s\" topic notification: %s", d.Id(), err) + } - if notification.Id != nil { - conf["id"] = *notification.Id + // SQS Notification + if err := d.Set("queue", flattenQueueConfigurations(notificationConfigs.QueueConfigurations)); err != nil { + return fmt.Errorf("error reading S3 bucket \"%s\" queue notification: %s", d.Id(), err) + } + + // Lambda Notification + if err := d.Set("lambda_function", flattenLambdaFunctionConfigurations(notificationConfigs.LambdaFunctionConfigurations)); err != nil { + return fmt.Errorf("error reading S3 bucket \"%s\" lambda function notification: %s", d.Id(), err) + } + + return nil +} + +func flattenNotificationConfigurationFilter(filter *s3.NotificationConfigurationFilter) map[string]interface{} { + filterRules := map[string]interface{}{} + for _, f := range filter.Key.FilterRules { + if strings.ToLower(*f.Name) == s3.FilterRuleNamePrefix { + filterRules["filter_prefix"] = *f.Value } + if strings.ToLower(*f.Name) == s3.FilterRuleNameSuffix { + filterRules["filter_suffix"] = *f.Value + } + } + return filterRules +} +func flattenTopicConfigurations(configs []*s3.TopicConfiguration) []map[string]interface{} { + topicNotifications := make([]map[string]interface{}, 0, len(configs)) + for _, notification := range configs { + var conf map[string]interface{} if filter := notification.Filter; filter != nil { - for _, f := range filter.Key.FilterRules { - if strings.ToLower(*f.Name) == "prefix" { - conf["filter_prefix"] = *f.Value - } - if strings.ToLower(*f.Name) == "suffix" { - conf["filter_suffix"] = *f.Value - } - } + conf = flattenNotificationConfigurationFilter(filter) + } else { + conf = map[string]interface{}{} } + conf["id"] = *notification.Id conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) conf["topic"] = *notification.TopicArn topicNotifications = append(topicNotifications, conf) } - if err := d.Set("topic", topicNotifications); err != nil { - return fmt.Errorf("error reading S3 bucket \"%s\" topic notification: %s", d.Id(), err) - } - // Lambda Notification - lambdaFunctionNotifications := make([]map[string]interface{}, 0, len(notificationConfigs.LambdaFunctionConfigurations)) - for _, notification := range notificationConfigs.LambdaFunctionConfigurations { - conf := map[string]interface{}{} - - if notification.Id != nil { - conf["id"] = *notification.Id - } + return topicNotifications +} +func flattenQueueConfigurations(configs []*s3.QueueConfiguration) []map[string]interface{} { + queueNotifications := make([]map[string]interface{}, 0, len(configs)) + for _, notification := range configs { + var conf map[string]interface{} if filter := notification.Filter; filter != nil { - for _, f := range filter.Key.FilterRules { - if strings.ToLower(*f.Name) == "prefix" { - conf["filter_prefix"] = *f.Value - } - if strings.ToLower(*f.Name) == "suffix" { - conf["filter_suffix"] = *f.Value - } - } + conf = flattenNotificationConfigurationFilter(filter) + } else { + conf = map[string]interface{}{} } + conf["id"] = *notification.Id conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) - conf["lambda_function"] = *notification.LambdaFunctionArn - lambdaFunctionNotifications = append(lambdaFunctionNotifications, conf) - } - if err := d.Set("lambda_function", lambdaFunctionNotifications); err != nil { - return fmt.Errorf("error reading S3 bucket \"%s\" lambda function notification: %s", d.Id(), err) + conf["queue"] = *notification.QueueArn + queueNotifications = append(queueNotifications, conf) } - // SQS Notification - queueNotifications := make([]map[string]interface{}, 0, len(notificationConfigs.QueueConfigurations)) - for _, notification := range notificationConfigs.QueueConfigurations { - conf := map[string]interface{}{} - - if notification.Id != nil { - conf["id"] = *notification.Id - } + return queueNotifications +} +func flattenLambdaFunctionConfigurations(configs []*s3.LambdaFunctionConfiguration) []map[string]interface{} { + lambdaFunctionNotifications := make([]map[string]interface{}, 0, len(configs)) + for _, notification := range configs { + var conf map[string]interface{} if filter := notification.Filter; filter != nil { - for _, f := range filter.Key.FilterRules { - if strings.ToLower(*f.Name) == "prefix" { - conf["filter_prefix"] = *f.Value - } - if strings.ToLower(*f.Name) == "suffix" { - conf["filter_suffix"] = *f.Value - } - } + conf = flattenNotificationConfigurationFilter(filter) + } else { + conf = map[string]interface{}{} } + conf["id"] = *notification.Id conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) - conf["queue"] = *notification.QueueArn - queueNotifications = append(queueNotifications, conf) - } - if err := d.Set("queue", queueNotifications); err != nil { - return fmt.Errorf("error reading S3 bucket \"%s\" queue notification: %s", d.Id(), err) + conf["lambda_function"] = *notification.LambdaFunctionArn + lambdaFunctionNotifications = append(lambdaFunctionNotifications, conf) } - return nil + return lambdaFunctionNotifications } From cf1748463246fe51504e7460788fa424ac171e9e Mon Sep 17 00:00:00 2001 From: Kazunori Kojima Date: Sun, 20 Mar 2016 00:15:06 +0900 Subject: [PATCH 10/13] Use resource.Retry in s3 bucket notification test --- ...esource_aws_s3_bucket_notification_test.go | 241 ++++++++++-------- 1 file changed, 131 insertions(+), 110 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go b/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go index b35108bedbd1..6a1588236054 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go @@ -5,6 +5,7 @@ import ( "reflect" "sort" "testing" + "time" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" @@ -139,24 +140,32 @@ func testAccCheckAWSS3BucketNotificationDestroy(s *terraform.State) error { if rs.Type != "aws_s3_bucket_notification" { continue } - out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ - Bucket: aws.String(rs.Primary.ID), + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String(rs.Primary.ID), + }) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoSuchBucket" { + return nil + } + return resource.NonRetryableError(err) + } + if len(out.TopicConfigurations) > 0 { + return resource.RetryableError(fmt.Errorf("TopicConfigurations is exists: %v", out)) + } + if len(out.LambdaFunctionConfigurations) > 0 { + return resource.RetryableError(fmt.Errorf("LambdaFunctionConfigurations is exists: %v", out)) + } + if len(out.QueueConfigurations) > 0 { + return resource.RetryableError(fmt.Errorf("QueueConfigurations is exists: %v", out)) + } + + return nil }) + if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoSuchBucket" { - return nil - } return err } - if len(out.TopicConfigurations) > 0 { - return fmt.Errorf("TopicConfigurations is exists: %v", out) - } - if len(out.LambdaFunctionConfigurations) > 0 { - return fmt.Errorf("LambdaFunctionConfigurations is exists: %v", out) - } - if len(out.QueueConfigurations) > 0 { - return fmt.Errorf("QueueConfigurations is exists: %v", out) - } } return nil } @@ -167,50 +176,54 @@ func testAccCheckAWSS3BucketTopicNotification(n, i, t string, events []string, f topicArn := s.RootModule().Resources[t].Primary.ID conn := testAccProvider.Meta().(*AWSClient).s3conn - out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ - Bucket: aws.String(rs.Primary.ID), - }) + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String(rs.Primary.ID), + }) - if err != nil { - return fmt.Errorf("GetBucketNotification error: %v", err) - } + if err != nil { + return resource.NonRetryableError(fmt.Errorf("GetBucketNotification error: %v", err)) + } - eventSlice := sort.StringSlice(events) - eventSlice.Sort() + eventSlice := sort.StringSlice(events) + eventSlice.Sort() - outputTopics := out.TopicConfigurations - matched := false - for _, outputTopic := range outputTopics { - if *outputTopic.Id == i { - matched = true + outputTopics := out.TopicConfigurations + matched := false + for _, outputTopic := range outputTopics { + if *outputTopic.Id == i { + matched = true - if *outputTopic.TopicArn != topicArn { - return fmt.Errorf("bad topic arn, expected: %s, got %#v", topicArn, *outputTopic.TopicArn) - } + if *outputTopic.TopicArn != topicArn { + return resource.RetryableError(fmt.Errorf("bad topic arn, expected: %s, got %#v", topicArn, *outputTopic.TopicArn)) + } - if filters != nil { - if !reflect.DeepEqual(filters, outputTopic.Filter.Key) { - return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputTopic.Filter.Key) + if filters != nil { + if !reflect.DeepEqual(filters, outputTopic.Filter.Key) { + return resource.RetryableError(fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputTopic.Filter.Key)) + } + } else { + if outputTopic.Filter != nil { + return resource.RetryableError(fmt.Errorf("bad notification filters, expected: nil, got %#v", outputTopic.Filter)) + } } - } else { - if outputTopic.Filter != nil { - return fmt.Errorf("bad notification filters, expected: nil, got %#v", outputTopic.Filter) + + outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputTopic.Events)) + outputEventSlice.Sort() + if !reflect.DeepEqual(eventSlice, outputEventSlice) { + return resource.RetryableError(fmt.Errorf("bad notification events, expected: %#v, got %#v", events, outputEventSlice)) } } + } - outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputTopic.Events)) - outputEventSlice.Sort() - if !reflect.DeepEqual(eventSlice, outputEventSlice) { - return fmt.Errorf("bad notification events, expected: %#v, got %#v", events, outputEventSlice) - } + if !matched { + return resource.RetryableError(fmt.Errorf("No match topic configurations: %#v", out)) } - } - if !matched { - return fmt.Errorf("No match topic configurations: %#v", out) - } + return nil + }) - return nil + return err } } @@ -220,50 +233,54 @@ func testAccCheckAWSS3BucketQueueNotification(n, i, t string, events []string, f queueArn := s.RootModule().Resources[t].Primary.Attributes["arn"] conn := testAccProvider.Meta().(*AWSClient).s3conn - out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ - Bucket: aws.String(rs.Primary.ID), - }) + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String(rs.Primary.ID), + }) - if err != nil { - return fmt.Errorf("GetBucketNotification error: %v", err) - } + if err != nil { + return resource.NonRetryableError(fmt.Errorf("GetBucketNotification error: %v", err)) + } - eventSlice := sort.StringSlice(events) - eventSlice.Sort() + eventSlice := sort.StringSlice(events) + eventSlice.Sort() - outputQueues := out.QueueConfigurations - matched := false - for _, outputQueue := range outputQueues { - if *outputQueue.Id == i { - matched = true + outputQueues := out.QueueConfigurations + matched := false + for _, outputQueue := range outputQueues { + if *outputQueue.Id == i { + matched = true - if *outputQueue.QueueArn != queueArn { - return fmt.Errorf("bad queue arn, expected: %s, got %#v", queueArn, *outputQueue.QueueArn) - } + if *outputQueue.QueueArn != queueArn { + return resource.RetryableError(fmt.Errorf("bad queue arn, expected: %s, got %#v", queueArn, *outputQueue.QueueArn)) + } - if filters != nil { - if !reflect.DeepEqual(filters, outputQueue.Filter.Key) { - return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputQueue.Filter.Key) + if filters != nil { + if !reflect.DeepEqual(filters, outputQueue.Filter.Key) { + return resource.RetryableError(fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputQueue.Filter.Key)) + } + } else { + if outputQueue.Filter != nil { + return resource.RetryableError(fmt.Errorf("bad notification filters, expected: nil, got %#v", outputQueue.Filter)) + } } - } else { - if outputQueue.Filter != nil { - return fmt.Errorf("bad notification filters, expected: nil, got %#v", outputQueue.Filter) + + outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputQueue.Events)) + outputEventSlice.Sort() + if !reflect.DeepEqual(eventSlice, outputEventSlice) { + return resource.RetryableError(fmt.Errorf("bad notification events, expected: %#v, got %#v", events, outputEventSlice)) } } + } - outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputQueue.Events)) - outputEventSlice.Sort() - if !reflect.DeepEqual(eventSlice, outputEventSlice) { - return fmt.Errorf("bad notification events, expected: %#v, got %#v", events, outputEventSlice) - } + if !matched { + return resource.RetryableError(fmt.Errorf("No match queue configurations: %#v", out)) } - } - if !matched { - return fmt.Errorf("No match queue configurations: %#v", out) - } + return nil + }) - return nil + return err } } @@ -273,50 +290,54 @@ func testAccCheckAWSS3BucketLambdaFunctionConfiguration(n, i, t string, events [ funcArn := s.RootModule().Resources[t].Primary.Attributes["arn"] conn := testAccProvider.Meta().(*AWSClient).s3conn - out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ - Bucket: aws.String(rs.Primary.ID), - }) + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + out, err := conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String(rs.Primary.ID), + }) - if err != nil { - return fmt.Errorf("GetBucketNotification error: %v", err) - } + if err != nil { + return resource.NonRetryableError(fmt.Errorf("GetBucketNotification error: %v", err)) + } - eventSlice := sort.StringSlice(events) - eventSlice.Sort() + eventSlice := sort.StringSlice(events) + eventSlice.Sort() - outputFunctions := out.LambdaFunctionConfigurations - matched := false - for _, outputFunc := range outputFunctions { - if *outputFunc.Id == i { - matched = true + outputFunctions := out.LambdaFunctionConfigurations + matched := false + for _, outputFunc := range outputFunctions { + if *outputFunc.Id == i { + matched = true - if *outputFunc.LambdaFunctionArn != funcArn { - return fmt.Errorf("bad lambda function arn, expected: %s, got %#v", funcArn, *outputFunc.LambdaFunctionArn) - } + if *outputFunc.LambdaFunctionArn != funcArn { + return resource.RetryableError(fmt.Errorf("bad lambda function arn, expected: %s, got %#v", funcArn, *outputFunc.LambdaFunctionArn)) + } - if filters != nil { - if !reflect.DeepEqual(filters, outputFunc.Filter.Key) { - return fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputFunc.Filter.Key) + if filters != nil { + if !reflect.DeepEqual(filters, outputFunc.Filter.Key) { + return resource.RetryableError(fmt.Errorf("bad notification filters, expected: %#v, got %#v", filters, outputFunc.Filter.Key)) + } + } else { + if outputFunc.Filter != nil { + return resource.RetryableError(fmt.Errorf("bad notification filters, expected: nil, got %#v", outputFunc.Filter)) + } } - } else { - if outputFunc.Filter != nil { - return fmt.Errorf("bad notification filters, expected: nil, got %#v", outputFunc.Filter) + + outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputFunc.Events)) + outputEventSlice.Sort() + if !reflect.DeepEqual(eventSlice, outputEventSlice) { + return resource.RetryableError(fmt.Errorf("bad notification events, expected: %#v, got %#v", events, outputEventSlice)) } } + } - outputEventSlice := sort.StringSlice(aws.StringValueSlice(outputFunc.Events)) - outputEventSlice.Sort() - if !reflect.DeepEqual(eventSlice, outputEventSlice) { - return fmt.Errorf("bad notification events, expected: %#v, got %#v", events, outputEventSlice) - } + if !matched { + return resource.RetryableError(fmt.Errorf("No match lambda function configurations: %#v", out)) } - } - if !matched { - return fmt.Errorf("No match lambda function configurations: %#v", out) - } + return nil + }) - return nil + return err } } From 4b72b0daedcfb65581aefa120934c630b0f932cb Mon Sep 17 00:00:00 2001 From: Kazunori Kojima Date: Sun, 20 Mar 2016 00:23:31 +0900 Subject: [PATCH 11/13] Add `_arn` suffix --- .../aws/resource_aws_s3_bucket_notification.go | 18 +++++++++--------- ...resource_aws_s3_bucket_notification_test.go | 10 +++++----- .../aws/r/s3_bucket_notification.html.markdown | 12 ++++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification.go b/builtin/providers/aws/resource_aws_s3_bucket_notification.go index 9ec07f27cb59..3ab4224589a2 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification.go @@ -46,7 +46,7 @@ func resourceAwsS3BucketNotification() *schema.Resource { Type: schema.TypeString, Optional: true, }, - "topic": &schema.Schema{ + "topic_arn": &schema.Schema{ Type: schema.TypeString, Required: true, }, @@ -78,7 +78,7 @@ func resourceAwsS3BucketNotification() *schema.Resource { Type: schema.TypeString, Optional: true, }, - "queue": &schema.Schema{ + "queue_arn": &schema.Schema{ Type: schema.TypeString, Required: true, }, @@ -110,7 +110,7 @@ func resourceAwsS3BucketNotification() *schema.Resource { Type: schema.TypeString, Optional: true, }, - "lambda_function": &schema.Schema{ + "lambda_function_arn": &schema.Schema{ Type: schema.TypeString, Optional: true, }, @@ -147,7 +147,7 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // TopicArn - if val, ok := c["topic"].(string); ok { + if val, ok := c["topic_arn"].(string); ok { tc.TopicArn = aws.String(val) } @@ -199,7 +199,7 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // LambdaFunctionArn - if val, ok := c["lambda_function"].(string); ok { + if val, ok := c["lambda_function_arn"].(string); ok { lc.LambdaFunctionArn = aws.String(val) } @@ -251,7 +251,7 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // QueueArn - if val, ok := c["queue"].(string); ok { + if val, ok := c["queue_arn"].(string); ok { qc.QueueArn = aws.String(val) } @@ -415,7 +415,7 @@ func flattenTopicConfigurations(configs []*s3.TopicConfiguration) []map[string]i conf["id"] = *notification.Id conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) - conf["topic"] = *notification.TopicArn + conf["topic_arn"] = *notification.TopicArn topicNotifications = append(topicNotifications, conf) } @@ -434,7 +434,7 @@ func flattenQueueConfigurations(configs []*s3.QueueConfiguration) []map[string]i conf["id"] = *notification.Id conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) - conf["queue"] = *notification.QueueArn + conf["queue_arn"] = *notification.QueueArn queueNotifications = append(queueNotifications, conf) } @@ -453,7 +453,7 @@ func flattenLambdaFunctionConfigurations(configs []*s3.LambdaFunctionConfigurati conf["id"] = *notification.Id conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) - conf["lambda_function"] = *notification.LambdaFunctionArn + conf["lambda_function_arn"] = *notification.LambdaFunctionArn lambdaFunctionNotifications = append(lambdaFunctionNotifications, conf) } diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go b/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go index 6a1588236054..58ffd9a1d0b6 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification_test.go @@ -371,7 +371,7 @@ resource "aws_s3_bucket_notification" "notification" { bucket = "${aws_s3_bucket.bucket.id}" topic { id = "notification-sns1" - topic = "${aws_sns_topic.topic.arn}" + topic_arn = "${aws_sns_topic.topic.arn}" events = [ "s3:ObjectCreated:*", "s3:ObjectRemoved:Delete", @@ -381,7 +381,7 @@ resource "aws_s3_bucket_notification" "notification" { } topic { id = "notification-sns2" - topic = "${aws_sns_topic.topic.arn}" + topic_arn = "${aws_sns_topic.topic.arn}" events = [ "s3:ObjectCreated:*", "s3:ObjectRemoved:Delete", @@ -408,7 +408,7 @@ resource "aws_s3_bucket_notification" "notification" { bucket = "${aws_s3_bucket.bucket.id}" queue { id = "notification-sqs" - queue = "${aws_sqs_queue.queue.arn}" + queue_arn = "${aws_sqs_queue.queue.arn}" events = [ "s3:ObjectCreated:*", "s3:ObjectRemoved:Delete", @@ -466,7 +466,7 @@ resource "aws_s3_bucket_notification" "notification" { bucket = "${aws_s3_bucket.bucket.id}" lambda_function { id = "notification-lambda" - lambda_function = "${aws_lambda_function.func.arn}" + lambda_function_arn = "${aws_lambda_function.func.arn}" events = [ "s3:ObjectCreated:*", "s3:ObjectRemoved:Delete", @@ -508,7 +508,7 @@ resource "aws_s3_bucket_notification" "notification" { bucket = "${aws_s3_bucket.bucket.id}" topic { id = "notification-sns1" - topic = "${aws_sns_topic.topic.arn}" + topic_arn = "${aws_sns_topic.topic.arn}" events = [ "s3:ObjectCreated:*", "s3:ObjectRemoved:Delete", diff --git a/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown b/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown index bc95b39d5d6c..fac3a8d497c0 100644 --- a/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown +++ b/website/source/docs/providers/aws/r/s3_bucket_notification.html.markdown @@ -40,7 +40,7 @@ resource "aws_s3_bucket" "bucket" { resource "aws_s3_bucket_notification" "bucket_notification" { bucket = "${aws_s3_bucket.bucket.id}" topic { - topic = "${aws_sns_topic.topic.arn}" + topic_arn = "${aws_sns_topic.topic.arn}" events = ["s3:ObjectCreated:*"] filter_suffix = ".log" } @@ -77,7 +77,7 @@ resource "aws_s3_bucket" "bucket" { resource "aws_s3_bucket_notification" "bucket_notification" { bucket = "${aws_s3_bucket.bucket.id}" queue { - queue = "${aws_sqs_queue.queue.arn}" + queue_arn = "${aws_sqs_queue.queue.arn}" events = ["s3:ObjectCreated:*"] filter_suffix = ".log" } @@ -127,7 +127,7 @@ resource "aws_s3_bucket" "bucket" { resource "aws_s3_bucket_notification" "bucket_notification" { bucket = "${aws_s3_bucket.bucket.id}" lambda_function { - lambda_function = "${aws_lambda_function.func.arn}" + lambda_function_arn = "${aws_lambda_function.func.arn}" events = ["s3:ObjectCreated:*"] filter_prefix = "AWSLogs/" filter_suffix = ".log" @@ -147,7 +147,7 @@ The following arguments are supported: The `topic` notification configuration supports the following: * `id` - (Optional) Specifies unique identifier for each of the notification configurations. -* `topic` - (Required) Specifies Amazon SNS topic ARN. +* `topic_arn` - (Required) Specifies Amazon SNS topic ARN. * `events` - (Required) Specifies [event](http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html#notification-how-to-event-types-and-destinations) for which to send notifications. * `filter_prefix` - (Optional) Specifies object key name prefix. * `filter_suffix` - (Optional) Specifies object key name suffix. @@ -155,7 +155,7 @@ The `topic` notification configuration supports the following: The `queue` notification configuration supports the following: * `id` - (Optional) Specifies unique identifier for each of the notification configurations. -* `queue` - (Required) Specifies Amazon SQS queue ARN. +* `queue_arn` - (Required) Specifies Amazon SQS queue ARN. * `events` - (Required) Specifies [event](http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html#notification-how-to-event-types-and-destinations) for which to send notifications. * `filter_prefix` - (Optional) Specifies object key name prefix. * `filter_suffix` - (Optional) Specifies object key name suffix. @@ -163,7 +163,7 @@ The `queue` notification configuration supports the following: The `lambda_function` notification configuration supports the following: * `id` - (Optional) Specifies unique identifier for each of the notification configurations. -* `lambda_function` - (Required) Specifies Amazon Lambda function ARN. +* `lambda_function_arn` - (Required) Specifies Amazon Lambda function ARN. * `events` - (Required) Specifies [event](http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html#notification-how-to-event-types-and-destinations) for which to send notifications. * `filter_prefix` - (Optional) Specifies object key name prefix. * `filter_suffix` - (Optional) Specifies object key name suffix. From f21c5780cbc99a19d27ea300410f4f76499fa4b1 Mon Sep 17 00:00:00 2001 From: Kazunori Kojima Date: Mon, 21 Mar 2016 15:40:45 +0900 Subject: [PATCH 12/13] Fix empty events error when additional notification configurations --- .../resource_aws_s3_bucket_notification.go | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification.go b/builtin/providers/aws/resource_aws_s3_bucket_notification.go index 3ab4224589a2..62bc1810d1c8 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification.go @@ -134,7 +134,7 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} // TopicNotifications topicNotifications := d.Get("topic").([]interface{}) topicConfigs := make([]*s3.TopicConfiguration, 0, len(topicNotifications)) - for _, c := range topicNotifications { + for i, c := range topicNotifications { tc := &s3.TopicConfiguration{} c := c.(map[string]interface{}) @@ -152,8 +152,9 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // Events - tc.Events = make([]*string, 0, len(c["events"].(*schema.Set).List())) - for _, e := range c["events"].(*schema.Set).List() { + events := d.Get(fmt.Sprintf("topic.%d.events", i)).(*schema.Set).List() + tc.Events = make([]*string, 0, len(events)) + for _, e := range events { tc.Events = append(tc.Events, aws.String(e.(string))) } @@ -186,7 +187,7 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} // Lambda lambdaFunctionNotifications := d.Get("lambda_function").([]interface{}) lambdaConfigs := make([]*s3.LambdaFunctionConfiguration, 0, len(lambdaFunctionNotifications)) - for _, c := range lambdaFunctionNotifications { + for i, c := range lambdaFunctionNotifications { lc := &s3.LambdaFunctionConfiguration{} c := c.(map[string]interface{}) @@ -204,8 +205,9 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // Events - lc.Events = make([]*string, 0, len(c["events"].(*schema.Set).List())) - for _, e := range c["events"].(*schema.Set).List() { + events := d.Get(fmt.Sprintf("lambda_function.%d.events", i)).(*schema.Set).List() + lc.Events = make([]*string, 0, len(events)) + for _, e := range events { lc.Events = append(lc.Events, aws.String(e.(string))) } @@ -238,7 +240,7 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} // SQS queueNotifications := d.Get("queue").([]interface{}) queueConfigs := make([]*s3.QueueConfiguration, 0, len(queueNotifications)) - for _, c := range queueNotifications { + for i, c := range queueNotifications { qc := &s3.QueueConfiguration{} c := c.(map[string]interface{}) @@ -256,8 +258,9 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} } // Events - qc.Events = make([]*string, 0, len(c["events"].(*schema.Set).List())) - for _, e := range c["events"].(*schema.Set).List() { + events := d.Get(fmt.Sprintf("queue.%d.events", i)).(*schema.Set).List() + qc.Events = make([]*string, 0, len(events)) + for _, e := range events { qc.Events = append(qc.Events, aws.String(e.(string))) } From f681ff6672ad72fdc869d21150909eb64476d7fa Mon Sep 17 00:00:00 2001 From: Kazunori Kojima Date: Mon, 21 Mar 2016 15:42:09 +0900 Subject: [PATCH 13/13] Swap queue and lambda function block --- .../resource_aws_s3_bucket_notification.go | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_notification.go b/builtin/providers/aws/resource_aws_s3_bucket_notification.go index 62bc1810d1c8..562dc723aa76 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_notification.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_notification.go @@ -184,31 +184,31 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} topicConfigs = append(topicConfigs, tc) } - // Lambda - lambdaFunctionNotifications := d.Get("lambda_function").([]interface{}) - lambdaConfigs := make([]*s3.LambdaFunctionConfiguration, 0, len(lambdaFunctionNotifications)) - for i, c := range lambdaFunctionNotifications { - lc := &s3.LambdaFunctionConfiguration{} + // SQS + queueNotifications := d.Get("queue").([]interface{}) + queueConfigs := make([]*s3.QueueConfiguration, 0, len(queueNotifications)) + for i, c := range queueNotifications { + qc := &s3.QueueConfiguration{} c := c.(map[string]interface{}) // Id if val, ok := c["id"].(string); ok && val != "" { - lc.Id = aws.String(val) + qc.Id = aws.String(val) } else { - lc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-lambda-")) + qc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-queue-")) } - // LambdaFunctionArn - if val, ok := c["lambda_function_arn"].(string); ok { - lc.LambdaFunctionArn = aws.String(val) + // QueueArn + if val, ok := c["queue_arn"].(string); ok { + qc.QueueArn = aws.String(val) } // Events - events := d.Get(fmt.Sprintf("lambda_function.%d.events", i)).(*schema.Set).List() - lc.Events = make([]*string, 0, len(events)) + events := d.Get(fmt.Sprintf("queue.%d.events", i)).(*schema.Set).List() + qc.Events = make([]*string, 0, len(events)) for _, e := range events { - lc.Events = append(lc.Events, aws.String(e.(string))) + qc.Events = append(qc.Events, aws.String(e.(string))) } // Filter @@ -228,40 +228,40 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} filterRules = append(filterRules, filterRule) } if len(filterRules) > 0 { - lc.Filter = &s3.NotificationConfigurationFilter{ + qc.Filter = &s3.NotificationConfigurationFilter{ Key: &s3.KeyFilter{ FilterRules: filterRules, }, } } - lambdaConfigs = append(lambdaConfigs, lc) + queueConfigs = append(queueConfigs, qc) } - // SQS - queueNotifications := d.Get("queue").([]interface{}) - queueConfigs := make([]*s3.QueueConfiguration, 0, len(queueNotifications)) - for i, c := range queueNotifications { - qc := &s3.QueueConfiguration{} + // Lambda + lambdaFunctionNotifications := d.Get("lambda_function").([]interface{}) + lambdaConfigs := make([]*s3.LambdaFunctionConfiguration, 0, len(lambdaFunctionNotifications)) + for i, c := range lambdaFunctionNotifications { + lc := &s3.LambdaFunctionConfiguration{} c := c.(map[string]interface{}) // Id if val, ok := c["id"].(string); ok && val != "" { - qc.Id = aws.String(val) + lc.Id = aws.String(val) } else { - qc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-queue-")) + lc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-lambda-")) } - // QueueArn - if val, ok := c["queue_arn"].(string); ok { - qc.QueueArn = aws.String(val) + // LambdaFunctionArn + if val, ok := c["lambda_function_arn"].(string); ok { + lc.LambdaFunctionArn = aws.String(val) } // Events - events := d.Get(fmt.Sprintf("queue.%d.events", i)).(*schema.Set).List() - qc.Events = make([]*string, 0, len(events)) + events := d.Get(fmt.Sprintf("lambda_function.%d.events", i)).(*schema.Set).List() + lc.Events = make([]*string, 0, len(events)) for _, e := range events { - qc.Events = append(qc.Events, aws.String(e.(string))) + lc.Events = append(lc.Events, aws.String(e.(string))) } // Filter @@ -281,13 +281,13 @@ func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{} filterRules = append(filterRules, filterRule) } if len(filterRules) > 0 { - qc.Filter = &s3.NotificationConfigurationFilter{ + lc.Filter = &s3.NotificationConfigurationFilter{ Key: &s3.KeyFilter{ FilterRules: filterRules, }, } } - queueConfigs = append(queueConfigs, qc) + lambdaConfigs = append(lambdaConfigs, lc) } notificationConfiguration := &s3.NotificationConfiguration{}