From cce4ab1a46815989f40b56bc6b3f5e8f60b00ee8 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 11 Oct 2017 17:09:07 -0700 Subject: [PATCH 1/5] wait for running status on a cluster on read --- google/resource_container_cluster.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/google/resource_container_cluster.go b/google/resource_container_cluster.go index dcc7938c279..7921f0ca09a 100644 --- a/google/resource_container_cluster.go +++ b/google/resource_container_cluster.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "google.golang.org/api/container/v1" @@ -435,8 +436,18 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro zoneName := d.Get("zone").(string) - cluster, err := config.clientContainer.Projects.Zones.Clusters.Get( - project, zoneName, d.Get("name").(string)).Do() + var cluster *container.Cluster + err = resource.Retry(1*time.Minute, func() *resource.RetryError { + cluster, err = config.clientContainer.Projects.Zones.Clusters.Get( + project, zoneName, d.Get("name").(string)).Do() + if err != nil { + return resource.NonRetryableError(err) + } + if cluster.Status != "RUNNING" { + return resource.RetryableError(fmt.Errorf("Cluster %q has status %q with message %q", d.Get("name"), cluster.Status, cluster.StatusMessage)) + } + return nil + }) if err != nil { return handleNotFoundError(err, d, fmt.Sprintf("Container Cluster %q", d.Get("name").(string))) } From cc7cc83a398cda69b97a213d86f7132984ba9515 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 11 Oct 2017 18:11:20 -0700 Subject: [PATCH 2/5] add min_master_version field --- google/resource_container_cluster.go | 23 ++++++++++++++++------- google/resource_container_cluster_test.go | 6 +++--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/google/resource_container_cluster.go b/google/resource_container_cluster.go index 7921f0ca09a..d549f5f767d 100644 --- a/google/resource_container_cluster.go +++ b/google/resource_container_cluster.go @@ -245,10 +245,14 @@ func resourceContainerCluster() *schema.Resource { "master_version": { Type: schema.TypeString, - Optional: true, Computed: true, }, + "min_master_version": { + Type: schema.TypeString, + Optional: true, + }, + "node_config": schemaNodeConfig, "node_version": { @@ -303,12 +307,17 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er } } - if v, ok := d.GetOk("master_version"); ok { + if v, ok := d.GetOk("min_master_version"); ok { cluster.InitialClusterVersion = v.(string) } - if _, ok := d.GetOk("node_version"); ok { - return fmt.Errorf("cannot set node_version on create, use master_version instead") + if v, ok := d.GetOk("node_version"); ok { + // ignore -gke.X suffix for now. if it becomes a problem later, we can fix it. + mv := strings.Split(cluster.InitialClusterVersion, "-")[0] + nv := strings.Split(v.(string), "-")[0] + if mv != nv { + return fmt.Errorf("node_version and min_master_version must be set to equivalent values on create") + } } if v, ok := d.GetOk("additional_zones"); ok { @@ -519,8 +528,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er d.Partial(true) // The master must be updated before the nodes - if d.HasChange("master_version") { - desiredMasterVersion := d.Get("master_version").(string) + if d.HasChange("min_master_version") { + desiredMasterVersion := d.Get("min_master_version").(string) req := &container.UpdateClusterRequest{ Update: &container.ClusterUpdate{ DesiredMasterVersion: desiredMasterVersion, @@ -541,7 +550,7 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er log.Printf("[INFO] GKE cluster %s: master has been updated to %s", d.Id(), desiredMasterVersion) - d.SetPartial("master_version") + d.SetPartial("min_master_version") } if d.HasChange("node_version") { diff --git a/google/resource_container_cluster_test.go b/google/resource_container_cluster_test.go index 56fdf3ea225..01eefa00920 100644 --- a/google/resource_container_cluster_test.go +++ b/google/resource_container_cluster_test.go @@ -824,7 +824,7 @@ data "google_container_engine_versions" "central1a" { resource "google_container_cluster" "with_version" { name = "cluster-test-%s" zone = "us-central1-a" - master_version = "${data.google_container_engine_versions.central1a.latest_master_version}" + min_master_version = "${data.google_container_engine_versions.central1a.latest_master_version}" initial_node_count = 1 master_auth { @@ -843,7 +843,7 @@ data "google_container_engine_versions" "central1a" { resource "google_container_cluster" "with_version" { name = "cluster-test-%s" zone = "us-central1-a" - master_version = "${data.google_container_engine_versions.central1a.valid_master_versions.1}" + min_master_version = "${data.google_container_engine_versions.central1a.valid_master_versions.1}" initial_node_count = 1 master_auth { @@ -862,7 +862,7 @@ data "google_container_engine_versions" "central1a" { resource "google_container_cluster" "with_version" { name = "cluster-test-%s" zone = "us-central1-a" - master_version = "${data.google_container_engine_versions.central1a.latest_master_version}" + min_master_version = "${data.google_container_engine_versions.central1a.latest_master_version}" node_version = "${data.google_container_engine_versions.central1a.latest_node_version}" initial_node_count = 1 From 44abbf5afe85f6cc15fbd8a68b0acaf24469f380 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 12 Oct 2017 09:54:45 -0700 Subject: [PATCH 3/5] respond to comments --- google/resource_container_cluster.go | 56 +++++++++++++++-------- google/resource_container_cluster_test.go | 6 +-- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/google/resource_container_cluster.go b/google/resource_container_cluster.go index d549f5f767d..4bba857fe9f 100644 --- a/google/resource_container_cluster.go +++ b/google/resource_container_cluster.go @@ -8,6 +8,7 @@ import ( "strings" "time" + version "github.com/hashicorp/go-version" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" @@ -312,11 +313,15 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er } if v, ok := d.GetOk("node_version"); ok { - // ignore -gke.X suffix for now. if it becomes a problem later, we can fix it. - mv := strings.Split(cluster.InitialClusterVersion, "-")[0] - nv := strings.Split(v.(string), "-")[0] - if mv != nv { - return fmt.Errorf("node_version and min_master_version must be set to equivalent values on create") + if cluster.InitialClusterVersion != "" { + // ignore -gke.X suffix for now. if it becomes a problem later, we can fix it. + mv := strings.Split(cluster.InitialClusterVersion, "-")[0] + nv := strings.Split(v.(string), "-")[0] + if mv != nv { + return fmt.Errorf("node_version and min_master_version must be set to equivalent values on create") + } + } else { + cluster.InitialClusterVersion = v.(string) } } @@ -446,7 +451,7 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro zoneName := d.Get("zone").(string) var cluster *container.Cluster - err = resource.Retry(1*time.Minute, func() *resource.RetryError { + err = resource.Retry(2*time.Minute, func() *resource.RetryError { cluster, err = config.clientContainer.Projects.Zones.Clusters.Get( project, zoneName, d.Get("name").(string)).Do() if err != nil { @@ -530,25 +535,38 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er // The master must be updated before the nodes if d.HasChange("min_master_version") { desiredMasterVersion := d.Get("min_master_version").(string) - req := &container.UpdateClusterRequest{ - Update: &container.ClusterUpdate{ - DesiredMasterVersion: desiredMasterVersion, - }, + currentMasterVersion := d.Get("master_version").(string) + des, err := version.NewVersion(desiredMasterVersion) + if err != nil { + return err } - op, err := config.clientContainer.Projects.Zones.Clusters.Update( - project, zoneName, clusterName, req).Do() + cur, err := version.NewVersion(currentMasterVersion) if err != nil { return err } - // Wait until it's updated - waitErr := containerOperationWait(config, op, project, zoneName, "updating GKE master version", timeoutInMinutes, 2) - if waitErr != nil { - return waitErr - } + // Only upgrade the master if the current version is lower than the desired version + if cur.LessThan(des) { + req := &container.UpdateClusterRequest{ + Update: &container.ClusterUpdate{ + DesiredMasterVersion: desiredMasterVersion, + }, + } + op, err := config.clientContainer.Projects.Zones.Clusters.Update( + project, zoneName, clusterName, req).Do() + if err != nil { + return err + } - log.Printf("[INFO] GKE cluster %s: master has been updated to %s", d.Id(), - desiredMasterVersion) + // Wait until it's updated + waitErr := containerOperationWait(config, op, project, zoneName, "updating GKE master version", timeoutInMinutes, 2) + if waitErr != nil { + return waitErr + } + + log.Printf("[INFO] GKE cluster %s: master has been updated to %s", d.Id(), + desiredMasterVersion) + } d.SetPartial("min_master_version") } diff --git a/google/resource_container_cluster_test.go b/google/resource_container_cluster_test.go index 01eefa00920..a358b610395 100644 --- a/google/resource_container_cluster_test.go +++ b/google/resource_container_cluster_test.go @@ -843,7 +843,7 @@ data "google_container_engine_versions" "central1a" { resource "google_container_cluster" "with_version" { name = "cluster-test-%s" zone = "us-central1-a" - min_master_version = "${data.google_container_engine_versions.central1a.valid_master_versions.1}" + min_master_version = "${data.google_container_engine_versions.central1a.valid_master_versions.2}" initial_node_count = 1 master_auth { @@ -862,8 +862,8 @@ data "google_container_engine_versions" "central1a" { resource "google_container_cluster" "with_version" { name = "cluster-test-%s" zone = "us-central1-a" - min_master_version = "${data.google_container_engine_versions.central1a.latest_master_version}" - node_version = "${data.google_container_engine_versions.central1a.latest_node_version}" + min_master_version = "${data.google_container_engine_versions.central1a.valid_master_versions.1}" + node_version = "${data.google_container_engine_versions.central1a.valid_node_versions.1}" initial_node_count = 1 master_auth { From f85ddeac7d4643aa9ea6065edf39b4d947e7db1b Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 12 Oct 2017 10:21:25 -0700 Subject: [PATCH 4/5] add docs --- website/docs/r/container_cluster.html.markdown | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/website/docs/r/container_cluster.html.markdown b/website/docs/r/container_cluster.html.markdown index e82ffa763d0..b398fb72133 100644 --- a/website/docs/r/container_cluster.html.markdown +++ b/website/docs/r/container_cluster.html.markdown @@ -13,10 +13,6 @@ Creates a GKE cluster. For more information see and [API](https://cloud.google.com/container-engine/reference/rest/v1/projects.zones.clusters). -!> **Warning:** Due to limitations of the API, all arguments except -`node_version` are non-updateable. Changing any will cause recreation of the -whole cluster! - ~> **Note:** All arguments including the username and password will be stored in the raw state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html). @@ -85,6 +81,12 @@ resource "google_container_cluster" "primary" { write logs to. Available options include `logging.googleapis.com` and `none`. Defaults to `logging.googleapis.com` +* `min_master_version` - (Optional) The minimum version of the master. GKE + will auto-update the master to new versions, so this does not guarantee the + current master version--use the read-only `master_version` field to obtain that. + If unset, the cluster's version will be set by GKE to the version of the most recent + official release (which is not necessarily the latest version). + * `monitoring_service` - (Optional) The monitoring service that the cluster should write metrics to. Available options include `monitoring.googleapis.com` and `none`. Defaults to @@ -209,6 +211,10 @@ exported: * `master_auth.cluster_ca_certificate` - Base64 encoded public certificate that is the root of trust for the cluster +* `master_version` - The current version of the master in the cluster. This may + be different than the `min_master_version` set in the config if the master + has been updated by GKE. + ## Timeouts From 3cad14abb98cabcfb4a292a1913bafd526673594 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 12 Oct 2017 10:28:47 -0700 Subject: [PATCH 5/5] no node_version on create --- google/resource_container_cluster.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/google/resource_container_cluster.go b/google/resource_container_cluster.go index 4bba857fe9f..17be40e811e 100644 --- a/google/resource_container_cluster.go +++ b/google/resource_container_cluster.go @@ -312,16 +312,14 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er cluster.InitialClusterVersion = v.(string) } + // Only allow setting node_version on create if it's set to the equivalent master version, + // since `InitialClusterVersion` only accepts valid master-style versions. if v, ok := d.GetOk("node_version"); ok { - if cluster.InitialClusterVersion != "" { - // ignore -gke.X suffix for now. if it becomes a problem later, we can fix it. - mv := strings.Split(cluster.InitialClusterVersion, "-")[0] - nv := strings.Split(v.(string), "-")[0] - if mv != nv { - return fmt.Errorf("node_version and min_master_version must be set to equivalent values on create") - } - } else { - cluster.InitialClusterVersion = v.(string) + // ignore -gke.X suffix for now. if it becomes a problem later, we can fix it. + mv := strings.Split(cluster.InitialClusterVersion, "-")[0] + nv := strings.Split(v.(string), "-")[0] + if mv != nv { + return fmt.Errorf("node_version and min_master_version must be set to equivalent values on create") } }