Skip to content

Commit

Permalink
Implement aws_db_subnet_group resource
Browse files Browse the repository at this point in the history
  • Loading branch information
Alek Storm committed Aug 21, 2014
1 parent cdc2a53 commit 0b9fd66
Show file tree
Hide file tree
Showing 7 changed files with 405 additions and 0 deletions.
7 changes: 7 additions & 0 deletions builtin/providers/aws/resource_aws_db_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ func resource_aws_db_instance_create(
rs.Attributes, "security_group_names").([]interface{}))
}

if attr = rs.Attributes["db_subnet_group_name"]; attr != "" {
opts.DBSubnetGroupName = attr
}

opts.DBInstanceIdentifier = rs.Attributes["identifier"]
opts.DBName = rs.Attributes["name"]
opts.MasterUsername = rs.Attributes["username"]
Expand Down Expand Up @@ -227,6 +231,7 @@ func resource_aws_db_instance_diff(
"vpc_security_group_ids": diff.AttrTypeCreate,
"security_group_names": diff.AttrTypeCreate,
"skip_final_snapshot": diff.AttrTypeUpdate,
"db_subnet_group_name": diff.AttrTypeCreate,
"final_snapshot_identifier": diff.AttrTypeUpdate,
},

Expand Down Expand Up @@ -258,6 +263,7 @@ func resource_aws_db_instance_update_state(
s.Attributes["availability_zone"] = v.AvailabilityZone
s.Attributes["backup_retention_period"] = strconv.Itoa(v.BackupRetentionPeriod)
s.Attributes["backup_window"] = v.PreferredBackupWindow
s.Attributes["db_subnet_group_name"] = v.DBSubnetGroup.Name
s.Attributes["endpoint"] = fmt.Sprintf("%s:%s", s.Attributes["address"], strconv.Itoa(v.Port))
s.Attributes["engine"] = v.Engine
s.Attributes["engine_version"] = v.EngineVersion
Expand Down Expand Up @@ -341,6 +347,7 @@ func resource_aws_db_instance_validation() *config.Validator {
"vpc_security_group_ids.*",
"skip_final_snapshot",
"security_group_names.*",
"db_subnet_group_name",
},
}
}
Expand Down
207 changes: 207 additions & 0 deletions builtin/providers/aws/resource_aws_db_subnet_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package aws

import (
"fmt"
"log"
"strings"
"time"

"github.com/hashicorp/terraform/flatmap"
"github.com/hashicorp/terraform/helper/config"
"github.com/hashicorp/terraform/helper/diff"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/rds"
)

func resource_aws_db_subnet_group_create(
s *terraform.ResourceState,
d *terraform.ResourceDiff,
meta interface{}) (*terraform.ResourceState, error) {
p := meta.(*ResourceProvider)
conn := p.rdsconn

// Merge the diff into the state so that we have all the attributes
// properly.
rs := s.MergeDiff(d)

var err error

opts := rds.CreateDBSubnetGroup{
DBSubnetGroupName: rs.Attributes["name"],
DBSubnetGroupDescription: rs.Attributes["description"],
SubnetIds: expandStringList(flatmap.Expand(
rs.Attributes, "subnet_ids").([]interface{})),
}

log.Printf("[DEBUG] DB Subnet Group create configuration: %#v", opts)
_, err = conn.CreateDBSubnetGroup(&opts)
if err != nil {
return nil, fmt.Errorf("Error creating DB Subnet Group: %s", err)
}

rs.ID = rs.Attributes["name"]

log.Printf("[INFO] DB Subnet Group ID: %s", rs.ID)

log.Println(
"[INFO] Waiting for DB Subnet Group creation to be complete")

stateConf := &resource.StateChangeConf{
// TODO are there any other states?
Pending: []string{},
Target: "Complete",
Refresh: DBSubnetGroupStateRefreshFunc(rs.ID, conn),
Timeout: 10 * time.Minute,
}

// Wait, catching any errors
_, err = stateConf.WaitForState()
if err != nil {
return rs, err
}

v, err := resource_aws_db_subnet_group_retrieve(rs.ID, conn)
if err != nil {
return rs, err
}

return resource_aws_db_subnet_group_update_state(rs, v)
}

func resource_aws_db_subnet_group_update(
s *terraform.ResourceState,
d *terraform.ResourceDiff,
meta interface{}) (*terraform.ResourceState, error) {

panic("Cannot update DB Subnet Group")

return nil, nil
}

func resource_aws_db_subnet_group_destroy(
s *terraform.ResourceState,
meta interface{}) error {
p := meta.(*ResourceProvider)
conn := p.rdsconn

log.Printf("[DEBUG] DB Subnet Group destroy: %v", s.ID)

opts := rds.DeleteDBSubnetGroup{DBSubnetGroupName: s.ID}

log.Printf("[DEBUG] DB Subnet Group destroy configuration: %v", opts)
_, err := conn.DeleteDBSubnetGroup(&opts)

if err != nil {
newerr, ok := err.(*rds.Error)
if ok && newerr.Code == "DBSubnetGroupNotFoundFault" {
return nil
}
return err
}

return nil
}

func resource_aws_db_subnet_group_refresh(
s *terraform.ResourceState,
meta interface{}) (*terraform.ResourceState, error) {
p := meta.(*ResourceProvider)
conn := p.rdsconn

v, err := resource_aws_db_subnet_group_retrieve(s.ID, conn)

if err != nil || v == nil {
return s, err
}

return resource_aws_db_subnet_group_update_state(s, v)
}

func resource_aws_db_subnet_group_diff(
s *terraform.ResourceState,
c *terraform.ResourceConfig,
meta interface{}) (*terraform.ResourceDiff, error) {

b := &diff.ResourceBuilder{
Attrs: map[string]diff.AttrType{
"name": diff.AttrTypeCreate,
"description": diff.AttrTypeCreate,
"subnet_ids": diff.AttrTypeCreate,
},
}

return b.Diff(s, c)
}

func resource_aws_db_subnet_group_update_state(
s *terraform.ResourceState,
v *rds.DBSubnetGroup) (*terraform.ResourceState, error) {

s.Attributes["name"] = v.Name
s.Attributes["description"] = v.Description

// Flatten our group values
toFlatten := make(map[string]interface{})

if len(v.SubnetIds) > 0 && v.SubnetIds[0] != "" {
toFlatten["subnet_ids"] = v.SubnetIds
}

for k, v := range flatmap.Flatten(toFlatten) {
s.Attributes[k] = v
}

return s, nil
}

func resource_aws_db_subnet_group_retrieve(name string, conn *rds.Rds) (*rds.DBSubnetGroup, error) {
opts := rds.DescribeDBSubnetGroups{
DBSubnetGroupName: name,
}

log.Printf("[DEBUG] DB Subnet Group describe configuration: %#v", opts)

resp, err := conn.DescribeDBSubnetGroups(&opts)

if err != nil {
if strings.Contains(err.Error(), "DBSubnetGroupNotFound") {
return nil, nil
}
return nil, fmt.Errorf("Error retrieving DB Subnet Groups: %s", err)
}

if len(resp.DBSubnetGroups) != 1 ||
resp.DBSubnetGroups[0].Name != name {
if err != nil {
return nil, nil
}
}

v := resp.DBSubnetGroups[0]

return &v, nil
}

func resource_aws_db_subnet_group_validation() *config.Validator {
return &config.Validator{
Required: []string{
"name",
"description",
"subnet_ids.*",
},
}
}

func DBSubnetGroupStateRefreshFunc(name string, conn *rds.Rds) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
v, err := resource_aws_db_subnet_group_retrieve(name, conn)

if err != nil {
log.Printf("Error on retrieving DB Subnet Group when waiting: %s", err)
return nil, "", err
}

return v, v.Status, nil
}
}
143 changes: 143 additions & 0 deletions builtin/providers/aws/resource_aws_db_subnet_group_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package aws

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/rds"
)

func TestAccAWSDBSubnetGroup(t *testing.T) {
var v rds.DBSubnetGroup

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBSubnetGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDBSubnetGroupConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBSubnetGroupExists("aws_db_subnet_group.foo", &v),
testAccCheckAWSDBSubnetGroupAttributes(&v),
resource.TestCheckResourceAttr(
"aws_db_subnet_group.foo", "name", "subgroup-terraform"),
resource.TestCheckResourceAttr(
"aws_db_subnet_group.foo", "description", "just cuz"),
// TODO check subnet ID contents
resource.TestCheckResourceAttr(
"aws_db_subnet_group.foo", "subnet_ids.#", "2"),
),
},
},
})
}

func testAccCheckAWSDBSubnetGroupDestroy(s *terraform.State) error {
conn := testAccProvider.rdsconn

for _, rs := range s.Resources {
if rs.Type != "aws_db_subnet_group" {
continue
}

// Try to find the Group
resp, err := conn.DescribeDBSubnetGroups(
&rds.DescribeDBSubnetGroups{
DBSubnetGroupName: rs.ID,
})

if err == nil {
if len(resp.DBSubnetGroups) != 0 &&
resp.DBSubnetGroups[0].Name == rs.ID {
return fmt.Errorf("DB Subnet Group still exists")
}
}

// Verify the error
_, ok := err.(*rds.Error)
if !ok {
return err
}
}

return nil
}

func testAccCheckAWSDBSubnetGroupAttributes(group *rds.DBSubnetGroup) resource.TestCheckFunc {
return func(s *terraform.State) error {
if len(group.SubnetIds) == 0 {
return fmt.Errorf("no subnets: %#v", group.SubnetIds)
}

if group.Name != "subgroup-terraform" {
return fmt.Errorf("bad name: %#v", group.Name)
}

if group.Description != "just cuz" {
return fmt.Errorf("bad description: %#v", group.Description)
}

return nil
}
}

func testAccCheckAWSDBSubnetGroupExists(n string, v *rds.DBSubnetGroup) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.ID == "" {
return fmt.Errorf("No DB Subnet Group ID is set")
}

conn := testAccProvider.rdsconn

opts := rds.DescribeDBSubnetGroups{
DBSubnetGroupName: rs.ID,
}

resp, err := conn.DescribeDBSubnetGroups(&opts)

if err != nil {
return err
}

if len(resp.DBSubnetGroups) != 1 ||
resp.DBSubnetGroups[0].Name != rs.ID {
return fmt.Errorf("DB Subnet Group not found")
}

*v = resp.DBSubnetGroups[0]

return nil
}
}

const testAccAWSDBSubnetGroupConfig = `
resource "aws_vpc" "foo" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "foo" {
cidr_block = "10.0.0.0/24"
vpc_id = "${aws_vpc.foo.id}"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "bar" {
cidr_block = "10.0.1.0/24"
vpc_id = "${aws_vpc.foo.id}"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "foo" {
name = "subgroup-terraform"
description = "just cuz"
subnet_ids = ["${aws_subnet.foo.id}", "${aws_subnet.bar.id}"]
}
`
Loading

0 comments on commit 0b9fd66

Please sign in to comment.