Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow updating additional_zones, turn it into a set #152

Merged
merged 3 commits into from
Jul 5, 2017
Merged
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
11 changes: 11 additions & 0 deletions google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,14 @@ func linkDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
}
return false
}

func convertStringArr(ifaceArr []interface{}) []string {
var arr []string
for _, v := range ifaceArr {
if v == nil {
continue
}
arr = append(arr, v.(string))
}
return arr
}
11 changes: 0 additions & 11 deletions google/resource_compute_target_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,6 @@ func resourceComputeTargetPool() *schema.Resource {
}
}

func convertStringArr(ifaceArr []interface{}) []string {
var arr []string
for _, v := range ifaceArr {
if v == nil {
continue
}
arr = append(arr, v.(string))
}
return arr
}

// Healthchecks need to exist before being referred to from the target pool.
func convertHealthChecks(config *Config, project string, names []string) ([]string, error) {
urls := make([]string, len(names))
Expand Down
76 changes: 57 additions & 19 deletions google/resource_container_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ func resourceContainerCluster() *schema.Resource {
Delete: schema.DefaultTimeout(10 * time.Minute),
},

SchemaVersion: 1,
MigrateState: resourceContainerClusterMigrateState,

Schema: map[string]*schema.Schema{
"master_auth": {
Type: schema.TypeList,
Expand Down Expand Up @@ -106,10 +109,9 @@ func resourceContainerCluster() *schema.Resource {
},

"additional_zones": {
Type: schema.TypeList,
Type: schema.TypeSet,
Optional: true,
Computed: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

Expand Down Expand Up @@ -386,7 +388,7 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
}

if v, ok := d.GetOk("additional_zones"); ok {
locationsList := v.([]interface{})
locationsList := v.(*schema.Set).List()
locations := []string{}
for _, v := range locationsList {
location := v.(string)
Expand Down Expand Up @@ -634,29 +636,65 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er

zoneName := d.Get("zone").(string)
clusterName := d.Get("name").(string)
desiredNodeVersion := d.Get("node_version").(string)
timeoutInMinutes := int(d.Timeout(schema.TimeoutUpdate).Minutes())

req := &container.UpdateClusterRequest{
Update: &container.ClusterUpdate{
DesiredNodeVersion: desiredNodeVersion,
},
}
op, err := config.clientContainer.Projects.Zones.Clusters.Update(
project, zoneName, clusterName, req).Do()
if err != nil {
return err
d.Partial(true)

if d.HasChange("node_version") {
desiredNodeVersion := d.Get("node_version").(string)

req := &container.UpdateClusterRequest{
Update: &container.ClusterUpdate{
DesiredNodeVersion: desiredNodeVersion,
},
}
op, err := config.clientContainer.Projects.Zones.Clusters.Update(
project, zoneName, clusterName, req).Do()
if err != nil {
return err
}

// Wait until it's updated
waitErr := containerOperationWait(config, op, project, zoneName, "updating GKE cluster version", timeoutInMinutes, 2)
if waitErr != nil {
return waitErr
}

log.Printf("[INFO] GKE cluster %s has been updated to %s", d.Id(),
desiredNodeVersion)

d.SetPartial("node_version")
}

// Wait until it's updated
if d.HasChange("additional_zones") {
azSet := d.Get("additional_zones").(*schema.Set)
if azSet.Contains(zoneName) {
return fmt.Errorf("additional_zones should not contain the original 'zone'.")
}
azs := convertStringArr(azSet.List())
locations := append(azs, zoneName)
req := &container.UpdateClusterRequest{
Update: &container.ClusterUpdate{
DesiredLocations: locations,
},
}
op, err := config.clientContainer.Projects.Zones.Clusters.Update(
project, zoneName, clusterName, req).Do()
if err != nil {
return err
}

// Wait until it's updated
waitErr := containerOperationWait(config, op, project, zoneName, "updating GKE cluster locations", timeoutInMinutes, 2)
if waitErr != nil {
return waitErr
}

waitErr := containerOperationWait(config, op, project, zoneName, "updating GKE cluster", timeoutInMinutes, 2)
if waitErr != nil {
return waitErr
log.Printf("[INFO] GKE cluster %s locations have been updated to %v", d.Id(),
locations)
}

log.Printf("[INFO] GKE cluster %s has been updated to %s", d.Id(),
desiredNodeVersion)
d.Partial(false)

return resourceContainerClusterRead(d, meta)
}
Expand Down
70 changes: 70 additions & 0 deletions google/resource_container_cluster_migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package google

import (
"fmt"
"log"
"strconv"
"strings"

"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)

func resourceContainerClusterMigrateState(
v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
if is.Empty() {
log.Println("[DEBUG] Empty InstanceState; nothing to migrate.")
return is, nil
}

switch v {
case 0:
log.Println("[INFO] Found Container Cluster State v0; migrating to v1")
return migrateClusterStateV0toV1(is)
default:
return is, fmt.Errorf("Unexpected schema version: %d", v)
}
}

func migrateClusterStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: capital 't' in the to part of migrateClusterStateV0toV1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving it lowercase to match the other files

log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes)

newZones := []string{}

for k, v := range is.Attributes {
if !strings.HasPrefix(k, "additional_zones.") {
continue
}

if k == "additional_zones.#" {
continue
}

// Key is now of the form additional_zones.%d
kParts := strings.Split(k, ".")

// Sanity check: two parts should be there and <N> should be a number
badFormat := false
if len(kParts) != 2 {
badFormat = true
} else if _, err := strconv.Atoi(kParts[1]); err != nil {
badFormat = true
}

if badFormat {
return is, fmt.Errorf("migration error: found additional_zones key in unexpected format: %s", k)
}

newZones = append(newZones, v)
delete(is.Attributes, k)
}

for _, v := range newZones {
hash := schema.HashString(v)
newKey := fmt.Sprintf("additional_zones.%d", hash)
is.Attributes[newKey] = v
}

log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes)
return is, nil
}
75 changes: 75 additions & 0 deletions google/resource_container_cluster_migrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package google

import (
"testing"

"github.com/hashicorp/terraform/terraform"
)

func TestContainerClusterMigrateState(t *testing.T) {
cases := map[string]struct {
StateVersion int
Attributes map[string]string
Expected map[string]string
Meta interface{}
}{
"change additional_zones from list to set": {
StateVersion: 0,
Attributes: map[string]string{
"additional_zones.#": "2",
"additional_zones.0": "us-central1-c",
"additional_zones.1": "us-central1-b",
},
Expected: map[string]string{
"additional_zones.#": "2",
"additional_zones.90274510": "us-central1-c",
"additional_zones.1919306328": "us-central1-b",
},
Meta: &Config{},
},
}

for tn, tc := range cases {
is := &terraform.InstanceState{
ID: "i-abc123",
Attributes: tc.Attributes,
}
is, err := resourceContainerClusterMigrateState(
tc.StateVersion, is, tc.Meta)

if err != nil {
t.Fatalf("bad: %s, err: %#v", tn, err)
}

for k, v := range tc.Expected {
if is.Attributes[k] != v {
t.Fatalf(
"bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v",
tn, k, v, k, is.Attributes[k], is.Attributes)
}
}
}
}

func TestContainerClusterMigrateState_empty(t *testing.T) {
var is *terraform.InstanceState
var meta *Config

// should handle nil
is, err := resourceContainerClusterMigrateState(0, is, meta)

if err != nil {
t.Fatalf("err: %#v", err)
}
if is != nil {
t.Fatalf("expected nil instancestate, got: %#v", is)
}

// should handle non-nil but empty
is = &terraform.InstanceState{}
is, err = resourceContainerClusterMigrateState(0, is, meta)

if err != nil {
t.Fatalf("err: %#v", err)
}
}
Loading