From e6d9dcfb1a5b25dcb56017fc0cb8e9cec85d36a1 Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Sat, 25 Apr 2015 19:36:00 -0400 Subject: [PATCH 01/13] providers/aws: Initial S3 bucket website support --- .../providers/aws/resource_aws_s3_bucket.go | 53 +++++++++++++++++++ .../aws/resource_aws_s3_bucket_test.go | 24 +++++++-- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index 712bdb5f2bb0..cb2608427cbf 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -31,6 +31,19 @@ func resourceAwsS3Bucket() *schema.Resource { ForceNew: true, }, + "website": &schema.Schema{ + Type: schema.TypeBool, + Default: false, + Optional: true, + ForceNew: false, + }, + + "index_document": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "tags": tagsSchema(), }, } @@ -75,6 +88,11 @@ func resourceAwsS3BucketUpdate(d *schema.ResourceData, meta interface{}) error { if err := setTagsS3(s3conn, d); err != nil { return err } + + if err := updateWebsite(s3conn, d); err != nil { + return err + } + return resourceAwsS3BucketRead(d, meta) } @@ -118,3 +136,38 @@ func resourceAwsS3BucketDelete(d *schema.ResourceData, meta interface{}) error { } return nil } + +func updateWebsite(s3conn *s3.S3, d *schema.ResourceData) error { + website := d.Get("website").(bool) + bucket := d.Get("bucket").(string) + indexDocument := d.Get("index_document").(string) + + websiteConfiguration := &s3.WebsiteConfiguration{ + IndexDocument: &s3.IndexDocument{Suffix: aws.String(indexDocument)}, + } + + if website { + input := &s3.PutBucketWebsiteInput{ + Bucket: aws.String(bucket), + WebsiteConfiguration: websiteConfiguration, + } + + log.Printf("[DEBUG] S3 put bucket website: %s", input) + + _, err := s3conn.PutBucketWebsite(input) + if err != nil { + return fmt.Errorf("Error putting S3 website: %s", err) + } + } else { + deleteInput := &s3.DeleteBucketWebsiteInput{Bucket: aws.String(bucket)} + + log.Printf("[DEBUG] S3 delete bucket website: %s", deleteInput) + + _, err := s3conn.DeleteBucketWebsite(deleteInput) + if err != nil { + return fmt.Errorf("Error deleting S3 website: %s", err) + } + } + + return nil +} diff --git a/builtin/providers/aws/resource_aws_s3_bucket_test.go b/builtin/providers/aws/resource_aws_s3_bucket_test.go index 4e396801dd76..3e5e17e55685 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_test.go @@ -23,7 +23,13 @@ func TestAccAWSS3Bucket(t *testing.T) { resource.TestStep{ Config: testAccAWSS3BucketConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckAWSS3BucketExists("aws_s3_bucket.bar"), + testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"), + ), + }, + resource.TestStep{ + Config: testAccAWSS3BucketWebsiteConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketExists("aws_s3_bucket.website"), ), }, }, @@ -70,11 +76,21 @@ func testAccCheckAWSS3BucketExists(n string) resource.TestCheckFunc { } } -// This needs a bit of randoness as the name can only be -// used once globally within AWS +// These need a bit of randoness as the name can only be used once globally +// within AWS var testAccAWSS3BucketConfig = fmt.Sprintf(` -resource "aws_s3_bucket" "bar" { +resource "aws_s3_bucket" "bucket" { bucket = "tf-test-bucket-%d" acl = "public-read" } `, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) + +var testAccAWSS3BucketWebsiteConfig = fmt.Sprintf(` +resource "aws_s3_bucket" "website" { + bucket = "tf-test-bucket-website-%d" + acl = "public-read" + + website = true + index_document = "index.html" +} +`, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) From b7a9ef5ef6a17a92df323f1adf37fbb7a22ad7db Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Wed, 29 Apr 2015 12:16:01 -0400 Subject: [PATCH 02/13] providers/aws: Add S3 error_document Also fix when index/error document is empty --- .../providers/aws/resource_aws_s3_bucket.go | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index cb2608427cbf..9eb8c9adb0a5 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -44,6 +44,12 @@ func resourceAwsS3Bucket() *schema.Resource { ForceNew: false, }, + "error_document": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "tags": tagsSchema(), }, } @@ -141,20 +147,27 @@ func updateWebsite(s3conn *s3.S3, d *schema.ResourceData) error { website := d.Get("website").(bool) bucket := d.Get("bucket").(string) indexDocument := d.Get("index_document").(string) - - websiteConfiguration := &s3.WebsiteConfiguration{ - IndexDocument: &s3.IndexDocument{Suffix: aws.String(indexDocument)}, - } + errorDocument := d.Get("error_document").(string) if website { - input := &s3.PutBucketWebsiteInput{ + websiteConfiguration := &s3.WebsiteConfiguration{} + + if indexDocument != "" { + websiteConfiguration.IndexDocument = &s3.IndexDocument{Suffix: aws.String(indexDocument)} + } + + if errorDocument != "" { + websiteConfiguration.ErrorDocument = &s3.ErrorDocument{Key: aws.String(errorDocument)} + } + + putInput := &s3.PutBucketWebsiteInput{ Bucket: aws.String(bucket), WebsiteConfiguration: websiteConfiguration, } - log.Printf("[DEBUG] S3 put bucket website: %s", input) + log.Printf("[DEBUG] S3 put bucket website: %s", putInput) - _, err := s3conn.PutBucketWebsite(input) + _, err := s3conn.PutBucketWebsite(putInput) if err != nil { return fmt.Errorf("Error putting S3 website: %s", err) } From 38e04b3765125dece9950c566255caf8b1f1effa Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Wed, 29 Apr 2015 15:47:21 -0400 Subject: [PATCH 03/13] providers/aws: Add website_endpoint to S3 output --- .../providers/aws/resource_aws_s3_bucket.go | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index 9eb8c9adb0a5..2ab8fb81a33b 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -35,19 +35,22 @@ func resourceAwsS3Bucket() *schema.Resource { Type: schema.TypeBool, Default: false, Optional: true, - ForceNew: false, }, "index_document": &schema.Schema{ Type: schema.TypeString, Optional: true, - ForceNew: false, }, "error_document": &schema.Schema{ Type: schema.TypeString, Optional: true, - ForceNew: false, + }, + + "website_endpoint": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, }, "tags": tagsSchema(), @@ -118,6 +121,15 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error { } } + // Add website_endpoint as an output + endpoint, err := websiteEndpoint(s3conn, d) + if err != nil { + return err + } + if err := d.Set("website_endpoint", endpoint); err != nil { + return err + } + tagSet, err := getTagSetS3(s3conn, d.Id()) if err != nil { return err @@ -184,3 +196,31 @@ func updateWebsite(s3conn *s3.S3, d *schema.ResourceData) error { return nil } + +func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (endpoint string, err error) { + // If the bucket doess't have a website configuration, return an empty endpoint + if !d.Get("website").(bool) { + return + } + + bucket := d.Get("bucket").(string) + + // Lookup the region for this bucket + location, err := s3conn.GetBucketLocation(&s3.GetBucketLocationInput{Bucket: aws.String(bucket)}) + if err != nil { + return + } + var region string + if location.LocationConstraint != nil { + region = *location.LocationConstraint + } + + // Default to us-east-1 if the bucket doesn't have a region + if region == "" { + region = "us-east-1" + } + + endpoint = fmt.Sprintf("%s.s3-website-%s.amazonaws.com", bucket, region) + + return +} From 30f737c78132706260352a8dfa0e80c922581c72 Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Fri, 1 May 2015 09:19:54 -0400 Subject: [PATCH 04/13] providers/aws: Change S3 website to block --- .../providers/aws/resource_aws_s3_bucket.go | 113 +++++++++++------- .../aws/resource_aws_s3_bucket_test.go | 5 +- 2 files changed, 72 insertions(+), 46 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index 2ab8fb81a33b..04cd98745760 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -32,19 +32,21 @@ func resourceAwsS3Bucket() *schema.Resource { }, "website": &schema.Schema{ - Type: schema.TypeBool, - Default: false, - Optional: true, - }, - - "index_document": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - - "error_document": &schema.Schema{ - Type: schema.TypeString, + Type: schema.TypeList, Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "index_document": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "error_document": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, }, "website_endpoint": &schema.Schema{ @@ -98,7 +100,7 @@ func resourceAwsS3BucketUpdate(d *schema.ResourceData, meta interface{}) error { return err } - if err := updateWebsite(s3conn, d); err != nil { + if err := resourceAwsS3BucketWebsiteUpdate(s3conn, d); err != nil { return err } @@ -155,58 +157,81 @@ func resourceAwsS3BucketDelete(d *schema.ResourceData, meta interface{}) error { return nil } -func updateWebsite(s3conn *s3.S3, d *schema.ResourceData) error { - website := d.Get("website").(bool) +func resourceAwsS3BucketWebsiteUpdate(s3conn *s3.S3, d *schema.ResourceData) error { + if !d.HasChange("website") { + return nil + } + + ws := d.Get("website").([]interface{}) + + if len(ws) == 1 { + w := ws[0].(map[string]interface{}) + return resourceAwsS3BucketWebsitePut(s3conn, d, w) + } else if len(ws) == 0 { + return resourceAwsS3BucketWebsiteDelete(s3conn, d) + } else { + return fmt.Errorf("Cannot specify more than one website.") + } +} + +func resourceAwsS3BucketWebsitePut(s3conn *s3.S3, d *schema.ResourceData, website map[string]interface{}) error { bucket := d.Get("bucket").(string) - indexDocument := d.Get("index_document").(string) - errorDocument := d.Get("error_document").(string) - if website { - websiteConfiguration := &s3.WebsiteConfiguration{} + indexDocument := website["index_document"].(string) + errorDocument := website["error_document"].(string) - if indexDocument != "" { - websiteConfiguration.IndexDocument = &s3.IndexDocument{Suffix: aws.String(indexDocument)} - } + websiteConfiguration := &s3.WebsiteConfiguration{} - if errorDocument != "" { - websiteConfiguration.ErrorDocument = &s3.ErrorDocument{Key: aws.String(errorDocument)} - } + websiteConfiguration.IndexDocument = &s3.IndexDocument{Suffix: aws.String(indexDocument)} - putInput := &s3.PutBucketWebsiteInput{ - Bucket: aws.String(bucket), - WebsiteConfiguration: websiteConfiguration, - } + if errorDocument != "" { + websiteConfiguration.ErrorDocument = &s3.ErrorDocument{Key: aws.String(errorDocument)} + } - log.Printf("[DEBUG] S3 put bucket website: %s", putInput) + putInput := &s3.PutBucketWebsiteInput{ + Bucket: aws.String(bucket), + WebsiteConfiguration: websiteConfiguration, + } - _, err := s3conn.PutBucketWebsite(putInput) - if err != nil { - return fmt.Errorf("Error putting S3 website: %s", err) - } - } else { - deleteInput := &s3.DeleteBucketWebsiteInput{Bucket: aws.String(bucket)} + log.Printf("[DEBUG] S3 put bucket website: %s", putInput) - log.Printf("[DEBUG] S3 delete bucket website: %s", deleteInput) + _, err := s3conn.PutBucketWebsite(putInput) + if err != nil { + return fmt.Errorf("Error putting S3 website: %s", err) + } - _, err := s3conn.DeleteBucketWebsite(deleteInput) - if err != nil { - return fmt.Errorf("Error deleting S3 website: %s", err) - } + return nil +} + +func resourceAwsS3BucketWebsiteDelete(s3conn *s3.S3, d *schema.ResourceData) error { + bucket := d.Get("bucket").(string) + deleteInput := &s3.DeleteBucketWebsiteInput{Bucket: aws.String(bucket)} + + log.Printf("[DEBUG] S3 delete bucket website: %s", deleteInput) + + _, err := s3conn.DeleteBucketWebsite(deleteInput) + if err != nil { + return fmt.Errorf("Error deleting S3 website: %s", err) } return nil } func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (endpoint string, err error) { - // If the bucket doess't have a website configuration, return an empty endpoint - if !d.Get("website").(bool) { + // If the bucket doesn't have a website configuration, return an empty + // endpoint + if len(d.Get("website").([]interface{})) == 0 { return } bucket := d.Get("bucket").(string) // Lookup the region for this bucket - location, err := s3conn.GetBucketLocation(&s3.GetBucketLocationInput{Bucket: aws.String(bucket)}) + location, err := s3conn.GetBucketLocation( + &s3.GetBucketLocationInput{ + Bucket: aws.String(bucket), + }, + ) if err != nil { return } diff --git a/builtin/providers/aws/resource_aws_s3_bucket_test.go b/builtin/providers/aws/resource_aws_s3_bucket_test.go index 3e5e17e55685..51f4185fdd51 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_test.go @@ -90,7 +90,8 @@ resource "aws_s3_bucket" "website" { bucket = "tf-test-bucket-website-%d" acl = "public-read" - website = true - index_document = "index.html" + website { + index_document = "index.html" + } } `, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) From 562bd6541b3d9ac422c0e83b88d00008e6004d08 Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Fri, 1 May 2015 09:37:02 -0400 Subject: [PATCH 05/13] providers/aws: Use explicit returns in websiteEndpoint --- builtin/providers/aws/resource_aws_s3_bucket.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index 04cd98745760..c5e3b51fd25e 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -217,11 +217,11 @@ func resourceAwsS3BucketWebsiteDelete(s3conn *s3.S3, d *schema.ResourceData) err return nil } -func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (endpoint string, err error) { +func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (string, error) { // If the bucket doesn't have a website configuration, return an empty // endpoint if len(d.Get("website").([]interface{})) == 0 { - return + return "", nil } bucket := d.Get("bucket").(string) @@ -233,7 +233,7 @@ func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (endpoint string, er }, ) if err != nil { - return + return "", err } var region string if location.LocationConstraint != nil { @@ -245,7 +245,7 @@ func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (endpoint string, er region = "us-east-1" } - endpoint = fmt.Sprintf("%s.s3-website-%s.amazonaws.com", bucket, region) + endpoint := fmt.Sprintf("%s.s3-website-%s.amazonaws.com", bucket, region) - return + return endpoint, nil } From 348942d3fb1f21134ccbc4fe0beccfcb95ad6d37 Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Fri, 1 May 2015 09:38:11 -0400 Subject: [PATCH 06/13] providers/aws: Use GetOk instead of Get + cast --- builtin/providers/aws/resource_aws_s3_bucket.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index c5e3b51fd25e..6605837f8ad1 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -220,7 +220,7 @@ func resourceAwsS3BucketWebsiteDelete(s3conn *s3.S3, d *schema.ResourceData) err func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (string, error) { // If the bucket doesn't have a website configuration, return an empty // endpoint - if len(d.Get("website").([]interface{})) == 0 { + if _, ok := d.GetOk("website"); !ok { return "", nil } From 564b7e1e67eb447c13fbecf70d4480bface2c1b3 Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Fri, 1 May 2015 09:48:08 -0400 Subject: [PATCH 07/13] providers/aws: Add docs for S3 website --- .../providers/aws/r/s3_bucket.html.markdown | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/aws/r/s3_bucket.html.markdown b/website/source/docs/providers/aws/r/s3_bucket.html.markdown index 49cee01ec8c0..821918804351 100644 --- a/website/source/docs/providers/aws/r/s3_bucket.html.markdown +++ b/website/source/docs/providers/aws/r/s3_bucket.html.markdown @@ -12,6 +12,8 @@ Provides a S3 bucket resource. ## Example Usage +### Private Bucket w/ Tags + ``` resource "aws_s3_bucket" "b" { bucket = "my_tf_test_bucket" @@ -24,6 +26,20 @@ resource "aws_s3_bucket" "b" { } ``` +### Static Website Hosting + +``` +resource "aws_s3_bucket" "b" { + bucket = "s3-website-test.hashicorp.com" + acl = "public-read" + + website { + index_document = "index.html" + error_document = "error.html" + } +} +``` + ## Argument Reference The following arguments are supported: @@ -31,10 +47,16 @@ The following arguments are supported: * `bucket` - (Required) The name of the bucket. * `acl` - (Optional) The [canned ACL](http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) to apply. Defaults to "private". * `tags` - (Optional) A mapping of tags to assign to the bucket. +* `website` - (Optional) A website object (documented below). + +The website object supports the following: + +* `index_document` - (Required) Amazon S3 returns this index document when requests are made to the root domain or any of the subfolders. +* `error_document` - (Optional) An absolute path to the document to return in case of a 4XX error. ## Attributes Reference The following attributes are exported: -* `id` - The name of the bucket - +* `id` - The name of the bucket. +* `website_endpoint` - The website endpoint, if the bucket is configured with a website. If not, this will be an empty string. From be84cf8a8c5a40190428ac61c601ab603d7f7eed Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Fri, 1 May 2015 09:50:49 -0400 Subject: [PATCH 08/13] providers/aws: Add note for us-east-1 empty location --- builtin/providers/aws/resource_aws_s3_bucket.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index 6605837f8ad1..466c14febec0 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -240,7 +240,8 @@ func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (string, error) { region = *location.LocationConstraint } - // Default to us-east-1 if the bucket doesn't have a region + // Default to us-east-1 if the bucket doesn't have a region: + // http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETlocation.html if region == "" { region = "us-east-1" } From 20e531ae0dbc2fcfae9fd17e85e93b994e2b71ad Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Fri, 1 May 2015 21:04:22 -0400 Subject: [PATCH 09/13] providers/aws: Check that S3 website sets docs --- .../aws/resource_aws_s3_bucket_test.go | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_test.go b/builtin/providers/aws/resource_aws_s3_bucket_test.go index 51f4185fdd51..f65ddeebd8c2 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_test.go @@ -14,7 +14,6 @@ import ( ) func TestAccAWSS3Bucket(t *testing.T) { - resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -26,10 +25,22 @@ func TestAccAWSS3Bucket(t *testing.T) { testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"), ), }, + }, + }) +} + +func TestAccAWSS3BucketWebsite(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3BucketDestroy, + Steps: []resource.TestStep{ resource.TestStep{ Config: testAccAWSS3BucketWebsiteConfig, Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketExists("aws_s3_bucket.website"), + testAccCheckAWSS3BucketWebsite( + "aws_s3_bucket.website", "index.html", "error.html"), ), }, }, @@ -76,6 +87,31 @@ func testAccCheckAWSS3BucketExists(n string) resource.TestCheckFunc { } } +func testAccCheckAWSS3BucketWebsite(n string, indexDoc string, errorDoc string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, _ := s.RootModule().Resources[n] + conn := testAccProvider.Meta().(*AWSClient).s3conn + + out, err := conn.GetBucketWebsite(&s3.GetBucketWebsiteInput{ + Bucket: aws.String(rs.Primary.ID), + }) + + if err != nil { + return fmt.Errorf("S3BucketWebsite error: %v", err) + } + + if *out.IndexDocument.Suffix != indexDoc { + return fmt.Errorf("bad: %s", out.IndexDocument) + } + + if *out.ErrorDocument.Key != errorDoc { + return fmt.Errorf("bad: %s", out.ErrorDocument) + } + + return nil + } +} + // These need a bit of randoness as the name can only be used once globally // within AWS var testAccAWSS3BucketConfig = fmt.Sprintf(` @@ -92,6 +128,7 @@ resource "aws_s3_bucket" "website" { website { index_document = "index.html" + error_document = "error.html" } } `, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) From eeb65b8d4c348d72e5a65c4e19fbad3085b3645b Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Wed, 6 May 2015 08:12:40 -0400 Subject: [PATCH 10/13] providers/aws: Read S3 website config --- builtin/providers/aws/resource_aws_s3_bucket.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index 466c14febec0..87ed91e8dd68 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -123,6 +123,21 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error { } } + // Read the website configuration + ws, err := s3conn.GetBucketWebsite(&s3.GetBucketWebsiteInput{ + Bucket: aws.String(d.Id()), + }) + var websites []map[string]interface{} + if err == nil { + websites = append(websites, map[string]interface{}{ + "index_document": *ws.IndexDocument.Suffix, + "error_document": *ws.ErrorDocument.Key, + }) + } + if err := d.Set("website", websites); err != nil { + return err + } + // Add website_endpoint as an output endpoint, err := websiteEndpoint(s3conn, d) if err != nil { From 0b78a71ed5eb70e9ef6e7ddd9a4e62979675943c Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Wed, 6 May 2015 08:15:07 -0400 Subject: [PATCH 11/13] providers/aws: Test S3 website removal --- .../aws/resource_aws_s3_bucket_test.go | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_test.go b/builtin/providers/aws/resource_aws_s3_bucket_test.go index f65ddeebd8c2..6f6a76004782 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_test.go @@ -38,9 +38,17 @@ func TestAccAWSS3BucketWebsite(t *testing.T) { resource.TestStep{ Config: testAccAWSS3BucketWebsiteConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckAWSS3BucketExists("aws_s3_bucket.website"), + testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"), + testAccCheckAWSS3BucketWebsite( + "aws_s3_bucket.bucket", "index.html", "error.html"), + ), + }, + resource.TestStep{ + Config: testAccAWSS3BucketConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"), testAccCheckAWSS3BucketWebsite( - "aws_s3_bucket.website", "index.html", "error.html"), + "aws_s3_bucket.bucket", "", ""), ), }, }, @@ -97,7 +105,13 @@ func testAccCheckAWSS3BucketWebsite(n string, indexDoc string, errorDoc string) }) if err != nil { - return fmt.Errorf("S3BucketWebsite error: %v", err) + if indexDoc == "" { + // If we want to assert that the website is not there, than + // this error is expected + return nil + } else { + return fmt.Errorf("S3BucketWebsite error: %v", err) + } } if *out.IndexDocument.Suffix != indexDoc { @@ -112,18 +126,19 @@ func testAccCheckAWSS3BucketWebsite(n string, indexDoc string, errorDoc string) } } -// These need a bit of randoness as the name can only be used once globally +// These need a bit of randomness as the name can only be used once globally // within AWS +var d = rand.New(rand.NewSource(time.Now().UnixNano())).Int() var testAccAWSS3BucketConfig = fmt.Sprintf(` resource "aws_s3_bucket" "bucket" { bucket = "tf-test-bucket-%d" acl = "public-read" } -`, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) +`, d) var testAccAWSS3BucketWebsiteConfig = fmt.Sprintf(` -resource "aws_s3_bucket" "website" { - bucket = "tf-test-bucket-website-%d" +resource "aws_s3_bucket" "bucket" { + bucket = "tf-test-bucket-%d" acl = "public-read" website { @@ -131,4 +146,4 @@ resource "aws_s3_bucket" "website" { error_document = "error.html" } } -`, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) +`, d) From 87e6d3d17ff0be839cb1fc7a5fe5339393bb723f Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Wed, 6 May 2015 08:40:32 -0400 Subject: [PATCH 12/13] providers/aws: Fix S3 website error doc --- .../providers/aws/resource_aws_s3_bucket.go | 13 ++++++--- .../aws/resource_aws_s3_bucket_test.go | 29 +++++++++++++++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index 87ed91e8dd68..fa802d6cf5ed 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -129,10 +129,15 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error { }) var websites []map[string]interface{} if err == nil { - websites = append(websites, map[string]interface{}{ - "index_document": *ws.IndexDocument.Suffix, - "error_document": *ws.ErrorDocument.Key, - }) + w := make(map[string]interface{}) + + w["index_document"] = *ws.IndexDocument.Suffix + + if v := ws.ErrorDocument; v != nil { + w["error_document"] = *v.Key + } + + websites = append(websites, w) } if err := d.Set("website", websites); err != nil { return err diff --git a/builtin/providers/aws/resource_aws_s3_bucket_test.go b/builtin/providers/aws/resource_aws_s3_bucket_test.go index 6f6a76004782..eb8e4e6cb812 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_test.go @@ -37,6 +37,14 @@ func TestAccAWSS3BucketWebsite(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccAWSS3BucketWebsiteConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"), + testAccCheckAWSS3BucketWebsite( + "aws_s3_bucket.bucket", "index.html", ""), + ), + }, + resource.TestStep{ + Config: testAccAWSS3BucketWebsiteConfigWithError, Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"), testAccCheckAWSS3BucketWebsite( @@ -118,8 +126,14 @@ func testAccCheckAWSS3BucketWebsite(n string, indexDoc string, errorDoc string) return fmt.Errorf("bad: %s", out.IndexDocument) } - if *out.ErrorDocument.Key != errorDoc { - return fmt.Errorf("bad: %s", out.ErrorDocument) + if v := out.ErrorDocument; v == nil { + if errorDoc != "" { + return fmt.Errorf("bad: %s", out.ErrorDocument) + } + } else { + if *v.Key != errorDoc { + return fmt.Errorf("bad: %s", out.ErrorDocument) + } } return nil @@ -141,6 +155,17 @@ resource "aws_s3_bucket" "bucket" { bucket = "tf-test-bucket-%d" acl = "public-read" + website { + index_document = "index.html" + } +} +`, d) + +var testAccAWSS3BucketWebsiteConfigWithError = fmt.Sprintf(` +resource "aws_s3_bucket" "bucket" { + bucket = "tf-test-bucket-%d" + acl = "public-read" + website { index_document = "index.html" error_document = "error.html" From 2745adba563ff872f11d2cc17d526f71b21f0be7 Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Wed, 6 May 2015 08:55:10 -0400 Subject: [PATCH 13/13] providers/aws: Test S3 website endpoint attr --- .../aws/resource_aws_s3_bucket_test.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket_test.go b/builtin/providers/aws/resource_aws_s3_bucket_test.go index eb8e4e6cb812..419c530cf97f 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_test.go @@ -23,6 +23,8 @@ func TestAccAWSS3Bucket(t *testing.T) { Config: testAccAWSS3BucketConfig, Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"), + resource.TestCheckResourceAttr( + "aws_s3_bucket.bucket", "website_endpoint", ""), ), }, }, @@ -41,6 +43,8 @@ func TestAccAWSS3BucketWebsite(t *testing.T) { testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"), testAccCheckAWSS3BucketWebsite( "aws_s3_bucket.bucket", "index.html", ""), + resource.TestCheckResourceAttr( + "aws_s3_bucket.bucket", "website_endpoint", testAccWebsiteEndpoint), ), }, resource.TestStep{ @@ -49,6 +53,8 @@ func TestAccAWSS3BucketWebsite(t *testing.T) { testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"), testAccCheckAWSS3BucketWebsite( "aws_s3_bucket.bucket", "index.html", "error.html"), + resource.TestCheckResourceAttr( + "aws_s3_bucket.bucket", "website_endpoint", testAccWebsiteEndpoint), ), }, resource.TestStep{ @@ -57,6 +63,8 @@ func TestAccAWSS3BucketWebsite(t *testing.T) { testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"), testAccCheckAWSS3BucketWebsite( "aws_s3_bucket.bucket", "", ""), + resource.TestCheckResourceAttr( + "aws_s3_bucket.bucket", "website_endpoint", ""), ), }, }, @@ -142,13 +150,14 @@ func testAccCheckAWSS3BucketWebsite(n string, indexDoc string, errorDoc string) // These need a bit of randomness as the name can only be used once globally // within AWS -var d = rand.New(rand.NewSource(time.Now().UnixNano())).Int() +var randInt = rand.New(rand.NewSource(time.Now().UnixNano())).Int() +var testAccWebsiteEndpoint = fmt.Sprintf("tf-test-bucket-%d.s3-website-us-east-1.amazonaws.com", randInt) var testAccAWSS3BucketConfig = fmt.Sprintf(` resource "aws_s3_bucket" "bucket" { bucket = "tf-test-bucket-%d" acl = "public-read" } -`, d) +`, randInt) var testAccAWSS3BucketWebsiteConfig = fmt.Sprintf(` resource "aws_s3_bucket" "bucket" { @@ -159,7 +168,7 @@ resource "aws_s3_bucket" "bucket" { index_document = "index.html" } } -`, d) +`, randInt) var testAccAWSS3BucketWebsiteConfigWithError = fmt.Sprintf(` resource "aws_s3_bucket" "bucket" { @@ -171,4 +180,4 @@ resource "aws_s3_bucket" "bucket" { error_document = "error.html" } } -`, d) +`, randInt)