From 9e293def6a07ccec054bc1acdab716f7ae176079 Mon Sep 17 00:00:00 2001 From: Jan Schumann Date: Mon, 5 Dec 2016 17:14:15 +0100 Subject: [PATCH] provider/aws: Add opsworks rds db resource (#10294) * add rds db for opsworks * switched to stack in vpc * implement update method * add docs * implement and document force new resource behavior * implement retry for update and delete * add test that forces new resource --- builtin/providers/aws/provider.go | 1 + .../resource_aws_opsworks_rds_db_instance.go | 202 ++++++++++++++++++ ...ource_aws_opsworks_rds_db_instance_test.go | 92 ++++++++ .../r/opsworks_rds_db_instance.html.markdown | 37 ++++ 4 files changed, 332 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_opsworks_rds_db_instance.go create mode 100644 builtin/providers/aws/resource_aws_opsworks_rds_db_instance_test.go create mode 100644 website/source/docs/providers/aws/r/opsworks_rds_db_instance.html.markdown diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 39069ac3c..642e979fd 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -312,6 +312,7 @@ func Provider() terraform.ResourceProvider { "aws_opsworks_instance": resourceAwsOpsworksInstance(), "aws_opsworks_user_profile": resourceAwsOpsworksUserProfile(), "aws_opsworks_permission": resourceAwsOpsworksPermission(), + "aws_opsworks_rds_db_instance": resourceAwsOpsworksRdsDbInstance(), "aws_placement_group": resourceAwsPlacementGroup(), "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), "aws_rds_cluster": resourceAwsRDSCluster(), diff --git a/builtin/providers/aws/resource_aws_opsworks_rds_db_instance.go b/builtin/providers/aws/resource_aws_opsworks_rds_db_instance.go new file mode 100644 index 000000000..91bfcce30 --- /dev/null +++ b/builtin/providers/aws/resource_aws_opsworks_rds_db_instance.go @@ -0,0 +1,202 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/opsworks" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsOpsworksRdsDbInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsOpsworksRdsDbInstanceRegister, + Update: resourceAwsOpsworksRdsDbInstanceUpdate, + Delete: resourceAwsOpsworksRdsDbInstanceDeregister, + Read: resourceAwsOpsworksRdsDbInstanceRead, + + Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "stack_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "rds_db_instance_arn": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "db_password": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + "db_user": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceAwsOpsworksRdsDbInstanceUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AWSClient).opsworksconn + + d.Partial(true) + + d.SetPartial("rds_db_instance_arn") + req := &opsworks.UpdateRdsDbInstanceInput{ + RdsDbInstanceArn: aws.String(d.Get("rds_db_instance_arn").(string)), + } + + requestUpdate := false + if d.HasChange("db_user") { + d.SetPartial("db_user") + req.DbUser = aws.String(d.Get("db_user").(string)) + requestUpdate = true + } + if d.HasChange("db_password") { + d.SetPartial("db_password") + req.DbPassword = aws.String(d.Get("db_password").(string)) + requestUpdate = true + } + + if true == requestUpdate { + log.Printf("[DEBUG] Opsworks RDS DB Instance Modification request: %s", req) + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + var cerr error + _, cerr = client.UpdateRdsDbInstance(req) + if cerr != nil { + log.Printf("[INFO] client error") + if opserr, ok := cerr.(awserr.Error); ok { + log.Printf("[ERROR] OpsWorks error: %s message: %s", opserr.Code(), opserr.Message()) + } + return resource.NonRetryableError(cerr) + } + return nil + }) + + if err != nil { + return err + } + + } + + d.Partial(false) + + return resourceAwsOpsworksRdsDbInstanceRead(d, meta) +} + +func resourceAwsOpsworksRdsDbInstanceDeregister(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AWSClient).opsworksconn + + req := &opsworks.DeregisterRdsDbInstanceInput{ + RdsDbInstanceArn: aws.String(d.Get("rds_db_instance_arn").(string)), + } + + log.Printf("[DEBUG] Unregistering rds db instance '%s' from stack: %s", d.Get("rds_db_instance_arn"), d.Get("stack_id")) + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + var cerr error + _, cerr = client.DeregisterRdsDbInstance(req) + if cerr != nil { + log.Printf("[INFO] client error") + if opserr, ok := cerr.(awserr.Error); ok { + if opserr.Code() == "ResourceNotFoundException" { + log.Printf("[INFO] The db instance could not be found. Remove it from state.") + d.SetId("") + + return nil + } + log.Printf("[ERROR] OpsWorks error: %s message: %s", opserr.Code(), opserr.Message()) + } + return resource.NonRetryableError(cerr) + } + + return nil + }) + + if err != nil { + return err + } + + return nil +} + +func resourceAwsOpsworksRdsDbInstanceRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AWSClient).opsworksconn + + req := &opsworks.DescribeRdsDbInstancesInput{ + StackId: aws.String(d.Get("stack_id").(string)), + } + + log.Printf("[DEBUG] Reading OpsWorks registerd rds db instances for stack: %s", d.Get("stack_id")) + + resp, err := client.DescribeRdsDbInstances(req) + if err != nil { + return err + } + + found := false + id := "" + for _, instance := range resp.RdsDbInstances { + id = fmt.Sprintf("%s%s", *instance.RdsDbInstanceArn, *instance.StackId) + + if fmt.Sprintf("%s%s", d.Get("rds_db_instance_arn").(string), d.Get("stack_id").(string)) == id { + found = true + d.SetId(id) + d.Set("id", id) + d.Set("stack_id", instance.StackId) + d.Set("rds_db_instance_arn", instance.RdsDbInstanceArn) + d.Set("db_user", instance.DbUser) + } + + } + + if false == found { + d.SetId("") + log.Printf("[INFO] The rds instance '%s' could not be found for stack: '%s'", d.Get("rds_db_instance_arn"), d.Get("stack_id")) + } + + return nil +} + +func resourceAwsOpsworksRdsDbInstanceRegister(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AWSClient).opsworksconn + + req := &opsworks.RegisterRdsDbInstanceInput{ + StackId: aws.String(d.Get("stack_id").(string)), + RdsDbInstanceArn: aws.String(d.Get("rds_db_instance_arn").(string)), + DbUser: aws.String(d.Get("db_user").(string)), + DbPassword: aws.String(d.Get("db_password").(string)), + } + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + var cerr error + _, cerr = client.RegisterRdsDbInstance(req) + if cerr != nil { + log.Printf("[INFO] client error") + if opserr, ok := cerr.(awserr.Error); ok { + log.Printf("[ERROR] OpsWorks error: %s message: %s", opserr.Code(), opserr.Message()) + } + return resource.NonRetryableError(cerr) + } + + return nil + }) + + if err != nil { + return err + } + + return resourceAwsOpsworksRdsDbInstanceRead(d, meta) +} diff --git a/builtin/providers/aws/resource_aws_opsworks_rds_db_instance_test.go b/builtin/providers/aws/resource_aws_opsworks_rds_db_instance_test.go new file mode 100644 index 000000000..0a3df85a1 --- /dev/null +++ b/builtin/providers/aws/resource_aws_opsworks_rds_db_instance_test.go @@ -0,0 +1,92 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAWSOpsworksRdsDbInstance(t *testing.T) { + sName := fmt.Sprintf("test-db-instance-%d", acctest.RandInt()) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAwsOpsworksRdsDbInstance(sName, "foo", "barbarbarbar"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "aws_opsworks_rds_db_instance.tf-acc-opsworks-db", "db_user", "foo", + ), + ), + }, + resource.TestStep{ + Config: testAccAwsOpsworksRdsDbInstance(sName, "bar", "barbarbarbar"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "aws_opsworks_rds_db_instance.tf-acc-opsworks-db", "db_user", "bar", + ), + ), + }, + resource.TestStep{ + Config: testAccAwsOpsworksRdsDbInstance(sName, "bar", "foofoofoofoofoo"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "aws_opsworks_rds_db_instance.tf-acc-opsworks-db", "db_user", "bar", + ), + ), + }, + resource.TestStep{ + Config: testAccAwsOpsworksRdsDbInstanceForceNew(sName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "aws_opsworks_rds_db_instance.tf-acc-opsworks-db", "db_user", "foo", + ), + ), + }, + }, + }) +} + +func testAccAwsOpsworksRdsDbInstance(name, userName, password string) string { + return fmt.Sprintf(` +resource "aws_opsworks_rds_db_instance" "tf-acc-opsworks-db" { + stack_id = "${aws_opsworks_stack.tf-acc.id}" + + rds_db_instance_arn = "${aws_db_instance.bar.arn}" + db_user = "%s" + db_password = "%s" +} + +%s + +%s +`, userName, password, testAccAwsOpsworksStackConfigVpcCreate(name), testAccAWSDBInstanceConfig) +} + +func testAccAwsOpsworksRdsDbInstanceForceNew(name string) string { + return fmt.Sprintf(` +resource "aws_opsworks_rds_db_instance" "tf-acc-opsworks-db" { + stack_id = "${aws_opsworks_stack.tf-acc.id}" + + rds_db_instance_arn = "${aws_db_instance.foo.arn}" + db_user = "foo" + db_password = "foofoofoofoo" +} + +%s + +resource "aws_db_instance" "foo" { + allocated_storage = 10 + engine = "MySQL" + engine_version = "5.6.21" + instance_class = "db.t1.micro" + name = "baz" + password = "foofoofoofoo" + username = "foo" + parameter_group_name = "default.mysql5.6" +} +`, testAccAwsOpsworksStackConfigVpcCreate(name)) +} diff --git a/website/source/docs/providers/aws/r/opsworks_rds_db_instance.html.markdown b/website/source/docs/providers/aws/r/opsworks_rds_db_instance.html.markdown new file mode 100644 index 000000000..30c9af272 --- /dev/null +++ b/website/source/docs/providers/aws/r/opsworks_rds_db_instance.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "aws" +page_title: "AWS: aws_opsworks_rds_db_instance" +sidebar_current: "docs-aws-resource-opsworks-rds-db-instance" +description: |- + Provides an OpsWorks RDS DB Instance resource. +------------------------------------------------ + +# aws\_opsworks\_rds\_db\_instance + +Provides an OpsWorks RDS DB Instance resource. + +## Example Usage + +``` +resource "aws_opsworks_rds_db_instance" "my_instance" { + stack_id = "${aws_opsworks_stack.my_stack.id}" + rds_db_instance_arn = "${aws_db_instance.my_instance.arn}" + db_user = "someUser" + db_password = "somePass" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `stack_id` - (Required) The stack to register a db inatance for. Changing this will force a new resource. +* `rds_db_instance_arn` - (Required) The db instance to register for this stack. Changing this will force a new resource. +* `db_user` - (Required) A db username +* `db_password` - (Required) A db password + +## Attributes Reference + +The following attributes are exported: + +* `id` - The computed id. Please note that this is only used internally to identify the stack <-> instance relation. This value is not used in aws.