github.com/lamielle/terraform@v0.3.2-0.20141121070651-81f008ba53d5/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 attr = rs.Attributes["parameter_group_name"]; attr != "" {
    83                  opts.DBParameterGroupName = attr
    84          }
    85  
    86  	if err != nil {
    87  		return nil, fmt.Errorf("Error parsing configuration: %s", err)
    88  	}
    89  
    90  	if _, ok := rs.Attributes["vpc_security_group_ids.#"]; ok {
    91  		opts.VpcSecurityGroupIds = expandStringList(flatmap.Expand(
    92  			rs.Attributes, "vpc_security_group_ids").([]interface{}))
    93  	}
    94  
    95  	if _, ok := rs.Attributes["security_group_names.#"]; ok {
    96  		opts.DBSecurityGroupNames = expandStringList(flatmap.Expand(
    97  			rs.Attributes, "security_group_names").([]interface{}))
    98  	}
    99  
   100  	opts.DBInstanceIdentifier = rs.Attributes["identifier"]
   101  	opts.DBName = rs.Attributes["name"]
   102  	opts.MasterUsername = rs.Attributes["username"]
   103  	opts.MasterUserPassword = rs.Attributes["password"]
   104  	opts.EngineVersion = rs.Attributes["engine_version"]
   105  	opts.Engine = rs.Attributes["engine"]
   106  
   107  	log.Printf("[DEBUG] DB Instance create configuration: %#v", opts)
   108  	_, err = conn.CreateDBInstance(&opts)
   109  	if err != nil {
   110  		return nil, fmt.Errorf("Error creating DB Instance: %s", err)
   111  	}
   112  
   113  	rs.ID = rs.Attributes["identifier"]
   114  
   115  	log.Printf("[INFO] DB Instance ID: %s", rs.ID)
   116  
   117  	log.Println(
   118  		"[INFO] Waiting for DB Instance to be available")
   119  
   120  	stateConf := &resource.StateChangeConf{
   121  		Pending:    []string{"creating", "backing-up", "modifying"},
   122  		Target:     "available",
   123  		Refresh:    DBInstanceStateRefreshFunc(rs.ID, conn),
   124  		Timeout:    20 * time.Minute,
   125  		MinTimeout: 10 * time.Second,
   126  		Delay:      30 * time.Second, // Wait 30 secs before starting
   127  	}
   128  
   129  	// Wait, catching any errors
   130  	_, err = stateConf.WaitForState()
   131  	if err != nil {
   132  		return rs, err
   133  	}
   134  
   135  	v, err := resource_aws_db_instance_retrieve(rs.ID, conn)
   136  	if err != nil {
   137  		return rs, err
   138  	}
   139  
   140  	return resource_aws_db_instance_update_state(rs, v)
   141  }
   142  
   143  func resource_aws_db_instance_update(
   144  	s *terraform.InstanceState,
   145  	d *terraform.InstanceDiff,
   146  	meta interface{}) (*terraform.InstanceState, error) {
   147  	panic("Cannot update DB")
   148  }
   149  
   150  func resource_aws_db_instance_destroy(
   151  	s *terraform.InstanceState,
   152  	meta interface{}) error {
   153  	p := meta.(*ResourceProvider)
   154  	conn := p.rdsconn
   155  
   156  	log.Printf("[DEBUG] DB Instance destroy: %v", s.ID)
   157  
   158  	opts := rds.DeleteDBInstance{DBInstanceIdentifier: s.ID}
   159  
   160  	if s.Attributes["skip_final_snapshot"] == "true" {
   161  		opts.SkipFinalSnapshot = true
   162  	} else {
   163  		opts.FinalDBSnapshotIdentifier = s.Attributes["final_snapshot_identifier"]
   164  	}
   165  
   166  	log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts)
   167  	if _, err := conn.DeleteDBInstance(&opts); err != nil {
   168  		return err
   169  	}
   170  
   171  	log.Println(
   172  		"[INFO] Waiting for DB Instance to be destroyed")
   173  	stateConf := &resource.StateChangeConf{
   174  		Pending: []string{"creating", "backing-up",
   175  			"modifying", "deleting", "available"},
   176  		Target:     "",
   177  		Refresh:    DBInstanceStateRefreshFunc(s.ID, conn),
   178  		Timeout:    20 * time.Minute,
   179  		MinTimeout: 10 * time.Second,
   180  		Delay:      30 * time.Second, // Wait 30 secs before starting
   181  	}
   182  	if _, err := stateConf.WaitForState(); err != nil {
   183  		return err
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  func resource_aws_db_instance_refresh(
   190  	s *terraform.InstanceState,
   191  	meta interface{}) (*terraform.InstanceState, error) {
   192  	p := meta.(*ResourceProvider)
   193  	conn := p.rdsconn
   194  
   195  	v, err := resource_aws_db_instance_retrieve(s.ID, conn)
   196  
   197  	if err != nil {
   198  		return s, err
   199  	}
   200  	if v == nil {
   201  		s.ID = ""
   202  		return s, nil
   203  	}
   204  
   205  	return resource_aws_db_instance_update_state(s, v)
   206  }
   207  
   208  func resource_aws_db_instance_diff(
   209  	s *terraform.InstanceState,
   210  	c *terraform.ResourceConfig,
   211  	meta interface{}) (*terraform.InstanceDiff, error) {
   212  
   213  	b := &diff.ResourceBuilder{
   214  		Attrs: map[string]diff.AttrType{
   215  			"allocated_storage":         diff.AttrTypeCreate,
   216  			"availability_zone":         diff.AttrTypeCreate,
   217  			"backup_retention_period":   diff.AttrTypeCreate,
   218  			"backup_window":             diff.AttrTypeCreate,
   219  			"engine":                    diff.AttrTypeCreate,
   220  			"engine_version":            diff.AttrTypeCreate,
   221  			"identifier":                diff.AttrTypeCreate,
   222  			"instance_class":            diff.AttrTypeCreate,
   223  			"iops":                      diff.AttrTypeCreate,
   224  			"maintenance_window":        diff.AttrTypeCreate,
   225  			"multi_az":                  diff.AttrTypeCreate,
   226  			"name":                      diff.AttrTypeCreate,
   227  			"password":                  diff.AttrTypeCreate,
   228  			"port":                      diff.AttrTypeCreate,
   229  			"publicly_accessible":       diff.AttrTypeCreate,
   230  			"username":                  diff.AttrTypeCreate,
   231  			"vpc_security_group_ids":    diff.AttrTypeCreate,
   232  			"security_group_names":      diff.AttrTypeCreate,
   233  			"db_subnet_group_name":      diff.AttrTypeCreate,
   234  			"parameter_group_name":      diff.AttrTypeCreate,
   235  			"skip_final_snapshot":       diff.AttrTypeUpdate,
   236  			"final_snapshot_identifier": diff.AttrTypeUpdate,
   237  		},
   238  
   239  		ComputedAttrs: []string{
   240  			"address",
   241  			"availability_zone",
   242  			"backup_retention_period",
   243  			"backup_window",
   244  			"engine_version",
   245  			"maintenance_window",
   246  			"endpoint",
   247  			"status",
   248  			"multi_az",
   249  			"port",
   250  			"address",
   251  			"password",
   252  		},
   253  	}
   254  
   255  	return b.Diff(s, c)
   256  }
   257  
   258  func resource_aws_db_instance_update_state(
   259  	s *terraform.InstanceState,
   260  	v *rds.DBInstance) (*terraform.InstanceState, error) {
   261  
   262  	s.Attributes["address"] = v.Address
   263  	s.Attributes["allocated_storage"] = strconv.Itoa(v.AllocatedStorage)
   264  	s.Attributes["availability_zone"] = v.AvailabilityZone
   265  	s.Attributes["backup_retention_period"] = strconv.Itoa(v.BackupRetentionPeriod)
   266  	s.Attributes["backup_window"] = v.PreferredBackupWindow
   267  	s.Attributes["endpoint"] = fmt.Sprintf("%s:%s", s.Attributes["address"], strconv.Itoa(v.Port))
   268  	s.Attributes["engine"] = v.Engine
   269  	s.Attributes["engine_version"] = v.EngineVersion
   270  	s.Attributes["instance_class"] = v.DBInstanceClass
   271  	s.Attributes["maintenance_window"] = v.PreferredMaintenanceWindow
   272  	s.Attributes["multi_az"] = strconv.FormatBool(v.MultiAZ)
   273  	s.Attributes["name"] = v.DBName
   274  	s.Attributes["port"] = strconv.Itoa(v.Port)
   275  	s.Attributes["status"] = v.DBInstanceStatus
   276  	s.Attributes["username"] = v.MasterUsername
   277  	s.Attributes["db_subnet_group_name"] = v.DBSubnetGroup.Name
   278  	s.Attributes["parameter_group_name"] = v.DBParameterGroupName
   279  
   280  	// Flatten our group values
   281  	toFlatten := make(map[string]interface{})
   282  
   283  	if len(v.DBSecurityGroupNames) > 0 && v.DBSecurityGroupNames[0] != "" {
   284  		toFlatten["security_group_names"] = v.DBSecurityGroupNames
   285  	}
   286  	if len(v.VpcSecurityGroupIds) > 0 && v.VpcSecurityGroupIds[0] != "" {
   287  		toFlatten["vpc_security_group_ids"] = v.VpcSecurityGroupIds
   288  	}
   289  	for k, v := range flatmap.Flatten(toFlatten) {
   290  		s.Attributes[k] = v
   291  	}
   292  
   293  	return s, nil
   294  }
   295  
   296  func resource_aws_db_instance_retrieve(id string, conn *rds.Rds) (*rds.DBInstance, error) {
   297  	opts := rds.DescribeDBInstances{
   298  		DBInstanceIdentifier: id,
   299  	}
   300  
   301  	log.Printf("[DEBUG] DB Instance describe configuration: %#v", opts)
   302  
   303  	resp, err := conn.DescribeDBInstances(&opts)
   304  
   305  	if err != nil {
   306  		if strings.Contains(err.Error(), "DBInstanceNotFound") {
   307  			return nil, nil
   308  		}
   309  		return nil, fmt.Errorf("Error retrieving DB Instances: %s", err)
   310  	}
   311  
   312  	if len(resp.DBInstances) != 1 ||
   313  		resp.DBInstances[0].DBInstanceIdentifier != id {
   314  		if err != nil {
   315  			return nil, nil
   316  		}
   317  	}
   318  
   319  	v := resp.DBInstances[0]
   320  
   321  	return &v, nil
   322  }
   323  
   324  func resource_aws_db_instance_validation() *config.Validator {
   325  	return &config.Validator{
   326  		Required: []string{
   327  			"allocated_storage",
   328  			"engine",
   329  			"engine_version",
   330  			"identifier",
   331  			"instance_class",
   332  			"name",
   333  			"password",
   334  			"username",
   335  		},
   336  		Optional: []string{
   337  			"availability_zone",
   338  			"backup_retention_period",
   339  			"backup_window",
   340  			"iops",
   341  			"maintenance_window",
   342  			"multi_az",
   343  			"port",
   344  			"publicly_accessible",
   345  			"vpc_security_group_ids.*",
   346  			"skip_final_snapshot",
   347  			"security_group_names.*",
   348  			"db_subnet_group_name",
   349  			"parameter_group_name",
   350  			"skip_final_snapshot",
   351  			"final_snapshot_identifier",
   352  		},
   353  	}
   354  }
   355  
   356  func DBInstanceStateRefreshFunc(id string, conn *rds.Rds) resource.StateRefreshFunc {
   357  	return func() (interface{}, string, error) {
   358  		v, err := resource_aws_db_instance_retrieve(id, conn)
   359  
   360  		if err != nil {
   361  			log.Printf("Error on retrieving DB Instance when waiting: %s", err)
   362  			return nil, "", err
   363  		}
   364  
   365  		if v == nil {
   366  			return nil, "", nil
   367  		}
   368  
   369  		return v, v.DBInstanceStatus, nil
   370  	}
   371  }