diff --git a/mmv1/third_party/terraform/services/container/resource_container_cluster.go.tmpl b/mmv1/third_party/terraform/services/container/resource_container_cluster.go.tmpl index 10a165354d6c..835025bc043b 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_cluster.go.tmpl +++ b/mmv1/third_party/terraform/services/container/resource_container_cluster.go.tmpl @@ -1246,6 +1246,33 @@ func ResourceContainerCluster() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{"logging.googleapis.com", "logging.googleapis.com/kubernetes", "none"}, false), Description: `The logging service that the cluster should write logs to. Available options include logging.googleapis.com(Legacy Stackdriver), logging.googleapis.com/kubernetes(Stackdriver Kubernetes Engine Logging), and none. Defaults to logging.googleapis.com/kubernetes.`, }, + "rollback_safe_upgrade": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: `Configuration for rollback-safe (two-step) upgrades.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "control_plane_soak_duration": { + Type: schema.TypeString, + Optional: true, + Description: `A user-defined period that the cluster remains in the rollbackable state. A duration in seconds with up to nine fractional digits, ending with 's'. Example: "604800s" for 7 days. Minimum is 6 hours, maximum is 7 days. If omitted, the two-step upgrade is skipped and a standard one-step upgrade is performed.`, + }, + }, + }, + }, + "desired_emulated_version": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[0-9]+\.[0-9]+$`), "desired_emulated_version must be in major.minor format"), + Description: "The desired emulated version for the cluster. Used to complete a rollback-safe upgrade after a soak period. Must be in major.minor format (e.g., \"1.31\"). To complete the upgrade declaratively, set this field to the target minor version. Removing this field from your configuration will not trigger completion.", + }, + + "emulated_version": { + Type: schema.TypeString, + Computed: true, + Description: `The current emulated Kubernetes version running on the GKE cluster control plane.`, + }, "maintenance_policy": { Type: schema.TypeList, @@ -3738,6 +3765,9 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro if err := d.Set("master_version", cluster.CurrentMasterVersion); err != nil { return fmt.Errorf("Error setting master_version: %s", err) } + if err := d.Set("emulated_version", cluster.CurrentEmulatedVersion); err != nil { + return fmt.Errorf("Error setting emulated_version: %s", err) + } if err := d.Set("node_version", cluster.CurrentNodeVersion); err != nil { return fmt.Errorf("Error setting node_version: %s", err) } @@ -4709,6 +4739,24 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er log.Printf("[INFO] GKE cluster %s legacy ABAC has been updated to %v", d.Id(), enabled) } + if d.HasChange("desired_emulated_version") { + emulatedVersion := d.Get("desired_emulated_version").(string) + if emulatedVersion != "" { + req := &container.UpdateClusterRequest{ + Update: &container.ClusterUpdate{ + DesiredEmulatedVersion: emulatedVersion, + }, + } + + updateF := updateFunc(req, "updating GKE master emulated version") + // Call update serially. + if err := transport_tpg.LockedCall(lockKey, updateF); err != nil { + return err + } + log.Printf("[INFO] GKE cluster %s: master emulated version has been updated to %s", d.Id(), emulatedVersion) + } + } + if d.HasChange("monitoring_service") || d.HasChange("logging_service") { logging := d.Get("logging_service").(string) monitoring := d.Get("monitoring_service").(string) @@ -4912,6 +4960,17 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er }, } + if r, ok := d.GetOk("rollback_safe_upgrade"); ok { + rls := r.([]interface{}) + if len(rls) > 0 && rls[0] != nil { + req.Update.DesiredRollbackSafeUpgrade = &container.RollbackSafeUpgrade{} + rl := rls[0].(map[string]interface{}) + if soakDuration, ok := rl["control_plane_soak_duration"].(string); ok && soakDuration != "" { + req.Update.DesiredRollbackSafeUpgrade.ControlPlaneSoakDuration = soakDuration + } + } + } + updateF := updateFunc(req, "updating GKE master version") // Call update serially. if err := transport_tpg.LockedCall(lockKey, updateF); err != nil { diff --git a/mmv1/third_party/terraform/services/container/resource_container_cluster_meta.yaml.tmpl b/mmv1/third_party/terraform/services/container/resource_container_cluster_meta.yaml.tmpl index 176695184635..69cc45517b19 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_cluster_meta.yaml.tmpl +++ b/mmv1/third_party/terraform/services/container/resource_container_cluster_meta.yaml.tmpl @@ -144,6 +144,8 @@ fields: - field: 'deletion_protection' provider_only: true - api_field: 'description' + - field: 'desired_emulated_version' + provider_only: true - field: 'dns_config.additive_vpc_scope_dns_domain' api_field: 'networkConfig.dnsConfig.additiveVpcScopeDnsDomain' - field: 'dns_config.cluster_dns' @@ -154,6 +156,8 @@ fields: api_field: 'networkConfig.dnsConfig.clusterDnsScope' - field: 'effective_labels' provider_only: true + - field: 'emulated_version' + api_field: 'currentEmulatedVersion' - field: 'enable_autopilot' api_field: 'autopilot.enabled' - field: 'enable_cilium_clusterwide_network_policy' @@ -905,6 +909,7 @@ fields: - api_field: 'resourceUsageExportConfig.enableNetworkEgressMetering' - field: 'resource_usage_export_config.enable_resource_consumption_metering' api_field: 'resourceUsageExportConfig.consumptionMeteringConfig.enabled' + - api_field: 'rollbackSafeUpgrade.controlPlaneSoakDuration' - api_field: 'secretManagerConfig.enabled' - api_field: 'secretManagerConfig.rotationConfig.enabled' - api_field: 'secretManagerConfig.rotationConfig.rotationInterval' diff --git a/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.tmpl b/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.tmpl index 4d68be526204..da21832b9613 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.tmpl +++ b/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.tmpl @@ -19375,6 +19375,450 @@ resource "google_container_node_pool" "extra" { `, suffix, suffix, clusterName, skipRefresh, poolName) } +func TestAccContainerCluster_desiredEmulatedVersion(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) + networkName := tpgcompute.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := tpgcompute.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + // Step 1 (Create): Spin up a standard cluster at STABLE default + { + Config: testAccContainerCluster_desiredEmulatedVersionBase(clusterName, networkName, subnetworkName), + }, + { + ResourceName: "google_container_cluster.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"}, + }, + // Step 2 (Soak Upgrade): Upgrade master to target version (RAPID latest) and activate soak + { + Config: testAccContainerCluster_desiredEmulatedVersionSoak(clusterName, networkName, subnetworkName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("google_container_cluster.primary", plancheck.ResourceActionUpdate), + }, + }, + Check: resource.ComposeTestCheckFunc( + // Assert that the computed emulated_version field is populated in state during soak + resource.TestCheckResourceAttrSet("google_container_cluster.primary", "emulated_version"), + ), + }, + { + ResourceName: "google_container_cluster.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection", "rollback_safe_upgrade"}, + }, + // Step 3 (Declarative Complete): Set desired_emulated_version to complete the upgrade + { + Config: testAccContainerCluster_desiredEmulatedVersionComplete(clusterName, networkName, subnetworkName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("google_container_cluster.primary", plancheck.ResourceActionUpdate), + }, + }, + Check: resource.ComposeTestCheckFunc( + // Assert that the computed emulated_version field is cleared in state after completion + resource.TestCheckResourceAttr("google_container_cluster.primary", "emulated_version", ""), + testAccCheckContainerClusterEmulatedVersion(t, "google_container_cluster.primary"), + ), + }, + { + ResourceName: "google_container_cluster.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection", "desired_emulated_version", "rollback_safe_upgrade"}, + }, + }, + }) +} + +func testAccCheckContainerClusterEmulatedVersion(t *testing.T, resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + if rs.Primary.ID == "" { + return fmt.Errorf("no GKE cluster ID is set in state") + } + + config := acctest.GoogleProviderConfig(t) + clusterName := rs.Primary.Attributes["name"] + location := rs.Primary.Attributes["location"] + project := rs.Primary.Attributes["project"] + + // Retrieve the live GKE cluster object from Google's REST GKE API client + cluster, err := container.NewClient(config, config.UserAgent).Projects.Locations.Clusters.Get( + fmt.Sprintf("projects/%s/locations/%s/clusters/%s", project, location, clusterName)).Do() + if err != nil { + return fmt.Errorf("failed to get GKE cluster details from API: %v", err) + } + + // Verify GKE emulated version has advanced on the server side and is cleared post-upgrade + if cluster.CurrentEmulatedVersion != "" { + return fmt.Errorf("expected emulated version to be empty post-upgrade, but live GKE cluster has %q", cluster.CurrentEmulatedVersion) + } + + return nil + } +} + + +func testAccContainerCluster_desiredEmulatedVersionBase(clusterName, networkName, subnetworkName string) string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1" { + location = "us-central1" +} + +locals { + valid_minors = distinct([ + for v in data.google_container_engine_versions.central1.valid_master_versions : + regex("^[0-9]+\\.([0-9]+)\\.", v)[0] + ]) + target_minor = local.valid_minors[0] + base_minor = local.valid_minors[1] +} + +data "google_container_engine_versions" "base" { + location = "us-central1" + version_prefix = "1.${local.base_minor}." +} + +data "google_container_engine_versions" "target" { + location = "us-central1" + version_prefix = "1.${local.target_minor}." +} + +resource "google_container_cluster" "primary" { + name = "%s" + location = "us-central1" + initial_node_count = 1 + + release_channel { + channel = "RAPID" + } + + min_master_version = data.google_container_engine_versions.base.latest_master_version + deletion_protection = false + network = "%s" + subnetwork = "%s" + + node_config { + machine_type = "e2-standard-2" + } +} +`, clusterName, networkName, subnetworkName) +} + +func testAccContainerCluster_desiredEmulatedVersionSoak(clusterName, networkName, subnetworkName string) string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1" { + location = "us-central1" +} + +locals { + valid_minors = distinct([ + for v in data.google_container_engine_versions.central1.valid_master_versions : + regex("^[0-9]+\\.([0-9]+)\\.", v)[0] + ]) + target_minor = local.valid_minors[0] + base_minor = local.valid_minors[1] +} + +data "google_container_engine_versions" "base" { + location = "us-central1" + version_prefix = "1.${local.base_minor}." +} + +data "google_container_engine_versions" "target" { + location = "us-central1" + version_prefix = "1.${local.target_minor}." +} + +resource "google_container_cluster" "primary" { + name = "%s" + location = "us-central1" + initial_node_count = 1 + + release_channel { + channel = "RAPID" + } + + min_master_version = data.google_container_engine_versions.target.latest_master_version + deletion_protection = false + network = "%s" + subnetwork = "%s" + + rollback_safe_upgrade { + control_plane_soak_duration = "259200s" + } + + node_config { + machine_type = "e2-standard-2" + } +} +`, clusterName, networkName, subnetworkName) +} + +func testAccContainerCluster_desiredEmulatedVersionComplete(clusterName, networkName, subnetworkName string) string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1" { + location = "us-central1" +} + +locals { + valid_minors = distinct([ + for v in data.google_container_engine_versions.central1.valid_master_versions : + regex("^[0-9]+\\.([0-9]+)\\.", v)[0] + ]) + target_minor = local.valid_minors[0] + base_minor = local.valid_minors[1] +} + +data "google_container_engine_versions" "base" { + location = "us-central1" + version_prefix = "1.${local.base_minor}." +} + +data "google_container_engine_versions" "target" { + location = "us-central1" + version_prefix = "1.${local.target_minor}." +} + +resource "google_container_cluster" "primary" { + name = "%s" + location = "us-central1" + initial_node_count = 1 + + release_channel { + channel = "RAPID" + } + + min_master_version = data.google_container_engine_versions.target.latest_master_version + deletion_protection = false + network = "%s" + subnetwork = "%s" + + rollback_safe_upgrade { + control_plane_soak_duration = "259200s" + } + + desired_emulated_version = regex("^[0-9]+\\.[0-9]+", data.google_container_engine_versions.target.latest_master_version) + + node_config { + machine_type = "e2-standard-2" + } +} +`, clusterName, networkName, subnetworkName) +} + +func TestAccContainerCluster_desiredEmulatedVersionAutopilot(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("tf-test-cluster-auto-%s", acctest.RandString(t, 10)) + networkName := tpgcompute.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := tpgcompute.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + // Step 1 (Create): Spin up a regional GKE Autopilot cluster at STABLE default + { + Config: testAccContainerCluster_desiredEmulatedVersionAutopilotBase(clusterName, networkName, subnetworkName), + }, + { + ResourceName: "google_container_cluster.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection", "node_pool.0.node_count"}, + }, + // Step 2 (Soak Upgrade): Upgrade regional master to target version (RAPID latest) with soak active + { + Config: testAccContainerCluster_desiredEmulatedVersionAutopilotSoak(clusterName, networkName, subnetworkName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("google_container_cluster.primary", plancheck.ResourceActionUpdate), + }, + }, + Check: resource.ComposeTestCheckFunc( + // Assert that the computed emulated_version field is populated in state during soak + resource.TestCheckResourceAttrSet("google_container_cluster.primary", "emulated_version"), + ), + }, + { + ResourceName: "google_container_cluster.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection", "node_pool.0.node_count", "rollback_safe_upgrade"}, + }, + // Step 3 (Declarative Complete): Set desired_emulated_version to complete the upgrade + { + Config: testAccContainerCluster_desiredEmulatedVersionAutopilotComplete(clusterName, networkName, subnetworkName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("google_container_cluster.primary", plancheck.ResourceActionUpdate), + }, + }, + Check: resource.ComposeTestCheckFunc( + // Assert that the computed emulated_version field is cleared in state after completion + resource.TestCheckResourceAttr("google_container_cluster.primary", "emulated_version", ""), + testAccCheckContainerClusterEmulatedVersion(t, "google_container_cluster.primary"), + ), + }, + { + ResourceName: "google_container_cluster.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection", "node_pool.0.node_count", "desired_emulated_version", "rollback_safe_upgrade"}, + }, + }, + }) +} + +func testAccContainerCluster_desiredEmulatedVersionAutopilotBase(clusterName, networkName, subnetworkName string) string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1" { + location = "us-central1" +} + +locals { + valid_minors = distinct([ + for v in data.google_container_engine_versions.central1.valid_master_versions : + regex("^[0-9]+\\.([0-9]+)\\.", v)[0] + ]) + target_minor = local.valid_minors[0] + base_minor = local.valid_minors[1] +} + +data "google_container_engine_versions" "base" { + location = "us-central1" + version_prefix = "1.${local.base_minor}." +} + +data "google_container_engine_versions" "target" { + location = "us-central1" + version_prefix = "1.${local.target_minor}." +} + +resource "google_container_cluster" "primary" { + name = "%s" + location = "us-central1" + enable_autopilot = true + min_master_version = data.google_container_engine_versions.base.latest_master_version + deletion_protection = false + network = "%s" + subnetwork = "%s" + ip_allocation_policy {} + + release_channel { + channel = "RAPID" + } +} +`, clusterName, networkName, subnetworkName) +} + +func testAccContainerCluster_desiredEmulatedVersionAutopilotSoak(clusterName, networkName, subnetworkName string) string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1" { + location = "us-central1" +} + +locals { + valid_minors = distinct([ + for v in data.google_container_engine_versions.central1.valid_master_versions : + regex("^[0-9]+\\.([0-9]+)\\.", v)[0] + ]) + target_minor = local.valid_minors[0] + base_minor = local.valid_minors[1] +} + +data "google_container_engine_versions" "base" { + location = "us-central1" + version_prefix = "1.${local.base_minor}." +} + +data "google_container_engine_versions" "target" { + location = "us-central1" + version_prefix = "1.${local.target_minor}." +} + +resource "google_container_cluster" "primary" { + name = "%s" + location = "us-central1" + enable_autopilot = true + min_master_version = data.google_container_engine_versions.target.latest_master_version + deletion_protection = false + network = "%s" + subnetwork = "%s" + ip_allocation_policy {} + + release_channel { + channel = "RAPID" + } + + rollback_safe_upgrade { + control_plane_soak_duration = "259200s" + } +} +`, clusterName, networkName, subnetworkName) +} + +func testAccContainerCluster_desiredEmulatedVersionAutopilotComplete(clusterName, networkName, subnetworkName string) string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1" { + location = "us-central1" +} + +locals { + valid_minors = distinct([ + for v in data.google_container_engine_versions.central1.valid_master_versions : + regex("^[0-9]+\\.([0-9]+)\\.", v)[0] + ]) + target_minor = local.valid_minors[0] + base_minor = local.valid_minors[1] +} + +data "google_container_engine_versions" "base" { + location = "us-central1" + version_prefix = "1.${local.base_minor}." +} + +data "google_container_engine_versions" "target" { + location = "us-central1" + version_prefix = "1.${local.target_minor}." +} + +resource "google_container_cluster" "primary" { + name = "%s" + location = "us-central1" + enable_autopilot = true + min_master_version = data.google_container_engine_versions.target.latest_master_version + deletion_protection = false + network = "%s" + subnetwork = "%s" + ip_allocation_policy {} + + release_channel { + channel = "RAPID" + } + + rollback_safe_upgrade { + control_plane_soak_duration = "259200s" + } + + desired_emulated_version = regex("^[0-9]+\\.[0-9]+", data.google_container_engine_versions.target.latest_master_version) +} +`, clusterName, networkName, subnetworkName) +} + func TestAccContainerCluster_withNodeReadinessConfig(t *testing.T) { t.Parallel() diff --git a/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown b/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown index ab281424e3e3..e0097c31f95f 100644 --- a/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown @@ -95,6 +95,44 @@ resource "google_container_cluster" "primary" { } ``` +## Example Usage - Rollback-safe (Two-Step) Upgrades + +To perform a rollback-safe (two-step) control plane upgrade, you first specify a soak duration in the `rollback_safe_upgrade` block when changing the `min_master_version`. This upgrades the master but keeps the control plane emulating the older version. + +```hcl +resource "google_container_cluster" "primary" { + name = "my-gke-cluster" + location = "us-central1" + initial_node_count = 1 + min_master_version = "1.32.4-gke.200" # Upgrading to the 1.32 minor track + + # Phase 1: Explicitly opt-in to a rollback-safe upgrade + rollback_safe_upgrade { + control_plane_soak_duration = "604800s" # Soak for 7 days + } +} +``` + +After the soak period concludes, you can declaratively complete the upgrade by specifying the target `desired_emulated_version`. + +```hcl +resource "google_container_cluster" "primary" { + name = "my-gke-cluster" + location = "us-central1" + initial_node_count = 1 + min_master_version = "1.32.4-gke.200" + + rollback_safe_upgrade { + control_plane_soak_duration = "604800s" + } + + # Phase 2: Complete the upgrade (updates emulated_version to 1.32) + desired_emulated_version = "1.32" +} +``` + +~> **Note:** If you omit the `control_plane_soak_duration` field completely, GKE bypasses the two-step feature and performs a standard one-step upgrade. You must specify a duration between 6 hours and 7 days. + ## Argument Reference * `name` - (Required) The name of the cluster, unique within the project and @@ -254,6 +292,10 @@ Structure is [documented below](#nested_master_auth). to the datasource. A region can have a different set of supported versions than its corresponding zones, and not all zones in a region are guaranteed to support the same version. +* `rollback_safe_upgrade` - (Optional) Configuration for rollback-safe (two-step) upgrades. Structure is [documented below](#nested_rollback_safe_upgrade). + +* `desired_emulated_version` - (Optional) The desired emulated version for the cluster. Used to complete a rollback-safe upgrade after a soak period. Must be in major.minor format (e.g., "1.31"). To complete the upgrade declaratively, set this field to the target minor version. Removing this field from your configuration will not trigger completion. + * `monitoring_config` - (Optional) Monitoring configuration for the cluster. Structure is [documented below](#nested_monitoring_config). @@ -1003,6 +1045,10 @@ Structure is [documented below](#nested_additional_ip_ranges_config). * `NETWORK_TIER_STANDARD`: Standard network tier. +The `rollback_safe_upgrade` block supports: + +* `control_plane_soak_duration` - (Optional) A user-defined period that the cluster remains in the rollbackable state. A duration in seconds with up to nine fractional digits, ending with 's'. Example: "604800s" for 7 days. Minimum is 6 hours, maximum is 7 days. If omitted, the two-step upgrade is skipped and a standard one-step upgrade is performed. + The `master_auth` block supports: * `client_certificate_config` - (Required) Whether client certificate authorization is enabled for this cluster. For example: @@ -2065,6 +2111,8 @@ exported: * `enterprise_config.0.cluster_tier` - The effective tier of the cluster. +* `emulated_version` - The current emulated Kubernetes version running on the GKE cluster control plane. + ## Timeouts This resource provides the following