github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/aws/resource_aws_s3_bucket_object.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "log" 8 "os" 9 "strings" 10 11 "github.com/hashicorp/terraform/helper/schema" 12 "github.com/mitchellh/go-homedir" 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 ) 18 19 func resourceAwsS3BucketObject() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceAwsS3BucketObjectPut, 22 Read: resourceAwsS3BucketObjectRead, 23 Update: resourceAwsS3BucketObjectPut, 24 Delete: resourceAwsS3BucketObjectDelete, 25 26 Schema: map[string]*schema.Schema{ 27 "bucket": &schema.Schema{ 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 }, 32 33 "cache_control": &schema.Schema{ 34 Type: schema.TypeString, 35 Optional: true, 36 }, 37 38 "content_disposition": &schema.Schema{ 39 Type: schema.TypeString, 40 Optional: true, 41 }, 42 43 "content_encoding": &schema.Schema{ 44 Type: schema.TypeString, 45 Optional: true, 46 }, 47 48 "content_language": &schema.Schema{ 49 Type: schema.TypeString, 50 Optional: true, 51 }, 52 53 "content_type": &schema.Schema{ 54 Type: schema.TypeString, 55 Optional: true, 56 Computed: true, 57 }, 58 59 "key": &schema.Schema{ 60 Type: schema.TypeString, 61 Required: true, 62 ForceNew: true, 63 }, 64 65 "source": &schema.Schema{ 66 Type: schema.TypeString, 67 Optional: true, 68 ConflictsWith: []string{"content"}, 69 }, 70 71 "content": &schema.Schema{ 72 Type: schema.TypeString, 73 Optional: true, 74 ConflictsWith: []string{"source"}, 75 }, 76 77 "kms_key_id": &schema.Schema{ 78 Type: schema.TypeString, 79 Optional: true, 80 }, 81 82 "etag": &schema.Schema{ 83 Type: schema.TypeString, 84 // This will conflict with SSE-C and SSE-KMS encryption and multi-part upload 85 // if/when it's actually implemented. The Etag then won't match raw-file MD5. 86 // See http://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html 87 Optional: true, 88 Computed: true, 89 }, 90 91 "version_id": &schema.Schema{ 92 Type: schema.TypeString, 93 Computed: true, 94 }, 95 }, 96 } 97 } 98 99 func resourceAwsS3BucketObjectPut(d *schema.ResourceData, meta interface{}) error { 100 s3conn := meta.(*AWSClient).s3conn 101 102 bucket := d.Get("bucket").(string) 103 key := d.Get("key").(string) 104 var body io.ReadSeeker 105 106 if v, ok := d.GetOk("source"); ok { 107 source := v.(string) 108 path, err := homedir.Expand(source) 109 if err != nil { 110 return fmt.Errorf("Error expanding homedir in source (%s): %s", source, err) 111 } 112 file, err := os.Open(path) 113 if err != nil { 114 return fmt.Errorf("Error opening S3 bucket object source (%s): %s", source, err) 115 } 116 117 body = file 118 } else if v, ok := d.GetOk("content"); ok { 119 content := v.(string) 120 body = bytes.NewReader([]byte(content)) 121 } else { 122 return fmt.Errorf("Must specify \"source\" or \"content\" field") 123 } 124 125 if _, ok := d.GetOk("kms_key_id"); ok { 126 if _, ok := d.GetOk("etag"); ok { 127 return fmt.Errorf("Unable to specify 'kms_key_id' and 'etag' together because 'etag' wouldn't equal the MD5 digest of the raw object data") 128 } 129 } 130 131 putInput := &s3.PutObjectInput{ 132 Bucket: aws.String(bucket), 133 Key: aws.String(key), 134 Body: body, 135 } 136 137 if v, ok := d.GetOk("cache_control"); ok { 138 putInput.CacheControl = aws.String(v.(string)) 139 } 140 141 if v, ok := d.GetOk("content_type"); ok { 142 putInput.ContentType = aws.String(v.(string)) 143 } 144 145 if v, ok := d.GetOk("content_encoding"); ok { 146 putInput.ContentEncoding = aws.String(v.(string)) 147 } 148 149 if v, ok := d.GetOk("content_language"); ok { 150 putInput.ContentLanguage = aws.String(v.(string)) 151 } 152 153 if v, ok := d.GetOk("content_disposition"); ok { 154 putInput.ContentDisposition = aws.String(v.(string)) 155 } 156 157 if v, ok := d.GetOk("kms_key_id"); ok { 158 putInput.SSEKMSKeyId = aws.String(v.(string)) 159 putInput.ServerSideEncryption = aws.String("aws:kms") 160 } 161 162 resp, err := s3conn.PutObject(putInput) 163 if err != nil { 164 return fmt.Errorf("Error putting object in S3 bucket (%s): %s", bucket, err) 165 } 166 167 // See https://forums.aws.amazon.com/thread.jspa?threadID=44003 168 d.Set("etag", strings.Trim(*resp.ETag, `"`)) 169 170 d.Set("version_id", resp.VersionId) 171 d.SetId(key) 172 return resourceAwsS3BucketObjectRead(d, meta) 173 } 174 175 func resourceAwsS3BucketObjectRead(d *schema.ResourceData, meta interface{}) error { 176 s3conn := meta.(*AWSClient).s3conn 177 178 bucket := d.Get("bucket").(string) 179 key := d.Get("key").(string) 180 etag := d.Get("etag").(string) 181 182 resp, err := s3conn.HeadObject( 183 &s3.HeadObjectInput{ 184 Bucket: aws.String(bucket), 185 Key: aws.String(key), 186 IfMatch: aws.String(etag), 187 }) 188 189 if err != nil { 190 // If S3 returns a 404 Request Failure, mark the object as destroyed 191 if awsErr, ok := err.(awserr.RequestFailure); ok && awsErr.StatusCode() == 404 { 192 d.SetId("") 193 log.Printf("[WARN] Error Reading Object (%s), object not found (HTTP status 404)", key) 194 return nil 195 } 196 return err 197 } 198 199 d.Set("cache_control", resp.CacheControl) 200 d.Set("content_disposition", resp.ContentDisposition) 201 d.Set("content_encoding", resp.ContentEncoding) 202 d.Set("content_language", resp.ContentLanguage) 203 d.Set("content_type", resp.ContentType) 204 d.Set("version_id", resp.VersionId) 205 d.Set("kms_key_id", resp.SSEKMSKeyId) 206 207 log.Printf("[DEBUG] Reading S3 Bucket Object meta: %s", resp) 208 return nil 209 } 210 211 func resourceAwsS3BucketObjectDelete(d *schema.ResourceData, meta interface{}) error { 212 s3conn := meta.(*AWSClient).s3conn 213 214 bucket := d.Get("bucket").(string) 215 key := d.Get("key").(string) 216 217 if _, ok := d.GetOk("version_id"); ok { 218 // Bucket is versioned, we need to delete all versions 219 vInput := s3.ListObjectVersionsInput{ 220 Bucket: aws.String(bucket), 221 Prefix: aws.String(key), 222 } 223 out, err := s3conn.ListObjectVersions(&vInput) 224 if err != nil { 225 return fmt.Errorf("Failed listing S3 object versions: %s", err) 226 } 227 228 for _, v := range out.Versions { 229 input := s3.DeleteObjectInput{ 230 Bucket: aws.String(bucket), 231 Key: aws.String(key), 232 VersionId: v.VersionId, 233 } 234 _, err := s3conn.DeleteObject(&input) 235 if err != nil { 236 return fmt.Errorf("Error deleting S3 object version of %s:\n %s:\n %s", 237 key, v, err) 238 } 239 } 240 } else { 241 // Just delete the object 242 input := s3.DeleteObjectInput{ 243 Bucket: aws.String(bucket), 244 Key: aws.String(key), 245 } 246 _, err := s3conn.DeleteObject(&input) 247 if err != nil { 248 return fmt.Errorf("Error deleting S3 bucket object: %s", err) 249 } 250 } 251 252 return nil 253 }