github.com/i0n/terraform@v0.4.3-0.20150506151324-010a39a58ec1/builtin/providers/aws/resource_aws_elasticache_cluster.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/awslabs/aws-sdk-go/aws" 9 "github.com/awslabs/aws-sdk-go/service/elasticache" 10 "github.com/hashicorp/terraform/helper/hashcode" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsElasticacheCluster() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsElasticacheClusterCreate, 18 Read: resourceAwsElasticacheClusterRead, 19 Delete: resourceAwsElasticacheClusterDelete, 20 21 Schema: map[string]*schema.Schema{ 22 "cluster_id": &schema.Schema{ 23 Type: schema.TypeString, 24 Required: true, 25 ForceNew: true, 26 }, 27 "engine": &schema.Schema{ 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 }, 32 "node_type": &schema.Schema{ 33 Type: schema.TypeString, 34 Required: true, 35 ForceNew: true, 36 }, 37 "num_cache_nodes": &schema.Schema{ 38 Type: schema.TypeInt, 39 Required: true, 40 ForceNew: true, 41 }, 42 "parameter_group_name": &schema.Schema{ 43 Type: schema.TypeString, 44 Optional: true, 45 ForceNew: true, 46 }, 47 "port": &schema.Schema{ 48 Type: schema.TypeInt, 49 Default: 11211, 50 Optional: true, 51 ForceNew: true, 52 }, 53 "engine_version": &schema.Schema{ 54 Type: schema.TypeString, 55 Optional: true, 56 ForceNew: true, 57 }, 58 "subnet_group_name": &schema.Schema{ 59 Type: schema.TypeString, 60 Optional: true, 61 ForceNew: true, 62 }, 63 "security_group_names": &schema.Schema{ 64 Type: schema.TypeSet, 65 Optional: true, 66 Computed: true, 67 ForceNew: true, 68 Elem: &schema.Schema{Type: schema.TypeString}, 69 Set: func(v interface{}) int { 70 return hashcode.String(v.(string)) 71 }, 72 }, 73 "security_group_ids": &schema.Schema{ 74 Type: schema.TypeSet, 75 Optional: true, 76 Computed: true, 77 ForceNew: true, 78 Elem: &schema.Schema{Type: schema.TypeString}, 79 Set: func(v interface{}) int { 80 return hashcode.String(v.(string)) 81 }, 82 }, 83 }, 84 } 85 } 86 87 func resourceAwsElasticacheClusterCreate(d *schema.ResourceData, meta interface{}) error { 88 conn := meta.(*AWSClient).elasticacheconn 89 90 clusterId := d.Get("cluster_id").(string) 91 nodeType := d.Get("node_type").(string) // e.g) cache.m1.small 92 numNodes := int64(d.Get("num_cache_nodes").(int)) // 2 93 engine := d.Get("engine").(string) // memcached 94 engineVersion := d.Get("engine_version").(string) // 1.4.14 95 port := int64(d.Get("port").(int)) // 11211 96 subnetGroupName := d.Get("subnet_group_name").(string) 97 securityNameSet := d.Get("security_group_names").(*schema.Set) 98 securityIdSet := d.Get("security_group_ids").(*schema.Set) 99 paramGroupName := d.Get("parameter_group_name").(string) // default.memcached1.4 100 101 securityNames := expandStringList(securityNameSet.List()) 102 securityIds := expandStringList(securityIdSet.List()) 103 104 req := &elasticache.CreateCacheClusterInput{ 105 CacheClusterID: aws.String(clusterId), 106 CacheNodeType: aws.String(nodeType), 107 NumCacheNodes: aws.Long(numNodes), 108 Engine: aws.String(engine), 109 EngineVersion: aws.String(engineVersion), 110 Port: aws.Long(port), 111 CacheSubnetGroupName: aws.String(subnetGroupName), 112 CacheSecurityGroupNames: securityNames, 113 SecurityGroupIDs: securityIds, 114 CacheParameterGroupName: aws.String(paramGroupName), 115 } 116 117 _, err := conn.CreateCacheCluster(req) 118 if err != nil { 119 return fmt.Errorf("Error creating Elasticache: %s", err) 120 } 121 122 pending := []string{"creating"} 123 stateConf := &resource.StateChangeConf{ 124 Pending: pending, 125 Target: "available", 126 Refresh: CacheClusterStateRefreshFunc(conn, d.Id(), "available", pending), 127 Timeout: 10 * time.Minute, 128 Delay: 10 * time.Second, 129 MinTimeout: 3 * time.Second, 130 } 131 132 log.Printf("[DEBUG] Waiting for state to become available: %v", d.Id()) 133 _, sterr := stateConf.WaitForState() 134 if sterr != nil { 135 return fmt.Errorf("Error waiting for elasticache (%s) to be created: %s", d.Id(), sterr) 136 } 137 138 d.SetId(clusterId) 139 140 return nil 141 } 142 143 func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{}) error { 144 conn := meta.(*AWSClient).elasticacheconn 145 req := &elasticache.DescribeCacheClustersInput{ 146 CacheClusterID: aws.String(d.Id()), 147 } 148 149 res, err := conn.DescribeCacheClusters(req) 150 if err != nil { 151 return err 152 } 153 154 if len(res.CacheClusters) == 1 { 155 c := res.CacheClusters[0] 156 d.Set("cluster_id", c.CacheClusterID) 157 d.Set("node_type", c.CacheNodeType) 158 d.Set("num_cache_nodes", c.NumCacheNodes) 159 d.Set("engine", c.Engine) 160 d.Set("engine_version", c.EngineVersion) 161 if c.ConfigurationEndpoint != nil { 162 d.Set("port", c.ConfigurationEndpoint.Port) 163 } 164 d.Set("subnet_group_name", c.CacheSubnetGroupName) 165 d.Set("security_group_names", c.CacheSecurityGroups) 166 d.Set("security_group_ids", c.SecurityGroups) 167 d.Set("parameter_group_name", c.CacheParameterGroup) 168 } 169 170 return nil 171 } 172 173 func resourceAwsElasticacheClusterDelete(d *schema.ResourceData, meta interface{}) error { 174 conn := meta.(*AWSClient).elasticacheconn 175 176 req := &elasticache.DeleteCacheClusterInput{ 177 CacheClusterID: aws.String(d.Id()), 178 } 179 _, err := conn.DeleteCacheCluster(req) 180 if err != nil { 181 return err 182 } 183 184 log.Printf("[DEBUG] Waiting for deletion: %v", d.Id()) 185 stateConf := &resource.StateChangeConf{ 186 Pending: []string{"creating", "available", "deleting", "incompatible-parameters", "incompatible-network", "restore-failed"}, 187 Target: "", 188 Refresh: CacheClusterStateRefreshFunc(conn, d.Id(), "", []string{}), 189 Timeout: 10 * time.Minute, 190 Delay: 10 * time.Second, 191 MinTimeout: 3 * time.Second, 192 } 193 194 _, sterr := stateConf.WaitForState() 195 if sterr != nil { 196 return fmt.Errorf("Error waiting for elasticache (%s) to delete: %s", d.Id(), sterr) 197 } 198 199 d.SetId("") 200 201 return nil 202 } 203 204 func CacheClusterStateRefreshFunc(conn *elasticache.ElastiCache, clusterID, givenState string, pending []string) resource.StateRefreshFunc { 205 return func() (interface{}, string, error) { 206 resp, err := conn.DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{ 207 CacheClusterID: aws.String(clusterID), 208 }) 209 if err != nil { 210 apierr := err.(aws.APIError) 211 log.Printf("[DEBUG] message: %v, code: %v", apierr.Message, apierr.Code) 212 if apierr.Message == fmt.Sprintf("CacheCluster not found: %v", clusterID) { 213 log.Printf("[DEBUG] Detect deletion") 214 return nil, "", nil 215 } 216 217 log.Printf("[ERROR] CacheClusterStateRefreshFunc: %s", err) 218 return nil, "", err 219 } 220 221 c := resp.CacheClusters[0] 222 log.Printf("[DEBUG] status: %v", *c.CacheClusterStatus) 223 224 // return the current state if it's in the pending array 225 for _, p := range pending { 226 s := *c.CacheClusterStatus 227 if p == s { 228 log.Printf("[DEBUG] Return with status: %v", *c.CacheClusterStatus) 229 return c, p, nil 230 } 231 } 232 233 // return given state if it's not in pending 234 if givenState != "" { 235 return c, givenState, nil 236 } 237 log.Printf("[DEBUG] current status: %v", *c.CacheClusterStatus) 238 return c, *c.CacheClusterStatus, nil 239 } 240 }