terraform/builtin/providers/aws/resource_aws_s3_bucket_noti...

468 lines
13 KiB
Go

package aws
import (
"fmt"
"log"
"strings"
"time"
"github.com/hashicorp/terraform/helper/resource"
"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,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"bucket": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"topic": &schema.Schema{
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,
Optional: true,
},
"filter_suffix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"topic_arn": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"events": &schema.Schema{
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},
"queue": &schema.Schema{
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,
Optional: true,
},
"filter_suffix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"queue_arn": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"events": &schema.Schema{
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},
"lambda_function": &schema.Schema{
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,
Optional: true,
},
"filter_suffix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"lambda_function_arn": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"events": &schema.Schema{
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},
},
}
}
func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{}) error {
s3conn := meta.(*AWSClient).s3conn
bucket := d.Get("bucket").(string)
// TopicNotifications
topicNotifications := d.Get("topic").([]interface{})
topicConfigs := make([]*s3.TopicConfiguration, 0, len(topicNotifications))
for i, c := range topicNotifications {
tc := &s3.TopicConfiguration{}
c := c.(map[string]interface{})
// Id
if val, ok := c["id"].(string); ok && val != "" {
tc.Id = aws.String(val)
} else {
tc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-topic-"))
}
// TopicArn
if val, ok := c["topic_arn"].(string); ok {
tc.TopicArn = aws.String(val)
}
// Events
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)))
}
// 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)
}
if len(filterRules) > 0 {
tc.Filter = &s3.NotificationConfigurationFilter{
Key: &s3.KeyFilter{
FilterRules: filterRules,
},
}
}
topicConfigs = append(topicConfigs, tc)
}
// 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 != "" {
qc.Id = aws.String(val)
} else {
qc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-queue-"))
}
// QueueArn
if val, ok := c["queue_arn"].(string); ok {
qc.QueueArn = aws.String(val)
}
// Events
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)))
}
// 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)
}
if len(filterRules) > 0 {
qc.Filter = &s3.NotificationConfigurationFilter{
Key: &s3.KeyFilter{
FilterRules: filterRules,
},
}
}
queueConfigs = append(queueConfigs, qc)
}
// 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 != "" {
lc.Id = aws.String(val)
} else {
lc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-lambda-"))
}
// LambdaFunctionArn
if val, ok := c["lambda_function_arn"].(string); ok {
lc.LambdaFunctionArn = aws.String(val)
}
// 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 {
lc.Events = append(lc.Events, aws.String(e.(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)
}
if len(filterRules) > 0 {
lc.Filter = &s3.NotificationConfigurationFilter{
Key: &s3.KeyFilter{
FilterRules: filterRules,
},
}
}
lambdaConfigs = append(lambdaConfigs, lc)
}
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 := 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)
}
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
if err := d.Set("topic", flattenTopicConfigurations(notificationConfigs.TopicConfigurations)); err != nil {
return fmt.Errorf("error reading S3 bucket \"%s\" topic notification: %s", d.Id(), err)
}
// 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 {
conf = flattenNotificationConfigurationFilter(filter)
} else {
conf = map[string]interface{}{}
}
conf["id"] = *notification.Id
conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events))
conf["topic_arn"] = *notification.TopicArn
topicNotifications = append(topicNotifications, conf)
}
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 {
conf = flattenNotificationConfigurationFilter(filter)
} else {
conf = map[string]interface{}{}
}
conf["id"] = *notification.Id
conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events))
conf["queue_arn"] = *notification.QueueArn
queueNotifications = append(queueNotifications, conf)
}
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 {
conf = flattenNotificationConfigurationFilter(filter)
} else {
conf = map[string]interface{}{}
}
conf["id"] = *notification.Id
conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events))
conf["lambda_function_arn"] = *notification.LambdaFunctionArn
lambdaFunctionNotifications = append(lambdaFunctionNotifications, conf)
}
return lambdaFunctionNotifications
}