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  }