github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/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 } 104 } 105 106 func dataSourceAwsS3BucketObjectRead(d *schema.ResourceData, meta interface{}) error { 107 conn := meta.(*AWSClient).s3conn 108 109 bucket := d.Get("bucket").(string) 110 key := d.Get("key").(string) 111 112 input := s3.HeadObjectInput{ 113 Bucket: aws.String(bucket), 114 Key: aws.String(key), 115 } 116 if v, ok := d.GetOk("range"); ok { 117 input.Range = aws.String(v.(string)) 118 } 119 if v, ok := d.GetOk("version_id"); ok { 120 input.VersionId = aws.String(v.(string)) 121 } 122 123 versionText := "" 124 uniqueId := bucket + "/" + key 125 if v, ok := d.GetOk("version_id"); ok { 126 versionText = fmt.Sprintf(" of version %q", v.(string)) 127 uniqueId += "@" + v.(string) 128 } 129 130 log.Printf("[DEBUG] Reading S3 object: %s", input) 131 out, err := conn.HeadObject(&input) 132 if err != nil { 133 return fmt.Errorf("Failed getting S3 object: %s", err) 134 } 135 if out.DeleteMarker != nil && *out.DeleteMarker == true { 136 return fmt.Errorf("Requested S3 object %q%s has been deleted", 137 bucket+key, versionText) 138 } 139 140 log.Printf("[DEBUG] Received S3 object: %s", out) 141 142 d.SetId(uniqueId) 143 144 d.Set("cache_control", out.CacheControl) 145 d.Set("content_disposition", out.ContentDisposition) 146 d.Set("content_encoding", out.ContentEncoding) 147 d.Set("content_language", out.ContentLanguage) 148 d.Set("content_length", out.ContentLength) 149 d.Set("content_type", out.ContentType) 150 // See https://forums.aws.amazon.com/thread.jspa?threadID=44003 151 d.Set("etag", strings.Trim(*out.ETag, `"`)) 152 d.Set("expiration", out.Expiration) 153 d.Set("expires", out.Expires) 154 d.Set("last_modified", out.LastModified.Format(time.RFC1123)) 155 d.Set("metadata", pointersMapToStringList(out.Metadata)) 156 d.Set("server_side_encryption", out.ServerSideEncryption) 157 d.Set("sse_kms_key_id", out.SSEKMSKeyId) 158 d.Set("version_id", out.VersionId) 159 d.Set("website_redirect_location", out.WebsiteRedirectLocation) 160 161 // The "STANDARD" (which is also the default) storage 162 // class when set would not be included in the results. 163 d.Set("storage_class", s3.StorageClassStandard) 164 if out.StorageClass != nil { 165 d.Set("storage_class", out.StorageClass) 166 } 167 168 if isContentTypeAllowed(out.ContentType) { 169 input := s3.GetObjectInput{ 170 Bucket: aws.String(bucket), 171 Key: aws.String(key), 172 } 173 if v, ok := d.GetOk("range"); ok { 174 input.Range = aws.String(v.(string)) 175 } 176 if out.VersionId != nil { 177 input.VersionId = out.VersionId 178 } 179 out, err := conn.GetObject(&input) 180 if err != nil { 181 return fmt.Errorf("Failed getting S3 object: %s", err) 182 } 183 184 buf := new(bytes.Buffer) 185 bytesRead, err := buf.ReadFrom(out.Body) 186 if err != nil { 187 return fmt.Errorf("Failed reading content of S3 object (%s): %s", 188 uniqueId, err) 189 } 190 log.Printf("[INFO] Saving %d bytes from S3 object %s", bytesRead, uniqueId) 191 d.Set("body", buf.String()) 192 } else { 193 contentType := "" 194 if out.ContentType == nil { 195 contentType = "<EMPTY>" 196 } else { 197 contentType = *out.ContentType 198 } 199 200 log.Printf("[INFO] Ignoring body of S3 object %s with Content-Type %q", 201 uniqueId, contentType) 202 } 203 204 return nil 205 } 206 207 // This is to prevent potential issues w/ binary files 208 // and generally unprintable characters 209 // See https://github.com/hashicorp/terraform/pull/3858#issuecomment-156856738 210 func isContentTypeAllowed(contentType *string) bool { 211 if contentType == nil { 212 return false 213 } 214 215 allowedContentTypes := []*regexp.Regexp{ 216 regexp.MustCompile("^text/.+"), 217 regexp.MustCompile("^application/json$"), 218 } 219 220 for _, r := range allowedContentTypes { 221 if r.MatchString(*contentType) { 222 return true 223 } 224 } 225 226 return false 227 }