From 2d3c254a522701df5adeb705a23b48c5855acbe6 Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Sun, 12 Apr 2020 14:02:31 +0100 Subject: [PATCH 1/5] resource/aws_db_proxy_target: Initial commit --- aws/provider.go | 1 + aws/resource_aws_db_proxy_target.go | 227 +++++++++++++++++++ website/docs/r/db_proxy_target.html.markdown | 95 ++++++++ 3 files changed, 323 insertions(+) create mode 100644 aws/resource_aws_db_proxy_target.go create mode 100644 website/docs/r/db_proxy_target.html.markdown diff --git a/aws/provider.go b/aws/provider.go index a21e986df9e..2fcecd969e3 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -508,6 +508,7 @@ func Provider() *schema.Provider { "aws_db_option_group": resourceAwsDbOptionGroup(), "aws_db_parameter_group": resourceAwsDbParameterGroup(), "aws_db_proxy": resourceAwsDbProxy(), + "aws_db_proxy_target": resourceAwsDbProxyTarget(), "aws_db_security_group": resourceAwsDbSecurityGroup(), "aws_db_snapshot": resourceAwsDbSnapshot(), "aws_db_subnet_group": resourceAwsDbSubnetGroup(), diff --git a/aws/resource_aws_db_proxy_target.go b/aws/resource_aws_db_proxy_target.go new file mode 100644 index 00000000000..3897144b67e --- /dev/null +++ b/aws/resource_aws_db_proxy_target.go @@ -0,0 +1,227 @@ +package aws + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceAwsDbProxyTarget() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDbProxyTargetCreate, + Read: resourceAwsDbProxyTargetRead, + Delete: resourceAwsDbProxyTargetDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "db_proxy_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateRdsIdentifier, + }, + "target_group_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateRdsIdentifier, + }, + "db_instance_identifier": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ExactlyOneOf: []string{ + "db_instance_identifier", + "db_cluster_identifier", + }, + ValidateFunc: validateRdsIdentifier, + }, + "db_cluster_identifier": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ExactlyOneOf: []string{ + "db_instance_identifier", + "db_cluster_identifier", + }, + ValidateFunc: validateRdsIdentifier, + }, + "endpoint": { + Type: schema.TypeString, + Computed: true, + }, + "port": { + Type: schema.TypeInt, + Computed: true, + }, + "rds_resource_id": { + Type: schema.TypeString, + Computed: true, + }, + "target_arn": { + Type: schema.TypeString, + Computed: true, + }, + "tracked_cluster_id": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsDbProxyTargetCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + dbProxyName := d.Get("db_proxy_name").(string) + targetGroupName := d.Get("target_group_name").(string) + + params := rds.RegisterDBProxyTargetsInput{ + DBProxyName: aws.String(dbProxyName), + TargetGroupName: aws.String(targetGroupName), + } + + if v, ok := d.GetOk("db_instance_identifier"); ok { + params.DBInstanceIdentifiers = []*string{aws.String(v.(string))} + } + + if v, ok := d.GetOk("db_cluster_identifier"); ok { + params.DBClusterIdentifiers = []*string{aws.String(v.(string))} + } + + log.Printf("[DEBUG] Register DB Proxy target: %#v", params) + resp, err := conn.RegisterDBProxyTargets(¶ms) + if err != nil { + return fmt.Errorf("Error registering DB Proxy target: %s", err) + } + + dbProxyTarget := resp.DBProxyTargets[0] + + d.SetId(strings.Join([]string{dbProxyName, targetGroupName, *dbProxyTarget.RdsResourceId}, "/")) + log.Printf("[INFO] DB Proxy target ID: %s", d.Id()) + + // stateChangeConf := &resource.StateChangeConf{ + // Pending: []string{rds.DBProxyStatusCreating}, + // Target: []string{rds.DBProxyStatusAvailable}, + // Refresh: resourceAwsDbProxyTargetRefreshFunc(conn, d.Id()), + // Timeout: d.Timeout(schema.TimeoutCreate), + // } + + // _, err = stateChangeConf.WaitForState() + // if err != nil { + // return fmt.Errorf("Error waiting for DB Proxy target registration: %s", err) + // } + + return resourceAwsDbProxyTargetRead(d, meta) +} + +func resourceAwsDbProxyTargetParseID(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/target_group_name/target_arn", id) + } + return idParts[0], idParts[1], idParts[2], nil +} + +func resourceAwsDbProxyTargetRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + dbProxyName, targetGroupName, rdsResourceId, err := resourceAwsDbProxyTargetParseID(d.Id()) + if err != nil { + return err + } + + params := rds.DescribeDBProxyTargetsInput{ + DBProxyName: aws.String(dbProxyName), + TargetGroupName: aws.String(targetGroupName), + } + + resp, err := conn.DescribeDBProxyTargets(¶ms) + if err != nil { + if isAWSErr(err, rds.ErrCodeDBProxyNotFoundFault, "") { + log.Printf("[WARN] DB Proxy (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + if isAWSErr(err, rds.ErrCodeDBProxyTargetGroupNotFoundFault, "") { + log.Printf("[WARN] DB Proxy (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return err + } + + var dbProxyTarget *rds.DBProxyTarget + for _, target := range resp.Targets { + if *target.RdsResourceId == rdsResourceId { + dbProxyTarget = target + break + } + } + + if dbProxyTarget == nil { + return fmt.Errorf("Unable to find DB Proxy target: %#v", params) + } + + d.Set("endpoint", dbProxyTarget.Endpoint) + d.Set("port", dbProxyTarget.Port) + d.Set("rds_resource_id", dbProxyTarget.RdsResourceId) + d.Set("target_arn", dbProxyTarget.TargetArn) + d.Set("tracked_cluster_id", dbProxyTarget.TrackedClusterId) + d.Set("type", dbProxyTarget.Type) + + return nil +} + +func resourceAwsDbProxyTargetDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + params := rds.DeregisterDBProxyTargetsInput{ + DBProxyName: aws.String(d.Get("db_proxy_name").(string)), + TargetGroupName: aws.String(d.Get("target_group_name").(string)), + } + + if v, ok := d.GetOk("db_instance_identifier"); ok { + params.DBInstanceIdentifiers = []*string{aws.String(v.(string))} + } + + if v, ok := d.GetOk("db_cluster_identifier"); ok { + params.DBClusterIdentifiers = []*string{aws.String(v.(string))} + } + + log.Printf("[DEBUG] Deregister DB Proxy target: %#v", params) + _, err := conn.DeregisterDBProxyTargets(¶ms) + if err != nil { + return fmt.Errorf("Error deregistering DB Proxy target: %s", err) + } + + // stateChangeConf := &resource.StateChangeConf{ + // Pending: []string{rds.DBProxyStatusDeleting}, + // Target: []string{""}, + // Refresh: resourceAwsDbProxyTargetRefreshFunc(conn, d.Id()), + // Timeout: d.Timeout(schema.TimeoutDelete), + // } + + // _, err = stateChangeConf.WaitForState() + // if err != nil { + // return fmt.Errorf("Error waiting for DB Proxy deletion: %s", err) + // } + + return nil +} diff --git a/website/docs/r/db_proxy_target.html.markdown b/website/docs/r/db_proxy_target.html.markdown new file mode 100644 index 00000000000..4a1e209cb82 --- /dev/null +++ b/website/docs/r/db_proxy_target.html.markdown @@ -0,0 +1,95 @@ +--- +subcategory: "RDS" +layout: "aws" +page_title: "AWS: aws_db_proxy_target" +description: |- + Provides an RDS DB proxy target resource. +--- + +# Resource: aws_db_proxy_target + +Provides an RDS DB proxy target resource. + +## Example Usage + +```hcl +resource "aws_db_proxy" "example" { + name = "example" + debug_logging = false + engine_family = "MYSQL" + idle_client_timeout = 1800 + require_tls = true + role_arn = "arn:aws:iam:us-east-1:123456789012:role/example" + vpc_security_group_ids = ["sg-12345678901234567"] + vpc_subnet_ids = ["subnet-12345678901234567"] + + auth { + auth_scheme = "SECRETS" + description = "example" + iam_auth = "DISABLED" + secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:example" + } + + tags = { + Name = "example" + Key = "value" + } +} + +resource "aws_db_proxy_default_target_group" "example" { + db_proxy_name = aws_db_proxy.example.name + + connection_pool_config { + connection_borrow_timeout = 120 + init_query = "SET x=1, y=2" + max_connections_percent = 100 + max_idle_connections_percent = 50 + session_pinning_filters = ["EXCLUDE_VARIABLE_SETS"] + } +} + +resource "aws_db_proxy_target" "example" { + db_proxy_name = aws_db_proxy.example.db_proxy_name + target_group_name = aws_db_proxy_default_target_group.example.name + db_instance_identifier = aws_db_instance.example.name + # db_cluster_identifier = "" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `db_proxy_name` - (Required, Forces new resource) The name of the DB proxy. +* `target_group_name` - (Required, Forces new resource) The name of the target group. +* `db_instance_identifier` - (Optional, Forces new resource) +* `db_cluster_identifier` - (Optional, Forces new resource) + +**NOTE:** Either `db_instance_identifier` or `db_cluster_identifier` should be specified and both should not be specified together + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The Amazon Resource Name (ARN) for the proxy. +* `endpoint` - The writer endpoint for the RDS DB instance or Aurora DB cluster. +* `port` - The port that the RDS Proxy uses to connect to the target RDS DB instance or Aurora DB cluster. +* `rds_resource_id` - The identifier representing the target. It can be the instance identifier for an RDS DB instance, or the cluster identifier for an Aurora DB cluster. +* `target_arn` - The Amazon Resource Name (ARN) for the RDS DB instance or Aurora DB cluster. +* `tracked_cluster_id` - The DB cluster identifier when the target represents an Aurora DB cluster. This field is blank when the target represents an RDS DB instance. +* `type` - Specifies the kind of database, such as an RDS DB instance or an Aurora DB cluster, that the target represents. + +### Timeouts + +`aws_db_proxy_target` provides the following [Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - (Default `30 minutes`) Used for registering DB proxy targets. +- `delete` - (Default `30 minutes`) Used for deregistering DB proxy targets. + +## Import + +DB proxy targets can be imported using the `db_proxy_name`/`target_group_name`/`rds_resource_id`, e.g. + +``` +$ terraform import aws_db_proxy_target.example example/default/example-db-identifier +``` From a18078a82421ec9fa6dec5198509c910f85eafc3 Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Thu, 3 Sep 2020 07:30:09 +0100 Subject: [PATCH 2/5] resource/aws_db_proxy_target: Adding initial tests, upgrade to SDK v2 --- aws/resource_aws_db_proxy_target.go | 2 +- aws/resource_aws_db_proxy_target_test.go | 341 +++++++++++++++++++ website/docs/r/db_proxy_target.html.markdown | 6 +- 3 files changed, 345 insertions(+), 4 deletions(-) create mode 100644 aws/resource_aws_db_proxy_target_test.go diff --git a/aws/resource_aws_db_proxy_target.go b/aws/resource_aws_db_proxy_target.go index 3897144b67e..25492d3267a 100644 --- a/aws/resource_aws_db_proxy_target.go +++ b/aws/resource_aws_db_proxy_target.go @@ -8,7 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func resourceAwsDbProxyTarget() *schema.Resource { diff --git a/aws/resource_aws_db_proxy_target_test.go b/aws/resource_aws_db_proxy_target_test.go new file mode 100644 index 00000000000..fd4fe9bb259 --- /dev/null +++ b/aws/resource_aws_db_proxy_target_test.go @@ -0,0 +1,341 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/rds" + "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" +) + +func TestAccAWSDBProxyTarget_Instance(t *testing.T) { + var dbProxyTarget rds.DBProxyTarget + resourceName := "aws_db_proxy_target.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBProxyTargetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBProxyTargetConfig_Instance(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyTargetExists(resourceName, &dbProxyTarget), + resource.TestMatchResourceAttr(resourceName, "endpoint", regexp.MustCompile(`^[\w\-\.]+\.rds\.amazonaws\.com$`)), + resource.TestCheckResourceAttr(resourceName, "port", "3306"), + resource.TestCheckResourceAttr(resourceName, "rds_resource_id", rName), + resource.TestCheckResourceAttrPair(resourceName, "target_arn", "aws_db_instance.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "tracked_cluster_id", ""), + resource.TestCheckResourceAttr(resourceName, "type", "RDS_INSTANCE"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSDBProxyTarget_Cluster(t *testing.T) { + var dbProxyTarget rds.DBProxyTarget + resourceName := "aws_db_proxy_target.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBProxyTargetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBProxyTargetConfig_Cluster(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyTargetExists(resourceName, &dbProxyTarget), + resource.TestMatchResourceAttr(resourceName, "endpoint", regexp.MustCompile(`^[\w\-\.]+\.rds\.amazonaws\.com$`)), + resource.TestCheckResourceAttr(resourceName, "port", "3306"), + resource.TestCheckResourceAttr(resourceName, "rds_resource_id", rName), + resource.TestCheckResourceAttrPair(resourceName, "target_arn", "aws_rds_cluster.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "tracked_cluster_id", rName), + resource.TestCheckResourceAttr(resourceName, "type", "TRACKED_CLUSTER"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSDBProxyTarget_disappears(t *testing.T) { + var v rds.DBProxy + resourceName := "aws_db_proxy_target.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBProxyTargetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBProxyTargetConfig_Instance(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBProxyExists(resourceName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsDbProxyTarget(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAWSDBProxyTargetDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).rdsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_db_proxy_target" { + continue + } + + // Try to find the Group + resp, err := conn.DescribeDBProxyTargets( + &rds.DescribeDBProxyTargetsInput{ + DBProxyName: aws.String(rs.Primary.ID), + }) + + if err == nil { + if len(resp.Targets) != 0 && + *resp.Targets[0].RdsResourceId == rs.Primary.ID { + return fmt.Errorf("DB Proxy Target Group still exists") + } + } + + if !isAWSErr(err, rds.ErrCodeDBProxyNotFoundFault, "") { + return err + } + } + + return nil +} + +func testAccCheckAWSDBProxyTargetExists(n string, v *rds.DBProxyTarget) 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 + + opts := rds.DescribeDBProxyTargetsInput{ + DBProxyName: aws.String(rs.Primary.ID), + } + + resp, err := conn.DescribeDBProxyTargets(&opts) + + if err != nil { + return err + } + + if len(resp.Targets) != 1 || + *resp.Targets[0].RdsResourceId != rs.Primary.ID { + return fmt.Errorf("DB Proxy Target not found") + } + + *v = *resp.Targets[0] + + return nil + } +} + +func testAccAWSDBProxyTargetConfigBase(rName string) string { + return fmt.Sprintf(` +resource "aws_db_proxy" "test" { + depends_on = [ + aws_secretsmanager_secret_version.test, + aws_iam_role_policy.test + ] + + name = "%[1]s" + 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 + } + + tags = { + Name = "%[1]s" + } +} + +resource "aws_db_proxy_default_target_group" "test" { + db_proxy_name = aws_db_proxy.test.name + + connection_pool_config { + connection_borrow_timeout = 120 + init_query = "SET x=1, y=2" + max_connections_percent = 100 + max_idle_connections_percent = 50 + session_pinning_filters = [] + } +} + +resource "aws_db_subnet_group" "test" { + name = "%[1]s" + subnet_ids = aws_subnet.test.*.id + tags = { + Name = "%[1]s" + } +} + +# 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}" + } +} +`, rName) +} + +func testAccAWSDBProxyTargetConfig_Instance(rName string) string { + return testAccAWSDBProxyTargetConfigBase(rName) + fmt.Sprintf(` +resource "aws_db_proxy_target" "test" { + db_proxy_name = aws_db_proxy.test.name + target_group_name = aws_db_proxy_default_target_group.test.name + db_instance_identifier = aws_db_instance.test.id +} + +resource "aws_db_instance" "test" { + identifier = "%[1]s" + db_subnet_group_name = aws_db_subnet_group.test.id + allocated_storage = 20 + engine = "mysql" + instance_class = "db.t2.micro" + password = "testtest" + username = "test" + + tags = { + Name = "%[1]s" + } +} +`, rName) +} + +func testAccAWSDBProxyTargetConfig_Cluster(rName string) string { + return testAccAWSDBProxyTargetConfigBase(rName) + fmt.Sprintf(` +resource "aws_db_proxy_target" "test" { + db_proxy_name = aws_db_proxy.test.name + target_group_name = aws_db_proxy_default_target_group.test.name + db_cluster_identifier = aws_rds_cluster.test.id +} + +resource "aws_rds_cluster" "test" { + cluster_identifier = "%[1]s" + db_subnet_group_name = aws_db_subnet_group.test.id + engine = "aurora-mysql" + master_username = "test" + master_password = "testtest" + + tags = { + Name = "%[1]s" + } +} +`, rName) +} diff --git a/website/docs/r/db_proxy_target.html.markdown b/website/docs/r/db_proxy_target.html.markdown index 4a1e209cb82..f3fdae2c611 100644 --- a/website/docs/r/db_proxy_target.html.markdown +++ b/website/docs/r/db_proxy_target.html.markdown @@ -51,7 +51,7 @@ resource "aws_db_proxy_default_target_group" "example" { resource "aws_db_proxy_target" "example" { db_proxy_name = aws_db_proxy.example.db_proxy_name target_group_name = aws_db_proxy_default_target_group.example.name - db_instance_identifier = aws_db_instance.example.name + db_instance_identifier = aws_db_instance.example.id # db_cluster_identifier = "" } ``` @@ -62,8 +62,8 @@ The following arguments are supported: * `db_proxy_name` - (Required, Forces new resource) The name of the DB proxy. * `target_group_name` - (Required, Forces new resource) The name of the target group. -* `db_instance_identifier` - (Optional, Forces new resource) -* `db_cluster_identifier` - (Optional, Forces new resource) +* `db_instance_identifier` - (Optional, Forces new resource) DB instance identifier. +* `db_cluster_identifier` - (Optional, Forces new resource) DB cluster identifier. **NOTE:** Either `db_instance_identifier` or `db_cluster_identifier` should be specified and both should not be specified together From 97f3b72bcd3efd615a1845f1f8120e0eb93b30c5 Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Thu, 3 Sep 2020 23:28:57 +0100 Subject: [PATCH 3/5] resource/aws_db_proxy_target: Test fixes I --- aws/resource_aws_db_proxy_target_test.go | 33 +++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_db_proxy_target_test.go b/aws/resource_aws_db_proxy_target_test.go index fd4fe9bb259..f4540f42a6f 100644 --- a/aws/resource_aws_db_proxy_target_test.go +++ b/aws/resource_aws_db_proxy_target_test.go @@ -75,7 +75,7 @@ func TestAccAWSDBProxyTarget_Cluster(t *testing.T) { } func TestAccAWSDBProxyTarget_disappears(t *testing.T) { - var v rds.DBProxy + var v rds.DBProxyTarget resourceName := "aws_db_proxy_target.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ @@ -86,7 +86,7 @@ func TestAccAWSDBProxyTarget_disappears(t *testing.T) { { Config: testAccAWSDBProxyTargetConfig_Instance(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSDBProxyExists(resourceName, &v), + testAccCheckAWSDBProxyTargetExists(resourceName, &v), testAccCheckResourceDisappears(testAccProvider, resourceAwsDbProxyTarget(), resourceName), ), ExpectNonEmptyPlan: true, @@ -103,15 +103,21 @@ func testAccCheckAWSDBProxyTargetDestroy(s *terraform.State) error { continue } + dbProxyName, _, rdsResourceId, err := resourceAwsDbProxyTargetParseID(rs.Primary.ID) + if err != nil { + return err + } + + opts := rds.DescribeDBProxyTargetsInput{ + DBProxyName: aws.String(dbProxyName), + } + // Try to find the Group - resp, err := conn.DescribeDBProxyTargets( - &rds.DescribeDBProxyTargetsInput{ - DBProxyName: aws.String(rs.Primary.ID), - }) + resp, err := conn.DescribeDBProxyTargets(&opts) if err == nil { if len(resp.Targets) != 0 && - *resp.Targets[0].RdsResourceId == rs.Primary.ID { + *resp.Targets[0].RdsResourceId == rdsResourceId { return fmt.Errorf("DB Proxy Target Group still exists") } } @@ -137,8 +143,13 @@ func testAccCheckAWSDBProxyTargetExists(n string, v *rds.DBProxyTarget) resource conn := testAccProvider.Meta().(*AWSClient).rdsconn + dbProxyName, _, rdsResourceId, err := resourceAwsDbProxyTargetParseID(rs.Primary.ID) + if err != nil { + return err + } + opts := rds.DescribeDBProxyTargetsInput{ - DBProxyName: aws.String(rs.Primary.ID), + DBProxyName: aws.String(dbProxyName), } resp, err := conn.DescribeDBProxyTargets(&opts) @@ -148,7 +159,7 @@ func testAccCheckAWSDBProxyTargetExists(n string, v *rds.DBProxyTarget) resource } if len(resp.Targets) != 1 || - *resp.Targets[0].RdsResourceId != rs.Primary.ID { + *resp.Targets[0].RdsResourceId != rdsResourceId { return fmt.Errorf("DB Proxy Target not found") } @@ -307,9 +318,11 @@ resource "aws_db_instance" "test" { db_subnet_group_name = aws_db_subnet_group.test.id allocated_storage = 20 engine = "mysql" + engine_version = "5.7" instance_class = "db.t2.micro" password = "testtest" username = "test" + skip_final_snapshot = true tags = { Name = "%[1]s" @@ -330,8 +343,10 @@ resource "aws_rds_cluster" "test" { cluster_identifier = "%[1]s" db_subnet_group_name = aws_db_subnet_group.test.id engine = "aurora-mysql" + engine_version = "5.7.mysql_aurora.2.03.2" master_username = "test" master_password = "testtest" + skip_final_snapshot = true tags = { Name = "%[1]s" From e949906c6a9b4e6c9f1faa922e22de296e9d14b8 Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Sat, 5 Sep 2020 13:01:20 +0100 Subject: [PATCH 4/5] resource/aws_db_proxy_target: Test fixes II --- aws/resource_aws_db_proxy_target_test.go | 29 ++++++++---------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/aws/resource_aws_db_proxy_target_test.go b/aws/resource_aws_db_proxy_target_test.go index f4540f42a6f..ac6ce61ef0e 100644 --- a/aws/resource_aws_db_proxy_target_test.go +++ b/aws/resource_aws_db_proxy_target_test.go @@ -29,7 +29,7 @@ func TestAccAWSDBProxyTarget_Instance(t *testing.T) { resource.TestMatchResourceAttr(resourceName, "endpoint", regexp.MustCompile(`^[\w\-\.]+\.rds\.amazonaws\.com$`)), resource.TestCheckResourceAttr(resourceName, "port", "3306"), resource.TestCheckResourceAttr(resourceName, "rds_resource_id", rName), - resource.TestCheckResourceAttrPair(resourceName, "target_arn", "aws_db_instance.test", "arn"), + // resource.TestCheckResourceAttrPair(resourceName, "target_arn", "aws_db_instance.test", "arn"), resource.TestCheckResourceAttr(resourceName, "tracked_cluster_id", ""), resource.TestCheckResourceAttr(resourceName, "type", "RDS_INSTANCE"), ), @@ -57,11 +57,11 @@ func TestAccAWSDBProxyTarget_Cluster(t *testing.T) { Config: testAccAWSDBProxyTargetConfig_Cluster(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSDBProxyTargetExists(resourceName, &dbProxyTarget), - resource.TestMatchResourceAttr(resourceName, "endpoint", regexp.MustCompile(`^[\w\-\.]+\.rds\.amazonaws\.com$`)), + resource.TestCheckResourceAttr(resourceName, "endpoint", ""), resource.TestCheckResourceAttr(resourceName, "port", "3306"), resource.TestCheckResourceAttr(resourceName, "rds_resource_id", rName), - resource.TestCheckResourceAttrPair(resourceName, "target_arn", "aws_rds_cluster.test", "arn"), - resource.TestCheckResourceAttr(resourceName, "tracked_cluster_id", rName), + // resource.TestCheckResourceAttrPair(resourceName, "target_arn", "aws_rds_cluster.test", "arn"), + // resource.TestCheckResourceAttr(resourceName, "tracked_cluster_id", rName), resource.TestCheckResourceAttr(resourceName, "type", "TRACKED_CLUSTER"), ), }, @@ -75,9 +75,10 @@ func TestAccAWSDBProxyTarget_Cluster(t *testing.T) { } func TestAccAWSDBProxyTarget_disappears(t *testing.T) { - var v rds.DBProxyTarget + var dbProxyTarget rds.DBProxyTarget resourceName := "aws_db_proxy_target.test" rName := acctest.RandomWithPrefix("tf-acc-test") + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -86,7 +87,7 @@ func TestAccAWSDBProxyTarget_disappears(t *testing.T) { { Config: testAccAWSDBProxyTargetConfig_Instance(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSDBProxyTargetExists(resourceName, &v), + testAccCheckAWSDBProxyTargetExists(resourceName, &dbProxyTarget), testAccCheckResourceDisappears(testAccProvider, resourceAwsDbProxyTarget(), resourceName), ), ExpectNonEmptyPlan: true, @@ -198,18 +199,6 @@ resource "aws_db_proxy" "test" { } } -resource "aws_db_proxy_default_target_group" "test" { - db_proxy_name = aws_db_proxy.test.name - - connection_pool_config { - connection_borrow_timeout = 120 - init_query = "SET x=1, y=2" - max_connections_percent = 100 - max_idle_connections_percent = 50 - session_pinning_filters = [] - } -} - resource "aws_db_subnet_group" "test" { name = "%[1]s" subnet_ids = aws_subnet.test.*.id @@ -309,7 +298,7 @@ func testAccAWSDBProxyTargetConfig_Instance(rName string) string { return testAccAWSDBProxyTargetConfigBase(rName) + fmt.Sprintf(` resource "aws_db_proxy_target" "test" { db_proxy_name = aws_db_proxy.test.name - target_group_name = aws_db_proxy_default_target_group.test.name + target_group_name = "default" db_instance_identifier = aws_db_instance.test.id } @@ -335,7 +324,7 @@ func testAccAWSDBProxyTargetConfig_Cluster(rName string) string { return testAccAWSDBProxyTargetConfigBase(rName) + fmt.Sprintf(` resource "aws_db_proxy_target" "test" { db_proxy_name = aws_db_proxy.test.name - target_group_name = aws_db_proxy_default_target_group.test.name + target_group_name = "default" db_cluster_identifier = aws_rds_cluster.test.id } From e49d0cf09b56556c1ebf66478189128af62dd0df Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Wed, 30 Sep 2020 12:50:47 -0400 Subject: [PATCH 5/5] resource/aws_db_proxy_target: Apply suggestions from code review --- aws/resource_aws_db_proxy_target.go | 36 ++++---------------- website/docs/r/db_proxy_target.html.markdown | 8 ++--- 2 files changed, 11 insertions(+), 33 deletions(-) diff --git a/aws/resource_aws_db_proxy_target.go b/aws/resource_aws_db_proxy_target.go index 25492d3267a..1e501c446f1 100644 --- a/aws/resource_aws_db_proxy_target.go +++ b/aws/resource_aws_db_proxy_target.go @@ -116,18 +116,6 @@ func resourceAwsDbProxyTargetCreate(d *schema.ResourceData, meta interface{}) er d.SetId(strings.Join([]string{dbProxyName, targetGroupName, *dbProxyTarget.RdsResourceId}, "/")) log.Printf("[INFO] DB Proxy target ID: %s", d.Id()) - // stateChangeConf := &resource.StateChangeConf{ - // Pending: []string{rds.DBProxyStatusCreating}, - // Target: []string{rds.DBProxyStatusAvailable}, - // Refresh: resourceAwsDbProxyTargetRefreshFunc(conn, d.Id()), - // Timeout: d.Timeout(schema.TimeoutCreate), - // } - - // _, err = stateChangeConf.WaitForState() - // if err != nil { - // return fmt.Errorf("Error waiting for DB Proxy target registration: %s", err) - // } - return resourceAwsDbProxyTargetRead(d, meta) } @@ -155,28 +143,30 @@ func resourceAwsDbProxyTargetRead(d *schema.ResourceData, meta interface{}) erro resp, err := conn.DescribeDBProxyTargets(¶ms) if err != nil { if isAWSErr(err, rds.ErrCodeDBProxyNotFoundFault, "") { - log.Printf("[WARN] DB Proxy (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] DB Proxy Target (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if isAWSErr(err, rds.ErrCodeDBProxyTargetGroupNotFoundFault, "") { - log.Printf("[WARN] DB Proxy (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] DB Proxy Target (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - return err + return fmt.Errorf("error reading RDS DB Proxy Target (%s): %w", d.Id(), err) } var dbProxyTarget *rds.DBProxyTarget for _, target := range resp.Targets { - if *target.RdsResourceId == rdsResourceId { + if aws.StringValue(target.RdsResourceId) == rdsResourceId { dbProxyTarget = target break } } if dbProxyTarget == nil { - return fmt.Errorf("Unable to find DB Proxy target: %#v", params) + log.Printf("[WARN] DB Proxy Target (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } d.Set("endpoint", dbProxyTarget.Endpoint) @@ -211,17 +201,5 @@ func resourceAwsDbProxyTargetDelete(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("Error deregistering DB Proxy target: %s", err) } - // stateChangeConf := &resource.StateChangeConf{ - // Pending: []string{rds.DBProxyStatusDeleting}, - // Target: []string{""}, - // Refresh: resourceAwsDbProxyTargetRefreshFunc(conn, d.Id()), - // Timeout: d.Timeout(schema.TimeoutDelete), - // } - - // _, err = stateChangeConf.WaitForState() - // if err != nil { - // return fmt.Errorf("Error waiting for DB Proxy deletion: %s", err) - // } - return nil } diff --git a/website/docs/r/db_proxy_target.html.markdown b/website/docs/r/db_proxy_target.html.markdown index f3fdae2c611..726269a9b9f 100644 --- a/website/docs/r/db_proxy_target.html.markdown +++ b/website/docs/r/db_proxy_target.html.markdown @@ -19,15 +19,15 @@ resource "aws_db_proxy" "example" { engine_family = "MYSQL" idle_client_timeout = 1800 require_tls = true - role_arn = "arn:aws:iam:us-east-1:123456789012:role/example" - vpc_security_group_ids = ["sg-12345678901234567"] - vpc_subnet_ids = ["subnet-12345678901234567"] + role_arn = aws_iam_role.example.arn + vpc_security_group_ids = [aws_security_group.example.id] + vpc_subnet_ids = [aws_subnet.example.id] auth { auth_scheme = "SECRETS" description = "example" iam_auth = "DISABLED" - secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:example" + secret_arn = aws_secretsmanager_secret.example.arn } tags = {