github.com/jrperritt/terraform@v0.1.1-0.20170525065507-96f391dafc38/builtin/providers/aws/resource_aws_rds_cluster_instance.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/service/rds"
    11  	"github.com/hashicorp/terraform/helper/resource"
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  )
    14  
    15  func resourceAwsRDSClusterInstance() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceAwsRDSClusterInstanceCreate,
    18  		Read:   resourceAwsRDSClusterInstanceRead,
    19  		Update: resourceAwsRDSClusterInstanceUpdate,
    20  		Delete: resourceAwsRDSClusterInstanceDelete,
    21  		Importer: &schema.ResourceImporter{
    22  			State: schema.ImportStatePassthrough,
    23  		},
    24  
    25  		Timeouts: &schema.ResourceTimeout{
    26  			Create: schema.DefaultTimeout(90 * time.Minute),
    27  			Update: schema.DefaultTimeout(90 * time.Minute),
    28  			Delete: schema.DefaultTimeout(90 * time.Minute),
    29  		},
    30  
    31  		Schema: map[string]*schema.Schema{
    32  			"identifier": {
    33  				Type:          schema.TypeString,
    34  				Optional:      true,
    35  				Computed:      true,
    36  				ForceNew:      true,
    37  				ConflictsWith: []string{"identifier_prefix"},
    38  				ValidateFunc:  validateRdsIdentifier,
    39  			},
    40  			"identifier_prefix": {
    41  				Type:         schema.TypeString,
    42  				Optional:     true,
    43  				Computed:     true,
    44  				ForceNew:     true,
    45  				ValidateFunc: validateRdsIdentifierPrefix,
    46  			},
    47  
    48  			"db_subnet_group_name": {
    49  				Type:     schema.TypeString,
    50  				Optional: true,
    51  				ForceNew: true,
    52  				Computed: true,
    53  			},
    54  
    55  			"writer": {
    56  				Type:     schema.TypeBool,
    57  				Computed: true,
    58  			},
    59  
    60  			"cluster_identifier": {
    61  				Type:     schema.TypeString,
    62  				Required: true,
    63  				ForceNew: true,
    64  			},
    65  
    66  			"endpoint": {
    67  				Type:     schema.TypeString,
    68  				Computed: true,
    69  			},
    70  
    71  			"port": {
    72  				Type:     schema.TypeInt,
    73  				Computed: true,
    74  			},
    75  
    76  			"publicly_accessible": {
    77  				Type:     schema.TypeBool,
    78  				Optional: true,
    79  				Default:  false,
    80  			},
    81  
    82  			"instance_class": {
    83  				Type:     schema.TypeString,
    84  				Required: true,
    85  			},
    86  
    87  			"db_parameter_group_name": {
    88  				Type:     schema.TypeString,
    89  				Optional: true,
    90  				Computed: true,
    91  			},
    92  
    93  			// apply_immediately is used to determine when the update modifications
    94  			// take place.
    95  			// See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html
    96  			"apply_immediately": {
    97  				Type:     schema.TypeBool,
    98  				Optional: true,
    99  				Computed: true,
   100  			},
   101  
   102  			"kms_key_id": {
   103  				Type:     schema.TypeString,
   104  				Computed: true,
   105  			},
   106  
   107  			"storage_encrypted": {
   108  				Type:     schema.TypeBool,
   109  				Computed: true,
   110  			},
   111  
   112  			"auto_minor_version_upgrade": {
   113  				Type:     schema.TypeBool,
   114  				Optional: true,
   115  				Default:  true,
   116  			},
   117  
   118  			"monitoring_role_arn": {
   119  				Type:     schema.TypeString,
   120  				Optional: true,
   121  				Computed: true,
   122  			},
   123  
   124  			"preferred_maintenance_window": {
   125  				Type:     schema.TypeString,
   126  				Optional: true,
   127  				Computed: true,
   128  				StateFunc: func(v interface{}) string {
   129  					if v != nil {
   130  						value := v.(string)
   131  						return strings.ToLower(value)
   132  					}
   133  					return ""
   134  				},
   135  				ValidateFunc: validateOnceAWeekWindowFormat,
   136  			},
   137  
   138  			"preferred_backup_window": {
   139  				Type:         schema.TypeString,
   140  				Optional:     true,
   141  				Computed:     true,
   142  				ValidateFunc: validateOnceADayWindowFormat,
   143  			},
   144  
   145  			"monitoring_interval": {
   146  				Type:     schema.TypeInt,
   147  				Optional: true,
   148  				Default:  0,
   149  			},
   150  
   151  			"promotion_tier": {
   152  				Type:     schema.TypeInt,
   153  				Optional: true,
   154  				Default:  0,
   155  			},
   156  
   157  			"tags": tagsSchema(),
   158  		},
   159  	}
   160  }
   161  
   162  func resourceAwsRDSClusterInstanceCreate(d *schema.ResourceData, meta interface{}) error {
   163  	conn := meta.(*AWSClient).rdsconn
   164  	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
   165  
   166  	createOpts := &rds.CreateDBInstanceInput{
   167  		DBInstanceClass:         aws.String(d.Get("instance_class").(string)),
   168  		DBClusterIdentifier:     aws.String(d.Get("cluster_identifier").(string)),
   169  		Engine:                  aws.String("aurora"),
   170  		PubliclyAccessible:      aws.Bool(d.Get("publicly_accessible").(bool)),
   171  		PromotionTier:           aws.Int64(int64(d.Get("promotion_tier").(int))),
   172  		AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)),
   173  		Tags: tags,
   174  	}
   175  
   176  	if attr, ok := d.GetOk("db_parameter_group_name"); ok {
   177  		createOpts.DBParameterGroupName = aws.String(attr.(string))
   178  	}
   179  
   180  	if v, ok := d.GetOk("identifier"); ok {
   181  		createOpts.DBInstanceIdentifier = aws.String(v.(string))
   182  	} else {
   183  		if v, ok := d.GetOk("identifier_prefix"); ok {
   184  			createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId(v.(string)))
   185  		} else {
   186  			createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId("tf-"))
   187  		}
   188  	}
   189  
   190  	if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   191  		createOpts.DBSubnetGroupName = aws.String(attr.(string))
   192  	}
   193  
   194  	if attr, ok := d.GetOk("monitoring_role_arn"); ok {
   195  		createOpts.MonitoringRoleArn = aws.String(attr.(string))
   196  	}
   197  
   198  	if attr, ok := d.GetOk("preferred_backup_window"); ok {
   199  		createOpts.PreferredBackupWindow = aws.String(attr.(string))
   200  	}
   201  
   202  	if attr, ok := d.GetOk("preferred_maintenance_window"); ok {
   203  		createOpts.PreferredMaintenanceWindow = aws.String(attr.(string))
   204  	}
   205  
   206  	if attr, ok := d.GetOk("monitoring_interval"); ok {
   207  		createOpts.MonitoringInterval = aws.Int64(int64(attr.(int)))
   208  	}
   209  
   210  	log.Printf("[DEBUG] Creating RDS DB Instance opts: %s", createOpts)
   211  	resp, err := conn.CreateDBInstance(createOpts)
   212  	if err != nil {
   213  		return err
   214  	}
   215  
   216  	d.SetId(*resp.DBInstance.DBInstanceIdentifier)
   217  
   218  	// reuse db_instance refresh func
   219  	stateConf := &resource.StateChangeConf{
   220  		Pending:    []string{"creating", "backing-up", "modifying"},
   221  		Target:     []string{"available"},
   222  		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
   223  		Timeout:    d.Timeout(schema.TimeoutCreate),
   224  		MinTimeout: 10 * time.Second,
   225  		Delay:      30 * time.Second,
   226  	}
   227  
   228  	// Wait, catching any errors
   229  	_, err = stateConf.WaitForState()
   230  	if err != nil {
   231  		return err
   232  	}
   233  
   234  	return resourceAwsRDSClusterInstanceRead(d, meta)
   235  }
   236  
   237  func resourceAwsRDSClusterInstanceRead(d *schema.ResourceData, meta interface{}) error {
   238  	db, err := resourceAwsDbInstanceRetrieve(d, meta)
   239  	// Errors from this helper are always reportable
   240  	if err != nil {
   241  		return fmt.Errorf("[WARN] Error on retrieving RDS Cluster Instance (%s): %s", d.Id(), err)
   242  	}
   243  	// A nil response means "not found"
   244  	if db == nil {
   245  		log.Printf("[WARN] RDS Cluster Instance (%s): not found, removing from state.", d.Id())
   246  		d.SetId("")
   247  		return nil
   248  	}
   249  
   250  	// Retrieve DB Cluster information, to determine if this Instance is a writer
   251  	conn := meta.(*AWSClient).rdsconn
   252  	resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
   253  		DBClusterIdentifier: db.DBClusterIdentifier,
   254  	})
   255  
   256  	var dbc *rds.DBCluster
   257  	for _, c := range resp.DBClusters {
   258  		if *c.DBClusterIdentifier == *db.DBClusterIdentifier {
   259  			dbc = c
   260  		}
   261  	}
   262  
   263  	if dbc == nil {
   264  		return fmt.Errorf("[WARN] Error finding RDS Cluster (%s) for Cluster Instance (%s): %s",
   265  			*db.DBClusterIdentifier, *db.DBInstanceIdentifier, err)
   266  	}
   267  
   268  	for _, m := range dbc.DBClusterMembers {
   269  		if *db.DBInstanceIdentifier == *m.DBInstanceIdentifier {
   270  			if *m.IsClusterWriter == true {
   271  				d.Set("writer", true)
   272  			} else {
   273  				d.Set("writer", false)
   274  			}
   275  		}
   276  	}
   277  
   278  	if db.Endpoint != nil {
   279  		d.Set("endpoint", db.Endpoint.Address)
   280  		d.Set("port", db.Endpoint.Port)
   281  	}
   282  
   283  	d.Set("publicly_accessible", db.PubliclyAccessible)
   284  	d.Set("cluster_identifier", db.DBClusterIdentifier)
   285  	d.Set("instance_class", db.DBInstanceClass)
   286  	d.Set("identifier", db.DBInstanceIdentifier)
   287  	d.Set("storage_encrypted", db.StorageEncrypted)
   288  	d.Set("kms_key_id", db.KmsKeyId)
   289  	d.Set("auto_minor_version_upgrade", db.AutoMinorVersionUpgrade)
   290  	d.Set("promotion_tier", db.PromotionTier)
   291  	d.Set("preferred_backup_window", db.PreferredBackupWindow)
   292  	d.Set("preferred_maintenance_window", db.PreferredMaintenanceWindow)
   293  
   294  	if db.MonitoringInterval != nil {
   295  		d.Set("monitoring_interval", db.MonitoringInterval)
   296  	}
   297  
   298  	if db.MonitoringRoleArn != nil {
   299  		d.Set("monitoring_role_arn", db.MonitoringRoleArn)
   300  	}
   301  
   302  	if len(db.DBParameterGroups) > 0 {
   303  		d.Set("db_parameter_group_name", db.DBParameterGroups[0].DBParameterGroupName)
   304  	}
   305  
   306  	// Fetch and save tags
   307  	arn, err := buildRDSARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region)
   308  	if err != nil {
   309  		log.Printf("[DEBUG] Error building ARN for RDS Cluster Instance (%s), not setting Tags", *db.DBInstanceIdentifier)
   310  	} else {
   311  		if err := saveTagsRDS(conn, d, arn); err != nil {
   312  			log.Printf("[WARN] Failed to save tags for RDS Cluster Instance (%s): %s", *db.DBClusterIdentifier, err)
   313  		}
   314  	}
   315  
   316  	return nil
   317  }
   318  
   319  func resourceAwsRDSClusterInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   320  	conn := meta.(*AWSClient).rdsconn
   321  	requestUpdate := false
   322  
   323  	req := &rds.ModifyDBInstanceInput{
   324  		ApplyImmediately:     aws.Bool(d.Get("apply_immediately").(bool)),
   325  		DBInstanceIdentifier: aws.String(d.Id()),
   326  	}
   327  
   328  	if d.HasChange("db_parameter_group_name") {
   329  		req.DBParameterGroupName = aws.String(d.Get("db_parameter_group_name").(string))
   330  		requestUpdate = true
   331  	}
   332  
   333  	if d.HasChange("instance_class") {
   334  		req.DBInstanceClass = aws.String(d.Get("instance_class").(string))
   335  		requestUpdate = true
   336  	}
   337  
   338  	if d.HasChange("monitoring_role_arn") {
   339  		d.SetPartial("monitoring_role_arn")
   340  		req.MonitoringRoleArn = aws.String(d.Get("monitoring_role_arn").(string))
   341  		requestUpdate = true
   342  	}
   343  
   344  	if d.HasChange("preferred_backup_window") {
   345  		d.SetPartial("preferred_backup_window")
   346  		req.PreferredBackupWindow = aws.String(d.Get("preferred_backup_window").(string))
   347  		requestUpdate = true
   348  	}
   349  
   350  	if d.HasChange("preferred_maintenance_window") {
   351  		d.SetPartial("preferred_maintenance_window")
   352  		req.PreferredMaintenanceWindow = aws.String(d.Get("preferred_maintenance_window").(string))
   353  		requestUpdate = true
   354  	}
   355  
   356  	if d.HasChange("monitoring_interval") {
   357  		d.SetPartial("monitoring_interval")
   358  		req.MonitoringInterval = aws.Int64(int64(d.Get("monitoring_interval").(int)))
   359  		requestUpdate = true
   360  	}
   361  
   362  	if d.HasChange("auto_minor_version_upgrade") {
   363  		d.SetPartial("auto_minor_version_upgrade")
   364  		req.AutoMinorVersionUpgrade = aws.Bool(d.Get("auto_minor_version_upgrade").(bool))
   365  		requestUpdate = true
   366  	}
   367  
   368  	if d.HasChange("promotion_tier") {
   369  		d.SetPartial("promotion_tier")
   370  		req.PromotionTier = aws.Int64(int64(d.Get("promotion_tier").(int)))
   371  		requestUpdate = true
   372  	}
   373  
   374  	log.Printf("[DEBUG] Send DB Instance Modification request: %#v", requestUpdate)
   375  	if requestUpdate {
   376  		log.Printf("[DEBUG] DB Instance Modification request: %#v", req)
   377  		_, err := conn.ModifyDBInstance(req)
   378  		if err != nil {
   379  			return fmt.Errorf("Error modifying DB Instance %s: %s", d.Id(), err)
   380  		}
   381  
   382  		// reuse db_instance refresh func
   383  		stateConf := &resource.StateChangeConf{
   384  			Pending:    []string{"creating", "backing-up", "modifying"},
   385  			Target:     []string{"available"},
   386  			Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
   387  			Timeout:    d.Timeout(schema.TimeoutUpdate),
   388  			MinTimeout: 10 * time.Second,
   389  			Delay:      30 * time.Second, // Wait 30 secs before starting
   390  		}
   391  
   392  		// Wait, catching any errors
   393  		_, err = stateConf.WaitForState()
   394  		if err != nil {
   395  			return err
   396  		}
   397  
   398  	}
   399  
   400  	if arn, err := buildRDSARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region); err == nil {
   401  		if err := setTagsRDS(conn, d, arn); err != nil {
   402  			return err
   403  		}
   404  	}
   405  
   406  	return resourceAwsRDSClusterInstanceRead(d, meta)
   407  }
   408  
   409  func resourceAwsRDSClusterInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   410  	conn := meta.(*AWSClient).rdsconn
   411  
   412  	log.Printf("[DEBUG] RDS Cluster Instance destroy: %v", d.Id())
   413  
   414  	opts := rds.DeleteDBInstanceInput{DBInstanceIdentifier: aws.String(d.Id())}
   415  
   416  	log.Printf("[DEBUG] RDS Cluster Instance destroy configuration: %s", opts)
   417  	if _, err := conn.DeleteDBInstance(&opts); err != nil {
   418  		return err
   419  	}
   420  
   421  	// re-uses db_instance refresh func
   422  	log.Println("[INFO] Waiting for RDS Cluster Instance to be destroyed")
   423  	stateConf := &resource.StateChangeConf{
   424  		Pending:    []string{"modifying", "deleting"},
   425  		Target:     []string{},
   426  		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
   427  		Timeout:    d.Timeout(schema.TimeoutDelete),
   428  		MinTimeout: 10 * time.Second,
   429  		Delay:      30 * time.Second, // Wait 30 secs before starting
   430  	}
   431  
   432  	if _, err := stateConf.WaitForState(); err != nil {
   433  		return err
   434  	}
   435  
   436  	return nil
   437  
   438  }