github.com/erriapo/terraform@v0.6.12-0.20160203182612-0340ea72354f/builtin/providers/aws/resource_aws_s3_bucket.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "log" 8 "net/url" 9 "time" 10 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 14 "github.com/aws/aws-sdk-go/aws" 15 "github.com/aws/aws-sdk-go/aws/awserr" 16 "github.com/aws/aws-sdk-go/service/s3" 17 "github.com/hashicorp/terraform/helper/hashcode" 18 ) 19 20 func resourceAwsS3Bucket() *schema.Resource { 21 return &schema.Resource{ 22 Create: resourceAwsS3BucketCreate, 23 Read: resourceAwsS3BucketRead, 24 Update: resourceAwsS3BucketUpdate, 25 Delete: resourceAwsS3BucketDelete, 26 27 Schema: map[string]*schema.Schema{ 28 "bucket": &schema.Schema{ 29 Type: schema.TypeString, 30 Required: true, 31 ForceNew: true, 32 }, 33 34 "arn": &schema.Schema{ 35 Type: schema.TypeString, 36 Optional: true, 37 Computed: true, 38 }, 39 40 "acl": &schema.Schema{ 41 Type: schema.TypeString, 42 Default: "private", 43 Optional: true, 44 }, 45 46 "policy": &schema.Schema{ 47 Type: schema.TypeString, 48 Optional: true, 49 StateFunc: normalizeJson, 50 }, 51 52 "cors_rule": &schema.Schema{ 53 Type: schema.TypeList, 54 Optional: true, 55 Elem: &schema.Resource{ 56 Schema: map[string]*schema.Schema{ 57 "allowed_headers": &schema.Schema{ 58 Type: schema.TypeList, 59 Optional: true, 60 Elem: &schema.Schema{Type: schema.TypeString}, 61 }, 62 "allowed_methods": &schema.Schema{ 63 Type: schema.TypeList, 64 Required: true, 65 Elem: &schema.Schema{Type: schema.TypeString}, 66 }, 67 "allowed_origins": &schema.Schema{ 68 Type: schema.TypeList, 69 Required: true, 70 Elem: &schema.Schema{Type: schema.TypeString}, 71 }, 72 "expose_headers": &schema.Schema{ 73 Type: schema.TypeList, 74 Optional: true, 75 Elem: &schema.Schema{Type: schema.TypeString}, 76 }, 77 "max_age_seconds": &schema.Schema{ 78 Type: schema.TypeInt, 79 Optional: true, 80 }, 81 }, 82 }, 83 }, 84 85 "website": &schema.Schema{ 86 Type: schema.TypeList, 87 Optional: true, 88 Elem: &schema.Resource{ 89 Schema: map[string]*schema.Schema{ 90 "index_document": &schema.Schema{ 91 Type: schema.TypeString, 92 Optional: true, 93 }, 94 95 "error_document": &schema.Schema{ 96 Type: schema.TypeString, 97 Optional: true, 98 }, 99 100 "redirect_all_requests_to": &schema.Schema{ 101 Type: schema.TypeString, 102 ConflictsWith: []string{ 103 "website.0.index_document", 104 "website.0.error_document", 105 }, 106 Optional: true, 107 }, 108 }, 109 }, 110 }, 111 112 "hosted_zone_id": &schema.Schema{ 113 Type: schema.TypeString, 114 Optional: true, 115 Computed: true, 116 }, 117 118 "region": &schema.Schema{ 119 Type: schema.TypeString, 120 Optional: true, 121 Computed: true, 122 }, 123 "website_endpoint": &schema.Schema{ 124 Type: schema.TypeString, 125 Optional: true, 126 Computed: true, 127 }, 128 "website_domain": &schema.Schema{ 129 Type: schema.TypeString, 130 Optional: true, 131 Computed: true, 132 }, 133 134 "versioning": &schema.Schema{ 135 Type: schema.TypeSet, 136 Optional: true, 137 Elem: &schema.Resource{ 138 Schema: map[string]*schema.Schema{ 139 "enabled": &schema.Schema{ 140 Type: schema.TypeBool, 141 Optional: true, 142 Default: false, 143 }, 144 }, 145 }, 146 Set: func(v interface{}) int { 147 var buf bytes.Buffer 148 m := v.(map[string]interface{}) 149 buf.WriteString(fmt.Sprintf("%t-", m["enabled"].(bool))) 150 151 return hashcode.String(buf.String()) 152 }, 153 }, 154 155 "logging": &schema.Schema{ 156 Type: schema.TypeSet, 157 Optional: true, 158 Elem: &schema.Resource{ 159 Schema: map[string]*schema.Schema{ 160 "target_bucket": &schema.Schema{ 161 Type: schema.TypeString, 162 Required: true, 163 }, 164 "target_prefix": &schema.Schema{ 165 Type: schema.TypeString, 166 Optional: true, 167 }, 168 }, 169 }, 170 Set: func(v interface{}) int { 171 var buf bytes.Buffer 172 m := v.(map[string]interface{}) 173 buf.WriteString(fmt.Sprintf("%s-", m["target_bucket"])) 174 buf.WriteString(fmt.Sprintf("%s-", m["target_prefix"])) 175 return hashcode.String(buf.String()) 176 }, 177 }, 178 179 "tags": tagsSchema(), 180 181 "force_destroy": &schema.Schema{ 182 Type: schema.TypeBool, 183 Optional: true, 184 Default: false, 185 }, 186 }, 187 } 188 } 189 190 func resourceAwsS3BucketCreate(d *schema.ResourceData, meta interface{}) error { 191 s3conn := meta.(*AWSClient).s3conn 192 awsRegion := meta.(*AWSClient).region 193 194 // Get the bucket and acl 195 bucket := d.Get("bucket").(string) 196 acl := d.Get("acl").(string) 197 198 log.Printf("[DEBUG] S3 bucket create: %s, ACL: %s", bucket, acl) 199 200 req := &s3.CreateBucketInput{ 201 Bucket: aws.String(bucket), 202 ACL: aws.String(acl), 203 } 204 205 // Special case us-east-1 region and do not set the LocationConstraint. 206 // See "Request Elements: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUT.html 207 if awsRegion != "us-east-1" { 208 req.CreateBucketConfiguration = &s3.CreateBucketConfiguration{ 209 LocationConstraint: aws.String(awsRegion), 210 } 211 } 212 213 _, err := s3conn.CreateBucket(req) 214 if err != nil { 215 return fmt.Errorf("Error creating S3 bucket: %s", err) 216 } 217 218 // Assign the bucket name as the resource ID 219 d.SetId(bucket) 220 221 return resourceAwsS3BucketUpdate(d, meta) 222 } 223 224 func resourceAwsS3BucketUpdate(d *schema.ResourceData, meta interface{}) error { 225 s3conn := meta.(*AWSClient).s3conn 226 if err := setTagsS3(s3conn, d); err != nil { 227 return err 228 } 229 230 if d.HasChange("policy") { 231 if err := resourceAwsS3BucketPolicyUpdate(s3conn, d); err != nil { 232 return err 233 } 234 } 235 236 if d.HasChange("cors_rule") { 237 if err := resourceAwsS3BucketCorsUpdate(s3conn, d); err != nil { 238 return err 239 } 240 } 241 242 if d.HasChange("website") { 243 if err := resourceAwsS3BucketWebsiteUpdate(s3conn, d); err != nil { 244 return err 245 } 246 } 247 248 if d.HasChange("versioning") { 249 if err := resourceAwsS3BucketVersioningUpdate(s3conn, d); err != nil { 250 return err 251 } 252 } 253 if d.HasChange("acl") { 254 if err := resourceAwsS3BucketAclUpdate(s3conn, d); err != nil { 255 return err 256 } 257 } 258 259 if d.HasChange("logging") { 260 if err := resourceAwsS3BucketLoggingUpdate(s3conn, d); err != nil { 261 return err 262 } 263 } 264 265 return resourceAwsS3BucketRead(d, meta) 266 } 267 268 func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error { 269 s3conn := meta.(*AWSClient).s3conn 270 271 var err error 272 _, err = s3conn.HeadBucket(&s3.HeadBucketInput{ 273 Bucket: aws.String(d.Id()), 274 }) 275 if err != nil { 276 if awsError, ok := err.(awserr.RequestFailure); ok && awsError.StatusCode() == 404 { 277 log.Printf("[WARN] S3 Bucket (%s) not found, error code (404)", d.Id()) 278 d.SetId("") 279 return nil 280 } else { 281 // some of the AWS SDK's errors can be empty strings, so let's add 282 // some additional context. 283 return fmt.Errorf("error reading S3 bucket \"%s\": %s", d.Id(), err) 284 } 285 } 286 287 // Read the policy 288 pol, err := s3conn.GetBucketPolicy(&s3.GetBucketPolicyInput{ 289 Bucket: aws.String(d.Id()), 290 }) 291 log.Printf("[DEBUG] S3 bucket: %s, read policy: %v", d.Id(), pol) 292 if err != nil { 293 if err := d.Set("policy", ""); err != nil { 294 return err 295 } 296 } else { 297 if v := pol.Policy; v == nil { 298 if err := d.Set("policy", ""); err != nil { 299 return err 300 } 301 } else if err := d.Set("policy", normalizeJson(*v)); err != nil { 302 return err 303 } 304 } 305 306 // Read the CORS 307 cors, err := s3conn.GetBucketCors(&s3.GetBucketCorsInput{ 308 Bucket: aws.String(d.Id()), 309 }) 310 log.Printf("[DEBUG] S3 bucket: %s, read CORS: %v", d.Id(), cors) 311 if err != nil { 312 rules := make([]map[string]interface{}, 0, len(cors.CORSRules)) 313 for _, ruleObject := range cors.CORSRules { 314 rule := make(map[string]interface{}) 315 rule["allowed_headers"] = ruleObject.AllowedHeaders 316 rule["allowed_methods"] = ruleObject.AllowedMethods 317 rule["allowed_origins"] = ruleObject.AllowedOrigins 318 rule["expose_headers"] = ruleObject.ExposeHeaders 319 rule["max_age_seconds"] = ruleObject.MaxAgeSeconds 320 rules = append(rules, rule) 321 } 322 if err := d.Set("cors_rule", rules); err != nil { 323 return fmt.Errorf("error reading S3 bucket \"%s\" CORS rules: %s", d.Id(), err) 324 } 325 } 326 327 // Read the website configuration 328 ws, err := s3conn.GetBucketWebsite(&s3.GetBucketWebsiteInput{ 329 Bucket: aws.String(d.Id()), 330 }) 331 var websites []map[string]interface{} 332 if err == nil { 333 w := make(map[string]interface{}) 334 335 if v := ws.IndexDocument; v != nil { 336 w["index_document"] = *v.Suffix 337 } 338 339 if v := ws.ErrorDocument; v != nil { 340 w["error_document"] = *v.Key 341 } 342 343 if v := ws.RedirectAllRequestsTo; v != nil { 344 if v.Protocol == nil { 345 w["redirect_all_requests_to"] = *v.HostName 346 } else { 347 w["redirect_all_requests_to"] = (&url.URL{ 348 Host: *v.HostName, 349 Scheme: *v.Protocol, 350 }).String() 351 } 352 } 353 354 websites = append(websites, w) 355 } 356 if err := d.Set("website", websites); err != nil { 357 return err 358 } 359 360 // Read the versioning configuration 361 versioning, err := s3conn.GetBucketVersioning(&s3.GetBucketVersioningInput{ 362 Bucket: aws.String(d.Id()), 363 }) 364 if err != nil { 365 return err 366 } 367 log.Printf("[DEBUG] S3 Bucket: %s, versioning: %v", d.Id(), versioning) 368 if versioning.Status != nil && *versioning.Status == s3.BucketVersioningStatusEnabled { 369 vcl := make([]map[string]interface{}, 0, 1) 370 vc := make(map[string]interface{}) 371 if *versioning.Status == s3.BucketVersioningStatusEnabled { 372 vc["enabled"] = true 373 } else { 374 vc["enabled"] = false 375 } 376 vcl = append(vcl, vc) 377 if err := d.Set("versioning", vcl); err != nil { 378 return err 379 } 380 } 381 382 // Read the logging configuration 383 logging, err := s3conn.GetBucketLogging(&s3.GetBucketLoggingInput{ 384 Bucket: aws.String(d.Id()), 385 }) 386 if err != nil { 387 return err 388 } 389 log.Printf("[DEBUG] S3 Bucket: %s, logging: %v", d.Id(), logging) 390 if v := logging.LoggingEnabled; v != nil { 391 lcl := make([]map[string]interface{}, 0, 1) 392 lc := make(map[string]interface{}) 393 if *v.TargetBucket != "" { 394 lc["target_bucket"] = *v.TargetBucket 395 } 396 if *v.TargetPrefix != "" { 397 lc["target_prefix"] = *v.TargetPrefix 398 } 399 lcl = append(lcl, lc) 400 if err := d.Set("logging", lcl); err != nil { 401 return err 402 } 403 } 404 405 // Add the region as an attribute 406 location, err := s3conn.GetBucketLocation( 407 &s3.GetBucketLocationInput{ 408 Bucket: aws.String(d.Id()), 409 }, 410 ) 411 if err != nil { 412 return err 413 } 414 var region string 415 if location.LocationConstraint != nil { 416 region = *location.LocationConstraint 417 } 418 region = normalizeRegion(region) 419 if err := d.Set("region", region); err != nil { 420 return err 421 } 422 423 // Add the hosted zone ID for this bucket's region as an attribute 424 hostedZoneID := HostedZoneIDForRegion(region) 425 if err := d.Set("hosted_zone_id", hostedZoneID); err != nil { 426 return err 427 } 428 429 // Add website_endpoint as an attribute 430 websiteEndpoint, err := websiteEndpoint(s3conn, d) 431 if err != nil { 432 return err 433 } 434 if websiteEndpoint != nil { 435 if err := d.Set("website_endpoint", websiteEndpoint.Endpoint); err != nil { 436 return err 437 } 438 if err := d.Set("website_domain", websiteEndpoint.Domain); err != nil { 439 return err 440 } 441 } 442 443 tagSet, err := getTagSetS3(s3conn, d.Id()) 444 if err != nil { 445 return err 446 } 447 448 if err := d.Set("tags", tagsToMapS3(tagSet)); err != nil { 449 return err 450 } 451 452 d.Set("arn", fmt.Sprint("arn:aws:s3:::", d.Id())) 453 454 return nil 455 } 456 457 func resourceAwsS3BucketDelete(d *schema.ResourceData, meta interface{}) error { 458 s3conn := meta.(*AWSClient).s3conn 459 460 log.Printf("[DEBUG] S3 Delete Bucket: %s", d.Id()) 461 _, err := s3conn.DeleteBucket(&s3.DeleteBucketInput{ 462 Bucket: aws.String(d.Id()), 463 }) 464 if err != nil { 465 ec2err, ok := err.(awserr.Error) 466 if ok && ec2err.Code() == "BucketNotEmpty" { 467 if d.Get("force_destroy").(bool) { 468 // bucket may have things delete them 469 log.Printf("[DEBUG] S3 Bucket attempting to forceDestroy %+v", err) 470 471 bucket := d.Get("bucket").(string) 472 resp, err := s3conn.ListObjectVersions( 473 &s3.ListObjectVersionsInput{ 474 Bucket: aws.String(bucket), 475 }, 476 ) 477 478 if err != nil { 479 return fmt.Errorf("Error S3 Bucket list Object Versions err: %s", err) 480 } 481 482 objectsToDelete := make([]*s3.ObjectIdentifier, 0) 483 484 if len(resp.DeleteMarkers) != 0 { 485 486 for _, v := range resp.DeleteMarkers { 487 objectsToDelete = append(objectsToDelete, &s3.ObjectIdentifier{ 488 Key: v.Key, 489 VersionId: v.VersionId, 490 }) 491 } 492 } 493 494 if len(resp.Versions) != 0 { 495 for _, v := range resp.Versions { 496 objectsToDelete = append(objectsToDelete, &s3.ObjectIdentifier{ 497 Key: v.Key, 498 VersionId: v.VersionId, 499 }) 500 } 501 } 502 503 params := &s3.DeleteObjectsInput{ 504 Bucket: aws.String(bucket), 505 Delete: &s3.Delete{ 506 Objects: objectsToDelete, 507 }, 508 } 509 510 _, err = s3conn.DeleteObjects(params) 511 512 if err != nil { 513 return fmt.Errorf("Error S3 Bucket force_destroy error deleting: %s", err) 514 } 515 516 // this line recurses until all objects are deleted or an error is returned 517 return resourceAwsS3BucketDelete(d, meta) 518 } 519 } 520 return fmt.Errorf("Error deleting S3 Bucket: %s", err) 521 } 522 return nil 523 } 524 525 func resourceAwsS3BucketPolicyUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 526 bucket := d.Get("bucket").(string) 527 policy := d.Get("policy").(string) 528 529 if policy != "" { 530 log.Printf("[DEBUG] S3 bucket: %s, put policy: %s", bucket, policy) 531 532 params := &s3.PutBucketPolicyInput{ 533 Bucket: aws.String(bucket), 534 Policy: aws.String(policy), 535 } 536 537 err := resource.Retry(1*time.Minute, func() error { 538 if _, err := s3conn.PutBucketPolicy(params); err != nil { 539 if awserr, ok := err.(awserr.Error); ok { 540 if awserr.Code() == "MalformedPolicy" { 541 // Retryable 542 return awserr 543 } 544 } 545 // Not retryable 546 return resource.RetryError{Err: err} 547 } 548 // No error 549 return nil 550 }) 551 552 if err != nil { 553 return fmt.Errorf("Error putting S3 policy: %s", err) 554 } 555 } else { 556 log.Printf("[DEBUG] S3 bucket: %s, delete policy: %s", bucket, policy) 557 _, err := s3conn.DeleteBucketPolicy(&s3.DeleteBucketPolicyInput{ 558 Bucket: aws.String(bucket), 559 }) 560 561 if err != nil { 562 return fmt.Errorf("Error deleting S3 policy: %s", err) 563 } 564 } 565 566 return nil 567 } 568 569 func resourceAwsS3BucketCorsUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 570 bucket := d.Get("bucket").(string) 571 rawCors := d.Get("cors_rule").([]interface{}) 572 573 if len(rawCors) == 0 { 574 // Delete CORS 575 log.Printf("[DEBUG] S3 bucket: %s, delete CORS", bucket) 576 _, err := s3conn.DeleteBucketCors(&s3.DeleteBucketCorsInput{ 577 Bucket: aws.String(bucket), 578 }) 579 if err != nil { 580 return fmt.Errorf("Error deleting S3 CORS: %s", err) 581 } 582 } else { 583 // Put CORS 584 rules := make([]*s3.CORSRule, 0, len(rawCors)) 585 for _, cors := range rawCors { 586 corsMap := cors.(map[string]interface{}) 587 r := &s3.CORSRule{} 588 for k, v := range corsMap { 589 log.Printf("[DEBUG] S3 bucket: %s, put CORS: %#v, %#v", bucket, k, v) 590 if k == "max_age_seconds" { 591 r.MaxAgeSeconds = aws.Int64(int64(v.(int))) 592 } else { 593 vMap := make([]*string, len(v.([]interface{}))) 594 for i, vv := range v.([]interface{}) { 595 str := vv.(string) 596 vMap[i] = aws.String(str) 597 } 598 switch k { 599 case "allowed_headers": 600 r.AllowedHeaders = vMap 601 case "allowed_methods": 602 r.AllowedMethods = vMap 603 case "allowed_origins": 604 r.AllowedOrigins = vMap 605 case "expose_headers": 606 r.ExposeHeaders = vMap 607 } 608 } 609 } 610 rules = append(rules, r) 611 } 612 corsInput := &s3.PutBucketCorsInput{ 613 Bucket: aws.String(bucket), 614 CORSConfiguration: &s3.CORSConfiguration{ 615 CORSRules: rules, 616 }, 617 } 618 log.Printf("[DEBUG] S3 bucket: %s, put CORS: %#v", bucket, corsInput) 619 _, err := s3conn.PutBucketCors(corsInput) 620 if err != nil { 621 return fmt.Errorf("Error putting S3 CORS: %s", err) 622 } 623 } 624 625 return nil 626 } 627 628 func resourceAwsS3BucketWebsiteUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 629 ws := d.Get("website").([]interface{}) 630 631 if len(ws) == 1 { 632 w := ws[0].(map[string]interface{}) 633 return resourceAwsS3BucketWebsitePut(s3conn, d, w) 634 } else if len(ws) == 0 { 635 return resourceAwsS3BucketWebsiteDelete(s3conn, d) 636 } else { 637 return fmt.Errorf("Cannot specify more than one website.") 638 } 639 } 640 641 func resourceAwsS3BucketWebsitePut(s3conn *s3.S3, d *schema.ResourceData, website map[string]interface{}) error { 642 bucket := d.Get("bucket").(string) 643 644 indexDocument := website["index_document"].(string) 645 errorDocument := website["error_document"].(string) 646 redirectAllRequestsTo := website["redirect_all_requests_to"].(string) 647 648 if indexDocument == "" && redirectAllRequestsTo == "" { 649 return fmt.Errorf("Must specify either index_document or redirect_all_requests_to.") 650 } 651 652 websiteConfiguration := &s3.WebsiteConfiguration{} 653 654 if indexDocument != "" { 655 websiteConfiguration.IndexDocument = &s3.IndexDocument{Suffix: aws.String(indexDocument)} 656 } 657 658 if errorDocument != "" { 659 websiteConfiguration.ErrorDocument = &s3.ErrorDocument{Key: aws.String(errorDocument)} 660 } 661 662 if redirectAllRequestsTo != "" { 663 redirect, err := url.Parse(redirectAllRequestsTo) 664 if err == nil && redirect.Scheme != "" { 665 websiteConfiguration.RedirectAllRequestsTo = &s3.RedirectAllRequestsTo{HostName: aws.String(redirect.Host), Protocol: aws.String(redirect.Scheme)} 666 } else { 667 websiteConfiguration.RedirectAllRequestsTo = &s3.RedirectAllRequestsTo{HostName: aws.String(redirectAllRequestsTo)} 668 } 669 } 670 671 putInput := &s3.PutBucketWebsiteInput{ 672 Bucket: aws.String(bucket), 673 WebsiteConfiguration: websiteConfiguration, 674 } 675 676 log.Printf("[DEBUG] S3 put bucket website: %#v", putInput) 677 678 _, err := s3conn.PutBucketWebsite(putInput) 679 if err != nil { 680 return fmt.Errorf("Error putting S3 website: %s", err) 681 } 682 683 return nil 684 } 685 686 func resourceAwsS3BucketWebsiteDelete(s3conn *s3.S3, d *schema.ResourceData) error { 687 bucket := d.Get("bucket").(string) 688 deleteInput := &s3.DeleteBucketWebsiteInput{Bucket: aws.String(bucket)} 689 690 log.Printf("[DEBUG] S3 delete bucket website: %#v", deleteInput) 691 692 _, err := s3conn.DeleteBucketWebsite(deleteInput) 693 if err != nil { 694 return fmt.Errorf("Error deleting S3 website: %s", err) 695 } 696 697 d.Set("website_endpoint", "") 698 d.Set("website_domain", "") 699 700 return nil 701 } 702 703 func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (*S3Website, error) { 704 // If the bucket doesn't have a website configuration, return an empty 705 // endpoint 706 if _, ok := d.GetOk("website"); !ok { 707 return nil, nil 708 } 709 710 bucket := d.Get("bucket").(string) 711 712 // Lookup the region for this bucket 713 location, err := s3conn.GetBucketLocation( 714 &s3.GetBucketLocationInput{ 715 Bucket: aws.String(bucket), 716 }, 717 ) 718 if err != nil { 719 return nil, err 720 } 721 var region string 722 if location.LocationConstraint != nil { 723 region = *location.LocationConstraint 724 } 725 726 return WebsiteEndpoint(bucket, region), nil 727 } 728 729 func WebsiteEndpoint(bucket string, region string) *S3Website { 730 domain := WebsiteDomainUrl(region) 731 return &S3Website{Endpoint: fmt.Sprintf("%s.%s", bucket, domain), Domain: domain} 732 } 733 734 func WebsiteDomainUrl(region string) string { 735 region = normalizeRegion(region) 736 737 // Frankfurt(and probably future) regions uses different syntax for website endpoints 738 // http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteEndpoints.html 739 if region == "eu-central-1" { 740 return fmt.Sprintf("s3-website.%s.amazonaws.com", region) 741 } 742 743 return fmt.Sprintf("s3-website-%s.amazonaws.com", region) 744 } 745 746 func resourceAwsS3BucketAclUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 747 acl := d.Get("acl").(string) 748 bucket := d.Get("bucket").(string) 749 750 i := &s3.PutBucketAclInput{ 751 Bucket: aws.String(bucket), 752 ACL: aws.String(acl), 753 } 754 log.Printf("[DEBUG] S3 put bucket ACL: %#v", i) 755 756 _, err := s3conn.PutBucketAcl(i) 757 if err != nil { 758 return fmt.Errorf("Error putting S3 ACL: %s", err) 759 } 760 761 return nil 762 } 763 764 func resourceAwsS3BucketVersioningUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 765 v := d.Get("versioning").(*schema.Set).List() 766 bucket := d.Get("bucket").(string) 767 vc := &s3.VersioningConfiguration{} 768 769 if len(v) > 0 { 770 c := v[0].(map[string]interface{}) 771 772 if c["enabled"].(bool) { 773 vc.Status = aws.String(s3.BucketVersioningStatusEnabled) 774 } else { 775 vc.Status = aws.String(s3.BucketVersioningStatusSuspended) 776 } 777 } else { 778 vc.Status = aws.String(s3.BucketVersioningStatusSuspended) 779 } 780 781 i := &s3.PutBucketVersioningInput{ 782 Bucket: aws.String(bucket), 783 VersioningConfiguration: vc, 784 } 785 log.Printf("[DEBUG] S3 put bucket versioning: %#v", i) 786 787 _, err := s3conn.PutBucketVersioning(i) 788 if err != nil { 789 return fmt.Errorf("Error putting S3 versioning: %s", err) 790 } 791 792 return nil 793 } 794 795 func resourceAwsS3BucketLoggingUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 796 logging := d.Get("logging").(*schema.Set).List() 797 bucket := d.Get("bucket").(string) 798 loggingStatus := &s3.BucketLoggingStatus{} 799 800 if len(logging) > 0 { 801 c := logging[0].(map[string]interface{}) 802 803 loggingEnabled := &s3.LoggingEnabled{} 804 if val, ok := c["target_bucket"]; ok { 805 loggingEnabled.TargetBucket = aws.String(val.(string)) 806 } 807 if val, ok := c["target_prefix"]; ok { 808 loggingEnabled.TargetPrefix = aws.String(val.(string)) 809 } 810 811 loggingStatus.LoggingEnabled = loggingEnabled 812 } 813 814 i := &s3.PutBucketLoggingInput{ 815 Bucket: aws.String(bucket), 816 BucketLoggingStatus: loggingStatus, 817 } 818 log.Printf("[DEBUG] S3 put bucket logging: %#v", i) 819 820 _, err := s3conn.PutBucketLogging(i) 821 if err != nil { 822 return fmt.Errorf("Error putting S3 logging: %s", err) 823 } 824 825 return nil 826 } 827 828 func normalizeJson(jsonString interface{}) string { 829 if jsonString == nil { 830 return "" 831 } 832 j := make(map[string]interface{}) 833 err := json.Unmarshal([]byte(jsonString.(string)), &j) 834 if err != nil { 835 return fmt.Sprintf("Error parsing JSON: %s", err) 836 } 837 b, _ := json.Marshal(j) 838 return string(b[:]) 839 } 840 841 func normalizeRegion(region string) string { 842 // Default to us-east-1 if the bucket doesn't have a region: 843 // http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETlocation.html 844 if region == "" { 845 region = "us-east-1" 846 } 847 848 return region 849 } 850 851 type S3Website struct { 852 Endpoint, Domain string 853 }