diff --git a/.changelog/36774.txt b/.changelog/36774.txt new file mode 100644 index 000000000000..44d53925f242 --- /dev/null +++ b/.changelog/36774.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_chime_voice_connector_group: Properly handle voice connector groups deleted out of band +``` diff --git a/go.mod b/go.mod index a06ee9a2225c..8028b705f73f 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/bedrockagent v1.16.0 github.com/aws/aws-sdk-go-v2/service/budgets v1.25.3 github.com/aws/aws-sdk-go-v2/service/chatbot v1.4.3 + github.com/aws/aws-sdk-go-v2/service/chime v1.32.3 github.com/aws/aws-sdk-go-v2/service/chimesdkmediapipelines v1.18.3 github.com/aws/aws-sdk-go-v2/service/chimesdkvoice v1.17.3 github.com/aws/aws-sdk-go-v2/service/cleanrooms v1.14.3 diff --git a/go.sum b/go.sum index 6b34fe844738..398244426a40 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,8 @@ github.com/aws/aws-sdk-go-v2/service/budgets v1.25.3 h1:BfuKcgSyNTzS2N57JSM4uQ/d github.com/aws/aws-sdk-go-v2/service/budgets v1.25.3/go.mod h1:QJ119U4g137qbYZRXqFxtvyARMT88athXWt9gYcRBjM= github.com/aws/aws-sdk-go-v2/service/chatbot v1.4.3 h1:BFVoEcC9czVq0/KHdNheLtPUGjBvu133EfgIF0hO3SI= github.com/aws/aws-sdk-go-v2/service/chatbot v1.4.3/go.mod h1:9jB/CYDhmh+LPD3iRNnu4Zj+9A3AMoBQkxPp1j8reSs= +github.com/aws/aws-sdk-go-v2/service/chime v1.32.3 h1:teC5V+ROwSQM81JEcpM5X1B+YCnf/auLo88wO4h8/iU= +github.com/aws/aws-sdk-go-v2/service/chime v1.32.3/go.mod h1:3C8KIz5+H5gCLKTvQIrZtofV5J6E7NyRCOBvaViDzO8= github.com/aws/aws-sdk-go-v2/service/chimesdkmediapipelines v1.18.3 h1:NY/98Ry+J3xzQXaH9uy8KXya6JiOnoXjFqGLL7aKHLw= github.com/aws/aws-sdk-go-v2/service/chimesdkmediapipelines v1.18.3/go.mod h1:AC5wH108q+kaTSjuQoKoKCH4fxGKoteUMRPb0wLYzGI= github.com/aws/aws-sdk-go-v2/service/chimesdkvoice v1.17.3 h1:e8mAmTy94SOhD/KdTRpocBj6+KOyxjQg7JYN1oBjT08= diff --git a/internal/conns/awsclient_gen.go b/internal/conns/awsclient_gen.go index 803afa93c399..0eedb6dc32f4 100644 --- a/internal/conns/awsclient_gen.go +++ b/internal/conns/awsclient_gen.go @@ -33,6 +33,7 @@ import ( bedrockagent_sdkv2 "github.com/aws/aws-sdk-go-v2/service/bedrockagent" budgets_sdkv2 "github.com/aws/aws-sdk-go-v2/service/budgets" chatbot_sdkv2 "github.com/aws/aws-sdk-go-v2/service/chatbot" + chime_sdkv2 "github.com/aws/aws-sdk-go-v2/service/chime" chimesdkmediapipelines_sdkv2 "github.com/aws/aws-sdk-go-v2/service/chimesdkmediapipelines" chimesdkvoice_sdkv2 "github.com/aws/aws-sdk-go-v2/service/chimesdkvoice" cleanrooms_sdkv2 "github.com/aws/aws-sdk-go-v2/service/cleanrooms" @@ -207,7 +208,6 @@ import ( xray_sdkv2 "github.com/aws/aws-sdk-go-v2/service/xray" appmesh_sdkv1 "github.com/aws/aws-sdk-go/service/appmesh" batch_sdkv1 "github.com/aws/aws-sdk-go/service/batch" - chime_sdkv1 "github.com/aws/aws-sdk-go/service/chime" connect_sdkv1 "github.com/aws/aws-sdk-go/service/connect" directconnect_sdkv1 "github.com/aws/aws-sdk-go/service/directconnect" ec2_sdkv1 "github.com/aws/aws-sdk-go/service/ec2" @@ -386,8 +386,8 @@ func (c *AWSClient) ChatbotClient(ctx context.Context) *chatbot_sdkv2.Client { return errs.Must(client[*chatbot_sdkv2.Client](ctx, c, names.Chatbot, make(map[string]any))) } -func (c *AWSClient) ChimeConn(ctx context.Context) *chime_sdkv1.Chime { - return errs.Must(conn[*chime_sdkv1.Chime](ctx, c, names.Chime, make(map[string]any))) +func (c *AWSClient) ChimeClient(ctx context.Context) *chime_sdkv2.Client { + return errs.Must(client[*chime_sdkv2.Client](ctx, c, names.Chime, make(map[string]any))) } func (c *AWSClient) ChimeSDKMediaPipelinesClient(ctx context.Context) *chimesdkmediapipelines_sdkv2.Client { diff --git a/internal/service/chime/service_endpoint_resolver_gen.go b/internal/service/chime/service_endpoint_resolver_gen.go index b143693a7b4f..91792ee1acd0 100644 --- a/internal/service/chime/service_endpoint_resolver_gen.go +++ b/internal/service/chime/service_endpoint_resolver_gen.go @@ -6,65 +6,63 @@ import ( "context" "fmt" "net" - "net/url" - endpoints_sdkv1 "github.com/aws/aws-sdk-go/aws/endpoints" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + chime_sdkv2 "github.com/aws/aws-sdk-go-v2/service/chime" + smithyendpoints "github.com/aws/smithy-go/endpoints" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-provider-aws/internal/errs" ) -var _ endpoints_sdkv1.Resolver = resolverSDKv1{} +var _ chime_sdkv2.EndpointResolverV2 = resolverSDKv2{} -type resolverSDKv1 struct { - ctx context.Context +type resolverSDKv2 struct { + defaultResolver chime_sdkv2.EndpointResolverV2 } -func newEndpointResolverSDKv1(ctx context.Context) resolverSDKv1 { - return resolverSDKv1{ - ctx: ctx, +func newEndpointResolverSDKv2() resolverSDKv2 { + return resolverSDKv2{ + defaultResolver: chime_sdkv2.NewDefaultEndpointResolverV2(), } } -func (r resolverSDKv1) EndpointFor(service, region string, opts ...func(*endpoints_sdkv1.Options)) (endpoint endpoints_sdkv1.ResolvedEndpoint, err error) { - ctx := r.ctx +func (r resolverSDKv2) ResolveEndpoint(ctx context.Context, params chime_sdkv2.EndpointParameters) (endpoint smithyendpoints.Endpoint, err error) { + params = params.WithDefaults() + useFIPS := aws_sdkv2.ToBool(params.UseFIPS) - var opt endpoints_sdkv1.Options - opt.Set(opts...) - - useFIPS := opt.UseFIPSEndpoint == endpoints_sdkv1.FIPSEndpointStateEnabled + if eps := params.Endpoint; aws_sdkv2.ToString(eps) != "" { + tflog.Debug(ctx, "setting endpoint", map[string]any{ + "tf_aws.endpoint": endpoint, + }) - defaultResolver := endpoints_sdkv1.DefaultResolver() + if useFIPS { + tflog.Debug(ctx, "endpoint set, ignoring UseFIPSEndpoint setting") + params.UseFIPS = aws_sdkv2.Bool(false) + } - if useFIPS { + return r.defaultResolver.ResolveEndpoint(ctx, params) + } else if useFIPS { ctx = tflog.SetField(ctx, "tf_aws.use_fips", useFIPS) - endpoint, err = defaultResolver.EndpointFor(service, region, opts...) + endpoint, err = r.defaultResolver.ResolveEndpoint(ctx, params) if err != nil { return endpoint, err } tflog.Debug(ctx, "endpoint resolved", map[string]any{ - "tf_aws.endpoint": endpoint.URL, + "tf_aws.endpoint": endpoint.URI.String(), }) - var endpointURL *url.URL - endpointURL, err = url.Parse(endpoint.URL) - if err != nil { - return endpoint, err - } - - hostname := endpointURL.Hostname() + hostname := endpoint.URI.Hostname() _, err = net.LookupHost(hostname) if err != nil { if dnsErr, ok := errs.As[*net.DNSError](err); ok && dnsErr.IsNotFound { tflog.Debug(ctx, "default endpoint host not found, disabling FIPS", map[string]any{ "tf_aws.hostname": hostname, }) - opts = append(opts, func(o *endpoints_sdkv1.Options) { - o.UseFIPSEndpoint = endpoints_sdkv1.FIPSEndpointStateDisabled - }) + params.UseFIPS = aws_sdkv2.Bool(false) } else { - err = fmt.Errorf("looking up accessanalyzer endpoint %q: %s", hostname, err) + err = fmt.Errorf("looking up chime endpoint %q: %s", hostname, err) return } } else { @@ -72,5 +70,13 @@ func (r resolverSDKv1) EndpointFor(service, region string, opts ...func(*endpoin } } - return defaultResolver.EndpointFor(service, region, opts...) + return r.defaultResolver.ResolveEndpoint(ctx, params) +} + +func withBaseEndpoint(endpoint string) func(*chime_sdkv2.Options) { + return func(o *chime_sdkv2.Options) { + if endpoint != "" { + o.BaseEndpoint = aws_sdkv2.String(endpoint) + } + } } diff --git a/internal/service/chime/service_endpoints_gen_test.go b/internal/service/chime/service_endpoints_gen_test.go index 9609fc1178f0..1ed11cd92e56 100644 --- a/internal/service/chime/service_endpoints_gen_test.go +++ b/internal/service/chime/service_endpoints_gen_test.go @@ -4,18 +4,22 @@ package chime_test import ( "context" + "errors" "fmt" "maps" "net" "net/url" "os" "path/filepath" + "reflect" "strings" "testing" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/endpoints" - chime_sdkv1 "github.com/aws/aws-sdk-go/service/chime" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + chime_sdkv2 "github.com/aws/aws-sdk-go-v2/service/chime" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" "github.com/google/go-cmp/cmp" "github.com/hashicorp/aws-sdk-go-base/v2/servicemocks" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -240,54 +244,63 @@ func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.S } func defaultEndpoint(region string) (url.URL, error) { - r := endpoints.DefaultResolver() + r := chime_sdkv2.NewDefaultEndpointResolverV2() - ep, err := r.EndpointFor(chime_sdkv1.EndpointsID, region) + ep, err := r.ResolveEndpoint(context.Background(), chime_sdkv2.EndpointParameters{ + Region: aws_sdkv2.String(region), + }) if err != nil { return url.URL{}, err } - url, _ := url.Parse(ep.URL) - - if url.Path == "" { - url.Path = "/" + if ep.URI.Path == "" { + ep.URI.Path = "/" } - return *url, nil + return ep.URI, nil } func defaultFIPSEndpoint(region string) (url.URL, error) { - r := endpoints.DefaultResolver() + r := chime_sdkv2.NewDefaultEndpointResolverV2() - ep, err := r.EndpointFor(chime_sdkv1.EndpointsID, region, func(opt *endpoints.Options) { - opt.UseFIPSEndpoint = endpoints.FIPSEndpointStateEnabled + ep, err := r.ResolveEndpoint(context.Background(), chime_sdkv2.EndpointParameters{ + Region: aws_sdkv2.String(region), + UseFIPS: aws_sdkv2.Bool(true), }) if err != nil { return url.URL{}, err } - url, _ := url.Parse(ep.URL) - - if url.Path == "" { - url.Path = "/" + if ep.URI.Path == "" { + ep.URI.Path = "/" } - return *url, nil + return ep.URI, nil } func callService(ctx context.Context, t *testing.T, meta *conns.AWSClient) apiCallParams { t.Helper() - client := meta.ChimeConn(ctx) + client := meta.ChimeClient(ctx) - req, _ := client.ListAccountsRequest(&chime_sdkv1.ListAccountsInput{}) + var result apiCallParams - req.HTTPRequest.URL.Path = "/" - - return apiCallParams{ - endpoint: req.HTTPRequest.URL.String(), - region: aws_sdkv1.StringValue(client.Config.Region), + _, err := client.ListAccounts(ctx, &chime_sdkv2.ListAccountsInput{}, + func(opts *chime_sdkv2.Options) { + opts.APIOptions = append(opts.APIOptions, + addRetrieveEndpointURLMiddleware(t, &result.endpoint), + addRetrieveRegionMiddleware(&result.region), + addCancelRequestMiddleware(), + ) + }, + ) + if err == nil { + t.Fatal("Expected an error, got none") + } else if !errors.Is(err, errCancelOperation) { + t.Fatalf("Unexpected error: %s", err) } + + return result } func withNoConfig(_ *caseSetup) { @@ -466,6 +479,89 @@ func testEndpointCase(t *testing.T, region string, testcase endpointTestCase, ca } } +func addRetrieveEndpointURLMiddleware(t *testing.T, endpoint *string) func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Finalize.Add( + retrieveEndpointURLMiddleware(t, endpoint), + middleware.After, + ) + } +} + +func retrieveEndpointURLMiddleware(t *testing.T, endpoint *string) middleware.FinalizeMiddleware { + return middleware.FinalizeMiddlewareFunc( + "Test: Retrieve Endpoint", + func(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) { + t.Helper() + + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + t.Fatalf("Expected *github.com/aws/smithy-go/transport/http.Request, got %s", fullTypeName(in.Request)) + } + + url := request.URL + url.RawQuery = "" + url.Path = "/" + + *endpoint = url.String() + + return next.HandleFinalize(ctx, in) + }) +} + +func addRetrieveRegionMiddleware(region *string) func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Serialize.Add( + retrieveRegionMiddleware(region), + middleware.After, + ) + } +} + +func retrieveRegionMiddleware(region *string) middleware.SerializeMiddleware { + return middleware.SerializeMiddlewareFunc( + "Test: Retrieve Region", + func(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) (middleware.SerializeOutput, middleware.Metadata, error) { + *region = awsmiddleware.GetRegion(ctx) + + return next.HandleSerialize(ctx, in) + }, + ) +} + +var errCancelOperation = fmt.Errorf("Test: Canceling request") + +func addCancelRequestMiddleware() func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Finalize.Add( + cancelRequestMiddleware(), + middleware.After, + ) + } +} + +// cancelRequestMiddleware creates a Smithy middleware that intercepts the request before sending and cancels it +func cancelRequestMiddleware() middleware.FinalizeMiddleware { + return middleware.FinalizeMiddlewareFunc( + "Test: Cancel Requests", + func(_ context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) { + return middleware.FinalizeOutput{}, middleware.Metadata{}, errCancelOperation + }) +} + +func fullTypeName(i interface{}) string { + return fullValueTypeName(reflect.ValueOf(i)) +} + +func fullValueTypeName(v reflect.Value) string { + if v.Kind() == reflect.Ptr { + return "*" + fullValueTypeName(reflect.Indirect(v)) + } + + requestType := v.Type() + return fmt.Sprintf("%s.%s", requestType.PkgPath(), requestType.Name()) +} + func generateSharedConfigFile(config configFile) string { var buf strings.Builder diff --git a/internal/service/chime/service_package.go b/internal/service/chime/service_package.go deleted file mode 100644 index 2ddfe0505eae..000000000000 --- a/internal/service/chime/service_package.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package chime - -import ( - "context" - - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - request_sdkv1 "github.com/aws/aws-sdk-go/aws/request" - chime_sdkv1 "github.com/aws/aws-sdk-go/service/chime" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" -) - -// CustomizeConn customizes a new AWS SDK for Go v1 client for this service package's AWS API. -func (p *servicePackage) CustomizeConn(ctx context.Context, conn *chime_sdkv1.Chime) (*chime_sdkv1.Chime, error) { - conn.Handlers.Retry.PushBack(func(r *request_sdkv1.Request) { - // When calling CreateVoiceConnector across multiple resources, - // the API can randomly return a BadRequestException without explanation - if r.Operation.Name == "CreateVoiceConnector" { - if tfawserr.ErrMessageContains(r.Error, chime_sdkv1.ErrCodeBadRequestException, "Service received a bad request") { - r.Retryable = aws_sdkv1.Bool(true) - } - } - }) - - return conn, nil -} diff --git a/internal/service/chime/service_package_gen.go b/internal/service/chime/service_package_gen.go index 23f3610851b8..e4c7d19dbf8d 100644 --- a/internal/service/chime/service_package_gen.go +++ b/internal/service/chime/service_package_gen.go @@ -5,10 +5,8 @@ package chime import ( "context" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - session_sdkv1 "github.com/aws/aws-sdk-go/aws/session" - chime_sdkv1 "github.com/aws/aws-sdk-go/service/chime" - "github.com/hashicorp/terraform-plugin-log/tflog" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + chime_sdkv2 "github.com/aws/aws-sdk-go-v2/service/chime" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/types" "github.com/hashicorp/terraform-provider-aws/names" @@ -69,22 +67,14 @@ func (p *servicePackage) ServicePackageName() string { return names.Chime } -// 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) (*chime_sdkv1.Chime, error) { - sess := config[names.AttrSession].(*session_sdkv1.Session) +// 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) (*chime_sdkv2.Client, error) { + cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config)) - 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) - } else { - cfg.EndpointResolver = newEndpointResolverSDKv1(ctx) - } - - return chime_sdkv1.New(sess.Copy(&cfg)), nil + return chime_sdkv2.NewFromConfig(cfg, + chime_sdkv2.WithEndpointResolverV2(newEndpointResolverSDKv2()), + withBaseEndpoint(config[names.AttrEndpoint].(string)), + ), nil } func ServicePackage(ctx context.Context) conns.ServicePackage { diff --git a/internal/service/chime/voice_connector_group.go b/internal/service/chime/voice_connector_group.go index 42d80c6f73f1..ef6f3042abdb 100644 --- a/internal/service/chime/voice_connector_group.go +++ b/internal/service/chime/voice_connector_group.go @@ -97,7 +97,7 @@ func resourceVoiceConnectorGroupRead(ctx context.Context, d *schema.ResourceData resp, err = findVoiceConnectorGroupByID(ctx, conn, d.Id()) } - if !d.IsNewResource() && tfresource.NotFound(err) { + if !d.IsNewResource() && errs.IsA[*awstypes.NotFoundException](err) { log.Printf("[WARN] Chime Voice conector group %s not found", d.Id()) d.SetId("") return diags @@ -147,6 +147,12 @@ func resourceVoiceConnectorGroupDelete(ctx context.Context, d *schema.ResourceDa conn := meta.(*conns.AWSClient).ChimeSDKVoiceClient(ctx) if v, ok := d.GetOk("connector"); ok && v.(*schema.Set).Len() > 0 { + // Exit before attempting connector updates if the group does not exist + _, err := findVoiceConnectorGroupByID(ctx, conn, d.Id()) + if errs.IsA[*awstypes.NotFoundException](err) { + return diags + } + if err := resourceVoiceConnectorGroupUpdate(ctx, d, meta); err != nil { return err } diff --git a/internal/service/chime/voice_connector_group_test.go b/internal/service/chime/voice_connector_group_test.go index d2409f306a8e..f844430b23fe 100644 --- a/internal/service/chime/voice_connector_group_test.go +++ b/internal/service/chime/voice_connector_group_test.go @@ -14,8 +14,8 @@ 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" tfchime "github.com/hashicorp/terraform-provider-aws/internal/service/chime" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -196,7 +196,7 @@ func testAccCheckVoiceConnectorGroupDestroy(ctx context.Context) resource.TestCh return tfchime.FindVoiceConnectorGroupByID(ctx, conn, rs.Primary.ID) }) - if tfresource.NotFound(err) { + if errs.IsA[*awstypes.NotFoundException](err) { continue } diff --git a/names/data/names_data.hcl b/names/data/names_data.hcl index c0a14595d885..22e807901133 100644 --- a/names/data/names_data.hcl +++ b/names/data/names_data.hcl @@ -1115,7 +1115,7 @@ service "chime" { sdk { id = "Chime" - client_version = [1] + client_version = [2] } names { diff --git a/names/names.go b/names/names.go index e57c7a8c4eef..f0e1c67004ae 100644 --- a/names/names.go +++ b/names/names.go @@ -48,6 +48,7 @@ const ( BedrockAgentEndpointID = "bedrockagent" BedrockEndpointID = "bedrock" BudgetsEndpointID = "budgets" + ChimeEndpointID = "chime" ChimeSDKMediaPipelinesEndpointID = "media-pipelines-chime" ChimeSDKVoiceEndpointID = "voice-chime" Cloud9EndpointID = "cloud9"