From ae5ad95436ada5747eaf9a8d4d13de61ac5a8929 Mon Sep 17 00:00:00 2001 From: Antonio Lobato Date: Tue, 26 Sep 2017 21:40:14 -0700 Subject: [PATCH 1/8] initial work on adding IAP support for backend services --- google/resource_compute_backend_service.go | 34 +++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/google/resource_compute_backend_service.go b/google/resource_compute_backend_service.go index 422ca00cdce..c983107c2e5 100644 --- a/google/resource_compute_backend_service.go +++ b/google/resource_compute_backend_service.go @@ -37,6 +37,30 @@ func resourceComputeBackendService() *schema.Resource { MaxItems: 1, }, + "iap": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "oauth2_client_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "", + }, + "oauth2_client_secret": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "", + }, + }, + }, + Optional: true, + }, + "backend": &schema.Schema{ Type: schema.TypeSet, Elem: &schema.Resource{ @@ -309,7 +333,6 @@ func flattenBackends(backends []*compute.Backend) []map[string]interface{} { data["max_rate"] = b.MaxRate data["max_rate_per_instance"] = b.MaxRatePerInstance data["max_utilization"] = b.MaxUtilization - result = append(result, data) } @@ -328,6 +351,15 @@ func expandBackendService(d *schema.ResourceData) compute.BackendService { HealthChecks: healthChecks, } + if v, ok := d.GetOk("iap"); ok { + iap := v.(*schema.Set).List()[0].(map[string]interface{}) + service.Iap = &compute.BackendServiceIAP{ + Enabled: iap["enabled"].(bool), + Oauth2ClientId: iap["oauth2_client_id"].(string), + Oauth2ClientSecret: iap["oauth2_client_secret"].(string), + } + } + if v, ok := d.GetOk("backend"); ok { service.Backends = expandBackends(v.(*schema.Set).List()) } From 79df89575fe37ea220b30ce4901c09791bc1187d Mon Sep 17 00:00:00 2001 From: Antonio Lobato Date: Tue, 26 Sep 2017 21:51:19 -0700 Subject: [PATCH 2/8] readback of IAP --- google/resource_compute_backend_service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/google/resource_compute_backend_service.go b/google/resource_compute_backend_service.go index c983107c2e5..1851cce5200 100644 --- a/google/resource_compute_backend_service.go +++ b/google/resource_compute_backend_service.go @@ -227,6 +227,7 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) d.Set("self_link", service.SelfLink) d.Set("backend", flattenBackends(service.Backends)) d.Set("connection_draining_timeout_sec", service.ConnectionDraining.DrainingTimeoutSec) + d.Set("iap", service.Iap) d.Set("health_checks", service.HealthChecks) From 7b02cdbbbc09a1e75d78365f13333c9a28e298a8 Mon Sep 17 00:00:00 2001 From: Antonio Lobato Date: Tue, 26 Sep 2017 22:31:03 -0700 Subject: [PATCH 3/8] flatten IAP + static set id --- google/resource_compute_backend_service.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/google/resource_compute_backend_service.go b/google/resource_compute_backend_service.go index 1851cce5200..3edaf6a24a0 100644 --- a/google/resource_compute_backend_service.go +++ b/google/resource_compute_backend_service.go @@ -59,6 +59,8 @@ func resourceComputeBackendService() *schema.Resource { }, }, Optional: true, + MaxItems: 1, + Set: func(i interface{}) int { return 0 }, }, "backend": &schema.Schema{ @@ -227,7 +229,7 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) d.Set("self_link", service.SelfLink) d.Set("backend", flattenBackends(service.Backends)) d.Set("connection_draining_timeout_sec", service.ConnectionDraining.DrainingTimeoutSec) - d.Set("iap", service.Iap) + d.Set("iap", flattenIap(service.Iap)) d.Set("health_checks", service.HealthChecks) @@ -321,6 +323,17 @@ func expandBackends(configured []interface{}) []*compute.Backend { return backends } +func flattenIap(iap *compute.BackendServiceIAP) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + iapMap := map[string]interface{}{ + "enabled": iap.Enabled, + "oauth2_client_id": iap.Oauth2ClientId, + "oauth2_client_secret": iap.Oauth2ClientSecret, + } + result = append(result, iapMap) + return result +} + func flattenBackends(backends []*compute.Backend) []map[string]interface{} { result := make([]map[string]interface{}, 0, len(backends)) From b67c08e3d53a8fe9a14cdeee288b3e692aa4c987 Mon Sep 17 00:00:00 2001 From: Antonio Lobato Date: Tue, 26 Sep 2017 22:40:50 -0700 Subject: [PATCH 4/8] expandIap function --- google/resource_compute_backend_service.go | 39 ++++++++++++---------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/google/resource_compute_backend_service.go b/google/resource_compute_backend_service.go index 3edaf6a24a0..3a5ffebd5e7 100644 --- a/google/resource_compute_backend_service.go +++ b/google/resource_compute_backend_service.go @@ -288,6 +288,27 @@ func resourceComputeBackendServiceDelete(d *schema.ResourceData, meta interface{ return nil } +func expandIap(configured []interface{}) *compute.BackendServiceIAP { + data := configured[0].(map[string]interface{}) + iap := &compute.BackendServiceIAP{ + Enabled: data["enabled"].(bool), + Oauth2ClientId: data["oauth2_client_id"].(string), + Oauth2ClientSecret: data["oauth2_client_secret"].(string), + } + return iap +} + +func flattenIap(iap *compute.BackendServiceIAP) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + iapMap := map[string]interface{}{ + "enabled": iap.Enabled, + "oauth2_client_id": iap.Oauth2ClientId, + "oauth2_client_secret": iap.Oauth2ClientSecret, + } + result = append(result, iapMap) + return result +} + func expandBackends(configured []interface{}) []*compute.Backend { backends := make([]*compute.Backend, 0, len(configured)) @@ -323,17 +344,6 @@ func expandBackends(configured []interface{}) []*compute.Backend { return backends } -func flattenIap(iap *compute.BackendServiceIAP) []map[string]interface{} { - result := make([]map[string]interface{}, 0, 1) - iapMap := map[string]interface{}{ - "enabled": iap.Enabled, - "oauth2_client_id": iap.Oauth2ClientId, - "oauth2_client_secret": iap.Oauth2ClientSecret, - } - result = append(result, iapMap) - return result -} - func flattenBackends(backends []*compute.Backend) []map[string]interface{} { result := make([]map[string]interface{}, 0, len(backends)) @@ -366,12 +376,7 @@ func expandBackendService(d *schema.ResourceData) compute.BackendService { } if v, ok := d.GetOk("iap"); ok { - iap := v.(*schema.Set).List()[0].(map[string]interface{}) - service.Iap = &compute.BackendServiceIAP{ - Enabled: iap["enabled"].(bool), - Oauth2ClientId: iap["oauth2_client_id"].(string), - Oauth2ClientSecret: iap["oauth2_client_secret"].(string), - } + service.Iap = expandIap(v.(*schema.Set).List()) } if v, ok := d.GetOk("backend"); ok { From e865ea23c43010b52aecb5678df50aa965db2d95 Mon Sep 17 00:00:00 2001 From: Antonio Lobato Date: Wed, 27 Sep 2017 22:07:53 -0700 Subject: [PATCH 5/8] removed enabled flag/state rework Removed the enabled flag for IAP IAP is now enabled when the client id and secret are set IAP now correctly disables when IAP stanza is removed Client secret is now correctly hashed against the secret hash stored on the server --- google/resource_compute_backend_service.go | 49 +++++++++++++--------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/google/resource_compute_backend_service.go b/google/resource_compute_backend_service.go index 3a5ffebd5e7..c2c9b0aebee 100644 --- a/google/resource_compute_backend_service.go +++ b/google/resource_compute_backend_service.go @@ -2,6 +2,7 @@ package google import ( "bytes" + "crypto/sha256" "fmt" "log" @@ -38,33 +39,34 @@ func resourceComputeBackendService() *schema.Resource { }, "iap": &schema.Schema{ - Type: schema.TypeSet, + Type: schema.TypeList, + Optional: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "enabled": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: false, - }, "oauth2_client_id": &schema.Schema{ Type: schema.TypeString, - Optional: true, - Default: "", + Required: true, }, "oauth2_client_secret": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "", + Type: schema.TypeString, + Required: true, + Sensitive: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + if old == fmt.Sprintf("%x", sha256.Sum256([]byte(new))) { + return true + } + return false + }, }, }, }, - Optional: true, - MaxItems: 1, - Set: func(i interface{}) int { return 0 }, }, "backend": &schema.Schema{ - Type: schema.TypeSet, + Type: schema.TypeSet, + Optional: true, + Set: resourceGoogleComputeBackendServiceBackendHash, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "group": &schema.Schema{ @@ -100,8 +102,6 @@ func resourceComputeBackendService() *schema.Resource { }, }, }, - Optional: true, - Set: resourceGoogleComputeBackendServiceBackendHash, }, "description": &schema.Schema{ @@ -291,19 +291,25 @@ func resourceComputeBackendServiceDelete(d *schema.ResourceData, meta interface{ func expandIap(configured []interface{}) *compute.BackendServiceIAP { data := configured[0].(map[string]interface{}) iap := &compute.BackendServiceIAP{ - Enabled: data["enabled"].(bool), + Enabled: true, Oauth2ClientId: data["oauth2_client_id"].(string), Oauth2ClientSecret: data["oauth2_client_secret"].(string), + ForceSendFields: []string{"Enabled", "Oauth2ClientId", "Oauth2ClientSecret"}, } + return iap } func flattenIap(iap *compute.BackendServiceIAP) []map[string]interface{} { + if iap == nil { + return make([]map[string]interface{}, 1, 1) + } + result := make([]map[string]interface{}, 0, 1) iapMap := map[string]interface{}{ "enabled": iap.Enabled, "oauth2_client_id": iap.Oauth2ClientId, - "oauth2_client_secret": iap.Oauth2ClientSecret, + "oauth2_client_secret": iap.Oauth2ClientSecretSha256, } result = append(result, iapMap) return result @@ -373,10 +379,13 @@ func expandBackendService(d *schema.ResourceData) compute.BackendService { service := compute.BackendService{ Name: d.Get("name").(string), HealthChecks: healthChecks, + Iap: &compute.BackendServiceIAP{ + ForceSendFields: []string{"Enabled", "Oauth2ClientId", "Oauth2ClientSecret"}, + }, } if v, ok := d.GetOk("iap"); ok { - service.Iap = expandIap(v.(*schema.Set).List()) + service.Iap = expandIap(v.([]interface{})) } if v, ok := d.GetOk("backend"); ok { From 285b10bec3ae1a5703dcc9af46ff2906bb3f766d Mon Sep 17 00:00:00 2001 From: Antonio Lobato Date: Thu, 28 Sep 2017 18:38:04 -0700 Subject: [PATCH 6/8] Tests for IAP --- .../resource_compute_backend_service_test.go | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/google/resource_compute_backend_service_test.go b/google/resource_compute_backend_service_test.go index 18094f6e596..797de03f726 100644 --- a/google/resource_compute_backend_service_test.go +++ b/google/resource_compute_backend_service_test.go @@ -114,6 +114,47 @@ func TestAccComputeBackendService_withBackendAndUpdate(t *testing.T) { } } +func TestAccComputeBackendService_withBackendAndIAP(t *testing.T) { + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + igName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + itName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withBackendAndIAP( + serviceName, igName, itName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExistsWithIAP( + "google_compute_backend_service.lipsum", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeBackendService_withBackend( + serviceName, igName, itName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.lipsum", &svc), + ), + }, + }, + }) + + if svc.TimeoutSec != 10 { + t.Errorf("Expected TimeoutSec == 10, got %d", svc.TimeoutSec) + } + if svc.Protocol != "HTTP" { + t.Errorf("Expected Protocol to be HTTP, got %q", svc.Protocol) + } + if len(svc.Backends) != 1 { + t.Errorf("Expected 1 backend, got %d", len(svc.Backends)) + } +} + func TestAccComputeBackendService_updatePreservesOptionalParameters(t *testing.T) { serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) @@ -273,6 +314,38 @@ func testAccCheckComputeBackendServiceExists(n string, svc *compute.BackendServi } } +func testAccCheckComputeBackendServiceExistsWithIAP(n string, svc *compute.BackendService) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + found, err := config.clientCompute.BackendServices.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Backend service %s not found", rs.Primary.ID) + } + + if found.Iap == nil || found.Iap.Enabled == false { + return fmt.Errorf("IAP not found or not enabled.") + } + + *svc = *found + + return nil + } +} func TestAccComputeBackendService_withCDNEnabled(t *testing.T) { serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) @@ -438,6 +511,60 @@ resource "google_compute_http_health_check" "default" { `, serviceName, timeout, igName, itName, checkName) } +func testAccComputeBackendService_withBackendAndIAP( + serviceName, igName, itName, checkName string, timeout int64) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "lipsum" { + name = "%s" + description = "Hello World 1234" + port_name = "http" + protocol = "HTTP" + timeout_sec = %v + + backend { + group = "${google_compute_instance_group_manager.foobar.instance_group}" + } + + iap { + oauth2_client_id = "test" + oauth2_client_secret = "test" + } + + health_checks = ["${google_compute_http_health_check.default.self_link}"] +} + +resource "google_compute_instance_group_manager" "foobar" { + name = "%s" + instance_template = "${google_compute_instance_template.foobar.self_link}" + base_instance_name = "foobar" + zone = "us-central1-f" + target_size = 1 +} + +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + + network_interface { + network = "default" + } + + disk { + source_image = "debian-8-jessie-v20160803" + auto_delete = true + boot = true + } +} + +resource "google_compute_http_health_check" "default" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, timeout, igName, itName, checkName) +} + func testAccComputeBackendService_withSessionAffinity(serviceName, checkName, description, affinityName string) string { return fmt.Sprintf(` resource "google_compute_backend_service" "foobar" { From 9de4d86f1738e9118477b6978f298997f1b55ae9 Mon Sep 17 00:00:00 2001 From: Antonio Lobato Date: Fri, 20 Oct 2017 19:31:35 -0700 Subject: [PATCH 7/8] added comments, fixed tabs. --- google/resource_compute_backend_service.go | 11 ++++++++--- google/resource_compute_backend_service_test.go | 8 ++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/google/resource_compute_backend_service.go b/google/resource_compute_backend_service.go index c2c9b0aebee..6ff4e0ab0ea 100644 --- a/google/resource_compute_backend_service.go +++ b/google/resource_compute_backend_service.go @@ -305,14 +305,12 @@ func flattenIap(iap *compute.BackendServiceIAP) []map[string]interface{} { return make([]map[string]interface{}, 1, 1) } - result := make([]map[string]interface{}, 0, 1) iapMap := map[string]interface{}{ "enabled": iap.Enabled, "oauth2_client_id": iap.Oauth2ClientId, "oauth2_client_secret": iap.Oauth2ClientSecretSha256, } - result = append(result, iapMap) - return result + return []map[string]interface{}{iapMap} } func expandBackends(configured []interface{}) []*compute.Backend { @@ -376,6 +374,13 @@ func expandBackendService(d *schema.ResourceData) compute.BackendService { healthChecks = append(healthChecks, v.(string)) } + // The IAP service is enabled and disabled by adding or removing + // the IAP configuration block (and providing the client id + // and secret). We are force sending the three required API fields + // to enable/disable IAP at all times here, and relying on Golang's + // type defaults to enable or disable IAP in the existance or absense + // of the block, instead of checking if the block exists, zeroing out + // fields, etc. service := compute.BackendService{ Name: d.Get("name").(string), HealthChecks: healthChecks, diff --git a/google/resource_compute_backend_service_test.go b/google/resource_compute_backend_service_test.go index 797de03f726..94c56fef79f 100644 --- a/google/resource_compute_backend_service_test.go +++ b/google/resource_compute_backend_service_test.go @@ -525,10 +525,10 @@ resource "google_compute_backend_service" "lipsum" { group = "${google_compute_instance_group_manager.foobar.instance_group}" } - iap { - oauth2_client_id = "test" - oauth2_client_secret = "test" - } + iap { + oauth2_client_id = "test" + oauth2_client_secret = "test" + } health_checks = ["${google_compute_http_health_check.default.self_link}"] } From 95bb1c699f77962b510006d94e10ec20e6631e8b Mon Sep 17 00:00:00 2001 From: Antonio Lobato Date: Mon, 30 Oct 2017 22:26:04 -0700 Subject: [PATCH 8/8] testing for IAP disabled --- .../resource_compute_backend_service_test.go | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/google/resource_compute_backend_service_test.go b/google/resource_compute_backend_service_test.go index 9a21d07b93b..663442c708e 100644 --- a/google/resource_compute_backend_service_test.go +++ b/google/resource_compute_backend_service_test.go @@ -143,7 +143,7 @@ func TestAccComputeBackendService_withBackendAndIAP(t *testing.T) { Config: testAccComputeBackendService_withBackend( serviceName, igName, itName, checkName, 10), Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( + testAccCheckComputeBackendServiceExistsWithoutIAP( "google_compute_backend_service.lipsum", &svc), ), }, @@ -159,6 +159,7 @@ func TestAccComputeBackendService_withBackendAndIAP(t *testing.T) { if len(svc.Backends) != 1 { t.Errorf("Expected 1 backend, got %d", len(svc.Backends)) } + } func TestAccComputeBackendService_updatePreservesOptionalParameters(t *testing.T) { @@ -360,6 +361,39 @@ func testAccCheckComputeBackendServiceExistsWithIAP(n string, svc *compute.Backe return nil } } + +func testAccCheckComputeBackendServiceExistsWithoutIAP(n string, svc *compute.BackendService) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + found, err := config.clientCompute.BackendServices.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Backend service %s not found", rs.Primary.ID) + } + + if found.Iap != nil && found.Iap.Enabled == true { + return fmt.Errorf("IAP enabled when it should be disabled") + } + + *svc = *found + + return nil + } +} func TestAccComputeBackendService_withCDNEnabled(t *testing.T) { t.Parallel()