From d2093ddd5fc4b14909d180d600e28f07ab050c23 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Mon, 21 Oct 2019 11:01:09 -0700 Subject: [PATCH 01/37] Add project to inspec terraform config where needed for CI (#2459) Merged PR #2459. --- build/inspec | 2 +- templates/inspec/tests/integration/build/gcp-mm.tf | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/inspec b/build/inspec index fcc076d77165..179cd115de4d 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit fcc076d77165d672797a5063a4501e4fb733118d +Subproject commit 179cd115de4dc78342a1b10a6d278e0e4fd88d65 diff --git a/templates/inspec/tests/integration/build/gcp-mm.tf b/templates/inspec/tests/integration/build/gcp-mm.tf index 73c6e6791755..d03e10dce3b2 100644 --- a/templates/inspec/tests/integration/build/gcp-mm.tf +++ b/templates/inspec/tests/integration/build/gcp-mm.tf @@ -669,6 +669,7 @@ resource "google_ml_engine_model" "inspec-gcp-model" { } resource "google_compute_firewall" "dataproc" { + project = var.gcp_project_id name = "dataproc-firewall" network = "${google_compute_network.dataproc.name}" @@ -688,7 +689,8 @@ resource "google_compute_firewall" "dataproc" { } resource "google_compute_network" "dataproc" { - name = "dataproc-network" + project = var.gcp_project_id + name = "dataproc-network" } resource "google_dataproc_cluster" "mycluster" { From 4813be6a42030262cf69473a099d1c2d006902ff Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Mon, 21 Oct 2019 11:24:36 -0700 Subject: [PATCH 02/37] Organization policies can insert entries into this field, causing a permadiff. (#2496) Merged PR #2496. --- build/terraform | 2 +- build/terraform-beta | 2 +- products/pubsub/terraform.yaml | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index 1ed9f43abe5a..ac21aa97aa42 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 1ed9f43abe5a1118143bc460594341338ffc598d +Subproject commit ac21aa97aa42ef58a31246f9ed7dab88320a5fe7 diff --git a/build/terraform-beta b/build/terraform-beta index 0574383c5b92..5f6c9236ddfd 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 0574383c5b921b76b03bbb3a0791763f8e3155d6 +Subproject commit 5f6c9236ddfde90ef0d4b270998b3cde646a2932 diff --git a/products/pubsub/terraform.yaml b/products/pubsub/terraform.yaml index 771cf77e7323..57dec762fbe9 100644 --- a/products/pubsub/terraform.yaml +++ b/products/pubsub/terraform.yaml @@ -44,6 +44,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides diff_suppress_func: 'compareSelfLinkOrResourceName' custom_expand: templates/terraform/custom_expand/resource_from_self_link.go.erb custom_flatten: templates/terraform/custom_flatten/name_from_self_link.erb + messageStoragePolicy: !ruby/object:Overrides::Terraform::PropertyOverride + default_from_api: true custom_code: !ruby/object:Provider::Terraform::CustomCode encoder: templates/terraform/encoders/no_send_name.go.erb update_encoder: templates/terraform/update_encoder/pubsub_topic.erb From 2e6058d04420db2dcfbe725de259947e1801db59 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 21 Oct 2019 15:19:07 -0700 Subject: [PATCH 03/37] Support setting 'location' in google_sql_database_instance backup_configuration (#2498) Merged PR #2498. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../terraform/resources/resource_sql_database_instance.go | 6 ++++++ .../tests/resource_sql_database_instance_test.go.erb | 3 ++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/build/terraform b/build/terraform index ac21aa97aa42..dd8f51b0460d 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit ac21aa97aa42ef58a31246f9ed7dab88320a5fe7 +Subproject commit dd8f51b0460d5767c9241d8baf6c7e65c7a5d783 diff --git a/build/terraform-beta b/build/terraform-beta index 5f6c9236ddfd..c115c7ef5319 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 5f6c9236ddfde90ef0d4b270998b3cde646a2932 +Subproject commit c115c7ef53198db5e043924d753ffe06d9a787d2 diff --git a/third_party/terraform/resources/resource_sql_database_instance.go b/third_party/terraform/resources/resource_sql_database_instance.go index e05bcd2d61ac..dbf55492eecd 100644 --- a/third_party/terraform/resources/resource_sql_database_instance.go +++ b/third_party/terraform/resources/resource_sql_database_instance.go @@ -118,6 +118,10 @@ func resourceSqlDatabaseInstance() *schema.Resource { // start_time is randomly assigned if not set Computed: true, }, + "location": { + Type: schema.TypeString, + Optional: true, + }, }, }, }, @@ -690,6 +694,7 @@ func expandBackupConfiguration(configured []interface{}) *sqladmin.BackupConfigu BinaryLogEnabled: _backupConfiguration["binary_log_enabled"].(bool), Enabled: _backupConfiguration["enabled"].(bool), StartTime: _backupConfiguration["start_time"].(string), + Location: _backupConfiguration["location"].(string), } } @@ -898,6 +903,7 @@ func flattenBackupConfiguration(backupConfiguration *sqladmin.BackupConfiguratio "binary_log_enabled": backupConfiguration.BinaryLogEnabled, "enabled": backupConfiguration.Enabled, "start_time": backupConfiguration.StartTime, + "location": backupConfiguration.Location, } return []map[string]interface{}{data} diff --git a/third_party/terraform/tests/resource_sql_database_instance_test.go.erb b/third_party/terraform/tests/resource_sql_database_instance_test.go.erb index a7810e1d22a3..7240fd3f0198 100644 --- a/third_party/terraform/tests/resource_sql_database_instance_test.go.erb +++ b/third_party/terraform/tests/resource_sql_database_instance_test.go.erb @@ -911,7 +911,8 @@ resource "google_sql_database_instance" "instance" { availability_type = "REGIONAL" backup_configuration { - enabled = true + enabled = true + location = "us" } } } From db73da4ba01b68c1b401bba78f0cdf3deca61c58 Mon Sep 17 00:00:00 2001 From: megan07 Date: Tue, 22 Oct 2019 16:14:38 +0200 Subject: [PATCH 04/37] deprecate kubernetes_dashboard for 2.X (#2447) Merged PR #2447. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../terraform/resources/resource_container_cluster.go.erb | 7 ++++--- .../website/docs/r/container_cluster.html.markdown | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build/terraform b/build/terraform index dd8f51b0460d..2961311885c3 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit dd8f51b0460d5767c9241d8baf6c7e65c7a5d783 +Subproject commit 2961311885c3a0f977683c116ca6d417a51ee742 diff --git a/build/terraform-beta b/build/terraform-beta index c115c7ef5319..43ac96a67263 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit c115c7ef53198db5e043924d753ffe06d9a787d2 +Subproject commit 43ac96a67263dd4175376b9eb7c62d226a24ad14 diff --git a/third_party/terraform/resources/resource_container_cluster.go.erb b/third_party/terraform/resources/resource_container_cluster.go.erb index 7a426b8e7250..3af8db7155a4 100644 --- a/third_party/terraform/resources/resource_container_cluster.go.erb +++ b/third_party/terraform/resources/resource_container_cluster.go.erb @@ -179,9 +179,10 @@ func resourceContainerCluster() *schema.Resource { }, }, "kubernetes_dashboard": { - Type: schema.TypeList, - Optional: true, - Computed: true, + Type: schema.TypeList, + Optional: true, + Computed: true, + Deprecated: "The Kubernetes Dashboard addon is deprecated for clusters on GKE.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ diff --git a/third_party/terraform/website/docs/r/container_cluster.html.markdown b/third_party/terraform/website/docs/r/container_cluster.html.markdown index 9f7b7ed14810..c95311f9eb21 100644 --- a/third_party/terraform/website/docs/r/container_cluster.html.markdown +++ b/third_party/terraform/website/docs/r/container_cluster.html.markdown @@ -332,7 +332,7 @@ The `addons_config` block supports: controller addon, which makes it easy to set up HTTP load balancers for services in a cluster. It is enabled by default; set `disabled = true` to disable. -* `kubernetes_dashboard` - (Optional) The status of the Kubernetes Dashboard +* `kubernetes_dashboard` - (Optional, Deprecated) The status of the Kubernetes Dashboard add-on, which controls whether the Kubernetes Dashboard is enabled for this cluster. It is disabled by default; set `disabled = false` to enable. From 49d3c3f68b7577bac1c2c75cebee2e0fcd20819b Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Tue, 22 Oct 2019 08:56:41 -0700 Subject: [PATCH 05/37] Bump GKE node pool GPU disk to an appropriate size. (#2505) Merged PR #2505. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../terraform/tests/resource_container_node_pool_test.go.erb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/terraform b/build/terraform index 2961311885c3..6a45d4c38beb 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 2961311885c3a0f977683c116ca6d417a51ee742 +Subproject commit 6a45d4c38beb6883e844178353cf4be71943d97d diff --git a/build/terraform-beta b/build/terraform-beta index 43ac96a67263..c742433d97a2 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 43ac96a67263dd4175376b9eb7c62d226a24ad14 +Subproject commit c742433d97a27b13b65a53f055fb0390e3c05f13 diff --git a/third_party/terraform/tests/resource_container_node_pool_test.go.erb b/third_party/terraform/tests/resource_container_node_pool_test.go.erb index 802e641a53ad..b82b5e88b2e9 100644 --- a/third_party/terraform/tests/resource_container_node_pool_test.go.erb +++ b/third_party/terraform/tests/resource_container_node_pool_test.go.erb @@ -1238,7 +1238,7 @@ resource "google_container_node_pool" "np_with_gpu" { node_config { machine_type = "n1-standard-1" - disk_size_gb = 10 + disk_size_gb = 32 oauth_scopes = [ "https://www.googleapis.com/auth/devstorage.read_only", From aa7ede44e0c59a5f6e9e1582b7aebf24a29c0bfd Mon Sep 17 00:00:00 2001 From: megan07 Date: Tue, 22 Oct 2019 11:46:44 -0500 Subject: [PATCH 06/37] add ip_configuration to dataflow_job (#2504) Merged PR #2504. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resources/resource_dataflow_job.go | 8 ++++ .../tests/resource_dataflow_job_test.go | 45 +++++++++++++++++++ .../website/docs/r/dataflow_job.html.markdown | 1 + 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index 6a45d4c38beb..e053a9c5813c 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 6a45d4c38beb6883e844178353cf4be71943d97d +Subproject commit e053a9c5813c86e3f49a2b1f66e2992da96deedf diff --git a/build/terraform-beta b/build/terraform-beta index c742433d97a2..5e77cae1b3f8 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit c742433d97a27b13b65a53f055fb0390e3c05f13 +Subproject commit 5e77cae1b3f87c2f3f3cc67b592a446abd83bc0b diff --git a/third_party/terraform/resources/resource_dataflow_job.go b/third_party/terraform/resources/resource_dataflow_job.go index ff4996b34194..1cf0a9eaee89 100644 --- a/third_party/terraform/resources/resource_dataflow_job.go +++ b/third_party/terraform/resources/resource_dataflow_job.go @@ -122,6 +122,13 @@ func resourceDataflowJob() *schema.Resource { Optional: true, ForceNew: true, }, + + "ip_configuration": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"WORKER_IP_PUBLIC", "WORKER_IP_PRIVATE", ""}, false), + }, }, } } @@ -154,6 +161,7 @@ func resourceDataflowJobCreate(d *schema.ResourceData, meta interface{}) error { Subnetwork: d.Get("subnetwork").(string), TempLocation: d.Get("temp_gcs_location").(string), MachineType: d.Get("machine_type").(string), + IpConfiguration: d.Get("ip_configuration").(string), AdditionalUserLabels: labels, Zone: zone, } diff --git a/third_party/terraform/tests/resource_dataflow_job_test.go b/third_party/terraform/tests/resource_dataflow_job_test.go index 37ffc05f1809..ae38eeb8d63e 100644 --- a/third_party/terraform/tests/resource_dataflow_job_test.go +++ b/third_party/terraform/tests/resource_dataflow_job_test.go @@ -131,6 +131,24 @@ func TestAccDataflowJobCreateWithLabels(t *testing.T) { }) } +func TestAccDataflowJobCreateWithIpConfig(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDataflowJobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDataflowJobWithIpConfig, + Check: resource.ComposeTestCheckFunc( + testAccDataflowJobExists( + "google_dataflow_job.big_data"), + ), + }, + }, + }) +} + func testAccCheckDataflowJobDestroy(s *terraform.State) error { for _, rs := range s.RootModule().Resources { if rs.Type != "google_dataflow_job" { @@ -519,6 +537,33 @@ resource "google_dataflow_job" "big_data" { on_delete = "cancel" }`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10), getTestProjectFromEnv()) +var testAccDataflowJobWithIpConfig = fmt.Sprintf(` +resource "google_storage_bucket" "temp" { + name = "dfjob-test-%s-temp" + + force_destroy = true +} + +resource "google_dataflow_job" "big_data" { + name = "dfjob-test-%s" + + template_gcs_path = "gs://dataflow-templates/wordcount/template_file" + temp_gcs_location = "${google_storage_bucket.temp.url}" + machine_type = "n1-standard-2" + + parameters = { + inputFile = "gs://dataflow-samples/shakespeare/kinglear.txt" + output = "${google_storage_bucket.temp.url}/output" + } + + ip_configuration = "WORKER_IP_PRIVATE" + + zone = "us-central1-f" + project = "%s" + + on_delete = "cancel" +}`, acctest.RandString(10), acctest.RandString(10), getTestProjectFromEnv()) + func testAccDataflowJobWithLabels(key string) string { return fmt.Sprintf(` resource "google_storage_bucket" "temp" { diff --git a/third_party/terraform/website/docs/r/dataflow_job.html.markdown b/third_party/terraform/website/docs/r/dataflow_job.html.markdown index 9cc2e1e102f2..8b3901b4cf85 100644 --- a/third_party/terraform/website/docs/r/dataflow_job.html.markdown +++ b/third_party/terraform/website/docs/r/dataflow_job.html.markdown @@ -54,6 +54,7 @@ The following arguments are supported: * `network` - (Optional) The network to which VMs will be assigned. If it is not provided, "default" will be used. * `subnetwork` - (Optional) The subnetwork to which VMs will be assigned. Should be of the form "regions/REGION/subnetworks/SUBNETWORK". * `machine_type` - (Optional) The machine type to use for the job. +* `ip_configuration` - (Optional) The configuration for VM IPs. Options are `"WORKER_IP_PUBLIC"` or `"WORKER_IP_PUBLIC"`. ## Attributes Reference From 19a8a892c11558f04c0e6cf4b807aa3fc9151181 Mon Sep 17 00:00:00 2001 From: Cameron Thornton Date: Tue, 22 Oct 2019 10:06:27 -0700 Subject: [PATCH 07/37] Replace some unnecessary checks with import tests (#2500) Merged PR #2500. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../tests/resource_cloudiot_registry_test.go | 40 +-- .../resource_compute_autoscaler_test.go.erb | 119 +------ ...source_compute_backend_service_test.go.erb | 319 ++++-------------- .../tests/resource_compute_disk_test.go.erb | 161 +++------ ...esource_compute_global_address_test.go.erb | 62 ---- ...esource_compute_https_health_check_test.go | 83 +---- 8 files changed, 148 insertions(+), 640 deletions(-) diff --git a/build/terraform b/build/terraform index e053a9c5813c..215cca14609f 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit e053a9c5813c86e3f49a2b1f66e2992da96deedf +Subproject commit 215cca14609fdd8dcd86624b6d5344dfeeaf9b05 diff --git a/build/terraform-beta b/build/terraform-beta index 5e77cae1b3f8..8d85b10b3fa6 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 5e77cae1b3f87c2f3f3cc67b592a446abd83bc0b +Subproject commit 8d85b10b3fa61b0dd85f7ea2fadb54c5e80233b7 diff --git a/third_party/terraform/tests/resource_cloudiot_registry_test.go b/third_party/terraform/tests/resource_cloudiot_registry_test.go index f6bf7365b884..2468de141262 100644 --- a/third_party/terraform/tests/resource_cloudiot_registry_test.go +++ b/third_party/terraform/tests/resource_cloudiot_registry_test.go @@ -46,10 +46,6 @@ func TestAccCloudIoTRegistry_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCloudIoTRegistry_basic(registryName), - Check: resource.ComposeTestCheckFunc( - testAccCloudIoTRegistryExists( - "google_cloudiot_registry.foobar"), - ), }, { ResourceName: "google_cloudiot_registry.foobar", @@ -72,10 +68,6 @@ func TestAccCloudIoTRegistry_extended(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCloudIoTRegistry_extended(registryName), - Check: resource.ComposeTestCheckFunc( - testAccCloudIoTRegistryExists( - "google_cloudiot_registry.foobar"), - ), }, { ResourceName: "google_cloudiot_registry.foobar", @@ -98,14 +90,20 @@ func TestAccCloudIoTRegistry_update(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCloudIoTRegistry_basic(registryName), - Check: resource.ComposeTestCheckFunc( - testAccCloudIoTRegistryExists( - "google_cloudiot_registry.foobar"), - ), + }, + { + ResourceName: "google_cloudiot_registry.foobar", + ImportState: true, + ImportStateVerify: true, }, { Config: testAccCloudIoTRegistry_extended(registryName), }, + { + ResourceName: "google_cloudiot_registry.foobar", + ImportState: true, + ImportStateVerify: true, + }, { Config: testAccCloudIoTRegistry_basic(registryName), }, @@ -223,24 +221,6 @@ func testAccCheckCloudIoTRegistryDestroy(s *terraform.State) error { return nil } -func testAccCloudIoTRegistryExists(n string) 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) - _, err := config.clientCloudIoT.Projects.Locations.Registries.Get(rs.Primary.ID).Do() - if err != nil { - return fmt.Errorf("Registry does not exist") - } - return nil - } -} - func testAccCloudIoTRegistry_basic(registryName string) string { return fmt.Sprintf(` resource "google_cloudiot_registry" "foobar" { diff --git a/third_party/terraform/tests/resource_compute_autoscaler_test.go.erb b/third_party/terraform/tests/resource_compute_autoscaler_test.go.erb index b7c37b6b11e2..9b84b8158485 100644 --- a/third_party/terraform/tests/resource_compute_autoscaler_test.go.erb +++ b/third_party/terraform/tests/resource_compute_autoscaler_test.go.erb @@ -15,8 +15,6 @@ import ( func TestAccComputeAutoscaler_update(t *testing.T) { t.Parallel() - var ascaler compute.Autoscaler - var it_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) var tp_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) var igm_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) @@ -29,20 +27,21 @@ func TestAccComputeAutoscaler_update(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeAutoscaler_basic(it_name, tp_name, igm_name, autoscaler_name), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeAutoscalerExists( - "google_compute_autoscaler.foobar", &ascaler), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_autoscaler.foobar", + ImportState: true, + ImportStateVerify: true, }, resource.TestStep{ Config: testAccComputeAutoscaler_update(it_name, tp_name, igm_name, autoscaler_name), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeAutoscalerExists( - "google_compute_autoscaler.foobar", &ascaler), - testAccCheckComputeAutoscalerUpdated( - "google_compute_autoscaler.foobar", 10), - ), }, + resource.TestStep{ + ResourceName: "google_compute_autoscaler.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, }) } @@ -62,7 +61,6 @@ func TestAccComputeAutoscaler_multicondition(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeAutoscaler_multicondition(it_name, tp_name, igm_name, autoscaler_name), - Check: testAccCheckComputeAutoscalerMultifunction("google_compute_autoscaler.foobar"), }, resource.TestStep{ ResourceName: "google_compute_autoscaler.foobar", @@ -73,101 +71,6 @@ func TestAccComputeAutoscaler_multicondition(t *testing.T) { }) } -func testAccCheckComputeAutoscalerExists(n string, ascaler *compute.Autoscaler) 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) - - idParts := strings.Split(rs.Primary.ID, "/") - zone, name := idParts[0], idParts[1] - found, err := config.clientCompute.Autoscalers.Get( - config.Project, zone, name).Do() - if err != nil { - return err - } - - if found.Name != name { - return fmt.Errorf("Autoscaler not found") - } - - *ascaler = *found - - return nil - } -} - -func testAccCheckComputeAutoscalerMultifunction(n string) 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) - - idParts := strings.Split(rs.Primary.ID, "/") - zone, name := idParts[0], idParts[1] - found, err := config.clientCompute.Autoscalers.Get( - config.Project, zone, name).Do() - if err != nil { - return err - } - - if found.Name != name { - return fmt.Errorf("Autoscaler not found") - } - - if found.AutoscalingPolicy.CpuUtilization.UtilizationTarget == 0.5 && found.AutoscalingPolicy.LoadBalancingUtilization.UtilizationTarget == 0.5 { - return nil - } - return fmt.Errorf("Util target for CPU: %f, for LB: %f - should have been 0.5 for each.", - found.AutoscalingPolicy.CpuUtilization.UtilizationTarget, - found.AutoscalingPolicy.LoadBalancingUtilization.UtilizationTarget) - - } -} - -func testAccCheckComputeAutoscalerUpdated(n string, max int64) 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) - - idParts := strings.Split(rs.Primary.ID, "/") - zone, name := idParts[0], idParts[1] - ascaler, err := config.clientCompute.Autoscalers.Get( - config.Project, zone, name).Do() - if err != nil { - return err - } - - if ascaler.AutoscalingPolicy.MaxNumReplicas != max { - return fmt.Errorf("maximum replicas incorrect") - } - - return nil - } -} - func testAccComputeAutoscaler_scaffolding(it_name, tp_name, igm_name string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { diff --git a/third_party/terraform/tests/resource_compute_backend_service_test.go.erb b/third_party/terraform/tests/resource_compute_backend_service_test.go.erb index d28b241d1dfc..2dcf00869839 100644 --- a/third_party/terraform/tests/resource_compute_backend_service_test.go.erb +++ b/third_party/terraform/tests/resource_compute_backend_service_test.go.erb @@ -18,7 +18,6 @@ func TestAccComputeBackendService_basic(t *testing.T) { serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) extraCheckName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - var svc compute.BackendService resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -27,18 +26,15 @@ func TestAccComputeBackendService_basic(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeBackendService_basic(serviceName, checkName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, }, resource.TestStep{ Config: testAccComputeBackendService_basicModified( serviceName, checkName, extraCheckName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), }, resource.TestStep{ ResourceName: "google_compute_backend_service.foobar", @@ -56,7 +52,6 @@ func TestAccComputeBackendService_withBackend(t *testing.T) { 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, @@ -65,18 +60,15 @@ func TestAccComputeBackendService_withBackend(t *testing.T) { resource.TestStep{ Config: testAccComputeBackendService_withBackend( serviceName, igName, itName, checkName, 10), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.lipsum", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.lipsum", + ImportState: true, + ImportStateVerify: true, }, resource.TestStep{ Config: testAccComputeBackendService_withBackend( serviceName, igName, itName, checkName, 20), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.lipsum", &svc), - ), }, resource.TestStep{ ResourceName: "google_compute_backend_service.lipsum", @@ -85,16 +77,6 @@ func TestAccComputeBackendService_withBackend(t *testing.T) { }, }, }) - - if svc.TimeoutSec != 20 { - t.Errorf("Expected TimeoutSec == 20, 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_withBackendAndIAP(t *testing.T) { @@ -102,7 +84,6 @@ func TestAccComputeBackendService_withBackendAndIAP(t *testing.T) { 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, @@ -111,10 +92,6 @@ func TestAccComputeBackendService_withBackendAndIAP(t *testing.T) { resource.TestStep{ Config: testAccComputeBackendService_withBackendAndIAP( serviceName, igName, itName, checkName, 10), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExistsWithIAP("google_compute_backend_service.lipsum", &svc), - resource.TestCheckResourceAttr("google_compute_backend_service.lipsum", "iap.0.oauth2_client_secret", "test"), - ), }, resource.TestStep{ ResourceName: "google_compute_backend_service.lipsum", @@ -125,24 +102,14 @@ func TestAccComputeBackendService_withBackendAndIAP(t *testing.T) { resource.TestStep{ Config: testAccComputeBackendService_withBackend( serviceName, igName, itName, checkName, 10), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExistsWithoutIAP( - "google_compute_backend_service.lipsum", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.lipsum", + ImportState: true, + ImportStateVerify: true, }, }, }) - - 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) { @@ -150,7 +117,6 @@ func TestAccComputeBackendService_updatePreservesOptionalParameters(t *testing.T serviceName := 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) }, @@ -160,25 +126,23 @@ func TestAccComputeBackendService_updatePreservesOptionalParameters(t *testing.T resource.TestStep{ Config: testAccComputeBackendService_withSessionAffinity( serviceName, checkName, "initial-description", "GENERATED_COOKIE"), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, }, resource.TestStep{ Config: testAccComputeBackendService_withSessionAffinity( serviceName, checkName, "updated-description", "GENERATED_COOKIE"), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, }, }, }) - - if svc.SessionAffinity != "GENERATED_COOKIE" { - t.Errorf("Expected SessionAffinity == \"GENERATED_COOKIE\", got %s", svc.SessionAffinity) - } } func TestAccComputeBackendService_withConnectionDraining(t *testing.T) { @@ -186,7 +150,6 @@ func TestAccComputeBackendService_withConnectionDraining(t *testing.T) { serviceName := 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) }, @@ -195,10 +158,6 @@ func TestAccComputeBackendService_withConnectionDraining(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeBackendService_withConnectionDraining(serviceName, checkName, 10), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), }, resource.TestStep{ ResourceName: "google_compute_backend_service.foobar", @@ -207,10 +166,6 @@ func TestAccComputeBackendService_withConnectionDraining(t *testing.T) { }, }, }) - - if svc.ConnectionDraining.DrainingTimeoutSec != 10 { - t.Errorf("Expected ConnectionDraining.DrainingTimeoutSec == 10, got %d", svc.ConnectionDraining.DrainingTimeoutSec) - } } func TestAccComputeBackendService_withConnectionDrainingAndUpdate(t *testing.T) { @@ -218,7 +173,6 @@ func TestAccComputeBackendService_withConnectionDrainingAndUpdate(t *testing.T) serviceName := 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) }, @@ -227,24 +181,22 @@ func TestAccComputeBackendService_withConnectionDrainingAndUpdate(t *testing.T) Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeBackendService_withConnectionDraining(serviceName, checkName, 10), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, }, resource.TestStep{ Config: testAccComputeBackendService_basic(serviceName, checkName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, }, }, }) - - if svc.ConnectionDraining.DrainingTimeoutSec != 300 { - t.Errorf("Expected ConnectionDraining.DrainingTimeoutSec == 300, got %d", svc.ConnectionDraining.DrainingTimeoutSec) - } } func TestAccComputeBackendService_withHttpsHealthCheck(t *testing.T) { @@ -252,7 +204,6 @@ func TestAccComputeBackendService_withHttpsHealthCheck(t *testing.T) { serviceName := 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) }, @@ -261,10 +212,6 @@ func TestAccComputeBackendService_withHttpsHealthCheck(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeBackendService_withHttpsHealthCheck(serviceName, checkName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), }, resource.TestStep{ ResourceName: "google_compute_backend_service.foobar", @@ -280,7 +227,6 @@ func TestAccComputeBackendService_withCdnPolicy(t *testing.T) { serviceName := 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) }, @@ -289,10 +235,6 @@ func TestAccComputeBackendService_withCdnPolicy(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeBackendService_withCdnPolicy(serviceName, checkName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), }, resource.TestStep{ ResourceName: "google_compute_backend_service.foobar", @@ -309,7 +251,6 @@ func TestAccComputeBackendService_withSecurityPolicy(t *testing.T) { serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) polName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - var svc compute.BackendService resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -318,11 +259,6 @@ func TestAccComputeBackendService_withSecurityPolicy(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccComputeBackendService_withSecurityPolicy(serviceName, checkName, polName, "${google_compute_security_policy.policy.self_link}"), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - resource.TestMatchResourceAttr("google_compute_backend_service.foobar", "security_policy", regexp.MustCompile(polName)), - ), }, { ResourceName: "google_compute_backend_service.foobar", @@ -341,106 +277,11 @@ func TestAccComputeBackendService_withSecurityPolicy(t *testing.T) { }) } -func testAccCheckComputeBackendServiceExists(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) - } - - *svc = *found - - return nil - } -} - -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. Saw %v", found.Iap) - } - - *svc = *found - - 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() serviceName := 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) }, @@ -450,17 +291,14 @@ func TestAccComputeBackendService_withCDNEnabled(t *testing.T) { resource.TestStep{ Config: testAccComputeBackendService_withCDNEnabled( serviceName, checkName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, }, }, }) - - if svc.EnableCDN != true { - t.Errorf("Expected EnableCDN == true, got %t", svc.EnableCDN) - } } func TestAccComputeBackendService_withSessionAffinity(t *testing.T) { @@ -468,7 +306,6 @@ func TestAccComputeBackendService_withSessionAffinity(t *testing.T) { serviceName := 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) }, @@ -478,25 +315,23 @@ func TestAccComputeBackendService_withSessionAffinity(t *testing.T) { resource.TestStep{ Config: testAccComputeBackendService_withSessionAffinity( serviceName, checkName, "description", "CLIENT_IP"), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, }, resource.TestStep{ Config: testAccComputeBackendService_withSessionAffinity( serviceName, checkName, "description", "GENERATED_COOKIE"), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, }, }, }) - - if svc.SessionAffinity != "GENERATED_COOKIE" { - t.Errorf("Expected SessionAffinity == \"GENERATED_COOKIE\", got %s", svc.SessionAffinity) - } } func TestAccComputeBackendService_withAffinityCookieTtlSec(t *testing.T) { @@ -504,7 +339,6 @@ func TestAccComputeBackendService_withAffinityCookieTtlSec(t *testing.T) { serviceName := 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) }, @@ -514,21 +348,14 @@ func TestAccComputeBackendService_withAffinityCookieTtlSec(t *testing.T) { resource.TestStep{ Config: testAccComputeBackendService_withAffinityCookieTtlSec( serviceName, checkName, "description", "GENERATED_COOKIE", 300), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.foobar", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, }, }, }) - - if svc.SessionAffinity != "GENERATED_COOKIE" { - t.Errorf("Expected SessionAffinity == \"GENERATED_COOKIE\", got %s", svc.SessionAffinity) - } - - if svc.AffinityCookieTtlSec != 300 { - t.Errorf("Expected AffinityCookieTtlSec == 300, got %v", svc.AffinityCookieTtlSec) - } } func TestAccComputeBackendService_withMaxConnections(t *testing.T) { @@ -539,7 +366,6 @@ func TestAccComputeBackendService_withMaxConnections(t *testing.T) { 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, @@ -548,18 +374,15 @@ func TestAccComputeBackendService_withMaxConnections(t *testing.T) { resource.TestStep{ Config: testAccComputeBackendService_withMaxConnections( serviceName, igName, itName, checkName, 10), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.lipsum", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.lipsum", + ImportState: true, + ImportStateVerify: true, }, resource.TestStep{ Config: testAccComputeBackendService_withMaxConnections( serviceName, igName, itName, checkName, 20), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.lipsum", &svc), - ), }, resource.TestStep{ ResourceName: "google_compute_backend_service.lipsum", @@ -568,10 +391,6 @@ func TestAccComputeBackendService_withMaxConnections(t *testing.T) { }, }, }) - - if svc.Backends[0].MaxConnections != 20 { - t.Errorf("Expected MaxConnections == 20, got %d", svc.Backends[0].MaxConnections) - } } func TestAccComputeBackendService_withMaxConnectionsPerInstance(t *testing.T) { @@ -582,7 +401,6 @@ func TestAccComputeBackendService_withMaxConnectionsPerInstance(t *testing.T) { 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, @@ -591,18 +409,15 @@ func TestAccComputeBackendService_withMaxConnectionsPerInstance(t *testing.T) { resource.TestStep{ Config: testAccComputeBackendService_withMaxConnectionsPerInstance( serviceName, igName, itName, checkName, 10), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.lipsum", &svc), - ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.lipsum", + ImportState: true, + ImportStateVerify: true, }, resource.TestStep{ Config: testAccComputeBackendService_withMaxConnectionsPerInstance( serviceName, igName, itName, checkName, 20), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeBackendServiceExists( - "google_compute_backend_service.lipsum", &svc), - ), }, resource.TestStep{ ResourceName: "google_compute_backend_service.lipsum", @@ -611,10 +426,6 @@ func TestAccComputeBackendService_withMaxConnectionsPerInstance(t *testing.T) { }, }, }) - - if svc.Backends[0].MaxConnectionsPerInstance != 20 { - t.Errorf("Expected MaxConnectionsPerInstance == 20, got %d", svc.Backends[0].MaxConnectionsPerInstance) - } } func TestAccComputeBackendService_withMaxRatePerEndpoint(t *testing.T) { diff --git a/third_party/terraform/tests/resource_compute_disk_test.go.erb b/third_party/terraform/tests/resource_compute_disk_test.go.erb index 7a980d3d3945..3d54d6d653be 100644 --- a/third_party/terraform/tests/resource_compute_disk_test.go.erb +++ b/third_party/terraform/tests/resource_compute_disk_test.go.erb @@ -226,7 +226,6 @@ func TestAccComputeDisk_update(t *testing.T) { t.Parallel() diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - var disk compute.Disk resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -234,24 +233,19 @@ func TestAccComputeDisk_update(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccComputeDisk_basic(diskName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foobar", getTestProjectFromEnv(), &disk), - resource.TestCheckResourceAttr("google_compute_disk.foobar", "size", "50"), - testAccCheckComputeDiskHasLabel(&disk, "my-label", "my-label-value"), - testAccCheckComputeDiskHasLabelFingerprint(&disk, "google_compute_disk.foobar"), - ), + }, + { + ResourceName: "google_compute_disk.foobar", + ImportState: true, + ImportStateVerify: true, }, { Config: testAccComputeDisk_updated(diskName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foobar", getTestProjectFromEnv(), &disk), - resource.TestCheckResourceAttr("google_compute_disk.foobar", "size", "100"), - testAccCheckComputeDiskHasLabel(&disk, "my-label", "my-updated-label-value"), - testAccCheckComputeDiskHasLabel(&disk, "a-new-label", "a-new-label-value"), - testAccCheckComputeDiskHasLabelFingerprint(&disk, "google_compute_disk.foobar"), - ), + }, + { + ResourceName: "google_compute_disk.foobar", + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -265,8 +259,6 @@ func TestAccComputeDisk_fromSnapshot(t *testing.T) { snapshotName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) projectName := getTestProjectFromEnv() - var disk compute.Disk - resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -274,17 +266,19 @@ func TestAccComputeDisk_fromSnapshot(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeDisk_fromSnapshot(projectName, firstDiskName, snapshotName, diskName, "self_link"), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.seconddisk", getTestProjectFromEnv(), &disk), - ), + }, + { + ResourceName: "google_compute_disk.seconddisk", + ImportState: true, + ImportStateVerify: true, }, resource.TestStep{ Config: testAccComputeDisk_fromSnapshot(projectName, firstDiskName, snapshotName, diskName, "name"), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.seconddisk", getTestProjectFromEnv(), &disk), - ), + }, + { + ResourceName: "google_compute_disk.seconddisk", + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -352,7 +346,6 @@ func TestAccComputeDisk_deleteDetach(t *testing.T) { diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - var disk compute.Disk resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -361,10 +354,11 @@ func TestAccComputeDisk_deleteDetach(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeDisk_deleteDetach(instanceName, diskName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", getTestProjectFromEnv(), &disk), - ), + }, + { + ResourceName: "google_compute_disk.foo", + ImportState: true, + ImportStateVerify: true, }, // this needs to be a second step so we refresh and see the instance // listed as attached to the disk; the instance is created after the @@ -372,12 +366,11 @@ func TestAccComputeDisk_deleteDetach(t *testing.T) { // another step resource.TestStep{ Config: testAccComputeDisk_deleteDetach(instanceName, diskName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", getTestProjectFromEnv(), &disk), - testAccCheckComputeDiskInstances( - "google_compute_disk.foo", &disk), - ), + }, + { + ResourceName: "google_compute_disk.foo", + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -389,7 +382,6 @@ func TestAccComputeDisk_deleteDetachIGM(t *testing.T) { diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) diskName2 := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) mgrName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - var disk compute.Disk resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -398,10 +390,11 @@ func TestAccComputeDisk_deleteDetachIGM(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeDisk_deleteDetachIGM(diskName, mgrName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", getTestProjectFromEnv(), &disk), - ), + }, + { + ResourceName: "google_compute_disk.foo", + ImportState: true, + ImportStateVerify: true, }, // this needs to be a second step so we refresh and see the instance // listed as attached to the disk; the instance is created after the @@ -409,30 +402,29 @@ func TestAccComputeDisk_deleteDetachIGM(t *testing.T) { // another step resource.TestStep{ Config: testAccComputeDisk_deleteDetachIGM(diskName, mgrName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", getTestProjectFromEnv(), &disk), - testAccCheckComputeDiskInstances( - "google_compute_disk.foo", &disk), - ), + }, + { + ResourceName: "google_compute_disk.foo", + ImportState: true, + ImportStateVerify: true, }, // Change the disk name to recreate the instances resource.TestStep{ Config: testAccComputeDisk_deleteDetachIGM(diskName2, mgrName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", getTestProjectFromEnv(), &disk), - ), + }, + { + ResourceName: "google_compute_disk.foo", + ImportState: true, + ImportStateVerify: true, }, // Add the extra step like before resource.TestStep{ Config: testAccComputeDisk_deleteDetachIGM(diskName2, mgrName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", getTestProjectFromEnv(), &disk), - testAccCheckComputeDiskInstances( - "google_compute_disk.foo", &disk), - ), + }, + { + ResourceName: "google_compute_disk.foo", + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -491,37 +483,6 @@ func testAccCheckComputeDiskExists(n, p string, disk *compute.Disk) resource.Tes } } -func testAccCheckComputeDiskHasLabel(disk *compute.Disk, key, value string) resource.TestCheckFunc { - return func(s *terraform.State) error { - val, ok := disk.Labels[key] - if !ok { - return fmt.Errorf("Label with key %s not found", key) - } - - if val != value { - return fmt.Errorf("Label value did not match for key %s: expected %s but found %s", key, value, val) - } - return nil - } -} - -func testAccCheckComputeDiskHasLabelFingerprint(disk *compute.Disk, resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - state := s.RootModule().Resources[resourceName] - if state == nil { - return fmt.Errorf("Unable to find resource named %s", resourceName) - } - - labelFingerprint := state.Primary.Attributes["label_fingerprint"] - if labelFingerprint != disk.LabelFingerprint { - return fmt.Errorf("Label fingerprints do not match: api returned %s but state has %s", - disk.LabelFingerprint, labelFingerprint) - } - - return nil - } -} - func testAccCheckEncryptionKey(n string, disk *compute.Disk) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -540,28 +501,6 @@ func testAccCheckEncryptionKey(n string, disk *compute.Disk) resource.TestCheckF } } -func testAccCheckComputeDiskInstances(n string, disk *compute.Disk) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - attr := rs.Primary.Attributes["users.#"] - if strconv.Itoa(len(disk.Users)) != attr { - return fmt.Errorf("Disk %s has mismatched users.\nTF State: %+v\nGCP State: %+v", n, rs.Primary.Attributes["users"], disk.Users) - } - - for pos, user := range disk.Users { - if rs.Primary.Attributes["users."+strconv.Itoa(pos)] != user { - return fmt.Errorf("Disk %s has mismatched users.\nTF State: %+v.\nGCP State: %+v", - n, rs.Primary.Attributes["users"], disk.Users) - } - } - return nil - } -} - func testAccComputeDisk_basic(diskName string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { diff --git a/third_party/terraform/tests/resource_compute_global_address_test.go.erb b/third_party/terraform/tests/resource_compute_global_address_test.go.erb index dfb5034cf12a..fe5c7743cad9 100644 --- a/third_party/terraform/tests/resource_compute_global_address_test.go.erb +++ b/third_party/terraform/tests/resource_compute_global_address_test.go.erb @@ -15,8 +15,6 @@ import ( func TestAccComputeGlobalAddress_ipv6(t *testing.T) { t.Parallel() - var addr compute.Address - resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -24,11 +22,6 @@ func TestAccComputeGlobalAddress_ipv6(t *testing.T) { Steps: []resource.TestStep{ resource.TestStep{ Config: testAccComputeGlobalAddress_ipv6(), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeGlobalAddressExists( - "google_compute_global_address.foobar", &addr), - testAccCheckComputeGlobalAddressIpVersion("google_compute_global_address.foobar", "IPV6"), - ), }, resource.TestStep{ ResourceName: "google_compute_global_address.foobar", @@ -59,61 +52,6 @@ func TestAccComputeGlobalAddress_internal(t *testing.T) { }) } -func testAccCheckComputeGlobalAddressExists(n string, addr *compute.Address) 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.GlobalAddresses.Get( - config.Project, rs.Primary.ID).Do() - if err != nil { - return err - } - - if found.Name != rs.Primary.ID { - return fmt.Errorf("Addr not found") - } - - *addr = *found - - return nil - } -} - -func testAccCheckComputeGlobalAddressIpVersion(n, version string) 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) - - addr, err := config.clientCompute.GlobalAddresses.Get(config.Project, rs.Primary.ID).Do() - if err != nil { - return err - } - - if addr.IpVersion != version { - return fmt.Errorf("Expected IP version to be %s, got %s", version, addr.IpVersion) - } - - return nil - } -} - func testAccComputeGlobalAddress_ipv6() string { return fmt.Sprintf(` resource "google_compute_global_address" "foobar" { diff --git a/third_party/terraform/tests/resource_compute_https_health_check_test.go b/third_party/terraform/tests/resource_compute_https_health_check_test.go index 47edb4062fea..fbb3646ce7a9 100644 --- a/third_party/terraform/tests/resource_compute_https_health_check_test.go +++ b/third_party/terraform/tests/resource_compute_https_health_check_test.go @@ -6,15 +6,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/terraform" - "google.golang.org/api/compute/v1" ) func TestAccComputeHttpsHealthCheck_update(t *testing.T) { t.Parallel() - var healthCheck compute.HttpsHealthCheck - hhckName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) resource.Test(t, resource.TestCase{ @@ -24,83 +20,24 @@ func TestAccComputeHttpsHealthCheck_update(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccComputeHttpsHealthCheck_update1(hhckName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeHttpsHealthCheckExists( - "google_compute_https_health_check.foobar", &healthCheck), - testAccCheckComputeHttpsHealthCheckRequestPath( - "/not_default", &healthCheck), - testAccCheckComputeHttpsHealthCheckThresholds( - 2, 2, &healthCheck), - ), + }, + { + ResourceName: "google_compute_https_health_check.foobar", + ImportState: true, + ImportStateVerify: true, }, { Config: testAccComputeHttpsHealthCheck_update2(hhckName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeHttpsHealthCheckExists( - "google_compute_https_health_check.foobar", &healthCheck), - testAccCheckComputeHttpsHealthCheckRequestPath( - "/", &healthCheck), - testAccCheckComputeHttpsHealthCheckThresholds( - 10, 10, &healthCheck), - ), + }, + { + ResourceName: "google_compute_https_health_check.foobar", + ImportState: true, + ImportStateVerify: true, }, }, }) } -func testAccCheckComputeHttpsHealthCheckExists(n string, healthCheck *compute.HttpsHealthCheck) 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.HttpsHealthChecks.Get( - config.Project, rs.Primary.ID).Do() - if err != nil { - return err - } - - if found.Name != rs.Primary.ID { - return fmt.Errorf("HttpsHealthCheck not found") - } - - *healthCheck = *found - - return nil - } -} - -func testAccCheckComputeHttpsHealthCheckRequestPath(path string, healthCheck *compute.HttpsHealthCheck) resource.TestCheckFunc { - return func(s *terraform.State) error { - if healthCheck.RequestPath != path { - return fmt.Errorf("RequestPath doesn't match: expected %s, got %s", path, healthCheck.RequestPath) - } - - return nil - } -} - -func testAccCheckComputeHttpsHealthCheckThresholds(healthy, unhealthy int64, healthCheck *compute.HttpsHealthCheck) resource.TestCheckFunc { - return func(s *terraform.State) error { - if healthCheck.HealthyThreshold != healthy { - return fmt.Errorf("HealthyThreshold doesn't match: expected %d, got %d", healthy, healthCheck.HealthyThreshold) - } - - if healthCheck.UnhealthyThreshold != unhealthy { - return fmt.Errorf("UnhealthyThreshold doesn't match: expected %d, got %d", unhealthy, healthCheck.UnhealthyThreshold) - } - - return nil - } -} - func testAccComputeHttpsHealthCheck_update1(hhckName string) string { return fmt.Sprintf(` resource "google_compute_https_health_check" "foobar" { From 38796a4095d18f1c5da32f062500070cddd7ddd9 Mon Sep 17 00:00:00 2001 From: megan07 Date: Tue, 22 Oct 2019 13:56:36 -0500 Subject: [PATCH 08/37] add forcenew on shielded instance config (#2509) Merged PR #2509. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resource_container_node_pool_test.go.erb | 30 ------------------- .../terraform/utils/node_config.go.erb | 3 ++ 4 files changed, 5 insertions(+), 32 deletions(-) diff --git a/build/terraform b/build/terraform index 215cca14609f..e1b5947daaa5 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 215cca14609fdd8dcd86624b6d5344dfeeaf9b05 +Subproject commit e1b5947daaa56cdffb3f26a0b0f0ff5f55822abd diff --git a/build/terraform-beta b/build/terraform-beta index 8d85b10b3fa6..dfa84059bdcc 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 8d85b10b3fa61b0dd85f7ea2fadb54c5e80233b7 +Subproject commit dfa84059bdcc539ccb3d0640e27a4bf9aa4fcfda diff --git a/third_party/terraform/tests/resource_container_node_pool_test.go.erb b/third_party/terraform/tests/resource_container_node_pool_test.go.erb index b82b5e88b2e9..16cd9d0ab112 100644 --- a/third_party/terraform/tests/resource_container_node_pool_test.go.erb +++ b/third_party/terraform/tests/resource_container_node_pool_test.go.erb @@ -669,15 +669,6 @@ func TestAccContainerNodePool_shieldedInstanceConfig(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, - resource.TestStep{ - Config: testAccContainerNodePool_updateShieldedInstanceConfig(cluster, np), - }, - resource.TestStep{ - ResourceName: "google_container_node_pool.np", - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, - }, }, }) } @@ -1470,28 +1461,7 @@ resource "google_container_node_pool" "np" { node_config { shielded_instance_config { enable_integrity_monitoring = true - } - } -}`, cluster, np) -} - -func testAccContainerNodePool_updateShieldedInstanceConfig(cluster, np string) string { - return fmt.Sprintf(` -resource "google_container_cluster" "cluster" { - name = "%s" - location = "us-central1-a" - initial_node_count = 1 -} - -resource "google_container_node_pool" "np" { - name = "%s" - location = "us-central1-a" - cluster = "${google_container_cluster.cluster.name}" - initial_node_count = 2 - node_config { - shielded_instance_config { enable_secure_boot = true - enable_integrity_monitoring = true } } }`, cluster, np) diff --git a/third_party/terraform/utils/node_config.go.erb b/third_party/terraform/utils/node_config.go.erb index 10cead014348..b966d11d4ba6 100644 --- a/third_party/terraform/utils/node_config.go.erb +++ b/third_party/terraform/utils/node_config.go.erb @@ -152,17 +152,20 @@ var schemaNodeConfig = &schema.Schema{ Type: schema.TypeList, Optional: true, Computed: true, + ForceNew: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "enable_secure_boot": { Type: schema.TypeBool, Optional: true, + ForceNew: true, Default: false, }, "enable_integrity_monitoring": { Type: schema.TypeBool, Optional: true, + ForceNew: true, Default: true, }, }, From 211fc75fea1cf228ce5d4a2064c2929466eefb44 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Tue, 22 Oct 2019 12:27:06 -0700 Subject: [PATCH 09/37] Fix version behaviour in `google_compute_instance_group` (#2506) Merged PR #2506. --- build/terraform-mapper | 2 +- .../resource_compute_instance_group.go | 9 ++--- .../resources/resource_compute_target_pool.go | 30 ----------------- third_party/terraform/utils/utils.go.erb | 33 +++++++++++++++++++ 4 files changed, 39 insertions(+), 35 deletions(-) diff --git a/build/terraform-mapper b/build/terraform-mapper index 4700f1232f79..0813eaf03ce9 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 4700f1232f799d729d3422bb3e51055522ec4fb0 +Subproject commit 0813eaf03ce98bc18dc20760956b59101044e98a diff --git a/third_party/terraform/resources/resource_compute_instance_group.go b/third_party/terraform/resources/resource_compute_instance_group.go index 176a16a2518e..4ff3a889e2ef 100644 --- a/third_party/terraform/resources/resource_compute_instance_group.go +++ b/third_party/terraform/resources/resource_compute_instance_group.go @@ -71,10 +71,11 @@ func resourceComputeInstanceGroup() *schema.Resource { }, "network": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + ForceNew: true, }, "project": { diff --git a/third_party/terraform/resources/resource_compute_target_pool.go b/third_party/terraform/resources/resource_compute_target_pool.go index cc25a52f21a8..9596bd944c16 100644 --- a/third_party/terraform/resources/resource_compute_target_pool.go +++ b/third_party/terraform/resources/resource_compute_target_pool.go @@ -216,36 +216,6 @@ func resourceComputeTargetPoolCreate(d *schema.ResourceData, meta interface{}) e return resourceComputeTargetPoolRead(d, meta) } -func calcAddRemove(from []string, to []string) ([]string, []string) { - add := make([]string, 0) - remove := make([]string, 0) - for _, u := range to { - found := false - for _, v := range from { - if u == v { - found = true - break - } - } - if !found { - add = append(add, u) - } - } - for _, u := range from { - found := false - for _, v := range to { - if u == v { - found = true - break - } - } - if !found { - remove = append(remove, u) - } - } - return add, remove -} - func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) diff --git a/third_party/terraform/utils/utils.go.erb b/third_party/terraform/utils/utils.go.erb index f8544ed0970f..1da2266b4d18 100644 --- a/third_party/terraform/utils/utils.go.erb +++ b/third_party/terraform/utils/utils.go.erb @@ -542,3 +542,36 @@ func getInterconnectAttachmentLink(config *Config, project, region, ic string) ( return ic, nil } + +// Given two sets of references (with "from" values in self link form), +// determine which need to be added or removed // during an update using +// addX/removeX APIs. +func calcAddRemove(from []string, to []string) (add, remove []string) { + add = make([]string, 0) + remove = make([]string, 0) + for _, u := range to { + found := false + for _, v := range from { + if compareSelfLinkOrResourceName("", v, u, nil) { + found = true + break + } + } + if !found { + add = append(add, u) + } + } + for _, u := range from { + found := false + for _, v := range to { + if compareSelfLinkOrResourceName("", u, v, nil) { + found = true + break + } + } + if !found { + remove = append(remove, u) + } + } + return add, remove +} From ab2fe78d8a3666cc42bd3038aecd424d1f22f21b Mon Sep 17 00:00:00 2001 From: Denis Date: Tue, 22 Oct 2019 21:44:31 +0200 Subject: [PATCH 10/37] Fix erronous lowercase c character (#2490) Merged PR #2490. --- build/ansible | 2 +- provider/ansible/documentation.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/ansible b/build/ansible index e0d101beccb2..86c26c93c9b2 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit e0d101beccb2e021491ee749bde49dce1c5f0235 +Subproject commit 86c26c93c9b2a0ed3b181392fd5570696b047cf8 diff --git a/provider/ansible/documentation.rb b/provider/ansible/documentation.rb index 91dc59050a09..1232ec49eaae 100644 --- a/provider/ansible/documentation.rb +++ b/provider/ansible/documentation.rb @@ -168,8 +168,8 @@ def auth_docs # Notes related to authentication def auth_notes [ - 'for authentication, you can set service_account_file using the c(gcp_service_account_file) env variable.', - 'for authentication, you can set service_account_contents using the c(GCP_SERVICE_ACCOUNT_CONTENTS) env variable.', + 'for authentication, you can set service_account_file using the C(gcp_service_account_file) env variable.', + 'for authentication, you can set service_account_contents using the C(GCP_SERVICE_ACCOUNT_CONTENTS) env variable.', 'For authentication, you can set service_account_email using the C(GCP_SERVICE_ACCOUNT_EMAIL) env variable.', 'For authentication, you can set auth_kind using the C(GCP_AUTH_KIND) env variable.', 'For authentication, you can set scopes using the C(GCP_SCOPES) env variable.', From 28c2961f1345382d4359ad0ec6ed36bbfeadae53 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 22 Oct 2019 13:30:04 -0700 Subject: [PATCH 11/37] Add support for compute_router_nat to inspec (#2510) Merged PR #2510. --- build/inspec | 2 +- products/compute/api.yaml | 1 + products/compute/inspec.yaml | 2 -- .../google_compute_router_nat.erb | 16 ++++++++++++++++ .../google_compute_router_nat_attributes.erb | 4 ++++ .../google_compute_router_nats.erb | 7 +++++++ .../inspec/tests/integration/build/gcp-mm.tf | 19 +++++++++++++++++++ .../configuration/mm-attributes.yml | 10 +++++++++- 8 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 templates/inspec/examples/google_compute_router_nat/google_compute_router_nat.erb create mode 100644 templates/inspec/examples/google_compute_router_nat/google_compute_router_nat_attributes.erb create mode 100644 templates/inspec/examples/google_compute_router_nat/google_compute_router_nats.erb diff --git a/build/inspec b/build/inspec index 179cd115de4d..3b9afb35284f 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 179cd115de4dc78342a1b10a6d278e0e4fd88d65 +Subproject commit 3b9afb35284fdc2a6be0c6e831a757d935fdca0d diff --git a/products/compute/api.yaml b/products/compute/api.yaml index 0582c47e414b..efcad11866a5 100644 --- a/products/compute/api.yaml +++ b/products/compute/api.yaml @@ -7720,6 +7720,7 @@ objects: delete_verb: :PATCH identity: - name + collection_url_key: nats nested_query: !ruby/object:Api::Resource::NestedQuery modify_by_patch: true keys: diff --git a/products/compute/inspec.yaml b/products/compute/inspec.yaml index 53b1e8839916..1a6b8c03d695 100644 --- a/products/compute/inspec.yaml +++ b/products/compute/inspec.yaml @@ -80,8 +80,6 @@ overrides: !ruby/object:Overrides::ResourceOverrides exclude: true ResourcePolicy: !ruby/object:Overrides::Inspec::ResourceOverride exclude: true - RouterNat: !ruby/object:Overrides::Inspec::ResourceOverride - exclude: true Subnetwork: !ruby/object:Overrides::Inspec::ResourceOverride exclude_resource: true iam_policy: !ruby/object:Api::Resource::IamPolicy diff --git a/templates/inspec/examples/google_compute_router_nat/google_compute_router_nat.erb b/templates/inspec/examples/google_compute_router_nat/google_compute_router_nat.erb new file mode 100644 index 000000000000..89f3a5539cd6 --- /dev/null +++ b/templates/inspec/examples/google_compute_router_nat/google_compute_router_nat.erb @@ -0,0 +1,16 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% gcp_location = "#{external_attribute('gcp_location', doc_generation)}" -%> +<% router = grab_attributes['router'] -%> +<% router_nat = grab_attributes['router_nat'] -%> +describe google_compute_router_nat(project: <%= gcp_project_id -%>, region: <%= gcp_location -%>, router: <%= doc_generation ? "'#{router['name']}'" : "router['name']" -%>, name: <%= doc_generation ? "'#{router_nat['name']}'" : "router_nat['name']" -%>) do + it { should exist } + its('nat_ip_allocate_option') { should cmp <%= doc_generation ? "'#{router_nat['nat_ip_allocate_option']}'" : "router_nat['nat_ip_allocate_option']" -%> } + its('source_subnetwork_ip_ranges_to_nat') { should cmp <%= doc_generation ? "'#{router_nat['source_subnetwork_ip_ranges_to_nat']}'" : "router_nat['source_subnetwork_ip_ranges_to_nat']" -%> } + its('min_ports_per_vm') { should cmp <%= doc_generation ? "'#{router_nat['min_ports_per_vm']}'" : "router_nat['min_ports_per_vm']" -%> } + its('log_config.enable') { should cmp <%= doc_generation ? "'#{router_nat['log_config_enable']}'" : "router_nat['log_config_enable']" -%> } + its('log_config.filter') { should cmp <%= doc_generation ? "'#{router_nat['log_config_filter']}'" : "router_nat['log_config_filter']" -%> } +end + +describe google_compute_router(project: <%= gcp_project_id -%>, region: <%= gcp_location -%>, router: 'nonexistent', name: 'nonexistent') do + it { should_not exist } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_router_nat/google_compute_router_nat_attributes.erb b/templates/inspec/examples/google_compute_router_nat/google_compute_router_nat_attributes.erb new file mode 100644 index 000000000000..bfada640e1dc --- /dev/null +++ b/templates/inspec/examples/google_compute_router_nat/google_compute_router_nat_attributes.erb @@ -0,0 +1,4 @@ +gcp_project_id = attribute(:gcp_project_id, default: '<%= external_attribute('gcp_project_id') -%>', description: 'The GCP project identifier.') +gcp_location = attribute(:gcp_location, default: '<%= external_attribute('gcp_location') -%>', description: 'The GCP project region.') +router = attribute('router', default: <%= JSON.pretty_generate(grab_attributes['router']) -%>, description: 'Compute router description') +router_nat = attribute('router_nat', default: <%= JSON.pretty_generate(grab_attributes['router_nat']) -%>, description: 'Compute router NAT description') diff --git a/templates/inspec/examples/google_compute_router_nat/google_compute_router_nats.erb b/templates/inspec/examples/google_compute_router_nat/google_compute_router_nats.erb new file mode 100644 index 000000000000..13e0e53ad843 --- /dev/null +++ b/templates/inspec/examples/google_compute_router_nat/google_compute_router_nats.erb @@ -0,0 +1,7 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% gcp_location = "#{external_attribute('gcp_location', doc_generation)}" -%> +<% router = grab_attributes['router'] -%> +<% router_nat = grab_attributes['router_nat'] -%> +describe google_compute_router_nats(project: <%= gcp_project_id -%>, region: <%= gcp_location -%>, router: <%= doc_generation ? "'#{router['name']}'" : "router['name']" -%>) do + its('names') { should include <%= doc_generation ? "'#{router_nat['name']}'" : "router_nat['name']" -%> } +end \ No newline at end of file diff --git a/templates/inspec/tests/integration/build/gcp-mm.tf b/templates/inspec/tests/integration/build/gcp-mm.tf index d03e10dce3b2..4e9619e09d48 100644 --- a/templates/inspec/tests/integration/build/gcp-mm.tf +++ b/templates/inspec/tests/integration/build/gcp-mm.tf @@ -193,6 +193,10 @@ variable "node_group" { type = "map" } +variable "router_nat" { + type = "map" +} + resource "google_compute_ssl_policy" "custom-ssl-policy" { name = "${var.ssl_policy["name"]}" min_tls_version = "${var.ssl_policy["min_tls_version"]}" @@ -838,3 +842,18 @@ resource "google_compute_node_group" "inspec-node-group" { size = var.node_group["size"] node_template = "${google_compute_node_template.inspec-template.self_link}" } + +resource "google_compute_router_nat" "inspec-nat" { + project = var.gcp_project_id + name = var.router_nat["name"] + router = google_compute_router.gcp-inspec-router.name + region = google_compute_router.gcp-inspec-router.region + nat_ip_allocate_option = var.router_nat["nat_ip_allocate_option"] + source_subnetwork_ip_ranges_to_nat = var.router_nat["source_subnetwork_ip_ranges_to_nat"] + min_ports_per_vm = var.router_nat["min_ports_per_vm"] + + log_config { + enable = var.router_nat["log_config_enable"] + filter = var.router_nat["log_config_filter"] + } +} diff --git a/templates/inspec/tests/integration/configuration/mm-attributes.yml b/templates/inspec/tests/integration/configuration/mm-attributes.yml index 92b6cfdcba4d..89aabf593b47 100644 --- a/templates/inspec/tests/integration/configuration/mm-attributes.yml +++ b/templates/inspec/tests/integration/configuration/mm-attributes.yml @@ -315,4 +315,12 @@ node_template: node_group: name: inspec-node-group description: A description of the node group - size: 0 \ No newline at end of file + size: 0 + +router_nat: + name: inspec-router-nat + nat_ip_allocate_option: AUTO_ONLY + source_subnetwork_ip_ranges_to_nat: ALL_SUBNETWORKS_ALL_IP_RANGES + min_ports_per_vm: 2 + log_config_enable: true + log_config_filter: ERRORS_ONLY \ No newline at end of file From 939c0c0bb90a65be88ca27349cc755e750e745eb Mon Sep 17 00:00:00 2001 From: "Nick (Moorman) Sawyer" Date: Wed, 23 Oct 2019 08:51:33 -0700 Subject: [PATCH 12/37] Extract Bigtable instance type from InstanceInfo instead of deriving from clusters (#2488) Merged PR #2488. --- build/inspec | 2 +- build/terraform | 2 +- build/terraform-beta | 2 +- .../resources/resource_bigtable_instance.go | 23 ++++++++----------- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/build/inspec b/build/inspec index 3b9afb35284f..d07d28d32806 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 3b9afb35284fdc2a6be0c6e831a757d935fdca0d +Subproject commit d07d28d328064d025aa0c0d49a5de08421610936 diff --git a/build/terraform b/build/terraform index e1b5947daaa5..2cee1932d0a7 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit e1b5947daaa56cdffb3f26a0b0f0ff5f55822abd +Subproject commit 2cee1932d0a7019033e1046f30afae2e7f721738 diff --git a/build/terraform-beta b/build/terraform-beta index dfa84059bdcc..14ce1c32132f 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit dfa84059bdcc539ccb3d0640e27a4bf9aa4fcfda +Subproject commit 14ce1c32132fb1728bf3a605687a726658188cdf diff --git a/third_party/terraform/resources/resource_bigtable_instance.go b/third_party/terraform/resources/resource_bigtable_instance.go index de12259ba4c9..56d8f79d2dd7 100644 --- a/third_party/terraform/resources/resource_bigtable_instance.go +++ b/third_party/terraform/resources/resource_bigtable_instance.go @@ -189,26 +189,21 @@ func resourceBigtableInstanceRead(d *schema.ResourceData, meta interface{}) erro d.Set("project", project) + var instanceType string + if instance.InstanceType == bigtable.DEVELOPMENT { + instanceType = "DEVELOPMENT" + } else { + instanceType = "PRODUCTION" + } + d.Set("instance_type", instanceType) + clusters, err := c.Clusters(ctx, instance.Name) if err != nil { return fmt.Errorf("Error retrieving instance clusters. %s", err) } clustersNewState := []map[string]interface{}{} - for i, cluster := range clusters { - // DEVELOPMENT clusters have num_nodes = 0 on their first (and only) - // cluster while PRODUCTION clusters will have at least 3. - if i == 0 { - var instanceType string - if cluster.ServeNodes == 0 { - instanceType = "DEVELOPMENT" - } else { - instanceType = "PRODUCTION" - } - - d.Set("instance_type", instanceType) - } - + for _, cluster := range clusters { clustersNewState = append(clustersNewState, flattenBigtableCluster(cluster)) } From c7e8f13d82edd5b32b3465c1709ce05fc7ff6852 Mon Sep 17 00:00:00 2001 From: Tone Date: Wed, 23 Oct 2019 18:52:39 +0200 Subject: [PATCH 13/37] Added oidcToken to pubsub api. (#2440) Merged PR #2440. --- build/ansible | 2 +- build/inspec | 2 +- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- products/pubsub/api.yaml | 23 ++++++ .../resource_pubsub_subscription_test.go | 71 +++++++++++++++---- 7 files changed, 87 insertions(+), 17 deletions(-) diff --git a/build/ansible b/build/ansible index 86c26c93c9b2..033baceee559 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit 86c26c93c9b2a0ed3b181392fd5570696b047cf8 +Subproject commit 033baceee5591dea3b8ef45763b10b8c113ba301 diff --git a/build/inspec b/build/inspec index d07d28d32806..09889ea10ccc 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit d07d28d328064d025aa0c0d49a5de08421610936 +Subproject commit 09889ea10ccc9cdab7bcd5cfe22792f3888a9011 diff --git a/build/terraform b/build/terraform index 2cee1932d0a7..5b41d89db735 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 2cee1932d0a7019033e1046f30afae2e7f721738 +Subproject commit 5b41d89db735d09fa4c0c1178b54320a8fb8f0c4 diff --git a/build/terraform-beta b/build/terraform-beta index 14ce1c32132f..61950b57242a 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 14ce1c32132fb1728bf3a605687a726658188cdf +Subproject commit 61950b57242ad9cd417e6834646c674e6577c48b diff --git a/build/terraform-mapper b/build/terraform-mapper index 0813eaf03ce9..d9bdfb56f78f 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 0813eaf03ce98bc18dc20760956b59101044e98a +Subproject commit d9bdfb56f78f4c81582d57de7645abf190d5c39c diff --git a/products/pubsub/api.yaml b/products/pubsub/api.yaml index 910161db00e4..e9ee85d31b12 100644 --- a/products/pubsub/api.yaml +++ b/products/pubsub/api.yaml @@ -126,6 +126,29 @@ objects: configure it. An empty pushConfig signifies that the subscriber will pull and ack messages using API methods. properties: + - !ruby/object:Api::Type::NestedObject + name: 'oidcToken' + description: | + If specified, Pub/Sub will generate and attach an OIDC JWT token as + an Authorization header in the HTTP request for every pushed message. + properties: + - !ruby/object:Api::Type::String + name: 'serviceAccountEmail' + required: true + description: | + Service account email to be used for generating the OIDC token. + The caller (for subscriptions.create, subscriptions.patch, and + subscriptions.modifyPushConfig RPCs) must have the + iam.serviceAccounts.actAs permission for the service account. + - !ruby/object:Api::Type::String + name: 'audience' + description: | + Audience to be used when generating OIDC token. The audience claim + identifies the recipients that the JWT is intended for. The audience + value is a single case-sensitive string. Having multiple values (array) + for the audience field is not supported. More info about the OIDC JWT + token audience here: https://tools.ietf.org/html/rfc7519#section-4.1.3 + Note: if not specified, the Push endpoint URL will be used. - !ruby/object:Api::Type::String name: 'pushEndpoint' description: | diff --git a/third_party/terraform/tests/resource_pubsub_subscription_test.go b/third_party/terraform/tests/resource_pubsub_subscription_test.go index 55c379db8e1f..71e3cef79e57 100644 --- a/third_party/terraform/tests/resource_pubsub_subscription_test.go +++ b/third_party/terraform/tests/resource_pubsub_subscription_test.go @@ -105,6 +105,30 @@ func TestAccPubsubSubscription_update(t *testing.T) { }) } +func TestAccPubsubSubscription_push(t *testing.T) { + t.Parallel() + + topicFoo := fmt.Sprintf("tf-test-topic-foo-%s", acctest.RandString(10)) + subscription := fmt.Sprintf("projects/%s/subscriptions/tf-test-topic-foo-%s", getTestProjectFromEnv(), acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPubsubSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccPubsubSubscription_push(topicFoo, subscription), + }, + { + ResourceName: "google_pubsub_subscription.foo", + ImportStateId: subscription, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccPubsubSubscription_emptyTTL(topic, subscription string) string { return fmt.Sprintf(` resource "google_pubsub_topic" "foo" { @@ -123,18 +147,41 @@ resource "google_pubsub_subscription" "foo" { `, topic, subscription) } -// TODO: Add acceptance test for push delivery. -// -// Testing push endpoints is tricky for the following reason: -// - You need a publicly accessible HTTPS server to handle POST requests in order to receive push messages. -// - The server must present a valid SSL certificate signed by a certificate authority -// - The server must be routable by DNS. -// - You also need to validate that you own the domain (or have equivalent access to the endpoint). -// - Finally, you must register the endpoint domain with the GCP project. -// -// An easy way to test this would be to create an App Engine Hello World app. With AppEngine, SSL certificate, DNS and domain registry is handled for us. -// App Engine is not yet supported by Terraform but once it is, it will provide an easy path to testing push configs. -// Another option would be to use Cloud Functions once Terraform support is added. +func testAccPubsubSubscription_push(topicFoo string, subscription string) string { + return fmt.Sprintf(` +data "google_project" "project" {} + +resource "google_service_account" "pub_sub_service_account" { + account_id = "my-super-service" +} + +data "google_iam_policy" "admin" { + binding { + role = "roles/projects.topics.publish" + + members = [ + "serviceAccount:${google_service_account.pub_sub_service_account.email}", + ] + } +} + +resource "google_pubsub_topic" "foo" { + name = "%s" +} + +resource "google_pubsub_subscription" "foo" { + name = "%s" + topic = "${google_pubsub_topic.foo.name}" + ack_deadline_seconds = 10 + push_config { + push_endpoint = "https://${data.google_project.project.project_id}.appspot.com" + oidc_token { + service_account_email = "${google_service_account.pub_sub_service_account.email}" + } + } +} +`, topicFoo, subscription) +} func testAccPubsubSubscription_fullName(topic, subscription, label string, deadline int) string { return fmt.Sprintf(` From f685d5f9538954c7017c21348c0bd6a9fc1bea28 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 23 Oct 2019 09:57:39 -0700 Subject: [PATCH 14/37] Modify health check functionality to match description (#2494) Merged PR #2494. --- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- .../terraform/encoders/health_check_type.erb | 15 ++++++++++----- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/build/terraform b/build/terraform index 5b41d89db735..b35d35b59f15 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 5b41d89db735d09fa4c0c1178b54320a8fb8f0c4 +Subproject commit b35d35b59f1582c3c64ca40ea64e61746cb90a7d diff --git a/build/terraform-beta b/build/terraform-beta index 61950b57242a..dc93dfeb862c 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 61950b57242ad9cd417e6834646c674e6577c48b +Subproject commit dc93dfeb862cca0078b3033933d468bb31f22a71 diff --git a/build/terraform-mapper b/build/terraform-mapper index d9bdfb56f78f..10fdc7e0ac9b 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit d9bdfb56f78f4c81582d57de7645abf190d5c39c +Subproject commit 10fdc7e0ac9b0e2184cec17772f54116d48d041e diff --git a/templates/terraform/encoders/health_check_type.erb b/templates/terraform/encoders/health_check_type.erb index 74517732253a..55a1ec13d362 100644 --- a/templates/terraform/encoders/health_check_type.erb +++ b/templates/terraform/encoders/health_check_type.erb @@ -16,8 +16,9 @@ if _, ok := d.GetOk("http_health_check"); ok { hc := d.Get("http_health_check").([]interface{})[0] ps := hc.(map[string]interface{})["port_specification"] + pn := hc.(map[string]interface{})["port_name"] - if ps == "USE_FIXED_PORT" || ps == "" { + if ps == "USE_FIXED_PORT" || (ps == "" && pn == "") { m := obj["httpHealthCheck"].(map[string]interface{}) if m["port"] == nil { m["port"] = 80 @@ -29,8 +30,9 @@ if _, ok := d.GetOk("http_health_check"); ok { if _, ok := d.GetOk("https_health_check"); ok { hc := d.Get("https_health_check").([]interface{})[0] ps := hc.(map[string]interface{})["port_specification"] + pn := hc.(map[string]interface{})["port_name"] - if ps == "USE_FIXED_PORT" || ps == "" { + if ps == "USE_FIXED_PORT" || (ps == "" && pn == "") { m := obj["httpsHealthCheck"].(map[string]interface{}) if m["port"] == nil { m["port"] = 443 @@ -42,8 +44,9 @@ if _, ok := d.GetOk("https_health_check"); ok { if _, ok := d.GetOk("http2_health_check"); ok { hc := d.Get("http2_health_check").([]interface{})[0] ps := hc.(map[string]interface{})["port_specification"] + pn := hc.(map[string]interface{})["port_name"] - if ps == "USE_FIXED_PORT" || ps == "" { + if ps == "USE_FIXED_PORT" || (ps == "" && pn == "") { m := obj["http2HealthCheck"].(map[string]interface{}) if m["port"] == nil { m["port"] = 443 @@ -55,8 +58,9 @@ if _, ok := d.GetOk("http2_health_check"); ok { if _, ok := d.GetOk("tcp_health_check"); ok { hc := d.Get("tcp_health_check").([]interface{})[0] ps := hc.(map[string]interface{})["port_specification"] + pn := hc.(map[string]interface{})["port_name"] - if ps == "USE_FIXED_PORT" || ps == "" { + if ps == "USE_FIXED_PORT" || (ps == "" && pn == "") { m := obj["tcpHealthCheck"].(map[string]interface{}) if m["port"] == nil { m["port"] = 80 @@ -68,8 +72,9 @@ if _, ok := d.GetOk("tcp_health_check"); ok { if _, ok := d.GetOk("ssl_health_check"); ok { hc := d.Get("ssl_health_check").([]interface{})[0] ps := hc.(map[string]interface{})["port_specification"] + pn := hc.(map[string]interface{})["port_name"] - if ps == "USE_FIXED_PORT" || ps == "" { + if ps == "USE_FIXED_PORT" || (ps == "" && pn == "") { m := obj["sslHealthCheck"].(map[string]interface{}) if m["port"] == nil { m["port"] = 443 From 48680885fe838f5f06335c4a6e8aea0c50e150c9 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Wed, 23 Oct 2019 12:55:26 -0700 Subject: [PATCH 15/37] Runtime Config configs (#2518) Merged PR #2518. --- build/ansible | 2 +- products/runtimeconfig/ansible.yaml | 26 +++++++++++++++++++ .../runtimeconfig/ansible_version_added.yaml | 11 ++++++++ products/runtimeconfig/api.yaml | 3 ++- .../examples/ansible/config.yaml | 21 +++++++++++++++ 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 products/runtimeconfig/ansible.yaml create mode 100644 products/runtimeconfig/ansible_version_added.yaml create mode 100644 products/runtimeconfig/examples/ansible/config.yaml diff --git a/build/ansible b/build/ansible index 033baceee559..941348992017 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit 033baceee5591dea3b8ef45763b10b8c113ba301 +Subproject commit 94134899201737f2ad596aed290ae02c630d7496 diff --git a/products/runtimeconfig/ansible.yaml b/products/runtimeconfig/ansible.yaml new file mode 100644 index 000000000000..3f7be9489c73 --- /dev/null +++ b/products/runtimeconfig/ansible.yaml @@ -0,0 +1,26 @@ +# Copyright 2019 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- !ruby/object:Provider::Ansible::Config +datasources: !ruby/object:Overrides::ResourceOverrides + Config: !ruby/object:Overrides::Ansible::ResourceOverride + facts: !ruby/object:Provider::Ansible::FactsOverride + has_filters: false + Variable: !ruby/object:Overrides::Ansible::ResourceOverride + exclude: true +overrides: !ruby/object:Overrides::ResourceOverrides + Variable: !ruby/object:Overrides::Ansible::ResourceOverride + exclude: true +files: !ruby/object:Provider::Config::Files + resource: +<%= lines(indent(compile('provider/ansible/resource~compile.yaml'), 4)) -%> diff --git a/products/runtimeconfig/ansible_version_added.yaml b/products/runtimeconfig/ansible_version_added.yaml new file mode 100644 index 000000000000..6232f3fb58a6 --- /dev/null +++ b/products/runtimeconfig/ansible_version_added.yaml @@ -0,0 +1,11 @@ +--- +:facts: + :Config: + :version_added: '2.10' +:regular: + :Config: + :version_added: '2.10' + :description: + :version_added: '2.10' + :name: + :version_added: '2.10' diff --git a/products/runtimeconfig/api.yaml b/products/runtimeconfig/api.yaml index c9f677ed290d..44c0e50c3944 100644 --- a/products/runtimeconfig/api.yaml +++ b/products/runtimeconfig/api.yaml @@ -43,6 +43,7 @@ objects: The name of the runtime config. required: true input: true + pattern: projects/{{project}}/configs/{{name}} properties: - !ruby/object:Api::Type::String name: 'description' @@ -75,4 +76,4 @@ objects: - !ruby/object:Api::Type::String name: 'text' description: | - The string value of the variable. Either this or `value` can be set. \ No newline at end of file + The string value of the variable. Either this or `value` can be set. diff --git a/products/runtimeconfig/examples/ansible/config.yaml b/products/runtimeconfig/examples/ansible/config.yaml new file mode 100644 index 000000000000..e71a9de6562c --- /dev/null +++ b/products/runtimeconfig/examples/ansible/config.yaml @@ -0,0 +1,21 @@ +# Copyright 2019 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- !ruby/object:Provider::Ansible::Example +task: !ruby/object:Provider::Ansible::Task + name: gcp_runtimeconfig_config + code: + name: <%= ctx[:name] %> + description: 'My config' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> From 71ca60162376ae12d5f0ec715e2efb38a601edaa Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 23 Oct 2019 14:29:35 -0700 Subject: [PATCH 16/37] Fix disk source comparisons (#2520) Merged PR #2520. --- build/terraform | 2 +- build/terraform-beta | 2 +- templates/terraform/pre_delete/detach_disk.erb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/terraform b/build/terraform index b35d35b59f15..eaf41e9b440c 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit b35d35b59f1582c3c64ca40ea64e61746cb90a7d +Subproject commit eaf41e9b440ccdaff8812c84ed4e210f0057df54 diff --git a/build/terraform-beta b/build/terraform-beta index dc93dfeb862c..05064af3f82c 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit dc93dfeb862cca0078b3033933d468bb31f22a71 +Subproject commit 05064af3f82ca7e4a2edb083328d932665c7f6c0 diff --git a/templates/terraform/pre_delete/detach_disk.erb b/templates/terraform/pre_delete/detach_disk.erb index bd62053d08bc..875d62c1ef9a 100644 --- a/templates/terraform/pre_delete/detach_disk.erb +++ b/templates/terraform/pre_delete/detach_disk.erb @@ -24,7 +24,7 @@ if v, ok := readRes["users"].([]interface{}); ok { return fmt.Errorf("Error retrieving instance %s: %s", instance, err.Error()) } for _, disk := range i.Disks { - if disk.Source == self { + if compareSelfLinkOrResourceName("", disk.Source, self, nil) { detachCalls = append(detachCalls, detachArgs{ project: instanceProject, zone: GetResourceNameFromSelfLink(i.Zone), From 89c2b6b96d9a5325565bacae29a7eb574269a27f Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Wed, 23 Oct 2019 15:06:30 -0700 Subject: [PATCH 17/37] Add null check to arrays of resourcerefs. (#2511) Merged PR #2511. --- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- templates/terraform/expand_property_method.erb | 3 +++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/build/terraform b/build/terraform index eaf41e9b440c..0fdd0941e6cd 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit eaf41e9b440ccdaff8812c84ed4e210f0057df54 +Subproject commit 0fdd0941e6cd9865b02f64805fc974da52863de6 diff --git a/build/terraform-beta b/build/terraform-beta index 05064af3f82c..fb571d38b3e7 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 05064af3f82ca7e4a2edb083328d932665c7f6c0 +Subproject commit fb571d38b3e7fe6862d37949e1492eef92c0061e diff --git a/build/terraform-mapper b/build/terraform-mapper index 10fdc7e0ac9b..888d1d1b4f81 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 10fdc7e0ac9b0e2184cec17772f54116d48d041e +Subproject commit 888d1d1b4f8163cb12db0831001b7cfaa9455f7d diff --git a/templates/terraform/expand_property_method.erb b/templates/terraform/expand_property_method.erb index 42702823a59c..4dc6b09518b5 100644 --- a/templates/terraform/expand_property_method.erb +++ b/templates/terraform/expand_property_method.erb @@ -142,6 +142,9 @@ func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d T l := v.([]interface{}) req := make([]interface{}, 0, len(l)) for _, raw := range l { + if raw == nil { + return nil, fmt.Errorf("Invalid value for <%= property.name.underscore -%>: nil") + } f, err := <%= build_expand_resource_ref('raw.(string)', property.item_type) %> if err != nil { return nil, fmt.Errorf("Invalid value for <%= property.name.underscore -%>: %s", err) From a4ccdffc415eaf9305c5887c0fc0d3efbeeb96c4 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Wed, 23 Oct 2019 15:14:08 -0700 Subject: [PATCH 18/37] Update Router so that BGP removals work. (#2522) Merged PR #2522. --- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- products/compute/api.yaml | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build/terraform b/build/terraform index 0fdd0941e6cd..55d211c09e27 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 0fdd0941e6cd9865b02f64805fc974da52863de6 +Subproject commit 55d211c09e270117e0d134b685c077c5b5b4eb50 diff --git a/build/terraform-beta b/build/terraform-beta index fb571d38b3e7..d9e07e23549c 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit fb571d38b3e7fe6862d37949e1492eef92c0061e +Subproject commit d9e07e23549c337e35e4063ee0e3dc32c39a0f44 diff --git a/build/terraform-mapper b/build/terraform-mapper index 888d1d1b4f81..a788ab75a3d4 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 888d1d1b4f8163cb12db0831001b7cfaa9455f7d +Subproject commit a788ab75a3d4bbecb3c4f1f98f9f9e7a34f3c54b diff --git a/products/compute/api.yaml b/products/compute/api.yaml index efcad11866a5..ead81c8c1094 100644 --- a/products/compute/api.yaml +++ b/products/compute/api.yaml @@ -7655,6 +7655,7 @@ objects: name: bgp description: | BGP information specific to this router. + send_empty_value: true properties: - !ruby/object:Api::Type::Integer name: asn From cf81a3df8465721f861c21a86cf4b033bb91b466 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 23 Oct 2019 15:39:43 -0700 Subject: [PATCH 19/37] Clean up AppEngine examples (#2512) Merged PR #2512. --- build/terraform | 2 +- build/terraform-beta | 2 +- products/appengine/terraform.yaml | 7 +- ...pplication_url_dispatch_rules_basic.tf.erb | 56 ++++++++------- .../app_engine_standard_app_version.tf.erb | 70 ++++++++----------- 5 files changed, 63 insertions(+), 74 deletions(-) diff --git a/build/terraform b/build/terraform index 55d211c09e27..f771cd7dc0ec 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 55d211c09e270117e0d134b685c077c5b5b4eb50 +Subproject commit f771cd7dc0ec0322c4d3b3a9ab4e2ad3eb347625 diff --git a/build/terraform-beta b/build/terraform-beta index d9e07e23549c..3e950ce27997 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit d9e07e23549c337e35e4063ee0e3dc32c39a0f44 +Subproject commit 3e950ce27997d5917725e97d8e57a37c55c8e708 diff --git a/products/appengine/terraform.yaml b/products/appengine/terraform.yaml index 2dcbb57e9843..8d1e56f63a6e 100644 --- a/products/appengine/terraform.yaml +++ b/products/appengine/terraform.yaml @@ -60,9 +60,9 @@ overrides: !ruby/object:Overrides::ResourceOverrides examples: - !ruby/object:Provider::Terraform::Examples name: "app_engine_standard_app_version" - primary_resource_id: "version_id" + primary_resource_id: "myapp_v1" ignore_read_extra: - - "noop_on_destroy" + - "delete_service_on_destroy" vars: project_id: "test-project" bucket_name: "appengine-static-content" @@ -102,9 +102,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides examples: - !ruby/object:Provider::Terraform::Examples name: "app_engine_application_url_dispatch_rules_basic" - primary_resource_id: "service_rules" + primary_resource_id: "web_service" vars: - project: "my-project" bucket_name: "appengine-test-bucket" # This is for copying files over diff --git a/templates/terraform/examples/app_engine_application_url_dispatch_rules_basic.tf.erb b/templates/terraform/examples/app_engine_application_url_dispatch_rules_basic.tf.erb index 7fc7237b0b5c..056bb4804083 100644 --- a/templates/terraform/examples/app_engine_application_url_dispatch_rules_basic.tf.erb +++ b/templates/terraform/examples/app_engine_application_url_dispatch_rules_basic.tf.erb @@ -1,43 +1,45 @@ -resource "google_storage_bucket" "bucket" { - name = "<%= ctx[:vars]['bucket_name'] %>" -} +resource "google_app_engine_application_url_dispatch_rules" "<%= ctx[:primary_resource_id] %>" { + dispatch_rules { + domain = "*" + path = "/*" + service = "default" + } -resource "google_storage_bucket_object" "object" { - name = "hello-world.zip" - bucket = "${google_storage_bucket.bucket.name}" - source = "./test-fixtures/appengine/hello-world.zip" + dispatch_rules { + domain = "*" + path = "/admin/*" + service = "${google_app_engine_standard_app_version.admin_v3.service}" + } } -resource "google_app_engine_standard_app_version" "myapp_v1" { - version_id = "v1" - service = "myapp" +resource "google_app_engine_standard_app_version" "admin_v3" { + version_id = "v3" + service = "admin" runtime = "nodejs10" - noop_on_destroy = true + entrypoint { shell = "node ./app.js" } + deployment { zip { - source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/hello-world.zip" - } + source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.object.name}" + } } + env_variables = { port = "8080" - } - depends_on = ["google_storage_bucket_object.object"] + } + noop_on_destroy = true } -resource "google_app_engine_application_url_dispatch_rules" "service_rules" { - # project = "my-project" - dispatch_rules { - domain = "*" - path = "/default/*" - service = "default" - } - dispatch_rules { - domain = "*" - path = "/myapp/*" - service = "${google_app_engine_standard_app_version.myapp_v1.service}" - } +resource "google_storage_bucket" "bucket" { + name = "<%= ctx[:vars]['bucket_name'] %>" +} + +resource "google_storage_bucket_object" "object" { + name = "hello-world.zip" + bucket = "${google_storage_bucket.bucket.name}" + source = "./test-fixtures/appengine/hello-world.zip" } diff --git a/templates/terraform/examples/app_engine_standard_app_version.tf.erb b/templates/terraform/examples/app_engine_standard_app_version.tf.erb index c888d360cd13..f9bf973843a5 100644 --- a/templates/terraform/examples/app_engine_standard_app_version.tf.erb +++ b/templates/terraform/examples/app_engine_standard_app_version.tf.erb @@ -1,65 +1,53 @@ -resource "google_storage_bucket" "bucket" { - name = "<%= ctx[:vars]['bucket_name'] %>" -} - -resource "google_storage_bucket_object" "object" { - name = "hello-world.zip" - bucket = "${google_storage_bucket.bucket.name}" - source = "./test-fixtures/appengine/hello-world.zip" -} - resource "google_app_engine_standard_app_version" "<%= ctx[:primary_resource_id] %>" { - version_id = "v2" - service = "default" - runtime = "nodejs10" - noop_on_destroy = true + version_id = "v1" + service = "myapp" + runtime = "nodejs10" + entrypoint { shell = "node ./app.js" } + deployment { zip { - source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/hello-world.zip" - } + source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.object.name}" + } } + env_variables = { port = "8080" - } - -} + } -resource "google_app_engine_standard_app_version" "myapp_v1" { - version_id = "v1" - service = "myapp" - runtime = "nodejs10" delete_service_on_destroy = true - entrypoint { - shell = "node ./app.js" - } - deployment { - zip { - source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/hello-world.zip" - } - } - env_variables = { - port = "8080" - } - depends_on = ["google_storage_bucket_object.object"] } + resource "google_app_engine_standard_app_version" "myapp_v2" { version_id = "v2" - service = "myapp" - runtime = "nodejs10" + service = "myapp" + runtime = "nodejs10" + entrypoint { shell = "node ./app.js" } + deployment { zip { - source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/hello-world.zip" - } + source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.object.name}" + } } + env_variables = { port = "8080" - } - depends_on = ["google_app_engine_standard_app_version.myapp_v1"] + } + noop_on_destroy = true +} + +resource "google_storage_bucket" "bucket" { + name = "<%= ctx[:vars]['bucket_name'] %>" +} + +resource "google_storage_bucket_object" "object" { + name = "hello-world.zip" + bucket = "${google_storage_bucket.bucket.name}" + source = "./test-fixtures/appengine/hello-world.zip" } From bcc98018df5f13b2c3250cf1eab66535dfb0517a Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Wed, 23 Oct 2019 16:04:24 -0700 Subject: [PATCH 20/37] Ansible - RuntimeConfig Variables (#2521) Merged PR #2521. --- build/ansible | 2 +- products/runtimeconfig/ansible.yaml | 8 +++-- .../runtimeconfig/ansible_version_added.yaml | 12 +++++++ products/runtimeconfig/api.yaml | 1 + .../examples/ansible/variable.yaml | 32 +++++++++++++++++++ .../helpers/ansible/variable_delete_config.py | 19 +++++++++++ 6 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 products/runtimeconfig/examples/ansible/variable.yaml create mode 100644 products/runtimeconfig/helpers/ansible/variable_delete_config.py diff --git a/build/ansible b/build/ansible index 941348992017..0ee1fe8fbbd5 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit 94134899201737f2ad596aed290ae02c630d7496 +Subproject commit 0ee1fe8fbbd52d3e83c7951b71513b357d08eb65 diff --git a/products/runtimeconfig/ansible.yaml b/products/runtimeconfig/ansible.yaml index 3f7be9489c73..41999518ad3f 100644 --- a/products/runtimeconfig/ansible.yaml +++ b/products/runtimeconfig/ansible.yaml @@ -17,10 +17,14 @@ datasources: !ruby/object:Overrides::ResourceOverrides facts: !ruby/object:Provider::Ansible::FactsOverride has_filters: false Variable: !ruby/object:Overrides::Ansible::ResourceOverride - exclude: true + facts: !ruby/object:Provider::Ansible::FactsOverride + has_filters: false overrides: !ruby/object:Overrides::ResourceOverrides Variable: !ruby/object:Overrides::Ansible::ResourceOverride - exclude: true + provider_helpers: + - 'products/runtimeconfig/helpers/ansible/variable_delete_config.py' + transport: !ruby/object:Overrides::Ansible::Transport + encoder: encode_request files: !ruby/object:Provider::Config::Files resource: <%= lines(indent(compile('provider/ansible/resource~compile.yaml'), 4)) -%> diff --git a/products/runtimeconfig/ansible_version_added.yaml b/products/runtimeconfig/ansible_version_added.yaml index 6232f3fb58a6..9bd8de31b8d8 100644 --- a/products/runtimeconfig/ansible_version_added.yaml +++ b/products/runtimeconfig/ansible_version_added.yaml @@ -2,6 +2,8 @@ :facts: :Config: :version_added: '2.10' + :Variable: + :version_added: '2.10' :regular: :Config: :version_added: '2.10' @@ -9,3 +11,13 @@ :version_added: '2.10' :name: :version_added: '2.10' + :Variable: + :version_added: '2.10' + :value: + :version_added: '2.10' + :text: + :version_added: '2.10' + :name: + :version_added: '2.10' + :config: + :version_added: '2.10' diff --git a/products/runtimeconfig/api.yaml b/products/runtimeconfig/api.yaml index 44c0e50c3944..0f8d2ffd8965 100644 --- a/products/runtimeconfig/api.yaml +++ b/products/runtimeconfig/api.yaml @@ -62,6 +62,7 @@ objects: The name of the variable resource. required: true input: true + pattern: projects/{{project}}/configs/{{config}}/variables/{{name}} - !ruby/object:Api::Type::String name: 'config' description: | diff --git a/products/runtimeconfig/examples/ansible/variable.yaml b/products/runtimeconfig/examples/ansible/variable.yaml new file mode 100644 index 000000000000..3f496bed8e16 --- /dev/null +++ b/products/runtimeconfig/examples/ansible/variable.yaml @@ -0,0 +1,32 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- !ruby/object:Provider::Ansible::Example +task: !ruby/object:Provider::Ansible::Task + name: gcp_runtimeconfig_variable + code: + name: prod-variables/hostname + config: 'my-config' + text: example.com + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +dependencies: + - !ruby/object:Provider::Ansible::Task + name: gcp_runtimeconfig_config + code: + name: 'my-config' + description: 'My config' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + register: config diff --git a/products/runtimeconfig/helpers/ansible/variable_delete_config.py b/products/runtimeconfig/helpers/ansible/variable_delete_config.py new file mode 100644 index 000000000000..3013c2e63498 --- /dev/null +++ b/products/runtimeconfig/helpers/ansible/variable_delete_config.py @@ -0,0 +1,19 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# `config` is a useful parameter for declarative syntax, but +# is not a part of the GCP API +def encode_request(request, module): + if 'config' in request: + del request['config'] + return request From c1cebaf1374b73ad7ee5cf660ec29fa2cfe5d6d8 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 24 Oct 2019 07:47:45 -0700 Subject: [PATCH 21/37] Bump NetworkEndpoint timeouts to 6m (#2525) Merged PR #2525. --- build/terraform | 2 +- build/terraform-beta | 2 +- products/compute/api.yaml | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index f771cd7dc0ec..f55f419d8cbe 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit f771cd7dc0ec0322c4d3b3a9ab4e2ad3eb347625 +Subproject commit f55f419d8cbeccc1ca18bd3e689d00bbd360f144 diff --git a/build/terraform-beta b/build/terraform-beta index 3e950ce27997..96ace9bdd73d 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 3e950ce27997d5917725e97d8e57a37c55c8e708 +Subproject commit 96ace9bdd73d8d26d960a93e6ce8b507c96d1e21 diff --git a/products/compute/api.yaml b/products/compute/api.yaml index ead81c8c1094..b54ab386f1f7 100644 --- a/products/compute/api.yaml +++ b/products/compute/api.yaml @@ -5641,6 +5641,10 @@ objects: path: 'name' base_url: 'projects/{{project}}/zones/{{zone}}/operations/{{op_id}}' wait_ms: 1000 + timeouts: !ruby/object:Api::Timeouts + insert_minutes: 6 + update_minutes: 6 + delete_minutes: 6 result: !ruby/object:Api::Async::Result path: 'targetLink' status: !ruby/object:Api::Async::Status From ec82cb103c18205fb2f0bc9289d76c10e7b7f150 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 24 Oct 2019 12:41:52 -0700 Subject: [PATCH 22/37] Make image license test import-style (#2531) Merged PR #2531. --- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- .../tests/resource_compute_image_test.go | 70 ++++++------------- 4 files changed, 24 insertions(+), 52 deletions(-) diff --git a/build/terraform b/build/terraform index f55f419d8cbe..7a0191dc498b 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit f55f419d8cbeccc1ca18bd3e689d00bbd360f144 +Subproject commit 7a0191dc498b9a868966f4509281a20aac6d3068 diff --git a/build/terraform-beta b/build/terraform-beta index 96ace9bdd73d..7bd68b90fe2e 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 96ace9bdd73d8d26d960a93e6ce8b507c96d1e21 +Subproject commit 7bd68b90fe2ed568fc11faeb6a2197df56ed10c3 diff --git a/build/terraform-mapper b/build/terraform-mapper index a788ab75a3d4..54004ba54ac1 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit a788ab75a3d4bbecb3c4f1f98f9f9e7a34f3c54b +Subproject commit 54004ba54ac16a9fe5b8284e64cee22a8526392c diff --git a/third_party/terraform/tests/resource_compute_image_test.go b/third_party/terraform/tests/resource_compute_image_test.go index f6fe5bfab89e..59a956375e19 100644 --- a/third_party/terraform/tests/resource_compute_image_test.go +++ b/third_party/terraform/tests/resource_compute_image_test.go @@ -13,8 +13,6 @@ import ( func TestAccComputeImage_withLicense(t *testing.T) { t.Parallel() - var image compute.Image - resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -22,16 +20,11 @@ func TestAccComputeImage_withLicense(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccComputeImage_license("image-test-" + acctest.RandString(10)), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeImageExists( - "google_compute_image.foobar", &image), - testAccCheckComputeImageDescription(&image, "description-test"), - testAccCheckComputeImageFamily(&image, "family-test"), - testAccCheckComputeImageContainsLabel(&image, "my-label", "my-label-value"), - testAccCheckComputeImageContainsLabel(&image, "empty-label", ""), - testAccCheckComputeImageContainsLicense(&image, "https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx"), - testAccCheckComputeImageHasComputedFingerprint(&image, "google_compute_image.foobar"), - ), + }, + { + ResourceName: "google_compute_image.foobar", + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -223,24 +216,6 @@ func testAccCheckComputeImageResolution(n string) resource.TestCheckFunc { } } -func testAccCheckComputeImageDescription(image *compute.Image, description string) resource.TestCheckFunc { - return func(s *terraform.State) error { - if image.Description != description { - return fmt.Errorf("Wrong image description: expected '%s' got '%s'", description, image.Description) - } - return nil - } -} - -func testAccCheckComputeImageFamily(image *compute.Image, family string) resource.TestCheckFunc { - return func(s *terraform.State) error { - if image.Family != family { - return fmt.Errorf("Wrong image family: expected '%s' got '%s'", family, image.Family) - } - return nil - } -} - func testAccCheckComputeImageContainsLabel(image *compute.Image, key string, value string) resource.TestCheckFunc { return func(s *terraform.State) error { v, ok := image.Labels[key] @@ -254,19 +229,6 @@ func testAccCheckComputeImageContainsLabel(image *compute.Image, key string, val } } -func testAccCheckComputeImageContainsLicense(image *compute.Image, expectedLicense string) resource.TestCheckFunc { - return func(s *terraform.State) error { - - for _, thisLicense := range image.Licenses { - if thisLicense == expectedLicense { - return nil - } - } - - return fmt.Errorf("Expected license '%s' was not found", expectedLicense) - } -} - func testAccCheckComputeImageDoesNotContainLabel(image *compute.Image, key string) resource.TestCheckFunc { return func(s *terraform.State) error { if v, ok := image.Labels[key]; ok { @@ -346,21 +308,30 @@ resource "google_compute_image" "foobar" { func testAccComputeImage_license(name string) string { return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "disk-test-%s" + zone = "us-central1-a" + image = "${data.google_compute_image.my_image.self_link}" +} + resource "google_compute_image" "foobar" { name = "%s" description = "description-test" - family = "family-test" - raw_disk { - source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.tar.gz" - } + source_disk = "${google_compute_disk.foobar.self_link}" + labels = { my-label = "my-label-value" empty-label = "" } licenses = [ - "https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx", + "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/licenses/debian-9-stretch", ] -}`, name) +}`, name, name) } func testAccComputeImage_update(name string) string { @@ -391,6 +362,7 @@ resource "google_compute_disk" "foobar" { zone = "us-central1-a" image = "${data.google_compute_image.my_image.self_link}" } + resource "google_compute_image" "foobar" { name = "image-test-%s" source_disk = "${google_compute_disk.foobar.self_link}" From c87ea1c2b08a60300763cb17534c930994d099ae Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 24 Oct 2019 12:57:44 -0700 Subject: [PATCH 23/37] Fix `google_project` default network deletion by correcting URL (#2529) Merged PR #2529. --- build/terraform | 2 +- build/terraform-beta | 2 +- third_party/terraform/resources/resource_google_project.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/terraform b/build/terraform index 7a0191dc498b..927939580da9 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 7a0191dc498b9a868966f4509281a20aac6d3068 +Subproject commit 927939580da9860cef2e321b9b467e8d9ff902a0 diff --git a/build/terraform-beta b/build/terraform-beta index 7bd68b90fe2e..b3dbc6e5f65b 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 7bd68b90fe2ed568fc11faeb6a2197df56ed10c3 +Subproject commit b3dbc6e5f65be9a19e943447338d2a02fd26169f diff --git a/third_party/terraform/resources/resource_google_project.go b/third_party/terraform/resources/resource_google_project.go index 02e7247c2d10..5e03f413f2f3 100644 --- a/third_party/terraform/resources/resource_google_project.go +++ b/third_party/terraform/resources/resource_google_project.go @@ -496,7 +496,7 @@ func resourceProjectImportState(d *schema.ResourceData, meta interface{}) ([]*sc // Delete a compute network along with the firewall rules inside it. func forceDeleteComputeNetwork(d *schema.ResourceData, config *Config, projectId, networkName string) error { - networkLink, err := replaceVars(d, config, fmt.Sprintf("{{ComputeBasePath}}%s/global/networks/%s", projectId, networkName)) + networkLink, err := replaceVars(d, config, fmt.Sprintf("{{ComputeBasePath}}projects/%s/global/networks/%s", projectId, networkName)) if err != nil { return err } From d935f2ed7366d6e917a704d3fccdc9fc86f29f25 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 24 Oct 2019 13:18:58 -0700 Subject: [PATCH 24/37] Fix instance migrate test self link comparisons (#2530) Merged PR #2530. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resource_compute_instance_migrate_test.go | 37 +++++++++++++++---- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/build/terraform b/build/terraform index 927939580da9..c28772eb9416 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 927939580da9860cef2e321b9b467e8d9ff902a0 +Subproject commit c28772eb9416d2dcb0711937f86892a6c2ebe253 diff --git a/build/terraform-beta b/build/terraform-beta index b3dbc6e5f65b..03dff3130d34 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit b3dbc6e5f65be9a19e943447338d2a02fd26169f +Subproject commit 03dff3130d34f5a61b138623f61fe36841dec7ca diff --git a/third_party/terraform/tests/resource_compute_instance_migrate_test.go b/third_party/terraform/tests/resource_compute_instance_migrate_test.go index f3f4e6a99735..1d0a0ef5f677 100644 --- a/third_party/terraform/tests/resource_compute_instance_migrate_test.go +++ b/third_party/terraform/tests/resource_compute_instance_migrate_test.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "os" + "strings" "testing" "google.golang.org/api/compute/v1" @@ -863,18 +864,38 @@ func runInstanceMigrateTest(t *testing.T, id, testName string, version int, attr } for k, v := range expected { - if attributes[k] != v { - t.Fatalf( - "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", - testName, k, expected[k], k, attributes[k], attributes) + // source is the only self link, so compare by relpaths if source is being + // compared + if strings.HasSuffix(k, "source") { + if !compareSelfLinkOrResourceName("", attributes[k], v, nil) && attributes[k] != v { + t.Fatalf( + "bad uri: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + testName, k, expected[k], k, attributes[k], attributes) + } + } else { + if attributes[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + testName, k, expected[k], k, attributes[k], attributes) + } } } for k, v := range attributes { - if expected[k] != v { - t.Fatalf( - "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", - testName, k, expected[k], k, attributes[k], attributes) + // source is the only self link, so compare by relpaths if source is being + // compared + if strings.HasSuffix(k, "source") { + if !compareSelfLinkOrResourceName("", expected[k], v, nil) && expected[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + testName, k, expected[k], k, attributes[k], expected) + } + } else { + if expected[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + testName, k, expected[k], k, attributes[k], expected) + } } } } From e3b1400283b2333277ab550d74ece746601ce439 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 24 Oct 2019 13:30:37 -0700 Subject: [PATCH 25/37] Make GKE taint fields available in GA (#2523) Merged PR #2523. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resource_container_cluster_test.go.erb | 76 ++++++------------- .../resource_container_node_pool_test.go.erb | 68 +++++------------ .../terraform/utils/node_config.go.erb | 9 +-- .../docs/r/container_cluster.html.markdown | 11 ++- 6 files changed, 52 insertions(+), 116 deletions(-) diff --git a/build/terraform b/build/terraform index c28772eb9416..3116d2a23902 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit c28772eb9416d2dcb0711937f86892a6c2ebe253 +Subproject commit 3116d2a23902e91bd14a9bdb4beae162c9819923 diff --git a/build/terraform-beta b/build/terraform-beta index 03dff3130d34..92b14b47b7e4 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 03dff3130d34f5a61b138623f61fe36841dec7ca +Subproject commit 92b14b47b7e4ab0f68cd42c9a2432033f284de64 diff --git a/third_party/terraform/tests/resource_container_cluster_test.go.erb b/third_party/terraform/tests/resource_container_cluster_test.go.erb index 4d7990c33210..720f45b06459 100644 --- a/third_party/terraform/tests/resource_container_cluster_test.go.erb +++ b/third_party/terraform/tests/resource_container_cluster_test.go.erb @@ -814,33 +814,6 @@ func TestAccContainerCluster_withNodeConfigScopeAlias(t *testing.T) { }) } -<% unless version.nil? || version == 'ga' -%> -func TestAccContainerCluster_withNodeConfigTaints(t *testing.T) { - t.Parallel() - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckContainerClusterDestroy, - Steps: []resource.TestStep{ - { - Config: testAccContainerCluster_withNodeConfigTaints(), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("google_container_cluster.with_node_config", "node_config.0.taint.#", "2"), - ), - }, - { - ResourceName: "google_container_cluster.with_node_config", - ImportStateIdPrefix: "us-central1-f/", - ImportState: true, - ImportStateVerify: true, - }, - // Once taints are in GA, consider merging this test with the _withNodeConfig test. - }, - }) -} -<% end -%> - func TestAccContainerCluster_withNodeConfigShieldedInstanceConfig(t *testing.T) { t.Parallel() @@ -2402,6 +2375,18 @@ resource "google_container_cluster" "with_node_config" { preemptible = true min_cpu_platform = "Intel Broadwell" + taint { + key = "taint_key" + value = "taint_value" + effect = "PREFER_NO_SCHEDULE" + } + + taint { + key = "taint_key2" + value = "taint_value2" + effect = "NO_EXECUTE" + } + // Updatable fields image_type = "COS" } @@ -2438,6 +2423,18 @@ resource "google_container_cluster" "with_node_config" { preemptible = true min_cpu_platform = "Intel Broadwell" + taint { + key = "taint_key" + value = "taint_value" + effect = "PREFER_NO_SCHEDULE" + } + + taint { + key = "taint_key2" + value = "taint_value2" + effect = "NO_EXECUTE" + } + // Updatable fields image_type = "UBUNTU" } @@ -2459,31 +2456,6 @@ resource "google_container_cluster" "with_node_config_scope_alias" { }`, acctest.RandString(10)) } -<% unless version.nil? || version == 'ga' -%> -func testAccContainerCluster_withNodeConfigTaints() string { - return fmt.Sprintf(` -resource "google_container_cluster" "with_node_config" { - name = "cluster-test-%s" - zone = "us-central1-f" - initial_node_count = 1 - - node_config { - taint { - key = "taint_key" - value = "taint_value" - effect = "PREFER_NO_SCHEDULE" - } - taint { - key = "taint_key2" - value = "taint_value2" - effect = "NO_EXECUTE" - } - } -}`, acctest.RandString(10)) -} -<% end -%> - - func testAccContainerCluster_withNodeConfigShieldedInstanceConfig(clusterName string) string { return fmt.Sprintf(` resource "google_container_cluster" "with_node_config" { diff --git a/third_party/terraform/tests/resource_container_node_pool_test.go.erb b/third_party/terraform/tests/resource_container_node_pool_test.go.erb index 16cd9d0ab112..013e651aa1a7 100644 --- a/third_party/terraform/tests/resource_container_node_pool_test.go.erb +++ b/third_party/terraform/tests/resource_container_node_pool_test.go.erb @@ -163,35 +163,6 @@ func TestAccContainerNodePool_withNodeConfig(t *testing.T) { }) } -<% unless version.nil? || version == 'ga' -%> -func TestAccContainerNodePool_withNodeConfigTaints(t *testing.T) { - t.Parallel() - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckContainerNodePoolDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccContainerNodePool_withNodeConfigTaints(), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("google_container_node_pool.np_with_node_config", "node_config.0.taint.#", "2"), - ), - }, - { - ResourceName: "google_container_node_pool.np_with_node_config", - ImportState: true, - ImportStateVerify: true, - // autoscaling.# = 0 is equivalent to no autoscaling at all, - // but will still cause an import diff - ImportStateVerifyIgnore: []string{"autoscaling.#"}, - }, - // Once taints are in GA, consider merging this test with the _withNodeConfig test. - }, - }) -} -<% end -%> - <% unless version.nil? || version == 'ga' -%> func TestAccContainerNodePool_withWorkloadMetadataConfig(t *testing.T) { t.Parallel() @@ -1034,6 +1005,18 @@ resource "google_container_node_pool" "np_with_node_config" { preemptible = true min_cpu_platform = "Intel Broadwell" + taint { + key = "taint_key" + value = "taint_value" + effect = "PREFER_NO_SCHEDULE" + } + + taint { + key = "taint_key2" + value = "taint_value2" + effect = "NO_EXECUTE" + } + // Updatable fields image_type = "COS" } @@ -1064,40 +1047,23 @@ resource "google_container_node_pool" "np_with_node_config" { preemptible = true min_cpu_platform = "Intel Broadwell" - // Updatable fields - image_type = "UBUNTU" - } -}`, cluster, nodePool) -} - -<% unless version.nil? || version == 'ga' -%> -func testAccContainerNodePool_withNodeConfigTaints() string { - return fmt.Sprintf(` -resource "google_container_cluster" "cluster" { - name = "tf-cluster-nodepool-test-%s" - zone = "us-central1-a" - initial_node_count = 1 -} -resource "google_container_node_pool" "np_with_node_config" { - name = "tf-nodepool-test-%s" - zone = "us-central1-a" - cluster = "${google_container_cluster.cluster.name}" - initial_node_count = 1 - node_config { taint { key = "taint_key" value = "taint_value" effect = "PREFER_NO_SCHEDULE" } + taint { key = "taint_key2" value = "taint_value2" effect = "NO_EXECUTE" } + + // Updatable fields + image_type = "UBUNTU" } -}`, acctest.RandString(10), acctest.RandString(10)) +}`, cluster, nodePool) } -<% end -%> <% unless version.nil? || version == 'ga' -%> func testAccContainerNodePool_withWorkloadMetadataConfig() string { diff --git a/third_party/terraform/utils/node_config.go.erb b/third_party/terraform/utils/node_config.go.erb index b966d11d4ba6..ada68dab0f43 100644 --- a/third_party/terraform/utils/node_config.go.erb +++ b/third_party/terraform/utils/node_config.go.erb @@ -173,9 +173,6 @@ var schemaNodeConfig = &schema.Schema{ }, "taint": { -<% if version.nil? || version == 'ga' -%> - Removed: "This field is in beta. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", -<% end -%> Type: schema.TypeList, Optional: true, // Computed=true because GKE Sandbox will automatically add taints to nodes that can/cannot run sandboxed pods. @@ -348,7 +345,6 @@ func expandNodeConfig(v interface{}) *containerBeta.NodeConfig { nc.MinCpuPlatform = v.(string) } -<% unless version == 'ga' -%> if v, ok := nodeConfig["taint"]; ok && len(v.([]interface{})) > 0 { taints := v.([]interface{}) nodeTaints := make([]*containerBeta.NodeTaint, 0, len(taints)) @@ -363,7 +359,6 @@ func expandNodeConfig(v interface{}) *containerBeta.NodeConfig { } nc.Taints = nodeTaints } -<% end -%> <% unless version == 'ga' -%> if v, ok := nodeConfig["workload_metadata_config"]; ok && len(v.([]interface{})) > 0 { @@ -405,8 +400,8 @@ func flattenNodeConfig(c *containerBeta.NodeConfig) []map[string]interface{} { "preemptible": c.Preemptible, "min_cpu_platform": c.MinCpuPlatform, "shielded_instance_config": flattenShieldedInstanceConfig(c.ShieldedInstanceConfig), -<% unless version == 'ga' -%> "taint": flattenTaints(c.Taints), +<% unless version == 'ga' -%> "workload_metadata_config": flattenWorkloadMetadataConfig(c.WorkloadMetadataConfig), "sandbox_config": flattenSandboxConfig(c.SandboxConfig), <% end -%> @@ -441,7 +436,6 @@ func flattenShieldedInstanceConfig(c *containerBeta.ShieldedInstanceConfig) []ma return result } -<% unless version.nil? || version == 'ga' -%> func flattenTaints(c []*containerBeta.NodeTaint) []map[string]interface{} { result := []map[string]interface{}{} for _, taint := range c { @@ -453,7 +447,6 @@ func flattenTaints(c []*containerBeta.NodeTaint) []map[string]interface{} { } return result } -<% end -%> <% unless version.nil? || version == 'ga' -%> func flattenWorkloadMetadataConfig(c *containerBeta.WorkloadMetadataConfig) []map[string]interface{} { diff --git a/third_party/terraform/website/docs/r/container_cluster.html.markdown b/third_party/terraform/website/docs/r/container_cluster.html.markdown index c95311f9eb21..6193ac57f9f4 100644 --- a/third_party/terraform/website/docs/r/container_cluster.html.markdown +++ b/third_party/terraform/website/docs/r/container_cluster.html.markdown @@ -567,9 +567,14 @@ The `node_config` block supports: * `tags` - (Optional) The list of instance tags applied to all nodes. Tags are used to identify valid sources or targets for network firewalls. -* `taint` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) List of - [kubernetes taints](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) - to apply to each node. Structure is documented below. +* `taint` - (Optional) A list of [Kubernetes taints](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) +to apply to nodes. GKE's API can only set this field on cluster creation. +However, GKE will add taints to your nodes if you enable certain features such +as GPUs. If this field is set, any diffs on this field will cause Terraform to +recreate the underlying resource. Taint values can be updated safely in +Kubernetes (eg. through `kubectl`), and it's recommended that you do not use +this field to manage taints. If you do, `lifecycle.ignore_changes` is +recommended. Structure is documented below. * `workload_metadata_config` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) Metadata configuration to expose to workloads on the node pool. Structure is documented below. From 862e9c2046fb386fb55a6254973c41b0befcfc57 Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Thu, 24 Oct 2019 21:41:29 +0000 Subject: [PATCH 26/37] Added support for GCS IAM policies to Validator (#2497) Merged PR #2497. --- provider/terraform_object_library.rb | 2 + third_party/validator/storage_bucket_iam.go | 52 +++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 third_party/validator/storage_bucket_iam.go diff --git a/provider/terraform_object_library.rb b/provider/terraform_object_library.rb index 9bc2a3529569..2567ec02e904 100644 --- a/provider/terraform_object_library.rb +++ b/provider/terraform_object_library.rb @@ -78,6 +78,8 @@ def copy_common_files(output_folder) 'third_party/validator/sql_database_instance.go'], ['google/storage_bucket.go', 'third_party/validator/storage_bucket.go'], + ['google/storage_bucket_iam.go', + 'third_party/validator/storage_bucket_iam.go'], ['google/iam_helpers.go', 'third_party/validator/iam_helpers.go'], ['google/iam_helpers_test.go', diff --git a/third_party/validator/storage_bucket_iam.go b/third_party/validator/storage_bucket_iam.go new file mode 100644 index 000000000000..adb4946cd065 --- /dev/null +++ b/third_party/validator/storage_bucket_iam.go @@ -0,0 +1,52 @@ +package google + +import "fmt" + +func GetBucketIamPolicyCaiObject(d TerraformResourceData, config *Config) (Asset, error) { + return newBucketIamAsset(d, config, expandIamPolicyBindings) +} + +func GetBucketIamBindingCaiObject(d TerraformResourceData, config *Config) (Asset, error) { + return newBucketIamAsset(d, config, expandIamRoleBindings) +} + +func GetBucketIamMemberCaiObject(d TerraformResourceData, config *Config) (Asset, error) { + return newBucketIamAsset(d, config, expandIamMemberBindings) +} + +func MergeBucketIamPolicy(existing, incoming Asset) Asset { + existing.IAMPolicy = incoming.IAMPolicy + return existing +} + +func MergeBucketIamBinding(existing, incoming Asset) Asset { + return mergeIamAssets(existing, incoming, mergeAuthoritativeBindings) +} + +func MergeBucketIamMember(existing, incoming Asset) Asset { + return mergeIamAssets(existing, incoming, mergeAdditiveBindings) +} + +func newBucketIamAsset( + d TerraformResourceData, + config *Config, + expandBindings func(d TerraformResourceData) ([]IAMBinding, error), +) (Asset, error) { + bindings, err := expandBindings(d) + if err != nil { + return Asset{}, fmt.Errorf("expanding bindings: %v", err) + } + + name, err := assetName(d, config, "//storage.googleapis.com/{{name}}") + if err != nil { + return Asset{}, err + } + + return Asset{ + Name: name, + Type: "storage.googleapis.com/Bucket", + IAMPolicy: &IAMPolicy{ + Bindings: bindings, + }, + }, nil +} From d7d7fe2ec2f164d70f3b7ddb013b41349cb712b0 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Thu, 24 Oct 2019 16:07:41 -0700 Subject: [PATCH 27/37] Pubsub expiration policy docs are unclear about what happens. (#2532) Merged PR #2532. --- build/ansible | 2 +- build/inspec | 2 +- build/terraform | 2 +- build/terraform-beta | 2 +- products/pubsub/api.yaml | 11 +++++------ 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/build/ansible b/build/ansible index 0ee1fe8fbbd5..2780d4eadf87 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit 0ee1fe8fbbd52d3e83c7951b71513b357d08eb65 +Subproject commit 2780d4eadf87b64cdbe5c530e826517dbcf6f1cc diff --git a/build/inspec b/build/inspec index 09889ea10ccc..d8801b70953f 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 09889ea10ccc9cdab7bcd5cfe22792f3888a9011 +Subproject commit d8801b70953fc6197fdfe876ae28dde36f63c5fa diff --git a/build/terraform b/build/terraform index 3116d2a23902..518ee73c32db 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 3116d2a23902e91bd14a9bdb4beae162c9819923 +Subproject commit 518ee73c32dbcc87aeb6975c86e1340dcb1f5adc diff --git a/build/terraform-beta b/build/terraform-beta index 92b14b47b7e4..e82649e68920 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 92b14b47b7e4ab0f68cd42c9a2432033f284de64 +Subproject commit e82649e68920cbf3334a3098838d8f60977fd653 diff --git a/products/pubsub/api.yaml b/products/pubsub/api.yaml index e9ee85d31b12..2af625676fc1 100644 --- a/products/pubsub/api.yaml +++ b/products/pubsub/api.yaml @@ -232,16 +232,15 @@ objects: A subscription is considered active as long as any connected subscriber is successfully consuming messages from the subscription or is issuing operations on the subscription. If expirationPolicy is not set, a default - policy with ttl of 31 days will be used. The minimum allowed value for - expirationPolicy.ttl is 1 day. + policy with ttl of 31 days will be used. If it is set but left empty, the + resource never expires. The minimum allowed value for expirationPolicy.ttl + is 1 day. properties: - !ruby/object:Api::Type::String name: 'ttl' description: | Specifies the "time-to-live" duration for an associated resource. The - resource expires if it is not active for a period of ttl. The definition - of "activity" depends on the type of the associated resource. The minimum - and maximum allowed values for ttl depend on the type of the associated - resource, as well. If ttl is not set, the associated resource never expires. + resource expires if it is not active for a period of ttl. + If ttl is not set, the associated resource never expires. A duration in seconds with up to nine fractional digits, terminated by 's'. Example - "3.5s". From b77dd5a20def0ad004ca816950c5831450a0afd4 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Thu, 24 Oct 2019 16:39:22 -0700 Subject: [PATCH 28/37] Add the ability to use the GKE recurring maintenance window in beta. (#2514) Merged PR #2514. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resource_container_cluster.go.erb | 150 ++++++++++++++---- .../resource_container_cluster_test.go.erb | 88 +++++++++- .../docs/r/container_cluster.html.markdown | 22 ++- 5 files changed, 227 insertions(+), 37 deletions(-) diff --git a/build/terraform b/build/terraform index 518ee73c32db..264f981107ba 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 518ee73c32dbcc87aeb6975c86e1340dcb1f5adc +Subproject commit 264f981107bae8e34ed44e04cf380f251029103c diff --git a/build/terraform-beta b/build/terraform-beta index e82649e68920..b40632cd5c53 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit e82649e68920cbf3334a3098838d8f60977fd653 +Subproject commit b40632cd5c53fc2c4ac86cf9ae70c2b48a47c48f diff --git a/third_party/terraform/resources/resource_container_cluster.go.erb b/third_party/terraform/resources/resource_container_cluster.go.erb index 3af8db7155a4..3f600bc5aed2 100644 --- a/third_party/terraform/resources/resource_container_cluster.go.erb +++ b/third_party/terraform/resources/resource_container_cluster.go.erb @@ -48,6 +48,17 @@ var ( ipAllocationRangeFields = []string{"ip_allocation_policy.0.cluster_secondary_range_name", "ip_allocation_policy.0.services_secondary_range_name"} ) +<% unless version == 'ga' -%> +<%# This is here because it is unused in ga, which trips up linters - when it starts being used, move it to validation.go. %> +func validateRFC3339Date(v interface{}, k string) (warnings []string, errors []error) { + _, err := time.Parse(time.RFC3339, v.(string)) + if err != nil { + errors = append(errors, err) + } + return +} +<% end %> + func resourceContainerCluster() *schema.Resource { return &schema.Resource{ Create: resourceContainerClusterCreate, @@ -390,7 +401,11 @@ func resourceContainerCluster() *schema.Resource { Schema: map[string]*schema.Schema{ "daily_maintenance_window": { Type: schema.TypeList, +<% if version == 'ga' -%> Required: true, +<% else %> + Optional: true, +<% end %> MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -407,6 +422,32 @@ func resourceContainerCluster() *schema.Resource { }, }, }, +<% unless version == 'ga' -%> + "recurring_window": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ConflictsWith: []string{"maintenance_policy.0.daily_maintenance_window"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "start_time": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateRFC3339Date, + }, + "end_time": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateRFC3339Date, + }, + "recurrence": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, +<% end %> }, }, }, @@ -957,7 +998,7 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er cluster := &containerBeta.Cluster{ Name: clusterName, InitialNodeCount: int64(d.Get("initial_node_count").(int)), - MaintenancePolicy: expandMaintenancePolicy(d.Get("maintenance_policy")), + MaintenancePolicy: expandMaintenancePolicy(d, meta), MasterAuthorizedNetworksConfig: expandMasterAuthorizedNetworksConfig(d.Get("master_authorized_networks_config")), InitialClusterVersion: d.Get("min_master_version").(string), ClusterIpv4Cidr: d.Get("cluster_ipv4_cidr").(string), @@ -1486,15 +1527,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er <% end -%> if d.HasChange("maintenance_policy") { - var req *containerBeta.SetMaintenancePolicyRequest - if mp, ok := d.GetOk("maintenance_policy"); ok { - req = &containerBeta.SetMaintenancePolicyRequest{ - MaintenancePolicy: expandMaintenancePolicy(mp), - } - } else { - req = &containerBeta.SetMaintenancePolicyRequest{ - NullFields: []string{"MaintenancePolicy"}, - } + req := &containerBeta.SetMaintenancePolicyRequest{ + MaintenancePolicy: expandMaintenancePolicy(d, meta), } updateF := func() error { @@ -2226,22 +2260,64 @@ func expandIPAllocationPolicy(configured interface{}) *containerBeta.IPAllocatio } } -func expandMaintenancePolicy(configured interface{}) *containerBeta.MaintenancePolicy { +func expandMaintenancePolicy(d *schema.ResourceData, meta interface{}) *containerBeta.MaintenancePolicy { + config := meta.(*Config) + // We have to perform a full Get() as part of this, to get the fingerprint. We can't do this + // at any other time, because the fingerprint update might happen between plan and apply. + // We can omit error checks, since to have gotten this far, a project is definitely configured. + project, _ := getProject(d, config) + location, _ := getLocation(d, config) + clusterName := d.Get("name").(string) + name := containerClusterFullName(project, location, clusterName) + cluster, _ := config.clientContainerBeta.Projects.Locations.Clusters.Get(name).Do() + resourceVersion := "" + // If the cluster doesn't exist or if there is a read error of any kind, we will pass in an empty + // resourceVersion. If there happens to be a change to maintenance policy, we will fail at that + // point. This is a compromise between code cleanliness and a slightly worse user experience in + // an unlikely error case - we choose code cleanliness. + if cluster != nil && cluster.MaintenancePolicy != nil { + resourceVersion = cluster.MaintenancePolicy.ResourceVersion + } + + configured := d.Get("maintenance_policy") l := configured.([]interface{}) if len(l) == 0 || l[0] == nil { - return nil + return &containerBeta.MaintenancePolicy{ + ResourceVersion: resourceVersion, + } } - maintenancePolicy := l[0].(map[string]interface{}) - dailyMaintenanceWindow := maintenancePolicy["daily_maintenance_window"].([]interface{})[0].(map[string]interface{}) - startTime := dailyMaintenanceWindow["start_time"].(string) - return &containerBeta.MaintenancePolicy{ - Window: &containerBeta.MaintenanceWindow{ - DailyMaintenanceWindow: &containerBeta.DailyMaintenanceWindow{ - StartTime: startTime, + + if dailyMaintenanceWindow, ok := maintenancePolicy["daily_maintenance_window"]; ok && len(dailyMaintenanceWindow.([]interface{})) > 0 { + dmw := dailyMaintenanceWindow.([]interface{})[0].(map[string]interface{}) + startTime := dmw["start_time"].(string) + return &containerBeta.MaintenancePolicy{ + Window: &containerBeta.MaintenanceWindow{ + DailyMaintenanceWindow: &containerBeta.DailyMaintenanceWindow{ + StartTime: startTime, + }, }, - }, + ResourceVersion: resourceVersion, + } } +<% unless version == 'ga' -%> + if recurringWindow, ok := maintenancePolicy["recurring_window"]; ok && len(recurringWindow.([]interface{})) > 0 { + rw := recurringWindow.([]interface{})[0].(map[string]interface{}) + return &containerBeta.MaintenancePolicy{ + Window: &containerBeta.MaintenanceWindow{ + RecurringWindow: &containerBeta.RecurringTimeWindow{ + Window: &containerBeta.TimeWindow{ + StartTime: rw["start_time"].(string), + EndTime: rw["end_time"].(string), + }, + Recurrence: rw["recurrence"].(string), + }, + }, + ResourceVersion: resourceVersion, + } + } +<% end %> + return nil } <% unless version == 'ga' -%> @@ -2672,19 +2748,37 @@ func flattenIPAllocationPolicy(c *containerBeta.Cluster, d *schema.ResourceData, } func flattenMaintenancePolicy(mp *containerBeta.MaintenancePolicy) []map[string]interface{} { - if mp == nil || mp.Window == nil || mp.Window.DailyMaintenanceWindow == nil { + if mp == nil || mp.Window == nil { return nil } - return []map[string]interface{}{ - { - "daily_maintenance_window": []map[string]interface{}{ - { - "start_time": mp.Window.DailyMaintenanceWindow.StartTime, - "duration": mp.Window.DailyMaintenanceWindow.Duration, + if mp.Window.DailyMaintenanceWindow != nil { + return []map[string]interface{}{ + { + "daily_maintenance_window": []map[string]interface{}{ + { + "start_time": mp.Window.DailyMaintenanceWindow.StartTime, + "duration": mp.Window.DailyMaintenanceWindow.Duration, + }, }, }, - }, + } } +<% unless version == 'ga' -%> + if mp.Window.RecurringWindow != nil { + return []map[string]interface{}{ + { + "recurring_window": []map[string]interface{}{ + { + "start_time": mp.Window.RecurringWindow.Window.StartTime, + "end_time": mp.Window.RecurringWindow.Window.EndTime, + "recurrence": mp.Window.RecurringWindow.Recurrence, + }, + }, + }, + } + } +<% end %> + return nil } func flattenMasterAuth(ma *containerBeta.MasterAuth) []map[string]interface{} { diff --git a/third_party/terraform/tests/resource_container_cluster_test.go.erb b/third_party/terraform/tests/resource_container_cluster_test.go.erb index 720f45b06459..8c43d29fdf3d 100644 --- a/third_party/terraform/tests/resource_container_cluster_test.go.erb +++ b/third_party/terraform/tests/resource_container_cluster_test.go.erb @@ -1211,6 +1211,55 @@ func TestAccContainerCluster_withMaintenanceWindow(t *testing.T) { }) } +<% unless version == 'ga' -%> + +func TestAccContainerCluster_withRecurringMaintenanceWindow(t *testing.T) { + t.Parallel() + clusterName := acctest.RandString(10) + resourceName := "google_container_cluster.with_recurring_maintenance_window" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withRecurringMaintenanceWindow(clusterName, "2019-01-01T00:00:00Z", "2019-01-02T00:00:00Z"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr(resourceName, + "maintenance_policy.0.daily_maintenance_window.0.start_time"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerCluster_withRecurringMaintenanceWindow(clusterName, "", ""), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr(resourceName, + "maintenance_policy.0.daily_maintenance_window.0.start_time"), + resource.TestCheckNoResourceAttr(resourceName, + "maintenance_policy.0.recurring_window.0.start_time"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + // maintenance_policy.# = 0 is equivalent to no maintenance policy at all, + // but will still cause an import diff + ImportStateVerifyIgnore: []string{"maintenance_policy.#"}, + }, + }, + }) +} + +<% end %> + func TestAccContainerCluster_withIPAllocationPolicy_existingSecondaryRanges(t *testing.T) { t.Parallel() @@ -2039,7 +2088,7 @@ resource "google_container_cluster" "with_release_channel" { initial_node_count = 1 release_channel { - channel = "%s" + channel = "%s" } }`, clusterName, channel) } @@ -2908,6 +2957,33 @@ resource "google_container_cluster" "with_maintenance_window" { }`, clusterName, maintenancePolicy) } + +<% unless version == 'ga' -%> +func testAccContainerCluster_withRecurringMaintenanceWindow(clusterName string, startTime, endTime string) string { + maintenancePolicy := "" + if len(startTime) > 0 { + maintenancePolicy = fmt.Sprintf(` + maintenance_policy { + recurring_window { + start_time = "%s" + end_time = "%s" + recurrence = "FREQ=DAILY" + } + }`, startTime, endTime) + } + + return fmt.Sprintf(` +resource "google_container_cluster" "with_recurring_maintenance_window" { + name = "cluster-test-%s" + zone = "us-central1-a" + initial_node_count = 1 + + %s +}`, clusterName, maintenancePolicy) + +} +<% end %> + func testAccContainerCluster_withIPAllocationPolicy_existingSecondaryRanges(cluster string) string { return fmt.Sprintf(` resource "google_compute_network" "container_network" { @@ -3413,8 +3489,8 @@ resource "google_container_cluster" "cidr_error_overlap" { initial_node_count = 1 ip_allocation_policy { - cluster_ipv4_cidr_block = "10.0.0.0/16" - services_ipv4_cidr_block = "10.1.0.0/16" + cluster_ipv4_cidr_block = "10.0.0.0/16" + services_ipv4_cidr_block = "10.1.0.0/16" } } `, initConfig, secondCluster) @@ -3437,11 +3513,11 @@ data "google_project" "project" {} data "google_iam_policy" "test_kms_binding" { binding { - role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" - members = [ + members = [ "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", - ] + ] } } diff --git a/third_party/terraform/website/docs/r/container_cluster.html.markdown b/third_party/terraform/website/docs/r/container_cluster.html.markdown index 6193ac57f9f4..9427dedae7dd 100644 --- a/third_party/terraform/website/docs/r/container_cluster.html.markdown +++ b/third_party/terraform/website/docs/r/container_cluster.html.markdown @@ -402,7 +402,7 @@ The `authenticator_groups_config` block supports: The `maintenance_policy` block supports: -* `daily_maintenance_window` - (Required) Time window specified for daily maintenance operations. +* `daily_maintenance_window` - (Required in GA, Optional in Beta) Time window specified for daily maintenance operations. Specify `start_time` in [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) format "HH:MM”, where HH : \[00-23\] and MM : \[00-59\] GMT. For example: @@ -414,6 +414,26 @@ maintenance_policy { } ``` +* `recurring_window` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) Time window for +recurring maintenance operations. + +Specify `start_time` and `end_time` in [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) date format. The start time's date is +the initial date that the window starts, and the end time is used for calculating duration. Specify `recurrence` in +[RFC5545](https://tools.ietf.org/html/rfc5545#section-3.8.5.3) RRULE format, to specify when this recurs. + +For example: +``` +maintenance_policy { + recurring_window { + start_time = "2019-01-01T03:00" + end_time = "2019-01-01T06:00" + recurrence = "FREQ=DAILY" + } +} +``` + +In beta, one or the other of `recurring_window` and `daily_maintenance_window` is required if a `maintenance_policy` block is supplied. + The `ip_allocation_policy` block supports: * `use_ip_aliases` - (Optional) Whether alias IPs will be used for pod IPs in From 943ee03cf312e98368dd4f823c6d5ff4fb26ea97 Mon Sep 17 00:00:00 2001 From: Paddy Date: Fri, 25 Oct 2019 09:20:57 -0700 Subject: [PATCH 29/37] Add templates for the changelog generator. (#2524) Merged PR #2524. --- .ci/changelog.tmpl | 66 +++++++++++++++++++++++++++++++++++++++++++ .ci/release-note.tmpl | 1 + 2 files changed, 67 insertions(+) create mode 100644 .ci/changelog.tmpl create mode 100644 .ci/release-note.tmpl diff --git a/.ci/changelog.tmpl b/.ci/changelog.tmpl new file mode 100644 index 000000000000..7db175f51030 --- /dev/null +++ b/.ci/changelog.tmpl @@ -0,0 +1,66 @@ +{{- $notes := newStringList -}} +{{- $unknown := newStringList -}} +{{- $breaking := newStringList -}} +{{- $deprecations := newStringList -}} +{{- $features := newStringList -}} +{{- $improvements := newStringList -}} +{{- $bugs := newStringList -}} +{{- range . -}} + {{if eq "note" .Type -}} + {{$notes = append $notes (renderReleaseNote .) -}} + {{else if eq "breaking-change" .Type -}} + {{$breaking = append $breaking (renderReleaseNote .) -}} + {{else if eq "deprecation" .Type -}} + {{$deprecations = append $deprecations (renderReleaseNote .) -}} + {{else if or (eq "new-resource" .Type) (eq "new-datasource" .Type) (eq "feature" .Type) -}} + {{$features = append $features (renderReleaseNote .) -}} + {{else if or (eq "improvement" .Type) (eq "enhancement" .Type) -}} + {{$improvements = append $improvements (renderReleaseNote .) -}} + {{ else if eq "bug" .Type -}} + {{$bugs = append $bugs (renderReleaseNote .) -}} + {{ else -}} + {{$unknown = append $unknown (renderReleaseNote .) -}} + {{end -}} +{{- end -}} +{{- if gt (len $unknown) 0 -}} +UNKNOWN CHANGELOG TYPE: +{{range $unknown | sortAlpha -}} +* {{. }} +{{- end -}} +{{- end -}} +{{- if gt (len $notes) 0 -}} +NOTES: +{{range $notes | sortAlpha -}} +* {{. }} +{{- end -}} +{{- end -}} +{{- if gt (len $deprecations) 0 -}} +DEPRECATIONS: +{{range $deprecations | sortAlpha -}} +* {{. }} +{{- end -}} +{{- end -}} +{{- if gt (len $breaking) 0 -}} +BREAKING CHANGES: +{{range $breaking | sortAlpha -}} +* {{. }} +{{- end -}} +{{- end -}} +{{- if gt (len $features) 0}} +FEATURES: +{{range $features | sortAlpha -}} +* {{. }} +{{- end -}} +{{- end -}} +{{- if gt (len $improvements) 0}} +IMPROVEMENTS: +{{range $improvements | sortAlpha -}} +* {{. }} +{{- end -}} +{{- end -}} +{{- if gt (len $bugs) 0}} +BUGS: +{{range $bugs | sortAlpha -}} +* {{. }} +{{- end -}} +{{- end -}} diff --git a/.ci/release-note.tmpl b/.ci/release-note.tmpl new file mode 100644 index 000000000000..5a3e1d645980 --- /dev/null +++ b/.ci/release-note.tmpl @@ -0,0 +1 @@ +{{if eq "new-resource" .Type}}**New Resource:** {{else if eq "new-data-source" .Type}}**New Data Source:** {{ end }}{{.Text }} ([#{{- .PRNumber -}}]({{- .PRURL -}})) From e876e09c152d4d48a181d7303c4254e3740d888f Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Fri, 25 Oct 2019 13:04:37 -0700 Subject: [PATCH 30/37] Don't lock policy if it is already locked. (#2534) Merged PR #2534. --- build/terraform | 2 +- build/terraform-beta | 2 +- third_party/terraform/resources/resource_storage_bucket.go | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build/terraform b/build/terraform index 264f981107ba..b28342443e78 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 264f981107bae8e34ed44e04cf380f251029103c +Subproject commit b28342443e78709c362d893bda09c28e45938116 diff --git a/build/terraform-beta b/build/terraform-beta index b40632cd5c53..379f53be9fb5 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit b40632cd5c53fc2c4ac86cf9ae70c2b48a47c48f +Subproject commit 379f53be9fb5a0c68cac127910147d6215a40ca5 diff --git a/third_party/terraform/resources/resource_storage_bucket.go b/third_party/terraform/resources/resource_storage_bucket.go index b1fbcb75bc66..683cd690a246 100644 --- a/third_party/terraform/resources/resource_storage_bucket.go +++ b/third_party/terraform/resources/resource_storage_bucket.go @@ -392,7 +392,9 @@ func resourceStorageBucketCreate(d *schema.ResourceData, meta interface{}) error log.Printf("[DEBUG] Created bucket %v at location %v\n\n", res.Name, res.SelfLink) d.SetId(res.Id) - if v, ok := d.GetOk("retention_policy"); ok { + // If the retention policy is not already locked, check if it + // needs to be locked. + if v, ok := d.GetOk("retention_policy"); ok && !res.RetentionPolicy.IsLocked { retention_policies := v.([]interface{}) sb.RetentionPolicy = &storage.BucketRetentionPolicy{} @@ -514,7 +516,7 @@ func resourceStorageBucketUpdate(d *schema.ResourceData, meta interface{}) error retentionPolicy := retention_policies[0].(map[string]interface{}) - if locked, ok := retentionPolicy["is_locked"]; ok && locked.(bool) { + if locked, ok := retentionPolicy["is_locked"]; ok && locked.(bool) && d.HasChange("retention_policy.0.is_locked") { err = lockRetentionPolicy(config.clientStorage.Buckets, d.Get("name").(string), res.Metageneration) if err != nil { return err From 5dd36161ab8e027a080ec7b33989ab87c389c151 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Fri, 25 Oct 2019 13:24:46 -0700 Subject: [PATCH 31/37] If certificate_id is unset, it should not cause a diff. (#2533) Merged PR #2533. --- build/terraform | 2 +- build/terraform-beta | 2 +- products/appengine/terraform.yaml | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index b28342443e78..77891f3dde32 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit b28342443e78709c362d893bda09c28e45938116 +Subproject commit 77891f3dde3268522b443eea3e58e603eb63942b diff --git a/build/terraform-beta b/build/terraform-beta index 379f53be9fb5..01c066a89e27 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 379f53be9fb5a0c68cac127910147d6215a40ca5 +Subproject commit 01c066a89e277b5590e7c6d30b00f31736bb88ac diff --git a/products/appengine/terraform.yaml b/products/appengine/terraform.yaml index 8d1e56f63a6e..829d31ed1ae8 100644 --- a/products/appengine/terraform.yaml +++ b/products/appengine/terraform.yaml @@ -93,6 +93,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides update_mask_fields: - "ssl_settings.certificate_id" - "ssl_settings.ssl_management_type" + sslSettings.certificateId: !ruby/object:Overrides::Terraform::PropertyOverride + default_from_api: true ApplicationUrlDispatchRules: !ruby/object:Overrides::Terraform::ResourceOverride id_format: "{{project}}" import_format: ["{{project}}"] From 728b471aaa1f4aba7cd21589890559a9f4198252 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Fri, 25 Oct 2019 15:05:14 -0700 Subject: [PATCH 32/37] Add description to service account. (#2513) Merged PR #2513. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resource_google_service_account.go | 17 ++++++++++++++++- .../resource_google_service_account_test.go | 2 ++ .../docs/r/google_service_account.html.markdown | 2 ++ 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/build/terraform b/build/terraform index 77891f3dde32..887d1b1f0128 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 77891f3dde3268522b443eea3e58e603eb63942b +Subproject commit 887d1b1f0128888b018694a5f3692cd6f2bc739f diff --git a/build/terraform-beta b/build/terraform-beta index 01c066a89e27..d52448f5f3d8 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 01c066a89e277b5590e7c6d30b00f31736bb88ac +Subproject commit d52448f5f3d8b5a8a4fc10a4290bd77179840ce0 diff --git a/third_party/terraform/resources/resource_google_service_account.go b/third_party/terraform/resources/resource_google_service_account.go index b367b1a501c1..c5f16ded01eb 100644 --- a/third_party/terraform/resources/resource_google_service_account.go +++ b/third_party/terraform/resources/resource_google_service_account.go @@ -3,6 +3,7 @@ package google import ( "fmt" "strings" + "time" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "google.golang.org/api/iam/v1" @@ -40,6 +41,10 @@ func resourceGoogleServiceAccount() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "description": { + Type: schema.TypeString, + Optional: true, + }, "project": { Type: schema.TypeString, Computed: true, @@ -63,9 +68,11 @@ func resourceGoogleServiceAccountCreate(d *schema.ResourceData, meta interface{} } aid := d.Get("account_id").(string) displayName := d.Get("display_name").(string) + description := d.Get("description").(string) sa := &iam.ServiceAccount{ DisplayName: displayName, + Description: description, } r := &iam.CreateServiceAccountRequest{ @@ -79,6 +86,10 @@ func resourceGoogleServiceAccountCreate(d *schema.ResourceData, meta interface{} } d.SetId(sa.Name) + // This API is meant to be synchronous, but in practice it shows the old value for + // a few milliseconds after the update goes through. A second is more than enough + // time to ensure following reads are correct. + time.Sleep(time.Second) return resourceGoogleServiceAccountRead(d, meta) } @@ -98,6 +109,7 @@ func resourceGoogleServiceAccountRead(d *schema.ResourceData, meta interface{}) d.Set("account_id", strings.Split(sa.Email, "@")[0]) d.Set("name", sa.Name) d.Set("display_name", sa.DisplayName) + d.Set("description", sa.Description) return nil } @@ -114,7 +126,7 @@ func resourceGoogleServiceAccountDelete(d *schema.ResourceData, meta interface{} func resourceGoogleServiceAccountUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - if ok := d.HasChange("display_name"); ok { + if d.HasChange("display_name") || d.HasChange("description") { sa, err := config.clientIAM.Projects.ServiceAccounts.Get(d.Id()).Do() if err != nil { return fmt.Errorf("Error retrieving service account %q: %s", d.Id(), err) @@ -122,11 +134,14 @@ func resourceGoogleServiceAccountUpdate(d *schema.ResourceData, meta interface{} _, err = config.clientIAM.Projects.ServiceAccounts.Update(d.Id(), &iam.ServiceAccount{ DisplayName: d.Get("display_name").(string), + Description: d.Get("description").(string), Etag: sa.Etag, }).Do() if err != nil { return fmt.Errorf("Error updating service account %q: %s", d.Id(), err) } + // See comment in Create. + time.Sleep(time.Second) } return nil diff --git a/third_party/terraform/tests/resource_google_service_account_test.go b/third_party/terraform/tests/resource_google_service_account_test.go index 3b9c66a3eab2..d44584bbbd41 100644 --- a/third_party/terraform/tests/resource_google_service_account_test.go +++ b/third_party/terraform/tests/resource_google_service_account_test.go @@ -95,6 +95,7 @@ func testAccServiceAccountBasic(account, name string) string { resource "google_service_account" "acceptance" { account_id = "%v" display_name = "%v" + description = "foo" } `, account, name) } @@ -105,6 +106,7 @@ resource "google_service_account" "acceptance" { project = "%v" account_id = "%v" display_name = "%v" + description = "foo" } `, project, account, name) } diff --git a/third_party/terraform/website/docs/r/google_service_account.html.markdown b/third_party/terraform/website/docs/r/google_service_account.html.markdown index 680fec6f2063..0a35288b907c 100644 --- a/third_party/terraform/website/docs/r/google_service_account.html.markdown +++ b/third_party/terraform/website/docs/r/google_service_account.html.markdown @@ -39,6 +39,8 @@ The following arguments are supported: * `display_name` - (Optional) The display name for the service account. Can be updated without creating a new resource. +* `description` - (Optional) A text description of the service account. + * `project` - (Optional) The ID of the project that the service account will be created in. Defaults to the provider project configuration. From 31e311e6dfa71c7c208f52a0b5e5f158b77fdeca Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Fri, 25 Oct 2019 15:09:31 -0700 Subject: [PATCH 33/37] Move the tests to the new subdirectories. (#2541) Merged PR #2541. --- .ci/ci.yml.tmpl | 15 ++++++++------- .ci/unit-tests/tf-3.sh | 2 +- .ci/unit-tests/tf-3.yml | 1 + build/ansible | 2 +- build/inspec | 2 +- build/terraform | 2 +- build/terraform-beta | 2 +- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.ci/ci.yml.tmpl b/.ci/ci.yml.tmpl index de645d661533..56c0d69dc39c 100644 --- a/.ci/ci.yml.tmpl +++ b/.ci/ci.yml.tmpl @@ -183,13 +183,6 @@ jobs: GITHUB_ORG: {{v.github_org}} OVERRIDE_PROVIDER: {{v.override_provider}} - - task: test-{{v.short_name}} - file: magic-modules-branched/.ci/unit-tests/tf-3.yml - timeout: 30m - params: - PROVIDER_NAME: {{v.provider_name}} - TEST_DIR: {{v.test_dir}} - - put: {{v.short_name}}-intermediate params: repository: terraform-diff/{{k}}/new @@ -206,6 +199,14 @@ jobs: get_params: skip_clone: true + - task: test-{{v.short_name}} + file: magic-modules-branched/.ci/unit-tests/tf-3.yml + timeout: 30m + params: + PROVIDER_NAME: {{v.provider_name}} + TEST_DIR: {{v.test_dir}} + SUBDIR: {{k}} + {% endfor %} on_failure: diff --git a/.ci/unit-tests/tf-3.sh b/.ci/unit-tests/tf-3.sh index ba618b8c1acd..76168df49deb 100755 --- a/.ci/unit-tests/tf-3.sh +++ b/.ci/unit-tests/tf-3.sh @@ -9,7 +9,7 @@ set -x # Create GOPATH structure mkdir -p "${GOPATH}/src/github.com/terraform-providers" -ln -s "${PWD}/terraform-diff/new" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" +ln -s "${PWD}/terraform-diff/${SUBDIR}/new" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" cd "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" diff --git a/.ci/unit-tests/tf-3.yml b/.ci/unit-tests/tf-3.yml index f59a418afeca..e6c60a528ce0 100644 --- a/.ci/unit-tests/tf-3.yml +++ b/.ci/unit-tests/tf-3.yml @@ -12,3 +12,4 @@ run: params: PROVIDER_NAME: "" TEST_DIR: "" + SUBDIR: "" diff --git a/build/ansible b/build/ansible index 2780d4eadf87..2bbb08735bfb 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit 2780d4eadf87b64cdbe5c530e826517dbcf6f1cc +Subproject commit 2bbb08735bfb233645351fee2a988dc0e7d337e3 diff --git a/build/inspec b/build/inspec index d8801b70953f..e046d0b3311c 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit d8801b70953fc6197fdfe876ae28dde36f63c5fa +Subproject commit e046d0b3311c05ff7ec809bb18b167f11371207c diff --git a/build/terraform b/build/terraform index 887d1b1f0128..842d348e24e5 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 887d1b1f0128888b018694a5f3692cd6f2bc739f +Subproject commit 842d348e24e5d2c03d264a92dd6f9af98ceb37cc diff --git a/build/terraform-beta b/build/terraform-beta index d52448f5f3d8..a3f44b501946 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit d52448f5f3d8b5a8a4fc10a4290bd77179840ce0 +Subproject commit a3f44b501946af06d17ff5b0d47f6ab65809d298 From accdf164cab0a1e09b6ff09ae5758b5ca6f3ba86 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Fri, 25 Oct 2019 15:13:24 -0700 Subject: [PATCH 34/37] clarify fields that are regexes. (#2535) Merged PR #2535. --- products/cloudbuild/api.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/products/cloudbuild/api.yaml b/products/cloudbuild/api.yaml index be5918053f7f..282e69e815d0 100644 --- a/products/cloudbuild/api.yaml +++ b/products/cloudbuild/api.yaml @@ -132,10 +132,12 @@ objects: name: 'branchName' description: | Name of the branch to build. Exactly one a of branch name, tag, or commit SHA must be provided. + This field is a regular expression. - !ruby/object:Api::Type::String name: 'tagName' description: | Name of the tag to build. Exactly one of a branch name, tag, or commit SHA must be provided. + This field is a regular expression. - !ruby/object:Api::Type::String name: 'commitSha' description: | From f132365fdab19f0a05be8719233d05e742ac6ec0 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Fri, 25 Oct 2019 15:33:05 -0700 Subject: [PATCH 35/37] Say BUG FIXES instead of BUGS in changelogs (#2545) Merged PR #2545. --- .ci/changelog.tmpl | 2 +- build/terraform | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/changelog.tmpl b/.ci/changelog.tmpl index 7db175f51030..229d4e5e4ed3 100644 --- a/.ci/changelog.tmpl +++ b/.ci/changelog.tmpl @@ -59,7 +59,7 @@ IMPROVEMENTS: {{- end -}} {{- end -}} {{- if gt (len $bugs) 0}} -BUGS: +BUG FIXES: {{range $bugs | sortAlpha -}} * {{. }} {{- end -}} diff --git a/build/terraform b/build/terraform index 842d348e24e5..bfb298f1062d 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 842d348e24e5d2c03d264a92dd6f9af98ceb37cc +Subproject commit bfb298f1062d0717c256cf7426fab7c13a0dcc96 From 109ee4f48c408022ba6f57fe85140abeda302bc3 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Fri, 25 Oct 2019 16:24:32 -0700 Subject: [PATCH 36/37] Make it clear that we don't need to ForceSend nested fields in ComputeInstanceFromTemplate (#2543) Merged PR #2543. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resources/resource_compute_instance_from_template.go | 1 - .../tests/resource_compute_instance_from_template_test.go | 8 ++++++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/build/terraform b/build/terraform index bfb298f1062d..7f460c8ccbce 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit bfb298f1062d0717c256cf7426fab7c13a0dcc96 +Subproject commit 7f460c8ccbce8b203e0aa848858a1b6ac3957760 diff --git a/build/terraform-beta b/build/terraform-beta index a3f44b501946..04e6447c28a6 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit a3f44b501946af06d17ff5b0d47f6ab65809d298 +Subproject commit 04e6447c28a646cf93a24f3a256965a8ce79101b diff --git a/third_party/terraform/resources/resource_compute_instance_from_template.go b/third_party/terraform/resources/resource_compute_instance_from_template.go index b7a4019ec589..4150af0a69de 100644 --- a/third_party/terraform/resources/resource_compute_instance_from_template.go +++ b/third_party/terraform/resources/resource_compute_instance_from_template.go @@ -127,7 +127,6 @@ func resourceComputeInstanceFromTemplateCreate(d *schema.ResourceData, meta inte } // Force send all top-level fields that have been set in case they're overridden to zero values. - // TODO: consider doing so for nested fields as well. // Initialize ForceSendFields to empty so we don't get things that the instance resource // always force-sends. instance.ForceSendFields = []string{} diff --git a/third_party/terraform/tests/resource_compute_instance_from_template_test.go b/third_party/terraform/tests/resource_compute_instance_from_template_test.go index 166d0619842b..84d5d77d064b 100644 --- a/third_party/terraform/tests/resource_compute_instance_from_template_test.go +++ b/third_party/terraform/tests/resource_compute_instance_from_template_test.go @@ -32,6 +32,7 @@ func TestAccComputeInstanceFromTemplate_basic(t *testing.T) { // Check that fields were set based on the template resource.TestCheckResourceAttr(resourceName, "machine_type", "n1-standard-1"), resource.TestCheckResourceAttr(resourceName, "attached_disk.#", "1"), + resource.TestCheckResourceAttr(resourceName, "scheduling.0.automatic_restart", "false"), ), }, }, @@ -255,6 +256,10 @@ resource "google_compute_instance_template" "foobar" { foo = "bar" } + scheduling { + automatic_restart = true + } + can_ip_forward = true } @@ -269,6 +274,9 @@ resource "google_compute_instance_from_template" "foobar" { labels = { my_key = "my_value" } + scheduling { + automatic_restart = false + } } `, template, template, instance) } From 8ba57989382e7a6c8226e0cdf1a848ead654911b Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 28 Oct 2019 10:30:52 -0700 Subject: [PATCH 37/37] Add IAM Conditions support; enable it in service account IAM (#2372) Merged PR #2372. --- build/terraform | 2 +- build/terraform-beta | 2 +- ...o => data_source_google_iam_policy.go.erb} | 34 +- .../resources/resource_iam_binding.go.erb | 154 ++++++++- ...m_member.go => resource_iam_member.go.erb} | 132 +++++++- ...m_policy.go => resource_iam_policy.go.erb} | 4 + ...rce_google_service_account_iam_test.go.erb | 317 ++++++++++++++++-- .../terraform/utils/{iam.go => iam.go.erb} | 82 +++-- ..._account.go => iam_service_account.go.erb} | 6 + .../utils/{iam_test.go => iam_test.go.erb} | 292 ++++++++++++++-- .../google_service_account_iam.html.markdown | 74 +++- 11 files changed, 983 insertions(+), 116 deletions(-) rename third_party/terraform/data_sources/{data_source_google_iam_policy.go => data_source_google_iam_policy.go.erb} (85%) rename third_party/terraform/resources/{resource_iam_member.go => resource_iam_member.go.erb} (58%) rename third_party/terraform/resources/{resource_iam_policy.go => resource_iam_policy.go.erb} (97%) rename third_party/terraform/utils/{iam.go => iam.go.erb} (84%) rename third_party/terraform/utils/{iam_service_account.go => iam_service_account.go.erb} (92%) rename third_party/terraform/utils/{iam_test.go => iam_test.go.erb} (78%) diff --git a/build/terraform b/build/terraform index 7f460c8ccbce..b86a93f8ffc2 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 7f460c8ccbce8b203e0aa848858a1b6ac3957760 +Subproject commit b86a93f8ffc2909f729afb0fbd3793a2a489cb24 diff --git a/build/terraform-beta b/build/terraform-beta index 04e6447c28a6..67c66aad64f7 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 04e6447c28a646cf93a24f3a256965a8ce79101b +Subproject commit 67c66aad64f73b68bbf71551db3393ffb101b695 diff --git a/third_party/terraform/data_sources/data_source_google_iam_policy.go b/third_party/terraform/data_sources/data_source_google_iam_policy.go.erb similarity index 85% rename from third_party/terraform/data_sources/data_source_google_iam_policy.go rename to third_party/terraform/data_sources/data_source_google_iam_policy.go.erb index 637d77526d5b..f67d4a41ad43 100644 --- a/third_party/terraform/data_sources/data_source_google_iam_policy.go +++ b/third_party/terraform/data_sources/data_source_google_iam_policy.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -41,6 +42,29 @@ func dataSourceGoogleIamPolicy() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, +<% unless version == 'ga' -%> + "condition": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "expression": { + Type: schema.TypeString, + Required: true, + }, + "title": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, +<% end -%> }, }, }, @@ -99,13 +123,19 @@ func dataSourceGoogleIamPolicyRead(d *schema.ResourceData, meta interface{}) err for i, v := range bset.List() { binding := v.(map[string]interface{}) members := convertStringSet(binding["members"].(*schema.Set)) +<% unless version == 'ga' -%> + condition := expandIamCondition(binding["condition"]) +<% end -%> // Sort members to get simpler diffs as it's what the API does sort.Strings(members) policy.Bindings[i] = &cloudresourcemanager.Binding{ - Role: binding["role"].(string), - Members: members, + Role: binding["role"].(string), + Members: members, +<% unless version == 'ga' -%> + Condition: condition, +<% end -%> } } diff --git a/third_party/terraform/resources/resource_iam_binding.go.erb b/third_party/terraform/resources/resource_iam_binding.go.erb index 14fb00e6f516..58e6d5079140 100644 --- a/third_party/terraform/resources/resource_iam_binding.go.erb +++ b/third_party/terraform/resources/resource_iam_binding.go.erb @@ -28,6 +28,33 @@ var iamBindingSchema = map[string]*schema.Schema{ return schema.HashString(strings.ToLower(v.(string))) }, }, +<% unless version == 'ga' -%> + "condition": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "expression": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "title": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + }, + }, +<% end -%> "etag": { Type: schema.TypeString, Computed: true, @@ -47,7 +74,7 @@ func ResourceIamBindingWithBatching(parentSpecificSchema map[string]*schema.Sche Delete: resourceIamBindingDelete(newUpdaterFunc, enableBatching), Schema: mergeSchemas(iamBindingSchema, parentSpecificSchema), Importer: &schema.ResourceImporter{ - State: iamBindingImport(resourceIdParser), + State: iamBindingImport(newUpdaterFunc, resourceIdParser), }, } } @@ -62,8 +89,11 @@ func resourceIamBindingCreateUpdate(newUpdaterFunc newResourceIamUpdaterFunc, en binding := getResourceIamBinding(d) modifyF := func(ep *cloudresourcemanager.Policy) error { - cleaned := removeAllBindingsWithRole(ep.Bindings, binding.Role) + cleaned := filterBindingsWithRoleAndCondition(ep.Bindings, binding.Role, binding.Condition) ep.Bindings = append(cleaned, binding) +<% unless version == 'ga' -%> + ep.Version = iamPolicyVersion +<% end -%> return nil } @@ -76,7 +106,13 @@ func resourceIamBindingCreateUpdate(newUpdaterFunc newResourceIamUpdaterFunc, en if err != nil { return err } + d.SetId(updater.GetResourceId() + "/" + binding.Role) +<% unless version == 'ga' -%> + if k := conditionKeyFromCondition(binding.Condition); !k.Empty() { + d.SetId(d.Id() + "/" + k.String()) + } +<% end -%> return resourceIamBindingRead(newUpdaterFunc)(d, meta) } } @@ -90,46 +126,67 @@ func resourceIamBindingRead(newUpdaterFunc newResourceIamUpdaterFunc) schema.Rea } eBinding := getResourceIamBinding(d) + eCondition := conditionKeyFromCondition(eBinding.Condition) p, err := iamPolicyReadWithRetry(updater) if err != nil { return handleNotFoundError(err, d, fmt.Sprintf("Resource %q with IAM Binding (Role %q)", updater.DescribeResource(), eBinding.Role)) } - log.Printf("[DEBUG]: Retrieved policy for %s: %+v", updater.DescribeResource(), p) + log.Printf("[DEBUG] Retrieved policy for %s: %+v", updater.DescribeResource(), p) + log.Printf("[DEBUG] Looking for binding with role %q and condition %+v", eBinding.Role, eCondition) var binding *cloudresourcemanager.Binding for _, b := range p.Bindings { - if b.Role != eBinding.Role { - continue + if b.Role == eBinding.Role && conditionKeyFromCondition(b.Condition) == eCondition { + binding = b + break } - binding = b - break } + if binding == nil { - log.Printf("[DEBUG]: Binding for role %q not found in policy for %s, assuming it has no members.", eBinding.Role, updater.DescribeResource()) + log.Printf("[DEBUG] Binding for role %q and condition %+v not found in policy for %s, assuming it has no members.", eBinding.Role, eCondition, updater.DescribeResource()) d.Set("role", eBinding.Role) d.Set("members", nil) return nil } else { d.Set("role", binding.Role) d.Set("members", binding.Members) +<% unless version == 'ga' -%> + d.Set("condition", flattenIamCondition(binding.Condition)) +<% end -%> } d.Set("etag", p.Etag) return nil } } -func iamBindingImport(resourceIdParser resourceIdParserFunc) schema.StateFunc { +func iamBindingImport(newUpdaterFunc newResourceIamUpdaterFunc, resourceIdParser resourceIdParserFunc) schema.StateFunc { return func(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { if resourceIdParser == nil { return nil, errors.New("Import not supported for this IAM resource.") } config := m.(*Config) s := strings.Fields(d.Id()) + var id, role string +<% if version == 'ga' -%> if len(s) != 2 { d.SetId("") return nil, fmt.Errorf("Wrong number of parts to Binding id %s; expected 'resource_name role'.", s) } - id, role := s[0], s[1] + id, role = s[0], s[1] +<% else -%> + if len(s) < 2 { + d.SetId("") + return nil, fmt.Errorf("Wrong number of parts to Binding id %s; expected 'resource_name role [condition_title]'.", s) + } + + var conditionTitle string + if len(s) == 2 { + id, role = s[0], s[1] + } else { + // condition titles can have any characters in them, so re-join the split string + id, role, conditionTitle = s[0], s[1], strings.Join(s[2:], " ") + } +<% end -%> // Set the ID only to the first part so all IAM types can share the same resourceIdParserFunc. d.SetId(id) @@ -142,6 +199,40 @@ func iamBindingImport(resourceIdParser resourceIdParserFunc) schema.StateFunc { // Set the ID again so that the ID matches the ID it would have if it had been created via TF. // Use the current ID in case it changed in the resourceIdParserFunc. d.SetId(d.Id() + "/" + role) + +<% unless version == 'ga' -%> + // Since condition titles can have any character in them, we can't separate them from any other + // field the user might set in import (like the condition description and expression). So, we + // have the user just specify the title and then read the upstream policy to set the full + // condition. We can't rely on the read fn to do this for us because it looks for a match of the + // full condition. + updater, err := newUpdaterFunc(d, config) + if err != nil { + return nil, err + } + p, err := iamPolicyReadWithRetry(updater) + if err != nil { + return nil, err + } + var binding *cloudresourcemanager.Binding + for _, b := range p.Bindings { + if (b.Role == role && conditionKeyFromCondition(b.Condition).Title == conditionTitle) { + if binding != nil { + return nil, fmt.Errorf("Cannot import IAM member with condition title %q, it matches multiple conditions", conditionTitle) + } + binding = b + } + } + if binding == nil { + return nil, fmt.Errorf("Cannot find binding for %q with role %q and condition title %q", updater.DescribeResource(), role, conditionTitle) + } + + d.Set("condition", flattenIamCondition(binding.Condition)) + if k := conditionKeyFromCondition(binding.Condition); !k.Empty() { + d.SetId(d.Id() + "/" + k.String()) + } +<% end -%> + // It is possible to return multiple bindings, since we can learn about all the bindings // for this resource here. Unfortunately, `terraform import` has some messy behavior here - // there's no way to know at this point which resource is being imported, so it's not possible @@ -166,7 +257,7 @@ func resourceIamBindingDelete(newUpdaterFunc newResourceIamUpdaterFunc, enableBa binding := getResourceIamBinding(d) modifyF := func(p *cloudresourcemanager.Policy) error { - p.Bindings = removeAllBindingsWithRole(p.Bindings, binding.Role) + p.Bindings = filterBindingsWithRoleAndCondition(p.Bindings, binding.Role, binding.Condition) return nil } @@ -186,8 +277,43 @@ func resourceIamBindingDelete(newUpdaterFunc newResourceIamUpdaterFunc, enableBa func getResourceIamBinding(d *schema.ResourceData) *cloudresourcemanager.Binding { members := d.Get("members").(*schema.Set).List() - return &cloudresourcemanager.Binding{ - Members: convertStringArr(members), - Role: d.Get("role").(string), + b := &cloudresourcemanager.Binding{ + Members: convertStringArr(members), + Role: d.Get("role").(string), + } +<% unless version == 'ga' -%> + if c := expandIamCondition(d.Get("condition")); c != nil { + b.Condition = c + } +<% end -%> + return b +} + +<% unless version == 'ga' -%> +func expandIamCondition(v interface{}) *cloudresourcemanager.Expr { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil + } + original := l[0].(map[string]interface{}) + return &cloudresourcemanager.Expr{ + Description: original["description"].(string), + Expression: original["expression"].(string), + Title: original["title"].(string), + ForceSendFields: []string{"Description", "Expression", "Title"}, + } +} + +func flattenIamCondition(condition *cloudresourcemanager.Expr) []map[string]interface{} { + if conditionKeyFromCondition(condition).Empty() { + return nil + } + return []map[string]interface{}{ + { + "expression": condition.Expression, + "title": condition.Title, + "description": condition.Description, + }, } } +<% end -%> diff --git a/third_party/terraform/resources/resource_iam_member.go b/third_party/terraform/resources/resource_iam_member.go.erb similarity index 58% rename from third_party/terraform/resources/resource_iam_member.go rename to third_party/terraform/resources/resource_iam_member.go.erb index ae3f1ed472c0..1d7ebf381b2a 100644 --- a/third_party/terraform/resources/resource_iam_member.go +++ b/third_party/terraform/resources/resource_iam_member.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -22,29 +23,73 @@ var IamMemberBaseSchema = map[string]*schema.Schema{ ForceNew: true, DiffSuppressFunc: caseDiffSuppress, }, +<% unless version == 'ga' -%> + "condition": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "expression": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "title": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + }, + }, +<% end -%> "etag": { Type: schema.TypeString, Computed: true, }, } -func iamMemberImport(resourceIdParser resourceIdParserFunc) schema.StateFunc { +func iamMemberImport(newUpdaterFunc newResourceIamUpdaterFunc, resourceIdParser resourceIdParserFunc) schema.StateFunc { return func(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { if resourceIdParser == nil { return nil, errors.New("Import not supported for this IAM resource.") } config := m.(*Config) s := strings.Fields(d.Id()) + var id, role, member string +<% if version == 'ga' -%> if len(s) != 3 { d.SetId("") return nil, fmt.Errorf("Wrong number of parts to Member id %s; expected 'resource_name role member'.", s) } - id, role, member := s[0], s[1], s[2] + id, role, member = s[0], s[1], s[2] +<% else -%> + if len(s) < 3 { + d.SetId("") + return nil, fmt.Errorf("Wrong number of parts to Member id %s; expected 'resource_name role member [condition_title]'.", s) + } + + var conditionTitle string + if len(s) == 3 { + id, role, member = s[0], s[1], s[2] + } else { + // condition titles can have any characters in them, so re-join the split string + id, role, member, conditionTitle = s[0], s[1], s[2], strings.Join(s[3:], " ") + } +<% end -%> // Set the ID only to the first part so all IAM types can share the same resourceIdParserFunc. d.SetId(id) d.Set("role", role) d.Set("member", strings.ToLower(member)) + err := resourceIdParser(d, config) if err != nil { return nil, err @@ -53,6 +98,46 @@ func iamMemberImport(resourceIdParser resourceIdParserFunc) schema.StateFunc { // Set the ID again so that the ID matches the ID it would have if it had been created via TF. // Use the current ID in case it changed in the resourceIdParserFunc. d.SetId(d.Id() + "/" + role + "/" + strings.ToLower(member)) + +<% unless version == 'ga' -%> + // Read the upstream policy so we can set the full condition. + updater, err := newUpdaterFunc(d, config) + if err != nil { + return nil, err + } + p, err := iamPolicyReadWithRetry(updater) + if err != nil { + return nil, err + } + var binding *cloudresourcemanager.Binding + for _, b := range p.Bindings { + if (b.Role == role && conditionKeyFromCondition(b.Condition).Title == conditionTitle) { + containsMember := false + for _, m := range b.Members { + if strings.ToLower(m) == strings.ToLower(member) { + containsMember = true + } + } + if !containsMember { + continue + } + + if binding != nil { + return nil, fmt.Errorf("Cannot import IAM member with condition title %q, it matches multiple conditions", conditionTitle) + } + binding = b + } + } + if binding == nil { + return nil, fmt.Errorf("Cannot find binding for %q with role %q, member %q, and condition title %q", updater.DescribeResource(), role, member, conditionTitle) + } + + d.Set("condition", flattenIamCondition(binding.Condition)) + if k := conditionKeyFromCondition(binding.Condition); !k.Empty() { + d.SetId(d.Id() + "/" + k.String()) + } +<% end -%> + return []*schema.ResourceData{d}, nil } } @@ -68,16 +153,22 @@ func ResourceIamMemberWithBatching(parentSpecificSchema map[string]*schema.Schem Delete: resourceIamMemberDelete(newUpdaterFunc, enableBatching), Schema: mergeSchemas(IamMemberBaseSchema, parentSpecificSchema), Importer: &schema.ResourceImporter{ - State: iamMemberImport(resourceIdParser), + State: iamMemberImport(newUpdaterFunc, resourceIdParser), }, } } func getResourceIamMember(d *schema.ResourceData) *cloudresourcemanager.Binding { - return &cloudresourcemanager.Binding{ - Members: []string{d.Get("member").(string)}, - Role: d.Get("role").(string), + b := &cloudresourcemanager.Binding{ + Members: []string{d.Get("member").(string)}, + Role: d.Get("role").(string), } +<% unless version == 'ga' -%> + if c := expandIamCondition(d.Get("condition")); c != nil { + b.Condition = c + } +<% end -%> + return b } func resourceIamMemberCreate(newUpdaterFunc newResourceIamUpdaterFunc, enableBatching bool) schema.CreateFunc { @@ -92,6 +183,9 @@ func resourceIamMemberCreate(newUpdaterFunc newResourceIamUpdaterFunc, enableBat modifyF := func(ep *cloudresourcemanager.Policy) error { // Merge the bindings together ep.Bindings = mergeBindings(append(ep.Bindings, memberBind)) +<% unless version == 'ga' -%> + ep.Version = iamPolicyVersion +<% end -%> return nil } if enableBatching { @@ -104,6 +198,11 @@ func resourceIamMemberCreate(newUpdaterFunc newResourceIamUpdaterFunc, enableBat return err } d.SetId(updater.GetResourceId() + "/" + memberBind.Role + "/" + strings.ToLower(memberBind.Members[0])) +<% unless version == 'ga' -%> + if k := conditionKeyFromCondition(memberBind.Condition); !k.Empty() { + d.SetId(d.Id() + "/" + k.String()) + } +<% end -%> return resourceIamMemberRead(newUpdaterFunc)(d, meta) } } @@ -117,39 +216,48 @@ func resourceIamMemberRead(newUpdaterFunc newResourceIamUpdaterFunc) schema.Read } eMember := getResourceIamMember(d) + eCondition := conditionKeyFromCondition(eMember.Condition) p, err := iamPolicyReadWithRetry(updater) if err != nil { return handleNotFoundError(err, d, fmt.Sprintf("Resource %q with IAM Member: Role %q Member %q", updater.DescribeResource(), eMember.Role, eMember.Members[0])) } log.Printf("[DEBUG]: Retrieved policy for %s: %+v\n", updater.DescribeResource(), p) + log.Printf("[DEBUG]: Looking for binding with role %q and condition %+v", eMember.Role, eCondition) var binding *cloudresourcemanager.Binding for _, b := range p.Bindings { - if b.Role != eMember.Role { - continue + if (b.Role == eMember.Role && conditionKeyFromCondition(b.Condition) == eCondition) { + binding = b + break } - binding = b - break } + if binding == nil { - log.Printf("[DEBUG]: Binding for role %q does not exist in policy of %s, removing member %q from state.", eMember.Role, updater.DescribeResource(), eMember.Members[0]) + log.Printf("[DEBUG]: Binding for role %q with condition %+v does not exist in policy of %s, removing member %q from state.", eMember.Role, eCondition, updater.DescribeResource(), eMember.Members[0]) d.SetId("") return nil } + + log.Printf("[DEBUG]: Looking for member %q in found binding", eMember.Members[0]) var member string for _, m := range binding.Members { if strings.ToLower(m) == strings.ToLower(eMember.Members[0]) { member = m } } + if member == "" { - log.Printf("[DEBUG]: Member %q for binding for role %q does not exist in policy of %s, removing from state.", eMember.Members[0], eMember.Role, updater.DescribeResource()) + log.Printf("[DEBUG]: Member %q for binding for role %q with condition %+v does not exist in policy of %s, removing from state.", eMember.Members[0], eMember.Role, eCondition, updater.DescribeResource()) d.SetId("") return nil } + d.Set("etag", p.Etag) d.Set("member", member) d.Set("role", binding.Role) +<% unless version == 'ga' -%> + d.Set("condition", flattenIamCondition(binding.Condition)) +<% end -%> return nil } } diff --git a/third_party/terraform/resources/resource_iam_policy.go b/third_party/terraform/resources/resource_iam_policy.go.erb similarity index 97% rename from third_party/terraform/resources/resource_iam_policy.go rename to third_party/terraform/resources/resource_iam_policy.go.erb index 5d11da3aaeab..055a7adefcb1 100644 --- a/third_party/terraform/resources/resource_iam_policy.go +++ b/third_party/terraform/resources/resource_iam_policy.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -129,6 +130,9 @@ func setIamPolicyData(d *schema.ResourceData, updater ResourceIamUpdater) error if err != nil { return fmt.Errorf("'policy_data' is not valid for %s: %s", updater.DescribeResource(), err) } +<% unless version == 'ga' -%> + policy.Version = iamPolicyVersion +<% end -%> err = updater.SetResourceIamPolicy(policy) if err != nil { diff --git a/third_party/terraform/tests/resource_google_service_account_iam_test.go.erb b/third_party/terraform/tests/resource_google_service_account_iam_test.go.erb index 7fd60b9cf48e..f6c46b55d69c 100644 --- a/third_party/terraform/tests/resource_google_service_account_iam_test.go.erb +++ b/third_party/terraform/tests/resource_google_service_account_iam_test.go.erb @@ -3,8 +3,6 @@ package google import ( "fmt" - "reflect" - "sort" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" @@ -23,20 +21,76 @@ func TestAccServiceAccountIamBinding(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccServiceAccountIamBinding_basic(account), - Check: testAccCheckGoogleServiceAccountIam(account, "roles/viewer", []string{ - fmt.Sprintf("serviceAccount:%s", serviceAccountCanonicalEmail(account)), - }), + Check: testAccCheckGoogleServiceAccountIam(account, 1), }, { ResourceName: "google_service_account_iam_binding.foo", - ImportStateId: fmt.Sprintf("%s %s", serviceAccountCanonicalId(account), "roles/viewer"), ImportState: true, ImportStateVerify: true, + ImportStateId: fmt.Sprintf("%s %s", serviceAccountCanonicalId(account), "roles/iam.serviceAccountUser"), }, }, }) } +<% unless version == 'ga' -%> +func TestAccServiceAccountIamBinding_withCondition(t *testing.T) { + t.Parallel() + + account := acctest.RandomWithPrefix("tf-test") + conditionExpr := `request.time < timestamp(\"2020-01-01T00:00:00Z\")` + conditionTitle := "expires_after_2019_12_31" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccServiceAccountIamBinding_withCondition(account, "user:admin@hashicorptest.com", conditionTitle, conditionExpr), + Check: testAccCheckGoogleServiceAccountIam(account, 1), + }, + { + ResourceName: "google_service_account_iam_binding.foo", + ImportState: true, + ImportStateVerify: true, + ImportStateId: fmt.Sprintf("%s %s %s", serviceAccountCanonicalId(account), "roles/iam.serviceAccountUser", conditionTitle), + }, + }, + }) +} + +func TestAccServiceAccountIamBinding_withAndWithoutCondition(t *testing.T) { + t.Parallel() + + account := acctest.RandomWithPrefix("tf-test") + conditionExpr := `request.time < timestamp(\"2020-01-01T00:00:00Z\")` + conditionTitle := "expires_after_2019_12_31" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccServiceAccountIamBinding_withAndWithoutCondition(account, "user:admin@hashicorptest.com", conditionTitle, conditionExpr), + Check: testAccCheckGoogleServiceAccountIam(account, 2), + }, + { + ResourceName: "google_service_account_iam_binding.foo", + ImportState: true, + ImportStateVerify: true, + ImportStateId: fmt.Sprintf("%s %s", serviceAccountCanonicalId(account), "roles/iam.serviceAccountUser"), + }, + { + ResourceName: "google_service_account_iam_binding.foo2", + ImportState: true, + ImportStateVerify: true, + ImportStateId: fmt.Sprintf("%s %s %s", serviceAccountCanonicalId(account), "roles/iam.serviceAccountUser", conditionTitle), + }, + }, + }) +} +<% end -%> + func TestAccServiceAccountIamMember(t *testing.T) { t.Parallel() @@ -49,11 +103,37 @@ func TestAccServiceAccountIamMember(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccServiceAccountIamMember_basic(account), - Check: testAccCheckGoogleServiceAccountIam(account, "roles/editor", []string{identity}), + Check: testAccCheckGoogleServiceAccountIam(account, 1), + }, + { + ResourceName: "google_service_account_iam_member.foo", + ImportStateId: fmt.Sprintf("%s %s %s", serviceAccountCanonicalId(account), "roles/iam.serviceAccountUser", identity), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +<% unless version == 'ga' -%> +func TestAccServiceAccountIamMember_withCondition(t *testing.T) { + t.Parallel() + + account := acctest.RandomWithPrefix("tf-test") + identity := fmt.Sprintf("serviceAccount:%s", serviceAccountCanonicalEmail(account)) + conditionTitle := "expires_after_2019_12_31" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccServiceAccountIamMember_withCondition(account, conditionTitle), + Check: testAccCheckGoogleServiceAccountIam(account, 1), }, { ResourceName: "google_service_account_iam_member.foo", - ImportStateId: fmt.Sprintf("%s %s %s", serviceAccountCanonicalId(account), "roles/editor", identity), + ImportStateId: fmt.Sprintf("%s %s %s %s", serviceAccountCanonicalId(account), "roles/iam.serviceAccountUser", identity, conditionTitle), ImportState: true, ImportStateVerify: true, }, @@ -61,6 +141,38 @@ func TestAccServiceAccountIamMember(t *testing.T) { }) } +func TestAccServiceAccountIamMember_withAndWithoutCondition(t *testing.T) { + t.Parallel() + + account := acctest.RandomWithPrefix("tf-test") + identity := fmt.Sprintf("serviceAccount:%s", serviceAccountCanonicalEmail(account)) + conditionTitle := "expires_after_2019_12_31" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccServiceAccountIamMember_withAndWithoutCondition(account, conditionTitle), + Check: testAccCheckGoogleServiceAccountIam(account, 2), + }, + { + ResourceName: "google_service_account_iam_member.foo", + ImportStateId: fmt.Sprintf("%s %s %s", serviceAccountCanonicalId(account), "roles/iam.serviceAccountUser", identity), + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "google_service_account_iam_member.foo2", + ImportStateId: fmt.Sprintf("%s %s %s %s", serviceAccountCanonicalId(account), "roles/iam.serviceAccountUser", identity, conditionTitle), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +<% end -%> + func TestAccServiceAccountIamPolicy(t *testing.T) { t.Parallel() @@ -72,9 +184,6 @@ func TestAccServiceAccountIamPolicy(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccServiceAccountIamPolicy_basic(account), - Check: testAccCheckGoogleServiceAccountIam(account, "roles/owner", []string{ - fmt.Sprintf("serviceAccount:%s", serviceAccountCanonicalEmail(account)), - }), }, { ResourceName: "google_service_account_iam_policy.foo", @@ -86,28 +195,49 @@ func TestAccServiceAccountIamPolicy(t *testing.T) { }) } -func testAccCheckGoogleServiceAccountIam(account, role string, members []string) resource.TestCheckFunc { +<% unless version == 'ga' -%> +func TestAccServiceAccountIamPolicy_withCondition(t *testing.T) { + t.Parallel() + + account := acctest.RandomWithPrefix("tf-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccServiceAccountIamPolicy_withCondition(account), + }, + { + ResourceName: "google_service_account_iam_policy.foo", + ImportStateId: serviceAccountCanonicalId(account), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +<% end -%> + +// Ensure that our tests only create the expected number of bindings. +// The content of the binding is tested in the import tests. +func testAccCheckGoogleServiceAccountIam(account string, numBindings int) resource.TestCheckFunc { return func(s *terraform.State) error { config := testAccProvider.Meta().(*Config) +<% if version == 'ga' -%> p, err := config.clientIAM.Projects.ServiceAccounts.GetIamPolicy(serviceAccountCanonicalId(account)).Do() +<% else -%> + p, err := config.clientIAM.Projects.ServiceAccounts.GetIamPolicy(serviceAccountCanonicalId(account)).OptionsRequestedPolicyVersion(iamPolicyVersion).Do() +<% end -%> if err != nil { return err } - for _, binding := range p.Bindings { - if binding.Role == role { - sort.Strings(members) - sort.Strings(binding.Members) - - if reflect.DeepEqual(members, binding.Members) { - return nil - } - - return fmt.Errorf("Binding found but expected members is %v, got %v", members, binding.Members) - } + if len(p.Bindings) != numBindings { + return fmt.Errorf("Expected exactly %d binding(s) for account %q, was %d", numBindings, account, len(p.Bindings)) } - return fmt.Errorf("No binding for role %q", role) + return nil } } @@ -128,12 +258,60 @@ resource "google_service_account" "test_account" { resource "google_service_account_iam_binding" "foo" { service_account_id = "${google_service_account.test_account.name}" - role = "roles/viewer" - members = ["serviceAccount:${google_service_account.test_account.email}"] + role = "roles/iam.serviceAccountUser" + members = ["user:admin@hashicorptest.com"] } `, account) } +<% unless version == 'ga' -%> +func testAccServiceAccountIamBinding_withCondition(account, member, conditionTitle, conditionExpr string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Service Account Iam Testing Account" +} + +resource "google_service_account_iam_binding" "foo" { + service_account_id = "${google_service_account.test_account.name}" + role = "roles/iam.serviceAccountUser" + members = ["%s"] + condition { + title = "%s" + description = "Expiring at midnight of 2019-12-31" + expression = "%s" + } +} +`, account, member, conditionTitle, conditionExpr) +} + +func testAccServiceAccountIamBinding_withAndWithoutCondition(account, member, conditionTitle, conditionExpr string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Service Account Iam Testing Account" +} + +resource "google_service_account_iam_binding" "foo" { + service_account_id = "${google_service_account.test_account.name}" + role = "roles/iam.serviceAccountUser" + members = ["%s"] +} + +resource "google_service_account_iam_binding" "foo2" { + service_account_id = "${google_service_account.test_account.name}" + role = "roles/iam.serviceAccountUser" + members = ["%s"] + condition { + title = "%s" + description = "Expiring at midnight of 2019-12-31" + expression = "%s" + } +} +`, account, member, member, conditionTitle, conditionExpr) +} +<% end -%> + func testAccServiceAccountIamMember_basic(account string) string { return fmt.Sprintf(` resource "google_service_account" "test_account" { @@ -143,12 +321,60 @@ resource "google_service_account" "test_account" { resource "google_service_account_iam_member" "foo" { service_account_id = "${google_service_account.test_account.name}" - role = "roles/editor" + role = "roles/iam.serviceAccountUser" member = "serviceAccount:${google_service_account.test_account.email}" } `, account) } +<% unless version == 'ga' -%> +func testAccServiceAccountIamMember_withCondition(account, conditionTitle string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Service Account Iam Testing Account" +} + +resource "google_service_account_iam_member" "foo" { + service_account_id = "${google_service_account.test_account.name}" + role = "roles/iam.serviceAccountUser" + member = "serviceAccount:${google_service_account.test_account.email}" + condition { + title = "%s" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } +} +`, account, conditionTitle) +} + +func testAccServiceAccountIamMember_withAndWithoutCondition(account, conditionTitle string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Service Account Iam Testing Account" +} + +resource "google_service_account_iam_member" "foo" { + service_account_id = "${google_service_account.test_account.name}" + role = "roles/iam.serviceAccountUser" + member = "serviceAccount:${google_service_account.test_account.email}" +} + +resource "google_service_account_iam_member" "foo2" { + service_account_id = "${google_service_account.test_account.name}" + role = "roles/iam.serviceAccountUser" + member = "serviceAccount:${google_service_account.test_account.email}" + condition { + title = "%s" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } +} +`, account, conditionTitle) +} +<% end -%> + func testAccServiceAccountIamPolicy_basic(account string) string { return fmt.Sprintf(` resource "google_service_account" "test_account" { @@ -157,11 +383,39 @@ resource "google_service_account" "test_account" { } data "google_iam_policy" "foo" { - binding { - role = "roles/owner" + binding { + role = "roles/iam.serviceAccountUser" - members = ["serviceAccount:${google_service_account.test_account.email}"] - } + members = ["serviceAccount:${google_service_account.test_account.email}"] + } +} + +resource "google_service_account_iam_policy" "foo" { + service_account_id = "${google_service_account.test_account.name}" + policy_data = "${data.google_iam_policy.foo.policy_data}" +} +`, account) +} + +<% unless version == 'ga' -%> +func testAccServiceAccountIamPolicy_withCondition(account string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Service Account Iam Testing Account" +} + +data "google_iam_policy" "foo" { + binding { + role = "roles/iam.serviceAccountUser" + + members = ["serviceAccount:${google_service_account.test_account.email}"] + condition { + title = "expires_after_2019_12_31" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } + } } resource "google_service_account_iam_policy" "foo" { @@ -170,3 +424,4 @@ resource "google_service_account_iam_policy" "foo" { } `, account) } +<% end -%> diff --git a/third_party/terraform/utils/iam.go b/third_party/terraform/utils/iam.go.erb similarity index 84% rename from third_party/terraform/utils/iam.go rename to third_party/terraform/utils/iam.go.erb index 58ff369e1d13..658661ed13e7 100644 --- a/third_party/terraform/utils/iam.go +++ b/third_party/terraform/utils/iam.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> // Utils for modifying IAM policies for resources across GCP package google @@ -15,6 +16,9 @@ import ( ) const maxBackoffSeconds = 30 +<% unless version == 'ga' -%> +const iamPolicyVersion = 3 +<% end -%> // These types are implemented per GCP resource type and specify how to do per-resource IAM operations. // They are used in the generic Terraform IAM resource definitions @@ -152,26 +156,53 @@ func iamPolicyReadModifyWrite(updater ResourceIamUpdater, modify iamPolicyModify return nil } -// Flattens AuditConfigs so each role has a single Binding with combined members +// Flattens a list of Bindings so each role+condition has a single Binding with combined members func mergeBindings(bindings []*cloudresourcemanager.Binding) []*cloudresourcemanager.Binding { bm := createIamBindingsMap(bindings) return listFromIamBindingMap(bm) } -// Flattens Bindings so each role has a single Binding with combined members -func removeAllBindingsWithRole(b []*cloudresourcemanager.Binding, role string) []*cloudresourcemanager.Binding { +type conditionKey struct { + Description string + Expression string + Title string +} + +func conditionKeyFromCondition(condition *cloudresourcemanager.Expr) conditionKey { + if condition == nil { + return conditionKey{} + } + return conditionKey{condition.Description, condition.Expression, condition.Title} +} + +func (k conditionKey) Empty() bool { + return k == conditionKey{} +} + +func (k conditionKey) String() string { + return fmt.Sprintf("%s/%s/%s", k.Title, k.Description, k.Expression) +} + +type iamBindingKey struct { + Role string + Condition conditionKey +} + +// Removes a single role+condition binding from a list of Bindings +func filterBindingsWithRoleAndCondition(b []*cloudresourcemanager.Binding, role string, condition *cloudresourcemanager.Expr) []*cloudresourcemanager.Binding { bMap := createIamBindingsMap(b) - delete(bMap, role) + key := iamBindingKey{role, conditionKeyFromCondition(condition)} + delete(bMap, key) return listFromIamBindingMap(bMap) } -// Removes given role/bound-member pairs from the given Bindings (i.e subtraction). +// Removes given role+condition/bound-member pairs from the given Bindings (i.e subtraction). func subtractFromBindings(bindings []*cloudresourcemanager.Binding, toRemove ...*cloudresourcemanager.Binding) []*cloudresourcemanager.Binding { currMap := createIamBindingsMap(bindings) toRemoveMap := createIamBindingsMap(toRemove) - for role, removeSet := range toRemoveMap { - members, ok := currMap[role] + for key, removeSet := range toRemoveMap { + members, ok := currMap[key] if !ok { continue } @@ -179,9 +210,9 @@ func subtractFromBindings(bindings []*cloudresourcemanager.Binding, toRemove ... for m := range removeSet { delete(members, m) } - // Remove role from bindings + // Remove role+condition from bindings if len(members) == 0 { - delete(currMap, role) + delete(currMap, key) } } @@ -189,14 +220,15 @@ func subtractFromBindings(bindings []*cloudresourcemanager.Binding, toRemove ... } // Construct map of role to set of members from list of bindings. -func createIamBindingsMap(bindings []*cloudresourcemanager.Binding) map[string]map[string]struct{} { - bm := make(map[string]map[string]struct{}) +func createIamBindingsMap(bindings []*cloudresourcemanager.Binding) map[iamBindingKey]map[string]struct{} { + bm := make(map[iamBindingKey]map[string]struct{}) // Get each binding for _, b := range bindings { members := make(map[string]struct{}) + key := iamBindingKey{b.Role, conditionKeyFromCondition(b.Condition)} // Initialize members map - if _, ok := bm[b.Role]; ok { - members = bm[b.Role] + if _, ok := bm[key]; ok { + members = bm[key] } // Get each member (user/principal) for the binding for _, m := range b.Members { @@ -214,25 +246,35 @@ func createIamBindingsMap(bindings []*cloudresourcemanager.Binding) map[string]m members[m] = struct{}{} } if len(members) > 0 { - bm[b.Role] = members + bm[key] = members } else { - delete(bm, b.Role) + delete(bm, key) } } return bm } // Return list of Bindings for a map of role to member sets -func listFromIamBindingMap(bm map[string]map[string]struct{}) []*cloudresourcemanager.Binding { +func listFromIamBindingMap(bm map[iamBindingKey]map[string]struct{}) []*cloudresourcemanager.Binding { rb := make([]*cloudresourcemanager.Binding, 0, len(bm)) - for role, members := range bm { + for key, members := range bm { if len(members) == 0 { continue } - rb = append(rb, &cloudresourcemanager.Binding{ - Role: role, + b := &cloudresourcemanager.Binding{ + Role: key.Role, Members: stringSliceFromGolangSet(members), - }) + } +<% unless version == 'ga' -%> + if !key.Condition.Empty() { + b.Condition = &cloudresourcemanager.Expr{ + Description: key.Condition.Description, + Expression: key.Condition.Expression, + Title: key.Condition.Title, + } + } +<% end -%> + rb = append(rb, b) } return rb } diff --git a/third_party/terraform/utils/iam_service_account.go b/third_party/terraform/utils/iam_service_account.go.erb similarity index 92% rename from third_party/terraform/utils/iam_service_account.go rename to third_party/terraform/utils/iam_service_account.go.erb index adee07eb30b3..93e456d71352 100644 --- a/third_party/terraform/utils/iam_service_account.go +++ b/third_party/terraform/utils/iam_service_account.go.erb @@ -1,7 +1,9 @@ +<% autogen_exception -%> package google import ( "fmt" + "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "google.golang.org/api/cloudresourcemanager/v1" @@ -35,7 +37,11 @@ func ServiceAccountIdParseFunc(d *schema.ResourceData, _ *Config) error { } func (u *ServiceAccountIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { +<% if version == 'ga' -%> p, err := u.Config.clientIAM.Projects.ServiceAccounts.GetIamPolicy(u.serviceAccountId).Do() +<% else -%> + p, err := u.Config.clientIAM.Projects.ServiceAccounts.GetIamPolicy(u.serviceAccountId).OptionsRequestedPolicyVersion(iamPolicyVersion).Do() +<% end -%> if err != nil { return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) diff --git a/third_party/terraform/utils/iam_test.go b/third_party/terraform/utils/iam_test.go.erb similarity index 78% rename from third_party/terraform/utils/iam_test.go rename to third_party/terraform/utils/iam_test.go.erb index eed54b6257c7..33205dd40594 100644 --- a/third_party/terraform/utils/iam_test.go +++ b/third_party/terraform/utils/iam_test.go.erb @@ -1,10 +1,12 @@ +<% autogen_exception -%> package google import ( "encoding/json" - "google.golang.org/api/cloudresourcemanager/v1" "reflect" "testing" + + "google.golang.org/api/cloudresourcemanager/v1" ) func TestIamMergeBindings(t *testing.T) { @@ -152,6 +154,100 @@ func TestIamMergeBindings(t *testing.T) { }, }, }, +<% unless version == 'ga' -%> + // Same role+members, different condition + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-1", "member-2"}, + }, + { + Role: "role-1", + Members: []string{"member-1", "member-2"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + }, + }, + }, + expect: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-1", "member-2"}, + }, + { + Role: "role-1", + Members: []string{"member-1", "member-2"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + }, + }, + }, + }, + // Same role, same condition + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-1", "member-2"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + }, + }, + { + Role: "role-1", + Members: []string{"member-3"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + }, + }, + }, + expect: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-1", "member-2", "member-3"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + }, + }, + }, + }, + // Different roles, same condition + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-1", "member-2"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + }, + }, + { + Role: "role-2", + Members: []string{"member-3"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + }, + }, + }, + expect: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-1", "member-2"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + }, + }, + { + Role: "role-2", + Members: []string{"member-3"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + }, + }, + }, + }, +<% end -%> } for _, tc := range testCases { @@ -163,11 +259,12 @@ func TestIamMergeBindings(t *testing.T) { } } -func TestIamRemoveAllBindingsWithRole(t *testing.T) { +func TestIamFilterBindingsWithRoleAndCondition(t *testing.T) { testCases := []struct { - input []*cloudresourcemanager.Binding - role string - expect []*cloudresourcemanager.Binding + input []*cloudresourcemanager.Binding + role string + conditionTitle string + expect []*cloudresourcemanager.Binding }{ // No-op { @@ -241,10 +338,34 @@ func TestIamRemoveAllBindingsWithRole(t *testing.T) { }, }, }, +<% unless version == 'ga' -%> + // Remove one binding with condition + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-1", "member-2"}, + }, + { + Role: "role-1", + Members: []string{"member-3", "member-4"}, + Condition: &cloudresourcemanager.Expr{Title: "condition-1"}, + }, + }, + role: "role-1", + conditionTitle: "condition-1", + expect: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-1", "member-2"}, + }, + }, + }, +<% end -%> } for _, tc := range testCases { - got := removeAllBindingsWithRole(tc.input, tc.role) + got := filterBindingsWithRoleAndCondition(tc.input, tc.role, &cloudresourcemanager.Expr{Title: tc.conditionTitle}) if !compareBindings(got, tc.expect) { t.Errorf("Got unexpected value for removeAllBindingsWithRole(%s, %s).\nActual: %s\nExpected: %s", debugPrintBindings(tc.input), tc.role, debugPrintBindings(got), debugPrintBindings(tc.expect)) @@ -396,6 +517,59 @@ func TestIamSubtractFromBindings(t *testing.T) { }, }, }, +<% unless version == 'ga' -%> + // With conditions + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-2", "member-3"}, + }, + { + Role: "role-2", + Members: []string{"member-1"}, + }, + { + Role: "role-1", + Members: []string{"member-1"}, + }, + { + Role: "role-3", + Members: []string{"member-1"}, + }, + { + Role: "role-2", + Members: []string{"member-1"}, + Condition: &cloudresourcemanager.Expr{Title: "condition-1"}, + }, + }, + remove: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-2", "member-4"}, + }, + { + Role: "role-2", + Members: []string{"member-1"}, + Condition: &cloudresourcemanager.Expr{Title: "condition-1"}, + }, + }, + expect: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"member-1", "member-3"}, + }, + { + Role: "role-2", + Members: []string{"member-1"}, + }, + { + Role: "role-3", + Members: []string{"member-1"}, + }, + }, + }, +<% end -%> } for _, tc := range testCases { @@ -410,11 +584,11 @@ func TestIamSubtractFromBindings(t *testing.T) { func TestIamCreateIamBindingsMap(t *testing.T) { testCases := []struct { input []*cloudresourcemanager.Binding - expect map[string]map[string]struct{} + expect map[iamBindingKey]map[string]struct{} }{ { input: []*cloudresourcemanager.Binding{}, - expect: map[string]map[string]struct{}{}, + expect: map[iamBindingKey]map[string]struct{}{}, }, { input: []*cloudresourcemanager.Binding{ @@ -423,8 +597,8 @@ func TestIamCreateIamBindingsMap(t *testing.T) { Members: []string{"user-1", "user-2"}, }, }, - expect: map[string]map[string]struct{}{ - "role-1": {"user-1": {}, "user-2": {}}, + expect: map[iamBindingKey]map[string]struct{}{ + iamBindingKey{"role-1", conditionKey{}}: {"user-1": {}, "user-2": {}}, }, }, { @@ -438,8 +612,8 @@ func TestIamCreateIamBindingsMap(t *testing.T) { Members: []string{"user-3"}, }, }, - expect: map[string]map[string]struct{}{ - "role-1": {"user-1": {}, "user-2": {}, "user-3": {}}, + expect: map[iamBindingKey]map[string]struct{}{ + iamBindingKey{"role-1", conditionKey{}}: {"user-1": {}, "user-2": {}, "user-3": {}}, }, }, { @@ -453,9 +627,9 @@ func TestIamCreateIamBindingsMap(t *testing.T) { Members: []string{"user-1"}, }, }, - expect: map[string]map[string]struct{}{ - "role-1": {"user-1": {}, "user-2": {}}, - "role-2": {"user-1": {}}, + expect: map[iamBindingKey]map[string]struct{}{ + iamBindingKey{"role-1", conditionKey{}}: {"user-1": {}, "user-2": {}}, + iamBindingKey{"role-2", conditionKey{}}: {"user-1": {}}, }, }, { @@ -481,18 +655,74 @@ func TestIamCreateIamBindingsMap(t *testing.T) { Members: []string{"user-3"}, }, }, - expect: map[string]map[string]struct{}{ - "role-1": {"user-1": {}, "user-2": {}, "user-3": {}}, - "role-2": {"user-1": {}, "user-2": {}}, - "role-3": {"user-3": {}}, + expect: map[iamBindingKey]map[string]struct{}{ + iamBindingKey{"role-1", conditionKey{}}: {"user-1": {}, "user-2": {}, "user-3": {}}, + iamBindingKey{"role-2", conditionKey{}}: {"user-1": {}, "user-2": {}}, + iamBindingKey{"role-3", conditionKey{}}: {"user-3": {}}, + }, + }, +<% unless version == 'ga' -%> + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{"user-1", "user-2"}, + }, + { + Role: "role-2", + Members: []string{"user-1"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + Description: "condition-1-desc", + Expression: "condition-1-expr", + }, + }, + { + Role: "role-2", + Members: []string{"user-2"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-1", + Description: "condition-1-desc", + Expression: "condition-1-expr", + }, + }, + { + Role: "role-2", + Members: []string{"user-1"}, + Condition: &cloudresourcemanager.Expr{ + Title: "condition-2", + Description: "condition-2-desc", + Expression: "condition-2-expr", + }, + }, + }, + expect: map[iamBindingKey]map[string]struct{}{ + iamBindingKey{"role-1", conditionKey{}}: {"user-1": {}, "user-2": {}}, + iamBindingKey{ + Role: "role-2", + Condition: conditionKey{ + Title: "condition-1", + Description: "condition-1-desc", + Expression: "condition-1-expr", + }, + }: {"user-1": {}, "user-2": {}}, + iamBindingKey{ + Role: "role-2", + Condition: conditionKey{ + Title: "condition-2", + Description: "condition-2-desc", + Expression: "condition-2-expr", + }, + }: {"user-1": {}}, }, }, +<% end -%> } for _, tc := range testCases { got := createIamBindingsMap(tc.input) if !reflect.DeepEqual(got, tc.expect) { - t.Errorf("Unexpected value for subtractFromBindings(%s).\nActual: %#v\nExpected: %#v\n", + t.Errorf("Unexpected value for createIamBindingsMap(%s).\nActual: %#v\nExpected: %#v\n", debugPrintBindings(tc.input), got, tc.expect) } } @@ -500,16 +730,16 @@ func TestIamCreateIamBindingsMap(t *testing.T) { func TestIamListFromIamBindingMap(t *testing.T) { testCases := []struct { - input map[string]map[string]struct{} + input map[iamBindingKey]map[string]struct{} expect []*cloudresourcemanager.Binding }{ { - input: map[string]map[string]struct{}{}, + input: map[iamBindingKey]map[string]struct{}{}, expect: []*cloudresourcemanager.Binding{}, }, { - input: map[string]map[string]struct{}{ - "role-1": {"user-1": {}, "user-2": {}}, + input: map[iamBindingKey]map[string]struct{}{ + iamBindingKey{"role-1", conditionKey{}}: {"user-1": {}, "user-2": {}}, }, expect: []*cloudresourcemanager.Binding{ { @@ -519,9 +749,9 @@ func TestIamListFromIamBindingMap(t *testing.T) { }, }, { - input: map[string]map[string]struct{}{ - "role-1": {"user-1": {}}, - "role-2": {"user-1": {}, "user-2": {}}, + input: map[iamBindingKey]map[string]struct{}{ + iamBindingKey{"role-1", conditionKey{}}: {"user-1": {}}, + iamBindingKey{"role-2", conditionKey{}}: {"user-1": {}, "user-2": {}}, }, expect: []*cloudresourcemanager.Binding{ { @@ -535,9 +765,9 @@ func TestIamListFromIamBindingMap(t *testing.T) { }, }, { - input: map[string]map[string]struct{}{ - "role-1": {"user-1": {}, "user-2": {}}, - "role-2": {}, + input: map[iamBindingKey]map[string]struct{}{ + iamBindingKey{"role-1", conditionKey{}}: {"user-1": {}, "user-2": {}}, + iamBindingKey{"role-2", conditionKey{}}: {}, }, expect: []*cloudresourcemanager.Binding{ { @@ -551,7 +781,7 @@ func TestIamListFromIamBindingMap(t *testing.T) { for _, tc := range testCases { got := listFromIamBindingMap(tc.input) if !compareBindings(got, tc.expect) { - t.Errorf("Unexpected value for subtractFromBindings(%s).\nActual: %#v\nExpected: %#v\n", + t.Errorf("Unexpected value for subtractFromBindings(%v).\nActual: %#v\nExpected: %#v\n", tc.input, debugPrintBindings(got), debugPrintBindings(tc.expect)) } } diff --git a/third_party/terraform/website/docs/r/google_service_account_iam.html.markdown b/third_party/terraform/website/docs/r/google_service_account_iam.html.markdown index 033532c2dbf8..16f26505bcca 100644 --- a/third_party/terraform/website/docs/r/google_service_account_iam.html.markdown +++ b/third_party/terraform/website/docs/r/google_service_account_iam.html.markdown @@ -63,6 +63,30 @@ resource "google_service_account_iam_binding" "admin-account-iam" { } ``` +With IAM Conditions ([beta](https://terraform.io/docs/providers/google/provider_versions.html)): + +```hcl +resource "google_service_account" "sa" { + account_id = "my-service-account" + display_name = "A service account that only Jane can use" +} + +resource "google_service_account_iam_binding" "admin-account-iam" { + service_account_id = "${google_service_account.sa.name}" + role = "roles/iam.serviceAccountUser" + + members = [ + "user:jane@example.com", + ] + + condition { + title = "expires_after_2019_12_31" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } +} +``` + ## google\_service\_account\_iam\_member ```hcl @@ -87,6 +111,27 @@ resource "google_service_account_iam_member" "gce-default-account-iam" { } ``` +With IAM Conditions ([beta](https://terraform.io/docs/providers/google/provider_versions.html)): + +```hcl +resource "google_service_account" "sa" { + account_id = "my-service-account" + display_name = "A service account that Jane can use" +} + +resource "google_service_account_iam_member" "admin-account-iam" { + service_account_id = "${google_service_account.sa.name}" + role = "roles/iam.serviceAccountUser" + member = "user:jane@example.com" + + condition { + title = "expires_after_2019_12_31" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } +} +``` + ## Argument Reference The following arguments are supported: @@ -109,6 +154,21 @@ The following arguments are supported: * `policy_data` - (Required only by `google_service_account_iam_policy`) The policy data generated by a `google_iam_policy` data source. +* `condition` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) An [IAM Condition](https://cloud.google.com/iam/docs/conditions-overview) for a given binding. + Structure is documented below. + +The `condition` block supports: + +* `expression` - (Required) Textual representation of an expression in Common Expression Language syntax. + +* `title` - (Required) A title for the expression, i.e. a short string describing its purpose. + +* `description` - (Optional) An optional description of the expression. This is a longer text which describes the expression, e.g. when hovered over it in a UI. + +~> **Warning:** Terraform considers the `role` and condition contents (`title`+`description`+`expression`) as the + identifier for the binding. This means that if any part of the condition is changed out-of-band, Terraform will + consider it to be an entirely different resource and will treat it as such. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are @@ -118,12 +178,18 @@ exported: ## Import -Service account IAM resources can be imported using the project, service account email, role and member identity. +Service account IAM resources can be imported using the project, service account email, role, member identity, and condition (beta). ``` $ terraform import google_service_account_iam_policy.admin-account-iam projects/{your-project-id}/serviceAccounts/{your-service-account-email} -$ terraform import google_service_account_iam_binding.admin-account-iam "projects/{your-project-id}/serviceAccounts/{your-service-account-email} roles/editor" +$ terraform import google_service_account_iam_binding.admin-account-iam "projects/{your-project-id}/serviceAccounts/{your-service-account-email} iam.serviceAccountUser" + +$ terraform import google_service_account_iam_member.admin-account-iam "projects/{your-project-id}/serviceAccounts/{your-service-account-email} iam.serviceAccountUser user:foo@example.com" +``` + +With conditions: +``` +$ terraform import -provider=google-beta google_service_account_iam_binding.admin-account-iam "projects/{your-project-id}/serviceAccounts/{your-service-account-email} iam.serviceAccountUser expires_after_2019_12_31" -$ terraform import google_service_account_iam_member.admin-account-iam "projects/{your-project-id}/serviceAccounts/{your-service-account-email} roles/editor user:foo@example.com" -``` \ No newline at end of file +$ terraform import -provider=google-beta google_service_account_iam_member.admin-account-iam "projects/{your-project-id}/serviceAccounts/{your-service-account-email} iam.serviceAccountUser user:foo@example.com expires_after_2019_12_31" \ No newline at end of file