github.com/jgadling/terraform@v0.3.8-0.20150227214559-abd68c2c87bc/builtin/providers/aws/resource_aws_db_instance.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/hashicorp/aws-sdk-go/aws"
     9  	"github.com/hashicorp/aws-sdk-go/gen/rds"
    10  
    11  	"github.com/hashicorp/terraform/helper/hashcode"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func resourceAwsDbInstance() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceAwsDbInstanceCreate,
    19  		Read:   resourceAwsDbInstanceRead,
    20  		Delete: resourceAwsDbInstanceDelete,
    21  
    22  		Schema: map[string]*schema.Schema{
    23  			"name": &schema.Schema{
    24  				Type:     schema.TypeString,
    25  				Optional: true,
    26  				ForceNew: true,
    27  			},
    28  
    29  			"username": &schema.Schema{
    30  				Type:     schema.TypeString,
    31  				Required: true,
    32  				ForceNew: true,
    33  			},
    34  
    35  			"password": &schema.Schema{
    36  				Type:     schema.TypeString,
    37  				Required: true,
    38  				ForceNew: true,
    39  			},
    40  
    41  			"engine": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Required: true,
    44  				ForceNew: true,
    45  			},
    46  
    47  			"engine_version": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Required: true,
    50  				ForceNew: true,
    51  			},
    52  
    53  			"allocated_storage": &schema.Schema{
    54  				Type:     schema.TypeInt,
    55  				Required: true,
    56  				ForceNew: true,
    57  			},
    58  
    59  			"storage_type": &schema.Schema{
    60  				Type:     schema.TypeString,
    61  				Optional: true,
    62  				Computed: true,
    63  				ForceNew: true,
    64  			},
    65  
    66  			"identifier": &schema.Schema{
    67  				Type:     schema.TypeString,
    68  				Required: true,
    69  				ForceNew: true,
    70  			},
    71  
    72  			"instance_class": &schema.Schema{
    73  				Type:     schema.TypeString,
    74  				Required: true,
    75  				ForceNew: true,
    76  			},
    77  
    78  			"availability_zone": &schema.Schema{
    79  				Type:     schema.TypeString,
    80  				Optional: true,
    81  				Computed: true,
    82  				ForceNew: true,
    83  			},
    84  
    85  			"backup_retention_period": &schema.Schema{
    86  				Type:     schema.TypeInt,
    87  				Optional: true,
    88  				ForceNew: true,
    89  				Default:  1,
    90  			},
    91  
    92  			"backup_window": &schema.Schema{
    93  				Type:     schema.TypeString,
    94  				Optional: true,
    95  				Computed: true,
    96  				ForceNew: true,
    97  			},
    98  
    99  			"iops": &schema.Schema{
   100  				Type:     schema.TypeInt,
   101  				Optional: true,
   102  				ForceNew: true,
   103  			},
   104  
   105  			"maintenance_window": &schema.Schema{
   106  				Type:     schema.TypeString,
   107  				Optional: true,
   108  				Computed: true,
   109  				ForceNew: true,
   110  			},
   111  
   112  			"multi_az": &schema.Schema{
   113  				Type:     schema.TypeBool,
   114  				Optional: true,
   115  				Computed: true,
   116  				ForceNew: true,
   117  			},
   118  
   119  			"port": &schema.Schema{
   120  				Type:     schema.TypeInt,
   121  				Optional: true,
   122  				Computed: true,
   123  				ForceNew: true,
   124  			},
   125  
   126  			"publicly_accessible": &schema.Schema{
   127  				Type:     schema.TypeBool,
   128  				Optional: true,
   129  				ForceNew: true,
   130  			},
   131  
   132  			"vpc_security_group_ids": &schema.Schema{
   133  				Type:     schema.TypeSet,
   134  				Optional: true,
   135  				Elem:     &schema.Schema{Type: schema.TypeString},
   136  				Set: func(v interface{}) int {
   137  					return hashcode.String(v.(string))
   138  				},
   139  			},
   140  
   141  			"security_group_names": &schema.Schema{
   142  				Type:     schema.TypeSet,
   143  				Optional: true,
   144  				Elem:     &schema.Schema{Type: schema.TypeString},
   145  				Set: func(v interface{}) int {
   146  					return hashcode.String(v.(string))
   147  				},
   148  			},
   149  
   150  			"final_snapshot_identifier": &schema.Schema{
   151  				Type:     schema.TypeString,
   152  				Optional: true,
   153  				ForceNew: true,
   154  			},
   155  
   156  			"db_subnet_group_name": &schema.Schema{
   157  				Type:     schema.TypeString,
   158  				Optional: true,
   159  				ForceNew: true,
   160  			},
   161  
   162  			"parameter_group_name": &schema.Schema{
   163  				Type:     schema.TypeString,
   164  				Optional: true,
   165  				Computed: true,
   166  				ForceNew: true,
   167  			},
   168  
   169  			"address": &schema.Schema{
   170  				Type:     schema.TypeString,
   171  				Computed: true,
   172  			},
   173  
   174  			"endpoint": &schema.Schema{
   175  				Type:     schema.TypeString,
   176  				Computed: true,
   177  			},
   178  
   179  			"status": &schema.Schema{
   180  				Type:     schema.TypeString,
   181  				Computed: true,
   182  			},
   183  		},
   184  	}
   185  }
   186  
   187  func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error {
   188  	conn := meta.(*AWSClient).rdsconn
   189  	opts := rds.CreateDBInstanceMessage{
   190  		AllocatedStorage:     aws.Integer(d.Get("allocated_storage").(int)),
   191  		DBInstanceClass:      aws.String(d.Get("instance_class").(string)),
   192  		DBInstanceIdentifier: aws.String(d.Get("identifier").(string)),
   193  		DBName:               aws.String(d.Get("name").(string)),
   194  		MasterUsername:       aws.String(d.Get("username").(string)),
   195  		MasterUserPassword:   aws.String(d.Get("password").(string)),
   196  		Engine:               aws.String(d.Get("engine").(string)),
   197  		EngineVersion:        aws.String(d.Get("engine_version").(string)),
   198  	}
   199  
   200  	if attr, ok := d.GetOk("storage_type"); ok {
   201  		opts.StorageType = aws.String(attr.(string))
   202  	}
   203  
   204  	attr := d.Get("backup_retention_period")
   205  	opts.BackupRetentionPeriod = aws.Integer(attr.(int))
   206  
   207  	if attr, ok := d.GetOk("iops"); ok {
   208  		opts.IOPS = aws.Integer(attr.(int))
   209  	}
   210  
   211  	if attr, ok := d.GetOk("port"); ok {
   212  		opts.Port = aws.Integer(attr.(int))
   213  	}
   214  
   215  	if attr, ok := d.GetOk("multi_az"); ok {
   216  		opts.MultiAZ = aws.Boolean(attr.(bool))
   217  	}
   218  
   219  	if attr, ok := d.GetOk("availability_zone"); ok {
   220  		opts.AvailabilityZone = aws.String(attr.(string))
   221  	}
   222  
   223  	if attr, ok := d.GetOk("maintenance_window"); ok {
   224  		opts.PreferredMaintenanceWindow = aws.String(attr.(string))
   225  	}
   226  
   227  	if attr, ok := d.GetOk("backup_window"); ok {
   228  		opts.PreferredBackupWindow = aws.String(attr.(string))
   229  	}
   230  
   231  	if attr, ok := d.GetOk("publicly_accessible"); ok {
   232  		opts.PubliclyAccessible = aws.Boolean(attr.(bool))
   233  	}
   234  
   235  	if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   236  		opts.DBSubnetGroupName = aws.String(attr.(string))
   237  	}
   238  
   239  	if attr, ok := d.GetOk("parameter_group_name"); ok {
   240  		opts.DBParameterGroupName = aws.String(attr.(string))
   241  	}
   242  
   243  	if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   244  		var s []string
   245  		for _, v := range attr.List() {
   246  			s = append(s, v.(string))
   247  		}
   248  		opts.VPCSecurityGroupIDs = s
   249  	}
   250  
   251  	if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 {
   252  		var s []string
   253  		for _, v := range attr.List() {
   254  			s = append(s, v.(string))
   255  		}
   256  		opts.DBSecurityGroups = s
   257  	}
   258  
   259  	log.Printf("[DEBUG] DB Instance create configuration: %#v", opts)
   260  	_, err := conn.CreateDBInstance(&opts)
   261  	if err != nil {
   262  		return fmt.Errorf("Error creating DB Instance: %s", err)
   263  	}
   264  
   265  	d.SetId(d.Get("identifier").(string))
   266  
   267  	log.Printf("[INFO] DB Instance ID: %s", d.Id())
   268  
   269  	log.Println(
   270  		"[INFO] Waiting for DB Instance to be available")
   271  
   272  	stateConf := &resource.StateChangeConf{
   273  		Pending:    []string{"creating", "backing-up", "modifying"},
   274  		Target:     "available",
   275  		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
   276  		Timeout:    40 * time.Minute,
   277  		MinTimeout: 10 * time.Second,
   278  		Delay:      30 * time.Second, // Wait 30 secs before starting
   279  	}
   280  
   281  	// Wait, catching any errors
   282  	_, err = stateConf.WaitForState()
   283  	if err != nil {
   284  		return err
   285  	}
   286  
   287  	return resourceAwsDbInstanceRead(d, meta)
   288  }
   289  
   290  func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error {
   291  	v, err := resourceAwsBbInstanceRetrieve(d, meta)
   292  
   293  	if err != nil {
   294  		return err
   295  	}
   296  	if v == nil {
   297  		d.SetId("")
   298  		return nil
   299  	}
   300  
   301  	d.Set("name", *v.DBName)
   302  	d.Set("username", *v.MasterUsername)
   303  	d.Set("engine", *v.Engine)
   304  	d.Set("engine_version", *v.EngineVersion)
   305  	d.Set("allocated_storage", *v.AllocatedStorage)
   306  	d.Set("storage_type", *v.StorageType)
   307  	d.Set("instance_class", *v.DBInstanceClass)
   308  	d.Set("availability_zone", *v.AvailabilityZone)
   309  	d.Set("backup_retention_period", *v.BackupRetentionPeriod)
   310  	d.Set("backup_window", *v.PreferredBackupWindow)
   311  	d.Set("maintenance_window", *v.PreferredMaintenanceWindow)
   312  	d.Set("multi_az", *v.MultiAZ)
   313  	d.Set("port", *v.Endpoint.Port)
   314  	d.Set("db_subnet_group_name", *v.DBSubnetGroup.DBSubnetGroupName)
   315  
   316  	if len(v.DBParameterGroups) > 0 {
   317  		d.Set("parameter_group_name", *v.DBParameterGroups[0].DBParameterGroupName)
   318  	}
   319  
   320  	d.Set("address", *v.Endpoint.Port)
   321  	d.Set("endpoint", fmt.Sprintf("%s:%d", *v.Endpoint.Address, *v.Endpoint.Port))
   322  	d.Set("status", *v.DBInstanceStatus)
   323  
   324  	// Create an empty schema.Set to hold all vpc security group ids
   325  	ids := &schema.Set{
   326  		F: func(v interface{}) int {
   327  			return hashcode.String(v.(string))
   328  		},
   329  	}
   330  	for _, v := range v.VPCSecurityGroups {
   331  		ids.Add(*v.VPCSecurityGroupID)
   332  	}
   333  	d.Set("vpc_security_group_ids", ids)
   334  
   335  	// Create an empty schema.Set to hold all security group names
   336  	sgn := &schema.Set{
   337  		F: func(v interface{}) int {
   338  			return hashcode.String(v.(string))
   339  		},
   340  	}
   341  	for _, v := range v.DBSecurityGroups {
   342  		sgn.Add(*v.DBSecurityGroupName)
   343  	}
   344  	d.Set("security_group_names", sgn)
   345  
   346  	return nil
   347  }
   348  
   349  func resourceAwsDbInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   350  	conn := meta.(*AWSClient).rdsconn
   351  
   352  	log.Printf("[DEBUG] DB Instance destroy: %v", d.Id())
   353  
   354  	opts := rds.DeleteDBInstanceMessage{DBInstanceIdentifier: aws.String(d.Id())}
   355  
   356  	finalSnapshot := d.Get("final_snapshot_identifier").(string)
   357  	if finalSnapshot == "" {
   358  		opts.SkipFinalSnapshot = aws.Boolean(true)
   359  	} else {
   360  		opts.FinalDBSnapshotIdentifier = aws.String(finalSnapshot)
   361  	}
   362  
   363  	log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts)
   364  	if _, err := conn.DeleteDBInstance(&opts); err != nil {
   365  		return err
   366  	}
   367  
   368  	log.Println(
   369  		"[INFO] Waiting for DB Instance to be destroyed")
   370  	stateConf := &resource.StateChangeConf{
   371  		Pending: []string{"creating", "backing-up",
   372  			"modifying", "deleting", "available"},
   373  		Target:     "",
   374  		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
   375  		Timeout:    40 * time.Minute,
   376  		MinTimeout: 10 * time.Second,
   377  		Delay:      30 * time.Second, // Wait 30 secs before starting
   378  	}
   379  	if _, err := stateConf.WaitForState(); err != nil {
   380  		return err
   381  	}
   382  
   383  	return nil
   384  }
   385  
   386  func resourceAwsBbInstanceRetrieve(
   387  	d *schema.ResourceData, meta interface{}) (*rds.DBInstance, error) {
   388  	conn := meta.(*AWSClient).rdsconn
   389  
   390  	opts := rds.DescribeDBInstancesMessage{
   391  		DBInstanceIdentifier: aws.String(d.Id()),
   392  	}
   393  
   394  	log.Printf("[DEBUG] DB Instance describe configuration: %#v", opts)
   395  
   396  	resp, err := conn.DescribeDBInstances(&opts)
   397  
   398  	if err != nil {
   399  		dbinstanceerr, ok := err.(aws.APIError)
   400  		if ok && dbinstanceerr.Code == "DBInstanceNotFound" {
   401  			return nil, nil
   402  		}
   403  		return nil, fmt.Errorf("Error retrieving DB Instances: %s", err)
   404  	}
   405  
   406  	if len(resp.DBInstances) != 1 ||
   407  		*resp.DBInstances[0].DBInstanceIdentifier != d.Id() {
   408  		if err != nil {
   409  			return nil, nil
   410  		}
   411  	}
   412  
   413  	v := resp.DBInstances[0]
   414  
   415  	return &v, nil
   416  }
   417  
   418  func resourceAwsDbInstanceStateRefreshFunc(
   419  	d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   420  	return func() (interface{}, string, error) {
   421  		v, err := resourceAwsBbInstanceRetrieve(d, meta)
   422  
   423  		if err != nil {
   424  			log.Printf("Error on retrieving DB Instance when waiting: %s", err)
   425  			return nil, "", err
   426  		}
   427  
   428  		if v == nil {
   429  			return nil, "", nil
   430  		}
   431  
   432  		return v, *v.DBInstanceStatus, nil
   433  	}
   434  }