468 lines
13 KiB
Go
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
|
|
}
|