From b767689924d36c14374847b499de50ef5c11a130 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Mon, 12 Apr 2021 21:51:13 +0300 Subject: [PATCH 01/16] initial commit --- aws/internal/service/rds/finder/finder.go | 28 ++- aws/provider.go | 1 + aws/resource_aws_db_proxy_endpoint.go | 202 ++++++++++++++++++++++ 3 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 aws/resource_aws_db_proxy_endpoint.go diff --git a/aws/internal/service/rds/finder/finder.go b/aws/internal/service/rds/finder/finder.go index a05a70c61d5..f79cab9a002 100644 --- a/aws/internal/service/rds/finder/finder.go +++ b/aws/internal/service/rds/finder/finder.go @@ -6,7 +6,7 @@ import ( ) // DBProxyTarget returns matching DBProxyTarget. -func DBProxyTarget(conn *rds.RDS, dbProxyName string, targetGroupName string, targetType string, rdsResourceId string) (*rds.DBProxyTarget, error) { +func DBProxyTarget(conn *rds.RDS, dbProxyName, targetGroupName, targetType, rdsResourceId string) (*rds.DBProxyTarget, error) { input := &rds.DescribeDBProxyTargetsInput{ DBProxyName: aws.String(dbProxyName), TargetGroupName: aws.String(targetGroupName), @@ -30,3 +30,29 @@ func DBProxyTarget(conn *rds.RDS, dbProxyName string, targetGroupName string, ta return dbProxyTarget, err } + +// DBProxyEndpoint returns matching DBProxyEndpoint. +func DBProxyEndpoint(conn *rds.RDS, dbProxyName, dbProxyEndpointName, arn string) (*rds.DBProxyEndpoint, error) { + input := &rds.DescribeDBProxyEndpointsInput{ + DBProxyName: aws.String(dbProxyName), + DBProxyEndpointName: aws.String(dbProxyEndpointName), + } + var dbProxyEndpoint *rds.DBProxyEndpoint + + err := conn.DescribeDBProxyEndpointsPages(input, func(page *rds.DescribeDBProxyEndpointsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, endpoint := range page.DBProxyEndpoints { + if aws.StringValue(endpoint.DBProxyEndpointArn) == arn { + dbProxyEndpoint = endpoint + return false + } + } + + return !lastPage + }) + + return dbProxyEndpoint, err +} diff --git a/aws/provider.go b/aws/provider.go index 28610649eac..2ae3f33f6b2 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -597,6 +597,7 @@ func Provider() *schema.Provider { "aws_db_parameter_group": resourceAwsDbParameterGroup(), "aws_db_proxy": resourceAwsDbProxy(), "aws_db_proxy_default_target_group": resourceAwsDbProxyDefaultTargetGroup(), + "aws_db_proxy_endpoint": resourceAwsDbProxyEndpoint(), "aws_db_proxy_target": resourceAwsDbProxyTarget(), "aws_db_security_group": resourceAwsDbSecurityGroup(), "aws_db_snapshot": resourceAwsDbSnapshot(), diff --git a/aws/resource_aws_db_proxy_endpoint.go b/aws/resource_aws_db_proxy_endpoint.go new file mode 100644 index 00000000000..69d78aec560 --- /dev/null +++ b/aws/resource_aws_db_proxy_endpoint.go @@ -0,0 +1,202 @@ +package aws + +import ( + "fmt" + "log" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/finder" +) + +func resourceAwsDbProxyEndpoint() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDbProxyEndpointCreate, + Read: resourceAwsDbProxyEndpointRead, + Delete: resourceAwsDbProxyEndpointDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "db_proxy_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateRdsIdentifier, + }, + "db_proxy_endpoint_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateRdsIdentifier, + }, + "tags": tagsSchema(), + "target_role": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: rds.DBProxyEndpointTargetRoleReadWrite, + ValidateFunc: validation.StringInSlice(rds.DBProxyEndpointTargetRole_Values(), false), + }, + "vpc_security_group_ids": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "vpc_subnet_ids": { + Type: schema.TypeSet, + Required: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "endpoint": { + Type: schema.TypeString, + Computed: true, + }, + "vpc_id": { + Type: schema.TypeInt, + Computed: true, + }, + "is_default": { + Type: schema.TypeBool, + Computed: true, + }, + }, + } +} + +func resourceAwsDbProxyEndpointCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + dbProxyName := d.Get("db_proxy_name").(string) + dbProxyEndpointName := d.Get("db_proxy_endpoint_name").(string) + + params := rds.CreateDBProxyEndpointInput{ + DBProxyName: aws.String(dbProxyName), + DBProxyEndpointName: aws.String(dbProxyEndpointName), + TargetRole: aws.String(d.Get("target_role").(string)), + VpcSubnetIds: expandStringSet(d.Get("vpc_subnet_ids").(*schema.Set)), + } + + if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 { + params.VpcSecurityGroupIds = expandStringSet(v) + } + + resp, err := conn.CreateDBProxyEndpoint(¶ms) + + if err != nil { + return fmt.Errorf("error Creating RDS DB Proxy Endpoint (%s/%s): %w", dbProxyName, dbProxyEndpointName, err) + } + + dbProxyEndpoint := resp.DBProxyEndpoint + + d.SetId(strings.Join([]string{dbProxyName, dbProxyEndpointName, aws.StringValue(dbProxyEndpoint.DBProxyEndpointArn)}, "/")) + + return resourceAwsDbProxyTargetRead(d, meta) +} + +func resourceAwsDbProxyEndpointParseID(id string) (string, string, string, error) { + idParts := strings.SplitN(id, "/", 3) + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + return "", "", "", fmt.Errorf("unexpected format of ID (%s), expected db_proxy_name/db_proxy_endpoint_name/arn", id) + } + return idParts[0], idParts[1], idParts[2], nil +} + +func resourceAwsDbProxyEndpointRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + dbProxyName, dbProxyEndpointName, dbProxyEndpointArn, err := resourceAwsDbProxyEndpointParseID(d.Id()) + if err != nil { + return err + } + + dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, dbProxyName, dbProxyEndpointName, dbProxyEndpointArn) + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) { + log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyEndpointNotFoundFault) { + log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading RDS DB Proxy Endpoint (%s): %w", d.Id(), err) + } + + if dbProxyEndpoint == nil { + log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + endpointArn := aws.StringValue(dbProxyEndpoint.DBProxyEndpointArn) + d.Set("arn", endpointArn) + d.Set("db_proxy_name", dbProxyName) + d.Set("endpoint", dbProxyEndpoint.Endpoint) + d.Set("db_proxy_endpoint_name", dbProxyEndpointName) + d.Set("is_default", dbProxyEndpoint.IsDefault) + d.Set("target_role", dbProxyEndpoint.TargetRole) + d.Set("vpc_id", dbProxyEndpoint.VpcId) + d.Set("target_role", dbProxyEndpoint.TargetRole) + d.Set("vpc_subnet_ids", flattenStringSet(dbProxyEndpoint.VpcSubnetIds)) + d.Set("vpc_security_group_ids", flattenStringSet(dbProxyEndpoint.VpcSecurityGroupIds)) + + tags, err := keyvaluetags.RdsListTags(conn, endpointArn) + + if err != nil { + return fmt.Errorf("Error listing tags for RDS DB Proxy Endpoint (%s): %w", endpointArn, err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("Error setting tags: %w", err) + } + + return nil +} + +func resourceAwsDbProxyEndpointDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + params := rds.DeleteDBProxyEndpointInput{ + DBProxyEndpointName: aws.String(d.Get("db_proxy_endpoint_name").(string)), + } + + log.Printf("[DEBUG] Delete DB Proxy Endpoint: %#v", params) + _, err := conn.DeleteDBProxyEndpoint(¶ms) + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) { + return nil + } + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyTargetGroupNotFoundFault) { + return nil + } + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyTargetNotFoundFault) { + return nil + } + + if err != nil { + return fmt.Errorf("Error Deleting DB Proxy Endpoint: %w", err) + } + + return nil +} From b1a35e8281876a470f84df4aa6ef9f2427853a0b Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 14 Apr 2021 12:44:57 +0300 Subject: [PATCH 02/16] create endpoint --- aws/resource_aws_db_proxy_endpoint.go | 19 +- aws/resource_aws_db_proxy_endpoint_test.go | 466 +++++++++++++++++++++ 2 files changed, 483 insertions(+), 2 deletions(-) create mode 100644 aws/resource_aws_db_proxy_endpoint_test.go diff --git a/aws/resource_aws_db_proxy_endpoint.go b/aws/resource_aws_db_proxy_endpoint.go index 69d78aec560..7eb8c6f0102 100644 --- a/aws/resource_aws_db_proxy_endpoint.go +++ b/aws/resource_aws_db_proxy_endpoint.go @@ -19,6 +19,7 @@ func resourceAwsDbProxyEndpoint() *schema.Resource { Create: resourceAwsDbProxyEndpointCreate, Read: resourceAwsDbProxyEndpointRead, Delete: resourceAwsDbProxyEndpointDelete, + Update: resourceAwsDbProxyEndpointUpdate, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -65,7 +66,7 @@ func resourceAwsDbProxyEndpoint() *schema.Resource { Computed: true, }, "vpc_id": { - Type: schema.TypeInt, + Type: schema.TypeString, Computed: true, }, "is_default": { @@ -103,7 +104,7 @@ func resourceAwsDbProxyEndpointCreate(d *schema.ResourceData, meta interface{}) d.SetId(strings.Join([]string{dbProxyName, dbProxyEndpointName, aws.StringValue(dbProxyEndpoint.DBProxyEndpointArn)}, "/")) - return resourceAwsDbProxyTargetRead(d, meta) + return resourceAwsDbProxyEndpointRead(d, meta) } func resourceAwsDbProxyEndpointParseID(id string) (string, string, string, error) { @@ -172,6 +173,20 @@ func resourceAwsDbProxyEndpointRead(d *schema.ResourceData, meta interface{}) er return nil } +func resourceAwsDbProxyEndpointUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + if err := keyvaluetags.RdsUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("Error updating RDS DB Proxy Endpoint (%s) tags: %w", d.Get("arn").(string), err) + } + } + + return resourceAwsDbProxyEndpointRead(d, meta) +} + func resourceAwsDbProxyEndpointDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn diff --git a/aws/resource_aws_db_proxy_endpoint_test.go b/aws/resource_aws_db_proxy_endpoint_test.go new file mode 100644 index 00000000000..40099faa04f --- /dev/null +++ b/aws/resource_aws_db_proxy_endpoint_test.go @@ -0,0 +1,466 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/finder" +) + +// func init() { +// resource.AddTestSweepers("aws_db_proxy_endpoint", &resource.Sweeper{ +// Name: "aws_db_proxy_endpoint", +// F: testSweepRdsDbProxies, +// }) +// } + +// func testSweepRdsDbProxies(region string) error { +// client, err := sharedClientForRegion(region) +// if err != nil { +// return fmt.Errorf("Error getting client: %s", err) +// } +// conn := client.(*AWSClient).rdsconn + +// err = conn.DescribeDBProxiesPages(&rds.DescribeDBProxiesInput{}, func(out *rds.DescribeDBProxiesOutput, lastPage bool) bool { +// for _, dbpg := range out.DBProxies { +// if dbpg == nil { +// continue +// } + +// input := &rds.DeleteDBProxyInput{ +// DBProxyName: dbpg.DBProxyName, +// } +// name := aws.StringValue(dbpg.DBProxyName) + +// log.Printf("[INFO] Deleting DB Proxy: %s", name) + +// _, err := conn.DeleteDBProxy(input) + +// if err != nil { +// log.Printf("[ERROR] Failed to delete DB Proxy %s: %s", name, err) +// continue +// } +// } + +// return !lastPage +// }) + +// if testSweepSkipSweepError(err) { +// log.Printf("[WARN] Skipping RDS DB Proxy sweep for %s: %s", region, err) +// return nil +// } + +// if err != nil { +// return fmt.Errorf("Error retrieving DB Proxies: %s", err) +// } + +// return nil +// } + +func TestAccAWSDBProxyEndpoint_basic(t *testing.T) { + var v rds.DBProxyEndpoint + resourceName := "aws_db_proxy_endpoint.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccDBProxyEndpointPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBProxyEndpointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBProxyEndpointConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyEndpointExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "db_proxy_endpoint_name", rName), + resource.TestCheckResourceAttrPair(resourceName, "db_proxy_name", "aws_db_proxy.test", "name"), + resource.TestCheckResourceAttr(resourceName, "target_role", "READ_WRITE"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "rds", regexp.MustCompile(`db-proxy-endpoint:.+`)), + resource.TestCheckResourceAttr(resourceName, "vpc_subnet_ids.#", "2"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_subnet_ids.*", "aws_subnet.test.0", "id"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_subnet_ids.*", "aws_subnet.test.1", "id"), + resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "0"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_id", "aws_vpc.test", "id"), + resource.TestMatchResourceAttr(resourceName, "endpoint", regexp.MustCompile(`^[\w\-\.]+\.rds\.amazonaws\.com$`)), + resource.TestCheckResourceAttr(resourceName, "tags.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSDBProxyEndpoint_vpcSecurityGroupIds(t *testing.T) { + var dbProxy rds.DBProxyEndpoint + resourceName := "aws_db_proxy_endpoint.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccDBProxyEndpointPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBProxyEndpointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), + resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test2", "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSDBProxyEndpoint_tags(t *testing.T) { + var dbProxy rds.DBProxyEndpoint + resourceName := "aws_db_proxy_endpoint.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccDBProxyEndpointPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBProxyEndpointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBProxyEndpointConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSDBProxyEndpointConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSDBProxyEndpointConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccAWSDBProxyEndpoint_disappears(t *testing.T) { + var v rds.DBProxyEndpoint + resourceName := "aws_db_proxy_endpoint.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccDBProxyEndpointPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBProxyEndpointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBProxyEndpointConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyEndpointExists(resourceName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsDbProxyEndpoint(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSDBProxyEndpoint_disappears_proxy(t *testing.T) { + var v rds.DBProxyEndpoint + resourceName := "aws_db_proxy_endpoint.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccDBProxyEndpointPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBProxyEndpointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBProxyEndpointConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyEndpointExists(resourceName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsDbProxy(), "aws_db_proxy.test"), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +// testAccDBProxyEndpointPreCheck checks if a call to describe db proxies errors out meaning feature not supported +func testAccDBProxyEndpointPreCheck(t *testing.T) { + conn := testAccProvider.Meta().(*AWSClient).rdsconn + + input := &rds.DescribeDBProxiesInput{} + _, err := conn.DescribeDBProxies(input) + + if isAWSErr(err, "InvalidAction", "") { + t.Skipf("skipping acceptance test, RDS Proxy not supported: %s", err) + } + + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccCheckAWSDBProxyEndpointDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).rdsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_db_proxy_endpoint" { + continue + } + + dbProxyName, dbProxyEndpointName, dbProxyEndpointArn, err := resourceAwsDbProxyEndpointParseID(rs.Primary.ID) + if err != nil { + return err + } + + dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, dbProxyName, dbProxyEndpointName, dbProxyEndpointArn) + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) || tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyEndpointNotFoundFault) { + continue + } + + if err != nil { + return err + } + + if dbProxyEndpoint != nil { + return fmt.Errorf("RDS DB Proxy Endpoint (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSDBProxyEndpointExists(n string, v *rds.DBProxyEndpoint) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No DB Proxy ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).rdsconn + + dbProxyName, dbProxyEndpointName, dbProxyEndpointArn, err := resourceAwsDbProxyEndpointParseID(rs.Primary.ID) + if err != nil { + return err + } + + dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, dbProxyName, dbProxyEndpointName, dbProxyEndpointArn) + + if err != nil { + return err + } + + if dbProxyEndpoint == nil { + return fmt.Errorf("RDS DB Proxy Endpoint (%s) still not found", rs.Primary.ID) + } + + *v = *dbProxyEndpoint + + return nil + } +} + +func testAccAWSDBProxyEndpointConfigBase(rName string) string { + return fmt.Sprintf(` +# Secrets Manager setup + +resource "aws_secretsmanager_secret" "test" { + name = "%[1]s" + recovery_window_in_days = 0 +} + +resource "aws_secretsmanager_secret_version" "test" { + secret_id = aws_secretsmanager_secret.test.id + secret_string = "{\"username\":\"db_user\",\"password\":\"db_user_password\"}" +} + +# IAM setup + +resource "aws_iam_role" "test" { + name = "%[1]s" + assume_role_policy = data.aws_iam_policy_document.assume.json +} + +data "aws_iam_policy_document" "assume" { + statement { + actions = ["sts:AssumeRole"] + principals { + type = "Service" + identifiers = ["rds.amazonaws.com"] + } + } +} + +resource "aws_iam_role_policy" "test" { + role = aws_iam_role.test.id + policy = data.aws_iam_policy_document.test.json +} + +data "aws_iam_policy_document" "test" { + statement { + actions = [ + "secretsmanager:GetRandomPassword", + "secretsmanager:CreateSecret", + "secretsmanager:ListSecrets", + ] + resources = ["*"] + } + + statement { + actions = ["secretsmanager:*"] + resources = [aws_secretsmanager_secret.test.arn] + } +} + +# VPC setup + +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = "%[1]s" + } +} + +resource "aws_security_group" "test" { + name = "%[1]s" + vpc_id = aws_vpc.test.id +} + +resource "aws_subnet" "test" { + count = 2 + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, count.index) + availability_zone = data.aws_availability_zones.available.names[count.index] + vpc_id = aws_vpc.test.id + + tags = { + Name = "%[1]s-${count.index}" + } +} + +resource "aws_db_proxy" "test" { + depends_on = [ + aws_secretsmanager_secret_version.test, + aws_iam_role_policy.test + ] + + name = %[1]q + debug_logging = false + engine_family = "MYSQL" + idle_client_timeout = 1800 + require_tls = true + role_arn = aws_iam_role.test.arn + vpc_security_group_ids = [aws_security_group.test.id] + vpc_subnet_ids = aws_subnet.test.*.id + + auth { + auth_scheme = "SECRETS" + description = "test" + iam_auth = "DISABLED" + secret_arn = aws_secretsmanager_secret.test.arn + } + } +`, rName) +} + +func testAccAWSDBProxyEndpointConfig(rName string) string { + return testAccAWSDBProxyEndpointConfigBase(rName) + fmt.Sprintf(` +resource "aws_db_proxy_endpoint" "test" { + db_proxy_name = aws_db_proxy.test.name + db_proxy_endpoint_name = %[1]q + vpc_subnet_ids = aws_subnet.test.*.id +} +`, rName) +} + +func testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName string) string { + return testAccAWSDBProxyEndpointConfigBase(rName) + fmt.Sprintf(` +resource "aws_db_proxy_endpoint" "test" { + db_proxy_name = aws_db_proxy.test.name + db_proxy_endpoint_name = %[1]q + vpc_subnet_ids = aws_subnet.test.*.id + vpc_security_group_ids = [aws_security_group.test.id] +} + +resource "aws_security_group" "test" { + name = %[1]q + vpc_id = aws_vpc.test.id +} +`, rName) +} + +func testAccAWSDBProxyEndpointConfigTags1(rName, key1, value1 string) string { + return testAccAWSDBProxyEndpointConfigBase(rName) + fmt.Sprintf(` +resource "aws_db_proxy_endpoint" "test" { + db_proxy_name = aws_db_proxy.test.name + db_proxy_endpoint_name = %[1]q + vpc_subnet_ids = aws_subnet.test.*.id + + tags = { + %[2]q = %[3]q + } +} +`, rName, key1, value1) +} + +func testAccAWSDBProxyEndpointConfigTags2(rName, key1, value1, key2, value2 string) string { + return testAccAWSDBProxyEndpointConfigBase(rName) + fmt.Sprintf(` +resource "aws_db_proxy_endpoint" "test" { + db_proxy_name = aws_db_proxy.test.name + db_proxy_endpoint_name = %[1]q + vpc_subnet_ids = aws_subnet.test.*.id + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, key1, value1, key2, value2) +} From dc9e0b3eadeb6c4002a0c0aee23d1e2a5048e3a1 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 14 Apr 2021 20:13:14 +0300 Subject: [PATCH 03/16] waiter --- aws/internal/service/rds/waiter/status.go | 24 ++++++++++++ aws/internal/service/rds/waiter/waiter.go | 45 +++++++++++++++++++++- aws/resource_aws_db_proxy_endpoint_test.go | 3 +- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/aws/internal/service/rds/waiter/status.go b/aws/internal/service/rds/waiter/status.go index 59039e5a825..7a33877e4f5 100644 --- a/aws/internal/service/rds/waiter/status.go +++ b/aws/internal/service/rds/waiter/status.go @@ -4,6 +4,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/finder" ) const ( @@ -12,6 +13,12 @@ const ( // EventSubscription Unknown EventSubscriptionStatusUnknown = "Unknown" + + // ProxyEndpoint NotFound + ProxyEndpointStatusNotFound = "NotFound" + + // ProxyEndpoint Unknown + ProxyEndpointStatusUnknown = "Unknown" ) // EventSubscriptionStatus fetches the EventSubscription and its Status @@ -34,3 +41,20 @@ func EventSubscriptionStatus(conn *rds.RDS, subscriptionName string) resource.St return output.EventSubscriptionsList[0], aws.StringValue(output.EventSubscriptionsList[0].Status), nil } } + +// DBProxyEndpointStatus fetches the ProxyEndpoint and its Status +func DBProxyEndpointStatus(conn *rds.RDS, dbProxyName, dbProxyEndpointName, arn string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.DBProxyEndpoint(conn, dbProxyName, dbProxyEndpointName, arn) + + if err != nil { + return nil, ProxyEndpointStatusUnknown, err + } + + if output == nil { + return nil, ProxyEndpointStatusNotFound, nil + } + + return output, aws.StringValue(output.Status), nil + } +} diff --git a/aws/internal/service/rds/waiter/waiter.go b/aws/internal/service/rds/waiter/waiter.go index aa18ccb2090..ea1fe4f2302 100644 --- a/aws/internal/service/rds/waiter/waiter.go +++ b/aws/internal/service/rds/waiter/waiter.go @@ -10,9 +10,13 @@ import ( const ( // Maximum amount of time to wait for an EventSubscription to return Deleted EventSubscriptionDeletedTimeout = 10 * time.Minute + // Maximum amount of time to wait for an DBProxyEndpoint to return Available + DBProxyEndpointAvailableTimeout = 10 * time.Minute + // Maximum amount of time to wait for an DBProxyEndpoint to return Deleted + DBProxyEndpointDeletedTimeout = 10 * time.Minute ) -// DeploymentDeployed waits for a EventSubscription to return Deleted +// EventSubscriptionDeleted waits for a EventSubscription to return Deleted func EventSubscriptionDeleted(conn *rds.RDS, subscriptionName string) (*rds.EventSubscription, error) { stateConf := &resource.StateChangeConf{ Pending: []string{"deleting"}, @@ -29,3 +33,42 @@ func EventSubscriptionDeleted(conn *rds.RDS, subscriptionName string) (*rds.Even return nil, err } + +// DBProxyEndpointAvailable waits for a DBProxyEndpoint to return Available +func DBProxyEndpointAvailable(conn *rds.RDS, dbProxyName, dbProxyEndpointName, arn string) (*rds.DBProxyEndpoint, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + rds.DBProxyEndpointStatusCreating, + rds.DBProxyEndpointStatusModifying, + }, + Target: []string{rds.DBProxyEndpointStatusAvailable}, + Refresh: DBProxyEndpointStatus(conn, dbProxyName, dbProxyEndpointName, arn), + Timeout: DBProxyEndpointAvailableTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*rds.DBProxyEndpoint); ok { + return v, err + } + + return nil, err +} + +// DBProxyEndpointDeleted waits for a DBProxyEndpoint to return Deleted +func DBProxyEndpointDeleted(conn *rds.RDS, dbProxyName, dbProxyEndpointName, arn string) (*rds.DBProxyEndpoint, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{rds.DBProxyEndpointStatusDeleting}, + Target: []string{}, + Refresh: DBProxyEndpointStatus(conn, dbProxyName, dbProxyEndpointName, arn), + Timeout: DBProxyEndpointDeletedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*rds.DBProxyEndpoint); ok { + return v, err + } + + return nil, err +} diff --git a/aws/resource_aws_db_proxy_endpoint_test.go b/aws/resource_aws_db_proxy_endpoint_test.go index 40099faa04f..fb454e2b4f3 100644 --- a/aws/resource_aws_db_proxy_endpoint_test.go +++ b/aws/resource_aws_db_proxy_endpoint_test.go @@ -85,9 +85,10 @@ func TestAccAWSDBProxyEndpoint_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vpc_subnet_ids.#", "2"), resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_subnet_ids.*", "aws_subnet.test.0", "id"), resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_subnet_ids.*", "aws_subnet.test.1", "id"), - resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "1"), resource.TestCheckResourceAttrPair(resourceName, "vpc_id", "aws_vpc.test", "id"), resource.TestMatchResourceAttr(resourceName, "endpoint", regexp.MustCompile(`^[\w\-\.]+\.rds\.amazonaws\.com$`)), + resource.TestCheckResourceAttr(resourceName, "is_default", "false"), resource.TestCheckResourceAttr(resourceName, "tags.#", "0"), ), }, From 9923e16d8ad0410d4c7a6a0049c98d65d4912bf8 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 14 Apr 2021 21:33:05 +0300 Subject: [PATCH 04/16] refactor waiter --- aws/internal/service/rds/finder/finder.go | 12 ++- aws/internal/service/rds/id.go | 14 ++++ aws/internal/service/rds/waiter/status.go | 4 +- aws/internal/service/rds/waiter/waiter.go | 16 ++-- aws/resource_aws_db_proxy_endpoint.go | 93 ++++++++++++---------- aws/resource_aws_db_proxy_endpoint_test.go | 47 ++++++----- 6 files changed, 111 insertions(+), 75 deletions(-) create mode 100644 aws/internal/service/rds/id.go diff --git a/aws/internal/service/rds/finder/finder.go b/aws/internal/service/rds/finder/finder.go index f79cab9a002..6b4d0d5dd92 100644 --- a/aws/internal/service/rds/finder/finder.go +++ b/aws/internal/service/rds/finder/finder.go @@ -3,6 +3,7 @@ package finder import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" + tfrds "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds" ) // DBProxyTarget returns matching DBProxyTarget. @@ -32,20 +33,25 @@ func DBProxyTarget(conn *rds.RDS, dbProxyName, targetGroupName, targetType, rdsR } // DBProxyEndpoint returns matching DBProxyEndpoint. -func DBProxyEndpoint(conn *rds.RDS, dbProxyName, dbProxyEndpointName, arn string) (*rds.DBProxyEndpoint, error) { +func DBProxyEndpoint(conn *rds.RDS, id string) (*rds.DBProxyEndpoint, error) { + dbProxyName, dbProxyEndpointName, dbProxyEndpointArn, err := tfrds.ResourceAwsDbProxyEndpointParseID(id) + if err != nil { + return nil, err + } + input := &rds.DescribeDBProxyEndpointsInput{ DBProxyName: aws.String(dbProxyName), DBProxyEndpointName: aws.String(dbProxyEndpointName), } var dbProxyEndpoint *rds.DBProxyEndpoint - err := conn.DescribeDBProxyEndpointsPages(input, func(page *rds.DescribeDBProxyEndpointsOutput, lastPage bool) bool { + err = conn.DescribeDBProxyEndpointsPages(input, func(page *rds.DescribeDBProxyEndpointsOutput, lastPage bool) bool { if page == nil { return !lastPage } for _, endpoint := range page.DBProxyEndpoints { - if aws.StringValue(endpoint.DBProxyEndpointArn) == arn { + if aws.StringValue(endpoint.DBProxyEndpointArn) == dbProxyEndpointArn { dbProxyEndpoint = endpoint return false } diff --git a/aws/internal/service/rds/id.go b/aws/internal/service/rds/id.go new file mode 100644 index 00000000000..2276d47a4e6 --- /dev/null +++ b/aws/internal/service/rds/id.go @@ -0,0 +1,14 @@ +package rds + +import ( + "fmt" + "strings" +) + +func ResourceAwsDbProxyEndpointParseID(id string) (string, string, string, error) { + idParts := strings.SplitN(id, "/", 3) + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + return "", "", "", fmt.Errorf("unexpected format of ID (%s), expected db_proxy_name/db_proxy_endpoint_name/arn", id) + } + return idParts[0], idParts[1], idParts[2], nil +} diff --git a/aws/internal/service/rds/waiter/status.go b/aws/internal/service/rds/waiter/status.go index 7a33877e4f5..59fa5e5b5e4 100644 --- a/aws/internal/service/rds/waiter/status.go +++ b/aws/internal/service/rds/waiter/status.go @@ -43,9 +43,9 @@ func EventSubscriptionStatus(conn *rds.RDS, subscriptionName string) resource.St } // DBProxyEndpointStatus fetches the ProxyEndpoint and its Status -func DBProxyEndpointStatus(conn *rds.RDS, dbProxyName, dbProxyEndpointName, arn string) resource.StateRefreshFunc { +func DBProxyEndpointStatus(conn *rds.RDS, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := finder.DBProxyEndpoint(conn, dbProxyName, dbProxyEndpointName, arn) + output, err := finder.DBProxyEndpoint(conn, id) if err != nil { return nil, ProxyEndpointStatusUnknown, err diff --git a/aws/internal/service/rds/waiter/waiter.go b/aws/internal/service/rds/waiter/waiter.go index ea1fe4f2302..f322d4d989e 100644 --- a/aws/internal/service/rds/waiter/waiter.go +++ b/aws/internal/service/rds/waiter/waiter.go @@ -10,10 +10,6 @@ import ( const ( // Maximum amount of time to wait for an EventSubscription to return Deleted EventSubscriptionDeletedTimeout = 10 * time.Minute - // Maximum amount of time to wait for an DBProxyEndpoint to return Available - DBProxyEndpointAvailableTimeout = 10 * time.Minute - // Maximum amount of time to wait for an DBProxyEndpoint to return Deleted - DBProxyEndpointDeletedTimeout = 10 * time.Minute ) // EventSubscriptionDeleted waits for a EventSubscription to return Deleted @@ -35,15 +31,15 @@ func EventSubscriptionDeleted(conn *rds.RDS, subscriptionName string) (*rds.Even } // DBProxyEndpointAvailable waits for a DBProxyEndpoint to return Available -func DBProxyEndpointAvailable(conn *rds.RDS, dbProxyName, dbProxyEndpointName, arn string) (*rds.DBProxyEndpoint, error) { +func DBProxyEndpointAvailable(conn *rds.RDS, id string, timeout time.Duration) (*rds.DBProxyEndpoint, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ rds.DBProxyEndpointStatusCreating, rds.DBProxyEndpointStatusModifying, }, Target: []string{rds.DBProxyEndpointStatusAvailable}, - Refresh: DBProxyEndpointStatus(conn, dbProxyName, dbProxyEndpointName, arn), - Timeout: DBProxyEndpointAvailableTimeout, + Refresh: DBProxyEndpointStatus(conn, id), + Timeout: timeout, } outputRaw, err := stateConf.WaitForState() @@ -56,12 +52,12 @@ func DBProxyEndpointAvailable(conn *rds.RDS, dbProxyName, dbProxyEndpointName, a } // DBProxyEndpointDeleted waits for a DBProxyEndpoint to return Deleted -func DBProxyEndpointDeleted(conn *rds.RDS, dbProxyName, dbProxyEndpointName, arn string) (*rds.DBProxyEndpoint, error) { +func DBProxyEndpointDeleted(conn *rds.RDS, id string, timeout time.Duration) (*rds.DBProxyEndpoint, error) { stateConf := &resource.StateChangeConf{ Pending: []string{rds.DBProxyEndpointStatusDeleting}, Target: []string{}, - Refresh: DBProxyEndpointStatus(conn, dbProxyName, dbProxyEndpointName, arn), - Timeout: DBProxyEndpointDeletedTimeout, + Refresh: DBProxyEndpointStatus(conn, id), + Timeout: timeout, } outputRaw, err := stateConf.WaitForState() diff --git a/aws/resource_aws_db_proxy_endpoint.go b/aws/resource_aws_db_proxy_endpoint.go index 7eb8c6f0102..e09ddcac2bd 100644 --- a/aws/resource_aws_db_proxy_endpoint.go +++ b/aws/resource_aws_db_proxy_endpoint.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "strings" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" @@ -12,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/waiter" ) func resourceAwsDbProxyEndpoint() *schema.Resource { @@ -24,6 +26,12 @@ func resourceAwsDbProxyEndpoint() *schema.Resource { State: schema.ImportStatePassthrough, }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), + }, + Schema: map[string]*schema.Schema{ "arn": { Type: schema.TypeString, @@ -41,6 +49,14 @@ func resourceAwsDbProxyEndpoint() *schema.Resource { ForceNew: true, ValidateFunc: validateRdsIdentifier, }, + "endpoint": { + Type: schema.TypeString, + Computed: true, + }, + "is_default": { + Type: schema.TypeBool, + Computed: true, + }, "tags": tagsSchema(), "target_role": { Type: schema.TypeString, @@ -49,6 +65,10 @@ func resourceAwsDbProxyEndpoint() *schema.Resource { Default: rds.DBProxyEndpointTargetRoleReadWrite, ValidateFunc: validation.StringInSlice(rds.DBProxyEndpointTargetRole_Values(), false), }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + }, "vpc_security_group_ids": { Type: schema.TypeSet, Optional: true, @@ -61,18 +81,6 @@ func resourceAwsDbProxyEndpoint() *schema.Resource { ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "endpoint": { - Type: schema.TypeString, - Computed: true, - }, - "vpc_id": { - Type: schema.TypeString, - Computed: true, - }, - "is_default": { - Type: schema.TypeBool, - Computed: true, - }, }, } } @@ -104,27 +112,18 @@ func resourceAwsDbProxyEndpointCreate(d *schema.ResourceData, meta interface{}) d.SetId(strings.Join([]string{dbProxyName, dbProxyEndpointName, aws.StringValue(dbProxyEndpoint.DBProxyEndpointArn)}, "/")) - return resourceAwsDbProxyEndpointRead(d, meta) -} - -func resourceAwsDbProxyEndpointParseID(id string) (string, string, string, error) { - idParts := strings.SplitN(id, "/", 3) - if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { - return "", "", "", fmt.Errorf("unexpected format of ID (%s), expected db_proxy_name/db_proxy_endpoint_name/arn", id) + if _, err := waiter.DBProxyEndpointAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return fmt.Errorf("error waiting for RDS DB Proxy Endpoint (%s) to become available: %w", d.Id(), err) } - return idParts[0], idParts[1], idParts[2], nil + + return resourceAwsDbProxyEndpointRead(d, meta) } func resourceAwsDbProxyEndpointRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - dbProxyName, dbProxyEndpointName, dbProxyEndpointArn, err := resourceAwsDbProxyEndpointParseID(d.Id()) - if err != nil { - return err - } - - dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, dbProxyName, dbProxyEndpointName, dbProxyEndpointArn) + dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, d.Id()) if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) { log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id()) @@ -150,9 +149,9 @@ func resourceAwsDbProxyEndpointRead(d *schema.ResourceData, meta interface{}) er endpointArn := aws.StringValue(dbProxyEndpoint.DBProxyEndpointArn) d.Set("arn", endpointArn) - d.Set("db_proxy_name", dbProxyName) + d.Set("db_proxy_name", dbProxyEndpoint.DBProxyName) d.Set("endpoint", dbProxyEndpoint.Endpoint) - d.Set("db_proxy_endpoint_name", dbProxyEndpointName) + d.Set("db_proxy_endpoint_name", dbProxyEndpoint.DBProxyEndpointName) d.Set("is_default", dbProxyEndpoint.IsDefault) d.Set("target_role", dbProxyEndpoint.TargetRole) d.Set("vpc_id", dbProxyEndpoint.VpcId) @@ -176,6 +175,22 @@ func resourceAwsDbProxyEndpointRead(d *schema.ResourceData, meta interface{}) er func resourceAwsDbProxyEndpointUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn + if d.HasChange("vpc_security_group_ids") { + params := rds.ModifyDBProxyEndpointInput{ + DBProxyEndpointName: aws.String(d.Get("db_proxy_endpoint_name").(string)), + VpcSecurityGroupIds: expandStringSet(d.Get("vpc_security_group_ids").(*schema.Set)), + } + + _, err := conn.ModifyDBProxyEndpoint(¶ms) + if err != nil { + return fmt.Errorf("Error updating DB Proxy Endpoint: %w", err) + } + + if _, err := waiter.DBProxyEndpointAvailable(conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { + return fmt.Errorf("error waiting for RDS DB Proxy Endpoint (%s) to become modified: %w", d.Id(), err) + } + } + if d.HasChange("tags") { o, n := d.GetChange("tags") @@ -197,21 +212,19 @@ func resourceAwsDbProxyEndpointDelete(d *schema.ResourceData, meta interface{}) log.Printf("[DEBUG] Delete DB Proxy Endpoint: %#v", params) _, err := conn.DeleteDBProxyEndpoint(¶ms) - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) { - return nil - } - - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyTargetGroupNotFoundFault) { - return nil - } - - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyTargetNotFoundFault) { - return nil - } - if err != nil { + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) || tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyEndpointNotFoundFault) { + return nil + } return fmt.Errorf("Error Deleting DB Proxy Endpoint: %w", err) } + if _, err := waiter.DBProxyEndpointDeleted(conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) || tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyEndpointNotFoundFault) { + return nil + } + return fmt.Errorf("error waiting for RDS DB Proxy Endpoint (%s) to become deleted: %w", d.Id(), err) + } + return nil } diff --git a/aws/resource_aws_db_proxy_endpoint_test.go b/aws/resource_aws_db_proxy_endpoint_test.go index fb454e2b4f3..606e8fc8b68 100644 --- a/aws/resource_aws_db_proxy_endpoint_test.go +++ b/aws/resource_aws_db_proxy_endpoint_test.go @@ -113,11 +113,11 @@ func TestAccAWSDBProxyEndpoint_vpcSecurityGroupIds(t *testing.T) { CheckDestroy: testAccCheckAWSDBProxyEndpointDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName), + Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName, 1), Check: resource.ComposeTestCheckFunc( testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test2", "id"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test.0", "id"), ), }, { @@ -125,6 +125,23 @@ func TestAccAWSDBProxyEndpoint_vpcSecurityGroupIds(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName, 2), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), + resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "2"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test.0", "id"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test.1", "id"), + ), + }, + { + Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName, 1), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), + resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test.0", "id"), + ), + }, }, }) } @@ -222,8 +239,7 @@ func TestAccAWSDBProxyEndpoint_disappears_proxy(t *testing.T) { func testAccDBProxyEndpointPreCheck(t *testing.T) { conn := testAccProvider.Meta().(*AWSClient).rdsconn - input := &rds.DescribeDBProxiesInput{} - _, err := conn.DescribeDBProxies(input) + _, err := conn.DescribeDBProxyEndpoints(&rds.DescribeDBProxyEndpointsInput{}) if isAWSErr(err, "InvalidAction", "") { t.Skipf("skipping acceptance test, RDS Proxy not supported: %s", err) @@ -242,12 +258,7 @@ func testAccCheckAWSDBProxyEndpointDestroy(s *terraform.State) error { continue } - dbProxyName, dbProxyEndpointName, dbProxyEndpointArn, err := resourceAwsDbProxyEndpointParseID(rs.Primary.ID) - if err != nil { - return err - } - - dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, dbProxyName, dbProxyEndpointName, dbProxyEndpointArn) + dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, rs.Primary.ID) if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) || tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyEndpointNotFoundFault) { continue @@ -278,12 +289,7 @@ func testAccCheckAWSDBProxyEndpointExists(n string, v *rds.DBProxyEndpoint) reso conn := testAccProvider.Meta().(*AWSClient).rdsconn - dbProxyName, dbProxyEndpointName, dbProxyEndpointArn, err := resourceAwsDbProxyEndpointParseID(rs.Primary.ID) - if err != nil { - return err - } - - dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, dbProxyName, dbProxyEndpointName, dbProxyEndpointArn) + dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, rs.Primary.ID) if err != nil { return err @@ -421,20 +427,21 @@ resource "aws_db_proxy_endpoint" "test" { `, rName) } -func testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName string) string { +func testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName string, cnt int) string { return testAccAWSDBProxyEndpointConfigBase(rName) + fmt.Sprintf(` resource "aws_db_proxy_endpoint" "test" { db_proxy_name = aws_db_proxy.test.name db_proxy_endpoint_name = %[1]q vpc_subnet_ids = aws_subnet.test.*.id - vpc_security_group_ids = [aws_security_group.test.id] + vpc_security_group_ids = [aws_security_group.*.test.id] } resource "aws_security_group" "test" { - name = %[1]q + count = %[2]d + name = "%[1]s-${count.index}" vpc_id = aws_vpc.test.id } -`, rName) +`, rName, cnt) } func testAccAWSDBProxyEndpointConfigTags1(rName, key1, value1 string) string { From fa18c71d412b01cc8a20955310950dba0e6fb60d Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 15 Apr 2021 00:36:21 +0300 Subject: [PATCH 05/16] fix failing tests --- aws/resource_aws_db_proxy_endpoint.go | 1 + aws/resource_aws_db_proxy_endpoint_test.go | 36 ++++++++++++++-------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/aws/resource_aws_db_proxy_endpoint.go b/aws/resource_aws_db_proxy_endpoint.go index e09ddcac2bd..1ee6924d459 100644 --- a/aws/resource_aws_db_proxy_endpoint.go +++ b/aws/resource_aws_db_proxy_endpoint.go @@ -96,6 +96,7 @@ func resourceAwsDbProxyEndpointCreate(d *schema.ResourceData, meta interface{}) DBProxyEndpointName: aws.String(dbProxyEndpointName), TargetRole: aws.String(d.Get("target_role").(string)), VpcSubnetIds: expandStringSet(d.Get("vpc_subnet_ids").(*schema.Set)), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().RdsTags(), } if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 { diff --git a/aws/resource_aws_db_proxy_endpoint_test.go b/aws/resource_aws_db_proxy_endpoint_test.go index 606e8fc8b68..bdaba4feee8 100644 --- a/aws/resource_aws_db_proxy_endpoint_test.go +++ b/aws/resource_aws_db_proxy_endpoint_test.go @@ -113,11 +113,11 @@ func TestAccAWSDBProxyEndpoint_vpcSecurityGroupIds(t *testing.T) { CheckDestroy: testAccCheckAWSDBProxyEndpointDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName, 1), + Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds1(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test.0", "id"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test", "id"), ), }, { @@ -126,20 +126,20 @@ func TestAccAWSDBProxyEndpoint_vpcSecurityGroupIds(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName, 2), + Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds2(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "2"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test.0", "id"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test.1", "id"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test", "id"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test2", "id"), ), }, { - Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName, 1), + Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds1(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test.0", "id"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test", "id"), ), }, }, @@ -427,21 +427,31 @@ resource "aws_db_proxy_endpoint" "test" { `, rName) } -func testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds(rName string, cnt int) string { +func testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds1(rName string) string { return testAccAWSDBProxyEndpointConfigBase(rName) + fmt.Sprintf(` resource "aws_db_proxy_endpoint" "test" { db_proxy_name = aws_db_proxy.test.name db_proxy_endpoint_name = %[1]q vpc_subnet_ids = aws_subnet.test.*.id - vpc_security_group_ids = [aws_security_group.*.test.id] + vpc_security_group_ids = [aws_security_group.test.id] +} +`, rName) } -resource "aws_security_group" "test" { - count = %[2]d - name = "%[1]s-${count.index}" +func testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds2(rName string) string { + return testAccAWSDBProxyEndpointConfigBase(rName) + fmt.Sprintf(` +resource "aws_db_proxy_endpoint" "test" { + db_proxy_name = aws_db_proxy.test.name + db_proxy_endpoint_name = %[1]q + vpc_subnet_ids = aws_subnet.test.*.id + vpc_security_group_ids = [aws_security_group.test.id] +} + +resource "aws_security_group" "test2" { + name = "%[1]s-2" vpc_id = aws_vpc.test.id } -`, rName, cnt) +`, rName) } func testAccAWSDBProxyEndpointConfigTags1(rName, key1, value1 string) string { From c04ac32f52ae5a8e3aee419bd16119419b74c4a7 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 15 Apr 2021 00:41:47 +0300 Subject: [PATCH 06/16] add target role test --- aws/resource_aws_db_proxy_endpoint_test.go | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/aws/resource_aws_db_proxy_endpoint_test.go b/aws/resource_aws_db_proxy_endpoint_test.go index bdaba4feee8..b6324a4fde7 100644 --- a/aws/resource_aws_db_proxy_endpoint_test.go +++ b/aws/resource_aws_db_proxy_endpoint_test.go @@ -101,6 +101,33 @@ func TestAccAWSDBProxyEndpoint_basic(t *testing.T) { }) } +func TestAccAWSDBProxyEndpoint_targetRole(t *testing.T) { + var v rds.DBProxyEndpoint + resourceName := "aws_db_proxy_endpoint.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccDBProxyEndpointPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBProxyEndpointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBProxyEndpointConfigTargetRole(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyEndpointExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "target_role", "READ_ONLY"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAWSDBProxyEndpoint_vpcSecurityGroupIds(t *testing.T) { var dbProxy rds.DBProxyEndpoint resourceName := "aws_db_proxy_endpoint.test" @@ -427,6 +454,17 @@ resource "aws_db_proxy_endpoint" "test" { `, rName) } +func testAccAWSDBProxyEndpointConfigTargetRole(rName string) string { + return testAccAWSDBProxyEndpointConfigBase(rName) + fmt.Sprintf(` +resource "aws_db_proxy_endpoint" "test" { + db_proxy_name = aws_db_proxy.test.name + db_proxy_endpoint_name = %[1]q + vpc_subnet_ids = aws_subnet.test.*.id + target_role = "READ_ONLY" +} +`, rName) +} + func testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds1(rName string) string { return testAccAWSDBProxyEndpointConfigBase(rName) + fmt.Sprintf(` resource "aws_db_proxy_endpoint" "test" { From 0d6a2895da7c516ef4faa9d369e94a49653883cd Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 15 Apr 2021 11:27:19 +0300 Subject: [PATCH 07/16] fix sg test --- aws/resource_aws_db_proxy_endpoint_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_db_proxy_endpoint_test.go b/aws/resource_aws_db_proxy_endpoint_test.go index b6324a4fde7..8a09449bb5c 100644 --- a/aws/resource_aws_db_proxy_endpoint_test.go +++ b/aws/resource_aws_db_proxy_endpoint_test.go @@ -482,7 +482,7 @@ resource "aws_db_proxy_endpoint" "test" { db_proxy_name = aws_db_proxy.test.name db_proxy_endpoint_name = %[1]q vpc_subnet_ids = aws_subnet.test.*.id - vpc_security_group_ids = [aws_security_group.test.id] + vpc_security_group_ids = [aws_security_group.test.id, aws_security_group.test2.id] } resource "aws_security_group" "test2" { From 929e0701598216f0b1c06b89b4de38a29688a723 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 15 Apr 2021 11:41:24 +0300 Subject: [PATCH 08/16] docs --- .../docs/r/db_proxy_endpoint.html.markdown | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 website/docs/r/db_proxy_endpoint.html.markdown diff --git a/website/docs/r/db_proxy_endpoint.html.markdown b/website/docs/r/db_proxy_endpoint.html.markdown new file mode 100644 index 00000000000..651b9b4e894 --- /dev/null +++ b/website/docs/r/db_proxy_endpoint.html.markdown @@ -0,0 +1,59 @@ +--- +subcategory: "RDS" +layout: "aws" +page_title: "AWS: aws_db_proxy_endpoint" +description: |- + Provides an RDS DB proxy endpoint resource. +--- + +# Resource: aws_db_proxy_endpoint + +Provides an RDS DB proxy endpoint resource. For additional information, see the [RDS User Guide](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html#rds-proxy-endpoints). + +## Example Usage + +```terraform +resource "aws_db_proxy_endpoint" "example" { + db_proxy_name = aws_db_proxy.test.name + db_proxy_endpoint_name = "example" + vpc_subnet_ids = aws_subnet.test.*.id + target_role = "READ_ONLY" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `db_proxy_endpoint_name` - (Required) The identifier for the proxy endpoint. An identifier must begin with a letter and must contain only ASCII letters, digits, and hyphens; it can't end with a hyphen or contain two consecutive hyphens. +* `db_proxy_name` - (Required) The name of the DB proxy associated with the DB proxy endpoint that you create. +* `vpc_subnet_ids` - (Required) One or more VPC subnet IDs to associate with the new proxy. +* `vpc_security_group_ids` - (Optional) One or more VPC security group IDs to associate with the new proxy. +* `target_role` - (Optional) Indicates whether the DB proxy endpoint can be used for read/write or read-only operations. The default is `READ_WRITE`. Valid values are `READ_WRITE` and `READ_ONLY`. +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The Amazon Resource Name (ARN) for the proxy. +* `arn` - The Amazon Resource Name (ARN) for the proxy endpoint. +* `endpoint` - The endpoint that you can use to connect to the proxy. You include the endpoint value in the connection string for a database client application. +* `is_default` - Indicates whether this endpoint is the default endpoint for the associated DB proxy. +* `vpc_id` - The VPC ID of the DB proxy endpoint. + +### Timeouts + +`aws_db_proxy_endpoint` provides the following [Timeouts](https://www.terraform.io/docs/configuration/blocks/resources/syntax.html#operation-timeouts) configuration options: + +- `create` - (Default `30 minutes`) Used for creating DB proxy endpoint. +- `update` - (Default `30 minutes`) Used for modifying DB proxy endpoint. +- `delete` - (Default `60 minutes`) Used for destroying DB proxy endpoint. + +## Import + +DB proxy endpoints can be imported using the `arn`, e.g. + +``` +$ terraform import aws_db_proxy_endpoint.example example +``` From 977c80000f8319551189fd2462a2dcf687e86f8f Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 15 Apr 2021 11:49:30 +0300 Subject: [PATCH 09/16] remove arn from search --- aws/internal/service/rds/finder/finder.go | 5 +++-- aws/internal/service/rds/id.go | 10 +++++----- aws/resource_aws_db_proxy_endpoint.go | 6 ++---- website/docs/r/db_proxy_endpoint.html.markdown | 6 +++--- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/aws/internal/service/rds/finder/finder.go b/aws/internal/service/rds/finder/finder.go index 6b4d0d5dd92..8ef227f7099 100644 --- a/aws/internal/service/rds/finder/finder.go +++ b/aws/internal/service/rds/finder/finder.go @@ -34,7 +34,7 @@ func DBProxyTarget(conn *rds.RDS, dbProxyName, targetGroupName, targetType, rdsR // DBProxyEndpoint returns matching DBProxyEndpoint. func DBProxyEndpoint(conn *rds.RDS, id string) (*rds.DBProxyEndpoint, error) { - dbProxyName, dbProxyEndpointName, dbProxyEndpointArn, err := tfrds.ResourceAwsDbProxyEndpointParseID(id) + dbProxyName, dbProxyEndpointName, err := tfrds.ResourceAwsDbProxyEndpointParseID(id) if err != nil { return nil, err } @@ -51,7 +51,8 @@ func DBProxyEndpoint(conn *rds.RDS, id string) (*rds.DBProxyEndpoint, error) { } for _, endpoint := range page.DBProxyEndpoints { - if aws.StringValue(endpoint.DBProxyEndpointArn) == dbProxyEndpointArn { + if aws.StringValue(endpoint.DBProxyEndpointName) == dbProxyEndpointName && + aws.StringValue(endpoint.DBProxyName) == dbProxyName { dbProxyEndpoint = endpoint return false } diff --git a/aws/internal/service/rds/id.go b/aws/internal/service/rds/id.go index 2276d47a4e6..f8a8250f33a 100644 --- a/aws/internal/service/rds/id.go +++ b/aws/internal/service/rds/id.go @@ -5,10 +5,10 @@ import ( "strings" ) -func ResourceAwsDbProxyEndpointParseID(id string) (string, string, string, error) { - idParts := strings.SplitN(id, "/", 3) - if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { - return "", "", "", fmt.Errorf("unexpected format of ID (%s), expected db_proxy_name/db_proxy_endpoint_name/arn", id) +func ResourceAwsDbProxyEndpointParseID(id string) (string, string, error) { + idParts := strings.SplitN(id, "/", 2) + if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%s), expected db_proxy_name/db_proxy_endpoint_name", id) } - return idParts[0], idParts[1], idParts[2], nil + return idParts[0], idParts[1], nil } diff --git a/aws/resource_aws_db_proxy_endpoint.go b/aws/resource_aws_db_proxy_endpoint.go index 1ee6924d459..361de038bf3 100644 --- a/aws/resource_aws_db_proxy_endpoint.go +++ b/aws/resource_aws_db_proxy_endpoint.go @@ -103,15 +103,13 @@ func resourceAwsDbProxyEndpointCreate(d *schema.ResourceData, meta interface{}) params.VpcSecurityGroupIds = expandStringSet(v) } - resp, err := conn.CreateDBProxyEndpoint(¶ms) + _, err := conn.CreateDBProxyEndpoint(¶ms) if err != nil { return fmt.Errorf("error Creating RDS DB Proxy Endpoint (%s/%s): %w", dbProxyName, dbProxyEndpointName, err) } - dbProxyEndpoint := resp.DBProxyEndpoint - - d.SetId(strings.Join([]string{dbProxyName, dbProxyEndpointName, aws.StringValue(dbProxyEndpoint.DBProxyEndpointArn)}, "/")) + d.SetId(strings.Join([]string{dbProxyName, dbProxyEndpointName}, "/")) if _, err := waiter.DBProxyEndpointAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return fmt.Errorf("error waiting for RDS DB Proxy Endpoint (%s) to become available: %w", d.Id(), err) diff --git a/website/docs/r/db_proxy_endpoint.html.markdown b/website/docs/r/db_proxy_endpoint.html.markdown index 651b9b4e894..7bf8b30c1ad 100644 --- a/website/docs/r/db_proxy_endpoint.html.markdown +++ b/website/docs/r/db_proxy_endpoint.html.markdown @@ -36,7 +36,7 @@ The following arguments are supported: In addition to all arguments above, the following attributes are exported: -* `id` - The Amazon Resource Name (ARN) for the proxy. +* `id` - The name of the proxy and proxy endpoint seperated by `/`, `DB-PROXY-NAME/DB-PROXY-ENDPOINT-NAME`. * `arn` - The Amazon Resource Name (ARN) for the proxy endpoint. * `endpoint` - The endpoint that you can use to connect to the proxy. You include the endpoint value in the connection string for a database client application. * `is_default` - Indicates whether this endpoint is the default endpoint for the associated DB proxy. @@ -52,8 +52,8 @@ In addition to all arguments above, the following attributes are exported: ## Import -DB proxy endpoints can be imported using the `arn`, e.g. +DB proxy endpoints can be imported using the `DB-PROXY-NAME/DB-PROXY-ENDPOINT-NAME`, e.g. ``` -$ terraform import aws_db_proxy_endpoint.example example +$ terraform import aws_db_proxy_endpoint.example example/example ``` From 5294f5160cfd6291d33b2b6c92005a2bcdba67c0 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 15 Apr 2021 13:23:27 +0300 Subject: [PATCH 10/16] sg test --- aws/resource_aws_db_proxy_endpoint_test.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/aws/resource_aws_db_proxy_endpoint_test.go b/aws/resource_aws_db_proxy_endpoint_test.go index 8a09449bb5c..4e5af914cb0 100644 --- a/aws/resource_aws_db_proxy_endpoint_test.go +++ b/aws/resource_aws_db_proxy_endpoint_test.go @@ -161,14 +161,6 @@ func TestAccAWSDBProxyEndpoint_vpcSecurityGroupIds(t *testing.T) { resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test2", "id"), ), }, - { - Config: testAccAWSDBProxyEndpointConfigVpcSecurityGroupIds1(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSDBProxyEndpointExists(resourceName, &dbProxy), - resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "vpc_security_group_ids.*", "aws_security_group.test", "id"), - ), - }, }, }) } From cb64db925efeadca6ec3892937db140c73a96c9c Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 15 Apr 2021 13:25:45 +0300 Subject: [PATCH 11/16] changelog --- .changelog/18881.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/18881.txt diff --git a/.changelog/18881.txt b/.changelog/18881.txt new file mode 100644 index 00000000000..a87a64d88cf --- /dev/null +++ b/.changelog/18881.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_rds_proxy_endpoint +``` From 3a8bde574d52e7a5599840eb8396350a4c1cc090 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 15 Apr 2021 13:26:33 +0300 Subject: [PATCH 12/16] docs --- website/docs/r/db_proxy_endpoint.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/db_proxy_endpoint.html.markdown b/website/docs/r/db_proxy_endpoint.html.markdown index 7bf8b30c1ad..7c763bb9fa1 100644 --- a/website/docs/r/db_proxy_endpoint.html.markdown +++ b/website/docs/r/db_proxy_endpoint.html.markdown @@ -36,7 +36,7 @@ The following arguments are supported: In addition to all arguments above, the following attributes are exported: -* `id` - The name of the proxy and proxy endpoint seperated by `/`, `DB-PROXY-NAME/DB-PROXY-ENDPOINT-NAME`. +* `id` - The name of the proxy and proxy endpoint separated by `/`, `DB-PROXY-NAME/DB-PROXY-ENDPOINT-NAME`. * `arn` - The Amazon Resource Name (ARN) for the proxy endpoint. * `endpoint` - The endpoint that you can use to connect to the proxy. You include the endpoint value in the connection string for a database client application. * `is_default` - Indicates whether this endpoint is the default endpoint for the associated DB proxy. From 04c3eb4591d37aa04f77e163372a212320d27a87 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 15 Apr 2021 13:30:40 +0300 Subject: [PATCH 13/16] format --- aws/resource_aws_db_proxy_endpoint_test.go | 86 +++++----------------- 1 file changed, 18 insertions(+), 68 deletions(-) diff --git a/aws/resource_aws_db_proxy_endpoint_test.go b/aws/resource_aws_db_proxy_endpoint_test.go index 4e5af914cb0..8d1b3dea158 100644 --- a/aws/resource_aws_db_proxy_endpoint_test.go +++ b/aws/resource_aws_db_proxy_endpoint_test.go @@ -13,56 +13,6 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/finder" ) -// func init() { -// resource.AddTestSweepers("aws_db_proxy_endpoint", &resource.Sweeper{ -// Name: "aws_db_proxy_endpoint", -// F: testSweepRdsDbProxies, -// }) -// } - -// func testSweepRdsDbProxies(region string) error { -// client, err := sharedClientForRegion(region) -// if err != nil { -// return fmt.Errorf("Error getting client: %s", err) -// } -// conn := client.(*AWSClient).rdsconn - -// err = conn.DescribeDBProxiesPages(&rds.DescribeDBProxiesInput{}, func(out *rds.DescribeDBProxiesOutput, lastPage bool) bool { -// for _, dbpg := range out.DBProxies { -// if dbpg == nil { -// continue -// } - -// input := &rds.DeleteDBProxyInput{ -// DBProxyName: dbpg.DBProxyName, -// } -// name := aws.StringValue(dbpg.DBProxyName) - -// log.Printf("[INFO] Deleting DB Proxy: %s", name) - -// _, err := conn.DeleteDBProxy(input) - -// if err != nil { -// log.Printf("[ERROR] Failed to delete DB Proxy %s: %s", name, err) -// continue -// } -// } - -// return !lastPage -// }) - -// if testSweepSkipSweepError(err) { -// log.Printf("[WARN] Skipping RDS DB Proxy sweep for %s: %s", region, err) -// return nil -// } - -// if err != nil { -// return fmt.Errorf("Error retrieving DB Proxies: %s", err) -// } - -// return nil -// } - func TestAccAWSDBProxyEndpoint_basic(t *testing.T) { var v rds.DBProxyEndpoint resourceName := "aws_db_proxy_endpoint.test" @@ -412,26 +362,26 @@ resource "aws_subnet" "test" { } resource "aws_db_proxy" "test" { - depends_on = [ - aws_secretsmanager_secret_version.test, - aws_iam_role_policy.test - ] + depends_on = [ + aws_secretsmanager_secret_version.test, + aws_iam_role_policy.test + ] - name = %[1]q - debug_logging = false - engine_family = "MYSQL" - idle_client_timeout = 1800 - require_tls = true - role_arn = aws_iam_role.test.arn - vpc_security_group_ids = [aws_security_group.test.id] - vpc_subnet_ids = aws_subnet.test.*.id + name = %[1]q + debug_logging = false + engine_family = "MYSQL" + idle_client_timeout = 1800 + require_tls = true + role_arn = aws_iam_role.test.arn + vpc_security_group_ids = [aws_security_group.test.id] + vpc_subnet_ids = aws_subnet.test.*.id - auth { - auth_scheme = "SECRETS" - description = "test" - iam_auth = "DISABLED" - secret_arn = aws_secretsmanager_secret.test.arn - } + auth { + auth_scheme = "SECRETS" + description = "test" + iam_auth = "DISABLED" + secret_arn = aws_secretsmanager_secret.test.arn + } } `, rName) } From cbd4752aaac041ec29e3aa013e1c118ada9ab543 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 15 Apr 2021 13:50:02 +0300 Subject: [PATCH 14/16] format --- aws/resource_aws_db_proxy_endpoint_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_db_proxy_endpoint_test.go b/aws/resource_aws_db_proxy_endpoint_test.go index 8d1b3dea158..dbb77f6d89f 100644 --- a/aws/resource_aws_db_proxy_endpoint_test.go +++ b/aws/resource_aws_db_proxy_endpoint_test.go @@ -366,7 +366,7 @@ resource "aws_db_proxy" "test" { aws_secretsmanager_secret_version.test, aws_iam_role_policy.test ] - + name = %[1]q debug_logging = false engine_family = "MYSQL" @@ -375,14 +375,14 @@ resource "aws_db_proxy" "test" { role_arn = aws_iam_role.test.arn vpc_security_group_ids = [aws_security_group.test.id] vpc_subnet_ids = aws_subnet.test.*.id - + auth { auth_scheme = "SECRETS" description = "test" iam_auth = "DISABLED" secret_arn = aws_secretsmanager_secret.test.arn } - } +} `, rName) } From 1011ffb2098ca284d6de930cc6aa8ea067ef539c Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Thu, 29 Apr 2021 14:22:28 -0400 Subject: [PATCH 15/16] Apply suggestions from code review --- aws/resource_aws_db_proxy_endpoint.go | 29 ++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_db_proxy_endpoint.go b/aws/resource_aws_db_proxy_endpoint.go index 361de038bf3..ed77c4eaebc 100644 --- a/aws/resource_aws_db_proxy_endpoint.go +++ b/aws/resource_aws_db_proxy_endpoint.go @@ -25,7 +25,7 @@ func resourceAwsDbProxyEndpoint() *schema.Resource { Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, - + CustomizeDiff: SetTagsDiff, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(30 * time.Minute), Update: schema.DefaultTimeout(30 * time.Minute), @@ -57,7 +57,8 @@ func resourceAwsDbProxyEndpoint() *schema.Resource { Type: schema.TypeBool, Computed: true, }, - "tags": tagsSchema(), + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), "target_role": { Type: schema.TypeString, Optional: true, @@ -87,6 +88,8 @@ func resourceAwsDbProxyEndpoint() *schema.Resource { func resourceAwsDbProxyEndpointCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) dbProxyName := d.Get("db_proxy_name").(string) dbProxyEndpointName := d.Get("db_proxy_endpoint_name").(string) @@ -96,7 +99,7 @@ func resourceAwsDbProxyEndpointCreate(d *schema.ResourceData, meta interface{}) DBProxyEndpointName: aws.String(dbProxyEndpointName), TargetRole: aws.String(d.Get("target_role").(string)), VpcSubnetIds: expandStringSet(d.Get("vpc_subnet_ids").(*schema.Set)), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().RdsTags(), + Tags: tags.IgnoreAws().RdsTags(), } if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 { @@ -120,17 +123,18 @@ func resourceAwsDbProxyEndpointCreate(d *schema.ResourceData, meta interface{}) func resourceAwsDbProxyEndpointRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, d.Id()) - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) { + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) { log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyEndpointNotFoundFault) { + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyEndpointNotFoundFault) { log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id()) d.SetId("") return nil @@ -141,6 +145,10 @@ func resourceAwsDbProxyEndpointRead(d *schema.ResourceData, meta interface{}) er } if dbProxyEndpoint == nil { + if d.IsNewResource() { + return fmt.Errorf("error reading RDS DB Proxy Endpoint (%s): not found after creation") + } + log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id()) d.SetId("") return nil @@ -164,8 +172,15 @@ func resourceAwsDbProxyEndpointRead(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("Error listing tags for RDS DB Proxy Endpoint (%s): %w", endpointArn, err) } - if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("Error setting tags: %w", err) + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) } return nil From d574a4b5cc4c29038ae8b153b40fd9b112402e38 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Thu, 29 Apr 2021 14:25:49 -0400 Subject: [PATCH 16/16] Update aws/resource_aws_db_proxy_endpoint.go --- aws/resource_aws_db_proxy_endpoint.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_db_proxy_endpoint.go b/aws/resource_aws_db_proxy_endpoint.go index ed77c4eaebc..148ab3be6c1 100644 --- a/aws/resource_aws_db_proxy_endpoint.go +++ b/aws/resource_aws_db_proxy_endpoint.go @@ -146,7 +146,7 @@ func resourceAwsDbProxyEndpointRead(d *schema.ResourceData, meta interface{}) er if dbProxyEndpoint == nil { if d.IsNewResource() { - return fmt.Errorf("error reading RDS DB Proxy Endpoint (%s): not found after creation") + return fmt.Errorf("error reading RDS DB Proxy Endpoint (%s): not found after creation", d.Id()) } log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id())