github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 if err != nil { 285 return fmt.Errorf("Error making Read request on Azure Redis Cache %s: %s", name, err) 286 } 287 if resp.StatusCode == http.StatusNotFound { 288 d.SetId("") 289 return nil 290 } 291 292 keysResp, err := client.ListKeys(resGroup, name) 293 if err != nil { 294 return fmt.Errorf("Error making ListKeys request on Azure Redis Cache %s: %s", name, err) 295 } 296 297 d.Set("name", name) 298 d.Set("resource_group_name", resGroup) 299 d.Set("location", azureRMNormalizeLocation(*resp.Location)) 300 d.Set("ssl_port", resp.SslPort) 301 d.Set("host_name", resp.HostName) 302 d.Set("port", resp.Port) 303 d.Set("enable_non_ssl_port", resp.EnableNonSslPort) 304 d.Set("capacity", resp.Sku.Capacity) 305 d.Set("family", resp.Sku.Family) 306 d.Set("sku_name", resp.Sku.Name) 307 308 if resp.ShardCount != nil { 309 d.Set("shard_count", resp.ShardCount) 310 } 311 312 redisConfiguration := flattenRedisConfiguration(resp.RedisConfiguration) 313 d.Set("redis_configuration", &redisConfiguration) 314 315 d.Set("primary_access_key", keysResp.PrimaryKey) 316 d.Set("secondary_access_key", keysResp.SecondaryKey) 317 318 flattenAndSetTags(d, resp.Tags) 319 320 return nil 321 } 322 323 func resourceArmRedisCacheDelete(d *schema.ResourceData, meta interface{}) error { 324 redisClient := meta.(*ArmClient).redisClient 325 326 id, err := parseAzureResourceID(d.Id()) 327 if err != nil { 328 return err 329 } 330 resGroup := id.ResourceGroup 331 name := id.Path["Redis"] 332 333 resp, err := redisClient.Delete(resGroup, name, make(chan struct{})) 334 335 if resp.StatusCode != http.StatusOK { 336 return fmt.Errorf("Error issuing Azure ARM delete request of Redis Cache Instance '%s': %s", name, err) 337 } 338 339 checkResp, _ := redisClient.Get(resGroup, name) 340 if checkResp.StatusCode != http.StatusNotFound { 341 return fmt.Errorf("Error issuing Azure ARM delete request of Redis Cache Instance '%s': it still exists after deletion", name) 342 } 343 344 return nil 345 } 346 347 func redisStateRefreshFunc(client redis.GroupClient, resourceGroupName string, sgName string) resource.StateRefreshFunc { 348 return func() (interface{}, string, error) { 349 res, err := client.Get(resourceGroupName, sgName) 350 if err != nil { 351 return nil, "", fmt.Errorf("Error issuing read request in redisStateRefreshFunc to Azure ARM for Redis Cache Instance '%s' (RG: '%s'): %s", sgName, resourceGroupName, err) 352 } 353 354 return res, *res.ProvisioningState, nil 355 } 356 } 357 358 func expandRedisConfiguration(d *schema.ResourceData) *map[string]*string { 359 configuration := d.Get("redis_configuration").([]interface{}) 360 361 output := make(map[string]*string) 362 363 if configuration == nil { 364 return &output 365 } 366 367 // TODO: can we use this to remove the below? \/ 368 //config := configuration[0].(map[string]interface{}) 369 370 for _, v := range configuration { 371 config := v.(map[string]interface{}) 372 373 maxClients := config["maxclients"].(string) 374 if maxClients != "" { 375 output["maxclients"] = azure.String(maxClients) 376 } 377 378 maxMemoryDelta := config["maxmemory_delta"].(string) 379 if maxMemoryDelta != "" { 380 output["maxmemory-delta"] = azure.String(maxMemoryDelta) 381 } 382 383 maxMemoryReserved := config["maxmemory_reserved"].(string) 384 if maxMemoryReserved != "" { 385 output["maxmemory-reserved"] = azure.String(maxMemoryReserved) 386 } 387 388 maxMemoryPolicy := config["maxmemory_policy"].(string) 389 if maxMemoryPolicy != "" { 390 output["maxmemory-policy"] = azure.String(maxMemoryPolicy) 391 } 392 } 393 394 return &output 395 } 396 397 func flattenRedisConfiguration(configuration *map[string]*string) map[string]*string { 398 redisConfiguration := make(map[string]*string, len(*configuration)) 399 config := *configuration 400 401 redisConfiguration["maxclients"] = config["maxclients"] 402 redisConfiguration["maxmemory_delta"] = config["maxmemory-delta"] 403 redisConfiguration["maxmemory_reserved"] = config["maxmemory-reserved"] 404 redisConfiguration["maxmemory_policy"] = config["maxmemory-policy"] 405 406 return redisConfiguration 407 } 408 409 func validateRedisFamily(v interface{}, k string) (ws []string, errors []error) { 410 value := strings.ToLower(v.(string)) 411 families := map[string]bool{ 412 "c": true, 413 "p": true, 414 } 415 416 if !families[value] { 417 errors = append(errors, fmt.Errorf("Redis Family can only be C or P")) 418 } 419 return 420 } 421 422 func validateRedisMaxMemoryPolicy(v interface{}, k string) (ws []string, errors []error) { 423 value := strings.ToLower(v.(string)) 424 families := map[string]bool{ 425 "noeviction": true, 426 "allkeys-lru": true, 427 "volatile-lru": true, 428 "allkeys-random": true, 429 "volatile-random": true, 430 "volatile-ttl": true, 431 } 432 433 if !families[value] { 434 errors = append(errors, fmt.Errorf("Redis Max Memory Policy can only be 'noeviction' / 'allkeys-lru' / 'volatile-lru' / 'allkeys-random' / 'volatile-random' / 'volatile-ttl'")) 435 } 436 437 return 438 } 439 440 func validateRedisSku(v interface{}, k string) (ws []string, errors []error) { 441 value := strings.ToLower(v.(string)) 442 skus := map[string]bool{ 443 "basic": true, 444 "standard": true, 445 "premium": true, 446 } 447 448 if !skus[value] { 449 errors = append(errors, fmt.Errorf("Redis SKU can only be Basic, Standard or Premium")) 450 } 451 return 452 }