From 99f2724d2a4c69ad7b77684ec66ce3277325d3a2 Mon Sep 17 00:00:00 2001 From: showwin Date: Tue, 9 Jan 2024 15:37:11 +0900 Subject: [PATCH 01/24] add New Resource aws_rds_integration --- internal/service/rds/generate.go | 1 + internal/service/rds/integration.go | 366 +++++++++++ internal/service/rds/integration_test.go | 605 +++++++++++++++++++ internal/service/rds/service_package_gen.go | 7 + internal/service/rds/tagsv2_gen.go | 106 ++++ website/docs/r/rds_integration.html.markdown | 141 +++++ 6 files changed, 1226 insertions(+) create mode 100644 internal/service/rds/integration.go create mode 100644 internal/service/rds/integration_test.go create mode 100644 internal/service/rds/tagsv2_gen.go create mode 100644 website/docs/r/rds_integration.html.markdown diff --git a/internal/service/rds/generate.go b/internal/service/rds/generate.go index 887fb1d8ded..342084e225c 100644 --- a/internal/service/rds/generate.go +++ b/internal/service/rds/generate.go @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MPL-2.0 //go:generate go run ../../generate/tags/main.go -ListTags -ListTagsInIDElem=ResourceName -ListTagsOutTagsElem=TagList -ServiceTagsSlice -TagOp=AddTagsToResource -TagInIDElem=ResourceName -UntagOp=RemoveTagsFromResource -UpdateTags +//go:generate go run ../../generate/tags/main.go -AWSSDKVersion=2 -ServiceTagsSlice -TagsFunc=tagsV2 -KeyValueTagsFunc=keyValueTagsV2 -GetTagsInFunc=getTagsInV2 -SetTagsOutFunc=setTagsOutV2 -TagInIDElem=ResourceName -UntagInTagsElem=TagKeys -TagOp=AddTagsToResource -UntagOp=RemoveTagsFromResource -UpdateTagsFunc=updateTagsV2 -UpdateTags -- tagsv2_gen.go //go:generate go run ../../generate/servicepackage/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. diff --git a/internal/service/rds/integration.go b/internal/service/rds/integration.go new file mode 100644 index 00000000000..9912cb916ba --- /dev/null +++ b/internal/service/rds/integration.go @@ -0,0 +1,366 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package rds + +import ( + "context" + "errors" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds" + awstypes "github.com/aws/aws-sdk-go-v2/service/rds/types" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkResource(name="Integration") +// @Tags(identifierAttribute="arn") +func newResourceIntegration(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &resourceIntegration{} + + r.SetDefaultCreateTimeout(60 * time.Minute) + r.SetDefaultUpdateTimeout(10 * time.Minute) + r.SetDefaultDeleteTimeout(30 * time.Minute) + + return r, nil +} + +const ( + ResNameIntegration = "Integration" + + IntegrationStatusCreating = "creating" + IntegrationStatusActive = "active" + IntegrationStatusModifying = "modifying" + IntegrationStatusFailed = "failed" + IntegrationStatusDeleting = "deleting" + IntegrationStatusSyncing = "syncing" + IntegrationStatusNeedsAttention = "needs_attention" +) + +type resourceIntegration struct { + framework.ResourceWithConfigure + framework.WithTimeouts +} + +func (r *resourceIntegration) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_rds_integration" +} + +func (r *resourceIntegration) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "arn": framework.ARNAttributeComputedOnly(), + "additional_encryption_context": schema.MapAttribute{ + ElementType: types.StringType, + Optional: true, + PlanModifiers: []planmodifier.Map{ + mapplanmodifier.RequiresReplace(), + }, + }, + "id": framework.IDAttribute(), + "integration_name": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "kms_key_id": schema.StringAttribute{ + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "source_arn": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + names.AttrTags: tftags.TagsAttribute(), + names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), + "target_arn": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + Blocks: map[string]schema.Block{ + "timeouts": timeouts.Block(ctx, timeouts.Opts{ + Create: true, + Update: true, + Delete: true, + }), + }, + } +} + +func (r *resourceIntegration) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().RDSClient(ctx) + + var plan resourceIntegrationData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + in := &rds.CreateIntegrationInput{ + IntegrationName: aws.String(plan.IntegrationName.ValueString()), + SourceArn: aws.String(plan.SourceArn.ValueString()), + Tags: getTagsInV2(ctx), + TargetArn: aws.String(plan.TargetArn.ValueString()), + } + if !plan.KMSKeyId.IsNull() { + in.KMSKeyId = aws.String(plan.KMSKeyId.ValueString()) + } + if !plan.AdditionalEncryptionContext.IsNull() { + in.AdditionalEncryptionContext = flex.ExpandFrameworkStringValueMap(ctx, plan.AdditionalEncryptionContext) + } + + out, err := conn.CreateIntegration(ctx, in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.RDS, create.ErrActionCreating, ResNameIntegration, plan.IntegrationName.String(), err), + err.Error(), + ) + return + } + if out == nil || out.IntegrationName == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.RDS, create.ErrActionCreating, ResNameIntegration, plan.IntegrationName.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + plan.ARN = flex.StringToFramework(ctx, out.IntegrationArn) + plan.ID = types.StringValue(aws.ToString(out.IntegrationArn)) + + createTimeout := r.CreateTimeout(ctx, plan.Timeouts) + _, err = waitIntegrationCreated(ctx, conn, plan.ARN.ValueString(), createTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.RDS, create.ErrActionWaitingForCreation, ResNameIntegration, plan.IntegrationName.String(), err), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *resourceIntegration) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + conn := r.Meta().RDSClient(ctx) + + var state resourceIntegrationData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + out, err := FindIntegrationByARN(ctx, conn, state.ARN.ValueString()) + + if tfresource.NotFound(err) { + resp.State.RemoveResource(ctx) + return + } + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.RDS, create.ErrActionSetting, ResNameIntegration, state.ARN.String(), err), + err.Error(), + ) + return + } + if out == nil { + resp.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(errors.New("not found"))) + resp.State.RemoveResource(ctx) + return + } + + state.ARN = flex.StringToFramework(ctx, out.IntegrationArn) + + state.refreshFromOutput(ctx, out) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceIntegration) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan, state resourceIntegrationData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *resourceIntegration) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + conn := r.Meta().RDSClient(ctx) + + var state resourceIntegrationData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + in := &rds.DeleteIntegrationInput{ + IntegrationIdentifier: aws.String(state.ARN.ValueString()), + } + + _, err := conn.DeleteIntegration(ctx, in) + if err != nil { + var nfe *awstypes.ResourceNotFoundFault + if errors.As(err, &nfe) { + return + } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.RDS, create.ErrActionDeleting, ResNameIntegration, state.ARN.String(), err), + err.Error(), + ) + return + } + + deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) + _, err = waitIntegrationDeleted(ctx, conn, state.ARN.ValueString(), deleteTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.RDS, create.ErrActionWaitingForDeletion, ResNameIntegration, state.ARN.String(), err), + err.Error(), + ) + return + } +} + +func (r *resourceIntegration) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("arn"), req, resp) +} + +func (r *resourceIntegration) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, request, response) +} + +func waitIntegrationCreated(ctx context.Context, conn *rds.Client, arn string, timeout time.Duration) (*awstypes.Integration, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{IntegrationStatusCreating, IntegrationStatusModifying}, + Target: []string{IntegrationStatusActive}, + Refresh: statusIntegration(ctx, conn, arn), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*awstypes.Integration); ok { + return out, err + } + + return nil, err +} + +func waitIntegrationDeleted(ctx context.Context, conn *rds.Client, arn string, timeout time.Duration) (*awstypes.Integration, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{IntegrationStatusDeleting, IntegrationStatusActive}, + Target: []string{}, + Refresh: statusIntegration(ctx, conn, arn), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*awstypes.Integration); ok { + return out, err + } + + return nil, err +} + +func statusIntegration(ctx context.Context, conn *rds.Client, arn string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + out, err := FindIntegrationByARN(ctx, conn, arn) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return out, string(out.Status), nil + } +} + +func FindIntegrationByARN(ctx context.Context, conn *rds.Client, arn string) (*awstypes.Integration, error) { + in := &rds.DescribeIntegrationsInput{ + IntegrationIdentifier: aws.String(arn), + } + + out, err := conn.DescribeIntegrations(ctx, in) + + if errs.IsA[*awstypes.IntegrationNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + if err != nil { + return nil, err + } + + if out == nil || len(out.Integrations) == 0 { + return nil, tfresource.NewEmptyResultError(in) + } + + return &out.Integrations[0], nil +} + +type resourceIntegrationData struct { + ARN types.String `tfsdk:"arn"` + AdditionalEncryptionContext types.Map `tfsdk:"additional_encryption_context"` + ID types.String `tfsdk:"id"` + IntegrationName types.String `tfsdk:"integration_name"` + KMSKeyId types.String `tfsdk:"kms_key_id"` + SourceArn fwtypes.ARN `tfsdk:"source_arn"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` + TargetArn fwtypes.ARN `tfsdk:"target_arn"` + Timeouts timeouts.Value `tfsdk:"timeouts"` +} + +// refreshFromOutput writes state data from an AWS response object +func (r *resourceIntegrationData) refreshFromOutput(ctx context.Context, out *awstypes.Integration) { + if out == nil { + return + } + + r.ARN = flex.StringToFramework(ctx, out.IntegrationArn) + r.AdditionalEncryptionContext = flex.FlattenFrameworkStringValueMap(ctx, out.AdditionalEncryptionContext) + r.ID = types.StringValue(aws.ToString(out.IntegrationArn)) + r.IntegrationName = flex.StringToFramework(ctx, out.IntegrationName) + r.KMSKeyId = flex.StringToFramework(ctx, out.KMSKeyId) + r.SourceArn = flex.StringToFrameworkARN(ctx, out.SourceArn) + r.TargetArn = flex.StringToFrameworkARN(ctx, out.TargetArn) + + setTagsOutV2(ctx, out.Tags) +} diff --git a/internal/service/rds/integration_test.go b/internal/service/rds/integration_test.go new file mode 100644 index 00000000000..2b65110a625 --- /dev/null +++ b/internal/service/rds/integration_test.go @@ -0,0 +1,605 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package rds_test + +import ( + "context" + "errors" + "fmt" + "testing" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds" + rdsv1 "github.com/aws/aws-sdk-go/service/rds" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + tfrds "github.com/hashicorp/terraform-provider-aws/internal/service/rds" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccRDSIntegration_basic(t *testing.T) { + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + ctx := acctest.Context(t) + + var integration rds.DescribeIntegrationsOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_rds_integration.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, rdsv1.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckIntegrationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccIntegrationConfigBase(rName), + Check: resource.ComposeTestCheckFunc( + waitUntilRDSReboot(ctx, rName), + ), + }, + { + Config: testAccIntegrationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckIntegrationExists(ctx, resourceName, &integration), + resource.TestCheckResourceAttr(resourceName, "integration_name", rName), + resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.mysql_test", "arn"), + resource.TestCheckResourceAttrPair(resourceName, "target_arn", "aws_redshiftserverless_namespace.test", "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccRDSIntegration_optional(t *testing.T) { + ctx := acctest.Context(t) + + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var integration rds.DescribeIntegrationsOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_rds_integration.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, rdsv1.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckIntegrationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccIntegrationConfigBase(rName), + Check: resource.ComposeTestCheckFunc( + waitUntilRDSReboot(ctx, rName), + ), + }, + { + Config: testAccIntegrationConfig_optional(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckIntegrationExists(ctx, resourceName, &integration), + resource.TestCheckResourceAttr(resourceName, "integration_name", rName), + resource.TestCheckResourceAttrPair(resourceName, "kms_key_id", "aws_kms_key.test", "arn"), + resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.mysql_test", "arn"), + resource.TestCheckResourceAttrPair(resourceName, "target_arn", "aws_redshiftserverless_namespace.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "additional_encryption_context.department", "test"), + resource.TestCheckResourceAttr(resourceName, "tags.Test", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckIntegrationDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_rds_integration" { + continue + } + + _, err := tfrds.FindIntegrationByARN(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + return nil + } + if err != nil { + return create.Error(names.RDS, create.ErrActionCheckingDestroyed, tfrds.ResNameIntegration, rs.Primary.ID, err) + } + + return create.Error(names.RDS, create.ErrActionCheckingDestroyed, tfrds.ResNameIntegration, rs.Primary.ID, errors.New("not destroyed")) + } + + return nil + } +} + +func testAccCheckIntegrationExists(ctx context.Context, name string, integration *rds.DescribeIntegrationsOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.RDS, create.ErrActionCheckingExistence, tfrds.ResNameIntegration, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.RDS, create.ErrActionCheckingExistence, tfrds.ResNameIntegration, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) + resp, err := conn.DescribeIntegrations(ctx, &rds.DescribeIntegrationsInput{ + IntegrationIdentifier: aws.String(rs.Primary.ID), + }) + + if err != nil { + return create.Error(names.RDS, create.ErrActionCheckingExistence, tfrds.ResNameIntegration, rs.Primary.ID, err) + } + + *integration = *resp + + return nil + } +} + +// Wait RDS rebooting for static DB parameter changes +func waitUntilRDSReboot(ctx context.Context, instanceIdentifier string) resource.TestCheckFunc { + return func(s *terraform.State) error { + + // Wait for rebooting + time.Sleep(60 * time.Second) + + // Wait for being available + for { + status := getDBInstanceStatus(ctx, instanceIdentifier) + if status == "available" { + break + } + + time.Sleep(10 * time.Second) + } + + return nil + } +} + +func getDBInstanceStatus(ctx context.Context, instanceIdentifier string) string { + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) + + result, err := conn.DescribeDBInstances(ctx, &rds.DescribeDBInstancesInput{ + DBInstanceIdentifier: aws.String(instanceIdentifier), + }) + if err != nil { + fmt.Errorf("failed to describe DB instances, %v", err) + } + + if len(result.DBInstances) == 0 { + fmt.Errorf("DB instance %s not found", instanceIdentifier) + } + + instance := result.DBInstances[0] + status := *instance.DBInstanceStatus + fmt.Printf("Current DB instance status: %s\n", status) + + return status +} + +func testAccIntegrationConfigBase(rName string) string { + return fmt.Sprintf(` +locals { + cluster_parameters = { + "binlog_replication_globaldb" = { + value = "0" + apply_method = "pending-reboot" + }, + "binlog_format" = { + value = "ROW" + apply_method = "pending-reboot" + }, + "binlog_row_metadata" = { + value = "full" + apply_method = "immediate" + }, + "binlog_row_image" = { + value = "full" + apply_method = "immediate" + }, + "aurora_enhanced_binlog" = { + value = "1" + apply_method = "pending-reboot" + }, + "binlog_backup" = { + value = "0" + apply_method = "pending-reboot" + }, + } +} + +data "aws_caller_identity" "current" {} + +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +resource "aws_vpc" "test" { + cidr_block = "10.1.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test1" { + cidr_block = "10.1.1.0/24" + availability_zone = data.aws_availability_zones.available.names[0] + vpc_id = aws_vpc.test.id + + tags = { + Name = "%[1]s-1" + } +} + +resource "aws_subnet" "test2" { + cidr_block = "10.1.2.0/24" + availability_zone = data.aws_availability_zones.available.names[1] + vpc_id = aws_vpc.test.id + + tags = { + Name = "%[1]s-2" + } +} + +resource "aws_subnet" "test3" { + cidr_block = "10.1.3.0/24" + availability_zone = data.aws_availability_zones.available.names[2] + vpc_id = aws_vpc.test.id + + tags = { + Name = "%[1]s-3" + } +} + +resource "aws_security_group" "test" { + vpc_id = aws_vpc.test.id + + ingress { + protocol = -1 + self = true + from_port = 0 + to_port = 0 + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_db_subnet_group" "test" { + name = %[1]q + subnet_ids = [ + aws_subnet.test1.id, + aws_subnet.test2.id, + ] + + tags = { + Name = %[1]q + } +} + +resource "aws_rds_cluster_parameter_group" "test" { + name = "%[1]s" + family = "aurora-mysql8.0" + + dynamic "parameter" { + for_each = local.cluster_parameters + content { + name = parameter.key + value = parameter.value["value"] + apply_method = parameter.value["apply_method"] + } + } +} + +resource "aws_rds_cluster" "mysql_test" { + cluster_identifier = %[1]q + engine = "aurora-mysql" + engine_version = "8.0.mysql_aurora.3.05.1" + database_name = "tftest" + master_username = "testuser" + master_password = "Testpassword123" + skip_final_snapshot = true + vpc_security_group_ids = [aws_security_group.test.id] + db_subnet_group_name = aws_db_subnet_group.test.name + db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.test.name + apply_immediately = true +} + +resource "aws_rds_cluster_instance" "mysql_test" { + identifier = %[1]q + cluster_identifier = aws_rds_cluster.mysql_test.id + instance_class = "db.r6g.large" + engine = aws_rds_cluster.mysql_test.engine + engine_version = aws_rds_cluster.mysql_test.engine_version +} + +resource "aws_redshift_cluster" "test" { + cluster_identifier = %[1]q + availability_zone = data.aws_availability_zones.available.names[0] + database_name = "test" + master_username = "testuser" + master_password = "Testpassword123" + node_type = "dc2.large" + cluster_type = "single-node" + skip_final_snapshot = true +} + +`, rName) +} + +func testAccIntegrationConfig_basic(rName string) string { + return acctest.ConfigCompose( + testAccIntegrationConfigBase(rName), + fmt.Sprintf(` +resource "aws_redshiftserverless_namespace" "test" { + namespace_name = %[1]q +} + +resource "aws_redshiftserverless_workgroup" "test" { + namespace_name = aws_redshiftserverless_namespace.test.namespace_name + workgroup_name = %[1]q + base_capacity = 8 + publicly_accessible = false + subnet_ids = [ + aws_subnet.test1.id, + aws_subnet.test2.id, + aws_subnet.test3.id, + ] + + config_parameter { + parameter_key = "enable_case_sensitive_identifier" + parameter_value = "true" + } + config_parameter { + parameter_key = "auto_mv" + parameter_value = "true" + } + config_parameter { + parameter_key = "datestyle" + parameter_value = "ISO, MDY" + } + config_parameter { + parameter_key = "enable_user_activity_logging" + parameter_value = "true" + } + config_parameter { + parameter_key = "max_query_execution_time" + parameter_value = "14400" + } + config_parameter { + parameter_key = "query_group" + parameter_value = "default" + } + config_parameter { + parameter_key = "require_ssl" + parameter_value = "false" + } + config_parameter { + parameter_key = "search_path" + parameter_value = "$user, public" + } + config_parameter { + parameter_key = "use_fips_ssl" + parameter_value = "false" + } +} + +# The "aws_redshiftserverless_resource_policy" resource doesn't support the following action types. +# Therefore we need to use the "aws_redshift_resource_policy" resource for RedShift-serverless instead. +resource "aws_redshift_resource_policy" "test" { + resource_arn = aws_redshiftserverless_namespace.test.arn + policy = jsonencode({ + Version = "2008-10-17" + Statement = [{ + Effect = "Allow" + Principal = { + AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" + } + Action = "redshift:CreateInboundIntegration" + Resource = aws_redshiftserverless_namespace.test.arn + },{ + Effect = "Allow" + Principal = { + Service = "redshift.amazonaws.com" + } + Action = "redshift:AuthorizeInboundIntegration" + Resource = aws_redshiftserverless_namespace.test.arn + Condition = { + StringEquals = { + "aws:SourceArn" = aws_rds_cluster.mysql_test.arn + } + } + }] + }) +} + +resource "aws_rds_integration" "test" { + integration_name = %[1]q + source_arn = aws_rds_cluster.mysql_test.arn + target_arn = aws_redshiftserverless_namespace.test.arn + + depends_on = [ + aws_rds_cluster.mysql_test, + aws_redshiftserverless_namespace.test, + aws_redshiftserverless_workgroup.test, + aws_redshift_resource_policy.test, + ] + + lifecycle { + ignore_changes = [ + kms_key_id + ] + } +} +`, rName)) +} + +func testAccIntegrationConfig_optional(rName string) string { + return acctest.ConfigCompose( + testAccIntegrationConfigBase(rName), + fmt.Sprintf(` +resource "aws_redshiftserverless_namespace" "test" { + namespace_name = %[1]q +} + +resource "aws_redshiftserverless_workgroup" "test" { + namespace_name = aws_redshiftserverless_namespace.test.namespace_name + workgroup_name = %[1]q + base_capacity = 8 + publicly_accessible = false + subnet_ids = [ + aws_subnet.test1.id, + aws_subnet.test2.id, + aws_subnet.test3.id, + ] + + config_parameter { + parameter_key = "enable_case_sensitive_identifier" + parameter_value = "true" + } + config_parameter { + parameter_key = "auto_mv" + parameter_value = "true" + } + config_parameter { + parameter_key = "datestyle" + parameter_value = "ISO, MDY" + } + config_parameter { + parameter_key = "enable_user_activity_logging" + parameter_value = "true" + } + config_parameter { + parameter_key = "max_query_execution_time" + parameter_value = "14400" + } + config_parameter { + parameter_key = "query_group" + parameter_value = "default" + } + config_parameter { + parameter_key = "require_ssl" + parameter_value = "false" + } + config_parameter { + parameter_key = "search_path" + parameter_value = "$user, public" + } + config_parameter { + parameter_key = "use_fips_ssl" + parameter_value = "false" + } +} + +# The "aws_redshiftserverless_resource_policy" resource doesn't support the following action types. +# Therefore we need to use the "aws_redshift_resource_policy" resource for RedShift-serverless instead. +resource "aws_redshift_resource_policy" "test" { + resource_arn = aws_redshiftserverless_namespace.test.arn + policy = jsonencode({ + Version = "2008-10-17" + Statement = [{ + Effect = "Allow" + Principal = { + AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" + } + Action = "redshift:CreateInboundIntegration" + Resource = aws_redshiftserverless_namespace.test.arn + },{ + Effect = "Allow" + Principal = { + Service = "redshift.amazonaws.com" + } + Action = "redshift:AuthorizeInboundIntegration" + Resource = aws_redshiftserverless_namespace.test.arn + Condition = { + StringEquals = { + "aws:SourceArn" = aws_rds_cluster.mysql_test.arn + } + } + }] + }) +} + +resource "aws_kms_key" "test" { + deletion_window_in_days = 10 + policy = data.aws_iam_policy_document.key_policy.json +} + +data "aws_iam_policy_document" "key_policy" { + statement { + actions = ["kms:*"] + resources = ["*"] + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] + } + } + + statement { + actions = ["kms:CreateGrant"] + resources = ["*"] + principals { + type = "Service" + identifiers = ["redshift.amazonaws.com"] + } + } +} + +resource "aws_rds_integration" "test" { + integration_name = %[1]q + source_arn = aws_rds_cluster.mysql_test.arn + target_arn = aws_redshiftserverless_namespace.test.arn + + kms_key_id = aws_kms_key.test.arn + additional_encryption_context = { + "department": "test", + } + + tags = { + Test = "true" + } + + depends_on = [ + aws_rds_cluster.mysql_test, + aws_redshiftserverless_namespace.test, + aws_redshiftserverless_workgroup.test, + aws_redshift_resource_policy.test, + ] +} +`, rName)) +} diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index f6d0e9d990a..eda61ca9558 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -26,6 +26,13 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic { Factory: newResourceExportTask, }, + { + Factory: newResourceIntegration, + Name: "Integration", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: "arn", + }, + }, } } diff --git a/internal/service/rds/tagsv2_gen.go b/internal/service/rds/tagsv2_gen.go new file mode 100644 index 00000000000..3abccb783a9 --- /dev/null +++ b/internal/service/rds/tagsv2_gen.go @@ -0,0 +1,106 @@ +// Code generated by internal/generate/tags/main.go; DO NOT EDIT. +package rds + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds" + awstypes "github.com/aws/aws-sdk-go-v2/service/rds/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "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" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// []*SERVICE.Tag handling + +// tagsV2 returns rds service tags. +func tagsV2(tags tftags.KeyValueTags) []awstypes.Tag { + result := make([]awstypes.Tag, 0, len(tags)) + + for k, v := range tags.Map() { + tag := awstypes.Tag{ + Key: aws.String(k), + Value: aws.String(v), + } + + result = append(result, tag) + } + + return result +} + +// keyValueTagsV2 creates tftags.KeyValueTags from rds service tags. +func keyValueTagsV2(ctx context.Context, tags []awstypes.Tag) tftags.KeyValueTags { + m := make(map[string]*string, len(tags)) + + for _, tag := range tags { + m[aws.ToString(tag.Key)] = tag.Value + } + + return tftags.New(ctx, m) +} + +// getTagsInV2 returns rds service tags from Context. +// nil is returned if there are no input tags. +func getTagsInV2(ctx context.Context) []awstypes.Tag { + if inContext, ok := tftags.FromContext(ctx); ok { + if tags := tagsV2(inContext.TagsIn.UnwrapOrDefault()); len(tags) > 0 { + return tags + } + } + + return nil +} + +// setTagsOutV2 sets rds service tags in Context. +func setTagsOutV2(ctx context.Context, tags []awstypes.Tag) { + if inContext, ok := tftags.FromContext(ctx); ok { + inContext.TagsOut = types.Some(keyValueTagsV2(ctx, tags)) + } +} + +// updateTagsV2 updates rds service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func updateTagsV2(ctx context.Context, conn *rds.Client, identifier string, oldTagsMap, newTagsMap any) 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.RDS) + if len(removedTags) > 0 { + input := &rds.RemoveTagsFromResourceInput{ + ResourceName: aws.String(identifier), + TagKeys: removedTags.Keys(), + } + + _, err := conn.RemoveTagsFromResource(ctx, input) + + if err != nil { + return fmt.Errorf("untagging resource (%s): %w", identifier, err) + } + } + + updatedTags := oldTags.Updated(newTags) + updatedTags = updatedTags.IgnoreSystem(names.RDS) + if len(updatedTags) > 0 { + input := &rds.AddTagsToResourceInput{ + ResourceName: aws.String(identifier), + Tags: tagsV2(updatedTags), + } + + _, err := conn.AddTagsToResource(ctx, input) + + if err != nil { + return fmt.Errorf("tagging resource (%s): %w", identifier, err) + } + } + + return nil +} diff --git a/website/docs/r/rds_integration.html.markdown b/website/docs/r/rds_integration.html.markdown new file mode 100644 index 00000000000..2f7e396f069 --- /dev/null +++ b/website/docs/r/rds_integration.html.markdown @@ -0,0 +1,141 @@ +--- +subcategory: "RDS (Relational Database)" +layout: "aws" +page_title: "AWS: aws_rds_integration" +description: |- + Terraform resource for managing an AWS RDS (Relational Database) Integration. +--- + +# Resource: aws_rds_integration + +Terraform resource for managing an AWS RDS (Relational Database) zero-ETL integration. You can refer to the [User Guide](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/zero-etl.setting-up.html). + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_redshiftserverless_namespace" "example" { + namespace_name = "redshift-example" +} + +resource "aws_redshiftserverless_workgroup" "example" { + namespace_name = aws_redshiftserverless_namespace.example.namespace_name + workgroup_name = "example-workspace" + base_capacity = 8 + publicly_accessible = false + subnet_ids = [aws_subnet.example1.id, aws_subnet.example2.id, aws_subnet.example3.id] + + config_parameter { + parameter_key = "enable_case_sensitive_identifier" + parameter_value = "true" + } +} + +resource "aws_rds_integration" "example" { + integration_name = "example" + source_arn = aws_rds_cluster.example.arn + target_arn = aws_redshiftserverless_namespace.example.arn + + lifecycle { + ignore_changes = [ + kms_key_id + ] + } +} +``` + +### Use own KMS key + +```terraform +data "aws_caller_identity" "current" {} + +resource "aws_kms_key" "example" { + deletion_window_in_days = 10 + policy = data.aws_iam_policy_document.key_policy.json +} + +data "aws_iam_policy_document" "key_policy" { + statement { + actions = ["kms:*"] + resources = ["*"] + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] + } + } + + statement { + actions = ["kms:CreateGrant"] + resources = ["*"] + principals { + type = "Service" + identifiers = ["redshift.amazonaws.com"] + } + } +} + +resource "aws_rds_integration" "example" { + integration_name = "example" + source_arn = aws_rds_cluster.example.arn + target_arn = aws_redshiftserverless_namespace.example.arn + + kms_key_id = aws_kms_key.example.arn + additional_encryption_context = { + "example": "test", + } +} +``` + +## Argument Reference + +For more detailed documentation about each argument, refer to the [AWS official documentation](https://docs.aws.amazon.com/cli/latest/reference/rds/create-integration.html). + +The following arguments are required: + +* `integration_name` - (Required, Forces new resources) Name of the integration. + +* `source_arn` - (Required, Forces new resources) ARN of the database to use as the source for replication. + +* `target_arn` - (Required, Forces new resources) ARN of the Redshift data warehouse to use as the target for replication. + +The following arguments are optional: + +* `kms_key_id` - (Optional, Forces new resources) KMS key identifier for the key to use to encrypt the integration. If you don't specify an encryption key, RDS uses a default AWS owned key. If you use the default AWS owned key, you should ignore `kms_key_id` parameter by using [`lifecycle` parameter](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes) to avoid unintended change after the first creation. + +* `additional_encryption_context` - (Optional, Forces new resources) Set of non-secret key–value pairs that contains additional contextual information about the data. For more information, see the [User Guide](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#encrypt_context). You can only include this parameter if you specify the `kms_key_id` parameter. + +* `tags` - (Optional) Key-value map of resource tags. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Integration. +* `id` - ID of the Integration. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `60m`) +* `update` - (Default `10m`) +* `delete` - (Default `30m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import RDS (Relational Database) Integration using the `arn`. For example: + +```terraform +import { + to = aws_rds_integration.example + id = "arn:aws:rds:us-west-2:123456789012:integration:abcdefgh-0000-1111-2222-123456789012" +} +``` + +Using `terraform import`, import RDS (Relational Database) Integration using the `arn`. For example: + +```console +% terraform import aws_rds_integration.example arn:aws:rds:us-west-2:123456789012:integration:abcdefgh-0000-1111-2222-123456789012 +``` From b7f1a83f1da029f83d7a7553a002d336e3cc9b4f Mon Sep 17 00:00:00 2001 From: showwin Date: Tue, 9 Jan 2024 15:46:50 +0900 Subject: [PATCH 02/24] add CHANGELOG --- .changelog/35199.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/35199.txt diff --git a/.changelog/35199.txt b/.changelog/35199.txt new file mode 100644 index 00000000000..9b075259b93 --- /dev/null +++ b/.changelog/35199.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_rds_integration +``` From 1502737a92c7f4df1842bd010732d061cb63755a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 1 Aug 2024 13:49:30 -0400 Subject: [PATCH 03/24] Run 'make fix-constants PKG=rds'. --- internal/service/rds/integration.go | 12 ++++++------ internal/service/rds/integration_test.go | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/service/rds/integration.go b/internal/service/rds/integration.go index bbc8be0aad7..b972750a270 100644 --- a/internal/service/rds/integration.go +++ b/internal/service/rds/integration.go @@ -68,7 +68,7 @@ func (r *resourceIntegration) Metadata(_ context.Context, req resource.MetadataR func (r *resourceIntegration) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - "arn": framework.ARNAttributeComputedOnly(), + names.AttrARN: framework.ARNAttributeComputedOnly(), "additional_encryption_context": schema.MapAttribute{ ElementType: types.StringType, Optional: true, @@ -76,14 +76,14 @@ func (r *resourceIntegration) Schema(ctx context.Context, req resource.SchemaReq mapplanmodifier.RequiresReplace(), }, }, - "id": framework.IDAttribute(), + names.AttrID: framework.IDAttribute(), "integration_name": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, }, - "kms_key_id": schema.StringAttribute{ + names.AttrKMSKeyID: schema.StringAttribute{ Optional: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), @@ -98,7 +98,7 @@ func (r *resourceIntegration) Schema(ctx context.Context, req resource.SchemaReq }, names.AttrTags: tftags.TagsAttribute(), names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), - "target_arn": schema.StringAttribute{ + names.AttrTargetARN: schema.StringAttribute{ CustomType: fwtypes.ARNType, Required: true, PlanModifiers: []planmodifier.String{ @@ -107,7 +107,7 @@ func (r *resourceIntegration) Schema(ctx context.Context, req resource.SchemaReq }, }, Blocks: map[string]schema.Block{ - "timeouts": timeouts.Block(ctx, timeouts.Opts{ + names.AttrTimeouts: timeouts.Block(ctx, timeouts.Opts{ Create: true, Update: true, Delete: true, @@ -254,7 +254,7 @@ func (r *resourceIntegration) Delete(ctx context.Context, req resource.DeleteReq } func (r *resourceIntegration) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("arn"), req, resp) + resource.ImportStatePassthroughID(ctx, path.Root(names.AttrARN), req, resp) } func (r *resourceIntegration) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { diff --git a/internal/service/rds/integration_test.go b/internal/service/rds/integration_test.go index 2b65110a625..9a0beac03b3 100644 --- a/internal/service/rds/integration_test.go +++ b/internal/service/rds/integration_test.go @@ -52,8 +52,8 @@ func TestAccRDSIntegration_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckIntegrationExists(ctx, resourceName, &integration), resource.TestCheckResourceAttr(resourceName, "integration_name", rName), - resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.mysql_test", "arn"), - resource.TestCheckResourceAttrPair(resourceName, "target_arn", "aws_redshiftserverless_namespace.test", "arn"), + resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.mysql_test", names.AttrARN), + resource.TestCheckResourceAttrPair(resourceName, names.AttrTargetARN, "aws_redshiftserverless_namespace.test", names.AttrARN), ), }, { @@ -93,11 +93,11 @@ func TestAccRDSIntegration_optional(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckIntegrationExists(ctx, resourceName, &integration), resource.TestCheckResourceAttr(resourceName, "integration_name", rName), - resource.TestCheckResourceAttrPair(resourceName, "kms_key_id", "aws_kms_key.test", "arn"), - resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.mysql_test", "arn"), - resource.TestCheckResourceAttrPair(resourceName, "target_arn", "aws_redshiftserverless_namespace.test", "arn"), + resource.TestCheckResourceAttrPair(resourceName, names.AttrKMSKeyID, "aws_kms_key.test", names.AttrARN), + resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.mysql_test", names.AttrARN), + resource.TestCheckResourceAttrPair(resourceName, names.AttrTargetARN, "aws_redshiftserverless_namespace.test", names.AttrARN), resource.TestCheckResourceAttr(resourceName, "additional_encryption_context.department", "test"), - resource.TestCheckResourceAttr(resourceName, "tags.Test", "true"), + resource.TestCheckResourceAttr(resourceName, "tags.Test", acctest.CtTrue), ), }, { From 55d9f470ae4c7e9b3607746125b4e03edad5757f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 1 Aug 2024 14:42:16 -0400 Subject: [PATCH 04/24] r/aws_rds_integration: Tidy up. --- internal/service/rds/exports_test.go | 2 + internal/service/rds/integration.go | 336 ++++++++++---------- internal/service/rds/integration_test.go | 261 ++++++--------- internal/service/rds/service_package_gen.go | 2 +- 4 files changed, 269 insertions(+), 332 deletions(-) diff --git a/internal/service/rds/exports_test.go b/internal/service/rds/exports_test.go index be465b9cb7e..b6985d10656 100644 --- a/internal/service/rds/exports_test.go +++ b/internal/service/rds/exports_test.go @@ -15,6 +15,7 @@ var ( ResourceEventSubscription = resourceEventSubscription ResourceInstanceAutomatedBackupsReplication = resourceInstanceAutomatedBackupsReplication ResourceInstanceRoleAssociation = resourceInstanceRoleAssociation + ResourceIntegration = newIntegrationResource ResourceOptionGroup = resourceOptionGroup ResourceParameterGroup = resourceParameterGroup ResourceProxy = resourceProxy @@ -42,6 +43,7 @@ var ( FindDefaultCertificate = findDefaultCertificate FindDefaultDBProxyTargetGroupByDBProxyName = findDefaultDBProxyTargetGroupByDBProxyName FindEventSubscriptionByID = findEventSubscriptionByID + FindIntegrationByARN = findIntegrationByARN FindOptionGroupByName = findOptionGroupByName ListTags = listTags NewBlueGreenOrchestrator = newBlueGreenOrchestrator diff --git a/internal/service/rds/integration.go b/internal/service/rds/integration.go index b972750a270..43f03e464bb 100644 --- a/internal/service/rds/integration.go +++ b/internal/service/rds/integration.go @@ -6,6 +6,7 @@ package rds import ( "context" "errors" + "fmt" "time" "github.com/aws/aws-sdk-go-v2/aws" @@ -20,12 +21,12 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" "github.com/hashicorp/terraform-provider-aws/internal/framework" - "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" @@ -34,41 +35,39 @@ import ( // @FrameworkResource("aws_rds_integration", name="Integration") // @Tags(identifierAttribute="arn") // @Testing(tagsTest=false) -func newResourceIntegration(_ context.Context) (resource.ResourceWithConfigure, error) { - r := &resourceIntegration{} +func newIntegrationResource(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &integrationResource{} r.SetDefaultCreateTimeout(60 * time.Minute) - r.SetDefaultUpdateTimeout(10 * time.Minute) r.SetDefaultDeleteTimeout(30 * time.Minute) return r, nil } const ( - ResNameIntegration = "Integration" - - IntegrationStatusCreating = "creating" - IntegrationStatusActive = "active" - IntegrationStatusModifying = "modifying" - IntegrationStatusFailed = "failed" - IntegrationStatusDeleting = "deleting" - IntegrationStatusSyncing = "syncing" - IntegrationStatusNeedsAttention = "needs_attention" + integrationStatusActive = "active" + integrationStatusCreating = "creating" + integrationStatusDeleting = "deleting" + integrationStatusFailed = "failed" + integrationStatusModifying = "modifying" + integrationStatusNeedsAttention = "needs_attention" + integrationStatusSyncing = "syncing" ) -type resourceIntegration struct { +type integrationResource struct { framework.ResourceWithConfigure + framework.WithNoOpUpdate[integrationResourceModel] + framework.WithImportByID framework.WithTimeouts } -func (r *resourceIntegration) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = "aws_rds_integration" +func (*integrationResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_rds_integration" } -func (r *resourceIntegration) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ +func (r *integrationResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - names.AttrARN: framework.ARNAttributeComputedOnly(), "additional_encryption_context": schema.MapAttribute{ ElementType: types.StringType, Optional: true, @@ -76,7 +75,8 @@ func (r *resourceIntegration) Schema(ctx context.Context, req resource.SchemaReq mapplanmodifier.RequiresReplace(), }, }, - names.AttrID: framework.IDAttribute(), + names.AttrARN: framework.ARNAttributeComputedOnly(), + names.AttrID: framework.IDAttribute(), "integration_name": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -109,195 +109,177 @@ func (r *resourceIntegration) Schema(ctx context.Context, req resource.SchemaReq Blocks: map[string]schema.Block{ names.AttrTimeouts: timeouts.Block(ctx, timeouts.Opts{ Create: true, - Update: true, Delete: true, }), }, } } -func (r *resourceIntegration) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { +func (r *integrationResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var data integrationResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + conn := r.Meta().RDSClient(ctx) - var plan resourceIntegrationData - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { + name := data.IntegrationName.ValueString() + input := &rds.CreateIntegrationInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, data, &input)...) + if response.Diagnostics.HasError() { return } - in := &rds.CreateIntegrationInput{ - IntegrationName: aws.String(plan.IntegrationName.ValueString()), - SourceArn: aws.String(plan.SourceArn.ValueString()), - Tags: getTagsInV2(ctx), - TargetArn: aws.String(plan.TargetArn.ValueString()), - } - if !plan.KMSKeyId.IsNull() { - in.KMSKeyId = aws.String(plan.KMSKeyId.ValueString()) - } - if !plan.AdditionalEncryptionContext.IsNull() { - in.AdditionalEncryptionContext = flex.ExpandFrameworkStringValueMap(ctx, plan.AdditionalEncryptionContext) - } + // Additional fields. + input.Tags = getTagsInV2(ctx) + + output, err := conn.CreateIntegration(ctx, input) - out, err := conn.CreateIntegration(ctx, in) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.RDS, create.ErrActionCreating, ResNameIntegration, plan.IntegrationName.String(), err), - err.Error(), - ) - return - } - if out == nil || out.IntegrationName == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.RDS, create.ErrActionCreating, ResNameIntegration, plan.IntegrationName.String(), nil), - errors.New("empty output").Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("creating RDS Integration (%s)", name), err.Error()) + return } - plan.ARN = flex.StringToFramework(ctx, out.IntegrationArn) - plan.ID = types.StringValue(aws.ToString(out.IntegrationArn)) + // Set values for unknowns. + data.IntegrationARN = fwflex.StringToFramework(ctx, output.IntegrationArn) + data.setID() + + if _, err := waitIntegrationCreated(ctx, conn, data.ID.ValueString(), r.CreateTimeout(ctx, data.Timeouts)); err != nil { + response.State.SetAttribute(ctx, path.Root(names.AttrID), data.ID) // Set 'id' so as to taint the resource. + response.Diagnostics.AddError(fmt.Sprintf("waiting for RDS Integration (%s) create", data.ID.ValueString()), err.Error()) - createTimeout := r.CreateTimeout(ctx, plan.Timeouts) - _, err = waitIntegrationCreated(ctx, conn, plan.ARN.ValueString(), createTimeout) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.RDS, create.ErrActionWaitingForCreation, ResNameIntegration, plan.IntegrationName.String(), err), - err.Error(), - ) return } - resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + response.Diagnostics.Append(response.State.Set(ctx, data)...) } -func (r *resourceIntegration) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - conn := r.Meta().RDSClient(ctx) +func (r *integrationResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var data integrationResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + if err := data.InitFromID(); err != nil { + response.Diagnostics.AddError("parsing resource ID", err.Error()) - var state resourceIntegrationData - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { return } - out, err := FindIntegrationByARN(ctx, conn, state.ARN.ValueString()) + conn := r.Meta().RDSClient(ctx) + + output, err := findIntegrationByARN(ctx, conn, data.ID.ValueString()) if tfresource.NotFound(err) { - resp.State.RemoveResource(ctx) + response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + response.State.RemoveResource(ctx) + return } + if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.RDS, create.ErrActionSetting, ResNameIntegration, state.ARN.String(), err), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("reading RDS Integration (%s)", data.ID.ValueString()), err.Error()) + return } - if out == nil { - resp.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(errors.New("not found"))) - resp.State.RemoveResource(ctx) + + // Set attributes for import. + response.Diagnostics.Append(fwflex.Flatten(ctx, output, &data)...) + if response.Diagnostics.HasError() { return } - state.ARN = flex.StringToFramework(ctx, out.IntegrationArn) + setTagsOutV2(ctx, output.Tags) - state.refreshFromOutput(ctx, out) - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } -func (r *resourceIntegration) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan, state resourceIntegrationData - - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *integrationResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var data integrationResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *resourceIntegration) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { conn := r.Meta().RDSClient(ctx) - var state resourceIntegrationData - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } + _, err := conn.DeleteIntegration(ctx, &rds.DeleteIntegrationInput{ + IntegrationIdentifier: aws.String(data.ID.ValueString()), + }) - in := &rds.DeleteIntegrationInput{ - IntegrationIdentifier: aws.String(state.ARN.ValueString()), + if errs.IsA[*awstypes.ResourceNotFoundFault](err) { + return } - _, err := conn.DeleteIntegration(ctx, in) if err != nil { - var nfe *awstypes.ResourceNotFoundFault - if errors.As(err, &nfe) { - return - } - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.RDS, create.ErrActionDeleting, ResNameIntegration, state.ARN.String(), err), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("deleting RDS Integration (%s)", data.ID.ValueString()), err.Error()) + return } - deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) - _, err = waitIntegrationDeleted(ctx, conn, state.ARN.ValueString(), deleteTimeout) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.RDS, create.ErrActionWaitingForDeletion, ResNameIntegration, state.ARN.String(), err), - err.Error(), - ) + if _, err := waitIntegrationDeleted(ctx, conn, data.ID.ValueString(), r.DeleteTimeout(ctx, data.Timeouts)); err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for RDS Integration (%s) delete", data.ID.ValueString()), err.Error()) + return } } -func (r *resourceIntegration) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root(names.AttrARN), req, resp) -} - -func (r *resourceIntegration) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { +func (r *integrationResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { r.SetTagsAll(ctx, request, response) } -func waitIntegrationCreated(ctx context.Context, conn *rds.Client, arn string, timeout time.Duration) (*awstypes.Integration, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{IntegrationStatusCreating, IntegrationStatusModifying}, - Target: []string{IntegrationStatusActive}, - Refresh: statusIntegration(ctx, conn, arn), - Timeout: timeout, - NotFoundChecks: 20, - ContinuousTargetOccurence: 2, +func findIntegrationByARN(ctx context.Context, conn *rds.Client, arn string) (*awstypes.Integration, error) { + input := &rds.DescribeIntegrationsInput{ + IntegrationIdentifier: aws.String(arn), } - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*awstypes.Integration); ok { - return out, err + return findIntegration(ctx, conn, input, tfslices.PredicateTrue[*awstypes.Integration]()) +} + +func findIntegration(ctx context.Context, conn *rds.Client, input *rds.DescribeIntegrationsInput, filter tfslices.Predicate[*awstypes.Integration]) (*awstypes.Integration, error) { + output, err := findIntegrations(ctx, conn, input, filter) + + if err != nil { + return nil, err } - return nil, err + return tfresource.AssertSingleValueResult(output) } -func waitIntegrationDeleted(ctx context.Context, conn *rds.Client, arn string, timeout time.Duration) (*awstypes.Integration, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{IntegrationStatusDeleting, IntegrationStatusActive}, - Target: []string{}, - Refresh: statusIntegration(ctx, conn, arn), - Timeout: timeout, - } +func findIntegrations(ctx context.Context, conn *rds.Client, input *rds.DescribeIntegrationsInput, filter tfslices.Predicate[*awstypes.Integration]) ([]awstypes.Integration, error) { + var output []awstypes.Integration - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*awstypes.Integration); ok { - return out, err + pages := rds.NewDescribeIntegrationsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*awstypes.IntegrationNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + for _, v := range page.Integrations { + if filter(&v) { + output = append(output, v) + } + } } - return nil, err + return output, nil } func statusIntegration(ctx context.Context, conn *rds.Client, arn string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - out, err := FindIntegrationByARN(ctx, conn, arn) + output, err := findIntegrationByARN(ctx, conn, arn) if tfresource.NotFound(err) { return nil, "", nil @@ -307,61 +289,71 @@ func statusIntegration(ctx context.Context, conn *rds.Client, arn string) retry. return nil, "", err } - return out, string(out.Status), nil + return output, string(output.Status), nil } } -func FindIntegrationByARN(ctx context.Context, conn *rds.Client, arn string) (*awstypes.Integration, error) { - in := &rds.DescribeIntegrationsInput{ - IntegrationIdentifier: aws.String(arn), +func waitIntegrationCreated(ctx context.Context, conn *rds.Client, arn string, timeout time.Duration) (*awstypes.Integration, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{integrationStatusCreating, integrationStatusModifying}, + Target: []string{integrationStatusActive}, + Refresh: statusIntegration(ctx, conn, arn), + Timeout: timeout, } - out, err := conn.DescribeIntegrations(ctx, in) + outputRaw, err := stateConf.WaitForStateContext(ctx) - if errs.IsA[*awstypes.IntegrationNotFoundFault](err) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: in, - } + if output, ok := outputRaw.(*awstypes.Integration); ok { + tfresource.SetLastError(err, errors.Join(tfslices.ApplyToAll(output.Errors, integrationError)...)) + + return output, err } - if err != nil { - return nil, err + return nil, err +} + +func waitIntegrationDeleted(ctx context.Context, conn *rds.Client, arn string, timeout time.Duration) (*awstypes.Integration, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{integrationStatusDeleting, integrationStatusActive}, + Target: []string{}, + Refresh: statusIntegration(ctx, conn, arn), + Timeout: timeout, } - if out == nil || len(out.Integrations) == 0 { - return nil, tfresource.NewEmptyResultError(in) + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.Integration); ok { + tfresource.SetLastError(err, errors.Join(tfslices.ApplyToAll(output.Errors, integrationError)...)) + + return output, err } - return &out.Integrations[0], nil + return nil, err } -type resourceIntegrationData struct { - ARN types.String `tfsdk:"arn"` +func integrationError(v awstypes.IntegrationError) error { + return fmt.Errorf("%s: %s", aws.ToString(v.ErrorCode), aws.ToString(v.ErrorMessage)) +} + +type integrationResourceModel struct { AdditionalEncryptionContext types.Map `tfsdk:"additional_encryption_context"` ID types.String `tfsdk:"id"` + IntegrationARN types.String `tfsdk:"arn"` IntegrationName types.String `tfsdk:"integration_name"` - KMSKeyId types.String `tfsdk:"kms_key_id"` - SourceArn fwtypes.ARN `tfsdk:"source_arn"` + KMSKeyID types.String `tfsdk:"kms_key_id"` + SourceARN fwtypes.ARN `tfsdk:"source_arn"` Tags types.Map `tfsdk:"tags"` TagsAll types.Map `tfsdk:"tags_all"` - TargetArn fwtypes.ARN `tfsdk:"target_arn"` + TargetARN fwtypes.ARN `tfsdk:"target_arn"` Timeouts timeouts.Value `tfsdk:"timeouts"` } -// refreshFromOutput writes state data from an AWS response object -func (r *resourceIntegrationData) refreshFromOutput(ctx context.Context, out *awstypes.Integration) { - if out == nil { - return - } +func (model *integrationResourceModel) InitFromID() error { + model.IntegrationARN = model.ID - r.ARN = flex.StringToFramework(ctx, out.IntegrationArn) - r.AdditionalEncryptionContext = flex.FlattenFrameworkStringValueMap(ctx, out.AdditionalEncryptionContext) - r.ID = types.StringValue(aws.ToString(out.IntegrationArn)) - r.IntegrationName = flex.StringToFramework(ctx, out.IntegrationName) - r.KMSKeyId = flex.StringToFramework(ctx, out.KMSKeyId) - r.SourceArn = flex.StringToFrameworkARN(ctx, out.SourceArn) - r.TargetArn = flex.StringToFrameworkARN(ctx, out.TargetArn) + return nil +} - setTagsOutV2(ctx, out.Tags) +func (model *integrationResourceModel) setID() { + model.ID = model.IntegrationARN } diff --git a/internal/service/rds/integration_test.go b/internal/service/rds/integration_test.go index 9a0beac03b3..6a374e00a55 100644 --- a/internal/service/rds/integration_test.go +++ b/internal/service/rds/integration_test.go @@ -5,20 +5,16 @@ package rds_test import ( "context" - "errors" "fmt" "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/rds" - rdsv1 "github.com/aws/aws-sdk-go/service/rds" + awstypes "github.com/aws/aws-sdk-go-v2/service/rds/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/create" tfrds "github.com/hashicorp/terraform-provider-aws/internal/service/rds" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" @@ -30,29 +26,29 @@ func TestAccRDSIntegration_basic(t *testing.T) { } ctx := acctest.Context(t) - - var integration rds.DescribeIntegrationsOutput + var integration awstypes.Integration rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_integration.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, rdsv1.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIntegrationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIntegrationConfigBase(rName), + Config: testAccIntegrationConfig_base(rName), Check: resource.ComposeTestCheckFunc( waitUntilRDSReboot(ctx, rName), ), }, { Config: testAccIntegrationConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckIntegrationExists(ctx, resourceName, &integration), resource.TestCheckResourceAttr(resourceName, "integration_name", rName), - resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.mysql_test", names.AttrARN), + resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.test", names.AttrARN), + resource.TestCheckResourceAttr(resourceName, "tags.#", acctest.Ct0), resource.TestCheckResourceAttrPair(resourceName, names.AttrTargetARN, "aws_redshiftserverless_namespace.test", names.AttrARN), ), }, @@ -65,6 +61,40 @@ func TestAccRDSIntegration_basic(t *testing.T) { }) } +func TestAccRDSIntegration_disappears(t *testing.T) { + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + ctx := acctest.Context(t) + var integration awstypes.Integration + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_rds_integration.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckIntegrationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccIntegrationConfig_base(rName), + Check: resource.ComposeTestCheckFunc( + waitUntilRDSReboot(ctx, rName), + ), + }, + { + Config: testAccIntegrationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckIntegrationExists(ctx, resourceName, &integration), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfrds.ResourceIntegration, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccRDSIntegration_optional(t *testing.T) { ctx := acctest.Context(t) @@ -72,18 +102,18 @@ func TestAccRDSIntegration_optional(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var integration rds.DescribeIntegrationsOutput + var integration awstypes.Integration rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_integration.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, rdsv1.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIntegrationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIntegrationConfigBase(rName), + Config: testAccIntegrationConfig_base(rName), Check: resource.ComposeTestCheckFunc( waitUntilRDSReboot(ctx, rName), ), @@ -94,10 +124,11 @@ func TestAccRDSIntegration_optional(t *testing.T) { testAccCheckIntegrationExists(ctx, resourceName, &integration), resource.TestCheckResourceAttr(resourceName, "integration_name", rName), resource.TestCheckResourceAttrPair(resourceName, names.AttrKMSKeyID, "aws_kms_key.test", names.AttrARN), - resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.mysql_test", names.AttrARN), + resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.test", names.AttrARN), resource.TestCheckResourceAttrPair(resourceName, names.AttrTargetARN, "aws_redshiftserverless_namespace.test", names.AttrARN), resource.TestCheckResourceAttr(resourceName, "additional_encryption_context.department", "test"), - resource.TestCheckResourceAttr(resourceName, "tags.Test", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "tags.%", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), ), }, { @@ -121,89 +152,54 @@ func testAccCheckIntegrationDestroy(ctx context.Context) resource.TestCheckFunc _, err := tfrds.FindIntegrationByARN(ctx, conn, rs.Primary.ID) if tfresource.NotFound(err) { - return nil + continue } + if err != nil { - return create.Error(names.RDS, create.ErrActionCheckingDestroyed, tfrds.ResNameIntegration, rs.Primary.ID, err) + return err } - return create.Error(names.RDS, create.ErrActionCheckingDestroyed, tfrds.ResNameIntegration, rs.Primary.ID, errors.New("not destroyed")) + return fmt.Errorf("RDS Integration %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckIntegrationExists(ctx context.Context, name string, integration *rds.DescribeIntegrationsOutput) resource.TestCheckFunc { +func testAccCheckIntegrationExists(ctx context.Context, n string, v *awstypes.Integration) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return create.Error(names.RDS, create.ErrActionCheckingExistence, tfrds.ResNameIntegration, name, errors.New("not found")) - } - - if rs.Primary.ID == "" { - return create.Error(names.RDS, create.ErrActionCheckingExistence, tfrds.ResNameIntegration, name, errors.New("not set")) + return fmt.Errorf("Not found: %s", n) } conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) - resp, err := conn.DescribeIntegrations(ctx, &rds.DescribeIntegrationsInput{ - IntegrationIdentifier: aws.String(rs.Primary.ID), - }) + + output, err := tfrds.FindIntegrationByARN(ctx, conn, rs.Primary.ID) if err != nil { - return create.Error(names.RDS, create.ErrActionCheckingExistence, tfrds.ResNameIntegration, rs.Primary.ID, err) + return err } - *integration = *resp + *v = *output return nil } } -// Wait RDS rebooting for static DB parameter changes func waitUntilRDSReboot(ctx context.Context, instanceIdentifier string) resource.TestCheckFunc { return func(s *terraform.State) error { - - // Wait for rebooting + // Wait for rebooting. time.Sleep(60 * time.Second) - // Wait for being available - for { - status := getDBInstanceStatus(ctx, instanceIdentifier) - if status == "available" { - break - } - - time.Sleep(10 * time.Second) - } + _, err := tfrds.WaitDBInstanceAvailable(ctx, acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx), instanceIdentifier, 30*time.Minute) - return nil + return err } } -func getDBInstanceStatus(ctx context.Context, instanceIdentifier string) string { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) - - result, err := conn.DescribeDBInstances(ctx, &rds.DescribeDBInstancesInput{ - DBInstanceIdentifier: aws.String(instanceIdentifier), - }) - if err != nil { - fmt.Errorf("failed to describe DB instances, %v", err) - } - - if len(result.DBInstances) == 0 { - fmt.Errorf("DB instance %s not found", instanceIdentifier) - } - - instance := result.DBInstances[0] - status := *instance.DBInstanceStatus - fmt.Printf("Current DB instance status: %s\n", status) - - return status -} - -func testAccIntegrationConfigBase(rName string) string { - return fmt.Sprintf(` +func testAccIntegrationConfig_base(rName string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 3), fmt.Sprintf(` locals { cluster_parameters = { "binlog_replication_globaldb" = { @@ -235,54 +231,8 @@ locals { data "aws_caller_identity" "current" {} -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - -resource "aws_vpc" "test" { - cidr_block = "10.1.0.0/16" - - tags = { - Name = %[1]q - } -} - -resource "aws_subnet" "test1" { - cidr_block = "10.1.1.0/24" - availability_zone = data.aws_availability_zones.available.names[0] - vpc_id = aws_vpc.test.id - - tags = { - Name = "%[1]s-1" - } -} - -resource "aws_subnet" "test2" { - cidr_block = "10.1.2.0/24" - availability_zone = data.aws_availability_zones.available.names[1] - vpc_id = aws_vpc.test.id - - tags = { - Name = "%[1]s-2" - } -} - -resource "aws_subnet" "test3" { - cidr_block = "10.1.3.0/24" - availability_zone = data.aws_availability_zones.available.names[2] - vpc_id = aws_vpc.test.id - - tags = { - Name = "%[1]s-3" - } -} - resource "aws_security_group" "test" { + name = %[1]q vpc_id = aws_vpc.test.id ingress { @@ -298,14 +248,15 @@ resource "aws_security_group" "test" { protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } + + tags = { + Name = %[1]q + } } resource "aws_db_subnet_group" "test" { name = %[1]q - subnet_ids = [ - aws_subnet.test1.id, - aws_subnet.test2.id, - ] + subnet_ids = aws_subnet.test[*].id tags = { Name = %[1]q @@ -313,7 +264,7 @@ resource "aws_db_subnet_group" "test" { } resource "aws_rds_cluster_parameter_group" "test" { - name = "%[1]s" + name = %[1]q family = "aurora-mysql8.0" dynamic "parameter" { @@ -326,46 +277,45 @@ resource "aws_rds_cluster_parameter_group" "test" { } } -resource "aws_rds_cluster" "mysql_test" { +resource "aws_rds_cluster" "test" { cluster_identifier = %[1]q engine = "aurora-mysql" engine_version = "8.0.mysql_aurora.3.05.1" - database_name = "tftest" - master_username = "testuser" - master_password = "Testpassword123" + database_name = "test" + master_username = "tfacctest" + master_password = "avoid-plaintext-passwords" skip_final_snapshot = true + vpc_security_group_ids = [aws_security_group.test.id] db_subnet_group_name = aws_db_subnet_group.test.name db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.test.name + apply_immediately = true } -resource "aws_rds_cluster_instance" "mysql_test" { - identifier = %[1]q - cluster_identifier = aws_rds_cluster.mysql_test.id +resource "aws_rds_cluster_instance" "test" { + identifier = %[1]q + cluster_identifier = aws_rds_cluster.test.id instance_class = "db.r6g.large" - engine = aws_rds_cluster.mysql_test.engine - engine_version = aws_rds_cluster.mysql_test.engine_version + engine = aws_rds_cluster.test.engine + engine_version = aws_rds_cluster.test.engine_version } resource "aws_redshift_cluster" "test" { cluster_identifier = %[1]q availability_zone = data.aws_availability_zones.available.names[0] - database_name = "test" - master_username = "testuser" - master_password = "Testpassword123" + database_name = "mydb" + master_username = "foo" + master_password = "Mustbe8characters" node_type = "dc2.large" cluster_type = "single-node" skip_final_snapshot = true } - -`, rName) +`, rName)) } func testAccIntegrationConfig_basic(rName string) string { - return acctest.ConfigCompose( - testAccIntegrationConfigBase(rName), - fmt.Sprintf(` + return acctest.ConfigCompose(testAccIntegrationConfig_base(rName), fmt.Sprintf(` resource "aws_redshiftserverless_namespace" "test" { namespace_name = %[1]q } @@ -373,13 +323,10 @@ resource "aws_redshiftserverless_namespace" "test" { resource "aws_redshiftserverless_workgroup" "test" { namespace_name = aws_redshiftserverless_namespace.test.namespace_name workgroup_name = %[1]q - base_capacity = 8 + base_capacity = 8 + publicly_accessible = false - subnet_ids = [ - aws_subnet.test1.id, - aws_subnet.test2.id, - aws_subnet.test3.id, - ] + subnet_ids = aws_subnet.test[*].id config_parameter { parameter_key = "enable_case_sensitive_identifier" @@ -441,7 +388,7 @@ resource "aws_redshift_resource_policy" "test" { Resource = aws_redshiftserverless_namespace.test.arn Condition = { StringEquals = { - "aws:SourceArn" = aws_rds_cluster.mysql_test.arn + "aws:SourceArn" = aws_rds_cluster.test.arn } } }] @@ -450,11 +397,11 @@ resource "aws_redshift_resource_policy" "test" { resource "aws_rds_integration" "test" { integration_name = %[1]q - source_arn = aws_rds_cluster.mysql_test.arn + source_arn = aws_rds_cluster.test.arn target_arn = aws_redshiftserverless_namespace.test.arn depends_on = [ - aws_rds_cluster.mysql_test, + aws_rds_cluster.test, aws_redshiftserverless_namespace.test, aws_redshiftserverless_workgroup.test, aws_redshift_resource_policy.test, @@ -470,9 +417,7 @@ resource "aws_rds_integration" "test" { } func testAccIntegrationConfig_optional(rName string) string { - return acctest.ConfigCompose( - testAccIntegrationConfigBase(rName), - fmt.Sprintf(` + return acctest.ConfigCompose(testAccIntegrationConfig_base(rName), fmt.Sprintf(` resource "aws_redshiftserverless_namespace" "test" { namespace_name = %[1]q } @@ -480,13 +425,10 @@ resource "aws_redshiftserverless_namespace" "test" { resource "aws_redshiftserverless_workgroup" "test" { namespace_name = aws_redshiftserverless_namespace.test.namespace_name workgroup_name = %[1]q - base_capacity = 8 + base_capacity = 8 + publicly_accessible = false - subnet_ids = [ - aws_subnet.test1.id, - aws_subnet.test2.id, - aws_subnet.test3.id, - ] + subnet_ids = aws_subnet.test[*].id config_parameter { parameter_key = "enable_case_sensitive_identifier" @@ -530,6 +472,7 @@ resource "aws_redshiftserverless_workgroup" "test" { # Therefore we need to use the "aws_redshift_resource_policy" resource for RedShift-serverless instead. resource "aws_redshift_resource_policy" "test" { resource_arn = aws_redshiftserverless_namespace.test.arn + policy = jsonencode({ Version = "2008-10-17" Statement = [{ @@ -548,7 +491,7 @@ resource "aws_redshift_resource_policy" "test" { Resource = aws_redshiftserverless_namespace.test.arn Condition = { StringEquals = { - "aws:SourceArn" = aws_rds_cluster.mysql_test.arn + "aws:SourceArn" = aws_rds_cluster.test.arn } } }] @@ -582,20 +525,20 @@ data "aws_iam_policy_document" "key_policy" { resource "aws_rds_integration" "test" { integration_name = %[1]q - source_arn = aws_rds_cluster.mysql_test.arn + source_arn = aws_rds_cluster.test.arn target_arn = aws_redshiftserverless_namespace.test.arn - kms_key_id = aws_kms_key.test.arn + additional_encryption_context = { "department": "test", } tags = { - Test = "true" + Name = %[1]q } depends_on = [ - aws_rds_cluster.mysql_test, + aws_rds_cluster.test, aws_redshiftserverless_namespace.test, aws_redshiftserverless_workgroup.test, aws_redshift_resource_policy.test, diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index df8fa5b4c51..54e2997ef72 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -28,7 +28,7 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic Factory: newResourceExportTask, }, { - Factory: newResourceIntegration, + Factory: newIntegrationResource, Name: "Integration", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, From cd46119787e2710a3069ef8c14cfa2a17f266660 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 1 Aug 2024 14:44:44 -0400 Subject: [PATCH 05/24] r/aws_rds_integration: Fix semgrep errors. --- internal/service/rds/integration_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/rds/integration_test.go b/internal/service/rds/integration_test.go index 6a374e00a55..e4d18463faa 100644 --- a/internal/service/rds/integration_test.go +++ b/internal/service/rds/integration_test.go @@ -39,7 +39,7 @@ func TestAccRDSIntegration_basic(t *testing.T) { { Config: testAccIntegrationConfig_base(rName), Check: resource.ComposeTestCheckFunc( - waitUntilRDSReboot(ctx, rName), + waitUntilDBInstanceRebooted(ctx, rName), ), }, { @@ -48,7 +48,7 @@ func TestAccRDSIntegration_basic(t *testing.T) { testAccCheckIntegrationExists(ctx, resourceName, &integration), resource.TestCheckResourceAttr(resourceName, "integration_name", rName), resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.test", names.AttrARN), - resource.TestCheckResourceAttr(resourceName, "tags.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestCheckResourceAttrPair(resourceName, names.AttrTargetARN, "aws_redshiftserverless_namespace.test", names.AttrARN), ), }, @@ -80,7 +80,7 @@ func TestAccRDSIntegration_disappears(t *testing.T) { { Config: testAccIntegrationConfig_base(rName), Check: resource.ComposeTestCheckFunc( - waitUntilRDSReboot(ctx, rName), + waitUntilDBInstanceRebooted(ctx, rName), ), }, { @@ -115,7 +115,7 @@ func TestAccRDSIntegration_optional(t *testing.T) { { Config: testAccIntegrationConfig_base(rName), Check: resource.ComposeTestCheckFunc( - waitUntilRDSReboot(ctx, rName), + waitUntilDBInstanceRebooted(ctx, rName), ), }, { @@ -127,7 +127,7 @@ func TestAccRDSIntegration_optional(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "source_arn", "aws_rds_cluster.test", names.AttrARN), resource.TestCheckResourceAttrPair(resourceName, names.AttrTargetARN, "aws_redshiftserverless_namespace.test", names.AttrARN), resource.TestCheckResourceAttr(resourceName, "additional_encryption_context.department", "test"), - resource.TestCheckResourceAttr(resourceName, "tags.%", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), ), }, @@ -187,7 +187,7 @@ func testAccCheckIntegrationExists(ctx context.Context, n string, v *awstypes.In } } -func waitUntilRDSReboot(ctx context.Context, instanceIdentifier string) resource.TestCheckFunc { +func waitUntilDBInstanceRebooted(ctx context.Context, instanceIdentifier string) resource.TestCheckFunc { return func(s *terraform.State) error { // Wait for rebooting. time.Sleep(60 * time.Second) From b22d6e42f36586e080db944dc42cf0c64f2f4ae2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 1 Aug 2024 15:08:25 -0400 Subject: [PATCH 06/24] Fix terrafmt errors. --- internal/service/rds/integration_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/service/rds/integration_test.go b/internal/service/rds/integration_test.go index e4d18463faa..0080fd67c39 100644 --- a/internal/service/rds/integration_test.go +++ b/internal/service/rds/integration_test.go @@ -264,8 +264,8 @@ resource "aws_db_subnet_group" "test" { } resource "aws_rds_cluster_parameter_group" "test" { - name = %[1]q - family = "aurora-mysql8.0" + name = %[1]q + family = "aurora-mysql8.0" dynamic "parameter" { for_each = local.cluster_parameters @@ -278,7 +278,7 @@ resource "aws_rds_cluster_parameter_group" "test" { } resource "aws_rds_cluster" "test" { - cluster_identifier = %[1]q + cluster_identifier = %[1]q engine = "aurora-mysql" engine_version = "8.0.mysql_aurora.3.05.1" database_name = "test" @@ -329,7 +329,7 @@ resource "aws_redshiftserverless_workgroup" "test" { subnet_ids = aws_subnet.test[*].id config_parameter { - parameter_key = "enable_case_sensitive_identifier" + parameter_key = "enable_case_sensitive_identifier" parameter_value = "true" } config_parameter { @@ -379,7 +379,7 @@ resource "aws_redshift_resource_policy" "test" { } Action = "redshift:CreateInboundIntegration" Resource = aws_redshiftserverless_namespace.test.arn - },{ + }, { Effect = "Allow" Principal = { Service = "redshift.amazonaws.com" @@ -431,7 +431,7 @@ resource "aws_redshiftserverless_workgroup" "test" { subnet_ids = aws_subnet.test[*].id config_parameter { - parameter_key = "enable_case_sensitive_identifier" + parameter_key = "enable_case_sensitive_identifier" parameter_value = "true" } config_parameter { @@ -482,7 +482,7 @@ resource "aws_redshift_resource_policy" "test" { } Action = "redshift:CreateInboundIntegration" Resource = aws_redshiftserverless_namespace.test.arn - },{ + }, { Effect = "Allow" Principal = { Service = "redshift.amazonaws.com" @@ -530,7 +530,7 @@ resource "aws_rds_integration" "test" { kms_key_id = aws_kms_key.test.arn additional_encryption_context = { - "department": "test", + "department" : "test", } tags = { From 58d0a5b582b2da4763544473be9a8b776a9882b5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 1 Aug 2024 15:19:01 -0400 Subject: [PATCH 07/24] r/aws_rds_integration: 'kms_key_id' is Computed. --- internal/service/rds/integration.go | 9 +- internal/service/rds/integration_test.go | 103 +++-------------------- 2 files changed, 19 insertions(+), 93 deletions(-) diff --git a/internal/service/rds/integration.go b/internal/service/rds/integration.go index 43f03e464bb..f20c66b812a 100644 --- a/internal/service/rds/integration.go +++ b/internal/service/rds/integration.go @@ -85,8 +85,10 @@ func (r *integrationResource) Schema(ctx context.Context, request resource.Schem }, names.AttrKMSKeyID: schema.StringAttribute{ Optional: true, + Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), + stringplanmodifier.UseStateForUnknown(), }, }, "source_arn": schema.StringAttribute{ @@ -146,13 +148,18 @@ func (r *integrationResource) Create(ctx context.Context, request resource.Creat data.IntegrationARN = fwflex.StringToFramework(ctx, output.IntegrationArn) data.setID() - if _, err := waitIntegrationCreated(ctx, conn, data.ID.ValueString(), r.CreateTimeout(ctx, data.Timeouts)); err != nil { + integration, err := waitIntegrationCreated(ctx, conn, data.ID.ValueString(), r.CreateTimeout(ctx, data.Timeouts)) + + if err != nil { response.State.SetAttribute(ctx, path.Root(names.AttrID), data.ID) // Set 'id' so as to taint the resource. response.Diagnostics.AddError(fmt.Sprintf("waiting for RDS Integration (%s) create", data.ID.ValueString()), err.Error()) return } + // Set values for unknowns. + data.KMSKeyID = fwflex.StringToFramework(ctx, integration.KMSKeyId) + response.Diagnostics.Append(response.State.Set(ctx, data)...) } diff --git a/internal/service/rds/integration_test.go b/internal/service/rds/integration_test.go index 0080fd67c39..beaac759504 100644 --- a/internal/service/rds/integration_test.go +++ b/internal/service/rds/integration_test.go @@ -37,7 +37,7 @@ func TestAccRDSIntegration_basic(t *testing.T) { CheckDestroy: testAccCheckIntegrationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIntegrationConfig_base(rName), + Config: testAccIntegrationConfig_baseClusterWithInstance(rName), Check: resource.ComposeTestCheckFunc( waitUntilDBInstanceRebooted(ctx, rName), ), @@ -78,7 +78,7 @@ func TestAccRDSIntegration_disappears(t *testing.T) { CheckDestroy: testAccCheckIntegrationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIntegrationConfig_base(rName), + Config: testAccIntegrationConfig_baseClusterWithInstance(rName), Check: resource.ComposeTestCheckFunc( waitUntilDBInstanceRebooted(ctx, rName), ), @@ -113,7 +113,7 @@ func TestAccRDSIntegration_optional(t *testing.T) { CheckDestroy: testAccCheckIntegrationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIntegrationConfig_base(rName), + Config: testAccIntegrationConfig_baseClusterWithInstance(rName), Check: resource.ComposeTestCheckFunc( waitUntilDBInstanceRebooted(ctx, rName), ), @@ -198,7 +198,7 @@ func waitUntilDBInstanceRebooted(ctx context.Context, instanceIdentifier string) } } -func testAccIntegrationConfig_base(rName string) string { +func testAccIntegrationConfig_baseClusterWithInstance(rName string) string { return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 3), fmt.Sprintf(` locals { cluster_parameters = { @@ -314,8 +314,8 @@ resource "aws_redshift_cluster" "test" { `, rName)) } -func testAccIntegrationConfig_basic(rName string) string { - return acctest.ConfigCompose(testAccIntegrationConfig_base(rName), fmt.Sprintf(` +func testAccIntegrationConfig_base(rName string) string { + return acctest.ConfigCompose(testAccIntegrationConfig_baseClusterWithInstance(rName), fmt.Sprintf(` resource "aws_redshiftserverless_namespace" "test" { namespace_name = %[1]q } @@ -394,7 +394,11 @@ resource "aws_redshift_resource_policy" "test" { }] }) } +`, rName)) +} +func testAccIntegrationConfig_basic(rName string) string { + return acctest.ConfigCompose(testAccIntegrationConfig_base(rName), fmt.Sprintf(` resource "aws_rds_integration" "test" { integration_name = %[1]q source_arn = aws_rds_cluster.test.arn @@ -406,99 +410,14 @@ resource "aws_rds_integration" "test" { aws_redshiftserverless_workgroup.test, aws_redshift_resource_policy.test, ] - - lifecycle { - ignore_changes = [ - kms_key_id - ] - } } `, rName)) } func testAccIntegrationConfig_optional(rName string) string { return acctest.ConfigCompose(testAccIntegrationConfig_base(rName), fmt.Sprintf(` -resource "aws_redshiftserverless_namespace" "test" { - namespace_name = %[1]q -} - -resource "aws_redshiftserverless_workgroup" "test" { - namespace_name = aws_redshiftserverless_namespace.test.namespace_name - workgroup_name = %[1]q - base_capacity = 8 - - publicly_accessible = false - subnet_ids = aws_subnet.test[*].id - - config_parameter { - parameter_key = "enable_case_sensitive_identifier" - parameter_value = "true" - } - config_parameter { - parameter_key = "auto_mv" - parameter_value = "true" - } - config_parameter { - parameter_key = "datestyle" - parameter_value = "ISO, MDY" - } - config_parameter { - parameter_key = "enable_user_activity_logging" - parameter_value = "true" - } - config_parameter { - parameter_key = "max_query_execution_time" - parameter_value = "14400" - } - config_parameter { - parameter_key = "query_group" - parameter_value = "default" - } - config_parameter { - parameter_key = "require_ssl" - parameter_value = "false" - } - config_parameter { - parameter_key = "search_path" - parameter_value = "$user, public" - } - config_parameter { - parameter_key = "use_fips_ssl" - parameter_value = "false" - } -} - -# The "aws_redshiftserverless_resource_policy" resource doesn't support the following action types. -# Therefore we need to use the "aws_redshift_resource_policy" resource for RedShift-serverless instead. -resource "aws_redshift_resource_policy" "test" { - resource_arn = aws_redshiftserverless_namespace.test.arn - - policy = jsonencode({ - Version = "2008-10-17" - Statement = [{ - Effect = "Allow" - Principal = { - AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" - } - Action = "redshift:CreateInboundIntegration" - Resource = aws_redshiftserverless_namespace.test.arn - }, { - Effect = "Allow" - Principal = { - Service = "redshift.amazonaws.com" - } - Action = "redshift:AuthorizeInboundIntegration" - Resource = aws_redshiftserverless_namespace.test.arn - Condition = { - StringEquals = { - "aws:SourceArn" = aws_rds_cluster.test.arn - } - } - }] - }) -} - resource "aws_kms_key" "test" { + description = %[1]q deletion_window_in_days = 10 policy = data.aws_iam_policy_document.key_policy.json } From 5fab0ffac1735a26fb630a8bcaae9da55176cba8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 09:28:52 -0400 Subject: [PATCH 08/24] r/aws_rds_integration: Fixes after some testing. --- internal/service/rds/integration.go | 30 +++++++++++++++--------- internal/service/rds/integration_test.go | 4 ++-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/internal/service/rds/integration.go b/internal/service/rds/integration.go index f20c66b812a..a069f1a4c27 100644 --- a/internal/service/rds/integration.go +++ b/internal/service/rds/integration.go @@ -69,6 +69,7 @@ func (r *integrationResource) Schema(ctx context.Context, request resource.Schem response.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "additional_encryption_context": schema.MapAttribute{ + CustomType: fwtypes.MapOfStringType, ElementType: types.StringType, Optional: true, PlanModifiers: []planmodifier.Map{ @@ -128,7 +129,7 @@ func (r *integrationResource) Create(ctx context.Context, request resource.Creat name := data.IntegrationName.ValueString() input := &rds.CreateIntegrationInput{} - response.Diagnostics.Append(fwflex.Expand(ctx, data, &input)...) + response.Diagnostics.Append(fwflex.Expand(ctx, data, input)...) if response.Diagnostics.HasError() { return } @@ -193,12 +194,19 @@ func (r *integrationResource) Read(ctx context.Context, request resource.ReadReq return } + prevAdditionalEncryptionContext := data.AdditionalEncryptionContext + // Set attributes for import. response.Diagnostics.Append(fwflex.Flatten(ctx, output, &data)...) if response.Diagnostics.HasError() { return } + // Null vs. empty map handling. + if prevAdditionalEncryptionContext.IsNull() && !data.AdditionalEncryptionContext.IsNull() && len(data.AdditionalEncryptionContext.Elements()) == 0 { + data.AdditionalEncryptionContext = prevAdditionalEncryptionContext + } + setTagsOutV2(ctx, output.Tags) response.Diagnostics.Append(response.State.Set(ctx, &data)...) @@ -343,16 +351,16 @@ func integrationError(v awstypes.IntegrationError) error { } type integrationResourceModel struct { - AdditionalEncryptionContext types.Map `tfsdk:"additional_encryption_context"` - ID types.String `tfsdk:"id"` - IntegrationARN types.String `tfsdk:"arn"` - IntegrationName types.String `tfsdk:"integration_name"` - KMSKeyID types.String `tfsdk:"kms_key_id"` - SourceARN fwtypes.ARN `tfsdk:"source_arn"` - Tags types.Map `tfsdk:"tags"` - TagsAll types.Map `tfsdk:"tags_all"` - TargetARN fwtypes.ARN `tfsdk:"target_arn"` - Timeouts timeouts.Value `tfsdk:"timeouts"` + AdditionalEncryptionContext fwtypes.MapValueOf[types.String] `tfsdk:"additional_encryption_context"` + ID types.String `tfsdk:"id"` + IntegrationARN types.String `tfsdk:"arn"` + IntegrationName types.String `tfsdk:"integration_name"` + KMSKeyID types.String `tfsdk:"kms_key_id"` + SourceARN fwtypes.ARN `tfsdk:"source_arn"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` + TargetARN fwtypes.ARN `tfsdk:"target_arn"` + Timeouts timeouts.Value `tfsdk:"timeouts"` } func (model *integrationResourceModel) InitFromID() error { diff --git a/internal/service/rds/integration_test.go b/internal/service/rds/integration_test.go index beaac759504..82fcf1730bd 100644 --- a/internal/service/rds/integration_test.go +++ b/internal/service/rds/integration_test.go @@ -120,7 +120,7 @@ func TestAccRDSIntegration_optional(t *testing.T) { }, { Config: testAccIntegrationConfig_optional(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckIntegrationExists(ctx, resourceName, &integration), resource.TestCheckResourceAttr(resourceName, "integration_name", rName), resource.TestCheckResourceAttrPair(resourceName, names.AttrKMSKeyID, "aws_kms_key.test", names.AttrARN), @@ -280,7 +280,7 @@ resource "aws_rds_cluster_parameter_group" "test" { resource "aws_rds_cluster" "test" { cluster_identifier = %[1]q engine = "aurora-mysql" - engine_version = "8.0.mysql_aurora.3.05.1" + engine_version = "8.0.mysql_aurora.3.05.2" database_name = "test" master_username = "tfacctest" master_password = "avoid-plaintext-passwords" From 7da216d82aca953ea0c6913ae1db625f3667cce8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 09:57:59 -0400 Subject: [PATCH 09/24] d/aws_rds_orderable_db_instance: Migrate to AWS SDK for Go v2. --- .../rds/orderable_instance_data_source.go | 143 +++++++----------- .../orderable_instance_data_source_test.go | 8 +- internal/service/rds/service_package_gen.go | 9 +- 3 files changed, 64 insertions(+), 96 deletions(-) diff --git a/internal/service/rds/orderable_instance_data_source.go b/internal/service/rds/orderable_instance_data_source.go index 3f9a707632c..b928cbf0761 100644 --- a/internal/service/rds/orderable_instance_data_source.go +++ b/internal/service/rds/orderable_instance_data_source.go @@ -8,18 +8,20 @@ import ( "sort" "github.com/YakDriver/go-version" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/rds" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds" + awstypes "github.com/aws/aws-sdk-go-v2/service/rds/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_rds_orderable_db_instance") -func DataSourceOrderableInstance() *schema.Resource { +// @SDKDataSource("aws_rds_orderable_db_instance", name="Orderable DB Instance") +func dataSourceOrderableInstance() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceOrderableInstanceRead, Schema: map[string]*schema.Schema{ @@ -28,179 +30,148 @@ func DataSourceOrderableInstance() *schema.Resource { Optional: true, Computed: true, }, - names.AttrAvailabilityZones: { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - names.AttrEngine: { Type: schema.TypeString, Required: true, }, - + "engine_latest_version": { + Type: schema.TypeBool, + Optional: true, + }, names.AttrEngineVersion: { Type: schema.TypeString, Optional: true, Computed: true, }, - "instance_class": { Type: schema.TypeString, Optional: true, Computed: true, }, - - "engine_latest_version": { - Type: schema.TypeBool, - Optional: true, - }, - "license_model": { Type: schema.TypeString, Optional: true, Computed: true, }, - "max_iops_per_db_instance": { Type: schema.TypeInt, Computed: true, }, - "max_iops_per_gib": { Type: schema.TypeFloat, Computed: true, }, - "max_storage_size": { Type: schema.TypeInt, Computed: true, }, - "min_iops_per_db_instance": { Type: schema.TypeInt, Computed: true, }, - "min_iops_per_gib": { Type: schema.TypeFloat, Computed: true, }, - "min_storage_size": { Type: schema.TypeInt, Computed: true, }, - "multi_az_capable": { Type: schema.TypeBool, Computed: true, }, - "outpost_capable": { Type: schema.TypeBool, Computed: true, }, - - "preferred_instance_classes": { + "preferred_engine_versions": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - - "preferred_engine_versions": { + "preferred_instance_classes": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "read_replica_capable": { Type: schema.TypeBool, Optional: true, Computed: true, }, - names.AttrStorageType: { Type: schema.TypeString, Optional: true, Computed: true, }, - "supported_engine_modes": { Type: schema.TypeList, Computed: true, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "supported_network_types": { Type: schema.TypeList, Computed: true, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "supports_clusters": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "supports_enhanced_monitoring": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "supports_global_databases": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "supports_iam_database_authentication": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "supports_iops": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "supports_kerberos_authentication": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "supports_multi_az": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "supports_performance_insights": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "supports_storage_autoscaling": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "supports_storage_encryption": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "vpc": { Type: schema.TypeBool, Optional: true, @@ -212,10 +183,10 @@ func DataSourceOrderableInstance() *schema.Resource { func dataSourceOrderableInstanceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) input := &rds.DescribeOrderableDBInstanceOptionsInput{ - MaxRecords: aws.Int64(1000), + MaxRecords: aws.Int32(1000), } if v, ok := d.GetOk("availability_zone_group"); ok { @@ -242,93 +213,91 @@ func dataSourceOrderableInstanceRead(ctx context.Context, d *schema.ResourceData input.Vpc = aws.Bool(v.(bool)) } - var instanceClassResults []*rds.OrderableDBInstanceOption + var instanceClassResults []awstypes.OrderableDBInstanceOption - err := conn.DescribeOrderableDBInstanceOptionsPagesWithContext(ctx, input, func(resp *rds.DescribeOrderableDBInstanceOptionsOutput, lastPage bool) bool { - for _, instanceOption := range resp.OrderableDBInstanceOptions { - if instanceOption == nil { - continue - } + pages := rds.NewDescribeOrderableDBInstanceOptionsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading RDS Orderable DB Instance Options: %s", err) + } + for _, instanceOption := range page.OrderableDBInstanceOptions { if v, ok := d.GetOk("read_replica_capable"); ok { - if aws.BoolValue(instanceOption.ReadReplicaCapable) != v.(bool) { + if aws.ToBool(instanceOption.ReadReplicaCapable) != v.(bool) { continue } } if v, ok := d.GetOk(names.AttrStorageType); ok { - if aws.StringValue(instanceOption.StorageType) != v.(string) { + if aws.ToString(instanceOption.StorageType) != v.(string) { continue } } if v, ok := d.GetOk("supports_clusters"); ok { - if aws.BoolValue(instanceOption.SupportsClusters) != v.(bool) { + if aws.ToBool(instanceOption.SupportsClusters) != v.(bool) { continue } } if v, ok := d.GetOk("supports_enhanced_monitoring"); ok { - if aws.BoolValue(instanceOption.SupportsEnhancedMonitoring) != v.(bool) { + if aws.ToBool(instanceOption.SupportsEnhancedMonitoring) != v.(bool) { continue } } if v, ok := d.GetOk("supports_global_databases"); ok { - if aws.BoolValue(instanceOption.SupportsGlobalDatabases) != v.(bool) { + if aws.ToBool(instanceOption.SupportsGlobalDatabases) != v.(bool) { continue } } if v, ok := d.GetOk("supports_iam_database_authentication"); ok { - if aws.BoolValue(instanceOption.SupportsIAMDatabaseAuthentication) != v.(bool) { + if aws.ToBool(instanceOption.SupportsIAMDatabaseAuthentication) != v.(bool) { continue } } if v, ok := d.GetOk("supports_iops"); ok { - if aws.BoolValue(instanceOption.SupportsIops) != v.(bool) { + if aws.ToBool(instanceOption.SupportsIops) != v.(bool) { continue } } if v, ok := d.GetOk("supports_kerberos_authentication"); ok { - if aws.BoolValue(instanceOption.SupportsKerberosAuthentication) != v.(bool) { + if aws.ToBool(instanceOption.SupportsKerberosAuthentication) != v.(bool) { continue } } if v, ok := d.GetOk("supports_multi_az"); ok { - if aws.BoolValue(instanceOption.MultiAZCapable) != v.(bool) { + if aws.ToBool(instanceOption.MultiAZCapable) != v.(bool) { continue } } if v, ok := d.GetOk("supports_performance_insights"); ok { - if aws.BoolValue(instanceOption.SupportsPerformanceInsights) != v.(bool) { + if aws.ToBool(instanceOption.SupportsPerformanceInsights) != v.(bool) { continue } } if v, ok := d.GetOk("supports_storage_autoscaling"); ok { - if aws.BoolValue(instanceOption.SupportsStorageAutoscaling) != v.(bool) { + if aws.ToBool(instanceOption.SupportsStorageAutoscaling) != v.(bool) { continue } } if v, ok := d.GetOk("supports_storage_encryption"); ok { - if aws.BoolValue(instanceOption.SupportsStorageEncryption) != v.(bool) { + if aws.ToBool(instanceOption.SupportsStorageEncryption) != v.(bool) { continue } } instanceClassResults = append(instanceClassResults, instanceOption) } - return !lastPage - }) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS Orderable DB Instance Options: %s", err) } if len(instanceClassResults) == 0 { @@ -336,14 +305,14 @@ func dataSourceOrderableInstanceRead(ctx context.Context, d *schema.ResourceData } if v, ok := d.GetOk("supported_engine_modes"); ok && len(v.([]interface{})) > 0 { - var matches []*rds.OrderableDBInstanceOption + var matches []awstypes.OrderableDBInstanceOption search := flex.ExpandStringValueList(v.([]interface{})) for _, ic := range instanceClassResults { searchedModes: for _, s := range search { for _, mode := range ic.SupportedEngineModes { - if aws.StringValue(mode) == s { + if mode == s { matches = append(matches, ic) break searchedModes } @@ -359,14 +328,14 @@ func dataSourceOrderableInstanceRead(ctx context.Context, d *schema.ResourceData } if v, ok := d.GetOk("supported_network_types"); ok && len(v.([]interface{})) > 0 { - var matches []*rds.OrderableDBInstanceOption + var matches []awstypes.OrderableDBInstanceOption search := flex.ExpandStringValueList(v.([]interface{})) for _, ic := range instanceClassResults { searchedNetworks: for _, s := range search { for _, netType := range ic.SupportedNetworkTypes { - if aws.StringValue(netType) == s { + if netType == s { matches = append(matches, ic) break searchedNetworks } @@ -384,12 +353,12 @@ func dataSourceOrderableInstanceRead(ctx context.Context, d *schema.ResourceData prefSearch := false if v, ok := d.GetOk("preferred_engine_versions"); ok && len(v.([]interface{})) > 0 { - var matches []*rds.OrderableDBInstanceOption + var matches []awstypes.OrderableDBInstanceOption search := flex.ExpandStringValueList(v.([]interface{})) for _, s := range search { for _, ic := range instanceClassResults { - if aws.StringValue(ic.EngineVersion) == s { + if aws.ToString(ic.EngineVersion) == s { matches = append(matches, ic) } // keeping all the instance classes to ensure we can match any preferred instance classes @@ -407,12 +376,12 @@ func dataSourceOrderableInstanceRead(ctx context.Context, d *schema.ResourceData latestVersion := d.Get("engine_latest_version").(bool) if v, ok := d.GetOk("preferred_instance_classes"); ok && len(v.([]interface{})) > 0 { - var matches []*rds.OrderableDBInstanceOption + var matches []awstypes.OrderableDBInstanceOption search := flex.ExpandStringValueList(v.([]interface{})) for _, s := range search { for _, ic := range instanceClassResults { - if aws.StringValue(ic.DBInstanceClass) == s { + if aws.ToString(ic.DBInstanceClass) == s { matches = append(matches, ic) } @@ -437,19 +406,19 @@ func dataSourceOrderableInstanceRead(ctx context.Context, d *schema.ResourceData instanceClassResults = matches } - var found *rds.OrderableDBInstanceOption + var found *awstypes.OrderableDBInstanceOption if latestVersion && prefSearch { sortInstanceClassesByVersion(instanceClassResults) - found = instanceClassResults[len(instanceClassResults)-1] + found = &instanceClassResults[len(instanceClassResults)-1] } if found == nil && len(instanceClassResults) > 0 && prefSearch { - found = instanceClassResults[0] + found = &instanceClassResults[0] } if found == nil && len(instanceClassResults) == 1 { - found = instanceClassResults[0] + found = &instanceClassResults[0] } if found == nil && len(instanceClassResults) > 4 { @@ -466,13 +435,11 @@ func dataSourceOrderableInstanceRead(ctx context.Context, d *schema.ResourceData return sdkdiag.AppendErrorf(diags, "no RDS DB Instance Classes match the criteria; try a different search") } - d.SetId(aws.StringValue(found.DBInstanceClass)) + d.SetId(aws.ToString(found.DBInstanceClass)) d.Set("availability_zone_group", found.AvailabilityZoneGroup) - var availabilityZones []string - for _, v := range found.AvailabilityZones { - availabilityZones = append(availabilityZones, aws.StringValue(v.Name)) - } - d.Set(names.AttrAvailabilityZones, availabilityZones) + d.Set(names.AttrAvailabilityZones, tfslices.ApplyToAll(found.AvailabilityZones, func(v awstypes.AvailabilityZone) string { + return aws.ToString(v.Name) + })) d.Set(names.AttrEngine, found.Engine) d.Set(names.AttrEngineVersion, found.EngineVersion) d.Set("instance_class", found.DBInstanceClass) @@ -487,8 +454,8 @@ func dataSourceOrderableInstanceRead(ctx context.Context, d *schema.ResourceData d.Set("outpost_capable", found.OutpostCapable) d.Set("read_replica_capable", found.ReadReplicaCapable) d.Set(names.AttrStorageType, found.StorageType) - d.Set("supported_engine_modes", aws.StringValueSlice(found.SupportedEngineModes)) - d.Set("supported_network_types", aws.StringValueSlice(found.SupportedNetworkTypes)) + d.Set("supported_engine_modes", found.SupportedEngineModes) + d.Set("supported_network_types", found.SupportedNetworkTypes) d.Set("supports_clusters", found.SupportsClusters) d.Set("supports_enhanced_monitoring", found.SupportsEnhancedMonitoring) d.Set("supports_global_databases", found.SupportsGlobalDatabases) @@ -504,12 +471,12 @@ func dataSourceOrderableInstanceRead(ctx context.Context, d *schema.ResourceData return diags } -func sortInstanceClassesByVersion(ic []*rds.OrderableDBInstanceOption) { +func sortInstanceClassesByVersion(ic []awstypes.OrderableDBInstanceOption) { if len(ic) < 2 { return } sort.Slice(ic, func(i, j int) bool { - return version.LessThan(aws.StringValue(ic[i].EngineVersion), aws.StringValue(ic[j].EngineVersion)) + return version.LessThan(aws.ToString(ic[i].EngineVersion), aws.ToString(ic[j].EngineVersion)) }) } diff --git a/internal/service/rds/orderable_instance_data_source_test.go b/internal/service/rds/orderable_instance_data_source_test.go index 0dc97dad7bd..3a3710f9047 100644 --- a/internal/service/rds/orderable_instance_data_source_test.go +++ b/internal/service/rds/orderable_instance_data_source_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/rds" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -465,14 +465,14 @@ func TestAccRDSOrderableInstanceDataSource_supportsStorageEncryption(t *testing. } func testAccOrderableInstancePreCheck(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) input := &rds.DescribeOrderableDBInstanceOptionsInput{ Engine: aws.String("mysql"), DBInstanceClass: aws.String("db.m5.xlarge"), } - _, err := conn.DescribeOrderableDBInstanceOptionsWithContext(ctx, input) + _, err := conn.DescribeOrderableDBInstanceOptions(ctx, input) if acctest.PreCheckSkipError(err) { t.Skipf("skipping acceptance testing: %s", err) diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index 54e2997ef72..a24e6fe7627 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -24,9 +24,6 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { return []*types.ServicePackageFrameworkResource{ - { - Factory: newResourceExportTask, - }, { Factory: newIntegrationResource, Name: "Integration", @@ -34,6 +31,9 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic IdentifierAttribute: names.AttrARN, }, }, + { + Factory: newResourceExportTask, + }, } } @@ -97,8 +97,9 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac TypeName: "aws_rds_engine_version", }, { - Factory: DataSourceOrderableInstance, + Factory: dataSourceOrderableInstance, TypeName: "aws_rds_orderable_db_instance", + Name: "Orderable DB Instance", }, { Factory: dataSourceReservedOffering, From df426f37638cb59ed57f0f134eb59d67f48002f8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 10:12:25 -0400 Subject: [PATCH 10/24] Fix 'TestAccRDSIntegration_disappears'. --- internal/service/rds/integration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/rds/integration.go b/internal/service/rds/integration.go index a069f1a4c27..11d794634af 100644 --- a/internal/service/rds/integration.go +++ b/internal/service/rds/integration.go @@ -225,7 +225,7 @@ func (r *integrationResource) Delete(ctx context.Context, request resource.Delet IntegrationIdentifier: aws.String(data.ID.ValueString()), }) - if errs.IsA[*awstypes.ResourceNotFoundFault](err) { + if errs.IsA[*awstypes.IntegrationNotFoundFault](err) { return } From 9727b17f730873a6f5b749004a42a2417a2902a0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 11:09:02 -0400 Subject: [PATCH 11/24] internal/generate/namevaluesfiltersv2: Actually generate. --- internal/generate/common/generator.go | 12 +- .../generate/namevaluesfiltersv2/generate.go | 7 ++ .../generators/servicefilters/file.tmpl | 36 ++++++ .../generators/servicefilters/main.go | 104 ++++-------------- .../service_filters_gen.go | 24 ++++ .../service_generation_customizations.go | 16 ++- 6 files changed, 112 insertions(+), 87 deletions(-) create mode 100644 internal/generate/namevaluesfiltersv2/generate.go create mode 100644 internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl diff --git a/internal/generate/common/generator.go b/internal/generate/common/generator.go index 7dbbd25dd94..5a53fbbab1d 100644 --- a/internal/generate/common/generator.go +++ b/internal/generate/common/generator.go @@ -7,6 +7,7 @@ import ( "bytes" "fmt" "go/format" + "maps" "os" "path" "strings" @@ -58,7 +59,7 @@ type Destination interface { CreateDirectories() error Write() error WriteBytes(body []byte) error - WriteTemplate(templateName, templateBody string, templateData any) error + WriteTemplate(templateName, templateBody string, templateData any, funcMaps ...template.FuncMap) error WriteTemplateSet(templates *template.Template, templateData any) error } @@ -129,8 +130,8 @@ func (d *baseDestination) WriteBytes(body []byte) error { return err } -func (d *baseDestination) WriteTemplate(templateName, templateBody string, templateData any) error { - body, err := parseTemplate(templateName, templateBody, templateData) +func (d *baseDestination) WriteTemplate(templateName, templateBody string, templateData any, funcMaps ...template.FuncMap) error { + body, err := parseTemplate(templateName, templateBody, templateData, funcMaps...) if err != nil { return err @@ -144,7 +145,7 @@ func (d *baseDestination) WriteTemplate(templateName, templateBody string, templ return d.WriteBytes(body) } -func parseTemplate(templateName, templateBody string, templateData any) ([]byte, error) { +func parseTemplate(templateName, templateBody string, templateData any, funcMaps ...template.FuncMap) ([]byte, error) { funcMap := template.FuncMap{ // FirstUpper returns a string with the first character as upper case. "FirstUpper": func(s string) string { @@ -157,6 +158,9 @@ func parseTemplate(templateName, templateBody string, templateData any) ([]byte, // Title returns a string with the first character of each word as upper case. "Title": cases.Title(language.Und, cases.NoLower).String, } + for _, v := range funcMaps { + maps.Copy(funcMap, v) // Extras overwrite defaults. + } tmpl, err := template.New(templateName).Funcs(funcMap).Parse(templateBody) if err != nil { diff --git a/internal/generate/namevaluesfiltersv2/generate.go b/internal/generate/namevaluesfiltersv2/generate.go new file mode 100644 index 00000000000..e9c3cc60b5b --- /dev/null +++ b/internal/generate/namevaluesfiltersv2/generate.go @@ -0,0 +1,7 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:generate go run ./generators/servicefilters/main.go +// ONLY generate directives and package declaration! Do not add anything else to this file. + +package namevaluesfiltersv2 diff --git a/internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl b/internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl new file mode 100644 index 00000000000..d38dd5abbd0 --- /dev/null +++ b/internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl @@ -0,0 +1,36 @@ +// Code generated by generators/servicefilters/main.go; DO NOT EDIT. + +package namevaluesfiltersv2 + +import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports + "github.com/aws/aws-sdk-go-v2/aws" +{{- range .SliceServiceNames }} + {{ . | FilterPackage}} +{{- end }} +) + +// []*SERVICE.Filter handling +{{- range .SliceServiceNames }} + +// {{ . | Title }}Filters returns {{ . }} service filters. +func (filters NameValuesFilters) {{ . | Title }}Filters() []{{ . | FilterPackagePrefix }}.{{ . | FilterType }} { + m := filters.Map() + + if len(m) == 0 { + return nil + } + + result := make([]{{ . | FilterPackagePrefix }}.{{ . | FilterType }}, 0, len(m)) + + for k, v := range m { + filter := {{ . | FilterPackagePrefix }}.{{ . | FilterType }}{ + {{ . | FilterTypeNameField }}: {{ . | FilterTypeNameFunc }}(k), + {{ . | FilterTypeValuesField }}: v, + } + + result = append(result, filter) + } + + return result +} +{{- end }} diff --git a/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go b/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go index 88368f5110d..3bda3ac4f50 100644 --- a/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go +++ b/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go @@ -4,33 +4,35 @@ package main import ( - "bytes" - "go/format" - "log" - "os" + _ "embed" "sort" - "strings" "text/template" + "github.com/hashicorp/terraform-provider-aws/internal/generate/common" "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfiltersv2" ) -const filename = `service_filters_v2_gen.go` - -// Representing types such as []*ec2.Filter, []*rds.Filter, ... -var sliceServiceNames = []string{ - "secretsmanager", -} - type TemplateData struct { SliceServiceNames []string } func main() { + const ( + filename = `service_filters_gen.go` + ) + g := common.NewGenerator() + + g.Infof("Generating internal/generate/namevaluesfiltersv2/%s", filename) + + // Representing types such as []*ec2.Filter, []*rds.Filter, ... + sliceServiceNames := []string{ + "rds", + "secretsmanager", + } // Always sort to reduce any potential generation churn sort.Strings(sliceServiceNames) - templateData := TemplateData{ + td := TemplateData{ SliceServiceNames: sliceServiceNames, } templateFuncMap := template.FuncMap{ @@ -38,80 +40,20 @@ func main() { "FilterPackagePrefix": namevaluesfiltersv2.ServiceFilterPackagePrefix, "FilterType": namevaluesfiltersv2.ServiceFilterType, "FilterTypeNameField": namevaluesfiltersv2.ServiceFilterTypeNameField, + "FilterTypeNameFunc": namevaluesfiltersv2.ServiceFilterTypeNameFunc, "FilterTypeValuesField": namevaluesfiltersv2.ServiceFilterTypeValuesField, - "Title": strings.Title, - } - - tmpl, err := template.New("servicefilters").Funcs(templateFuncMap).Parse(templateBody) - - if err != nil { - log.Fatalf("error parsing template: %s", err) - } - - var buffer bytes.Buffer - err = tmpl.Execute(&buffer, templateData) - - if err != nil { - log.Fatalf("error executing template: %s", err) - } - - generatedFileContents, err := format.Source(buffer.Bytes()) - - if err != nil { - log.Fatalf("error formatting generated file: %s", err) } - f, err := os.Create(filename) + d := g.NewGoFileDestination(filename) - if err != nil { - log.Fatalf("error creating file (%s): %s", filename, err) + if err := d.WriteTemplate("namevaluesfiltersv2", tmpl, td, templateFuncMap); err != nil { + g.Fatalf("generating file (%s): %s", filename, err) } - defer f.Close() - - _, err = f.Write(generatedFileContents) - - if err != nil { - log.Fatalf("error writing to file (%s): %s", filename, err) + if err := d.Write(); err != nil { + g.Fatalf("generating file (%s): %s", filename, err) } } -var templateBody = ` -// Code generated by generators/servicefilters/main.go; DO NOT EDIT. - -package namevaluesfiltersv2 - -import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports -{{- range .SliceServiceNames }} -{{- if eq . (. | FilterPackage) }} - {{ . }} -{{- end }} -{{- end }} -) - -// []*SERVICE.Filter handling -{{- range .SliceServiceNames }} - -// {{ . | Title }}Filters returns {{ . }} service filters. -func (filters NameValuesFilters) {{ . | Title }}Filters() []*{{ . | FilterPackagePrefix }}.{{ . | FilterType }} { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]{{ . | FilterPackagePrefix }}.{{ . | FilterType }}, 0, len(m)) - - for k, v := range m { - filter := {{ . | FilterPackagePrefix }}.{{ . | FilterType }}{ - {{ . | FilterTypeNameField }}: { . | FilterPackagePrefix }}.FilterNameStringType(k), - {{ . | FilterTypeValuesField }}: v, - } - - result = append(result, filter) - } - - return result -} -{{- end }} -` +//go:embed file.tmpl +var tmpl string diff --git a/internal/generate/namevaluesfiltersv2/service_filters_gen.go b/internal/generate/namevaluesfiltersv2/service_filters_gen.go index e25053e6244..7b35dd2f4b0 100644 --- a/internal/generate/namevaluesfiltersv2/service_filters_gen.go +++ b/internal/generate/namevaluesfiltersv2/service_filters_gen.go @@ -3,11 +3,35 @@ package namevaluesfiltersv2 import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports + "github.com/aws/aws-sdk-go-v2/aws" + rdstypes "github.com/aws/aws-sdk-go-v2/service/rds/types" secretsmanagertypes "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types" ) // []*SERVICE.Filter handling +// RdsFilters returns rds service filters. +func (filters NameValuesFilters) RdsFilters() []rdstypes.Filter { + m := filters.Map() + + if len(m) == 0 { + return nil + } + + result := make([]rdstypes.Filter, 0, len(m)) + + for k, v := range m { + filter := rdstypes.Filter{ + Name: aws.String(k), + Values: v, + } + + result = append(result, filter) + } + + return result +} + // SecretsmanagerFilters returns secretsmanager service filters. func (filters NameValuesFilters) SecretsmanagerFilters() []secretsmanagertypes.Filter { m := filters.Map() diff --git a/internal/generate/namevaluesfiltersv2/service_generation_customizations.go b/internal/generate/namevaluesfiltersv2/service_generation_customizations.go index 31bbba1fce2..5614a793ee3 100644 --- a/internal/generate/namevaluesfiltersv2/service_generation_customizations.go +++ b/internal/generate/namevaluesfiltersv2/service_generation_customizations.go @@ -11,7 +11,7 @@ import "fmt" func ServiceFilterPackage(serviceName string) string { switch serviceName { default: - return fmt.Sprintf("%[1]stypes \"github.com/aws/aws-sdk-go-v2/service/%[1]s/types\"", serviceName) + return fmt.Sprintf("%[1]s \"github.com/aws/aws-sdk-go-v2/service/%[2]s/types\"", ServiceFilterPackagePrefix(serviceName), serviceName) } } @@ -34,8 +34,20 @@ func ServiceFilterType(serviceName string) string { // ServiceFilterTypeNameField determines the service filter type name field. func ServiceFilterTypeNameField(serviceName string) string { switch serviceName { - default: + case "secretsmanager": return "Key" + default: + return "Name" + } +} + +// ServiceFilterTypeNameFunc determines the function called on the service filter type name. +func ServiceFilterTypeNameFunc(serviceName string) string { + switch serviceName { + case "secretsmanager": + return fmt.Sprintf("%[1]s.FilterNameStringType", ServiceFilterPackagePrefix(serviceName)) + default: + return "aws.String" } } From 4e6a3538b27ac8dedfd040f5edb2f86058aa8570 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 11:12:17 -0400 Subject: [PATCH 12/24] d/aws_rds_engine_version: Migrate to AWS SDK for Go v2. --- .../service/rds/engine_version_data_source.go | 141 ++++++------------ .../rds/engine_version_data_source_test.go | 8 +- .../rds/orderable_instance_data_source.go | 1 + internal/service/rds/service_package_gen.go | 3 +- 4 files changed, 53 insertions(+), 100 deletions(-) diff --git a/internal/service/rds/engine_version_data_source.go b/internal/service/rds/engine_version_data_source.go index 19431053a6b..2623950d432 100644 --- a/internal/service/rds/engine_version_data_source.go +++ b/internal/service/rds/engine_version_data_source.go @@ -5,189 +5,154 @@ package rds import ( "context" - "log" "sort" "github.com/YakDriver/go-version" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/rds" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds" + awstypes "github.com/aws/aws-sdk-go-v2/service/rds/types" "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfiltersv2" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_rds_engine_version") -func DataSourceEngineVersion() *schema.Resource { +// @SDKDataSource("aws_rds_engine_version", name="Engine Version") +func dataSourceEngineVersion() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceEngineVersionRead, + Schema: map[string]*schema.Schema{ "default_character_set": { Type: schema.TypeString, Computed: true, }, - "default_only": { Type: schema.TypeBool, Optional: true, }, - names.AttrEngine: { Type: schema.TypeString, Required: true, }, - "engine_description": { Type: schema.TypeString, Computed: true, }, - "exportable_log_types": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, - - names.AttrFilter: namevaluesfilters.Schema(), - + names.AttrFilter: namevaluesfiltersv2.Schema(), "has_major_target": { Type: schema.TypeBool, Optional: true, }, - "has_minor_target": { Type: schema.TypeBool, Optional: true, }, - "include_all": { Type: schema.TypeBool, Optional: true, }, - "latest": { Type: schema.TypeBool, Optional: true, }, - "parameter_group_family": { Type: schema.TypeString, Computed: true, Optional: true, }, - "preferred_major_targets": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "preferred_upgrade_targets": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "preferred_versions": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - names.AttrStatus: { Type: schema.TypeString, Computed: true, }, - "supported_character_sets": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, - "supported_feature_names": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, - "supported_modes": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, - "supported_timezones": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, - "supports_global_databases": { Type: schema.TypeBool, Computed: true, }, - "supports_limitless_database": { Type: schema.TypeBool, Computed: true, }, - "supports_log_exports_to_cloudwatch": { Type: schema.TypeBool, Computed: true, }, - "supports_parallel_query": { Type: schema.TypeBool, Computed: true, }, - "supports_read_replica": { Type: schema.TypeBool, Computed: true, }, - "valid_major_targets": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, - "valid_minor_targets": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, - "valid_upgrade_targets": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, - names.AttrVersion: { Type: schema.TypeString, Computed: true, Optional: true, }, - "version_actual": { Type: schema.TypeString, Computed: true, }, - "version_description": { Type: schema.TypeString, Computed: true, @@ -198,7 +163,7 @@ func DataSourceEngineVersion() *schema.Resource { func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) input := &rds.DescribeDBEngineVersionsInput{ ListSupportedCharacterSets: aws.Bool(true), @@ -210,7 +175,7 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me } if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).RDSFilters() + input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).RdsFilters() } if v, ok := d.GetOk("parameter_group_family"); ok { @@ -244,21 +209,17 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me input.DefaultOnly = aws.Bool(true) } - log.Printf("[DEBUG] Reading RDS engine versions: %v", input) - var engineVersions []*rds.DBEngineVersion + var engineVersions []awstypes.DBEngineVersion - err := conn.DescribeDBEngineVersionsPagesWithContext(ctx, input, func(resp *rds.DescribeDBEngineVersionsOutput, lastPage bool) bool { - for _, engineVersion := range resp.DBEngineVersions { - if engineVersion == nil { - continue - } + pages := rds.NewDescribeDBEngineVersionsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - engineVersions = append(engineVersions, engineVersion) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading RDS engine versions: %s", err) } - return !lastPage - }) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS engine versions: %s", err) + + engineVersions = append(engineVersions, page.DBEngineVersions...) } if len(engineVersions) == 0 { @@ -269,7 +230,7 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me // preferred versions if l := d.Get("preferred_versions").([]interface{}); len(l) > 0 { - var preferredVersions []*rds.DBEngineVersion + var preferredVersions []awstypes.DBEngineVersion for _, elem := range l { preferredVersion, ok := elem.(string) @@ -279,7 +240,7 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me } for _, engineVersion := range engineVersions { - if preferredVersion == aws.StringValue(engineVersion.EngineVersion) { + if preferredVersion == aws.ToString(engineVersion.EngineVersion) { preferredVersions = append(preferredVersions, engineVersion) } } @@ -295,7 +256,7 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me // preferred upgrade targets if l := d.Get("preferred_upgrade_targets").([]interface{}); len(l) > 0 { - var prefUTs []*rds.DBEngineVersion + var prefUTs []awstypes.DBEngineVersion engineVersionsLoop: for _, engineVersion := range engineVersions { @@ -306,7 +267,7 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me continue } - if prefUT == aws.StringValue(upgradeTarget.EngineVersion) { + if prefUT == aws.ToString(upgradeTarget.EngineVersion) { prefUTs = append(prefUTs, engineVersion) continue engineVersionsLoop } @@ -324,7 +285,7 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me // preferred major targets if l := d.Get("preferred_major_targets").([]interface{}); len(l) > 0 { - var prefMTs []*rds.DBEngineVersion + var prefMTs []awstypes.DBEngineVersion majorsLoop: for _, engineVersion := range engineVersions { @@ -335,7 +296,7 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me continue } - if prefMT == aws.StringValue(upgradeTarget.EngineVersion) && aws.BoolValue(upgradeTarget.IsMajorVersionUpgrade) { + if prefMT == aws.ToString(upgradeTarget.EngineVersion) && aws.ToBool(upgradeTarget.IsMajorVersionUpgrade) { prefMTs = append(prefMTs, engineVersion) continue majorsLoop } @@ -352,12 +313,12 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me } if v, ok := d.GetOk("has_minor_target"); ok && v.(bool) { - var wMinor []*rds.DBEngineVersion + var wMinor []awstypes.DBEngineVersion hasMinorLoop: for _, engineVersion := range engineVersions { for _, upgradeTarget := range engineVersion.ValidUpgradeTarget { - if !aws.BoolValue(upgradeTarget.IsMajorVersionUpgrade) { + if !aws.ToBool(upgradeTarget.IsMajorVersionUpgrade) { wMinor = append(wMinor, engineVersion) continue hasMinorLoop } @@ -372,12 +333,12 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me } if v, ok := d.GetOk("has_major_target"); ok && v.(bool) { - var wMajor []*rds.DBEngineVersion + var wMajor []awstypes.DBEngineVersion hasMajorLoop: for _, engineVersion := range engineVersions { for _, upgradeTarget := range engineVersion.ValidUpgradeTarget { - if aws.BoolValue(upgradeTarget.IsMajorVersionUpgrade) { + if aws.ToBool(upgradeTarget.IsMajorVersionUpgrade) { wMajor = append(wMajor, engineVersion) continue hasMajorLoop } @@ -391,19 +352,19 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me engineVersions = wMajor } - var found *rds.DBEngineVersion + var found *awstypes.DBEngineVersion if v, ok := d.GetOk("latest"); ok && v.(bool) { sortEngineVersions(engineVersions) - found = engineVersions[len(engineVersions)-1] + found = &engineVersions[len(engineVersions)-1] } if found == nil && len(engineVersions) == 1 { - found = engineVersions[0] + found = &engineVersions[0] } if found == nil && len(engineVersions) > 0 && prefSearch { - found = engineVersions[0] + found = &engineVersions[0] } if found == nil && len(engineVersions) > 1 { @@ -414,33 +375,23 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me return sdkdiag.AppendErrorf(diags, "no RDS engine versions match the criteria: %+v", input) } - d.SetId(aws.StringValue(found.EngineVersion)) - + d.SetId(aws.ToString(found.EngineVersion)) if found.DefaultCharacterSet != nil { d.Set("default_character_set", found.DefaultCharacterSet.CharacterSetName) } - d.Set(names.AttrEngine, found.Engine) d.Set("engine_description", found.DBEngineDescription) d.Set("exportable_log_types", found.ExportableLogTypes) d.Set("parameter_group_family", found.DBParameterGroupFamily) d.Set(names.AttrStatus, found.Status) - - var characterSets []string - for _, cs := range found.SupportedCharacterSets { - characterSets = append(characterSets, aws.StringValue(cs.CharacterSetName)) - } - d.Set("supported_character_sets", characterSets) - + d.Set("supported_character_sets", tfslices.ApplyToAll(found.SupportedCharacterSets, func(v awstypes.CharacterSet) string { + return aws.ToString(v.CharacterSetName) + })) d.Set("supported_feature_names", found.SupportedFeatureNames) d.Set("supported_modes", found.SupportedEngineModes) - - var timezones []string - for _, tz := range found.SupportedTimezones { - timezones = append(timezones, aws.StringValue(tz.TimezoneName)) - } - d.Set("supported_timezones", timezones) - + d.Set("supported_timezones", tfslices.ApplyToAll(found.SupportedTimezones, func(v awstypes.Timezone) string { + return aws.ToString(v.TimezoneName) + })) d.Set("supports_global_databases", found.SupportsGlobalDatabases) d.Set("supports_limitless_database", found.SupportsLimitlessDatabase) d.Set("supports_log_exports_to_cloudwatch", found.SupportsLogExportsToCloudwatchLogs) @@ -451,14 +402,14 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me var minorTargets []string var majorTargets []string for _, ut := range found.ValidUpgradeTarget { - upgradeTargets = append(upgradeTargets, aws.StringValue(ut.EngineVersion)) + upgradeTargets = append(upgradeTargets, aws.ToString(ut.EngineVersion)) - if aws.BoolValue(ut.IsMajorVersionUpgrade) { - majorTargets = append(majorTargets, aws.StringValue(ut.EngineVersion)) + if aws.ToBool(ut.IsMajorVersionUpgrade) { + majorTargets = append(majorTargets, aws.ToString(ut.EngineVersion)) continue } - minorTargets = append(minorTargets, aws.StringValue(ut.EngineVersion)) + minorTargets = append(minorTargets, aws.ToString(ut.EngineVersion)) } d.Set("valid_upgrade_targets", upgradeTargets) d.Set("valid_minor_targets", minorTargets) @@ -471,13 +422,13 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me return diags } -func sortEngineVersions(engineVersions []*rds.DBEngineVersion) { +func sortEngineVersions(engineVersions []awstypes.DBEngineVersion) { if len(engineVersions) < 2 { return } sort.Slice(engineVersions, func(i, j int) bool { - return version.LessThanWithTime(engineVersions[i].CreateTime, engineVersions[j].CreateTime, aws.StringValue(engineVersions[i].EngineVersion), aws.StringValue(engineVersions[j].EngineVersion)) + return version.LessThanWithTime(engineVersions[i].CreateTime, engineVersions[j].CreateTime, aws.ToString(engineVersions[i].EngineVersion), aws.ToString(engineVersions[j].EngineVersion)) }) } diff --git a/internal/service/rds/engine_version_data_source_test.go b/internal/service/rds/engine_version_data_source_test.go index 53537119ed2..b639a681e52 100644 --- a/internal/service/rds/engine_version_data_source_test.go +++ b/internal/service/rds/engine_version_data_source_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/rds" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -389,14 +389,14 @@ func TestAccRDSEngineVersionDataSource_hasMinorMajor(t *testing.T) { } func testAccEngineVersionPreCheck(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) input := &rds.DescribeDBEngineVersionsInput{ Engine: aws.String(tfrds.InstanceEngineMySQL), DefaultOnly: aws.Bool(true), } - _, err := conn.DescribeDBEngineVersionsWithContext(ctx, input) + _, err := conn.DescribeDBEngineVersions(ctx, input) if acctest.PreCheckSkipError(err) { t.Skipf("skipping acceptance testing: %s", err) diff --git a/internal/service/rds/orderable_instance_data_source.go b/internal/service/rds/orderable_instance_data_source.go index b928cbf0761..6ce97d9f3e5 100644 --- a/internal/service/rds/orderable_instance_data_source.go +++ b/internal/service/rds/orderable_instance_data_source.go @@ -24,6 +24,7 @@ import ( func dataSourceOrderableInstance() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceOrderableInstanceRead, + Schema: map[string]*schema.Schema{ "availability_zone_group": { Type: schema.TypeString, diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index a24e6fe7627..b492319bc8c 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -93,8 +93,9 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac TypeName: "aws_rds_clusters", }, { - Factory: DataSourceEngineVersion, + Factory: dataSourceEngineVersion, TypeName: "aws_rds_engine_version", + Name: "Engine Version", }, { Factory: dataSourceOrderableInstance, From 86cde186996c06b3baa47ec521a17d716a8214ec Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 11:29:23 -0400 Subject: [PATCH 13/24] Fix 'TestAccRDSEngineVersionDataSource_preferredVersionsPreferredUpgradeTargets'. --- internal/service/rds/engine_version_data_source_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/rds/engine_version_data_source_test.go b/internal/service/rds/engine_version_data_source_test.go index b639a681e52..021c23fbd52 100644 --- a/internal/service/rds/engine_version_data_source_test.go +++ b/internal/service/rds/engine_version_data_source_test.go @@ -119,9 +119,9 @@ func TestAccRDSEngineVersionDataSource_preferredVersionsPreferredUpgradeTargets( CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: testAccEngineVersionDataSourceConfig_preferredVersionsPreferredUpgrades(tfrds.InstanceEngineMySQL, `"5.7.37", "5.7.38", "5.7.39"`, `"8.0.34"`), + Config: testAccEngineVersionDataSourceConfig_preferredVersionsPreferredUpgrades(tfrds.InstanceEngineMySQL, `"8.0.32", "8.0.33", "8.0.34"`, `"8.0.37"`), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(dataSourceName, names.AttrVersion, "5.7.39"), + resource.TestCheckResourceAttr(dataSourceName, names.AttrVersion, "8.0.34"), ), }, { From 460ada7364d6e8cc3e6b33d14db18c0e14af8800 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 11:35:24 -0400 Subject: [PATCH 14/24] d/aws_rds_certificate: Migrate to AWS SDK for Go v2. --- .../service/rds/certificate_data_source.go | 56 ++++++++----------- .../rds/certificate_data_source_test.go | 6 +- internal/service/rds/service_package_gen.go | 3 +- 3 files changed, 28 insertions(+), 37 deletions(-) diff --git a/internal/service/rds/certificate_data_source.go b/internal/service/rds/certificate_data_source.go index f125f8ab09b..ec69392de46 100644 --- a/internal/service/rds/certificate_data_source.go +++ b/internal/service/rds/certificate_data_source.go @@ -8,8 +8,9 @@ import ( "slices" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/rds" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds" + "github.com/aws/aws-sdk-go-v2/service/rds/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -17,10 +18,11 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_rds_certificate") -func DataSourceCertificate() *schema.Resource { +// @SDKDataSource("aws_rds_certificate", name="Certificate") +func dataSourceCertificate() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceCertificateRead, + Schema: map[string]*schema.Schema{ names.AttrARN: { Type: schema.TypeString, @@ -65,7 +67,7 @@ func DataSourceCertificate() *schema.Resource { func dataSourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) input := &rds.DescribeCertificatesInput{} @@ -73,24 +75,17 @@ func dataSourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta input.CertificateIdentifier = aws.String(v.(string)) } - var certificates []*rds.Certificate - - err := conn.DescribeCertificatesPagesWithContext(ctx, input, func(page *rds.DescribeCertificatesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + var certificates []types.Certificate - for _, certificate := range page.Certificates { - if certificate == nil { - continue - } + pages := rds.NewDescribeCertificatesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - certificates = append(certificates, certificate) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading RDS Certificates: %s", err) } - return !lastPage - }) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS Certificates: %s", err) + + certificates = append(certificates, page.Certificates...) } if len(certificates) == 0 { @@ -98,13 +93,13 @@ func dataSourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta } // client side filtering - var certificate *rds.Certificate + var certificate *types.Certificate if d.Get("latest_valid_till").(bool) { - slices.SortFunc(certificates, func(a, b *rds.Certificate) int { + slices.SortFunc(certificates, func(a, b types.Certificate) int { return a.ValidTill.Compare(*b.ValidTill) }) - certificate = certificates[len(certificates)-1] + certificate = &certificates[len(certificates)-1] } else { if len(certificates) > 1 { return sdkdiag.AppendErrorf(diags, "multiple RDS Certificates match the criteria; try changing search query") @@ -112,27 +107,22 @@ func dataSourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta if len(certificates) == 0 { return sdkdiag.AppendErrorf(diags, "no RDS Certificates match the criteria") } - certificate = certificates[0] + certificate = &certificates[0] } - d.SetId(aws.StringValue(certificate.CertificateIdentifier)) - + d.SetId(aws.ToString(certificate.CertificateIdentifier)) d.Set(names.AttrARN, certificate.CertificateArn) d.Set("certificate_type", certificate.CertificateType) d.Set("customer_override", certificate.CustomerOverride) - if certificate.CustomerOverrideValidTill != nil { - d.Set("customer_override_valid_till", aws.TimeValue(certificate.CustomerOverrideValidTill).Format(time.RFC3339)) + d.Set("customer_override_valid_till", aws.ToTime(certificate.CustomerOverrideValidTill).Format(time.RFC3339)) } - d.Set("thumbprint", certificate.Thumbprint) - if certificate.ValidFrom != nil { - d.Set("valid_from", aws.TimeValue(certificate.ValidFrom).Format(time.RFC3339)) + d.Set("valid_from", aws.ToTime(certificate.ValidFrom).Format(time.RFC3339)) } - if certificate.ValidTill != nil { - d.Set("valid_till", aws.TimeValue(certificate.ValidTill).Format(time.RFC3339)) + d.Set("valid_till", aws.ToTime(certificate.ValidTill).Format(time.RFC3339)) } return diags diff --git a/internal/service/rds/certificate_data_source_test.go b/internal/service/rds/certificate_data_source_test.go index 5017c4a7a4e..b72e284089f 100644 --- a/internal/service/rds/certificate_data_source_test.go +++ b/internal/service/rds/certificate_data_source_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/service/rds" + "github.com/aws/aws-sdk-go-v2/service/rds" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -63,11 +63,11 @@ func TestAccRDSCertificateDataSource_latestValidTill(t *testing.T) { } func testAccCertificatePreCheck(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) input := &rds.DescribeCertificatesInput{} - _, err := conn.DescribeCertificatesWithContext(ctx, input) + _, err := conn.DescribeCertificates(ctx, input) if acctest.PreCheckSkipError(err) { t.Skipf("skipping acceptance testing: %s", err) diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index b492319bc8c..005b2545f95 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -81,8 +81,9 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac Name: "DB Subnet Group", }, { - Factory: DataSourceCertificate, + Factory: dataSourceCertificate, TypeName: "aws_rds_certificate", + Name: "Certificate", }, { Factory: DataSourceCluster, From 747a18f5e5d88e09f41935dbc8ad88c942b559d6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 11:44:40 -0400 Subject: [PATCH 15/24] d/aws_db_parameter_group: Migrate to AWS SDK for Go v2. --- .../rds/parameter_group_data_source.go | 37 +++++++------------ .../rds/parameter_group_data_source_test.go | 11 ------ internal/service/rds/service_package_gen.go | 3 +- 3 files changed, 15 insertions(+), 36 deletions(-) diff --git a/internal/service/rds/parameter_group_data_source.go b/internal/service/rds/parameter_group_data_source.go index 5fecde68904..44f1574f088 100644 --- a/internal/service/rds/parameter_group_data_source.go +++ b/internal/service/rds/parameter_group_data_source.go @@ -6,35 +6,33 @@ package rds import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/rds" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_db_parameter_group") -func DataSourceParameterGroup() *schema.Resource { +// @SDKDataSource("aws_db_parameter_group", name="DB Parameter Group") +func dataSourceParameterGroup() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceParameterGroupRead, + Schema: map[string]*schema.Schema{ names.AttrARN: { Type: schema.TypeString, Computed: true, }, - names.AttrDescription: { Type: schema.TypeString, Computed: true, }, - names.AttrFamily: { Type: schema.TypeString, Computed: true, }, - names.AttrName: { Type: schema.TypeString, Required: true, @@ -45,28 +43,19 @@ func DataSourceParameterGroup() *schema.Resource { func dataSourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) - groupName := d.Get(names.AttrName).(string) + output, err := findDBParameterGroupByName(ctx, conn, d.Get(names.AttrName).(string)) - input := rds.DescribeDBParameterGroupsInput{ - DBParameterGroupName: aws.String(groupName), - } - - output, err := conn.DescribeDBParameterGroupsWithContext(ctx, &input) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS DB Parameter Groups (%s): %s", d.Get(names.AttrName).(string), err) - } - - if len(output.DBParameterGroups) != 1 || aws.StringValue(output.DBParameterGroups[0].DBParameterGroupName) != groupName { - return sdkdiag.AppendErrorf(diags, "RDS DB Parameter Group not found (%#v): %s", output, err) + return sdkdiag.AppendFromErr(diags, tfresource.SingularDataSourceFindError("RDS DB Parameter Group", err)) } - d.SetId(aws.StringValue(output.DBParameterGroups[0].DBParameterGroupName)) - d.Set(names.AttrName, output.DBParameterGroups[0].DBParameterGroupName) - d.Set(names.AttrARN, output.DBParameterGroups[0].DBParameterGroupArn) - d.Set(names.AttrFamily, output.DBParameterGroups[0].DBParameterGroupFamily) - d.Set(names.AttrDescription, output.DBParameterGroups[0].Description) + d.SetId(aws.ToString(output.DBParameterGroupName)) + d.Set(names.AttrARN, output.DBParameterGroupArn) + d.Set(names.AttrDescription, output.Description) + d.Set(names.AttrFamily, output.DBParameterGroupFamily) + d.Set(names.AttrName, output.DBParameterGroupName) return diags } diff --git a/internal/service/rds/parameter_group_data_source_test.go b/internal/service/rds/parameter_group_data_source_test.go index cf8e3016009..dd8e951cb87 100644 --- a/internal/service/rds/parameter_group_data_source_test.go +++ b/internal/service/rds/parameter_group_data_source_test.go @@ -7,7 +7,6 @@ import ( "fmt" "testing" - "github.com/YakDriver/regexache" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" @@ -25,10 +24,6 @@ func TestAccRDSParameterGroupDataSource_basic(t *testing.T) { ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ - { - Config: testAccParameterGroupDataSourceConfig_nonExistent, - ExpectError: regexache.MustCompile(`not found`), - }, { Config: testAccParameterGroupDataSourceConfig_basic(rName), Check: resource.ComposeTestCheckFunc( @@ -42,12 +37,6 @@ func TestAccRDSParameterGroupDataSource_basic(t *testing.T) { }) } -const testAccParameterGroupDataSourceConfig_nonExistent = ` -data "aws_db_parameter_group" "test" { - name = "tf-acc-test-does-not-exist" -} -` - func testAccParameterGroupDataSourceConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_db_parameter_group" "test" { diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index 005b2545f95..9c4bc1c7ee5 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -61,8 +61,9 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac TypeName: "aws_db_instances", }, { - Factory: DataSourceParameterGroup, + Factory: dataSourceParameterGroup, TypeName: "aws_db_parameter_group", + Name: "DB Parameter Group", }, { Factory: dataSourceProxy, From 37c5d9aa049ca2cc3fdbb2b3aedf682b1ae84344 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 12:02:25 -0400 Subject: [PATCH 16/24] r/aws_rds_reserved_instance: Migrate to AWS SDK for Go v2. --- internal/service/rds/consts.go | 6 +- internal/service/rds/exports_test.go | 2 + internal/service/rds/reserved_instance.go | 135 +++++++++++------- .../service/rds/reserved_instance_test.go | 23 ++- internal/service/rds/service_package_gen.go | 2 +- 5 files changed, 97 insertions(+), 71 deletions(-) diff --git a/internal/service/rds/consts.go b/internal/service/rds/consts.go index 045337bd217..299a9951288 100644 --- a/internal/service/rds/consts.go +++ b/internal/service/rds/consts.go @@ -321,9 +321,9 @@ const ( ) const ( - ReservedInstanceStateActive = "active" - ReservedInstanceStateRetired = "retired" - ReservedInstanceStatePaymentPending = "payment-pending" + reservedInstanceStateActive = "active" + reservedInstanceStateRetired = "retired" + reservedInstanceStatePaymentPending = "payment-pending" ) const ( diff --git a/internal/service/rds/exports_test.go b/internal/service/rds/exports_test.go index b6985d10656..f85e8ab3959 100644 --- a/internal/service/rds/exports_test.go +++ b/internal/service/rds/exports_test.go @@ -22,6 +22,7 @@ var ( ResourceProxyDefaultTargetGroup = resourceProxyDefaultTargetGroup ResourceProxyEndpoint = resourceProxyEndpoint ResourceProxyTarget = resourceProxyTarget + ResourceReservedInstance = resourceReservedInstance ResourceSnapshot = resourceSnapshot ResourceSnapshotCopy = resourceSnapshotCopy ResourceSubnetGroup = resourceSubnetGroup @@ -45,6 +46,7 @@ var ( FindEventSubscriptionByID = findEventSubscriptionByID FindIntegrationByARN = findIntegrationByARN FindOptionGroupByName = findOptionGroupByName + FindReservedDBInstanceByID = findReservedDBInstanceByID ListTags = listTags NewBlueGreenOrchestrator = newBlueGreenOrchestrator ParameterGroupModifyChunk = parameterGroupModifyChunk diff --git a/internal/service/rds/reserved_instance.go b/internal/service/rds/reserved_instance.go index f5121a4791a..89014e845ef 100644 --- a/internal/service/rds/reserved_instance.go +++ b/internal/service/rds/reserved_instance.go @@ -5,31 +5,29 @@ package rds import ( "context" - "fmt" + "log" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/rds" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds" + "github.com/aws/aws-sdk-go-v2/service/rds/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) -const ( - ResNameReservedInstance = "Reserved Instance" -) - // @SDKResource("aws_rds_reserved_instance", name="Reserved Instance") // @Tags(identifierAttribute="arn") // @Testing(tagsTest=false) -func ResourceReservedInstance() *schema.Resource { +func resourceReservedInstance() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceReservedInstanceCreate, ReadWithoutTimeout: resourceReservedInstanceRead, @@ -45,6 +43,7 @@ func ResourceReservedInstance() *schema.Resource { Update: schema.DefaultTimeout(10 * time.Minute), Delete: schema.DefaultTimeout(1 * time.Minute), }, + Schema: map[string]*schema.Schema{ names.AttrARN: { Type: schema.TypeString, @@ -136,30 +135,31 @@ func ResourceReservedInstance() *schema.Resource { func resourceReservedInstanceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) input := &rds.PurchaseReservedDBInstancesOfferingInput{ ReservedDBInstancesOfferingId: aws.String(d.Get("offering_id").(string)), - Tags: getTagsIn(ctx), + Tags: getTagsInV2(ctx), } if v, ok := d.Get(names.AttrInstanceCount).(int); ok && v > 0 { - input.DBInstanceCount = aws.Int64(int64(d.Get(names.AttrInstanceCount).(int))) + input.DBInstanceCount = aws.Int32(int32(d.Get(names.AttrInstanceCount).(int))) } if v, ok := d.Get("reservation_id").(string); ok && v != "" { input.ReservedDBInstanceId = aws.String(v) } - resp, err := conn.PurchaseReservedDBInstancesOfferingWithContext(ctx, input) + output, err := conn.PurchaseReservedDBInstancesOffering(ctx, input) + if err != nil { - return create.AppendDiagError(diags, names.RDS, create.ErrActionCreating, ResNameReservedInstance, fmt.Sprintf("offering_id: %s, reservation_id: %s", d.Get("offering_id").(string), d.Get("reservation_id").(string)), err) + return sdkdiag.AppendErrorf(diags, "creating RDS Reserved Instance: %s", err) } - d.SetId(aws.StringValue(resp.ReservedDBInstance.ReservedDBInstanceId)) + d.SetId(aws.ToString(output.ReservedDBInstance.ReservedDBInstanceId)) - if err := waitReservedInstanceCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return create.AppendDiagError(diags, names.RDS, create.ErrActionWaitingForCreation, ResNameReservedInstance, d.Id(), err) + if _, err := waitReservedInstanceCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for RDS Reserved Instance (%s) create: %s", d.Id(), err) } return append(diags, resourceReservedInstanceRead(ctx, d, meta)...) @@ -167,18 +167,18 @@ func resourceReservedInstanceCreate(ctx context.Context, d *schema.ResourceData, func resourceReservedInstanceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) - reservation, err := FindReservedDBInstanceByID(ctx, conn, d.Id()) + reservation, err := findReservedDBInstanceByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { - create.LogNotFoundRemoveState(names.RDS, create.ErrActionReading, ResNameReservedInstance, d.Id()) + log.Printf("[WARN] RDS Reserved Instance (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return create.AppendDiagError(diags, names.RDS, create.ErrActionReading, ResNameReservedInstance, d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading RDS Reserved Instance (%s): %s", d.Id(), err) } d.Set(names.AttrARN, reservation.ReservedDBInstanceArn) @@ -194,7 +194,7 @@ func resourceReservedInstanceRead(ctx context.Context, d *schema.ResourceData, m d.Set("product_description", reservation.ProductDescription) d.Set("recurring_charges", flattenRecurringCharges(reservation.RecurringCharges)) d.Set("reservation_id", reservation.ReservedDBInstanceId) - d.Set(names.AttrStartTime, (reservation.StartTime).Format(time.RFC3339)) + d.Set(names.AttrStartTime, reservation.StartTime.Format(time.RFC3339)) d.Set(names.AttrState, reservation.State) d.Set("usage_price", reservation.UsagePrice) @@ -206,38 +206,67 @@ func resourceReservedInstanceUpdate(ctx context.Context, d *schema.ResourceData, return resourceReservedInstanceRead(ctx, d, meta) } -func FindReservedDBInstanceByID(ctx context.Context, conn *rds.RDS, id string) (*rds.ReservedDBInstance, error) { +func findReservedDBInstanceByID(ctx context.Context, conn *rds.Client, id string) (*types.ReservedDBInstance, error) { input := &rds.DescribeReservedDBInstancesInput{ ReservedDBInstanceId: aws.String(id), } + output, err := findReservedDBInstance(ctx, conn, input, tfslices.PredicateTrue[*types.ReservedDBInstance]()) - output, err := conn.DescribeReservedDBInstancesWithContext(ctx, input) + if err != nil { + return nil, err + } - if tfawserr.ErrCodeEquals(err, rds.ErrCodeReservedDBInstanceNotFoundFault) { + // Eventual consistency check. + if aws.ToString(output.ReservedDBInstanceId) != id { return nil, &retry.NotFoundError{ - LastError: err, LastRequest: input, } } + return output, nil +} + +func findReservedDBInstance(ctx context.Context, conn *rds.Client, input *rds.DescribeReservedDBInstancesInput, filter tfslices.Predicate[*types.ReservedDBInstance]) (*types.ReservedDBInstance, error) { + output, err := findReservedDBInstances(ctx, conn, input, filter) + if err != nil { return nil, err } - if output == nil || len(output.ReservedDBInstances) == 0 || output.ReservedDBInstances[0] == nil { - return nil, tfresource.NewEmptyResultError(input) - } + return tfresource.AssertSingleValueResult(output) +} - if count := len(output.ReservedDBInstances); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) +func findReservedDBInstances(ctx context.Context, conn *rds.Client, input *rds.DescribeReservedDBInstancesInput, filter tfslices.Predicate[*types.ReservedDBInstance]) ([]types.ReservedDBInstance, error) { + var output []types.ReservedDBInstance + + pages := rds.NewDescribeReservedDBInstancesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*types.ReservedDBInstanceNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + for _, v := range page.ReservedDBInstances { + if filter(&v) { + output = append(output, v) + } + } } - return output.ReservedDBInstances[0], nil + return output, nil } -func statusReservedInstance(ctx context.Context, conn *rds.RDS, id string) retry.StateRefreshFunc { +func statusReservedInstance(ctx context.Context, conn *rds.Client, id string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindReservedDBInstanceByID(ctx, conn, id) + output, err := findReservedDBInstanceByID(ctx, conn, id) if tfresource.NotFound(err) { return nil, "", nil @@ -247,16 +276,14 @@ func statusReservedInstance(ctx context.Context, conn *rds.RDS, id string) retry return nil, "", err } - return output, aws.StringValue(output.State), nil + return output, aws.ToString(output.State), nil } } -func waitReservedInstanceCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) error { +func waitReservedInstanceCreated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.ReservedDBInstance, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{ - ReservedInstanceStatePaymentPending, - }, - Target: []string{ReservedInstanceStateActive}, + Pending: []string{reservedInstanceStatePaymentPending}, + Target: []string{reservedInstanceStateActive}, Refresh: statusReservedInstance(ctx, conn, id), NotFoundChecks: 5, Timeout: timeout, @@ -264,25 +291,29 @@ func waitReservedInstanceCreated(ctx context.Context, conn *rds.RDS, id string, Delay: 30 * time.Second, } - _, err := stateConf.WaitForStateContext(ctx) + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*types.ReservedDBInstance); ok { + return output, err + } - return err + return nil, err } -func flattenRecurringCharges(recurringCharges []*rds.RecurringCharge) []interface{} { - if len(recurringCharges) == 0 { +func flattenRecurringCharges(apiObjects []types.RecurringCharge) []interface{} { + if len(apiObjects) == 0 { return []interface{}{} } - var rawRecurringCharges []interface{} - for _, recurringCharge := range recurringCharges { - rawRecurringCharge := map[string]interface{}{ - "recurring_charge_amount": recurringCharge.RecurringChargeAmount, - "recurring_charge_frequency": aws.StringValue(recurringCharge.RecurringChargeFrequency), + var tfList []interface{} + for _, apiObject := range apiObjects { + tfMap := map[string]interface{}{ + "recurring_charge_amount": aws.ToFloat64(apiObject.RecurringChargeAmount), + "recurring_charge_frequency": aws.ToString(apiObject.RecurringChargeFrequency), } - rawRecurringCharges = append(rawRecurringCharges, rawRecurringCharge) + tfList = append(tfList, tfMap) } - return rawRecurringCharges + return tfList } diff --git a/internal/service/rds/reserved_instance_test.go b/internal/service/rds/reserved_instance_test.go index 43c2c77271e..9989dcc34ac 100644 --- a/internal/service/rds/reserved_instance_test.go +++ b/internal/service/rds/reserved_instance_test.go @@ -10,7 +10,7 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/service/rds" + "github.com/aws/aws-sdk-go-v2/service/rds/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -28,7 +28,7 @@ func TestAccRDSReservedInstance_basic(t *testing.T) { t.Skipf("Environment variable %s is not set to true", key) } - var reservation rds.ReservedDBInstance + var reservation types.ReservedDBInstance rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_reserved_instance.test" dataSourceName := "data.aws_rds_reserved_instance_offering.test" @@ -36,7 +36,7 @@ func TestAccRDSReservedInstance_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: nil, + CheckDestroy: acctest.CheckDestroyNoop, ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), Steps: []resource.TestStep{ { @@ -65,29 +65,22 @@ func TestAccRDSReservedInstance_basic(t *testing.T) { }) } -func testAccReservedInstanceExists(ctx context.Context, n string, reservation *rds.ReservedDBInstance) resource.TestCheckFunc { +func testAccReservedInstanceExists(ctx context.Context, n string, v *types.ReservedDBInstance) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) - rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No RDS Reserved Instance reservation id is set") - } + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) + + output, err := tfrds.FindReservedDBInstanceByID(ctx, conn, rs.Primary.ID) - resp, err := tfrds.FindReservedDBInstanceByID(ctx, conn, rs.Primary.ID) if err != nil { return err } - if resp == nil { - return fmt.Errorf("RDS Reserved Instance %q does not exist", rs.Primary.ID) - } - - *reservation = *resp + *v = *output return nil } diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index 9c4bc1c7ee5..c3944913c96 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -273,7 +273,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka TypeName: "aws_rds_global_cluster", }, { - Factory: ResourceReservedInstance, + Factory: resourceReservedInstance, TypeName: "aws_rds_reserved_instance", Name: "Reserved Instance", Tags: &types.ServicePackageResourceTags{ From 4355e155407d46a6fe14c786e4a0afc82679b29f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 12:16:47 -0400 Subject: [PATCH 17/24] internal/generate/namevaluesfilters: Actually generate. --- .../generate/namevaluesfilters/generate.go | 7 + .../generators/servicefilters/file.tmpl | 38 ++++++ .../generators/servicefilters/main.go | 128 +++++------------- .../namevaluesfilters/service_filters_gen.go | 16 +-- .../generators/servicefilters/main.go | 3 + 5 files changed, 91 insertions(+), 101 deletions(-) create mode 100644 internal/generate/namevaluesfilters/generate.go create mode 100644 internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl diff --git a/internal/generate/namevaluesfilters/generate.go b/internal/generate/namevaluesfilters/generate.go new file mode 100644 index 00000000000..f730a080648 --- /dev/null +++ b/internal/generate/namevaluesfilters/generate.go @@ -0,0 +1,7 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:generate go run ./generators/servicefilters/main.go +// ONLY generate directives and package declaration! Do not add anything else to this file. + +package namevaluesfilters diff --git a/internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl b/internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl new file mode 100644 index 00000000000..455e3b18df8 --- /dev/null +++ b/internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl @@ -0,0 +1,38 @@ +// Code generated by generators/servicefilters/main.go; DO NOT EDIT. + +package namevaluesfilters + +import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports + "github.com/aws/aws-sdk-go/aws" +{{- range .SliceServiceNames }} +{{- if eq . (. | FilterPackage) }} + "github.com/aws/aws-sdk-go/service/{{ . }}" +{{- end }} +{{- end }} +) + +// []*SERVICE.Filter handling +{{- range .SliceServiceNames }} + +// {{ . | Title }}Filters returns {{ . }} service filters. +func (filters NameValuesFilters) {{ . | Title }}Filters() []*{{ . | FilterPackage }}.{{ . | FilterType }} { + m := filters.Map() + + if len(m) == 0 { + return nil + } + + result := make([]*{{ . | FilterPackage }}.{{ . | FilterType }}, 0, len(m)) + + for k, v := range m { + filter := &{{ . | FilterPackage }}.{{ . | FilterType }}{ + {{ . | FilterTypeNameField }}: aws.String(k), + {{ . | FilterTypeValuesField }}: aws.StringSlice(v), + } + + result = append(result, filter) + } + + return result +} +{{- end }} diff --git a/internal/generate/namevaluesfilters/generators/servicefilters/main.go b/internal/generate/namevaluesfilters/generators/servicefilters/main.go index 1c734fc7e02..5d91e0d59c1 100644 --- a/internal/generate/namevaluesfilters/generators/servicefilters/main.go +++ b/internal/generate/namevaluesfilters/generators/servicefilters/main.go @@ -1,47 +1,51 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build generate // +build generate package main import ( - "bytes" - "go/format" - "log" - "os" + _ "embed" "sort" - "strings" "text/template" + "github.com/hashicorp/terraform-provider-aws/internal/generate/common" "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" ) -const filename = `service_filters_gen.go` - -// Representing types such as []*fsx.Filter, []*rds.Filter, ... -var sliceServiceNames = []string{ - "autoscaling", - "databasemigrationservice", - "docdb", - "elasticinference", - "elasticsearchservice", - "fsx", - "imagebuilder", - "licensemanager", - "neptune", - "rds", - "resourcegroupstaggingapi", - "route53resolver", -} - type TemplateData struct { SliceServiceNames []string } func main() { + const ( + filename = `service_filters_gen.go` + ) + g := common.NewGenerator() + + g.Infof("Generating internal/generate/namevaluesfilters/%s", filename) + + // Representing types such as []*fsx.Filter, []*rds.Filter, ... + sliceServiceNames := []string{ + "autoscaling", + "databasemigrationservice", + "docdb", + "elasticinference", + "elasticsearchservice", + "fsx", + "imagebuilder", + "licensemanager", + "neptune", + "rds", + "resourcegroupstaggingapi", + "route53resolver", + } // Always sort to reduce any potential generation churn sort.Strings(sliceServiceNames) - templateData := TemplateData{ + td := TemplateData{ SliceServiceNames: sliceServiceNames, } templateFuncMap := template.FuncMap{ @@ -49,80 +53,18 @@ func main() { "FilterType": namevaluesfilters.ServiceFilterType, "FilterTypeNameField": namevaluesfilters.ServiceFilterTypeNameField, "FilterTypeValuesField": namevaluesfilters.ServiceFilterTypeValuesField, - "Title": strings.Title, } - tmpl, err := template.New("servicefilters").Funcs(templateFuncMap).Parse(templateBody) + d := g.NewGoFileDestination(filename) - if err != nil { - log.Fatalf("error parsing template: %s", err) + if err := d.WriteTemplate("namevaluesfilters", tmpl, td, templateFuncMap); err != nil { + g.Fatalf("generating file (%s): %s", filename, err) } - var buffer bytes.Buffer - err = tmpl.Execute(&buffer, templateData) - - if err != nil { - log.Fatalf("error executing template: %s", err) - } - - generatedFileContents, err := format.Source(buffer.Bytes()) - - if err != nil { - log.Fatalf("error formatting generated file: %s", err) - } - - f, err := os.Create(filename) - - if err != nil { - log.Fatalf("error creating file (%s): %s", filename, err) - } - - defer f.Close() - - _, err = f.Write(generatedFileContents) - - if err != nil { - log.Fatalf("error writing to file (%s): %s", filename, err) + if err := d.Write(); err != nil { + g.Fatalf("generating file (%s): %s", filename, err) } } -var templateBody = ` -// Code generated by generators/servicefilters/main.go; DO NOT EDIT. - -package namevaluesfilters - -import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports - "github.com/aws/aws-sdk-go/aws" -{{- range .SliceServiceNames }} -{{- if eq . (. | FilterPackage) }} - "github.com/aws/aws-sdk-go/service/{{ . }}" -{{- end }} -{{- end }} -) - -// []*SERVICE.Filter handling -{{- range .SliceServiceNames }} - -// {{ . | Title }}Filters returns {{ . }} service filters. -func (filters NameValuesFilters) {{ . | Title }}Filters() []*{{ . | FilterPackage }}.{{ . | FilterType }} { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*{{ . | FilterPackage }}.{{ . | FilterType }}, 0, len(m)) - - for k, v := range m { - filter := &{{ . | FilterPackage }}.{{ . | FilterType }}{ - {{ . | FilterTypeNameField }}: aws.String(k), - {{ . | FilterTypeValuesField }}: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} -{{- end }} -` +//go:embed file.tmpl +var tmpl string diff --git a/internal/generate/namevaluesfilters/service_filters_gen.go b/internal/generate/namevaluesfilters/service_filters_gen.go index 737bce86a01..89a3d6eb8c6 100644 --- a/internal/generate/namevaluesfilters/service_filters_gen.go +++ b/internal/generate/namevaluesfilters/service_filters_gen.go @@ -20,8 +20,8 @@ import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports // []*SERVICE.Filter handling -// AutoScalingFilters returns autoscaling service filters. -func (filters NameValuesFilters) AutoScalingFilters() []*autoscaling.Filter { +// AutoscalingFilters returns autoscaling service filters. +func (filters NameValuesFilters) AutoscalingFilters() []*autoscaling.Filter { m := filters.Map() if len(m) == 0 { @@ -64,8 +64,8 @@ func (filters NameValuesFilters) DatabasemigrationserviceFilters() []*databasemi return result } -// DocDBFilters returns docdb service filters. -func (filters NameValuesFilters) DocDBFilters() []*docdb.Filter { +// DocdbFilters returns docdb service filters. +func (filters NameValuesFilters) DocdbFilters() []*docdb.Filter { m := filters.Map() if len(m) == 0 { @@ -130,8 +130,8 @@ func (filters NameValuesFilters) ElasticsearchserviceFilters() []*elasticsearchs return result } -// FSxFilters returns fsx service filters. -func (filters NameValuesFilters) FSxFilters() []*fsx.Filter { +// FsxFilters returns fsx service filters. +func (filters NameValuesFilters) FsxFilters() []*fsx.Filter { m := filters.Map() if len(m) == 0 { @@ -218,8 +218,8 @@ func (filters NameValuesFilters) NeptuneFilters() []*neptune.Filter { return result } -// RDSFilters returns rds service filters. -func (filters NameValuesFilters) RDSFilters() []*rds.Filter { +// RdsFilters returns rds service filters. +func (filters NameValuesFilters) RdsFilters() []*rds.Filter { m := filters.Map() if len(m) == 0 { diff --git a/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go b/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go index 3bda3ac4f50..ef1a6ebddd2 100644 --- a/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go +++ b/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build generate // +build generate From 4e108425469a7dbefa1546bcb9ed712e41a11378 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 12:30:36 -0400 Subject: [PATCH 18/24] internal/generate/namevaluesfilters: Use 'names.ProviderNameUpper' to generate function names. --- .../generators/servicefilters/file.tmpl | 4 +- .../generators/servicefilters/main.go | 11 +- .../namevaluesfilters/service_filters_gen.go | 219 +----------------- .../imagebuilder/components_data_source.go | 2 +- .../container_recipes_data_source.go | 2 +- ...distribution_configurations_data_source.go | 2 +- .../image_pipelines_data_source.go | 2 +- .../imagebuilder/image_recipes_data_source.go | 2 +- ...frastructure_configurations_data_source.go | 2 +- .../query_log_config_data_source.go | 2 +- 10 files changed, 17 insertions(+), 231 deletions(-) diff --git a/internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl b/internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl index 455e3b18df8..294a9b605dd 100644 --- a/internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl +++ b/internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl @@ -14,8 +14,8 @@ import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports // []*SERVICE.Filter handling {{- range .SliceServiceNames }} -// {{ . | Title }}Filters returns {{ . }} service filters. -func (filters NameValuesFilters) {{ . | Title }}Filters() []*{{ . | FilterPackage }}.{{ . | FilterType }} { +// {{ . | ProviderNameUpper }}Filters returns {{ . }} service filters. +func (filters NameValuesFilters) {{ . | ProviderNameUpper }}Filters() []*{{ . | FilterPackage }}.{{ . | FilterType }} { m := filters.Map() if len(m) == 0 { diff --git a/internal/generate/namevaluesfilters/generators/servicefilters/main.go b/internal/generate/namevaluesfilters/generators/servicefilters/main.go index 5d91e0d59c1..8220d1d336a 100644 --- a/internal/generate/namevaluesfilters/generators/servicefilters/main.go +++ b/internal/generate/namevaluesfilters/generators/servicefilters/main.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/generate/common" "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/names" ) type TemplateData struct { @@ -29,17 +30,8 @@ func main() { // Representing types such as []*fsx.Filter, []*rds.Filter, ... sliceServiceNames := []string{ - "autoscaling", - "databasemigrationservice", - "docdb", - "elasticinference", - "elasticsearchservice", - "fsx", "imagebuilder", - "licensemanager", - "neptune", "rds", - "resourcegroupstaggingapi", "route53resolver", } // Always sort to reduce any potential generation churn @@ -53,6 +45,7 @@ func main() { "FilterType": namevaluesfilters.ServiceFilterType, "FilterTypeNameField": namevaluesfilters.ServiceFilterTypeNameField, "FilterTypeValuesField": namevaluesfilters.ServiceFilterTypeValuesField, + "ProviderNameUpper": names.ProviderNameUpper, } d := g.NewGoFileDestination(filename) diff --git a/internal/generate/namevaluesfilters/service_filters_gen.go b/internal/generate/namevaluesfilters/service_filters_gen.go index 89a3d6eb8c6..3cb01862029 100644 --- a/internal/generate/namevaluesfilters/service_filters_gen.go +++ b/internal/generate/namevaluesfilters/service_filters_gen.go @@ -4,156 +4,15 @@ package namevaluesfilters import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/autoscaling" - "github.com/aws/aws-sdk-go/service/databasemigrationservice" - "github.com/aws/aws-sdk-go/service/docdb" - "github.com/aws/aws-sdk-go/service/elasticinference" - "github.com/aws/aws-sdk-go/service/elasticsearchservice" - "github.com/aws/aws-sdk-go/service/fsx" "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/aws/aws-sdk-go/service/licensemanager" - "github.com/aws/aws-sdk-go/service/neptune" "github.com/aws/aws-sdk-go/service/rds" - "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" "github.com/aws/aws-sdk-go/service/route53resolver" ) // []*SERVICE.Filter handling -// AutoscalingFilters returns autoscaling service filters. -func (filters NameValuesFilters) AutoscalingFilters() []*autoscaling.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*autoscaling.Filter, 0, len(m)) - - for k, v := range m { - filter := &autoscaling.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} - -// DatabasemigrationserviceFilters returns databasemigrationservice service filters. -func (filters NameValuesFilters) DatabasemigrationserviceFilters() []*databasemigrationservice.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*databasemigrationservice.Filter, 0, len(m)) - - for k, v := range m { - filter := &databasemigrationservice.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} - -// DocdbFilters returns docdb service filters. -func (filters NameValuesFilters) DocdbFilters() []*docdb.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*docdb.Filter, 0, len(m)) - - for k, v := range m { - filter := &docdb.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} - -// ElasticinferenceFilters returns elasticinference service filters. -func (filters NameValuesFilters) ElasticinferenceFilters() []*elasticinference.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*elasticinference.Filter, 0, len(m)) - - for k, v := range m { - filter := &elasticinference.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} - -// ElasticsearchserviceFilters returns elasticsearchservice service filters. -func (filters NameValuesFilters) ElasticsearchserviceFilters() []*elasticsearchservice.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*elasticsearchservice.Filter, 0, len(m)) - - for k, v := range m { - filter := &elasticsearchservice.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} - -// FsxFilters returns fsx service filters. -func (filters NameValuesFilters) FsxFilters() []*fsx.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*fsx.Filter, 0, len(m)) - - for k, v := range m { - filter := &fsx.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} - -// ImagebuilderFilters returns imagebuilder service filters. -func (filters NameValuesFilters) ImagebuilderFilters() []*imagebuilder.Filter { +// ImageBuilderFilters returns imagebuilder service filters. +func (filters NameValuesFilters) ImageBuilderFilters() []*imagebuilder.Filter { m := filters.Map() if len(m) == 0 { @@ -174,52 +33,8 @@ func (filters NameValuesFilters) ImagebuilderFilters() []*imagebuilder.Filter { return result } -// LicensemanagerFilters returns licensemanager service filters. -func (filters NameValuesFilters) LicensemanagerFilters() []*licensemanager.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*licensemanager.Filter, 0, len(m)) - - for k, v := range m { - filter := &licensemanager.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} - -// NeptuneFilters returns neptune service filters. -func (filters NameValuesFilters) NeptuneFilters() []*neptune.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*neptune.Filter, 0, len(m)) - - for k, v := range m { - filter := &neptune.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} - -// RdsFilters returns rds service filters. -func (filters NameValuesFilters) RdsFilters() []*rds.Filter { +// RDSFilters returns rds service filters. +func (filters NameValuesFilters) RDSFilters() []*rds.Filter { m := filters.Map() if len(m) == 0 { @@ -240,30 +55,8 @@ func (filters NameValuesFilters) RdsFilters() []*rds.Filter { return result } -// ResourcegroupstaggingapiFilters returns resourcegroupstaggingapi service filters. -func (filters NameValuesFilters) ResourcegroupstaggingapiFilters() []*resourcegroupstaggingapi.TagFilter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*resourcegroupstaggingapi.TagFilter, 0, len(m)) - - for k, v := range m { - filter := &resourcegroupstaggingapi.TagFilter{ - Key: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} - -// Route53resolverFilters returns route53resolver service filters. -func (filters NameValuesFilters) Route53resolverFilters() []*route53resolver.Filter { +// Route53ResolverFilters returns route53resolver service filters. +func (filters NameValuesFilters) Route53ResolverFilters() []*route53resolver.Filter { m := filters.Map() if len(m) == 0 { diff --git a/internal/service/imagebuilder/components_data_source.go b/internal/service/imagebuilder/components_data_source.go index c707acab6a2..70116e44a87 100644 --- a/internal/service/imagebuilder/components_data_source.go +++ b/internal/service/imagebuilder/components_data_source.go @@ -53,7 +53,7 @@ func dataSourceComponentsRead(ctx context.Context, d *schema.ResourceData, meta } if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImagebuilderFilters() + input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.ComponentVersion diff --git a/internal/service/imagebuilder/container_recipes_data_source.go b/internal/service/imagebuilder/container_recipes_data_source.go index 836ebfdf458..bc9054d519f 100644 --- a/internal/service/imagebuilder/container_recipes_data_source.go +++ b/internal/service/imagebuilder/container_recipes_data_source.go @@ -53,7 +53,7 @@ func dataSourceContainerRecipesRead(ctx context.Context, d *schema.ResourceData, } if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImagebuilderFilters() + input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.ContainerRecipeSummary diff --git a/internal/service/imagebuilder/distribution_configurations_data_source.go b/internal/service/imagebuilder/distribution_configurations_data_source.go index 513236a1cef..12c82e278a1 100644 --- a/internal/service/imagebuilder/distribution_configurations_data_source.go +++ b/internal/service/imagebuilder/distribution_configurations_data_source.go @@ -43,7 +43,7 @@ func dataSourceDistributionConfigurationsRead(ctx context.Context, d *schema.Res input := &imagebuilder.ListDistributionConfigurationsInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImagebuilderFilters() + input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.DistributionConfigurationSummary diff --git a/internal/service/imagebuilder/image_pipelines_data_source.go b/internal/service/imagebuilder/image_pipelines_data_source.go index 49fcb643ee5..bdb08294c9a 100644 --- a/internal/service/imagebuilder/image_pipelines_data_source.go +++ b/internal/service/imagebuilder/image_pipelines_data_source.go @@ -43,7 +43,7 @@ func dataSourceImagePipelinesRead(ctx context.Context, d *schema.ResourceData, m input := &imagebuilder.ListImagePipelinesInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImagebuilderFilters() + input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.ImagePipeline diff --git a/internal/service/imagebuilder/image_recipes_data_source.go b/internal/service/imagebuilder/image_recipes_data_source.go index 862c84d9df3..e0bf7c01e66 100644 --- a/internal/service/imagebuilder/image_recipes_data_source.go +++ b/internal/service/imagebuilder/image_recipes_data_source.go @@ -53,7 +53,7 @@ func dataSourceImageRecipesRead(ctx context.Context, d *schema.ResourceData, met } if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImagebuilderFilters() + input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.ImageRecipeSummary diff --git a/internal/service/imagebuilder/infrastructure_configurations_data_source.go b/internal/service/imagebuilder/infrastructure_configurations_data_source.go index 0e392a8342e..005a9ef2b55 100644 --- a/internal/service/imagebuilder/infrastructure_configurations_data_source.go +++ b/internal/service/imagebuilder/infrastructure_configurations_data_source.go @@ -43,7 +43,7 @@ func dataSourceInfrastructureConfigurationsRead(ctx context.Context, d *schema.R input := &imagebuilder.ListInfrastructureConfigurationsInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImagebuilderFilters() + input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.InfrastructureConfigurationSummary diff --git a/internal/service/route53resolver/query_log_config_data_source.go b/internal/service/route53resolver/query_log_config_data_source.go index ac8dbc1eb10..08ada57b2ea 100644 --- a/internal/service/route53resolver/query_log_config_data_source.go +++ b/internal/service/route53resolver/query_log_config_data_source.go @@ -69,7 +69,7 @@ func dataSourceQueryLogConfigRead(ctx context.Context, d *schema.ResourceData, m input := &route53resolver.ListResolverQueryLogConfigsInput{} if v, ok := d.GetOk(names.AttrFilter); ok && v.(*schema.Set).Len() > 0 { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).Route53resolverFilters() + input.Filters = namevaluesfilters.New(v.(*schema.Set)).Route53ResolverFilters() } var configs []*route53resolver.ResolverQueryLogConfig From cf43e20ab589eaca83dd6486413e996e561c8ceb Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 12:33:02 -0400 Subject: [PATCH 19/24] internal/generate/namevaluesfiltersv2: Use 'names.ProviderNameUpper' to generate function names. --- .../generators/servicefilters/file.tmpl | 4 ++-- .../namevaluesfiltersv2/generators/servicefilters/main.go | 2 ++ .../generate/namevaluesfiltersv2/service_filters_gen.go | 8 ++++---- internal/service/rds/engine_version_data_source.go | 2 +- internal/service/secretsmanager/secrets_data_source.go | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl b/internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl index d38dd5abbd0..2275d611c23 100644 --- a/internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl +++ b/internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl @@ -12,8 +12,8 @@ import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports // []*SERVICE.Filter handling {{- range .SliceServiceNames }} -// {{ . | Title }}Filters returns {{ . }} service filters. -func (filters NameValuesFilters) {{ . | Title }}Filters() []{{ . | FilterPackagePrefix }}.{{ . | FilterType }} { +// {{ . | ProviderNameUpper }}Filters returns {{ . }} service filters. +func (filters NameValuesFilters) {{ . | ProviderNameUpper }}Filters() []{{ . | FilterPackagePrefix }}.{{ . | FilterType }} { m := filters.Map() if len(m) == 0 { diff --git a/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go b/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go index ef1a6ebddd2..a98b6686930 100644 --- a/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go +++ b/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/generate/common" "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfiltersv2" + "github.com/hashicorp/terraform-provider-aws/names" ) type TemplateData struct { @@ -45,6 +46,7 @@ func main() { "FilterTypeNameField": namevaluesfiltersv2.ServiceFilterTypeNameField, "FilterTypeNameFunc": namevaluesfiltersv2.ServiceFilterTypeNameFunc, "FilterTypeValuesField": namevaluesfiltersv2.ServiceFilterTypeValuesField, + "ProviderNameUpper": names.ProviderNameUpper, } d := g.NewGoFileDestination(filename) diff --git a/internal/generate/namevaluesfiltersv2/service_filters_gen.go b/internal/generate/namevaluesfiltersv2/service_filters_gen.go index 7b35dd2f4b0..33029b62729 100644 --- a/internal/generate/namevaluesfiltersv2/service_filters_gen.go +++ b/internal/generate/namevaluesfiltersv2/service_filters_gen.go @@ -10,8 +10,8 @@ import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports // []*SERVICE.Filter handling -// RdsFilters returns rds service filters. -func (filters NameValuesFilters) RdsFilters() []rdstypes.Filter { +// RDSFilters returns rds service filters. +func (filters NameValuesFilters) RDSFilters() []rdstypes.Filter { m := filters.Map() if len(m) == 0 { @@ -32,8 +32,8 @@ func (filters NameValuesFilters) RdsFilters() []rdstypes.Filter { return result } -// SecretsmanagerFilters returns secretsmanager service filters. -func (filters NameValuesFilters) SecretsmanagerFilters() []secretsmanagertypes.Filter { +// SecretsManagerFilters returns secretsmanager service filters. +func (filters NameValuesFilters) SecretsManagerFilters() []secretsmanagertypes.Filter { m := filters.Map() if len(m) == 0 { diff --git a/internal/service/rds/engine_version_data_source.go b/internal/service/rds/engine_version_data_source.go index 2623950d432..f59563e52f1 100644 --- a/internal/service/rds/engine_version_data_source.go +++ b/internal/service/rds/engine_version_data_source.go @@ -175,7 +175,7 @@ func dataSourceEngineVersionRead(ctx context.Context, d *schema.ResourceData, me } if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).RdsFilters() + input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).RDSFilters() } if v, ok := d.GetOk("parameter_group_family"); ok { diff --git a/internal/service/secretsmanager/secrets_data_source.go b/internal/service/secretsmanager/secrets_data_source.go index 112d7db328c..b9c9bcfe370 100644 --- a/internal/service/secretsmanager/secrets_data_source.go +++ b/internal/service/secretsmanager/secrets_data_source.go @@ -45,7 +45,7 @@ func dataSourceSecretsRead(ctx context.Context, d *schema.ResourceData, meta int input := &secretsmanager.ListSecretsInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).SecretsmanagerFilters() + input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).SecretsManagerFilters() } var results []types.SecretListEntry From bef379e5bc68c00c81fcc9bfaf26f102ca189f6f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 14:34:17 -0400 Subject: [PATCH 20/24] namevaluesfilters: Move files around. --- internal/generate/namevaluesfilters/README.md | 24 --- .../generators/servicefilters/README.md | 42 ---- .../servicefilters => v1}/file.tmpl | 2 +- .../{generators/servicefilters => v1}/main.go | 12 +- .../v2}/file.tmpl | 2 +- .../v2}/main.go | 4 +- .../generate/namevaluesfiltersv2/README.md | 24 --- .../generators/servicefilters/README.md | 42 ---- .../name_values_filters.go | 128 ------------ .../name_values_filters_test.go | 184 ------------------ .../namevaluesfilters/name_values_filters.go | 0 .../name_values_filters_test.go | 0 internal/namevaluesfilters/v1/README.md | 5 + .../v1}/generate.go | 4 +- .../v1/name_values_filters.go | 16 ++ .../v1}/service_filters_gen.go | 2 +- .../v1}/service_generation_customizations.go | 2 +- internal/namevaluesfilters/v2/README.md | 5 + .../v2}/generate.go | 4 +- .../v2/name_values_filters.go | 16 ++ .../v2}/service_filters_gen.go | 2 +- .../v2}/service_generation_customizations.go | 2 +- .../imagebuilder/components_data_source.go | 5 +- .../container_recipes_data_source.go | 5 +- ...distribution_configurations_data_source.go | 5 +- .../image_pipelines_data_source.go | 5 +- .../imagebuilder/image_recipes_data_source.go | 5 +- ...frastructure_configurations_data_source.go | 5 +- internal/service/rds/clusters_data_source.go | 5 +- .../service/rds/engine_version_data_source.go | 5 +- internal/service/rds/instances_data_source.go | 5 +- .../query_log_config_data_source.go | 5 +- .../secretsmanager/secrets_data_source.go | 5 +- 33 files changed, 93 insertions(+), 484 deletions(-) delete mode 100644 internal/generate/namevaluesfilters/README.md delete mode 100644 internal/generate/namevaluesfilters/generators/servicefilters/README.md rename internal/generate/namevaluesfilters/{generators/servicefilters => v1}/file.tmpl (97%) rename internal/generate/namevaluesfilters/{generators/servicefilters => v1}/main.go (72%) rename internal/generate/{namevaluesfiltersv2/generators/servicefilters => namevaluesfilters/v2}/file.tmpl (96%) rename internal/generate/{namevaluesfiltersv2/generators/servicefilters => namevaluesfilters/v2}/main.go (90%) delete mode 100644 internal/generate/namevaluesfiltersv2/README.md delete mode 100644 internal/generate/namevaluesfiltersv2/generators/servicefilters/README.md delete mode 100644 internal/generate/namevaluesfiltersv2/name_values_filters.go delete mode 100644 internal/generate/namevaluesfiltersv2/name_values_filters_test.go rename internal/{generate => }/namevaluesfilters/name_values_filters.go (100%) rename internal/{generate => }/namevaluesfilters/name_values_filters_test.go (100%) create mode 100644 internal/namevaluesfilters/v1/README.md rename internal/{generate/namevaluesfilters => namevaluesfilters/v1}/generate.go (66%) create mode 100644 internal/namevaluesfilters/v1/name_values_filters.go rename internal/{generate/namevaluesfilters => namevaluesfilters/v1}/service_filters_gen.go (98%) rename internal/{generate/namevaluesfilters => namevaluesfilters/v1}/service_generation_customizations.go (97%) create mode 100644 internal/namevaluesfilters/v2/README.md rename internal/{generate/namevaluesfiltersv2 => namevaluesfilters/v2}/generate.go (65%) create mode 100644 internal/namevaluesfilters/v2/name_values_filters.go rename internal/{generate/namevaluesfiltersv2 => namevaluesfilters/v2}/service_filters_gen.go (97%) rename internal/{generate/namevaluesfiltersv2 => namevaluesfilters/v2}/service_generation_customizations.go (98%) diff --git a/internal/generate/namevaluesfilters/README.md b/internal/generate/namevaluesfilters/README.md deleted file mode 100644 index 757e5294809..00000000000 --- a/internal/generate/namevaluesfilters/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# namevaluesfilters - -The `namevaluesfilters` package is designed to provide a consistent interface for handling AWS resource filtering. - -This package implements a single `NameValuesFilters` type, which covers all filter handling logic, such as merging filters, via functions on the single type. The underlying implementation is compatible with Go operations such as `len()`. - -Full documentation for this package can be found on [GoDoc](https://godoc.org/github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters). - -Many AWS Go SDK services that support resource filtering have their service-specific Go type conversion functions to and from `NameValuesFilters` code generated. Converting from `NameValuesFilters` to AWS Go SDK types is done via `{SERVICE}Filters()` functions on the type. For more information about this code generation, see the [`generators/servicefilters` README](generators/servicefilters/README.md). - -Any filtering functions that cannot be generated should be hand implemented in a service-specific source file and follow the format of similar generated code wherever possible. The first line of the source file should be `// +build !generate`. This prevents the file's inclusion during the code generation phase. - -## Code Structure - -```text -internal/generate/namevaluesfilters -├── generators -│ └── servicefilters (generates service_filters_gen.go) -├── name_values_filters_test.go (unit tests for core logic) -├── name_values_filters.go (core logic) -├── service_generation_customizations.go (shared AWS Go SDK service customizations for generators) -├── service_filters_gen.go (generated AWS Go SDK service conversion functions) -└── _filters.go (any service-specific functions that cannot be generated) -``` diff --git a/internal/generate/namevaluesfilters/generators/servicefilters/README.md b/internal/generate/namevaluesfilters/generators/servicefilters/README.md deleted file mode 100644 index e86cf2585a0..00000000000 --- a/internal/generate/namevaluesfilters/generators/servicefilters/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# servicefilters - -This package contains a code generator to consistently handle the various AWS Go SDK service implementations for converting service filter types to/from `NameValuesFilters`. Not all AWS Go SDK services that support filters are generated in this manner. - -To run this code generator, execute `go generate ./...` from the root of the repository. The general workflow for the generator is: - -- Generate Go file contents via template from local variables and functions -- Go format file contents -- Write file contents to `service_filters_gen.go` file - -## Example Output - -```go -// DocDBFilters returns docdb service filters. -func (filters NameValuesFilters) DocDBFilters() []*docdb.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*docdb.Filter, 0, len(m)) - - for k, v := range m { - filter := &docdb.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} -``` - -## Implementing a New Generated Service - -- In `main.go`: Add service name, e.g. `docdb`, to one of the implementation handlers - - Use `sliceServiceNames` if the AWS Go SDK service implements a specific Go type such as `Filter` -- Run `go generate ./...` (or `make gen`) from the root of the repository to regenerate the code -- Run `go test ./...` (or `make test`) from the root of the repository to ensure the generated code compiles diff --git a/internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl b/internal/generate/namevaluesfilters/v1/file.tmpl similarity index 97% rename from internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl rename to internal/generate/namevaluesfilters/v1/file.tmpl index 294a9b605dd..7d1c17124d5 100644 --- a/internal/generate/namevaluesfilters/generators/servicefilters/file.tmpl +++ b/internal/generate/namevaluesfilters/v1/file.tmpl @@ -1,6 +1,6 @@ // Code generated by generators/servicefilters/main.go; DO NOT EDIT. -package namevaluesfilters +package v1 import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports "github.com/aws/aws-sdk-go/aws" diff --git a/internal/generate/namevaluesfilters/generators/servicefilters/main.go b/internal/generate/namevaluesfilters/v1/main.go similarity index 72% rename from internal/generate/namevaluesfilters/generators/servicefilters/main.go rename to internal/generate/namevaluesfilters/v1/main.go index 8220d1d336a..88f2a12033b 100644 --- a/internal/generate/namevaluesfilters/generators/servicefilters/main.go +++ b/internal/generate/namevaluesfilters/v1/main.go @@ -12,7 +12,7 @@ import ( "text/template" "github.com/hashicorp/terraform-provider-aws/internal/generate/common" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -26,7 +26,7 @@ func main() { ) g := common.NewGenerator() - g.Infof("Generating internal/generate/namevaluesfilters/%s", filename) + g.Infof("Generating internal/namevaluesfilters/v1/%s", filename) // Representing types such as []*fsx.Filter, []*rds.Filter, ... sliceServiceNames := []string{ @@ -41,10 +41,10 @@ func main() { SliceServiceNames: sliceServiceNames, } templateFuncMap := template.FuncMap{ - "FilterPackage": namevaluesfilters.ServiceFilterPackage, - "FilterType": namevaluesfilters.ServiceFilterType, - "FilterTypeNameField": namevaluesfilters.ServiceFilterTypeNameField, - "FilterTypeValuesField": namevaluesfilters.ServiceFilterTypeValuesField, + "FilterPackage": namevaluesfiltersv1.ServiceFilterPackage, + "FilterType": namevaluesfiltersv1.ServiceFilterType, + "FilterTypeNameField": namevaluesfiltersv1.ServiceFilterTypeNameField, + "FilterTypeValuesField": namevaluesfiltersv1.ServiceFilterTypeValuesField, "ProviderNameUpper": names.ProviderNameUpper, } diff --git a/internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl b/internal/generate/namevaluesfilters/v2/file.tmpl similarity index 96% rename from internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl rename to internal/generate/namevaluesfilters/v2/file.tmpl index 2275d611c23..42543ef94e0 100644 --- a/internal/generate/namevaluesfiltersv2/generators/servicefilters/file.tmpl +++ b/internal/generate/namevaluesfilters/v2/file.tmpl @@ -1,6 +1,6 @@ // Code generated by generators/servicefilters/main.go; DO NOT EDIT. -package namevaluesfiltersv2 +package v2 import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports "github.com/aws/aws-sdk-go-v2/aws" diff --git a/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go b/internal/generate/namevaluesfilters/v2/main.go similarity index 90% rename from internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go rename to internal/generate/namevaluesfilters/v2/main.go index a98b6686930..bb0eadba131 100644 --- a/internal/generate/namevaluesfiltersv2/generators/servicefilters/main.go +++ b/internal/generate/namevaluesfilters/v2/main.go @@ -12,7 +12,7 @@ import ( "text/template" "github.com/hashicorp/terraform-provider-aws/internal/generate/common" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfiltersv2" + namevaluesfiltersv2 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v2" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -26,7 +26,7 @@ func main() { ) g := common.NewGenerator() - g.Infof("Generating internal/generate/namevaluesfiltersv2/%s", filename) + g.Infof("Generating internal/namevaluesfilters/v2/%s", filename) // Representing types such as []*ec2.Filter, []*rds.Filter, ... sliceServiceNames := []string{ diff --git a/internal/generate/namevaluesfiltersv2/README.md b/internal/generate/namevaluesfiltersv2/README.md deleted file mode 100644 index 73e80758c4a..00000000000 --- a/internal/generate/namevaluesfiltersv2/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# namevaluesfiltersv2 - -The `namevaluesfiltersv2` package is designed to provide a consistent interface for handling AWS resource filtering for AWS SDK for Go v2. - -This package implements a single `NameValuesFilters` type, which covers all filter handling logic, such as merging filters, via functions on the single type. The underlying implementation is compatible with Go operations such as `len()`. - -Full documentation for this package can be found on [GoDoc](https://godoc.org/github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfiltersv2). - -Many AWS Go SDK v2 services that support resource filtering have their service-specific Go type conversion functions to and from `NameValuesFilters` code generated. Converting from `NameValuesFilters` to AWS Go SDK v2 types is done via `{SERVICE}Filters()` functions on the type. For more information about this code generation, see the [`generators/servicefilters` README](generators/servicefilters/README.md). - -Any filtering functions that cannot be generated should be hand implemented in a service-specific source file and follow the format of similar generated code wherever possible. The first line of the source file should be `// +build !generate`. This prevents the file's inclusion during the code generation phase. - -## Code Structure - -```text -internal/generate/namevaluesfiltersv2 -├── generators -│ └── servicefilters (generates service_filters_gen.go) -├── name_values_filters_test.go (unit tests for core logic) -├── name_values_filters.go (core logic) -├── service_generation_customizations.go (shared AWS Go SDK service customizations for generators) -├── service_filters_gen.go (generated AWS Go SDK service conversion functions) -└── _filters.go (any service-specific functions that cannot be generated) -``` diff --git a/internal/generate/namevaluesfiltersv2/generators/servicefilters/README.md b/internal/generate/namevaluesfiltersv2/generators/servicefilters/README.md deleted file mode 100644 index e86cf2585a0..00000000000 --- a/internal/generate/namevaluesfiltersv2/generators/servicefilters/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# servicefilters - -This package contains a code generator to consistently handle the various AWS Go SDK service implementations for converting service filter types to/from `NameValuesFilters`. Not all AWS Go SDK services that support filters are generated in this manner. - -To run this code generator, execute `go generate ./...` from the root of the repository. The general workflow for the generator is: - -- Generate Go file contents via template from local variables and functions -- Go format file contents -- Write file contents to `service_filters_gen.go` file - -## Example Output - -```go -// DocDBFilters returns docdb service filters. -func (filters NameValuesFilters) DocDBFilters() []*docdb.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*docdb.Filter, 0, len(m)) - - for k, v := range m { - filter := &docdb.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} -``` - -## Implementing a New Generated Service - -- In `main.go`: Add service name, e.g. `docdb`, to one of the implementation handlers - - Use `sliceServiceNames` if the AWS Go SDK service implements a specific Go type such as `Filter` -- Run `go generate ./...` (or `make gen`) from the root of the repository to regenerate the code -- Run `go test ./...` (or `make test`) from the root of the repository to ensure the generated code compiles diff --git a/internal/generate/namevaluesfiltersv2/name_values_filters.go b/internal/generate/namevaluesfiltersv2/name_values_filters.go deleted file mode 100644 index 2bba6c56ced..00000000000 --- a/internal/generate/namevaluesfiltersv2/name_values_filters.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package namevaluesfiltersv2 - -import ( - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -// NameValuesFilters is a standard implementation for AWS resource filters. -// The AWS Go SDK is split into multiple service packages, each service with -// its own Go struct type representing a resource filter. To standardize logic -// across all these Go types, we convert them into this Go type. -type NameValuesFilters map[string][]string - -// Add adds missing and updates existing filters from common Terraform Provider SDK types. -// Supports map[string]string, map[string][]string, *schema.Set. -func (filters NameValuesFilters) Add(i interface{}) NameValuesFilters { - switch value := i.(type) { - case map[string]string: - for name, v := range value { - if values, ok := filters[name]; ok { - filters[name] = append(values, v) - } else { - values = []string{v} - filters[name] = values - } - } - - case map[string][]string: - // We can't use fallthrough here, so recurse. - return filters.Add(NameValuesFilters(value)) - - case NameValuesFilters: - for name, vs := range value { - if values, ok := filters[name]; ok { - filters[name] = append(values, vs...) - } else { - values = make([]string, len(vs)) - copy(values, vs) - filters[name] = values - } - } - - case *schema.Set: - // The set of filters described by Schema(). - for _, filter := range value.List() { - m := filter.(map[string]interface{}) - name := m["name"].(string) - - for _, v := range m["values"].(*schema.Set).List() { - if values, ok := filters[name]; ok { - filters[name] = append(values, v.(string)) - } else { - values = []string{v.(string)} - filters[name] = values - } - } - } - } - - return filters -} - -// Map returns filter names mapped to their values. -// Duplicate values are eliminated and empty values removed. -func (filters NameValuesFilters) Map() map[string][]string { - result := make(map[string][]string) - - for k, v := range filters { - targetValues := make([]string, 0) - - SOURCE_VALUES: - for _, sourceValue := range v { - if sourceValue == "" { - continue - } - - for _, targetValue := range targetValues { - if sourceValue == targetValue { - continue SOURCE_VALUES - } - } - - targetValues = append(targetValues, sourceValue) - } - - if len(targetValues) == 0 { - continue - } - - result[k] = targetValues - } - - return result -} - -// New creates NameValuesFilters from common Terraform Provider SDK types. -// Supports map[string]string, map[string][]string, *schema.Set. -func New(i interface{}) NameValuesFilters { - return make(NameValuesFilters).Add(i) -} - -// Schema returns a *schema.Schema that represents a set of custom filtering criteria -// that a user can specify as input to a data source. -// It is conventional for an attribute of this type to be included as a top-level attribute called "filter". -func Schema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - - "values": { - Type: schema.TypeSet, - Required: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - }, - } -} diff --git a/internal/generate/namevaluesfiltersv2/name_values_filters_test.go b/internal/generate/namevaluesfiltersv2/name_values_filters_test.go deleted file mode 100644 index 5e98ab0ba72..00000000000 --- a/internal/generate/namevaluesfiltersv2/name_values_filters_test.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package namevaluesfiltersv2_test - -import ( - "reflect" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" -) - -func TestNameValuesFiltersMap(t *testing.T) { - t.Parallel() - - testCases := []struct { - name string - filters namevaluesfilters.NameValuesFilters - want map[string][]string - }{ - { - name: "empty", - filters: namevaluesfilters.New(map[string][]string{}), - want: map[string][]string{}, - }, - { - name: "empty_strings", - filters: namevaluesfilters.New(map[string][]string{ - "name1": {""}, - "name2": {"", ""}, - }), - want: map[string][]string{}, - }, - { - name: "duplicates", - filters: namevaluesfilters.New(map[string][]string{ - "name1": {"value1"}, - "name2": {"value2a", "value2b", "", "value2a", "value2c", "value2c"}, - }), - want: map[string][]string{ - "name1": {"value1"}, - "name2": {"value2a", "value2b", "value2c"}, - }, - }, - } - - for _, testCase := range testCases { - testCase := testCase - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - - got := testCase.filters.Map() - - testNameValuesFiltersVerifyMap(t, got, testCase.want) - }) - } -} - -func TestNameValuesFiltersAdd(t *testing.T) { - t.Parallel() - - testCases := []struct { - name string - filters namevaluesfilters.NameValuesFilters - add interface{} - want map[string][]string - }{ - { - name: "empty", - filters: namevaluesfilters.New(map[string][]string{}), - add: nil, - want: map[string][]string{}, - }, - { - name: "add_all", - filters: namevaluesfilters.New(map[string]string{ - "name1": "value1", - "name2": "value2", - "name3": "value3", - }), - add: namevaluesfilters.New(map[string][]string{ - "name4": {"value4a", "value4b"}, - "name5": {"value5"}, - "name6": {"value6a", "value6b", "value6c"}, - }), - want: map[string][]string{ - "name1": {"value1"}, - "name2": {"value2"}, - "name3": {"value3"}, - "name4": {"value4a", "value4b"}, - "name5": {"value5"}, - "name6": {"value6a", "value6b", "value6c"}, - }, - }, - { - name: "mixed", - filters: namevaluesfilters.New(map[string][]string{ - "name1": {"value1a"}, - "name2": {"value2a", "value2b"}, - }), - add: map[string]string{ - "name1": "value1b", - "name3": "value3", - }, - want: map[string][]string{ - "name1": {"value1a", "value1b"}, - "name2": {"value2a", "value2b"}, - "name3": {"value3"}, - }, - }, - { - name: "from_set", - filters: namevaluesfilters.New(schema.NewSet(testNameValuesFiltersHashSet, []interface{}{ - map[string]interface{}{ - "name": "name1", - "values": schema.NewSet(schema.HashString, []interface{}{ - "value1", - }), - }, - map[string]interface{}{ - "name": "name2", - "values": schema.NewSet(schema.HashString, []interface{}{ - "value2a", - "value2b", - }), - }, - map[string]interface{}{ - "name": "name3", - "values": schema.NewSet(schema.HashString, []interface{}{ - "value3", - }), - }, - })), - add: map[string][]string{ - "name1": {"value1"}, - "name2": {"value2c"}, - }, - want: map[string][]string{ - "name1": {"value1"}, - "name2": {"value2a", "value2b", "value2c"}, - "name3": {"value3"}, - }, - }, - } - - for _, testCase := range testCases { - testCase := testCase - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - - got := testCase.filters.Add(testCase.add) - - testNameValuesFiltersVerifyMap(t, got.Map(), testCase.want) - }) - } -} - -func testNameValuesFiltersVerifyMap(t *testing.T, got map[string][]string, want map[string][]string) { - for k, wantV := range want { - gotV, ok := got[k] - - if !ok { - t.Errorf("want missing name: %s", k) - continue - } - - if !reflect.DeepEqual(gotV, wantV) { - t.Errorf("got name (%s) values %s; want values %s", k, gotV, wantV) - } - } - - for k := range got { - if _, ok := want[k]; !ok { - t.Errorf("got extra name: %s", k) - } - } -} - -func testNameValuesFiltersHashSet(v interface{}) int { - m := v.(map[string]interface{}) - return create.StringHashcode(m["name"].(string)) -} diff --git a/internal/generate/namevaluesfilters/name_values_filters.go b/internal/namevaluesfilters/name_values_filters.go similarity index 100% rename from internal/generate/namevaluesfilters/name_values_filters.go rename to internal/namevaluesfilters/name_values_filters.go diff --git a/internal/generate/namevaluesfilters/name_values_filters_test.go b/internal/namevaluesfilters/name_values_filters_test.go similarity index 100% rename from internal/generate/namevaluesfilters/name_values_filters_test.go rename to internal/namevaluesfilters/name_values_filters_test.go diff --git a/internal/namevaluesfilters/v1/README.md b/internal/namevaluesfilters/v1/README.md new file mode 100644 index 00000000000..3793f5a824f --- /dev/null +++ b/internal/namevaluesfilters/v1/README.md @@ -0,0 +1,5 @@ +# namevaluesfilters + +The `namevaluesfilters/v1` package is designed to provide a consistent interface for handling AWS resource filtering with AWS SDK for Go v1. + +This package implements a single `NameValuesFilters` type, which covers all filter handling logic, such as merging filters, via functions on the single type. The underlying implementation is compatible with Go operations such as `len()`. diff --git a/internal/generate/namevaluesfilters/generate.go b/internal/namevaluesfilters/v1/generate.go similarity index 66% rename from internal/generate/namevaluesfilters/generate.go rename to internal/namevaluesfilters/v1/generate.go index f730a080648..e5639504819 100644 --- a/internal/generate/namevaluesfilters/generate.go +++ b/internal/namevaluesfilters/v1/generate.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//go:generate go run ./generators/servicefilters/main.go +//go:generate go run ../../generate/namevaluesfilters/v1/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. -package namevaluesfilters +package v1 diff --git a/internal/namevaluesfilters/v1/name_values_filters.go b/internal/namevaluesfilters/v1/name_values_filters.go new file mode 100644 index 00000000000..c8b81fabb46 --- /dev/null +++ b/internal/namevaluesfilters/v1/name_values_filters.go @@ -0,0 +1,16 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1 + +import ( + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" +) + +type NameValuesFilters struct { + namevaluesfilters.NameValuesFilters +} + +func New(i interface{}) NameValuesFilters { + return NameValuesFilters{NameValuesFilters: namevaluesfilters.New(i)} +} diff --git a/internal/generate/namevaluesfilters/service_filters_gen.go b/internal/namevaluesfilters/v1/service_filters_gen.go similarity index 98% rename from internal/generate/namevaluesfilters/service_filters_gen.go rename to internal/namevaluesfilters/v1/service_filters_gen.go index 3cb01862029..bab79ede734 100644 --- a/internal/generate/namevaluesfilters/service_filters_gen.go +++ b/internal/namevaluesfilters/v1/service_filters_gen.go @@ -1,6 +1,6 @@ // Code generated by generators/servicefilters/main.go; DO NOT EDIT. -package namevaluesfilters +package v1 import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports "github.com/aws/aws-sdk-go/aws" diff --git a/internal/generate/namevaluesfilters/service_generation_customizations.go b/internal/namevaluesfilters/v1/service_generation_customizations.go similarity index 97% rename from internal/generate/namevaluesfilters/service_generation_customizations.go rename to internal/namevaluesfilters/v1/service_generation_customizations.go index 0f57d9385e2..9af370ab50b 100644 --- a/internal/generate/namevaluesfilters/service_generation_customizations.go +++ b/internal/namevaluesfilters/v1/service_generation_customizations.go @@ -3,7 +3,7 @@ // This file contains code generation customizations for each AWS Go SDK service. -package namevaluesfilters +package v1 // ServiceFilterPackage determines the service filter type package. func ServiceFilterPackage(serviceName string) string { diff --git a/internal/namevaluesfilters/v2/README.md b/internal/namevaluesfilters/v2/README.md new file mode 100644 index 00000000000..cf3527987a2 --- /dev/null +++ b/internal/namevaluesfilters/v2/README.md @@ -0,0 +1,5 @@ +# namevaluesfiltersv2 + +The `namevaluesfilters/v2` package is designed to provide a consistent interface for handling AWS resource filtering with AWS SDK for Go v2. + +This package implements a single `NameValuesFilters` type, which covers all filter handling logic, such as merging filters, via functions on the single type. The underlying implementation is compatible with Go operations such as `len()`. diff --git a/internal/generate/namevaluesfiltersv2/generate.go b/internal/namevaluesfilters/v2/generate.go similarity index 65% rename from internal/generate/namevaluesfiltersv2/generate.go rename to internal/namevaluesfilters/v2/generate.go index e9c3cc60b5b..b990aefdc7f 100644 --- a/internal/generate/namevaluesfiltersv2/generate.go +++ b/internal/namevaluesfilters/v2/generate.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//go:generate go run ./generators/servicefilters/main.go +//go:generate go run ../../generate/namevaluesfilters/v2/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. -package namevaluesfiltersv2 +package v2 diff --git a/internal/namevaluesfilters/v2/name_values_filters.go b/internal/namevaluesfilters/v2/name_values_filters.go new file mode 100644 index 00000000000..18d0279d8b3 --- /dev/null +++ b/internal/namevaluesfilters/v2/name_values_filters.go @@ -0,0 +1,16 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2 + +import ( + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" +) + +type NameValuesFilters struct { + namevaluesfilters.NameValuesFilters +} + +func New(i interface{}) NameValuesFilters { + return NameValuesFilters{NameValuesFilters: namevaluesfilters.New(i)} +} diff --git a/internal/generate/namevaluesfiltersv2/service_filters_gen.go b/internal/namevaluesfilters/v2/service_filters_gen.go similarity index 97% rename from internal/generate/namevaluesfiltersv2/service_filters_gen.go rename to internal/namevaluesfilters/v2/service_filters_gen.go index 33029b62729..afbe17801a3 100644 --- a/internal/generate/namevaluesfiltersv2/service_filters_gen.go +++ b/internal/namevaluesfilters/v2/service_filters_gen.go @@ -1,6 +1,6 @@ // Code generated by generators/servicefilters/main.go; DO NOT EDIT. -package namevaluesfiltersv2 +package v2 import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports "github.com/aws/aws-sdk-go-v2/aws" diff --git a/internal/generate/namevaluesfiltersv2/service_generation_customizations.go b/internal/namevaluesfilters/v2/service_generation_customizations.go similarity index 98% rename from internal/generate/namevaluesfiltersv2/service_generation_customizations.go rename to internal/namevaluesfilters/v2/service_generation_customizations.go index 5614a793ee3..9e140fb6fe2 100644 --- a/internal/generate/namevaluesfiltersv2/service_generation_customizations.go +++ b/internal/namevaluesfilters/v2/service_generation_customizations.go @@ -3,7 +3,7 @@ // This file contains code generation customizations for each AWS Go SDK service. -package namevaluesfiltersv2 +package v2 import "fmt" diff --git a/internal/service/imagebuilder/components_data_source.go b/internal/service/imagebuilder/components_data_source.go index 70116e44a87..0ec523328c9 100644 --- a/internal/service/imagebuilder/components_data_source.go +++ b/internal/service/imagebuilder/components_data_source.go @@ -13,7 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -53,7 +54,7 @@ func dataSourceComponentsRead(ctx context.Context, d *schema.ResourceData, meta } if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() + input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.ComponentVersion diff --git a/internal/service/imagebuilder/container_recipes_data_source.go b/internal/service/imagebuilder/container_recipes_data_source.go index bc9054d519f..af125a2578a 100644 --- a/internal/service/imagebuilder/container_recipes_data_source.go +++ b/internal/service/imagebuilder/container_recipes_data_source.go @@ -13,7 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -53,7 +54,7 @@ func dataSourceContainerRecipesRead(ctx context.Context, d *schema.ResourceData, } if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() + input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.ContainerRecipeSummary diff --git a/internal/service/imagebuilder/distribution_configurations_data_source.go b/internal/service/imagebuilder/distribution_configurations_data_source.go index 12c82e278a1..f36f054521b 100644 --- a/internal/service/imagebuilder/distribution_configurations_data_source.go +++ b/internal/service/imagebuilder/distribution_configurations_data_source.go @@ -12,7 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -43,7 +44,7 @@ func dataSourceDistributionConfigurationsRead(ctx context.Context, d *schema.Res input := &imagebuilder.ListDistributionConfigurationsInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() + input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.DistributionConfigurationSummary diff --git a/internal/service/imagebuilder/image_pipelines_data_source.go b/internal/service/imagebuilder/image_pipelines_data_source.go index bdb08294c9a..0531e0f38ad 100644 --- a/internal/service/imagebuilder/image_pipelines_data_source.go +++ b/internal/service/imagebuilder/image_pipelines_data_source.go @@ -12,7 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -43,7 +44,7 @@ func dataSourceImagePipelinesRead(ctx context.Context, d *schema.ResourceData, m input := &imagebuilder.ListImagePipelinesInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() + input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.ImagePipeline diff --git a/internal/service/imagebuilder/image_recipes_data_source.go b/internal/service/imagebuilder/image_recipes_data_source.go index e0bf7c01e66..9294eb90f40 100644 --- a/internal/service/imagebuilder/image_recipes_data_source.go +++ b/internal/service/imagebuilder/image_recipes_data_source.go @@ -13,7 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -53,7 +54,7 @@ func dataSourceImageRecipesRead(ctx context.Context, d *schema.ResourceData, met } if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() + input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.ImageRecipeSummary diff --git a/internal/service/imagebuilder/infrastructure_configurations_data_source.go b/internal/service/imagebuilder/infrastructure_configurations_data_source.go index 005a9ef2b55..a729eeb563b 100644 --- a/internal/service/imagebuilder/infrastructure_configurations_data_source.go +++ b/internal/service/imagebuilder/infrastructure_configurations_data_source.go @@ -12,7 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -43,7 +44,7 @@ func dataSourceInfrastructureConfigurationsRead(ctx context.Context, d *schema.R input := &imagebuilder.ListInfrastructureConfigurationsInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).ImageBuilderFilters() + input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() } var results []*imagebuilder.InfrastructureConfigurationSummary diff --git a/internal/service/rds/clusters_data_source.go b/internal/service/rds/clusters_data_source.go index f56fbccf486..c8cde3ae3b9 100644 --- a/internal/service/rds/clusters_data_source.go +++ b/internal/service/rds/clusters_data_source.go @@ -12,7 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -48,7 +49,7 @@ func dataSourceClustersRead(ctx context.Context, d *schema.ResourceData, meta in input := &rds.DescribeDBClustersInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).RDSFilters() + input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).RDSFilters() } var clusterArns []string diff --git a/internal/service/rds/engine_version_data_source.go b/internal/service/rds/engine_version_data_source.go index f59563e52f1..0d697e2d909 100644 --- a/internal/service/rds/engine_version_data_source.go +++ b/internal/service/rds/engine_version_data_source.go @@ -16,7 +16,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfiltersv2" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv2 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v2" tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -48,7 +49,7 @@ func dataSourceEngineVersion() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, }, - names.AttrFilter: namevaluesfiltersv2.Schema(), + names.AttrFilter: namevaluesfilters.Schema(), "has_major_target": { Type: schema.TypeBool, Optional: true, diff --git a/internal/service/rds/instances_data_source.go b/internal/service/rds/instances_data_source.go index 8df76e76339..05e1059b351 100644 --- a/internal/service/rds/instances_data_source.go +++ b/internal/service/rds/instances_data_source.go @@ -12,7 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/names" @@ -47,7 +48,7 @@ func dataSourceInstancesRead(ctx context.Context, d *schema.ResourceData, meta i input := &rds.DescribeDBInstancesInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).RDSFilters() + input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).RDSFilters() } filter := tfslices.PredicateTrue[*rds.DBInstance]() diff --git a/internal/service/route53resolver/query_log_config_data_source.go b/internal/service/route53resolver/query_log_config_data_source.go index 08ada57b2ea..e22f6e1805d 100644 --- a/internal/service/route53resolver/query_log_config_data_source.go +++ b/internal/service/route53resolver/query_log_config_data_source.go @@ -14,7 +14,8 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -69,7 +70,7 @@ func dataSourceQueryLogConfigRead(ctx context.Context, d *schema.ResourceData, m input := &route53resolver.ListResolverQueryLogConfigsInput{} if v, ok := d.GetOk(names.AttrFilter); ok && v.(*schema.Set).Len() > 0 { - input.Filters = namevaluesfilters.New(v.(*schema.Set)).Route53ResolverFilters() + input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).Route53ResolverFilters() } var configs []*route53resolver.ResolverQueryLogConfig diff --git a/internal/service/secretsmanager/secrets_data_source.go b/internal/service/secretsmanager/secrets_data_source.go index b9c9bcfe370..164800db8a7 100644 --- a/internal/service/secretsmanager/secrets_data_source.go +++ b/internal/service/secretsmanager/secrets_data_source.go @@ -13,7 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfiltersv2" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" + namevaluesfiltersv2 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v2" tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -28,7 +29,7 @@ func dataSourceSecrets() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - names.AttrFilter: namevaluesfiltersv2.Schema(), + names.AttrFilter: namevaluesfilters.Schema(), names.AttrNames: { Type: schema.TypeSet, Computed: true, From 88980ecdc0eb46349f87cd57185518e58b0af261 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 14:40:00 -0400 Subject: [PATCH 21/24] Fix terrafmt errors. --- website/docs/r/rds_integration.html.markdown | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/website/docs/r/rds_integration.html.markdown b/website/docs/r/rds_integration.html.markdown index 2f7e396f069..8ad0391a830 100644 --- a/website/docs/r/rds_integration.html.markdown +++ b/website/docs/r/rds_integration.html.markdown @@ -20,14 +20,15 @@ resource "aws_redshiftserverless_namespace" "example" { } resource "aws_redshiftserverless_workgroup" "example" { - namespace_name = aws_redshiftserverless_namespace.example.namespace_name - workgroup_name = "example-workspace" - base_capacity = 8 + namespace_name = aws_redshiftserverless_namespace.example.namespace_name + workgroup_name = "example-workspace" + base_capacity = 8 publicly_accessible = false + subnet_ids = [aws_subnet.example1.id, aws_subnet.example2.id, aws_subnet.example3.id] config_parameter { - parameter_key = "enable_case_sensitive_identifier" + parameter_key = "enable_case_sensitive_identifier" parameter_value = "true" } } @@ -79,10 +80,10 @@ resource "aws_rds_integration" "example" { integration_name = "example" source_arn = aws_rds_cluster.example.arn target_arn = aws_redshiftserverless_namespace.example.arn - kms_key_id = aws_kms_key.example.arn + additional_encryption_context = { - "example": "test", + "example" : "test", } } ``` From a5b1d63811a7a7f52cb8646ecc313f3aebefc1df Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 14:55:47 -0400 Subject: [PATCH 22/24] Run 'make gen'. --- .teamcity/scripts/provider_tests/acceptance_tests.sh | 1 + .teamcity/scripts/provider_tests/unit_tests.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/.teamcity/scripts/provider_tests/acceptance_tests.sh b/.teamcity/scripts/provider_tests/acceptance_tests.sh index c7c400ea2f6..48a2becdeb4 100644 --- a/.teamcity/scripts/provider_tests/acceptance_tests.sh +++ b/.teamcity/scripts/provider_tests/acceptance_tests.sh @@ -48,6 +48,7 @@ TF_ACC=1 go test \ ./internal/json/... \ ./internal/logging/... \ ./internal/maps/... \ + ./internal/namevaluesfilters/... \ ./internal/provider/... \ ./internal/retry/... \ ./internal/sdkv2/... \ diff --git a/.teamcity/scripts/provider_tests/unit_tests.sh b/.teamcity/scripts/provider_tests/unit_tests.sh index 3065a44e82a..0c942b55a55 100644 --- a/.teamcity/scripts/provider_tests/unit_tests.sh +++ b/.teamcity/scripts/provider_tests/unit_tests.sh @@ -20,6 +20,7 @@ go test \ ./internal/json/... \ ./internal/logging/... \ ./internal/maps/... \ + ./internal/namevaluesfilters/... \ ./internal/provider/... \ ./internal/retry/... \ ./internal/sdkv2/... \ From 04bdac991216605bca9ab46644c16adb28856868 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 15:42:19 -0400 Subject: [PATCH 23/24] internal/namevaluesfilters: Fix tests. --- internal/namevaluesfilters/name_values_filters_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/namevaluesfilters/name_values_filters_test.go b/internal/namevaluesfilters/name_values_filters_test.go index 07a377e852b..e4de0f0bf2e 100644 --- a/internal/namevaluesfilters/name_values_filters_test.go +++ b/internal/namevaluesfilters/name_values_filters_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" + "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" ) func TestNameValuesFiltersMap(t *testing.T) { From 6d7e4ed54da15b988747527286b14ef8c402a4ef Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2024 15:44:19 -0400 Subject: [PATCH 24/24] Fix providerlint 'AWSAT005: avoid hardcoded ARN AWS partitions, use aws_partition data source'. --- internal/service/rds/integration_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/service/rds/integration_test.go b/internal/service/rds/integration_test.go index 82fcf1730bd..1802fe99229 100644 --- a/internal/service/rds/integration_test.go +++ b/internal/service/rds/integration_test.go @@ -230,6 +230,7 @@ locals { } data "aws_caller_identity" "current" {} +data "aws_partition" "current" {} resource "aws_security_group" "test" { name = %[1]q @@ -375,7 +376,7 @@ resource "aws_redshift_resource_policy" "test" { Statement = [{ Effect = "Allow" Principal = { - AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" + AWS = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root" } Action = "redshift:CreateInboundIntegration" Resource = aws_redshiftserverless_namespace.test.arn @@ -428,7 +429,7 @@ data "aws_iam_policy_document" "key_policy" { resources = ["*"] principals { type = "AWS" - identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] + identifiers = ["arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root"] } }