Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_redis_cache: support for Patch Schedules #540

Merged
merged 4 commits into from
Nov 9, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,9 @@ type ArmClient struct {

deploymentsClient resources.DeploymentsClient

redisClient redis.GroupClient
redisFirewallClient redis.FirewallRuleClient
redisClient redis.GroupClient
redisFirewallClient redis.FirewallRuleClient
redisPatchSchedulesClient redis.PatchSchedulesClient

trafficManagerProfilesClient trafficmanager.ProfilesClient
trafficManagerEndpointsClient trafficmanager.EndpointsClient
Expand Down Expand Up @@ -787,17 +788,23 @@ func (c *ArmClient) registerKeyVaultClients(endpoint, subscriptionId string, aut
}

func (c *ArmClient) registerRedisClients(endpoint, subscriptionId string, auth autorest.Authorizer, sender autorest.Sender) {
rdc := redis.NewGroupClientWithBaseURI(endpoint, subscriptionId)
setUserAgent(&rdc.Client)
rdc.Authorizer = auth
rdc.Sender = sender
c.redisClient = rdc

rdfc := redis.NewFirewallRuleClientWithBaseURI(endpoint, subscriptionId)
setUserAgent(&rdfc.Client)
rdfc.Authorizer = auth
rdfc.Sender = sender
c.redisFirewallClient = rdfc
groupsClient := redis.NewGroupClientWithBaseURI(endpoint, subscriptionId)
setUserAgent(&groupsClient.Client)
groupsClient.Authorizer = auth
groupsClient.Sender = sender
c.redisClient = groupsClient

firewallRuleClient := redis.NewFirewallRuleClientWithBaseURI(endpoint, subscriptionId)
setUserAgent(&firewallRuleClient.Client)
firewallRuleClient.Authorizer = auth
firewallRuleClient.Sender = sender
c.redisFirewallClient = firewallRuleClient

patchSchedulesClient := redis.NewPatchSchedulesClientWithBaseURI(endpoint, subscriptionId)
setUserAgent(&patchSchedulesClient.Client)
patchSchedulesClient.Authorizer = auth
patchSchedulesClient.Sender = sender
c.redisPatchSchedulesClient = patchSchedulesClient
}

func (armClient *ArmClient) getKeyForStorageAccount(resourceGroupName, storageAccountName string) (string, bool, error) {
Expand Down
125 changes: 121 additions & 4 deletions azurerm/resource_arm_redis_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,34 @@ func resourceArmRedisCache() *schema.Resource {
},
},

"patch_schedule": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"day_of_week": {
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
ValidateFunc: validation.StringInSlice([]string{
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
}, true),
},
"start_hour_utc": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 23),
},
},
},
},

"hostname": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -170,6 +198,11 @@ func resourceArmRedisCacheCreate(d *schema.ResourceData, meta interface{}) error
tags := d.Get("tags").(map[string]interface{})
expandedTags := expandTags(tags)

patchSchedule, err := expandRedisPatchSchedule(d)
if err != nil {
return fmt.Errorf("Error parsing Patch Schedule: %+v", err)
}

parameters := redis.CreateParameters{
Name: &name,
Location: &location,
Expand All @@ -191,7 +224,7 @@ func resourceArmRedisCacheCreate(d *schema.ResourceData, meta interface{}) error
}

_, error := client.Create(resGroup, name, parameters, make(chan struct{}))
err := <-error
err = <-error
if err != nil {
return err
}
Expand All @@ -218,6 +251,15 @@ func resourceArmRedisCacheCreate(d *schema.ResourceData, meta interface{}) error

d.SetId(*read.ID)

// TODO: this _may_ need an `if premium` check
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this TODO for later or do we want to get it looked at now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good spot, I'd meant to remove that - I'll do that now..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

if schedule := patchSchedule; schedule != nil {
patchClient := meta.(*ArmClient).redisPatchSchedulesClient
_, err = patchClient.CreateOrUpdate(resGroup, name, *schedule)
if err != nil {
return fmt.Errorf("Error setting Redis Patch Schedule: %+v", err)
}
}

return resourceArmRedisCacheRead(d, meta)
}

Expand Down Expand Up @@ -288,6 +330,25 @@ func resourceArmRedisCacheUpdate(d *schema.ResourceData, meta interface{}) error

d.SetId(*read.ID)

// TODO: this _may_ need an `if premium` check
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 here

patchSchedule, err := expandRedisPatchSchedule(d)
if err != nil {
return fmt.Errorf("Error parsing Patch Schedule: %+v", err)
}

patchClient := meta.(*ArmClient).redisPatchSchedulesClient
if patchSchedule == nil || len(*patchSchedule.ScheduleEntries.ScheduleEntries) == 0 {
_, err = patchClient.Delete(resGroup, name)
if err != nil {
return fmt.Errorf("Error deleting Redis Patch Schedule: %+v", err)
}
} else {
_, err = patchClient.CreateOrUpdate(resGroup, name, *patchSchedule)
if err != nil {
return fmt.Errorf("Error setting Redis Patch Schedule: %+v", err)
}
}

return resourceArmRedisCacheRead(d, meta)
}

Expand Down Expand Up @@ -318,16 +379,29 @@ func resourceArmRedisCacheRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error making ListKeys request on Azure Redis Cache %s: %s", name, err)
}

patchSchedulesClient := meta.(*ArmClient).redisPatchSchedulesClient

schedule, err := patchSchedulesClient.Get(resGroup, name)
if err == nil {
patchSchedule := flattenRedisPatchSchedules(schedule)
if err := d.Set("patch_schedule", patchSchedule); err != nil {
return fmt.Errorf("Error setting `patch_schedule`: %+v", err)
}
}

d.Set("name", name)
d.Set("resource_group_name", resGroup)
d.Set("location", azureRMNormalizeLocation(*resp.Location))
d.Set("ssl_port", resp.SslPort)
d.Set("hostname", resp.HostName)
d.Set("port", resp.Port)
d.Set("enable_non_ssl_port", resp.EnableNonSslPort)
d.Set("capacity", resp.Sku.Capacity)
d.Set("family", resp.Sku.Family)
d.Set("sku_name", resp.Sku.Name)

if sku := resp.Sku; sku != nil {
d.Set("capacity", sku.Capacity)
d.Set("family", sku.Family)
d.Set("sku_name", sku.Name)
}

if resp.ShardCount != nil {
d.Set("shard_count", resp.ShardCount)
Expand Down Expand Up @@ -426,6 +500,34 @@ func expandRedisConfiguration(d *schema.ResourceData) *map[string]*string {
return &output
}

func expandRedisPatchSchedule(d *schema.ResourceData) (*redis.PatchSchedule, error) {
v, ok := d.GetOk("patch_schedule")
if !ok {
return nil, nil
}

scheduleValues := v.([]interface{})
entries := make([]redis.ScheduleEntry, 0)
for _, scheduleValue := range scheduleValues {
vals := scheduleValue.(map[string]interface{})
dayOfWeek := vals["day_of_week"].(string)
startHourUtc := vals["start_hour_utc"].(int)

entry := redis.ScheduleEntry{
DayOfWeek: redis.DayOfWeek(dayOfWeek),
StartHourUtc: utils.Int32(int32(startHourUtc)),
}
entries = append(entries, entry)
}

schedule := redis.PatchSchedule{
ScheduleEntries: &redis.ScheduleEntries{
ScheduleEntries: &entries,
},
}
return &schedule, nil
}

func flattenRedisConfiguration(configuration *map[string]*string) map[string]*string {
redisConfiguration := make(map[string]*string, len(*configuration))
config := *configuration
Expand All @@ -443,6 +545,21 @@ func flattenRedisConfiguration(configuration *map[string]*string) map[string]*st
return redisConfiguration
}

func flattenRedisPatchSchedules(schedule redis.PatchSchedule) []interface{} {
outputs := make([]interface{}, 0)

for _, entry := range *schedule.ScheduleEntries.ScheduleEntries {
output := make(map[string]interface{}, 0)

output["day_of_week"] = string(entry.DayOfWeek)
output["start_hour_utc"] = int(*entry.StartHourUtc)

outputs = append(outputs, output)
}

return outputs
}

func validateRedisFamily(v interface{}, k string) (ws []string, errors []error) {
value := strings.ToLower(v.(string))
families := map[string]bool{
Expand Down
77 changes: 77 additions & 0 deletions azurerm/resource_arm_redis_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,53 @@ func TestAccAzureRMRedisCache_BackupEnabledDisabled(t *testing.T) {
})
}

func TestAccAzureRMRedisCache_PatchSchedule(t *testing.T) {
ri := acctest.RandInt()
location := testLocation()
config := testAccAzureRMRedisCachePatchSchedule(ri, location)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMRedisCacheDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMRedisCacheExists("azurerm_redis_cache.test"),
),
},
},
})
}

func TestAccAzureRMRedisCache_PatchScheduleUpdated(t *testing.T) {
ri := acctest.RandInt()
location := testLocation()
config := testAccAzureRMRedisCachePatchSchedule(ri, location)
updatedConfig := testAccAzureRMRedisCache_premium(ri, location)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMRedisCacheDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMRedisCacheExists("azurerm_redis_cache.test"),
),
},
{
Config: updatedConfig,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMRedisCacheExists("azurerm_redis_cache.test"),
),
},
},
})
}

func testCheckAzureRMRedisCacheExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
// Ensure we have enough information in state to look up in API
Expand Down Expand Up @@ -507,3 +554,33 @@ resource "azurerm_redis_cache" "test" {
}
`, rInt, location, rString, rInt)
}

func testAccAzureRMRedisCachePatchSchedule(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}

resource "azurerm_redis_cache" "test" {
name = "acctestRedis-%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
capacity = 1
family = "P"
sku_name = "Premium"
enable_non_ssl_port = false
redis_configuration {
maxclients = 256,
maxmemory_reserved = 2,
maxmemory_delta = 2
maxmemory_policy = "allkeys-lru"
}

patch_schedule {
day_of_week = "Tuesday"
start_hour_utc = 8
}
}
`, rInt, location, rInt)
}
43 changes: 27 additions & 16 deletions website/docs/r/redis_cache.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -111,31 +111,33 @@ resource "azurerm_redis_cache" "test" {

```hcl
resource "azurerm_resource_group" "test" {
name = "redisrg"
location = "West US"
name = "redisrg"
location = "West US"
}

resource "azurerm_storage_account" "test" {
name = "redissa"
resource_group_name = "${azurerm_resource_group.test.name}"
location = "${azurerm_resource_group.test.location}"
account_tier = "Standard"
account_replication_type = "GRS"
}

resource "azurerm_redis_cache" "test" {
name = "example-redis"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
capacity = 3
family = "P"
sku_name = "Premium"
enable_non_ssl_port = false
redis_configuration {
maxclients = 256
rdb_backup_enabled = true
rdb_backup_frequency = 60
rdb_backup_max_snapshot_count = 1
rdb_storage_connection_string = "DefaultEndpointsProtocol=https;BlobEndpoint=${azurerm_storage_account.test.primary_blob_endpoint};AccountName=${azurerm_storage_account.test.name};AccountKey=${azurerm_storage_account.test.primary_access_key}"
}
name = "example-redis"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
capacity = 3
family = "P"
sku_name = "Premium"
enable_non_ssl_port = false
redis_configuration {
maxclients = 256
rdb_backup_enabled = true
rdb_backup_frequency = 60
rdb_backup_max_snapshot_count = 1
rdb_storage_connection_string = "DefaultEndpointsProtocol=https;BlobEndpoint=${azurerm_storage_account.test.primary_blob_endpoint};AccountName=${azurerm_storage_account.test.name};AccountKey=${azurerm_storage_account.test.primary_access_key}"
}
}
```

Expand Down Expand Up @@ -165,6 +167,8 @@ The pricing group for the Redis Family - either "C" or "P" at present.

* `redis_configuration` - (Required) A `redis_configuration` as defined below - with some limitations by SKU - defaults/details are shown below.

* `patch_schedule` - (Optional) A list of `patch_schedule` blocks as defined below - only available for Premium SKU's.

---

* `redis_configuration` supports the following:
Expand Down Expand Up @@ -198,6 +202,13 @@ redis_configuration {

_*Important*: The maxmemory_reserved setting is only available for Standard and Premium caches. More details are available in the Relevant Links section below._

* `patch_schedule` supports the following:

* `day_of_week` (Required) the Weekday name - possible values include `Monday`, `Tuesday`, `Wednesday` etc.
* `start_hour_utc` - (Optional) the Start Hour for maintenance in UTC - possible values range from `0 - 23`.

~> **Note:** The Patch Window lasts for 5 hours from the `start_hour_utc`.

## Attributes Reference

The following attributes are exported:
Expand Down