From beedab81192affd526dccbc8934d2fd7a149e2aa Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 4 Oct 2017 16:49:02 -0700 Subject: [PATCH] Fix BackendService group hash when instance groups use beta features (#522) * change backend hash function * update if statement --- google/resource_compute_backend_service.go | 9 +- ...esource_compute_backend_service_migrate.go | 90 ++++++++++++++++++ ...ce_compute_backend_service_migrate_test.go | 95 +++++++++++++++++++ .../resource_compute_backend_service_test.go | 4 + 4 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 google/resource_compute_backend_service_migrate.go create mode 100644 google/resource_compute_backend_service_migrate_test.go diff --git a/google/resource_compute_backend_service.go b/google/resource_compute_backend_service.go index 422ca00cdce..58b0262765a 100644 --- a/google/resource_compute_backend_service.go +++ b/google/resource_compute_backend_service.go @@ -19,6 +19,7 @@ func resourceComputeBackendService() *schema.Resource { Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, + SchemaVersion: 1, Schema: map[string]*schema.Schema{ "name": &schema.Schema{ @@ -42,8 +43,9 @@ func resourceComputeBackendService() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "group": &schema.Schema{ - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: compareSelfLinkRelativePaths, }, "balancing_mode": &schema.Schema{ Type: schema.TypeString, @@ -374,7 +376,8 @@ func resourceGoogleComputeBackendServiceBackendHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["group"].(string))) + group, _ := getRelativePath(m["group"].(string)) + buf.WriteString(fmt.Sprintf("%s-", group)) if v, ok := m["balancing_mode"]; ok { buf.WriteString(fmt.Sprintf("%s-", v.(string))) diff --git a/google/resource_compute_backend_service_migrate.go b/google/resource_compute_backend_service_migrate.go new file mode 100644 index 00000000000..c43ad5e35b9 --- /dev/null +++ b/google/resource_compute_backend_service_migrate.go @@ -0,0 +1,90 @@ +package google + +import ( + "fmt" + "log" + "strconv" + "strings" + + "github.com/hashicorp/terraform/terraform" +) + +func resourceComputeBackendServiceMigrateState( + 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 Compute Backend Service State v0; migrating to v1") + is, err := migrateBackendServiceStateV0toV1(is) + if err != nil { + return is, err + } + return is, nil + default: + return is, fmt.Errorf("Unexpected schema version: %d", v) + } +} + +func migrateBackendServiceStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) { + log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes) + + oldHashToValue := map[string]map[string]interface{}{} + for k, v := range is.Attributes { + if !strings.HasPrefix(k, "backend.") || k == "backend.#" { + continue + } + + // Key is now of the form backend.%d.%s + kParts := strings.Split(k, ".") + + // Sanity check: two parts should be there and should be a number + badFormat := false + if len(kParts) != 3 { + badFormat = true + } else if _, err := strconv.Atoi(kParts[1]); err != nil { + badFormat = true + } + + if badFormat { + return is, fmt.Errorf("migration error: found backend key in unexpected format: %s", k) + } + + if oldHashToValue[kParts[1]] == nil { + oldHashToValue[kParts[1]] = map[string]interface{}{} + } + oldHashToValue[kParts[1]][kParts[2]] = v + } + + oldHashToNewHash := map[string]int{} + for k, v := range oldHashToValue { + oldHashToNewHash[k] = resourceGoogleComputeBackendServiceBackendHash(v) + } + + values := map[string]string{} + for k, v := range is.Attributes { + if !strings.HasPrefix(k, "backend.") { + continue + } + + if k == "backend.#" { + continue + } + + // Key is now of the form backend.%d.%s + kParts := strings.Split(k, ".") + newKey := fmt.Sprintf("%s.%d.%s", kParts[0], oldHashToNewHash[kParts[1]], kParts[2]) + values[newKey] = v + delete(is.Attributes, k) + } + + for k, v := range values { + is.Attributes[k] = v + } + + log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes) + return is, nil +} diff --git a/google/resource_compute_backend_service_migrate_test.go b/google/resource_compute_backend_service_migrate_test.go new file mode 100644 index 00000000000..1d162f5207c --- /dev/null +++ b/google/resource_compute_backend_service_migrate_test.go @@ -0,0 +1,95 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform/terraform" +) + +func TestComputeBackendServiceMigrateState(t *testing.T) { + cases := map[string]struct { + StateVersion int + Attributes map[string]string + ExpectedAttributes map[string]string + Meta interface{} + }{ + "v0 to v1": { + StateVersion: 0, + Attributes: map[string]string{ + "backend.#": "1", + "backend.242332812.group": "https://www.googleapis.com/compute/v1/projects/project_name/zones/zone_name/instances/instanceGroups/igName", + "backend.242332812.balancing_mode": "UTILIZATION", + "backend.242332812.max_utilization": "0.8", + }, + ExpectedAttributes: map[string]string{ + "backend.#": "1", + "backend.2573491210.group": "https://www.googleapis.com/compute/v1/projects/project_name/zones/zone_name/instances/instanceGroups/igName", + "backend.2573491210.balancing_mode": "UTILIZATION", + "backend.2573491210.max_utilization": "0.8", + }, + Meta: &Config{}, + }, + } + + for tn, tc := range cases { + is := &terraform.InstanceState{ + ID: "i-abc123", + Attributes: tc.Attributes, + } + is, err := resourceComputeBackendServiceMigrateState( + tc.StateVersion, is, tc.Meta) + + if err != nil { + t.Fatalf("bad: %s, err: %#v", tn, err) + } + + for k, v := range tc.ExpectedAttributes { + 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) + } + } + + for k, v := range is.Attributes { + if tc.ExpectedAttributes[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + tn, k, tc.ExpectedAttributes[k], k, v, is.Attributes) + } + } + } +} + +func TestComputeBackendServiceMigrateState_empty(t *testing.T) { + cases := map[string]struct { + StateVersion int + }{ + "v0": { + StateVersion: 0, + }, + } + + for tn, tc := range cases { + var is *terraform.InstanceState + var meta *Config + + // should handle nil + is, err := resourceComputeBackendServiceMigrateState(tc.StateVersion, is, meta) + + if err != nil { + t.Fatalf("bad %s, err: %#v", tn, err) + } + if is != nil { + t.Fatalf("bad %s, expected nil instancestate, got: %#v", tn, is) + } + + // should handle non-nil but empty + is = &terraform.InstanceState{} + is, err = resourceComputeBackendServiceMigrateState(tc.StateVersion, is, meta) + + if err != nil { + t.Fatalf("bad %s, err: %#v", tn, err) + } + } +} diff --git a/google/resource_compute_backend_service_test.go b/google/resource_compute_backend_service_test.go index 18094f6e596..f29ea1b7685 100644 --- a/google/resource_compute_backend_service_test.go +++ b/google/resource_compute_backend_service_test.go @@ -412,6 +412,10 @@ resource "google_compute_instance_group_manager" "foobar" { base_instance_name = "foobar" zone = "us-central1-f" target_size = 1 + auto_healing_policies { + health_check = "${google_compute_http_health_check.default.self_link}" + initial_delay_sec = "10" + } } resource "google_compute_instance_template" "foobar" {