github.com/blacked/terraform@v0.6.2-0.20150806163846-669c4ad71586/builtin/providers/aws/resource_aws_elasticache_replication_group.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/awserr" 10 "github.com/aws/aws-sdk-go/service/elasticache" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsElasticacheReplicationGroup() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsElasticacheReplicationGroupCreate, 18 Read: resourceAwsElasticacheReplicationGroupRead, 19 Update: resourceAwsElasticacheReplicationGroupUpdate, 20 Delete: resourceAwsElasticacheReplicationGroupDelete, 21 22 Schema: map[string]*schema.Schema{ 23 "replication_group_id": &schema.Schema{ 24 Type: schema.TypeString, 25 Required: true, 26 ForceNew: true, 27 }, 28 "description": &schema.Schema{ 29 Type: schema.TypeString, 30 Required: true, 31 }, 32 "cache_node_type": &schema.Schema{ 33 Type: schema.TypeString, 34 Optional: true, 35 ForceNew: true, 36 }, 37 "automatic_failover": &schema.Schema{ 38 Type: schema.TypeBool, 39 Optional: true, 40 }, 41 "num_cache_clusters": &schema.Schema{ 42 Type: schema.TypeInt, 43 Optional: true, 44 Default: 1, 45 ForceNew: true, 46 }, 47 "parameter_group_name": &schema.Schema{ 48 Type: schema.TypeString, 49 Optional: true, 50 }, 51 "subnet_group_name": &schema.Schema{ 52 Type: schema.TypeString, 53 Optional: true, 54 ForceNew: true, 55 }, 56 "security_group_names": &schema.Schema{ 57 Type: schema.TypeSet, 58 Optional: true, 59 Computed: true, 60 Elem: &schema.Schema{Type: schema.TypeString}, 61 Set: schema.HashString, 62 }, 63 "security_group_ids": &schema.Schema{ 64 Type: schema.TypeSet, 65 Optional: true, 66 Computed: true, 67 Elem: &schema.Schema{Type: schema.TypeString}, 68 Set: schema.HashString, 69 }, 70 "engine": &schema.Schema{ 71 Type: schema.TypeString, 72 Optional: true, 73 ForceNew: true, 74 Default: "redis", 75 }, 76 "engine_version": &schema.Schema{ 77 Type: schema.TypeString, 78 Optional: true, 79 }, 80 "primary_endpoint": &schema.Schema{ 81 Type: schema.TypeString, 82 Computed: true, 83 }, 84 }, 85 } 86 } 87 88 func resourceAwsElasticacheReplicationGroupCreate(d *schema.ResourceData, meta interface{}) error { 89 conn := meta.(*AWSClient).elasticacheconn 90 91 replicationGroupId := d.Get("replication_group_id").(string) 92 description := d.Get("description").(string) 93 cacheNodeType := d.Get("cache_node_type").(string) 94 automaticFailover := d.Get("automatic_failover").(bool) 95 numCacheClusters := d.Get("num_cache_clusters").(int) 96 engine := d.Get("engine").(string) 97 engineVersion := d.Get("engine_version").(string) 98 securityNameSet := d.Get("security_group_names").(*schema.Set) 99 securityIdSet := d.Get("security_group_ids").(*schema.Set) 100 subnetGroupName := d.Get("subnet_group_name").(string) 101 102 securityNames := expandStringList(securityNameSet.List()) 103 securityIds := expandStringList(securityIdSet.List()) 104 105 req := &elasticache.CreateReplicationGroupInput{ 106 ReplicationGroupID: aws.String(replicationGroupId), 107 ReplicationGroupDescription: aws.String(description), 108 CacheNodeType: aws.String(cacheNodeType), 109 AutomaticFailoverEnabled: aws.Bool(automaticFailover), 110 NumCacheClusters: aws.Int64(int64(numCacheClusters)), 111 Engine: aws.String(engine), 112 CacheSubnetGroupName: aws.String(subnetGroupName), 113 EngineVersion: aws.String(engineVersion), 114 CacheSecurityGroupNames: securityNames, 115 SecurityGroupIDs: securityIds, 116 } 117 118 if v, ok := d.GetOk("parameter_group_name"); ok { 119 req.CacheParameterGroupName = aws.String(v.(string)) 120 } 121 122 _, err := conn.CreateReplicationGroup(req) 123 if err != nil { 124 return fmt.Errorf("Error creating Elasticache replication group: %s", err) 125 } 126 127 pending := []string{"creating"} 128 stateConf := &resource.StateChangeConf{ 129 Pending: pending, 130 Target: "available", 131 Refresh: ReplicationGroupStateRefreshFunc(conn, d.Id(), "available", pending), 132 Timeout: 60 * time.Minute, 133 Delay: 20 * time.Second, 134 MinTimeout: 5 * time.Second, 135 } 136 137 log.Printf("[DEBUG] Waiting for state to become available: %v", d.Id()) 138 _, sterr := stateConf.WaitForState() 139 if sterr != nil { 140 return fmt.Errorf("Error waiting for elasticache (%s) to be created: %s", d.Id(), sterr) 141 } 142 143 d.SetId(replicationGroupId) 144 145 return nil 146 } 147 148 func resourceAwsElasticacheReplicationGroupRead(d *schema.ResourceData, meta interface{}) error { 149 conn := meta.(*AWSClient).elasticacheconn 150 151 req := &elasticache.DescribeReplicationGroupsInput{ 152 ReplicationGroupID: aws.String(d.Id()), 153 } 154 155 res, err := conn.DescribeReplicationGroups(req) 156 if err != nil { 157 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "ReplicationGroupNotFoundFault" { 158 // Update state to indicate the replication group no longer exists. 159 d.SetId("") 160 return nil 161 } 162 163 return err 164 } 165 166 if len(res.ReplicationGroups) == 1 { 167 c := res.ReplicationGroups[0] 168 d.Set("replication_group_id", c.ReplicationGroupID) 169 d.Set("description", c.Description) 170 d.Set("automatic_failover", c.AutomaticFailover) 171 d.Set("num_cache_clusters", len(c.MemberClusters)) 172 } 173 d.Set("primary_endpoint", res.ReplicationGroups[0].NodeGroups[0].PrimaryEndpoint.Address) 174 175 return nil 176 } 177 178 func resourceAwsElasticacheReplicationGroupUpdate(d *schema.ResourceData, meta interface{}) error { 179 conn := meta.(*AWSClient).elasticacheconn 180 181 req := &elasticache.ModifyReplicationGroupInput{ 182 ApplyImmediately: aws.Bool(true), 183 ReplicationGroupID: aws.String(d.Id()), 184 } 185 186 if d.HasChange("automatic_failover") { 187 automaticFailover := d.Get("automatic_failover").(bool) 188 req.AutomaticFailoverEnabled = aws.Bool(automaticFailover) 189 } 190 191 if d.HasChange("description") { 192 description := d.Get("description").(string) 193 req.ReplicationGroupDescription = aws.String(description) 194 } 195 196 if d.HasChange("engine_version") { 197 engine_version := d.Get("engine_version").(string) 198 req.EngineVersion = aws.String(engine_version) 199 } 200 201 if d.HasChange("security_group_ids") { 202 securityIdSet := d.Get("security_group_ids").(*schema.Set) 203 securityIds := expandStringList(securityIdSet.List()) 204 req.SecurityGroupIDs = securityIds 205 } 206 207 if d.HasChange("security_group_names") { 208 securityNameSet := d.Get("security_group_names").(*schema.Set) 209 securityNames := expandStringList(securityNameSet.List()) 210 req.CacheSecurityGroupNames = securityNames 211 } 212 213 _, err := conn.ModifyReplicationGroup(req) 214 if err != nil { 215 return fmt.Errorf("Error updating Elasticache replication group: %s", err) 216 } 217 218 return resourceAwsElasticacheReplicationGroupRead(d, meta) 219 } 220 221 func resourceAwsElasticacheReplicationGroupDelete(d *schema.ResourceData, meta interface{}) error { 222 conn := meta.(*AWSClient).elasticacheconn 223 224 req := &elasticache.DeleteReplicationGroupInput{ 225 ReplicationGroupID: aws.String(d.Id()), 226 } 227 228 _, err := conn.DeleteReplicationGroup(req) 229 if err != nil { 230 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "ReplicationGroupNotFoundFault" { 231 // Update state to indicate the replication group no longer exists. 232 d.SetId("") 233 return nil 234 } 235 236 return fmt.Errorf("Error deleting Elasticache replication group: %s", err) 237 } 238 239 log.Printf("[DEBUG] Waiting for deletion: %v", d.Id()) 240 stateConf := &resource.StateChangeConf{ 241 Pending: []string{"creating", "available", "deleting"}, 242 Target: "", 243 Refresh: ReplicationGroupStateRefreshFunc(conn, d.Id(), "", []string{}), 244 Timeout: 15 * time.Minute, 245 Delay: 20 * time.Second, 246 MinTimeout: 5 * time.Second, 247 } 248 249 _, sterr := stateConf.WaitForState() 250 if sterr != nil { 251 return fmt.Errorf("Error waiting for replication group (%s) to delete: %s", d.Id(), sterr) 252 } 253 254 return nil 255 } 256 257 func ReplicationGroupStateRefreshFunc(conn *elasticache.ElastiCache, replicationGroupID, givenState string, pending []string) resource.StateRefreshFunc { 258 return func() (interface{}, string, error) { 259 resp, err := conn.DescribeReplicationGroups(&elasticache.DescribeReplicationGroupsInput{ 260 ReplicationGroupID: aws.String(replicationGroupID), 261 }) 262 if err != nil { 263 ec2err, ok := err.(awserr.Error) 264 265 if ok { 266 log.Printf("[DEBUG] message: %v, code: %v", ec2err.Message(), ec2err.Code()) 267 if ec2err.Code() == "ReplicationGroupNotFoundFault" { 268 log.Printf("[DEBUG] Detect deletion") 269 return nil, "", nil 270 } 271 } 272 273 log.Printf("[ERROR] ReplicationGroupStateRefreshFunc: %s", err) 274 return nil, "", err 275 } 276 277 c := resp.ReplicationGroups[0] 278 log.Printf("[DEBUG] status: %v", *c.Status) 279 280 // return the current state if it's in the pending array 281 for _, p := range pending { 282 s := *c.Status 283 if p == s { 284 log.Printf("[DEBUG] Return with status: %v", *c.Status) 285 return c, p, nil 286 } 287 } 288 289 // return given state if it's not in pending 290 if givenState != "" { 291 return c, givenState, nil 292 } 293 log.Printf("[DEBUG] current status: %v", *c.Status) 294 return c, *c.Status, nil 295 } 296 }