From 88766e555d43e31a3be8bb9ae424d0be4648b3db Mon Sep 17 00:00:00 2001 From: The Magician Date: Tue, 17 Nov 2020 10:44:20 -0800 Subject: [PATCH] added maintenance exclusions to GKE (#4197) (#7830) * added maintenance exclusions to GKE * exclusion name added to the schema * PR comments implemented * spacing corrected in doc Signed-off-by: Modular Magician --- .changelog/4197.txt | 4 + google/resource_container_cluster.go | 48 ++++++++++ google/resource_container_cluster_test.go | 88 +++++++++++++++++++ .../docs/r/container_cluster.html.markdown | 40 ++++++++- 4 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 .changelog/4197.txt diff --git a/.changelog/4197.txt b/.changelog/4197.txt new file mode 100644 index 00000000000..0509885f523 --- /dev/null +++ b/.changelog/4197.txt @@ -0,0 +1,4 @@ +```release-note:enhancement +container : added maintenance_exclusions_window to `resource_google_container_cluster` + +``` diff --git a/google/resource_container_cluster.go b/google/resource_container_cluster.go index f2d01cdb49c..3166989e1b8 100644 --- a/google/resource_container_cluster.go +++ b/google/resource_container_cluster.go @@ -463,6 +463,30 @@ func resourceContainerCluster() *schema.Resource { }, }, }, + "maintenance_exclusion": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 3, + Description: `Exceptions to maintenance window. Non-emergency maintenance should not occur in these windows.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "exclusion_name": { + Type: schema.TypeString, + Required: true, + }, + "start_time": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateRFC3339Date, + }, + "end_time": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateRFC3339Date, + }, + }, + }, + }, }, }, }, @@ -2353,6 +2377,16 @@ func expandMaintenancePolicy(d *schema.ResourceData, meta interface{}) *containe } maintenancePolicy := l[0].(map[string]interface{}) + if maintenanceExclusions, ok := maintenancePolicy["maintenance_exclusion"]; ok && len(maintenanceExclusions.(*schema.Set).List()) > 0 { + for _, me := range maintenanceExclusions.(*schema.Set).List() { + exclusion := me.(map[string]interface{}) + exclusions[exclusion["exclusion_name"].(string)] = containerBeta.TimeWindow{ + StartTime: exclusion["start_time"].(string), + EndTime: exclusion["end_time"].(string), + } + } + } + if dailyMaintenanceWindow, ok := maintenancePolicy["daily_maintenance_window"]; ok && len(dailyMaintenanceWindow.([]interface{})) > 0 { dmw := dailyMaintenanceWindow.([]interface{})[0].(map[string]interface{}) startTime := dmw["start_time"].(string) @@ -2782,6 +2816,18 @@ func flattenMaintenancePolicy(mp *containerBeta.MaintenancePolicy) []map[string] if mp == nil || mp.Window == nil { return nil } + + exclusions := []map[string]interface{}{} + if mp.Window.MaintenanceExclusions != nil { + for wName, window := range mp.Window.MaintenanceExclusions { + exclusions = append(exclusions, map[string]interface{}{ + "start_time": window.StartTime, + "end_time": window.EndTime, + "exclusion_name": wName, + }) + } + } + if mp.Window.DailyMaintenanceWindow != nil { return []map[string]interface{}{ { @@ -2791,6 +2837,7 @@ func flattenMaintenancePolicy(mp *containerBeta.MaintenancePolicy) []map[string] "duration": mp.Window.DailyMaintenanceWindow.Duration, }, }, + "maintenance_exclusion": exclusions, }, } } @@ -2804,6 +2851,7 @@ func flattenMaintenancePolicy(mp *containerBeta.MaintenancePolicy) []map[string] "recurrence": mp.Window.RecurringWindow.Recurrence, }, }, + "maintenance_exclusion": exclusions, }, } } diff --git a/google/resource_container_cluster_test.go b/google/resource_container_cluster_test.go index 2334af4a77b..8a966b6f59d 100644 --- a/google/resource_container_cluster_test.go +++ b/google/resource_container_cluster_test.go @@ -1147,6 +1147,38 @@ func TestAccContainerCluster_withRecurringMaintenanceWindow(t *testing.T) { }) } +func TestAccContainerCluster_withMaintenanceExclusionWindow(t *testing.T) { + t.Parallel() + cluster := fmt.Sprintf("tf-test-cluster-%s", randString(t, 10)) + resourceName := "google_container_cluster.with_maintenance_exclusion_window" + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withExclusion_RecurringMaintenanceWindow(cluster, "2019-01-01T00:00:00Z", "2019-01-02T00:00:00Z", "2019-05-01T00:00:00Z", "2019-05-02T00:00:00Z"), + }, + { + ResourceName: resourceName, + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerCluster_withExclusion_DailyMaintenanceWindow(cluster, "2020-01-01T00:00:00Z", "2020-01-02T00:00:00Z", "2020-05-01T00:00:00Z", "2020-05-02T00:00:00Z"), + }, + { + ResourceName: resourceName, + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccContainerCluster_withIPAllocationPolicy_existingSecondaryRanges(t *testing.T) { t.Parallel() @@ -2791,6 +2823,62 @@ resource "google_container_cluster" "with_recurring_maintenance_window" { } +func testAccContainerCluster_withExclusion_RecurringMaintenanceWindow(clusterName string, w1startTime, w1endTime, w2startTime, w2endTime string) string { + + return fmt.Sprintf(` +resource "google_container_cluster" "with_maintenance_exclusion_window" { + name = "%s" + location = "us-central1-a" + initial_node_count = 1 + + maintenance_policy { + recurring_window { + start_time = "%s" + end_time = "%s" + recurrence = "FREQ=DAILY" + } + maintenance_exclusion { + exclusion_name = "batch job" + start_time = "%s" + end_time = "%s" + } + maintenance_exclusion { + exclusion_name = "holiday data load" + start_time = "%s" + end_time = "%s" + } + } +} +`, clusterName, w1startTime, w1endTime, w1startTime, w1endTime, w2startTime, w2endTime) +} + +func testAccContainerCluster_withExclusion_DailyMaintenanceWindow(clusterName string, w1startTime, w1endTime, w2startTime, w2endTime string) string { + + return fmt.Sprintf(` +resource "google_container_cluster" "with_maintenance_exclusion_window" { + name = "%s" + location = "us-central1-a" + initial_node_count = 1 + + maintenance_policy { + daily_maintenance_window { + start_time = "03:00" + } + maintenance_exclusion { + exclusion_name = "batch job" + start_time = "%s" + end_time = "%s" + } + maintenance_exclusion { + exclusion_name = "holiday data load" + start_time = "%s" + end_time = "%s" + } + } +} +`, clusterName, w1startTime, w1endTime, w2startTime, w2endTime) +} + func testAccContainerCluster_withIPAllocationPolicy_existingSecondaryRanges(containerNetName string, clusterName string) string { return fmt.Sprintf(` resource "google_compute_network" "container_network" { diff --git a/website/docs/r/container_cluster.html.markdown b/website/docs/r/container_cluster.html.markdown index 63a08897e00..c582182395d 100644 --- a/website/docs/r/container_cluster.html.markdown +++ b/website/docs/r/container_cluster.html.markdown @@ -449,11 +449,17 @@ The `authenticator_groups_config` block supports: * `security_group` - (Required) The name of the RBAC security group for use with Google security groups in Kubernetes RBAC. Group name must be in format `gke-security-groups@yourdomain.com`. The `maintenance_policy` block supports: +* `daily_maintenance_window` - (Optional) structure documented below. +* `recurring_window` - (Optional) structure documented below +* `maintenance_exclusion` - (Optional) structure documented below -* `daily_maintenance_window` - (Required in GA, Optional in Beta) Time window specified for daily maintenance operations. +In beta, one or the other of `recurring_window` and `daily_maintenance_window` is required if a `maintenance_policy` block is supplied. + +* `daily_maintenance_window` - Time window specified for daily maintenance operations. Specify `start_time` in [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) format "HH:MM”, where HH : \[00-23\] and MM : \[00-59\] GMT. For example: +Examples: ```hcl maintenance_policy { daily_maintenance_window { @@ -462,8 +468,7 @@ maintenance_policy { } ``` -* `recurring_window` - (Optional) Time window for -recurring maintenance operations. +* `recurring_window` - Time window for recurring maintenance operations. Specify `start_time` and `end_time` in [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) "Zulu" date format. The start time's date is the initial date that the window starts, and the end time is used for calculating duration. Specify `recurrence` in @@ -491,7 +496,34 @@ maintenance_policy { } ``` -In beta, one or the other of `recurring_window` and `daily_maintenance_window` is required if a `maintenance_policy` block is supplied. +* `maintenance_exclusion` - Exceptions to maintenance window. Non-emergency maintenance should not occur in these windows. A cluster can have up to three maintenance exclusions at a time [Maintenance Window and Exclusions](https://cloud.google.com/kubernetes-engine/docs/concepts/maintenance-windows-and-exclusions) + +Specify `start_time` and `end_time` in [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) "Zulu" date format. The start time's date is +the initial date that the window starts, and the end time is used for calculating duration.Specify `recurrence` in +[RFC5545](https://tools.ietf.org/html/rfc5545#section-3.8.5.3) RRULE format, to specify when this recurs. +Note that GKE may accept other formats, but will return values in UTC, causing a permanent diff. + +Examples: + +``` +maintenance_policy { + recurring_window { + start_time = "2019-01-01T00:00:00Z" + end_time = "2019-01-02T00:00:00Z" + recurrence = "FREQ=DAILY" + } + maintenance_exclusion{ + exclusion_name = "batch job" + start_time = "2019-01-01T00:00:00Z" + end_time = "2019-01-02T00:00:00Z" + } + maintenance_exclusion{ + exclusion_name = "holiday data load" + start_time = "2019-05-01T00:00:00Z" + end_time = "2019-05-02T00:00:00Z" + } +} +``` The `ip_allocation_policy` block supports: