From 31e250da117dd6bd80ab129b87fe1a31b63923e0 Mon Sep 17 00:00:00 2001 From: aristosvo <8375124+aristosvo@users.noreply.github.com> Date: Tue, 25 Nov 2025 21:32:25 +0100 Subject: [PATCH 1/4] [New Service]: MWAA Serverless --- .ci/.semgrep-service-name0.yml | 14 + .ci/.semgrep-service-name1.yml | 43 +- .ci/.semgrep-service-name2.yml | 105 +-- .ci/.semgrep-service-name3.yml | 15 + .github/labeler-issue-triage.yml | 2 + .github/labeler-pr-triage.yml | 6 + .../components/generated/services_all.kt | 1 + infrastructure/repository/labels-service.tf | 1 + internal/conns/awsclient_gen.go | 5 + internal/provider/fwprovider/provider_gen.go | 7 + internal/provider/provider_gen.go | 8 + .../service_endpoints_gen_test.go | 601 ++++++++++++++++++ names/consts_gen.go | 2 + names/data/names_data.hcl | 23 + website/allowed-subcategories.txt | 1 + .../custom-service-endpoints.html.markdown | 1 + 16 files changed, 777 insertions(+), 58 deletions(-) create mode 100644 internal/service/mwaaserverless/service_endpoints_gen_test.go diff --git a/.ci/.semgrep-service-name0.yml b/.ci/.semgrep-service-name0.yml index b6af79c4d859..9b7eb3499304 100644 --- a/.ci/.semgrep-service-name0.yml +++ b/.ci/.semgrep-service-name0.yml @@ -4468,3 +4468,17 @@ rules: patterns: - pattern-regex: "(?i)ConnectCases" severity: WARNING + - id: connectcases-in-var-name + languages: + - go + message: Do not use "ConnectCases" in var name inside connectcases package + paths: + include: + - internal/service/connectcases + patterns: + - pattern: var $NAME = ... + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)ConnectCases" + severity: WARNING diff --git a/.ci/.semgrep-service-name1.yml b/.ci/.semgrep-service-name1.yml index 1e988b95ec16..9080a1a63aa9 100644 --- a/.ci/.semgrep-service-name1.yml +++ b/.ci/.semgrep-service-name1.yml @@ -1,19 +1,5 @@ # Generated by internal/generate/servicesemgrep/main.go; DO NOT EDIT. rules: - - id: connectcases-in-var-name - languages: - - go - message: Do not use "ConnectCases" in var name inside connectcases package - paths: - include: - - internal/service/connectcases - patterns: - - pattern: var $NAME = ... - - metavariable-pattern: - metavariable: $NAME - patterns: - - pattern-regex: "(?i)ConnectCases" - severity: WARNING - id: controltower-in-func-name languages: - go @@ -4457,3 +4443,32 @@ rules: patterns: - pattern-regex: "(?i)IoTEvents" severity: WARNING + - id: iotevents-in-var-name + languages: + - go + message: Do not use "IoTEvents" in var name inside iotevents package + paths: + include: + - internal/service/iotevents + patterns: + - pattern: var $NAME = ... + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)IoTEvents" + severity: WARNING + - id: ipam-in-test-name + languages: + - go + message: Include "IPAM" in test name + paths: + include: + - internal/service/ec2/ipam_*_test.go + patterns: + - pattern: func $NAME( ... ) + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-not-regex: "^TestAccIPAM" + - pattern-regex: ^TestAcc.* + severity: WARNING diff --git a/.ci/.semgrep-service-name2.yml b/.ci/.semgrep-service-name2.yml index 9ffd0793d9b1..ac0c5a048af8 100644 --- a/.ci/.semgrep-service-name2.yml +++ b/.ci/.semgrep-service-name2.yml @@ -1,34 +1,5 @@ # Generated by internal/generate/servicesemgrep/main.go; DO NOT EDIT. rules: - - id: iotevents-in-var-name - languages: - - go - message: Do not use "IoTEvents" in var name inside iotevents package - paths: - include: - - internal/service/iotevents - patterns: - - pattern: var $NAME = ... - - metavariable-pattern: - metavariable: $NAME - patterns: - - pattern-regex: "(?i)IoTEvents" - severity: WARNING - - id: ipam-in-test-name - languages: - - go - message: Include "IPAM" in test name - paths: - include: - - internal/service/ec2/ipam_*_test.go - patterns: - - pattern: func $NAME( ... ) - - metavariable-pattern: - metavariable: $NAME - patterns: - - pattern-not-regex: "^TestAccIPAM" - - pattern-regex: ^TestAcc.* - severity: WARNING - id: ivs-in-func-name languages: - go @@ -2486,6 +2457,67 @@ rules: patterns: - pattern-regex: "(?i)MWAA" severity: WARNING + - id: mwaaserverless-in-func-name + languages: + - go + message: Do not use "MWAAServerless" in func name inside mwaaserverless package + paths: + include: + - internal/service/mwaaserverless + exclude: + - internal/service/mwaaserverless/list_pages_gen.go + patterns: + - pattern: func $NAME( ... ) + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)MWAAServerless" + - focus-metavariable: $NAME + - pattern-not: func $NAME($T *testing.T) + severity: WARNING + - id: mwaaserverless-in-test-name + languages: + - go + message: Include "MWAAServerless" in test name + paths: + include: + - internal/service/mwaaserverless/*_test.go + patterns: + - pattern: func $NAME( ... ) + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-not-regex: "^TestAccMWAAServerless" + - pattern-regex: ^TestAcc.* + severity: WARNING + - id: mwaaserverless-in-const-name + languages: + - go + message: Do not use "MWAAServerless" in const name inside mwaaserverless package + paths: + include: + - internal/service/mwaaserverless + patterns: + - pattern: const $NAME = ... + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)MWAAServerless" + severity: WARNING + - id: mwaaserverless-in-var-name + languages: + - go + message: Do not use "MWAAServerless" in var name inside mwaaserverless package + paths: + include: + - internal/service/mwaaserverless + patterns: + - pattern: var $NAME = ... + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)MWAAServerless" + severity: WARNING - id: neptune-in-func-name languages: - go @@ -4448,18 +4480,3 @@ rules: - focus-metavariable: $NAME - pattern-not: func $NAME($T *testing.T) severity: WARNING - - id: redshift-in-test-name - languages: - - go - message: Include "Redshift" in test name - paths: - include: - - internal/service/redshift/*_test.go - patterns: - - pattern: func $NAME( ... ) - - metavariable-pattern: - metavariable: $NAME - patterns: - - pattern-not-regex: "^TestAccRedshift" - - pattern-regex: ^TestAcc.* - severity: WARNING diff --git a/.ci/.semgrep-service-name3.yml b/.ci/.semgrep-service-name3.yml index e4160705c61a..616065b2fc06 100644 --- a/.ci/.semgrep-service-name3.yml +++ b/.ci/.semgrep-service-name3.yml @@ -1,5 +1,20 @@ # Generated by internal/generate/servicesemgrep/main.go; DO NOT EDIT. rules: + - id: redshift-in-test-name + languages: + - go + message: Include "Redshift" in test name + paths: + include: + - internal/service/redshift/*_test.go + patterns: + - pattern: func $NAME( ... ) + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-not-regex: "^TestAccRedshift" + - pattern-regex: ^TestAcc.* + severity: WARNING - id: redshift-in-const-name languages: - go diff --git a/.github/labeler-issue-triage.yml b/.github/labeler-issue-triage.yml index 924aa5a7febc..5c3e86fda88a 100644 --- a/.github/labeler-issue-triage.yml +++ b/.github/labeler-issue-triage.yml @@ -487,6 +487,8 @@ service/mturk: - '((\*|-)\s*`?|(data|resource)\s+"?)aws_mturk_' service/mwaa: - '((\*|-)\s*`?|(data|resource)\s+"?)aws_mwaa_' +service/mwaaserverless: + - '((\*|-)\s*`?|(data|resource)\s+"?)aws_mwaaserverless_' service/neptune: - '((\*|-)\s*`?|(data|resource)\s+"?)aws_neptune_' service/neptunegraph: diff --git a/.github/labeler-pr-triage.yml b/.github/labeler-pr-triage.yml index fa54dda754cf..a86f5329ee97 100644 --- a/.github/labeler-pr-triage.yml +++ b/.github/labeler-pr-triage.yml @@ -1547,6 +1547,12 @@ service/mwaa: - any-glob-to-any-file: - 'internal/service/mwaa/**/*' - 'website/**/mwaa_*' +service/mwaaserverless: + - any: + - changed-files: + - any-glob-to-any-file: + - 'internal/service/mwaaserverless/**/*' + - 'website/**/mwaaserverless_*' service/neptune: - any: - changed-files: diff --git a/.teamcity/components/generated/services_all.kt b/.teamcity/components/generated/services_all.kt index fc6df21832b0..73555177e9b9 100644 --- a/.teamcity/components/generated/services_all.kt +++ b/.teamcity/components/generated/services_all.kt @@ -162,6 +162,7 @@ val services = mapOf( "mgn" to ServiceSpec("Application Migration (Mgn)"), "mq" to ServiceSpec("MQ", vpcLock = true), "mwaa" to ServiceSpec("MWAA (Managed Workflows for Apache Airflow)", vpcLock = true), + "mwaaserverless" to ServiceSpec("MWAA (Managed Workflows for Apache Airflow) Serverless"), "neptune" to ServiceSpec("Neptune"), "neptunegraph" to ServiceSpec("Neptune Analytics"), "networkfirewall" to ServiceSpec("Network Firewall", vpcLock = true), diff --git a/infrastructure/repository/labels-service.tf b/infrastructure/repository/labels-service.tf index cd13188084d7..cef1508044a9 100644 --- a/infrastructure/repository/labels-service.tf +++ b/infrastructure/repository/labels-service.tf @@ -231,6 +231,7 @@ variable "service_labels" { "mq", "mturk", "mwaa", + "mwaaserverless", "neptune", "neptunegraph", "networkfirewall", diff --git a/internal/conns/awsclient_gen.go b/internal/conns/awsclient_gen.go index 692d0901fa18..955855af2422 100644 --- a/internal/conns/awsclient_gen.go +++ b/internal/conns/awsclient_gen.go @@ -165,6 +165,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/mgn" "github.com/aws/aws-sdk-go-v2/service/mq" "github.com/aws/aws-sdk-go-v2/service/mwaa" + "github.com/aws/aws-sdk-go-v2/service/mwaaserverless" "github.com/aws/aws-sdk-go-v2/service/neptune" "github.com/aws/aws-sdk-go-v2/service/neptunegraph" "github.com/aws/aws-sdk-go-v2/service/networkfirewall" @@ -866,6 +867,10 @@ func (c *AWSClient) MWAAClient(ctx context.Context) *mwaa.Client { return errs.Must(client[*mwaa.Client](ctx, c, names.MWAA, make(map[string]any))) } +func (c *AWSClient) MWAAServerlessClient(ctx context.Context) *mwaaserverless.Client { + return errs.Must(client[*mwaaserverless.Client](ctx, c, names.MWAAServerless, make(map[string]any))) +} + func (c *AWSClient) Macie2Client(ctx context.Context) *macie2.Client { return errs.Must(client[*macie2.Client](ctx, c, names.Macie2, make(map[string]any))) } diff --git a/internal/provider/fwprovider/provider_gen.go b/internal/provider/fwprovider/provider_gen.go index d009cf3fbb95..b7af02258521 100644 --- a/internal/provider/fwprovider/provider_gen.go +++ b/internal/provider/fwprovider/provider_gen.go @@ -1307,6 +1307,13 @@ func endpointsBlock() schema.SetNestedBlock { Description: "Use this to override the default service endpoint URL", }, + // mwaaserverless + + "mwaaserverless": schema.StringAttribute{ + Optional: true, + Description: "Use this to override the default service endpoint URL", + }, + // neptune "neptune": schema.StringAttribute{ diff --git a/internal/provider/provider_gen.go b/internal/provider/provider_gen.go index 8dd2b893a417..19a36b6d18f8 100644 --- a/internal/provider/provider_gen.go +++ b/internal/provider/provider_gen.go @@ -1511,6 +1511,14 @@ func endpointsSchema() *schema.Schema { Description: "Use this to override the default service endpoint URL", }, + // mwaaserverless + + "mwaaserverless": { + Type: schema.TypeString, + Optional: true, + Description: "Use this to override the default service endpoint URL", + }, + // neptune "neptune": { diff --git a/internal/service/mwaaserverless/service_endpoints_gen_test.go b/internal/service/mwaaserverless/service_endpoints_gen_test.go new file mode 100644 index 000000000000..b4b7c8cce4a6 --- /dev/null +++ b/internal/service/mwaaserverless/service_endpoints_gen_test.go @@ -0,0 +1,601 @@ +// Code generated by internal/generate/serviceendpointtests/main.go; DO NOT EDIT. + +package mwaaserverless_test + +import ( + "context" + "errors" + "fmt" + "maps" + "net" + "net/url" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/mwaaserverless" + "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" + terraformsdk "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "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/provider" + "github.com/hashicorp/terraform-provider-aws/names" +) + +type endpointTestCase struct { + with []setupFunc + expected caseExpectations +} + +type caseSetup struct { + config map[string]any + configFile configFile + environmentVariables map[string]string +} + +type configFile struct { + baseUrl string + serviceUrl string +} + +type caseExpectations struct { + diags diag.Diagnostics + endpoint string + region string +} + +type apiCallParams struct { + endpoint string + region string +} + +type setupFunc func(setup *caseSetup) + +type callFunc func(ctx context.Context, t *testing.T, meta *conns.AWSClient) apiCallParams + +const ( + packageNameConfigEndpoint = "https://packagename-config.endpoint.test/" + awsServiceEnvvarEndpoint = "https://service-envvar.endpoint.test/" + baseEnvvarEndpoint = "https://base-envvar.endpoint.test/" + serviceConfigFileEndpoint = "https://service-configfile.endpoint.test/" + baseConfigFileEndpoint = "https://base-configfile.endpoint.test/" +) + +const ( + packageName = "mwaaserverless" + awsEnvVar = "AWS_ENDPOINT_URL_MWAA_SERVERLESS" + baseEnvVar = "AWS_ENDPOINT_URL" + configParam = "mwaa_serverless" +) + +const ( + expectedCallRegion = "us-west-2" //lintignore:AWSAT003 +) + +func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.Setenv + const providerRegion = "us-west-2" //lintignore:AWSAT003 + const expectedEndpointRegion = providerRegion + + testcases := map[string]endpointTestCase{ + "no config": { + with: []setupFunc{withNoConfig}, + expected: expectDefaultEndpoint(t, expectedEndpointRegion), + }, + + // Package name endpoint on Config + + "package name endpoint config": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides aws service envvar": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withAwsEnvVar, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides base envvar": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withBaseEnvVar, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides service config file": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withServiceEndpointInConfigFile, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides base config file": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withBaseEndpointInConfigFile, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + // Service endpoint in AWS envvar + + "service aws envvar": { + with: []setupFunc{ + withAwsEnvVar, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + "service aws envvar overrides base envvar": { + with: []setupFunc{ + withAwsEnvVar, + withBaseEnvVar, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + "service aws envvar overrides service config file": { + with: []setupFunc{ + withAwsEnvVar, + withServiceEndpointInConfigFile, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + "service aws envvar overrides base config file": { + with: []setupFunc{ + withAwsEnvVar, + withBaseEndpointInConfigFile, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + // Base endpoint in envvar + + "base endpoint envvar": { + with: []setupFunc{ + withBaseEnvVar, + }, + expected: expectBaseEnvVarEndpoint(), + }, + + "base endpoint envvar overrides service config file": { + with: []setupFunc{ + withBaseEnvVar, + withServiceEndpointInConfigFile, + }, + expected: expectBaseEnvVarEndpoint(), + }, + + "base endpoint envvar overrides base config file": { + with: []setupFunc{ + withBaseEnvVar, + withBaseEndpointInConfigFile, + }, + expected: expectBaseEnvVarEndpoint(), + }, + + // Service endpoint in config file + + "service config file": { + with: []setupFunc{ + withServiceEndpointInConfigFile, + }, + expected: expectServiceConfigFileEndpoint(), + }, + + "service config file overrides base config file": { + with: []setupFunc{ + withServiceEndpointInConfigFile, + withBaseEndpointInConfigFile, + }, + expected: expectServiceConfigFileEndpoint(), + }, + + // Base endpoint in config file + + "base endpoint config file": { + with: []setupFunc{ + withBaseEndpointInConfigFile, + }, + expected: expectBaseConfigFileEndpoint(), + }, + + // Use FIPS endpoint on Config + + "use fips config": { + with: []setupFunc{ + withUseFIPSInConfig, + }, + expected: expectDefaultFIPSEndpoint(t, expectedEndpointRegion), + }, + + "use fips config with package name endpoint config": { + with: []setupFunc{ + withUseFIPSInConfig, + withPackageNameEndpointInConfig, + }, + expected: expectPackageNameConfigEndpoint(), + }, + } + + for name, testcase := range testcases { //nolint:paralleltest // uses t.Setenv + t.Run(name, func(t *testing.T) { + testEndpointCase(t, providerRegion, testcase, callService) + }) + } +} + +func defaultEndpoint(region string) (url.URL, error) { + r := mwaaserverless.NewDefaultEndpointResolverV2() + + ep, err := r.ResolveEndpoint(context.Background(), mwaaserverless.EndpointParameters{ + Region: aws.String(region), + }) + if err != nil { + return url.URL{}, err + } + + if ep.URI.Path == "" { + ep.URI.Path = "/" + } + + return ep.URI, nil +} + +func defaultFIPSEndpoint(region string) (url.URL, error) { + r := mwaaserverless.NewDefaultEndpointResolverV2() + + ep, err := r.ResolveEndpoint(context.Background(), mwaaserverless.EndpointParameters{ + Region: aws.String(region), + UseFIPS: aws.Bool(true), + }) + if err != nil { + return url.URL{}, err + } + + if ep.URI.Path == "" { + ep.URI.Path = "/" + } + + return ep.URI, nil +} + +func callService(ctx context.Context, t *testing.T, meta *conns.AWSClient) apiCallParams { + t.Helper() + + client := meta.MWAAServerlessClient(ctx) + + var result apiCallParams + + input := mwaaserverless.ListWorkflowsInput{} + _, err := client.ListWorkflows(ctx, &input, + func(opts *mwaaserverless.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) { + // no-op +} + +func withPackageNameEndpointInConfig(setup *caseSetup) { + if _, ok := setup.config[names.AttrEndpoints]; !ok { + setup.config[names.AttrEndpoints] = []any{ + map[string]any{}, + } + } + endpoints := setup.config[names.AttrEndpoints].([]any)[0].(map[string]any) + endpoints[packageName] = packageNameConfigEndpoint +} + +func withAwsEnvVar(setup *caseSetup) { + setup.environmentVariables[awsEnvVar] = awsServiceEnvvarEndpoint +} + +func withBaseEnvVar(setup *caseSetup) { + setup.environmentVariables[baseEnvVar] = baseEnvvarEndpoint +} + +func withServiceEndpointInConfigFile(setup *caseSetup) { + setup.configFile.serviceUrl = serviceConfigFileEndpoint +} + +func withBaseEndpointInConfigFile(setup *caseSetup) { + setup.configFile.baseUrl = baseConfigFileEndpoint +} + +func withUseFIPSInConfig(setup *caseSetup) { + setup.config["use_fips_endpoint"] = true +} + +func expectDefaultEndpoint(t *testing.T, region string) caseExpectations { + t.Helper() + + endpoint, err := defaultEndpoint(region) + if err != nil { + t.Fatalf("resolving accessanalyzer default endpoint: %s", err) + } + + return caseExpectations{ + endpoint: endpoint.String(), + region: expectedCallRegion, + } +} + +func expectDefaultFIPSEndpoint(t *testing.T, region string) caseExpectations { + t.Helper() + + endpoint, err := defaultFIPSEndpoint(region) + if err != nil { + t.Fatalf("resolving accessanalyzer FIPS endpoint: %s", err) + } + + hostname := endpoint.Hostname() + _, err = net.LookupHost(hostname) + if dnsErr, ok := errs.As[*net.DNSError](err); ok && dnsErr.IsNotFound { + return expectDefaultEndpoint(t, region) + } else if err != nil { + t.Fatalf("looking up accessanalyzer endpoint %q: %s", hostname, err) + } + + return caseExpectations{ + endpoint: endpoint.String(), + region: expectedCallRegion, + } +} + +func expectPackageNameConfigEndpoint() caseExpectations { + return caseExpectations{ + endpoint: packageNameConfigEndpoint, + region: expectedCallRegion, + } +} + +func expectAwsEnvVarEndpoint() caseExpectations { + return caseExpectations{ + endpoint: awsServiceEnvvarEndpoint, + region: expectedCallRegion, + } +} + +func expectBaseEnvVarEndpoint() caseExpectations { + return caseExpectations{ + endpoint: baseEnvvarEndpoint, + region: expectedCallRegion, + } +} + +func expectServiceConfigFileEndpoint() caseExpectations { + return caseExpectations{ + endpoint: serviceConfigFileEndpoint, + region: expectedCallRegion, + } +} + +func expectBaseConfigFileEndpoint() caseExpectations { + return caseExpectations{ + endpoint: baseConfigFileEndpoint, + region: expectedCallRegion, + } +} + +func testEndpointCase(t *testing.T, region string, testcase endpointTestCase, callF callFunc) { + t.Helper() + + ctx := context.Background() + + setup := caseSetup{ + config: map[string]any{}, + environmentVariables: map[string]string{}, + } + + for _, f := range testcase.with { + f(&setup) + } + + config := map[string]any{ + names.AttrAccessKey: servicemocks.MockStaticAccessKey, + names.AttrSecretKey: servicemocks.MockStaticSecretKey, + names.AttrRegion: region, + names.AttrSkipCredentialsValidation: true, + names.AttrSkipRequestingAccountID: true, + } + + maps.Copy(config, setup.config) + + if setup.configFile.baseUrl != "" || setup.configFile.serviceUrl != "" { + config[names.AttrProfile] = "default" + tempDir := t.TempDir() + writeSharedConfigFile(t, &config, tempDir, generateSharedConfigFile(setup.configFile)) + } + + for k, v := range setup.environmentVariables { + t.Setenv(k, v) + } + + p, err := provider.New(ctx) + if err != nil { + t.Fatal(err) + } + + expectedDiags := testcase.expected.diags + diags := p.Configure(ctx, terraformsdk.NewResourceConfigRaw(config)) + + if diff := cmp.Diff(diags, expectedDiags, cmp.Comparer(sdkdiag.Comparer)); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + + if diags.HasError() { + return + } + + meta := p.Meta().(*conns.AWSClient) + + callParams := callF(ctx, t, meta) + + if e, a := testcase.expected.endpoint, callParams.endpoint; e != a { + t.Errorf("expected endpoint %q, got %q", e, a) + } + + if e, a := testcase.expected.region, callParams.region; e != a { + t.Errorf("expected region %q, got %q", e, a) + } +} + +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 any) 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 + + buf.WriteString(` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey +`) + if config.baseUrl != "" { + fmt.Fprintf(&buf, "endpoint_url = %s\n", config.baseUrl) + } + + if config.serviceUrl != "" { + fmt.Fprintf(&buf, ` +services = endpoint-test + +[services endpoint-test] +%[1]s = + endpoint_url = %[2]s +`, configParam, serviceConfigFileEndpoint) + } + + return buf.String() +} + +func writeSharedConfigFile(t *testing.T, config *map[string]any, tempDir, content string) string { + t.Helper() + + file, err := os.Create(filepath.Join(tempDir, "aws-sdk-go-base-shared-configuration-file")) + if err != nil { + t.Fatalf("creating shared configuration file: %s", err) + } + + _, err = file.WriteString(content) + if err != nil { + t.Fatalf(" writing shared configuration file: %s", err) + } + + if v, ok := (*config)[names.AttrSharedConfigFiles]; !ok { + (*config)[names.AttrSharedConfigFiles] = []any{file.Name()} + } else { + (*config)[names.AttrSharedConfigFiles] = append(v.([]any), file.Name()) + } + + return file.Name() +} diff --git a/names/consts_gen.go b/names/consts_gen.go index d7c4a4f8c441..5df065697967 100644 --- a/names/consts_gen.go +++ b/names/consts_gen.go @@ -153,6 +153,7 @@ const ( M2 = "m2" MQ = "mq" MWAA = "mwaa" + MWAAServerless = "mwaaserverless" Macie2 = "macie2" MediaConnect = "mediaconnect" MediaConvert = "mediaconvert" @@ -413,6 +414,7 @@ const ( M2ServiceID = "m2" MQServiceID = "mq" MWAAServiceID = "MWAA" + MWAAServerlessServiceID = "MWAA Serverless" Macie2ServiceID = "Macie2" MediaConnectServiceID = "MediaConnect" MediaConvertServiceID = "MediaConvert" diff --git a/names/data/names_data.hcl b/names/data/names_data.hcl index abd7b1428c88..ad92c6d423c7 100644 --- a/names/data/names_data.hcl +++ b/names/data/names_data.hcl @@ -5518,6 +5518,29 @@ service "mwaa" { brand = "AWS" } +service "mwaaserverless" { + sdk { + id = "MWAA Serverless" + } + + names { + provider_name_upper = "MWAAServerless" + human_friendly = "MWAA (Managed Workflows for Apache Airflow) Serverless" + } + + endpoint_info { + endpoint_api_call = "ListWorkflows" + } + + resource_prefix { + correct = "aws_mwaaserverless_" + } + + provider_package_correct = "mwaaserverless" + doc_prefix = ["mwaaserverless_"] + brand = "AWS" +} + service "neptune" { sdk { id = "Neptune" diff --git a/website/allowed-subcategories.txt b/website/allowed-subcategories.txt index 6b1ad725dce2..856e44e9b066 100644 --- a/website/allowed-subcategories.txt +++ b/website/allowed-subcategories.txt @@ -162,6 +162,7 @@ Location Lookout for Metrics MQ MWAA (Managed Workflows for Apache Airflow) +MWAA (Managed Workflows for Apache Airflow) Serverless Macie Mainframe Modernization Managed Grafana diff --git a/website/docs/guides/custom-service-endpoints.html.markdown b/website/docs/guides/custom-service-endpoints.html.markdown index fe505c9ccd0a..8e9aa705a87c 100644 --- a/website/docs/guides/custom-service-endpoints.html.markdown +++ b/website/docs/guides/custom-service-endpoints.html.markdown @@ -244,6 +244,7 @@ provider "aws" { |Application Migration (Mgn)|`mgn`|`AWS_ENDPOINT_URL_MGN`|`mgn`| |MQ|`mq`|`AWS_ENDPOINT_URL_MQ`|`mq`| |MWAA (Managed Workflows for Apache Airflow)|`mwaa`|`AWS_ENDPOINT_URL_MWAA`|`mwaa`| +|MWAA (Managed Workflows for Apache Airflow) Serverless|`mwaaserverless`|`AWS_ENDPOINT_URL_MWAA_SERVERLESS`|`mwaa_serverless`| |Neptune|`neptune`|`AWS_ENDPOINT_URL_NEPTUNE`|`neptune`| |Neptune Analytics|`neptunegraph`|`AWS_ENDPOINT_URL_NEPTUNE_GRAPH`|`neptune_graph`| |Network Firewall|`networkfirewall`|`AWS_ENDPOINT_URL_NETWORK_FIREWALL`|`network_firewall`| From c99875ef36cee137342b7a892862082232564775 Mon Sep 17 00:00:00 2001 From: aristosvo <8375124+aristosvo@users.noreply.github.com> Date: Tue, 25 Nov 2025 21:32:25 +0100 Subject: [PATCH 2/4] [New Service]: MWAA Serverless --- .ci/.semgrep-service-name0.yml | 15 + .ci/.semgrep-service-name1.yml | 43 +- .ci/.semgrep-service-name2.yml | 103 +-- .ci/.semgrep-service-name3.yml | 14 + .github/labeler-issue-triage.yml | 2 + .github/labeler-pr-triage.yml | 6 + .../components/generated/services_all.kt | 1 + infrastructure/repository/labels-service.tf | 1 + internal/conns/awsclient_gen.go | 5 + internal/provider/framework/provider_gen.go | 7 + internal/provider/sdkv2/provider_gen.go | 8 + .../service_endpoints_gen_test.go | 602 ++++++++++++++++++ names/consts_gen.go | 2 + names/data/names_data.hcl | 24 + website/allowed-subcategories.txt | 1 + .../custom-service-endpoints.html.markdown | 1 + 16 files changed, 778 insertions(+), 57 deletions(-) create mode 100644 internal/service/mwaaserverless/service_endpoints_gen_test.go diff --git a/.ci/.semgrep-service-name0.yml b/.ci/.semgrep-service-name0.yml index 293c2ea95cb9..2f7bb26513b5 100644 --- a/.ci/.semgrep-service-name0.yml +++ b/.ci/.semgrep-service-name0.yml @@ -4498,3 +4498,18 @@ rules: - focus-metavariable: $NAME - pattern-not: func $NAME($T *testing.T) severity: WARNING + - id: connect-in-test-name + languages: + - go + message: Include "Connect" in test name + paths: + include: + - "/internal/service/connect/*_test.go" + patterns: + - pattern: func $NAME( ... ) + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-not-regex: "^TestAccConnect" + - pattern-regex: ^TestAcc.* + severity: WARNING diff --git a/.ci/.semgrep-service-name1.yml b/.ci/.semgrep-service-name1.yml index 1a61f7e48351..f453d11ae0f4 100644 --- a/.ci/.semgrep-service-name1.yml +++ b/.ci/.semgrep-service-name1.yml @@ -1,20 +1,5 @@ # Generated by internal/generate/servicesemgrep/main.go; DO NOT EDIT. rules: - - id: connect-in-test-name - languages: - - go - message: Include "Connect" in test name - paths: - include: - - "/internal/service/connect/*_test.go" - patterns: - - pattern: func $NAME( ... ) - - metavariable-pattern: - metavariable: $NAME - patterns: - - pattern-not-regex: "^TestAccConnect" - - pattern-regex: ^TestAcc.* - severity: WARNING - id: connect-in-const-name languages: - go @@ -4489,3 +4474,31 @@ rules: - pattern-not-regex: "^TestAccIVS" - pattern-regex: ^TestAcc.* severity: WARNING + - id: ivs-in-const-name + languages: + - go + message: Do not use "IVS" in const name inside ivs package + paths: + include: + - "/internal/service/ivs" + patterns: + - pattern: const $NAME = ... + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)IVS" + severity: WARNING + - id: ivs-in-var-name + languages: + - go + message: Do not use "IVS" in var name inside ivs package + paths: + include: + - "/internal/service/ivs" + patterns: + - pattern: var $NAME = ... + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)IVS" + severity: WARNING diff --git a/.ci/.semgrep-service-name2.yml b/.ci/.semgrep-service-name2.yml index 2d884c23a6a2..086a4735c01c 100644 --- a/.ci/.semgrep-service-name2.yml +++ b/.ci/.semgrep-service-name2.yml @@ -1,33 +1,5 @@ # Generated by internal/generate/servicesemgrep/main.go; DO NOT EDIT. rules: - - id: ivs-in-const-name - languages: - - go - message: Do not use "IVS" in const name inside ivs package - paths: - include: - - "/internal/service/ivs" - patterns: - - pattern: const $NAME = ... - - metavariable-pattern: - metavariable: $NAME - patterns: - - pattern-regex: "(?i)IVS" - severity: WARNING - - id: ivs-in-var-name - languages: - - go - message: Do not use "IVS" in var name inside ivs package - paths: - include: - - "/internal/service/ivs" - patterns: - - pattern: var $NAME = ... - - metavariable-pattern: - metavariable: $NAME - patterns: - - pattern-regex: "(?i)IVS" - severity: WARNING - id: ivschat-in-func-name languages: - go @@ -2363,6 +2335,67 @@ rules: patterns: - pattern-regex: "(?i)MWAA" severity: WARNING + - id: mwaaserverless-in-func-name + languages: + - go + message: Do not use "MWAAServerless" in func name inside mwaaserverless package + paths: + include: + - "/internal/service/mwaaserverless" + exclude: + - "/internal/service/mwaaserverless/list_pages_gen.go" + patterns: + - pattern: func $NAME( ... ) + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)MWAAServerless" + - focus-metavariable: $NAME + - pattern-not: func $NAME($T *testing.T) + severity: WARNING + - id: mwaaserverless-in-test-name + languages: + - go + message: Include "MWAAServerless" in test name + paths: + include: + - "/internal/service/mwaaserverless/*_test.go" + patterns: + - pattern: func $NAME( ... ) + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-not-regex: "^TestAccMWAAServerless" + - pattern-regex: ^TestAcc.* + severity: WARNING + - id: mwaaserverless-in-const-name + languages: + - go + message: Do not use "MWAAServerless" in const name inside mwaaserverless package + paths: + include: + - "/internal/service/mwaaserverless" + patterns: + - pattern: const $NAME = ... + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)MWAAServerless" + severity: WARNING + - id: mwaaserverless-in-var-name + languages: + - go + message: Do not use "MWAAServerless" in var name inside mwaaserverless package + paths: + include: + - "/internal/service/mwaaserverless" + patterns: + - pattern: var $NAME = ... + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)MWAAServerless" + severity: WARNING - id: neptune-in-func-name languages: - go @@ -4476,17 +4509,3 @@ rules: patterns: - pattern-regex: "(?i)rdsdataservice" severity: WARNING - - id: rdsdataservice-in-var-name - languages: - - go - message: Do not use "rdsdataservice" in var name inside rdsdata package - paths: - include: - - "/internal/service/rdsdata" - patterns: - - pattern: var $NAME = ... - - metavariable-pattern: - metavariable: $NAME - patterns: - - pattern-regex: "(?i)rdsdataservice" - severity: WARNING diff --git a/.ci/.semgrep-service-name3.yml b/.ci/.semgrep-service-name3.yml index 5ab562dfa4dd..766e58d87dc9 100644 --- a/.ci/.semgrep-service-name3.yml +++ b/.ci/.semgrep-service-name3.yml @@ -1,5 +1,19 @@ # Generated by internal/generate/servicesemgrep/main.go; DO NOT EDIT. rules: + - id: rdsdataservice-in-var-name + languages: + - go + message: Do not use "rdsdataservice" in var name inside rdsdata package + paths: + include: + - "/internal/service/rdsdata" + patterns: + - pattern: var $NAME = ... + - metavariable-pattern: + metavariable: $NAME + patterns: + - pattern-regex: "(?i)rdsdataservice" + severity: WARNING - id: recyclebin-in-func-name languages: - go diff --git a/.github/labeler-issue-triage.yml b/.github/labeler-issue-triage.yml index de4fbdb0e1d1..b965e0eb7407 100644 --- a/.github/labeler-issue-triage.yml +++ b/.github/labeler-issue-triage.yml @@ -491,6 +491,8 @@ service/mturk: - '((\*|-)\s*`?|(data|resource)\s+"?)aws_mturk_' service/mwaa: - '((\*|-)\s*`?|(data|resource)\s+"?)aws_mwaa_' +service/mwaaserverless: + - '((\*|-)\s*`?|(data|resource)\s+"?)aws_mwaaserverless_' service/neptune: - '((\*|-)\s*`?|(data|resource)\s+"?)aws_neptune_' service/neptunegraph: diff --git a/.github/labeler-pr-triage.yml b/.github/labeler-pr-triage.yml index 5ee1b6d5f3d2..245cb48f2dd2 100644 --- a/.github/labeler-pr-triage.yml +++ b/.github/labeler-pr-triage.yml @@ -1562,6 +1562,12 @@ service/mwaa: - any-glob-to-any-file: - 'internal/service/mwaa/**/*' - 'website/**/mwaa_*' +service/mwaaserverless: + - any: + - changed-files: + - any-glob-to-any-file: + - 'internal/service/mwaaserverless/**/*' + - 'website/**/mwaaserverless_*' service/neptune: - any: - changed-files: diff --git a/.teamcity/components/generated/services_all.kt b/.teamcity/components/generated/services_all.kt index 2fb3374599b6..418ae005eb10 100644 --- a/.teamcity/components/generated/services_all.kt +++ b/.teamcity/components/generated/services_all.kt @@ -156,6 +156,7 @@ val services = mapOf( "mgn" to ServiceSpec("Application Migration (Mgn)"), "mq" to ServiceSpec("MQ", vpcLock = true), "mwaa" to ServiceSpec("MWAA (Managed Workflows for Apache Airflow)", vpcLock = true), + "mwaaserverless" to ServiceSpec("MWAA (Managed Workflows for Apache Airflow) Serverless"), "neptune" to ServiceSpec("Neptune"), "neptunegraph" to ServiceSpec("Neptune Analytics"), "networkfirewall" to ServiceSpec("Network Firewall", vpcLock = true), diff --git a/infrastructure/repository/labels-service.tf b/infrastructure/repository/labels-service.tf index e1591e2453d1..77c45cbb5265 100644 --- a/infrastructure/repository/labels-service.tf +++ b/infrastructure/repository/labels-service.tf @@ -233,6 +233,7 @@ variable "service_labels" { "mq", "mturk", "mwaa", + "mwaaserverless", "neptune", "neptunegraph", "networkfirewall", diff --git a/internal/conns/awsclient_gen.go b/internal/conns/awsclient_gen.go index b1c5f728931b..6af3bf5b5929 100644 --- a/internal/conns/awsclient_gen.go +++ b/internal/conns/awsclient_gen.go @@ -164,6 +164,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/mgn" "github.com/aws/aws-sdk-go-v2/service/mq" "github.com/aws/aws-sdk-go-v2/service/mwaa" + "github.com/aws/aws-sdk-go-v2/service/mwaaserverless" "github.com/aws/aws-sdk-go-v2/service/neptune" "github.com/aws/aws-sdk-go-v2/service/neptunegraph" "github.com/aws/aws-sdk-go-v2/service/networkfirewall" @@ -865,6 +866,10 @@ func (c *AWSClient) MWAAClient(ctx context.Context) *mwaa.Client { return errs.Must(client[*mwaa.Client](ctx, c, names.MWAA, make(map[string]any))) } +func (c *AWSClient) MWAAServerlessClient(ctx context.Context) *mwaaserverless.Client { + return errs.Must(client[*mwaaserverless.Client](ctx, c, names.MWAAServerless, make(map[string]any))) +} + func (c *AWSClient) Macie2Client(ctx context.Context) *macie2.Client { return errs.Must(client[*macie2.Client](ctx, c, names.Macie2, make(map[string]any))) } diff --git a/internal/provider/framework/provider_gen.go b/internal/provider/framework/provider_gen.go index ef85bf430d40..03e978417efd 100644 --- a/internal/provider/framework/provider_gen.go +++ b/internal/provider/framework/provider_gen.go @@ -1300,6 +1300,13 @@ func endpointsBlock() schema.SetNestedBlock { Description: "Use this to override the default service endpoint URL", }, + // mwaaserverless + + "mwaaserverless": schema.StringAttribute{ + Optional: true, + Description: "Use this to override the default service endpoint URL", + }, + // neptune "neptune": schema.StringAttribute{ diff --git a/internal/provider/sdkv2/provider_gen.go b/internal/provider/sdkv2/provider_gen.go index e2d39ddd45d9..13ea6dee03fc 100644 --- a/internal/provider/sdkv2/provider_gen.go +++ b/internal/provider/sdkv2/provider_gen.go @@ -1503,6 +1503,14 @@ func endpointsSchema() *schema.Schema { Description: "Use this to override the default service endpoint URL", }, + // mwaaserverless + + "mwaaserverless": { + Type: schema.TypeString, + Optional: true, + Description: "Use this to override the default service endpoint URL", + }, + // neptune "neptune": { diff --git a/internal/service/mwaaserverless/service_endpoints_gen_test.go b/internal/service/mwaaserverless/service_endpoints_gen_test.go new file mode 100644 index 000000000000..815a28d010c9 --- /dev/null +++ b/internal/service/mwaaserverless/service_endpoints_gen_test.go @@ -0,0 +1,602 @@ +// Code generated by internal/generate/serviceendpointtests/main.go; DO NOT EDIT. + +package mwaaserverless_test + +import ( + "context" + "errors" + "fmt" + "maps" + "net" + "net/url" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/mwaaserverless" + "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" + terraformsdk "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "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/provider/sdkv2" + "github.com/hashicorp/terraform-provider-aws/names" +) + +type endpointTestCase struct { + with []setupFunc + expected caseExpectations +} + +type caseSetup struct { + config map[string]any + configFile configFile + environmentVariables map[string]string +} + +type configFile struct { + baseUrl string + serviceUrl string +} + +type caseExpectations struct { + diags diag.Diagnostics + endpoint string + region string +} + +type apiCallParams struct { + endpoint string + region string +} + +type setupFunc func(setup *caseSetup) + +type callFunc func(ctx context.Context, t *testing.T, meta *conns.AWSClient) apiCallParams + +const ( + packageNameConfigEndpoint = "https://packagename-config.endpoint.test/" + awsServiceEnvvarEndpoint = "https://service-envvar.endpoint.test/" + baseEnvvarEndpoint = "https://base-envvar.endpoint.test/" + serviceConfigFileEndpoint = "https://service-configfile.endpoint.test/" + baseConfigFileEndpoint = "https://base-configfile.endpoint.test/" +) + +const ( + packageName = "mwaaserverless" + awsEnvVar = "AWS_ENDPOINT_URL_MWAA_SERVERLESS" + baseEnvVar = "AWS_ENDPOINT_URL" + configParam = "mwaa_serverless" +) + +const ( + expectedCallRegion = "us-west-2" //lintignore:AWSAT003 +) + +func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.Setenv + ctx := t.Context() + const providerRegion = "us-west-2" //lintignore:AWSAT003 + const expectedEndpointRegion = providerRegion + + testcases := map[string]endpointTestCase{ + "no config": { + with: []setupFunc{withNoConfig}, + expected: expectDefaultEndpoint(ctx, t, expectedEndpointRegion), + }, + + // Package name endpoint on Config + + "package name endpoint config": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides aws service envvar": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withAwsEnvVar, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides base envvar": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withBaseEnvVar, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides service config file": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withServiceEndpointInConfigFile, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides base config file": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withBaseEndpointInConfigFile, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + // Service endpoint in AWS envvar + + "service aws envvar": { + with: []setupFunc{ + withAwsEnvVar, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + "service aws envvar overrides base envvar": { + with: []setupFunc{ + withAwsEnvVar, + withBaseEnvVar, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + "service aws envvar overrides service config file": { + with: []setupFunc{ + withAwsEnvVar, + withServiceEndpointInConfigFile, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + "service aws envvar overrides base config file": { + with: []setupFunc{ + withAwsEnvVar, + withBaseEndpointInConfigFile, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + // Base endpoint in envvar + + "base endpoint envvar": { + with: []setupFunc{ + withBaseEnvVar, + }, + expected: expectBaseEnvVarEndpoint(), + }, + + "base endpoint envvar overrides service config file": { + with: []setupFunc{ + withBaseEnvVar, + withServiceEndpointInConfigFile, + }, + expected: expectBaseEnvVarEndpoint(), + }, + + "base endpoint envvar overrides base config file": { + with: []setupFunc{ + withBaseEnvVar, + withBaseEndpointInConfigFile, + }, + expected: expectBaseEnvVarEndpoint(), + }, + + // Service endpoint in config file + + "service config file": { + with: []setupFunc{ + withServiceEndpointInConfigFile, + }, + expected: expectServiceConfigFileEndpoint(), + }, + + "service config file overrides base config file": { + with: []setupFunc{ + withServiceEndpointInConfigFile, + withBaseEndpointInConfigFile, + }, + expected: expectServiceConfigFileEndpoint(), + }, + + // Base endpoint in config file + + "base endpoint config file": { + with: []setupFunc{ + withBaseEndpointInConfigFile, + }, + expected: expectBaseConfigFileEndpoint(), + }, + + // Use FIPS endpoint on Config + + "use fips config": { + with: []setupFunc{ + withUseFIPSInConfig, + }, + expected: expectDefaultFIPSEndpoint(ctx, t, expectedEndpointRegion), + }, + + "use fips config with package name endpoint config": { + with: []setupFunc{ + withUseFIPSInConfig, + withPackageNameEndpointInConfig, + }, + expected: expectPackageNameConfigEndpoint(), + }, + } + + for name, testcase := range testcases { //nolint:paralleltest // uses t.Setenv + t.Run(name, func(t *testing.T) { + testEndpointCase(ctx, t, providerRegion, testcase, callService) + }) + } +} + +func defaultEndpoint(ctx context.Context, region string) (url.URL, error) { + r := mwaaserverless.NewDefaultEndpointResolverV2() + + ep, err := r.ResolveEndpoint(ctx, mwaaserverless.EndpointParameters{ + Region: aws.String(region), + }) + if err != nil { + return url.URL{}, err + } + + if ep.URI.Path == "" { + ep.URI.Path = "/" + } + + return ep.URI, nil +} + +func defaultFIPSEndpoint(ctx context.Context, region string) (url.URL, error) { + r := mwaaserverless.NewDefaultEndpointResolverV2() + + ep, err := r.ResolveEndpoint(ctx, mwaaserverless.EndpointParameters{ + Region: aws.String(region), + UseFIPS: aws.Bool(true), + }) + if err != nil { + return url.URL{}, err + } + + if ep.URI.Path == "" { + ep.URI.Path = "/" + } + + return ep.URI, nil +} + +func callService(ctx context.Context, t *testing.T, meta *conns.AWSClient) apiCallParams { + t.Helper() + + client := meta.MWAAServerlessClient(ctx) + + var result apiCallParams + + input := mwaaserverless.ListWorkflowsInput{} + _, err := client.ListWorkflows(ctx, &input, + func(opts *mwaaserverless.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) { + // no-op +} + +func withPackageNameEndpointInConfig(setup *caseSetup) { + if _, ok := setup.config[names.AttrEndpoints]; !ok { + setup.config[names.AttrEndpoints] = []any{ + map[string]any{}, + } + } + endpoints := setup.config[names.AttrEndpoints].([]any)[0].(map[string]any) + endpoints[packageName] = packageNameConfigEndpoint +} + +func withAwsEnvVar(setup *caseSetup) { + setup.environmentVariables[awsEnvVar] = awsServiceEnvvarEndpoint +} + +func withBaseEnvVar(setup *caseSetup) { + setup.environmentVariables[baseEnvVar] = baseEnvvarEndpoint +} + +func withServiceEndpointInConfigFile(setup *caseSetup) { + setup.configFile.serviceUrl = serviceConfigFileEndpoint +} + +func withBaseEndpointInConfigFile(setup *caseSetup) { + setup.configFile.baseUrl = baseConfigFileEndpoint +} + +func withUseFIPSInConfig(setup *caseSetup) { + setup.config["use_fips_endpoint"] = true +} + +func expectDefaultEndpoint(ctx context.Context, t *testing.T, region string) caseExpectations { + t.Helper() + + endpoint, err := defaultEndpoint(ctx, region) + if err != nil { + t.Fatalf("resolving accessanalyzer default endpoint: %s", err) + } + + return caseExpectations{ + endpoint: endpoint.String(), + region: expectedCallRegion, + } +} + +func expectDefaultFIPSEndpoint(ctx context.Context, t *testing.T, region string) caseExpectations { + t.Helper() + + endpoint, err := defaultFIPSEndpoint(ctx, region) + if err != nil { + t.Fatalf("resolving accessanalyzer FIPS endpoint: %s", err) + } + + hostname := endpoint.Hostname() + _, err = net.LookupHost(hostname) + if dnsErr, ok := errs.As[*net.DNSError](err); ok && dnsErr.IsNotFound { + return expectDefaultEndpoint(ctx, t, region) + } else if err != nil { + t.Fatalf("looking up accessanalyzer endpoint %q: %s", hostname, err) + } + + return caseExpectations{ + endpoint: endpoint.String(), + region: expectedCallRegion, + } +} + +func expectPackageNameConfigEndpoint() caseExpectations { + return caseExpectations{ + endpoint: packageNameConfigEndpoint, + region: expectedCallRegion, + } +} + +func expectAwsEnvVarEndpoint() caseExpectations { + return caseExpectations{ + endpoint: awsServiceEnvvarEndpoint, + region: expectedCallRegion, + } +} + +func expectBaseEnvVarEndpoint() caseExpectations { + return caseExpectations{ + endpoint: baseEnvvarEndpoint, + region: expectedCallRegion, + } +} + +func expectServiceConfigFileEndpoint() caseExpectations { + return caseExpectations{ + endpoint: serviceConfigFileEndpoint, + region: expectedCallRegion, + } +} + +func expectBaseConfigFileEndpoint() caseExpectations { + return caseExpectations{ + endpoint: baseConfigFileEndpoint, + region: expectedCallRegion, + } +} + +func testEndpointCase(ctx context.Context, t *testing.T, region string, testcase endpointTestCase, callF callFunc) { + t.Helper() + + setup := caseSetup{ + config: map[string]any{}, + environmentVariables: map[string]string{}, + } + + for _, f := range testcase.with { + f(&setup) + } + + config := map[string]any{ + names.AttrAccessKey: servicemocks.MockStaticAccessKey, + names.AttrSecretKey: servicemocks.MockStaticSecretKey, + names.AttrRegion: region, + names.AttrSkipCredentialsValidation: true, + names.AttrSkipRequestingAccountID: true, + } + + maps.Copy(config, setup.config) + + if setup.configFile.baseUrl != "" || setup.configFile.serviceUrl != "" { + config[names.AttrProfile] = "default" + tempDir := t.TempDir() + writeSharedConfigFile(t, &config, tempDir, generateSharedConfigFile(setup.configFile)) + } + + for k, v := range setup.environmentVariables { + t.Setenv(k, v) + } + + p, err := sdkv2.NewProvider(ctx) + if err != nil { + t.Fatal(err) + } + + p.TerraformVersion = "1.0.0" + + expectedDiags := testcase.expected.diags + diags := p.Configure(ctx, terraformsdk.NewResourceConfigRaw(config)) + + if diff := cmp.Diff(diags, expectedDiags, cmp.Comparer(sdkdiag.Comparer)); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + + if diags.HasError() { + return + } + + meta := p.Meta().(*conns.AWSClient) + + callParams := callF(ctx, t, meta) + + if e, a := testcase.expected.endpoint, callParams.endpoint; e != a { + t.Errorf("expected endpoint %q, got %q", e, a) + } + + if e, a := testcase.expected.region, callParams.region; e != a { + t.Errorf("expected region %q, got %q", e, a) + } +} + +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 = errors.New("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 any) 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 + + buf.WriteString(` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey +`) + if config.baseUrl != "" { + fmt.Fprintf(&buf, "endpoint_url = %s\n", config.baseUrl) + } + + if config.serviceUrl != "" { + fmt.Fprintf(&buf, ` +services = endpoint-test + +[services endpoint-test] +%[1]s = + endpoint_url = %[2]s +`, configParam, serviceConfigFileEndpoint) + } + + return buf.String() +} + +func writeSharedConfigFile(t *testing.T, config *map[string]any, tempDir, content string) string { + t.Helper() + + file, err := os.Create(filepath.Join(tempDir, "aws-sdk-go-base-shared-configuration-file")) + if err != nil { + t.Fatalf("creating shared configuration file: %s", err) + } + + _, err = file.WriteString(content) + if err != nil { + t.Fatalf(" writing shared configuration file: %s", err) + } + + if v, ok := (*config)[names.AttrSharedConfigFiles]; !ok { + (*config)[names.AttrSharedConfigFiles] = []any{file.Name()} + } else { + (*config)[names.AttrSharedConfigFiles] = append(v.([]any), file.Name()) + } + + return file.Name() +} diff --git a/names/consts_gen.go b/names/consts_gen.go index 536c88eb5cbf..5b266c364964 100644 --- a/names/consts_gen.go +++ b/names/consts_gen.go @@ -152,6 +152,7 @@ const ( M2 = "m2" MQ = "mq" MWAA = "mwaa" + MWAAServerless = "mwaaserverless" Macie2 = "macie2" MediaConnect = "mediaconnect" MediaConvert = "mediaconvert" @@ -414,6 +415,7 @@ const ( M2ServiceID = "m2" MQServiceID = "mq" MWAAServiceID = "MWAA" + MWAAServerlessServiceID = "MWAA Serverless" Macie2ServiceID = "Macie2" MediaConnectServiceID = "MediaConnect" MediaConvertServiceID = "MediaConvert" diff --git a/names/data/names_data.hcl b/names/data/names_data.hcl index 8d135d1c1ef1..7f8981238c59 100644 --- a/names/data/names_data.hcl +++ b/names/data/names_data.hcl @@ -5862,6 +5862,30 @@ service "mwaa" { brand = "AWS" } +service "mwaaserverless" { + sdk { + id = "MWAA Serverless" + arn_namespace = "airflow-serverless" + } + + names { + provider_name_upper = "MWAAServerless" + human_friendly = "MWAA (Managed Workflows for Apache Airflow) Serverless" + } + + endpoint_info { + endpoint_api_call = "ListWorkflows" + } + + resource_prefix { + correct = "aws_mwaaserverless_" + } + + provider_package_correct = "mwaaserverless" + doc_prefix = ["mwaaserverless_"] + brand = "AWS" +} + service "neptune" { sdk { id = "Neptune" diff --git a/website/allowed-subcategories.txt b/website/allowed-subcategories.txt index 7c4a895968e7..978f7fa34eed 100644 --- a/website/allowed-subcategories.txt +++ b/website/allowed-subcategories.txt @@ -163,6 +163,7 @@ Lightsail Location MQ MWAA (Managed Workflows for Apache Airflow) +MWAA (Managed Workflows for Apache Airflow) Serverless Macie Mainframe Modernization Managed Grafana diff --git a/website/docs/guides/custom-service-endpoints.html.markdown b/website/docs/guides/custom-service-endpoints.html.markdown index 033448c07fb6..21115add1f61 100644 --- a/website/docs/guides/custom-service-endpoints.html.markdown +++ b/website/docs/guides/custom-service-endpoints.html.markdown @@ -243,6 +243,7 @@ provider "aws" { |Application Migration (Mgn)|`mgn`|`AWS_ENDPOINT_URL_MGN`|`mgn`| |MQ|`mq`|`AWS_ENDPOINT_URL_MQ`|`mq`| |MWAA (Managed Workflows for Apache Airflow)|`mwaa`|`AWS_ENDPOINT_URL_MWAA`|`mwaa`| +|MWAA (Managed Workflows for Apache Airflow) Serverless|`mwaaserverless`|`AWS_ENDPOINT_URL_MWAA_SERVERLESS`|`mwaa_serverless`| |Neptune|`neptune`|`AWS_ENDPOINT_URL_NEPTUNE`|`neptune`| |Neptune Analytics|`neptunegraph`|`AWS_ENDPOINT_URL_NEPTUNE_GRAPH`|`neptune_graph`| |Network Firewall|`networkfirewall`|`AWS_ENDPOINT_URL_NETWORK_FIREWALL`|`network_firewall`| From 92f5d287ad409501698c50eeb1a44e499ce6f7a6 Mon Sep 17 00:00:00 2001 From: aristosvo <8375124+aristosvo@users.noreply.github.com> Date: Tue, 25 Nov 2025 22:24:49 +0100 Subject: [PATCH 3/4] go mod tidy --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index de106a7fd17f..d825f1e90857 100644 --- a/go.mod +++ b/go.mod @@ -176,6 +176,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/mgn v1.38.5 github.com/aws/aws-sdk-go-v2/service/mq v1.34.11 github.com/aws/aws-sdk-go-v2/service/mwaa v1.39.13 + github.com/aws/aws-sdk-go-v2/service/mwaaserverless v1.0.2 github.com/aws/aws-sdk-go-v2/service/neptune v1.43.4 github.com/aws/aws-sdk-go-v2/service/neptunegraph v1.21.12 github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.58.1 @@ -277,7 +278,6 @@ require ( github.com/beevik/etree v1.6.0 github.com/cedar-policy/cedar-go v1.3.0 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc -github.com/dlclark/regexp2 v1.11.5 github.com/gertd/go-pluralize v0.2.1 github.com/goccy/go-yaml v1.18.0 github.com/google/go-cmp v0.7.0 diff --git a/go.sum b/go.sum index 2abbd1fab063..d2b4fcca4fb6 100644 --- a/go.sum +++ b/go.sum @@ -373,6 +373,8 @@ github.com/aws/aws-sdk-go-v2/service/mq v1.34.11 h1:D8r2O16aBEH/dG8xYYlhX+Tb0yWs github.com/aws/aws-sdk-go-v2/service/mq v1.34.11/go.mod h1:dp1WwnKbfltN4RJ39DIu2HFIif7DoZIQ1EsfB8BGUWA= github.com/aws/aws-sdk-go-v2/service/mwaa v1.39.13 h1:wIqi6JVRFEfiN6kTVGWjg7zb3FEVPTvT2tpI3PArORU= github.com/aws/aws-sdk-go-v2/service/mwaa v1.39.13/go.mod h1:06+ehiGrk+iaZXv4/BaooFPq8XRvmw4VWnxuNPoX6SM= +github.com/aws/aws-sdk-go-v2/service/mwaaserverless v1.0.2 h1:s/vHYmoo0bFKWOMC5aAD8KR6e5mw8zFhxo2YLaxyxh4= +github.com/aws/aws-sdk-go-v2/service/mwaaserverless v1.0.2/go.mod h1:+FkUp9Aq1PTm+Bj4ONYYw8mbMC1FpeLsgMBQwiBj9KM= github.com/aws/aws-sdk-go-v2/service/neptune v1.43.4 h1:SR0OowjSvC8VhRvqvhp8mOTgnulFWdnYJZMbP4jfRfI= github.com/aws/aws-sdk-go-v2/service/neptune v1.43.4/go.mod h1:u/db+lzLiyv8jAQe0S6t74UVXhqRGvB5tTkFBzmJ9Eg= github.com/aws/aws-sdk-go-v2/service/neptunegraph v1.21.12 h1:cnBJ7+jr3h9PTSBu6/bVOGRoZM9hz0+pGDKC5mT7Yi0= @@ -591,8 +593,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= -github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= From 808b3999a2b48a825212177d0d701b494156fd1d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 25 Nov 2025 16:29:44 -0500 Subject: [PATCH 4/4] Add 'internal/service/mwaaserverless/generate.go'. --- go.mod | 2 +- go.sum | 4 +- .../provider/sdkv2/service_packages_gen.go | 2 + internal/service/mwaaserverless/generate.go | 8 ++ .../service_endpoint_resolver_gen.go | 83 ++++++++++++++++ .../mwaaserverless/service_package_gen.go | 87 +++++++++++++++++ internal/service/mwaaserverless/tags_gen.go | 95 +++++++++++++++++++ internal/sweep/service_packages_gen_test.go | 2 + tools/tfsdk2fw/go.mod | 2 + tools/tfsdk2fw/go.sum | 4 + 10 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 internal/service/mwaaserverless/generate.go create mode 100644 internal/service/mwaaserverless/service_endpoint_resolver_gen.go create mode 100644 internal/service/mwaaserverless/service_package_gen.go create mode 100644 internal/service/mwaaserverless/tags_gen.go diff --git a/go.mod b/go.mod index de106a7fd17f..10f84c18b139 100644 --- a/go.mod +++ b/go.mod @@ -176,6 +176,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/mgn v1.38.5 github.com/aws/aws-sdk-go-v2/service/mq v1.34.11 github.com/aws/aws-sdk-go-v2/service/mwaa v1.39.13 + github.com/aws/aws-sdk-go-v2/service/mwaaserverless v1.0.1 github.com/aws/aws-sdk-go-v2/service/neptune v1.43.4 github.com/aws/aws-sdk-go-v2/service/neptunegraph v1.21.12 github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.58.1 @@ -277,7 +278,6 @@ require ( github.com/beevik/etree v1.6.0 github.com/cedar-policy/cedar-go v1.3.0 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc -github.com/dlclark/regexp2 v1.11.5 github.com/gertd/go-pluralize v0.2.1 github.com/goccy/go-yaml v1.18.0 github.com/google/go-cmp v0.7.0 diff --git a/go.sum b/go.sum index 2abbd1fab063..4df31ba08095 100644 --- a/go.sum +++ b/go.sum @@ -373,6 +373,8 @@ github.com/aws/aws-sdk-go-v2/service/mq v1.34.11 h1:D8r2O16aBEH/dG8xYYlhX+Tb0yWs github.com/aws/aws-sdk-go-v2/service/mq v1.34.11/go.mod h1:dp1WwnKbfltN4RJ39DIu2HFIif7DoZIQ1EsfB8BGUWA= github.com/aws/aws-sdk-go-v2/service/mwaa v1.39.13 h1:wIqi6JVRFEfiN6kTVGWjg7zb3FEVPTvT2tpI3PArORU= github.com/aws/aws-sdk-go-v2/service/mwaa v1.39.13/go.mod h1:06+ehiGrk+iaZXv4/BaooFPq8XRvmw4VWnxuNPoX6SM= +github.com/aws/aws-sdk-go-v2/service/mwaaserverless v1.0.1 h1:AWcrFVyaX6mt6QSmhLZHC0k3ey9IPl8SnNpW7Sc+y3g= +github.com/aws/aws-sdk-go-v2/service/mwaaserverless v1.0.1/go.mod h1:+FkUp9Aq1PTm+Bj4ONYYw8mbMC1FpeLsgMBQwiBj9KM= github.com/aws/aws-sdk-go-v2/service/neptune v1.43.4 h1:SR0OowjSvC8VhRvqvhp8mOTgnulFWdnYJZMbP4jfRfI= github.com/aws/aws-sdk-go-v2/service/neptune v1.43.4/go.mod h1:u/db+lzLiyv8jAQe0S6t74UVXhqRGvB5tTkFBzmJ9Eg= github.com/aws/aws-sdk-go-v2/service/neptunegraph v1.21.12 h1:cnBJ7+jr3h9PTSBu6/bVOGRoZM9hz0+pGDKC5mT7Yi0= @@ -591,8 +593,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= -github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= diff --git a/internal/provider/sdkv2/service_packages_gen.go b/internal/provider/sdkv2/service_packages_gen.go index 060f29c8f6b4..7a7f35aca997 100644 --- a/internal/provider/sdkv2/service_packages_gen.go +++ b/internal/provider/sdkv2/service_packages_gen.go @@ -168,6 +168,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/service/mgn" "github.com/hashicorp/terraform-provider-aws/internal/service/mq" "github.com/hashicorp/terraform-provider-aws/internal/service/mwaa" + "github.com/hashicorp/terraform-provider-aws/internal/service/mwaaserverless" "github.com/hashicorp/terraform-provider-aws/internal/service/neptune" "github.com/hashicorp/terraform-provider-aws/internal/service/neptunegraph" "github.com/hashicorp/terraform-provider-aws/internal/service/networkfirewall" @@ -430,6 +431,7 @@ func servicePackages(ctx context.Context) []conns.ServicePackage { mgn.ServicePackage(ctx), mq.ServicePackage(ctx), mwaa.ServicePackage(ctx), + mwaaserverless.ServicePackage(ctx), neptune.ServicePackage(ctx), neptunegraph.ServicePackage(ctx), networkfirewall.ServicePackage(ctx), diff --git a/internal/service/mwaaserverless/generate.go b/internal/service/mwaaserverless/generate.go new file mode 100644 index 000000000000..c71a92670170 --- /dev/null +++ b/internal/service/mwaaserverless/generate.go @@ -0,0 +1,8 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:generate go run ../../generate/tags/main.go -KVTValues -ListTagsOp=ListTags -ServiceTagsMap -UpdateTags +//go:generate go run ../../generate/servicepackage/main.go +// ONLY generate directives and package declaration! Do not add anything else to this file. + +package mwaaserverless diff --git a/internal/service/mwaaserverless/service_endpoint_resolver_gen.go b/internal/service/mwaaserverless/service_endpoint_resolver_gen.go new file mode 100644 index 000000000000..e82a609afcc2 --- /dev/null +++ b/internal/service/mwaaserverless/service_endpoint_resolver_gen.go @@ -0,0 +1,83 @@ +// Code generated by internal/generate/servicepackage/main.go; DO NOT EDIT. + +package mwaaserverless + +import ( + "context" + "fmt" + "net" + + "github.com/YakDriver/smarterr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/mwaaserverless" + smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-provider-aws/internal/errs" +) + +var _ mwaaserverless.EndpointResolverV2 = resolverV2{} + +type resolverV2 struct { + defaultResolver mwaaserverless.EndpointResolverV2 +} + +func newEndpointResolverV2() resolverV2 { + return resolverV2{ + defaultResolver: mwaaserverless.NewDefaultEndpointResolverV2(), + } +} + +func (r resolverV2) ResolveEndpoint(ctx context.Context, params mwaaserverless.EndpointParameters) (endpoint smithyendpoints.Endpoint, err error) { + params = params.WithDefaults() + useFIPS := aws.ToBool(params.UseFIPS) + + if eps := params.Endpoint; aws.ToString(eps) != "" { + tflog.Debug(ctx, "setting endpoint", map[string]any{ + "tf_aws.endpoint": endpoint, + }) + + if useFIPS { + tflog.Debug(ctx, "endpoint set, ignoring UseFIPSEndpoint setting") + params.UseFIPS = aws.Bool(false) + } + + return r.defaultResolver.ResolveEndpoint(ctx, params) + } else if useFIPS { + ctx = tflog.SetField(ctx, "tf_aws.use_fips", useFIPS) + + endpoint, err = r.defaultResolver.ResolveEndpoint(ctx, params) + if err != nil { + return endpoint, smarterr.NewError(err) + } + + tflog.Debug(ctx, "endpoint resolved", map[string]any{ + "tf_aws.endpoint": endpoint.URI.String(), + }) + + 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, + }) + params.UseFIPS = aws.Bool(false) + } else { + err = fmt.Errorf("looking up mwaaserverless endpoint %q: %w", hostname, err) + return + } + } else { + return endpoint, smarterr.NewError(err) + } + } + + return r.defaultResolver.ResolveEndpoint(ctx, params) +} + +func withBaseEndpoint(endpoint string) func(*mwaaserverless.Options) { + return func(o *mwaaserverless.Options) { + if endpoint != "" { + o.BaseEndpoint = aws.String(endpoint) + } + } +} diff --git a/internal/service/mwaaserverless/service_package_gen.go b/internal/service/mwaaserverless/service_package_gen.go new file mode 100644 index 000000000000..767629095ac4 --- /dev/null +++ b/internal/service/mwaaserverless/service_package_gen.go @@ -0,0 +1,87 @@ +// Code generated by internal/generate/servicepackage/main.go; DO NOT EDIT. + +package mwaaserverless + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/mwaaserverless" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + inttypes "github.com/hashicorp/terraform-provider-aws/internal/types" + "github.com/hashicorp/terraform-provider-aws/internal/vcr" + "github.com/hashicorp/terraform-provider-aws/names" +) + +type servicePackage struct{} + +func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*inttypes.ServicePackageFrameworkDataSource { + return []*inttypes.ServicePackageFrameworkDataSource{} +} + +func (p *servicePackage) FrameworkResources(ctx context.Context) []*inttypes.ServicePackageFrameworkResource { + return []*inttypes.ServicePackageFrameworkResource{} +} + +func (p *servicePackage) SDKDataSources(ctx context.Context) []*inttypes.ServicePackageSDKDataSource { + return []*inttypes.ServicePackageSDKDataSource{} +} + +func (p *servicePackage) SDKResources(ctx context.Context) []*inttypes.ServicePackageSDKResource { + return []*inttypes.ServicePackageSDKResource{} +} + +func (p *servicePackage) ServicePackageName() string { + return names.MWAAServerless +} + +// 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) (*mwaaserverless.Client, error) { + cfg := *(config["aws_sdkv2_config"].(*aws.Config)) + optFns := []func(*mwaaserverless.Options){ + mwaaserverless.WithEndpointResolverV2(newEndpointResolverV2()), + withBaseEndpoint(config[names.AttrEndpoint].(string)), + func(o *mwaaserverless.Options) { + if region := config[names.AttrRegion].(string); o.Region != region { + tflog.Info(ctx, "overriding provider-configured AWS API region", map[string]any{ + "service": p.ServicePackageName(), + "original_region": o.Region, + "override_region": region, + }) + o.Region = region + } + }, + func(o *mwaaserverless.Options) { + if inContext, ok := conns.FromContext(ctx); ok && inContext.VCREnabled() { + tflog.Info(ctx, "overriding retry behavior to immediately return VCR errors") + o.Retryer = conns.AddIsErrorRetryables(cfg.Retryer().(aws.RetryerV2), vcr.InteractionNotFoundRetryableFunc) + } + }, + withExtraOptions(ctx, p, config), + } + + return mwaaserverless.NewFromConfig(cfg, optFns...), nil +} + +// withExtraOptions returns a functional option that allows this service package to specify extra API client options. +// This option is always called after any generated options. +func withExtraOptions(ctx context.Context, sp conns.ServicePackage, config map[string]any) func(*mwaaserverless.Options) { + if v, ok := sp.(interface { + withExtraOptions(context.Context, map[string]any) []func(*mwaaserverless.Options) + }); ok { + optFns := v.withExtraOptions(ctx, config) + + return func(o *mwaaserverless.Options) { + for _, optFn := range optFns { + optFn(o) + } + } + } + + return func(*mwaaserverless.Options) {} +} + +func ServicePackage(ctx context.Context) conns.ServicePackage { + return &servicePackage{} +} diff --git a/internal/service/mwaaserverless/tags_gen.go b/internal/service/mwaaserverless/tags_gen.go new file mode 100644 index 000000000000..d60f1ef3e4cf --- /dev/null +++ b/internal/service/mwaaserverless/tags_gen.go @@ -0,0 +1,95 @@ +// Code generated by internal/generate/tags/main.go; DO NOT EDIT. +package mwaaserverless + +import ( + "context" + + "github.com/YakDriver/smarterr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/mwaaserverless" + "github.com/hashicorp/terraform-plugin-log/tflog" + "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/types/option" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// map[string]string handling + +// svcTags returns mwaaserverless service tags. +func svcTags(tags tftags.KeyValueTags) map[string]string { + return tags.Map() +} + +// keyValueTags creates tftags.KeyValueTags from mwaaserverless service tags. +func keyValueTags(ctx context.Context, tags map[string]string) tftags.KeyValueTags { + return tftags.New(ctx, tags) +} + +// getTagsIn returns mwaaserverless service tags from Context. +// nil is returned if there are no input tags. +func getTagsIn(ctx context.Context) map[string]string { + if inContext, ok := tftags.FromContext(ctx); ok { + if tags := svcTags(inContext.TagsIn.UnwrapOrDefault()); len(tags) > 0 { + return tags + } + } + + return nil +} + +// setTagsOut sets mwaaserverless service tags in Context. +func setTagsOut(ctx context.Context, tags map[string]string) { + if inContext, ok := tftags.FromContext(ctx); ok { + inContext.TagsOut = option.Some(keyValueTags(ctx, tags)) + } +} + +// updateTags updates mwaaserverless 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 *mwaaserverless.Client, identifier string, oldTagsMap, newTagsMap any, optFns ...func(*mwaaserverless.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.MWAAServerless) + if len(removedTags) > 0 { + input := mwaaserverless.UntagResourceInput{ + ResourceArn: aws.String(identifier), + TagKeys: removedTags.Keys(), + } + + _, err := conn.UntagResource(ctx, &input, optFns...) + + if err != nil { + return smarterr.NewError(err) + } + } + + updatedTags := oldTags.Updated(newTags) + updatedTags = updatedTags.IgnoreSystem(names.MWAAServerless) + if len(updatedTags) > 0 { + input := mwaaserverless.TagResourceInput{ + ResourceArn: aws.String(identifier), + Tags: svcTags(updatedTags), + } + + _, err := conn.TagResource(ctx, &input, optFns...) + + if err != nil { + return smarterr.NewError(err) + } + } + + return nil +} + +// UpdateTags updates mwaaserverless 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).MWAAServerlessClient(ctx), identifier, oldTags, newTags) +} diff --git a/internal/sweep/service_packages_gen_test.go b/internal/sweep/service_packages_gen_test.go index fb1b9b844db3..679638d8da62 100644 --- a/internal/sweep/service_packages_gen_test.go +++ b/internal/sweep/service_packages_gen_test.go @@ -168,6 +168,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/service/mgn" "github.com/hashicorp/terraform-provider-aws/internal/service/mq" "github.com/hashicorp/terraform-provider-aws/internal/service/mwaa" + "github.com/hashicorp/terraform-provider-aws/internal/service/mwaaserverless" "github.com/hashicorp/terraform-provider-aws/internal/service/neptune" "github.com/hashicorp/terraform-provider-aws/internal/service/neptunegraph" "github.com/hashicorp/terraform-provider-aws/internal/service/networkfirewall" @@ -430,6 +431,7 @@ func servicePackages(ctx context.Context) []conns.ServicePackage { mgn.ServicePackage(ctx), mq.ServicePackage(ctx), mwaa.ServicePackage(ctx), + mwaaserverless.ServicePackage(ctx), neptune.ServicePackage(ctx), neptunegraph.ServicePackage(ctx), networkfirewall.ServicePackage(ctx), diff --git a/tools/tfsdk2fw/go.mod b/tools/tfsdk2fw/go.mod index afc7d66f577e..bebad7829b2b 100644 --- a/tools/tfsdk2fw/go.mod +++ b/tools/tfsdk2fw/go.mod @@ -193,6 +193,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/mgn v1.38.5 // indirect github.com/aws/aws-sdk-go-v2/service/mq v1.34.11 // indirect github.com/aws/aws-sdk-go-v2/service/mwaa v1.39.13 // indirect + github.com/aws/aws-sdk-go-v2/service/mwaaserverless v1.0.1 // indirect github.com/aws/aws-sdk-go-v2/service/neptune v1.43.4 // indirect github.com/aws/aws-sdk-go-v2/service/neptunegraph v1.21.12 // indirect github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.58.1 // indirect @@ -223,6 +224,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ram v1.34.14 // indirect github.com/aws/aws-sdk-go-v2/service/rbin v1.27.0 // indirect github.com/aws/aws-sdk-go-v2/service/rds v1.111.0 // indirect + github.com/aws/aws-sdk-go-v2/service/rdsdata v1.32.13 // indirect github.com/aws/aws-sdk-go-v2/service/redshift v1.61.0 // indirect github.com/aws/aws-sdk-go-v2/service/redshiftdata v1.38.0 // indirect github.com/aws/aws-sdk-go-v2/service/redshiftserverless v1.32.0 // indirect diff --git a/tools/tfsdk2fw/go.sum b/tools/tfsdk2fw/go.sum index ea60c9074061..712686b6b5b2 100644 --- a/tools/tfsdk2fw/go.sum +++ b/tools/tfsdk2fw/go.sum @@ -373,6 +373,8 @@ github.com/aws/aws-sdk-go-v2/service/mq v1.34.11 h1:D8r2O16aBEH/dG8xYYlhX+Tb0yWs github.com/aws/aws-sdk-go-v2/service/mq v1.34.11/go.mod h1:dp1WwnKbfltN4RJ39DIu2HFIif7DoZIQ1EsfB8BGUWA= github.com/aws/aws-sdk-go-v2/service/mwaa v1.39.13 h1:wIqi6JVRFEfiN6kTVGWjg7zb3FEVPTvT2tpI3PArORU= github.com/aws/aws-sdk-go-v2/service/mwaa v1.39.13/go.mod h1:06+ehiGrk+iaZXv4/BaooFPq8XRvmw4VWnxuNPoX6SM= +github.com/aws/aws-sdk-go-v2/service/mwaaserverless v1.0.1 h1:AWcrFVyaX6mt6QSmhLZHC0k3ey9IPl8SnNpW7Sc+y3g= +github.com/aws/aws-sdk-go-v2/service/mwaaserverless v1.0.1/go.mod h1:+FkUp9Aq1PTm+Bj4ONYYw8mbMC1FpeLsgMBQwiBj9KM= github.com/aws/aws-sdk-go-v2/service/neptune v1.43.4 h1:SR0OowjSvC8VhRvqvhp8mOTgnulFWdnYJZMbP4jfRfI= github.com/aws/aws-sdk-go-v2/service/neptune v1.43.4/go.mod h1:u/db+lzLiyv8jAQe0S6t74UVXhqRGvB5tTkFBzmJ9Eg= github.com/aws/aws-sdk-go-v2/service/neptunegraph v1.21.12 h1:cnBJ7+jr3h9PTSBu6/bVOGRoZM9hz0+pGDKC5mT7Yi0= @@ -433,6 +435,8 @@ github.com/aws/aws-sdk-go-v2/service/rbin v1.27.0 h1:6n6wd4xLZmBfpMB70fnS174K7sl github.com/aws/aws-sdk-go-v2/service/rbin v1.27.0/go.mod h1:n9hrfwpLS7AUP0s6iGtCGFFRCixnlCFQqax6IIuf2FE= github.com/aws/aws-sdk-go-v2/service/rds v1.111.0 h1:OX6mXXK8V9lEt3NiiQIctLDntsN616R8Pj3Os9+SQ4c= github.com/aws/aws-sdk-go-v2/service/rds v1.111.0/go.mod h1:DCoBFX5nu7ZQxaZqGe+5Ai8Qd3lLpcQF1EhMrlC/FWU= +github.com/aws/aws-sdk-go-v2/service/rdsdata v1.32.13 h1:xqoXKcTbUER/GsQkPq3gNrncMDSiUuyrOZ1m2RpzBVc= +github.com/aws/aws-sdk-go-v2/service/rdsdata v1.32.13/go.mod h1:56C7CVaLNcTyEaDzGVfonoOzB3GV5DicgD9SWmnjGxQ= github.com/aws/aws-sdk-go-v2/service/redshift v1.61.0 h1:CK+ZEHOveSnFfUdS1Q9xtFEK9KsU698VfEEWU1Ld8K0= github.com/aws/aws-sdk-go-v2/service/redshift v1.61.0/go.mod h1:i/7qjbmYknaQFO0ngVOwQxom9SR4RAxG1ZgJgcxAJZg= github.com/aws/aws-sdk-go-v2/service/redshiftdata v1.38.0 h1:LoIvzYFQjsts0dkYtrkkX4C238k5CNiOjeejbPA3Iog=