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 }