Merge pull request #2469 from hashicorp/f-aws-update-elasticache

provider/aws: Allow in-place updates for ElastiCache cluster
This commit is contained in:
Clint 2015-06-25 11:29:33 -05:00
commit d072e8eb50
2 changed files with 74 additions and 9 deletions

View File

@ -9,6 +9,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/service/elasticache" "github.com/aws/aws-sdk-go/service/elasticache"
"github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/hashcode"
@ -32,7 +33,6 @@ func resourceAwsElasticacheCluster() *schema.Resource {
"engine": &schema.Schema{ "engine": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
ForceNew: true,
}, },
"node_type": &schema.Schema{ "node_type": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
@ -42,13 +42,11 @@ func resourceAwsElasticacheCluster() *schema.Resource {
"num_cache_nodes": &schema.Schema{ "num_cache_nodes": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
Required: true, Required: true,
ForceNew: true,
}, },
"parameter_group_name": &schema.Schema{ "parameter_group_name": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Computed: true, Computed: true,
ForceNew: true,
}, },
"port": &schema.Schema{ "port": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
@ -59,7 +57,6 @@ func resourceAwsElasticacheCluster() *schema.Resource {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Computed: true, Computed: true,
ForceNew: true,
}, },
"subnet_group_name": &schema.Schema{ "subnet_group_name": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
@ -81,7 +78,6 @@ func resourceAwsElasticacheCluster() *schema.Resource {
Type: schema.TypeSet, Type: schema.TypeSet,
Optional: true, Optional: true,
Computed: true, Computed: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString}, Elem: &schema.Schema{Type: schema.TypeString},
Set: func(v interface{}) int { Set: func(v interface{}) int {
return hashcode.String(v.(string)) return hashcode.String(v.(string))
@ -110,6 +106,15 @@ func resourceAwsElasticacheCluster() *schema.Resource {
}, },
"tags": tagsSchema(), "tags": tagsSchema(),
// apply_immediately is used to determine when the update modifications
// take place.
// See http://docs.aws.amazon.com/AmazonElastiCache/latest/APIReference/API_ModifyCacheCluster.html
"apply_immediately": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
}, },
} }
} }
@ -158,7 +163,7 @@ func resourceAwsElasticacheClusterCreate(d *schema.ResourceData, meta interface{
stateConf := &resource.StateChangeConf{ stateConf := &resource.StateChangeConf{
Pending: pending, Pending: pending,
Target: "available", Target: "available",
Refresh: CacheClusterStateRefreshFunc(conn, d.Id(), "available", pending), Refresh: cacheClusterStateRefreshFunc(conn, d.Id(), "available", pending),
Timeout: 10 * time.Minute, Timeout: 10 * time.Minute,
Delay: 10 * time.Second, Delay: 10 * time.Second,
MinTimeout: 3 * time.Second, MinTimeout: 3 * time.Second,
@ -240,6 +245,59 @@ func resourceAwsElasticacheClusterUpdate(d *schema.ResourceData, meta interface{
return err return err
} }
} }
req := &elasticache.ModifyCacheClusterInput{
CacheClusterID: aws.String(d.Id()),
ApplyImmediately: aws.Boolean(d.Get("apply_immediately").(bool)),
}
requestUpdate := false
if d.HasChange("security_group_ids") {
if attr := d.Get("security_group_ids").(*schema.Set); attr.Len() > 0 {
req.SecurityGroupIDs = expandStringList(attr.List())
requestUpdate = true
}
}
if d.HasChange("parameter_group_name") {
req.CacheParameterGroupName = aws.String(d.Get("parameter_group_name").(string))
requestUpdate = true
}
if d.HasChange("engine_version") {
req.EngineVersion = aws.String(d.Get("engine_version").(string))
requestUpdate = true
}
if d.HasChange("num_cache_nodes") {
req.NumCacheNodes = aws.Long(int64(d.Get("num_cache_nodes").(int)))
requestUpdate = true
}
if requestUpdate {
log.Printf("[DEBUG] Modifying ElastiCache Cluster (%s), opts:\n%s", d.Id(), awsutil.StringValue(req))
_, err := conn.ModifyCacheCluster(req)
if err != nil {
return fmt.Errorf("[WARN] Error updating ElastiCache cluster (%s), error: %s", d.Id(), err)
}
log.Printf("[DEBUG] Waiting for update: %s", d.Id())
pending := []string{"modifying", "rebooting cache cluster nodes", "snapshotting"}
stateConf := &resource.StateChangeConf{
Pending: pending,
Target: "available",
Refresh: cacheClusterStateRefreshFunc(conn, d.Id(), "available", pending),
Timeout: 5 * time.Minute,
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
_, sterr := stateConf.WaitForState()
if sterr != nil {
return fmt.Errorf("Error waiting for elasticache (%s) to update: %s", d.Id(), sterr)
}
}
return resourceAwsElasticacheClusterRead(d, meta) return resourceAwsElasticacheClusterRead(d, meta)
} }
@ -252,7 +310,7 @@ func setCacheNodeData(d *schema.ResourceData, c *elasticache.CacheCluster) error
for _, node := range sortedCacheNodes { for _, node := range sortedCacheNodes {
if node.CacheNodeID == nil || node.Endpoint == nil || node.Endpoint.Address == nil || node.Endpoint.Port == nil { if node.CacheNodeID == nil || node.Endpoint == nil || node.Endpoint.Address == nil || node.Endpoint.Port == nil {
return fmt.Errorf("Unexpected nil pointer in: %#v", node) return fmt.Errorf("Unexpected nil pointer in: %s", awsutil.StringValue(node))
} }
cacheNodeData = append(cacheNodeData, map[string]interface{}{ cacheNodeData = append(cacheNodeData, map[string]interface{}{
"id": *node.CacheNodeID, "id": *node.CacheNodeID,
@ -288,7 +346,7 @@ func resourceAwsElasticacheClusterDelete(d *schema.ResourceData, meta interface{
stateConf := &resource.StateChangeConf{ stateConf := &resource.StateChangeConf{
Pending: []string{"creating", "available", "deleting", "incompatible-parameters", "incompatible-network", "restore-failed"}, Pending: []string{"creating", "available", "deleting", "incompatible-parameters", "incompatible-network", "restore-failed"},
Target: "", Target: "",
Refresh: CacheClusterStateRefreshFunc(conn, d.Id(), "", []string{}), Refresh: cacheClusterStateRefreshFunc(conn, d.Id(), "", []string{}),
Timeout: 10 * time.Minute, Timeout: 10 * time.Minute,
Delay: 10 * time.Second, Delay: 10 * time.Second,
MinTimeout: 3 * time.Second, MinTimeout: 3 * time.Second,
@ -304,7 +362,7 @@ func resourceAwsElasticacheClusterDelete(d *schema.ResourceData, meta interface{
return nil return nil
} }
func CacheClusterStateRefreshFunc(conn *elasticache.ElastiCache, clusterID, givenState string, pending []string) resource.StateRefreshFunc { func cacheClusterStateRefreshFunc(conn *elasticache.ElastiCache, clusterID, givenState string, pending []string) resource.StateRefreshFunc {
return func() (interface{}, string, error) { return func() (interface{}, string, error) {
resp, err := conn.DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{ resp, err := conn.DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{
CacheClusterID: aws.String(clusterID), CacheClusterID: aws.String(clusterID),

View File

@ -60,6 +60,11 @@ names to associate with this cache cluster
* `security_group_ids` (Optional, VPC only) One or more VPC security groups associated * `security_group_ids` (Optional, VPC only) One or more VPC security groups associated
with the cache cluster with the cache cluster
* `apply_immediately` - (Optional) Specifies whether any database modifications
are applied immediately, or during the next maintenance window. Default is
`false`. See [Amazon ElastiCache Documentation for more information.][1]
(Available since v0.6.0)
* `tags` - (Optional) A mapping of tags to assign to the resource. * `tags` - (Optional) A mapping of tags to assign to the resource.
@ -69,3 +74,5 @@ The following attributes are exported:
* `cache_nodes` - List of node objects including `id`, `address` and `port`. * `cache_nodes` - List of node objects including `id`, `address` and `port`.
Referenceable e.g. as `${aws_elasticache_cluster.bar.cache_nodes.0.address}` Referenceable e.g. as `${aws_elasticache_cluster.bar.cache_nodes.0.address}`
[1]: http://docs.aws.amazon.com/AmazonElastiCache/latest/APIReference/API_ModifyCacheCluster.html