From 6cce8d6c1a6750cdfdf6965e524eefcad5f11863 Mon Sep 17 00:00:00 2001 From: Paddy Date: Mon, 6 Mar 2017 21:14:32 -0800 Subject: [PATCH] provider/google: fix container instance group URLs Google Container Engine's cluster API returned instance group manager URLs when it meant to return instance group URLs. See #4336 for details about the bug. While this is undeniably an upstream problem, this PR: * detects the error, meaning it will work as expected when the API is fixed. * corrects the error by requesting the instance group manager, then retrieving its instance group URL, and using that instead. * adds a test that exercises the error and the solution, to ensure it is functioning properly. --- .../google/resource_container_cluster.go | 27 +++++++- .../google/resource_container_cluster_test.go | 66 ++++++++++++++++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/builtin/providers/google/resource_container_cluster.go b/builtin/providers/google/resource_container_cluster.go index fd9aa43a9631..ccea976b6dad 100644 --- a/builtin/providers/google/resource_container_cluster.go +++ b/builtin/providers/google/resource_container_cluster.go @@ -13,6 +13,10 @@ import ( "google.golang.org/api/googleapi" ) +var ( + instanceGroupManagerURL = regexp.MustCompile("^https://www.googleapis.com/compute/v1/projects/([a-z][a-z0-9-]{5}(?:[-a-z0-9]{0,23}[a-z0-9])?)/zones/([a-z0-9-]*)/instanceGroupManagers/([^/]*)") +) + func resourceContainerCluster() *schema.Resource { return &schema.Resource{ Create: resourceContainerClusterCreate, @@ -474,7 +478,28 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro d.Set("network", d.Get("network").(string)) d.Set("subnetwork", cluster.Subnetwork) d.Set("node_config", flattenClusterNodeConfig(cluster.NodeConfig)) - d.Set("instance_group_urls", cluster.InstanceGroupUrls) + + // container engine's API currently mistakenly returns the instance group manager's + // URL instead of the instance group's URL in its responses. This shim detects that + // error, and corrects it, by fetching the instance group manager URL and retrieving + // the instance group manager, then using that to look up the instance group URL, which + // is then substituted. + // + // This should be removed when the API response is fixed. + instanceGroupURLs := make([]string, 0, len(cluster.InstanceGroupUrls)) + for _, u := range cluster.InstanceGroupUrls { + if !instanceGroupManagerURL.MatchString(u) { + instanceGroupURLs = append(instanceGroupURLs, u) + continue + } + matches := instanceGroupManagerURL.FindStringSubmatch(u) + instanceGroupManager, err := config.clientCompute.InstanceGroupManagers.Get(matches[1], matches[2], matches[3]).Do() + if err != nil { + return fmt.Errorf("Error reading instance group manager returned as an instance group URL: %s", err) + } + instanceGroupURLs = append(instanceGroupURLs, instanceGroupManager.InstanceGroup) + } + d.Set("instance_group_urls", instanceGroupURLs) return nil } diff --git a/builtin/providers/google/resource_container_cluster_test.go b/builtin/providers/google/resource_container_cluster_test.go index 4f4ff8201031..1461af9324ca 100644 --- a/builtin/providers/google/resource_container_cluster_test.go +++ b/builtin/providers/google/resource_container_cluster_test.go @@ -4,10 +4,11 @@ import ( "fmt" "testing" + "strconv" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "strconv" ) func TestAccContainerCluster_basic(t *testing.T) { @@ -116,6 +117,23 @@ func TestAccContainerCluster_network(t *testing.T) { }) } +func TestAccContainerCluster_backend(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccContainerCluster_backendRef, + Check: resource.ComposeTestCheckFunc( + testAccCheckContainerClusterExists( + "google_container_cluster.primary"), + ), + }, + }, + }) +} + func testAccCheckContainerClusterDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) @@ -296,3 +314,49 @@ resource "google_container_cluster" "with_net_ref_by_name" { network = "${google_compute_network.container_network.name}" }`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10)) + +var testAccContainerCluster_backendRef = fmt.Sprintf(` +resource "google_compute_backend_service" "my-backend-service" { + name = "terraform-test-%s" + port_name = "http" + protocol = "HTTP" + + backend { + group = "${element(google_container_cluster.primary.instance_group_urls, 1)}" + } + + health_checks = ["${google_compute_http_health_check.default.self_link}"] +} + +resource "google_compute_http_health_check" "default" { + name = "terraform-test-%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_container_cluster" "primary" { + name = "terraform-test-%s" + zone = "us-central1-a" + initial_node_count = 3 + + additional_zones = [ + "us-central1-b", + "us-central1-c", + ] + + master_auth { + username = "mr.yoda" + password = "adoy.rm" + } + + node_config { + oauth_scopes = [ + "https://www.googleapis.com/auth/compute", + "https://www.googleapis.com/auth/devstorage.read_only", + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring", + ] + } +} +`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10))