Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,21 @@ func ResourceContainerCluster() *schema.Resource {
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.`,
},

{{- if ne $.TargetVersionName "ga" }}
"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. Set this to complete a rollback-safe upgrade.",
},

"emulated_version": {
Type: schema.TypeString,
Computed: true,
Description: `The current emulated Kubernetes version running on the GKE cluster control plane.`,
},
{{- end }}

"maintenance_policy": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -3584,6 +3599,11 @@ 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 ne $.TargetVersionName "ga" }}
if err := d.Set("emulated_version", cluster.CurrentEmulatedVersion); err != nil {
return fmt.Errorf("Error setting emulated_version: %s", err)
}
{{- end }}
if err := d.Set("node_version", cluster.CurrentNodeVersion); err != nil {
return fmt.Errorf("Error setting node_version: %s", err)
}
Expand Down Expand Up @@ -4546,6 +4566,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 ne $.TargetVersionName "ga" }}
if d.HasChange("desired_emulated_version") {
emulatedVersion := d.Get("desired_emulated_version").(string)
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)
}
{{- end }}

if d.HasChange("monitoring_service") || d.HasChange("logging_service") {
logging := d.Get("logging_service").(string)
monitoring := d.Get("monitoring_service").(string)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18210,3 +18210,153 @@ func testAccContainerCluster_custom_subnet(clusterName string, networkName strin

`, clusterName, networkName, sri[0].SubnetName, additionalIpRangesStr, firstSubnet, firstSubnet)
}

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)

// We utilize standard, valid GKE staging versions for E2E minor upgrade transitions
fromVersion := "1.34.8-gke.1211000"
targetVersion := "1.35.5-gke.1117000"

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 version 1.30 (Baseline)
{
Config: testAccContainerCluster_desiredEmulatedVersionBase(clusterName, networkName, subnetworkName, fromVersion),
},
{
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 1.31 and activate 25-hour soak
{
Config: testAccContainerCluster_desiredEmulatedVersionSoak(clusterName, networkName, subnetworkName, fromVersion, targetVersion),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("google_container_cluster.primary", "min_master_version", targetVersion),
),
},
{
ResourceName: "google_container_cluster.primary",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"},
},
// Step 3 (Declarative Complete): Set desired_emulated_version to 1.35 to complete the upgrade while soaking
{
Config: testAccContainerCluster_desiredEmulatedVersionComplete(clusterName, networkName, subnetworkName, fromVersion, targetVersion),
Check: resource.ComposeTestCheckFunc(
testAccCheckContainerClusterEmulatedVersion(t, "google_container_cluster.primary"),
),
},
{
ResourceName: "google_container_cluster.primary",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"},
},
},
})
}

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, version string) string {
return fmt.Sprintf(`
resource "google_container_cluster" "primary" {
name = "%s"
location = "us-central1-a"
initial_node_count = 1
min_master_version = "%s"
deletion_protection = false
network = "%s"
subnetwork = "%s"

node_config {
machine_type = "e2-standard-2"
}
}
`, clusterName, version, networkName, subnetworkName)
}

func testAccContainerCluster_desiredEmulatedVersionSoak(clusterName, networkName, subnetworkName, fromVersion, targetVersion string) string {
return fmt.Sprintf(`
resource "google_container_cluster" "primary" {
name = "%s"
location = "us-central1-a"
initial_node_count = 1
min_master_version = "%s"
deletion_protection = false
network = "%s"
subnetwork = "%s"

desired_rollback_safe_upgrade {
control_plane_soak_duration = "90000s"
}

node_config {
machine_type = "e2-standard-2"
}
}
`, clusterName, targetVersion, networkName, subnetworkName)
}

func testAccContainerCluster_desiredEmulatedVersionComplete(clusterName, networkName, subnetworkName, fromVersion, targetVersion string) string {
return fmt.Sprintf(`
resource "google_container_cluster" "primary" {
name = "%s"
location = "us-central1-a"
initial_node_count = 1
min_master_version = "%s"
deletion_protection = false
network = "%s"
subnetwork = "%s"

desired_rollback_safe_upgrade {
control_plane_soak_duration = "90000s"
}

desired_emulated_version = "%s"

node_config {
machine_type = "e2-standard-2"
}
}
`, clusterName, targetVersion, networkName, subnetworkName, targetVersion)
}
Loading