github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/aws/resource_aws_s3_bucket_notification.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/resource"
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  
    12  	"github.com/aws/aws-sdk-go/aws"
    13  	"github.com/aws/aws-sdk-go/aws/awserr"
    14  	"github.com/aws/aws-sdk-go/service/s3"
    15  )
    16  
    17  func resourceAwsS3BucketNotification() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsS3BucketNotificationPut,
    20  		Read:   resourceAwsS3BucketNotificationRead,
    21  		Update: resourceAwsS3BucketNotificationPut,
    22  		Delete: resourceAwsS3BucketNotificationDelete,
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"bucket": &schema.Schema{
    26  				Type:     schema.TypeString,
    27  				Required: true,
    28  				ForceNew: true,
    29  			},
    30  
    31  			"topic": &schema.Schema{
    32  				Type:     schema.TypeList,
    33  				Optional: true,
    34  				Elem: &schema.Resource{
    35  					Schema: map[string]*schema.Schema{
    36  						"id": &schema.Schema{
    37  							Type:     schema.TypeString,
    38  							Optional: true,
    39  							Computed: true,
    40  						},
    41  						"filter_prefix": &schema.Schema{
    42  							Type:     schema.TypeString,
    43  							Optional: true,
    44  						},
    45  						"filter_suffix": &schema.Schema{
    46  							Type:     schema.TypeString,
    47  							Optional: true,
    48  						},
    49  						"topic_arn": &schema.Schema{
    50  							Type:     schema.TypeString,
    51  							Required: true,
    52  						},
    53  						"events": &schema.Schema{
    54  							Type:     schema.TypeSet,
    55  							Required: true,
    56  							Elem:     &schema.Schema{Type: schema.TypeString},
    57  							Set:      schema.HashString,
    58  						},
    59  					},
    60  				},
    61  			},
    62  
    63  			"queue": &schema.Schema{
    64  				Type:     schema.TypeList,
    65  				Optional: true,
    66  				Elem: &schema.Resource{
    67  					Schema: map[string]*schema.Schema{
    68  						"id": &schema.Schema{
    69  							Type:     schema.TypeString,
    70  							Optional: true,
    71  							Computed: true,
    72  						},
    73  						"filter_prefix": &schema.Schema{
    74  							Type:     schema.TypeString,
    75  							Optional: true,
    76  						},
    77  						"filter_suffix": &schema.Schema{
    78  							Type:     schema.TypeString,
    79  							Optional: true,
    80  						},
    81  						"queue_arn": &schema.Schema{
    82  							Type:     schema.TypeString,
    83  							Required: true,
    84  						},
    85  						"events": &schema.Schema{
    86  							Type:     schema.TypeSet,
    87  							Required: true,
    88  							Elem:     &schema.Schema{Type: schema.TypeString},
    89  							Set:      schema.HashString,
    90  						},
    91  					},
    92  				},
    93  			},
    94  
    95  			"lambda_function": &schema.Schema{
    96  				Type:     schema.TypeList,
    97  				Optional: true,
    98  				Elem: &schema.Resource{
    99  					Schema: map[string]*schema.Schema{
   100  						"id": &schema.Schema{
   101  							Type:     schema.TypeString,
   102  							Optional: true,
   103  							Computed: true,
   104  						},
   105  						"filter_prefix": &schema.Schema{
   106  							Type:     schema.TypeString,
   107  							Optional: true,
   108  						},
   109  						"filter_suffix": &schema.Schema{
   110  							Type:     schema.TypeString,
   111  							Optional: true,
   112  						},
   113  						"lambda_function_arn": &schema.Schema{
   114  							Type:     schema.TypeString,
   115  							Optional: true,
   116  						},
   117  						"events": &schema.Schema{
   118  							Type:     schema.TypeSet,
   119  							Required: true,
   120  							Elem:     &schema.Schema{Type: schema.TypeString},
   121  							Set:      schema.HashString,
   122  						},
   123  					},
   124  				},
   125  			},
   126  		},
   127  	}
   128  }
   129  
   130  func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{}) error {
   131  	s3conn := meta.(*AWSClient).s3conn
   132  	bucket := d.Get("bucket").(string)
   133  
   134  	// TopicNotifications
   135  	topicNotifications := d.Get("topic").([]interface{})
   136  	topicConfigs := make([]*s3.TopicConfiguration, 0, len(topicNotifications))
   137  	for i, c := range topicNotifications {
   138  		tc := &s3.TopicConfiguration{}
   139  
   140  		c := c.(map[string]interface{})
   141  
   142  		// Id
   143  		if val, ok := c["id"].(string); ok && val != "" {
   144  			tc.Id = aws.String(val)
   145  		} else {
   146  			tc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-topic-"))
   147  		}
   148  
   149  		// TopicArn
   150  		if val, ok := c["topic_arn"].(string); ok {
   151  			tc.TopicArn = aws.String(val)
   152  		}
   153  
   154  		// Events
   155  		events := d.Get(fmt.Sprintf("topic.%d.events", i)).(*schema.Set).List()
   156  		tc.Events = make([]*string, 0, len(events))
   157  		for _, e := range events {
   158  			tc.Events = append(tc.Events, aws.String(e.(string)))
   159  		}
   160  
   161  		// Filter
   162  		filterRules := make([]*s3.FilterRule, 0, 2)
   163  		if val, ok := c["filter_prefix"].(string); ok && val != "" {
   164  			filterRule := &s3.FilterRule{
   165  				Name:  aws.String("prefix"),
   166  				Value: aws.String(val),
   167  			}
   168  			filterRules = append(filterRules, filterRule)
   169  		}
   170  		if val, ok := c["filter_suffix"].(string); ok && val != "" {
   171  			filterRule := &s3.FilterRule{
   172  				Name:  aws.String("suffix"),
   173  				Value: aws.String(val),
   174  			}
   175  			filterRules = append(filterRules, filterRule)
   176  		}
   177  		if len(filterRules) > 0 {
   178  			tc.Filter = &s3.NotificationConfigurationFilter{
   179  				Key: &s3.KeyFilter{
   180  					FilterRules: filterRules,
   181  				},
   182  			}
   183  		}
   184  		topicConfigs = append(topicConfigs, tc)
   185  	}
   186  
   187  	// SQS
   188  	queueNotifications := d.Get("queue").([]interface{})
   189  	queueConfigs := make([]*s3.QueueConfiguration, 0, len(queueNotifications))
   190  	for i, c := range queueNotifications {
   191  		qc := &s3.QueueConfiguration{}
   192  
   193  		c := c.(map[string]interface{})
   194  
   195  		// Id
   196  		if val, ok := c["id"].(string); ok && val != "" {
   197  			qc.Id = aws.String(val)
   198  		} else {
   199  			qc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-queue-"))
   200  		}
   201  
   202  		// QueueArn
   203  		if val, ok := c["queue_arn"].(string); ok {
   204  			qc.QueueArn = aws.String(val)
   205  		}
   206  
   207  		// Events
   208  		events := d.Get(fmt.Sprintf("queue.%d.events", i)).(*schema.Set).List()
   209  		qc.Events = make([]*string, 0, len(events))
   210  		for _, e := range events {
   211  			qc.Events = append(qc.Events, aws.String(e.(string)))
   212  		}
   213  
   214  		// Filter
   215  		filterRules := make([]*s3.FilterRule, 0, 2)
   216  		if val, ok := c["filter_prefix"].(string); ok && val != "" {
   217  			filterRule := &s3.FilterRule{
   218  				Name:  aws.String("prefix"),
   219  				Value: aws.String(val),
   220  			}
   221  			filterRules = append(filterRules, filterRule)
   222  		}
   223  		if val, ok := c["filter_suffix"].(string); ok && val != "" {
   224  			filterRule := &s3.FilterRule{
   225  				Name:  aws.String("suffix"),
   226  				Value: aws.String(val),
   227  			}
   228  			filterRules = append(filterRules, filterRule)
   229  		}
   230  		if len(filterRules) > 0 {
   231  			qc.Filter = &s3.NotificationConfigurationFilter{
   232  				Key: &s3.KeyFilter{
   233  					FilterRules: filterRules,
   234  				},
   235  			}
   236  		}
   237  		queueConfigs = append(queueConfigs, qc)
   238  	}
   239  
   240  	// Lambda
   241  	lambdaFunctionNotifications := d.Get("lambda_function").([]interface{})
   242  	lambdaConfigs := make([]*s3.LambdaFunctionConfiguration, 0, len(lambdaFunctionNotifications))
   243  	for i, c := range lambdaFunctionNotifications {
   244  		lc := &s3.LambdaFunctionConfiguration{}
   245  
   246  		c := c.(map[string]interface{})
   247  
   248  		// Id
   249  		if val, ok := c["id"].(string); ok && val != "" {
   250  			lc.Id = aws.String(val)
   251  		} else {
   252  			lc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-lambda-"))
   253  		}
   254  
   255  		// LambdaFunctionArn
   256  		if val, ok := c["lambda_function_arn"].(string); ok {
   257  			lc.LambdaFunctionArn = aws.String(val)
   258  		}
   259  
   260  		// Events
   261  		events := d.Get(fmt.Sprintf("lambda_function.%d.events", i)).(*schema.Set).List()
   262  		lc.Events = make([]*string, 0, len(events))
   263  		for _, e := range events {
   264  			lc.Events = append(lc.Events, aws.String(e.(string)))
   265  		}
   266  
   267  		// Filter
   268  		filterRules := make([]*s3.FilterRule, 0, 2)
   269  		if val, ok := c["filter_prefix"].(string); ok && val != "" {
   270  			filterRule := &s3.FilterRule{
   271  				Name:  aws.String("prefix"),
   272  				Value: aws.String(val),
   273  			}
   274  			filterRules = append(filterRules, filterRule)
   275  		}
   276  		if val, ok := c["filter_suffix"].(string); ok && val != "" {
   277  			filterRule := &s3.FilterRule{
   278  				Name:  aws.String("suffix"),
   279  				Value: aws.String(val),
   280  			}
   281  			filterRules = append(filterRules, filterRule)
   282  		}
   283  		if len(filterRules) > 0 {
   284  			lc.Filter = &s3.NotificationConfigurationFilter{
   285  				Key: &s3.KeyFilter{
   286  					FilterRules: filterRules,
   287  				},
   288  			}
   289  		}
   290  		lambdaConfigs = append(lambdaConfigs, lc)
   291  	}
   292  
   293  	notificationConfiguration := &s3.NotificationConfiguration{}
   294  	if len(lambdaConfigs) > 0 {
   295  		notificationConfiguration.LambdaFunctionConfigurations = lambdaConfigs
   296  	}
   297  	if len(queueConfigs) > 0 {
   298  		notificationConfiguration.QueueConfigurations = queueConfigs
   299  	}
   300  	if len(topicConfigs) > 0 {
   301  		notificationConfiguration.TopicConfigurations = topicConfigs
   302  	}
   303  	i := &s3.PutBucketNotificationConfigurationInput{
   304  		Bucket: aws.String(bucket),
   305  		NotificationConfiguration: notificationConfiguration,
   306  	}
   307  
   308  	log.Printf("[DEBUG] S3 bucket: %s, Putting notification: %v", bucket, i)
   309  	err := resource.Retry(1*time.Minute, func() *resource.RetryError {
   310  		if _, err := s3conn.PutBucketNotificationConfiguration(i); err != nil {
   311  			if awserr, ok := err.(awserr.Error); ok {
   312  				switch awserr.Message() {
   313  				case "Unable to validate the following destination configurations":
   314  					return resource.RetryableError(awserr)
   315  				}
   316  			}
   317  			// Didn't recognize the error, so shouldn't retry.
   318  			return resource.NonRetryableError(err)
   319  		}
   320  		// Successful put configuration
   321  		return nil
   322  	})
   323  	if err != nil {
   324  		return fmt.Errorf("Error putting S3 notification configuration: %s", err)
   325  	}
   326  
   327  	d.SetId(bucket)
   328  
   329  	return resourceAwsS3BucketNotificationRead(d, meta)
   330  }
   331  
   332  func resourceAwsS3BucketNotificationDelete(d *schema.ResourceData, meta interface{}) error {
   333  	s3conn := meta.(*AWSClient).s3conn
   334  
   335  	i := &s3.PutBucketNotificationConfigurationInput{
   336  		Bucket: aws.String(d.Id()),
   337  		NotificationConfiguration: &s3.NotificationConfiguration{},
   338  	}
   339  
   340  	log.Printf("[DEBUG] S3 bucket: %s, Deleting notification: %v", d.Id(), i)
   341  	_, err := s3conn.PutBucketNotificationConfiguration(i)
   342  	if err != nil {
   343  		return fmt.Errorf("Error deleting S3 notification configuration: %s", err)
   344  	}
   345  
   346  	d.SetId("")
   347  
   348  	return nil
   349  }
   350  
   351  func resourceAwsS3BucketNotificationRead(d *schema.ResourceData, meta interface{}) error {
   352  	s3conn := meta.(*AWSClient).s3conn
   353  
   354  	var err error
   355  	_, err = s3conn.HeadBucket(&s3.HeadBucketInput{
   356  		Bucket: aws.String(d.Id()),
   357  	})
   358  	if err != nil {
   359  		if awsError, ok := err.(awserr.RequestFailure); ok && awsError.StatusCode() == 404 {
   360  			log.Printf("[WARN] S3 Bucket (%s) not found, error code (404)", d.Id())
   361  			d.SetId("")
   362  			return nil
   363  		} else {
   364  			// some of the AWS SDK's errors can be empty strings, so let's add
   365  			// some additional context.
   366  			return fmt.Errorf("error reading S3 bucket \"%s\": %s", d.Id(), err)
   367  		}
   368  	}
   369  
   370  	// Read the notification configuration
   371  	notificationConfigs, err := s3conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{
   372  		Bucket: aws.String(d.Id()),
   373  	})
   374  	if err != nil {
   375  		return err
   376  	}
   377  	log.Printf("[DEBUG] S3 Bucket: %s, get notification: %v", d.Id(), notificationConfigs)
   378  	// Topic Notification
   379  	if err := d.Set("topic", flattenTopicConfigurations(notificationConfigs.TopicConfigurations)); err != nil {
   380  		return fmt.Errorf("error reading S3 bucket \"%s\" topic notification: %s", d.Id(), err)
   381  	}
   382  
   383  	// SQS Notification
   384  	if err := d.Set("queue", flattenQueueConfigurations(notificationConfigs.QueueConfigurations)); err != nil {
   385  		return fmt.Errorf("error reading S3 bucket \"%s\" queue notification: %s", d.Id(), err)
   386  	}
   387  
   388  	// Lambda Notification
   389  	if err := d.Set("lambda_function", flattenLambdaFunctionConfigurations(notificationConfigs.LambdaFunctionConfigurations)); err != nil {
   390  		return fmt.Errorf("error reading S3 bucket \"%s\" lambda function notification: %s", d.Id(), err)
   391  	}
   392  
   393  	return nil
   394  }
   395  
   396  func flattenNotificationConfigurationFilter(filter *s3.NotificationConfigurationFilter) map[string]interface{} {
   397  	filterRules := map[string]interface{}{}
   398  	for _, f := range filter.Key.FilterRules {
   399  		if strings.ToLower(*f.Name) == s3.FilterRuleNamePrefix {
   400  			filterRules["filter_prefix"] = *f.Value
   401  		}
   402  		if strings.ToLower(*f.Name) == s3.FilterRuleNameSuffix {
   403  			filterRules["filter_suffix"] = *f.Value
   404  		}
   405  	}
   406  	return filterRules
   407  }
   408  
   409  func flattenTopicConfigurations(configs []*s3.TopicConfiguration) []map[string]interface{} {
   410  	topicNotifications := make([]map[string]interface{}, 0, len(configs))
   411  	for _, notification := range configs {
   412  		var conf map[string]interface{}
   413  		if filter := notification.Filter; filter != nil {
   414  			conf = flattenNotificationConfigurationFilter(filter)
   415  		} else {
   416  			conf = map[string]interface{}{}
   417  		}
   418  
   419  		conf["id"] = *notification.Id
   420  		conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events))
   421  		conf["topic_arn"] = *notification.TopicArn
   422  		topicNotifications = append(topicNotifications, conf)
   423  	}
   424  
   425  	return topicNotifications
   426  }
   427  
   428  func flattenQueueConfigurations(configs []*s3.QueueConfiguration) []map[string]interface{} {
   429  	queueNotifications := make([]map[string]interface{}, 0, len(configs))
   430  	for _, notification := range configs {
   431  		var conf map[string]interface{}
   432  		if filter := notification.Filter; filter != nil {
   433  			conf = flattenNotificationConfigurationFilter(filter)
   434  		} else {
   435  			conf = map[string]interface{}{}
   436  		}
   437  
   438  		conf["id"] = *notification.Id
   439  		conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events))
   440  		conf["queue_arn"] = *notification.QueueArn
   441  		queueNotifications = append(queueNotifications, conf)
   442  	}
   443  
   444  	return queueNotifications
   445  }
   446  
   447  func flattenLambdaFunctionConfigurations(configs []*s3.LambdaFunctionConfiguration) []map[string]interface{} {
   448  	lambdaFunctionNotifications := make([]map[string]interface{}, 0, len(configs))
   449  	for _, notification := range configs {
   450  		var conf map[string]interface{}
   451  		if filter := notification.Filter; filter != nil {
   452  			conf = flattenNotificationConfigurationFilter(filter)
   453  		} else {
   454  			conf = map[string]interface{}{}
   455  		}
   456  
   457  		conf["id"] = *notification.Id
   458  		conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events))
   459  		conf["lambda_function_arn"] = *notification.LambdaFunctionArn
   460  		lambdaFunctionNotifications = append(lambdaFunctionNotifications, conf)
   461  	}
   462  
   463  	return lambdaFunctionNotifications
   464  }