diff --git a/.changelog/3834.txt b/.changelog/3834.txt new file mode 100644 index 00000000000..70cc6a65326 --- /dev/null +++ b/.changelog/3834.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +container: added `release_channel` to `google_container-cluster` (GA) +``` diff --git a/google/resource_container_cluster.go b/google/resource_container_cluster.go index 121b3e3e92e..4c664ebefb1 100644 --- a/google/resource_container_cluster.go +++ b/google/resource_container_cluster.go @@ -1056,6 +1056,7 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er Enabled: d.Get("enable_shielded_nodes").(bool), ForceSendFields: []string{"Enabled"}, }, + ReleaseChannel: expandReleaseChannel(d.Get("release_channel")), MasterAuth: expandMasterAuth(d.Get("master_auth")), ResourceLabels: expandStringMap(d, "resource_labels"), @@ -1311,6 +1312,9 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro if cluster.ShieldedNodes != nil { d.Set("enable_shielded_nodes", cluster.ShieldedNodes.Enabled) } + if err := d.Set("release_channel", flattenReleaseChannel(cluster.ReleaseChannel)); err != nil { + return err + } if err := d.Set("authenticator_groups_config", flattenAuthenticatorGroupsConfig(cluster.AuthenticatorGroupsConfig)); err != nil { return err @@ -1507,6 +1511,36 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er d.SetPartial("enable_shielded_nodes") } + if d.HasChange("release_channel") { + req := &containerBeta.UpdateClusterRequest{ + Update: &containerBeta.ClusterUpdate{ + DesiredReleaseChannel: expandReleaseChannel(d.Get("release_channel")), + }, + } + updateF := func() error { + log.Println("[DEBUG] updating release_channel") + name := containerClusterFullName(project, location, clusterName) + op, err := config.clientContainerBeta.Projects.Locations.Clusters.Update(name, req).Do() + if err != nil { + return err + } + + // Wait until it's updated + err = containerOperationWait(config, op, project, location, "updating Release Channel", d.Timeout(schema.TimeoutUpdate)) + log.Println("[DEBUG] done updating release_channel") + return err + } + + // Call update serially. + if err := lockedCall(lockKey, updateF); err != nil { + return err + } + + log.Printf("[INFO] GKE cluster %s Release Channel has been updated to %#v", d.Id(), req.Update.DesiredReleaseChannel) + + d.SetPartial("release_channel") + } + if d.HasChange("maintenance_policy") { req := &containerBeta.SetMaintenancePolicyRequest{ MaintenancePolicy: expandMaintenancePolicy(d, meta), @@ -2429,6 +2463,17 @@ func expandDatabaseEncryption(configured interface{}) *containerBeta.DatabaseEnc } } +func expandReleaseChannel(configured interface{}) *containerBeta.ReleaseChannel { + l := configured.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil + } + config := l[0].(map[string]interface{}) + return &containerBeta.ReleaseChannel{ + Channel: config["channel"].(string), + } +} + func expandWorkloadIdentityConfig(configured interface{}) *containerBeta.WorkloadIdentityConfig { l := configured.([]interface{}) if len(l) == 0 || l[0] == nil { @@ -2592,6 +2637,21 @@ func flattenVerticalPodAutoscaling(c *containerBeta.VerticalPodAutoscaling) []ma } } +func flattenReleaseChannel(c *containerBeta.ReleaseChannel) []map[string]interface{} { + result := []map[string]interface{}{} + if c != nil { + result = append(result, map[string]interface{}{ + "channel": c.Channel, + }) + } else { + // Explicitly set the release channel to the default. + result = append(result, map[string]interface{}{ + "channel": "UNSPECIFIED", + }) + } + return result +} + func flattenWorkloadIdentityConfig(c *containerBeta.WorkloadIdentityConfig) []map[string]interface{} { if c == nil { return nil diff --git a/google/resource_container_cluster_test.go b/google/resource_container_cluster_test.go index 666a4815fb4..f54f4f1c7a5 100644 --- a/google/resource_container_cluster_test.go +++ b/google/resource_container_cluster_test.go @@ -338,6 +338,96 @@ func TestAccContainerCluster_withNetworkPolicyEnabled(t *testing.T) { }) } +func TestAccContainerCluster_withReleaseChannelEnabled(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("tf-test-cluster-%s", randString(t, 10)) + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "STABLE"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version"}, + }, + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "UNSPECIFIED"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version"}, + }, + }, + }) +} + +func TestAccContainerCluster_withReleaseChannelEnabledDefaultVersion(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("tf-test-cluster-%s", randString(t, 10)) + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withReleaseChannelEnabledDefaultVersion(clusterName, "REGULAR"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version"}, + }, + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "REGULAR"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version"}, + }, + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "UNSPECIFIED"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version"}, + }, + }, + }) +} + +func TestAccContainerCluster_withInvalidReleaseChannel(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("tf-test-cluster-%s", randString(t, 10)) + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "CANARY"), + ExpectError: regexp.MustCompile(`config is invalid: expected release_channel\.0\.channel to be one of \[UNSPECIFIED RAPID REGULAR STABLE\], got CANARY`), + }, + }, + }) +} + func TestAccContainerCluster_withMasterAuthorizedNetworksConfig(t *testing.T) { t.Parallel() @@ -1767,6 +1857,36 @@ resource "google_container_cluster" "with_network_policy_enabled" { `, clusterName) } +func testAccContainerCluster_withReleaseChannelEnabled(clusterName string, channel string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_release_channel" { + name = "%s" + location = "us-central1-a" + initial_node_count = 1 + + release_channel { + channel = "%s" + } +} +`, clusterName, channel) +} + +func testAccContainerCluster_withReleaseChannelEnabledDefaultVersion(clusterName string, channel string) string { + return fmt.Sprintf(` + +data "google_container_engine_versions" "central1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "with_release_channel" { + name = "%s" + location = "us-central1-a" + initial_node_count = 1 + min_master_version = data.google_container_engine_versions.central1a.release_channel_default_version["%s"] +} +`, clusterName, channel) +} + func testAccContainerCluster_removeNetworkPolicy(clusterName string) string { return fmt.Sprintf(` resource "google_container_cluster" "with_network_policy_enabled" {