From 334286c48af1a53cc7463512a0c2119ab83110fa Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Thu, 13 Jun 2024 22:46:45 +0100 Subject: [PATCH 01/60] ecs: Migrate to AWS SDK v2 --- internal/conns/awsclient_gen.go | 5 ----- names/data/names_data.hcl | 6 +----- names/names.go | 1 + 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/internal/conns/awsclient_gen.go b/internal/conns/awsclient_gen.go index fa56803e051..d852f1d9696 100644 --- a/internal/conns/awsclient_gen.go +++ b/internal/conns/awsclient_gen.go @@ -198,7 +198,6 @@ import ( directconnect_sdkv1 "github.com/aws/aws-sdk-go/service/directconnect" directoryservice_sdkv1 "github.com/aws/aws-sdk-go/service/directoryservice" ec2_sdkv1 "github.com/aws/aws-sdk-go/service/ec2" - ecs_sdkv1 "github.com/aws/aws-sdk-go/service/ecs" efs_sdkv1 "github.com/aws/aws-sdk-go/service/efs" elasticache_sdkv1 "github.com/aws/aws-sdk-go/service/elasticache" elasticsearchservice_sdkv1 "github.com/aws/aws-sdk-go/service/elasticsearchservice" @@ -599,10 +598,6 @@ func (c *AWSClient) ECRPublicClient(ctx context.Context) *ecrpublic_sdkv2.Client return errs.Must(client[*ecrpublic_sdkv2.Client](ctx, c, names.ECRPublic, make(map[string]any))) } -func (c *AWSClient) ECSConn(ctx context.Context) *ecs_sdkv1.ECS { - return errs.Must(conn[*ecs_sdkv1.ECS](ctx, c, names.ECS, make(map[string]any))) -} - func (c *AWSClient) ECSClient(ctx context.Context) *ecs_sdkv2.Client { return errs.Must(client[*ecs_sdkv2.Client](ctx, c, names.ECS, make(map[string]any))) } diff --git a/names/data/names_data.hcl b/names/data/names_data.hcl index 0168a7dc585..0b1de6c4eef 100644 --- a/names/data/names_data.hcl +++ b/names/data/names_data.hcl @@ -3198,7 +3198,7 @@ service "ecs" { sdk { id = "ECS" - client_version = [1, 2] + client_version = [2] } names { @@ -3206,10 +3206,6 @@ service "ecs" { human_friendly = "ECS (Elastic Container)" } - client { - go_v1_client_typename = "ECS" - } - endpoint_info { endpoint_api_call = "ListClusters" } diff --git a/names/names.go b/names/names.go index 39923e10b49..6356e53266d 100644 --- a/names/names.go +++ b/names/names.go @@ -62,6 +62,7 @@ const ( DevOpsGuruEndpointID = "devops-guru" DLMEndpointID = "dlm" ECREndpointID = "api.ecr" + ECSEndpointID = "ecs" EKSEndpointID = "eks" EMREndpointID = "elasticmapreduce" EventsEndpointID = "events" From a91da09a995fc7a527f4ee6a4a560dbd980bf706 Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Thu, 13 Jun 2024 23:11:23 +0100 Subject: [PATCH 02/60] Run make gen --- internal/service/ecs/generate.go | 7 +- internal/service/ecs/list_pages_gen.go | 13 +- .../service/ecs/service_endpoints_gen_test.go | 43 +---- internal/service/ecs/service_package_gen.go | 25 --- internal/service/ecs/tag_gen.go | 6 +- internal/service/ecs/tags_gen.go | 60 ++++--- internal/service/ecs/tagsv2_gen.go | 152 ------------------ 7 files changed, 59 insertions(+), 247 deletions(-) delete mode 100644 internal/service/ecs/tagsv2_gen.go diff --git a/internal/service/ecs/generate.go b/internal/service/ecs/generate.go index e816460f34d..4e0939ee4ec 100644 --- a/internal/service/ecs/generate.go +++ b/internal/service/ecs/generate.go @@ -1,10 +1,9 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//go:generate go run ../../generate/listpages/main.go -ListOps=DescribeCapacityProviders -//go:generate go run ../../generate/tagresource/main.go -UpdateTagsFunc=updateTagsV2 -//go:generate go run ../../generate/tags/main.go -ListTags -ServiceTagsSlice -UpdateTags -CreateTags -ParentNotFoundErrCode=InvalidParameterException "-ParentNotFoundErrMsg=The specified cluster is inactive. Specify an active cluster and try again." -//go:generate go run ../../generate/tags/main.go -AWSSDKVersion=2 -GetTag -ListTags -ServiceTagsSlice -TagsFunc=TagsV2 -KeyValueTagsFunc=keyValueTagsV2 -GetTagsInFunc=getTagsInV2 -SetTagsOutFunc=setTagsOutV2 -ListTagsFunc=listTagsV2 -UpdateTagsFunc=updateTagsV2 -UpdateTags -ParentNotFoundErrCode=InvalidParameterException "-ParentNotFoundErrMsg=The specified cluster is inactive. Specify an active cluster and try again." -- tagsv2_gen.go +//go:generate go run ../../generate/listpages/main.go -AWSSDKVersion=2 -ListOps=DescribeCapacityProviders +//go:generate go run ../../generate/tagresource/main.go +//go:generate go run ../../generate/tags/main.go -AWSSDKVersion=2 -GetTag -ListTags -ServiceTagsSlice -UpdateTags -CreateTags -ParentNotFoundErrCode=InvalidParameterException "-ParentNotFoundErrMsg=The specified cluster is inactive. Specify an active cluster and try again." //go:generate go run ../../generate/servicepackage/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. diff --git a/internal/service/ecs/list_pages_gen.go b/internal/service/ecs/list_pages_gen.go index 0d3b567d7fe..378d6b93958 100644 --- a/internal/service/ecs/list_pages_gen.go +++ b/internal/service/ecs/list_pages_gen.go @@ -1,23 +1,22 @@ -// Code generated by "internal/generate/listpages/main.go -ListOps=DescribeCapacityProviders"; DO NOT EDIT. +// Code generated by "internal/generate/listpages/main.go -AWSSDKVersion=2 -ListOps=DescribeCapacityProviders"; DO NOT EDIT. package ecs import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/aws/aws-sdk-go/service/ecs/ecsiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" ) -func describeCapacityProvidersPages(ctx context.Context, conn ecsiface.ECSAPI, input *ecs.DescribeCapacityProvidersInput, fn func(*ecs.DescribeCapacityProvidersOutput, bool) bool) error { +func describeCapacityProvidersPages(ctx context.Context, conn *ecs.Client, input *ecs.DescribeCapacityProvidersInput, fn func(*ecs.DescribeCapacityProvidersOutput, bool) bool) error { for { - output, err := conn.DescribeCapacityProvidersWithContext(ctx, input) + output, err := conn.DescribeCapacityProviders(ctx, input) if err != nil { return err } - lastPage := aws.StringValue(output.NextToken) == "" + lastPage := aws.ToString(output.NextToken) == "" if !fn(output, lastPage) || lastPage { break } diff --git a/internal/service/ecs/service_endpoints_gen_test.go b/internal/service/ecs/service_endpoints_gen_test.go index 2e7e6ba0262..47bb80cc865 100644 --- a/internal/service/ecs/service_endpoints_gen_test.go +++ b/internal/service/ecs/service_endpoints_gen_test.go @@ -16,8 +16,6 @@ import ( aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" ecs_sdkv2 "github.com/aws/aws-sdk-go-v2/service/ecs" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - ecs_sdkv1 "github.com/aws/aws-sdk-go/service/ecs" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" "github.com/google/go-cmp/cmp" @@ -234,25 +232,13 @@ func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.S }, } - t.Run("v1", func(t *testing.T) { - for name, testcase := range testcases { //nolint:paralleltest // uses t.Setenv - testcase := testcase + for name, testcase := range testcases { //nolint:paralleltest // uses t.Setenv + testcase := testcase - t.Run(name, func(t *testing.T) { - testEndpointCase(t, providerRegion, testcase, callServiceV1) - }) - } - }) - - t.Run("v2", func(t *testing.T) { - for name, testcase := range testcases { //nolint:paralleltest // uses t.Setenv - testcase := testcase - - t.Run(name, func(t *testing.T) { - testEndpointCase(t, providerRegion, testcase, callServiceV2) - }) - } - }) + t.Run(name, func(t *testing.T) { + testEndpointCase(t, providerRegion, testcase, callService) + }) + } } func defaultEndpoint(region string) string { @@ -290,7 +276,7 @@ func defaultFIPSEndpoint(region string) string { return ep.URI.String() } -func callServiceV2(ctx context.Context, t *testing.T, meta *conns.AWSClient) apiCallParams { +func callService(ctx context.Context, t *testing.T, meta *conns.AWSClient) apiCallParams { t.Helper() client := meta.ECSClient(ctx) @@ -315,21 +301,6 @@ func callServiceV2(ctx context.Context, t *testing.T, meta *conns.AWSClient) api return result } -func callServiceV1(ctx context.Context, t *testing.T, meta *conns.AWSClient) apiCallParams { - t.Helper() - - client := meta.ECSConn(ctx) - - req, _ := client.ListClustersRequest(&ecs_sdkv1.ListClustersInput{}) - - req.HTTPRequest.URL.Path = "/" - - return apiCallParams{ - endpoint: req.HTTPRequest.URL.String(), - region: aws_sdkv1.StringValue(client.Config.Region), - } -} - func withNoConfig(_ *caseSetup) { // no-op } diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index 1166a226303..1cf25946849 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -7,10 +7,6 @@ import ( aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" ecs_sdkv2 "github.com/aws/aws-sdk-go-v2/service/ecs" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - endpoints_sdkv1 "github.com/aws/aws-sdk-go/aws/endpoints" - session_sdkv1 "github.com/aws/aws-sdk-go/aws/session" - ecs_sdkv1 "github.com/aws/aws-sdk-go/service/ecs" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/types" @@ -115,27 +111,6 @@ func (p *servicePackage) ServicePackageName() string { return names.ECS } -// NewConn returns a new AWS SDK for Go v1 client for this service package's AWS API. -func (p *servicePackage) NewConn(ctx context.Context, config map[string]any) (*ecs_sdkv1.ECS, error) { - sess := config[names.AttrSession].(*session_sdkv1.Session) - - cfg := aws_sdkv1.Config{} - - if endpoint := config[names.AttrEndpoint].(string); endpoint != "" { - tflog.Debug(ctx, "setting endpoint", map[string]any{ - "tf_aws.endpoint": endpoint, - }) - cfg.Endpoint = aws_sdkv1.String(endpoint) - - if sess.Config.UseFIPSEndpoint == endpoints_sdkv1.FIPSEndpointStateEnabled { - tflog.Debug(ctx, "endpoint set, ignoring UseFIPSEndpoint setting") - cfg.UseFIPSEndpoint = endpoints_sdkv1.FIPSEndpointStateDisabled - } - } - - return ecs_sdkv1.New(sess.Copy(&cfg)), nil -} - // NewClient returns a new AWS SDK for Go v2 client for this service package's AWS API. func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (*ecs_sdkv2.Client, error) { cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config)) diff --git a/internal/service/ecs/tag_gen.go b/internal/service/ecs/tag_gen.go index 99a6709e1b4..75a30d04b85 100644 --- a/internal/service/ecs/tag_gen.go +++ b/internal/service/ecs/tag_gen.go @@ -54,7 +54,7 @@ func resourceTagCreate(ctx context.Context, d *schema.ResourceData, meta interfa key := d.Get(names.AttrKey).(string) value := d.Get(names.AttrValue).(string) - if err := updateTagsV2(ctx, conn, identifier, nil, map[string]string{key: value}); err != nil { + if err := updateTags(ctx, conn, identifier, nil, map[string]string{key: value}); err != nil { return sdkdiag.AppendErrorf(diags, "creating %s resource (%s) tag (%s): %s", names.ECS, identifier, key, err) } @@ -100,7 +100,7 @@ func resourceTagUpdate(ctx context.Context, d *schema.ResourceData, meta interfa return sdkdiag.AppendFromErr(diags, err) } - if err := updateTagsV2(ctx, conn, identifier, nil, map[string]string{key: d.Get(names.AttrValue).(string)}); err != nil { + if err := updateTags(ctx, conn, identifier, nil, map[string]string{key: d.Get(names.AttrValue).(string)}); err != nil { return sdkdiag.AppendErrorf(diags, "updating %s resource (%s) tag (%s): %s", names.ECS, identifier, key, err) } @@ -116,7 +116,7 @@ func resourceTagDelete(ctx context.Context, d *schema.ResourceData, meta interfa return sdkdiag.AppendFromErr(diags, err) } - if err := updateTagsV2(ctx, conn, identifier, map[string]string{key: d.Get(names.AttrValue).(string)}, nil); err != nil { + if err := updateTags(ctx, conn, identifier, map[string]string{key: d.Get(names.AttrValue).(string)}, nil); err != nil { return sdkdiag.AppendErrorf(diags, "deleting %s resource (%s) tag (%s): %s", names.ECS, identifier, key, err) } diff --git a/internal/service/ecs/tags_gen.go b/internal/service/ecs/tags_gen.go index ea4f4f2b6bb..f9729dd1450 100644 --- a/internal/service/ecs/tags_gen.go +++ b/internal/service/ecs/tags_gen.go @@ -5,28 +5,48 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/aws/aws-sdk-go/service/ecs/ecsiface" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/logging" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/types/option" "github.com/hashicorp/terraform-provider-aws/names" ) +// findTag fetches an individual ecs service tag for a resource. +// Returns whether the key value and any errors. A NotFoundError is used to signal that no value was found. +// This function will optimise the handling over listTags, if possible. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func findTag(ctx context.Context, conn *ecs.Client, identifier, key string, optFns ...func(*ecs.Options)) (*string, error) { + listTags, err := listTags(ctx, conn, identifier, optFns...) + + if err != nil { + return nil, err + } + + if !listTags.KeyExists(key) { + return nil, tfresource.NewEmptyResultError(nil) + } + + return listTags.KeyValue(key), nil +} + // listTags lists ecs service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. -func listTags(ctx context.Context, conn ecsiface.ECSAPI, identifier string) (tftags.KeyValueTags, error) { +func listTags(ctx context.Context, conn *ecs.Client, identifier string, optFns ...func(*ecs.Options)) (tftags.KeyValueTags, error) { input := &ecs.ListTagsForResourceInput{ ResourceArn: aws.String(identifier), } - output, err := conn.ListTagsForResourceWithContext(ctx, input) + output, err := conn.ListTagsForResource(ctx, input, optFns...) if tfawserr.ErrMessageContains(err, "InvalidParameterException", "The specified cluster is inactive. Specify an active cluster and try again.") { return nil, &retry.NotFoundError{ @@ -45,7 +65,7 @@ func listTags(ctx context.Context, conn ecsiface.ECSAPI, identifier string) (tft // ListTags lists ecs service tags and set them in Context. // It is called from outside this package. func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier string) error { - tags, err := listTags(ctx, meta.(*conns.AWSClient).ECSConn(ctx), identifier) + tags, err := listTags(ctx, meta.(*conns.AWSClient).ECSClient(ctx), identifier) if err != nil { return err @@ -61,11 +81,11 @@ func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier stri // []*SERVICE.Tag handling // Tags returns ecs service tags. -func Tags(tags tftags.KeyValueTags) []*ecs.Tag { - result := make([]*ecs.Tag, 0, len(tags)) +func Tags(tags tftags.KeyValueTags) []awstypes.Tag { + result := make([]awstypes.Tag, 0, len(tags)) for k, v := range tags.Map() { - tag := &ecs.Tag{ + tag := awstypes.Tag{ Key: aws.String(k), Value: aws.String(v), } @@ -77,11 +97,11 @@ func Tags(tags tftags.KeyValueTags) []*ecs.Tag { } // KeyValueTags creates tftags.KeyValueTags from ecs service tags. -func KeyValueTags(ctx context.Context, tags []*ecs.Tag) tftags.KeyValueTags { +func KeyValueTags(ctx context.Context, tags []awstypes.Tag) tftags.KeyValueTags { m := make(map[string]*string, len(tags)) for _, tag := range tags { - m[aws.StringValue(tag.Key)] = tag.Value + m[aws.ToString(tag.Key)] = tag.Value } return tftags.New(ctx, m) @@ -89,7 +109,7 @@ func KeyValueTags(ctx context.Context, tags []*ecs.Tag) tftags.KeyValueTags { // getTagsIn returns ecs service tags from Context. // nil is returned if there are no input tags. -func getTagsIn(ctx context.Context) []*ecs.Tag { +func getTagsIn(ctx context.Context) []awstypes.Tag { if inContext, ok := tftags.FromContext(ctx); ok { if tags := Tags(inContext.TagsIn.UnwrapOrDefault()); len(tags) > 0 { return tags @@ -100,14 +120,14 @@ func getTagsIn(ctx context.Context) []*ecs.Tag { } // setTagsOut sets ecs service tags in Context. -func setTagsOut(ctx context.Context, tags []*ecs.Tag) { +func setTagsOut(ctx context.Context, tags []awstypes.Tag) { if inContext, ok := tftags.FromContext(ctx); ok { inContext.TagsOut = option.Some(KeyValueTags(ctx, tags)) } } // createTags creates ecs service tags for new resources. -func createTags(ctx context.Context, conn ecsiface.ECSAPI, identifier string, tags []*ecs.Tag) error { +func createTags(ctx context.Context, conn *ecs.Client, identifier string, tags []awstypes.Tag) error { if len(tags) == 0 { return nil } @@ -118,7 +138,7 @@ func createTags(ctx context.Context, conn ecsiface.ECSAPI, identifier string, ta // updateTags updates ecs service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. -func updateTags(ctx context.Context, conn ecsiface.ECSAPI, identifier string, oldTagsMap, newTagsMap any) error { +func updateTags(ctx context.Context, conn *ecs.Client, identifier string, oldTagsMap, newTagsMap any, optFns ...func(*ecs.Options)) error { oldTags := tftags.New(ctx, oldTagsMap) newTags := tftags.New(ctx, newTagsMap) @@ -129,10 +149,10 @@ func updateTags(ctx context.Context, conn ecsiface.ECSAPI, identifier string, ol if len(removedTags) > 0 { input := &ecs.UntagResourceInput{ ResourceArn: aws.String(identifier), - TagKeys: aws.StringSlice(removedTags.Keys()), + TagKeys: removedTags.Keys(), } - _, err := conn.UntagResourceWithContext(ctx, input) + _, err := conn.UntagResource(ctx, input, optFns...) if err != nil { return fmt.Errorf("untagging resource (%s): %w", identifier, err) @@ -147,7 +167,7 @@ func updateTags(ctx context.Context, conn ecsiface.ECSAPI, identifier string, ol Tags: Tags(updatedTags), } - _, err := conn.TagResourceWithContext(ctx, input) + _, err := conn.TagResource(ctx, input, optFns...) if err != nil { return fmt.Errorf("tagging resource (%s): %w", identifier, err) @@ -160,5 +180,5 @@ func updateTags(ctx context.Context, conn ecsiface.ECSAPI, identifier string, ol // UpdateTags updates ecs service tags. // It is called from outside this package. func (p *servicePackage) UpdateTags(ctx context.Context, meta any, identifier string, oldTags, newTags any) error { - return updateTags(ctx, meta.(*conns.AWSClient).ECSConn(ctx), identifier, oldTags, newTags) + return updateTags(ctx, meta.(*conns.AWSClient).ECSClient(ctx), identifier, oldTags, newTags) } diff --git a/internal/service/ecs/tagsv2_gen.go b/internal/service/ecs/tagsv2_gen.go deleted file mode 100644 index 81c3a6a55da..00000000000 --- a/internal/service/ecs/tagsv2_gen.go +++ /dev/null @@ -1,152 +0,0 @@ -// Code generated by internal/generate/tags/main.go; DO NOT EDIT. -package ecs - -import ( - "context" - "fmt" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ecs" - awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" - "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/logging" - tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" - "github.com/hashicorp/terraform-provider-aws/internal/types/option" - "github.com/hashicorp/terraform-provider-aws/names" -) - -// findTag fetches an individual ecs service tag for a resource. -// Returns whether the key value and any errors. A NotFoundError is used to signal that no value was found. -// This function will optimise the handling over listTagsV2, if possible. -// The identifier is typically the Amazon Resource Name (ARN), although -// it may also be a different identifier depending on the service. -func findTag(ctx context.Context, conn *ecs.Client, identifier, key string, optFns ...func(*ecs.Options)) (*string, error) { - listTags, err := listTagsV2(ctx, conn, identifier, optFns...) - - if err != nil { - return nil, err - } - - if !listTags.KeyExists(key) { - return nil, tfresource.NewEmptyResultError(nil) - } - - return listTags.KeyValue(key), nil -} - -// listTagsV2 lists ecs service tags. -// The identifier is typically the Amazon Resource Name (ARN), although -// it may also be a different identifier depending on the service. -func listTagsV2(ctx context.Context, conn *ecs.Client, identifier string, optFns ...func(*ecs.Options)) (tftags.KeyValueTags, error) { - input := &ecs.ListTagsForResourceInput{ - ResourceArn: aws.String(identifier), - } - - output, err := conn.ListTagsForResource(ctx, input, optFns...) - - if tfawserr.ErrMessageContains(err, "InvalidParameterException", "The specified cluster is inactive. Specify an active cluster and try again.") { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - - if err != nil { - return tftags.New(ctx, nil), err - } - - return keyValueTagsV2(ctx, output.Tags), nil -} - -// []*SERVICE.Tag handling - -// TagsV2 returns ecs service tags. -func TagsV2(tags tftags.KeyValueTags) []awstypes.Tag { - result := make([]awstypes.Tag, 0, len(tags)) - - for k, v := range tags.Map() { - tag := awstypes.Tag{ - Key: aws.String(k), - Value: aws.String(v), - } - - result = append(result, tag) - } - - return result -} - -// keyValueTagsV2 creates tftags.KeyValueTags from ecs service tags. -func keyValueTagsV2(ctx context.Context, tags []awstypes.Tag) tftags.KeyValueTags { - m := make(map[string]*string, len(tags)) - - for _, tag := range tags { - m[aws.ToString(tag.Key)] = tag.Value - } - - return tftags.New(ctx, m) -} - -// getTagsInV2 returns ecs service tags from Context. -// nil is returned if there are no input tags. -func getTagsInV2(ctx context.Context) []awstypes.Tag { - if inContext, ok := tftags.FromContext(ctx); ok { - if tags := TagsV2(inContext.TagsIn.UnwrapOrDefault()); len(tags) > 0 { - return tags - } - } - - return nil -} - -// setTagsOutV2 sets ecs service tags in Context. -func setTagsOutV2(ctx context.Context, tags []awstypes.Tag) { - if inContext, ok := tftags.FromContext(ctx); ok { - inContext.TagsOut = option.Some(keyValueTagsV2(ctx, tags)) - } -} - -// updateTagsV2 updates ecs service tags. -// The identifier is typically the Amazon Resource Name (ARN), although -// it may also be a different identifier depending on the service. -func updateTagsV2(ctx context.Context, conn *ecs.Client, identifier string, oldTagsMap, newTagsMap any, optFns ...func(*ecs.Options)) error { - oldTags := tftags.New(ctx, oldTagsMap) - newTags := tftags.New(ctx, newTagsMap) - - ctx = tflog.SetField(ctx, logging.KeyResourceId, identifier) - - removedTags := oldTags.Removed(newTags) - removedTags = removedTags.IgnoreSystem(names.ECS) - if len(removedTags) > 0 { - input := &ecs.UntagResourceInput{ - ResourceArn: aws.String(identifier), - TagKeys: removedTags.Keys(), - } - - _, err := conn.UntagResource(ctx, input, optFns...) - - if err != nil { - return fmt.Errorf("untagging resource (%s): %w", identifier, err) - } - } - - updatedTags := oldTags.Updated(newTags) - updatedTags = updatedTags.IgnoreSystem(names.ECS) - if len(updatedTags) > 0 { - input := &ecs.TagResourceInput{ - ResourceArn: aws.String(identifier), - Tags: TagsV2(updatedTags), - } - - _, err := conn.TagResource(ctx, input, optFns...) - - if err != nil { - return fmt.Errorf("tagging resource (%s): %w", identifier, err) - } - } - - return nil -} From 1d7af072c59ce258ff1eca0660207726cafcce7e Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Thu, 13 Jun 2024 22:48:08 +0100 Subject: [PATCH 03/60] r/ecs_account_setting_default: Migrate to AWS SDK v2 --- .../service/ecs/account_setting_default.go | 57 ++++++++++--------- .../ecs/account_setting_default_test.go | 41 ++++++------- 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/internal/service/ecs/account_setting_default.go b/internal/service/ecs/account_setting_default.go index 9f324d029bd..c2978239946 100644 --- a/internal/service/ecs/account_setting_default.go +++ b/internal/service/ecs/account_setting_default.go @@ -8,14 +8,15 @@ import ( "fmt" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/arn" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -33,10 +34,10 @@ func ResourceAccountSettingDefault() *schema.Resource { Schema: map[string]*schema.Schema{ names.AttrName: { - Type: schema.TypeString, - ForceNew: true, - Required: true, - ValidateFunc: validation.StringInSlice(ecs.SettingName_Values(), false), + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateDiagFunc: enum.Validate[awstypes.SettingName](), }, "principal_arn": { Type: schema.TypeString, @@ -56,7 +57,7 @@ func resourceAccountSettingDefaultImport(ctx context.Context, d *schema.Resource Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, AccountID: meta.(*conns.AWSClient).AccountID, - Service: ecs.ServiceName, + Service: names.ECSEndpointID, Resource: fmt.Sprintf("cluster/%s", d.Id()), }.String()) return []*schema.ResourceData{d}, nil @@ -64,25 +65,25 @@ func resourceAccountSettingDefaultImport(ctx context.Context, d *schema.Resource func resourceAccountSettingDefaultCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) settingName := d.Get(names.AttrName).(string) settingValue := d.Get(names.AttrValue).(string) log.Printf("[DEBUG] Setting Account Default %s", settingName) input := ecs.PutAccountSettingDefaultInput{ - Name: aws.String(settingName), + Name: awstypes.SettingName(settingName), Value: aws.String(settingValue), } - out, err := conn.PutAccountSettingDefaultWithContext(ctx, &input) + out, err := conn.PutAccountSettingDefault(ctx, &input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating ECS Account Setting Defauilt (%s): %s", settingName, err) } - log.Printf("[DEBUG] Account Setting Default %s set", aws.StringValue(out.Setting.Value)) + log.Printf("[DEBUG] Account Setting Default %s set", aws.ToString(out.Setting.Value)) - d.SetId(aws.StringValue(out.Setting.Value)) + d.SetId(aws.ToString(out.Setting.Value)) d.Set("principal_arn", out.Setting.PrincipalArn) return append(diags, resourceAccountSettingDefaultRead(ctx, d, meta)...) @@ -90,15 +91,15 @@ func resourceAccountSettingDefaultCreate(ctx context.Context, d *schema.Resource func resourceAccountSettingDefaultRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) input := &ecs.ListAccountSettingsInput{ - Name: aws.String(d.Get(names.AttrName).(string)), - EffectiveSettings: aws.Bool(true), + Name: awstypes.SettingName(d.Get(names.AttrName).(string)), + EffectiveSettings: true, } log.Printf("[DEBUG] Reading Default Account Settings: %s", input) - resp, err := conn.ListAccountSettingsWithContext(ctx, input) + resp, err := conn.ListAccountSettings(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS Account Setting Defauilt (%s): %s", d.Get(names.AttrName).(string), err) @@ -111,7 +112,7 @@ func resourceAccountSettingDefaultRead(ctx context.Context, d *schema.ResourceDa } for _, r := range resp.Settings { - d.SetId(aws.StringValue(r.PrincipalArn)) + d.SetId(aws.ToString(r.PrincipalArn)) d.Set(names.AttrName, r.Name) d.Set("principal_arn", r.PrincipalArn) d.Set(names.AttrValue, r.Value) @@ -122,18 +123,18 @@ func resourceAccountSettingDefaultRead(ctx context.Context, d *schema.ResourceDa func resourceAccountSettingDefaultUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) settingName := d.Get(names.AttrName).(string) settingValue := d.Get(names.AttrValue).(string) if d.HasChange(names.AttrValue) { input := ecs.PutAccountSettingDefaultInput{ - Name: aws.String(settingName), + Name: awstypes.SettingName(settingName), Value: aws.String(settingValue), } - _, err := conn.PutAccountSettingDefaultWithContext(ctx, &input) + _, err := conn.PutAccountSettingDefault(ctx, &input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating ECS Account Setting Default (%s): %s", settingName, err) } @@ -144,25 +145,25 @@ func resourceAccountSettingDefaultUpdate(ctx context.Context, d *schema.Resource func resourceAccountSettingDefaultDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) settingName := d.Get(names.AttrName).(string) settingValue := "disabled" //Default value: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-maintenance.html#task-retirement-change - if settingName == ecs.SettingNameFargateTaskRetirementWaitPeriod { + if settingName == string(awstypes.SettingNameFargateTaskRetirementWaitPeriod) { settingValue = fargateTaskRetirementWaitPeriodValue } log.Printf("[WARN] Disabling ECS Account Setting Default %s", settingName) input := ecs.PutAccountSettingDefaultInput{ - Name: aws.String(settingName), + Name: awstypes.SettingName(settingName), Value: aws.String(settingValue), } - _, err := conn.PutAccountSettingDefaultWithContext(ctx, &input) + _, err := conn.PutAccountSettingDefault(ctx, &input) - if tfawserr.ErrMessageContains(err, ecs.ErrCodeInvalidParameterException, "You can no longer disable") { + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "You can no longer disable") { log.Printf("[DEBUG] ECS Account Setting Default (%q) could not be disabled: %s", settingName, err) return diags } diff --git a/internal/service/ecs/account_setting_default_test.go b/internal/service/ecs/account_setting_default_test.go index f664a3e58cf..f8ff1403a4e 100644 --- a/internal/service/ecs/account_setting_default_test.go +++ b/internal/service/ecs/account_setting_default_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -37,7 +38,7 @@ func TestAccECSAccountSettingDefault_serial(t *testing.T) { func testAccAccountSettingDefault_containerInstanceLongARNFormat(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_ecs_account_setting_default.test" - settingName := ecs.SettingNameContainerInstanceLongArnFormat + settingName := string(awstypes.SettingNameContainerInstanceLongArnFormat) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -66,7 +67,7 @@ func testAccAccountSettingDefault_containerInstanceLongARNFormat(t *testing.T) { func testAccAccountSettingDefault_serviceLongARNFormat(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_ecs_account_setting_default.test" - settingName := ecs.SettingNameServiceLongArnFormat + settingName := string(awstypes.SettingNameServiceLongArnFormat) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -95,7 +96,7 @@ func testAccAccountSettingDefault_serviceLongARNFormat(t *testing.T) { func testAccAccountSettingDefault_taskLongARNFormat(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_ecs_account_setting_default.test" - settingName := ecs.SettingNameTaskLongArnFormat + settingName := string(awstypes.SettingNameTaskLongArnFormat) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -124,7 +125,7 @@ func testAccAccountSettingDefault_taskLongARNFormat(t *testing.T) { func testAccAccountSettingDefault_vpcTrunking(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_ecs_account_setting_default.test" - settingName := ecs.SettingNameAwsvpcTrunking + settingName := string(awstypes.SettingNameAwsvpcTrunking) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -153,7 +154,7 @@ func testAccAccountSettingDefault_vpcTrunking(t *testing.T) { func testAccAccountSettingDefault_containerInsights(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_ecs_account_setting_default.test" - settingName := ecs.SettingNameContainerInsights + settingName := string(awstypes.SettingNameContainerInsights) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -182,7 +183,7 @@ func testAccAccountSettingDefault_containerInsights(t *testing.T) { func testAccAccountSettingDefault_fargateTaskRetirementWaitPeriod(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_ecs_account_setting_default.test" - settingName := ecs.SettingNameFargateTaskRetirementWaitPeriod + settingName := string(awstypes.SettingNameFargateTaskRetirementWaitPeriod) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -210,7 +211,7 @@ func testAccAccountSettingDefault_fargateTaskRetirementWaitPeriod(t *testing.T) func testAccCheckAccountSettingDefaultDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_ecs_account_setting_default" { @@ -220,13 +221,13 @@ func testAccCheckAccountSettingDefaultDestroy(ctx context.Context) resource.Test name := rs.Primary.Attributes[names.AttrName] input := &ecs.ListAccountSettingsInput{ - Name: aws.String(name), - EffectiveSettings: aws.Bool(true), + Name: awstypes.SettingName(name), + EffectiveSettings: true, } - resp, err := conn.ListAccountSettingsWithContext(ctx, input) + resp, err := conn.ListAccountSettings(ctx, input) - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeResourceNotFoundException) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { continue } @@ -235,16 +236,16 @@ func testAccCheckAccountSettingDefaultDestroy(ctx context.Context) resource.Test } for _, value := range resp.Settings { - if aws.StringValue(value.Value) != "disabled" && aws.StringValue(value.Value) != "7" { - switch name { - case ecs.SettingNameContainerInstanceLongArnFormat: + if aws.ToString(value.Value) != "disabled" && aws.ToString(value.Value) != "7" { + switch awstypes.SettingName(name) { + case awstypes.SettingNameContainerInstanceLongArnFormat: return nil - case ecs.SettingNameServiceLongArnFormat: + case awstypes.SettingNameServiceLongArnFormat: return nil - case ecs.SettingNameTaskLongArnFormat: + case awstypes.SettingNameTaskLongArnFormat: return nil default: - return fmt.Errorf("[Destroy Error] Account Settings (%s), still enabled", aws.StringValue(value.Name)) + return fmt.Errorf("[Destroy Error] Account Settings (%s), still enabled", string(value.Name)) } } } From c5bbf42213902758bb5aadfd8a57f87dffca60da Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Thu, 13 Jun 2024 23:09:14 +0100 Subject: [PATCH 04/60] r/ecs_capacity_provider: Migrate to AWS SDK v2 --- internal/service/ecs/capacity_provider.go | 110 +++++++++--------- .../service/ecs/capacity_provider_test.go | 24 ++-- 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/internal/service/ecs/capacity_provider.go b/internal/service/ecs/capacity_provider.go index ca4558db8bb..29442934cbe 100644 --- a/internal/service/ecs/capacity_provider.go +++ b/internal/service/ecs/capacity_provider.go @@ -8,15 +8,16 @@ import ( "fmt" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/arn" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -59,10 +60,10 @@ func ResourceCapacityProvider() *schema.Resource { ValidateFunc: verify.ValidARN, }, "managed_draining": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice(ecs.ManagedDraining_Values(), false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateDiagFunc: enum.Validate[awstypes.ManagedDraining](), }, "managed_scaling": { Type: schema.TypeList, @@ -90,10 +91,10 @@ func ResourceCapacityProvider() *schema.Resource { ValidateFunc: validation.IntBetween(1, 10000), }, names.AttrStatus: { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice(ecs.ManagedScalingStatus_Values(), false)}, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateDiagFunc: enum.Validate[awstypes.ManagedScalingStatus]()}, "target_capacity": { Type: schema.TypeInt, Optional: true, @@ -104,10 +105,10 @@ func ResourceCapacityProvider() *schema.Resource { }, }, "managed_termination_protection": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice(ecs.ManagedTerminationProtection_Values(), false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateDiagFunc: enum.Validate[awstypes.ManagedTerminationProtection](), }, }, }, @@ -125,7 +126,8 @@ func ResourceCapacityProvider() *schema.Resource { func resourceCapacityProviderCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition name := d.Get(names.AttrName).(string) input := ecs.CreateCapacityProviderInput{ @@ -134,27 +136,27 @@ func resourceCapacityProviderCreate(ctx context.Context, d *schema.ResourceData, Tags: getTagsIn(ctx), } - output, err := conn.CreateCapacityProviderWithContext(ctx, &input) + output, err := conn.CreateCapacityProvider(ctx, &input) // Some partitions (e.g. ISO) may not support tag-on-create. - if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(partition, err) { input.Tags = nil - output, err = conn.CreateCapacityProviderWithContext(ctx, &input) + output, err = conn.CreateCapacityProvider(ctx, &input) } if err != nil { return sdkdiag.AppendErrorf(diags, "creating ECS Capacity Provider (%s): %s", name, err) } - d.SetId(aws.StringValue(output.CapacityProvider.CapacityProviderArn)) + d.SetId(aws.ToString(output.CapacityProvider.CapacityProviderArn)) // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { err := createTags(ctx, conn, d.Id(), tags) // If default tags only, continue. Otherwise, error. - if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(partition, err) { return append(diags, resourceCapacityProviderRead(ctx, d, meta)...) } @@ -168,7 +170,7 @@ func resourceCapacityProviderCreate(ctx context.Context, d *schema.ResourceData, func resourceCapacityProviderRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) output, err := FindCapacityProviderByARN(ctx, conn, d.Id()) @@ -197,7 +199,7 @@ func resourceCapacityProviderRead(ctx context.Context, d *schema.ResourceData, m func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { input := &ecs.UpdateCapacityProviderInput{ @@ -207,9 +209,9 @@ func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, log.Printf("[DEBUG] Updating ECS Capacity Provider: %s", input) err := retry.RetryContext(ctx, capacityProviderUpdateTimeout, func() *retry.RetryError { - _, err := conn.UpdateCapacityProviderWithContext(ctx, input) + _, err := conn.UpdateCapacityProvider(ctx, input) - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeUpdateInProgressException) { + if errs.IsA[*awstypes.UpdateInProgressException](err) { return retry.RetryableError(err) } @@ -221,7 +223,7 @@ func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, }) if tfresource.TimedOut(err) { - _, err = conn.UpdateCapacityProviderWithContext(ctx, input) + _, err = conn.UpdateCapacityProvider(ctx, input) } if err != nil { @@ -238,15 +240,15 @@ func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, func resourceCapacityProviderDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) log.Printf("[DEBUG] Deleting ECS Capacity Provider (%s)", d.Id()) - _, err := conn.DeleteCapacityProviderWithContext(ctx, &ecs.DeleteCapacityProviderInput{ + _, err := conn.DeleteCapacityProvider(ctx, &ecs.DeleteCapacityProviderInput{ CapacityProvider: aws.String(d.Id()), }) // "An error occurred (ClientException) when calling the DeleteCapacityProvider operation: The specified capacity provider does not exist. Specify a valid name or ARN and try again." - if tfawserr.ErrMessageContains(err, ecs.ErrCodeClientException, "capacity provider does not exist") { + if errs.IsAErrorMessageContains[*awstypes.ClientException](err, "capacity provider does not exist") { return diags } @@ -273,7 +275,7 @@ func resourceCapacityProviderImport(ctx context.Context, d *schema.ResourceData, return []*schema.ResourceData{d}, nil } -func expandAutoScalingGroupProviderCreate(configured interface{}) *ecs.AutoScalingGroupProvider { +func expandAutoScalingGroupProviderCreate(configured interface{}) *awstypes.AutoScalingGroupProvider { if configured == nil { return nil } @@ -282,25 +284,25 @@ func expandAutoScalingGroupProviderCreate(configured interface{}) *ecs.AutoScali return nil } - prov := ecs.AutoScalingGroupProvider{} + prov := awstypes.AutoScalingGroupProvider{} p := configured.([]interface{})[0].(map[string]interface{}) arn := p["auto_scaling_group_arn"].(string) prov.AutoScalingGroupArn = aws.String(arn) if mtp := p["managed_draining"].(string); len(mtp) > 0 { - prov.ManagedDraining = aws.String(mtp) + prov.ManagedDraining = awstypes.ManagedDraining(mtp) } prov.ManagedScaling = expandManagedScaling(p["managed_scaling"]) if mtp := p["managed_termination_protection"].(string); len(mtp) > 0 { - prov.ManagedTerminationProtection = aws.String(mtp) + prov.ManagedTerminationProtection = awstypes.ManagedTerminationProtection(mtp) } return &prov } -func expandAutoScalingGroupProviderUpdate(configured interface{}) *ecs.AutoScalingGroupProviderUpdate { +func expandAutoScalingGroupProviderUpdate(configured interface{}) *awstypes.AutoScalingGroupProviderUpdate { if configured == nil { return nil } @@ -309,23 +311,23 @@ func expandAutoScalingGroupProviderUpdate(configured interface{}) *ecs.AutoScali return nil } - prov := ecs.AutoScalingGroupProviderUpdate{} + prov := awstypes.AutoScalingGroupProviderUpdate{} p := configured.([]interface{})[0].(map[string]interface{}) if mtp := p["managed_draining"].(string); len(mtp) > 0 { - prov.ManagedDraining = aws.String(mtp) + prov.ManagedDraining = awstypes.ManagedDraining(mtp) } prov.ManagedScaling = expandManagedScaling(p["managed_scaling"]) if mtp := p["managed_termination_protection"].(string); len(mtp) > 0 { - prov.ManagedTerminationProtection = aws.String(mtp) + prov.ManagedTerminationProtection = awstypes.ManagedTerminationProtection(mtp) } return &prov } -func expandManagedScaling(configured interface{}) *ecs.ManagedScaling { +func expandManagedScaling(configured interface{}) *awstypes.ManagedScaling { if configured == nil { return nil } @@ -336,46 +338,46 @@ func expandManagedScaling(configured interface{}) *ecs.ManagedScaling { tfMap := configured.([]interface{})[0].(map[string]interface{}) - managedScaling := ecs.ManagedScaling{} + managedScaling := awstypes.ManagedScaling{} if v, ok := tfMap["instance_warmup_period"].(int); ok { - managedScaling.InstanceWarmupPeriod = aws.Int64(int64(v)) + managedScaling.InstanceWarmupPeriod = aws.Int32(int32(v)) } if v, ok := tfMap["maximum_scaling_step_size"].(int); ok && v != 0 { - managedScaling.MaximumScalingStepSize = aws.Int64(int64(v)) + managedScaling.MaximumScalingStepSize = aws.Int32(int32(v)) } if v, ok := tfMap["minimum_scaling_step_size"].(int); ok && v != 0 { - managedScaling.MinimumScalingStepSize = aws.Int64(int64(v)) + managedScaling.MinimumScalingStepSize = aws.Int32(int32(v)) } if v, ok := tfMap[names.AttrStatus].(string); ok && len(v) > 0 { - managedScaling.Status = aws.String(v) + managedScaling.Status = awstypes.ManagedScalingStatus(v) } if v, ok := tfMap["target_capacity"].(int); ok && v != 0 { - managedScaling.TargetCapacity = aws.Int64(int64(v)) + managedScaling.TargetCapacity = aws.Int32(int32(v)) } return &managedScaling } -func flattenAutoScalingGroupProvider(provider *ecs.AutoScalingGroupProvider) []map[string]interface{} { +func flattenAutoScalingGroupProvider(provider *awstypes.AutoScalingGroupProvider) []map[string]interface{} { if provider == nil { return nil } p := map[string]interface{}{ - "auto_scaling_group_arn": aws.StringValue(provider.AutoScalingGroupArn), - "managed_draining": aws.StringValue(provider.ManagedDraining), + "auto_scaling_group_arn": aws.ToString(provider.AutoScalingGroupArn), + "managed_draining": string(provider.ManagedDraining), "managed_scaling": []map[string]interface{}{}, - "managed_termination_protection": aws.StringValue(provider.ManagedTerminationProtection), + "managed_termination_protection": string(provider.ManagedTerminationProtection), } if provider.ManagedScaling != nil { m := map[string]interface{}{ - "instance_warmup_period": aws.Int64Value(provider.ManagedScaling.InstanceWarmupPeriod), - "maximum_scaling_step_size": aws.Int64Value(provider.ManagedScaling.MaximumScalingStepSize), - "minimum_scaling_step_size": aws.Int64Value(provider.ManagedScaling.MinimumScalingStepSize), - names.AttrStatus: aws.StringValue(provider.ManagedScaling.Status), - "target_capacity": aws.Int64Value(provider.ManagedScaling.TargetCapacity), + "instance_warmup_period": aws.ToInt32(provider.ManagedScaling.InstanceWarmupPeriod), + "maximum_scaling_step_size": aws.ToInt32(provider.ManagedScaling.MaximumScalingStepSize), + "minimum_scaling_step_size": aws.ToInt32(provider.ManagedScaling.MinimumScalingStepSize), + names.AttrStatus: string(provider.ManagedScaling.Status), + "target_capacity": aws.ToInt32(provider.ManagedScaling.TargetCapacity), } p["managed_scaling"] = []map[string]interface{}{m} diff --git a/internal/service/ecs/capacity_provider_test.go b/internal/service/ecs/capacity_provider_test.go index f1ebf646ef9..ac753259a9e 100644 --- a/internal/service/ecs/capacity_provider_test.go +++ b/internal/service/ecs/capacity_provider_test.go @@ -8,7 +8,7 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -21,7 +21,7 @@ import ( func TestAccECSCapacityProvider_basic(t *testing.T) { ctx := acctest.Context(t) - var provider ecs.CapacityProvider + var provider awstypes.CapacityProvider rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_capacity_provider.test" @@ -63,7 +63,7 @@ func TestAccECSCapacityProvider_basic(t *testing.T) { func TestAccECSCapacityProvider_disappears(t *testing.T) { ctx := acctest.Context(t) - var provider ecs.CapacityProvider + var provider awstypes.CapacityProvider rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_capacity_provider.test" @@ -87,7 +87,7 @@ func TestAccECSCapacityProvider_disappears(t *testing.T) { func TestAccECSCapacityProvider_managedScaling(t *testing.T) { ctx := acctest.Context(t) - var provider ecs.CapacityProvider + var provider awstypes.CapacityProvider rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_capacity_provider.test" @@ -98,7 +98,7 @@ func TestAccECSCapacityProvider_managedScaling(t *testing.T) { CheckDestroy: testAccCheckCapacityProviderDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccCapacityProviderConfig_managedScaling(rName, ecs.ManagedScalingStatusEnabled, 300, 10, 1, 50), + Config: testAccCapacityProviderConfig_managedScaling(rName, string(awstypes.ManagedScalingStatusEnabled), 300, 10, 1, 50), Check: resource.ComposeTestCheckFunc( testAccCheckCapacityProviderExists(ctx, resourceName, &provider), resource.TestCheckResourceAttrPair(resourceName, "auto_scaling_group_provider.0.auto_scaling_group_arn", "aws_autoscaling_group.test", names.AttrARN), @@ -118,7 +118,7 @@ func TestAccECSCapacityProvider_managedScaling(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccCapacityProviderConfig_managedScaling(rName, ecs.ManagedScalingStatusDisabled, 400, 100, 10, 100), + Config: testAccCapacityProviderConfig_managedScaling(rName, string(awstypes.ManagedScalingStatusDisabled), 400, 100, 10, 100), Check: resource.ComposeTestCheckFunc( testAccCheckCapacityProviderExists(ctx, resourceName, &provider), resource.TestCheckResourceAttrPair(resourceName, "auto_scaling_group_provider.0.auto_scaling_group_arn", "aws_autoscaling_group.test", names.AttrARN), @@ -132,7 +132,7 @@ func TestAccECSCapacityProvider_managedScaling(t *testing.T) { ), }, { - Config: testAccCapacityProviderConfig_managedScaling(rName, ecs.ManagedScalingStatusEnabled, 0, 100, 10, 100), + Config: testAccCapacityProviderConfig_managedScaling(rName, string(awstypes.ManagedScalingStatusEnabled), 0, 100, 10, 100), Check: resource.ComposeTestCheckFunc( testAccCheckCapacityProviderExists(ctx, resourceName, &provider), resource.TestCheckResourceAttrPair(resourceName, "auto_scaling_group_provider.0.auto_scaling_group_arn", "aws_autoscaling_group.test", names.AttrARN), @@ -151,7 +151,7 @@ func TestAccECSCapacityProvider_managedScaling(t *testing.T) { func TestAccECSCapacityProvider_managedScalingPartial(t *testing.T) { ctx := acctest.Context(t) - var provider ecs.CapacityProvider + var provider awstypes.CapacityProvider rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_capacity_provider.test" @@ -188,7 +188,7 @@ func TestAccECSCapacityProvider_managedScalingPartial(t *testing.T) { func TestAccECSCapacityProvider_tags(t *testing.T) { ctx := acctest.Context(t) - var provider ecs.CapacityProvider + var provider awstypes.CapacityProvider rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_capacity_provider.test" @@ -235,7 +235,7 @@ func TestAccECSCapacityProvider_tags(t *testing.T) { func testAccCheckCapacityProviderDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_ecs_capacity_provider" { @@ -259,7 +259,7 @@ func testAccCheckCapacityProviderDestroy(ctx context.Context) resource.TestCheck } } -func testAccCheckCapacityProviderExists(ctx context.Context, resourceName string, provider *ecs.CapacityProvider) resource.TestCheckFunc { +func testAccCheckCapacityProviderExists(ctx context.Context, resourceName string, provider *awstypes.CapacityProvider) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -270,7 +270,7 @@ func testAccCheckCapacityProviderExists(ctx context.Context, resourceName string return fmt.Errorf("No ECS Capacity Provider ID is set") } - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) output, err := tfecs.FindCapacityProviderByARN(ctx, conn, rs.Primary.ID) From e53d5169ce2f8dc63c0a67800f14366055867502 Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Thu, 13 Jun 2024 23:27:23 +0100 Subject: [PATCH 05/60] r/cluster_capacity_providers: Migrate to AWS SDK v2 --- .../service/ecs/cluster_capacity_providers.go | 31 +++++----- .../ecs/cluster_capacity_providers_test.go | 17 +++-- internal/service/ecs/flex.go | 62 +++++++++---------- 3 files changed, 53 insertions(+), 57 deletions(-) diff --git a/internal/service/ecs/cluster_capacity_providers.go b/internal/service/ecs/cluster_capacity_providers.go index 7fce8f19a64..bbf9bbc6a61 100644 --- a/internal/service/ecs/cluster_capacity_providers.go +++ b/internal/service/ecs/cluster_capacity_providers.go @@ -7,13 +7,14 @@ import ( "context" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "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" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -80,11 +81,11 @@ func ResourceClusterCapacityProviders() *schema.Resource { func resourceClusterCapacityProvidersPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) clusterName := d.Get(names.AttrClusterName).(string) input := &ecs.PutClusterCapacityProvidersInput{ - CapacityProviders: flex.ExpandStringSet(d.Get("capacity_providers").(*schema.Set)), + CapacityProviders: flex.ExpandStringValueSet(d.Get("capacity_providers").(*schema.Set)), Cluster: aws.String(clusterName), DefaultCapacityProviderStrategy: expandCapacityProviderStrategy(d.Get("default_capacity_provider_strategy").(*schema.Set)), } @@ -109,7 +110,7 @@ func resourceClusterCapacityProvidersPut(ctx context.Context, d *schema.Resource func resourceClusterCapacityProvidersRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) cluster, err := FindClusterByNameOrARN(ctx, conn, d.Id()) @@ -123,7 +124,7 @@ func resourceClusterCapacityProvidersRead(ctx context.Context, d *schema.Resourc return sdkdiag.AppendErrorf(diags, "reading ECS Cluster (%s): %s", d.Id(), err) } - if err := d.Set("capacity_providers", aws.StringValueSlice(cluster.CapacityProviders)); err != nil { + if err := d.Set("capacity_providers", cluster.CapacityProviders); err != nil { return sdkdiag.AppendErrorf(diags, "setting capacity_providers: %s", err) } d.Set(names.AttrClusterName, cluster.ClusterName) @@ -137,18 +138,18 @@ func resourceClusterCapacityProvidersRead(ctx context.Context, d *schema.Resourc func resourceClusterCapacityProvidersDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) input := &ecs.PutClusterCapacityProvidersInput{ - CapacityProviders: []*string{}, + CapacityProviders: []string{}, Cluster: aws.String(d.Id()), - DefaultCapacityProviderStrategy: []*ecs.CapacityProviderStrategyItem{}, + DefaultCapacityProviderStrategy: []awstypes.CapacityProviderStrategyItem{}, } log.Printf("[DEBUG] Deleting ECS Cluster Capacity Providers: %s", d.Id()) err := retryClusterCapacityProvidersPut(ctx, conn, input) - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeClusterNotFoundException) { + if errs.IsA[*awstypes.ClusterNotFoundException](err) { return diags } @@ -163,17 +164,17 @@ func resourceClusterCapacityProvidersDelete(ctx context.Context, d *schema.Resou return diags } -func retryClusterCapacityProvidersPut(ctx context.Context, conn *ecs.ECS, input *ecs.PutClusterCapacityProvidersInput) error { +func retryClusterCapacityProvidersPut(ctx context.Context, conn *ecs.Client, input *ecs.PutClusterCapacityProvidersInput) error { _, err := tfresource.RetryWhen(ctx, clusterUpdateTimeout, func() (interface{}, error) { - return conn.PutClusterCapacityProvidersWithContext(ctx, input) + return conn.PutClusterCapacityProviders(ctx, input) }, func(err error) (bool, error) { - if tfawserr.ErrMessageContains(err, ecs.ErrCodeClientException, "Cluster was not ACTIVE") { + if errs.IsAErrorMessageContains[*awstypes.ClientException](err, "Cluster was not ACTIVE") { return true, err } - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeResourceInUseException, ecs.ErrCodeUpdateInProgressException) { + if errs.IsA[*awstypes.ResourceInUseException](err) || errs.IsA[*awstypes.UpdateInProgressException](err) { return true, err } diff --git a/internal/service/ecs/cluster_capacity_providers_test.go b/internal/service/ecs/cluster_capacity_providers_test.go index 0edfd36880d..880eeb82be5 100644 --- a/internal/service/ecs/cluster_capacity_providers_test.go +++ b/internal/service/ecs/cluster_capacity_providers_test.go @@ -7,8 +7,7 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -19,7 +18,7 @@ import ( func TestAccECSClusterCapacityProviders_basic(t *testing.T) { ctx := acctest.Context(t) - var cluster ecs.Cluster + var cluster awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_cluster_capacity_providers.test" @@ -55,7 +54,7 @@ func TestAccECSClusterCapacityProviders_basic(t *testing.T) { func TestAccECSClusterCapacityProviders_disappears(t *testing.T) { ctx := acctest.Context(t) - var cluster ecs.Cluster + var cluster awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_cluster_capacity_providers.test" @@ -79,7 +78,7 @@ func TestAccECSClusterCapacityProviders_disappears(t *testing.T) { func TestAccECSClusterCapacityProviders_defaults(t *testing.T) { ctx := acctest.Context(t) - var cluster ecs.Cluster + var cluster awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_cluster_capacity_providers.test" @@ -115,7 +114,7 @@ func TestAccECSClusterCapacityProviders_destroy(t *testing.T) { // // If we were configuring capacity providers directly on the cluster, the // test would fail with a timeout error. - var cluster ecs.Cluster + var cluster awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ @@ -129,7 +128,7 @@ func TestAccECSClusterCapacityProviders_destroy(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckClusterExists(ctx, "aws_ecs_cluster.test", &cluster), func(s *terraform.State) error { - if got, want := int(aws.Int64Value(cluster.RegisteredContainerInstancesCount)), 2; got != want { + if got, want := int(cluster.RegisteredContainerInstancesCount), 2; got != want { return fmt.Errorf("RegisteredContainerInstancesCount = %v, want %v", got, want) } @@ -146,7 +145,7 @@ func TestAccECSClusterCapacityProviders_destroy(t *testing.T) { func TestAccECSClusterCapacityProviders_Update_capacityProviders(t *testing.T) { ctx := acctest.Context(t) - var cluster ecs.Cluster + var cluster awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_cluster_capacity_providers.test" @@ -214,7 +213,7 @@ func TestAccECSClusterCapacityProviders_Update_capacityProviders(t *testing.T) { func TestAccECSClusterCapacityProviders_Update_defaultStrategy(t *testing.T) { ctx := acctest.Context(t) - var cluster ecs.Cluster + var cluster awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_cluster_capacity_providers.test" diff --git a/internal/service/ecs/flex.go b/internal/service/ecs/flex.go index 5ff5aa811f2..a52b07ed3d6 100644 --- a/internal/service/ecs/flex.go +++ b/internal/service/ecs/flex.go @@ -4,23 +4,23 @@ package ecs import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/names" ) -func expandCapacityProviderStrategy(cps *schema.Set) []*ecs.CapacityProviderStrategyItem { +func expandCapacityProviderStrategy(cps *schema.Set) []awstypes.CapacityProviderStrategyItem { list := cps.List() - results := make([]*ecs.CapacityProviderStrategyItem, 0) + results := make([]awstypes.CapacityProviderStrategyItem, 0) for _, raw := range list { cp := raw.(map[string]interface{}) - ps := &ecs.CapacityProviderStrategyItem{} + ps := awstypes.CapacityProviderStrategyItem{} if val, ok := cp["base"]; ok { - ps.Base = aws.Int64(int64(val.(int))) + ps.Base = int32(val.(int)) } if val, ok := cp[names.AttrWeight]; ok { - ps.Weight = aws.Int64(int64(val.(int))) + ps.Weight = int32(val.(int)) } if val, ok := cp["capacity_provider"]; ok { ps.CapacityProvider = aws.String(val.(string)) @@ -31,20 +31,16 @@ func expandCapacityProviderStrategy(cps *schema.Set) []*ecs.CapacityProviderStra return results } -func flattenCapacityProviderStrategy(cps []*ecs.CapacityProviderStrategyItem) []map[string]interface{} { +func flattenCapacityProviderStrategy(cps []awstypes.CapacityProviderStrategyItem) []map[string]interface{} { if cps == nil { return nil } results := make([]map[string]interface{}, 0) for _, cp := range cps { s := make(map[string]interface{}) - s["capacity_provider"] = aws.StringValue(cp.CapacityProvider) - if cp.Weight != nil { - s[names.AttrWeight] = aws.Int64Value(cp.Weight) - } - if cp.Base != nil { - s["base"] = aws.Int64Value(cp.Base) - } + s["capacity_provider"] = aws.ToString(cp.CapacityProvider) + s[names.AttrWeight] = cp.Weight + s["base"] = cp.Base results = append(results, s) } return results @@ -52,15 +48,15 @@ func flattenCapacityProviderStrategy(cps []*ecs.CapacityProviderStrategyItem) [] // Takes the result of flatmap. Expand for an array of load balancers and // returns ecs.LoadBalancer compatible objects -func expandLoadBalancers(configured []interface{}) []*ecs.LoadBalancer { - loadBalancers := make([]*ecs.LoadBalancer, 0, len(configured)) +func expandLoadBalancers(configured []interface{}) []*awstypes.LoadBalancer { + loadBalancers := make([]*awstypes.LoadBalancer, 0, len(configured)) // Loop over our configured load balancers and create // an array of aws-sdk-go compatible objects for _, lRaw := range configured { data := lRaw.(map[string]interface{}) - l := &ecs.LoadBalancer{ + l := &awstypes.LoadBalancer{ ContainerName: aws.String(data["container_name"].(string)), ContainerPort: aws.Int64(int64(data["container_port"].(int))), } @@ -79,7 +75,7 @@ func expandLoadBalancers(configured []interface{}) []*ecs.LoadBalancer { } // Flattens an array of ECS LoadBalancers into a []map[string]interface{} -func flattenLoadBalancers(list []*ecs.LoadBalancer) []map[string]interface{} { +func flattenLoadBalancers(list []*awstypes.LoadBalancer) []map[string]interface{} { result := make([]map[string]interface{}, 0, len(list)) for _, loadBalancer := range list { l := map[string]interface{}{ @@ -88,11 +84,11 @@ func flattenLoadBalancers(list []*ecs.LoadBalancer) []map[string]interface{} { } if loadBalancer.LoadBalancerName != nil { - l["elb_name"] = aws.StringValue(loadBalancer.LoadBalancerName) + l["elb_name"] = aws.ToString(loadBalancer.LoadBalancerName) } if loadBalancer.TargetGroupArn != nil { - l["target_group_arn"] = aws.StringValue(loadBalancer.TargetGroupArn) + l["target_group_arn"] = aws.ToString(loadBalancer.TargetGroupArn) } result = append(result, l) @@ -102,19 +98,19 @@ func flattenLoadBalancers(list []*ecs.LoadBalancer) []map[string]interface{} { // Expand for an array of load balancers and // returns ecs.LoadBalancer compatible objects for an ECS TaskSet -func expandTaskSetLoadBalancers(l []interface{}) []*ecs.LoadBalancer { +func expandTaskSetLoadBalancers(l []interface{}) []*awstypes.LoadBalancer { if len(l) == 0 || l[0] == nil { return nil } - loadBalancers := make([]*ecs.LoadBalancer, 0, len(l)) + loadBalancers := make([]*awstypes.LoadBalancer, 0, len(l)) // Loop over our configured load balancers and create // an array of aws-sdk-go compatible objects for _, lRaw := range l { data := lRaw.(map[string]interface{}) - l := &ecs.LoadBalancer{} + l := &awstypes.LoadBalancer{} if v, ok := data["container_name"].(string); ok && v != "" { l.ContainerName = aws.String(v) @@ -138,7 +134,7 @@ func expandTaskSetLoadBalancers(l []interface{}) []*ecs.LoadBalancer { } // Flattens an array of ECS LoadBalancers (of an ECS TaskSet) into a []map[string]interface{} -func flattenTaskSetLoadBalancers(list []*ecs.LoadBalancer) []map[string]interface{} { +func flattenTaskSetLoadBalancers(list []*awstypes.LoadBalancer) []map[string]interface{} { result := make([]map[string]interface{}, 0, len(list)) for _, loadBalancer := range list { l := map[string]interface{}{ @@ -161,12 +157,12 @@ func flattenTaskSetLoadBalancers(list []*ecs.LoadBalancer) []map[string]interfac // Expand for an array of service registries and // returns ecs.ServiceRegistry compatible objects for an ECS TaskSet -func expandServiceRegistries(l []interface{}) []*ecs.ServiceRegistry { - result := make([]*ecs.ServiceRegistry, 0, len(l)) +func expandServiceRegistries(l []interface{}) []*awstypes.ServiceRegistry { + result := make([]*awstypes.ServiceRegistry, 0, len(l)) for _, v := range l { m := v.(map[string]interface{}) - sr := &ecs.ServiceRegistry{ + sr := &awstypes.ServiceRegistry{ RegistryArn: aws.String(m["registry_arn"].(string)), } if raw, ok := m["container_name"].(string); ok && raw != "" { @@ -186,7 +182,7 @@ func expandServiceRegistries(l []interface{}) []*ecs.ServiceRegistry { // Expand for an array of scale configurations and // returns an ecs.Scale compatible object for an ECS TaskSet -func expandScale(l []interface{}) *ecs.Scale { +func expandScale(l []interface{}) *awstypes.Scale { if len(l) == 0 || l[0] == nil { return nil } @@ -196,7 +192,7 @@ func expandScale(l []interface{}) *ecs.Scale { return nil } - result := &ecs.Scale{} + result := &awstypes.Scale{} if v, ok := tfMap[names.AttrUnit].(string); ok && v != "" { result.Unit = aws.String(v) @@ -210,14 +206,14 @@ func expandScale(l []interface{}) *ecs.Scale { } // Flattens an ECS Scale configuration into a []map[string]interface{} -func flattenScale(scale *ecs.Scale) []map[string]interface{} { +func flattenScale(scale *awstypes.Scale) []map[string]interface{} { if scale == nil { return nil } m := make(map[string]interface{}) - m[names.AttrUnit] = aws.StringValue(scale.Unit) - m[names.AttrValue] = aws.Float64Value(scale.Value) + m[names.AttrUnit] = aws.ToString(scale.Unit) + m[names.AttrValue] = aws.ToFloat64(scale.Value) return []map[string]interface{}{m} } From 1b3457b0cb8ba60bbfdc53337d1fa70e3e333986 Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Thu, 13 Jun 2024 23:59:27 +0100 Subject: [PATCH 06/60] d/ecs_cluster, r/ecs_cluster: Migrate to AWS SDK v2 Fix --- internal/service/ecs/cluster.go | 182 +++++++++--------- .../service/ecs/cluster_capacity_providers.go | 3 +- internal/service/ecs/cluster_data_source.go | 10 +- internal/service/ecs/cluster_test.go | 26 +-- internal/tfresource/retry.go | 10 + 5 files changed, 123 insertions(+), 108 deletions(-) diff --git a/internal/service/ecs/cluster.go b/internal/service/ecs/cluster.go index 366d29aae7a..86e56f6592b 100644 --- a/internal/service/ecs/cluster.go +++ b/internal/service/ecs/cluster.go @@ -8,15 +8,15 @@ import ( "fmt" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/arn" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -91,10 +91,10 @@ func ResourceCluster() *schema.Resource { }, }, "logging": { - Type: schema.TypeString, - Optional: true, - Default: ecs.ExecuteCommandLoggingDefault, - ValidateFunc: validation.StringInSlice(ecs.ExecuteCommandLogging_Values(), false), + Type: schema.TypeString, + Optional: true, + Default: awstypes.ExecuteCommandLoggingDefault, + ValidateDiagFunc: enum.Validate[awstypes.ExecuteCommandLogging](), }, }, }, @@ -129,9 +129,9 @@ func ResourceCluster() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ names.AttrName: { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(ecs.ClusterSettingName_Values(), false), + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: enum.Validate[awstypes.ClusterSettingName](), }, names.AttrValue: { Type: schema.TypeString, @@ -160,7 +160,8 @@ func resourceClusterImport(ctx context.Context, d *schema.ResourceData, meta int func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition clusterName := d.Get(names.AttrName).(string) input := &ecs.CreateClusterInput{ @@ -185,7 +186,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int output, err := retryClusterCreate(ctx, conn, input) // Some partitions (e.g. ISO) may not support tag-on-create. - if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(partition, err) { input.Tags = nil output, err = retryClusterCreate(ctx, conn, input) @@ -195,7 +196,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int return sdkdiag.AppendErrorf(diags, "creating ECS Cluster (%s): %s", clusterName, err) } - d.SetId(aws.StringValue(output.Cluster.ClusterArn)) + d.SetId(aws.ToString(output.Cluster.ClusterArn)) if _, err := waitClusterAvailable(ctx, conn, d.Id()); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for ECS Cluster (%s) create: %s", d.Id(), err) @@ -206,7 +207,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int err := createTags(ctx, conn, d.Id(), tags) // If default tags only, continue. Otherwise, error. - if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(partition, err) { return append(diags, resourceClusterRead(ctx, d, meta)...) } @@ -220,10 +221,11 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition outputRaw, err := tfresource.RetryWhenNewResourceNotFound(ctx, clusterReadTimeout, func() (interface{}, error) { - return FindClusterByNameOrARN(ctx, conn, d.Id()) + return FindClusterByNameOrARN(ctx, conn, partition, d.Id()) }, d.IsNewResource()) if !d.IsNewResource() && tfresource.NotFound(err) { @@ -236,7 +238,7 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter return sdkdiag.AppendErrorf(diags, "reading ECS Cluster (%s): %s", d.Id(), err) } - cluster := outputRaw.(*ecs.Cluster) + cluster := outputRaw.(*awstypes.Cluster) d.Set(names.AttrARN, cluster.ClusterArn) if cluster.Configuration != nil { if err := d.Set(names.AttrConfiguration, flattenClusterConfiguration(cluster.Configuration)); err != nil { @@ -263,7 +265,7 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) if d.HasChanges(names.AttrConfiguration, "service_connect_defaults", "setting") { input := &ecs.UpdateClusterInput{ @@ -282,7 +284,7 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int input.Settings = expandClusterSettings(v.(*schema.Set)) } - _, err := conn.UpdateClusterWithContext(ctx, input) + _, err := conn.UpdateCluster(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating ECS Cluster (%s): %s", d.Id(), err) @@ -296,29 +298,28 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int return diags } -func FindClusterByNameOrARN(ctx context.Context, conn *ecs.ECS, nameOrARN string) (*ecs.Cluster, error) { +func FindClusterByNameOrARN(ctx context.Context, conn *ecs.Client, partition string, nameOrARN string) (*awstypes.Cluster, error) { input := &ecs.DescribeClustersInput{ - Clusters: aws.StringSlice([]string{nameOrARN}), - Include: aws.StringSlice([]string{ecs.ClusterFieldTags, ecs.ClusterFieldConfigurations, ecs.ClusterFieldSettings}), + Clusters: []string{nameOrARN}, + Include: []awstypes.ClusterField{awstypes.ClusterFieldTags, awstypes.ClusterFieldConfigurations, awstypes.ClusterFieldSettings}, } - output, err := conn.DescribeClustersWithContext(ctx, input) + output, err := conn.DescribeClusters(ctx, input) // Some partitions (e.g. ISO) may not support tagging. - if errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { - input.Include = aws.StringSlice([]string{ecs.ClusterFieldConfigurations, ecs.ClusterFieldSettings}) + if errs.IsUnsupportedOperationInPartitionError(partition, err) { + input.Include = []awstypes.ClusterField{awstypes.ClusterFieldConfigurations, awstypes.ClusterFieldSettings} - output, err = conn.DescribeClustersWithContext(ctx, input) + output, err = conn.DescribeClusters(ctx, input) } // Some partitions (e.g. ISO) may not support describe including configuration. - if errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { - input.Include = aws.StringSlice([]string{ecs.ClusterFieldSettings}) - - output, err = conn.DescribeClustersWithContext(ctx, input) + if errs.IsUnsupportedOperationInPartitionError(partition, err) { + input.Include = []awstypes.ClusterField{awstypes.ClusterFieldSettings} + output, err = conn.DescribeClusters(ctx, input) } - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeClusterNotFoundException) { + if errs.IsA[*awstypes.ClusterNotFoundException](err) { return nil, &retry.NotFoundError{ LastError: err, LastRequest: input, @@ -329,7 +330,7 @@ func FindClusterByNameOrARN(ctx context.Context, conn *ecs.ECS, nameOrARN string return nil, err } - if output == nil || len(output.Clusters) == 0 || output.Clusters[0] == nil { + if output == nil || len(output.Clusters) == 0 { return nil, tfresource.NewEmptyResultError(input) } @@ -337,19 +338,25 @@ func FindClusterByNameOrARN(ctx context.Context, conn *ecs.ECS, nameOrARN string return nil, tfresource.NewTooManyResultsError(count, input) } - if status := aws.StringValue(output.Clusters[0].Status); status == clusterStatusInactive { + if status := aws.ToString(output.Clusters[0].Status); status == clusterStatusInactive { return nil, &retry.NotFoundError{ Message: status, LastRequest: input, } } - return output.Clusters[0], nil + return tfresource.AssertFirstValueResult(output.Clusters) } -func statusCluster(ctx context.Context, conn *ecs.ECS, arn string) retry.StateRefreshFunc { +func statusCluster(ctx context.Context, conn *ecs.Client, clusterArn string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - cluster, err := FindClusterByNameOrARN(ctx, conn, arn) + parsedArn, err := arn.Parse(clusterArn) + + if err != nil { + return nil, "", err + } + + cluster, err := FindClusterByNameOrARN(ctx, conn, parsedArn.Partition, clusterArn) if tfresource.NotFound(err) { return nil, "", nil @@ -359,11 +366,11 @@ func statusCluster(ctx context.Context, conn *ecs.ECS, arn string) retry.StateRe return nil, "", err } - return cluster, aws.StringValue(cluster.Status), err + return cluster, aws.ToString(cluster.Status), err } } -func waitClusterAvailable(ctx context.Context, conn *ecs.ECS, arn string) (*ecs.Cluster, error) { //nolint:unparam +func waitClusterAvailable(ctx context.Context, conn *ecs.Client, arn string) (*awstypes.Cluster, error) { //nolint:unparam stateConf := &retry.StateChangeConf{ Pending: []string{clusterStatusProvisioning}, Target: []string{clusterStatusActive}, @@ -374,14 +381,14 @@ func waitClusterAvailable(ctx context.Context, conn *ecs.ECS, arn string) (*ecs. outputRaw, err := stateConf.WaitForStateContext(ctx) - if v, ok := outputRaw.(*ecs.Cluster); ok { + if v, ok := outputRaw.(*awstypes.Cluster); ok { return v, err } return nil, err } -func waitClusterDeleted(ctx context.Context, conn *ecs.ECS, arn string) (*ecs.Cluster, error) { +func waitClusterDeleted(ctx context.Context, conn *ecs.Client, arn string) (*awstypes.Cluster, error) { stateConf := &retry.StateChangeConf{ Pending: []string{clusterStatusActive, clusterStatusDeprovisioning}, Target: []string{}, @@ -391,7 +398,7 @@ func waitClusterDeleted(ctx context.Context, conn *ecs.ECS, arn string) (*ecs.Cl outputRaw, err := stateConf.WaitForStateContext(ctx) - if v, ok := outputRaw.(*ecs.Cluster); ok { + if v, ok := outputRaw.(*awstypes.Cluster); ok { return v, err } @@ -400,19 +407,14 @@ func waitClusterDeleted(ctx context.Context, conn *ecs.ECS, arn string) (*ecs.Cl func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) log.Printf("[DEBUG] Deleting ECS Cluster: %s", d.Id()) - _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, clusterDeleteTimeout, func() (interface{}, error) { - return conn.DeleteClusterWithContext(ctx, &ecs.DeleteClusterInput{ + _, err := tfresource.RetryWhenIsOneOf4[*awstypes.ClusterContainsContainerInstancesException, *awstypes.ClusterContainsServicesException, *awstypes.ClusterContainsTasksException, *awstypes.UpdateInProgressException](ctx, clusterDeleteTimeout, func() (interface{}, error) { + return conn.DeleteCluster(ctx, &ecs.DeleteClusterInput{ Cluster: aws.String(d.Id()), }) - }, - ecs.ErrCodeClusterContainsContainerInstancesException, - ecs.ErrCodeClusterContainsServicesException, - ecs.ErrCodeClusterContainsTasksException, - ecs.ErrCodeUpdateInProgressException, - ) + }) if err != nil { return sdkdiag.AppendErrorf(diags, "deleting ECS Cluster (%s): %s", d.Id(), err) @@ -425,13 +427,13 @@ func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta int return diags } -func retryClusterCreate(ctx context.Context, conn *ecs.ECS, input *ecs.CreateClusterInput) (*ecs.CreateClusterOutput, error) { +func retryClusterCreate(ctx context.Context, conn *ecs.Client, input *ecs.CreateClusterInput) (*ecs.CreateClusterOutput, error) { var output *ecs.CreateClusterOutput err := retry.RetryContext(ctx, propagationTimeout, func() *retry.RetryError { var err error - output, err = conn.CreateClusterWithContext(ctx, input) + output, err = conn.CreateCluster(ctx, input) - if tfawserr.ErrMessageContains(err, ecs.ErrCodeInvalidParameterException, "Unable to assume the service linked role") { + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "Unable to assume the service linked role") { return retry.RetryableError(err) } @@ -443,25 +445,25 @@ func retryClusterCreate(ctx context.Context, conn *ecs.ECS, input *ecs.CreateClu }) if tfresource.TimedOut(err) { - output, err = conn.CreateClusterWithContext(ctx, input) + output, err = conn.CreateCluster(ctx, input) } return output, err } -func expandClusterSettings(configured *schema.Set) []*ecs.ClusterSetting { +func expandClusterSettings(configured *schema.Set) []awstypes.ClusterSetting { list := configured.List() if len(list) == 0 { return nil } - settings := make([]*ecs.ClusterSetting, 0, len(list)) + settings := make([]awstypes.ClusterSetting, 0, len(list)) for _, raw := range list { data := raw.(map[string]interface{}) - setting := &ecs.ClusterSetting{ - Name: aws.String(data[names.AttrName].(string)), + setting := awstypes.ClusterSetting{ + Name: awstypes.ClusterSettingName(data[names.AttrName].(string)), Value: aws.String(data[names.AttrValue].(string)), } @@ -471,12 +473,12 @@ func expandClusterSettings(configured *schema.Set) []*ecs.ClusterSetting { return settings } -func expandClusterServiceConnectDefaultsRequest(tfMap map[string]interface{}) *ecs.ClusterServiceConnectDefaultsRequest { +func expandClusterServiceConnectDefaultsRequest(tfMap map[string]interface{}) *awstypes.ClusterServiceConnectDefaultsRequest { if tfMap == nil { return nil } - apiObject := &ecs.ClusterServiceConnectDefaultsRequest{} + apiObject := &awstypes.ClusterServiceConnectDefaultsRequest{} if v, ok := tfMap[names.AttrNamespace].(string); ok && v != "" { apiObject.Namespace = aws.String(v) @@ -485,7 +487,7 @@ func expandClusterServiceConnectDefaultsRequest(tfMap map[string]interface{}) *e return apiObject } -func flattenClusterServiceConnectDefaults(apiObject *ecs.ClusterServiceConnectDefaults) map[string]interface{} { +func flattenClusterServiceConnectDefaults(apiObject *awstypes.ClusterServiceConnectDefaults) map[string]interface{} { if apiObject == nil { return nil } @@ -493,13 +495,13 @@ func flattenClusterServiceConnectDefaults(apiObject *ecs.ClusterServiceConnectDe tfMap := map[string]interface{}{} if v := apiObject.Namespace; v != nil { - tfMap[names.AttrNamespace] = aws.StringValue(v) + tfMap[names.AttrNamespace] = aws.ToString(v) } return tfMap } -func flattenClusterSettings(list []*ecs.ClusterSetting) []map[string]interface{} { +func flattenClusterSettings(list []awstypes.ClusterSetting) []map[string]interface{} { if len(list) == 0 { return nil } @@ -507,8 +509,8 @@ func flattenClusterSettings(list []*ecs.ClusterSetting) []map[string]interface{} result := make([]map[string]interface{}, 0, len(list)) for _, setting := range list { l := map[string]interface{}{ - names.AttrName: aws.StringValue(setting.Name), - names.AttrValue: aws.StringValue(setting.Value), + names.AttrName: string(setting.Name), + names.AttrValue: aws.ToString(setting.Value), } result = append(result, l) @@ -516,7 +518,7 @@ func flattenClusterSettings(list []*ecs.ClusterSetting) []map[string]interface{} return result } -func flattenClusterConfiguration(apiObject *ecs.ClusterConfiguration) []interface{} { +func flattenClusterConfiguration(apiObject *awstypes.ClusterConfiguration) []interface{} { if apiObject == nil { return nil } @@ -529,7 +531,7 @@ func flattenClusterConfiguration(apiObject *ecs.ClusterConfiguration) []interfac return []interface{}{tfMap} } -func flattenClusterConfigurationExecuteCommandConfiguration(apiObject *ecs.ExecuteCommandConfiguration) []interface{} { +func flattenClusterConfigurationExecuteCommandConfiguration(apiObject *awstypes.ExecuteCommandConfiguration) []interface{} { if apiObject == nil { return nil } @@ -537,52 +539,50 @@ func flattenClusterConfigurationExecuteCommandConfiguration(apiObject *ecs.Execu tfMap := map[string]interface{}{} if apiObject.KmsKeyId != nil { - tfMap[names.AttrKMSKeyID] = aws.StringValue(apiObject.KmsKeyId) + tfMap[names.AttrKMSKeyID] = aws.ToString(apiObject.KmsKeyId) } if apiObject.LogConfiguration != nil { tfMap["log_configuration"] = flattenClusterConfigurationExecuteCommandConfigurationLogConfiguration(apiObject.LogConfiguration) } - if apiObject.Logging != nil { - tfMap["logging"] = aws.StringValue(apiObject.Logging) - } + tfMap["logging"] = string(apiObject.Logging) return []interface{}{tfMap} } -func flattenClusterConfigurationExecuteCommandConfigurationLogConfiguration(apiObject *ecs.ExecuteCommandLogConfiguration) []interface{} { +func flattenClusterConfigurationExecuteCommandConfigurationLogConfiguration(apiObject *awstypes.ExecuteCommandLogConfiguration) []interface{} { if apiObject == nil { return nil } tfMap := map[string]interface{}{} - tfMap["cloud_watch_encryption_enabled"] = aws.BoolValue(apiObject.CloudWatchEncryptionEnabled) - tfMap["s3_bucket_encryption_enabled"] = aws.BoolValue(apiObject.S3EncryptionEnabled) + tfMap["cloud_watch_encryption_enabled"] = apiObject.CloudWatchEncryptionEnabled + tfMap["s3_bucket_encryption_enabled"] = apiObject.S3EncryptionEnabled if apiObject.CloudWatchLogGroupName != nil { - tfMap["cloud_watch_log_group_name"] = aws.StringValue(apiObject.CloudWatchLogGroupName) + tfMap["cloud_watch_log_group_name"] = aws.ToString(apiObject.CloudWatchLogGroupName) } if apiObject.S3BucketName != nil { - tfMap[names.AttrS3BucketName] = aws.StringValue(apiObject.S3BucketName) + tfMap[names.AttrS3BucketName] = aws.ToString(apiObject.S3BucketName) } if apiObject.S3KeyPrefix != nil { - tfMap[names.AttrS3KeyPrefix] = aws.StringValue(apiObject.S3KeyPrefix) + tfMap[names.AttrS3KeyPrefix] = aws.ToString(apiObject.S3KeyPrefix) } return []interface{}{tfMap} } -func expandClusterConfiguration(nc []interface{}) *ecs.ClusterConfiguration { +func expandClusterConfiguration(nc []interface{}) *awstypes.ClusterConfiguration { if len(nc) == 0 || nc[0] == nil { - return &ecs.ClusterConfiguration{} + return &awstypes.ClusterConfiguration{} } raw := nc[0].(map[string]interface{}) - config := &ecs.ClusterConfiguration{} + config := &awstypes.ClusterConfiguration{} if v, ok := raw["execute_command_configuration"].([]interface{}); ok && len(v) > 0 { config.ExecuteCommandConfiguration = expandClusterConfigurationExecuteCommandConfiguration(v) } @@ -590,13 +590,13 @@ func expandClusterConfiguration(nc []interface{}) *ecs.ClusterConfiguration { return config } -func expandClusterConfigurationExecuteCommandConfiguration(nc []interface{}) *ecs.ExecuteCommandConfiguration { +func expandClusterConfigurationExecuteCommandConfiguration(nc []interface{}) *awstypes.ExecuteCommandConfiguration { if len(nc) == 0 || nc[0] == nil { - return &ecs.ExecuteCommandConfiguration{} + return &awstypes.ExecuteCommandConfiguration{} } raw := nc[0].(map[string]interface{}) - config := &ecs.ExecuteCommandConfiguration{} + config := &awstypes.ExecuteCommandConfiguration{} if v, ok := raw["log_configuration"].([]interface{}); ok && len(v) > 0 { config.LogConfiguration = expandClusterConfigurationExecuteCommandLogConfiguration(v) } @@ -606,19 +606,19 @@ func expandClusterConfigurationExecuteCommandConfiguration(nc []interface{}) *ec } if v, ok := raw["logging"].(string); ok && v != "" { - config.Logging = aws.String(v) + config.Logging = awstypes.ExecuteCommandLogging(v) } return config } -func expandClusterConfigurationExecuteCommandLogConfiguration(nc []interface{}) *ecs.ExecuteCommandLogConfiguration { +func expandClusterConfigurationExecuteCommandLogConfiguration(nc []interface{}) *awstypes.ExecuteCommandLogConfiguration { if len(nc) == 0 || nc[0] == nil { - return &ecs.ExecuteCommandLogConfiguration{} + return &awstypes.ExecuteCommandLogConfiguration{} } raw := nc[0].(map[string]interface{}) - config := &ecs.ExecuteCommandLogConfiguration{} + config := &awstypes.ExecuteCommandLogConfiguration{} if v, ok := raw["cloud_watch_log_group_name"].(string); ok && v != "" { config.CloudWatchLogGroupName = aws.String(v) @@ -633,11 +633,11 @@ func expandClusterConfigurationExecuteCommandLogConfiguration(nc []interface{}) } if v, ok := raw["cloud_watch_encryption_enabled"].(bool); ok { - config.CloudWatchEncryptionEnabled = aws.Bool(v) + config.CloudWatchEncryptionEnabled = v } if v, ok := raw["s3_bucket_encryption_enabled"].(bool); ok { - config.S3EncryptionEnabled = aws.Bool(v) + config.S3EncryptionEnabled = v } return config diff --git a/internal/service/ecs/cluster_capacity_providers.go b/internal/service/ecs/cluster_capacity_providers.go index bbf9bbc6a61..2ee24509ac1 100644 --- a/internal/service/ecs/cluster_capacity_providers.go +++ b/internal/service/ecs/cluster_capacity_providers.go @@ -111,8 +111,9 @@ func resourceClusterCapacityProvidersRead(ctx context.Context, d *schema.Resourc var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition - cluster, err := FindClusterByNameOrARN(ctx, conn, d.Id()) + cluster, err := FindClusterByNameOrARN(ctx, conn, partition, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { sdkdiag.AppendErrorf(diags, "[WARN] ECS Cluster (%s) not found, removing from state", d.Id()) diff --git a/internal/service/ecs/cluster_data_source.go b/internal/service/ecs/cluster_data_source.go index ca8be066731..2acb1b74d86 100644 --- a/internal/service/ecs/cluster_data_source.go +++ b/internal/service/ecs/cluster_data_source.go @@ -6,7 +6,7 @@ package ecs import ( "context" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -81,17 +81,19 @@ func DataSourceCluster() *schema.Resource { func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig clusterName := d.Get(names.AttrClusterName).(string) - cluster, err := FindClusterByNameOrARN(ctx, conn, d.Get(names.AttrClusterName).(string)) + cluster, err := FindClusterByNameOrARN(ctx, conn, partition, d.Get(names.AttrClusterName).(string)) if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS Cluster (%s): %s", clusterName, err) } - d.SetId(aws.StringValue(cluster.ClusterArn)) + d.SetId(aws.ToString(cluster.ClusterArn)) d.Set(names.AttrARN, cluster.ClusterArn) d.Set("pending_tasks_count", cluster.PendingTasksCount) d.Set("running_tasks_count", cluster.RunningTasksCount) diff --git a/internal/service/ecs/cluster_test.go b/internal/service/ecs/cluster_test.go index 305b38a7109..d0e2ab42715 100644 --- a/internal/service/ecs/cluster_test.go +++ b/internal/service/ecs/cluster_test.go @@ -8,7 +8,7 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -21,7 +21,7 @@ import ( func TestAccECSCluster_basic(t *testing.T) { ctx := acctest.Context(t) - var v ecs.Cluster + var v awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_cluster.test" @@ -60,7 +60,7 @@ func TestAccECSCluster_basic(t *testing.T) { func TestAccECSCluster_disappears(t *testing.T) { ctx := acctest.Context(t) - var v ecs.Cluster + var v awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_cluster.test" @@ -84,7 +84,7 @@ func TestAccECSCluster_disappears(t *testing.T) { func TestAccECSCluster_tags(t *testing.T) { ctx := acctest.Context(t) - var v ecs.Cluster + var v awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_cluster.test" @@ -131,7 +131,7 @@ func TestAccECSCluster_tags(t *testing.T) { func TestAccECSCluster_serviceConnectDefaults(t *testing.T) { ctx := acctest.Context(t) - var v ecs.Cluster + var v awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) ns := fmt.Sprintf("%s-%s", acctest.ResourcePrefix, sdkacctest.RandStringFromCharSet(8, sdkacctest.CharSetAlpha)) resourceName := "aws_ecs_cluster.test" @@ -172,7 +172,7 @@ func TestAccECSCluster_serviceConnectDefaults(t *testing.T) { func TestAccECSCluster_containerInsights(t *testing.T) { ctx := acctest.Context(t) - var cluster1 ecs.Cluster + var cluster1 awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_cluster.test" @@ -219,7 +219,7 @@ func TestAccECSCluster_containerInsights(t *testing.T) { func TestAccECSCluster_configuration(t *testing.T) { ctx := acctest.Context(t) - var cluster1 ecs.Cluster + var cluster1 awstypes.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_cluster.test" @@ -267,14 +267,15 @@ func TestAccECSCluster_configuration(t *testing.T) { func testAccCheckClusterDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) + partition := acctest.Provider.Meta().(*conns.AWSClient).Partition for _, rs := range s.RootModule().Resources { if rs.Type != "aws_ecs_cluster" { continue } - _, err := tfecs.FindClusterByNameOrARN(ctx, conn, rs.Primary.ID) + _, err := tfecs.FindClusterByNameOrARN(ctx, conn, partition, rs.Primary.ID) if tfresource.NotFound(err) { continue @@ -291,7 +292,7 @@ func testAccCheckClusterDestroy(ctx context.Context) resource.TestCheckFunc { } } -func testAccCheckClusterExists(ctx context.Context, n string, v *ecs.Cluster) resource.TestCheckFunc { +func testAccCheckClusterExists(ctx context.Context, n string, v *awstypes.Cluster) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -302,9 +303,10 @@ func testAccCheckClusterExists(ctx context.Context, n string, v *ecs.Cluster) re return fmt.Errorf("No ECS Cluster ID is set") } - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) + partition := acctest.Provider.Meta().(*conns.AWSClient).Partition - output, err := tfecs.FindClusterByNameOrARN(ctx, conn, rs.Primary.ID) + output, err := tfecs.FindClusterByNameOrARN(ctx, conn, partition, rs.Primary.ID) if err != nil { return err diff --git a/internal/tfresource/retry.go b/internal/tfresource/retry.go index 1f8ed0194d8..c12b38a6b28 100644 --- a/internal/tfresource/retry.go +++ b/internal/tfresource/retry.go @@ -182,6 +182,16 @@ func RetryWhenIsOneOf3[T1, T2, T3 error](ctx context.Context, timeout time.Durat }) } +func RetryWhenIsOneOf4[T1, T2, T3, T4 error](ctx context.Context, timeout time.Duration, f func() (interface{}, error)) (interface{}, error) { + return RetryWhen(ctx, timeout, f, func(err error) (bool, error) { + if errs.IsA[T1](err) || errs.IsA[T2](err) || errs.IsA[T3](err) || errs.IsA[T4](err) { + return true, err + } + + return false, err + }) +} + func RetryWhenIsAErrorMessageContains[T errs.ErrorWithErrorMessage](ctx context.Context, timeout time.Duration, f func() (interface{}, error), needle string) (interface{}, error) { return RetryWhen(ctx, timeout, f, func(err error) (bool, error) { if errs.IsAErrorMessageContains[T](err, needle) { From 0d4a4a299c96360b2e8fa3e15b0d56050b5007a8 Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Fri, 14 Jun 2024 00:00:33 +0100 Subject: [PATCH 07/60] d/container_definition: Migrate to AWS SDK v2 --- .../ecs/container_definition_data_source.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/service/ecs/container_definition_data_source.go b/internal/service/ecs/container_definition_data_source.go index 4a2f6cc705c..3f9718a800d 100644 --- a/internal/service/ecs/container_definition_data_source.go +++ b/internal/service/ecs/container_definition_data_source.go @@ -9,8 +9,8 @@ import ( "log" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -73,13 +73,13 @@ func DataSourceContainerDefinition() *schema.Resource { func dataSourceContainerDefinitionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) params := &ecs.DescribeTaskDefinitionInput{ TaskDefinition: aws.String(d.Get("task_definition").(string)), } log.Printf("[DEBUG] Reading ECS Container Definition: %s", params) - desc, err := conn.DescribeTaskDefinitionWithContext(ctx, params) + desc, err := conn.DescribeTaskDefinition(ctx, params) if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition: %s", err) @@ -91,13 +91,13 @@ func dataSourceContainerDefinitionRead(ctx context.Context, d *schema.ResourceDa taskDefinition := desc.TaskDefinition for _, def := range taskDefinition.ContainerDefinitions { - if aws.StringValue(def.Name) != d.Get("container_name").(string) { + if aws.ToString(def.Name) != d.Get("container_name").(string) { continue } - d.SetId(fmt.Sprintf("%s/%s", aws.StringValue(taskDefinition.TaskDefinitionArn), d.Get("container_name").(string))) + d.SetId(fmt.Sprintf("%s/%s", aws.ToString(taskDefinition.TaskDefinitionArn), d.Get("container_name").(string))) d.Set("image", def.Image) - image := aws.StringValue(def.Image) + image := aws.ToString(def.Image) if strings.Contains(image, ":") { d.Set("image_digest", strings.Split(image, ":")[1]) } @@ -105,11 +105,11 @@ func dataSourceContainerDefinitionRead(ctx context.Context, d *schema.ResourceDa d.Set("memory", def.Memory) d.Set("memory_reservation", def.MemoryReservation) d.Set("disable_networking", def.DisableNetworking) - d.Set("docker_labels", aws.StringValueMap(def.DockerLabels)) + d.Set("docker_labels", def.DockerLabels) var environment = map[string]string{} for _, keyValuePair := range def.Environment { - environment[aws.StringValue(keyValuePair.Name)] = aws.StringValue(keyValuePair.Value) + environment[aws.ToString(keyValuePair.Name)] = aws.ToString(keyValuePair.Value) } d.Set(names.AttrEnvironment, environment) } From 67804c9335290c4dbd5359ac63c7eca7f1952487 Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Fri, 14 Jun 2024 00:02:22 +0100 Subject: [PATCH 08/60] d/ecs_service: Migrate to AWS SDK v2 --- internal/service/ecs/service_data_source.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/ecs/service_data_source.go b/internal/service/ecs/service_data_source.go index 9c57ba5f386..ae564969bb9 100644 --- a/internal/service/ecs/service_data_source.go +++ b/internal/service/ecs/service_data_source.go @@ -7,8 +7,8 @@ import ( "context" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -58,7 +58,7 @@ func DataSourceService() *schema.Resource { func dataSourceServiceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig clusterArn := d.Get("cluster_arn").(string) @@ -66,11 +66,11 @@ func dataSourceServiceRead(ctx context.Context, d *schema.ResourceData, meta int params := &ecs.DescribeServicesInput{ Cluster: aws.String(clusterArn), - Services: []*string{aws.String(serviceName)}, + Services: []string{serviceName}, } log.Printf("[DEBUG] Reading ECS Service: %s", params) - desc, err := conn.DescribeServicesWithContext(ctx, params) + desc, err := conn.DescribeServices(ctx, params) if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS Service (%s): %s", serviceName, err) @@ -85,7 +85,7 @@ func dataSourceServiceRead(ctx context.Context, d *schema.ResourceData, meta int } service := desc.Services[0] - d.SetId(aws.StringValue(service.ServiceArn)) + d.SetId(aws.ToString(service.ServiceArn)) d.Set(names.AttrServiceName, service.ServiceName) d.Set(names.AttrARN, service.ServiceArn) From 5f12a376a6e5fdee7250edf20dba384094a1b23c Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Mon, 17 Jun 2024 18:13:01 +0100 Subject: [PATCH 09/60] r/ecs_task_set: Migrate to AWS SDK v2 --- internal/service/ecs/flex.go | 24 ++++----- internal/service/ecs/task_set.go | 75 ++++++++++++++------------- internal/service/ecs/task_set_test.go | 25 ++++----- internal/service/ecs/wait.go | 42 ++++++++------- 4 files changed, 86 insertions(+), 80 deletions(-) diff --git a/internal/service/ecs/flex.go b/internal/service/ecs/flex.go index a52b07ed3d6..e299fc37e33 100644 --- a/internal/service/ecs/flex.go +++ b/internal/service/ecs/flex.go @@ -48,17 +48,17 @@ func flattenCapacityProviderStrategy(cps []awstypes.CapacityProviderStrategyItem // Takes the result of flatmap. Expand for an array of load balancers and // returns ecs.LoadBalancer compatible objects -func expandLoadBalancers(configured []interface{}) []*awstypes.LoadBalancer { - loadBalancers := make([]*awstypes.LoadBalancer, 0, len(configured)) +func expandLoadBalancers(configured []interface{}) []awstypes.LoadBalancer { + loadBalancers := make([]awstypes.LoadBalancer, 0, len(configured)) // Loop over our configured load balancers and create // an array of aws-sdk-go compatible objects for _, lRaw := range configured { data := lRaw.(map[string]interface{}) - l := &awstypes.LoadBalancer{ + l := awstypes.LoadBalancer{ ContainerName: aws.String(data["container_name"].(string)), - ContainerPort: aws.Int64(int64(data["container_port"].(int))), + ContainerPort: aws.Int32(int32(data["container_port"].(int))), } if v, ok := data["elb_name"]; ok && v.(string) != "" { @@ -98,12 +98,12 @@ func flattenLoadBalancers(list []*awstypes.LoadBalancer) []map[string]interface{ // Expand for an array of load balancers and // returns ecs.LoadBalancer compatible objects for an ECS TaskSet -func expandTaskSetLoadBalancers(l []interface{}) []*awstypes.LoadBalancer { +func expandTaskSetLoadBalancers(l []interface{}) []awstypes.LoadBalancer { if len(l) == 0 || l[0] == nil { return nil } - loadBalancers := make([]*awstypes.LoadBalancer, 0, len(l)) + loadBalancers := make([]awstypes.LoadBalancer, 0, len(l)) // Loop over our configured load balancers and create // an array of aws-sdk-go compatible objects @@ -117,7 +117,7 @@ func expandTaskSetLoadBalancers(l []interface{}) []*awstypes.LoadBalancer { } if v, ok := data["container_port"].(int); ok { - l.ContainerPort = aws.Int64(int64(v)) + l.ContainerPort = aws.Int32(int32(v)) } if v, ok := data["load_balancer_name"]; ok && v.(string) != "" { @@ -134,7 +134,7 @@ func expandTaskSetLoadBalancers(l []interface{}) []*awstypes.LoadBalancer { } // Flattens an array of ECS LoadBalancers (of an ECS TaskSet) into a []map[string]interface{} -func flattenTaskSetLoadBalancers(list []*awstypes.LoadBalancer) []map[string]interface{} { +func flattenTaskSetLoadBalancers(list []awstypes.LoadBalancer) []map[string]interface{} { result := make([]map[string]interface{}, 0, len(list)) for _, loadBalancer := range list { l := map[string]interface{}{ @@ -157,8 +157,8 @@ func flattenTaskSetLoadBalancers(list []*awstypes.LoadBalancer) []map[string]int // Expand for an array of service registries and // returns ecs.ServiceRegistry compatible objects for an ECS TaskSet -func expandServiceRegistries(l []interface{}) []*awstypes.ServiceRegistry { - result := make([]*awstypes.ServiceRegistry, 0, len(l)) +func expandServiceRegistries(l []interface{}) []awstypes.ServiceRegistry { + result := make([]awstypes.ServiceRegistry, 0, len(l)) for _, v := range l { m := v.(map[string]interface{}) @@ -169,10 +169,10 @@ func expandServiceRegistries(l []interface{}) []*awstypes.ServiceRegistry { sr.ContainerName = aws.String(raw) } if raw, ok := m["container_port"].(int); ok && raw > 0 { - sr.ContainerPort = aws.Int64(int64(raw)) + sr.ContainerPort = aws.Int32(int32(raw)) } if raw, ok := m[names.AttrPort].(int); ok && raw > 0 { - sr.Port = aws.Int64(int64(raw)) + sr.Port = aws.Int32(int32(raw)) } result = append(result, sr) } diff --git a/internal/service/ecs/task_set.go b/internal/service/ecs/task_set.go index 37e82531fc6..ca5a0b239e8 100644 --- a/internal/service/ecs/task_set.go +++ b/internal/service/ecs/task_set.go @@ -10,14 +10,15 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -87,12 +88,12 @@ func ResourceTaskSet() *schema.Resource { Optional: true, }, "launch_type": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Computed: true, - ValidateFunc: validation.StringInSlice(ecs.LaunchType_Values(), false), - ConflictsWith: []string{names.AttrCapacityProviderStrategy}, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + ValidateDiagFunc: enum.Validate[awstypes.LaunchType](), + ConflictsWith: []string{names.AttrCapacityProviderStrategy}, }, // If you are using the CodeDeploy or an external deployment controller, // multiple target groups are not supported. @@ -172,10 +173,10 @@ func ResourceTaskSet() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ names.AttrUnit: { - Type: schema.TypeString, - Optional: true, - Default: ecs.ScaleUnitPercent, - ValidateFunc: validation.StringInSlice(ecs.ScaleUnit_Values(), false), + Type: schema.TypeString, + Optional: true, + Default: awstypes.ScaleUnitPercent, + ValidateDiagFunc: enum.Validate[awstypes.ScaleUnit](), }, names.AttrValue: { Type: schema.TypeFloat, @@ -273,7 +274,8 @@ func ResourceTaskSet() *schema.Resource { func resourceTaskSetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition cluster := d.Get("cluster").(string) service := d.Get("service").(string) @@ -294,7 +296,7 @@ func resourceTaskSetCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("launch_type"); ok { - input.LaunchType = aws.String(v.(string)) + input.LaunchType = awstypes.LaunchType(v.(string)) } if v, ok := d.GetOk("load_balancer"); ok && v.(*schema.Set).Len() > 0 { @@ -320,7 +322,7 @@ func resourceTaskSetCreate(ctx context.Context, d *schema.ResourceData, meta int output, err := retryTaskSetCreate(ctx, conn, input) // Some partitions (e.g. ISO) may not support tag-on-create. - if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(partition, err) { input.Tags = nil output, err = retryTaskSetCreate(ctx, conn, input) @@ -330,7 +332,7 @@ func resourceTaskSetCreate(ctx context.Context, d *schema.ResourceData, meta int return sdkdiag.AppendErrorf(diags, "creating ECS TaskSet: %s", err) } - taskSetId := aws.StringValue(output.TaskSet.Id) + taskSetId := aws.ToString(output.TaskSet.Id) d.SetId(fmt.Sprintf("%s,%s,%s", taskSetId, service, cluster)) @@ -343,10 +345,10 @@ func resourceTaskSetCreate(ctx context.Context, d *schema.ResourceData, meta int // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { - err := createTags(ctx, conn, aws.StringValue(output.TaskSet.TaskSetArn), tags) + err := createTags(ctx, conn, aws.ToString(output.TaskSet.TaskSetArn), tags) // If default tags only, continue. Otherwise, error. - if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(partition, err) { return append(diags, resourceTaskSetRead(ctx, d, meta)...) } @@ -360,7 +362,8 @@ func resourceTaskSetCreate(ctx context.Context, d *schema.ResourceData, meta int func resourceTaskSetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition taskSetId, service, cluster, err := TaskSetParseID(d.Id()) @@ -370,25 +373,25 @@ func resourceTaskSetRead(ctx context.Context, d *schema.ResourceData, meta inter input := &ecs.DescribeTaskSetsInput{ Cluster: aws.String(cluster), - Include: aws.StringSlice([]string{ecs.TaskSetFieldTags}), + Include: []awstypes.TaskSetField{awstypes.TaskSetFieldTags}, Service: aws.String(service), - TaskSets: aws.StringSlice([]string{taskSetId}), + TaskSets: []string{taskSetId}, } - out, err := conn.DescribeTaskSetsWithContext(ctx, input) + out, err := conn.DescribeTaskSets(ctx, input) - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecs.ErrCodeClusterNotFoundException, ecs.ErrCodeServiceNotFoundException, ecs.ErrCodeTaskSetNotFoundException) { + if !d.IsNewResource() && (errs.IsA[*awstypes.ClusterNotFoundException](err) || errs.IsA[*awstypes.ServiceNotFoundException](err) || errs.IsA[*awstypes.TaskSetNotFoundException](err)) { log.Printf("[WARN] ECS Task Set (%s) not found, removing from state", d.Id()) d.SetId("") return diags } // Some partitions (i.e., ISO) may not support tagging, giving error - if errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if errs.IsUnsupportedOperationInPartitionError(partition, err) { log.Printf("[WARN] ECS tagging failed describing Task Set (%s) with tags: %s; retrying without tags", d.Id(), err) input.Include = nil - out, err = conn.DescribeTaskSetsWithContext(ctx, input) + out, err = conn.DescribeTaskSets(ctx, input) } if err != nil { @@ -444,7 +447,7 @@ func resourceTaskSetRead(ctx context.Context, d *schema.ResourceData, meta inter func resourceTaskSetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { taskSetId, service, cluster, err := TaskSetParseID(d.Id()) @@ -460,7 +463,7 @@ func resourceTaskSetUpdate(ctx context.Context, d *schema.ResourceData, meta int Scale: expandScale(d.Get("scale").([]interface{})), } - _, err = conn.UpdateTaskSetWithContext(ctx, input) + _, err = conn.UpdateTaskSet(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating ECS Task Set (%s): %s", d.Id(), err) @@ -479,7 +482,7 @@ func resourceTaskSetUpdate(ctx context.Context, d *schema.ResourceData, meta int func resourceTaskSetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) taskSetId, service, cluster, err := TaskSetParseID(d.Id()) @@ -494,9 +497,9 @@ func resourceTaskSetDelete(ctx context.Context, d *schema.ResourceData, meta int Force: aws.Bool(d.Get(names.AttrForceDelete).(bool)), } - _, err = conn.DeleteTaskSetWithContext(ctx, input) + _, err = conn.DeleteTaskSet(ctx, input) - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeTaskSetNotFoundException) { + if errs.IsA[*awstypes.TaskSetNotFoundException](err) { return diags } @@ -505,7 +508,7 @@ func resourceTaskSetDelete(ctx context.Context, d *schema.ResourceData, meta int } if err := waitTaskSetDeleted(ctx, conn, taskSetId, service, cluster); err != nil { - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeTaskSetNotFoundException) { + if errs.IsA[*awstypes.TaskSetNotFoundException](err) { return diags } return sdkdiag.AppendErrorf(diags, "deleting ECS Task Set (%s): waiting for completion: %s", d.Id(), err) @@ -524,14 +527,14 @@ func TaskSetParseID(id string) (string, string, string, error) { return parts[0], parts[1], parts[2], nil } -func retryTaskSetCreate(ctx context.Context, conn *ecs.ECS, input *ecs.CreateTaskSetInput) (*ecs.CreateTaskSetOutput, error) { +func retryTaskSetCreate(ctx context.Context, conn *ecs.Client, input *ecs.CreateTaskSetInput) (*ecs.CreateTaskSetOutput, error) { outputRaw, err := tfresource.RetryWhen(ctx, propagationTimeout+taskSetCreateTimeout, func() (interface{}, error) { - return conn.CreateTaskSetWithContext(ctx, input) + return conn.CreateTaskSet(ctx, input) }, func(err error) (bool, error) { - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeClusterNotFoundException, ecs.ErrCodeServiceNotFoundException, ecs.ErrCodeTaskSetNotFoundException) || - tfawserr.ErrMessageContains(err, ecs.ErrCodeInvalidParameterException, "does not have an associated load balancer") { + if errs.IsA[*awstypes.ClusterNotFoundException](err) || errs.IsA[*awstypes.ServiceNotFoundException](err) || errs.IsA[*awstypes.TaskSetNotFoundException](err) || + errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "does not have an associated load balancer") { return true, err } return false, err diff --git a/internal/service/ecs/task_set_test.go b/internal/service/ecs/task_set_test.go index bfb85c04592..27fd20bd202 100644 --- a/internal/service/ecs/task_set_test.go +++ b/internal/service/ecs/task_set_test.go @@ -9,14 +9,15 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs" tfecs "github.com/hashicorp/terraform-provider-aws/internal/service/ecs" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -103,7 +104,7 @@ func TestAccECSTaskSet_withScale(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckTaskSetExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "scale.#", acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "scale.0.unit", ecs.ScaleUnitPercent), + resource.TestCheckResourceAttr(resourceName, "scale.0.unit", string(awstypes.ScaleUnitPercent)), resource.TestCheckResourceAttr(resourceName, "scale.0.value", acctest.Ct0), ), }, @@ -120,7 +121,7 @@ func TestAccECSTaskSet_withScale(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckTaskSetExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "scale.#", acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "scale.0.unit", ecs.ScaleUnitPercent), + resource.TestCheckResourceAttr(resourceName, "scale.0.unit", string(awstypes.ScaleUnitPercent)), resource.TestCheckResourceAttr(resourceName, "scale.0.value", "100"), ), }, @@ -397,7 +398,7 @@ func testAccCheckTaskSetExists(ctx context.Context, name string) resource.TestCh return fmt.Errorf("Not found: %s", name) } - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) taskSetId, service, cluster, err := tfecs.TaskSetParseID(rs.Primary.ID) @@ -406,12 +407,12 @@ func testAccCheckTaskSetExists(ctx context.Context, name string) resource.TestCh } input := &ecs.DescribeTaskSetsInput{ - TaskSets: aws.StringSlice([]string{taskSetId}), + TaskSets: []string{taskSetId}, Cluster: aws.String(cluster), Service: aws.String(service), } - output, err := conn.DescribeTaskSetsWithContext(ctx, input) + output, err := conn.DescribeTaskSets(ctx, input) if err != nil { return err @@ -427,7 +428,7 @@ func testAccCheckTaskSetExists(ctx context.Context, name string) resource.TestCh func testAccCheckTaskSetDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_ecs_task_set" { @@ -441,14 +442,14 @@ func testAccCheckTaskSetDestroy(ctx context.Context) resource.TestCheckFunc { } input := &ecs.DescribeTaskSetsInput{ - TaskSets: aws.StringSlice([]string{taskSetId}), + TaskSets: []string{taskSetId}, Cluster: aws.String(cluster), Service: aws.String(service), } - output, err := conn.DescribeTaskSetsWithContext(ctx, input) + output, err := conn.DescribeTaskSets(ctx, input) - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeClusterNotFoundException, ecs.ErrCodeServiceNotFoundException, ecs.ErrCodeTaskSetNotFoundException) { + if errs.IsA[*awstypes.ClusterNotFoundException](err) || errs.IsA[*awstypes.ServiceNotFoundException](err) || errs.IsA[*awstypes.TaskSetNotFoundException](err) { continue } diff --git a/internal/service/ecs/wait.go b/internal/service/ecs/wait.go index 47546258871..d73c83f8c0b 100644 --- a/internal/service/ecs/wait.go +++ b/internal/service/ecs/wait.go @@ -7,9 +7,11 @@ import ( "context" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/enum" ) const ( @@ -31,9 +33,9 @@ const ( taskSetDeleteTimeout = 10 * time.Minute ) -func waitCapacityProviderDeleted(ctx context.Context, conn *ecs.ECS, arn string) (*ecs.CapacityProvider, error) { +func waitCapacityProviderDeleted(ctx context.Context, conn *ecs.Client, arn string) (*awstypes.CapacityProvider, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{ecs.CapacityProviderStatusActive}, + Pending: enum.Slice(awstypes.CapacityProviderStatusActive), Target: []string{}, Refresh: statusCapacityProvider(ctx, conn, arn), Timeout: capacityProviderDeleteTimeout, @@ -41,24 +43,24 @@ func waitCapacityProviderDeleted(ctx context.Context, conn *ecs.ECS, arn string) outputRaw, err := stateConf.WaitForStateContext(ctx) - if v, ok := outputRaw.(*ecs.CapacityProvider); ok { + if v, ok := outputRaw.(*awstypes.CapacityProvider); ok { return v, err } return nil, err } -func waitCapacityProviderUpdated(ctx context.Context, conn *ecs.ECS, arn string) (*ecs.CapacityProvider, error) { +func waitCapacityProviderUpdated(ctx context.Context, conn *ecs.Client, arn string) (*awstypes.CapacityProvider, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{ecs.CapacityProviderUpdateStatusUpdateInProgress}, - Target: []string{ecs.CapacityProviderUpdateStatusUpdateComplete}, + Pending: enum.Slice(awstypes.CapacityProviderUpdateStatusUpdateInProgress), + Target: enum.Slice(awstypes.CapacityProviderUpdateStatusUpdateComplete), Refresh: statusCapacityProviderUpdate(ctx, conn, arn), Timeout: capacityProviderUpdateTimeout, } outputRaw, err := stateConf.WaitForStateContext(ctx) - if v, ok := outputRaw.(*ecs.CapacityProvider); ok { + if v, ok := outputRaw.(*awstypes.CapacityProvider); ok { return v, err } @@ -66,9 +68,9 @@ func waitCapacityProviderUpdated(ctx context.Context, conn *ecs.ECS, arn string) } // waitServiceStable waits for an ECS Service to reach the status "ACTIVE" and have all desired tasks running. Does not return tags. -func waitServiceStable(ctx context.Context, conn *ecs.ECS, id, cluster string, timeout time.Duration) (*ecs.Service, error) { +func waitServiceStable(ctx context.Context, conn *ecs.Client, id, cluster string, timeout time.Duration) (*awstypes.Service, error) { input := &ecs.DescribeServicesInput{ - Services: aws.StringSlice([]string{id}), + Services: []string{id}, } if cluster != "" { @@ -84,7 +86,7 @@ func waitServiceStable(ctx context.Context, conn *ecs.ECS, id, cluster string, t outputRaw, err := stateConf.WaitForStateContext(ctx) - if v, ok := outputRaw.(*ecs.Service); ok { + if v, ok := outputRaw.(*awstypes.Service); ok { return v, err } @@ -92,9 +94,9 @@ func waitServiceStable(ctx context.Context, conn *ecs.ECS, id, cluster string, t } // waitServiceInactive waits for an ECS Service to reach the status "INACTIVE". -func waitServiceInactive(ctx context.Context, conn *ecs.ECS, id, cluster string, timeout time.Duration) error { +func waitServiceInactive(ctx context.Context, conn *ecs.Client, id, cluster string, timeout time.Duration) error { input := &ecs.DescribeServicesInput{ - Services: aws.StringSlice([]string{id}), + Services: []string{id}, } if cluster != "" { @@ -115,7 +117,7 @@ func waitServiceInactive(ctx context.Context, conn *ecs.ECS, id, cluster string, } // waitServiceActive waits for an ECS Service to reach the status "ACTIVE". Does not return tags. -func waitServiceActive(ctx context.Context, conn *ecs.ECS, id, cluster string, timeout time.Duration) (*ecs.Service, error) { +func waitServiceActive(ctx context.Context, conn *ecs.Client, id, cluster string, timeout time.Duration) (*awstypes.Service, error) { stateConf := &retry.StateChangeConf{ Pending: []string{serviceStatusInactive, serviceStatusDraining}, Target: []string{serviceStatusActive}, @@ -125,17 +127,17 @@ func waitServiceActive(ctx context.Context, conn *ecs.ECS, id, cluster string, t outputRaw, err := stateConf.WaitForStateContext(ctx) - if v, ok := outputRaw.(*ecs.Service); ok { + if v, ok := outputRaw.(*awstypes.Service); ok { return v, err } return nil, err } -func waitTaskSetStable(ctx context.Context, conn *ecs.ECS, timeout time.Duration, taskSetID, service, cluster string) error { +func waitTaskSetStable(ctx context.Context, conn *ecs.Client, timeout time.Duration, taskSetID, service, cluster string) error { stateConf := &retry.StateChangeConf{ - Pending: []string{ecs.StabilityStatusStabilizing}, - Target: []string{ecs.StabilityStatusSteadyState}, + Pending: enum.Slice(awstypes.StabilityStatusStabilizing), + Target: enum.Slice(awstypes.StabilityStatusSteadyState), Refresh: stabilityStatusTaskSet(ctx, conn, taskSetID, service, cluster), Timeout: timeout, } @@ -145,7 +147,7 @@ func waitTaskSetStable(ctx context.Context, conn *ecs.ECS, timeout time.Duration return err } -func waitTaskSetDeleted(ctx context.Context, conn *ecs.ECS, taskSetID, service, cluster string) error { +func waitTaskSetDeleted(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) error { stateConf := &retry.StateChangeConf{ Pending: []string{taskSetStatusActive, taskSetStatusPrimary, taskSetStatusDraining}, Target: []string{}, From ec8eb531ac934b705bcc486e0d6e71098f79f65d Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Mon, 17 Jun 2024 18:27:16 +0100 Subject: [PATCH 10/60] d/ecs_task_execution: Migrate to AWS SDK v2 --- .../service/ecs/task_execution_data_source.go | 82 ++++++++++--------- .../ecs/task_execution_data_source_test.go | 7 +- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/internal/service/ecs/task_execution_data_source.go b/internal/service/ecs/task_execution_data_source.go index b7a03839bfa..e5730950738 100644 --- a/internal/service/ecs/task_execution_data_source.go +++ b/internal/service/ecs/task_execution_data_source.go @@ -7,13 +7,15 @@ import ( "context" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -74,9 +76,9 @@ func DataSourceTaskExecution() *schema.Resource { Optional: true, }, "launch_type": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(ecs.LaunchType_Values(), false), + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: enum.Validate[awstypes.LaunchType](), }, names.AttrNetworkConfiguration: { Type: schema.TypeList, @@ -158,9 +160,9 @@ func DataSourceTaskExecution() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ names.AttrType: { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(ecs.ResourceType_Values(), false), + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: enum.Validate[awstypes.ResourceType](), }, names.AttrValue: { Type: schema.TypeString, @@ -218,9 +220,9 @@ func DataSourceTaskExecution() *schema.Resource { Optional: true, }, names.AttrType: { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(ecs.PlacementConstraintType_Values(), false), + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: enum.Validate[awstypes.PlacementConstraintType](), }, }, }, @@ -247,9 +249,9 @@ func DataSourceTaskExecution() *schema.Resource { Optional: true, }, names.AttrPropagateTags: { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(ecs.PropagateTags_Values(), false), + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: enum.Validate[awstypes.PropagateTags](), }, "reference_id": { Type: schema.TypeString, @@ -279,7 +281,7 @@ const ( func dataSourceTaskExecutionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) cluster := d.Get("cluster").(string) taskDefinition := d.Get("task_definition").(string) @@ -303,19 +305,19 @@ func dataSourceTaskExecutionRead(ctx context.Context, d *schema.ResourceData, me input.ClientToken = aws.String(v.(string)) } if v, ok := d.GetOk("desired_count"); ok { - input.Count = aws.Int64(int64(v.(int))) + input.Count = aws.Int32(int32(v.(int))) } if v, ok := d.GetOk("enable_ecs_managed_tags"); ok { - input.EnableECSManagedTags = aws.Bool(v.(bool)) + input.EnableECSManagedTags = v.(bool) } if v, ok := d.GetOk("enable_execute_command"); ok { - input.EnableExecuteCommand = aws.Bool(v.(bool)) + input.EnableExecuteCommand = v.(bool) } if v, ok := d.GetOk("group"); ok { input.Group = aws.String(v.(string)) } if v, ok := d.GetOk("launch_type"); ok { - input.LaunchType = aws.String(v.(string)) + input.LaunchType = awstypes.LaunchType(v.(string)) } if v, ok := d.GetOk(names.AttrNetworkConfiguration); ok { input.NetworkConfiguration = expandNetworkConfiguration(v.([]interface{})) @@ -341,7 +343,7 @@ func dataSourceTaskExecutionRead(ctx context.Context, d *schema.ResourceData, me input.PlatformVersion = aws.String(v.(string)) } if v, ok := d.GetOk(names.AttrPropagateTags); ok { - input.PropagateTags = aws.String(v.(string)) + input.PropagateTags = awstypes.PropagateTags(v.(string)) } if v, ok := d.GetOk("reference_id"); ok { input.ReferenceId = aws.String(v.(string)) @@ -350,7 +352,7 @@ func dataSourceTaskExecutionRead(ctx context.Context, d *schema.ResourceData, me input.StartedBy = aws.String(v.(string)) } - out, err := conn.RunTaskWithContext(ctx, &input) + out, err := conn.RunTask(ctx, &input) if err != nil { return create.AppendDiagError(diags, names.ECS, create.ErrActionCreating, DSNameTaskExecution, d.Id(), err) } @@ -367,12 +369,12 @@ func dataSourceTaskExecutionRead(ctx context.Context, d *schema.ResourceData, me return diags } -func expandTaskOverride(tfList []interface{}) *ecs.TaskOverride { +func expandTaskOverride(tfList []interface{}) *awstypes.TaskOverride { if len(tfList) == 0 { return nil } - apiObject := &ecs.TaskOverride{} + apiObject := &awstypes.TaskOverride{} tfMap := tfList[0].(map[string]interface{}) if v, ok := tfMap["cpu"]; ok { @@ -397,15 +399,15 @@ func expandTaskOverride(tfList []interface{}) *ecs.TaskOverride { return apiObject } -func expandInferenceAcceleratorOverrides(tfSet *schema.Set) []*ecs.InferenceAcceleratorOverride { +func expandInferenceAcceleratorOverrides(tfSet *schema.Set) []awstypes.InferenceAcceleratorOverride { if tfSet.Len() == 0 { return nil } - apiObject := make([]*ecs.InferenceAcceleratorOverride, 0) + apiObject := make([]awstypes.InferenceAcceleratorOverride, 0) for _, item := range tfSet.List() { tfMap := item.(map[string]interface{}) - iao := &ecs.InferenceAcceleratorOverride{ + iao := awstypes.InferenceAcceleratorOverride{ DeviceName: aws.String(tfMap[names.AttrDeviceName].(string)), DeviceType: aws.String(tfMap["device_type"].(string)), } @@ -415,32 +417,32 @@ func expandInferenceAcceleratorOverrides(tfSet *schema.Set) []*ecs.InferenceAcce return apiObject } -func expandContainerOverride(tfList []interface{}) []*ecs.ContainerOverride { +func expandContainerOverride(tfList []interface{}) []awstypes.ContainerOverride { if len(tfList) == 0 { return nil } - apiObject := make([]*ecs.ContainerOverride, 0) + apiObject := make([]awstypes.ContainerOverride, 0) for _, item := range tfList { tfMap := item.(map[string]interface{}) - co := &ecs.ContainerOverride{ + co := awstypes.ContainerOverride{ Name: aws.String(tfMap[names.AttrName].(string)), } if v, ok := tfMap["command"]; ok { commandStrings := v.([]interface{}) - co.Command = flex.ExpandStringList(commandStrings) + co.Command = flex.ExpandStringValueList(commandStrings) } if v, ok := tfMap["cpu"]; ok { - co.Cpu = aws.Int64(int64(v.(int))) + co.Cpu = aws.Int32(int32(v.(int))) } if v, ok := tfMap[names.AttrEnvironment]; ok { co.Environment = expandTaskEnvironment(v.(*schema.Set)) } if v, ok := tfMap["memory"]; ok { - co.Memory = aws.Int64(int64(v.(int))) + co.Memory = aws.Int32(int32(v.(int))) } if v, ok := tfMap["memory_reservation"]; ok { - co.MemoryReservation = aws.Int64(int64(v.(int))) + co.MemoryReservation = aws.Int32(int32(v.(int))) } if v, ok := tfMap["resource_requirements"]; ok { co.ResourceRequirements = expandResourceRequirements(v.(*schema.Set)) @@ -451,15 +453,15 @@ func expandContainerOverride(tfList []interface{}) []*ecs.ContainerOverride { return apiObject } -func expandTaskEnvironment(tfSet *schema.Set) []*ecs.KeyValuePair { +func expandTaskEnvironment(tfSet *schema.Set) []awstypes.KeyValuePair { if tfSet.Len() == 0 { return nil } - apiObject := make([]*ecs.KeyValuePair, 0) + apiObject := make([]awstypes.KeyValuePair, 0) for _, item := range tfSet.List() { tfMap := item.(map[string]interface{}) - te := &ecs.KeyValuePair{ + te := awstypes.KeyValuePair{ Name: aws.String(tfMap[names.AttrKey].(string)), Value: aws.String(tfMap[names.AttrValue].(string)), } @@ -469,16 +471,16 @@ func expandTaskEnvironment(tfSet *schema.Set) []*ecs.KeyValuePair { return apiObject } -func expandResourceRequirements(tfSet *schema.Set) []*ecs.ResourceRequirement { +func expandResourceRequirements(tfSet *schema.Set) []awstypes.ResourceRequirement { if tfSet.Len() == 0 { return nil } - apiObject := make([]*ecs.ResourceRequirement, 0) + apiObject := make([]awstypes.ResourceRequirement, 0) for _, item := range tfSet.List() { tfMap := item.(map[string]interface{}) - rr := &ecs.ResourceRequirement{ - Type: aws.String(tfMap[names.AttrType].(string)), + rr := awstypes.ResourceRequirement{ + Type: awstypes.ResourceType(tfMap[names.AttrType].(string)), Value: aws.String(tfMap[names.AttrValue].(string)), } apiObject = append(apiObject, rr) diff --git a/internal/service/ecs/task_execution_data_source_test.go b/internal/service/ecs/task_execution_data_source_test.go index 3537c37f3fc..b5b2dc43b63 100644 --- a/internal/service/ecs/task_execution_data_source_test.go +++ b/internal/service/ecs/task_execution_data_source_test.go @@ -7,7 +7,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/service/ecs" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" @@ -28,7 +27,7 @@ func TestAccECSTaskExecutionDataSource_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, ecs.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.ECSEndpointID) }, ErrorCheck: acctest.ErrorCheck(t, names.ECSServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -63,7 +62,7 @@ func TestAccECSTaskExecutionDataSource_overrides(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, ecs.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.ECSEndpointID) }, ErrorCheck: acctest.ErrorCheck(t, names.ECSServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -102,7 +101,7 @@ func TestAccECSTaskExecutionDataSource_tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, ecs.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.ECSEndpointID) }, ErrorCheck: acctest.ErrorCheck(t, names.ECSServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, From cb7a2ff49fd5e83ed2b10c971db62e35ebdeebe3 Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:22:53 +0100 Subject: [PATCH 11/60] d/ecs_task_definition, r/ecs_task_definition: Migrate to AWS SDK v2 --- internal/service/ecs/task_definition.go | 326 +++++++++--------- .../ecs/task_definition_data_source.go | 12 +- .../ecs/task_definition_equivalency.go | 24 +- .../service/ecs/task_definition_migrate.go | 10 +- internal/service/ecs/task_definition_test.go | 99 +++--- 5 files changed, 229 insertions(+), 242 deletions(-) diff --git a/internal/service/ecs/task_definition.go b/internal/service/ecs/task_definition.go index acaeb3477f7..17c90e85b96 100644 --- a/internal/service/ecs/task_definition.go +++ b/internal/service/ecs/task_definition.go @@ -12,16 +12,18 @@ import ( "strings" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/arn" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" - "github.com/aws/aws-sdk-go/service/ecs" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "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/enum" "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" @@ -92,7 +94,7 @@ func ResourceTaskDefinition() *schema.Resource { }, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { networkMode, ok := d.GetOk("network_mode") - isAWSVPC := ok && networkMode.(string) == ecs.NetworkModeAwsvpc + isAWSVPC := ok && networkMode.(string) == string(awstypes.NetworkModeAwsvpc) equal, _ := ContainerDefinitionsAreEquivalent(old, new, isAWSVPC) return equal }, @@ -154,10 +156,10 @@ func ResourceTaskDefinition() *schema.Resource { }, }, "ipc_mode": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(ecs.IpcMode_Values(), false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.IpcMode](), }, "memory": { Type: schema.TypeString, @@ -165,17 +167,17 @@ func ResourceTaskDefinition() *schema.Resource { ForceNew: true, }, "network_mode": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(ecs.NetworkMode_Values(), false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.NetworkMode](), }, "pid_mode": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(ecs.PidMode_Values(), false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.PidMode](), }, "placement_constraints": { Type: schema.TypeSet, @@ -190,10 +192,10 @@ func ResourceTaskDefinition() *schema.Resource { Optional: true, }, names.AttrType: { - Type: schema.TypeString, - ForceNew: true, - Required: true, - ValidateFunc: validation.StringInSlice(ecs.TaskDefinitionPlacementConstraintType_Values(), false), + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateDiagFunc: enum.Validate[awstypes.TaskDefinitionPlacementConstraintType](), }, }, }, @@ -217,11 +219,11 @@ func ResourceTaskDefinition() *schema.Resource { ForceNew: true, }, names.AttrType: { - Type: schema.TypeString, - Default: ecs.ProxyConfigurationTypeAppmesh, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(ecs.ProxyConfigurationType_Values(), false), + Type: schema.TypeString, + Default: awstypes.ProxyConfigurationTypeAppmesh, + Optional: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.ProxyConfigurationType](), }, }, }, @@ -251,16 +253,16 @@ func ResourceTaskDefinition() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "cpu_architecture": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(ecs.CPUArchitecture_Values(), false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.CPUArchitecture](), }, "operating_system_family": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(ecs.OSFamily_Values(), false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.OSFamily](), }, }, }, @@ -320,11 +322,11 @@ func ResourceTaskDefinition() *schema.Resource { Optional: true, }, names.AttrScope: { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(ecs.Scope_Values(), false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.Scope](), }, }, }, @@ -349,10 +351,10 @@ func ResourceTaskDefinition() *schema.Resource { Optional: true, }, "iam": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - ValidateFunc: validation.StringInSlice(ecs.EFSAuthorizationConfigIAM_Values(), false), + Type: schema.TypeString, + ForceNew: true, + Optional: true, + ValidateDiagFunc: enum.Validate[awstypes.EFSAuthorizationConfigIAM](), }, }, }, @@ -369,10 +371,10 @@ func ResourceTaskDefinition() *schema.Resource { Default: "/", }, "transit_encryption": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - ValidateFunc: validation.StringInSlice(ecs.EFSTransitEncryption_Values(), false), + Type: schema.TypeString, + ForceNew: true, + Optional: true, + ValidateDiagFunc: enum.Validate[awstypes.EFSTransitEncryption](), }, "transit_encryption_port": { Type: schema.TypeInt, @@ -460,7 +462,8 @@ func ValidTaskDefinitionContainerDefinitions(v interface{}, k string) (ws []stri func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition rawDefinitions := d.Get("container_definitions").(string) definitions, err := expandContainerDefinitions(rawDefinitions) @@ -491,7 +494,7 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m } if v, ok := d.GetOk("ipc_mode"); ok { - input.IpcMode = aws.String(v.(string)) + input.IpcMode = awstypes.IpcMode(v.(string)) } if v, ok := d.GetOk("memory"); ok { @@ -499,11 +502,11 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m } if v, ok := d.GetOk("network_mode"); ok { - input.NetworkMode = aws.String(v.(string)) + input.NetworkMode = awstypes.NetworkMode(v.(string)) } if v, ok := d.GetOk("pid_mode"); ok { - input.PidMode = aws.String(v.(string)) + input.PidMode = awstypes.PidMode(v.(string)) } if constraints := d.Get("placement_constraints").(*schema.Set).List(); len(constraints) > 0 { @@ -519,7 +522,7 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m } if v, ok := d.GetOk("requires_compatibilities"); ok && v.(*schema.Set).Len() > 0 { - input.RequiresCompatibilities = flex.ExpandStringSet(v.(*schema.Set)) + input.RequiresCompatibilities = flex.ExpandStringyValueSet[awstypes.Compatibility](v.(*schema.Set)) } if runtimePlatformConfigs := d.Get("runtime_platform").([]interface{}); len(runtimePlatformConfigs) > 0 && runtimePlatformConfigs[0] != nil { @@ -535,13 +538,13 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m input.Volumes = volumes } - output, err := conn.RegisterTaskDefinitionWithContext(ctx, input) + output, err := conn.RegisterTaskDefinition(ctx, input) // Some partitions (e.g. ISO) may not support tag-on-create. - if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(partition, err) { input.Tags = nil - output, err = conn.RegisterTaskDefinitionWithContext(ctx, input) + output, err = conn.RegisterTaskDefinition(ctx, input) } if err != nil { @@ -550,16 +553,16 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m taskDefinition := *output.TaskDefinition // nosemgrep:ci.semgrep.aws.prefer-pointer-conversion-assignment // false positive - d.SetId(aws.StringValue(taskDefinition.Family)) + d.SetId(aws.ToString(taskDefinition.Family)) d.Set(names.AttrARN, taskDefinition.TaskDefinitionArn) - d.Set("arn_without_revision", StripRevision(aws.StringValue(taskDefinition.TaskDefinitionArn))) + d.Set("arn_without_revision", StripRevision(aws.ToString(taskDefinition.TaskDefinitionArn))) // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { - err := createTags(ctx, conn, aws.StringValue(taskDefinition.TaskDefinitionArn), tags) + err := createTags(ctx, conn, aws.ToString(taskDefinition.TaskDefinitionArn), tags) // If default tags only, continue. Otherwise, error. - if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(partition, err) { return append(diags, resourceTaskDefinitionRead(ctx, d, meta)...) } @@ -573,7 +576,8 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m func resourceTaskDefinitionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition trackedTaskDefinition := d.Get(names.AttrARN).(string) if _, ok := d.GetOk("track_latest"); ok { @@ -581,38 +585,38 @@ func resourceTaskDefinitionRead(ctx context.Context, d *schema.ResourceData, met } input := ecs.DescribeTaskDefinitionInput{ - Include: aws.StringSlice([]string{ecs.TaskDefinitionFieldTags}), + Include: []awstypes.TaskDefinitionField{awstypes.TaskDefinitionFieldTags}, TaskDefinition: aws.String(trackedTaskDefinition), } - out, err := conn.DescribeTaskDefinitionWithContext(ctx, &input) + out, err := conn.DescribeTaskDefinition(ctx, &input) // Some partitions (i.e., ISO) may not support tagging, giving error - if errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if errs.IsUnsupportedOperationInPartitionError(partition, err) { log.Printf("[WARN] ECS tagging failed describing Task Definition (%s) with tags: %s; retrying without tags", d.Id(), err) input.Include = nil - out, err = conn.DescribeTaskDefinitionWithContext(ctx, &input) + out, err = conn.DescribeTaskDefinition(ctx, &input) } if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition (%s): %s", d.Id(), err) } - log.Printf("[DEBUG] Received task definition %s, status:%s\n %s", aws.StringValue(out.TaskDefinition.Family), - aws.StringValue(out.TaskDefinition.Status), out) + log.Printf("[DEBUG] Received task definition %s, status:%s\n %s", aws.ToString(out.TaskDefinition.Family), + string(out.TaskDefinition.Status), out) taskDefinition := out.TaskDefinition - if aws.StringValue(taskDefinition.Status) == ecs.TaskDefinitionStatusInactive { - log.Printf("[DEBUG] Removing ECS task definition %s because it's INACTIVE", aws.StringValue(out.TaskDefinition.Family)) + if taskDefinition.Status == awstypes.TaskDefinitionStatusInactive { + log.Printf("[DEBUG] Removing ECS task definition %s because it's INACTIVE", aws.ToString(out.TaskDefinition.Family)) d.SetId("") return diags } - d.SetId(aws.StringValue(taskDefinition.Family)) + d.SetId(aws.ToString(taskDefinition.Family)) d.Set(names.AttrARN, taskDefinition.TaskDefinitionArn) - d.Set("arn_without_revision", StripRevision(aws.StringValue(taskDefinition.TaskDefinitionArn))) + d.Set("arn_without_revision", StripRevision(aws.ToString(taskDefinition.TaskDefinitionArn))) d.Set(names.AttrFamily, taskDefinition.Family) d.Set("revision", taskDefinition.Revision) d.Set("track_latest", d.Get("track_latest")) @@ -653,7 +657,7 @@ func resourceTaskDefinitionRead(ctx context.Context, d *schema.ResourceData, met return sdkdiag.AppendErrorf(diags, "setting placement_constraints: %s", err) } - if err := d.Set("requires_compatibilities", flex.FlattenStringList(taskDefinition.RequiresCompatibilities)); err != nil { + if err := d.Set("requires_compatibilities", flex.FlattenStringyValueList(taskDefinition.RequiresCompatibilities)); err != nil { return sdkdiag.AppendErrorf(diags, "setting requires_compatibilities: %s", err) } @@ -689,9 +693,9 @@ func resourceTaskDefinitionDelete(ctx context.Context, d *schema.ResourceData, m return diags } - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) - _, err := conn.DeregisterTaskDefinitionWithContext(ctx, &ecs.DeregisterTaskDefinitionInput{ + _, err := conn.DeregisterTaskDefinition(ctx, &ecs.DeregisterTaskDefinitionInput{ TaskDefinition: aws.String(d.Get(names.AttrARN).(string)), }) if err != nil { @@ -760,27 +764,27 @@ func resourceTaskDefinitionVolumeHash(v interface{}) int { return create.StringHashcode(buf.String()) } -func flattenPlacementConstraints(pcs []*ecs.TaskDefinitionPlacementConstraint) []map[string]interface{} { +func flattenPlacementConstraints(pcs []awstypes.TaskDefinitionPlacementConstraint) []map[string]interface{} { if len(pcs) == 0 { return nil } results := make([]map[string]interface{}, 0) for _, pc := range pcs { c := make(map[string]interface{}) - c[names.AttrType] = aws.StringValue(pc.Type) - c[names.AttrExpression] = aws.StringValue(pc.Expression) + c[names.AttrType] = string(pc.Type) + c[names.AttrExpression] = aws.ToString(pc.Expression) results = append(results, c) } return results } -func flattenRuntimePlatform(rp *ecs.RuntimePlatform) []map[string]interface{} { +func flattenRuntimePlatform(rp *awstypes.RuntimePlatform) []map[string]interface{} { if rp == nil { return nil } - os := aws.StringValue(rp.OperatingSystemFamily) - cpu := aws.StringValue(rp.CpuArchitecture) + os := string(rp.OperatingSystemFamily) + cpu := string(rp.CpuArchitecture) if os == "" && cpu == "" { return nil @@ -800,7 +804,7 @@ func flattenRuntimePlatform(rp *ecs.RuntimePlatform) []map[string]interface{} { } } -func flattenProxyConfiguration(pc *ecs.ProxyConfiguration) []map[string]interface{} { +func flattenProxyConfiguration(pc *awstypes.ProxyConfiguration) []map[string]interface{} { if pc == nil { return nil } @@ -808,13 +812,13 @@ func flattenProxyConfiguration(pc *ecs.ProxyConfiguration) []map[string]interfac meshProperties := make(map[string]string) if pc.Properties != nil { for _, prop := range pc.Properties { - meshProperties[aws.StringValue(prop.Name)] = aws.StringValue(prop.Value) + meshProperties[aws.ToString(prop.Name)] = aws.ToString(prop.Value) } } config := make(map[string]interface{}) - config["container_name"] = aws.StringValue(pc.ContainerName) - config[names.AttrType] = aws.StringValue(pc.Type) + config["container_name"] = aws.ToString(pc.ContainerName) + config[names.AttrType] = string(pc.Type) config[names.AttrProperties] = meshProperties return []map[string]interface{}{ @@ -822,12 +826,12 @@ func flattenProxyConfiguration(pc *ecs.ProxyConfiguration) []map[string]interfac } } -func flattenInferenceAccelerators(list []*ecs.InferenceAccelerator) []map[string]interface{} { +func flattenInferenceAccelerators(list []awstypes.InferenceAccelerator) []map[string]interface{} { result := make([]map[string]interface{}, 0, len(list)) for _, iAcc := range list { l := map[string]interface{}{ - names.AttrDeviceName: aws.StringValue(iAcc.DeviceName), - "device_type": aws.StringValue(iAcc.DeviceType), + names.AttrDeviceName: aws.ToString(iAcc.DeviceName), + "device_type": aws.ToString(iAcc.DeviceType), } result = append(result, l) @@ -835,11 +839,11 @@ func flattenInferenceAccelerators(list []*ecs.InferenceAccelerator) []map[string return result } -func expandInferenceAccelerators(configured []interface{}) []*ecs.InferenceAccelerator { - iAccs := make([]*ecs.InferenceAccelerator, 0, len(configured)) +func expandInferenceAccelerators(configured []interface{}) []awstypes.InferenceAccelerator { + iAccs := make([]awstypes.InferenceAccelerator, 0, len(configured)) for _, lRaw := range configured { data := lRaw.(map[string]interface{}) - l := &ecs.InferenceAccelerator{ + l := awstypes.InferenceAccelerator{ DeviceName: aws.String(data[names.AttrDeviceName].(string)), DeviceType: aws.String(data["device_type"].(string)), } @@ -849,8 +853,8 @@ func expandInferenceAccelerators(configured []interface{}) []*ecs.InferenceAccel return iAccs } -func expandTaskDefinitionPlacementConstraints(constraints []interface{}) ([]*ecs.TaskDefinitionPlacementConstraint, error) { - var pc []*ecs.TaskDefinitionPlacementConstraint +func expandTaskDefinitionPlacementConstraints(constraints []interface{}) ([]awstypes.TaskDefinitionPlacementConstraint, error) { + var pc []awstypes.TaskDefinitionPlacementConstraint for _, raw := range constraints { p := raw.(map[string]interface{}) t := p[names.AttrType].(string) @@ -858,8 +862,8 @@ func expandTaskDefinitionPlacementConstraints(constraints []interface{}) ([]*ecs if err := validPlacementConstraint(t, e); err != nil { return nil, err } - pc = append(pc, &ecs.TaskDefinitionPlacementConstraint{ - Type: aws.String(t), + pc = append(pc, awstypes.TaskDefinitionPlacementConstraint{ + Type: awstypes.TaskDefinitionPlacementConstraintType(t), Expression: aws.String(e), }) } @@ -867,65 +871,65 @@ func expandTaskDefinitionPlacementConstraints(constraints []interface{}) ([]*ecs return pc, nil } -func expandTaskDefinitionRuntimePlatformConfiguration(runtimePlatformConfig []interface{}) *ecs.RuntimePlatform { +func expandTaskDefinitionRuntimePlatformConfiguration(runtimePlatformConfig []interface{}) *awstypes.RuntimePlatform { config := runtimePlatformConfig[0] configMap := config.(map[string]interface{}) - ecsProxyConfig := &ecs.RuntimePlatform{} + ecsProxyConfig := &awstypes.RuntimePlatform{} os := configMap["operating_system_family"].(string) if os != "" { - ecsProxyConfig.OperatingSystemFamily = aws.String(os) + ecsProxyConfig.OperatingSystemFamily = awstypes.OSFamily(os) } osFamily := configMap["cpu_architecture"].(string) if osFamily != "" { - ecsProxyConfig.CpuArchitecture = aws.String(osFamily) + ecsProxyConfig.CpuArchitecture = awstypes.CPUArchitecture(osFamily) } return ecsProxyConfig } -func expandTaskDefinitionProxyConfiguration(proxyConfigs []interface{}) *ecs.ProxyConfiguration { +func expandTaskDefinitionProxyConfiguration(proxyConfigs []interface{}) *awstypes.ProxyConfiguration { proxyConfig := proxyConfigs[0] configMap := proxyConfig.(map[string]interface{}) rawProperties := configMap[names.AttrProperties].(map[string]interface{}) - properties := make([]*ecs.KeyValuePair, len(rawProperties)) + properties := make([]awstypes.KeyValuePair, len(rawProperties)) i := 0 for name, value := range rawProperties { - properties[i] = &ecs.KeyValuePair{ + properties[i] = awstypes.KeyValuePair{ Name: aws.String(name), Value: aws.String(value.(string)), } i++ } - ecsProxyConfig := &ecs.ProxyConfiguration{ + ecsProxyConfig := &awstypes.ProxyConfiguration{ ContainerName: aws.String(configMap["container_name"].(string)), - Type: aws.String(configMap[names.AttrType].(string)), + Type: awstypes.ProxyConfigurationType(configMap[names.AttrType].(string)), Properties: properties, } return ecsProxyConfig } -func expandVolumes(configured []interface{}) []*ecs.Volume { - volumes := make([]*ecs.Volume, 0, len(configured)) +func expandVolumes(configured []interface{}) []awstypes.Volume { + volumes := make([]awstypes.Volume, 0, len(configured)) // Loop over our configured volumes and create // an array of aws-sdk-go compatible objects for _, lRaw := range configured { data := lRaw.(map[string]interface{}) - l := &ecs.Volume{ + l := awstypes.Volume{ Name: aws.String(data[names.AttrName].(string)), } hostPath := data["host_path"].(string) if hostPath != "" { - l.Host = &ecs.HostVolumeProperties{ + l.Host = &awstypes.HostVolumeProperties{ SourcePath: aws.String(hostPath), } } @@ -952,16 +956,16 @@ func expandVolumes(configured []interface{}) []*ecs.Volume { return volumes } -func expandVolumesDockerVolume(configList []interface{}) *ecs.DockerVolumeConfiguration { +func expandVolumesDockerVolume(configList []interface{}) *awstypes.DockerVolumeConfiguration { config := configList[0].(map[string]interface{}) - dockerVol := &ecs.DockerVolumeConfiguration{} + dockerVol := &awstypes.DockerVolumeConfiguration{} if v, ok := config[names.AttrScope].(string); ok && v != "" { - dockerVol.Scope = aws.String(v) + dockerVol.Scope = awstypes.Scope(v) } if v, ok := config["autoprovision"]; ok && v != "" { - if dockerVol.Scope == nil || aws.StringValue(dockerVol.Scope) != ecs.ScopeTask || v.(bool) { + if dockerVol.Scope != awstypes.ScopeTask || v.(bool) { dockerVol.Autoprovision = aws.Bool(v.(bool)) } } @@ -971,19 +975,19 @@ func expandVolumesDockerVolume(configList []interface{}) *ecs.DockerVolumeConfig } if v, ok := config["driver_opts"].(map[string]interface{}); ok && len(v) > 0 { - dockerVol.DriverOpts = flex.ExpandStringMap(v) + dockerVol.DriverOpts = flex.ExpandStringValueMap(v) } if v, ok := config["labels"].(map[string]interface{}); ok && len(v) > 0 { - dockerVol.Labels = flex.ExpandStringMap(v) + dockerVol.Labels = flex.ExpandStringValueMap(v) } return dockerVol } -func expandVolumesEFSVolume(efsConfig []interface{}) *ecs.EFSVolumeConfiguration { +func expandVolumesEFSVolume(efsConfig []interface{}) *awstypes.EFSVolumeConfiguration { config := efsConfig[0].(map[string]interface{}) - efsVol := &ecs.EFSVolumeConfiguration{} + efsVol := &awstypes.EFSVolumeConfiguration{} if v, ok := config[names.AttrFileSystemID].(string); ok && v != "" { efsVol.FileSystemId = aws.String(v) @@ -993,11 +997,11 @@ func expandVolumesEFSVolume(efsConfig []interface{}) *ecs.EFSVolumeConfiguration efsVol.RootDirectory = aws.String(v) } if v, ok := config["transit_encryption"].(string); ok && v != "" { - efsVol.TransitEncryption = aws.String(v) + efsVol.TransitEncryption = awstypes.EFSTransitEncryption(v) } if v, ok := config["transit_encryption_port"].(int); ok && v > 0 { - efsVol.TransitEncryptionPort = aws.Int64(int64(v)) + efsVol.TransitEncryptionPort = aws.Int32(int32(v)) } if v, ok := config["authorization_config"].([]interface{}); ok && len(v) > 0 { efsVol.AuthorizationConfig = expandVolumesEFSVolumeAuthorizationConfig(v) @@ -1006,24 +1010,24 @@ func expandVolumesEFSVolume(efsConfig []interface{}) *ecs.EFSVolumeConfiguration return efsVol } -func expandVolumesEFSVolumeAuthorizationConfig(efsConfig []interface{}) *ecs.EFSAuthorizationConfig { +func expandVolumesEFSVolumeAuthorizationConfig(efsConfig []interface{}) *awstypes.EFSAuthorizationConfig { authconfig := efsConfig[0].(map[string]interface{}) - auth := &ecs.EFSAuthorizationConfig{} + auth := &awstypes.EFSAuthorizationConfig{} if v, ok := authconfig["access_point_id"].(string); ok && v != "" { auth.AccessPointId = aws.String(v) } if v, ok := authconfig["iam"].(string); ok && v != "" { - auth.Iam = aws.String(v) + auth.Iam = awstypes.EFSAuthorizationConfigIAM(v) } return auth } -func expandVolumesFSxWinVolume(fsxWinConfig []interface{}) *ecs.FSxWindowsFileServerVolumeConfiguration { +func expandVolumesFSxWinVolume(fsxWinConfig []interface{}) *awstypes.FSxWindowsFileServerVolumeConfiguration { config := fsxWinConfig[0].(map[string]interface{}) - fsxVol := &ecs.FSxWindowsFileServerVolumeConfiguration{} + fsxVol := &awstypes.FSxWindowsFileServerVolumeConfiguration{} if v, ok := config[names.AttrFileSystemID].(string); ok && v != "" { fsxVol.FileSystemId = aws.String(v) @@ -1040,9 +1044,9 @@ func expandVolumesFSxWinVolume(fsxWinConfig []interface{}) *ecs.FSxWindowsFileSe return fsxVol } -func expandVolumesFSxWinVolumeAuthorizationConfig(config []interface{}) *ecs.FSxWindowsFileServerAuthorizationConfig { +func expandVolumesFSxWinVolumeAuthorizationConfig(config []interface{}) *awstypes.FSxWindowsFileServerAuthorizationConfig { authconfig := config[0].(map[string]interface{}) - auth := &ecs.FSxWindowsFileServerAuthorizationConfig{} + auth := &awstypes.FSxWindowsFileServerAuthorizationConfig{} if v, ok := authconfig["credentials_parameter"].(string); ok && v != "" { auth.CredentialsParameter = aws.String(v) @@ -1055,19 +1059,19 @@ func expandVolumesFSxWinVolumeAuthorizationConfig(config []interface{}) *ecs.FSx return auth } -func flattenVolumes(list []*ecs.Volume) []map[string]interface{} { +func flattenVolumes(list []awstypes.Volume) []map[string]interface{} { result := make([]map[string]interface{}, 0, len(list)) for _, volume := range list { l := map[string]interface{}{ - names.AttrName: aws.StringValue(volume.Name), + names.AttrName: aws.ToString(volume.Name), } if volume.Host != nil && volume.Host.SourcePath != nil { - l["host_path"] = aws.StringValue(volume.Host.SourcePath) + l["host_path"] = aws.ToString(volume.Host.SourcePath) } if volume.ConfiguredAtLaunch != nil { - l["configure_at_launch"] = aws.BoolValue(volume.ConfiguredAtLaunch) + l["configure_at_launch"] = aws.ToBool(volume.ConfiguredAtLaunch) } if volume.DockerVolumeConfiguration != nil { @@ -1087,51 +1091,47 @@ func flattenVolumes(list []*ecs.Volume) []map[string]interface{} { return result } -func flattenDockerVolumeConfiguration(config *ecs.DockerVolumeConfiguration) []interface{} { +func flattenDockerVolumeConfiguration(config *awstypes.DockerVolumeConfiguration) []interface{} { var items []interface{} m := make(map[string]interface{}) - if v := config.Scope; v != nil { - m[names.AttrScope] = aws.StringValue(v) - } + m[names.AttrScope] = string(config.Scope) if v := config.Autoprovision; v != nil { - m["autoprovision"] = aws.BoolValue(v) + m["autoprovision"] = aws.ToBool(v) } if v := config.Driver; v != nil { - m["driver"] = aws.StringValue(v) + m["driver"] = aws.ToString(v) } if config.DriverOpts != nil { - m["driver_opts"] = flex.FlattenStringMap(config.DriverOpts) + m["driver_opts"] = flex.FlattenStringValueMap(config.DriverOpts) } if v := config.Labels; v != nil { - m["labels"] = flex.FlattenStringMap(v) + m["labels"] = flex.FlattenStringValueMap(v) } items = append(items, m) return items } -func flattenEFSVolumeConfiguration(config *ecs.EFSVolumeConfiguration) []interface{} { +func flattenEFSVolumeConfiguration(config *awstypes.EFSVolumeConfiguration) []interface{} { var items []interface{} m := make(map[string]interface{}) if config != nil { if v := config.FileSystemId; v != nil { - m[names.AttrFileSystemID] = aws.StringValue(v) + m[names.AttrFileSystemID] = aws.ToString(v) } if v := config.RootDirectory; v != nil { - m["root_directory"] = aws.StringValue(v) - } - if v := config.TransitEncryption; v != nil { - m["transit_encryption"] = aws.StringValue(v) + m["root_directory"] = aws.ToString(v) } + m["transit_encryption"] = string(config.TransitEncryption) if v := config.TransitEncryptionPort; v != nil { - m["transit_encryption_port"] = int(aws.Int64Value(v)) + m["transit_encryption_port"] = int(aws.ToInt32(v)) } if v := config.AuthorizationConfig; v != nil { @@ -1143,32 +1143,30 @@ func flattenEFSVolumeConfiguration(config *ecs.EFSVolumeConfiguration) []interfa return items } -func flattenEFSVolumeAuthorizationConfig(config *ecs.EFSAuthorizationConfig) []interface{} { +func flattenEFSVolumeAuthorizationConfig(config *awstypes.EFSAuthorizationConfig) []interface{} { var items []interface{} m := make(map[string]interface{}) if config != nil { if v := config.AccessPointId; v != nil { - m["access_point_id"] = aws.StringValue(v) - } - if v := config.Iam; v != nil { - m["iam"] = aws.StringValue(v) + m["access_point_id"] = aws.ToString(v) } + m["iam"] = string(config.Iam) } items = append(items, m) return items } -func flattenFSxWinVolumeConfiguration(config *ecs.FSxWindowsFileServerVolumeConfiguration) []interface{} { +func flattenFSxWinVolumeConfiguration(config *awstypes.FSxWindowsFileServerVolumeConfiguration) []interface{} { var items []interface{} m := make(map[string]interface{}) if config != nil { if v := config.FileSystemId; v != nil { - m[names.AttrFileSystemID] = aws.StringValue(v) + m[names.AttrFileSystemID] = aws.ToString(v) } if v := config.RootDirectory; v != nil { - m["root_directory"] = aws.StringValue(v) + m["root_directory"] = aws.ToString(v) } if v := config.AuthorizationConfig; v != nil { @@ -1180,15 +1178,15 @@ func flattenFSxWinVolumeConfiguration(config *ecs.FSxWindowsFileServerVolumeConf return items } -func flattenFSxWinVolumeAuthorizationConfig(config *ecs.FSxWindowsFileServerAuthorizationConfig) []interface{} { +func flattenFSxWinVolumeAuthorizationConfig(config *awstypes.FSxWindowsFileServerAuthorizationConfig) []interface{} { var items []interface{} m := make(map[string]interface{}) if config != nil { if v := config.CredentialsParameter; v != nil { - m["credentials_parameter"] = aws.StringValue(v) + m["credentials_parameter"] = aws.ToString(v) } if v := config.Domain; v != nil { - m[names.AttrDomain] = aws.StringValue(v) + m[names.AttrDomain] = aws.ToString(v) } } @@ -1196,7 +1194,7 @@ func flattenFSxWinVolumeAuthorizationConfig(config *ecs.FSxWindowsFileServerAuth return items } -func flattenContainerDefinitions(definitions []*ecs.ContainerDefinition) (string, error) { +func flattenContainerDefinitions(definitions []awstypes.ContainerDefinition) (string, error) { b, err := jsonutil.BuildJSON(definitions) if err != nil { return "", err @@ -1205,40 +1203,34 @@ func flattenContainerDefinitions(definitions []*ecs.ContainerDefinition) (string return string(b), nil } -func expandContainerDefinitions(rawDefinitions string) ([]*ecs.ContainerDefinition, error) { - var definitions []*ecs.ContainerDefinition +func expandContainerDefinitions(rawDefinitions string) ([]awstypes.ContainerDefinition, error) { + var definitions []awstypes.ContainerDefinition err := json.Unmarshal([]byte(rawDefinitions), &definitions) if err != nil { return nil, fmt.Errorf("decoding JSON: %s", err) } - for i, c := range definitions { - if c == nil { - return nil, fmt.Errorf("invalid container definition supplied at index (%d)", i) - } - } - return definitions, nil } -func expandTaskDefinitionEphemeralStorage(config []interface{}) *ecs.EphemeralStorage { +func expandTaskDefinitionEphemeralStorage(config []interface{}) *awstypes.EphemeralStorage { configMap := config[0].(map[string]interface{}) - es := &ecs.EphemeralStorage{ - SizeInGiB: aws.Int64(int64(configMap["size_in_gib"].(int))), + es := &awstypes.EphemeralStorage{ + SizeInGiB: int32(configMap["size_in_gib"].(int)), } return es } -func flattenTaskDefinitionEphemeralStorage(pc *ecs.EphemeralStorage) []map[string]interface{} { +func flattenTaskDefinitionEphemeralStorage(pc *awstypes.EphemeralStorage) []map[string]interface{} { if pc == nil { return nil } m := make(map[string]interface{}) - m["size_in_gib"] = aws.Int64Value(pc.SizeInGiB) + m["size_in_gib"] = pc.SizeInGiB return []map[string]interface{}{m} } diff --git a/internal/service/ecs/task_definition_data_source.go b/internal/service/ecs/task_definition_data_source.go index d55b5246519..bdf5d806fdd 100644 --- a/internal/service/ecs/task_definition_data_source.go +++ b/internal/service/ecs/task_definition_data_source.go @@ -6,8 +6,8 @@ package ecs import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -64,23 +64,23 @@ func DataSourceTaskDefinition() *schema.Resource { func dataSourceTaskDefinitionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) taskDefinitionName := d.Get("task_definition").(string) input := &ecs.DescribeTaskDefinitionInput{ TaskDefinition: aws.String(taskDefinitionName), } - output, err := conn.DescribeTaskDefinitionWithContext(ctx, input) + output, err := conn.DescribeTaskDefinition(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition (%s): %s", taskDefinitionName, err) } taskDefinition := output.TaskDefinition - d.SetId(aws.StringValue(taskDefinition.TaskDefinitionArn)) + d.SetId(aws.ToString(taskDefinition.TaskDefinitionArn)) d.Set(names.AttrARN, taskDefinition.TaskDefinitionArn) - d.Set("arn_without_revision", StripRevision(aws.StringValue(taskDefinition.TaskDefinitionArn))) + d.Set("arn_without_revision", StripRevision(aws.ToString(taskDefinition.TaskDefinitionArn))) d.Set(names.AttrExecutionRoleARN, taskDefinition.ExecutionRoleArn) d.Set(names.AttrFamily, taskDefinition.Family) d.Set("network_mode", taskDefinition.NetworkMode) diff --git a/internal/service/ecs/task_definition_equivalency.go b/internal/service/ecs/task_definition_equivalency.go index 3d757997b50..c370770b892 100644 --- a/internal/service/ecs/task_definition_equivalency.go +++ b/internal/service/ecs/task_definition_equivalency.go @@ -10,9 +10,9 @@ import ( "reflect" "sort" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" - "github.com/aws/aws-sdk-go/service/ecs" "github.com/mitchellh/copystructure" ) @@ -56,7 +56,7 @@ func ContainerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, return equal, nil } -type containerDefinitions []*ecs.ContainerDefinition +type containerDefinitions []awstypes.ContainerDefinition func (cd containerDefinitions) Reduce(isAWSVPC bool) error { // Deal with fields which may be re-ordered in the API @@ -66,17 +66,11 @@ func (cd containerDefinitions) Reduce(isAWSVPC bool) error { for i, def := range cd { // Deal with special fields which have defaults - if def.Cpu != nil && aws.Int64Value(def.Cpu) == 0 { - def.Cpu = nil - } if def.Essential == nil { def.Essential = aws.Bool(true) } for j, pm := range def.PortMappings { - if pm.Protocol != nil && aws.StringValue(pm.Protocol) == "tcp" { - cd[i].PortMappings[j].Protocol = nil - } - if pm.HostPort != nil && aws.Int64Value(pm.HostPort) == 0 { + if aws.ToInt32(pm.HostPort) == 0 { cd[i].PortMappings[j].HostPort = nil } if isAWSVPC && cd[i].PortMappings[j].HostPort == nil { @@ -101,8 +95,8 @@ func (cd containerDefinitions) Reduce(isAWSVPC bool) error { } } } - iface := definition.Interface().(ecs.ContainerDefinition) - cd[i] = &iface + iface := definition.Interface().(awstypes.ContainerDefinition) + cd[i] = iface } return nil } @@ -110,7 +104,7 @@ func (cd containerDefinitions) Reduce(isAWSVPC bool) error { func (cd containerDefinitions) OrderEnvironmentVariables() { for _, def := range cd { sort.Slice(def.Environment, func(i, j int) bool { - return aws.StringValue(def.Environment[i].Name) < aws.StringValue(def.Environment[j].Name) + return aws.ToString(def.Environment[i].Name) < aws.ToString(def.Environment[j].Name) }) } } @@ -118,13 +112,13 @@ func (cd containerDefinitions) OrderEnvironmentVariables() { func (cd containerDefinitions) OrderSecrets() { for _, def := range cd { sort.Slice(def.Secrets, func(i, j int) bool { - return aws.StringValue(def.Secrets[i].Name) < aws.StringValue(def.Secrets[j].Name) + return aws.ToString(def.Secrets[i].Name) < aws.ToString(def.Secrets[j].Name) }) } } func (cd containerDefinitions) OrderContainers() { sort.Slice(cd, func(i, j int) bool { - return aws.StringValue(cd[i].Name) < aws.StringValue(cd[j].Name) + return aws.ToString(cd[i].Name) < aws.ToString(cd[j].Name) }) } diff --git a/internal/service/ecs/task_definition_migrate.go b/internal/service/ecs/task_definition_migrate.go index 52eb9ab6c96..d87059daf1a 100644 --- a/internal/service/ecs/task_definition_migrate.go +++ b/internal/service/ecs/task_definition_migrate.go @@ -8,9 +8,9 @@ import ( "fmt" "log" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" - "github.com/aws/aws-sdk-go/service/ecs" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/names" @@ -18,7 +18,7 @@ import ( func resourceTaskDefinitionMigrateState(v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { ctx := context.Background() - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) switch v { case 0: @@ -29,12 +29,12 @@ func resourceTaskDefinitionMigrateState(v int, is *terraform.InstanceState, meta } } -func migrateTaskDefinitionStateV0toV1(is *terraform.InstanceState, conn *ecs.ECS) (*terraform.InstanceState, error) { +func migrateTaskDefinitionStateV0toV1(is *terraform.InstanceState, conn *ecs.Client) (*terraform.InstanceState, error) { arn := is.Attributes[names.AttrARN] ctx := context.TODO() // nosemgrep:ci.semgrep.migrate.context-todo // We need to pull definitions from the API b/c they're unrecoverable from the checksum - td, err := conn.DescribeTaskDefinitionWithContext(ctx, &ecs.DescribeTaskDefinitionInput{ + td, err := conn.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ TaskDefinition: aws.String(arn), }) if err != nil { diff --git a/internal/service/ecs/task_definition_test.go b/internal/service/ecs/task_definition_test.go index 09dd276c64e..f943fd97760 100644 --- a/internal/service/ecs/task_definition_test.go +++ b/internal/service/ecs/task_definition_test.go @@ -9,9 +9,10 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/aws-sdk-go/service/ecs" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -69,7 +70,7 @@ func Test_StripRevision(t *testing.T) { func TestAccECSTaskDefinition_basic(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -110,7 +111,7 @@ func TestAccECSTaskDefinition_basic(t *testing.T) { // Regression for https://github.com/hashicorp/terraform/issues/2370 func TestAccECSTaskDefinition_scratchVolume(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -139,7 +140,7 @@ func TestAccECSTaskDefinition_scratchVolume(t *testing.T) { func TestAccECSTaskDefinition_configuredAtLaunch(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -170,7 +171,7 @@ func TestAccECSTaskDefinition_configuredAtLaunch(t *testing.T) { func TestAccECSTaskDefinition_DockerVolume_basic(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -213,7 +214,7 @@ func TestAccECSTaskDefinition_DockerVolume_basic(t *testing.T) { func TestAccECSTaskDefinition_DockerVolume_minimal(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -248,7 +249,7 @@ func TestAccECSTaskDefinition_DockerVolume_minimal(t *testing.T) { func TestAccECSTaskDefinition_runtimePlatform(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -282,7 +283,7 @@ func TestAccECSTaskDefinition_runtimePlatform(t *testing.T) { func TestAccECSTaskDefinition_Fargate_runtimePlatform(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -316,7 +317,7 @@ func TestAccECSTaskDefinition_Fargate_runtimePlatform(t *testing.T) { func TestAccECSTaskDefinition_Fargate_runtimePlatformWithoutArch(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -349,7 +350,7 @@ func TestAccECSTaskDefinition_Fargate_runtimePlatformWithoutArch(t *testing.T) { func TestAccECSTaskDefinition_EFSVolume_minimal(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -384,7 +385,7 @@ func TestAccECSTaskDefinition_EFSVolume_minimal(t *testing.T) { func TestAccECSTaskDefinition_EFSVolume_basic(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -420,7 +421,7 @@ func TestAccECSTaskDefinition_EFSVolume_basic(t *testing.T) { func TestAccECSTaskDefinition_EFSVolume_transitEncryptionMinimal(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -458,7 +459,7 @@ func TestAccECSTaskDefinition_EFSVolume_transitEncryptionMinimal(t *testing.T) { func TestAccECSTaskDefinition_EFSVolume_transitEncryption(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -496,7 +497,7 @@ func TestAccECSTaskDefinition_EFSVolume_transitEncryption(t *testing.T) { func TestAccECSTaskDefinition_EFSVolume_transitEncryptionDisabled(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -533,7 +534,7 @@ func TestAccECSTaskDefinition_EFSVolume_transitEncryptionDisabled(t *testing.T) func TestAccECSTaskDefinition_EFSVolume_accessPoint(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -574,7 +575,7 @@ func TestAccECSTaskDefinition_EFSVolume_accessPoint(t *testing.T) { func TestAccECSTaskDefinition_fsxWinFileSystem(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" domainName := acctest.RandomDomainName() @@ -622,7 +623,7 @@ func TestAccECSTaskDefinition_fsxWinFileSystem(t *testing.T) { func TestAccECSTaskDefinition_DockerVolume_taskScoped(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -647,10 +648,10 @@ func TestAccECSTaskDefinition_DockerVolume_taskScoped(t *testing.T) { // Regression for https://github.com/hashicorp/terraform/issues/2694 func TestAccECSTaskDefinition_service(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" - var service ecs.Service + var service awstypes.Service resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -685,7 +686,7 @@ func TestAccECSTaskDefinition_service(t *testing.T) { func TestAccECSTaskDefinition_taskRoleARN(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -714,7 +715,7 @@ func TestAccECSTaskDefinition_taskRoleARN(t *testing.T) { func TestAccECSTaskDefinition_networkMode(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -744,7 +745,7 @@ func TestAccECSTaskDefinition_networkMode(t *testing.T) { func TestAccECSTaskDefinition_ipcMode(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -774,7 +775,7 @@ func TestAccECSTaskDefinition_ipcMode(t *testing.T) { func TestAccECSTaskDefinition_pidMode(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -804,7 +805,7 @@ func TestAccECSTaskDefinition_pidMode(t *testing.T) { func TestAccECSTaskDefinition_constraint(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -835,7 +836,7 @@ func TestAccECSTaskDefinition_constraint(t *testing.T) { func TestAccECSTaskDefinition_changeVolumesForcesNewResource(t *testing.T) { ctx := acctest.Context(t) - var before, after ecs.TaskDefinition + var before, after awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -872,7 +873,7 @@ func TestAccECSTaskDefinition_changeVolumesForcesNewResource(t *testing.T) { // Regression for https://github.com/hashicorp/terraform-provider-aws/issues/2336 func TestAccECSTaskDefinition_arrays(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -901,7 +902,7 @@ func TestAccECSTaskDefinition_arrays(t *testing.T) { func TestAccECSTaskDefinition_Fargate_basic(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -938,7 +939,7 @@ func TestAccECSTaskDefinition_Fargate_basic(t *testing.T) { func TestAccECSTaskDefinition_Fargate_ephemeralStorage(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -972,7 +973,7 @@ func TestAccECSTaskDefinition_Fargate_ephemeralStorage(t *testing.T) { func TestAccECSTaskDefinition_executionRole(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -1002,7 +1003,7 @@ func TestAccECSTaskDefinition_executionRole(t *testing.T) { // Regression for https://github.com/hashicorp/terraform/issues/3582#issuecomment-286409786 func TestAccECSTaskDefinition_disappears(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -1030,7 +1031,7 @@ func TestAccECSTaskDefinition_disappears(t *testing.T) { func TestAccECSTaskDefinition_tags(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -1078,7 +1079,7 @@ func TestAccECSTaskDefinition_tags(t *testing.T) { func TestAccECSTaskDefinition_proxy(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" containerName := "web" @@ -1117,7 +1118,7 @@ func TestAccECSTaskDefinition_proxy(t *testing.T) { func TestAccECSTaskDefinition_inferenceAccelerator(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -1165,7 +1166,7 @@ func TestAccECSTaskDefinition_invalidContainerDefinition(t *testing.T) { func TestAccECSTaskDefinition_trackLatest(t *testing.T) { ctx := acctest.Context(t) - var def ecs.TaskDefinition + var def awstypes.TaskDefinition rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_task_definition.test" @@ -1236,12 +1237,12 @@ DEFINITION `, rName, proxyType, containerName, ignoredUid, ignoredGid, appPorts, proxyIngressPort, proxyEgressPort, egressIgnoredPorts, egressIgnoredIPs, containerName) } -func testAccCheckTaskDefinitionProxyConfiguration(after *ecs.TaskDefinition, containerName string, proxyType string, +func testAccCheckTaskDefinitionProxyConfiguration(after *awstypes.TaskDefinition, containerName string, proxyType string, ignoredUid string, ignoredGid string, appPorts string, proxyIngressPort string, proxyEgressPort string, egressIgnoredPorts string, egressIgnoredIPs string) resource.TestCheckFunc { return func(s *terraform.State) error { - if *after.ProxyConfiguration.Type != proxyType { - return fmt.Errorf("Expected (%s) ProxyConfiguration.Type, got (%s)", proxyType, *after.ProxyConfiguration.Type) + if string(after.ProxyConfiguration.Type) != proxyType { + return fmt.Errorf("Expected (%s) ProxyConfiguration.Type, got (%s)", proxyType, string(after.ProxyConfiguration.Type)) } if *after.ProxyConfiguration.ContainerName != containerName { @@ -1256,7 +1257,7 @@ func testAccCheckTaskDefinitionProxyConfiguration(after *ecs.TaskDefinition, con propertyLookups := make(map[string]string) for _, property := range properties { - propertyLookups[aws.StringValue(property.Name)] = aws.StringValue(property.Value) + propertyLookups[aws.ToString(property.Name)] = aws.ToString(property.Value) } if propertyLookups["IgnoredUID"] != ignoredUid { @@ -1292,16 +1293,16 @@ func testAccCheckTaskDefinitionProxyConfiguration(after *ecs.TaskDefinition, con } func testAccCheckTaskDefinitionRecreated(t *testing.T, - before, after *ecs.TaskDefinition) resource.TestCheckFunc { + before, after *awstypes.TaskDefinition) resource.TestCheckFunc { return func(s *terraform.State) error { - if *before.Revision == *after.Revision { + if before.Revision == after.Revision { t.Fatalf("Expected change of TaskDefinition Revisions, but both were %v", before.Revision) } return nil } } -func testAccCheckTaskDefinitionConstraintsAttrs(def *ecs.TaskDefinition) resource.TestCheckFunc { +func testAccCheckTaskDefinitionConstraintsAttrs(def *awstypes.TaskDefinition) resource.TestCheckFunc { return func(s *terraform.State) error { if len(def.PlacementConstraints) != 1 { return fmt.Errorf("Expected (1) placement_constraints, got (%d)", len(def.PlacementConstraints)) @@ -1336,7 +1337,7 @@ func TestValidTaskDefinitionContainerDefinitions(t *testing.T) { func testAccCheckTaskDefinitionDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_ecs_task_definition" { @@ -1347,13 +1348,13 @@ func testAccCheckTaskDefinitionDestroy(ctx context.Context) resource.TestCheckFu TaskDefinition: aws.String(rs.Primary.Attributes[names.AttrARN]), } - out, err := conn.DescribeTaskDefinitionWithContext(ctx, &input) + out, err := conn.DescribeTaskDefinition(ctx, &input) if err != nil { return err } - if out.TaskDefinition != nil && *out.TaskDefinition.Status != ecs.TaskDefinitionStatusInactive { + if out.TaskDefinition != nil && out.TaskDefinition.Status != awstypes.TaskDefinitionStatusInactive { return fmt.Errorf("ECS task definition still exists:\n%#v", *out.TaskDefinition) } } @@ -1362,16 +1363,16 @@ func testAccCheckTaskDefinitionDestroy(ctx context.Context) resource.TestCheckFu } } -func testAccCheckTaskDefinitionExists(ctx context.Context, name string, def *ecs.TaskDefinition) resource.TestCheckFunc { +func testAccCheckTaskDefinitionExists(ctx context.Context, name string, def *awstypes.TaskDefinition) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { return fmt.Errorf("Not found: %s", name) } - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) - out, err := conn.DescribeTaskDefinitionWithContext(ctx, &ecs.DescribeTaskDefinitionInput{ + out, err := conn.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ TaskDefinition: aws.String(rs.Primary.Attributes[names.AttrARN]), }) if err != nil { @@ -1383,7 +1384,7 @@ func testAccCheckTaskDefinitionExists(ctx context.Context, name string, def *ecs } } -func testAccCheckTaskDefinitionDockerVolumeConfigurationAutoprovisionNil(def *ecs.TaskDefinition) resource.TestCheckFunc { +func testAccCheckTaskDefinitionDockerVolumeConfigurationAutoprovisionNil(def *awstypes.TaskDefinition) resource.TestCheckFunc { return func(s *terraform.State) error { if len(def.Volumes) != 1 { return fmt.Errorf("Expected (1) volumes, got (%d)", len(def.Volumes)) From e3233feeace582cf6831acb1995e191f51adbb0c Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Mon, 17 Jun 2024 20:43:14 +0100 Subject: [PATCH 12/60] r/ecs_service: Migrate to AWS SDK v2 --- .../service/ecs/account_setting_default.go | 2 +- internal/service/ecs/capacity_provider.go | 11 +- .../service/ecs/capacity_provider_test.go | 7 +- .../ecs/container_definition_data_source.go | 2 +- internal/service/ecs/find.go | 66 +-- internal/service/ecs/flex.go | 14 +- internal/service/ecs/service.go | 418 +++++++++--------- internal/service/ecs/service_data_source.go | 2 +- internal/service/ecs/service_test.go | 140 +++--- internal/service/ecs/status.go | 49 +- internal/service/ecs/sweep.go | 130 +++--- internal/service/ecs/task_definition.go | 2 +- internal/service/ecs/wait.go | 20 +- 13 files changed, 431 insertions(+), 432 deletions(-) diff --git a/internal/service/ecs/account_setting_default.go b/internal/service/ecs/account_setting_default.go index c2978239946..6612f1e84b9 100644 --- a/internal/service/ecs/account_setting_default.go +++ b/internal/service/ecs/account_setting_default.go @@ -98,7 +98,7 @@ func resourceAccountSettingDefaultRead(ctx context.Context, d *schema.ResourceDa EffectiveSettings: true, } - log.Printf("[DEBUG] Reading Default Account Settings: %s", input) + log.Printf("[DEBUG] Reading Default Account Settings: %+v", input) resp, err := conn.ListAccountSettings(ctx, input) if err != nil { diff --git a/internal/service/ecs/capacity_provider.go b/internal/service/ecs/capacity_provider.go index 29442934cbe..3b2c9bcdc04 100644 --- a/internal/service/ecs/capacity_provider.go +++ b/internal/service/ecs/capacity_provider.go @@ -171,8 +171,9 @@ func resourceCapacityProviderCreate(ctx context.Context, d *schema.ResourceData, func resourceCapacityProviderRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition - output, err := FindCapacityProviderByARN(ctx, conn, d.Id()) + output, err := FindCapacityProviderByARN(ctx, conn, partition, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] ECS Capacity Provider (%s) not found, removing from state", d.Id()) @@ -200,6 +201,7 @@ func resourceCapacityProviderRead(ctx context.Context, d *schema.ResourceData, m func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { input := &ecs.UpdateCapacityProviderInput{ @@ -207,7 +209,7 @@ func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, Name: aws.String(d.Get(names.AttrName).(string)), } - log.Printf("[DEBUG] Updating ECS Capacity Provider: %s", input) + log.Printf("[DEBUG] Updating ECS Capacity Provider: %+v", input) err := retry.RetryContext(ctx, capacityProviderUpdateTimeout, func() *retry.RetryError { _, err := conn.UpdateCapacityProvider(ctx, input) @@ -230,7 +232,7 @@ func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, return sdkdiag.AppendErrorf(diags, "updating ECS Capacity Provider (%s): %s", d.Id(), err) } - if _, err = waitCapacityProviderUpdated(ctx, conn, d.Id()); err != nil { + if _, err = waitCapacityProviderUpdated(ctx, conn, partition, d.Id()); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for ECS Capacity Provider (%s) to update: %s", d.Id(), err) } } @@ -241,6 +243,7 @@ func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, func resourceCapacityProviderDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition log.Printf("[DEBUG] Deleting ECS Capacity Provider (%s)", d.Id()) _, err := conn.DeleteCapacityProvider(ctx, &ecs.DeleteCapacityProviderInput{ @@ -256,7 +259,7 @@ func resourceCapacityProviderDelete(ctx context.Context, d *schema.ResourceData, return sdkdiag.AppendErrorf(diags, "deleting ECS Capacity Provider (%s): %s", d.Id(), err) } - if _, err := waitCapacityProviderDeleted(ctx, conn, d.Id()); err != nil { + if _, err := waitCapacityProviderDeleted(ctx, conn, partition, d.Id()); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for ECS Capacity Provider (%s) to delete: %s", d.Id(), err) } diff --git a/internal/service/ecs/capacity_provider_test.go b/internal/service/ecs/capacity_provider_test.go index ac753259a9e..72e8ba462d1 100644 --- a/internal/service/ecs/capacity_provider_test.go +++ b/internal/service/ecs/capacity_provider_test.go @@ -236,18 +236,18 @@ func TestAccECSCapacityProvider_tags(t *testing.T) { func testAccCheckCapacityProviderDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) + partition := acctest.Provider.Meta().(*conns.AWSClient).Partition for _, rs := range s.RootModule().Resources { if rs.Type != "aws_ecs_capacity_provider" { continue } - _, err := tfecs.FindCapacityProviderByARN(ctx, conn, rs.Primary.ID) + _, err := tfecs.FindCapacityProviderByARN(ctx, conn, partition, rs.Primary.ID) if tfresource.NotFound(err) { continue } - if err != nil { return err } @@ -271,8 +271,9 @@ func testAccCheckCapacityProviderExists(ctx context.Context, resourceName string } conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) + partition := acctest.Provider.Meta().(*conns.AWSClient).Partition - output, err := tfecs.FindCapacityProviderByARN(ctx, conn, rs.Primary.ID) + output, err := tfecs.FindCapacityProviderByARN(ctx, conn, partition, rs.Primary.ID) if err != nil { return err diff --git a/internal/service/ecs/container_definition_data_source.go b/internal/service/ecs/container_definition_data_source.go index 3f9718a800d..a055204390a 100644 --- a/internal/service/ecs/container_definition_data_source.go +++ b/internal/service/ecs/container_definition_data_source.go @@ -78,7 +78,7 @@ func dataSourceContainerDefinitionRead(ctx context.Context, d *schema.ResourceDa params := &ecs.DescribeTaskDefinitionInput{ TaskDefinition: aws.String(d.Get("task_definition").(string)), } - log.Printf("[DEBUG] Reading ECS Container Definition: %s", params) + log.Printf("[DEBUG] Reading ECS Container Definition: %+v", params) desc, err := conn.DescribeTaskDefinition(ctx, params) if err != nil { diff --git a/internal/service/ecs/find.go b/internal/service/ecs/find.go index c5dbd37e8d2..c179f3b1d82 100644 --- a/internal/service/ecs/find.go +++ b/internal/service/ecs/find.go @@ -8,35 +8,35 @@ import ( "fmt" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func FindCapacityProviderByARN(ctx context.Context, conn *ecs.ECS, arn string) (*ecs.CapacityProvider, error) { +func FindCapacityProviderByARN(ctx context.Context, conn *ecs.Client, partition string, arn string) (*awstypes.CapacityProvider, error) { input := &ecs.DescribeCapacityProvidersInput{ - CapacityProviders: aws.StringSlice([]string{arn}), - Include: aws.StringSlice([]string{ecs.CapacityProviderFieldTags}), + CapacityProviders: []string{arn}, + Include: []awstypes.CapacityProviderField{awstypes.CapacityProviderFieldTags}, } - output, err := conn.DescribeCapacityProvidersWithContext(ctx, input) + output, err := conn.DescribeCapacityProviders(ctx, input) // Some partitions (i.e., ISO) may not support tagging, giving error - if errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if errs.IsUnsupportedOperationInPartitionError(partition, err) { log.Printf("[WARN] ECS tagging failed describing Capacity Provider (%s) with tags: %s; retrying without tags", arn, err) input.Include = nil - output, err = conn.DescribeCapacityProvidersWithContext(ctx, input) + output, err = conn.DescribeCapacityProviders(ctx, input) } if err != nil { return nil, err } - if output == nil || len(output.CapacityProviders) == 0 || output.CapacityProviders[0] == nil { + if output == nil || len(output.CapacityProviders) == 0 { return nil, &retry.NotFoundError{ Message: "Empty result", LastRequest: input, @@ -45,35 +45,35 @@ func FindCapacityProviderByARN(ctx context.Context, conn *ecs.ECS, arn string) ( capacityProvider := output.CapacityProviders[0] - if status := aws.StringValue(capacityProvider.Status); status == ecs.CapacityProviderStatusInactive { + if capacityProvider.Status == awstypes.CapacityProviderStatusInactive { return nil, &retry.NotFoundError{ - Message: status, + Message: string(capacityProvider.Status), LastRequest: input, } } - return capacityProvider, nil + return &capacityProvider, nil } -func FindServiceByID(ctx context.Context, conn *ecs.ECS, id, cluster string) (*ecs.Service, error) { +func FindServiceByID(ctx context.Context, conn *ecs.Client, partition, id, cluster string) (*awstypes.Service, error) { input := &ecs.DescribeServicesInput{ Cluster: aws.String(cluster), - Include: aws.StringSlice([]string{ecs.ServiceFieldTags}), - Services: aws.StringSlice([]string{id}), + Include: []awstypes.ServiceField{awstypes.ServiceFieldTags}, + Services: []string{id}, } - return FindService(ctx, conn, input) + return FindService(ctx, conn, partition, input) } -func FindServiceNoTagsByID(ctx context.Context, conn *ecs.ECS, id, cluster string) (*ecs.Service, error) { +func FindServiceNoTagsByID(ctx context.Context, conn *ecs.Client, partition, id, cluster string) (*awstypes.Service, error) { input := &ecs.DescribeServicesInput{ - Services: aws.StringSlice([]string{id}), + Services: []string{id}, } if cluster != "" { input.Cluster = aws.String(cluster) } - return FindService(ctx, conn, input) + return FindService(ctx, conn, partition, input) } type expectActiveError struct { @@ -90,12 +90,12 @@ func (e *expectActiveError) Error() string { return fmt.Sprintf("expected status %[1]q, was %[2]q", serviceStatusActive, e.status) } -func FindServiceByIDWaitForActive(ctx context.Context, conn *ecs.ECS, id, cluster string) (*ecs.Service, error) { - var service *ecs.Service +func FindServiceByIDWaitForActive(ctx context.Context, conn *ecs.Client, partition, id, cluster string) (*awstypes.Service, error) { + var service *awstypes.Service // Use the retry.RetryContext function instead of WaitForState() because we don't want the timeout error, if any err := retry.RetryContext(ctx, serviceDescribeTimeout, func() *retry.RetryError { var err error - service, err = FindServiceByID(ctx, conn, id, cluster) + service, err = FindServiceByID(ctx, conn, id, partition, cluster) if tfresource.NotFound(err) { return retry.RetryableError(err) } @@ -103,33 +103,33 @@ func FindServiceByIDWaitForActive(ctx context.Context, conn *ecs.ECS, id, cluste return retry.NonRetryableError(err) } - if status := aws.StringValue(service.Status); status != serviceStatusActive { + if status := aws.ToString(service.Status); status != serviceStatusActive { return retry.RetryableError(newExpectActiveError(status)) } return nil }) if tfresource.TimedOut(err) { - service, err = FindServiceByID(ctx, conn, id, cluster) + service, err = FindServiceByID(ctx, conn, id, partition, cluster) } return service, err } -func FindService(ctx context.Context, conn *ecs.ECS, input *ecs.DescribeServicesInput) (*ecs.Service, error) { - output, err := conn.DescribeServicesWithContext(ctx, input) +func FindService(ctx context.Context, conn *ecs.Client, partition string, input *ecs.DescribeServicesInput) (*awstypes.Service, error) { + output, err := conn.DescribeServices(ctx, input) - if errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) && input.Include != nil { - id := aws.StringValueSlice(input.Services)[0] + if errs.IsUnsupportedOperationInPartitionError(partition, err) && input.Include != nil { + id := input.Services[0] log.Printf("[WARN] failed describing ECS Service (%s) with tags: %s; retrying without tags", id, err) input.Include = nil - output, err = conn.DescribeServicesWithContext(ctx, input) + output, err = conn.DescribeServices(ctx, input) } // As of AWS SDK for Go v1.44.42, DescribeServices does not return the error code ecs.ErrCodeServiceNotFoundException // Keep this here in case it ever does - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeServiceNotFoundException) { + if errs.IsA[*awstypes.ServiceNotFoundException](err) { return nil, &retry.NotFoundError{ LastError: err, LastRequest: input, @@ -141,7 +141,7 @@ func FindService(ctx context.Context, conn *ecs.ECS, input *ecs.DescribeServices // When an ECS Service is not found by DescribeServices(), it will return a Failure struct with Reason = "MISSING" for _, v := range output.Failures { - if aws.StringValue(v.Reason) == "MISSING" { + if aws.ToString(v.Reason) == "MISSING" { return nil, &retry.NotFoundError{ LastRequest: input, } @@ -155,5 +155,5 @@ func FindService(ctx context.Context, conn *ecs.ECS, input *ecs.DescribeServices return nil, tfresource.NewTooManyResultsError(n, input) } - return output.Services[0], nil + return &output.Services[0], nil } diff --git a/internal/service/ecs/flex.go b/internal/service/ecs/flex.go index e299fc37e33..56b78d30255 100644 --- a/internal/service/ecs/flex.go +++ b/internal/service/ecs/flex.go @@ -75,7 +75,7 @@ func expandLoadBalancers(configured []interface{}) []awstypes.LoadBalancer { } // Flattens an array of ECS LoadBalancers into a []map[string]interface{} -func flattenLoadBalancers(list []*awstypes.LoadBalancer) []map[string]interface{} { +func flattenLoadBalancers(list []awstypes.LoadBalancer) []map[string]interface{} { result := make([]map[string]interface{}, 0, len(list)) for _, loadBalancer := range list { l := map[string]interface{}{ @@ -110,7 +110,7 @@ func expandTaskSetLoadBalancers(l []interface{}) []awstypes.LoadBalancer { for _, lRaw := range l { data := lRaw.(map[string]interface{}) - l := &awstypes.LoadBalancer{} + l := awstypes.LoadBalancer{} if v, ok := data["container_name"].(string); ok && v != "" { l.ContainerName = aws.String(v) @@ -162,7 +162,7 @@ func expandServiceRegistries(l []interface{}) []awstypes.ServiceRegistry { for _, v := range l { m := v.(map[string]interface{}) - sr := &awstypes.ServiceRegistry{ + sr := awstypes.ServiceRegistry{ RegistryArn: aws.String(m["registry_arn"].(string)), } if raw, ok := m["container_name"].(string); ok && raw != "" { @@ -195,11 +195,11 @@ func expandScale(l []interface{}) *awstypes.Scale { result := &awstypes.Scale{} if v, ok := tfMap[names.AttrUnit].(string); ok && v != "" { - result.Unit = aws.String(v) + result.Unit = awstypes.ScaleUnit(v) } if v, ok := tfMap[names.AttrValue].(float64); ok { - result.Value = aws.Float64(v) + result.Value = v } return result @@ -212,8 +212,8 @@ func flattenScale(scale *awstypes.Scale) []map[string]interface{} { } m := make(map[string]interface{}) - m[names.AttrUnit] = aws.ToString(scale.Unit) - m[names.AttrValue] = aws.ToFloat64(scale.Value) + m[names.AttrUnit] = string(scale.Unit) + m[names.AttrValue] = scale.Value return []map[string]interface{}{m} } diff --git a/internal/service/ecs/service.go b/internal/service/ecs/service.go index 820465e5af3..81c97d524a4 100644 --- a/internal/service/ecs/service.go +++ b/internal/service/ecs/service.go @@ -13,10 +13,11 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/arn" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" @@ -25,6 +26,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/enum" "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" @@ -133,11 +135,11 @@ func ResourceService() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ names.AttrType: { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Default: ecs.DeploymentControllerTypeEcs, - ValidateFunc: validation.StringInSlice(ecs.DeploymentControllerType_Values(), false), + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Default: awstypes.DeploymentControllerTypeEcs, + ValidateDiagFunc: enum.Validate[awstypes.DeploymentControllerType](), }, }, }, @@ -147,7 +149,7 @@ func ResourceService() *schema.Resource { Optional: true, Default: 200, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if d.Get("scheduling_strategy").(string) == ecs.SchedulingStrategyDaemon && new == "200" { + if d.Get("scheduling_strategy").(string) == string(awstypes.SchedulingStrategyDaemon) && new == "200" { return true } return false @@ -158,7 +160,7 @@ func ResourceService() *schema.Resource { Optional: true, Default: 100, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if d.Get("scheduling_strategy").(string) == ecs.SchedulingStrategyDaemon && new == "100" { + if d.Get("scheduling_strategy").(string) == string(awstypes.SchedulingStrategyDaemon) && new == "100" { return true } return false @@ -168,7 +170,7 @@ func ResourceService() *schema.Resource { Type: schema.TypeInt, Optional: true, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return d.Get("scheduling_strategy").(string) == ecs.SchedulingStrategyDaemon + return d.Get("scheduling_strategy").(string) == string(awstypes.SchedulingStrategyDaemon) }, }, "enable_ecs_managed_tags": { @@ -197,11 +199,11 @@ func ResourceService() *schema.Resource { Computed: true, }, "launch_type": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice(ecs.LaunchType_Values(), false), + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Computed: true, + ValidateDiagFunc: enum.Validate[awstypes.LaunchType](), }, "load_balancer": { Type: schema.TypeSet, @@ -280,9 +282,9 @@ func ResourceService() *schema.Resource { }, }, names.AttrType: { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(ecs.PlacementStrategyType_Values(), false), + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: enum.Validate[awstypes.PlacementStrategyType](), }, }, }, @@ -298,9 +300,9 @@ func ResourceService() *schema.Resource { Optional: true, }, names.AttrType: { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(ecs.PlacementConstraintType_Values(), false), + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: enum.Validate[awstypes.PlacementConstraintType](), }, }, }, @@ -319,14 +321,14 @@ func ResourceService() *schema.Resource { } return false }, - ValidateFunc: validation.StringInSlice(ecs.PropagateTags_Values(), false), + ValidateDiagFunc: enum.Validate[awstypes.PropagateTags](), }, "scheduling_strategy": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: ecs.SchedulingStrategyReplica, - ValidateFunc: validation.StringInSlice(ecs.SchedulingStrategy_Values(), false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: awstypes.SchedulingStrategyReplica, + ValidateDiagFunc: enum.Validate[awstypes.SchedulingStrategy](), }, "service_connect_configuration": { Type: schema.TypeList, @@ -345,9 +347,9 @@ func ResourceService() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "log_driver": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(ecs.LogDriver_Values(), false), + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: enum.Validate[awstypes.LogDriver](), }, "options": { Type: schema.TypeMap, @@ -545,10 +547,10 @@ func ResourceService() *schema.Resource { Default: true, }, "file_system_type": { - Type: schema.TypeString, - Optional: true, - Default: ecs.TaskFilesystemTypeXfs, - ValidateFunc: validation.StringInSlice(ecs.TaskFilesystemType_Values(), false), + Type: schema.TypeString, + Optional: true, + Default: awstypes.TaskFilesystemTypeXfs, + ValidateDiagFunc: enum.Validate[awstypes.TaskFilesystemType](), }, names.AttrIOPS: { Type: schema.TypeInt, @@ -593,7 +595,8 @@ func ResourceService() *schema.Resource { func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition deploymentController := expandDeploymentController(d.Get("deployment_controller").([]interface{})) deploymentMinimumHealthyPercent := d.Get("deployment_minimum_healthy_percent").(int) @@ -602,12 +605,12 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int input := ecs.CreateServiceInput{ CapacityProviderStrategy: expandCapacityProviderStrategy(d.Get(names.AttrCapacityProviderStrategy).(*schema.Set)), ClientToken: aws.String(id.UniqueId()), - DeploymentConfiguration: &ecs.DeploymentConfiguration{}, + DeploymentConfiguration: &awstypes.DeploymentConfiguration{}, DeploymentController: deploymentController, - EnableECSManagedTags: aws.Bool(d.Get("enable_ecs_managed_tags").(bool)), - EnableExecuteCommand: aws.Bool(d.Get("enable_execute_command").(bool)), + EnableECSManagedTags: d.Get("enable_ecs_managed_tags").(bool), + EnableExecuteCommand: d.Get("enable_execute_command").(bool), NetworkConfiguration: expandNetworkConfiguration(d.Get(names.AttrNetworkConfiguration).([]interface{})), - SchedulingStrategy: aws.String(schedulingStrategy), + SchedulingStrategy: awstypes.SchedulingStrategy(schedulingStrategy), ServiceName: aws.String(name), Tags: getTagsIn(ctx), } @@ -620,12 +623,12 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int input.Cluster = aws.String(v.(string)) } - if schedulingStrategy == ecs.SchedulingStrategyDaemon && deploymentMinimumHealthyPercent != 100 { - input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int64(int64(deploymentMinimumHealthyPercent)) - } else if schedulingStrategy == ecs.SchedulingStrategyReplica { - input.DeploymentConfiguration.MaximumPercent = aws.Int64(int64(d.Get("deployment_maximum_percent").(int))) - input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int64(int64(deploymentMinimumHealthyPercent)) - input.DesiredCount = aws.Int64(int64(d.Get("desired_count").(int))) + if schedulingStrategy == string(awstypes.SchedulingStrategyDaemon) && deploymentMinimumHealthyPercent != 100 { + input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int32(int32(deploymentMinimumHealthyPercent)) + } else if schedulingStrategy == string(awstypes.SchedulingStrategyReplica) { + input.DeploymentConfiguration.MaximumPercent = aws.Int32(int32(d.Get("deployment_maximum_percent").(int))) + input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int32(int32(deploymentMinimumHealthyPercent)) + input.DesiredCount = aws.Int32(int32(d.Get("desired_count").(int))) } if v, ok := d.GetOk("deployment_circuit_breaker"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { @@ -633,7 +636,7 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("health_check_grace_period_seconds"); ok { - input.HealthCheckGracePeriodSeconds = aws.Int64(int64(v.(int))) + input.HealthCheckGracePeriodSeconds = aws.Int32(int32(v.(int))) } if v, ok := d.GetOk("iam_role"); ok { @@ -641,13 +644,13 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("launch_type"); ok { - input.LaunchType = aws.String(v.(string)) + input.LaunchType = awstypes.LaunchType(v.(string)) // When creating a service that uses the EXTERNAL deployment controller, // you can specify only parameters that aren't controlled at the task set level // hence you cannot set LaunchType, not changing the default launch_type from EC2 to empty // string to have backward compatibility - if deploymentController != nil && aws.StringValue(deploymentController.Type) == ecs.DeploymentControllerTypeExternal { - input.LaunchType = aws.String("") + if deploymentController != nil && deploymentController.Type == awstypes.DeploymentControllerTypeExternal { + input.LaunchType = awstypes.LaunchType("") } } @@ -681,7 +684,7 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk(names.AttrPropagateTags); ok { - input.PropagateTags = aws.String(v.(string)) + input.PropagateTags = awstypes.PropagateTags(v.(string)) } if v, ok := d.GetOk("service_connect_configuration"); ok && len(v.([]interface{})) > 0 { @@ -694,17 +697,17 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int serviceRegistries := d.Get("service_registries").([]interface{}) if len(serviceRegistries) > 0 { - srs := make([]*ecs.ServiceRegistry, 0, len(serviceRegistries)) + srs := make([]awstypes.ServiceRegistry, 0, len(serviceRegistries)) for _, v := range serviceRegistries { raw := v.(map[string]interface{}) - sr := &ecs.ServiceRegistry{ + sr := awstypes.ServiceRegistry{ RegistryArn: aws.String(raw["registry_arn"].(string)), } if port, ok := raw[names.AttrPort].(int); ok && port != 0 { - sr.Port = aws.Int64(int64(port)) + sr.Port = aws.Int32(int32(port)) } if raw, ok := raw["container_port"].(int); ok && raw != 0 { - sr.ContainerPort = aws.Int64(int64(raw)) + sr.ContainerPort = aws.Int32(int32(raw)) } if raw, ok := raw["container_name"].(string); ok && raw != "" { sr.ContainerName = aws.String(raw) @@ -722,7 +725,7 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int output, err := serviceCreateWithRetry(ctx, conn, input) // Some partitions (e.g. ISO) may not support tag-on-create. - if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(partition, err) { input.Tags = nil output, err = serviceCreateWithRetry(ctx, conn, input) @@ -732,13 +735,13 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int return sdkdiag.AppendErrorf(diags, "creating ECS Service (%s): %s", name, err) } - d.SetId(aws.StringValue(output.Service.ServiceArn)) + d.SetId(aws.ToString(output.Service.ServiceArn)) fn := waitServiceActive if d.Get("wait_for_steady_state").(bool) { fn = waitServiceStable } - if _, err := fn(ctx, conn, d.Id(), d.Get("cluster").(string), d.Timeout(schema.TimeoutCreate)); err != nil { + if _, err := fn(ctx, conn, partition, d.Id(), d.Get("cluster").(string), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for ECS Service (%s) create: %s", d.Id(), err) } @@ -747,7 +750,7 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int err := createTags(ctx, conn, d.Id(), tags) // If default tags only, continue. Otherwise, error. - if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(partition, err) { return append(diags, resourceServiceRead(ctx, d, meta)...) } @@ -761,11 +764,12 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition cluster := d.Get("cluster").(string) - service, err := FindServiceByIDWaitForActive(ctx, conn, d.Id(), cluster) + service, err := FindServiceByIDWaitForActive(ctx, conn, partition, d.Id(), cluster) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] ECS Service (%s) not found, removing from state", d.Id()) @@ -773,7 +777,7 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta inter return diags } - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeClusterNotFoundException) { + if errs.IsA[*awstypes.ClusterNotFoundException](err) { log.Printf("[WARN] ECS Service (%s) parent cluster (%s) not found, removing from state.", d.Id(), cluster) d.SetId("") return diags @@ -790,7 +794,7 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta inter return sdkdiag.AppendErrorf(diags, "reading ECS service (%s): %s", d.Id(), err) } - d.SetId(aws.StringValue(service.ServiceArn)) + d.SetId(aws.ToString(service.ServiceArn)) d.Set(names.AttrName, service.ServiceName) // When creating a service that uses the EXTERNAL deployment controller, @@ -801,7 +805,7 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta inter if strings.HasPrefix(d.Get("task_definition").(string), "arn:"+meta.(*conns.AWSClient).Partition+":ecs:") { d.Set("task_definition", service.TaskDefinition) } else { - taskDefinition := buildFamilyAndRevisionFromARN(aws.StringValue(service.TaskDefinition)) + taskDefinition := buildFamilyAndRevisionFromARN(aws.ToString(service.TaskDefinition)) d.Set("task_definition", taskDefinition) } } @@ -821,7 +825,7 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta inter if strings.HasPrefix(d.Get("cluster").(string), "arn:"+meta.(*conns.AWSClient).Partition+":ecs:") { d.Set("cluster", service.ClusterArn) } else { - clusterARN := GetClusterNameFromARN(aws.StringValue(service.ClusterArn)) + clusterARN := GetClusterNameFromARN(aws.ToString(service.ClusterArn)) d.Set("cluster", clusterARN) } @@ -830,7 +834,7 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta inter if strings.HasPrefix(d.Get("iam_role").(string), "arn:"+meta.(*conns.AWSClient).Partition+":iam:") { d.Set("iam_role", service.RoleArn) } else { - roleARN := GetRoleNameFromARN(aws.StringValue(service.RoleArn)) + roleARN := GetRoleNameFromARN(aws.ToString(service.RoleArn)) d.Set("iam_role", roleARN) } } @@ -897,18 +901,19 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta inter func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { input := &ecs.UpdateServiceInput{ Cluster: aws.String(d.Get("cluster").(string)), - ForceNewDeployment: aws.Bool(d.Get("force_new_deployment").(bool)), + ForceNewDeployment: d.Get("force_new_deployment").(bool), Service: aws.String(d.Id()), } if d.HasChange("alarms") { if input.DeploymentConfiguration == nil { - input.DeploymentConfiguration = &ecs.DeploymentConfiguration{} + input.DeploymentConfiguration = &awstypes.DeploymentConfiguration{} } if v, ok := d.GetOk("alarms"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { @@ -922,11 +927,11 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int if d.HasChange("deployment_circuit_breaker") { if input.DeploymentConfiguration == nil { - input.DeploymentConfiguration = &ecs.DeploymentConfiguration{} + input.DeploymentConfiguration = &awstypes.DeploymentConfiguration{} } // To remove an existing deployment circuit breaker, specify an empty object. - input.DeploymentConfiguration.DeploymentCircuitBreaker = &ecs.DeploymentCircuitBreaker{} + input.DeploymentConfiguration.DeploymentCircuitBreaker = &awstypes.DeploymentCircuitBreaker{} if v, ok := d.GetOk("deployment_circuit_breaker"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { input.DeploymentConfiguration.DeploymentCircuitBreaker = expandDeploymentCircuitBreaker(v.([]interface{})[0].(map[string]interface{})) @@ -934,26 +939,26 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int } switch schedulingStrategy := d.Get("scheduling_strategy").(string); schedulingStrategy { - case ecs.SchedulingStrategyDaemon: + case string(awstypes.SchedulingStrategyDaemon): if d.HasChange("deployment_minimum_healthy_percent") { if input.DeploymentConfiguration == nil { - input.DeploymentConfiguration = &ecs.DeploymentConfiguration{} + input.DeploymentConfiguration = &awstypes.DeploymentConfiguration{} } - input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int64(int64(d.Get("deployment_minimum_healthy_percent").(int))) + input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int32(int32(d.Get("deployment_minimum_healthy_percent").(int))) } - case ecs.SchedulingStrategyReplica: + case string(awstypes.SchedulingStrategyReplica): if d.HasChanges("deployment_maximum_percent", "deployment_minimum_healthy_percent") { if input.DeploymentConfiguration == nil { - input.DeploymentConfiguration = &ecs.DeploymentConfiguration{} + input.DeploymentConfiguration = &awstypes.DeploymentConfiguration{} } - input.DeploymentConfiguration.MaximumPercent = aws.Int64(int64(d.Get("deployment_maximum_percent").(int))) - input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int64(int64(d.Get("deployment_minimum_healthy_percent").(int))) + input.DeploymentConfiguration.MaximumPercent = aws.Int32(int32(d.Get("deployment_maximum_percent").(int))) + input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int32(int32(d.Get("deployment_minimum_healthy_percent").(int))) } if d.HasChange("desired_count") { - input.DesiredCount = aws.Int64(int64(d.Get("desired_count").(int))) + input.DesiredCount = aws.Int32(int32(d.Get("desired_count").(int))) } } @@ -966,7 +971,7 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange("health_check_grace_period_seconds") { - input.HealthCheckGracePeriodSeconds = aws.Int64(int64(d.Get("health_check_grace_period_seconds").(int))) + input.HealthCheckGracePeriodSeconds = aws.Int32(int32(d.Get("health_check_grace_period_seconds").(int))) } if d.HasChange("load_balancer") { @@ -982,7 +987,7 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int if d.HasChange("ordered_placement_strategy") { // Reference: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_UpdateService.html#ECS-UpdateService-request-placementStrategy // To remove an existing placement strategy, specify an empty object. - input.PlacementStrategy = []*ecs.PlacementStrategy{} + input.PlacementStrategy = []awstypes.PlacementStrategy{} if v, ok := d.GetOk("ordered_placement_strategy"); ok && len(v.([]interface{})) > 0 { ps, err := expandPlacementStrategy(v.([]interface{})) @@ -998,7 +1003,7 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int if d.HasChange("placement_constraints") { // Reference: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_UpdateService.html#ECS-UpdateService-request-placementConstraints // To remove all existing placement constraints, specify an empty array. - input.PlacementConstraints = []*ecs.PlacementConstraint{} + input.PlacementConstraints = []awstypes.PlacementConstraint{} if v, ok := d.Get("placement_constraints").(*schema.Set); ok && v.Len() > 0 { pc, err := expandPlacementConstraints(v.List()) @@ -1016,7 +1021,7 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange(names.AttrPropagateTags) { - input.PropagateTags = aws.String(d.Get(names.AttrPropagateTags).(string)) + input.PropagateTags = awstypes.PropagateTags(d.Get(names.AttrPropagateTags).(string)) } if d.HasChange("service_connect_configuration") { @@ -1037,14 +1042,14 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int // Retry due to IAM eventual consistency err := retry.RetryContext(ctx, propagationTimeout+serviceUpdateTimeout, func() *retry.RetryError { - _, err := conn.UpdateServiceWithContext(ctx, input) + _, err := conn.UpdateService(ctx, input) if err != nil { - if tfawserr.ErrMessageContains(err, ecs.ErrCodeInvalidParameterException, "verify that the ECS service role being passed has the proper permissions") { + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "verify that the ECS service role being passed has the proper permissions") { return retry.RetryableError(err) } - if tfawserr.ErrMessageContains(err, ecs.ErrCodeInvalidParameterException, "does not have an associated load balancer") { + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "does not have an associated load balancer") { return retry.RetryableError(err) } @@ -1054,7 +1059,7 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int }) if tfresource.TimedOut(err) { - _, err = conn.UpdateServiceWithContext(ctx, input) + _, err = conn.UpdateService(ctx, input) } if err != nil { @@ -1065,7 +1070,7 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int if d.Get("wait_for_steady_state").(bool) { fn = waitServiceStable } - if _, err := fn(ctx, conn, d.Id(), d.Get("cluster").(string), d.Timeout(schema.TimeoutUpdate)); err != nil { + if _, err := fn(ctx, conn, partition, d.Id(), d.Get("cluster").(string), d.Timeout(schema.TimeoutUpdate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for ECS Service (%s) update: %s", d.Id(), err) } } @@ -1075,9 +1080,10 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int func resourceServiceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSConn(ctx) + conn := meta.(*conns.AWSClient).ECSClient(ctx) + partition := meta.(*conns.AWSClient).Partition - service, err := FindServiceNoTagsByID(ctx, conn, d.Id(), d.Get("cluster").(string)) + service, err := FindServiceNoTagsByID(ctx, conn, partition, d.Id(), d.Get("cluster").(string)) if tfresource.NotFound(err) { return diags @@ -1087,16 +1093,16 @@ func resourceServiceDelete(ctx context.Context, d *schema.ResourceData, meta int return sdkdiag.AppendErrorf(diags, "reading ECS Service (%s): %s", d.Id(), err) } - if aws.StringValue(service.Status) == serviceStatusInactive { + if aws.ToString(service.Status) == serviceStatusInactive { return diags } // Drain the ECS service - if aws.StringValue(service.Status) != serviceStatusDraining && aws.StringValue(service.SchedulingStrategy) != ecs.SchedulingStrategyDaemon { - _, err := conn.UpdateServiceWithContext(ctx, &ecs.UpdateServiceInput{ + if aws.ToString(service.Status) != serviceStatusDraining && service.SchedulingStrategy != awstypes.SchedulingStrategyDaemon { + _, err := conn.UpdateService(ctx, &ecs.UpdateServiceInput{ Service: aws.String(d.Id()), Cluster: aws.String(d.Get("cluster").(string)), - DesiredCount: aws.Int64(0), + DesiredCount: aws.Int32(0), }) if err != nil { @@ -1110,10 +1116,10 @@ func resourceServiceDelete(ctx context.Context, d *schema.ResourceData, meta int } log.Printf("[DEBUG] Deleting ECS Service: %s", d.Id()) err = retry.RetryContext(ctx, d.Timeout(schema.TimeoutDelete), func() *retry.RetryError { - _, err := conn.DeleteServiceWithContext(ctx, &input) + _, err := conn.DeleteService(ctx, &input) if err != nil { - if tfawserr.ErrMessageContains(err, ecs.ErrCodeInvalidParameterException, "The service cannot be stopped while deployments are active.") { + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "The service cannot be stopped while deployments are active.") { return retry.RetryableError(err) } @@ -1128,14 +1134,14 @@ func resourceServiceDelete(ctx context.Context, d *schema.ResourceData, meta int }) if tfresource.TimedOut(err) { - _, err = conn.DeleteServiceWithContext(ctx, &input) + _, err = conn.DeleteService(ctx, &input) } if err != nil { return sdkdiag.AppendErrorf(diags, "deleting ECS Service (%s): %s", d.Id(), err) } - if err := waitServiceInactive(ctx, conn, d.Id(), d.Get("cluster").(string), d.Timeout(schema.TimeoutDelete)); err != nil { + if err := waitServiceInactive(ctx, conn, partition, d.Id(), d.Get("cluster").(string), d.Timeout(schema.TimeoutDelete)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for ECS Service (%s) delete: %s", d.Id(), err) } @@ -1216,29 +1222,29 @@ func capacityProviderStrategyForceNew(d *schema.ResourceDiff) error { return nil } -func expandAlarms(tfMap map[string]interface{}) *ecs.DeploymentAlarms { +func expandAlarms(tfMap map[string]interface{}) *awstypes.DeploymentAlarms { if tfMap == nil { return nil } - apiObject := &ecs.DeploymentAlarms{} + apiObject := &awstypes.DeploymentAlarms{} if v, ok := tfMap["enable"].(bool); ok { - apiObject.Enable = aws.Bool(v) + apiObject.Enable = v } if v, ok := tfMap["rollback"].(bool); ok { - apiObject.Rollback = aws.Bool(v) + apiObject.Rollback = v } if v, ok := tfMap["alarm_names"].(*schema.Set); ok && v.Len() > 0 { - apiObject.AlarmNames = flex.ExpandStringSet(v) + apiObject.AlarmNames = flex.ExpandStringValueSet(v) } return apiObject } -func flattenAlarms(apiObject *ecs.DeploymentAlarms) map[string]interface{} { +func flattenAlarms(apiObject *awstypes.DeploymentAlarms) map[string]interface{} { if apiObject == nil { return nil } @@ -1246,116 +1252,110 @@ func flattenAlarms(apiObject *ecs.DeploymentAlarms) map[string]interface{} { tfMap := map[string]interface{}{} if v := apiObject.AlarmNames; v != nil { - tfMap["alarm_names"] = aws.StringValueSlice(v) + tfMap["alarm_names"] = v } - if v := apiObject.Enable; v != nil { - tfMap["enable"] = aws.BoolValue(v) - } + tfMap["enable"] = apiObject.Enable - if v := apiObject.Rollback; v != nil { - tfMap["rollback"] = aws.BoolValue(v) - } + tfMap["rollback"] = apiObject.Rollback return tfMap } -func expandDeploymentController(l []interface{}) *ecs.DeploymentController { +func expandDeploymentController(l []interface{}) *awstypes.DeploymentController { if len(l) == 0 || l[0] == nil { return nil } m := l[0].(map[string]interface{}) - deploymentController := &ecs.DeploymentController{ - Type: aws.String(m[names.AttrType].(string)), + deploymentController := &awstypes.DeploymentController{ + Type: awstypes.DeploymentControllerType(m[names.AttrType].(string)), } return deploymentController } -func flattenDeploymentController(deploymentController *ecs.DeploymentController) []interface{} { +func flattenDeploymentController(deploymentController *awstypes.DeploymentController) []interface{} { m := map[string]interface{}{ - names.AttrType: ecs.DeploymentControllerTypeEcs, + names.AttrType: awstypes.DeploymentControllerTypeEcs, } if deploymentController == nil { return []interface{}{m} } - m[names.AttrType] = aws.StringValue(deploymentController.Type) + m[names.AttrType] = string(deploymentController.Type) return []interface{}{m} } -func expandDeploymentCircuitBreaker(tfMap map[string]interface{}) *ecs.DeploymentCircuitBreaker { +func expandDeploymentCircuitBreaker(tfMap map[string]interface{}) *awstypes.DeploymentCircuitBreaker { if tfMap == nil { return nil } - apiObject := &ecs.DeploymentCircuitBreaker{} + apiObject := &awstypes.DeploymentCircuitBreaker{} - apiObject.Enable = aws.Bool(tfMap["enable"].(bool)) - apiObject.Rollback = aws.Bool(tfMap["rollback"].(bool)) + apiObject.Enable = tfMap["enable"].(bool) + apiObject.Rollback = tfMap["rollback"].(bool) return apiObject } -func flattenDeploymentCircuitBreaker(apiObject *ecs.DeploymentCircuitBreaker) map[string]interface{} { +func flattenDeploymentCircuitBreaker(apiObject *awstypes.DeploymentCircuitBreaker) map[string]interface{} { if apiObject == nil { return nil } tfMap := map[string]interface{}{} - tfMap["enable"] = aws.BoolValue(apiObject.Enable) - tfMap["rollback"] = aws.BoolValue(apiObject.Rollback) + tfMap["enable"] = apiObject.Enable + tfMap["rollback"] = apiObject.Rollback return tfMap } -func flattenNetworkConfiguration(nc *ecs.NetworkConfiguration) []interface{} { +func flattenNetworkConfiguration(nc *awstypes.NetworkConfiguration) []interface{} { if nc == nil { return nil } result := make(map[string]interface{}) - result[names.AttrSecurityGroups] = flex.FlattenStringSet(nc.AwsvpcConfiguration.SecurityGroups) - result[names.AttrSubnets] = flex.FlattenStringSet(nc.AwsvpcConfiguration.Subnets) + result[names.AttrSecurityGroups] = flex.FlattenStringValueSet(nc.AwsvpcConfiguration.SecurityGroups) + result[names.AttrSubnets] = flex.FlattenStringValueSet(nc.AwsvpcConfiguration.Subnets) - if nc.AwsvpcConfiguration.AssignPublicIp != nil { - result["assign_public_ip"] = aws.StringValue(nc.AwsvpcConfiguration.AssignPublicIp) == ecs.AssignPublicIpEnabled - } + result["assign_public_ip"] = nc.AwsvpcConfiguration.AssignPublicIp == awstypes.AssignPublicIpEnabled return []interface{}{result} } -func expandNetworkConfiguration(nc []interface{}) *ecs.NetworkConfiguration { +func expandNetworkConfiguration(nc []interface{}) *awstypes.NetworkConfiguration { if len(nc) == 0 { return nil } - awsVpcConfig := &ecs.AwsVpcConfiguration{} + awsVpcConfig := &awstypes.AwsVpcConfiguration{} raw := nc[0].(map[string]interface{}) if val, ok := raw[names.AttrSecurityGroups]; ok { - awsVpcConfig.SecurityGroups = flex.ExpandStringSet(val.(*schema.Set)) + awsVpcConfig.SecurityGroups = flex.ExpandStringValueSet(val.(*schema.Set)) } - awsVpcConfig.Subnets = flex.ExpandStringSet(raw[names.AttrSubnets].(*schema.Set)) + awsVpcConfig.Subnets = flex.ExpandStringValueSet(raw[names.AttrSubnets].(*schema.Set)) if val, ok := raw["assign_public_ip"].(bool); ok { - awsVpcConfig.AssignPublicIp = aws.String(ecs.AssignPublicIpDisabled) + awsVpcConfig.AssignPublicIp = awstypes.AssignPublicIp(awstypes.AssignPublicIpDisabled) if val { - awsVpcConfig.AssignPublicIp = aws.String(ecs.AssignPublicIpEnabled) + awsVpcConfig.AssignPublicIp = awstypes.AssignPublicIp(awstypes.AssignPublicIpEnabled) } } - return &ecs.NetworkConfiguration{AwsvpcConfiguration: awsVpcConfig} + return &awstypes.NetworkConfiguration{AwsvpcConfiguration: awsVpcConfig} } -func expandPlacementConstraints(tfList []interface{}) ([]*ecs.PlacementConstraint, error) { +func expandPlacementConstraints(tfList []interface{}) ([]awstypes.PlacementConstraint, error) { if len(tfList) == 0 { return nil, nil } - var result []*ecs.PlacementConstraint + var result []awstypes.PlacementConstraint for _, tfMapRaw := range tfList { if tfMapRaw == nil { @@ -1364,17 +1364,17 @@ func expandPlacementConstraints(tfList []interface{}) ([]*ecs.PlacementConstrain tfMap := tfMapRaw.(map[string]interface{}) - apiObject := &ecs.PlacementConstraint{} + apiObject := awstypes.PlacementConstraint{} if v, ok := tfMap[names.AttrExpression].(string); ok && v != "" { apiObject.Expression = aws.String(v) } if v, ok := tfMap[names.AttrType].(string); ok && v != "" { - apiObject.Type = aws.String(v) + apiObject.Type = awstypes.PlacementConstraintType(v) } - if err := validPlacementConstraint(aws.StringValue(apiObject.Type), aws.StringValue(apiObject.Expression)); err != nil { + if err := validPlacementConstraint(string(apiObject.Type), aws.ToString(apiObject.Expression)); err != nil { return result, err } @@ -1384,16 +1384,16 @@ func expandPlacementConstraints(tfList []interface{}) ([]*ecs.PlacementConstrain return result, nil } -func flattenServicePlacementConstraints(pcs []*ecs.PlacementConstraint) []map[string]interface{} { +func flattenServicePlacementConstraints(pcs []awstypes.PlacementConstraint) []map[string]interface{} { if len(pcs) == 0 { return nil } results := make([]map[string]interface{}, 0) for _, pc := range pcs { c := make(map[string]interface{}) - c[names.AttrType] = aws.StringValue(pc.Type) + c[names.AttrType] = string(pc.Type) if pc.Expression != nil { - c[names.AttrExpression] = aws.StringValue(pc.Expression) + c[names.AttrExpression] = aws.ToString(pc.Expression) } results = append(results, c) @@ -1401,11 +1401,11 @@ func flattenServicePlacementConstraints(pcs []*ecs.PlacementConstraint) []map[st return results } -func expandPlacementStrategy(s []interface{}) ([]*ecs.PlacementStrategy, error) { +func expandPlacementStrategy(s []interface{}) ([]awstypes.PlacementStrategy, error) { if len(s) == 0 { return nil, nil } - pss := make([]*ecs.PlacementStrategy, 0) + pss := make([]awstypes.PlacementStrategy, 0) for _, raw := range s { p, ok := raw.(map[string]interface{}) @@ -1428,8 +1428,8 @@ func expandPlacementStrategy(s []interface{}) ([]*ecs.PlacementStrategy, error) if err := validPlacementStrategy(t, f); err != nil { return nil, err } - ps := &ecs.PlacementStrategy{ - Type: aws.String(t), + ps := awstypes.PlacementStrategy{ + Type: awstypes.PlacementStrategyType(t), } if f != "" { // Field must be omitted (i.e. not empty string) for random strategy @@ -1440,21 +1440,21 @@ func expandPlacementStrategy(s []interface{}) ([]*ecs.PlacementStrategy, error) return pss, nil } -func flattenPlacementStrategy(pss []*ecs.PlacementStrategy) []interface{} { +func flattenPlacementStrategy(pss []awstypes.PlacementStrategy) []interface{} { if len(pss) == 0 { return nil } results := make([]interface{}, 0, len(pss)) for _, ps := range pss { c := make(map[string]interface{}) - c[names.AttrType] = aws.StringValue(ps.Type) + c[names.AttrType] = string(ps.Type) if ps.Field != nil { - c[names.AttrField] = aws.StringValue(ps.Field) + c[names.AttrField] = aws.ToString(ps.Field) // for some fields the API requires lowercase for creation but will return uppercase on query - if aws.StringValue(ps.Field) == "MEMORY" || aws.StringValue(ps.Field) == "CPU" { - c[names.AttrField] = strings.ToLower(aws.StringValue(ps.Field)) + if aws.ToString(ps.Field) == "MEMORY" || aws.ToString(ps.Field) == "CPU" { + c[names.AttrField] = strings.ToLower(aws.ToString(ps.Field)) } } @@ -1463,17 +1463,17 @@ func flattenPlacementStrategy(pss []*ecs.PlacementStrategy) []interface{} { return results } -func expandServiceConnectConfiguration(sc []interface{}) *ecs.ServiceConnectConfiguration { +func expandServiceConnectConfiguration(sc []interface{}) *awstypes.ServiceConnectConfiguration { if len(sc) == 0 { - return &ecs.ServiceConnectConfiguration{ - Enabled: aws.Bool(false), + return &awstypes.ServiceConnectConfiguration{ + Enabled: false, } } raw := sc[0].(map[string]interface{}) - config := &ecs.ServiceConnectConfiguration{} + config := &awstypes.ServiceConnectConfiguration{} if v, ok := raw[names.AttrEnabled].(bool); ok { - config.Enabled = aws.Bool(v) + config.Enabled = v } if v, ok := raw["log_configuration"].([]interface{}); ok && len(v) > 0 { @@ -1491,18 +1491,18 @@ func expandServiceConnectConfiguration(sc []interface{}) *ecs.ServiceConnectConf return config } -func expandLogConfiguration(lc []interface{}) *ecs.LogConfiguration { +func expandLogConfiguration(lc []interface{}) *awstypes.LogConfiguration { if len(lc) == 0 { - return &ecs.LogConfiguration{} + return &awstypes.LogConfiguration{} } raw := lc[0].(map[string]interface{}) - config := &ecs.LogConfiguration{} + config := &awstypes.LogConfiguration{} if v, ok := raw["log_driver"].(string); ok && v != "" { - config.LogDriver = aws.String(v) + config.LogDriver = awstypes.LogDriver(v) } if v, ok := raw["options"].(map[string]interface{}); ok && len(v) > 0 { - config.Options = flex.ExpandStringMap(v) + config.Options = flex.ExpandStringValueMap(v) } if v, ok := raw["secret_option"].([]interface{}); ok && len(v) > 0 { config.SecretOptions = expandSecretOptions(v) @@ -1511,19 +1511,19 @@ func expandLogConfiguration(lc []interface{}) *ecs.LogConfiguration { return config } -func expandSecretOptions(sop []interface{}) []*ecs.Secret { +func expandSecretOptions(sop []interface{}) []awstypes.Secret { if len(sop) == 0 { return nil } - var out []*ecs.Secret + var out []awstypes.Secret for _, item := range sop { raw, ok := item.(map[string]interface{}) if !ok { continue } - var config ecs.Secret + var config awstypes.Secret if v, ok := raw[names.AttrName].(string); ok && v != "" { config.Name = aws.String(v) } @@ -1531,23 +1531,23 @@ func expandSecretOptions(sop []interface{}) []*ecs.Secret { config.ValueFrom = aws.String(v) } - out = append(out, &config) + out = append(out, config) } return out } -func expandVolumeConfigurations(vc []interface{}) []*ecs.ServiceVolumeConfiguration { +func expandVolumeConfigurations(vc []interface{}) []awstypes.ServiceVolumeConfiguration { if len(vc) == 0 { return nil } - vcs := make([]*ecs.ServiceVolumeConfiguration, 0) + vcs := make([]awstypes.ServiceVolumeConfiguration, 0) for _, raw := range vc { p := raw.(map[string]interface{}) - config := &ecs.ServiceVolumeConfiguration{ + config := awstypes.ServiceVolumeConfiguration{ Name: aws.String(p[names.AttrName].(string)), } @@ -1560,13 +1560,13 @@ func expandVolumeConfigurations(vc []interface{}) []*ecs.ServiceVolumeConfigurat return vcs } -func expandManagedEBSVolume(ebs []interface{}) *ecs.ServiceManagedEBSVolumeConfiguration { +func expandManagedEBSVolume(ebs []interface{}) *awstypes.ServiceManagedEBSVolumeConfiguration { if len(ebs) == 0 { - return &ecs.ServiceManagedEBSVolumeConfiguration{} + return &awstypes.ServiceManagedEBSVolumeConfiguration{} } raw := ebs[0].(map[string]interface{}) - config := &ecs.ServiceManagedEBSVolumeConfiguration{} + config := &awstypes.ServiceManagedEBSVolumeConfiguration{} if v, ok := raw[names.AttrRoleARN].(string); ok && v != "" { config.RoleArn = aws.String(v) } @@ -1574,22 +1574,22 @@ func expandManagedEBSVolume(ebs []interface{}) *ecs.ServiceManagedEBSVolumeConfi config.Encrypted = aws.Bool(v) } if v, ok := raw["file_system_type"].(string); ok && v != "" { - config.FilesystemType = aws.String(v) + config.FilesystemType = awstypes.TaskFilesystemType(v) } if v, ok := raw[names.AttrIOPS].(int); ok && v != 0 { - config.Iops = aws.Int64(int64(v)) + config.Iops = aws.Int32(int32(v)) } if v, ok := raw[names.AttrKMSKeyID].(string); ok && v != "" { config.KmsKeyId = aws.String(v) } if v, ok := raw["size_in_gb"].(int); ok && v != 0 { - config.SizeInGiB = aws.Int64(int64(v)) + config.SizeInGiB = aws.Int32(int32(v)) } if v, ok := raw[names.AttrSnapshotID].(string); ok && v != "" { config.SnapshotId = aws.String(v) } if v, ok := raw[names.AttrThroughput].(int); ok && v != 0 { - config.Throughput = aws.Int64(int64(v)) + config.Throughput = aws.Int32(int32(v)) } if v, ok := raw[names.AttrVolumeType].(string); ok && v != "" { config.VolumeType = aws.String(v) @@ -1598,19 +1598,19 @@ func expandManagedEBSVolume(ebs []interface{}) *ecs.ServiceManagedEBSVolumeConfi return config } -func expandServices(srv []interface{}) []*ecs.ServiceConnectService { +func expandServices(srv []interface{}) []awstypes.ServiceConnectService { if len(srv) == 0 { return nil } - var out []*ecs.ServiceConnectService + var out []awstypes.ServiceConnectService for _, item := range srv { raw, ok := item.(map[string]interface{}) if !ok { continue } - var config ecs.ServiceConnectService + var config awstypes.ServiceConnectService if v, ok := raw["client_alias"].([]interface{}); ok && len(v) > 0 { config.ClientAliases = expandClientAliases(v) } @@ -1618,7 +1618,7 @@ func expandServices(srv []interface{}) []*ecs.ServiceConnectService { config.DiscoveryName = aws.String(v) } if v, ok := raw["ingress_port_override"].(int); ok && v != 0 { - config.IngressPortOverride = aws.Int64(int64(v)) + config.IngressPortOverride = aws.Int32(int32(v)) } if v, ok := raw["port_name"].(string); ok && v != "" { config.PortName = aws.String(v) @@ -1632,13 +1632,13 @@ func expandServices(srv []interface{}) []*ecs.ServiceConnectService { config.Tls = expandTLS(v) } - out = append(out, &config) + out = append(out, config) } return out } -func expandTimeout(timeout []interface{}) *ecs.TimeoutConfiguration { +func expandTimeout(timeout []interface{}) *awstypes.TimeoutConfiguration { if len(timeout) == 0 { return nil } @@ -1647,17 +1647,17 @@ func expandTimeout(timeout []interface{}) *ecs.TimeoutConfiguration { if !ok { return nil } - timeoutConfig := &ecs.TimeoutConfiguration{} + timeoutConfig := &awstypes.TimeoutConfiguration{} if v, ok := raw["idle_timeout_seconds"].(int); ok { - timeoutConfig.IdleTimeoutSeconds = aws.Int64(int64(v)) + timeoutConfig.IdleTimeoutSeconds = aws.Int32(int32(v)) } if v, ok := raw["per_request_timeout_seconds"].(int); ok { - timeoutConfig.PerRequestTimeoutSeconds = aws.Int64(int64(v)) + timeoutConfig.PerRequestTimeoutSeconds = aws.Int32(int32(v)) } return timeoutConfig } -func expandTLS(tls []interface{}) *ecs.ServiceConnectTlsConfiguration { +func expandTLS(tls []interface{}) *awstypes.ServiceConnectTlsConfiguration { if len(tls) == 0 { return nil } @@ -1666,7 +1666,7 @@ func expandTLS(tls []interface{}) *ecs.ServiceConnectTlsConfiguration { if !ok { return nil } - tlsConfig := &ecs.ServiceConnectTlsConfiguration{} + tlsConfig := &awstypes.ServiceConnectTlsConfiguration{} if v, ok := raw["issuer_cert_authority"].([]interface{}); ok && len(v) > 0 { tlsConfig.IssuerCertificateAuthority = expandIssuerCertAuthority(v) } @@ -1679,7 +1679,7 @@ func expandTLS(tls []interface{}) *ecs.ServiceConnectTlsConfiguration { return tlsConfig } -func expandIssuerCertAuthority(pca []interface{}) *ecs.ServiceConnectTlsCertificateAuthority { +func expandIssuerCertAuthority(pca []interface{}) *awstypes.ServiceConnectTlsCertificateAuthority { if len(pca) == 0 { return nil } @@ -1688,7 +1688,7 @@ func expandIssuerCertAuthority(pca []interface{}) *ecs.ServiceConnectTlsCertific if !ok { return nil } - config := &ecs.ServiceConnectTlsCertificateAuthority{} + config := &awstypes.ServiceConnectTlsCertificateAuthority{} if v, ok := raw["aws_pca_authority_arn"].(string); ok && v != "" { config.AwsPcaAuthorityArn = aws.String(v) @@ -1696,49 +1696,49 @@ func expandIssuerCertAuthority(pca []interface{}) *ecs.ServiceConnectTlsCertific return config } -func expandClientAliases(srv []interface{}) []*ecs.ServiceConnectClientAlias { +func expandClientAliases(srv []interface{}) []awstypes.ServiceConnectClientAlias { if len(srv) == 0 { return nil } - var out []*ecs.ServiceConnectClientAlias + var out []awstypes.ServiceConnectClientAlias for _, item := range srv { raw, ok := item.(map[string]interface{}) if !ok { continue } - var config ecs.ServiceConnectClientAlias + var config awstypes.ServiceConnectClientAlias if v, ok := raw[names.AttrPort].(int); ok { - config.Port = aws.Int64(int64(v)) + config.Port = aws.Int32(int32(v)) } if v, ok := raw[names.AttrDNSName].(string); ok && v != "" { config.DnsName = aws.String(v) } - out = append(out, &config) + out = append(out, config) } return out } -func flattenServiceRegistries(srs []*ecs.ServiceRegistry) []map[string]interface{} { +func flattenServiceRegistries(srs []awstypes.ServiceRegistry) []map[string]interface{} { if len(srs) == 0 { return nil } results := make([]map[string]interface{}, 0) for _, sr := range srs { c := map[string]interface{}{ - "registry_arn": aws.StringValue(sr.RegistryArn), + "registry_arn": aws.ToString(sr.RegistryArn), } if sr.Port != nil { - c[names.AttrPort] = int(aws.Int64Value(sr.Port)) + c[names.AttrPort] = int(aws.ToInt32(sr.Port)) } if sr.ContainerPort != nil { - c["container_port"] = int(aws.Int64Value(sr.ContainerPort)) + c["container_port"] = int(aws.ToInt32(sr.ContainerPort)) } if sr.ContainerName != nil { - c["container_name"] = aws.StringValue(sr.ContainerName) + c["container_name"] = aws.ToString(sr.ContainerName) } results = append(results, c) } @@ -1760,26 +1760,26 @@ func resourceLoadBalancerHash(v interface{}) int { return create.StringHashcode(buf.String()) } -func serviceCreateWithRetry(ctx context.Context, conn *ecs.ECS, input ecs.CreateServiceInput) (*ecs.CreateServiceOutput, error) { +func serviceCreateWithRetry(ctx context.Context, conn *ecs.Client, input ecs.CreateServiceInput) (*ecs.CreateServiceOutput, error) { var output *ecs.CreateServiceOutput err := retry.RetryContext(ctx, propagationTimeout+serviceCreateTimeout, func() *retry.RetryError { var err error - output, err = conn.CreateServiceWithContext(ctx, &input) + output, err = conn.CreateService(ctx, &input) if err != nil { - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeClusterNotFoundException) { + if errs.IsA[*awstypes.ClusterNotFoundException](err) { return retry.RetryableError(err) } - if tfawserr.ErrMessageContains(err, ecs.ErrCodeInvalidParameterException, "verify that the ECS service role being passed has the proper permissions") { + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "verify that the ECS service role being passed has the proper permissions") { return retry.RetryableError(err) } - if tfawserr.ErrMessageContains(err, ecs.ErrCodeInvalidParameterException, "does not have an associated load balancer") { + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "does not have an associated load balancer") { return retry.RetryableError(err) } - if tfawserr.ErrMessageContains(err, ecs.ErrCodeInvalidParameterException, "Unable to assume the service linked role") { + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "Unable to assume the service linked role") { return retry.RetryableError(err) } @@ -1790,7 +1790,7 @@ func serviceCreateWithRetry(ctx context.Context, conn *ecs.ECS, input ecs.Create }) if tfresource.TimedOut(err) { - output, err = conn.CreateServiceWithContext(ctx, &input) + output, err = conn.CreateService(ctx, &input) } return output, err diff --git a/internal/service/ecs/service_data_source.go b/internal/service/ecs/service_data_source.go index ae564969bb9..cea98f7cd94 100644 --- a/internal/service/ecs/service_data_source.go +++ b/internal/service/ecs/service_data_source.go @@ -69,7 +69,7 @@ func dataSourceServiceRead(ctx context.Context, d *schema.ResourceData, meta int Services: []string{serviceName}, } - log.Printf("[DEBUG] Reading ECS Service: %s", params) + log.Printf("[DEBUG] Reading ECS Service: %+v", params) desc, err := conn.DescribeServices(ctx, params) if err != nil { diff --git a/internal/service/ecs/service_test.go b/internal/service/ecs/service_test.go index db71b220f6d..ea3b98c1570 100644 --- a/internal/service/ecs/service_test.go +++ b/internal/service/ecs/service_test.go @@ -11,16 +11,16 @@ import ( "time" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/aws/aws-sdk-go/service/servicediscovery" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs" tfecs "github.com/hashicorp/terraform-provider-aws/internal/service/ecs" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" @@ -90,7 +90,7 @@ func Test_GetClustereNameFromARN(t *testing.T) { func TestAccECSService_basic(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -125,7 +125,7 @@ func TestAccECSService_basic(t *testing.T) { func TestAccECSService_basicImport(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" importInput := fmt.Sprintf("%s/%s", rName, rName) @@ -165,7 +165,7 @@ func TestAccECSService_basicImport(t *testing.T) { func TestAccECSService_disappears(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -189,7 +189,7 @@ func TestAccECSService_disappears(t *testing.T) { func TestAccECSService_PlacementStrategy_unnormalized(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -211,7 +211,7 @@ func TestAccECSService_PlacementStrategy_unnormalized(t *testing.T) { func TestAccECSService_CapacityProviderStrategy_basic(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -239,7 +239,7 @@ func TestAccECSService_CapacityProviderStrategy_basic(t *testing.T) { func TestAccECSService_CapacityProviderStrategy_forceNewDeployment(t *testing.T) { ctx := acctest.Context(t) - var service1, service2 ecs.Service + var service1, service2 awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -268,7 +268,7 @@ func TestAccECSService_CapacityProviderStrategy_forceNewDeployment(t *testing.T) func TestAccECSService_CapacityProviderStrategy_update(t *testing.T) { ctx := acctest.Context(t) - var service1, service2 ecs.Service + var service1, service2 awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -309,7 +309,7 @@ func TestAccECSService_CapacityProviderStrategy_update(t *testing.T) { func TestAccECSService_VolumeConfiguration_basic(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -331,7 +331,7 @@ func TestAccECSService_VolumeConfiguration_basic(t *testing.T) { func TestAccECSService_VolumeConfiguration_update(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -365,7 +365,7 @@ func TestAccECSService_VolumeConfiguration_update(t *testing.T) { func TestAccECSService_familyAndRevision(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -394,7 +394,7 @@ func TestAccECSService_familyAndRevision(t *testing.T) { func TestAccECSService_healthCheckGracePeriodSeconds(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -443,7 +443,7 @@ func TestAccECSService_healthCheckGracePeriodSeconds(t *testing.T) { func TestAccECSService_iamRole(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -465,7 +465,7 @@ func TestAccECSService_iamRole(t *testing.T) { func TestAccECSService_DeploymentControllerType_codeDeploy(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -480,7 +480,7 @@ func TestAccECSService_DeploymentControllerType_codeDeploy(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckServiceExists(ctx, resourceName, &service), resource.TestCheckResourceAttr(resourceName, "deployment_controller.#", acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "deployment_controller.0.type", ecs.DeploymentControllerTypeCodeDeploy), + resource.TestCheckResourceAttr(resourceName, "deployment_controller.0.type", string(awstypes.DeploymentControllerTypeCodeDeploy)), ), }, { @@ -498,7 +498,7 @@ func TestAccECSService_DeploymentControllerType_codeDeploy(t *testing.T) { func TestAccECSService_DeploymentControllerType_codeDeployUpdateDesiredCountAndHealthCheckGracePeriod(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -517,7 +517,7 @@ func TestAccECSService_DeploymentControllerType_codeDeployUpdateDesiredCountAndH Check: resource.ComposeTestCheckFunc( testAccCheckServiceExists(ctx, resourceName, &service), resource.TestCheckResourceAttr(resourceName, "deployment_controller.#", acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "deployment_controller.0.type", ecs.DeploymentControllerTypeCodeDeploy), + resource.TestCheckResourceAttr(resourceName, "deployment_controller.0.type", string(awstypes.DeploymentControllerTypeCodeDeploy)), resource.TestCheckResourceAttr(resourceName, "desired_count", acctest.Ct1), ), }, @@ -541,7 +541,7 @@ func TestAccECSService_DeploymentControllerType_codeDeployUpdateDesiredCountAndH func TestAccECSService_DeploymentControllerType_external(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -556,7 +556,7 @@ func TestAccECSService_DeploymentControllerType_external(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckServiceExists(ctx, resourceName, &service), resource.TestCheckResourceAttr(resourceName, "deployment_controller.#", acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "deployment_controller.0.type", ecs.DeploymentControllerTypeExternal), + resource.TestCheckResourceAttr(resourceName, "deployment_controller.0.type", string(awstypes.DeploymentControllerTypeExternal)), ), }, { @@ -573,7 +573,7 @@ func TestAccECSService_DeploymentControllerType_external(t *testing.T) { func TestAccECSService_alarmsAdd(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -604,7 +604,7 @@ func TestAccECSService_alarmsAdd(t *testing.T) { func TestAccECSService_alarmsUpdate(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -636,7 +636,7 @@ func TestAccECSService_alarmsUpdate(t *testing.T) { func TestAccECSService_DeploymentValues_basic(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -661,7 +661,7 @@ func TestAccECSService_DeploymentValues_basic(t *testing.T) { // Regression for https://github.com/hashicorp/terraform-provider-aws/issues/6315 func TestAccECSService_DeploymentValues_minZeroMaxOneHundred(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -685,7 +685,7 @@ func TestAccECSService_DeploymentValues_minZeroMaxOneHundred(t *testing.T) { func TestAccECSService_deploymentCircuitBreaker(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -711,7 +711,7 @@ func TestAccECSService_deploymentCircuitBreaker(t *testing.T) { // Regression for https://github.com/hashicorp/terraform/issues/3444 func TestAccECSService_loadBalancerChanges(t *testing.T) { ctx := acctest.Context(t) - var s1, s2 ecs.Service + var s1, s2 awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -741,7 +741,7 @@ func TestAccECSService_loadBalancerChanges(t *testing.T) { // Regression for https://github.com/hashicorp/terraform/issues/3361 func TestAccECSService_clusterName(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -764,7 +764,7 @@ func TestAccECSService_clusterName(t *testing.T) { func TestAccECSService_alb(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -787,7 +787,7 @@ func TestAccECSService_alb(t *testing.T) { func TestAccECSService_multipleTargetGroups(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -810,7 +810,7 @@ func TestAccECSService_multipleTargetGroups(t *testing.T) { func TestAccECSService_forceNewDeployment(t *testing.T) { ctx := acctest.Context(t) - var service1, service2 ecs.Service + var service1, service2 awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -843,7 +843,7 @@ func TestAccECSService_forceNewDeployment(t *testing.T) { func TestAccECSService_forceNewDeploymentTriggers(t *testing.T) { ctx := acctest.Context(t) - var service1, service2 ecs.Service + var service1, service2 awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -880,7 +880,7 @@ func TestAccECSService_forceNewDeploymentTriggers(t *testing.T) { func TestAccECSService_PlacementStrategy_basic(t *testing.T) { ctx := acctest.Context(t) - var service1, service2, service3, service4 ecs.Service + var service1, service2, service3, service4 awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -954,7 +954,7 @@ func TestAccECSService_PlacementStrategy_missing(t *testing.T) { func TestAccECSService_PlacementConstraints_basic(t *testing.T) { ctx := acctest.Context(t) - var service1, service2 ecs.Service + var service1, service2 awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -985,7 +985,7 @@ func TestAccECSService_PlacementConstraints_basic(t *testing.T) { func TestAccECSService_PlacementConstraints_emptyExpression(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1008,7 +1008,7 @@ func TestAccECSService_PlacementConstraints_emptyExpression(t *testing.T) { func TestAccECSService_LaunchTypeFargate_basic(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1049,7 +1049,7 @@ func TestAccECSService_LaunchTypeFargate_basic(t *testing.T) { func TestAccECSService_LaunchTypeFargate_platformVersion(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1086,7 +1086,7 @@ func TestAccECSService_LaunchTypeFargate_platformVersion(t *testing.T) { func TestAccECSService_LaunchTypeFargate_waitForSteadyState(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1120,7 +1120,7 @@ func TestAccECSService_LaunchTypeFargate_waitForSteadyState(t *testing.T) { func TestAccECSService_LaunchTypeFargate_updateWaitForSteadyState(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1162,7 +1162,7 @@ func TestAccECSService_LaunchTypeFargate_updateWaitForSteadyState(t *testing.T) func TestAccECSService_LaunchTypeEC2_network(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1196,7 +1196,7 @@ func TestAccECSService_LaunchTypeEC2_network(t *testing.T) { func TestAccECSService_DaemonSchedulingStrategy_basic(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1219,7 +1219,7 @@ func TestAccECSService_DaemonSchedulingStrategy_basic(t *testing.T) { func TestAccECSService_DaemonSchedulingStrategy_setDeploymentMinimum(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1242,7 +1242,7 @@ func TestAccECSService_DaemonSchedulingStrategy_setDeploymentMinimum(t *testing. func TestAccECSService_replicaSchedulingStrategy(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1265,7 +1265,7 @@ func TestAccECSService_replicaSchedulingStrategy(t *testing.T) { func TestAccECSService_ServiceRegistries_basic(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1288,7 +1288,7 @@ func TestAccECSService_ServiceRegistries_basic(t *testing.T) { func TestAccECSService_ServiceRegistries_container(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1311,7 +1311,7 @@ func TestAccECSService_ServiceRegistries_container(t *testing.T) { func TestAccECSService_ServiceRegistries_changes(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) serviceDiscoveryName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) updatedServiceDiscoveryName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -1347,7 +1347,7 @@ func TestAccECSService_ServiceRegistries_changes(t *testing.T) { func TestAccECSService_ServiceRegistries_removal(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) serviceDiscoveryName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1382,7 +1382,7 @@ func TestAccECSService_ServiceRegistries_removal(t *testing.T) { func TestAccECSService_ServiceConnect_basic(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1405,7 +1405,7 @@ func TestAccECSService_ServiceConnect_basic(t *testing.T) { func TestAccECSService_ServiceConnect_full(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1428,7 +1428,7 @@ func TestAccECSService_ServiceConnect_full(t *testing.T) { func TestAccECSService_ServiceConnect_tls_with_empty_timeout(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1451,7 +1451,7 @@ func TestAccECSService_ServiceConnect_tls_with_empty_timeout(t *testing.T) { func TestAccECSService_ServiceConnect_ingressPortOverride(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1484,7 +1484,7 @@ func TestAccECSService_ServiceConnect_ingressPortOverride(t *testing.T) { func TestAccECSService_ServiceConnect_remove(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1515,7 +1515,7 @@ func TestAccECSService_ServiceConnect_remove(t *testing.T) { func TestAccECSService_Tags_basic(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1565,7 +1565,7 @@ func TestAccECSService_Tags_basic(t *testing.T) { func TestAccECSService_Tags_managed(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1589,7 +1589,7 @@ func TestAccECSService_Tags_managed(t *testing.T) { func TestAccECSService_Tags_propagate(t *testing.T) { ctx := acctest.Context(t) - var first, second, third ecs.Service + var first, second, third awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1604,21 +1604,21 @@ func TestAccECSService_Tags_propagate(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckServiceExists(ctx, resourceName, &first), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, names.AttrPropagateTags, ecs.PropagateTagsService), + resource.TestCheckResourceAttr(resourceName, names.AttrPropagateTags, string(awstypes.PropagateTagsService)), ), }, { Config: testAccServiceConfig_propagateTags(rName, "TASK_DEFINITION"), Check: resource.ComposeTestCheckFunc( testAccCheckServiceExists(ctx, resourceName, &second), - resource.TestCheckResourceAttr(resourceName, names.AttrPropagateTags, ecs.PropagateTagsTaskDefinition), + resource.TestCheckResourceAttr(resourceName, names.AttrPropagateTags, string(awstypes.PropagateTagsTaskDefinition)), ), }, { Config: testAccServiceConfig_propagateTags(rName, "NONE"), Check: resource.ComposeTestCheckFunc( testAccCheckServiceExists(ctx, resourceName, &third), - resource.TestCheckResourceAttr(resourceName, names.AttrPropagateTags, ecs.PropagateTagsNone), + resource.TestCheckResourceAttr(resourceName, names.AttrPropagateTags, string(awstypes.PropagateTagsNone)), ), }, }, @@ -1627,7 +1627,7 @@ func TestAccECSService_Tags_propagate(t *testing.T) { func TestAccECSService_executeCommand(t *testing.T) { ctx := acctest.Context(t) - var service ecs.Service + var service awstypes.Service rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ecs_service.test" @@ -1657,14 +1657,15 @@ func TestAccECSService_executeCommand(t *testing.T) { func testAccCheckServiceDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) + partition := acctest.Provider.Meta().(*conns.AWSClient).Partition for _, rs := range s.RootModule().Resources { if rs.Type != "aws_ecs_service" { continue } - service, err := tfecs.FindServiceNoTagsByID(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["cluster"]) + service, err := tfecs.FindServiceNoTagsByID(ctx, conn, partition, rs.Primary.ID, rs.Primary.Attributes["cluster"]) if tfresource.NotFound(err) { return nil } @@ -1672,7 +1673,7 @@ func testAccCheckServiceDestroy(ctx context.Context) resource.TestCheckFunc { return err } - if aws.StringValue(service.Status) == "INACTIVE" { + if aws.ToString(service.Status) == "INACTIVE" { return nil } @@ -1683,22 +1684,23 @@ func testAccCheckServiceDestroy(ctx context.Context) resource.TestCheckFunc { } } -func testAccCheckServiceExists(ctx context.Context, name string, service *ecs.Service) resource.TestCheckFunc { +func testAccCheckServiceExists(ctx context.Context, name string, service *awstypes.Service) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { return fmt.Errorf("Not found: %s", name) } - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) + partition := acctest.Provider.Meta().(*conns.AWSClient).Partition err := retry.RetryContext(ctx, 1*time.Minute, func() *retry.RetryError { var err error - service, err = tfecs.FindServiceNoTagsByID(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["cluster"]) + service, err = tfecs.FindServiceNoTagsByID(ctx, conn, partition, rs.Primary.ID, rs.Primary.Attributes["cluster"]) if tfresource.NotFound(err) { return retry.RetryableError(err) } - if tfawserr.ErrCodeEquals(err, ecs.ErrCodeClusterNotFoundException) { + if errs.IsA[*awstypes.ClusterNotFoundException](err) { return retry.RetryableError(err) } if err != nil { @@ -1712,10 +1714,10 @@ func testAccCheckServiceExists(ctx context.Context, name string, service *ecs.Se } } -func testAccCheckServiceNotRecreated(i, j *ecs.Service) resource.TestCheckFunc { +func testAccCheckServiceNotRecreated(i, j *awstypes.Service) resource.TestCheckFunc { return func(s *terraform.State) error { - if !aws.TimeValue(i.CreatedAt).Equal(aws.TimeValue(j.CreatedAt)) { - return fmt.Errorf("ECS Service (%s) unexpectedly recreated", aws.StringValue(j.ServiceArn)) + if !aws.ToTime(i.CreatedAt).Equal(aws.ToTime(j.CreatedAt)) { + return fmt.Errorf("ECS Service (%s) unexpectedly recreated", aws.ToString(j.ServiceArn)) } return nil diff --git a/internal/service/ecs/status.go b/internal/service/ecs/status.go index 580b79ee184..e8c9cbe76a1 100644 --- a/internal/service/ecs/status.go +++ b/internal/service/ecs/status.go @@ -6,8 +6,9 @@ package ecs import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) @@ -25,9 +26,9 @@ const ( taskSetStatusPrimary = "PRIMARY" ) -func statusCapacityProvider(ctx context.Context, conn *ecs.ECS, arn string) retry.StateRefreshFunc { +func statusCapacityProvider(ctx context.Context, conn *ecs.Client, partition, arn string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindCapacityProviderByARN(ctx, conn, arn) + output, err := FindCapacityProviderByARN(ctx, conn, partition, arn) if tfresource.NotFound(err) { return nil, "", nil @@ -37,13 +38,13 @@ func statusCapacityProvider(ctx context.Context, conn *ecs.ECS, arn string) retr return nil, "", err } - return output, aws.StringValue(output.Status), nil + return output, string(output.Status), nil } } -func statusCapacityProviderUpdate(ctx context.Context, conn *ecs.ECS, arn string) retry.StateRefreshFunc { +func statusCapacityProviderUpdate(ctx context.Context, conn *ecs.Client, partition, arn string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindCapacityProviderByARN(ctx, conn, arn) + output, err := FindCapacityProviderByARN(ctx, conn, partition, arn) if tfresource.NotFound(err) { return nil, "", nil @@ -53,13 +54,13 @@ func statusCapacityProviderUpdate(ctx context.Context, conn *ecs.ECS, arn string return nil, "", err } - return output, aws.StringValue(output.UpdateStatus), nil + return output, string(output.UpdateStatus), nil } } -func statusServiceNoTags(ctx context.Context, conn *ecs.ECS, id, cluster string) retry.StateRefreshFunc { +func statusServiceNoTags(ctx context.Context, conn *ecs.Client, partition, id, cluster string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - service, err := FindServiceNoTagsByID(ctx, conn, id, cluster) + service, err := FindServiceNoTagsByID(ctx, conn, partition, id, cluster) if tfresource.NotFound(err) { return nil, "", nil } @@ -67,13 +68,13 @@ func statusServiceNoTags(ctx context.Context, conn *ecs.ECS, id, cluster string) return nil, "", err } - return service, aws.StringValue(service.Status), err + return service, aws.ToString(service.Status), err } } -func statusServiceWaitForStable(ctx context.Context, conn *ecs.ECS, id, cluster string) retry.StateRefreshFunc { +func statusServiceWaitForStable(ctx context.Context, conn *ecs.Client, partition, id, cluster string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - serviceRaw, status, err := statusServiceNoTags(ctx, conn, id, cluster)() + serviceRaw, status, err := statusServiceNoTags(ctx, conn, partition, id, cluster)() if err != nil { return nil, "", err } @@ -82,11 +83,11 @@ func statusServiceWaitForStable(ctx context.Context, conn *ecs.ECS, id, cluster return serviceRaw, status, nil } - service := serviceRaw.(*ecs.Service) + service := serviceRaw.(*awstypes.Service) if d, dc, rc := len(service.Deployments), - aws.Int64Value(service.DesiredCount), - aws.Int64Value(service.RunningCount); d == 1 && dc == rc { + service.DesiredCount, + service.RunningCount; d == 1 && dc == rc { status = serviceStatusStable } else { status = serviceStatusPending @@ -96,15 +97,15 @@ func statusServiceWaitForStable(ctx context.Context, conn *ecs.ECS, id, cluster } } -func stabilityStatusTaskSet(ctx context.Context, conn *ecs.ECS, taskSetID, service, cluster string) retry.StateRefreshFunc { +func stabilityStatusTaskSet(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) retry.StateRefreshFunc { return func() (interface{}, string, error) { input := &ecs.DescribeTaskSetsInput{ Cluster: aws.String(cluster), Service: aws.String(service), - TaskSets: aws.StringSlice([]string{taskSetID}), + TaskSets: []string{taskSetID}, } - output, err := conn.DescribeTaskSetsWithContext(ctx, input) + output, err := conn.DescribeTaskSets(ctx, input) if err != nil { return nil, "", err @@ -114,19 +115,19 @@ func stabilityStatusTaskSet(ctx context.Context, conn *ecs.ECS, taskSetID, servi return nil, "", nil } - return output.TaskSets[0], aws.StringValue(output.TaskSets[0].StabilityStatus), nil + return output.TaskSets[0], string(output.TaskSets[0].StabilityStatus), nil } } -func statusTaskSet(ctx context.Context, conn *ecs.ECS, taskSetID, service, cluster string) retry.StateRefreshFunc { +func statusTaskSet(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) retry.StateRefreshFunc { return func() (interface{}, string, error) { input := &ecs.DescribeTaskSetsInput{ Cluster: aws.String(cluster), Service: aws.String(service), - TaskSets: aws.StringSlice([]string{taskSetID}), + TaskSets: []string{taskSetID}, } - output, err := conn.DescribeTaskSetsWithContext(ctx, input) + output, err := conn.DescribeTaskSets(ctx, input) if err != nil { return nil, "", err @@ -136,6 +137,6 @@ func statusTaskSet(ctx context.Context, conn *ecs.ECS, taskSetID, service, clust return nil, "", nil } - return output.TaskSets[0], aws.StringValue(output.TaskSets[0].Status), nil + return output.TaskSets[0], aws.ToString(output.TaskSets[0].Status), nil } } diff --git a/internal/service/ecs/sweep.go b/internal/service/ecs/sweep.go index 28b54cddadc..692c66df077 100644 --- a/internal/service/ecs/sweep.go +++ b/internal/service/ecs/sweep.go @@ -7,12 +7,12 @@ import ( "fmt" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ecs" multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/sweep" - "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv1" + "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv2" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -54,7 +54,7 @@ func sweepCapacityProviders(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.ECSConn(ctx) + conn := client.ECSClient(ctx) input := &ecs.DescribeCapacityProvidersInput{} sweepResources := make([]sweep.Sweepable, 0) @@ -64,9 +64,9 @@ func sweepCapacityProviders(region string) error { } for _, v := range page.CapacityProviders { - arn := aws.StringValue(v.CapacityProviderArn) + arn := aws.ToString(v.CapacityProviderArn) - if name := aws.StringValue(v.Name); name == "FARGATE" || name == "FARGATE_SPOT" { + if name := aws.ToString(v.Name); name == "FARGATE" || name == "FARGATE_SPOT" { log.Printf("[INFO] Skipping AWS managed ECS Capacity Provider: %s", arn) continue } @@ -81,7 +81,7 @@ func sweepCapacityProviders(region string) error { return !lastPage }) - if awsv1.SkipSweepError(err) { + if awsv2.SkipSweepError(err) { log.Printf("[WARN] Skipping ECS Capacity Provider sweep for %s: %s", region, err) return nil } @@ -105,33 +105,31 @@ func sweepClusters(region string) error { if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.ECSConn(ctx) + conn := client.ECSClient(ctx) input := &ecs.ListClustersInput{} sweepResources := make([]sweep.Sweepable, 0) - err = conn.ListClustersPagesWithContext(ctx, input, func(page *ecs.ListClustersOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := ecs.NewListClustersPaginator(conn, input) + + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping ECS Cluster sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing ECS Clusters (%s): %w", region, err) } for _, v := range page.ClusterArns { r := ResourceCluster() d := r.Data(nil) - d.SetId(aws.StringValue(v)) + d.SetId(v) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping ECS Cluster sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing ECS Clusters (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -149,58 +147,54 @@ func sweepServices(region string) error { if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.ECSConn(ctx) + conn := client.ECSClient(ctx) input := &ecs.ListClustersInput{} var sweeperErrs *multierror.Error sweepResources := make([]sweep.Sweepable, 0) - err = conn.ListClustersPagesWithContext(ctx, input, func(page *ecs.ListClustersOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := ecs.NewListClustersPaginator(conn, input) + + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping ECS Service sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors } - for _, v := range page.ClusterArns { - clusterARN := aws.StringValue(v) + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing ECS Clusters (%s): %w", region, err)) + } + + for _, clusterARN := range page.ClusterArns { input := &ecs.ListServicesInput{ Cluster: aws.String(clusterARN), } - err := conn.ListServicesPagesWithContext(ctx, input, func(page *ecs.ListServicesOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := ecs.NewListServicesPaginator(conn, input) + + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + continue + } + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing ECS Services (%s): %w", region, err)) } for _, v := range page.ServiceArns { r := ResourceService() d := r.Data(nil) - d.SetId(aws.StringValue(v)) + d.SetId(v) d.Set("cluster", clusterARN) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - continue - } - - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing ECS Services (%s): %w", region, err)) } } - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping ECS Service sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors - } - - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing ECS Clusters (%s): %w", region, err)) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -218,34 +212,32 @@ func sweepTaskDefinitions(region string) error { if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.ECSConn(ctx) + conn := client.ECSClient(ctx) input := &ecs.ListTaskDefinitionsInput{} sweepResources := make([]sweep.Sweepable, 0) - err = conn.ListTaskDefinitionsPagesWithContext(ctx, input, func(page *ecs.ListTaskDefinitionsOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := ecs.NewListTaskDefinitionsPaginator(conn, input) + + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping ECS Task Definition sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing ECS Task Definitions (%s): %w", region, err) } for _, v := range page.TaskDefinitionArns { r := ResourceTaskDefinition() d := r.Data(nil) - d.SetId(aws.StringValue(v)) + d.SetId(v) d.Set(names.AttrARN, v) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping ECS Task Definition sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing ECS Task Definitions (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) diff --git a/internal/service/ecs/task_definition.go b/internal/service/ecs/task_definition.go index 17c90e85b96..71bbf9afbdb 100644 --- a/internal/service/ecs/task_definition.go +++ b/internal/service/ecs/task_definition.go @@ -603,7 +603,7 @@ func resourceTaskDefinitionRead(ctx context.Context, d *schema.ResourceData, met return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition (%s): %s", d.Id(), err) } - log.Printf("[DEBUG] Received task definition %s, status:%s\n %s", aws.ToString(out.TaskDefinition.Family), + log.Printf("[DEBUG] Received task definition %s, status:%s\n %+v", aws.ToString(out.TaskDefinition.Family), string(out.TaskDefinition.Status), out) taskDefinition := out.TaskDefinition diff --git a/internal/service/ecs/wait.go b/internal/service/ecs/wait.go index d73c83f8c0b..b3578204653 100644 --- a/internal/service/ecs/wait.go +++ b/internal/service/ecs/wait.go @@ -33,11 +33,11 @@ const ( taskSetDeleteTimeout = 10 * time.Minute ) -func waitCapacityProviderDeleted(ctx context.Context, conn *ecs.Client, arn string) (*awstypes.CapacityProvider, error) { +func waitCapacityProviderDeleted(ctx context.Context, conn *ecs.Client, partition, arn string) (*awstypes.CapacityProvider, error) { stateConf := &retry.StateChangeConf{ Pending: enum.Slice(awstypes.CapacityProviderStatusActive), Target: []string{}, - Refresh: statusCapacityProvider(ctx, conn, arn), + Refresh: statusCapacityProvider(ctx, conn, partition, arn), Timeout: capacityProviderDeleteTimeout, } @@ -50,11 +50,11 @@ func waitCapacityProviderDeleted(ctx context.Context, conn *ecs.Client, arn stri return nil, err } -func waitCapacityProviderUpdated(ctx context.Context, conn *ecs.Client, arn string) (*awstypes.CapacityProvider, error) { +func waitCapacityProviderUpdated(ctx context.Context, conn *ecs.Client, partition, arn string) (*awstypes.CapacityProvider, error) { stateConf := &retry.StateChangeConf{ Pending: enum.Slice(awstypes.CapacityProviderUpdateStatusUpdateInProgress), Target: enum.Slice(awstypes.CapacityProviderUpdateStatusUpdateComplete), - Refresh: statusCapacityProviderUpdate(ctx, conn, arn), + Refresh: statusCapacityProviderUpdate(ctx, conn, partition, arn), Timeout: capacityProviderUpdateTimeout, } @@ -68,7 +68,7 @@ func waitCapacityProviderUpdated(ctx context.Context, conn *ecs.Client, arn stri } // waitServiceStable waits for an ECS Service to reach the status "ACTIVE" and have all desired tasks running. Does not return tags. -func waitServiceStable(ctx context.Context, conn *ecs.Client, id, cluster string, timeout time.Duration) (*awstypes.Service, error) { +func waitServiceStable(ctx context.Context, conn *ecs.Client, partition, id, cluster string, timeout time.Duration) (*awstypes.Service, error) { input := &ecs.DescribeServicesInput{ Services: []string{id}, } @@ -80,7 +80,7 @@ func waitServiceStable(ctx context.Context, conn *ecs.Client, id, cluster string stateConf := &retry.StateChangeConf{ Pending: []string{serviceStatusInactive, serviceStatusDraining, serviceStatusPending}, Target: []string{serviceStatusStable}, - Refresh: statusServiceWaitForStable(ctx, conn, id, cluster), + Refresh: statusServiceWaitForStable(ctx, conn, partition, id, cluster), Timeout: timeout, } @@ -94,7 +94,7 @@ func waitServiceStable(ctx context.Context, conn *ecs.Client, id, cluster string } // waitServiceInactive waits for an ECS Service to reach the status "INACTIVE". -func waitServiceInactive(ctx context.Context, conn *ecs.Client, id, cluster string, timeout time.Duration) error { +func waitServiceInactive(ctx context.Context, conn *ecs.Client, partition, id, cluster string, timeout time.Duration) error { input := &ecs.DescribeServicesInput{ Services: []string{id}, } @@ -106,7 +106,7 @@ func waitServiceInactive(ctx context.Context, conn *ecs.Client, id, cluster stri stateConf := &retry.StateChangeConf{ Pending: []string{serviceStatusActive, serviceStatusDraining}, Target: []string{serviceStatusInactive}, - Refresh: statusServiceNoTags(ctx, conn, id, cluster), + Refresh: statusServiceNoTags(ctx, conn, partition, id, cluster), Timeout: timeout, MinTimeout: serviceInactiveMinTimeout, } @@ -117,11 +117,11 @@ func waitServiceInactive(ctx context.Context, conn *ecs.Client, id, cluster stri } // waitServiceActive waits for an ECS Service to reach the status "ACTIVE". Does not return tags. -func waitServiceActive(ctx context.Context, conn *ecs.Client, id, cluster string, timeout time.Duration) (*awstypes.Service, error) { +func waitServiceActive(ctx context.Context, conn *ecs.Client, partition, id, cluster string, timeout time.Duration) (*awstypes.Service, error) { stateConf := &retry.StateChangeConf{ Pending: []string{serviceStatusInactive, serviceStatusDraining}, Target: []string{serviceStatusActive}, - Refresh: statusServiceNoTags(ctx, conn, id, cluster), + Refresh: statusServiceNoTags(ctx, conn, partition, id, cluster), Timeout: timeout, } From f15233576a5537f5b0d3dcf24a7de7bdbf850310 Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Mon, 17 Jun 2024 22:45:14 +0100 Subject: [PATCH 13/60] Fix lint --- internal/service/ecs/service.go | 4 ++-- internal/service/ecs/sweep.go | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/service/ecs/service.go b/internal/service/ecs/service.go index 81c97d524a4..fb083e38174 100644 --- a/internal/service/ecs/service.go +++ b/internal/service/ecs/service.go @@ -1341,9 +1341,9 @@ func expandNetworkConfiguration(nc []interface{}) *awstypes.NetworkConfiguration } awsVpcConfig.Subnets = flex.ExpandStringValueSet(raw[names.AttrSubnets].(*schema.Set)) if val, ok := raw["assign_public_ip"].(bool); ok { - awsVpcConfig.AssignPublicIp = awstypes.AssignPublicIp(awstypes.AssignPublicIpDisabled) + awsVpcConfig.AssignPublicIp = awstypes.AssignPublicIpDisabled if val { - awsVpcConfig.AssignPublicIp = awstypes.AssignPublicIp(awstypes.AssignPublicIpEnabled) + awsVpcConfig.AssignPublicIp = awstypes.AssignPublicIpEnabled } } diff --git a/internal/service/ecs/sweep.go b/internal/service/ecs/sweep.go index 692c66df077..9b91a08174c 100644 --- a/internal/service/ecs/sweep.go +++ b/internal/service/ecs/sweep.go @@ -194,7 +194,6 @@ func sweepServices(region string) error { } } } - } err = sweep.SweepOrchestrator(ctx, sweepResources) From fa7cf55065b1daeb58d0cac635e52e111bf301ae Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 15 Jul 2024 17:10:37 -0400 Subject: [PATCH 14/60] ecs: Comment out ContainerDefinitionsAreEquivalent tests. --- .../ecs/task_definition_equivalency.go | 64 ++++++++++++++++--- .../ecs/task_definition_equivalency_test.go | 4 ++ 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/internal/service/ecs/task_definition_equivalency.go b/internal/service/ecs/task_definition_equivalency.go index 7412e4d9216..81b500d084e 100644 --- a/internal/service/ecs/task_definition_equivalency.go +++ b/internal/service/ecs/task_definition_equivalency.go @@ -7,13 +7,11 @@ import ( "bytes" "encoding/json" "log" - "reflect" "sort" "github.com/aws/aws-sdk-go-v2/aws" awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" - "github.com/mitchellh/copystructure" ) // ContainerDefinitionsAreEquivalent determines equality between two ECS container definition JSON strings @@ -78,15 +76,61 @@ func (cd containerDefinitions) Reduce(isAWSVPC bool) error { } } - // Create a mutable copy - defCopy, err := copystructure.Copy(def) - if err != nil { - return err + // Set all empty slices to nil. + if len(def.Command) == 0 { + cd[i].Command = nil + } + if len(def.CredentialSpecs) == 0 { + cd[i].CredentialSpecs = nil + } + if len(def.DependsOn) == 0 { + cd[i].DependsOn = nil + } + if len(def.DnsSearchDomains) == 0 { + cd[i].DnsSearchDomains = nil + } + if len(def.DnsServers) == 0 { + cd[i].DnsServers = nil + } + if len(def.DockerSecurityOptions) == 0 { + cd[i].DockerSecurityOptions = nil + } + if len(def.EntryPoint) == 0 { + cd[i].EntryPoint = nil + } + if len(def.Environment) == 0 { + cd[i].Environment = nil + } + if len(def.EnvironmentFiles) == 0 { + cd[i].EnvironmentFiles = nil + } + if len(def.ExtraHosts) == 0 { + cd[i].ExtraHosts = nil + } + if len(def.Links) == 0 { + cd[i].Links = nil + } + if len(def.MountPoints) == 0 { + cd[i].MountPoints = nil + } + if len(def.PortMappings) == 0 { + cd[i].PortMappings = nil + } + if len(def.ResourceRequirements) == 0 { + cd[i].ResourceRequirements = nil + } + if len(def.Secrets) == 0 { + cd[i].Secrets = nil + } + if len(def.SystemControls) == 0 { + cd[i].SystemControls = nil + } + if len(def.Ulimits) == 0 { + cd[i].Ulimits = nil + } + if len(def.VolumesFrom) == 0 { + cd[i].VolumesFrom = nil } - - definition := reflect.ValueOf(defCopy) - iface := definition.Interface().(awstypes.ContainerDefinition) - cd[i] = iface } return nil } diff --git a/internal/service/ecs/task_definition_equivalency_test.go b/internal/service/ecs/task_definition_equivalency_test.go index 40efcc046aa..a6a2b6026cc 100644 --- a/internal/service/ecs/task_definition_equivalency_test.go +++ b/internal/service/ecs/task_definition_equivalency_test.go @@ -3,6 +3,9 @@ package ecs_test +// TODO Restore once we have a suitable replacement for github.com/aws/aws-sdk-go/private/protocol. +/* + import ( "testing" @@ -578,3 +581,4 @@ func TestContainerDefinitionsAreEquivalent_missingEnvironmentName(t *testing.T) t.Fatal("Expected definitions to be equal.") } } +*/ From 4bbfea2bf1a3ee54fa359933140560249ac8d73f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 09:19:09 -0400 Subject: [PATCH 15/60] r/aws_ecs_account_setting_default: Reduce visibility. --- .../service/ecs/account_setting_default.go | 167 +++++++++--------- .../ecs/account_setting_default_test.go | 64 ++++--- internal/service/ecs/exports_test.go | 6 +- internal/service/ecs/service_package_gen.go | 4 +- 4 files changed, 134 insertions(+), 107 deletions(-) diff --git a/internal/service/ecs/account_setting_default.go b/internal/service/ecs/account_setting_default.go index 6612f1e84b9..113bc1cf67e 100644 --- a/internal/service/ecs/account_setting_default.go +++ b/internal/service/ecs/account_setting_default.go @@ -5,7 +5,6 @@ package ecs import ( "context" - "fmt" "log" "github.com/aws/aws-sdk-go-v2/aws" @@ -18,16 +17,18 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_ecs_account_setting_default", name="Account Setting Defauilt") -func ResourceAccountSettingDefault() *schema.Resource { +// @SDKResource("aws_ecs_account_setting_default", name="Account Setting Default") +func resourceAccountSettingDefault() *schema.Resource { return &schema.Resource{ - CreateWithoutTimeout: resourceAccountSettingDefaultCreate, + CreateWithoutTimeout: resourceAccountSettingDefaultPut, ReadWithoutTimeout: resourceAccountSettingDefaultRead, - UpdateWithoutTimeout: resourceAccountSettingDefaultUpdate, + UpdateWithoutTimeout: resourceAccountSettingDefaultPut, DeleteWithoutTimeout: resourceAccountSettingDefaultDelete, + Importer: &schema.ResourceImporter{ StateContext: resourceAccountSettingDefaultImport, }, @@ -51,40 +52,26 @@ func ResourceAccountSettingDefault() *schema.Resource { } } -func resourceAccountSettingDefaultImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - d.Set(names.AttrName, d.Id()) - d.SetId(arn.ARN{ - Partition: meta.(*conns.AWSClient).Partition, - Region: meta.(*conns.AWSClient).Region, - AccountID: meta.(*conns.AWSClient).AccountID, - Service: names.ECSEndpointID, - Resource: fmt.Sprintf("cluster/%s", d.Id()), - }.String()) - return []*schema.ResourceData{d}, nil -} - -func resourceAccountSettingDefaultCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceAccountSettingDefaultPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - settingName := d.Get(names.AttrName).(string) - settingValue := d.Get(names.AttrValue).(string) - log.Printf("[DEBUG] Setting Account Default %s", settingName) - - input := ecs.PutAccountSettingDefaultInput{ - Name: awstypes.SettingName(settingName), - Value: aws.String(settingValue), + settingName := awstypes.SettingName(d.Get(names.AttrName).(string)) + input := &ecs.PutAccountSettingDefaultInput{ + Name: settingName, + Value: aws.String(d.Get(names.AttrValue).(string)), } - out, err := conn.PutAccountSettingDefault(ctx, &input) + output, err := conn.PutAccountSettingDefault(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "creating ECS Account Setting Defauilt (%s): %s", settingName, err) + return sdkdiag.AppendErrorf(diags, "putting ECS Account Setting Default (%s): %s", settingName, err) } - log.Printf("[DEBUG] Account Setting Default %s set", aws.ToString(out.Setting.Value)) - d.SetId(aws.ToString(out.Setting.Value)) - d.Set("principal_arn", out.Setting.PrincipalArn) + if d.IsNewResource() { + // Huh? + d.SetId(aws.ToString(output.Setting.Value)) + } return append(diags, resourceAccountSettingDefaultRead(ctx, d, meta)...) } @@ -93,52 +80,24 @@ func resourceAccountSettingDefaultRead(ctx context.Context, d *schema.ResourceDa var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - input := &ecs.ListAccountSettingsInput{ - Name: awstypes.SettingName(d.Get(names.AttrName).(string)), - EffectiveSettings: true, - } - - log.Printf("[DEBUG] Reading Default Account Settings: %+v", input) - resp, err := conn.ListAccountSettings(ctx, input) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading ECS Account Setting Defauilt (%s): %s", d.Get(names.AttrName).(string), err) - } + settingName := awstypes.SettingName(d.Get(names.AttrName).(string)) + setting, err := findEffectiveAccountSettingByName(ctx, conn, settingName) - if len(resp.Settings) == 0 { - log.Printf("[WARN] Account Setting Default not set. Removing from state") + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] ECS Account Setting Default (%s) not found, removing from state", settingName) d.SetId("") return diags } - for _, r := range resp.Settings { - d.SetId(aws.ToString(r.PrincipalArn)) - d.Set(names.AttrName, r.Name) - d.Set("principal_arn", r.PrincipalArn) - d.Set(names.AttrValue, r.Value) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading ECS Account Setting Default (%s): %s", settingName, err) } - return diags -} - -func resourceAccountSettingDefaultUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSClient(ctx) - - settingName := d.Get(names.AttrName).(string) - settingValue := d.Get(names.AttrValue).(string) - - if d.HasChange(names.AttrValue) { - input := ecs.PutAccountSettingDefaultInput{ - Name: awstypes.SettingName(settingName), - Value: aws.String(settingValue), - } - - _, err := conn.PutAccountSettingDefault(ctx, &input) - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating ECS Account Setting Default (%s): %s", settingName, err) - } - } + principalARN := aws.ToString(setting.PrincipalArn) + d.SetId(principalARN) + d.Set(names.AttrName, setting.Name) + d.Set("principal_arn", principalARN) + d.Set(names.AttrValue, setting.Value) return diags } @@ -147,31 +106,81 @@ func resourceAccountSettingDefaultDelete(ctx context.Context, d *schema.Resource var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - settingName := d.Get(names.AttrName).(string) + settingName := awstypes.SettingName(d.Get(names.AttrName).(string)) settingValue := "disabled" - //Default value: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-maintenance.html#task-retirement-change - if settingName == string(awstypes.SettingNameFargateTaskRetirementWaitPeriod) { + // Default value: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-maintenance.html#task-retirement-change. + if settingName == awstypes.SettingNameFargateTaskRetirementWaitPeriod { + const ( + fargateTaskRetirementWaitPeriodValue = "7" + ) settingValue = fargateTaskRetirementWaitPeriodValue } - log.Printf("[WARN] Disabling ECS Account Setting Default %s", settingName) - input := ecs.PutAccountSettingDefaultInput{ - Name: awstypes.SettingName(settingName), + log.Printf("[WARN] Deleting ECS Account Setting Default: %s", settingName) + input := &ecs.PutAccountSettingDefaultInput{ + Name: settingName, Value: aws.String(settingValue), } - _, err := conn.PutAccountSettingDefault(ctx, &input) + _, err := conn.PutAccountSettingDefault(ctx, input) if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "You can no longer disable") { - log.Printf("[DEBUG] ECS Account Setting Default (%q) could not be disabled: %s", settingName, err) return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "disabling ECS Account Setting Default: %s", err) + return sdkdiag.AppendErrorf(diags, "disabling ECS Account Setting Default (%s): %s", settingName, err) } - log.Printf("[DEBUG] ECS Account Setting Default (%q) disabled", settingName) return diags } + +func resourceAccountSettingDefaultImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + d.Set(names.AttrName, d.Id()) + d.SetId(arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Region: meta.(*conns.AWSClient).Region, + AccountID: meta.(*conns.AWSClient).AccountID, + Service: names.ECSEndpointID, + Resource: "cluster/" + d.Id(), + }.String()) + + return []*schema.ResourceData{d}, nil +} + +func findSetting(ctx context.Context, conn *ecs.Client, input *ecs.ListAccountSettingsInput) (*awstypes.Setting, error) { + output, err := findSettings(ctx, conn, input) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findSettings(ctx context.Context, conn *ecs.Client, input *ecs.ListAccountSettingsInput) ([]awstypes.Setting, error) { + var output []awstypes.Setting + + pages := ecs.NewListAccountSettingsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if err != nil { + return nil, err + } + + output = append(output, page.Settings...) + } + + return output, nil +} + +func findEffectiveAccountSettingByName(ctx context.Context, conn *ecs.Client, name awstypes.SettingName) (*awstypes.Setting, error) { + input := &ecs.ListAccountSettingsInput{ + EffectiveSettings: true, + Name: name, + } + + return findSetting(ctx, conn, input) +} diff --git a/internal/service/ecs/account_setting_default_test.go b/internal/service/ecs/account_setting_default_test.go index f8ff1403a4e..82086f0c3ec 100644 --- a/internal/service/ecs/account_setting_default_test.go +++ b/internal/service/ecs/account_setting_default_test.go @@ -10,13 +10,13 @@ import ( "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ecs" awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/errs" + tfecs "github.com/hashicorp/terraform-provider-aws/internal/service/ecs" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -49,6 +49,7 @@ func testAccAccountSettingDefault_containerInstanceLongARNFormat(t *testing.T) { { Config: testAccAccountSettingDefaultConfig_basic(settingName), Check: resource.ComposeTestCheckFunc( + testAccCheckAccountSettingDefaultExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, names.AttrName, settingName), resource.TestCheckResourceAttr(resourceName, names.AttrValue, names.AttrEnabled), acctest.MatchResourceAttrGlobalARN(resourceName, "principal_arn", "iam", regexache.MustCompile("root")), @@ -78,6 +79,7 @@ func testAccAccountSettingDefault_serviceLongARNFormat(t *testing.T) { { Config: testAccAccountSettingDefaultConfig_basic(settingName), Check: resource.ComposeTestCheckFunc( + testAccCheckAccountSettingDefaultExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, names.AttrName, settingName), resource.TestCheckResourceAttr(resourceName, names.AttrValue, names.AttrEnabled), acctest.MatchResourceAttrGlobalARN(resourceName, "principal_arn", "iam", regexache.MustCompile("root")), @@ -107,6 +109,7 @@ func testAccAccountSettingDefault_taskLongARNFormat(t *testing.T) { { Config: testAccAccountSettingDefaultConfig_basic(settingName), Check: resource.ComposeTestCheckFunc( + testAccCheckAccountSettingDefaultExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, names.AttrName, settingName), resource.TestCheckResourceAttr(resourceName, names.AttrValue, names.AttrEnabled), acctest.MatchResourceAttrGlobalARN(resourceName, "principal_arn", "iam", regexache.MustCompile("root")), @@ -136,6 +139,7 @@ func testAccAccountSettingDefault_vpcTrunking(t *testing.T) { { Config: testAccAccountSettingDefaultConfig_basic(settingName), Check: resource.ComposeTestCheckFunc( + testAccCheckAccountSettingDefaultExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, names.AttrName, settingName), resource.TestCheckResourceAttr(resourceName, names.AttrValue, names.AttrEnabled), acctest.MatchResourceAttrGlobalARN(resourceName, "principal_arn", "iam", regexache.MustCompile("root")), @@ -165,6 +169,7 @@ func testAccAccountSettingDefault_containerInsights(t *testing.T) { { Config: testAccAccountSettingDefaultConfig_basic(settingName), Check: resource.ComposeTestCheckFunc( + testAccCheckAccountSettingDefaultExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, names.AttrName, settingName), resource.TestCheckResourceAttr(resourceName, names.AttrValue, names.AttrEnabled), acctest.MatchResourceAttrGlobalARN(resourceName, "principal_arn", "iam", regexache.MustCompile("root")), @@ -194,6 +199,7 @@ func testAccAccountSettingDefault_fargateTaskRetirementWaitPeriod(t *testing.T) { Config: testAccAccountSettingDefaultConfig_fargateTaskRetirementWaitPeriod(settingName), Check: resource.ComposeTestCheckFunc( + testAccCheckAccountSettingDefaultExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, names.AttrName, "fargateTaskRetirementWaitPeriod"), resource.TestCheckResourceAttr(resourceName, names.AttrValue, "14"), acctest.MatchResourceAttrGlobalARN(resourceName, "principal_arn", "iam", regexache.MustCompile("root")), @@ -209,6 +215,22 @@ func testAccAccountSettingDefault_fargateTaskRetirementWaitPeriod(t *testing.T) }) } +func testAccCheckAccountSettingDefaultExists(ctx context.Context, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) + + settingName := awstypes.SettingName(rs.Primary.Attributes[names.AttrName]) + _, err := tfecs.FindEffectiveAccountSettingByName(ctx, conn, settingName) + + return err + } +} + func testAccCheckAccountSettingDefaultDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) @@ -218,37 +240,31 @@ func testAccCheckAccountSettingDefaultDestroy(ctx context.Context) resource.Test continue } - name := rs.Primary.Attributes[names.AttrName] + settingName := awstypes.SettingName(rs.Primary.Attributes[names.AttrName]) + output, err := tfecs.FindEffectiveAccountSettingByName(ctx, conn, settingName) - input := &ecs.ListAccountSettingsInput{ - Name: awstypes.SettingName(name), - EffectiveSettings: true, - } - - resp, err := conn.ListAccountSettings(ctx, input) - - if errs.IsA[*awstypes.ResourceNotFoundException](err) { - continue + if tfresource.NotFound(err) { + return nil } if err != nil { return err } - for _, value := range resp.Settings { - if aws.ToString(value.Value) != "disabled" && aws.ToString(value.Value) != "7" { - switch awstypes.SettingName(name) { - case awstypes.SettingNameContainerInstanceLongArnFormat: - return nil - case awstypes.SettingNameServiceLongArnFormat: - return nil - case awstypes.SettingNameTaskLongArnFormat: - return nil - default: - return fmt.Errorf("[Destroy Error] Account Settings (%s), still enabled", string(value.Name)) - } + switch value := aws.ToString(output.Value); settingName { + case awstypes.SettingNameContainerInstanceLongArnFormat, awstypes.SettingNameServiceLongArnFormat, awstypes.SettingNameTaskLongArnFormat: + return nil + case awstypes.SettingNameFargateTaskRetirementWaitPeriod: + if value == "7" { + return nil + } + default: + if value == "disabled" { + return nil } } + + return fmt.Errorf("ECS Account Setting Default %s still exists", settingName) } return nil diff --git a/internal/service/ecs/exports_test.go b/internal/service/ecs/exports_test.go index dc7f71fd436..c6ffa82b7c7 100644 --- a/internal/service/ecs/exports_test.go +++ b/internal/service/ecs/exports_test.go @@ -5,7 +5,9 @@ package ecs // Exports for use in tests only. var ( - ResourceTag = resourceTag + ResourceAccountSettingDefault = resourceAccountSettingDefault + ResourceTag = resourceTag - FindTag = findTag + FindEffectiveAccountSettingByName = findEffectiveAccountSettingByName + FindTag = findTag ) diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index 6e757d30974..4a7baf35d8d 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -52,9 +52,9 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePackageSDKResource { return []*types.ServicePackageSDKResource{ { - Factory: ResourceAccountSettingDefault, + Factory: resourceAccountSettingDefault, TypeName: "aws_ecs_account_setting_default", - Name: "Account Setting Defauilt", + Name: "Account Setting Default", }, { Factory: ResourceCapacityProvider, From e09bd2035901a4aac4e31bc06548d07ce573e90d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 09:28:39 -0400 Subject: [PATCH 16/60] Acceptance test output: % make testacc TESTARGS='-run=TestAccECSAccountSettingDefault_serial' PKG=ecs make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.5 test ./internal/service/ecs/... -v -count 1 -parallel 20 -run=TestAccECSAccountSettingDefault_serial -timeout 360m === RUN TestAccECSAccountSettingDefault_serial === PAUSE TestAccECSAccountSettingDefault_serial === CONT TestAccECSAccountSettingDefault_serial === RUN TestAccECSAccountSettingDefault_serial/taskLongARNFormat === RUN TestAccECSAccountSettingDefault_serial/vpcTrunking === RUN TestAccECSAccountSettingDefault_serial/containerInsights === RUN TestAccECSAccountSettingDefault_serial/fargateTaskRetirementWaitPeriod === RUN TestAccECSAccountSettingDefault_serial/containerInstanceLongARNFormat === RUN TestAccECSAccountSettingDefault_serial/serviceLongARNFormat --- PASS: TestAccECSAccountSettingDefault_serial (66.39s) --- PASS: TestAccECSAccountSettingDefault_serial/taskLongARNFormat (11.69s) --- PASS: TestAccECSAccountSettingDefault_serial/vpcTrunking (11.02s) --- PASS: TestAccECSAccountSettingDefault_serial/containerInsights (11.09s) --- PASS: TestAccECSAccountSettingDefault_serial/fargateTaskRetirementWaitPeriod (10.78s) --- PASS: TestAccECSAccountSettingDefault_serial/containerInstanceLongARNFormat (10.86s) --- PASS: TestAccECSAccountSettingDefault_serial/serviceLongARNFormat (10.96s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ecs 71.221s From 252baa7a996a5b9b0d3a0044eb8069267ca076a4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 12:22:34 -0400 Subject: [PATCH 17/60] Add 'json.DecodeFromString' and 'json.DecodeFromReader'. --- internal/json/decode.go | 30 ++++++++++++++ internal/json/decode_test.go | 76 ++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 internal/json/decode.go create mode 100644 internal/json/decode_test.go diff --git a/internal/json/decode.go b/internal/json/decode.go new file mode 100644 index 00000000000..dbc62d126df --- /dev/null +++ b/internal/json/decode.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package json + +import ( + "encoding/json" + "io" + "strings" +) + +// DecodeFromReader decodes (unmarshals) the given io.Reader, pointing to a JSON stream, into `to`. +func DecodeFromReader(r io.Reader, to any) error { + dec := json.NewDecoder(r) + + for { + if err := dec.Decode(to); err == io.EOF { + break + } else if err != nil { + return err + } + } + + return nil +} + +// DecodeFromString decodes (unmarshals) the given string, containing valid JSON, into `to`. +func DecodeFromString(s string, to any) error { + return DecodeFromReader(strings.NewReader(s), to) +} diff --git a/internal/json/decode_test.go b/internal/json/decode_test.go new file mode 100644 index 00000000000..d57628c8898 --- /dev/null +++ b/internal/json/decode_test.go @@ -0,0 +1,76 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package json + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestDecodeFromString(t *testing.T) { + t.Parallel() + + type to struct { + A string + B int + C struct { + A bool + } + D []string + } + var to0, to1, to2, to3 to + to4 := to{ + A: "test1", + B: 42, + C: struct { + A bool + }{A: true}, + D: []string{"test2", "test3"}, + } + + testCases := []struct { + testName string + input string + output any + wantOutput any + wantErr bool + }{ + { + testName: "empty JSON", + input: `{}`, + output: &to1, + wantOutput: &to0, + }, + { + testName: "bad JSON", + input: `{test`, + output: &to2, + wantErr: true, + }, + { + testName: "full JSON", + input: `{"A": "test1", "D": ["test2", "test3"], "C": {"A": true}, "B": 42}`, + output: &to3, + wantOutput: &to4, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.testName, func(t *testing.T) { + t.Parallel() + + err := DecodeFromString(testCase.input, testCase.output) + if got, want := err != nil, testCase.wantErr; !cmp.Equal(got, want) { + t.Errorf("DecodeFromString(%s) err %t, want %t", testCase.input, got, want) + } + if err == nil { + if diff := cmp.Diff(testCase.output, testCase.wantOutput); diff != "" { + t.Errorf("unexpected diff (+wanted, -got): %s", diff) + } + } + }) + } +} From 6f47839ec34b74a9d466da2b5fc1b66856e20b33 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 12:26:46 -0400 Subject: [PATCH 18/60] Replace 'internal/vault/sdk/helper/jsonutil' with 'internal/json'. --- internal/vault/helper/pgpkeys/keybase.go | 4 +-- internal/vault/sdk/helper/jsonutil/json.go | 28 ----------------- .../vault/sdk/helper/jsonutil/json_test.go | 31 ------------------- 3 files changed, 2 insertions(+), 61 deletions(-) delete mode 100644 internal/vault/sdk/helper/jsonutil/json.go delete mode 100644 internal/vault/sdk/helper/jsonutil/json_test.go diff --git a/internal/vault/helper/pgpkeys/keybase.go b/internal/vault/helper/pgpkeys/keybase.go index 06c13c4ddd9..e5cb288fd09 100644 --- a/internal/vault/helper/pgpkeys/keybase.go +++ b/internal/vault/helper/pgpkeys/keybase.go @@ -11,7 +11,7 @@ import ( "github.com/ProtonMail/go-crypto/openpgp" cleanhttp "github.com/hashicorp/go-cleanhttp" - "github.com/hashicorp/terraform-provider-aws/internal/vault/sdk/helper/jsonutil" + "github.com/hashicorp/terraform-provider-aws/internal/json" ) const ( @@ -73,7 +73,7 @@ func FetchKeybasePubkeys(input []string) (map[string]string, error) { Them: []LThem{}, } - if err := jsonutil.DecodeJSONFromReader(resp.Body, out); err != nil { + if err := json.DecodeFromReader(resp.Body, out); err != nil { return nil, err } diff --git a/internal/vault/sdk/helper/jsonutil/json.go b/internal/vault/sdk/helper/jsonutil/json.go deleted file mode 100644 index 11dde9bae2d..00000000000 --- a/internal/vault/sdk/helper/jsonutil/json.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package jsonutil - -import ( - "encoding/json" - "fmt" - "io" -) - -// Decodes/Unmarshals the given io.Reader pointing to a JSON, into a desired object -func DecodeJSONFromReader(r io.Reader, out interface{}) error { - if r == nil { - return fmt.Errorf("'io.Reader' being decoded is nil") - } - if out == nil { - return fmt.Errorf("output parameter 'out' is nil") - } - - dec := json.NewDecoder(r) - - // While decoding JSON values, interpret the integer values as `json.Number`s instead of `float64`. - dec.UseNumber() - - // Since 'out' is an interface representing a pointer, pass it to the decoder without an '&' - return dec.Decode(out) -} diff --git a/internal/vault/sdk/helper/jsonutil/json_test.go b/internal/vault/sdk/helper/jsonutil/json_test.go deleted file mode 100644 index 6b73c798534..00000000000 --- a/internal/vault/sdk/helper/jsonutil/json_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package jsonutil - -import ( - "bytes" - "reflect" - "testing" -) - -func TestJSONUtil_DecodeJSONFromReader(t *testing.T) { - t.Parallel() - - input := `{"test":"data","validation":"process"}` - - var actual map[string]interface{} - - err := DecodeJSONFromReader(bytes.NewReader([]byte(input)), &actual) - if err != nil { - t.Errorf("decoding err: %v", err) - } - - expected := map[string]interface{}{ - "test": "data", - "validation": "process", - } - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) - } -} From 00a7ca649d02ce55970763b914233ec527f69771 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 13:36:59 -0400 Subject: [PATCH 19/60] Add 'acctest/jsoncmp' package. --- internal/acctest/jsoncmp/compare.go | 32 +++++++++++++++++ internal/acctest/jsoncmp/compare_test.go | 44 ++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 internal/acctest/jsoncmp/compare.go create mode 100644 internal/acctest/jsoncmp/compare_test.go diff --git a/internal/acctest/jsoncmp/compare.go b/internal/acctest/jsoncmp/compare.go new file mode 100644 index 00000000000..39906fdf1ce --- /dev/null +++ b/internal/acctest/jsoncmp/compare.go @@ -0,0 +1,32 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package jsoncmp + +// Inspired by https://github.com/hgsgtk/jsoncmp. + +import ( + "encoding/json" + "fmt" + + "github.com/google/go-cmp/cmp" +) + +func Diff(x, y string) string { + xform := cmp.Transformer("jsoncmp", func(s string) (m map[string]any) { + if err := json.Unmarshal([]byte(s), &m); err != nil { + panic(fmt.Sprintf("json.Unmarshal(%s): %s", s, err)) + } + return m + }) + opt := cmp.FilterPath(func(p cmp.Path) bool { + for _, ps := range p { + if tr, ok := ps.(cmp.Transform); ok && tr.Option() == xform { + return false + } + } + return true + }, xform) + + return cmp.Diff(x, y, opt) +} diff --git a/internal/acctest/jsoncmp/compare_test.go b/internal/acctest/jsoncmp/compare_test.go new file mode 100644 index 00000000000..5607f8b447c --- /dev/null +++ b/internal/acctest/jsoncmp/compare_test.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package jsoncmp_test + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-aws/internal/acctest/jsoncmp" +) + +func TestDiff(t *testing.T) { + t.Parallel() + + testCases := []struct { + testName string + x, y string + wantDiff bool + }{ + { + testName: "no diff", + x: `{"A": "test1", "D": ["test2", "test3"], "C": {"A": true}, "B": 42}`, + y: `{"A":"test1", "B":42, "C":{"A":true}, "D": ["test2", "test3"]}`, + }, + { + testName: "has diff", + x: `{"A": "test1", "D": ["test2", "test3"], "C": {"A": true}, "B": 42}`, + y: `{"A":"test1", "B":41, "C":{"A":true}, "D": ["test3"]}`, + wantDiff: true, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.testName, func(t *testing.T) { + t.Parallel() + + output := jsoncmp.Diff(testCase.x, testCase.y) + if got, want := output != "", testCase.wantDiff; got != want { + t.Errorf("Diff(%s, %s) = %t, want %t", testCase.x, testCase.y, got, want) + } + }) + } +} From 77c976ccc0e4e3d4847a6d77b9ae6ad43def7b59 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 13:39:14 -0400 Subject: [PATCH 20/60] Add 'json.EncodeToString' and 'json.EncodeToBytes'. --- internal/json/decode_test.go | 5 +-- internal/json/encode.go | 42 ++++++++++++++++++++++ internal/json/encode_test.go | 69 ++++++++++++++++++++++++++++++++++++ internal/json/remove_test.go | 8 +++-- 4 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 internal/json/encode.go create mode 100644 internal/json/encode_test.go diff --git a/internal/json/decode_test.go b/internal/json/decode_test.go index d57628c8898..6e03986dbd1 100644 --- a/internal/json/decode_test.go +++ b/internal/json/decode_test.go @@ -1,12 +1,13 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package json +package json_test import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-provider-aws/internal/json" ) func TestDecodeFromString(t *testing.T) { @@ -62,7 +63,7 @@ func TestDecodeFromString(t *testing.T) { t.Run(testCase.testName, func(t *testing.T) { t.Parallel() - err := DecodeFromString(testCase.input, testCase.output) + err := json.DecodeFromString(testCase.input, testCase.output) if got, want := err != nil, testCase.wantErr; !cmp.Equal(got, want) { t.Errorf("DecodeFromString(%s) err %t, want %t", testCase.input, got, want) } diff --git a/internal/json/encode.go b/internal/json/encode.go new file mode 100644 index 00000000000..fd9e43b4370 --- /dev/null +++ b/internal/json/encode.go @@ -0,0 +1,42 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package json + +import ( + "bytes" + "encoding/json" +) + +// EncodeToBytes JSON encodes (marshals) `from` into a byte slice. +func EncodeToBytes(from any) ([]byte, error) { + to, err := encodeToBuffer(from) + + if err != nil { + return nil, err + } + + return to.Bytes(), nil +} + +// EncodeToString JSON encodes (marshals) `from` into a string. +func EncodeToString(from any) (string, error) { + to, err := encodeToBuffer(from) + + if err != nil { + return "", err + } + + return to.String(), nil +} + +func encodeToBuffer(from any) (*bytes.Buffer, error) { + to := new(bytes.Buffer) + enc := json.NewEncoder(to) + + if err := enc.Encode(from); err != nil { + return nil, err + } + + return to, nil +} diff --git a/internal/json/encode_test.go b/internal/json/encode_test.go new file mode 100644 index 00000000000..118a86d6561 --- /dev/null +++ b/internal/json/encode_test.go @@ -0,0 +1,69 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package json_test + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-provider-aws/internal/acctest/jsoncmp" + "github.com/hashicorp/terraform-provider-aws/internal/json" +) + +func TestEncodeToString(t *testing.T) { + t.Parallel() + + type from struct { + A string + B int + C struct { + A bool + } + D []string + } + var from0 from + from1 := from{ + A: "test1", + B: 42, + C: struct { + A bool + }{A: true}, + D: []string{"test2", "test3"}, + } + + testCases := []struct { + testName string + input any + wantOutput string + wantErr bool + }{ + { + testName: "empty JSON", + input: &from0, + wantOutput: `{"A": "", "B": 0, "C": {"A": false}, "D": null}`, + }, + { + testName: "empty JSON", + input: &from1, + wantOutput: `{"A": "test1", "D": ["test2", "test3"], "C": {"A": true}, "B": 42}`, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.testName, func(t *testing.T) { + t.Parallel() + + output, err := json.EncodeToString(testCase.input) + if got, want := err != nil, testCase.wantErr; !cmp.Equal(got, want) { + t.Errorf("EncodeToString(%v) err %t, want %t", testCase.input, got, want) + } + if err == nil { + if diff := jsoncmp.Diff(output, testCase.wantOutput); diff != "" { + t.Errorf("unexpected diff (+wanted, -got): %s", diff) + } + } + }) + } +} diff --git a/internal/json/remove_test.go b/internal/json/remove_test.go index 0e4b8445e30..6ce2788d029 100644 --- a/internal/json/remove_test.go +++ b/internal/json/remove_test.go @@ -1,10 +1,12 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package json +package json_test import ( "testing" + + "github.com/hashicorp/terraform-provider-aws/internal/json" ) func TestRemoveFields(t *testing.T) { @@ -37,7 +39,7 @@ func TestRemoveFields(t *testing.T) { t.Run(testCase.testName, func(t *testing.T) { t.Parallel() - if got, want := RemoveFields(testCase.input, `"plugins"`), testCase.want; got != want { + if got, want := json.RemoveFields(testCase.input, `"plugins"`), testCase.want; got != want { t.Errorf("RemoveReadOnlyFields(%q) = %q, want %q", testCase.input, got, want) } }) @@ -119,7 +121,7 @@ func TestRemoveEmptyFields(t *testing.T) { t.Run(testCase.testName, func(t *testing.T) { t.Parallel() - if got, want := RemoveEmptyFields([]byte(testCase.input)), testCase.want; string(got) != want { + if got, want := json.RemoveEmptyFields([]byte(testCase.input)), testCase.want; string(got) != want { t.Errorf("RemoveEmptyFields(%q) = %q, want %q", testCase.input, got, want) } }) From 35a37341fb3127c7683b2a71bc1bb962808dbb14 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 13:42:08 -0400 Subject: [PATCH 21/60] dynamodb: Use 'tfjson' functions. --- internal/service/dynamodb/flex.go | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/internal/service/dynamodb/flex.go b/internal/service/dynamodb/flex.go index 4c7942c3d47..7352c10a59c 100644 --- a/internal/service/dynamodb/flex.go +++ b/internal/service/dynamodb/flex.go @@ -4,13 +4,10 @@ package dynamodb import ( - "bytes" - "encoding/json" "fmt" - "io" - "strings" awstypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + tfjson "github.com/hashicorp/terraform-provider-aws/internal/json" tfmaps "github.com/hashicorp/terraform-provider-aws/internal/maps" tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" itypes "github.com/hashicorp/terraform-provider-aws/internal/types" @@ -18,14 +15,10 @@ import ( func expandTableItemAttributes(jsonStream string) (map[string]awstypes.AttributeValue, error) { var m map[string]any - dec := json.NewDecoder(strings.NewReader(jsonStream)) - for { - if err := dec.Decode(&m); err == io.EOF { - break - } else if err != nil { - return nil, err - } + err := tfjson.DecodeFromString(jsonStream, &m) + if err != nil { + return nil, err } return tfmaps.ApplyToAllValuesWithError(m, attributeFromRaw) @@ -37,14 +30,7 @@ func flattenTableItemAttributes(apiObject map[string]awstypes.AttributeValue) (s return "", err } - b := new(bytes.Buffer) - enc := json.NewEncoder(b) - - if err := enc.Encode(m); err != nil { - return "", err - } - - return b.String(), nil + return tfjson.EncodeToString(m) } func attributeFromRaw(v any) (awstypes.AttributeValue, error) { From e3444e31773fd54c3df9356abfc1e776e3e547ee Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 13:45:29 -0400 Subject: [PATCH 22/60] Acceptance test output: % make testacc TESTARGS='-run=TestAccDynamoDBTableItem_' PKG=dynamodb ACCTEST_PARALLELISM=3 make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.5 test ./internal/service/dynamodb/... -v -count 1 -parallel 3 -run=TestAccDynamoDBTableItem_ -timeout 360m === RUN TestAccDynamoDBTableItem_basic === PAUSE TestAccDynamoDBTableItem_basic === RUN TestAccDynamoDBTableItem_rangeKey === PAUSE TestAccDynamoDBTableItem_rangeKey === RUN TestAccDynamoDBTableItem_withMultipleItems === PAUSE TestAccDynamoDBTableItem_withMultipleItems === RUN TestAccDynamoDBTableItem_withDuplicateItemsSameRangeKey === PAUSE TestAccDynamoDBTableItem_withDuplicateItemsSameRangeKey === RUN TestAccDynamoDBTableItem_withDuplicateItemsDifferentRangeKey === PAUSE TestAccDynamoDBTableItem_withDuplicateItemsDifferentRangeKey === RUN TestAccDynamoDBTableItem_wonkyItems === PAUSE TestAccDynamoDBTableItem_wonkyItems === RUN TestAccDynamoDBTableItem_update === PAUSE TestAccDynamoDBTableItem_update === RUN TestAccDynamoDBTableItem_updateWithRangeKey === PAUSE TestAccDynamoDBTableItem_updateWithRangeKey === RUN TestAccDynamoDBTableItem_disappears === PAUSE TestAccDynamoDBTableItem_disappears === RUN TestAccDynamoDBTableItem_mapOutOfBandUpdate === PAUSE TestAccDynamoDBTableItem_mapOutOfBandUpdate === CONT TestAccDynamoDBTableItem_basic === CONT TestAccDynamoDBTableItem_wonkyItems === CONT TestAccDynamoDBTableItem_disappears --- PASS: TestAccDynamoDBTableItem_disappears (25.87s) === CONT TestAccDynamoDBTableItem_mapOutOfBandUpdate --- PASS: TestAccDynamoDBTableItem_basic (26.99s) === CONT TestAccDynamoDBTableItem_withDuplicateItemsSameRangeKey --- PASS: TestAccDynamoDBTableItem_wonkyItems (27.11s) === CONT TestAccDynamoDBTableItem_withDuplicateItemsDifferentRangeKey --- PASS: TestAccDynamoDBTableItem_withDuplicateItemsSameRangeKey (17.05s) === CONT TestAccDynamoDBTableItem_withMultipleItems --- PASS: TestAccDynamoDBTableItem_withDuplicateItemsDifferentRangeKey (19.25s) === CONT TestAccDynamoDBTableItem_updateWithRangeKey --- PASS: TestAccDynamoDBTableItem_mapOutOfBandUpdate (25.45s) === CONT TestAccDynamoDBTableItem_update --- PASS: TestAccDynamoDBTableItem_withMultipleItems (19.54s) === CONT TestAccDynamoDBTableItem_rangeKey --- PASS: TestAccDynamoDBTableItem_updateWithRangeKey (22.90s) --- PASS: TestAccDynamoDBTableItem_update (26.09s) --- PASS: TestAccDynamoDBTableItem_rangeKey (17.48s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/dynamodb 86.206s From ed4e16fb1635f74da236b796fd5bf2a01d6f85e9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 14:03:14 -0400 Subject: [PATCH 23/60] Add 'json.DecodeFromBytes'. --- internal/json/decode.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/json/decode.go b/internal/json/decode.go index dbc62d126df..9e76589699d 100644 --- a/internal/json/decode.go +++ b/internal/json/decode.go @@ -4,11 +4,17 @@ package json import ( + "bytes" "encoding/json" "io" "strings" ) +// DecodeFromBytes decodes (unmarshals) the given byte slice, containing valid JSON, into `to`. +func DecodeFromBytes(b []byte, to any) error { + return DecodeFromReader(bytes.NewReader(b), to) +} + // DecodeFromReader decodes (unmarshals) the given io.Reader, pointing to a JSON stream, into `to`. func DecodeFromReader(r io.Reader, to any) error { dec := json.NewDecoder(r) From b811e73b8a232c3481772242860ae5998b9853e0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 16:16:51 -0400 Subject: [PATCH 24/60] Add 'json.EqualBytes' and 'json.EqualStrings'. --- internal/json/equal.go | 28 ++++++++++++++ internal/json/equal_test.go | 74 +++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 internal/json/equal.go create mode 100644 internal/json/equal_test.go diff --git a/internal/json/equal.go b/internal/json/equal.go new file mode 100644 index 00000000000..6a1dff29afe --- /dev/null +++ b/internal/json/equal.go @@ -0,0 +1,28 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package json + +import ( + "reflect" +) + +// EqualBytes returns whether the JSON documents in the given byte slices are equal. +func EqualBytes(b1, b2 []byte) bool { + var v1 any + if err := DecodeFromBytes(b1, &v1); err != nil { + return false + } + + var v2 any + if err := DecodeFromBytes(b2, &v2); err != nil { + return false + } + + return reflect.DeepEqual(v1, v2) +} + +// EqualStrings returns whether the JSON documents in the given strings are equal. +func EqualStrings(s1, s2 string) bool { + return EqualBytes([]byte(s1), []byte(s2)) +} diff --git a/internal/json/equal_test.go b/internal/json/equal_test.go new file mode 100644 index 00000000000..95108474987 --- /dev/null +++ b/internal/json/equal_test.go @@ -0,0 +1,74 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package json_test + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-provider-aws/internal/json" +) + +func TestEqualStrings(t *testing.T) { + t.Parallel() + + testCases := []struct { + testName string + x, y string + wantEqual bool + }{ + { + testName: "invalid JSON first", + x: `test`, + y: `{}`, + }, + { + testName: "invalid JSON second", + x: `{}`, + y: `test`, + }, + { + testName: "empty JSON", + x: `{}`, + y: ` { } `, + wantEqual: true, + }, + { + testName: "empty JSON first, not empty second", + x: `{}`, + y: `{"A": "test"}`, + }, + { + testName: "not empty, equal", + x: `{"A": "test1", "D": ["test2", "test3"], "C": {"A": true}, "B": 42}`, + y: `{ + "C": {"A": true}, + "D": ["test2", "test3"], + "A":"test1","B": 42 + }`, + wantEqual: true, + }, + { + testName: "not empty, not equal", + x: `{"A": "test1", "D": ["test2", "test3"], "C": {"A": true}, "B": 42}`, + y: `{ + "C": {"A": true}, + "D": ["test2", "test3"], + "A":"test4","B": 42 + }`, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.testName, func(t *testing.T) { + t.Parallel() + + equal := json.EqualStrings(testCase.x, testCase.y) + if got, want := equal, testCase.wantEqual; !cmp.Equal(got, want) { + t.Errorf("EqualStrings(%s, %s) = %t, want %t", testCase.x, testCase.y, got, want) + } + }) + } +} From 5b63982b8965568bb07d6bc9d4dbb2c237a45a0d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 16:17:22 -0400 Subject: [PATCH 25/60] Some tweaks. --- internal/json/smithy.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/internal/json/smithy.go b/internal/json/smithy.go index c784d2139d9..9a0918d0d24 100644 --- a/internal/json/smithy.go +++ b/internal/json/smithy.go @@ -4,15 +4,13 @@ package json import ( - "encoding/json" - smithydocument "github.com/aws/smithy-go/document" ) func SmithyDocumentFromString[T smithydocument.Marshaler](s string, f func(any) T) (T, error) { var v map[string]interface{} - err := json.Unmarshal([]byte(s), &v) + err := DecodeFromString(s, &v) if err != nil { var zero T return zero, err @@ -30,12 +28,7 @@ func SmithyDocumentToString(document smithydocument.Unmarshaler) (string, error) return "", err } - bytes, err := json.Marshal(v) - if err != nil { - return "", err - } - - return string(bytes), nil + return EncodeToString(v) } // JSONStringer interface is used to marshal and unmarshal JSON interface objects. From bb0e0a59ef5903bf3c7c1b5a7ef5058f50051bd1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 16:54:39 -0400 Subject: [PATCH 26/60] ContainerDefinitionsAreEquivalent: Restore unit tests. --- .../ecs/task_definition_equivalency.go | 30 ++++++++----------- .../ecs/task_definition_equivalency_test.go | 4 --- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/internal/service/ecs/task_definition_equivalency.go b/internal/service/ecs/task_definition_equivalency.go index 81b500d084e..5398300c73c 100644 --- a/internal/service/ecs/task_definition_equivalency.go +++ b/internal/service/ecs/task_definition_equivalency.go @@ -4,21 +4,18 @@ package ecs import ( - "bytes" - "encoding/json" - "log" "sort" "github.com/aws/aws-sdk-go-v2/aws" awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" - "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" + tfjson "github.com/hashicorp/terraform-provider-aws/internal/json" ) // ContainerDefinitionsAreEquivalent determines equality between two ECS container definition JSON strings // Note: This function will be moved out of the aws package in the future. func ContainerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, error) { var obj1 containerDefinitions - err := json.Unmarshal([]byte(def1), &obj1) + err := tfjson.DecodeFromString(def1, &obj1) if err != nil { return false, err } @@ -26,13 +23,13 @@ func ContainerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, if err != nil { return false, err } - canonicalJson1, err := jsonutil.BuildJSON(obj1) + b1, err := tfjson.EncodeToBytes(obj1) if err != nil { return false, err } var obj2 containerDefinitions - err = json.Unmarshal([]byte(def2), &obj2) + err = tfjson.DecodeFromString(def2, &obj2) if err != nil { return false, err } @@ -40,34 +37,31 @@ func ContainerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, if err != nil { return false, err } - - canonicalJson2, err := jsonutil.BuildJSON(obj2) + b2, err := tfjson.EncodeToBytes(obj2) if err != nil { return false, err } - equal := bytes.Equal(canonicalJson1, canonicalJson2) - if !equal { - log.Printf("[DEBUG] Canonical definitions are not equal.\nFirst: %s\nSecond: %s\n", - canonicalJson1, canonicalJson2) - } - return equal, nil + return tfjson.EqualBytes(b1, b2), nil } type containerDefinitions []awstypes.ContainerDefinition func (cd containerDefinitions) Reduce(isAWSVPC bool) error { - // Deal with fields which may be re-ordered in the API + // Deal with fields which may be re-ordered in the API. cd.OrderContainers() cd.OrderEnvironmentVariables() cd.OrderSecrets() for i, def := range cd { - // Deal with special fields which have defaults + // Deal with special fields which have defaults. if def.Essential == nil { - def.Essential = aws.Bool(true) + cd[i].Essential = aws.Bool(true) } for j, pm := range def.PortMappings { + if pm.Protocol == awstypes.TransportProtocolTcp { + cd[i].PortMappings[j].Protocol = "" + } if aws.ToInt32(pm.HostPort) == 0 { cd[i].PortMappings[j].HostPort = nil } diff --git a/internal/service/ecs/task_definition_equivalency_test.go b/internal/service/ecs/task_definition_equivalency_test.go index a6a2b6026cc..40efcc046aa 100644 --- a/internal/service/ecs/task_definition_equivalency_test.go +++ b/internal/service/ecs/task_definition_equivalency_test.go @@ -3,9 +3,6 @@ package ecs_test -// TODO Restore once we have a suitable replacement for github.com/aws/aws-sdk-go/private/protocol. -/* - import ( "testing" @@ -581,4 +578,3 @@ func TestContainerDefinitionsAreEquivalent_missingEnvironmentName(t *testing.T) t.Fatal("Expected definitions to be equal.") } } -*/ From 1165b8f4dba07685f6e5318b2b3b98b196bd16c5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 16:55:03 -0400 Subject: [PATCH 27/60] Global 'fargateTaskRetirementWaitPeriodValue' is unused. --- internal/service/ecs/consts.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/service/ecs/consts.go b/internal/service/ecs/consts.go index fe907535958..51cea2cb9ae 100644 --- a/internal/service/ecs/consts.go +++ b/internal/service/ecs/consts.go @@ -17,7 +17,3 @@ const ( clusterStatusInactive = "INACTIVE" clusterStatusProvisioning = "PROVISIONING" ) - -const ( - fargateTaskRetirementWaitPeriodValue = "7" -) From 2ce7f18284589559878944769318a8d09891811f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 17:01:03 -0400 Subject: [PATCH 28/60] r/aws_dms_certificate: Remove use of 'tfslices.ToPointers'. --- internal/service/dms/certificate.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/service/dms/certificate.go b/internal/service/dms/certificate.go index 616bd7b5d69..7094b91beab 100644 --- a/internal/service/dms/certificate.go +++ b/internal/service/dms/certificate.go @@ -18,7 +18,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" itypes "github.com/hashicorp/terraform-provider-aws/internal/types" @@ -187,10 +186,10 @@ func findCertificate(ctx context.Context, conn *dms.Client, input *dms.DescribeC return nil, err } - return tfresource.AssertSinglePtrResult(output) + return tfresource.AssertSingleValueResult(output) } -func findCertificates(ctx context.Context, conn *dms.Client, input *dms.DescribeCertificatesInput) ([]*awstypes.Certificate, error) { +func findCertificates(ctx context.Context, conn *dms.Client, input *dms.DescribeCertificatesInput) ([]awstypes.Certificate, error) { var output []awstypes.Certificate pages := dms.NewDescribeCertificatesPaginator(conn, input) @@ -212,5 +211,5 @@ func findCertificates(ctx context.Context, conn *dms.Client, input *dms.Describe output = append(output, page.Certificates...) } - return tfslices.ToPointers(output), nil + return output, nil } From db22363a55925d9385e812d7a9cef06b40d5205c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 17:03:36 -0400 Subject: [PATCH 29/60] Acceptance test output: % make testacc TESTARGS='-run=TestAccDMSCertificate_' PKG=dms ACCTEST_PARALLELISM=3 make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.5 test ./internal/service/dms/... -v -count 1 -parallel 3 -run=TestAccDMSCertificate_ -timeout 360m === RUN TestAccDMSCertificate_basic === PAUSE TestAccDMSCertificate_basic === RUN TestAccDMSCertificate_disappears === PAUSE TestAccDMSCertificate_disappears === RUN TestAccDMSCertificate_certificateWallet === PAUSE TestAccDMSCertificate_certificateWallet === RUN TestAccDMSCertificate_tags === PAUSE TestAccDMSCertificate_tags === CONT TestAccDMSCertificate_basic === CONT TestAccDMSCertificate_certificateWallet === CONT TestAccDMSCertificate_disappears --- PASS: TestAccDMSCertificate_disappears (9.99s) === CONT TestAccDMSCertificate_tags --- PASS: TestAccDMSCertificate_certificateWallet (12.22s) --- PASS: TestAccDMSCertificate_basic (12.22s) --- PASS: TestAccDMSCertificate_tags (24.57s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/dms 39.517s From c02be8c39bd584ae3c2b4f7cbc2b6e71ea996557 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 17:04:50 -0400 Subject: [PATCH 30/60] r/aws_apprunner_connection: Remove use of 'tfslices.ToPointers'. --- internal/service/apprunner/connection.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/internal/service/apprunner/connection.go b/internal/service/apprunner/connection.go index 09b4dfebc37..2f8048920bc 100644 --- a/internal/service/apprunner/connection.go +++ b/internal/service/apprunner/connection.go @@ -18,7 +18,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -172,11 +171,11 @@ func findConnection(ctx context.Context, conn *apprunner.Client, input *apprunne return nil, err } - return tfresource.AssertSinglePtrResult(output) + return tfresource.AssertSingleValueResult(output) } -func findConnections(ctx context.Context, conn *apprunner.Client, input *apprunner.ListConnectionsInput) ([]*types.ConnectionSummary, error) { - var output []*types.ConnectionSummary +func findConnections(ctx context.Context, conn *apprunner.Client, input *apprunner.ListConnectionsInput) ([]types.ConnectionSummary, error) { + var output []types.ConnectionSummary pages := apprunner.NewListConnectionsPaginator(conn, input) for pages.HasMorePages() { @@ -193,7 +192,7 @@ func findConnections(ctx context.Context, conn *apprunner.Client, input *apprunn return nil, err } - output = append(output, tfslices.ToPointers(page.ConnectionSummaryList)...) + output = append(output, page.ConnectionSummaryList...) } return output, nil From b7e160e6f0205ab99c915882b25fa94babefe913 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 16 Jul 2024 17:06:23 -0400 Subject: [PATCH 31/60] Acceptance test output: % make testacc TESTARGS='-run=TestAccAppRunnerConnection_' PKG=apprunner ACCTEST_PARALLELISM=3 make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.5 test ./internal/service/apprunner/... -v -count 1 -parallel 3 -run=TestAccAppRunnerConnection_ -timeout 360m === RUN TestAccAppRunnerConnection_basic === PAUSE TestAccAppRunnerConnection_basic === RUN TestAccAppRunnerConnection_disappears === PAUSE TestAccAppRunnerConnection_disappears === RUN TestAccAppRunnerConnection_tags === PAUSE TestAccAppRunnerConnection_tags === CONT TestAccAppRunnerConnection_basic === CONT TestAccAppRunnerConnection_tags === CONT TestAccAppRunnerConnection_disappears --- PASS: TestAccAppRunnerConnection_disappears (10.54s) --- PASS: TestAccAppRunnerConnection_basic (12.55s) --- PASS: TestAccAppRunnerConnection_tags (25.59s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apprunner 30.617s From 5b993e02cbdd6c54098ac0533f8e81a9689c8bf1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 08:49:57 -0400 Subject: [PATCH 32/60] r/aws_appstream_user: Remove use of 'tfslices.ToPointers'. --- internal/service/appstream/find.go | 51 +++++++++++-------------- internal/service/appstream/status.go | 2 +- internal/service/appstream/user.go | 2 +- internal/service/appstream/user_test.go | 2 +- 4 files changed, 26 insertions(+), 31 deletions(-) diff --git a/internal/service/appstream/find.go b/internal/service/appstream/find.go index 6d4d3c66238..a9fc76ac7a3 100644 --- a/internal/service/appstream/find.go +++ b/internal/service/appstream/find.go @@ -45,20 +45,7 @@ func FindImageBuilderByName(ctx context.Context, conn *appstream.Client, name st Names: []string{name}, } - output, err := findImageBuilder(ctx, conn, input) - - if err != nil { - return nil, err - } - - // Eventual consistency check. - if aws.ToString(output.Name) != name { - return nil, &retry.NotFoundError{ - LastRequest: input, - } - } - - return output, nil + return findImageBuilder(ctx, conn, input) } func findImageBuilders(ctx context.Context, conn *appstream.Client, input *appstream.DescribeImageBuildersInput) ([]awstypes.ImageBuilder, error) { @@ -92,29 +79,33 @@ func findImageBuilder(ctx context.Context, conn *appstream.Client, input *appstr output, err := findImageBuilders(ctx, conn, input) if err != nil { - return &awstypes.ImageBuilder{}, err + return nil, err } return tfresource.AssertSingleValueResult(output) } -// FindUserByUserNameAndAuthType Retrieve a appstream fleet by Username and authentication type -func FindUserByUserNameAndAuthType(ctx context.Context, conn *appstream.Client, username, authType string) (*awstypes.User, error) { +func FindUserByTwoPartKey(ctx context.Context, conn *appstream.Client, username, authType string) (*awstypes.User, error) { input := &appstream.DescribeUsersInput{ AuthenticationType: awstypes.AuthenticationType(authType), } - var result *awstypes.User + return findUser(ctx, conn, input, func(v *awstypes.User) bool { + return aws.ToString(v.UserName) == username + }) +} + +func findUsers(ctx context.Context, conn *appstream.Client, input *appstream.DescribeUsersInput, filter tfslices.Predicate[*awstypes.User]) ([]awstypes.User, error) { + var output []awstypes.User err := describeUsersPages(ctx, conn, input, func(page *appstream.DescribeUsersOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, user := range tfslices.ToPointers(page.Users) { - if aws.ToString(user.UserName) == username { - result = user - return false + for _, v := range page.Users { + if filter(&v) { + output = append(output, v) } } @@ -127,18 +118,22 @@ func FindUserByUserNameAndAuthType(ctx context.Context, conn *appstream.Client, LastRequest: input, } } + if err != nil { return nil, err } - if result == nil { - return nil, &retry.NotFoundError{ - Message: "Empty result", - LastRequest: input, - } + return output, nil +} + +func findUser(ctx context.Context, conn *appstream.Client, input *appstream.DescribeUsersInput, filter tfslices.Predicate[*awstypes.User]) (*awstypes.User, error) { + output, err := findUsers(ctx, conn, input, filter) + + if err != nil { + return nil, err } - return result, nil + return tfresource.AssertSingleValueResult(output) } // FindFleetStackAssociation Validates that a fleet has the named associated stack diff --git a/internal/service/appstream/status.go b/internal/service/appstream/status.go index 81a48703f68..5e456f57dca 100644 --- a/internal/service/appstream/status.go +++ b/internal/service/appstream/status.go @@ -47,7 +47,7 @@ func statusImageBuilderState(ctx context.Context, conn *appstream.Client, name s // statusUserAvailable fetches the user available func statusUserAvailable(ctx context.Context, conn *appstream.Client, username, authType string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - user, err := FindUserByUserNameAndAuthType(ctx, conn, username, authType) + user, err := FindUserByTwoPartKey(ctx, conn, username, authType) if tfresource.NotFound(err) { return nil, "", nil diff --git a/internal/service/appstream/user.go b/internal/service/appstream/user.go index e13b11e669b..d9a2c78805a 100644 --- a/internal/service/appstream/user.go +++ b/internal/service/appstream/user.go @@ -146,7 +146,7 @@ func resourceUserRead(ctx context.Context, d *schema.ResourceData, meta interfac return sdkdiag.AppendErrorf(diags, "decoding AppStream User ID (%s): %s", d.Id(), err) } - user, err := FindUserByUserNameAndAuthType(ctx, conn, userName, authType) + user, err := FindUserByTwoPartKey(ctx, conn, userName, authType) if tfresource.NotFound(err) && !d.IsNewResource() { log.Printf("[WARN] AppStream User (%s) not found, removing from state", d.Id()) d.SetId("") diff --git a/internal/service/appstream/user_test.go b/internal/service/appstream/user_test.go index 4093bd59492..6fd25e8f1c0 100644 --- a/internal/service/appstream/user_test.go +++ b/internal/service/appstream/user_test.go @@ -157,7 +157,7 @@ func testAccCheckUserExists(ctx context.Context, resourceName string, appStreamU return err } - user, err := tfappstream.FindUserByUserNameAndAuthType(ctx, conn, userName, authType) + user, err := tfappstream.FindUserByTwoPartKey(ctx, conn, userName, authType) if tfresource.NotFound(err) { return fmt.Errorf("AppStream User %q does not exist", rs.Primary.ID) } From e7bf251c7ca66b43d2bd2719278911c02e758673 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 08:56:31 -0400 Subject: [PATCH 33/60] r/aws_appstream_image_builder: 'AppStream-WinServer2022-03-24-2024' -> 'AppStream-WinServer2022-06-17-2024' in acceptance test configurations. --- internal/service/appstream/image_builder_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/appstream/image_builder_test.go b/internal/service/appstream/image_builder_test.go index 327fa8594e5..afbf2a01846 100644 --- a/internal/service/appstream/image_builder_test.go +++ b/internal/service/appstream/image_builder_test.go @@ -54,7 +54,7 @@ func TestAccAppStreamImageBuilder_withIAMRole(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_appstream_image_builder.test" instanceType := "stream.standard.medium" - imageName := "AppStream-WinServer2022-03-24-2024" + imageName := "AppStream-WinServer2022-06-17-2024" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ @@ -203,7 +203,7 @@ func TestAccAppStreamImageBuilder_imageARN(t *testing.T) { resourceName := "aws_appstream_image_builder.test" // imageName selected from the available AWS Managed AppStream 2.0 Base Images // Reference: https://docs.aws.amazon.com/appstream2/latest/developerguide/base-image-version-history.html - imageName := "AppStream-WinServer2022-03-24-2024" + imageName := "AppStream-WinServer2022-06-17-2024" instanceType := "stream.standard.small" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -277,7 +277,7 @@ func testAccCheckImageBuilderDestroy(ctx context.Context) resource.TestCheckFunc func testAccImageBuilderConfig_basic(instanceType, rName string) string { return fmt.Sprintf(` resource "aws_appstream_image_builder" "test" { - image_name = "AppStream-WinServer2022-03-24-2024" + image_name = "AppStream-WinServer2022-06-17-2024" instance_type = %[1]q name = %[2]q } @@ -287,7 +287,7 @@ resource "aws_appstream_image_builder" "test" { func testAccImageBuilderConfig_complete(rName, description, instanceType string) string { return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` resource "aws_appstream_image_builder" "test" { - image_name = "AppStream-WinServer2022-03-24-2024" + image_name = "AppStream-WinServer2022-06-17-2024" name = %[1]q description = %[2]q enable_default_internet_access = false @@ -302,7 +302,7 @@ resource "aws_appstream_image_builder" "test" { func testAccImageBuilderConfig_tags1(instanceType, rName, key, value string) string { return fmt.Sprintf(` resource "aws_appstream_image_builder" "test" { - image_name = "AppStream-WinServer2022-03-24-2024" + image_name = "AppStream-WinServer2022-06-17-2024" instance_type = %[1]q name = %[2]q @@ -316,7 +316,7 @@ resource "aws_appstream_image_builder" "test" { func testAccImageBuilderConfig_tags2(instanceType, rName, key1, value1, key2, value2 string) string { return fmt.Sprintf(` resource "aws_appstream_image_builder" "test" { - image_name = "AppStream-WinServer2022-03-24-2024" + image_name = "AppStream-WinServer2022-06-17-2024" instance_type = %[1]q name = %[2]q From 498f83d0a15bc9513e482a6586fb5c817e7d4a34 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 09:30:22 -0400 Subject: [PATCH 34/60] Acceptance test output: % make testacc TESTARGS='-run=TestAccAppStreamUser_\|TestAccAppStreamImageBuilder_' PKG=appstream ACCTEST_PARALLELISM=3 make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.5 test ./internal/service/appstream/... -v -count 1 -parallel 3 -run=TestAccAppStreamUser_\|TestAccAppStreamImageBuilder_ -timeout 360m === RUN TestAccAppStreamImageBuilder_basic === PAUSE TestAccAppStreamImageBuilder_basic === RUN TestAccAppStreamImageBuilder_withIAMRole === PAUSE TestAccAppStreamImageBuilder_withIAMRole === RUN TestAccAppStreamImageBuilder_disappears === PAUSE TestAccAppStreamImageBuilder_disappears === RUN TestAccAppStreamImageBuilder_complete === PAUSE TestAccAppStreamImageBuilder_complete === RUN TestAccAppStreamImageBuilder_tags === PAUSE TestAccAppStreamImageBuilder_tags === RUN TestAccAppStreamImageBuilder_imageARN === PAUSE TestAccAppStreamImageBuilder_imageARN === RUN TestAccAppStreamUser_basic === PAUSE TestAccAppStreamUser_basic === RUN TestAccAppStreamUser_disappears === PAUSE TestAccAppStreamUser_disappears === RUN TestAccAppStreamUser_complete === PAUSE TestAccAppStreamUser_complete === CONT TestAccAppStreamImageBuilder_basic === CONT TestAccAppStreamImageBuilder_imageARN === CONT TestAccAppStreamImageBuilder_complete --- PASS: TestAccAppStreamImageBuilder_basic (578.90s) === CONT TestAccAppStreamImageBuilder_tags --- PASS: TestAccAppStreamImageBuilder_imageARN (578.99s) === CONT TestAccAppStreamImageBuilder_disappears --- PASS: TestAccAppStreamImageBuilder_complete (1106.53s) === CONT TestAccAppStreamUser_disappears --- PASS: TestAccAppStreamImageBuilder_disappears (536.62s) === CONT TestAccAppStreamUser_complete --- PASS: TestAccAppStreamUser_disappears (16.96s) === CONT TestAccAppStreamImageBuilder_withIAMRole --- PASS: TestAccAppStreamImageBuilder_tags (560.80s) === CONT TestAccAppStreamUser_basic --- PASS: TestAccAppStreamUser_basic (25.15s) --- PASS: TestAccAppStreamUser_complete (49.40s) --- PASS: TestAccAppStreamImageBuilder_withIAMRole (579.07s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/appstream 1707.281s From c872312970fa5f18aca7c96cdd4fec34e10b5f71 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 09:35:09 -0400 Subject: [PATCH 35/60] r/aws_autoscaling_lifecycle_hook: Remove use of 'tfslices.ToPointers'. --- .../service/autoscaling/lifecycle_hook.go | 55 +++++++++++-------- internal/slices/slices.go | 7 --- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/internal/service/autoscaling/lifecycle_hook.go b/internal/service/autoscaling/lifecycle_hook.go index 16e35125f0b..386d1f5df50 100644 --- a/internal/service/autoscaling/lifecycle_hook.go +++ b/internal/service/autoscaling/lifecycle_hook.go @@ -22,7 +22,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" @@ -186,12 +185,33 @@ func resourceLifecycleHookDelete(ctx context.Context, d *schema.ResourceData, me return diags } -func findLifecycleHookByTwoPartKey(ctx context.Context, conn *autoscaling.Client, asgName, hookName string) (*awstypes.LifecycleHook, error) { - input := &autoscaling.DescribeLifecycleHooksInput{ - AutoScalingGroupName: aws.String(asgName), - LifecycleHookNames: []string{hookName}, +func resourceLifecycleHookImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + idParts := strings.SplitN(d.Id(), "/", 2) + if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { + return nil, fmt.Errorf("unexpected format (%q), expected /", d.Id()) + } + + asgName := idParts[0] + lifecycleHookName := idParts[1] + + d.Set(names.AttrName, lifecycleHookName) + d.Set("autoscaling_group_name", asgName) + d.SetId(lifecycleHookName) + + return []*schema.ResourceData{d}, nil +} + +func findLifecycleHook(ctx context.Context, conn *autoscaling.Client, input *autoscaling.DescribeLifecycleHooksInput) (*awstypes.LifecycleHook, error) { + output, err := findLifecycleHooks(ctx, conn, input) + + if err != nil { + return nil, err } + return tfresource.AssertSingleValueResult(output) +} + +func findLifecycleHooks(ctx context.Context, conn *autoscaling.Client, input *autoscaling.DescribeLifecycleHooksInput) ([]awstypes.LifecycleHook, error) { output, err := conn.DescribeLifecycleHooks(ctx, input) if tfawserr.ErrMessageContains(err, errCodeValidationError, "not found") { @@ -209,27 +229,14 @@ func findLifecycleHookByTwoPartKey(ctx context.Context, conn *autoscaling.Client return nil, tfresource.NewEmptyResultError(input) } - for _, v := range tfslices.ToPointers(output.LifecycleHooks) { - if aws.ToString(v.LifecycleHookName) == hookName { - return v, nil - } - } - - return nil, &retry.NotFoundError{LastRequest: input} + return output.LifecycleHooks, nil } -func resourceLifecycleHookImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - idParts := strings.SplitN(d.Id(), "/", 2) - if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { - return nil, fmt.Errorf("unexpected format (%q), expected /", d.Id()) +func findLifecycleHookByTwoPartKey(ctx context.Context, conn *autoscaling.Client, asgName, hookName string) (*awstypes.LifecycleHook, error) { + input := &autoscaling.DescribeLifecycleHooksInput{ + AutoScalingGroupName: aws.String(asgName), + LifecycleHookNames: []string{hookName}, } - asgName := idParts[0] - lifecycleHookName := idParts[1] - - d.Set(names.AttrName, lifecycleHookName) - d.Set("autoscaling_group_name", asgName) - d.SetId(lifecycleHookName) - - return []*schema.ResourceData{d}, nil + return findLifecycleHook(ctx, conn, input) } diff --git a/internal/slices/slices.go b/internal/slices/slices.go index 0afd7cd2266..380a5307879 100644 --- a/internal/slices/slices.go +++ b/internal/slices/slices.go @@ -55,13 +55,6 @@ func ApplyToAllWithError[S ~[]E1, E1, E2 any](s S, f func(E1) (E2, error)) ([]E2 return v, nil } -// ToPointers returns a new slice containing pointers to each element of the original slice `s`. -func ToPointers[S ~[]E, E any](s S) []*E { - return ApplyToAll(s, func(e E) *E { - return &e - }) -} - // Values returns a new slice containing values from the pointers in each element of the original slice `s`. func Values[S ~[]*E, E any](s S) []E { return ApplyToAll(s, func(e *E) E { From 41510beb3f3d1352dfae6d87c1b4de8f71c92297 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 09:43:20 -0400 Subject: [PATCH 36/60] Acceptance test output: % make testacc TESTARGS='-run=TestAccAutoScalingLifecycleHook_' PKG=autoscaling ACCTEST_PARALLELISM=3 make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.5 test ./internal/service/autoscaling/... -v -count 1 -parallel 3 -run=TestAccAutoScalingLifecycleHook_ -timeout 360m === RUN TestAccAutoScalingLifecycleHook_basic === PAUSE TestAccAutoScalingLifecycleHook_basic === RUN TestAccAutoScalingLifecycleHook_disappears === PAUSE TestAccAutoScalingLifecycleHook_disappears === RUN TestAccAutoScalingLifecycleHook_omitDefaultResult === PAUSE TestAccAutoScalingLifecycleHook_omitDefaultResult === CONT TestAccAutoScalingLifecycleHook_basic === CONT TestAccAutoScalingLifecycleHook_omitDefaultResult === CONT TestAccAutoScalingLifecycleHook_disappears --- PASS: TestAccAutoScalingLifecycleHook_omitDefaultResult (151.45s) --- PASS: TestAccAutoScalingLifecycleHook_disappears (179.05s) --- PASS: TestAccAutoScalingLifecycleHook_basic (209.39s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/autoscaling 214.342s From 2a373f5cac3181d77ecf0b4e8fd074394fc0f475 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 10:02:16 -0400 Subject: [PATCH 37/60] r/aws_ecr_lifecycle_policy: Eliminate use of 'github.com/aws/aws-sdk-go/private/protocol/json/jsonutil'. --- internal/service/ecr/lifecycle_policy.go | 32 ++++++++---------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/internal/service/ecr/lifecycle_policy.go b/internal/service/ecr/lifecycle_policy.go index 2ddac99e462..ed8a7e7d744 100644 --- a/internal/service/ecr/lifecycle_policy.go +++ b/internal/service/ecr/lifecycle_policy.go @@ -4,9 +4,7 @@ package ecr import ( - "bytes" "context" - "encoding/json" "log" "sort" "strings" @@ -14,7 +12,6 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecr" "github.com/aws/aws-sdk-go-v2/service/ecr/types" - "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -23,6 +20,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfjson "github.com/hashicorp/terraform-provider-aws/internal/json" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -236,37 +234,27 @@ func equivalentLifecyclePolicyJSON(str1, str2 string) (bool, error) { str2 = "{}" } - var lp1, lp2 lifecyclePolicy - - if err := json.Unmarshal([]byte(str1), &lp1); err != nil { + var lp1 lifecyclePolicy + err := tfjson.DecodeFromString(str1, &lp1) + if err != nil { return false, err } - lp1.reduce() - - canonicalJSON1, err := jsonutil.BuildJSON(lp1) - + b1, err := tfjson.EncodeToBytes(lp1) if err != nil { return false, err } - if err := json.Unmarshal([]byte(str2), &lp2); err != nil { + var lp2 lifecyclePolicy + err = tfjson.DecodeFromString(str2, &lp2) + if err != nil { return false, err } - lp2.reduce() - - canonicalJSON2, err := jsonutil.BuildJSON(lp2) - + b2, err := tfjson.EncodeToBytes(lp2) if err != nil { return false, err } - equal := bytes.Equal(canonicalJSON1, canonicalJSON2) - - if !equal { - log.Printf("[DEBUG] Canonical Lifecycle Policy JSONs are not equal.\nFirst: %s\nSecond: %s\n", canonicalJSON1, canonicalJSON2) - } - - return equal, nil + return tfjson.EqualBytes(b1, b2), nil } From b21e242656708aeee0b2778b84d5735e5abde99a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 10:05:25 -0400 Subject: [PATCH 38/60] Acceptance test output: % make testacc TESTARGS='-run=TestAccECRLifecyclePolicy' PKG=ecr ACCTEST_PARALLELISM=3 make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.5 test ./internal/service/ecr/... -v -count 1 -parallel 3 -run=TestAccECRLifecyclePolicy -timeout 360m === RUN TestAccECRLifecyclePolicyDocumentDataSource_basic --- PASS: TestAccECRLifecyclePolicyDocumentDataSource_basic (5.92s) === RUN TestAccECRLifecyclePolicy_basic === PAUSE TestAccECRLifecyclePolicy_basic === RUN TestAccECRLifecyclePolicy_disappears === PAUSE TestAccECRLifecyclePolicy_disappears === RUN TestAccECRLifecyclePolicy_ignoreEquivalent === PAUSE TestAccECRLifecyclePolicy_ignoreEquivalent === RUN TestAccECRLifecyclePolicy_detectDiff === PAUSE TestAccECRLifecyclePolicy_detectDiff === RUN TestAccECRLifecyclePolicy_detectTagPatternListDiff === PAUSE TestAccECRLifecyclePolicy_detectTagPatternListDiff === CONT TestAccECRLifecyclePolicy_basic === CONT TestAccECRLifecyclePolicy_detectDiff === CONT TestAccECRLifecyclePolicy_detectTagPatternListDiff --- PASS: TestAccECRLifecyclePolicy_basic (11.87s) === CONT TestAccECRLifecyclePolicy_ignoreEquivalent --- PASS: TestAccECRLifecyclePolicy_detectDiff (13.53s) === CONT TestAccECRLifecyclePolicy_disappears --- PASS: TestAccECRLifecyclePolicy_detectTagPatternListDiff (13.59s) --- PASS: TestAccECRLifecyclePolicy_disappears (9.63s) --- PASS: TestAccECRLifecyclePolicy_ignoreEquivalent (12.08s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ecr 34.781s From 0b51dfc8fcd6f48ee6d8f20da7a431422a46742a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 10:16:57 -0400 Subject: [PATCH 39/60] r/aws_ecs_task_definition: Eliminate use of 'github.com/aws/aws-sdk-go/private/protocol/json/jsonutil'. --- internal/service/ecs/exports_test.go | 5 +- internal/service/ecs/task_definition.go | 49 ++++++++----------- .../ecs/task_definition_equivalency.go | 22 ++++----- .../ecs/task_definition_equivalency_test.go | 18 +++---- 4 files changed, 41 insertions(+), 53 deletions(-) diff --git a/internal/service/ecs/exports_test.go b/internal/service/ecs/exports_test.go index c6ffa82b7c7..5a03529c8ce 100644 --- a/internal/service/ecs/exports_test.go +++ b/internal/service/ecs/exports_test.go @@ -8,6 +8,7 @@ var ( ResourceAccountSettingDefault = resourceAccountSettingDefault ResourceTag = resourceTag - FindEffectiveAccountSettingByName = findEffectiveAccountSettingByName - FindTag = findTag + FindEffectiveAccountSettingByName = findEffectiveAccountSettingByName + FindTag = findTag + ValidTaskDefinitionContainerDefinitions = validTaskDefinitionContainerDefinitions ) diff --git a/internal/service/ecs/task_definition.go b/internal/service/ecs/task_definition.go index 71bbf9afbdb..e3104cb2c28 100644 --- a/internal/service/ecs/task_definition.go +++ b/internal/service/ecs/task_definition.go @@ -6,7 +6,6 @@ package ecs import ( "bytes" "context" - "encoding/json" "fmt" "log" "strings" @@ -16,7 +15,6 @@ import ( "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/aws/aws-sdk-go-v2/service/ecs" awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" - "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" @@ -27,6 +25,7 @@ import ( "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" + tfjson "github.com/hashicorp/terraform-provider-aws/internal/json" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" @@ -85,9 +84,9 @@ func ResourceTaskDefinition() *schema.Resource { // spurious reorderings in plans (diff is suppressed if the environment variables haven't changed, // but they still show in the plan if some other property changes). orderedCDs, _ := expandContainerDefinitions(v.(string)) - containerDefinitions(orderedCDs).OrderContainers() - containerDefinitions(orderedCDs).OrderEnvironmentVariables() - containerDefinitions(orderedCDs).OrderSecrets() + containerDefinitions(orderedCDs).orderContainers() + containerDefinitions(orderedCDs).orderEnvironmentVariables() + containerDefinitions(orderedCDs).orderSecrets() unnormalizedJson, _ := flattenContainerDefinitions(orderedCDs) json, _ := structure.NormalizeJsonString(unnormalizedJson) return json @@ -95,10 +94,10 @@ func ResourceTaskDefinition() *schema.Resource { DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { networkMode, ok := d.GetOk("network_mode") isAWSVPC := ok && networkMode.(string) == string(awstypes.NetworkModeAwsvpc) - equal, _ := ContainerDefinitionsAreEquivalent(old, new, isAWSVPC) + equal, _ := containerDefinitionsAreEquivalent(old, new, isAWSVPC) return equal }, - ValidateFunc: ValidTaskDefinitionContainerDefinitions, + ValidateFunc: validTaskDefinitionContainerDefinitions, }, "cpu": { Type: schema.TypeString, @@ -451,9 +450,8 @@ func ResourceTaskDefinition() *schema.Resource { } } -func ValidTaskDefinitionContainerDefinitions(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - _, err := expandContainerDefinitions(value) +func validTaskDefinitionContainerDefinitions(v interface{}, k string) (ws []string, errors []error) { + _, err := expandContainerDefinitions(v.(string)) if err != nil { errors = append(errors, fmt.Errorf("ECS Task Definition container_definitions is invalid: %s", err)) } @@ -465,10 +463,9 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m conn := meta.(*conns.AWSClient).ECSClient(ctx) partition := meta.(*conns.AWSClient).Partition - rawDefinitions := d.Get("container_definitions").(string) - definitions, err := expandContainerDefinitions(rawDefinitions) + definitions, err := expandContainerDefinitions(d.Get("container_definitions").(string)) if err != nil { - return sdkdiag.AppendErrorf(diags, "creating ECS Task Definition (%s): %s", d.Get(names.AttrFamily).(string), err) + return sdkdiag.AppendFromErr(diags, err) } input := &ecs.RegisterTaskDefinitionInput{ @@ -624,9 +621,9 @@ func resourceTaskDefinitionRead(ctx context.Context, d *schema.ResourceData, met // Sort the lists of environment variables as they come in, so we won't get spurious reorderings in plans // (diff is suppressed if the environment variables haven't changed, but they still show in the plan if // some other property changes). - containerDefinitions(taskDefinition.ContainerDefinitions).OrderContainers() - containerDefinitions(taskDefinition.ContainerDefinitions).OrderEnvironmentVariables() - containerDefinitions(taskDefinition.ContainerDefinitions).OrderSecrets() + containerDefinitions(taskDefinition.ContainerDefinitions).orderContainers() + containerDefinitions(taskDefinition.ContainerDefinitions).orderEnvironmentVariables() + containerDefinitions(taskDefinition.ContainerDefinitions).orderSecrets() defs, err := flattenContainerDefinitions(taskDefinition.ContainerDefinitions) if err != nil { @@ -1194,24 +1191,18 @@ func flattenFSxWinVolumeAuthorizationConfig(config *awstypes.FSxWindowsFileServe return items } -func flattenContainerDefinitions(definitions []awstypes.ContainerDefinition) (string, error) { - b, err := jsonutil.BuildJSON(definitions) - if err != nil { - return "", err - } - - return string(b), nil +func flattenContainerDefinitions(apiObjects []awstypes.ContainerDefinition) (string, error) { + return tfjson.EncodeToString(apiObjects) } -func expandContainerDefinitions(rawDefinitions string) ([]awstypes.ContainerDefinition, error) { - var definitions []awstypes.ContainerDefinition +func expandContainerDefinitions(tfString string) ([]awstypes.ContainerDefinition, error) { + var apiObjects []awstypes.ContainerDefinition - err := json.Unmarshal([]byte(rawDefinitions), &definitions) - if err != nil { - return nil, fmt.Errorf("decoding JSON: %s", err) + if err := tfjson.DecodeFromString(tfString, &apiObjects); err != nil { + return nil, err } - return definitions, nil + return apiObjects, nil } func expandTaskDefinitionEphemeralStorage(config []interface{}) *awstypes.EphemeralStorage { diff --git a/internal/service/ecs/task_definition_equivalency.go b/internal/service/ecs/task_definition_equivalency.go index 5398300c73c..8995ae59cd4 100644 --- a/internal/service/ecs/task_definition_equivalency.go +++ b/internal/service/ecs/task_definition_equivalency.go @@ -11,15 +11,13 @@ import ( tfjson "github.com/hashicorp/terraform-provider-aws/internal/json" ) -// ContainerDefinitionsAreEquivalent determines equality between two ECS container definition JSON strings -// Note: This function will be moved out of the aws package in the future. -func ContainerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, error) { +func containerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, error) { var obj1 containerDefinitions err := tfjson.DecodeFromString(def1, &obj1) if err != nil { return false, err } - err = obj1.Reduce(isAWSVPC) + err = obj1.reduce(isAWSVPC) if err != nil { return false, err } @@ -33,7 +31,7 @@ func ContainerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, if err != nil { return false, err } - err = obj2.Reduce(isAWSVPC) + err = obj2.reduce(isAWSVPC) if err != nil { return false, err } @@ -47,11 +45,11 @@ func ContainerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, type containerDefinitions []awstypes.ContainerDefinition -func (cd containerDefinitions) Reduce(isAWSVPC bool) error { +func (cd containerDefinitions) reduce(isAWSVPC bool) error { // Deal with fields which may be re-ordered in the API. - cd.OrderContainers() - cd.OrderEnvironmentVariables() - cd.OrderSecrets() + cd.orderContainers() + cd.orderEnvironmentVariables() + cd.orderSecrets() for i, def := range cd { // Deal with special fields which have defaults. @@ -129,7 +127,7 @@ func (cd containerDefinitions) Reduce(isAWSVPC bool) error { return nil } -func (cd containerDefinitions) OrderEnvironmentVariables() { +func (cd containerDefinitions) orderEnvironmentVariables() { for _, def := range cd { sort.Slice(def.Environment, func(i, j int) bool { return aws.ToString(def.Environment[i].Name) < aws.ToString(def.Environment[j].Name) @@ -137,7 +135,7 @@ func (cd containerDefinitions) OrderEnvironmentVariables() { } } -func (cd containerDefinitions) OrderSecrets() { +func (cd containerDefinitions) orderSecrets() { for _, def := range cd { sort.Slice(def.Secrets, func(i, j int) bool { return aws.ToString(def.Secrets[i].Name) < aws.ToString(def.Secrets[j].Name) @@ -145,7 +143,7 @@ func (cd containerDefinitions) OrderSecrets() { } } -func (cd containerDefinitions) OrderContainers() { +func (cd containerDefinitions) orderContainers() { sort.Slice(cd, func(i, j int) bool { return aws.ToString(cd[i].Name) < aws.ToString(cd[j].Name) }) diff --git a/internal/service/ecs/task_definition_equivalency_test.go b/internal/service/ecs/task_definition_equivalency_test.go index 40efcc046aa..2e75d80a1c6 100644 --- a/internal/service/ecs/task_definition_equivalency_test.go +++ b/internal/service/ecs/task_definition_equivalency_test.go @@ -1,12 +1,10 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package ecs_test +package ecs import ( "testing" - - tfecs "github.com/hashicorp/terraform-provider-aws/internal/service/ecs" ) func TestContainerDefinitionsAreEquivalent_basic(t *testing.T) { @@ -85,7 +83,7 @@ func TestContainerDefinitionsAreEquivalent_basic(t *testing.T) { } ]` - equal, err := tfecs.ContainerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) + equal, err := containerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) if err != nil { t.Fatal(err) } @@ -134,7 +132,7 @@ func TestContainerDefinitionsAreEquivalent_portMappings(t *testing.T) { } ]` - equal, err := tfecs.ContainerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) + equal, err := containerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) if err != nil { t.Fatal(err) } @@ -178,7 +176,7 @@ func TestContainerDefinitionsAreEquivalent_portMappingsIgnoreHostPort(t *testing err error ) - equal, err = tfecs.ContainerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) + equal, err = containerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) if err != nil { t.Fatal(err) } @@ -186,7 +184,7 @@ func TestContainerDefinitionsAreEquivalent_portMappingsIgnoreHostPort(t *testing t.Fatal("Expected definitions to differ.") } - equal, err = tfecs.ContainerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, true) + equal, err = containerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, true) if err != nil { t.Fatal(err) } @@ -441,7 +439,7 @@ func TestContainerDefinitionsAreEquivalent_arrays(t *testing.T) { ] ` - equal, err := tfecs.ContainerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) + equal, err := containerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) if err != nil { t.Fatal(err) } @@ -481,7 +479,7 @@ func TestContainerDefinitionsAreEquivalent_negative(t *testing.T) { } ]` - equal, err := tfecs.ContainerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) + equal, err := containerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) if err != nil { t.Fatal(err) } @@ -570,7 +568,7 @@ func TestContainerDefinitionsAreEquivalent_missingEnvironmentName(t *testing.T) } ]` - equal, err := tfecs.ContainerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) + equal, err := containerDefinitionsAreEquivalent(cfgRepresention, apiRepresentation, false) if err != nil { t.Fatal(err) } From eec09dcd0e955e87126d771dbdfb67a43a884dd5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 11:21:03 -0400 Subject: [PATCH 40/60] r/aws_ecs_capacity_provider: Reduce visibility. --- internal/service/ecs/capacity_provider.go | 177 +++++++++++++++--- .../service/ecs/capacity_provider_test.go | 10 +- internal/service/ecs/exports_test.go | 2 + internal/service/ecs/find.go | 40 ---- internal/service/ecs/service_package_gen.go | 2 +- internal/service/ecs/status.go | 32 ---- internal/service/ecs/sweep.go | 2 +- internal/service/ecs/wait.go | 37 ---- 8 files changed, 153 insertions(+), 149 deletions(-) diff --git a/internal/service/ecs/capacity_provider.go b/internal/service/ecs/capacity_provider.go index 3b2c9bcdc04..c42ada913ea 100644 --- a/internal/service/ecs/capacity_provider.go +++ b/internal/service/ecs/capacity_provider.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "log" + "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws/arn" @@ -28,7 +29,7 @@ import ( // @SDKResource("aws_ecs_capacity_provider", name="Capacity Provider") // @Tags(identifierAttribute="id") -func ResourceCapacityProvider() *schema.Resource { +func resourceCapacityProvider() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceCapacityProviderCreate, ReadWithoutTimeout: resourceCapacityProviderRead, @@ -131,8 +132,8 @@ func resourceCapacityProviderCreate(ctx context.Context, d *schema.ResourceData, name := d.Get(names.AttrName).(string) input := ecs.CreateCapacityProviderInput{ - Name: aws.String(name), AutoScalingGroupProvider: expandAutoScalingGroupProviderCreate(d.Get("auto_scaling_group_provider")), + Name: aws.String(name), Tags: getTagsIn(ctx), } @@ -171,9 +172,8 @@ func resourceCapacityProviderCreate(ctx context.Context, d *schema.ResourceData, func resourceCapacityProviderRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition - output, err := FindCapacityProviderByARN(ctx, conn, partition, d.Id()) + output, err := findCapacityProviderByARN(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] ECS Capacity Provider (%s) not found, removing from state", d.Id()) @@ -186,11 +186,9 @@ func resourceCapacityProviderRead(ctx context.Context, d *schema.ResourceData, m } d.Set(names.AttrARN, output.CapacityProviderArn) - if err := d.Set("auto_scaling_group_provider", flattenAutoScalingGroupProvider(output.AutoScalingGroupProvider)); err != nil { return sdkdiag.AppendErrorf(diags, "setting auto_scaling_group_provider: %s", err) } - d.Set(names.AttrName, output.Name) setTagsOut(ctx, output.Tags) @@ -201,7 +199,6 @@ func resourceCapacityProviderRead(ctx context.Context, d *schema.ResourceData, m func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { input := &ecs.UpdateCapacityProviderInput{ @@ -209,31 +206,19 @@ func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, Name: aws.String(d.Get(names.AttrName).(string)), } - log.Printf("[DEBUG] Updating ECS Capacity Provider: %+v", input) - err := retry.RetryContext(ctx, capacityProviderUpdateTimeout, func() *retry.RetryError { - _, err := conn.UpdateCapacityProvider(ctx, input) - - if errs.IsA[*awstypes.UpdateInProgressException](err) { - return retry.RetryableError(err) - } - - if err != nil { - return retry.NonRetryableError(err) - } - - return nil + const ( + timeout = 10 * time.Minute + ) + _, err := tfresource.RetryWhenIsA[*awstypes.UpdateInProgressException](ctx, timeout, func() (interface{}, error) { + return conn.UpdateCapacityProvider(ctx, input) }) - if tfresource.TimedOut(err) { - _, err = conn.UpdateCapacityProvider(ctx, input) - } - if err != nil { return sdkdiag.AppendErrorf(diags, "updating ECS Capacity Provider (%s): %s", d.Id(), err) } - if _, err = waitCapacityProviderUpdated(ctx, conn, partition, d.Id()); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for ECS Capacity Provider (%s) to update: %s", d.Id(), err) + if _, err = waitCapacityProviderUpdated(ctx, conn, d.Id(), timeout); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for ECS Capacity Provider (%s) update: %s", d.Id(), err) } } @@ -243,9 +228,8 @@ func resourceCapacityProviderUpdate(ctx context.Context, d *schema.ResourceData, func resourceCapacityProviderDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition - log.Printf("[DEBUG] Deleting ECS Capacity Provider (%s)", d.Id()) + log.Printf("[DEBUG] Deleting ECS Capacity Provider: %s", d.Id()) _, err := conn.DeleteCapacityProvider(ctx, &ecs.DeleteCapacityProviderInput{ CapacityProvider: aws.String(d.Id()), }) @@ -259,8 +243,11 @@ func resourceCapacityProviderDelete(ctx context.Context, d *schema.ResourceData, return sdkdiag.AppendErrorf(diags, "deleting ECS Capacity Provider (%s): %s", d.Id(), err) } - if _, err := waitCapacityProviderDeleted(ctx, conn, partition, d.Id()); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for ECS Capacity Provider (%s) to delete: %s", d.Id(), err) + const ( + timeout = 20 * time.Minute + ) + if _, err := waitCapacityProviderDeleted(ctx, conn, d.Id(), timeout); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for ECS Capacity Provider (%s) delete: %s", d.Id(), err) } return diags @@ -275,9 +262,139 @@ func resourceCapacityProviderImport(ctx context.Context, d *schema.ResourceData, Service: "ecs", Resource: fmt.Sprintf("capacity-provider/%s", d.Id()), }.String()) + return []*schema.ResourceData{d}, nil } +func partitionFromConn(conn *ecs.Client) string { + return names.PartitionForRegion(conn.Options().Region) +} + +func findCapacityProvider(ctx context.Context, conn *ecs.Client, input *ecs.DescribeCapacityProvidersInput) (*awstypes.CapacityProvider, error) { + output, err := findCapacityProviders(ctx, conn, input) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findCapacityProviders(ctx context.Context, conn *ecs.Client, input *ecs.DescribeCapacityProvidersInput) ([]awstypes.CapacityProvider, error) { + var output []awstypes.CapacityProvider + + err := describeCapacityProvidersPages(ctx, conn, input, func(page *ecs.DescribeCapacityProvidersOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + output = append(output, page.CapacityProviders...) + + return !lastPage + }) + + if err != nil { + return nil, err + } + + // Some partitions (i.e., ISO) may not support tagging, giving error. + if input.Include != nil && errs.IsUnsupportedOperationInPartitionError(partitionFromConn(conn), err) { + input.Include = nil + + return findCapacityProviders(ctx, conn, input) + } + + return output, nil +} + +func findCapacityProviderByARN(ctx context.Context, conn *ecs.Client, arn string) (*awstypes.CapacityProvider, error) { + input := &ecs.DescribeCapacityProvidersInput{ + CapacityProviders: []string{arn}, + Include: []awstypes.CapacityProviderField{awstypes.CapacityProviderFieldTags}, + } + + output, err := findCapacityProvider(ctx, conn, input) + + if err != nil { + return nil, err + } + + if status := output.Status; status == awstypes.CapacityProviderStatusInactive { + return nil, &retry.NotFoundError{ + Message: string(status), + LastRequest: input, + } + } + + return output, nil +} + +func statusCapacityProvider(ctx context.Context, conn *ecs.Client, arn string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findCapacityProviderByARN(ctx, conn, arn) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.Status), nil + } +} + +func statusCapacityProviderUpdate(ctx context.Context, conn *ecs.Client, arn string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findCapacityProviderByARN(ctx, conn, arn) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.UpdateStatus), nil + } +} + +func waitCapacityProviderUpdated(ctx context.Context, conn *ecs.Client, arn string, timeout time.Duration) (*awstypes.CapacityProvider, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.CapacityProviderUpdateStatusUpdateInProgress), + Target: enum.Slice(awstypes.CapacityProviderUpdateStatusUpdateComplete), + Refresh: statusCapacityProviderUpdate(ctx, conn, arn), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if v, ok := outputRaw.(*awstypes.CapacityProvider); ok { + return v, err + } + + return nil, err +} + +func waitCapacityProviderDeleted(ctx context.Context, conn *ecs.Client, arn string, timeout time.Duration) (*awstypes.CapacityProvider, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.CapacityProviderStatusActive), + Target: []string{}, + Refresh: statusCapacityProvider(ctx, conn, arn), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if v, ok := outputRaw.(*awstypes.CapacityProvider); ok { + return v, err + } + + return nil, err +} + func expandAutoScalingGroupProviderCreate(configured interface{}) *awstypes.AutoScalingGroupProvider { if configured == nil { return nil diff --git a/internal/service/ecs/capacity_provider_test.go b/internal/service/ecs/capacity_provider_test.go index 72e8ba462d1..c0e546e90cc 100644 --- a/internal/service/ecs/capacity_provider_test.go +++ b/internal/service/ecs/capacity_provider_test.go @@ -236,14 +236,13 @@ func TestAccECSCapacityProvider_tags(t *testing.T) { func testAccCheckCapacityProviderDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) - partition := acctest.Provider.Meta().(*conns.AWSClient).Partition for _, rs := range s.RootModule().Resources { if rs.Type != "aws_ecs_capacity_provider" { continue } - _, err := tfecs.FindCapacityProviderByARN(ctx, conn, partition, rs.Primary.ID) + _, err := tfecs.FindCapacityProviderByARN(ctx, conn, rs.Primary.ID) if tfresource.NotFound(err) { continue @@ -266,14 +265,9 @@ func testAccCheckCapacityProviderExists(ctx context.Context, resourceName string return fmt.Errorf("Not found: %s", resourceName) } - if rs.Primary.ID == "" { - return fmt.Errorf("No ECS Capacity Provider ID is set") - } - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) - partition := acctest.Provider.Meta().(*conns.AWSClient).Partition - output, err := tfecs.FindCapacityProviderByARN(ctx, conn, partition, rs.Primary.ID) + output, err := tfecs.FindCapacityProviderByARN(ctx, conn, rs.Primary.ID) if err != nil { return err diff --git a/internal/service/ecs/exports_test.go b/internal/service/ecs/exports_test.go index 5a03529c8ce..f9e3a1b4aa3 100644 --- a/internal/service/ecs/exports_test.go +++ b/internal/service/ecs/exports_test.go @@ -6,8 +6,10 @@ package ecs // Exports for use in tests only. var ( ResourceAccountSettingDefault = resourceAccountSettingDefault + ResourceCapacityProvider = resourceCapacityProvider ResourceTag = resourceTag + FindCapacityProviderByARN = findCapacityProviderByARN FindEffectiveAccountSettingByName = findEffectiveAccountSettingByName FindTag = findTag ValidTaskDefinitionContainerDefinitions = validTaskDefinitionContainerDefinitions diff --git a/internal/service/ecs/find.go b/internal/service/ecs/find.go index ec819487a34..e65a3895438 100644 --- a/internal/service/ecs/find.go +++ b/internal/service/ecs/find.go @@ -6,7 +6,6 @@ package ecs import ( "context" "fmt" - "log" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecs" @@ -16,45 +15,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func FindCapacityProviderByARN(ctx context.Context, conn *ecs.Client, partition, arn string) (*awstypes.CapacityProvider, error) { - input := &ecs.DescribeCapacityProvidersInput{ - CapacityProviders: []string{arn}, - Include: []awstypes.CapacityProviderField{awstypes.CapacityProviderFieldTags}, - } - - output, err := conn.DescribeCapacityProviders(ctx, input) - - // Some partitions (i.e., ISO) may not support tagging, giving error - if errs.IsUnsupportedOperationInPartitionError(partition, err) { - log.Printf("[WARN] ECS tagging failed describing Capacity Provider (%s) with tags: %s; retrying without tags", arn, err) - - input.Include = nil - output, err = conn.DescribeCapacityProviders(ctx, input) - } - - if err != nil { - return nil, err - } - - if output == nil || len(output.CapacityProviders) == 0 { - return nil, &retry.NotFoundError{ - Message: "Empty result", - LastRequest: input, - } - } - - capacityProvider := output.CapacityProviders[0] - - if status := capacityProvider.Status; status == awstypes.CapacityProviderStatusInactive { - return nil, &retry.NotFoundError{ - Message: string(status), - LastRequest: input, - } - } - - return &capacityProvider, nil -} - func findServiceByTwoPartKey(ctx context.Context, conn *ecs.Client, partition, serviceName, clusterNameOrARN string) (*awstypes.Service, error) { input := &ecs.DescribeServicesInput{ Cluster: aws.String(clusterNameOrARN), diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index 4a7baf35d8d..60eb46688b9 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -57,7 +57,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka Name: "Account Setting Default", }, { - Factory: ResourceCapacityProvider, + Factory: resourceCapacityProvider, TypeName: "aws_ecs_capacity_provider", Name: "Capacity Provider", Tags: &types.ServicePackageResourceTags{ diff --git a/internal/service/ecs/status.go b/internal/service/ecs/status.go index e8c9cbe76a1..39c7e3c997e 100644 --- a/internal/service/ecs/status.go +++ b/internal/service/ecs/status.go @@ -26,38 +26,6 @@ const ( taskSetStatusPrimary = "PRIMARY" ) -func statusCapacityProvider(ctx context.Context, conn *ecs.Client, partition, arn string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := FindCapacityProviderByARN(ctx, conn, partition, arn) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, string(output.Status), nil - } -} - -func statusCapacityProviderUpdate(ctx context.Context, conn *ecs.Client, partition, arn string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := FindCapacityProviderByARN(ctx, conn, partition, arn) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, string(output.UpdateStatus), nil - } -} - func statusServiceNoTags(ctx context.Context, conn *ecs.Client, partition, id, cluster string) retry.StateRefreshFunc { return func() (interface{}, string, error) { service, err := FindServiceNoTagsByID(ctx, conn, partition, id, cluster) diff --git a/internal/service/ecs/sweep.go b/internal/service/ecs/sweep.go index 9b91a08174c..7ee9c3bbf17 100644 --- a/internal/service/ecs/sweep.go +++ b/internal/service/ecs/sweep.go @@ -71,7 +71,7 @@ func sweepCapacityProviders(region string) error { continue } - r := ResourceCapacityProvider() + r := resourceCapacityProvider() d := r.Data(nil) d.SetId(arn) diff --git a/internal/service/ecs/wait.go b/internal/service/ecs/wait.go index b3578204653..cfe2c2f5cd2 100644 --- a/internal/service/ecs/wait.go +++ b/internal/service/ecs/wait.go @@ -15,9 +15,6 @@ import ( ) const ( - capacityProviderDeleteTimeout = 20 * time.Minute - capacityProviderUpdateTimeout = 10 * time.Minute - serviceCreateTimeout = 2 * time.Minute serviceInactiveMinTimeout = 1 * time.Second serviceDescribeTimeout = 2 * time.Minute @@ -33,40 +30,6 @@ const ( taskSetDeleteTimeout = 10 * time.Minute ) -func waitCapacityProviderDeleted(ctx context.Context, conn *ecs.Client, partition, arn string) (*awstypes.CapacityProvider, error) { - stateConf := &retry.StateChangeConf{ - Pending: enum.Slice(awstypes.CapacityProviderStatusActive), - Target: []string{}, - Refresh: statusCapacityProvider(ctx, conn, partition, arn), - Timeout: capacityProviderDeleteTimeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if v, ok := outputRaw.(*awstypes.CapacityProvider); ok { - return v, err - } - - return nil, err -} - -func waitCapacityProviderUpdated(ctx context.Context, conn *ecs.Client, partition, arn string) (*awstypes.CapacityProvider, error) { - stateConf := &retry.StateChangeConf{ - Pending: enum.Slice(awstypes.CapacityProviderUpdateStatusUpdateInProgress), - Target: enum.Slice(awstypes.CapacityProviderUpdateStatusUpdateComplete), - Refresh: statusCapacityProviderUpdate(ctx, conn, partition, arn), - Timeout: capacityProviderUpdateTimeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if v, ok := outputRaw.(*awstypes.CapacityProvider); ok { - return v, err - } - - return nil, err -} - // waitServiceStable waits for an ECS Service to reach the status "ACTIVE" and have all desired tasks running. Does not return tags. func waitServiceStable(ctx context.Context, conn *ecs.Client, partition, id, cluster string, timeout time.Duration) (*awstypes.Service, error) { input := &ecs.DescribeServicesInput{ From 59dfb267e3f709edc848160ea4530505cf1cd136 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 11:28:04 -0400 Subject: [PATCH 41/60] r/aws_ecs_cluster_capacity_providers: Reduce visibility. --- internal/service/ecs/capacity_provider.go | 28 ++++++++++--------- .../service/ecs/cluster_capacity_providers.go | 14 ++++++---- internal/service/ecs/exports_test.go | 7 +++-- internal/service/ecs/service_package_gen.go | 3 +- internal/service/ecs/wait.go | 1 - 5 files changed, 29 insertions(+), 24 deletions(-) diff --git a/internal/service/ecs/capacity_provider.go b/internal/service/ecs/capacity_provider.go index c42ada913ea..9affea997d6 100644 --- a/internal/service/ecs/capacity_provider.go +++ b/internal/service/ecs/capacity_provider.go @@ -5,7 +5,7 @@ package ecs import ( "context" - "fmt" + "errors" "log" "time" @@ -260,7 +260,7 @@ func resourceCapacityProviderImport(ctx context.Context, d *schema.ResourceData, Region: meta.(*conns.AWSClient).Region, AccountID: meta.(*conns.AWSClient).AccountID, Service: "ecs", - Resource: fmt.Sprintf("capacity-provider/%s", d.Id()), + Resource: "capacity-provider/" + d.Id(), }.String()) return []*schema.ResourceData{d}, nil @@ -297,13 +297,6 @@ func findCapacityProviders(ctx context.Context, conn *ecs.Client, input *ecs.Des return nil, err } - // Some partitions (i.e., ISO) may not support tagging, giving error. - if input.Include != nil && errs.IsUnsupportedOperationInPartitionError(partitionFromConn(conn), err) { - input.Include = nil - - return findCapacityProviders(ctx, conn, input) - } - return output, nil } @@ -315,6 +308,13 @@ func findCapacityProviderByARN(ctx context.Context, conn *ecs.Client, arn string output, err := findCapacityProvider(ctx, conn, input) + // Some partitions (i.e., ISO) may not support tagging, giving error. + if errs.IsUnsupportedOperationInPartitionError(partitionFromConn(conn), err) { + input.Include = nil + + output, err = findCapacityProvider(ctx, conn, input) + } + if err != nil { return nil, err } @@ -371,8 +371,10 @@ func waitCapacityProviderUpdated(ctx context.Context, conn *ecs.Client, arn stri outputRaw, err := stateConf.WaitForStateContext(ctx) - if v, ok := outputRaw.(*awstypes.CapacityProvider); ok { - return v, err + if output, ok := outputRaw.(*awstypes.CapacityProvider); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.UpdateStatusReason))) + + return output, err } return nil, err @@ -388,8 +390,8 @@ func waitCapacityProviderDeleted(ctx context.Context, conn *ecs.Client, arn stri outputRaw, err := stateConf.WaitForStateContext(ctx) - if v, ok := outputRaw.(*awstypes.CapacityProvider); ok { - return v, err + if output, ok := outputRaw.(*awstypes.CapacityProvider); ok { + return output, err } return nil, err diff --git a/internal/service/ecs/cluster_capacity_providers.go b/internal/service/ecs/cluster_capacity_providers.go index 2ee24509ac1..050087df3ba 100644 --- a/internal/service/ecs/cluster_capacity_providers.go +++ b/internal/service/ecs/cluster_capacity_providers.go @@ -6,6 +6,7 @@ package ecs import ( "context" "log" + "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecs" @@ -21,8 +22,8 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_ecs_cluster_capacity_providers") -func ResourceClusterCapacityProviders() *schema.Resource { +// @SDKResource("aws_ecs_cluster_capacity_providers", name="Cluster Capacity Providers") +func resourceClusterCapacityProviders() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceClusterCapacityProvidersPut, ReadWithoutTimeout: resourceClusterCapacityProvidersRead, @@ -109,14 +110,13 @@ func resourceClusterCapacityProvidersPut(ctx context.Context, d *schema.Resource func resourceClusterCapacityProvidersRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSClient(ctx) partition := meta.(*conns.AWSClient).Partition cluster, err := FindClusterByNameOrARN(ctx, conn, partition, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { - sdkdiag.AppendErrorf(diags, "[WARN] ECS Cluster (%s) not found, removing from state", d.Id()) + sdkdiag.AppendErrorf(diags, "[WARN] ECS Cluster Capacity Providers (%s) not found, removing from state", d.Id()) d.SetId("") return diags } @@ -138,7 +138,6 @@ func resourceClusterCapacityProvidersRead(ctx context.Context, d *schema.Resourc func resourceClusterCapacityProvidersDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSClient(ctx) input := &ecs.PutClusterCapacityProvidersInput{ @@ -166,7 +165,10 @@ func resourceClusterCapacityProvidersDelete(ctx context.Context, d *schema.Resou } func retryClusterCapacityProvidersPut(ctx context.Context, conn *ecs.Client, input *ecs.PutClusterCapacityProvidersInput) error { - _, err := tfresource.RetryWhen(ctx, clusterUpdateTimeout, + const ( + timeout = 10 * time.Minute + ) + _, err := tfresource.RetryWhen(ctx, timeout, func() (interface{}, error) { return conn.PutClusterCapacityProviders(ctx, input) }, diff --git a/internal/service/ecs/exports_test.go b/internal/service/ecs/exports_test.go index f9e3a1b4aa3..1af369902d1 100644 --- a/internal/service/ecs/exports_test.go +++ b/internal/service/ecs/exports_test.go @@ -5,9 +5,10 @@ package ecs // Exports for use in tests only. var ( - ResourceAccountSettingDefault = resourceAccountSettingDefault - ResourceCapacityProvider = resourceCapacityProvider - ResourceTag = resourceTag + ResourceAccountSettingDefault = resourceAccountSettingDefault + ResourceCapacityProvider = resourceCapacityProvider + ResourceClusterCapacityProviders = resourceClusterCapacityProviders + ResourceTag = resourceTag FindCapacityProviderByARN = findCapacityProviderByARN FindEffectiveAccountSettingByName = findEffectiveAccountSettingByName diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index 60eb46688b9..2139f97c0bd 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -73,8 +73,9 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceClusterCapacityProviders, + Factory: resourceClusterCapacityProviders, TypeName: "aws_ecs_cluster_capacity_providers", + Name: "Cluster Capacity Providers", }, { Factory: ResourceService, diff --git a/internal/service/ecs/wait.go b/internal/service/ecs/wait.go index cfe2c2f5cd2..d31b1c4d44c 100644 --- a/internal/service/ecs/wait.go +++ b/internal/service/ecs/wait.go @@ -24,7 +24,6 @@ const ( clusterAvailableTimeout = 10 * time.Minute clusterDeleteTimeout = 10 * time.Minute clusterReadTimeout = 2 * time.Second - clusterUpdateTimeout = 10 * time.Minute taskSetCreateTimeout = 10 * time.Minute taskSetDeleteTimeout = 10 * time.Minute From 61fd3931fde4334512f961c91d25cfdce7648c4f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 12:30:27 -0400 Subject: [PATCH 42/60] r/aws_ecs+cluster: Reduce visibility. --- internal/service/ecs/cluster.go | 310 +++++++++--------- .../service/ecs/cluster_capacity_providers.go | 3 +- internal/service/ecs/cluster_data_source.go | 5 +- internal/service/ecs/cluster_test.go | 10 +- internal/service/ecs/exports_test.go | 2 + internal/service/ecs/service_package_gen.go | 2 +- internal/service/ecs/sweep.go | 2 +- internal/service/ecs/wait.go | 5 - 8 files changed, 169 insertions(+), 170 deletions(-) diff --git a/internal/service/ecs/cluster.go b/internal/service/ecs/cluster.go index a1c96f7d11c..28a1f6040a2 100644 --- a/internal/service/ecs/cluster.go +++ b/internal/service/ecs/cluster.go @@ -5,8 +5,8 @@ package ecs import ( "context" - "fmt" "log" + "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws/arn" @@ -27,7 +27,7 @@ import ( // @SDKResource("aws_ecs_cluster", name="Cluster") // @Tags(identifierAttribute="id") -func ResourceCluster() *schema.Resource { +func resourceCluster() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceClusterCreate, ReadWithoutTimeout: resourceClusterRead, @@ -163,18 +163,6 @@ func ResourceCluster() *schema.Resource { } } -func resourceClusterImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - d.Set(names.AttrName, d.Id()) - d.SetId(arn.ARN{ - Partition: meta.(*conns.AWSClient).Partition, - Region: meta.(*conns.AWSClient).Region, - AccountID: meta.(*conns.AWSClient).AccountID, - Service: "ecs", - Resource: fmt.Sprintf("cluster/%s", d.Id()), - }.String()) - return []*schema.ResourceData{d}, nil -} - func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) @@ -239,10 +227,12 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition - outputRaw, err := tfresource.RetryWhenNewResourceNotFound(ctx, clusterReadTimeout, func() (interface{}, error) { - return FindClusterByNameOrARN(ctx, conn, partition, d.Id()) + const ( + timeout = 2 * time.Second + ) + outputRaw, err := tfresource.RetryWhenNewResourceNotFound(ctx, timeout, func() (interface{}, error) { + return findClusterByNameOrARN(ctx, conn, d.Id()) }, d.IsNewResource()) if !d.IsNewResource() && tfresource.NotFound(err) { @@ -281,7 +271,6 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSClient(ctx) if d.HasChanges(names.AttrConfiguration, "service_connect_defaults", "setting") { @@ -315,27 +304,69 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int return diags } -func FindClusterByNameOrARN(ctx context.Context, conn *ecs.Client, partition string, nameOrARN string) (*awstypes.Cluster, error) { - input := &ecs.DescribeClustersInput{ - Clusters: []string{nameOrARN}, - Include: []awstypes.ClusterField{awstypes.ClusterFieldTags, awstypes.ClusterFieldConfigurations, awstypes.ClusterFieldSettings}, +func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).ECSClient(ctx) + + log.Printf("[DEBUG] Deleting ECS Cluster: %s", d.Id()) + const ( + timeout = 10 * time.Minute + ) + _, err := tfresource.RetryWhenIsOneOf4[*awstypes.ClusterContainsContainerInstancesException, *awstypes.ClusterContainsServicesException, *awstypes.ClusterContainsTasksException, *awstypes.UpdateInProgressException](ctx, timeout, func() (interface{}, error) { + return conn.DeleteCluster(ctx, &ecs.DeleteClusterInput{ + Cluster: aws.String(d.Id()), + }) + }) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting ECS Cluster (%s): %s", d.Id(), err) } - output, err := conn.DescribeClusters(ctx, input) + if _, err := waitClusterDeleted(ctx, conn, d.Id(), timeout); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for ECS Cluster (%s) delete: %s", d.Id(), err) + } - // Some partitions (e.g. ISO) may not support tagging. - if errs.IsUnsupportedOperationInPartitionError(partition, err) { - input.Include = []awstypes.ClusterField{awstypes.ClusterFieldConfigurations, awstypes.ClusterFieldSettings} + return diags +} + +func resourceClusterImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + d.Set(names.AttrName, d.Id()) + d.SetId(arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Region: meta.(*conns.AWSClient).Region, + AccountID: meta.(*conns.AWSClient).AccountID, + Service: "ecs", + Resource: "cluster/" + d.Id(), + }.String()) - output, err = conn.DescribeClusters(ctx, input) + return []*schema.ResourceData{d}, nil +} + +func retryClusterCreate(ctx context.Context, conn *ecs.Client, input *ecs.CreateClusterInput) (*ecs.CreateClusterOutput, error) { + outputRaw, err := tfresource.RetryWhenIsAErrorMessageContains[*awstypes.InvalidParameterException](ctx, propagationTimeout, func() (interface{}, error) { + return conn.CreateCluster(ctx, input) + }, "Unable to assume the service linked role") + + if err != nil { + return nil, err } - // Some partitions (e.g. ISO) may not support describe including configuration. - if errs.IsUnsupportedOperationInPartitionError(partition, err) { - input.Include = []awstypes.ClusterField{awstypes.ClusterFieldSettings} - output, err = conn.DescribeClusters(ctx, input) + return outputRaw.(*ecs.CreateClusterOutput), nil +} + +func findCluster(ctx context.Context, conn *ecs.Client, input *ecs.DescribeClustersInput) (*awstypes.Cluster, error) { + output, err := findClusters(ctx, conn, input) + + if err != nil { + return nil, err } + return tfresource.AssertSingleValueResult(output) +} + +func findClusters(ctx context.Context, conn *ecs.Client, input *ecs.DescribeClustersInput) ([]awstypes.Cluster, error) { + output, err := conn.DescribeClusters(ctx, input) + if errs.IsA[*awstypes.ClusterNotFoundException](err) { return nil, &retry.NotFoundError{ LastError: err, @@ -347,33 +378,53 @@ func FindClusterByNameOrARN(ctx context.Context, conn *ecs.Client, partition str return nil, err } - if output == nil || len(output.Clusters) == 0 { + if output == nil { return nil, tfresource.NewEmptyResultError(input) } - if count := len(output.Clusters); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) + return output.Clusters, nil +} + +func findClusterByNameOrARN(ctx context.Context, conn *ecs.Client, nameOrARN string) (*awstypes.Cluster, error) { + input := &ecs.DescribeClustersInput{ + Clusters: []string{nameOrARN}, + Include: []awstypes.ClusterField{awstypes.ClusterFieldTags, awstypes.ClusterFieldConfigurations, awstypes.ClusterFieldSettings}, + } + + output, err := findCluster(ctx, conn, input) + + // Some partitions (e.g. ISO) may not support tagging. + partition := partitionFromConn(conn) + if errs.IsUnsupportedOperationInPartitionError(partition, err) { + input.Include = []awstypes.ClusterField{awstypes.ClusterFieldConfigurations, awstypes.ClusterFieldSettings} + + output, err = findCluster(ctx, conn, input) + } + + // Some partitions (e.g. ISO) may not support describe including configuration. + if errs.IsUnsupportedOperationInPartitionError(partition, err) { + input.Include = []awstypes.ClusterField{awstypes.ClusterFieldSettings} + + output, err = findCluster(ctx, conn, input) + } + + if err != nil { + return nil, err } - if status := aws.ToString(output.Clusters[0].Status); status == clusterStatusInactive { + if status := aws.ToString(output.Status); status == clusterStatusInactive { return nil, &retry.NotFoundError{ Message: status, LastRequest: input, } } - return tfresource.AssertFirstValueResult(output.Clusters) + return output, nil } -func statusCluster(ctx context.Context, conn *ecs.Client, clusterArn string) retry.StateRefreshFunc { +func statusCluster(ctx context.Context, conn *ecs.Client, arn string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - parsedArn, err := arn.Parse(clusterArn) - - if err != nil { - return nil, "", err - } - - cluster, err := FindClusterByNameOrARN(ctx, conn, parsedArn.Partition, clusterArn) + cluster, err := findClusterByNameOrARN(ctx, conn, arn) if tfresource.NotFound(err) { return nil, "", nil @@ -388,12 +439,15 @@ func statusCluster(ctx context.Context, conn *ecs.Client, clusterArn string) ret } func waitClusterAvailable(ctx context.Context, conn *ecs.Client, arn string) (*awstypes.Cluster, error) { //nolint:unparam + const ( + timeout = 10 * time.Minute + ) stateConf := &retry.StateChangeConf{ Pending: []string{clusterStatusProvisioning}, Target: []string{clusterStatusActive}, Refresh: statusCluster(ctx, conn, arn), - Timeout: clusterAvailableTimeout, - Delay: clusterAvailableDelay, + Timeout: timeout, + Delay: 10 * time.Second, } outputRaw, err := stateConf.WaitForStateContext(ctx) @@ -405,12 +459,12 @@ func waitClusterAvailable(ctx context.Context, conn *ecs.Client, arn string) (*a return nil, err } -func waitClusterDeleted(ctx context.Context, conn *ecs.Client, arn string) (*awstypes.Cluster, error) { +func waitClusterDeleted(ctx context.Context, conn *ecs.Client, arn string, timeout time.Duration) (*awstypes.Cluster, error) { stateConf := &retry.StateChangeConf{ Pending: []string{clusterStatusActive, clusterStatusDeprovisioning}, Target: []string{}, Refresh: statusCluster(ctx, conn, arn), - Timeout: clusterDeleteTimeout, + Timeout: timeout, } outputRaw, err := stateConf.WaitForStateContext(ctx) @@ -422,72 +476,26 @@ func waitClusterDeleted(ctx context.Context, conn *ecs.Client, arn string) (*aws return nil, err } -func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSClient(ctx) - - log.Printf("[DEBUG] Deleting ECS Cluster: %s", d.Id()) - _, err := tfresource.RetryWhenIsOneOf4[*awstypes.ClusterContainsContainerInstancesException, *awstypes.ClusterContainsServicesException, *awstypes.ClusterContainsTasksException, *awstypes.UpdateInProgressException](ctx, clusterDeleteTimeout, func() (interface{}, error) { - return conn.DeleteCluster(ctx, &ecs.DeleteClusterInput{ - Cluster: aws.String(d.Id()), - }) - }) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting ECS Cluster (%s): %s", d.Id(), err) - } - - if _, err := waitClusterDeleted(ctx, conn, d.Id()); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for ECS Cluster (%s) delete: %s", d.Id(), err) - } - - return diags -} - -func retryClusterCreate(ctx context.Context, conn *ecs.Client, input *ecs.CreateClusterInput) (*ecs.CreateClusterOutput, error) { - var output *ecs.CreateClusterOutput - err := retry.RetryContext(ctx, propagationTimeout, func() *retry.RetryError { - var err error - output, err = conn.CreateCluster(ctx, input) - - if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "Unable to assume the service linked role") { - return retry.RetryableError(err) - } - - if err != nil { - return retry.NonRetryableError(err) - } - +func expandClusterSettings(tfSet *schema.Set) []awstypes.ClusterSetting { + tfList := tfSet.List() + if len(tfList) == 0 { return nil - }) - - if tfresource.TimedOut(err) { - output, err = conn.CreateCluster(ctx, input) } - return output, err -} + apiObjects := make([]awstypes.ClusterSetting, 0) -func expandClusterSettings(configured *schema.Set) []awstypes.ClusterSetting { - list := configured.List() - if len(list) == 0 { - return nil - } + for _, tfMapRaw := range tfList { + tfMap := tfMapRaw.(map[string]interface{}) - settings := make([]awstypes.ClusterSetting, 0, len(list)) - - for _, raw := range list { - data := raw.(map[string]interface{}) - - setting := awstypes.ClusterSetting{ - Name: awstypes.ClusterSettingName(data[names.AttrName].(string)), - Value: aws.String(data[names.AttrValue].(string)), + apiObject := awstypes.ClusterSetting{ + Name: awstypes.ClusterSettingName(tfMap[names.AttrName].(string)), + Value: aws.String(tfMap[names.AttrValue].(string)), } - settings = append(settings, setting) + apiObjects = append(apiObjects, apiObject) } - return settings + return apiObjects } func expandClusterServiceConnectDefaultsRequest(tfMap map[string]interface{}) *awstypes.ClusterServiceConnectDefaultsRequest { @@ -518,21 +526,23 @@ func flattenClusterServiceConnectDefaults(apiObject *awstypes.ClusterServiceConn return tfMap } -func flattenClusterSettings(list []awstypes.ClusterSetting) []map[string]interface{} { - if len(list) == 0 { +func flattenClusterSettings(apiObjects []awstypes.ClusterSetting) []interface{} { + if len(apiObjects) == 0 { return nil } - result := make([]map[string]interface{}, 0, len(list)) - for _, setting := range list { - l := map[string]interface{}{ - names.AttrName: string(setting.Name), - names.AttrValue: aws.ToString(setting.Value), + tfList := make([]interface{}, 0, len(apiObjects)) + + for _, apiObject := range apiObjects { + tfMap := map[string]interface{}{ + names.AttrName: string(apiObject.Name), + names.AttrValue: aws.ToString(apiObject.Value), } - result = append(result, l) + tfList = append(tfList, tfMap) } - return result + + return tfList } func flattenClusterConfiguration(apiObject *awstypes.ClusterConfiguration) []interface{} { @@ -616,75 +626,77 @@ func flattenManagedStorageConfiguration(apiObject *awstypes.ManagedStorageConfig return []interface{}{tfMap} } -func expandClusterConfiguration(nc []interface{}) *awstypes.ClusterConfiguration { - if len(nc) == 0 || nc[0] == nil { +func expandClusterConfiguration(tfList []interface{}) *awstypes.ClusterConfiguration { + if len(tfList) == 0 || tfList[0] == nil { return &awstypes.ClusterConfiguration{} } - raw := nc[0].(map[string]interface{}) - config := &awstypes.ClusterConfiguration{} - if v, ok := raw["execute_command_configuration"].([]interface{}); ok && len(v) > 0 { - config.ExecuteCommandConfiguration = expandClusterConfigurationExecuteCommandConfiguration(v) + tfMap := tfList[0].(map[string]interface{}) + apiObject := &awstypes.ClusterConfiguration{} + + if v, ok := tfMap["execute_command_configuration"].([]interface{}); ok && len(v) > 0 { + apiObject.ExecuteCommandConfiguration = expandClusterConfigurationExecuteCommandConfiguration(v) } - if v, ok := raw["managed_storage_configuration"].([]interface{}); ok && len(v) > 0 { - config.ManagedStorageConfiguration = expandManagedStorageConfiguration(v) + if v, ok := tfMap["managed_storage_configuration"].([]interface{}); ok && len(v) > 0 { + apiObject.ManagedStorageConfiguration = expandManagedStorageConfiguration(v) } - return config + return apiObject } -func expandClusterConfigurationExecuteCommandConfiguration(nc []interface{}) *awstypes.ExecuteCommandConfiguration { - if len(nc) == 0 || nc[0] == nil { +func expandClusterConfigurationExecuteCommandConfiguration(tfList []interface{}) *awstypes.ExecuteCommandConfiguration { + if len(tfList) == 0 || tfList[0] == nil { return &awstypes.ExecuteCommandConfiguration{} } - raw := nc[0].(map[string]interface{}) - config := &awstypes.ExecuteCommandConfiguration{} - if v, ok := raw["log_configuration"].([]interface{}); ok && len(v) > 0 { - config.LogConfiguration = expandClusterConfigurationExecuteCommandLogConfiguration(v) + tfMap := tfList[0].(map[string]interface{}) + apiObject := &awstypes.ExecuteCommandConfiguration{} + + if v, ok := tfMap["log_configuration"].([]interface{}); ok && len(v) > 0 { + apiObject.LogConfiguration = expandClusterConfigurationExecuteCommandLogConfiguration(v) } - if v, ok := raw[names.AttrKMSKeyID].(string); ok && v != "" { - config.KmsKeyId = aws.String(v) + if v, ok := tfMap[names.AttrKMSKeyID].(string); ok && v != "" { + apiObject.KmsKeyId = aws.String(v) } - if v, ok := raw["logging"].(string); ok && v != "" { - config.Logging = awstypes.ExecuteCommandLogging(v) + if v, ok := tfMap["logging"].(string); ok && v != "" { + apiObject.Logging = awstypes.ExecuteCommandLogging(v) } - return config + return apiObject } -func expandClusterConfigurationExecuteCommandLogConfiguration(nc []interface{}) *awstypes.ExecuteCommandLogConfiguration { - if len(nc) == 0 || nc[0] == nil { +func expandClusterConfigurationExecuteCommandLogConfiguration(tfList []interface{}) *awstypes.ExecuteCommandLogConfiguration { + if len(tfList) == 0 || tfList[0] == nil { return &awstypes.ExecuteCommandLogConfiguration{} } - raw := nc[0].(map[string]interface{}) - config := &awstypes.ExecuteCommandLogConfiguration{} + tfMap := tfList[0].(map[string]interface{}) + apiObject := &awstypes.ExecuteCommandLogConfiguration{} - if v, ok := raw["cloud_watch_log_group_name"].(string); ok && v != "" { - config.CloudWatchLogGroupName = aws.String(v) + if v, ok := tfMap["cloud_watch_log_group_name"].(string); ok && v != "" { + apiObject.CloudWatchLogGroupName = aws.String(v) } - if v, ok := raw[names.AttrS3BucketName].(string); ok && v != "" { - config.S3BucketName = aws.String(v) + if v, ok := tfMap[names.AttrS3BucketName].(string); ok && v != "" { + apiObject.S3BucketName = aws.String(v) } - if v, ok := raw[names.AttrS3KeyPrefix].(string); ok && v != "" { - config.S3KeyPrefix = aws.String(v) + if v, ok := tfMap[names.AttrS3KeyPrefix].(string); ok && v != "" { + apiObject.S3KeyPrefix = aws.String(v) } - if v, ok := raw["cloud_watch_encryption_enabled"].(bool); ok { - config.CloudWatchEncryptionEnabled = v + if v, ok := tfMap["cloud_watch_encryption_enabled"].(bool); ok { + apiObject.CloudWatchEncryptionEnabled = v } - if v, ok := raw["s3_bucket_encryption_enabled"].(bool); ok { - config.S3EncryptionEnabled = v + if v, ok := tfMap["s3_bucket_encryption_enabled"].(bool); ok { + apiObject.S3EncryptionEnabled = v } - return config + return apiObject } func expandManagedStorageConfiguration(tfList []interface{}) *awstypes.ManagedStorageConfiguration { diff --git a/internal/service/ecs/cluster_capacity_providers.go b/internal/service/ecs/cluster_capacity_providers.go index 050087df3ba..97528abe77b 100644 --- a/internal/service/ecs/cluster_capacity_providers.go +++ b/internal/service/ecs/cluster_capacity_providers.go @@ -111,9 +111,8 @@ func resourceClusterCapacityProvidersPut(ctx context.Context, d *schema.Resource func resourceClusterCapacityProvidersRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition - cluster, err := FindClusterByNameOrARN(ctx, conn, partition, d.Id()) + cluster, err := findClusterByNameOrARN(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { sdkdiag.AppendErrorf(diags, "[WARN] ECS Cluster Capacity Providers (%s) not found, removing from state", d.Id()) diff --git a/internal/service/ecs/cluster_data_source.go b/internal/service/ecs/cluster_data_source.go index 2acb1b74d86..b025e72d63a 100644 --- a/internal/service/ecs/cluster_data_source.go +++ b/internal/service/ecs/cluster_data_source.go @@ -80,14 +80,11 @@ func DataSourceCluster() *schema.Resource { func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig clusterName := d.Get(names.AttrClusterName).(string) - cluster, err := FindClusterByNameOrARN(ctx, conn, partition, d.Get(names.AttrClusterName).(string)) + cluster, err := findClusterByNameOrARN(ctx, conn, d.Get(names.AttrClusterName).(string)) if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS Cluster (%s): %s", clusterName, err) diff --git a/internal/service/ecs/cluster_test.go b/internal/service/ecs/cluster_test.go index 5dbc4632fa4..b7fdea00fe6 100644 --- a/internal/service/ecs/cluster_test.go +++ b/internal/service/ecs/cluster_test.go @@ -316,14 +316,13 @@ func TestAccECSCluster_managedStorageConfiguration(t *testing.T) { func testAccCheckClusterDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) - partition := acctest.Provider.Meta().(*conns.AWSClient).Partition for _, rs := range s.RootModule().Resources { if rs.Type != "aws_ecs_cluster" { continue } - _, err := tfecs.FindClusterByNameOrARN(ctx, conn, partition, rs.Primary.ID) + _, err := tfecs.FindClusterByNameOrARN(ctx, conn, rs.Primary.ID) if tfresource.NotFound(err) { continue @@ -347,14 +346,9 @@ func testAccCheckClusterExists(ctx context.Context, n string, v *awstypes.Cluste return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No ECS Cluster ID is set") - } - conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) - partition := acctest.Provider.Meta().(*conns.AWSClient).Partition - output, err := tfecs.FindClusterByNameOrARN(ctx, conn, partition, rs.Primary.ID) + output, err := tfecs.FindClusterByNameOrARN(ctx, conn, rs.Primary.ID) if err != nil { return err diff --git a/internal/service/ecs/exports_test.go b/internal/service/ecs/exports_test.go index 1af369902d1..da3a65268ab 100644 --- a/internal/service/ecs/exports_test.go +++ b/internal/service/ecs/exports_test.go @@ -7,10 +7,12 @@ package ecs var ( ResourceAccountSettingDefault = resourceAccountSettingDefault ResourceCapacityProvider = resourceCapacityProvider + ResourceCluster = resourceCluster ResourceClusterCapacityProviders = resourceClusterCapacityProviders ResourceTag = resourceTag FindCapacityProviderByARN = findCapacityProviderByARN + FindClusterByNameOrARN = findClusterByNameOrARN FindEffectiveAccountSettingByName = findEffectiveAccountSettingByName FindTag = findTag ValidTaskDefinitionContainerDefinitions = validTaskDefinitionContainerDefinitions diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index 2139f97c0bd..ac9988c5d4c 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -65,7 +65,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceCluster, + Factory: resourceCluster, TypeName: "aws_ecs_cluster", Name: "Cluster", Tags: &types.ServicePackageResourceTags{ diff --git a/internal/service/ecs/sweep.go b/internal/service/ecs/sweep.go index 7ee9c3bbf17..efbf1cda03f 100644 --- a/internal/service/ecs/sweep.go +++ b/internal/service/ecs/sweep.go @@ -124,7 +124,7 @@ func sweepClusters(region string) error { } for _, v := range page.ClusterArns { - r := ResourceCluster() + r := resourceCluster() d := r.Data(nil) d.SetId(v) diff --git a/internal/service/ecs/wait.go b/internal/service/ecs/wait.go index d31b1c4d44c..a9fc179f17a 100644 --- a/internal/service/ecs/wait.go +++ b/internal/service/ecs/wait.go @@ -20,11 +20,6 @@ const ( serviceDescribeTimeout = 2 * time.Minute serviceUpdateTimeout = 2 * time.Minute - clusterAvailableDelay = 10 * time.Second - clusterAvailableTimeout = 10 * time.Minute - clusterDeleteTimeout = 10 * time.Minute - clusterReadTimeout = 2 * time.Second - taskSetCreateTimeout = 10 * time.Minute taskSetDeleteTimeout = 10 * time.Minute ) From 40d7c91ebe5354559d4406545f434bf158f8807b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 12:32:45 -0400 Subject: [PATCH 43/60] d/aws_ecs_cluster: Reduce visibility. --- internal/service/ecs/cluster_data_source.go | 26 +++++++++------------ internal/service/ecs/service_package_gen.go | 4 +++- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/internal/service/ecs/cluster_data_source.go b/internal/service/ecs/cluster_data_source.go index b025e72d63a..84b8c5476fd 100644 --- a/internal/service/ecs/cluster_data_source.go +++ b/internal/service/ecs/cluster_data_source.go @@ -15,8 +15,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_ecs_cluster") -func DataSourceCluster() *schema.Resource { +// @SDKDataSource("aws_ecs_cluster", name="Cluster") +// @Tags +func dataSourceCluster() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceClusterRead, @@ -81,27 +82,20 @@ func DataSourceCluster() *schema.Resource { func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig clusterName := d.Get(names.AttrClusterName).(string) - cluster, err := findClusterByNameOrARN(ctx, conn, d.Get(names.AttrClusterName).(string)) + cluster, err := findClusterByNameOrARN(ctx, conn, clusterName) if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS Cluster (%s): %s", clusterName, err) } - d.SetId(aws.ToString(cluster.ClusterArn)) - d.Set(names.AttrARN, cluster.ClusterArn) + arn := aws.ToString(cluster.ClusterArn) + d.SetId(arn) + d.Set(names.AttrARN, arn) d.Set("pending_tasks_count", cluster.PendingTasksCount) - d.Set("running_tasks_count", cluster.RunningTasksCount) d.Set("registered_container_instances_count", cluster.RegisteredContainerInstancesCount) - d.Set(names.AttrStatus, cluster.Status) - - tags := KeyValueTags(ctx, cluster.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) - if err := d.Set(names.AttrTags, tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return sdkdiag.AppendErrorf(diags, "setting tags: %s", err) - } - + d.Set("running_tasks_count", cluster.RunningTasksCount) if cluster.ServiceConnectDefaults != nil { if err := d.Set("service_connect_defaults", []interface{}{flattenClusterServiceConnectDefaults(cluster.ServiceConnectDefaults)}); err != nil { return sdkdiag.AppendErrorf(diags, "setting service_connect_defaults: %s", err) @@ -109,10 +103,12 @@ func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta int } else { d.Set("service_connect_defaults", nil) } - if err := d.Set("setting", flattenClusterSettings(cluster.Settings)); err != nil { return sdkdiag.AppendErrorf(diags, "setting setting: %s", err) } + d.Set(names.AttrStatus, cluster.Status) + + setTagsOut(ctx, cluster.Tags) return diags } diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index ac9988c5d4c..181b34104e5 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -25,8 +25,10 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { return []*types.ServicePackageSDKDataSource{ { - Factory: DataSourceCluster, + Factory: dataSourceCluster, TypeName: "aws_ecs_cluster", + Name: "Cluster", + Tags: &types.ServicePackageResourceTags{}, }, { Factory: DataSourceContainerDefinition, From 27f061815781766b715a125959ae8793633f1bb2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 13:58:56 -0400 Subject: [PATCH 44/60] d/aws_ecs_container_definition: Reduce visibility. --- .../ecs/container_definition_data_source.go | 112 +++++++++--------- internal/service/ecs/service_package_gen.go | 3 +- internal/service/ecs/task_definition.go | 15 +++ 3 files changed, 72 insertions(+), 58 deletions(-) diff --git a/internal/service/ecs/container_definition_data_source.go b/internal/service/ecs/container_definition_data_source.go index a055204390a..ef9c97524f6 100644 --- a/internal/service/ecs/container_definition_data_source.go +++ b/internal/service/ecs/container_definition_data_source.go @@ -6,53 +6,34 @@ package ecs import ( "context" "fmt" - "log" "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecs" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_ecs_container_definition") -func DataSourceContainerDefinition() *schema.Resource { +// @SDKDataSource("aws_ecs_container_definition", name="Container Definition") +func dataSourceContainerDefinition() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceContainerDefinitionRead, Schema: map[string]*schema.Schema{ - "task_definition": { - Type: schema.TypeString, - Required: true, - }, "container_name": { Type: schema.TypeString, Required: true, }, - // Computed values. - "image": { - Type: schema.TypeString, - Computed: true, - }, - "image_digest": { - Type: schema.TypeString, - Computed: true, - }, "cpu": { Type: schema.TypeInt, Computed: true, }, - "memory": { - Type: schema.TypeInt, - Computed: true, - }, - "memory_reservation": { - Type: schema.TypeInt, - Computed: true, - }, "disable_networking": { Type: schema.TypeBool, Computed: true, @@ -67,6 +48,26 @@ func DataSourceContainerDefinition() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "image": { + Type: schema.TypeString, + Computed: true, + }, + "image_digest": { + Type: schema.TypeString, + Computed: true, + }, + "memory": { + Type: schema.TypeInt, + Computed: true, + }, + "memory_reservation": { + Type: schema.TypeInt, + Computed: true, + }, + "task_definition": { + Type: schema.TypeString, + Required: true, + }, }, } } @@ -75,48 +76,45 @@ func dataSourceContainerDefinitionRead(ctx context.Context, d *schema.ResourceDa var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - params := &ecs.DescribeTaskDefinitionInput{ - TaskDefinition: aws.String(d.Get("task_definition").(string)), - } - log.Printf("[DEBUG] Reading ECS Container Definition: %+v", params) - desc, err := conn.DescribeTaskDefinition(ctx, params) + taskDefinition := d.Get("task_definition").(string) + def, err := findContainerDefinitionByTwoPartKey(ctx, conn, taskDefinition, d.Get("container_name").(string)) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition: %s", err) + return sdkdiag.AppendFromErr(diags, tfresource.SingularDataSourceFindError("ECS Container Definition", err)) } - if desc == nil || desc.TaskDefinition == nil { - return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition: empty response") + d.SetId(fmt.Sprintf("%s/%s", taskDefinition, aws.ToString(def.Name))) + d.Set("cpu", def.Cpu) + d.Set("disable_networking", def.DisableNetworking) + d.Set("docker_labels", def.DockerLabels) + var environment = map[string]string{} + for _, v := range def.Environment { + environment[aws.ToString(v.Name)] = aws.ToString(v.Value) } + d.Set(names.AttrEnvironment, environment) + image := aws.ToString(def.Image) + d.Set("image", image) + if strings.Contains(image, ":") { + d.Set("image_digest", strings.Split(image, ":")[1]) + } + d.Set("memory", def.Memory) + d.Set("memory_reservation", def.MemoryReservation) - taskDefinition := desc.TaskDefinition - for _, def := range taskDefinition.ContainerDefinitions { - if aws.ToString(def.Name) != d.Get("container_name").(string) { - continue - } - - d.SetId(fmt.Sprintf("%s/%s", aws.ToString(taskDefinition.TaskDefinitionArn), d.Get("container_name").(string))) - d.Set("image", def.Image) - image := aws.ToString(def.Image) - if strings.Contains(image, ":") { - d.Set("image_digest", strings.Split(image, ":")[1]) - } - d.Set("cpu", def.Cpu) - d.Set("memory", def.Memory) - d.Set("memory_reservation", def.MemoryReservation) - d.Set("disable_networking", def.DisableNetworking) - d.Set("docker_labels", def.DockerLabels) + return diags +} - var environment = map[string]string{} - for _, keyValuePair := range def.Environment { - environment[aws.ToString(keyValuePair.Name)] = aws.ToString(keyValuePair.Value) - } - d.Set(names.AttrEnvironment, environment) +func findContainerDefinitionByTwoPartKey(ctx context.Context, conn *ecs.Client, taskDefinitionName, containerName string) (*awstypes.ContainerDefinition, error) { + input := &ecs.DescribeTaskDefinitionInput{ + TaskDefinition: aws.String(taskDefinitionName), } - if d.Id() == "" { - return sdkdiag.AppendErrorf(diags, "container with name %q not found in task definition %q", d.Get("container_name").(string), d.Get("task_definition").(string)) + taskDefinition, err := findTaskDefinition(ctx, conn, input) + + if err != nil { + return nil, err } - return diags + return tfresource.AssertSingleValueResult(tfslices.Filter(taskDefinition.ContainerDefinitions, func(v awstypes.ContainerDefinition) bool { + return aws.ToString(v.Name) == containerName + })) } diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index 181b34104e5..c3678ce1ca8 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -31,8 +31,9 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac Tags: &types.ServicePackageResourceTags{}, }, { - Factory: DataSourceContainerDefinition, + Factory: dataSourceContainerDefinition, TypeName: "aws_ecs_container_definition", + Name: "Container Definition", }, { Factory: dataSourceService, diff --git a/internal/service/ecs/task_definition.go b/internal/service/ecs/task_definition.go index e3104cb2c28..baaed4ab60b 100644 --- a/internal/service/ecs/task_definition.go +++ b/internal/service/ecs/task_definition.go @@ -27,6 +27,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/flex" tfjson "github.com/hashicorp/terraform-provider-aws/internal/json" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -702,6 +703,20 @@ func resourceTaskDefinitionDelete(ctx context.Context, d *schema.ResourceData, m return diags } +func findTaskDefinition(ctx context.Context, conn *ecs.Client, input *ecs.DescribeTaskDefinitionInput) (*awstypes.TaskDefinition, error) { + output, err := conn.DescribeTaskDefinition(ctx, input) + + if err != nil { + return nil, err + } + + if output == nil || output.TaskDefinition == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.TaskDefinition, nil +} + func resourceTaskDefinitionVolumeHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) From 10e8a0afd1085773f4fb2e3c388c9c7d1614bfd0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 15:32:37 -0400 Subject: [PATCH 45/60] r/aws_ecs_service: Reduce visibility. --- .../service/ecs/cluster_capacity_providers.go | 4 +- internal/service/ecs/errors.go | 36 + internal/service/ecs/exports_test.go | 4 + internal/service/ecs/find.go | 124 ---- internal/service/ecs/flex.go | 242 +++---- internal/service/ecs/service.go | 655 +++++++++++------- internal/service/ecs/service_data_source.go | 2 +- internal/service/ecs/service_package_gen.go | 2 +- internal/service/ecs/service_test.go | 20 +- internal/service/ecs/status.go | 48 -- internal/service/ecs/sweep.go | 2 +- .../service/ecs/task_execution_data_source.go | 2 +- internal/service/ecs/task_set.go | 4 +- internal/service/ecs/wait.go | 73 -- 14 files changed, 586 insertions(+), 632 deletions(-) create mode 100644 internal/service/ecs/errors.go delete mode 100644 internal/service/ecs/find.go diff --git a/internal/service/ecs/cluster_capacity_providers.go b/internal/service/ecs/cluster_capacity_providers.go index 97528abe77b..dca15365179 100644 --- a/internal/service/ecs/cluster_capacity_providers.go +++ b/internal/service/ecs/cluster_capacity_providers.go @@ -88,7 +88,7 @@ func resourceClusterCapacityProvidersPut(ctx context.Context, d *schema.Resource input := &ecs.PutClusterCapacityProvidersInput{ CapacityProviders: flex.ExpandStringValueSet(d.Get("capacity_providers").(*schema.Set)), Cluster: aws.String(clusterName), - DefaultCapacityProviderStrategy: expandCapacityProviderStrategy(d.Get("default_capacity_provider_strategy").(*schema.Set)), + DefaultCapacityProviderStrategy: expandCapacityProviderStrategyItems(d.Get("default_capacity_provider_strategy").(*schema.Set)), } err := retryClusterCapacityProvidersPut(ctx, conn, input) @@ -128,7 +128,7 @@ func resourceClusterCapacityProvidersRead(ctx context.Context, d *schema.Resourc return sdkdiag.AppendErrorf(diags, "setting capacity_providers: %s", err) } d.Set(names.AttrClusterName, cluster.ClusterName) - if err := d.Set("default_capacity_provider_strategy", flattenCapacityProviderStrategy(cluster.DefaultCapacityProviderStrategy)); err != nil { + if err := d.Set("default_capacity_provider_strategy", flattenCapacityProviderStrategyItems(cluster.DefaultCapacityProviderStrategy)); err != nil { return sdkdiag.AppendErrorf(diags, "setting default_capacity_provider_strategy: %s", err) } diff --git a/internal/service/ecs/errors.go b/internal/service/ecs/errors.go new file mode 100644 index 00000000000..cf6acffadf9 --- /dev/null +++ b/internal/service/ecs/errors.go @@ -0,0 +1,36 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ecs + +import ( + "errors" + "fmt" + + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" +) + +const ( + errCodeDependencyViolation = "DependencyViolation" +) + +func failureError(apiObject *awstypes.Failure) error { + if apiObject == nil { + return nil + } + + var err error + if reason, detail := aws.ToString(apiObject.Reason), aws.ToString(apiObject.Detail); detail == "" { + err = errors.New(reason) + } else { + err = fmt.Errorf("%s: %s", reason, detail) + } + + return fmt.Errorf("%s: %w", aws.ToString(apiObject.Arn), err) +} + +// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/api_failures_messages.html. +const ( + failureReasonMissing = "MISSING" +) diff --git a/internal/service/ecs/exports_test.go b/internal/service/ecs/exports_test.go index da3a65268ab..8a127ea52d7 100644 --- a/internal/service/ecs/exports_test.go +++ b/internal/service/ecs/exports_test.go @@ -9,11 +9,15 @@ var ( ResourceCapacityProvider = resourceCapacityProvider ResourceCluster = resourceCluster ResourceClusterCapacityProviders = resourceClusterCapacityProviders + ResourceService = resourceService ResourceTag = resourceTag + ClusterNameFromARN = clusterNameFromARN FindCapacityProviderByARN = findCapacityProviderByARN FindClusterByNameOrARN = findClusterByNameOrARN FindEffectiveAccountSettingByName = findEffectiveAccountSettingByName + FindServiceNoTagsByTwoPartKey = findServiceNoTagsByTwoPartKey FindTag = findTag + RoleNameFromARN = roleNameFromARN ValidTaskDefinitionContainerDefinitions = validTaskDefinitionContainerDefinitions ) diff --git a/internal/service/ecs/find.go b/internal/service/ecs/find.go deleted file mode 100644 index e65a3895438..00000000000 --- a/internal/service/ecs/find.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package ecs - -import ( - "context" - "fmt" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ecs" - awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/errs" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" -) - -func findServiceByTwoPartKey(ctx context.Context, conn *ecs.Client, partition, serviceName, clusterNameOrARN string) (*awstypes.Service, error) { - input := &ecs.DescribeServicesInput{ - Cluster: aws.String(clusterNameOrARN), - Include: []awstypes.ServiceField{awstypes.ServiceFieldTags}, - Services: []string{serviceName}, - } - - return findService(ctx, conn, partition, input) -} - -func FindServiceNoTagsByID(ctx context.Context, conn *ecs.Client, partition, id, cluster string) (*awstypes.Service, error) { - input := &ecs.DescribeServicesInput{ - Services: []string{id}, - } - if cluster != "" { - input.Cluster = aws.String(cluster) - } - - return findService(ctx, conn, partition, input) -} - -type expectActiveError struct { - status string -} - -func newExpectActiveError(status string) *expectActiveError { - return &expectActiveError{ - status: status, - } -} - -func (e *expectActiveError) Error() string { - return fmt.Sprintf("expected status %[1]q, was %[2]q", serviceStatusActive, e.status) -} - -func FindServiceByIDWaitForActive(ctx context.Context, conn *ecs.Client, partition, id, cluster string) (*awstypes.Service, error) { - var service *awstypes.Service - // Use the retry.RetryContext function instead of WaitForState() because we don't want the timeout error, if any - err := retry.RetryContext(ctx, serviceDescribeTimeout, func() *retry.RetryError { - var err error - service, err = findServiceByTwoPartKey(ctx, conn, partition, id, cluster) - if tfresource.NotFound(err) { - return retry.RetryableError(err) - } - if err != nil { - return retry.NonRetryableError(err) - } - - if status := aws.ToString(service.Status); status != serviceStatusActive { - return retry.RetryableError(newExpectActiveError(status)) - } - - return nil - }) - if tfresource.TimedOut(err) { - service, err = findServiceByTwoPartKey(ctx, conn, partition, id, cluster) - } - - return service, err -} - -func findService(ctx context.Context, conn *ecs.Client, partition string, input *ecs.DescribeServicesInput) (*awstypes.Service, error) { - output, err := findServices(ctx, conn, partition, input) - - if err != nil { - return nil, err - } - - return tfresource.AssertSingleValueResult(output) -} - -func findServices(ctx context.Context, conn *ecs.Client, partition string, input *ecs.DescribeServicesInput) ([]awstypes.Service, error) { - output, err := conn.DescribeServices(ctx, input) - - if errs.IsUnsupportedOperationInPartitionError(partition, err) && input.Include != nil { - input.Include = nil - output, err = conn.DescribeServices(ctx, input) - } - - // As of AWS SDK for Go v1.44.42, DescribeServices does not return the error code ecs.ErrCodeServiceNotFoundException - // Keep this here in case it ever does - if errs.IsA[*awstypes.ServiceNotFoundException](err) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - - if err != nil { - return nil, err - } - - if output == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - // When an ECS Service is not found by DescribeServices(), it will return a Failure struct with Reason = "MISSING" - for _, v := range output.Failures { - if aws.ToString(v.Reason) == "MISSING" { - return nil, &retry.NotFoundError{ - LastRequest: input, - } - } - } - - return output.Services, nil -} diff --git a/internal/service/ecs/flex.go b/internal/service/ecs/flex.go index 56b78d30255..0ffe28bc257 100644 --- a/internal/service/ecs/flex.go +++ b/internal/service/ecs/flex.go @@ -10,210 +10,214 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func expandCapacityProviderStrategy(cps *schema.Set) []awstypes.CapacityProviderStrategyItem { - list := cps.List() - results := make([]awstypes.CapacityProviderStrategyItem, 0) - for _, raw := range list { - cp := raw.(map[string]interface{}) - ps := awstypes.CapacityProviderStrategyItem{} - if val, ok := cp["base"]; ok { - ps.Base = int32(val.(int)) +func expandCapacityProviderStrategyItems(tfSet *schema.Set) []awstypes.CapacityProviderStrategyItem { + tfList := tfSet.List() + apiObjects := make([]awstypes.CapacityProviderStrategyItem, 0) + + for _, tfMapRaw := range tfList { + tfMap := tfMapRaw.(map[string]interface{}) + apiObject := awstypes.CapacityProviderStrategyItem{} + + if v, ok := tfMap["base"]; ok { + apiObject.Base = int32(v.(int)) } - if val, ok := cp[names.AttrWeight]; ok { - ps.Weight = int32(val.(int)) + + if v, ok := tfMap["capacity_provider"]; ok { + apiObject.CapacityProvider = aws.String(v.(string)) } - if val, ok := cp["capacity_provider"]; ok { - ps.CapacityProvider = aws.String(val.(string)) + + if v, ok := tfMap[names.AttrWeight]; ok { + apiObject.Weight = int32(v.(int)) } - results = append(results, ps) + apiObjects = append(apiObjects, apiObject) } - return results + + return apiObjects } -func flattenCapacityProviderStrategy(cps []awstypes.CapacityProviderStrategyItem) []map[string]interface{} { - if cps == nil { +func flattenCapacityProviderStrategyItems(apiObjects []awstypes.CapacityProviderStrategyItem) []interface{} { + if apiObjects == nil { return nil } - results := make([]map[string]interface{}, 0) - for _, cp := range cps { - s := make(map[string]interface{}) - s["capacity_provider"] = aws.ToString(cp.CapacityProvider) - s[names.AttrWeight] = cp.Weight - s["base"] = cp.Base - results = append(results, s) + + tfList := make([]interface{}, 0) + + for _, apiObject := range apiObjects { + tfMap := make(map[string]interface{}) + + tfMap["base"] = apiObject.Base + tfMap["capacity_provider"] = aws.ToString(apiObject.CapacityProvider) + tfMap[names.AttrWeight] = apiObject.Weight + + tfList = append(tfList, tfMap) } - return results + + return tfList } -// Takes the result of flatmap. Expand for an array of load balancers and -// returns ecs.LoadBalancer compatible objects -func expandLoadBalancers(configured []interface{}) []awstypes.LoadBalancer { - loadBalancers := make([]awstypes.LoadBalancer, 0, len(configured)) +func expandLoadBalancers(tfList []interface{}) []awstypes.LoadBalancer { + apiObjects := make([]awstypes.LoadBalancer, 0, len(tfList)) - // Loop over our configured load balancers and create - // an array of aws-sdk-go compatible objects - for _, lRaw := range configured { - data := lRaw.(map[string]interface{}) + for _, tfMapRaw := range tfList { + tfMap := tfMapRaw.(map[string]interface{}) - l := awstypes.LoadBalancer{ - ContainerName: aws.String(data["container_name"].(string)), - ContainerPort: aws.Int32(int32(data["container_port"].(int))), + apiObject := awstypes.LoadBalancer{ + ContainerName: aws.String(tfMap["container_name"].(string)), + ContainerPort: aws.Int32(int32(tfMap["container_port"].(int))), } - if v, ok := data["elb_name"]; ok && v.(string) != "" { - l.LoadBalancerName = aws.String(v.(string)) + if v, ok := tfMap["elb_name"]; ok && v.(string) != "" { + apiObject.LoadBalancerName = aws.String(v.(string)) } - if v, ok := data["target_group_arn"]; ok && v.(string) != "" { - l.TargetGroupArn = aws.String(v.(string)) + + if v, ok := tfMap["target_group_arn"]; ok && v.(string) != "" { + apiObject.TargetGroupArn = aws.String(v.(string)) } - loadBalancers = append(loadBalancers, l) + apiObjects = append(apiObjects, apiObject) } - return loadBalancers + return apiObjects } -// Flattens an array of ECS LoadBalancers into a []map[string]interface{} -func flattenLoadBalancers(list []awstypes.LoadBalancer) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(list)) - for _, loadBalancer := range list { - l := map[string]interface{}{ - "container_name": *loadBalancer.ContainerName, - "container_port": *loadBalancer.ContainerPort, +func flattenLoadBalancers(apiObjects []awstypes.LoadBalancer) []interface{} { + tfList := make([]interface{}, 0, len(apiObjects)) + + for _, apiObject := range apiObjects { + tfMap := map[string]interface{}{ + "container_name": aws.ToString(apiObject.ContainerName), + "container_port": aws.ToInt32(apiObject.ContainerPort), } - if loadBalancer.LoadBalancerName != nil { - l["elb_name"] = aws.ToString(loadBalancer.LoadBalancerName) + if apiObject.LoadBalancerName != nil { + tfMap["elb_name"] = aws.ToString(apiObject.LoadBalancerName) } - if loadBalancer.TargetGroupArn != nil { - l["target_group_arn"] = aws.ToString(loadBalancer.TargetGroupArn) + if apiObject.TargetGroupArn != nil { + tfMap["target_group_arn"] = aws.ToString(apiObject.TargetGroupArn) } - result = append(result, l) + tfList = append(tfList, tfMap) } - return result + + return tfList } -// Expand for an array of load balancers and -// returns ecs.LoadBalancer compatible objects for an ECS TaskSet -func expandTaskSetLoadBalancers(l []interface{}) []awstypes.LoadBalancer { - if len(l) == 0 || l[0] == nil { +func expandTaskSetLoadBalancers(tfList []interface{}) []awstypes.LoadBalancer { + if len(tfList) == 0 || tfList[0] == nil { return nil } - loadBalancers := make([]awstypes.LoadBalancer, 0, len(l)) + apiObjects := make([]awstypes.LoadBalancer, 0, len(tfList)) - // Loop over our configured load balancers and create - // an array of aws-sdk-go compatible objects - for _, lRaw := range l { - data := lRaw.(map[string]interface{}) + for _, tfMapRaw := range tfList { + tfMap := tfMapRaw.(map[string]interface{}) - l := awstypes.LoadBalancer{} + apiObject := awstypes.LoadBalancer{} - if v, ok := data["container_name"].(string); ok && v != "" { - l.ContainerName = aws.String(v) + if v, ok := tfMap["container_name"].(string); ok && v != "" { + apiObject.ContainerName = aws.String(v) } - if v, ok := data["container_port"].(int); ok { - l.ContainerPort = aws.Int32(int32(v)) + if v, ok := tfMap["container_port"].(int); ok { + apiObject.ContainerPort = aws.Int32(int32(v)) } - if v, ok := data["load_balancer_name"]; ok && v.(string) != "" { - l.LoadBalancerName = aws.String(v.(string)) + if v, ok := tfMap["load_balancer_name"]; ok && v.(string) != "" { + apiObject.LoadBalancerName = aws.String(v.(string)) } - if v, ok := data["target_group_arn"]; ok && v.(string) != "" { - l.TargetGroupArn = aws.String(v.(string)) + + if v, ok := tfMap["target_group_arn"]; ok && v.(string) != "" { + apiObject.TargetGroupArn = aws.String(v.(string)) } - loadBalancers = append(loadBalancers, l) + apiObjects = append(apiObjects, apiObject) } - return loadBalancers + return apiObjects } -// Flattens an array of ECS LoadBalancers (of an ECS TaskSet) into a []map[string]interface{} -func flattenTaskSetLoadBalancers(list []awstypes.LoadBalancer) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(list)) - for _, loadBalancer := range list { - l := map[string]interface{}{ - "container_name": loadBalancer.ContainerName, - "container_port": loadBalancer.ContainerPort, +func flattenTaskSetLoadBalancers(apiObjects []awstypes.LoadBalancer) []interface{} { + tfList := make([]interface{}, 0, len(apiObjects)) + + for _, apiObject := range apiObjects { + tfMap := map[string]interface{}{ + "container_name": aws.ToString(apiObject.ContainerName), + "container_port": aws.ToInt32(apiObject.ContainerPort), } - if loadBalancer.LoadBalancerName != nil { - l["load_balancer_name"] = loadBalancer.LoadBalancerName + if apiObject.LoadBalancerName != nil { + tfMap["load_balancer_name"] = aws.ToString(apiObject.LoadBalancerName) } - if loadBalancer.TargetGroupArn != nil { - l["target_group_arn"] = loadBalancer.TargetGroupArn + if apiObject.TargetGroupArn != nil { + tfMap["target_group_arn"] = aws.ToString(apiObject.TargetGroupArn) } - result = append(result, l) + tfList = append(tfList, tfMap) } - return result + return tfList } -// Expand for an array of service registries and -// returns ecs.ServiceRegistry compatible objects for an ECS TaskSet -func expandServiceRegistries(l []interface{}) []awstypes.ServiceRegistry { - result := make([]awstypes.ServiceRegistry, 0, len(l)) +func expandServiceRegistries(tfList []interface{}) []awstypes.ServiceRegistry { + apiObjects := make([]awstypes.ServiceRegistry, 0, len(tfList)) - for _, v := range l { - m := v.(map[string]interface{}) - sr := awstypes.ServiceRegistry{ - RegistryArn: aws.String(m["registry_arn"].(string)), + for _, tfMapRaw := range tfList { + tfMap := tfMapRaw.(map[string]interface{}) + apiObject := awstypes.ServiceRegistry{ + RegistryArn: aws.String(tfMap["registry_arn"].(string)), } - if raw, ok := m["container_name"].(string); ok && raw != "" { - sr.ContainerName = aws.String(raw) + + if v, ok := tfMap["container_name"].(string); ok && v != "" { + apiObject.ContainerName = aws.String(v) } - if raw, ok := m["container_port"].(int); ok && raw > 0 { - sr.ContainerPort = aws.Int32(int32(raw)) + + if v, ok := tfMap["container_port"].(int); ok && v > 0 { + apiObject.ContainerPort = aws.Int32(int32(v)) } - if raw, ok := m[names.AttrPort].(int); ok && raw > 0 { - sr.Port = aws.Int32(int32(raw)) + + if v, ok := tfMap[names.AttrPort].(int); ok && v > 0 { + apiObject.Port = aws.Int32(int32(v)) } - result = append(result, sr) + + apiObjects = append(apiObjects, apiObject) } - return result + return apiObjects } -// Expand for an array of scale configurations and -// returns an ecs.Scale compatible object for an ECS TaskSet -func expandScale(l []interface{}) *awstypes.Scale { - if len(l) == 0 || l[0] == nil { +func expandScale(tfList []interface{}) *awstypes.Scale { + if len(tfList) == 0 || tfList[0] == nil { return nil } - tfMap, ok := l[0].(map[string]interface{}) + tfMap, ok := tfList[0].(map[string]interface{}) if !ok { return nil } - result := &awstypes.Scale{} + apiObject := &awstypes.Scale{} if v, ok := tfMap[names.AttrUnit].(string); ok && v != "" { - result.Unit = awstypes.ScaleUnit(v) + apiObject.Unit = awstypes.ScaleUnit(v) } if v, ok := tfMap[names.AttrValue].(float64); ok { - result.Value = v + apiObject.Value = v } - return result + return apiObject } -// Flattens an ECS Scale configuration into a []map[string]interface{} -func flattenScale(scale *awstypes.Scale) []map[string]interface{} { - if scale == nil { +func flattenScale(apiObject *awstypes.Scale) []interface{} { + if apiObject == nil { return nil } - m := make(map[string]interface{}) - m[names.AttrUnit] = string(scale.Unit) - m[names.AttrValue] = scale.Value + tfMap := make(map[string]interface{}) + tfMap[names.AttrUnit] = string(apiObject.Unit) + tfMap[names.AttrValue] = apiObject.Value - return []map[string]interface{}{m} + return []interface{}{tfMap} } diff --git a/internal/service/ecs/service.go b/internal/service/ecs/service.go index 712cb7a4400..6ffb49b2614 100644 --- a/internal/service/ecs/service.go +++ b/internal/service/ecs/service.go @@ -4,9 +4,7 @@ package ecs import ( - "bytes" "context" - "errors" "fmt" "log" "math" @@ -25,11 +23,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "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/enum" "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" + "github.com/hashicorp/terraform-provider-aws/internal/sdkv2" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -38,7 +36,7 @@ import ( // @SDKResource("aws_ecs_service", name="Service") // @Tags(identifierAttribute="id") -func ResourceService() *schema.Resource { +func resourceService() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceServiceCreate, ReadWithoutTimeout: resourceServiceRead, @@ -149,7 +147,7 @@ func ResourceService() *schema.Resource { Optional: true, Default: 200, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if d.Get("scheduling_strategy").(string) == string(awstypes.SchedulingStrategyDaemon) && new == "200" { + if awstypes.SchedulingStrategy(d.Get("scheduling_strategy").(string)) == awstypes.SchedulingStrategyDaemon && new == "200" { return true } return false @@ -160,7 +158,7 @@ func ResourceService() *schema.Resource { Optional: true, Default: 100, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if d.Get("scheduling_strategy").(string) == string(awstypes.SchedulingStrategyDaemon) && new == "100" { + if awstypes.SchedulingStrategy(d.Get("scheduling_strategy").(string)) == awstypes.SchedulingStrategyDaemon && new == "100" { return true } return false @@ -170,7 +168,7 @@ func ResourceService() *schema.Resource { Type: schema.TypeInt, Optional: true, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return d.Get("scheduling_strategy").(string) == string(awstypes.SchedulingStrategyDaemon) + return awstypes.SchedulingStrategy(d.Get("scheduling_strategy").(string)) == awstypes.SchedulingStrategyDaemon }, }, "enable_ecs_managed_tags": { @@ -230,7 +228,6 @@ func ResourceService() *schema.Resource { }, }, }, - Set: resourceLoadBalancerHash, }, names.AttrName: { Type: schema.TypeString, @@ -277,9 +274,7 @@ func ResourceService() *schema.Resource { } return value }, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return strings.EqualFold(old, new) - }, + DiffSuppressFunc: sdkv2.SuppressEquivalentStringCaseInsensitive, }, names.AttrType: { Type: schema.TypeString, @@ -316,7 +311,7 @@ func ResourceService() *schema.Resource { Type: schema.TypeString, Optional: true, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if old == "NONE" && new == "" { + if awstypes.PropagateTags(old) == awstypes.PropagateTagsNone && new == "" { return true } return false @@ -536,11 +531,6 @@ func ResourceService() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - names.AttrRoleARN: { - Type: schema.TypeString, - Required: true, - ValidateFunc: verify.ValidARN, - }, names.AttrEncrypted: { Type: schema.TypeBool, Optional: true, @@ -560,6 +550,11 @@ func ResourceService() *schema.Resource { Type: schema.TypeString, Optional: true, }, + names.AttrRoleARN: { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, + }, "size_in_gb": { Type: schema.TypeInt, Optional: true, @@ -601,16 +596,16 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int deploymentController := expandDeploymentController(d.Get("deployment_controller").([]interface{})) deploymentMinimumHealthyPercent := d.Get("deployment_minimum_healthy_percent").(int) name := d.Get(names.AttrName).(string) - schedulingStrategy := d.Get("scheduling_strategy").(string) - input := ecs.CreateServiceInput{ - CapacityProviderStrategy: expandCapacityProviderStrategy(d.Get(names.AttrCapacityProviderStrategy).(*schema.Set)), + schedulingStrategy := awstypes.SchedulingStrategy(d.Get("scheduling_strategy").(string)) + input := &ecs.CreateServiceInput{ + CapacityProviderStrategy: expandCapacityProviderStrategyItems(d.Get(names.AttrCapacityProviderStrategy).(*schema.Set)), ClientToken: aws.String(id.UniqueId()), DeploymentConfiguration: &awstypes.DeploymentConfiguration{}, DeploymentController: deploymentController, EnableECSManagedTags: d.Get("enable_ecs_managed_tags").(bool), EnableExecuteCommand: d.Get("enable_execute_command").(bool), NetworkConfiguration: expandNetworkConfiguration(d.Get(names.AttrNetworkConfiguration).([]interface{})), - SchedulingStrategy: awstypes.SchedulingStrategy(schedulingStrategy), + SchedulingStrategy: schedulingStrategy, ServiceName: aws.String(name), Tags: getTagsIn(ctx), } @@ -623,9 +618,9 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int input.Cluster = aws.String(v.(string)) } - if schedulingStrategy == string(awstypes.SchedulingStrategyDaemon) && deploymentMinimumHealthyPercent != 100 { + if schedulingStrategy == awstypes.SchedulingStrategyDaemon && deploymentMinimumHealthyPercent != 100 { input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int32(int32(deploymentMinimumHealthyPercent)) - } else if schedulingStrategy == string(awstypes.SchedulingStrategyReplica) { + } else if schedulingStrategy == awstypes.SchedulingStrategyReplica { input.DeploymentConfiguration.MaximumPercent = aws.Int32(int32(d.Get("deployment_maximum_percent").(int))) input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int32(int32(deploymentMinimumHealthyPercent)) input.DesiredCount = aws.Int32(int32(d.Get("desired_count").(int))) @@ -654,29 +649,26 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int } } - loadBalancers := expandLoadBalancers(d.Get("load_balancer").(*schema.Set).List()) - if len(loadBalancers) > 0 { - input.LoadBalancers = loadBalancers + if v := expandLoadBalancers(d.Get("load_balancer").(*schema.Set).List()); len(v) > 0 { + input.LoadBalancers = v } if v, ok := d.GetOk("ordered_placement_strategy"); ok { - ps, err := expandPlacementStrategy(v.([]interface{})) - + apiObject, err := expandPlacementStrategy(v.([]interface{})) if err != nil { return sdkdiag.AppendFromErr(diags, err) } - input.PlacementStrategy = ps + input.PlacementStrategy = apiObject } if v, ok := d.Get("placement_constraints").(*schema.Set); ok { - pc, err := expandPlacementConstraints(v.List()) - + apiObject, err := expandPlacementConstraints(v.List()) if err != nil { return sdkdiag.AppendFromErr(diags, err) } - input.PlacementConstraints = pc + input.PlacementConstraints = apiObject } if v, ok := d.GetOk("platform_version"); ok { @@ -691,44 +683,25 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int input.ServiceConnectConfiguration = expandServiceConnectConfiguration(v.([]interface{})) } - if v, ok := d.GetOk("volume_configuration"); ok && len(v.([]interface{})) > 0 { - input.VolumeConfigurations = expandVolumeConfigurations(v.([]interface{})) - } - - serviceRegistries := d.Get("service_registries").([]interface{}) - if len(serviceRegistries) > 0 { - srs := make([]awstypes.ServiceRegistry, 0, len(serviceRegistries)) - for _, v := range serviceRegistries { - raw := v.(map[string]interface{}) - sr := awstypes.ServiceRegistry{ - RegistryArn: aws.String(raw["registry_arn"].(string)), - } - if port, ok := raw[names.AttrPort].(int); ok && port != 0 { - sr.Port = aws.Int32(int32(port)) - } - if raw, ok := raw["container_port"].(int); ok && raw != 0 { - sr.ContainerPort = aws.Int32(int32(raw)) - } - if raw, ok := raw["container_name"].(string); ok && raw != "" { - sr.ContainerName = aws.String(raw) - } - - srs = append(srs, sr) - } - input.ServiceRegistries = srs + if v := d.Get("service_registries").([]interface{}); len(v) > 0 { + input.ServiceRegistries = expandServiceRegistries(v) } if v, ok := d.GetOk("task_definition"); ok { input.TaskDefinition = aws.String(v.(string)) } - output, err := serviceCreateWithRetry(ctx, conn, input) + if v, ok := d.GetOk("volume_configuration"); ok && len(v.([]interface{})) > 0 { + input.VolumeConfigurations = expandVolumeConfigurations(v.([]interface{})) + } + + output, err := retryServiceCreate(ctx, conn, input) // Some partitions (e.g. ISO) may not support tag-on-create. if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(partition, err) { input.Tags = nil - output, err = serviceCreateWithRetry(ctx, conn, input) + output, err = retryServiceCreate(ctx, conn, input) } if err != nil { @@ -741,7 +714,7 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int if d.Get("wait_for_steady_state").(bool) { fn = waitServiceStable } - if _, err := fn(ctx, conn, partition, d.Id(), d.Get("cluster").(string), d.Timeout(schema.TimeoutCreate)); err != nil { + if _, err := fn(ctx, conn, d.Id(), d.Get("cluster").(string), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for ECS Service (%s) create: %s", d.Id(), err) } @@ -765,11 +738,9 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, meta int func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition cluster := d.Get("cluster").(string) - - service, err := FindServiceByIDWaitForActive(ctx, conn, partition, d.Id(), cluster) + service, err := findServiceByTwoPartKeyWaitForActive(ctx, conn, d.Id(), cluster) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] ECS Service (%s) not found, removing from state", d.Id()) @@ -777,68 +748,20 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta inter return diags } - if errs.IsA[*awstypes.ClusterNotFoundException](err) { - log.Printf("[WARN] ECS Service (%s) parent cluster (%s) not found, removing from state.", d.Id(), cluster) - d.SetId("") - return diags - } - - var ea *expectActiveError - if errors.As(err, &ea) { - log.Printf("[WARN] ECS Service (%s) in status %q, removing from state.", d.Id(), ea.status) - d.SetId("") - return diags - } - if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS service (%s): %s", d.Id(), err) } d.SetId(aws.ToString(service.ServiceArn)) - d.Set(names.AttrName, service.ServiceName) - - // When creating a service that uses the EXTERNAL deployment controller, - // you can specify only parameters that aren't controlled at the task set level - // hence TaskDefinition will not be set by aws sdk - if service.TaskDefinition != nil { - // Save task definition in the same format - if strings.HasPrefix(d.Get("task_definition").(string), "arn:"+meta.(*conns.AWSClient).Partition+":ecs:") { - d.Set("task_definition", service.TaskDefinition) - } else { - taskDefinition := buildFamilyAndRevisionFromARN(aws.ToString(service.TaskDefinition)) - d.Set("task_definition", taskDefinition) - } + if err := d.Set(names.AttrCapacityProviderStrategy, flattenCapacityProviderStrategyItems(service.CapacityProviderStrategy)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting capacity_provider_strategy: %s", err) } - - d.Set("scheduling_strategy", service.SchedulingStrategy) - d.Set("desired_count", service.DesiredCount) - d.Set("health_check_grace_period_seconds", service.HealthCheckGracePeriodSeconds) - d.Set("launch_type", service.LaunchType) - d.Set("enable_ecs_managed_tags", service.EnableECSManagedTags) - d.Set(names.AttrPropagateTags, service.PropagateTags) - d.Set("platform_version", service.PlatformVersion) - d.Set("enable_execute_command", service.EnableExecuteCommand) - - d.Set(names.AttrTriggers, d.Get(names.AttrTriggers)) - - // Save cluster in the same format - if strings.HasPrefix(d.Get("cluster").(string), "arn:"+meta.(*conns.AWSClient).Partition+":ecs:") { + // Save cluster in the same format. + if arn.IsARN(cluster) { d.Set("cluster", service.ClusterArn) } else { - clusterARN := GetClusterNameFromARN(aws.ToString(service.ClusterArn)) - d.Set("cluster", clusterARN) - } - - // Save IAM role in the same format - if service.RoleArn != nil { - if strings.HasPrefix(d.Get("iam_role").(string), "arn:"+meta.(*conns.AWSClient).Partition+":iam:") { - d.Set("iam_role", service.RoleArn) - } else { - roleARN := GetRoleNameFromARN(aws.ToString(service.RoleArn)) - d.Set("iam_role", roleARN) - } + d.Set("cluster", clusterNameFromARN(aws.ToString(service.ClusterArn))) } - if service.DeploymentConfiguration != nil { d.Set("deployment_maximum_percent", service.DeploymentConfiguration.MaximumPercent) d.Set("deployment_minimum_healthy_percent", service.DeploymentConfiguration.MinimumHealthyPercent) @@ -859,40 +782,58 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta inter d.Set("deployment_circuit_breaker", nil) } } - if err := d.Set("deployment_controller", flattenDeploymentController(service.DeploymentController)); err != nil { return sdkdiag.AppendErrorf(diags, "setting deployment_controller: %s", err) } - + d.Set("desired_count", service.DesiredCount) + d.Set("enable_execute_command", service.EnableExecuteCommand) + d.Set("enable_ecs_managed_tags", service.EnableECSManagedTags) + d.Set("health_check_grace_period_seconds", service.HealthCheckGracePeriodSeconds) + // Save IAM role in the same format. + if service.RoleArn != nil { + if arn.IsARN(d.Get("iam_role").(string)) { + d.Set("iam_role", service.RoleArn) + } else { + d.Set("iam_role", roleNameFromARN(aws.ToString(service.RoleArn))) + } + } + d.Set("launch_type", service.LaunchType) if service.LoadBalancers != nil { if err := d.Set("load_balancer", flattenLoadBalancers(service.LoadBalancers)); err != nil { return sdkdiag.AppendErrorf(diags, "setting load_balancer: %s", err) } } - - if err := d.Set(names.AttrCapacityProviderStrategy, flattenCapacityProviderStrategy(service.CapacityProviderStrategy)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting capacity_provider_strategy: %s", err) + d.Set(names.AttrName, service.ServiceName) + if err := d.Set(names.AttrNetworkConfiguration, flattenNetworkConfiguration(service.NetworkConfiguration)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting network_configuration: %s", err) } - if err := d.Set("ordered_placement_strategy", flattenPlacementStrategy(service.PlacementStrategy)); err != nil { return sdkdiag.AppendErrorf(diags, "setting ordered_placement_strategy: %s", err) } - if err := d.Set("placement_constraints", flattenServicePlacementConstraints(service.PlacementConstraints)); err != nil { return sdkdiag.AppendErrorf(diags, "setting placement_constraints: %s", err) } - - if err := d.Set(names.AttrNetworkConfiguration, flattenNetworkConfiguration(service.NetworkConfiguration)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting network_configuration: %s", err) - } - - //if err := d.Set("service_connect_configuration", flattenServiceConnectConfiguration(service.ServiceConnectConfiguration)); err != nil { - // return sdkdiag.AppendErrorf(diags, "setting service_connect_configuration: %s", err) - //} - + d.Set("platform_version", service.PlatformVersion) + d.Set(names.AttrPropagateTags, service.PropagateTags) + d.Set("scheduling_strategy", service.SchedulingStrategy) + // if err := d.Set("service_connect_configuration", flattenServiceConnectConfiguration(service.ServiceConnectConfiguration)); err != nil { + // return sdkdiag.AppendErrorf(diags, "setting service_connect_configuration: %s", err) + // } if err := d.Set("service_registries", flattenServiceRegistries(service.ServiceRegistries)); err != nil { return sdkdiag.AppendErrorf(diags, "setting service_registries: %s", err) } + // When creating a service that uses the EXTERNAL deployment controller, + // you can specify only parameters that aren't controlled at the task set level + // hence TaskDefinition will not be set by aws sdk + if service.TaskDefinition != nil { + // Save task definition in the same format. + if arn.IsARN(d.Get("task_definition").(string)) { + d.Set("task_definition", service.TaskDefinition) + } else { + d.Set("task_definition", familyAndRevisionFromTaskDefinitionARN(aws.ToString(service.TaskDefinition))) + } + } + d.Set(names.AttrTriggers, d.Get(names.AttrTriggers)) setTagsOut(ctx, service.Tags) @@ -902,11 +843,11 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta inter func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { + cluster := d.Get("cluster").(string) input := &ecs.UpdateServiceInput{ - Cluster: aws.String(d.Get("cluster").(string)), + Cluster: aws.String(cluster), ForceNewDeployment: d.Get("force_new_deployment").(bool), Service: aws.String(d.Id()), } @@ -922,7 +863,7 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange(names.AttrCapacityProviderStrategy) { - input.CapacityProviderStrategy = expandCapacityProviderStrategy(d.Get(names.AttrCapacityProviderStrategy).(*schema.Set)) + input.CapacityProviderStrategy = expandCapacityProviderStrategyItems(d.Get(names.AttrCapacityProviderStrategy).(*schema.Set)) } if d.HasChange("deployment_circuit_breaker") { @@ -938,8 +879,8 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int } } - switch schedulingStrategy := d.Get("scheduling_strategy").(string); schedulingStrategy { - case string(awstypes.SchedulingStrategyDaemon): + switch schedulingStrategy := awstypes.SchedulingStrategy(d.Get("scheduling_strategy").(string)); schedulingStrategy { + case awstypes.SchedulingStrategyDaemon: if d.HasChange("deployment_minimum_healthy_percent") { if input.DeploymentConfiguration == nil { input.DeploymentConfiguration = &awstypes.DeploymentConfiguration{} @@ -947,7 +888,7 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int input.DeploymentConfiguration.MinimumHealthyPercent = aws.Int32(int32(d.Get("deployment_minimum_healthy_percent").(int))) } - case string(awstypes.SchedulingStrategyReplica): + case awstypes.SchedulingStrategyReplica: if d.HasChanges("deployment_maximum_percent", "deployment_minimum_healthy_percent") { if input.DeploymentConfiguration == nil { input.DeploymentConfiguration = &awstypes.DeploymentConfiguration{} @@ -990,13 +931,12 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int input.PlacementStrategy = []awstypes.PlacementStrategy{} if v, ok := d.GetOk("ordered_placement_strategy"); ok && len(v.([]interface{})) > 0 { - ps, err := expandPlacementStrategy(v.([]interface{})) - + apiObject, err := expandPlacementStrategy(v.([]interface{})) if err != nil { return sdkdiag.AppendFromErr(diags, err) } - input.PlacementStrategy = ps + input.PlacementStrategy = apiObject } } @@ -1006,13 +946,12 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int input.PlacementConstraints = []awstypes.PlacementConstraint{} if v, ok := d.Get("placement_constraints").(*schema.Set); ok && v.Len() > 0 { - pc, err := expandPlacementConstraints(v.List()) - + apiObject, err := expandPlacementConstraints(v.List()) if err != nil { return sdkdiag.AppendFromErr(diags, err) } - input.PlacementConstraints = pc + input.PlacementConstraints = apiObject } } @@ -1028,10 +967,6 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int input.ServiceConnectConfiguration = expandServiceConnectConfiguration(d.Get("service_connect_configuration").([]interface{})) } - if d.HasChange("volume_configuration") { - input.VolumeConfigurations = expandVolumeConfigurations(d.Get("volume_configuration").([]interface{})) - } - if d.HasChange("service_registries") { input.ServiceRegistries = expandServiceRegistries(d.Get("service_registries").([]interface{})) } @@ -1040,27 +975,31 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int input.TaskDefinition = aws.String(d.Get("task_definition").(string)) } - // Retry due to IAM eventual consistency - err := retry.RetryContext(ctx, propagationTimeout+serviceUpdateTimeout, func() *retry.RetryError { - _, err := conn.UpdateService(ctx, input) + if d.HasChange("volume_configuration") { + input.VolumeConfigurations = expandVolumeConfigurations(d.Get("volume_configuration").([]interface{})) + } - if err != nil { + // Retry due to IAM eventual consistency. + const ( + serviceUpdateTimeout = 2 * time.Minute + timeout = propagationTimeout + serviceUpdateTimeout + ) + _, err := tfresource.RetryWhen(ctx, timeout, + func() (interface{}, error) { + return conn.UpdateService(ctx, input) + }, + func(err error) (bool, error) { if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "verify that the ECS service role being passed has the proper permissions") { - return retry.RetryableError(err) + return true, err } if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "does not have an associated load balancer") { - return retry.RetryableError(err) + return true, err } - return retry.NonRetryableError(err) - } - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.UpdateService(ctx, input) - } + return false, err + }, + ) if err != nil { return sdkdiag.AppendErrorf(diags, "updating ECS Service (%s): %s", d.Id(), err) @@ -1070,7 +1009,7 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int if d.Get("wait_for_steady_state").(bool) { fn = waitServiceStable } - if _, err := fn(ctx, conn, partition, d.Id(), d.Get("cluster").(string), d.Timeout(schema.TimeoutUpdate)); err != nil { + if _, err := fn(ctx, conn, d.Id(), cluster, d.Timeout(schema.TimeoutUpdate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for ECS Service (%s) update: %s", d.Id(), err) } } @@ -1081,9 +1020,9 @@ func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, meta int func resourceServiceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition - service, err := FindServiceNoTagsByID(ctx, conn, partition, d.Id(), d.Get("cluster").(string)) + cluster := d.Get("cluster").(string) + service, err := findServiceNoTagsByTwoPartKey(ctx, conn, d.Id(), cluster) if tfresource.NotFound(err) { return diags @@ -1093,55 +1032,52 @@ func resourceServiceDelete(ctx context.Context, d *schema.ResourceData, meta int return sdkdiag.AppendErrorf(diags, "reading ECS Service (%s): %s", d.Id(), err) } - if aws.ToString(service.Status) == serviceStatusInactive { + status := aws.ToString(service.Status) + if status == serviceStatusInactive { return diags } - // Drain the ECS service - if aws.ToString(service.Status) != serviceStatusDraining && service.SchedulingStrategy != awstypes.SchedulingStrategyDaemon { - _, err := conn.UpdateService(ctx, &ecs.UpdateServiceInput{ - Service: aws.String(d.Id()), - Cluster: aws.String(d.Get("cluster").(string)), + // Drain the ECS service. + if status != serviceStatusDraining && service.SchedulingStrategy != awstypes.SchedulingStrategyDaemon { + input := &ecs.UpdateServiceInput{ + Cluster: aws.String(cluster), DesiredCount: aws.Int32(0), - }) + Service: aws.String(d.Id()), + } + + _, err := conn.UpdateService(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "draining ECS Service (%s): %s", d.Id(), err) } } - input := ecs.DeleteServiceInput{ - Service: aws.String(d.Id()), - Cluster: aws.String(d.Get("cluster").(string)), - } log.Printf("[DEBUG] Deleting ECS Service: %s", d.Id()) - err = retry.RetryContext(ctx, d.Timeout(schema.TimeoutDelete), func() *retry.RetryError { - _, err := conn.DeleteService(ctx, &input) - - if err != nil { + _, err = tfresource.RetryWhen(ctx, d.Timeout(schema.TimeoutDelete), + func() (interface{}, error) { + return conn.DeleteService(ctx, &ecs.DeleteServiceInput{ + Cluster: aws.String(cluster), + Service: aws.String(d.Id()), + }) + }, + func(err error) (bool, error) { if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "The service cannot be stopped while deployments are active.") { - return retry.RetryableError(err) + return true, err } - if tfawserr.ErrMessageContains(err, "DependencyViolation", "has a dependent object") { - return retry.RetryableError(err) + if tfawserr.ErrMessageContains(err, errCodeDependencyViolation, "has a dependent object") { + return true, err } - return retry.NonRetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.DeleteService(ctx, &input) - } + return false, err + }, + ) if err != nil { return sdkdiag.AppendErrorf(diags, "deleting ECS Service (%s): %s", d.Id(), err) } - if err := waitServiceInactive(ctx, conn, partition, d.Id(), d.Get("cluster").(string), d.Timeout(schema.TimeoutDelete)); err != nil { + if _, err := waitServiceInactive(ctx, conn, d.Id(), cluster, d.Timeout(schema.TimeoutDelete)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for ECS Service (%s) delete: %s", d.Id(), err) } @@ -1168,6 +1104,278 @@ func resourceServiceImport(ctx context.Context, d *schema.ResourceData, meta int return []*schema.ResourceData{d}, nil } +func retryServiceCreate(ctx context.Context, conn *ecs.Client, input *ecs.CreateServiceInput) (*ecs.CreateServiceOutput, error) { + const ( + serviceCreateTimeout = 2 * time.Minute + timeout = propagationTimeout + serviceCreateTimeout + ) + outputRaw, err := tfresource.RetryWhen(ctx, timeout, + func() (interface{}, error) { + return conn.CreateService(ctx, input) + }, + func(err error) (bool, error) { + if errs.IsA[*awstypes.ClusterNotFoundException](err) { + return true, err + } + + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "verify that the ECS service role being passed has the proper permissions") { + return true, err + } + + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "does not have an associated load balancer") { + return true, err + } + + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "Unable to assume the service linked role") { + return true, err + } + + return false, err + }, + ) + + if err != nil { + return nil, err + } + + return outputRaw.(*ecs.CreateServiceOutput), err +} + +func findService(ctx context.Context, conn *ecs.Client, input *ecs.DescribeServicesInput) (*awstypes.Service, error) { + output, err := findServices(ctx, conn, input) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findServices(ctx context.Context, conn *ecs.Client, input *ecs.DescribeServicesInput) ([]awstypes.Service, error) { + output, err := conn.DescribeServices(ctx, input) + + if errs.IsA[*awstypes.ClusterNotFoundException](err) || errs.IsA[*awstypes.ServiceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + // When an ECS Service is not found by DescribeServices(), it will return a Failure struct with Reason = "MISSING" + for _, v := range output.Failures { + if aws.ToString(v.Reason) == failureReasonMissing { + return nil, &retry.NotFoundError{ + LastError: failureError(&v), + LastRequest: input, + } + } + } + + return output.Services, nil +} + +func findServiceByTwoPartKey(ctx context.Context, conn *ecs.Client, serviceName, clusterNameOrARN string) (*awstypes.Service, error) { + input := &ecs.DescribeServicesInput{ + Cluster: aws.String(clusterNameOrARN), + Include: []awstypes.ServiceField{awstypes.ServiceFieldTags}, + Services: []string{serviceName}, + } + + output, err := findService(ctx, conn, input) + + // Some partitions (i.e., ISO) may not support tagging, giving error. + if errs.IsUnsupportedOperationInPartitionError(partitionFromConn(conn), err) { + input.Include = nil + + output, err = findService(ctx, conn, input) + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func findServiceNoTagsByTwoPartKey(ctx context.Context, conn *ecs.Client, serviceName, clusterNameOrARN string) (*awstypes.Service, error) { + input := &ecs.DescribeServicesInput{ + Services: []string{serviceName}, + } + if clusterNameOrARN != "" { + input.Cluster = aws.String(clusterNameOrARN) + } + + return findService(ctx, conn, input) +} + +type expectServiceActiveError struct { + status string +} + +func newExpectServiceActiveError(status string) *expectServiceActiveError { + return &expectServiceActiveError{ + status: status, + } +} + +func (e *expectServiceActiveError) Error() string { + return fmt.Sprintf("expected status %[1]q, was %[2]q", serviceStatusActive, e.status) +} + +func findServiceByTwoPartKeyWaitForActive(ctx context.Context, conn *ecs.Client, serviceName, clusterNameOrARN string) (*awstypes.Service, error) { + var service *awstypes.Service + + // Use the retry.RetryContext function instead of WaitForState() because we don't want the timeout error, if any. + const ( + timeout = 2 * time.Minute + ) + err := retry.RetryContext(ctx, timeout, func() *retry.RetryError { + var err error + + service, err = findServiceByTwoPartKey(ctx, conn, serviceName, clusterNameOrARN) + + if tfresource.NotFound(err) { + return retry.RetryableError(err) + } + + if err != nil { + return retry.NonRetryableError(err) + } + + if status := aws.ToString(service.Status); status != serviceStatusActive { + return retry.RetryableError(newExpectServiceActiveError(status)) + } + + return nil + }) + + if tfresource.TimedOut(err) { + service, err = findServiceByTwoPartKey(ctx, conn, serviceName, clusterNameOrARN) + } + + if errs.IsA[*expectServiceActiveError](err) { + return nil, &retry.NotFoundError{ + LastError: err, + } + } + + return service, err +} + +const ( + serviceStatusInactive = "INACTIVE" + serviceStatusActive = "ACTIVE" + serviceStatusDraining = "DRAINING" + + // Non-standard statuses for statusServiceWaitForStable(). + serviceStatusPending = "tfPENDING" + serviceStatusStable = "tfSTABLE" +) + +func statusService(ctx context.Context, conn *ecs.Client, serviceName, clusterNameOrARN string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + service, err := findServiceNoTagsByTwoPartKey(ctx, conn, serviceName, clusterNameOrARN) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return service, aws.ToString(service.Status), err + } +} + +func statusServiceWaitForStable(ctx context.Context, conn *ecs.Client, serviceName, clusterNameOrARN string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + outputRaw, status, err := statusService(ctx, conn, serviceName, clusterNameOrARN)() + + if err != nil { + return nil, "", err + } + + if status != serviceStatusActive { + return outputRaw, status, nil + } + + output := outputRaw.(*awstypes.Service) + + if n, dc, rc := len(output.Deployments), output.DesiredCount, output.RunningCount; n == 1 && dc == rc { + status = serviceStatusStable + } else { + status = serviceStatusPending + } + + return output, status, nil + } +} + +// waitServiceStable waits for an ECS Service to reach the status "ACTIVE" and have all desired tasks running. +// Does not return tags. +func waitServiceStable(ctx context.Context, conn *ecs.Client, serviceName, clusterNameOrARN string, timeout time.Duration) (*awstypes.Service, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{serviceStatusInactive, serviceStatusDraining, serviceStatusPending}, + Target: []string{serviceStatusStable}, + Refresh: statusServiceWaitForStable(ctx, conn, serviceName, clusterNameOrARN), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if v, ok := outputRaw.(*awstypes.Service); ok { + return v, err + } + + return nil, err +} + +// Does not return tags. +func waitServiceActive(ctx context.Context, conn *ecs.Client, serviceName, clusterNameOrARN string, timeout time.Duration) (*awstypes.Service, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{serviceStatusInactive, serviceStatusDraining}, + Target: []string{serviceStatusActive}, + Refresh: statusService(ctx, conn, serviceName, clusterNameOrARN), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.Service); ok { + return output, err + } + + return nil, err +} + +// Does not return tags. +func waitServiceInactive(ctx context.Context, conn *ecs.Client, serviceName, clusterNameOrARN string, timeout time.Duration) (*awstypes.Service, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{serviceStatusActive, serviceStatusDraining}, + Target: []string{serviceStatusInactive}, + Refresh: statusService(ctx, conn, serviceName, clusterNameOrARN), + Timeout: timeout, + MinTimeout: 1 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.Service); ok { + return output, err + } + + return nil, err +} + func triggersCustomizeDiff(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { // clears diff to avoid extraneous diffs but lets it pass for triggering update fnd := false @@ -1745,62 +1953,11 @@ func flattenServiceRegistries(srs []awstypes.ServiceRegistry) []map[string]inter return results } -func resourceLoadBalancerHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - - buf.WriteString(fmt.Sprintf("%s-", m["elb_name"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["container_name"].(string))) - buf.WriteString(fmt.Sprintf("%d-", m["container_port"].(int))) - - if s := m["target_group_arn"].(string); s != "" { - buf.WriteString(fmt.Sprintf("%s-", s)) - } - - return create.StringHashcode(buf.String()) -} - -func serviceCreateWithRetry(ctx context.Context, conn *ecs.Client, input ecs.CreateServiceInput) (*ecs.CreateServiceOutput, error) { - var output *ecs.CreateServiceOutput - err := retry.RetryContext(ctx, propagationTimeout+serviceCreateTimeout, func() *retry.RetryError { - var err error - output, err = conn.CreateService(ctx, &input) - - if err != nil { - if errs.IsA[*awstypes.ClusterNotFoundException](err) { - return retry.RetryableError(err) - } - - if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "verify that the ECS service role being passed has the proper permissions") { - return retry.RetryableError(err) - } - - if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "does not have an associated load balancer") { - return retry.RetryableError(err) - } - - if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "Unable to assume the service linked role") { - return retry.RetryableError(err) - } - - return retry.NonRetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - output, err = conn.CreateService(ctx, &input) - } - - return output, err -} - -func buildFamilyAndRevisionFromARN(arn string) string { +func familyAndRevisionFromTaskDefinitionARN(arn string) string { return strings.Split(arn, "/")[1] } -// GetRoleNameFromARN parses a role name from a fully qualified ARN +// roleNameFromARN parses a role name from a fully qualified ARN // // When providing a role name with a path, it must be prefixed with the full path // including a leading `/`. @@ -1810,7 +1967,7 @@ func buildFamilyAndRevisionFromARN(arn string) string { // // arn:aws:iam::0123456789:role/EcsService // arn:aws:iam::0123456789:role/group/my-role -func GetRoleNameFromARN(arn string) string { +func roleNameFromARN(arn string) string { if parts := strings.Split(arn, "/"); len(parts) == 2 { return parts[1] } else if len(parts) > 2 { @@ -1819,12 +1976,12 @@ func GetRoleNameFromARN(arn string) string { return "" } -// GetClusterNameFromARN parses a cluster name from a fully qualified ARN +// clusterNameFromARN parses a cluster name from a fully qualified ARN // // Expects an ECS cluster ARN: // // arn:aws:ecs:us-west-2:0123456789:cluster/my-cluster -func GetClusterNameFromARN(arn string) string { +func clusterNameFromARN(arn string) string { if parts := strings.Split(arn, "/"); len(parts) == 2 { return parts[1] } diff --git a/internal/service/ecs/service_data_source.go b/internal/service/ecs/service_data_source.go index 0b077ab4868..99355fc437e 100644 --- a/internal/service/ecs/service_data_source.go +++ b/internal/service/ecs/service_data_source.go @@ -60,7 +60,7 @@ func dataSourceServiceRead(ctx context.Context, d *schema.ResourceData, meta int var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - service, err := findServiceByTwoPartKey(ctx, conn, meta.(*conns.AWSClient).Partition, d.Get(names.AttrServiceName).(string), d.Get("cluster_arn").(string)) + service, err := findServiceByTwoPartKey(ctx, conn, d.Get(names.AttrServiceName).(string), d.Get("cluster_arn").(string)) if err != nil { return sdkdiag.AppendFromErr(diags, tfresource.SingularDataSourceFindError("ECS Service", err)) diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index c3678ce1ca8..7e3333152f1 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -81,7 +81,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka Name: "Cluster Capacity Providers", }, { - Factory: ResourceService, + Factory: resourceService, TypeName: "aws_ecs_service", Name: "Service", Tags: &types.ServicePackageResourceTags{ diff --git a/internal/service/ecs/service_test.go b/internal/service/ecs/service_test.go index ea3b98c1570..f20f11606fa 100644 --- a/internal/service/ecs/service_test.go +++ b/internal/service/ecs/service_test.go @@ -20,7 +20,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/errs" tfecs "github.com/hashicorp/terraform-provider-aws/internal/service/ecs" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" @@ -55,14 +54,14 @@ func Test_GetRoleNameFromARN(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - if got := tfecs.GetRoleNameFromARN(tt.arn); got != tt.want { + if got := tfecs.RoleNameFromARN(tt.arn); got != tt.want { t.Errorf("GetRoleNameFromARN() = %v, want %v", got, tt.want) } }) } } -func Test_GetClustereNameFromARN(t *testing.T) { +func TestClustereNameFromARN(t *testing.T) { t.Parallel() tests := []struct { @@ -81,7 +80,7 @@ func Test_GetClustereNameFromARN(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - if got := tfecs.GetClusterNameFromARN(tt.arn); got != tt.want { + if got := tfecs.ClusterNameFromARN(tt.arn); got != tt.want { t.Errorf("GetClusterNameFromARN() = %v, want %v", got, tt.want) } }) @@ -1658,17 +1657,18 @@ func TestAccECSService_executeCommand(t *testing.T) { func testAccCheckServiceDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) - partition := acctest.Provider.Meta().(*conns.AWSClient).Partition for _, rs := range s.RootModule().Resources { if rs.Type != "aws_ecs_service" { continue } - service, err := tfecs.FindServiceNoTagsByID(ctx, conn, partition, rs.Primary.ID, rs.Primary.Attributes["cluster"]) + service, err := tfecs.FindServiceNoTagsByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["cluster"]) + if tfresource.NotFound(err) { return nil } + if err != nil { return err } @@ -1692,17 +1692,15 @@ func testAccCheckServiceExists(ctx context.Context, name string, service *awstyp } conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) - partition := acctest.Provider.Meta().(*conns.AWSClient).Partition err := retry.RetryContext(ctx, 1*time.Minute, func() *retry.RetryError { var err error - service, err = tfecs.FindServiceNoTagsByID(ctx, conn, partition, rs.Primary.ID, rs.Primary.Attributes["cluster"]) + service, err = tfecs.FindServiceNoTagsByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["cluster"]) + if tfresource.NotFound(err) { return retry.RetryableError(err) } - if errs.IsA[*awstypes.ClusterNotFoundException](err) { - return retry.RetryableError(err) - } + if err != nil { return retry.NonRetryableError(err) } diff --git a/internal/service/ecs/status.go b/internal/service/ecs/status.go index 39c7e3c997e..f06806bec4c 100644 --- a/internal/service/ecs/status.go +++ b/internal/service/ecs/status.go @@ -8,63 +8,15 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecs" - awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) const ( - serviceStatusInactive = "INACTIVE" - serviceStatusActive = "ACTIVE" - serviceStatusDraining = "DRAINING" - // Non-standard statuses for statusServiceWaitForStable() - serviceStatusPending = "tfPENDING" - serviceStatusStable = "tfSTABLE" - taskSetStatusActive = "ACTIVE" taskSetStatusDraining = "DRAINING" taskSetStatusPrimary = "PRIMARY" ) -func statusServiceNoTags(ctx context.Context, conn *ecs.Client, partition, id, cluster string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - service, err := FindServiceNoTagsByID(ctx, conn, partition, id, cluster) - if tfresource.NotFound(err) { - return nil, "", nil - } - if err != nil { - return nil, "", err - } - - return service, aws.ToString(service.Status), err - } -} - -func statusServiceWaitForStable(ctx context.Context, conn *ecs.Client, partition, id, cluster string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - serviceRaw, status, err := statusServiceNoTags(ctx, conn, partition, id, cluster)() - if err != nil { - return nil, "", err - } - - if status != serviceStatusActive { - return serviceRaw, status, nil - } - - service := serviceRaw.(*awstypes.Service) - - if d, dc, rc := len(service.Deployments), - service.DesiredCount, - service.RunningCount; d == 1 && dc == rc { - status = serviceStatusStable - } else { - status = serviceStatusPending - } - - return service, status, nil - } -} - func stabilityStatusTaskSet(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) retry.StateRefreshFunc { return func() (interface{}, string, error) { input := &ecs.DescribeTaskSetsInput{ diff --git a/internal/service/ecs/sweep.go b/internal/service/ecs/sweep.go index efbf1cda03f..24661f1514d 100644 --- a/internal/service/ecs/sweep.go +++ b/internal/service/ecs/sweep.go @@ -185,7 +185,7 @@ func sweepServices(region string) error { } for _, v := range page.ServiceArns { - r := ResourceService() + r := resourceService() d := r.Data(nil) d.SetId(v) d.Set("cluster", clusterARN) diff --git a/internal/service/ecs/task_execution_data_source.go b/internal/service/ecs/task_execution_data_source.go index e5730950738..36cba559de6 100644 --- a/internal/service/ecs/task_execution_data_source.go +++ b/internal/service/ecs/task_execution_data_source.go @@ -299,7 +299,7 @@ func dataSourceTaskExecutionRead(ctx context.Context, d *schema.ResourceData, me } if v, ok := d.GetOk(names.AttrCapacityProviderStrategy); ok { - input.CapacityProviderStrategy = expandCapacityProviderStrategy(v.(*schema.Set)) + input.CapacityProviderStrategy = expandCapacityProviderStrategyItems(v.(*schema.Set)) } if v, ok := d.GetOk("client_token"); ok { input.ClientToken = aws.String(v.(string)) diff --git a/internal/service/ecs/task_set.go b/internal/service/ecs/task_set.go index ca5a0b239e8..a27d5285113 100644 --- a/internal/service/ecs/task_set.go +++ b/internal/service/ecs/task_set.go @@ -288,7 +288,7 @@ func resourceTaskSetCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk(names.AttrCapacityProviderStrategy); ok && v.(*schema.Set).Len() > 0 { - input.CapacityProviderStrategy = expandCapacityProviderStrategy(v.(*schema.Set)) + input.CapacityProviderStrategy = expandCapacityProviderStrategyItems(v.(*schema.Set)) } if v, ok := d.GetOk(names.AttrExternalID); ok { @@ -420,7 +420,7 @@ func resourceTaskSetRead(ctx context.Context, d *schema.ResourceData, meta inter d.Set("task_definition", taskSet.TaskDefinition) d.Set("task_set_id", taskSet.Id) - if err := d.Set(names.AttrCapacityProviderStrategy, flattenCapacityProviderStrategy(taskSet.CapacityProviderStrategy)); err != nil { + if err := d.Set(names.AttrCapacityProviderStrategy, flattenCapacityProviderStrategyItems(taskSet.CapacityProviderStrategy)); err != nil { return sdkdiag.AppendErrorf(diags, "setting capacity_provider_strategy: %s", err) } diff --git a/internal/service/ecs/wait.go b/internal/service/ecs/wait.go index a9fc179f17a..d4cbc529bc5 100644 --- a/internal/service/ecs/wait.go +++ b/internal/service/ecs/wait.go @@ -7,7 +7,6 @@ import ( "context" "time" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecs" awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" @@ -15,82 +14,10 @@ import ( ) const ( - serviceCreateTimeout = 2 * time.Minute - serviceInactiveMinTimeout = 1 * time.Second - serviceDescribeTimeout = 2 * time.Minute - serviceUpdateTimeout = 2 * time.Minute - taskSetCreateTimeout = 10 * time.Minute taskSetDeleteTimeout = 10 * time.Minute ) -// waitServiceStable waits for an ECS Service to reach the status "ACTIVE" and have all desired tasks running. Does not return tags. -func waitServiceStable(ctx context.Context, conn *ecs.Client, partition, id, cluster string, timeout time.Duration) (*awstypes.Service, error) { - input := &ecs.DescribeServicesInput{ - Services: []string{id}, - } - - if cluster != "" { - input.Cluster = aws.String(cluster) - } - - stateConf := &retry.StateChangeConf{ - Pending: []string{serviceStatusInactive, serviceStatusDraining, serviceStatusPending}, - Target: []string{serviceStatusStable}, - Refresh: statusServiceWaitForStable(ctx, conn, partition, id, cluster), - Timeout: timeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if v, ok := outputRaw.(*awstypes.Service); ok { - return v, err - } - - return nil, err -} - -// waitServiceInactive waits for an ECS Service to reach the status "INACTIVE". -func waitServiceInactive(ctx context.Context, conn *ecs.Client, partition, id, cluster string, timeout time.Duration) error { - input := &ecs.DescribeServicesInput{ - Services: []string{id}, - } - - if cluster != "" { - input.Cluster = aws.String(cluster) - } - - stateConf := &retry.StateChangeConf{ - Pending: []string{serviceStatusActive, serviceStatusDraining}, - Target: []string{serviceStatusInactive}, - Refresh: statusServiceNoTags(ctx, conn, partition, id, cluster), - Timeout: timeout, - MinTimeout: serviceInactiveMinTimeout, - } - - _, err := stateConf.WaitForStateContext(ctx) - - return err -} - -// waitServiceActive waits for an ECS Service to reach the status "ACTIVE". Does not return tags. -func waitServiceActive(ctx context.Context, conn *ecs.Client, partition, id, cluster string, timeout time.Duration) (*awstypes.Service, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{serviceStatusInactive, serviceStatusDraining}, - Target: []string{serviceStatusActive}, - Refresh: statusServiceNoTags(ctx, conn, partition, id, cluster), - Timeout: timeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if v, ok := outputRaw.(*awstypes.Service); ok { - return v, err - } - - return nil, err -} - func waitTaskSetStable(ctx context.Context, conn *ecs.Client, timeout time.Duration, taskSetID, service, cluster string) error { stateConf := &retry.StateChangeConf{ Pending: enum.Slice(awstypes.StabilityStatusStabilizing), From 5289211b75d77547963efa0c2a1cacd258e1b7f4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 15:58:03 -0400 Subject: [PATCH 46/60] r/aws_ecs_task_definition: Reduce visibility. --- .../ecs/container_definition_data_source.go | 2 +- internal/service/ecs/exports_test.go | 3 + internal/service/ecs/service.go | 2 +- internal/service/ecs/service_package_gen.go | 2 +- internal/service/ecs/service_test.go | 2 +- internal/service/ecs/sweep.go | 2 +- internal/service/ecs/task_definition.go | 195 +++++++++--------- .../ecs/task_definition_data_source.go | 2 +- internal/service/ecs/task_definition_test.go | 30 ++- 9 files changed, 119 insertions(+), 121 deletions(-) diff --git a/internal/service/ecs/container_definition_data_source.go b/internal/service/ecs/container_definition_data_source.go index ef9c97524f6..72b2baeaf43 100644 --- a/internal/service/ecs/container_definition_data_source.go +++ b/internal/service/ecs/container_definition_data_source.go @@ -108,7 +108,7 @@ func findContainerDefinitionByTwoPartKey(ctx context.Context, conn *ecs.Client, TaskDefinition: aws.String(taskDefinitionName), } - taskDefinition, err := findTaskDefinition(ctx, conn, input) + taskDefinition, _, err := findTaskDefinition(ctx, conn, input) if err != nil { return nil, err diff --git a/internal/service/ecs/exports_test.go b/internal/service/ecs/exports_test.go index 8a127ea52d7..5ed2699691c 100644 --- a/internal/service/ecs/exports_test.go +++ b/internal/service/ecs/exports_test.go @@ -11,6 +11,7 @@ var ( ResourceClusterCapacityProviders = resourceClusterCapacityProviders ResourceService = resourceService ResourceTag = resourceTag + ResourceTaskDefinition = resourceTaskDefinition ClusterNameFromARN = clusterNameFromARN FindCapacityProviderByARN = findCapacityProviderByARN @@ -18,6 +19,8 @@ var ( FindEffectiveAccountSettingByName = findEffectiveAccountSettingByName FindServiceNoTagsByTwoPartKey = findServiceNoTagsByTwoPartKey FindTag = findTag + FindTaskDefinitionByFamilyOrARN = findTaskDefinitionByFamilyOrARN RoleNameFromARN = roleNameFromARN + TaskDefinitionARNStripRevision = taskDefinitionARNStripRevision ValidTaskDefinitionContainerDefinitions = validTaskDefinitionContainerDefinitions ) diff --git a/internal/service/ecs/service.go b/internal/service/ecs/service.go index 6ffb49b2614..17067ef859e 100644 --- a/internal/service/ecs/service.go +++ b/internal/service/ecs/service.go @@ -749,7 +749,7 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, meta inter } if err != nil { - return sdkdiag.AppendErrorf(diags, "reading ECS service (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading ECS Service (%s): %s", d.Id(), err) } d.SetId(aws.ToString(service.ServiceArn)) diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index 7e3333152f1..92e47bae729 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -94,7 +94,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka Name: "ECS Resource Tag", }, { - Factory: ResourceTaskDefinition, + Factory: resourceTaskDefinition, TypeName: "aws_ecs_task_definition", Name: "Task Definition", Tags: &types.ServicePackageResourceTags{ diff --git a/internal/service/ecs/service_test.go b/internal/service/ecs/service_test.go index f20f11606fa..51e9e2a31f6 100644 --- a/internal/service/ecs/service_test.go +++ b/internal/service/ecs/service_test.go @@ -1677,7 +1677,7 @@ func testAccCheckServiceDestroy(ctx context.Context) resource.TestCheckFunc { return nil } - return fmt.Errorf("ECS service still exists:\n%#v", service) + return fmt.Errorf("ECS Service %s still exists", rs.Primary.ID) } return nil diff --git a/internal/service/ecs/sweep.go b/internal/service/ecs/sweep.go index 24661f1514d..1dbaedf8252 100644 --- a/internal/service/ecs/sweep.go +++ b/internal/service/ecs/sweep.go @@ -230,7 +230,7 @@ func sweepTaskDefinitions(region string) error { } for _, v := range page.TaskDefinitionArns { - r := ResourceTaskDefinition() + r := resourceTaskDefinition() d := r.Data(nil) d.SetId(v) d.Set(names.AttrARN, v) diff --git a/internal/service/ecs/task_definition.go b/internal/service/ecs/task_definition.go index baaed4ab60b..42c567e08d0 100644 --- a/internal/service/ecs/task_definition.go +++ b/internal/service/ecs/task_definition.go @@ -16,6 +16,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/ecs" awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -34,7 +35,7 @@ import ( // @SDKResource("aws_ecs_task_definition", name="Task Definition") // @Tags(identifierAttribute="arn") -func ResourceTaskDefinition() *schema.Resource { +func resourceTaskDefinition() *schema.Resource { //lintignore:R011 return &schema.Resource{ CreateWithoutTimeout: resourceTaskDefinitionCreate, @@ -291,6 +292,12 @@ func ResourceTaskDefinition() *schema.Resource { ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "configure_at_launch": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, "docker_volume_configuration": { Type: schema.TypeList, Optional: true, @@ -437,12 +444,6 @@ func ResourceTaskDefinition() *schema.Resource { Required: true, ForceNew: true, }, - "configure_at_launch": { - Type: schema.TypeBool, - Optional: true, - Computed: true, - ForceNew: true, - }, }, }, Set: resourceTaskDefinitionVolumeHash, @@ -451,14 +452,6 @@ func ResourceTaskDefinition() *schema.Resource { } } -func validTaskDefinitionContainerDefinitions(v interface{}, k string) (ws []string, errors []error) { - _, err := expandContainerDefinitions(v.(string)) - if err != nil { - errors = append(errors, fmt.Errorf("ECS Task Definition container_definitions is invalid: %s", err)) - } - return -} - func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) @@ -507,12 +500,13 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m input.PidMode = awstypes.PidMode(v.(string)) } - if constraints := d.Get("placement_constraints").(*schema.Set).List(); len(constraints) > 0 { - cons, err := expandTaskDefinitionPlacementConstraints(constraints) + if v := d.Get("placement_constraints").(*schema.Set).List(); len(v) > 0 { + apiObject, err := expandTaskDefinitionPlacementConstraints(v) if err != nil { - return sdkdiag.AppendErrorf(diags, "creating ECS Task Definition (%s): %s", d.Get(names.AttrFamily).(string), err) + return sdkdiag.AppendFromErr(diags, err) } - input.PlacementConstraints = cons + + input.PlacementConstraints = apiObject } if proxyConfigs := d.Get("proxy_configuration").([]interface{}); len(proxyConfigs) > 0 { @@ -549,11 +543,9 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m return sdkdiag.AppendErrorf(diags, "creating ECS Task Definition (%s): %s", d.Get(names.AttrFamily).(string), err) } - taskDefinition := *output.TaskDefinition // nosemgrep:ci.semgrep.aws.prefer-pointer-conversion-assignment // false positive + taskDefinition := output.TaskDefinition d.SetId(aws.ToString(taskDefinition.Family)) - d.Set(names.AttrARN, taskDefinition.TaskDefinitionArn) - d.Set("arn_without_revision", StripRevision(aws.ToString(taskDefinition.TaskDefinitionArn))) // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { @@ -575,103 +567,70 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m func resourceTaskDefinitionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition - trackedTaskDefinition := d.Get(names.AttrARN).(string) + familyOrARN := d.Get(names.AttrARN).(string) if _, ok := d.GetOk("track_latest"); ok { - trackedTaskDefinition = d.Get(names.AttrFamily).(string) - } - - input := ecs.DescribeTaskDefinitionInput{ - Include: []awstypes.TaskDefinitionField{awstypes.TaskDefinitionFieldTags}, - TaskDefinition: aws.String(trackedTaskDefinition), - } - - out, err := conn.DescribeTaskDefinition(ctx, &input) - - // Some partitions (i.e., ISO) may not support tagging, giving error - if errs.IsUnsupportedOperationInPartitionError(partition, err) { - log.Printf("[WARN] ECS tagging failed describing Task Definition (%s) with tags: %s; retrying without tags", d.Id(), err) - - input.Include = nil - out, err = conn.DescribeTaskDefinition(ctx, &input) + familyOrARN = d.Get(names.AttrFamily).(string) } + taskDefinition, tags, err := findTaskDefinitionByFamilyOrARN(ctx, conn, familyOrARN) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition (%s): %s", d.Id(), err) - } - - log.Printf("[DEBUG] Received task definition %s, status:%s\n %+v", aws.ToString(out.TaskDefinition.Family), - string(out.TaskDefinition.Status), out) - - taskDefinition := out.TaskDefinition - - if taskDefinition.Status == awstypes.TaskDefinitionStatusInactive { - log.Printf("[DEBUG] Removing ECS task definition %s because it's INACTIVE", aws.ToString(out.TaskDefinition.Family)) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] ECS Task Definition (%s) not found, removing from state", d.Id()) d.SetId("") return diags } - d.SetId(aws.ToString(taskDefinition.Family)) - d.Set(names.AttrARN, taskDefinition.TaskDefinitionArn) - d.Set("arn_without_revision", StripRevision(aws.ToString(taskDefinition.TaskDefinitionArn))) - d.Set(names.AttrFamily, taskDefinition.Family) - d.Set("revision", taskDefinition.Revision) - d.Set("track_latest", d.Get("track_latest")) - - // Sort the lists of environment variables as they come in, so we won't get spurious reorderings in plans - // (diff is suppressed if the environment variables haven't changed, but they still show in the plan if - // some other property changes). - containerDefinitions(taskDefinition.ContainerDefinitions).orderContainers() - containerDefinitions(taskDefinition.ContainerDefinitions).orderEnvironmentVariables() - containerDefinitions(taskDefinition.ContainerDefinitions).orderSecrets() - - defs, err := flattenContainerDefinitions(taskDefinition.ContainerDefinitions) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition (%s): %s", d.Id(), err) - } - err = d.Set("container_definitions", defs) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition (%s): %s", familyOrARN, err) } - d.Set("task_role_arn", taskDefinition.TaskRoleArn) - d.Set(names.AttrExecutionRoleARN, taskDefinition.ExecutionRoleArn) + d.SetId(aws.ToString(taskDefinition.Family)) + d.Set(names.AttrARN, taskDefinition.TaskDefinitionArn) + d.Set("arn_without_revision", taskDefinitionARNStripRevision(aws.ToString(taskDefinition.TaskDefinitionArn))) d.Set("cpu", taskDefinition.Cpu) - d.Set("memory", taskDefinition.Memory) - d.Set("network_mode", taskDefinition.NetworkMode) - d.Set("ipc_mode", taskDefinition.IpcMode) - d.Set("pid_mode", taskDefinition.PidMode) - - if err := d.Set("volume", flattenVolumes(taskDefinition.Volumes)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting volume: %s", err) + if err := d.Set("ephemeral_storage", flattenTaskDefinitionEphemeralStorage(taskDefinition.EphemeralStorage)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting ephemeral_storage: %s", err) } - + d.Set(names.AttrExecutionRoleARN, taskDefinition.ExecutionRoleArn) + d.Set(names.AttrFamily, taskDefinition.Family) if err := d.Set("inference_accelerator", flattenInferenceAccelerators(taskDefinition.InferenceAccelerators)); err != nil { return sdkdiag.AppendErrorf(diags, "setting inference accelerators: %s", err) } - + d.Set("ipc_mode", taskDefinition.IpcMode) + d.Set("memory", taskDefinition.Memory) + d.Set("network_mode", taskDefinition.NetworkMode) + d.Set("pid_mode", taskDefinition.PidMode) if err := d.Set("placement_constraints", flattenPlacementConstraints(taskDefinition.PlacementConstraints)); err != nil { return sdkdiag.AppendErrorf(diags, "setting placement_constraints: %s", err) } - - if err := d.Set("requires_compatibilities", flex.FlattenStringyValueList(taskDefinition.RequiresCompatibilities)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting requires_compatibilities: %s", err) + if err := d.Set("proxy_configuration", flattenProxyConfiguration(taskDefinition.ProxyConfiguration)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting proxy_configuration: %s", err) } - + d.Set("requires_compatibilities", taskDefinition.RequiresCompatibilities) + d.Set("revision", taskDefinition.Revision) if err := d.Set("runtime_platform", flattenRuntimePlatform(taskDefinition.RuntimePlatform)); err != nil { return sdkdiag.AppendErrorf(diags, "setting runtime_platform: %s", err) } - - if err := d.Set("proxy_configuration", flattenProxyConfiguration(taskDefinition.ProxyConfiguration)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting proxy_configuration: %s", err) + d.Set("task_role_arn", taskDefinition.TaskRoleArn) + d.Set("track_latest", d.Get("track_latest")) + if err := d.Set("volume", flattenVolumes(taskDefinition.Volumes)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting volume: %s", err) } - if err := d.Set("ephemeral_storage", flattenTaskDefinitionEphemeralStorage(taskDefinition.EphemeralStorage)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting ephemeral_storage: %s", err) + // Sort the lists of environment variables as they come in, so we won't get spurious reorderings in plans + // (diff is suppressed if the environment variables haven't changed, but they still show in the plan if + // some other property changes). + containerDefinitions(taskDefinition.ContainerDefinitions).orderContainers() + containerDefinitions(taskDefinition.ContainerDefinitions).orderEnvironmentVariables() + containerDefinitions(taskDefinition.ContainerDefinitions).orderSecrets() + + defs, err := flattenContainerDefinitions(taskDefinition.ContainerDefinitions) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) } + d.Set("container_definitions", defs) - setTagsOut(ctx, out.Tags) + setTagsOut(ctx, tags) return diags } @@ -687,7 +646,7 @@ func resourceTaskDefinitionUpdate(ctx context.Context, d *schema.ResourceData, m func resourceTaskDefinitionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics if v, ok := d.GetOk(names.AttrSkipDestroy); ok && v.(bool) { - log.Printf("[DEBUG] Retaining ECS Task Definition Revision %q", d.Id()) + log.Printf("[DEBUG] Retaining ECS Task Definition Revision: %s", d.Id()) return diags } @@ -696,6 +655,7 @@ func resourceTaskDefinitionDelete(ctx context.Context, d *schema.ResourceData, m _, err := conn.DeregisterTaskDefinition(ctx, &ecs.DeregisterTaskDefinitionInput{ TaskDefinition: aws.String(d.Get(names.AttrARN).(string)), }) + if err != nil { return sdkdiag.AppendErrorf(diags, "deleting ECS Task Definition (%s): %s", d.Id(), err) } @@ -703,18 +663,55 @@ func resourceTaskDefinitionDelete(ctx context.Context, d *schema.ResourceData, m return diags } -func findTaskDefinition(ctx context.Context, conn *ecs.Client, input *ecs.DescribeTaskDefinitionInput) (*awstypes.TaskDefinition, error) { +func findTaskDefinition(ctx context.Context, conn *ecs.Client, input *ecs.DescribeTaskDefinitionInput) (*awstypes.TaskDefinition, []awstypes.Tag, error) { output, err := conn.DescribeTaskDefinition(ctx, input) if err != nil { - return nil, err + return nil, nil, err } if output == nil || output.TaskDefinition == nil { - return nil, tfresource.NewEmptyResultError(input) + return nil, nil, tfresource.NewEmptyResultError(input) + } + + return output.TaskDefinition, output.Tags, nil +} + +func findTaskDefinitionByFamilyOrARN(ctx context.Context, conn *ecs.Client, familyOrARN string) (*awstypes.TaskDefinition, []awstypes.Tag, error) { + input := &ecs.DescribeTaskDefinitionInput{ + Include: []awstypes.TaskDefinitionField{awstypes.TaskDefinitionFieldTags}, + TaskDefinition: aws.String(familyOrARN), + } + + taskDefinition, tags, err := findTaskDefinition(ctx, conn, input) + + // Some partitions (i.e., ISO) may not support tagging, giving error. + if errs.IsUnsupportedOperationInPartitionError(partitionFromConn(conn), err) { + input.Include = nil + + taskDefinition, tags, err = findTaskDefinition(ctx, conn, input) + } + + if err != nil { + return nil, nil, err } - return output.TaskDefinition, nil + if status := taskDefinition.Status; status == awstypes.TaskDefinitionStatusInactive { + return nil, nil, &retry.NotFoundError{ + Message: string(status), + LastRequest: input, + } + } + + return taskDefinition, tags, nil +} + +func validTaskDefinitionContainerDefinitions(v interface{}, k string) (ws []string, errors []error) { + _, err := expandContainerDefinitions(v.(string)) + if err != nil { + errors = append(errors, fmt.Errorf("ECS Task Definition container_definitions is invalid: %s", err)) + } + return } func resourceTaskDefinitionVolumeHash(v interface{}) int { @@ -1241,11 +1238,11 @@ func flattenTaskDefinitionEphemeralStorage(pc *awstypes.EphemeralStorage) []map[ return []map[string]interface{}{m} } -// StripRevision strips the trailing revision number from a task definition ARN +// taskDefinitionARNStripRevision strips the trailing revision number from a task definition ARN // // Invalid ARNs will return an empty string. ARNs with an unexpected number of // separators in the resource section are returned unmodified. -func StripRevision(s string) string { +func taskDefinitionARNStripRevision(s string) string { tdArn, err := arn.Parse(s) if err != nil { return "" diff --git a/internal/service/ecs/task_definition_data_source.go b/internal/service/ecs/task_definition_data_source.go index bdf5d806fdd..fa0c3aa4c34 100644 --- a/internal/service/ecs/task_definition_data_source.go +++ b/internal/service/ecs/task_definition_data_source.go @@ -80,7 +80,7 @@ func dataSourceTaskDefinitionRead(ctx context.Context, d *schema.ResourceData, m taskDefinition := output.TaskDefinition d.SetId(aws.ToString(taskDefinition.TaskDefinitionArn)) d.Set(names.AttrARN, taskDefinition.TaskDefinitionArn) - d.Set("arn_without_revision", StripRevision(aws.ToString(taskDefinition.TaskDefinitionArn))) + d.Set("arn_without_revision", taskDefinitionARNStripRevision(aws.ToString(taskDefinition.TaskDefinitionArn))) d.Set(names.AttrExecutionRoleARN, taskDefinition.ExecutionRoleArn) d.Set(names.AttrFamily, taskDefinition.Family) d.Set("network_mode", taskDefinition.NetworkMode) diff --git a/internal/service/ecs/task_definition_test.go b/internal/service/ecs/task_definition_test.go index f943fd97760..694f2547648 100644 --- a/internal/service/ecs/task_definition_test.go +++ b/internal/service/ecs/task_definition_test.go @@ -10,7 +10,6 @@ import ( "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ecs" awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/aws/aws-sdk-go/aws/endpoints" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" @@ -19,6 +18,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfecs "github.com/hashicorp/terraform-provider-aws/internal/service/ecs" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -61,7 +61,7 @@ func Test_StripRevision(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() - if got := tfecs.StripRevision(tc.s); got != tc.want { + if got := tfecs.TaskDefinitionARNStripRevision(tc.s); got != tc.want { t.Errorf("StripRevision() = %v, want %v", got, tc.want) } }) @@ -1344,41 +1344,39 @@ func testAccCheckTaskDefinitionDestroy(ctx context.Context) resource.TestCheckFu continue } - input := ecs.DescribeTaskDefinitionInput{ - TaskDefinition: aws.String(rs.Primary.Attributes[names.AttrARN]), - } + _, _, err := tfecs.FindTaskDefinitionByFamilyOrARN(ctx, conn, rs.Primary.Attributes[names.AttrARN]) - out, err := conn.DescribeTaskDefinition(ctx, &input) + if tfresource.NotFound(err) { + return nil + } if err != nil { return err } - if out.TaskDefinition != nil && out.TaskDefinition.Status != awstypes.TaskDefinitionStatusInactive { - return fmt.Errorf("ECS task definition still exists:\n%#v", *out.TaskDefinition) - } + return fmt.Errorf("ECS Task Definition %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckTaskDefinitionExists(ctx context.Context, name string, def *awstypes.TaskDefinition) resource.TestCheckFunc { +func testAccCheckTaskDefinitionExists(ctx context.Context, n string, v *awstypes.TaskDefinition) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %s", n) } conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) - out, err := conn.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ - TaskDefinition: aws.String(rs.Primary.Attributes[names.AttrARN]), - }) + output, _, err := tfecs.FindTaskDefinitionByFamilyOrARN(ctx, conn, rs.Primary.Attributes[names.AttrARN]) + if err != nil { return err } - *def = *out.TaskDefinition + + *v = *output return nil } From 9559009bb09fc9104ba059fea9b5172bb5a22a97 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 16:01:43 -0400 Subject: [PATCH 47/60] d/aws_ecs_task_definition: Reduce visibility. --- internal/service/ecs/service_package_gen.go | 3 ++- .../service/ecs/task_definition_data_source.go | 16 +++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index 92e47bae729..494df9ffae0 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -42,8 +42,9 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac Tags: &types.ServicePackageResourceTags{}, }, { - Factory: DataSourceTaskDefinition, + Factory: dataSourceTaskDefinition, TypeName: "aws_ecs_task_definition", + Name: "Task Definition", }, { Factory: DataSourceTaskExecution, diff --git a/internal/service/ecs/task_definition_data_source.go b/internal/service/ecs/task_definition_data_source.go index fa0c3aa4c34..a3f8b7eceaa 100644 --- a/internal/service/ecs/task_definition_data_source.go +++ b/internal/service/ecs/task_definition_data_source.go @@ -15,17 +15,12 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_ecs_task_definition") -func DataSourceTaskDefinition() *schema.Resource { +// @SDKDataSource("aws_ecs_task_definition", name="Task Definition") +func dataSourceTaskDefinition() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceTaskDefinitionRead, Schema: map[string]*schema.Schema{ - "task_definition": { - Type: schema.TypeString, - Required: true, - }, - // Computed values. names.AttrARN: { Type: schema.TypeString, Computed: true, @@ -54,6 +49,10 @@ func DataSourceTaskDefinition() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "task_definition": { + Type: schema.TypeString, + Required: true, + }, "task_role_arn": { Type: schema.TypeString, Computed: true, @@ -71,13 +70,12 @@ func dataSourceTaskDefinitionRead(ctx context.Context, d *schema.ResourceData, m TaskDefinition: aws.String(taskDefinitionName), } - output, err := conn.DescribeTaskDefinition(ctx, input) + taskDefinition, _, err := findTaskDefinition(ctx, conn, input) if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS Task Definition (%s): %s", taskDefinitionName, err) } - taskDefinition := output.TaskDefinition d.SetId(aws.ToString(taskDefinition.TaskDefinitionArn)) d.Set(names.AttrARN, taskDefinition.TaskDefinitionArn) d.Set("arn_without_revision", taskDefinitionARNStripRevision(aws.ToString(taskDefinition.TaskDefinitionArn))) From 6afe79d1527810f6ccfb68d4dd368d038d1025db Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 16:06:27 -0400 Subject: [PATCH 48/60] Eliminate use of 'github.com/aws/aws-sdk-go/private/protocol/json/jsonutil'. --- .../service/ecs/task_definition_migrate.go | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/internal/service/ecs/task_definition_migrate.go b/internal/service/ecs/task_definition_migrate.go index d87059daf1a..16dc2c774fd 100644 --- a/internal/service/ecs/task_definition_migrate.go +++ b/internal/service/ecs/task_definition_migrate.go @@ -10,9 +10,9 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecs" - "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfjson "github.com/hashicorp/terraform-provider-aws/internal/json" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -23,30 +23,32 @@ func resourceTaskDefinitionMigrateState(v int, is *terraform.InstanceState, meta switch v { case 0: log.Println("[INFO] Found AWS ECS Task Definition State v0; migrating to v1") - return migrateTaskDefinitionStateV0toV1(is, conn) + return migrateTaskDefinitionStateV0toV1(ctx, is, conn) default: return is, fmt.Errorf("Unexpected schema version: %d", v) } } -func migrateTaskDefinitionStateV0toV1(is *terraform.InstanceState, conn *ecs.Client) (*terraform.InstanceState, error) { +func migrateTaskDefinitionStateV0toV1(ctx context.Context, is *terraform.InstanceState, conn *ecs.Client) (*terraform.InstanceState, error) { arn := is.Attributes[names.AttrARN] - ctx := context.TODO() // nosemgrep:ci.semgrep.migrate.context-todo - // We need to pull definitions from the API b/c they're unrecoverable from the checksum - td, err := conn.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ + // We need to pull definitions from the API b/c they're unrecoverable from the checksum. + input := &ecs.DescribeTaskDefinitionInput{ TaskDefinition: aws.String(arn), - }) + } + td, _, err := findTaskDefinition(ctx, conn, input) + if err != nil { return nil, err } - b, err := jsonutil.BuildJSON(td.TaskDefinition.ContainerDefinitions) + s, err := tfjson.EncodeToString(td.ContainerDefinitions) + if err != nil { return nil, err } - is.Attributes["container_definitions"] = string(b) + is.Attributes["container_definitions"] = s return is, nil } From ddfed7ca319d570c9008c96d4f6ade9c362096ad Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 16:12:13 -0400 Subject: [PATCH 49/60] d/aws_ecs_task_execution: Reduce visibility. --- internal/service/ecs/service_package_gen.go | 3 +- .../service/ecs/task_execution_data_source.go | 60 ++++++++----------- 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index 494df9ffae0..292ad0e42a6 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -47,8 +47,9 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac Name: "Task Definition", }, { - Factory: DataSourceTaskExecution, + Factory: dataSourceTaskExecution, TypeName: "aws_ecs_task_execution", + Name: "Task Execution", }, } } diff --git a/internal/service/ecs/task_execution_data_source.go b/internal/service/ecs/task_execution_data_source.go index 36cba559de6..01ad680135b 100644 --- a/internal/service/ecs/task_execution_data_source.go +++ b/internal/service/ecs/task_execution_data_source.go @@ -14,16 +14,16 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "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/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_ecs_task_execution") -func DataSourceTaskExecution() *schema.Resource { +// @SDKDataSource("aws_ecs_task_execution", name="Task Execution") +func dataSourceTaskExecution() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceTaskExecutionRead, @@ -86,22 +86,20 @@ func DataSourceTaskExecution() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "assign_public_ip": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, names.AttrSecurityGroups: { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, names.AttrSubnets: { Type: schema.TypeSet, Required: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, - "assign_public_ip": { - Type: schema.TypeBool, - Optional: true, - Default: false, }, }, }, @@ -275,19 +273,14 @@ func DataSourceTaskExecution() *schema.Resource { } } -const ( - DSNameTaskExecution = "Task Execution Data Source" -) - func dataSourceTaskExecutionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) cluster := d.Get("cluster").(string) taskDefinition := d.Get("task_definition").(string) - d.SetId(strings.Join([]string{cluster, taskDefinition}, ",")) - - input := ecs.RunTaskInput{ + id := strings.Join([]string{cluster, taskDefinition}, ",") + input := &ecs.RunTaskInput{ Cluster: aws.String(cluster), TaskDefinition: aws.String(taskDefinition), } @@ -326,18 +319,20 @@ func dataSourceTaskExecutionRead(ctx context.Context, d *schema.ResourceData, me input.Overrides = expandTaskOverride(v.([]interface{})) } if v, ok := d.GetOk("placement_constraints"); ok { - pc, err := expandPlacementConstraints(v.(*schema.Set).List()) + apiObject, err := expandPlacementConstraints(v.(*schema.Set).List()) if err != nil { - return create.AppendDiagError(diags, names.ECS, create.ErrActionCreating, DSNameTaskExecution, d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } - input.PlacementConstraints = pc + + input.PlacementConstraints = apiObject } if v, ok := d.GetOk("placement_strategy"); ok { - ps, err := expandPlacementStrategy(v.([]interface{})) + apiObject, err := expandPlacementStrategy(v.([]interface{})) if err != nil { - return create.AppendDiagError(diags, names.ECS, create.ErrActionCreating, DSNameTaskExecution, d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } - input.PlacementStrategy = ps + + input.PlacementStrategy = apiObject } if v, ok := d.GetOk("platform_version"); ok { input.PlatformVersion = aws.String(v.(string)) @@ -352,19 +347,16 @@ func dataSourceTaskExecutionRead(ctx context.Context, d *schema.ResourceData, me input.StartedBy = aws.String(v.(string)) } - out, err := conn.RunTask(ctx, &input) + output, err := conn.RunTask(ctx, input) + if err != nil { - return create.AppendDiagError(diags, names.ECS, create.ErrActionCreating, DSNameTaskExecution, d.Id(), err) - } - if out == nil || len(out.Tasks) == 0 { - return create.AppendDiagError(diags, names.ECS, create.ErrActionCreating, DSNameTaskExecution, d.Id(), tfresource.NewEmptyResultError(input)) + return sdkdiag.AppendErrorf(diags, "running ECS Task (%s): %s", id, err) } - var taskArns []*string - for _, t := range out.Tasks { - taskArns = append(taskArns, t.TaskArn) - } - d.Set("task_arns", flex.FlattenStringList(taskArns)) + d.SetId(id) + d.Set("task_arns", tfslices.ApplyToAll(output.Tasks, func(v awstypes.Task) string { + return aws.ToString(v.TaskArn) + })) return diags } From 9b433825ef05b0138d336c6b9289de19900c301c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 16:44:19 -0400 Subject: [PATCH 50/60] r/aws_ecs_task_set: Reduce visibility. --- internal/service/ecs/exports_test.go | 2 + internal/service/ecs/service.go | 8 +- internal/service/ecs/service_package_gen.go | 2 +- internal/service/ecs/status.go | 62 ----- internal/service/ecs/task_set.go | 281 ++++++++++++++------ internal/service/ecs/task_set_test.go | 56 +--- internal/service/ecs/wait.go | 45 ---- 7 files changed, 215 insertions(+), 241 deletions(-) delete mode 100644 internal/service/ecs/status.go delete mode 100644 internal/service/ecs/wait.go diff --git a/internal/service/ecs/exports_test.go b/internal/service/ecs/exports_test.go index 5ed2699691c..57f09eff6ff 100644 --- a/internal/service/ecs/exports_test.go +++ b/internal/service/ecs/exports_test.go @@ -12,6 +12,7 @@ var ( ResourceService = resourceService ResourceTag = resourceTag ResourceTaskDefinition = resourceTaskDefinition + ResourceTaskSet = resourceTaskSet ClusterNameFromARN = clusterNameFromARN FindCapacityProviderByARN = findCapacityProviderByARN @@ -20,6 +21,7 @@ var ( FindServiceNoTagsByTwoPartKey = findServiceNoTagsByTwoPartKey FindTag = findTag FindTaskDefinitionByFamilyOrARN = findTaskDefinitionByFamilyOrARN + FindTaskSetNoTagsByThreePartKey = findTaskSetNoTagsByThreePartKey RoleNameFromARN = roleNameFromARN TaskDefinitionARNStripRevision = taskDefinitionARNStripRevision ValidTaskDefinitionContainerDefinitions = validTaskDefinitionContainerDefinitions diff --git a/internal/service/ecs/service.go b/internal/service/ecs/service.go index 17067ef859e..3d47a704c44 100644 --- a/internal/service/ecs/service.go +++ b/internal/service/ecs/service.go @@ -1282,7 +1282,7 @@ const ( func statusService(ctx context.Context, conn *ecs.Client, serviceName, clusterNameOrARN string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - service, err := findServiceNoTagsByTwoPartKey(ctx, conn, serviceName, clusterNameOrARN) + output, err := findServiceNoTagsByTwoPartKey(ctx, conn, serviceName, clusterNameOrARN) if tfresource.NotFound(err) { return nil, "", nil @@ -1292,7 +1292,7 @@ func statusService(ctx context.Context, conn *ecs.Client, serviceName, clusterNa return nil, "", err } - return service, aws.ToString(service.Status), err + return output, aws.ToString(output.Status), err } } @@ -1332,8 +1332,8 @@ func waitServiceStable(ctx context.Context, conn *ecs.Client, serviceName, clust outputRaw, err := stateConf.WaitForStateContext(ctx) - if v, ok := outputRaw.(*awstypes.Service); ok { - return v, err + if output, ok := outputRaw.(*awstypes.Service); ok { + return output, err } return nil, err diff --git a/internal/service/ecs/service_package_gen.go b/internal/service/ecs/service_package_gen.go index 292ad0e42a6..40b04aa3e44 100644 --- a/internal/service/ecs/service_package_gen.go +++ b/internal/service/ecs/service_package_gen.go @@ -104,7 +104,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceTaskSet, + Factory: resourceTaskSet, TypeName: "aws_ecs_task_set", Name: "Task Set", Tags: &types.ServicePackageResourceTags{ diff --git a/internal/service/ecs/status.go b/internal/service/ecs/status.go deleted file mode 100644 index f06806bec4c..00000000000 --- a/internal/service/ecs/status.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package ecs - -import ( - "context" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ecs" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" -) - -const ( - taskSetStatusActive = "ACTIVE" - taskSetStatusDraining = "DRAINING" - taskSetStatusPrimary = "PRIMARY" -) - -func stabilityStatusTaskSet(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - input := &ecs.DescribeTaskSetsInput{ - Cluster: aws.String(cluster), - Service: aws.String(service), - TaskSets: []string{taskSetID}, - } - - output, err := conn.DescribeTaskSets(ctx, input) - - if err != nil { - return nil, "", err - } - - if output == nil || len(output.TaskSets) == 0 { - return nil, "", nil - } - - return output.TaskSets[0], string(output.TaskSets[0].StabilityStatus), nil - } -} - -func statusTaskSet(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - input := &ecs.DescribeTaskSetsInput{ - Cluster: aws.String(cluster), - Service: aws.String(service), - TaskSets: []string{taskSetID}, - } - - output, err := conn.DescribeTaskSets(ctx, input) - - if err != nil { - return nil, "", err - } - - if output == nil || len(output.TaskSets) == 0 { - return nil, "", nil - } - - return output.TaskSets[0], aws.ToString(output.TaskSets[0].Status), nil - } -} diff --git a/internal/service/ecs/task_set.go b/internal/service/ecs/task_set.go index a27d5285113..06bc40f468b 100644 --- a/internal/service/ecs/task_set.go +++ b/internal/service/ecs/task_set.go @@ -15,6 +15,7 @@ import ( awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -29,7 +30,7 @@ import ( // @SDKResource("aws_ecs_task_set", name="Task Set") // @Tags(identifierAttribute="arn") -func ResourceTaskSet() *schema.Resource { +func resourceTaskSet() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceTaskSetCreate, ReadWithoutTimeout: resourceTaskSetRead, @@ -332,13 +333,12 @@ func resourceTaskSetCreate(ctx context.Context, d *schema.ResourceData, meta int return sdkdiag.AppendErrorf(diags, "creating ECS TaskSet: %s", err) } - taskSetId := aws.ToString(output.TaskSet.Id) - - d.SetId(fmt.Sprintf("%s,%s,%s", taskSetId, service, cluster)) + taskSetID := aws.ToString(output.TaskSet.Id) + d.SetId(taskSetCreateResourceID(taskSetID, service, cluster)) if d.Get("wait_until_stable").(bool) { timeout, _ := time.ParseDuration(d.Get("wait_until_stable_timeout").(string)) - if err := waitTaskSetStable(ctx, conn, timeout, taskSetId, service, cluster); err != nil { + if _, err := waitTaskSetStable(ctx, conn, taskSetID, service, cluster, timeout); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for ECS Task Set (%s) create: %s", d.Id(), err) } } @@ -363,82 +363,49 @@ func resourceTaskSetCreate(ctx context.Context, d *schema.ResourceData, meta int func resourceTaskSetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - partition := meta.(*conns.AWSClient).Partition - - taskSetId, service, cluster, err := TaskSetParseID(d.Id()) + taskSetID, service, cluster, err := taskSetParseResourceID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading ECS Task Set (%s): %s", d.Id(), err) - } - - input := &ecs.DescribeTaskSetsInput{ - Cluster: aws.String(cluster), - Include: []awstypes.TaskSetField{awstypes.TaskSetFieldTags}, - Service: aws.String(service), - TaskSets: []string{taskSetId}, + return sdkdiag.AppendFromErr(diags, err) } - out, err := conn.DescribeTaskSets(ctx, input) + taskSet, err := findTaskSetByThreePartKey(ctx, conn, taskSetID, service, cluster) - if !d.IsNewResource() && (errs.IsA[*awstypes.ClusterNotFoundException](err) || errs.IsA[*awstypes.ServiceNotFoundException](err) || errs.IsA[*awstypes.TaskSetNotFoundException](err)) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] ECS Task Set (%s) not found, removing from state", d.Id()) d.SetId("") return diags } - // Some partitions (i.e., ISO) may not support tagging, giving error - if errs.IsUnsupportedOperationInPartitionError(partition, err) { - log.Printf("[WARN] ECS tagging failed describing Task Set (%s) with tags: %s; retrying without tags", d.Id(), err) - - input.Include = nil - out, err = conn.DescribeTaskSets(ctx, input) - } - if err != nil { return sdkdiag.AppendErrorf(diags, "reading ECS Task Set (%s): %s", d.Id(), err) } - if out == nil || len(out.TaskSets) == 0 { - if d.IsNewResource() { - return sdkdiag.AppendErrorf(diags, "reading ECS Task Set (%s): empty output after creation", d.Id()) - } - log.Printf("[WARN] ECS Task Set (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } - - taskSet := out.TaskSets[0] - d.Set(names.AttrARN, taskSet.TaskSetArn) - d.Set("cluster", cluster) - d.Set("launch_type", taskSet.LaunchType) - d.Set("platform_version", taskSet.PlatformVersion) - d.Set(names.AttrExternalID, taskSet.ExternalId) - d.Set("service", service) - d.Set(names.AttrStatus, taskSet.Status) - d.Set("stability_status", taskSet.StabilityStatus) - d.Set("task_definition", taskSet.TaskDefinition) - d.Set("task_set_id", taskSet.Id) - if err := d.Set(names.AttrCapacityProviderStrategy, flattenCapacityProviderStrategyItems(taskSet.CapacityProviderStrategy)); err != nil { return sdkdiag.AppendErrorf(diags, "setting capacity_provider_strategy: %s", err) } - + d.Set("cluster", cluster) + d.Set(names.AttrExternalID, taskSet.ExternalId) + d.Set("launch_type", taskSet.LaunchType) if err := d.Set("load_balancer", flattenTaskSetLoadBalancers(taskSet.LoadBalancers)); err != nil { return sdkdiag.AppendErrorf(diags, "setting load_balancer: %s", err) } - if err := d.Set(names.AttrNetworkConfiguration, flattenNetworkConfiguration(taskSet.NetworkConfiguration)); err != nil { return sdkdiag.AppendErrorf(diags, "setting network_configuration: %s", err) } - + d.Set("platform_version", taskSet.PlatformVersion) if err := d.Set("scale", flattenScale(taskSet.Scale)); err != nil { return sdkdiag.AppendErrorf(diags, "setting scale: %s", err) } - + d.Set("service", service) if err := d.Set("service_registries", flattenServiceRegistries(taskSet.ServiceRegistries)); err != nil { return sdkdiag.AppendErrorf(diags, "setting service_registries: %s", err) } + d.Set("stability_status", taskSet.StabilityStatus) + d.Set(names.AttrStatus, taskSet.Status) + d.Set("task_definition", taskSet.TaskDefinition) + d.Set("task_set_id", taskSet.Id) setTagsOut(ctx, taskSet.Tags) @@ -450,17 +417,16 @@ func resourceTaskSetUpdate(ctx context.Context, d *schema.ResourceData, meta int conn := meta.(*conns.AWSClient).ECSClient(ctx) if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { - taskSetId, service, cluster, err := TaskSetParseID(d.Id()) - + taskSetID, service, cluster, err := taskSetParseResourceID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "updating ECS Task Set (%s): %s", d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } input := &ecs.UpdateTaskSetInput{ Cluster: aws.String(cluster), - Service: aws.String(service), - TaskSet: aws.String(taskSetId), Scale: expandScale(d.Get("scale").([]interface{})), + Service: aws.String(service), + TaskSet: aws.String(taskSetID), } _, err = conn.UpdateTaskSet(ctx, input) @@ -471,8 +437,8 @@ func resourceTaskSetUpdate(ctx context.Context, d *schema.ResourceData, meta int if d.Get("wait_until_stable").(bool) { timeout, _ := time.ParseDuration(d.Get("wait_until_stable_timeout").(string)) - if err := waitTaskSetStable(ctx, conn, timeout, taskSetId, service, cluster); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for ECS Task Set (%s) to be stable after update: %s", d.Id(), err) + if _, err := waitTaskSetStable(ctx, conn, taskSetID, service, cluster, timeout); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for ECS Task Set (%s) update: %s", d.Id(), err) } } } @@ -484,20 +450,18 @@ func resourceTaskSetDelete(ctx context.Context, d *schema.ResourceData, meta int var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ECSClient(ctx) - taskSetId, service, cluster, err := TaskSetParseID(d.Id()) - + taskSetID, service, cluster, err := taskSetParseResourceID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting ECS Task Set (%s): %s", d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } - input := &ecs.DeleteTaskSetInput{ + log.Printf("[DEBUG] Deleting ECS Task Set: %s", d.Id()) + _, err = conn.DeleteTaskSet(ctx, &ecs.DeleteTaskSetInput{ Cluster: aws.String(cluster), - Service: aws.String(service), - TaskSet: aws.String(taskSetId), Force: aws.Bool(d.Get(names.AttrForceDelete).(bool)), - } - - _, err = conn.DeleteTaskSet(ctx, input) + Service: aws.String(service), + TaskSet: aws.String(taskSetID), + }) if errs.IsA[*awstypes.TaskSetNotFoundException](err) { return diags @@ -507,44 +471,195 @@ func resourceTaskSetDelete(ctx context.Context, d *schema.ResourceData, meta int return sdkdiag.AppendErrorf(diags, "deleting ECS Task Set (%s): %s", d.Id(), err) } - if err := waitTaskSetDeleted(ctx, conn, taskSetId, service, cluster); err != nil { - if errs.IsA[*awstypes.TaskSetNotFoundException](err) { - return diags - } - return sdkdiag.AppendErrorf(diags, "deleting ECS Task Set (%s): waiting for completion: %s", d.Id(), err) + if _, err := waitTaskSetDeleted(ctx, conn, taskSetID, service, cluster); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for ECS Task Set (%s) delete: %s", d.Id(), err) } return diags } -func TaskSetParseID(id string) (string, string, string, error) { - parts := strings.Split(id, ",") +const taskSetResourceIDSeparator = "," + +func taskSetCreateResourceID(taskSetID, service, cluster string) string { + parts := []string{taskSetID, service, cluster} + id := strings.Join(parts, taskSetResourceIDSeparator) + + return id +} + +func taskSetParseResourceID(id string) (string, string, string, error) { + parts := strings.Split(id, taskSetResourceIDSeparator) if len(parts) != 3 || parts[0] == "" || parts[1] == "" || parts[2] == "" { - return "", "", "", fmt.Errorf("unexpected format of ID (%q), expected TASK_SET_ID,SERVICE,CLUSTER", id) + return parts[0], parts[1], parts[2], nil } - return parts[0], parts[1], parts[2], nil + return "", "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected TASK_SET_ID%[2]sSERVICE%[2]sCLUSTER", id, taskSetResourceIDSeparator) } func retryTaskSetCreate(ctx context.Context, conn *ecs.Client, input *ecs.CreateTaskSetInput) (*ecs.CreateTaskSetOutput, error) { - outputRaw, err := tfresource.RetryWhen(ctx, propagationTimeout+taskSetCreateTimeout, + const ( + taskSetCreateTimeout = 10 * time.Minute + timeout = propagationTimeout + taskSetCreateTimeout + ) + outputRaw, err := tfresource.RetryWhen(ctx, timeout, func() (interface{}, error) { return conn.CreateTaskSet(ctx, input) }, func(err error) (bool, error) { - if errs.IsA[*awstypes.ClusterNotFoundException](err) || errs.IsA[*awstypes.ServiceNotFoundException](err) || errs.IsA[*awstypes.TaskSetNotFoundException](err) || - errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "does not have an associated load balancer") { + if errs.IsA[*awstypes.ClusterNotFoundException](err) || errs.IsA[*awstypes.ServiceNotFoundException](err) || errs.IsA[*awstypes.TaskSetNotFoundException](err) || errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "does not have an associated load balancer") { return true, err } + return false, err }, ) - output, ok := outputRaw.(*ecs.CreateTaskSetOutput) - if !ok || output == nil || output.TaskSet == nil { - return nil, fmt.Errorf("creating ECS TaskSet: empty output") + if err != nil { + return nil, err + } + + return outputRaw.(*ecs.CreateTaskSetOutput), nil +} + +func findTaskSet(ctx context.Context, conn *ecs.Client, input *ecs.DescribeTaskSetsInput) (*awstypes.TaskSet, error) { + output, err := findTaskSets(ctx, conn, input) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findTaskSets(ctx context.Context, conn *ecs.Client, input *ecs.DescribeTaskSetsInput) ([]awstypes.TaskSet, error) { + output, err := conn.DescribeTaskSets(ctx, input) + + if errs.IsA[*awstypes.ClusterNotFoundException](err) || errs.IsA[*awstypes.ServiceNotFoundException](err) || errs.IsA[*awstypes.TaskSetNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.TaskSets, nil +} + +func findTaskSetByThreePartKey(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) (*awstypes.TaskSet, error) { + input := &ecs.DescribeTaskSetsInput{ + Cluster: aws.String(cluster), + Include: []awstypes.TaskSetField{awstypes.TaskSetFieldTags}, + Service: aws.String(service), + TaskSets: []string{taskSetID}, + } + + output, err := findTaskSet(ctx, conn, input) + + // Some partitions (i.e., ISO) may not support tagging, giving error. + if errs.IsUnsupportedOperationInPartitionError(partitionFromConn(conn), err) { + input.Include = nil + + output, err = findTaskSet(ctx, conn, input) + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func findTaskSetNoTagsByThreePartKey(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) (*awstypes.TaskSet, error) { + input := &ecs.DescribeTaskSetsInput{ + Cluster: aws.String(cluster), + Service: aws.String(service), + TaskSets: []string{taskSetID}, + } + + return findTaskSet(ctx, conn, input) +} + +func statusTaskSetStability(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findTaskSetNoTagsByThreePartKey(ctx, conn, taskSetID, service, cluster) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.StabilityStatus), err + } +} + +func statusTaskSet(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findTaskSetNoTagsByThreePartKey(ctx, conn, taskSetID, service, cluster) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.ToString(output.Status), err + } +} + +const ( + taskSetStatusActive = "ACTIVE" + taskSetStatusDraining = "DRAINING" + taskSetStatusPrimary = "PRIMARY" +) + +// Does not return tags. +func waitTaskSetStable(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string, timeout time.Duration) (*awstypes.TaskSet, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.StabilityStatusStabilizing), + Target: enum.Slice(awstypes.StabilityStatusSteadyState), + Refresh: statusTaskSetStability(ctx, conn, taskSetID, service, cluster), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.TaskSet); ok { + return output, err + } + + return nil, err +} + +// Does not return tags. +func waitTaskSetDeleted(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) (*awstypes.TaskSet, error) { + const ( + timeout = 10 * time.Minute + ) + stateConf := &retry.StateChangeConf{ + Pending: []string{taskSetStatusActive, taskSetStatusPrimary, taskSetStatusDraining}, + Target: []string{}, + Refresh: statusTaskSet(ctx, conn, taskSetID, service, cluster), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.TaskSet); ok { + return output, err } - return output, err + return nil, err } diff --git a/internal/service/ecs/task_set_test.go b/internal/service/ecs/task_set_test.go index 27fd20bd202..fd0e62bd0bc 100644 --- a/internal/service/ecs/task_set_test.go +++ b/internal/service/ecs/task_set_test.go @@ -9,16 +9,14 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ecs" awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/errs" tfecs "github.com/hashicorp/terraform-provider-aws/internal/service/ecs" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -391,38 +389,18 @@ func TestAccECSTaskSet_tags(t *testing.T) { }) } -func testAccCheckTaskSetExists(ctx context.Context, name string) resource.TestCheckFunc { +func testAccCheckTaskSetExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %s", n) } conn := acctest.Provider.Meta().(*conns.AWSClient).ECSClient(ctx) - taskSetId, service, cluster, err := tfecs.TaskSetParseID(rs.Primary.ID) + _, err := tfecs.FindTaskSetNoTagsByThreePartKey(ctx, conn, rs.Primary.Attributes["task_set_id"], rs.Primary.Attributes["service"], rs.Primary.Attributes["cluster"]) - if err != nil { - return err - } - - input := &ecs.DescribeTaskSetsInput{ - TaskSets: []string{taskSetId}, - Cluster: aws.String(cluster), - Service: aws.String(service), - } - - output, err := conn.DescribeTaskSets(ctx, input) - - if err != nil { - return err - } - - if output == nil || len(output.TaskSets) == 0 { - return fmt.Errorf("ECS TaskSet (%s) not found", rs.Primary.ID) - } - - return nil + return err } } @@ -435,31 +413,17 @@ func testAccCheckTaskSetDestroy(ctx context.Context) resource.TestCheckFunc { continue } - taskSetId, service, cluster, err := tfecs.TaskSetParseID(rs.Primary.ID) + _, err := tfecs.FindTaskSetNoTagsByThreePartKey(ctx, conn, rs.Primary.Attributes["task_set_id"], rs.Primary.Attributes["service"], rs.Primary.Attributes["cluster"]) - if err != nil { - return err - } - - input := &ecs.DescribeTaskSetsInput{ - TaskSets: []string{taskSetId}, - Cluster: aws.String(cluster), - Service: aws.String(service), - } - - output, err := conn.DescribeTaskSets(ctx, input) - - if errs.IsA[*awstypes.ClusterNotFoundException](err) || errs.IsA[*awstypes.ServiceNotFoundException](err) || errs.IsA[*awstypes.TaskSetNotFoundException](err) { - continue + if tfresource.NotFound(err) { + return nil } if err != nil { return err } - if output != nil && len(output.TaskSets) == 1 { - return fmt.Errorf("ECS TaskSet (%s) still exists", rs.Primary.ID) - } + return fmt.Errorf("ECS Task Set %s still exists", rs.Primary.ID) } return nil diff --git a/internal/service/ecs/wait.go b/internal/service/ecs/wait.go deleted file mode 100644 index d4cbc529bc5..00000000000 --- a/internal/service/ecs/wait.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package ecs - -import ( - "context" - "time" - - "github.com/aws/aws-sdk-go-v2/service/ecs" - awstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/enum" -) - -const ( - taskSetCreateTimeout = 10 * time.Minute - taskSetDeleteTimeout = 10 * time.Minute -) - -func waitTaskSetStable(ctx context.Context, conn *ecs.Client, timeout time.Duration, taskSetID, service, cluster string) error { - stateConf := &retry.StateChangeConf{ - Pending: enum.Slice(awstypes.StabilityStatusStabilizing), - Target: enum.Slice(awstypes.StabilityStatusSteadyState), - Refresh: stabilityStatusTaskSet(ctx, conn, taskSetID, service, cluster), - Timeout: timeout, - } - - _, err := stateConf.WaitForStateContext(ctx) - - return err -} - -func waitTaskSetDeleted(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string) error { - stateConf := &retry.StateChangeConf{ - Pending: []string{taskSetStatusActive, taskSetStatusPrimary, taskSetStatusDraining}, - Target: []string{}, - Refresh: statusTaskSet(ctx, conn, taskSetID, service, cluster), - Timeout: taskSetDeleteTimeout, - } - - _, err := stateConf.WaitForStateContext(ctx) - - return err -} From dc56bb381fb8e5856a6c4eef2ba0bc8f6a6dafbc Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 16:49:17 -0400 Subject: [PATCH 51/60] Fix golangci-lint 'unparam'. --- internal/service/ecs/task_set.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/ecs/task_set.go b/internal/service/ecs/task_set.go index 06bc40f468b..ed90c9acb3d 100644 --- a/internal/service/ecs/task_set.go +++ b/internal/service/ecs/task_set.go @@ -626,7 +626,7 @@ const ( ) // Does not return tags. -func waitTaskSetStable(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string, timeout time.Duration) (*awstypes.TaskSet, error) { +func waitTaskSetStable(ctx context.Context, conn *ecs.Client, taskSetID, service, cluster string, timeout time.Duration) (*awstypes.TaskSet, error) { //nolint:unparam stateConf := &retry.StateChangeConf{ Pending: enum.Slice(awstypes.StabilityStatusStabilizing), Target: enum.Slice(awstypes.StabilityStatusSteadyState), From 1ada01767386f3fdb2b39299d588269784a9c731 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 16:50:39 -0400 Subject: [PATCH 52/60] Fix golangci-lint 'unparam'. --- internal/service/ecs/task_definition_equivalency.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/internal/service/ecs/task_definition_equivalency.go b/internal/service/ecs/task_definition_equivalency.go index 8995ae59cd4..7aaa1e06797 100644 --- a/internal/service/ecs/task_definition_equivalency.go +++ b/internal/service/ecs/task_definition_equivalency.go @@ -17,10 +17,7 @@ func containerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, if err != nil { return false, err } - err = obj1.reduce(isAWSVPC) - if err != nil { - return false, err - } + obj1.reduce(isAWSVPC) b1, err := tfjson.EncodeToBytes(obj1) if err != nil { return false, err @@ -31,10 +28,7 @@ func containerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, if err != nil { return false, err } - err = obj2.reduce(isAWSVPC) - if err != nil { - return false, err - } + obj2.reduce(isAWSVPC) b2, err := tfjson.EncodeToBytes(obj2) if err != nil { return false, err @@ -45,7 +39,7 @@ func containerDefinitionsAreEquivalent(def1, def2 string, isAWSVPC bool) (bool, type containerDefinitions []awstypes.ContainerDefinition -func (cd containerDefinitions) reduce(isAWSVPC bool) error { +func (cd containerDefinitions) reduce(isAWSVPC bool) { // Deal with fields which may be re-ordered in the API. cd.orderContainers() cd.orderEnvironmentVariables() @@ -124,7 +118,6 @@ func (cd containerDefinitions) reduce(isAWSVPC bool) error { cd[i].VolumesFrom = nil } } - return nil } func (cd containerDefinitions) orderEnvironmentVariables() { From e7fd9136a54f254c94eb4ee25c569dface793e95 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 16:54:15 -0400 Subject: [PATCH 53/60] Cosmetics. --- internal/service/ecs/service_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/ecs/service_test.go b/internal/service/ecs/service_test.go index 51e9e2a31f6..f45deed9f63 100644 --- a/internal/service/ecs/service_test.go +++ b/internal/service/ecs/service_test.go @@ -1663,7 +1663,7 @@ func testAccCheckServiceDestroy(ctx context.Context) resource.TestCheckFunc { continue } - service, err := tfecs.FindServiceNoTagsByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["cluster"]) + output, err := tfecs.FindServiceNoTagsByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["cluster"]) if tfresource.NotFound(err) { return nil @@ -1673,7 +1673,7 @@ func testAccCheckServiceDestroy(ctx context.Context) resource.TestCheckFunc { return err } - if aws.ToString(service.Status) == "INACTIVE" { + if aws.ToString(output.Status) == "INACTIVE" { return nil } From 86bfb11dd65f920f208d72e705957189d04d28b7 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 17:00:15 -0400 Subject: [PATCH 54/60] ecs: Tidy up sweepers. --- internal/service/ecs/sweep.go | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/internal/service/ecs/sweep.go b/internal/service/ecs/sweep.go index 1dbaedf8252..a07d90d05fc 100644 --- a/internal/service/ecs/sweep.go +++ b/internal/service/ecs/sweep.go @@ -9,7 +9,6 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecs" - multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/sweep" "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv2" @@ -110,7 +109,6 @@ func sweepClusters(region string) error { sweepResources := make([]sweep.Sweepable, 0) pages := ecs.NewListClustersPaginator(conn, input) - for pages.HasMorePages() { page, err := pages.NextPage(ctx) @@ -149,21 +147,19 @@ func sweepServices(region string) error { } conn := client.ECSClient(ctx) input := &ecs.ListClustersInput{} - var sweeperErrs *multierror.Error sweepResources := make([]sweep.Sweepable, 0) pages := ecs.NewListClustersPaginator(conn, input) - for pages.HasMorePages() { page, err := pages.NextPage(ctx) if awsv2.SkipSweepError(err) { log.Printf("[WARN] Skipping ECS Service sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + return nil } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing ECS Clusters (%s): %w", region, err)) + return fmt.Errorf("error listing ECS Clusters (%s): %w", region, err) } for _, clusterARN := range page.ClusterArns { @@ -172,16 +168,11 @@ func sweepServices(region string) error { } pages := ecs.NewListServicesPaginator(conn, input) - for pages.HasMorePages() { page, err := pages.NextPage(ctx) - if awsv2.SkipSweepError(err) { - continue - } - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing ECS Services (%s): %w", region, err)) + continue } for _, v := range page.ServiceArns { @@ -199,10 +190,10 @@ func sweepServices(region string) error { err = sweep.SweepOrchestrator(ctx, sweepResources) if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping ECS Services (%s): %w", region, err)) + return fmt.Errorf("error sweeping ECS Services (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func sweepTaskDefinitions(region string) error { @@ -216,7 +207,6 @@ func sweepTaskDefinitions(region string) error { sweepResources := make([]sweep.Sweepable, 0) pages := ecs.NewListTaskDefinitionsPaginator(conn, input) - for pages.HasMorePages() { page, err := pages.NextPage(ctx) From 2476b3e625d0c5d3e66f78092b588a05c746d11a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 17:03:48 -0400 Subject: [PATCH 55/60] r/aws_ecs_task_definition: Set 'arn' in Create. --- internal/service/ecs/task_definition.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/service/ecs/task_definition.go b/internal/service/ecs/task_definition.go index 42c567e08d0..6664ec64600 100644 --- a/internal/service/ecs/task_definition.go +++ b/internal/service/ecs/task_definition.go @@ -546,6 +546,7 @@ func resourceTaskDefinitionCreate(ctx context.Context, d *schema.ResourceData, m taskDefinition := output.TaskDefinition d.SetId(aws.ToString(taskDefinition.Family)) + d.Set(names.AttrARN, taskDefinition.TaskDefinitionArn) // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { @@ -585,8 +586,9 @@ func resourceTaskDefinitionRead(ctx context.Context, d *schema.ResourceData, met } d.SetId(aws.ToString(taskDefinition.Family)) - d.Set(names.AttrARN, taskDefinition.TaskDefinitionArn) - d.Set("arn_without_revision", taskDefinitionARNStripRevision(aws.ToString(taskDefinition.TaskDefinitionArn))) + arn := aws.ToString(taskDefinition.TaskDefinitionArn) + d.Set(names.AttrARN, arn) + d.Set("arn_without_revision", taskDefinitionARNStripRevision(arn)) d.Set("cpu", taskDefinition.Cpu) if err := d.Set("ephemeral_storage", flattenTaskDefinitionEphemeralStorage(taskDefinition.EphemeralStorage)); err != nil { return sdkdiag.AppendErrorf(diags, "setting ephemeral_storage: %s", err) From dd157df847ff29001b1ca1d358abd61fcb951a7e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 17:14:48 -0400 Subject: [PATCH 56/60] Fix 'taskSetParseResourceID'. --- internal/service/ecs/task_set.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/ecs/task_set.go b/internal/service/ecs/task_set.go index ed90c9acb3d..e2cf76a34f2 100644 --- a/internal/service/ecs/task_set.go +++ b/internal/service/ecs/task_set.go @@ -490,7 +490,7 @@ func taskSetCreateResourceID(taskSetID, service, cluster string) string { func taskSetParseResourceID(id string) (string, string, string, error) { parts := strings.Split(id, taskSetResourceIDSeparator) - if len(parts) != 3 || parts[0] == "" || parts[1] == "" || parts[2] == "" { + if len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != "" { return parts[0], parts[1], parts[2], nil } From 7f700ddfc3aad0c7012fb82cd7f74a46a54d9b3b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 19:20:23 -0400 Subject: [PATCH 57/60] Fix 'TestAccECSService_PlacementStrategy_missing'. --- internal/service/ecs/service_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/ecs/service_test.go b/internal/service/ecs/service_test.go index f45deed9f63..51e396b88dc 100644 --- a/internal/service/ecs/service_test.go +++ b/internal/service/ecs/service_test.go @@ -2476,7 +2476,7 @@ resource "aws_ecs_service" "test" { task_definition = aws_ecs_task_definition.test.arn ordered_placement_strategy { - type = %[1]q + type = %[2]q } } `, rName, placementStrategyType) From 1b56aa77216b1fd3a7468fae550728367c9e10a5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jul 2024 19:33:31 -0400 Subject: [PATCH 58/60] ecs: Skip acceptance test errors like 'InvalidParameterException: ECS managed EBS volume configuration was invalid for volume 'vol1'. Encountered 'VolumeTypeNotAvailableInZone' error from AmazonEC2: "The specified zone does not support gp3 volume type."'. --- internal/service/ecs/task_definition_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/ecs/task_definition_test.go b/internal/service/ecs/task_definition_test.go index 694f2547648..ca67aaa7ce3 100644 --- a/internal/service/ecs/task_definition_test.go +++ b/internal/service/ecs/task_definition_test.go @@ -29,6 +29,7 @@ func init() { func testAccErrorCheckSkip(t *testing.T) resource.ErrorCheckFunc { return acctest.ErrorCheckSkipMessagesContaining(t, "Unsupported field 'inferenceAccelerators'", + "Encountered 'VolumeTypeNotAvailableInZone' error from AmazonEC2", ) } From 8887eba134ee058b856980c228dc192360a62854 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Jul 2024 05:43:32 -0400 Subject: [PATCH 59/60] Fix 'TestAccECSService_PlacementStrategy_missing'. --- internal/service/ecs/service_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/ecs/service_test.go b/internal/service/ecs/service_test.go index 51e396b88dc..6d1d9521a35 100644 --- a/internal/service/ecs/service_test.go +++ b/internal/service/ecs/service_test.go @@ -945,7 +945,7 @@ func TestAccECSService_PlacementStrategy_missing(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccServiceConfig_placementStrategyType(rName, ""), - ExpectError: regexache.MustCompile(`expected ordered_placement_strategy.0.type to be one of`), + ExpectError: regexache.MustCompile(`expected type to be one of`), }, }, }) From 3c946096c8faf19562aa9c88a912e4c513cc9cb2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Jul 2024 07:52:04 -0400 Subject: [PATCH 60/60] Fix 'TestAccECSTaskDefinition_invalidContainerDefinition'. --- internal/service/ecs/task_definition.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/service/ecs/task_definition.go b/internal/service/ecs/task_definition.go index 6664ec64600..d7b485129ae 100644 --- a/internal/service/ecs/task_definition.go +++ b/internal/service/ecs/task_definition.go @@ -29,6 +29,7 @@ import ( tfjson "github.com/hashicorp/terraform-provider-aws/internal/json" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + itypes "github.com/hashicorp/terraform-provider-aws/internal/types" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -1216,6 +1217,12 @@ func expandContainerDefinitions(tfString string) ([]awstypes.ContainerDefinition return nil, err } + for i, apiObject := range apiObjects { + if itypes.IsZero(&apiObject) { + return nil, fmt.Errorf("invalid container definition supplied at index (%d)", i) + } + } + return apiObjects, nil }