Skip to content

Commit

Permalink
Merge branch 'coveord-support-metadata-on-s3-objects'
Browse files Browse the repository at this point in the history
  • Loading branch information
bflad committed Jul 24, 2019
2 parents 49930a9 + 7ff8b11 commit 1954516
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 5 deletions.
45 changes: 40 additions & 5 deletions aws/resource_aws_s3_bucket_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ func resourceAwsS3BucketObject() *schema.Resource {
Optional: true,
},

"metadata": {
Type: schema.TypeMap,
ValidateFunc: validateMetadataIsLowerCase,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"content_type": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -216,6 +223,10 @@ func resourceAwsS3BucketObjectPut(d *schema.ResourceData, meta interface{}) erro
putInput.ContentType = aws.String(v.(string))
}

if v, ok := d.GetOk("metadata"); ok {
putInput.Metadata = stringMapToPointers(v.(map[string]interface{}))
}

if v, ok := d.GetOk("content_encoding"); ok {
putInput.ContentEncoding = aws.String(v.(string))
}
Expand Down Expand Up @@ -290,6 +301,17 @@ func resourceAwsS3BucketObjectRead(d *schema.ResourceData, meta interface{}) err
d.Set("content_encoding", resp.ContentEncoding)
d.Set("content_language", resp.ContentLanguage)
d.Set("content_type", resp.ContentType)
metadata := pointersMapToStringList(resp.Metadata)

// AWS Go SDK capitalizes metadata, this is a workaround. https://github.com/aws/aws-sdk-go/issues/445
for k, v := range metadata {
delete(metadata, k)
metadata[strings.ToLower(k)] = v
}

if err := d.Set("metadata", metadata); err != nil {
return fmt.Errorf("error setting metadata: %s", err)
}
d.Set("version_id", resp.VersionId)
d.Set("server_side_encryption", resp.ServerSideEncryption)
d.Set("website_redirect", resp.WebsiteRedirectLocation)
Expand Down Expand Up @@ -331,17 +353,18 @@ func resourceAwsS3BucketObjectUpdate(d *schema.ResourceData, meta interface{}) e
// Changes to any of these attributes requires creation of a new object version (if bucket is versioned):
for _, key := range []string{
"cache_control",
"content_base64",
"content_disposition",
"content_encoding",
"content_language",
"content_type",
"source",
"content",
"content_base64",
"storage_class",
"server_side_encryption",
"kms_key_id",
"etag",
"kms_key_id",
"metadata",
"server_side_encryption",
"source",
"storage_class",
"website_redirect",
} {
if d.HasChange(key) {
Expand Down Expand Up @@ -415,6 +438,18 @@ func resourceAwsS3BucketObjectDelete(d *schema.ResourceData, meta interface{}) e
return nil
}

func validateMetadataIsLowerCase(v interface{}, k string) (ws []string, errors []error) {
value := v.(map[string]interface{})

for k := range value {
if k != strings.ToLower(k) {
errors = append(errors, fmt.Errorf(
"Metadata must be lowercase only. Offending key: %q", k))
}
}
return
}

func resourceAwsS3BucketObjectCustomizeDiff(d *schema.ResourceDiff, meta interface{}) error {
if d.HasChange("etag") {
d.SetNewComputed("version_id")
Expand Down
57 changes: 57 additions & 0 deletions aws/resource_aws_s3_bucket_object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,45 @@ func TestAccAWSS3BucketObject_acl(t *testing.T) {
})
}

func TestAccAWSS3BucketObject_metadata(t *testing.T) {
rInt := acctest.RandInt()
var obj s3.GetObjectOutput
resourceName := "aws_s3_bucket_object.object"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSS3BucketObjectDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSS3BucketObjectConfig_withMetadata(rInt, "key1", "value1", "key2", "value2"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSS3BucketObjectExists(resourceName, &obj),
resource.TestCheckResourceAttr(resourceName, "metadata.%", "2"),
resource.TestCheckResourceAttr(resourceName, "metadata.key1", "value1"),
resource.TestCheckResourceAttr(resourceName, "metadata.key2", "value2"),
),
},
{
Config: testAccAWSS3BucketObjectConfig_withMetadata(rInt, "key1", "value1updated", "key3", "value3"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSS3BucketObjectExists(resourceName, &obj),
resource.TestCheckResourceAttr(resourceName, "metadata.%", "2"),
resource.TestCheckResourceAttr(resourceName, "metadata.key1", "value1updated"),
resource.TestCheckResourceAttr(resourceName, "metadata.key3", "value3"),
),
},
{
Config: testAccAWSS3BucketObjectConfigEmpty(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSS3BucketObjectExists(resourceName, &obj),
resource.TestCheckResourceAttr(resourceName, "metadata.%", "0"),
),
},
},
})
}

func TestAccAWSS3BucketObject_storageClass(t *testing.T) {
var obj s3.GetObjectOutput
resourceName := "aws_s3_bucket_object.object"
Expand Down Expand Up @@ -1045,3 +1084,21 @@ resource "aws_s3_bucket_object" "object" {
}
`, randInt, key, content)
}

func testAccAWSS3BucketObjectConfig_withMetadata(randInt int, metadataKey1, metadataValue1, metadataKey2, metadataValue2 string) string {
return fmt.Sprintf(`
resource "aws_s3_bucket" "object_bucket" {
bucket = "tf-object-test-bucket-%d"
}
resource "aws_s3_bucket_object" "object" {
bucket = "${aws_s3_bucket.object_bucket.bucket}"
key = "test-key"
metadata = {
%[2]s = %[3]q
%[4]s = %[5]q
}
}
`, randInt, metadataKey1, metadataValue1, metadataKey2, metadataValue2)
}
1 change: 1 addition & 0 deletions website/docs/r/s3_bucket_object.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ This attribute is not compatible with KMS encryption, `kms_key_id` or `server_si
This value is a fully qualified **ARN** of the KMS Key. If using `aws_kms_key`,
use the exported `arn` attribute:
`kms_key_id = "${aws_kms_key.foo.arn}"`
* `metadata` - (Optional) A mapping of keys/values to provision metadata (will be automatically prefixed by `x-amz-meta-`, note that only lowercase label are currently supported by the AWS Go API).
* `tags` - (Optional) A mapping of tags to assign to the object.

If no content is provided through `source`, `content` or `content_base64`, then the object will be empty.
Expand Down

0 comments on commit 1954516

Please sign in to comment.