Skip to content

Commit

Permalink
Merge pull request #34612 from hashicorp/f-s3-express
Browse files Browse the repository at this point in the history
Amazon S3 Express
  • Loading branch information
ewbankkit authored Nov 29, 2023
2 parents 12310e4 + 1dd6bc3 commit 4260c30
Show file tree
Hide file tree
Showing 85 changed files with 2,145 additions and 118 deletions.
7 changes: 7 additions & 0 deletions .changelog/34612.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:new-resource
aws_s3_directory_bucket
```

```release-note:new-data-source
aws_s3_directory_buckets
```
2 changes: 1 addition & 1 deletion .github/labeler-issue-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ service/route53resolver:
service/rum:
- '((\*|-)\s*`?|(data|resource)\s+"?)aws_rum_'
service/s3:
- '((\*|-)\s*`?|(data|resource)\s+"?)aws_(canonical_user_id|s3_bucket|s3_object)'
- '((\*|-)\s*`?|(data|resource)\s+"?)aws_(canonical_user_id|s3_bucket|s3_object|s3_directory_bucket)'
service/s3control:
- '((\*|-)\s*`?|(data|resource)\s+"?)aws_(s3_account_|s3control_|s3_access_)'
service/s3outposts:
Expand Down
1 change: 1 addition & 0 deletions .github/labeler-pr-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,7 @@ service/rum:
service/s3:
- 'internal/service/s3/**/*'
- 'website/**/s3_bucket*'
- 'website/**/s3_directory_bucket*'
- 'website/**/s3_object*'
- 'website/**/canonical_user_id*'
service/s3control:
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/vpclattice v1.4.5
github.com/aws/aws-sdk-go-v2/service/workspaces v1.34.2
github.com/aws/aws-sdk-go-v2/service/xray v1.22.5
github.com/aws/smithy-go v1.17.0
github.com/beevik/etree v1.2.0
github.com/davecgh/go-spew v1.1.1
github.com/gertd/go-pluralize v0.2.1
Expand Down Expand Up @@ -154,7 +155,6 @@ require (
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.17.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.3 // indirect
github.com/aws/smithy-go v1.17.0 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/bufbuild/protocompile v0.6.0 // indirect
Expand Down
27 changes: 23 additions & 4 deletions internal/service/s3/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
smithy "github.com/aws/smithy-go"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
tfawserr_sdkv2 "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
Expand All @@ -33,6 +34,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/create"
"github.com/hashicorp/terraform-provider-aws/internal/errs"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
Expand Down Expand Up @@ -95,7 +97,10 @@ func ResourceBucket() *schema.Resource {
Computed: true,
ForceNew: true,
ConflictsWith: []string{"bucket_prefix"},
ValidateFunc: validation.StringLenBetween(0, 63),
ValidateFunc: validation.All(
validation.StringLenBetween(0, 63),
validation.StringDoesNotMatch(directoryBucketNameRegex, `must not be in the format [bucket_name]--[azid]--x-s3. Use the aws_s3_directory_bucket resource to manage S3 Express buckets`),
),
},
"bucket_domain_name": {
Type: schema.TypeString,
Expand All @@ -107,7 +112,9 @@ func ResourceBucket() *schema.Resource {
Computed: true,
ForceNew: true,
ConflictsWith: []string{"bucket"},
ValidateFunc: validation.StringLenBetween(0, 63-id.UniqueIDSuffixLength),
ValidateFunc: validation.All(
validation.StringLenBetween(0, 63-id.UniqueIDSuffixLength),
),
},
"bucket_regional_domain_name": {
Type: schema.TypeString,
Expand Down Expand Up @@ -1428,12 +1435,12 @@ func resourceBucketDelete(ctx context.Context, d *schema.ResourceData, meta inte
return nil
}

func findBucket(ctx context.Context, conn *s3_sdkv2.Client, bucket string) error {
func findBucket(ctx context.Context, conn *s3_sdkv2.Client, bucket string, optFns ...func(*s3_sdkv2.Options)) error {
input := &s3_sdkv2.HeadBucketInput{
Bucket: aws_sdkv2.String(bucket),
}

_, err := conn.HeadBucket(ctx, input)
_, err := conn.HeadBucket(ctx, input, optFns...)

if tfawserr_sdkv2.ErrHTTPStatusCodeEquals(err, http.StatusNotFound) || tfawserr_sdkv2.ErrCodeEquals(err, errCodeNoSuchBucket) {
return &retry.NotFoundError{
Expand All @@ -1442,6 +1449,18 @@ func findBucket(ctx context.Context, conn *s3_sdkv2.Client, bucket string) error
}
}

// FIXME Move to aws-sdk-go-base
// FIXME &smithy.OperationError{ServiceID:"S3", OperationName:"HeadBucket", Err:(*errors.errorString)(0xc00202bb60)}
// FIXME "operation error S3: HeadBucket, get identity: get credentials: operation error S3: CreateSession, https response error StatusCode: 404, RequestID: 0033eada6b00018c17de82890509d9eada65ba39, HostID: F31dBn, NoSuchBucket:"
if operationErr, ok := errs.As[*smithy.OperationError](err); ok {
if strings.Contains(operationErr.Err.Error(), errCodeNoSuchBucket) {
return &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}
}

return err
}

Expand Down
4 changes: 4 additions & 0 deletions internal/service/s3/bucket_accelerate_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ func resourceBucketAccelerateConfigurationCreate(ctx context.Context, d *schema.
return conn.PutBucketAccelerateConfiguration(ctx, input)
}, errCodeNoSuchBucket)

if tfawserr.ErrMessageContains(err, errCodeInvalidArgument, "AccelerateConfiguration is not valid, expected CreateBucketConfiguration") {
err = errDirectoryBucket(err)
}

if err != nil {
return diag.Errorf("creating S3 Bucket (%s) Accelerate Configuration: %s", bucket, err)
}
Expand Down
36 changes: 36 additions & 0 deletions internal/service/s3/bucket_accelerate_configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"testing"

"github.com/YakDriver/regexache"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
Expand Down Expand Up @@ -168,6 +169,24 @@ func TestAccS3BucketAccelerateConfiguration_migrate_withChange(t *testing.T) {
})
}

func TestAccS3BucketAccelerateConfiguration_directoryBucket(t *testing.T) {
ctx := acctest.Context(t)
bucketName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.S3EndpointID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckBucketAccelerateConfigurationDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccBucketAccelerateConfigurationConfig_directoryBucket(bucketName, string(types.BucketAccelerateStatusEnabled)),
ExpectError: regexache.MustCompile(`directory buckets are not supported`),
},
},
})
}

func testAccCheckBucketAccelerateConfigurationDestroy(ctx context.Context) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := acctest.Provider.Meta().(*conns.AWSClient).S3Client(ctx)
Expand Down Expand Up @@ -231,3 +250,20 @@ resource "aws_s3_bucket_accelerate_configuration" "test" {
}
`, bucketName, status)
}

func testAccBucketAccelerateConfigurationConfig_directoryBucket(bucketName, status string) string {
return acctest.ConfigCompose(testAccDirectoryBucketConfig_base(bucketName), fmt.Sprintf(`
resource "aws_s3_directory_bucket" "test" {
bucket = local.bucket
location {
name = local.location_name
}
}
resource "aws_s3_bucket_accelerate_configuration" "test" {
bucket = aws_s3_directory_bucket.test.id
status = %[1]q
}
`, status))
}
5 changes: 5 additions & 0 deletions internal/service/s3/bucket_acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"fmt"
"log"
"net/http"
"strings"

"github.com/YakDriver/regexache"
Expand Down Expand Up @@ -158,6 +159,10 @@ func resourceBucketACLCreate(ctx context.Context, d *schema.ResourceData, meta i
return conn.PutBucketAcl(ctx, input)
}, errCodeNoSuchBucket)

if tfawserr.ErrHTTPStatusCodeEquals(err, http.StatusNotImplemented) {
err = errDirectoryBucket(err)
}

if err != nil {
return diag.Errorf("creating S3 Bucket (%s) ACL: %s", bucket, err)
}
Expand Down
94 changes: 64 additions & 30 deletions internal/service/s3/bucket_acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/YakDriver/regexache"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/aws/aws-sdk-go/service/s3"
sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
Expand Down Expand Up @@ -87,22 +86,22 @@ func TestBucketACLParseResourceID(t *testing.T) {
},
{
TestName: "valid ID with bucket and acl",
InputID: tfs3.BucketACLCreateResourceID("example", "", s3.BucketCannedACLPrivate),
ExpectedACL: s3.BucketCannedACLPrivate,
InputID: tfs3.BucketACLCreateResourceID("example", "", string(types.BucketCannedACLPrivate)),
ExpectedACL: string(types.BucketCannedACLPrivate),
ExpectedBucket: "example",
ExpectedBucketOwner: "",
},
{
TestName: "valid ID with bucket and acl that has hyphens",
InputID: tfs3.BucketACLCreateResourceID("example", "", s3.BucketCannedACLPublicReadWrite),
ExpectedACL: s3.BucketCannedACLPublicReadWrite,
InputID: tfs3.BucketACLCreateResourceID("example", "", string(types.BucketCannedACLPublicReadWrite)),
ExpectedACL: string(types.BucketCannedACLPublicReadWrite),
ExpectedBucket: "example",
ExpectedBucketOwner: "",
},
{
TestName: "valid ID with bucket that has dot, hyphen, and number and acl that has hyphens",
InputID: tfs3.BucketACLCreateResourceID("my-example.bucket.4000", "", s3.BucketCannedACLPublicReadWrite),
ExpectedACL: s3.BucketCannedACLPublicReadWrite,
InputID: tfs3.BucketACLCreateResourceID("my-example.bucket.4000", "", string(types.BucketCannedACLPublicReadWrite)),
ExpectedACL: string(types.BucketCannedACLPublicReadWrite),
ExpectedBucket: "my-example.bucket.4000",
ExpectedBucketOwner: "",
},
Expand All @@ -122,22 +121,22 @@ func TestBucketACLParseResourceID(t *testing.T) {
},
{
TestName: "valid ID with bucket, bucket owner, and acl",
InputID: tfs3.BucketACLCreateResourceID("example", "123456789012", s3.BucketCannedACLPrivate),
ExpectedACL: s3.BucketCannedACLPrivate,
InputID: tfs3.BucketACLCreateResourceID("example", "123456789012", string(types.BucketCannedACLPrivate)),
ExpectedACL: string(types.BucketCannedACLPrivate),
ExpectedBucket: "example",
ExpectedBucketOwner: "123456789012",
},
{
TestName: "valid ID with bucket, bucket owner, and acl that has hyphens",
InputID: tfs3.BucketACLCreateResourceID("example", "123456789012", s3.BucketCannedACLPublicReadWrite),
ExpectedACL: s3.BucketCannedACLPublicReadWrite,
InputID: tfs3.BucketACLCreateResourceID("example", "123456789012", string(types.BucketCannedACLPublicReadWrite)),
ExpectedACL: string(types.BucketCannedACLPublicReadWrite),
ExpectedBucket: "example",
ExpectedBucketOwner: "123456789012",
},
{
TestName: "valid ID with bucket that has dot, hyphen, and numbers, bucket owner, and acl that has hyphens",
InputID: tfs3.BucketACLCreateResourceID("my-example.bucket.4000", "123456789012", s3.BucketCannedACLPublicReadWrite),
ExpectedACL: s3.BucketCannedACLPublicReadWrite,
InputID: tfs3.BucketACLCreateResourceID("my-example.bucket.4000", "123456789012", string(types.BucketCannedACLPublicReadWrite)),
ExpectedACL: string(types.BucketCannedACLPublicReadWrite),
ExpectedBucket: "my-example.bucket.4000",
ExpectedBucketOwner: "123456789012",
},
Expand Down Expand Up @@ -171,22 +170,22 @@ func TestBucketACLParseResourceID(t *testing.T) {
},
{
TestName: "valid ID with bucket (pre-2018, us-east-1) and acl", //lintignore:AWSAT003
InputID: tfs3.BucketACLCreateResourceID("Example", "", s3.BucketCannedACLPrivate),
ExpectedACL: s3.BucketCannedACLPrivate,
InputID: tfs3.BucketACLCreateResourceID("Example", "", string(types.BucketCannedACLPrivate)),
ExpectedACL: string(types.BucketCannedACLPrivate),
ExpectedBucket: "Example",
ExpectedBucketOwner: "",
},
{
TestName: "valid ID with bucket (pre-2018, us-east-1) and acl that has underscores", //lintignore:AWSAT003
InputID: tfs3.BucketACLCreateResourceID("My_Example_Bucket", "", s3.BucketCannedACLPublicReadWrite),
ExpectedACL: s3.BucketCannedACLPublicReadWrite,
InputID: tfs3.BucketACLCreateResourceID("My_Example_Bucket", "", string(types.BucketCannedACLPublicReadWrite)),
ExpectedACL: string(types.BucketCannedACLPublicReadWrite),
ExpectedBucket: "My_Example_Bucket",
ExpectedBucketOwner: "",
},
{
TestName: "valid ID with bucket (pre-2018, us-east-1) that has underscore, dot, hyphen, and number and acl that has hyphens", //lintignore:AWSAT003
InputID: tfs3.BucketACLCreateResourceID("My_Example-Bucket.4000", "", s3.BucketCannedACLPublicReadWrite),
ExpectedACL: s3.BucketCannedACLPublicReadWrite,
InputID: tfs3.BucketACLCreateResourceID("My_Example-Bucket.4000", "", string(types.BucketCannedACLPublicReadWrite)),
ExpectedACL: string(types.BucketCannedACLPublicReadWrite),
ExpectedBucket: "My_Example-Bucket.4000",
ExpectedBucketOwner: "",
},
Expand All @@ -206,22 +205,22 @@ func TestBucketACLParseResourceID(t *testing.T) {
},
{
TestName: "valid ID with bucket (pre-2018, us-east-1), bucket owner, and acl", //lintignore:AWSAT003
InputID: tfs3.BucketACLCreateResourceID("Example", "123456789012", s3.BucketCannedACLPrivate),
ExpectedACL: s3.BucketCannedACLPrivate,
InputID: tfs3.BucketACLCreateResourceID("Example", "123456789012", string(types.BucketCannedACLPrivate)),
ExpectedACL: string(types.BucketCannedACLPrivate),
ExpectedBucket: "Example",
ExpectedBucketOwner: "123456789012",
},
{
TestName: "valid ID with bucket (pre-2018, us-east-1), bucket owner, and acl that has hyphens", //lintignore:AWSAT003
InputID: tfs3.BucketACLCreateResourceID("Example", "123456789012", s3.BucketCannedACLPublicReadWrite),
ExpectedACL: s3.BucketCannedACLPublicReadWrite,
InputID: tfs3.BucketACLCreateResourceID("Example", "123456789012", string(types.BucketCannedACLPublicReadWrite)),
ExpectedACL: string(types.BucketCannedACLPublicReadWrite),
ExpectedBucket: "Example",
ExpectedBucketOwner: "123456789012",
},
{
TestName: "valid ID with bucket (pre-2018, us-east-1) that has underscore, dot, hyphen, and numbers, bucket owner, and acl that has hyphens", //lintignore:AWSAT003
InputID: tfs3.BucketACLCreateResourceID("My_Example-bucket.4000", "123456789012", s3.BucketCannedACLPublicReadWrite),
ExpectedACL: s3.BucketCannedACLPublicReadWrite,
InputID: tfs3.BucketACLCreateResourceID("My_Example-bucket.4000", "123456789012", string(types.BucketCannedACLPublicReadWrite)),
ExpectedACL: string(types.BucketCannedACLPublicReadWrite),
ExpectedBucket: "My_Example-bucket.4000",
ExpectedBucketOwner: "123456789012",
},
Expand Down Expand Up @@ -269,16 +268,16 @@ func TestAccS3BucketACL_basic(t *testing.T) {
CheckDestroy: acctest.CheckDestroyNoop,
Steps: []resource.TestStep{
{
Config: testAccBucketACLConfig_basic(bucketName, s3.BucketCannedACLPrivate),
Config: testAccBucketACLConfig_basic(bucketName, string(types.BucketCannedACLPrivate)),
Check: resource.ComposeTestCheckFunc(
testAccCheckBucketACLExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "acl", s3.BucketCannedACLPrivate),
resource.TestCheckResourceAttr(resourceName, "acl", string(types.BucketCannedACLPrivate)),
resource.TestCheckResourceAttr(resourceName, "access_control_policy.#", "1"),
resource.TestCheckResourceAttr(resourceName, "access_control_policy.0.owner.#", "1"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "access_control_policy.0.grant.*", map[string]string{
"grantee.#": "1",
"grantee.0.type": s3.TypeCanonicalUser,
"permission": s3.PermissionFullControl,
"grantee.0.type": string(types.TypeCanonicalUser),
"permission": string(types.PermissionFullControl),
}),
),
},
Expand All @@ -303,7 +302,7 @@ func TestAccS3BucketACL_disappears(t *testing.T) {
CheckDestroy: acctest.CheckDestroyNoop,
Steps: []resource.TestStep{
{
Config: testAccBucketACLConfig_basic(bucketName, s3.BucketCannedACLPrivate),
Config: testAccBucketACLConfig_basic(bucketName, string(types.BucketCannedACLPrivate)),
Check: resource.ComposeTestCheckFunc(
testAccCheckBucketACLExists(ctx, resourceName),
// Bucket ACL cannot be destroyed, but we can verify Bucket deletion
Expand Down Expand Up @@ -599,6 +598,24 @@ func TestAccS3BucketACL_grantToACL(t *testing.T) {
})
}

func TestAccS3BucketACL_directoryBucket(t *testing.T) {
ctx := acctest.Context(t)
bucketName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.S3EndpointID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: acctest.CheckDestroyNoop,
Steps: []resource.TestStep{
{
Config: testAccBucketACLConfig_directoryBucket(bucketName, string(types.BucketCannedACLPrivate)),
ExpectError: regexache.MustCompile(`directory buckets are not supported`),
},
},
})
}

func testAccCheckBucketACLExists(ctx context.Context, n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand Down Expand Up @@ -810,3 +827,20 @@ resource "aws_s3_bucket_acl" "test" {
}
`, rName)
}

func testAccBucketACLConfig_directoryBucket(rName, acl string) string {
return acctest.ConfigCompose(testAccDirectoryBucketConfig_base(rName), fmt.Sprintf(`
resource "aws_s3_directory_bucket" "test" {
bucket = local.bucket
location {
name = local.location_name
}
}
resource "aws_s3_bucket_acl" "test" {
bucket = aws_s3_directory_bucket.test.id
acl = %[1]q
}
`, acl))
}
Loading

0 comments on commit 4260c30

Please sign in to comment.