diff --git a/build/terraform b/build/terraform index a0dfdc80c5df..87a05b2fc09c 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit a0dfdc80c5df22af808f004180695f9e4d578356 +Subproject commit 87a05b2fc09ca86b6074e641792bf80dc228eef7 diff --git a/build/terraform-beta b/build/terraform-beta index c39a624b52e6..df6ba8c84acd 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit c39a624b52e61bf4ff6920e1973fc6f558c275cb +Subproject commit df6ba8c84acd790c636bbff74a45fd85bc8934dd diff --git a/third_party/terraform/resources/resource_container_cluster.go.erb b/third_party/terraform/resources/resource_container_cluster.go.erb index cf09740c5d06..7a426b8e7250 100644 --- a/third_party/terraform/resources/resource_container_cluster.go.erb +++ b/third_party/terraform/resources/resource_container_cluster.go.erb @@ -725,6 +725,26 @@ func resourceContainerCluster() *schema.Resource { }, <% unless version == 'ga' -%> + "release_channel": { + Type: schema.TypeList, + ForceNew: true, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "channel": { + Type: schema.TypeString, + Default: "UNSPECIFIED", + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"UNSPECIFIED", "RAPID", "REGULAR", "STABLE"}, false), + DiffSuppressFunc: emptyOrDefaultStringSuppress("UNSPECIFIED"), + }, + }, + }, + }, + "vertical_pod_autoscaling": { Type: schema.TypeList, MaxItems: 1, @@ -957,6 +977,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")), EnableTpu: d.Get("enable_tpu").(bool), BinaryAuthorization: &containerBeta.BinaryAuthorization{ Enabled: d.Get("enable_binary_authorization").(bool), @@ -1220,6 +1241,9 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro if err := d.Set("authenticator_groups_config", flattenAuthenticatorGroupsConfig(cluster.AuthenticatorGroupsConfig)); err != nil { return err } + if err := d.Set("release_channel", flattenReleaseChannel(cluster.ReleaseChannel)); err != nil { + return err + } d.Set("enable_intranode_visibility", cluster.NetworkConfig.EnableIntraNodeVisibility) <% else -%> if err := d.Set("cluster_autoscaling", nil); err != nil { @@ -2358,6 +2382,17 @@ func expandPrivateClusterConfig(configured interface{}) *containerBeta.PrivateCl } <% unless version == 'ga' -%> +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 expandDatabaseEncryption(configured interface{}) *containerBeta.DatabaseEncryption { l := configured.([]interface{}) if len(l) == 0 { @@ -2564,6 +2599,21 @@ func flattenPrivateClusterConfig(c *containerBeta.PrivateClusterConfig) []map[st } <% unless version == 'ga' -%> +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 flattenVerticalPodAutoscaling(c *containerBeta.VerticalPodAutoscaling) []map[string]interface{} { if c == nil { return nil diff --git a/third_party/terraform/tests/resource_container_cluster_test.go.erb b/third_party/terraform/tests/resource_container_cluster_test.go.erb index 7e7538e8748c..4d7990c33210 100644 --- a/third_party/terraform/tests/resource_container_cluster_test.go.erb +++ b/third_party/terraform/tests/resource_container_cluster_test.go.erb @@ -361,6 +361,93 @@ func TestAccContainerCluster_withNetworkPolicyEnabled(t *testing.T) { }) } +<% unless version == 'ga' -%> +func TestAccContainerCluster_withReleaseChannelEnabled(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("cluster-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "STABLE"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "REGULAR"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "RAPID"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "UNSPECIFIED"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccContainerCluster_withReleaseChannelDefault(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("cluster-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withReleaseChannelDefault(clusterName), + }, + { + ResourceName: "google_container_cluster.with_default_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccContainerCluster_withInvalidReleaseChannel(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("cluster-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + 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`), + }, + }, + }) +} +<% end -%> + func TestAccContainerCluster_withMasterAuthorizedNetworksConfig(t *testing.T) { t.Parallel() @@ -1970,6 +2057,31 @@ resource "google_container_cluster" "with_network_policy_enabled" { }`, clusterName) } +<% unless version == 'ga' -%> +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_withReleaseChannelDefault(clusterName string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_default_release_channel" { + name = "%s" + location = "us-central1-a" + initial_node_count = 1 + release_channel {} +}`, clusterName) +} +<% end -%> + func testAccContainerCluster_removeNetworkPolicy(clusterName string) string { return fmt.Sprintf(` resource "google_container_cluster" "with_network_policy_enabled" { diff --git a/third_party/terraform/website/docs/r/container_cluster.html.markdown b/third_party/terraform/website/docs/r/container_cluster.html.markdown index b82307a2754e..9f7b7ed14810 100644 --- a/third_party/terraform/website/docs/r/container_cluster.html.markdown +++ b/third_party/terraform/website/docs/r/container_cluster.html.markdown @@ -156,7 +156,7 @@ in this cluster in CIDR notation (e.g. 10.96.0.0/14). Leave blank to have one automatically chosen or specify a /14 block in 10.0.0.0/8. This field will only work if your cluster is not VPC-native- when an `ip_allocation_policy` block is not defined, or `ip_allocation_policy.use_ip_aliases` is set to false. If your -cluster is VPC-native, use `ip_allocation_policy.cluster_ipv4_cidr_block`. +cluster is VPC-native, use `ip_allocation_policy.cluster_ipv4_cidr_block`. * `cluster_autoscaling` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) Per-cluster configuration of Node Auto-Provisioning with Cluster Autoscaler to @@ -289,6 +289,10 @@ to the datasource. A `region` can have a different set of supported versions tha * `project` - (Optional) The ID of the project in which the resource belongs. If it is not provided, the provider project is used. +* `release_channel` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) Configuration options for the + [Release channel](https://cloud.google.com/kubernetes-engine/docs/concepts/release-channels) + feature, which provide more control over automatic upgrades of your GKE clusters. Structure is documented below. + * `remove_default_node_pool` - (Optional) If `true`, deletes the default node pool upon cluster creation. If you're using `google_container_node_pool` resources with no default node pool, this should be set to `true`, alongside @@ -433,7 +437,7 @@ to have a range chosen with a specific netmask. Set to a CIDR notation (e.g. 10. from the RFC-1918 private networks (e.g. 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) to pick a specific range to use. This field will only work if your cluster is VPC-native- when `ip_allocation_policy.use_ip_aliases` is undefined or set to -true. If your cluster is not VPC-native, use `cluster_ipv4_cidr`. +true. If your cluster is not VPC-native, use `cluster_ipv4_cidr`. * `node_ipv4_cidr_block` - (Optional) The IP address range of the node IPs in this cluster. This should be set only if `create_subnetwork` is true. @@ -547,7 +551,7 @@ The `node_config` block supports: are preemptible. See the [official documentation](https://cloud.google.com/container-engine/docs/preemptible-vm) for more information. Defaults to false. -* `sandbox_config` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) [GKE Sandbox](https://cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods) configuration. When enabling this feature you must specify `image_type = "COS_CONTAINERD"` and `node_version = "1.12.7-gke.17"` or later to use it. +* `sandbox_config` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) [GKE Sandbox](https://cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods) configuration. When enabling this feature you must specify `image_type = "COS_CONTAINERD"` and `node_version = "1.12.7-gke.17"` or later to use it. Structure is documented below. * `service_account` - (Optional) The service account to be used by the Node VMs. @@ -614,6 +618,15 @@ The `sandbox_type` block supports: * `"gvisor"`: Pods run within a gVisor sandbox. +The `release_channel` block supports: + +* `channel` - (Optional) The selected release channel. Defaults to `UNSPECIFIED`. + Accepted values are: + * UNSPECIFIED: Not set. + * RAPID: Weekly upgrade cadence; Early testers and developers who requires new features. + * REGULAR: Multiple per month upgrade cadence; Production users who need features not yet offered in the Stable channel. + * STABLE: Every few months upgrade cadence; Production users who need stability above all else, and for whom frequent upgrades are too risky. + The `resource_usage_export_config` block supports: * `enable_network_egress_metering` (Optional) - Whether to enable network egress metering for this cluster. If enabled, a daemonset will be created