github.com/bengesoff/terraform@v0.3.1-0.20141018223233-b25a53629922/builtin/providers/aws/resource_aws_db_instance.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strconv"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/hashicorp/terraform/flatmap"
    11  	"github.com/hashicorp/terraform/helper/config"
    12  	"github.com/hashicorp/terraform/helper/diff"
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/terraform"
    15  	"github.com/mitchellh/goamz/rds"
    16  )
    17  
    18  func resource_aws_db_instance_create(
    19  	s *terraform.InstanceState,
    20  	d *terraform.InstanceDiff,
    21  	meta interface{}) (*terraform.InstanceState, error) {
    22  	p := meta.(*ResourceProvider)
    23  	conn := p.rdsconn
    24  
    25  	// Merge the diff into the state so that we have all the attributes
    26  	// properly.
    27  	rs := s.MergeDiff(d)
    28  
    29  	var err error
    30  	var attr string
    31  
    32  	opts := rds.CreateDBInstance{}
    33  
    34  	if attr = rs.Attributes["allocated_storage"]; attr != "" {
    35  		opts.AllocatedStorage, err = strconv.Atoi(attr)
    36  		opts.SetAllocatedStorage = true
    37  	}
    38  
    39  	if attr = rs.Attributes["backup_retention_period"]; attr != "" {
    40  		opts.BackupRetentionPeriod, err = strconv.Atoi(attr)
    41  		opts.SetBackupRetentionPeriod = true
    42  	}
    43  
    44  	if attr = rs.Attributes["iops"]; attr != "" {
    45  		opts.Iops, err = strconv.Atoi(attr)
    46  		opts.SetIops = true
    47  	}
    48  
    49  	if attr = rs.Attributes["port"]; attr != "" {
    50  		opts.Port, err = strconv.Atoi(attr)
    51  		opts.SetPort = true
    52  	}
    53  
    54  	if attr = rs.Attributes["availability_zone"]; attr != "" {
    55  		opts.AvailabilityZone = attr
    56  	}
    57  
    58  	if attr = rs.Attributes["instance_class"]; attr != "" {
    59  		opts.DBInstanceClass = attr
    60  	}
    61  
    62  	if attr = rs.Attributes["maintenance_window"]; attr != "" {
    63  		opts.PreferredMaintenanceWindow = attr
    64  	}
    65  
    66  	if attr = rs.Attributes["backup_window"]; attr != "" {
    67  		opts.PreferredBackupWindow = attr
    68  	}
    69  
    70  	if attr = rs.Attributes["multi_az"]; attr == "true" {
    71  		opts.MultiAZ = true
    72  	}
    73  
    74  	if attr = rs.Attributes["publicly_accessible"]; attr == "true" {
    75  		opts.PubliclyAccessible = true
    76  	}
    77  
    78  	if attr = rs.Attributes["db_subnet_group_name"]; attr != "" {
    79  		opts.DBSubnetGroupName = attr
    80  	}
    81  
    82  	if err != nil {
    83  		return nil, fmt.Errorf("Error parsing configuration: %s", err)
    84  	}
    85  
    86  	if _, ok := rs.Attributes["vpc_security_group_ids.#"]; ok {
    87  		opts.VpcSecurityGroupIds = expandStringList(flatmap.Expand(
    88  			rs.Attributes, "vpc_security_group_ids").([]interface{}))
    89  	}
    90  
    91  	if _, ok := rs.Attributes["security_group_names.#"]; ok {
    92  		opts.DBSecurityGroupNames = expandStringList(flatmap.Expand(
    93  			rs.Attributes, "security_group_names").([]interface{}))
    94  	}
    95  
    96  	opts.DBInstanceIdentifier = rs.Attributes["identifier"]
    97  	opts.DBName = rs.Attributes["name"]
    98  	opts.MasterUsername = rs.Attributes["username"]
    99  	opts.MasterUserPassword = rs.Attributes["password"]
   100  	opts.EngineVersion = rs.Attributes["engine_version"]
   101  	opts.Engine = rs.Attributes["engine"]
   102  
   103  	log.Printf("[DEBUG] DB Instance create configuration: %#v", opts)
   104  	_, err = conn.CreateDBInstance(&opts)
   105  	if err != nil {
   106  		return nil, fmt.Errorf("Error creating DB Instance: %s", err)
   107  	}
   108  
   109  	rs.ID = rs.Attributes["identifier"]
   110  
   111  	log.Printf("[INFO] DB Instance ID: %s", rs.ID)
   112  
   113  	log.Println(
   114  		"[INFO] Waiting for DB Instance to be available")
   115  
   116  	stateConf := &resource.StateChangeConf{
   117  		Pending:    []string{"creating", "backing-up", "modifying"},
   118  		Target:     "available",
   119  		Refresh:    DBInstanceStateRefreshFunc(rs.ID, conn),
   120  		Timeout:    10 * time.Minute,
   121  		MinTimeout: 10 * time.Second,
   122  		Delay:      30 * time.Second, // Wait 30 secs before starting
   123  	}
   124  
   125  	// Wait, catching any errors
   126  	_, err = stateConf.WaitForState()
   127  	if err != nil {
   128  		return rs, err
   129  	}
   130  
   131  	v, err := resource_aws_db_instance_retrieve(rs.ID, conn)
   132  	if err != nil {
   133  		return rs, err
   134  	}
   135  
   136  	return resource_aws_db_instance_update_state(rs, v)
   137  }
   138  
   139  func resource_aws_db_instance_update(
   140  	s *terraform.InstanceState,
   141  	d *terraform.InstanceDiff,
   142  	meta interface{}) (*terraform.InstanceState, error) {
   143  	panic("Cannot update DB")
   144  }
   145  
   146  func resource_aws_db_instance_destroy(
   147  	s *terraform.InstanceState,
   148  	meta interface{}) error {
   149  	p := meta.(*ResourceProvider)
   150  	conn := p.rdsconn
   151  
   152  	log.Printf("[DEBUG] DB Instance destroy: %v", s.ID)
   153  
   154  	opts := rds.DeleteDBInstance{DBInstanceIdentifier: s.ID}
   155  
   156  	if s.Attributes["skip_final_snapshot"] == "true" {
   157  		opts.SkipFinalSnapshot = true
   158  	} else {
   159  		opts.FinalDBSnapshotIdentifier = s.Attributes["final_snapshot_identifier"]
   160  	}
   161  
   162  	log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts)
   163  	if _, err := conn.DeleteDBInstance(&opts); err != nil {
   164  		return err
   165  	}
   166  
   167  	log.Println(
   168  		"[INFO] Waiting for DB Instance to be destroyed")
   169  	stateConf := &resource.StateChangeConf{
   170  		Pending: []string{"creating", "backing-up",
   171  			"modifying", "deleting", "available"},
   172  		Target:     "",
   173  		Refresh:    DBInstanceStateRefreshFunc(s.ID, conn),
   174  		Timeout:    10 * time.Minute,
   175  		MinTimeout: 10 * time.Second,
   176  		Delay:      30 * time.Second, // Wait 30 secs before starting
   177  	}
   178  	if _, err := stateConf.WaitForState(); err != nil {
   179  		return err
   180  	}
   181  
   182  	return nil
   183  }
   184  
   185  func resource_aws_db_instance_refresh(
   186  	s *terraform.InstanceState,
   187  	meta interface{}) (*terraform.InstanceState, error) {
   188  	p := meta.(*ResourceProvider)
   189  	conn := p.rdsconn
   190  
   191  	v, err := resource_aws_db_instance_retrieve(s.ID, conn)
   192  
   193  	if err != nil {
   194  		return s, err
   195  	}
   196  	if v == nil {
   197  		s.ID = ""
   198  		return s, nil
   199  	}
   200  
   201  	return resource_aws_db_instance_update_state(s, v)
   202  }
   203  
   204  func resource_aws_db_instance_diff(
   205  	s *terraform.InstanceState,
   206  	c *terraform.ResourceConfig,
   207  	meta interface{}) (*terraform.InstanceDiff, error) {
   208  
   209  	b := &diff.ResourceBuilder{
   210  		Attrs: map[string]diff.AttrType{
   211  			"allocated_storage":         diff.AttrTypeCreate,
   212  			"availability_zone":         diff.AttrTypeCreate,
   213  			"backup_retention_period":   diff.AttrTypeCreate,
   214  			"backup_window":             diff.AttrTypeCreate,
   215  			"engine":                    diff.AttrTypeCreate,
   216  			"engine_version":            diff.AttrTypeCreate,
   217  			"identifier":                diff.AttrTypeCreate,
   218  			"instance_class":            diff.AttrTypeCreate,
   219  			"iops":                      diff.AttrTypeCreate,
   220  			"maintenance_window":        diff.AttrTypeCreate,
   221  			"multi_az":                  diff.AttrTypeCreate,
   222  			"name":                      diff.AttrTypeCreate,
   223  			"password":                  diff.AttrTypeCreate,
   224  			"port":                      diff.AttrTypeCreate,
   225  			"publicly_accessible":       diff.AttrTypeCreate,
   226  			"username":                  diff.AttrTypeCreate,
   227  			"vpc_security_group_ids":    diff.AttrTypeCreate,
   228  			"security_group_names":      diff.AttrTypeCreate,
   229  			"db_subnet_group_name":      diff.AttrTypeCreate,
   230  			"skip_final_snapshot":       diff.AttrTypeUpdate,
   231  			"final_snapshot_identifier": diff.AttrTypeUpdate,
   232  		},
   233  
   234  		ComputedAttrs: []string{
   235  			"address",
   236  			"availability_zone",
   237  			"backup_retention_period",
   238  			"backup_window",
   239  			"engine_version",
   240  			"maintenance_window",
   241  			"endpoint",
   242  			"status",
   243  			"multi_az",
   244  			"port",
   245  			"address",
   246  			"password",
   247  		},
   248  	}
   249  
   250  	return b.Diff(s, c)
   251  }
   252  
   253  func resource_aws_db_instance_update_state(
   254  	s *terraform.InstanceState,
   255  	v *rds.DBInstance) (*terraform.InstanceState, error) {
   256  
   257  	s.Attributes["address"] = v.Address
   258  	s.Attributes["allocated_storage"] = strconv.Itoa(v.AllocatedStorage)
   259  	s.Attributes["availability_zone"] = v.AvailabilityZone
   260  	s.Attributes["backup_retention_period"] = strconv.Itoa(v.BackupRetentionPeriod)
   261  	s.Attributes["backup_window"] = v.PreferredBackupWindow
   262  	s.Attributes["endpoint"] = fmt.Sprintf("%s:%s", s.Attributes["address"], strconv.Itoa(v.Port))
   263  	s.Attributes["engine"] = v.Engine
   264  	s.Attributes["engine_version"] = v.EngineVersion
   265  	s.Attributes["instance_class"] = v.DBInstanceClass
   266  	s.Attributes["maintenance_window"] = v.PreferredMaintenanceWindow
   267  	s.Attributes["multi_az"] = strconv.FormatBool(v.MultiAZ)
   268  	s.Attributes["name"] = v.DBName
   269  	s.Attributes["port"] = strconv.Itoa(v.Port)
   270  	s.Attributes["status"] = v.DBInstanceStatus
   271  	s.Attributes["username"] = v.MasterUsername
   272  	s.Attributes["db_subnet_group_name"] = v.DBSubnetGroup.Name
   273  
   274  	// Flatten our group values
   275  	toFlatten := make(map[string]interface{})
   276  
   277  	if len(v.DBSecurityGroupNames) > 0 && v.DBSecurityGroupNames[0] != "" {
   278  		toFlatten["security_group_names"] = v.DBSecurityGroupNames
   279  	}
   280  	if len(v.VpcSecurityGroupIds) > 0 && v.VpcSecurityGroupIds[0] != "" {
   281  		toFlatten["vpc_security_group_ids"] = v.VpcSecurityGroupIds
   282  	}
   283  	for k, v := range flatmap.Flatten(toFlatten) {
   284  		s.Attributes[k] = v
   285  	}
   286  
   287  	return s, nil
   288  }
   289  
   290  func resource_aws_db_instance_retrieve(id string, conn *rds.Rds) (*rds.DBInstance, error) {
   291  	opts := rds.DescribeDBInstances{
   292  		DBInstanceIdentifier: id,
   293  	}
   294  
   295  	log.Printf("[DEBUG] DB Instance describe configuration: %#v", opts)
   296  
   297  	resp, err := conn.DescribeDBInstances(&opts)
   298  
   299  	if err != nil {
   300  		if strings.Contains(err.Error(), "DBInstanceNotFound") {
   301  			return nil, nil
   302  		}
   303  		return nil, fmt.Errorf("Error retrieving DB Instances: %s", err)
   304  	}
   305  
   306  	if len(resp.DBInstances) != 1 ||
   307  		resp.DBInstances[0].DBInstanceIdentifier != id {
   308  		if err != nil {
   309  			return nil, nil
   310  		}
   311  	}
   312  
   313  	v := resp.DBInstances[0]
   314  
   315  	return &v, nil
   316  }
   317  
   318  func resource_aws_db_instance_validation() *config.Validator {
   319  	return &config.Validator{
   320  		Required: []string{
   321  			"allocated_storage",
   322  			"engine",
   323  			"engine_version",
   324  			"identifier",
   325  			"instance_class",
   326  			"name",
   327  			"password",
   328  			"username",
   329  		},
   330  		Optional: []string{
   331  			"availability_zone",
   332  			"backup_retention_period",
   333  			"backup_window",
   334  			"iops",
   335  			"maintenance_window",
   336  			"multi_az",
   337  			"port",
   338  			"publicly_accessible",
   339  			"vpc_security_group_ids.*",
   340  			"skip_final_snapshot",
   341  			"security_group_names.*",
   342  			"db_subnet_group_name",
   343  		},
   344  	}
   345  }
   346  
   347  func DBInstanceStateRefreshFunc(id string, conn *rds.Rds) resource.StateRefreshFunc {
   348  	return func() (interface{}, string, error) {
   349  		v, err := resource_aws_db_instance_retrieve(id, conn)
   350  
   351  		if err != nil {
   352  			log.Printf("Error on retrieving DB Instance when waiting: %s", err)
   353  			return nil, "", err
   354  		}
   355  
   356  		if v == nil {
   357  			return nil, "", nil
   358  		}
   359  
   360  		return v, v.DBInstanceStatus, nil
   361  	}
   362  }