github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/azurerm/resource_arm_redis_cache.go (about) 1 package azurerm 2 3 import ( 4 "fmt" 5 "log" 6 7 "net/http" 8 "strings" 9 "time" 10 11 "github.com/Azure/azure-sdk-for-go/arm/redis" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 "github.com/jen20/riviera/azure" 15 ) 16 17 func resourceArmRedisCache() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceArmRedisCacheCreate, 20 Read: resourceArmRedisCacheRead, 21 Update: resourceArmRedisCacheUpdate, 22 Delete: resourceArmRedisCacheDelete, 23 24 Schema: map[string]*schema.Schema{ 25 "name": { 26 Type: schema.TypeString, 27 Required: true, 28 ForceNew: true, 29 }, 30 31 "location": { 32 Type: schema.TypeString, 33 Required: true, 34 ForceNew: true, 35 StateFunc: azureRMNormalizeLocation, 36 }, 37 38 "resource_group_name": { 39 Type: schema.TypeString, 40 Required: true, 41 ForceNew: true, 42 }, 43 44 "capacity": { 45 Type: schema.TypeInt, 46 Required: true, 47 }, 48 49 "family": { 50 Type: schema.TypeString, 51 Required: true, 52 ValidateFunc: validateRedisFamily, 53 }, 54 55 "sku_name": { 56 Type: schema.TypeString, 57 Required: true, 58 ValidateFunc: validateRedisSku, 59 }, 60 61 "shard_count": { 62 Type: schema.TypeInt, 63 Optional: true, 64 }, 65 66 "enable_non_ssl_port": { 67 Type: schema.TypeBool, 68 Default: false, 69 Optional: true, 70 }, 71 72 "redis_configuration": { 73 Type: schema.TypeList, 74 Required: true, 75 MaxItems: 1, 76 Elem: &schema.Resource{ 77 Schema: map[string]*schema.Schema{ 78 "maxclients": { 79 Type: schema.TypeString, 80 Optional: true, 81 Computed: true, 82 }, 83 84 "maxmemory_delta": { 85 Type: schema.TypeString, 86 Optional: true, 87 Computed: true, 88 }, 89 90 "maxmemory_reserved": { 91 Type: schema.TypeString, 92 Optional: true, 93 Computed: true, 94 }, 95 96 "maxmemory_policy": { 97 Type: schema.TypeString, 98 Optional: true, 99 Default: "volatile-lru", 100 ValidateFunc: validateRedisMaxMemoryPolicy, 101 }, 102 }, 103 }, 104 }, 105 106 "hostname": { 107 Type: schema.TypeString, 108 Computed: true, 109 }, 110 111 "port": { 112 Type: schema.TypeInt, 113 Computed: true, 114 }, 115 116 "ssl_port": { 117 Type: schema.TypeInt, 118 Computed: true, 119 }, 120 121 "primary_access_key": { 122 Type: schema.TypeString, 123 Computed: true, 124 }, 125 126 "secondary_access_key": { 127 Type: schema.TypeString, 128 Computed: true, 129 }, 130 131 "tags": tagsSchema(), 132 }, 133 } 134 } 135 136 func resourceArmRedisCacheCreate(d *schema.ResourceData, meta interface{}) error { 137 client := meta.(*ArmClient).redisClient 138 log.Printf("[INFO] preparing arguments for Azure ARM Redis Cache creation.") 139 140 name := d.Get("name").(string) 141 location := d.Get("location").(string) 142 resGroup := d.Get("resource_group_name").(string) 143 144 enableNonSSLPort := d.Get("enable_non_ssl_port").(bool) 145 146 capacity := int32(d.Get("capacity").(int)) 147 family := redis.SkuFamily(d.Get("family").(string)) 148 sku := redis.SkuName(d.Get("sku_name").(string)) 149 150 tags := d.Get("tags").(map[string]interface{}) 151 expandedTags := expandTags(tags) 152 153 parameters := redis.CreateParameters{ 154 Name: &name, 155 Location: &location, 156 CreateProperties: &redis.CreateProperties{ 157 EnableNonSslPort: &enableNonSSLPort, 158 Sku: &redis.Sku{ 159 Capacity: &capacity, 160 Family: family, 161 Name: sku, 162 }, 163 RedisConfiguration: expandRedisConfiguration(d), 164 }, 165 Tags: expandedTags, 166 } 167 168 if v, ok := d.GetOk("shard_count"); ok { 169 shardCount := int32(v.(int)) 170 parameters.ShardCount = &shardCount 171 } 172 173 _, err := client.Create(resGroup, name, parameters, make(chan struct{})) 174 if err != nil { 175 return err 176 } 177 178 read, err := client.Get(resGroup, name) 179 if err != nil { 180 return err 181 } 182 if read.ID == nil { 183 return fmt.Errorf("Cannot read Redis Instance %s (resource group %s) ID", name, resGroup) 184 } 185 186 log.Printf("[DEBUG] Waiting for Redis Instance (%s) to become available", d.Get("name")) 187 stateConf := &resource.StateChangeConf{ 188 Pending: []string{"Updating", "Creating"}, 189 Target: []string{"Succeeded"}, 190 Refresh: redisStateRefreshFunc(client, resGroup, name), 191 Timeout: 60 * time.Minute, 192 MinTimeout: 15 * time.Second, 193 } 194 if _, err := stateConf.WaitForState(); err != nil { 195 return fmt.Errorf("Error waiting for Redis Instance (%s) to become available: %s", d.Get("name"), err) 196 } 197 198 d.SetId(*read.ID) 199 200 return resourceArmRedisCacheRead(d, meta) 201 } 202 203 func resourceArmRedisCacheUpdate(d *schema.ResourceData, meta interface{}) error { 204 client := meta.(*ArmClient).redisClient 205 log.Printf("[INFO] preparing arguments for Azure ARM Redis Cache update.") 206 207 name := d.Get("name").(string) 208 resGroup := d.Get("resource_group_name").(string) 209 210 enableNonSSLPort := d.Get("enable_non_ssl_port").(bool) 211 212 capacity := int32(d.Get("capacity").(int)) 213 family := redis.SkuFamily(d.Get("family").(string)) 214 sku := redis.SkuName(d.Get("sku_name").(string)) 215 216 tags := d.Get("tags").(map[string]interface{}) 217 expandedTags := expandTags(tags) 218 219 parameters := redis.UpdateParameters{ 220 UpdateProperties: &redis.UpdateProperties{ 221 EnableNonSslPort: &enableNonSSLPort, 222 Sku: &redis.Sku{ 223 Capacity: &capacity, 224 Family: family, 225 Name: sku, 226 }, 227 Tags: expandedTags, 228 }, 229 } 230 231 if v, ok := d.GetOk("shard_count"); ok { 232 if d.HasChange("shard_count") { 233 shardCount := int32(v.(int)) 234 parameters.ShardCount = &shardCount 235 } 236 } 237 238 if d.HasChange("redis_configuration") { 239 redisConfiguration := expandRedisConfiguration(d) 240 parameters.RedisConfiguration = redisConfiguration 241 } 242 243 _, err := client.Update(resGroup, name, parameters) 244 if err != nil { 245 return err 246 } 247 248 read, err := client.Get(resGroup, name) 249 if err != nil { 250 return err 251 } 252 if read.ID == nil { 253 return fmt.Errorf("Cannot read Redis Instance %s (resource group %s) ID", name, resGroup) 254 } 255 256 log.Printf("[DEBUG] Waiting for Redis Instance (%s) to become available", d.Get("name")) 257 stateConf := &resource.StateChangeConf{ 258 Pending: []string{"Updating", "Creating"}, 259 Target: []string{"Succeeded"}, 260 Refresh: redisStateRefreshFunc(client, resGroup, name), 261 Timeout: 60 * time.Minute, 262 MinTimeout: 15 * time.Second, 263 } 264 if _, err := stateConf.WaitForState(); err != nil { 265 return fmt.Errorf("Error waiting for Redis Instance (%s) to become available: %s", d.Get("name"), err) 266 } 267 268 d.SetId(*read.ID) 269 270 return resourceArmRedisCacheRead(d, meta) 271 } 272 273 func resourceArmRedisCacheRead(d *schema.ResourceData, meta interface{}) error { 274 client := meta.(*ArmClient).redisClient 275 276 id, err := parseAzureResourceID(d.Id()) 277 if err != nil { 278 return err 279 } 280 resGroup := id.ResourceGroup 281 name := id.Path["Redis"] 282 283 resp, err := client.Get(resGroup, name) 284 285 // covers if the resource has been deleted outside of TF, but is still in the state 286 if resp.StatusCode == http.StatusNotFound { 287 d.SetId("") 288 return nil 289 } 290 291 if err != nil { 292 return fmt.Errorf("Error making Read request on Azure Redis Cache %s: %s", name, err) 293 } 294 295 keysResp, err := client.ListKeys(resGroup, name) 296 if err != nil { 297 return fmt.Errorf("Error making ListKeys request on Azure Redis Cache %s: %s", name, err) 298 } 299 300 d.Set("name", name) 301 d.Set("resource_group_name", resGroup) 302 d.Set("location", azureRMNormalizeLocation(*resp.Location)) 303 d.Set("ssl_port", resp.SslPort) 304 d.Set("hostname", resp.HostName) 305 d.Set("port", resp.Port) 306 d.Set("enable_non_ssl_port", resp.EnableNonSslPort) 307 d.Set("capacity", resp.Sku.Capacity) 308 d.Set("family", resp.Sku.Family) 309 d.Set("sku_name", resp.Sku.Name) 310 311 if resp.ShardCount != nil { 312 d.Set("shard_count", resp.ShardCount) 313 } 314 315 redisConfiguration := flattenRedisConfiguration(resp.RedisConfiguration) 316 d.Set("redis_configuration", &redisConfiguration) 317 318 d.Set("primary_access_key", keysResp.PrimaryKey) 319 d.Set("secondary_access_key", keysResp.SecondaryKey) 320 321 flattenAndSetTags(d, resp.Tags) 322 323 return nil 324 } 325 326 func resourceArmRedisCacheDelete(d *schema.ResourceData, meta interface{}) error { 327 redisClient := meta.(*ArmClient).redisClient 328 329 id, err := parseAzureResourceID(d.Id()) 330 if err != nil { 331 return err 332 } 333 resGroup := id.ResourceGroup 334 name := id.Path["Redis"] 335 336 resp, err := redisClient.Delete(resGroup, name, make(chan struct{})) 337 338 if resp.StatusCode != http.StatusOK { 339 return fmt.Errorf("Error issuing Azure ARM delete request of Redis Cache Instance '%s': %s", name, err) 340 } 341 342 checkResp, _ := redisClient.Get(resGroup, name) 343 if checkResp.StatusCode != http.StatusNotFound { 344 return fmt.Errorf("Error issuing Azure ARM delete request of Redis Cache Instance '%s': it still exists after deletion", name) 345 } 346 347 return nil 348 } 349 350 func redisStateRefreshFunc(client redis.GroupClient, resourceGroupName string, sgName string) resource.StateRefreshFunc { 351 return func() (interface{}, string, error) { 352 res, err := client.Get(resourceGroupName, sgName) 353 if err != nil { 354 return nil, "", fmt.Errorf("Error issuing read request in redisStateRefreshFunc to Azure ARM for Redis Cache Instance '%s' (RG: '%s'): %s", sgName, resourceGroupName, err) 355 } 356 357 return res, *res.ProvisioningState, nil 358 } 359 } 360 361 func expandRedisConfiguration(d *schema.ResourceData) *map[string]*string { 362 configuration := d.Get("redis_configuration").([]interface{}) 363 364 output := make(map[string]*string) 365 366 if configuration == nil { 367 return &output 368 } 369 370 // TODO: can we use this to remove the below? \/ 371 //config := configuration[0].(map[string]interface{}) 372 373 for _, v := range configuration { 374 config := v.(map[string]interface{}) 375 376 maxClients := config["maxclients"].(string) 377 if maxClients != "" { 378 output["maxclients"] = azure.String(maxClients) 379 } 380 381 maxMemoryDelta := config["maxmemory_delta"].(string) 382 if maxMemoryDelta != "" { 383 output["maxmemory-delta"] = azure.String(maxMemoryDelta) 384 } 385 386 maxMemoryReserved := config["maxmemory_reserved"].(string) 387 if maxMemoryReserved != "" { 388 output["maxmemory-reserved"] = azure.String(maxMemoryReserved) 389 } 390 391 maxMemoryPolicy := config["maxmemory_policy"].(string) 392 if maxMemoryPolicy != "" { 393 output["maxmemory-policy"] = azure.String(maxMemoryPolicy) 394 } 395 } 396 397 return &output 398 } 399 400 func flattenRedisConfiguration(configuration *map[string]*string) map[string]*string { 401 redisConfiguration := make(map[string]*string, len(*configuration)) 402 config := *configuration 403 404 redisConfiguration["maxclients"] = config["maxclients"] 405 redisConfiguration["maxmemory_delta"] = config["maxmemory-delta"] 406 redisConfiguration["maxmemory_reserved"] = config["maxmemory-reserved"] 407 redisConfiguration["maxmemory_policy"] = config["maxmemory-policy"] 408 409 return redisConfiguration 410 } 411 412 func validateRedisFamily(v interface{}, k string) (ws []string, errors []error) { 413 value := strings.ToLower(v.(string)) 414 families := map[string]bool{ 415 "c": true, 416 "p": true, 417 } 418 419 if !families[value] { 420 errors = append(errors, fmt.Errorf("Redis Family can only be C or P")) 421 } 422 return 423 } 424 425 func validateRedisMaxMemoryPolicy(v interface{}, k string) (ws []string, errors []error) { 426 value := strings.ToLower(v.(string)) 427 families := map[string]bool{ 428 "noeviction": true, 429 "allkeys-lru": true, 430 "volatile-lru": true, 431 "allkeys-random": true, 432 "volatile-random": true, 433 "volatile-ttl": true, 434 } 435 436 if !families[value] { 437 errors = append(errors, fmt.Errorf("Redis Max Memory Policy can only be 'noeviction' / 'allkeys-lru' / 'volatile-lru' / 'allkeys-random' / 'volatile-random' / 'volatile-ttl'")) 438 } 439 440 return 441 } 442 443 func validateRedisSku(v interface{}, k string) (ws []string, errors []error) { 444 value := strings.ToLower(v.(string)) 445 skus := map[string]bool{ 446 "basic": true, 447 "standard": true, 448 "premium": true, 449 } 450 451 if !skus[value] { 452 errors = append(errors, fmt.Errorf("Redis SKU can only be Basic, Standard or Premium")) 453 } 454 return 455 }