github.com/markdia/terraform@v0.5.1-0.20150508012022-f1ae920aa970/builtin/providers/aws/resource_aws_s3_bucket.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/terraform/helper/schema" 8 9 "github.com/awslabs/aws-sdk-go/aws" 10 "github.com/awslabs/aws-sdk-go/service/s3" 11 ) 12 13 func resourceAwsS3Bucket() *schema.Resource { 14 return &schema.Resource{ 15 Create: resourceAwsS3BucketCreate, 16 Read: resourceAwsS3BucketRead, 17 Update: resourceAwsS3BucketUpdate, 18 Delete: resourceAwsS3BucketDelete, 19 20 Schema: map[string]*schema.Schema{ 21 "bucket": &schema.Schema{ 22 Type: schema.TypeString, 23 Required: true, 24 ForceNew: true, 25 }, 26 27 "acl": &schema.Schema{ 28 Type: schema.TypeString, 29 Default: "private", 30 Optional: true, 31 ForceNew: true, 32 }, 33 34 "website": &schema.Schema{ 35 Type: schema.TypeList, 36 Optional: true, 37 Elem: &schema.Resource{ 38 Schema: map[string]*schema.Schema{ 39 "index_document": &schema.Schema{ 40 Type: schema.TypeString, 41 Required: true, 42 }, 43 44 "error_document": &schema.Schema{ 45 Type: schema.TypeString, 46 Optional: true, 47 }, 48 }, 49 }, 50 }, 51 52 "website_endpoint": &schema.Schema{ 53 Type: schema.TypeString, 54 Optional: true, 55 Computed: true, 56 }, 57 58 "tags": tagsSchema(), 59 }, 60 } 61 } 62 63 func resourceAwsS3BucketCreate(d *schema.ResourceData, meta interface{}) error { 64 s3conn := meta.(*AWSClient).s3conn 65 awsRegion := meta.(*AWSClient).region 66 67 // Get the bucket and acl 68 bucket := d.Get("bucket").(string) 69 acl := d.Get("acl").(string) 70 71 log.Printf("[DEBUG] S3 bucket create: %s, ACL: %s", bucket, acl) 72 73 req := &s3.CreateBucketInput{ 74 Bucket: aws.String(bucket), 75 ACL: aws.String(acl), 76 } 77 78 // Special case us-east-1 region and do not set the LocationConstraint. 79 // See "Request Elements: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUT.html 80 if awsRegion != "us-east-1" { 81 req.CreateBucketConfiguration = &s3.CreateBucketConfiguration{ 82 LocationConstraint: aws.String(awsRegion), 83 } 84 } 85 86 _, err := s3conn.CreateBucket(req) 87 if err != nil { 88 return fmt.Errorf("Error creating S3 bucket: %s", err) 89 } 90 91 // Assign the bucket name as the resource ID 92 d.SetId(bucket) 93 94 return resourceAwsS3BucketUpdate(d, meta) 95 } 96 97 func resourceAwsS3BucketUpdate(d *schema.ResourceData, meta interface{}) error { 98 s3conn := meta.(*AWSClient).s3conn 99 if err := setTagsS3(s3conn, d); err != nil { 100 return err 101 } 102 103 if err := resourceAwsS3BucketWebsiteUpdate(s3conn, d); err != nil { 104 return err 105 } 106 107 return resourceAwsS3BucketRead(d, meta) 108 } 109 110 func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error { 111 s3conn := meta.(*AWSClient).s3conn 112 113 _, err := s3conn.HeadBucket(&s3.HeadBucketInput{ 114 Bucket: aws.String(d.Id()), 115 }) 116 if err != nil { 117 if awsError, ok := err.(aws.APIError); ok && awsError.StatusCode == 404 { 118 d.SetId("") 119 } else { 120 // some of the AWS SDK's errors can be empty strings, so let's add 121 // some additional context. 122 return fmt.Errorf("error reading S3 bucket \"%s\": %s", d.Id(), err) 123 } 124 } 125 126 // Read the website configuration 127 ws, err := s3conn.GetBucketWebsite(&s3.GetBucketWebsiteInput{ 128 Bucket: aws.String(d.Id()), 129 }) 130 var websites []map[string]interface{} 131 if err == nil { 132 w := make(map[string]interface{}) 133 134 w["index_document"] = *ws.IndexDocument.Suffix 135 136 if v := ws.ErrorDocument; v != nil { 137 w["error_document"] = *v.Key 138 } 139 140 websites = append(websites, w) 141 } 142 if err := d.Set("website", websites); err != nil { 143 return err 144 } 145 146 // Add website_endpoint as an output 147 endpoint, err := websiteEndpoint(s3conn, d) 148 if err != nil { 149 return err 150 } 151 if err := d.Set("website_endpoint", endpoint); err != nil { 152 return err 153 } 154 155 tagSet, err := getTagSetS3(s3conn, d.Id()) 156 if err != nil { 157 return err 158 } 159 160 if err := d.Set("tags", tagsToMapS3(tagSet)); err != nil { 161 return err 162 } 163 164 return nil 165 } 166 167 func resourceAwsS3BucketDelete(d *schema.ResourceData, meta interface{}) error { 168 s3conn := meta.(*AWSClient).s3conn 169 170 log.Printf("[DEBUG] S3 Delete Bucket: %s", d.Id()) 171 _, err := s3conn.DeleteBucket(&s3.DeleteBucketInput{ 172 Bucket: aws.String(d.Id()), 173 }) 174 if err != nil { 175 return err 176 } 177 return nil 178 } 179 180 func resourceAwsS3BucketWebsiteUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 181 if !d.HasChange("website") { 182 return nil 183 } 184 185 ws := d.Get("website").([]interface{}) 186 187 if len(ws) == 1 { 188 w := ws[0].(map[string]interface{}) 189 return resourceAwsS3BucketWebsitePut(s3conn, d, w) 190 } else if len(ws) == 0 { 191 return resourceAwsS3BucketWebsiteDelete(s3conn, d) 192 } else { 193 return fmt.Errorf("Cannot specify more than one website.") 194 } 195 } 196 197 func resourceAwsS3BucketWebsitePut(s3conn *s3.S3, d *schema.ResourceData, website map[string]interface{}) error { 198 bucket := d.Get("bucket").(string) 199 200 indexDocument := website["index_document"].(string) 201 errorDocument := website["error_document"].(string) 202 203 websiteConfiguration := &s3.WebsiteConfiguration{} 204 205 websiteConfiguration.IndexDocument = &s3.IndexDocument{Suffix: aws.String(indexDocument)} 206 207 if errorDocument != "" { 208 websiteConfiguration.ErrorDocument = &s3.ErrorDocument{Key: aws.String(errorDocument)} 209 } 210 211 putInput := &s3.PutBucketWebsiteInput{ 212 Bucket: aws.String(bucket), 213 WebsiteConfiguration: websiteConfiguration, 214 } 215 216 log.Printf("[DEBUG] S3 put bucket website: %#v", putInput) 217 218 _, err := s3conn.PutBucketWebsite(putInput) 219 if err != nil { 220 return fmt.Errorf("Error putting S3 website: %s", err) 221 } 222 223 return nil 224 } 225 226 func resourceAwsS3BucketWebsiteDelete(s3conn *s3.S3, d *schema.ResourceData) error { 227 bucket := d.Get("bucket").(string) 228 deleteInput := &s3.DeleteBucketWebsiteInput{Bucket: aws.String(bucket)} 229 230 log.Printf("[DEBUG] S3 delete bucket website: %#v", deleteInput) 231 232 _, err := s3conn.DeleteBucketWebsite(deleteInput) 233 if err != nil { 234 return fmt.Errorf("Error deleting S3 website: %s", err) 235 } 236 237 return nil 238 } 239 240 func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (string, error) { 241 // If the bucket doesn't have a website configuration, return an empty 242 // endpoint 243 if _, ok := d.GetOk("website"); !ok { 244 return "", nil 245 } 246 247 bucket := d.Get("bucket").(string) 248 249 // Lookup the region for this bucket 250 location, err := s3conn.GetBucketLocation( 251 &s3.GetBucketLocationInput{ 252 Bucket: aws.String(bucket), 253 }, 254 ) 255 if err != nil { 256 return "", err 257 } 258 var region string 259 if location.LocationConstraint != nil { 260 region = *location.LocationConstraint 261 } 262 263 // Default to us-east-1 if the bucket doesn't have a region: 264 // http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETlocation.html 265 if region == "" { 266 region = "us-east-1" 267 } 268 269 endpoint := fmt.Sprintf("%s.s3-website-%s.amazonaws.com", bucket, region) 270 271 return endpoint, nil 272 }