github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/data_source_aws_s3_bucket_object.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "regexp" 8 "strings" 9 "time" 10 11 "github.com/aws/aws-sdk-go/aws" 12 "github.com/aws/aws-sdk-go/service/s3" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func dataSourceAwsS3BucketObject() *schema.Resource { 17 return &schema.Resource{ 18 Read: dataSourceAwsS3BucketObjectRead, 19 20 Schema: map[string]*schema.Schema{ 21 "body": &schema.Schema{ 22 Type: schema.TypeString, 23 Computed: true, 24 }, 25 "bucket": &schema.Schema{ 26 Type: schema.TypeString, 27 Required: true, 28 }, 29 "cache_control": &schema.Schema{ 30 Type: schema.TypeString, 31 Computed: true, 32 }, 33 "content_disposition": &schema.Schema{ 34 Type: schema.TypeString, 35 Computed: true, 36 }, 37 "content_encoding": &schema.Schema{ 38 Type: schema.TypeString, 39 Computed: true, 40 }, 41 "content_language": &schema.Schema{ 42 Type: schema.TypeString, 43 Computed: true, 44 }, 45 "content_length": &schema.Schema{ 46 Type: schema.TypeInt, 47 Computed: true, 48 }, 49 "content_type": &schema.Schema{ 50 Type: schema.TypeString, 51 Computed: true, 52 }, 53 "etag": &schema.Schema{ 54 Type: schema.TypeString, 55 Computed: true, 56 }, 57 "expiration": &schema.Schema{ 58 Type: schema.TypeString, 59 Computed: true, 60 }, 61 "expires": &schema.Schema{ 62 Type: schema.TypeString, 63 Computed: true, 64 }, 65 "key": &schema.Schema{ 66 Type: schema.TypeString, 67 Required: true, 68 }, 69 "last_modified": &schema.Schema{ 70 Type: schema.TypeString, 71 Computed: true, 72 }, 73 "metadata": &schema.Schema{ 74 Type: schema.TypeMap, 75 Computed: true, 76 }, 77 "range": &schema.Schema{ 78 Type: schema.TypeString, 79 Optional: true, 80 }, 81 "server_side_encryption": &schema.Schema{ 82 Type: schema.TypeString, 83 Computed: true, 84 }, 85 "sse_kms_key_id": &schema.Schema{ 86 Type: schema.TypeString, 87 Computed: true, 88 }, 89 "storage_class": &schema.Schema{ 90 Type: schema.TypeString, 91 Computed: true, 92 }, 93 "version_id": &schema.Schema{ 94 Type: schema.TypeString, 95 Optional: true, 96 Computed: true, 97 }, 98 "website_redirect_location": &schema.Schema{ 99 Type: schema.TypeString, 100 Computed: true, 101 }, 102 103 "tags": tagsSchemaComputed(), 104 }, 105 } 106 } 107 108 func dataSourceAwsS3BucketObjectRead(d *schema.ResourceData, meta interface{}) error { 109 conn := meta.(*AWSClient).s3conn 110 111 bucket := d.Get("bucket").(string) 112 key := d.Get("key").(string) 113 114 input := s3.HeadObjectInput{ 115 Bucket: aws.String(bucket), 116 Key: aws.String(key), 117 } 118 if v, ok := d.GetOk("range"); ok { 119 input.Range = aws.String(v.(string)) 120 } 121 if v, ok := d.GetOk("version_id"); ok { 122 input.VersionId = aws.String(v.(string)) 123 } 124 125 versionText := "" 126 uniqueId := bucket + "/" + key 127 if v, ok := d.GetOk("version_id"); ok { 128 versionText = fmt.Sprintf(" of version %q", v.(string)) 129 uniqueId += "@" + v.(string) 130 } 131 132 log.Printf("[DEBUG] Reading S3 object: %s", input) 133 out, err := conn.HeadObject(&input) 134 if err != nil { 135 return fmt.Errorf("Failed getting S3 object: %s Bucket: %q Object: %q", err, bucket, key) 136 } 137 if out.DeleteMarker != nil && *out.DeleteMarker == true { 138 return fmt.Errorf("Requested S3 object %q%s has been deleted", 139 bucket+key, versionText) 140 } 141 142 log.Printf("[DEBUG] Received S3 object: %s", out) 143 144 d.SetId(uniqueId) 145 146 d.Set("cache_control", out.CacheControl) 147 d.Set("content_disposition", out.ContentDisposition) 148 d.Set("content_encoding", out.ContentEncoding) 149 d.Set("content_language", out.ContentLanguage) 150 d.Set("content_length", out.ContentLength) 151 d.Set("content_type", out.ContentType) 152 // See https://forums.aws.amazon.com/thread.jspa?threadID=44003 153 d.Set("etag", strings.Trim(*out.ETag, `"`)) 154 d.Set("expiration", out.Expiration) 155 d.Set("expires", out.Expires) 156 d.Set("last_modified", out.LastModified.Format(time.RFC1123)) 157 d.Set("metadata", pointersMapToStringList(out.Metadata)) 158 d.Set("server_side_encryption", out.ServerSideEncryption) 159 d.Set("sse_kms_key_id", out.SSEKMSKeyId) 160 d.Set("version_id", out.VersionId) 161 d.Set("website_redirect_location", out.WebsiteRedirectLocation) 162 163 // The "STANDARD" (which is also the default) storage 164 // class when set would not be included in the results. 165 d.Set("storage_class", s3.StorageClassStandard) 166 if out.StorageClass != nil { 167 d.Set("storage_class", out.StorageClass) 168 } 169 170 if isContentTypeAllowed(out.ContentType) { 171 input := s3.GetObjectInput{ 172 Bucket: aws.String(bucket), 173 Key: aws.String(key), 174 } 175 if v, ok := d.GetOk("range"); ok { 176 input.Range = aws.String(v.(string)) 177 } 178 if out.VersionId != nil { 179 input.VersionId = out.VersionId 180 } 181 out, err := conn.GetObject(&input) 182 if err != nil { 183 return fmt.Errorf("Failed getting S3 object: %s", err) 184 } 185 186 buf := new(bytes.Buffer) 187 bytesRead, err := buf.ReadFrom(out.Body) 188 if err != nil { 189 return fmt.Errorf("Failed reading content of S3 object (%s): %s", 190 uniqueId, err) 191 } 192 log.Printf("[INFO] Saving %d bytes from S3 object %s", bytesRead, uniqueId) 193 d.Set("body", buf.String()) 194 } else { 195 contentType := "" 196 if out.ContentType == nil { 197 contentType = "<EMPTY>" 198 } else { 199 contentType = *out.ContentType 200 } 201 202 log.Printf("[INFO] Ignoring body of S3 object %s with Content-Type %q", 203 uniqueId, contentType) 204 } 205 206 tagResp, err := conn.GetObjectTagging( 207 &s3.GetObjectTaggingInput{ 208 Bucket: aws.String(bucket), 209 Key: aws.String(key), 210 }) 211 if err != nil { 212 return err 213 } 214 d.Set("tags", tagsToMapS3(tagResp.TagSet)) 215 216 return nil 217 } 218 219 // This is to prevent potential issues w/ binary files 220 // and generally unprintable characters 221 // See https://github.com/hashicorp/terraform/pull/3858#issuecomment-156856738 222 func isContentTypeAllowed(contentType *string) bool { 223 if contentType == nil { 224 return false 225 } 226 227 allowedContentTypes := []*regexp.Regexp{ 228 regexp.MustCompile("^text/.+"), 229 regexp.MustCompile("^application/json$"), 230 } 231 232 for _, r := range allowedContentTypes { 233 if r.MatchString(*contentType) { 234 return true 235 } 236 } 237 238 return false 239 }