From 7a5a0112974327a42b238dafb03e2242bccf6e3c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Oct 2023 16:54:12 -0400 Subject: [PATCH 1/9] Add 'validators.AWSAccountID'. --- .../framework/validators/aws_account_id.go | 52 +++++++++++ .../validators/aws_account_id_test.go | 87 +++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 internal/framework/validators/aws_account_id.go create mode 100644 internal/framework/validators/aws_account_id_test.go diff --git a/internal/framework/validators/aws_account_id.go b/internal/framework/validators/aws_account_id.go new file mode 100644 index 00000000000..d091e02cbab --- /dev/null +++ b/internal/framework/validators/aws_account_id.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators + +import ( + "context" + + "github.com/YakDriver/regexache" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +// awsAccountIDValidator validates that a string Attribute's value is a valid AWS account ID. +type awsAccountIDValidator struct{} + +// Description describes the validation in plain text formatting. +func (validator awsAccountIDValidator) Description(_ context.Context) string { + return "value must be a valid AWS account ID" +} + +// MarkdownDescription describes the validation in Markdown formatting. +func (validator awsAccountIDValidator) MarkdownDescription(ctx context.Context) string { + return validator.Description(ctx) +} + +// Validate performs the validation. +func (validator awsAccountIDValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { + if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() { + return + } + + // https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-identifiers.html. + if !regexache.MustCompile(`^\d{12}$`).MatchString(request.ConfigValue.ValueString()) { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + validator.Description(ctx), + request.ConfigValue.ValueString(), + )) + return + } +} + +// AWSAccountID returns a string validator which ensures that any configured +// attribute value: +// +// - Is a string, which represents a valid AWS account ID. +// +// Null (unconfigured) and unknown (known after apply) values are skipped. +func AWSAccountID() validator.String { + return awsAccountIDValidator{} +} diff --git a/internal/framework/validators/aws_account_id_test.go b/internal/framework/validators/aws_account_id_test.go new file mode 100644 index 00000000000..23d7d7e021a --- /dev/null +++ b/internal/framework/validators/aws_account_id_test.go @@ -0,0 +1,87 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" +) + +func TestAWSAccountIDValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + val types.String + expectedDiagnostics diag.Diagnostics + } + tests := map[string]testCase{ + "unknown String": { + val: types.StringUnknown(), + }, + "null String": { + val: types.StringNull(), + }, + "invalid String": { + val: types.StringValue("test-value"), + expectedDiagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Value", + `Attribute test value must be a valid AWS account ID, got: test-value`, + ), + }, + }, + "valid AWS account ID": { + val: types.StringValue("123456789012"), + }, + "too long AWS account ID": { + val: types.StringValue("1234567890123"), + expectedDiagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Value", + `Attribute test value must be a valid AWS account ID, got: 1234567890123`, + ), + }, + }, + "too short AWS account ID": { + val: types.StringValue("12345678901"), + expectedDiagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Value", + `Attribute test value must be a valid AWS account ID, got: 12345678901`, + ), + }, + }, + } + + for name, test := range tests { + name, test := name, test + t.Run(name, func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + request := validator.StringRequest{ + Path: path.Root("test"), + PathExpression: path.MatchRoot("test"), + ConfigValue: test.val, + } + response := validator.StringResponse{} + fwvalidators.AWSAccountID().ValidateString(ctx, request, &response) + + if diff := cmp.Diff(response.Diagnostics, test.expectedDiagnostics); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} From d8c4afc4a50bfead8f74cf677fe937b476c82778 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 24 Oct 2023 11:15:56 -0400 Subject: [PATCH 2/9] Add 'StringValuable' assetions for custom string types. --- internal/framework/types/arn.go | 3 ++- internal/framework/types/cidr_block.go | 3 ++- internal/framework/types/duration.go | 3 ++- internal/framework/types/regexp.go | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/framework/types/arn.go b/internal/framework/types/arn.go index a57956f8b93..c753f300284 100644 --- a/internal/framework/types/arn.go +++ b/internal/framework/types/arn.go @@ -28,7 +28,8 @@ const ( ) var ( - _ xattr.TypeWithValidate = ARNType + _ xattr.TypeWithValidate = ARNType + _ basetypes.StringValuable = ARN{} ) func (t arnType) TerraformType(_ context.Context) tftypes.Type { diff --git a/internal/framework/types/cidr_block.go b/internal/framework/types/cidr_block.go index 648e0169988..60e6e442076 100644 --- a/internal/framework/types/cidr_block.go +++ b/internal/framework/types/cidr_block.go @@ -24,7 +24,8 @@ const ( ) var ( - _ xattr.TypeWithValidate = CIDRBlockType + _ xattr.TypeWithValidate = CIDRBlockType + _ basetypes.StringValuable = CIDRBlock{} ) func (t cidrBlockType) TerraformType(_ context.Context) tftypes.Type { diff --git a/internal/framework/types/duration.go b/internal/framework/types/duration.go index 982f464f14e..d0623f34898 100644 --- a/internal/framework/types/duration.go +++ b/internal/framework/types/duration.go @@ -24,7 +24,8 @@ const ( ) var ( - _ xattr.TypeWithValidate = DurationType + _ xattr.TypeWithValidate = DurationType + _ basetypes.StringValuable = Duration{} ) func (d durationType) TerraformType(_ context.Context) tftypes.Type { diff --git a/internal/framework/types/regexp.go b/internal/framework/types/regexp.go index aeff911f2c8..00b67e09273 100644 --- a/internal/framework/types/regexp.go +++ b/internal/framework/types/regexp.go @@ -24,7 +24,8 @@ var ( ) var ( - _ xattr.TypeWithValidate = RegexpType + _ xattr.TypeWithValidate = RegexpType + _ basetypes.StringValuable = Regexp{} ) func (t regexpType) TerraformType(_ context.Context) tftypes.Type { From e202bb4011aac149cc57feb6ca7d582037c9eb61 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 24 Oct 2023 11:30:04 -0400 Subject: [PATCH 3/9] Remove 'flex.ARNStringFromFramework' -- 'flex.StringFromFramework' now works with 'StringValuable'. --- internal/framework/flex/string.go | 11 ++--------- internal/framework/flex/string_test.go | 2 +- internal/service/batch/job_queue.go | 6 +++--- internal/service/cognitoidp/user_pool_client.go | 4 ++-- internal/service/lexv2models/bot.go | 4 ++-- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/internal/framework/flex/string.go b/internal/framework/flex/string.go index 5900028e62a..7b11be5c58f 100644 --- a/internal/framework/flex/string.go +++ b/internal/framework/flex/string.go @@ -9,12 +9,13 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" ) // StringFromFramework converts a Framework String value to a string pointer. // A null String is converted to a nil string pointer. -func StringFromFramework(ctx context.Context, v types.String) *string { +func StringFromFramework(ctx context.Context, v basetypes.StringValuable) *string { var output *string panicOnError(Expand(ctx, v, &output)) @@ -68,14 +69,6 @@ func StringToFrameworkLegacy(_ context.Context, v *string) types.String { return types.StringValue(aws.ToString(v)) } -func ARNStringFromFramework(ctx context.Context, v fwtypes.ARN) *string { - var output *string - - panicOnError(Expand(ctx, v, &output)) - - return output -} - func StringToFrameworkARN(ctx context.Context, v *string, diags *diag.Diagnostics) fwtypes.ARN { var output fwtypes.ARN diff --git a/internal/framework/flex/string_test.go b/internal/framework/flex/string_test.go index 96e3530580e..87ec11f04e1 100644 --- a/internal/framework/flex/string_test.go +++ b/internal/framework/flex/string_test.go @@ -227,7 +227,7 @@ func TestARNStringFromFramework(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - got := flex.ARNStringFromFramework(context.Background(), test.input) + got := flex.StringFromFramework(context.Background(), test.input) if diff := cmp.Diff(got, test.expected); diff != "" { t.Errorf("unexpected diff (+wanted, -got): %s", diff) diff --git a/internal/service/batch/job_queue.go b/internal/service/batch/job_queue.go index 0d922f48aad..7346ce68774 100644 --- a/internal/service/batch/job_queue.go +++ b/internal/service/batch/job_queue.go @@ -132,7 +132,7 @@ func (r *resourceJobQueue) Create(ctx context.Context, request resource.CreateRe } if !data.SchedulingPolicyARN.IsNull() { - input.SchedulingPolicyArn = flex.ARNStringFromFramework(ctx, data.SchedulingPolicyARN) + input.SchedulingPolicyArn = flex.StringFromFramework(ctx, data.SchedulingPolicyARN) } output, err := conn.CreateJobQueueWithContext(ctx, &input) @@ -229,13 +229,13 @@ func (r *resourceJobQueue) Update(ctx context.Context, request resource.UpdateRe } if !state.SchedulingPolicyARN.IsNull() { - input.SchedulingPolicyArn = flex.ARNStringFromFramework(ctx, state.SchedulingPolicyARN) + input.SchedulingPolicyArn = flex.StringFromFramework(ctx, state.SchedulingPolicyARN) update = true } if !plan.SchedulingPolicyARN.Equal(state.SchedulingPolicyARN) { if !plan.SchedulingPolicyARN.IsNull() || !plan.SchedulingPolicyARN.IsUnknown() { - input.SchedulingPolicyArn = flex.ARNStringFromFramework(ctx, plan.SchedulingPolicyARN) + input.SchedulingPolicyArn = flex.StringFromFramework(ctx, plan.SchedulingPolicyARN) update = true } else { diff --git a/internal/service/cognitoidp/user_pool_client.go b/internal/service/cognitoidp/user_pool_client.go index f1487ed8ac1..4a09f52c713 100644 --- a/internal/service/cognitoidp/user_pool_client.go +++ b/internal/service/cognitoidp/user_pool_client.go @@ -706,10 +706,10 @@ func (ac *analyticsConfiguration) expand(ctx context.Context) *cognitoidentitypr return nil } result := &cognitoidentityprovider.AnalyticsConfigurationType{ - ApplicationArn: flex.ARNStringFromFramework(ctx, ac.ApplicationARN), + ApplicationArn: flex.StringFromFramework(ctx, ac.ApplicationARN), ApplicationId: flex.StringFromFramework(ctx, ac.ApplicationID), ExternalId: flex.StringFromFramework(ctx, ac.ExternalID), - RoleArn: flex.ARNStringFromFramework(ctx, ac.RoleARN), + RoleArn: flex.StringFromFramework(ctx, ac.RoleARN), UserDataShared: flex.BoolFromFramework(ctx, ac.UserDataShared), } diff --git a/internal/service/lexv2models/bot.go b/internal/service/lexv2models/bot.go index 7a7181a0f95..83fdc880233 100644 --- a/internal/service/lexv2models/bot.go +++ b/internal/service/lexv2models/bot.go @@ -162,7 +162,7 @@ func (r *resourceBot) Create(ctx context.Context, req resource.CreateRequest, re BotName: aws.String(plan.Name.ValueString()), DataPrivacy: dpInput, IdleSessionTTLInSeconds: aws.Int32(int32(plan.IdleSessionTTLInSeconds.ValueInt64())), - RoleArn: flex.ARNStringFromFramework(ctx, plan.RoleARN), + RoleArn: flex.StringFromFramework(ctx, plan.RoleARN), BotTags: getTagsIn(ctx), } @@ -295,7 +295,7 @@ func (r *resourceBot) Update(ctx context.Context, req resource.UpdateRequest, re BotName: flex.StringFromFramework(ctx, plan.Name), IdleSessionTTLInSeconds: aws.Int32(int32(plan.IdleSessionTTLInSeconds.ValueInt64())), DataPrivacy: dpInput, - RoleArn: flex.ARNStringFromFramework(ctx, plan.RoleARN), + RoleArn: flex.StringFromFramework(ctx, plan.RoleARN), } if !plan.Description.IsNull() { From aedf56b1e47509f27992e4309679b7f8f59f41b5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 24 Oct 2023 11:42:32 -0400 Subject: [PATCH 4/9] d/aws_globalaccelerator_accelerator: Use 'flex.flex.StringToFrameworkARN'. --- .../service/globalaccelerator/accelerator_data_source.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/service/globalaccelerator/accelerator_data_source.go b/internal/service/globalaccelerator/accelerator_data_source.go index e09c797331c..171e51919a8 100644 --- a/internal/service/globalaccelerator/accelerator_data_source.go +++ b/internal/service/globalaccelerator/accelerator_data_source.go @@ -6,7 +6,6 @@ package globalaccelerator import ( "context" - "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/globalaccelerator" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -150,11 +149,7 @@ func (d *dataSourceAccelerator) Read(ctx context.Context, request datasource.Rea accelerator := results[0] acceleratorARN := aws.StringValue(accelerator.AcceleratorArn) - if v, err := arn.Parse(acceleratorARN); err != nil { - response.Diagnostics.AddError("parsing ARN", err.Error()) - } else { - data.ARN = fwtypes.ARNValue(v) - } + data.ARN = flex.StringToFrameworkARN(ctx, accelerator.AcceleratorArn, nil) data.DnsName = flex.StringToFrameworkLegacy(ctx, accelerator.DnsName) data.DualStackDNSName = flex.StringToFrameworkLegacy(ctx, accelerator.DualStackDnsName) data.Enabled = flex.BoolToFrameworkLegacy(ctx, accelerator.Enabled) From d26a8fc04c857571ae44617fb5fba17a393ccc62 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 24 Oct 2023 11:51:46 -0400 Subject: [PATCH 5/9] Document 'flex.StringToFrameworkARN'. --- internal/framework/flex/string.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/internal/framework/flex/string.go b/internal/framework/flex/string.go index 7b11be5c58f..e32fcd2ab68 100644 --- a/internal/framework/flex/string.go +++ b/internal/framework/flex/string.go @@ -25,7 +25,7 @@ func StringFromFramework(ctx context.Context, v basetypes.StringValuable) *strin // StringFromFramework converts a single Framework String value to a string pointer slice. // A null String is converted to a nil slice. -func StringSliceFromFramework(ctx context.Context, v types.String) []*string { +func StringSliceFromFramework(ctx context.Context, v basetypes.StringValuable) []*string { if v.IsNull() || v.IsUnknown() { return nil } @@ -69,10 +69,17 @@ func StringToFrameworkLegacy(_ context.Context, v *string) types.String { return types.StringValue(aws.ToString(v)) } +// StringToFrameworkARN converts a string pointer to a Framework custom ARN value. +// A nil string pointer is converted to a null ARN. +// If diags is nil, any errors cause a panic. func StringToFrameworkARN(ctx context.Context, v *string, diags *diag.Diagnostics) fwtypes.ARN { var output fwtypes.ARN - diags.Append(Flatten(ctx, v, &output)...) + if diags == nil { + panicOnError(Flatten(ctx, v, &output)) + } else { + diags.Append(Flatten(ctx, v, &output)...) + } return output } From 1154b39d56f7e700b62b5fd09a4e113cdc7e197b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 24 Oct 2023 11:58:25 -0400 Subject: [PATCH 6/9] framework/flex: Use 'Valuable' interfaces. --- internal/framework/flex/bool.go | 3 ++- internal/framework/flex/int.go | 3 ++- internal/framework/flex/list.go | 5 +++-- internal/framework/flex/map.go | 5 +++-- internal/framework/flex/set.go | 5 +++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/internal/framework/flex/bool.go b/internal/framework/flex/bool.go index 46c6cae6ed9..470d9483480 100644 --- a/internal/framework/flex/bool.go +++ b/internal/framework/flex/bool.go @@ -9,12 +9,13 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" ) // BoolFromFramework converts a Framework Bool value to a bool pointer. // A null Bool is converted to a nil bool pointer. -func BoolFromFramework(ctx context.Context, v types.Bool) *bool { +func BoolFromFramework(ctx context.Context, v basetypes.BoolValuable) *bool { var output *bool panicOnError(Expand(ctx, v, &output)) diff --git a/internal/framework/flex/int.go b/internal/framework/flex/int.go index a19917f130e..85f73e2d781 100644 --- a/internal/framework/flex/int.go +++ b/internal/framework/flex/int.go @@ -8,11 +8,12 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) // Int64FromFramework converts a Framework Int64 value to an int64 pointer. // A null Int64 is converted to a nil int64 pointer. -func Int64FromFramework(ctx context.Context, v types.Int64) *int64 { +func Int64FromFramework(ctx context.Context, v basetypes.Int64Valuable) *int64 { var output *int64 panicOnError(Expand(ctx, v, &output)) diff --git a/internal/framework/flex/list.go b/internal/framework/flex/list.go index 15259327b46..69a0b3df5d0 100644 --- a/internal/framework/flex/list.go +++ b/internal/framework/flex/list.go @@ -9,11 +9,12 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" "github.com/hashicorp/terraform-provider-aws/internal/slices" ) -func ExpandFrameworkStringList(ctx context.Context, v types.List) []*string { +func ExpandFrameworkStringList(ctx context.Context, v basetypes.ListValuable) []*string { var output []*string panicOnError(Expand(ctx, v, &output)) @@ -21,7 +22,7 @@ func ExpandFrameworkStringList(ctx context.Context, v types.List) []*string { return output } -func ExpandFrameworkStringValueList(ctx context.Context, v types.List) []string { +func ExpandFrameworkStringValueList(ctx context.Context, v basetypes.ListValuable) []string { var output []string panicOnError(Expand(ctx, v, &output)) diff --git a/internal/framework/flex/map.go b/internal/framework/flex/map.go index be4bfd01580..5d307e960f5 100644 --- a/internal/framework/flex/map.go +++ b/internal/framework/flex/map.go @@ -8,9 +8,10 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) -func ExpandFrameworkStringMap(ctx context.Context, v types.Map) map[string]*string { +func ExpandFrameworkStringMap(ctx context.Context, v basetypes.MapValuable) map[string]*string { var output map[string]*string panicOnError(Expand(ctx, v, &output)) @@ -18,7 +19,7 @@ func ExpandFrameworkStringMap(ctx context.Context, v types.Map) map[string]*stri return output } -func ExpandFrameworkStringValueMap(ctx context.Context, v types.Map) map[string]string { +func ExpandFrameworkStringValueMap(ctx context.Context, v basetypes.MapValuable) map[string]string { var output map[string]string panicOnError(Expand(ctx, v, &output)) diff --git a/internal/framework/flex/set.go b/internal/framework/flex/set.go index ab388dc40be..a9ce0a31730 100644 --- a/internal/framework/flex/set.go +++ b/internal/framework/flex/set.go @@ -9,9 +9,10 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) -func ExpandFrameworkStringSet(ctx context.Context, v types.Set) []*string { +func ExpandFrameworkStringSet(ctx context.Context, v basetypes.SetValuable) []*string { var output []*string panicOnError(Expand(ctx, v, &output)) @@ -19,7 +20,7 @@ func ExpandFrameworkStringSet(ctx context.Context, v types.Set) []*string { return output } -func ExpandFrameworkStringValueSet(ctx context.Context, v types.Set) Set[string] { +func ExpandFrameworkStringValueSet(ctx context.Context, v basetypes.SetValuable) Set[string] { var output []string panicOnError(Expand(ctx, v, &output)) From 157f728cf1bb1b9c4766cec1e5dc4f8b11a609ed Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 24 Oct 2023 14:47:06 -0400 Subject: [PATCH 7/9] Add 'tfresource.RetryWhenHTTPStatusCodeEquals'. --- internal/tfresource/retry.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/internal/tfresource/retry.go b/internal/tfresource/retry.go index dd70d289f81..fed9daaea01 100644 --- a/internal/tfresource/retry.go +++ b/internal/tfresource/retry.go @@ -57,7 +57,7 @@ func RetryWhen(ctx context.Context, timeout time.Duration, f func() (interface{} return output, nil } -// RetryWhenAWSErrCodeEquals retries the specified function when it returns one of the specified AWS error code. +// RetryWhenAWSErrCodeEquals retries the specified function when it returns one of the specified AWS error codes. func RetryWhenAWSErrCodeEquals(ctx context.Context, timeout time.Duration, f func() (interface{}, error), codes ...string) (interface{}, error) { // nosemgrep:ci.aws-in-func-name return RetryWhen(ctx, timeout, f, func(err error) (bool, error) { if tfawserr.ErrCodeEquals(err, codes...) || tfawserr_sdkv2.ErrCodeEquals(err, codes...) { @@ -135,6 +135,17 @@ func RetryUntilEqual[T comparable](ctx context.Context, timeout time.Duration, t return output, nil } +// RetryWhenHTTPStatusCodeEquals retries the specified function when it returns one of the specified HTTP status codes. +func RetryWhenHTTPStatusCodeEquals(ctx context.Context, timeout time.Duration, f func() (interface{}, error), statusCodes ...int) (interface{}, error) { // nosemgrep:ci.aws-in-func-name + return RetryWhen(ctx, timeout, f, func(err error) (bool, error) { + if tfawserr_sdkv2.ErrHTTPStatusCodeEquals(err, statusCodes...) { + return true, err + } + + return false, err + }) +} + var ErrFoundResource = errors.New(`found resource`) // RetryUntilNotFound retries the specified function until it returns a retry.NotFoundError. From ce3378f7091964ff73094f035aec517f2cb2d8ef Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 24 Oct 2023 16:14:57 -0400 Subject: [PATCH 8/9] Update internal/framework/validators/aws_account_id.go Co-authored-by: Jared Baker --- internal/framework/validators/aws_account_id.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/framework/validators/aws_account_id.go b/internal/framework/validators/aws_account_id.go index d091e02cbab..98e21d0629b 100644 --- a/internal/framework/validators/aws_account_id.go +++ b/internal/framework/validators/aws_account_id.go @@ -24,7 +24,7 @@ func (validator awsAccountIDValidator) MarkdownDescription(ctx context.Context) return validator.Description(ctx) } -// Validate performs the validation. +// ValidateString performs the validation. func (validator awsAccountIDValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() { return From 9c34f581b9eaa4a4bec810be1e09a9add6f7ba92 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 24 Oct 2023 16:32:46 -0400 Subject: [PATCH 9/9] Fix semgrep 'ci.aws-in-func-name'. --- internal/framework/validators/aws_account_id.go | 2 +- internal/framework/validators/aws_account_id_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/framework/validators/aws_account_id.go b/internal/framework/validators/aws_account_id.go index d091e02cbab..13e01839fa5 100644 --- a/internal/framework/validators/aws_account_id.go +++ b/internal/framework/validators/aws_account_id.go @@ -47,6 +47,6 @@ func (validator awsAccountIDValidator) ValidateString(ctx context.Context, reque // - Is a string, which represents a valid AWS account ID. // // Null (unconfigured) and unknown (known after apply) values are skipped. -func AWSAccountID() validator.String { +func AWSAccountID() validator.String { // nosemgrep:ci.aws-in-func-name return awsAccountIDValidator{} } diff --git a/internal/framework/validators/aws_account_id_test.go b/internal/framework/validators/aws_account_id_test.go index 23d7d7e021a..d4f344ef1b6 100644 --- a/internal/framework/validators/aws_account_id_test.go +++ b/internal/framework/validators/aws_account_id_test.go @@ -15,7 +15,7 @@ import ( fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" ) -func TestAWSAccountIDValidator(t *testing.T) { +func TestAWSAccountIDValidator(t *testing.T) { // nosemgrep:ci.aws-in-func-name t.Parallel() type testCase struct {