From d5584c59de084c1255df6410c278e934e5448a39 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Mon, 7 Oct 2019 10:25:57 -0700 Subject: [PATCH 01/87] region backend service uses the same hash function but needs to count failover status. (#2430) Merged PR #2430. --- build/inspec | 2 +- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- templates/terraform/constants/backend_service.go.erb | 9 +++++++++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/build/inspec b/build/inspec index 7860c53f0b92..555aecfef2cd 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 7860c53f0b92eeecea47b65e5acc77994ee19d9a +Subproject commit 555aecfef2cdfe5026b2c8612af89d01535b1943 diff --git a/build/terraform b/build/terraform index dbce9ada1e1c..37bb125cbd81 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit dbce9ada1e1cb22219773ed0afc45ed643659cbf +Subproject commit 37bb125cbd81547a1f0d1ca9bc2214773fa876d2 diff --git a/build/terraform-beta b/build/terraform-beta index 637b8c5acbc3..90119911a690 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 637b8c5acbc370be367fa7d105d6ce8a0a881970 +Subproject commit 90119911a690859023a37107f741429ab9a58532 diff --git a/build/terraform-mapper b/build/terraform-mapper index 2114619fe47b..f2e4a08f0339 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 2114619fe47b96ab9763d42cbc2d07882c2a0c4d +Subproject commit f2e4a08f03396880afa709d19d08ba07103f0a45 diff --git a/templates/terraform/constants/backend_service.go.erb b/templates/terraform/constants/backend_service.go.erb index aeb847122815..6eda332c8043 100644 --- a/templates/terraform/constants/backend_service.go.erb +++ b/templates/terraform/constants/backend_service.go.erb @@ -112,6 +112,15 @@ func resourceGoogleComputeBackendServiceBackendHash(v interface{}) int { buf.WriteString(fmt.Sprintf("%f-", v.(float64))) } + // This is in region backend service, but not in backend service. Should be a no-op + // if it's not present. + if v, ok := m["failover"]; ok { + if v == nil { + v = false + } + buf.WriteString(fmt.Sprintf("%v-", v.(bool))) + } + log.Printf("[DEBUG] computed hash value of %v from %v", hashcode.String(buf.String()), buf.String()) return hashcode.String(buf.String()) } From 745c08ae67a5b2cb01146554dbd65358034abef3 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Mon, 7 Oct 2019 13:47:43 -0700 Subject: [PATCH 02/87] Add InSpec support for node groups, node templates and network endpoint group (#2432) Merged PR #2432. --- build/inspec | 2 +- build/terraform | 2 +- build/terraform-beta | 2 +- products/compute/inspec.yaml | 6 -- .../google_compute_network_endpoint_group.erb | 11 ++++ ...pute_network_endpoint_group_attributes.erb | 3 + ...google_compute_network_endpoint_groups.erb | 7 ++ .../google_compute_node_group.erb | 12 ++++ .../google_compute_node_group_attributes.erb | 3 + .../google_compute_node_groups.erb | 9 +++ .../google_compute_node_template.erb | 11 ++++ ...oogle_compute_node_template_attributes.erb | 3 + .../google_compute_node_templates.erb | 6 ++ .../google_compute_snapshot.erb | 3 +- .../google_compute_snapshot_attributes.erb | 1 - .../google_compute_snapshots.erb | 3 +- .../inspec/tests/integration/build/gcp-mm.tf | 64 +++++++++++++++++-- .../configuration/mm-attributes.yml | 15 +++++ 18 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_group.erb create mode 100644 templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_group_attributes.erb create mode 100644 templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_groups.erb create mode 100644 templates/inspec/examples/google_compute_node_group/google_compute_node_group.erb create mode 100644 templates/inspec/examples/google_compute_node_group/google_compute_node_group_attributes.erb create mode 100644 templates/inspec/examples/google_compute_node_group/google_compute_node_groups.erb create mode 100644 templates/inspec/examples/google_compute_node_template/google_compute_node_template.erb create mode 100644 templates/inspec/examples/google_compute_node_template/google_compute_node_template_attributes.erb create mode 100644 templates/inspec/examples/google_compute_node_template/google_compute_node_templates.erb diff --git a/build/inspec b/build/inspec index 555aecfef2cd..307ff3b726e4 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 555aecfef2cdfe5026b2c8612af89d01535b1943 +Subproject commit 307ff3b726e4d2fb6372f325f6045b7d7a22f5ea diff --git a/build/terraform b/build/terraform index 37bb125cbd81..84bf5d189c9a 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 37bb125cbd81547a1f0d1ca9bc2214773fa876d2 +Subproject commit 84bf5d189c9a9d6bd7178fec25e7905890844c23 diff --git a/build/terraform-beta b/build/terraform-beta index 90119911a690..a0d2752d2251 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 90119911a690859023a37107f741429ab9a58532 +Subproject commit a0d2752d2251bf89b9a8272f7582bc90e8c24b64 diff --git a/products/compute/inspec.yaml b/products/compute/inspec.yaml index 867a9e9f3394..53b1e8839916 100644 --- a/products/compute/inspec.yaml +++ b/products/compute/inspec.yaml @@ -50,12 +50,6 @@ overrides: !ruby/object:Overrides::ResourceOverrides exclude: true NetworkEndpoint: !ruby/object:Overrides::Inspec::ResourceOverride exclude: true - NetworkEndpointGroup: !ruby/object:Overrides::Inspec::ResourceOverride - exclude: true - NodeGroup: !ruby/object:Overrides::Inspec::ResourceOverride - exclude: true - NodeTemplate: !ruby/object:Overrides::Inspec::ResourceOverride - exclude: true Region: !ruby/object:Overrides::Inspec::ResourceOverride additional_functions: | <%= lines(indent(compile('templates/inspec/custom_functions/google_compute_region.rb'), 6)) -%> diff --git a/templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_group.erb b/templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_group.erb new file mode 100644 index 000000000000..6034a69db465 --- /dev/null +++ b/templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_group.erb @@ -0,0 +1,11 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% network_endpoint_group = grab_attributes['network_endpoint_group'] -%> +<% gcp_zone = "#{external_attribute('gcp_zone', doc_generation)}" %> +describe google_compute_network_endpoint_group(project: <%= gcp_project_id -%>, zone: <%= gcp_zone -%>, name: <%= doc_generation ? "'#{network_endpoint_group['name']}'" : "network_endpoint_group['name']" -%>) do + it { should exist } + its('default_port') { should cmp <%= doc_generation ? "'#{network_endpoint_group['default_port']}'" : "network_endpoint_group['default_port']" -%> } +end + +describe google_compute_network_endpoint_group(project: <%= gcp_project_id -%>, zone: <%= gcp_zone -%>, name: 'nonexistent') do + it { should_not exist } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_group_attributes.erb b/templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_group_attributes.erb new file mode 100644 index 000000000000..e6fe6011cafe --- /dev/null +++ b/templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_group_attributes.erb @@ -0,0 +1,3 @@ +gcp_project_id = attribute(:gcp_project_id, default: '<%= external_attribute('gcp_project_id') -%>', description: 'The GCP project identifier.') +network_endpoint_group = attribute('network_endpoint_group', default: <%= JSON.pretty_generate(grab_attributes['network_endpoint_group']) -%>, description: 'Network endpoint group description') +gcp_zone = attribute(:gcp_zone, default: '<%= external_attribute('gcp_zone') -%>', description: 'GCP zone name') \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_groups.erb b/templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_groups.erb new file mode 100644 index 000000000000..b416a5075442 --- /dev/null +++ b/templates/inspec/examples/google_compute_network_endpoint_group/google_compute_network_endpoint_groups.erb @@ -0,0 +1,7 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% network_endpoint_group = grab_attributes['network_endpoint_group'] -%> +<% gcp_zone = "#{external_attribute('gcp_zone', doc_generation)}" %> +describe google_compute_network_endpoint_groups(project: <%= gcp_project_id -%>, zone: <%= gcp_zone -%>) do + its('default_ports') { should include <%= doc_generation ? "'#{network_endpoint_group['default_port']}'" : "network_endpoint_group['default_port']" -%> } + its('names') { should include <%= doc_generation ? "'#{network_endpoint_group['name']}'" : "network_endpoint_group['name']" -%> } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_node_group/google_compute_node_group.erb b/templates/inspec/examples/google_compute_node_group/google_compute_node_group.erb new file mode 100644 index 000000000000..a4567eaa3e9c --- /dev/null +++ b/templates/inspec/examples/google_compute_node_group/google_compute_node_group.erb @@ -0,0 +1,12 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% node_group = grab_attributes['node_group'] -%> +<% gcp_zone = "#{external_attribute('gcp_zone', doc_generation)}" %> +describe google_compute_node_group(project: <%= gcp_project_id -%>, zone: <%= gcp_zone -%>, name: <%= doc_generation ? "'#{node_group['name']}'" : "node_group['name']" -%>) do + it { should exist } + its('description') { should cmp <%= doc_generation ? "'#{node_group['description']}'" : "node_group['description']" -%> } + its('size') { should cmp <%= doc_generation ? "'#{node_group['size']}'" : "node_group['size']" -%> } +end + +describe google_compute_node_group(project: <%= gcp_project_id -%>, zone: <%= gcp_zone -%>, name: 'nonexistent') do + it { should_not exist } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_node_group/google_compute_node_group_attributes.erb b/templates/inspec/examples/google_compute_node_group/google_compute_node_group_attributes.erb new file mode 100644 index 000000000000..5d11c86bc48e --- /dev/null +++ b/templates/inspec/examples/google_compute_node_group/google_compute_node_group_attributes.erb @@ -0,0 +1,3 @@ +gcp_project_id = attribute(:gcp_project_id, default: '<%= external_attribute('gcp_project_id') -%>', description: 'The GCP project identifier.') +node_group = attribute('node_group', default: <%= JSON.pretty_generate(grab_attributes['node_group']) -%>, description: 'Node group description') +gcp_zone = attribute(:gcp_zone, default: '<%= external_attribute('gcp_zone') -%>', description: 'GCP zone name') \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_node_group/google_compute_node_groups.erb b/templates/inspec/examples/google_compute_node_group/google_compute_node_groups.erb new file mode 100644 index 000000000000..4eea2831b019 --- /dev/null +++ b/templates/inspec/examples/google_compute_node_group/google_compute_node_groups.erb @@ -0,0 +1,9 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% node_group = grab_attributes['node_group'] -%> +<% gcp_zone = "#{external_attribute('gcp_zone', doc_generation)}" %> +describe google_compute_node_groups(project: <%= gcp_project_id -%>, zone: <%= gcp_zone -%>) do + it { should exist } + its('descriptions') { should include <%= doc_generation ? "'#{node_group['description']}'" : "node_group['description']" -%> } + its('sizes') { should include <%= doc_generation ? "'#{node_group['size']}'" : "node_group['size']" -%> } + its('names') { should include <%= doc_generation ? "'#{node_group['name']}'" : "node_group['name']" -%> } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_node_template/google_compute_node_template.erb b/templates/inspec/examples/google_compute_node_template/google_compute_node_template.erb new file mode 100644 index 000000000000..574227febdff --- /dev/null +++ b/templates/inspec/examples/google_compute_node_template/google_compute_node_template.erb @@ -0,0 +1,11 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% node_template = grab_attributes['node_template'] -%> +<% gcp_location = "#{external_attribute('gcp_location', doc_generation)}" %> +describe google_compute_node_template(project: <%= gcp_project_id -%>, region: <%= gcp_location -%>, name: <%= doc_generation ? "'#{node_template['name']}'" : "node_template['name']" -%>) do + it { should exist } + its('node_affinity_labels') { should include(<%= doc_generation ? "'#{node_template['label_key']}'" : "node_template['label_key']" -%> => <%= doc_generation ? "'#{node_template['label_value']}'" : "node_template['label_value']" -%>) } +end + +describe google_compute_node_template(project: <%= gcp_project_id -%>, region: <%= gcp_location -%>, name: 'nonexistent') do + it { should_not exist } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_node_template/google_compute_node_template_attributes.erb b/templates/inspec/examples/google_compute_node_template/google_compute_node_template_attributes.erb new file mode 100644 index 000000000000..7ecffbee4933 --- /dev/null +++ b/templates/inspec/examples/google_compute_node_template/google_compute_node_template_attributes.erb @@ -0,0 +1,3 @@ +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.') +node_template = attribute('node_template', default: <%= JSON.pretty_generate(grab_attributes['node_template']) -%>, description: 'Node template description') \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_node_template/google_compute_node_templates.erb b/templates/inspec/examples/google_compute_node_template/google_compute_node_templates.erb new file mode 100644 index 000000000000..aaa36f94d300 --- /dev/null +++ b/templates/inspec/examples/google_compute_node_template/google_compute_node_templates.erb @@ -0,0 +1,6 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% node_template = grab_attributes['node_template'] -%> +<% gcp_location = "#{external_attribute('gcp_location', doc_generation)}" %> +describe google_compute_node_templates(project: <%= gcp_project_id -%>, region: <%= gcp_location -%>) do + its('names') { should include <%= doc_generation ? "'#{node_template['name']}'" : "node_template['name']" -%> } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_snapshot/google_compute_snapshot.erb b/templates/inspec/examples/google_compute_snapshot/google_compute_snapshot.erb index 2ca68e74fab5..233f7f4e3b4a 100644 --- a/templates/inspec/examples/google_compute_snapshot/google_compute_snapshot.erb +++ b/templates/inspec/examples/google_compute_snapshot/google_compute_snapshot.erb @@ -1,10 +1,9 @@ <% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> <% snapshot = grab_attributes['snapshot'] -%> <% gcp_zone = "#{external_attribute('gcp_zone', doc_generation)}" %> -<% gcp_compute_disk_name = "#{external_attribute('gcp_compute_disk_name', doc_generation)}" -%> describe google_compute_snapshot(project: <%= gcp_project_id -%>, name: <%= doc_generation ? "'#{snapshot['name']}'" : "snapshot['name']" -%>) do it { should exist } - its('source_disk') { should match <%= gcp_compute_disk_name -%> } + its('source_disk') { should match <%= doc_generation ? "'#{snapshot['disk_name']}'" : "snapshot['disk_name']" -%> } end describe google_compute_snapshot(project: <%= gcp_project_id -%>, name: 'nonexistent') do diff --git a/templates/inspec/examples/google_compute_snapshot/google_compute_snapshot_attributes.erb b/templates/inspec/examples/google_compute_snapshot/google_compute_snapshot_attributes.erb index a528cdf49034..03a36fba93c1 100644 --- a/templates/inspec/examples/google_compute_snapshot/google_compute_snapshot_attributes.erb +++ b/templates/inspec/examples/google_compute_snapshot/google_compute_snapshot_attributes.erb @@ -1,4 +1,3 @@ gcp_project_id = attribute(:gcp_project_id, default: '<%= external_attribute('gcp_project_id') -%>', description: 'The GCP project identifier.') gcp_zone = attribute(:gcp_zone, default: '<%= external_attribute('gcp_zone') -%>', description: 'GCP zone name of the compute disk') -gcp_compute_disk_name = attribute(:gcp_compute_disk_name, default: '<%= external_attribute('gcp_compute_disk_name') -%>', description: 'The name of the GCP compute disk to snapshot') snapshot = attribute('snapshot', default: <%= JSON.pretty_generate(grab_attributes['snapshot']) -%>, description: 'Compute disk snapshot description') \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_snapshot/google_compute_snapshots.erb b/templates/inspec/examples/google_compute_snapshot/google_compute_snapshots.erb index 9bd4c06a3bfc..a5e7f9c982c4 100644 --- a/templates/inspec/examples/google_compute_snapshot/google_compute_snapshots.erb +++ b/templates/inspec/examples/google_compute_snapshot/google_compute_snapshots.erb @@ -1,7 +1,6 @@ <% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> <% snapshot = grab_attributes['snapshot'] -%> <% gcp_zone = "#{external_attribute('gcp_zone', doc_generation)}" %> -<% gcp_compute_disk_name = "#{external_attribute('gcp_compute_disk_name', doc_generation)}" -%> describe google_compute_snapshots(project: <%= gcp_project_id -%>) do its('count') { should be >= 1 } end @@ -9,7 +8,7 @@ end describe.one do google_compute_snapshots(project: <%= gcp_project_id -%>).names do |snapshot_name| describe google_compute_snapshot(project: <%= gcp_project_id -%>, name: snapshot_name) do - its('source_disk') { should match <%= gcp_compute_disk_name -%> } + its('source_disk') { should match <%= doc_generation ? "'#{snapshot['disk_name']}'" : "snapshot['disk_name']" -%> } end end 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 90cf45e0f0d6..b25f9bd7185d 100644 --- a/templates/inspec/tests/integration/build/gcp-mm.tf +++ b/templates/inspec/tests/integration/build/gcp-mm.tf @@ -181,6 +181,18 @@ variable "redis" { type = "map" } +variable "network_endpoint_group" { + type = "map" +} + +variable "node_template" { + type = "map" +} + +variable "node_group" { + type = "map" +} + resource "google_compute_ssl_policy" "custom-ssl-policy" { name = "${var.ssl_policy["name"]}" min_tls_version = "${var.ssl_policy["min_tls_version"]}" @@ -485,14 +497,22 @@ resource "google_compute_router" "gcp-inspec-router" { } } +resource "google_compute_disk" "snapshot-disk" { + project = "${var.gcp_project_id}" + name = var.snapshot["disk_name"] + type = "${var.gcp_compute_disk_type}" + zone = "${var.gcp_zone}" + image = "${var.gcp_compute_disk_image}" + labels = { + environment = "generic_compute_disk_label" + } +} + resource "google_compute_snapshot" "gcp-inspec-snapshot" { project = "${var.gcp_project_id}" name = "${var.snapshot["name"]}" - source_disk = "${google_compute_disk.generic_compute_disk.name}" + source_disk = "${google_compute_disk.snapshot-disk.name}" zone = "${var.gcp_zone}" - # Depends on the instance of the disk we are using. Allow instance to spin up - # Before snapshotting the disk to avoid resourceInUse errors - depends_on = ["google_compute_instance.generic_external_vm_instance_data_disk"] } resource "google_compute_ssl_certificate" "gcp-inspec-ssl-certificate" { @@ -756,3 +776,39 @@ resource "google_redis_instance" "inspec-redis" { "${var.redis["label_key"]}" = var.redis["label_value"] } } + +resource "google_compute_network_endpoint_group" "inspec-endpoint-group" { + project = var.gcp_project_id + name = var.network_endpoint_group["name"] + network = google_compute_subnetwork.inspec-gcp-subnetwork.network + subnetwork = google_compute_subnetwork.inspec-gcp-subnetwork.self_link + default_port = var.network_endpoint_group["default_port"] + zone = var.gcp_zone +} + +data "google_compute_node_types" "zone-node-type" { + project = var.gcp_project_id + zone = var.gcp_zone +} + +resource "google_compute_node_template" "inspec-template" { + project = var.gcp_project_id + region = var.gcp_location + + name = var.node_template["name"] + node_type = "${data.google_compute_node_types.zone-node-type.names[0]}" + + node_affinity_labels = { + "${var.node_template["label_key"]}" = "${var.node_template["label_value"]}" + } +} + +resource "google_compute_node_group" "inspec-node-group" { + project = var.gcp_project_id + name = var.node_group["name"] + zone = var.gcp_zone + description = var.node_group["description"] + + size = var.node_group["size"] + node_template = "${google_compute_node_template.inspec-template.self_link}" +} diff --git a/templates/inspec/tests/integration/configuration/mm-attributes.yml b/templates/inspec/tests/integration/configuration/mm-attributes.yml index 1dfff21c5d7d..92b6cfdcba4d 100644 --- a/templates/inspec/tests/integration/configuration/mm-attributes.yml +++ b/templates/inspec/tests/integration/configuration/mm-attributes.yml @@ -148,6 +148,7 @@ router: snapshot: name: inspec-gcp-disk-snapshot + disk_name: inspec-snapshot-disk https_proxy: name: inspec-gcp-https-proxy @@ -301,3 +302,17 @@ redis: reserved_ip_range: "192.168.0.0/29" label_key: key label_value: value + +network_endpoint_group: + name: inspec-gcp-endpoint-group + default_port: 90 + +node_template: + name: inspec-node-template + label_key: key + label_value: value + +node_group: + name: inspec-node-group + description: A description of the node group + size: 0 \ No newline at end of file From 79af9aede8b104e76e38868ca1eedf670c19b583 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Mon, 7 Oct 2019 14:58:09 -0700 Subject: [PATCH 03/87] Update service renames comment (#2431) Merged PR #2431. --- .../terraform/resources/resource_google_project_service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/terraform/resources/resource_google_project_service.go b/third_party/terraform/resources/resource_google_project_service.go index c3d8fdd1808e..7e026db554f8 100644 --- a/third_party/terraform/resources/resource_google_project_service.go +++ b/third_party/terraform/resources/resource_google_project_service.go @@ -29,7 +29,7 @@ var ignoredProjectServicesSet = golangSetFromStringSlice(ignoredProjectServices) // We handle service renames in the provider by pretending that we've read both // the old and new service names from the API if we see either, and only setting // the one(s) that existed in prior state in config (if any). If neither exists, -// we'll set the new service name in state. +// we'll set the old service name in state. // Additionally, in case of service rename rollbacks or unexpected early // removals of services, if we fail to create or delete a service that's been // renamed we'll retry using an alternate name. From 253783c4041d8b9cf82f1632ec31a79712e30173 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Mon, 7 Oct 2019 16:21:53 -0700 Subject: [PATCH 04/87] IAP IAM AppEngine Version (#2386) Merged PR #2386. --- api/resource/iam_policy.rb | 11 +++ build/terraform | 2 +- build/terraform-beta | 2 +- products/iap/api.yaml | 48 +++++++++++++ products/iap/terraform.yaml | 43 ++++++++++++ templates/terraform/env_var_context.go.erb | 19 +++++ .../base_configs/iam_test_file.go.erb | 8 +-- .../examples/base_configs/test_file.go.erb | 20 +----- .../examples/iap_app_engine_version.tf.erb | 51 ++++++++++++++ .../app_engine_service.tf.erb | 3 + .../app_engine_version.tf.erb | 4 ++ ...ttributes.go.erb => iam_attributes.tf.erb} | 0 templates/terraform/iam/iam_context.go.erb | 6 +- templates/terraform/iam_policy.go.erb | 70 +++++-------------- .../terraform/resource_iam.html.markdown.erb | 6 +- .../terraform/website-compiled/google.erb | 18 +++++ 16 files changed, 224 insertions(+), 87 deletions(-) create mode 100644 templates/terraform/env_var_context.go.erb create mode 100644 templates/terraform/examples/iap_app_engine_version.tf.erb create mode 100644 templates/terraform/iam/example_config_body/app_engine_service.tf.erb create mode 100644 templates/terraform/iam/example_config_body/app_engine_version.tf.erb rename templates/terraform/iam/{iam_attributes.go.erb => iam_attributes.tf.erb} (100%) diff --git a/api/resource/iam_policy.rb b/api/resource/iam_policy.rb index fd913023a9f1..1e15e06b1995 100644 --- a/api/resource/iam_policy.rb +++ b/api/resource/iam_policy.rb @@ -54,6 +54,13 @@ class IamPolicy < Api::Object # compareSelfLinkOrResourceName attr_reader :custom_diff_suppress + # Some resources (IAP) use fields named differently from the parent resource. + # We need to use the parent's attributes to create an IAM policy, but they may not be + # named as the IAM IAM resource expects. + # This allows us to specify a file (relative to MM root) containing a partial terraform + # config with the test/example attributes of the IAM resource. + attr_reader :example_config_body + def validate super @@ -64,6 +71,10 @@ def validate check :allowed_iam_role, type: String, default: 'roles/viewer' check :parent_resource_attribute, type: String, default: 'id' check :test_project_name, type: String + check( + :example_config_body, + type: String, default: 'templates/terraform/iam/iam_attributes.tf.erb' + ) end end end diff --git a/build/terraform b/build/terraform index 84bf5d189c9a..96aa72c6cd3a 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 84bf5d189c9a9d6bd7178fec25e7905890844c23 +Subproject commit 96aa72c6cd3ac197fbd796ed3ce5d6ea7e9c5bbb diff --git a/build/terraform-beta b/build/terraform-beta index a0d2752d2251..03a4b662a5c2 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit a0d2752d2251bf89b9a8272f7582bc90e8c24b64 +Subproject commit 03a4b662a5c21caa39733f9852d6f3f8adab1bd5 diff --git a/products/iap/api.yaml b/products/iap/api.yaml index a5f17dac06f8..e4b31306ac44 100644 --- a/products/iap/api.yaml +++ b/products/iap/api.yaml @@ -25,6 +25,8 @@ apis_required: name: Cloud Identity-Aware Proxy url: https://console.cloud.google.com/apis/library/iap.googleapis.com/ objects: + # This resource is only used to generate IAM resources. They do not correspond to real + # GCP resources, and should not be used to generate anything other than IAM suppport. - !ruby/object:Api::Resource name: 'Web' base_url: 'projects/{{project}}/iap_web' @@ -37,6 +39,8 @@ objects: name: 'name' description: Dummy property. required: true + # This resource is only used to generate IAM resources. They do not correspond to real + # GCP resources, and should not be used to generate anything other than IAM suppport. - !ruby/object:Api::Resource name: 'WebTypeCompute' base_url: 'projects/{{project}}/iap_web/compute' @@ -49,6 +53,8 @@ objects: name: 'name' description: Dummy property. required: true + # This resource is only used to generate IAM resources. They do not correspond to real + # GCP resources, and should not be used to generate anything other than IAM suppport. - !ruby/object:Api::Resource name: 'WebTypeAppEngine' base_url: 'projects/{{project}}/iap_web/appengine-{{appId}}' @@ -61,6 +67,48 @@ objects: name: 'appId' description: Id of the App Engine application. required: true + # This resource is only used to generate IAM resources. They do not correspond to real + # GCP resources, and should not be used to generate anything other than IAM suppport. + - !ruby/object:Api::Resource + name: 'AppEngineVersion' + base_url: 'projects/{{project}}/iap_web/appengine-{{appId}}/services/{{service}}/versions/{{versionId}}' + self_link: 'projects/{{project}}/iap_web/appengine-{{appId}}/services/{{service}}/versions/{{versionId}}' + exclude_resource: true + description: | + Only used to generate IAM resources + properties: + - !ruby/object:Api::Type::String + name: 'appId' + description: Id of the App Engine application. + required: true + - !ruby/object:Api::Type::String + name: 'service' + description: Service id of the App Engine application + required: true + - !ruby/object:Api::Type::String + name: 'versionId' + description: Version id of the App Engine application + required: true + # This resource is only used to generate IAM resources. They do not correspond to real + # GCP resources, and should not be used to generate anything other than IAM suppport. + - !ruby/object:Api::Resource + name: 'AppEngineService' + base_url: 'projects/{{project}}/iap_web/appengine-{{appId}}/services/{{service}}' + self_link: 'projects/{{project}}/iap_web/appengine-{{appId}}/services/{{service}}' + exclude_resource: true + description: | + Only used to generate IAM resources + properties: + - !ruby/object:Api::Type::String + name: 'appId' + description: Id of the App Engine application. + required: true + - !ruby/object:Api::Type::String + name: 'service' + description: Service id of the App Engine application + required: true + # This resource is only used to generate IAM resources. They do not correspond to real + # GCP resources, and should not be used to generate anything other than IAM suppport. - !ruby/object:Api::Resource name: 'WebBackendService' base_url: 'projects/{{project}}/iap_web/compute/services/{{name}}' diff --git a/products/iap/terraform.yaml b/products/iap/terraform.yaml index bfcb09064f8a..2a24701658c2 100644 --- a/products/iap/terraform.yaml +++ b/products/iap/terraform.yaml @@ -66,6 +66,49 @@ overrides: !ruby/object:Overrides::ResourceOverrides primary_resource_name: "context[\"project_id\"]" test_env_vars: org_id: :ORG_ID + AppEngineVersion: !ruby/object:Overrides::Terraform::ResourceOverride + iam_policy: !ruby/object:Api::Resource::IamPolicy + exclude: false + method_name_separator: ':' + parent_resource_type: 'google_app_engine_standard_app_version' + parent_resource_attribute: 'version' + fetch_iam_policy_verb: :POST + allowed_iam_role: 'roles/iap.httpsResourceAccessor' + test_project_name: "tf-test" + example_config_body: 'templates/terraform/iam/example_config_body/app_engine_version.tf.erb' + id_format: 'projects/{{project}}/iap_web/appengine-{{appId}}/services/{{service}}/versions/{{versionId}}' + import_format: [ + 'projects/{{project}}/iap_web/appengine-{{appId}}/services/{{service}}/versions/{{versionId}}', + '{{version}}' + ] + examples: + - !ruby/object:Provider::Terraform::Examples + name: "iap_app_engine_version" + primary_resource_id: "version" + primary_resource_name: "context[\"project_id\"], \"default\", \"v2\"" + test_env_vars: + org_id: :ORG_ID + billing_account: :BILLING_ACCT + AppEngineService: !ruby/object:Overrides::Terraform::ResourceOverride + iam_policy: !ruby/object:Api::Resource::IamPolicy + exclude: false + method_name_separator: ':' + parent_resource_type: 'google_app_engine_standard_app_version' + parent_resource_attribute: 'service' + fetch_iam_policy_verb: :POST + allowed_iam_role: 'roles/iap.httpsResourceAccessor' + test_project_name: "tf-test" + example_config_body: 'templates/terraform/iam/example_config_body/app_engine_service.tf.erb' + id_format: 'projects/{{project}}/iap_web/appengine-{{appId}}/services/{{service}}' + import_format: ['projects/{{project}}/iap_web/appengine-{{appId}}/services/{{service}}', '{{service}}'] + examples: + - !ruby/object:Provider::Terraform::Examples + name: "iap_app_engine_version" + primary_resource_id: "version" + primary_resource_name: "context[\"project_id\"], \"default\"" + test_env_vars: + org_id: :ORG_ID + billing_account: :BILLING_ACCT WebBackendService: !ruby/object:Overrides::Terraform::ResourceOverride iam_policy: !ruby/object:Api::Resource::IamPolicy exclude: false diff --git a/templates/terraform/env_var_context.go.erb b/templates/terraform/env_var_context.go.erb new file mode 100644 index 000000000000..35f6ab8610a2 --- /dev/null +++ b/templates/terraform/env_var_context.go.erb @@ -0,0 +1,19 @@ +<% example.test_env_vars&.each do |var_name, var_type| -%> + <% if var_type == :ORG_ID -%> + "<%= var_name -%>": getTestOrgFromEnv(t), + <% elsif var_type == :CREDENTIALS -%> + "<%= var_name -%>": getTestCredsFromEnv(t), + <% elsif var_type == :REGION -%> + "<%= var_name -%>": getTestRegionFromEnv(), + <% elsif var_type == :ORG_TARGET -%> + "<%= var_name -%>": getTestOrgTargetFromEnv(t), + <% elsif var_type == :BILLING_ACCT -%> + "<%= var_name -%>": getTestBillingAccountFromEnv(t), + <% elsif var_type == :SERVICE_ACCT -%> + "<%= var_name -%>": getTestServiceAccountFromEnv(t), + <% elsif var_type == :PROJECT_NAME -%> + "<%= var_name -%>": getTestProjectFromEnv(), + <% elsif var_type == :FIRESTORE_PROJECT_NAME -%> + "<%= var_name -%>": getTestFirestoreProjectFromEnv(t), + <% end -%> +<% end -%> \ No newline at end of file diff --git a/templates/terraform/examples/base_configs/iam_test_file.go.erb b/templates/terraform/examples/base_configs/iam_test_file.go.erb index 1755a4591c61..9953880ca7f9 100644 --- a/templates/terraform/examples/base_configs/iam_test_file.go.erb +++ b/templates/terraform/examples/base_configs/iam_test_file.go.erb @@ -125,7 +125,7 @@ func testAcc<%= resource_name -%>IamMember_basicGenerated(context map[string]int <%= example.config_test_body -%> resource "<%= resource_ns_iam -%>_member" "foo" { -<%= lines(compile('templates/terraform/iam/iam_attributes.go.erb')) -%> +<%= lines(compile(object.iam_policy.example_config_body)) -%> role = "%{role}" member = "user:admin@hashicorptest.com" } @@ -144,7 +144,7 @@ data "google_iam_policy" "foo" { } resource "<%= resource_ns_iam -%>_policy" "foo" { -<%= lines(compile('templates/terraform/iam/iam_attributes.go.erb')) -%> +<%= lines(compile(object.iam_policy.example_config_body)) -%> policy_data = "${data.google_iam_policy.foo.policy_data}" } `, context) @@ -155,7 +155,7 @@ func testAcc<%= resource_name -%>IamBinding_basicGenerated(context map[string]in <%= example.config_test_body -%> resource "<%= resource_ns_iam -%>_binding" "foo" { -<%= lines(compile('templates/terraform/iam/iam_attributes.go.erb')) -%> +<%= lines(compile(object.iam_policy.example_config_body)) -%> role = "%{role}" members = ["user:admin@hashicorptest.com"] } @@ -167,7 +167,7 @@ func testAcc<%= resource_name -%>IamBinding_updateGenerated(context map[string]i <%= example.config_test_body -%> resource "<%= resource_ns_iam -%>_binding" "foo" { -<%= lines(compile('templates/terraform/iam/iam_attributes.go.erb')) -%> +<%= lines(compile(object.iam_policy.example_config_body)) -%> role = "%{role}" members = ["user:admin@hashicorptest.com", "user:paddy@hashicorp.com"] } diff --git a/templates/terraform/examples/base_configs/test_file.go.erb b/templates/terraform/examples/base_configs/test_file.go.erb index b540117ac12c..6e81feeb480d 100644 --- a/templates/terraform/examples/base_configs/test_file.go.erb +++ b/templates/terraform/examples/base_configs/test_file.go.erb @@ -47,25 +47,7 @@ func TestAcc<%= test_slug -%>(t *testing.T) { t.Parallel() context := map[string]interface{} { - <% example.test_env_vars.each do |var_name, var_type| -%> - <% if var_type == :ORG_ID -%> - "<%= var_name -%>": getTestOrgFromEnv(t), - <% elsif var_type == :CREDENTIALS -%> - "<%= var_name -%>": getTestCredsFromEnv(t), - <% elsif var_type == :REGION -%> - "<%= var_name -%>": getTestRegionFromEnv(), - <% elsif var_type == :ORG_TARGET -%> - "<%= var_name -%>": getTestOrgTargetFromEnv(t), - <% elsif var_type == :BILLING_ACCT -%> - "<%= var_name -%>": getTestBillingAccountFromEnv(t), - <% elsif var_type == :SERVICE_ACCT -%> - "<%= var_name -%>": getTestServiceAccountFromEnv(t), - <% elsif var_type == :PROJECT_NAME -%> - "<%= var_name -%>": getTestProjectFromEnv(), - <% elsif var_type == :FIRESTORE_PROJECT_NAME -%> - "<%= var_name -%>": getTestFirestoreProjectFromEnv(t), - <% end -%> - <% end -%> +<%= lines(indent(compile('templates/terraform/env_var_context.go.erb'), 4)) -%> <% unless example.test_custom_context.nil? -%> <% example.test_custom_context.each do |var_name, custom_val| -%> "<%= var_name %>": <%= custom_val %>, diff --git a/templates/terraform/examples/iap_app_engine_version.tf.erb b/templates/terraform/examples/iap_app_engine_version.tf.erb new file mode 100644 index 000000000000..48ce29f3d49d --- /dev/null +++ b/templates/terraform/examples/iap_app_engine_version.tf.erb @@ -0,0 +1,51 @@ +resource "google_project" "my_project" { + name = "%{project_id}" + project_id = "%{project_id}" + org_id = "%{org_id}" + billing_account = "%{billing_account}" +} + +resource "google_project_service" "project_service" { + project = "${google_project.my_project.project_id}" + service = "iap.googleapis.com" +} + +resource "google_project_service" "cloudbuild_service" { + project = "${google_project_service.project_service.project}" + service = "cloudbuild.googleapis.com" +} + +resource "google_app_engine_application" "app" { + project = "${google_project_service.cloudbuild_service.project}" + location_id = "us-central" +} + +resource "google_storage_bucket" "bucket" { + project = "${google_app_engine_application.app.project}" + name = "appengine-static-content-%{random_suffix}" +} + +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" "version" { + project = "${google_app_engine_application.app.project}" + version_id = "v2" + service = "default" + 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" + } + } + env_variables = { + port = "8080" + } +} \ No newline at end of file diff --git a/templates/terraform/iam/example_config_body/app_engine_service.tf.erb b/templates/terraform/iam/example_config_body/app_engine_service.tf.erb new file mode 100644 index 000000000000..cb0b8474d403 --- /dev/null +++ b/templates/terraform/iam/example_config_body/app_engine_service.tf.erb @@ -0,0 +1,3 @@ + project = "${google_app_engine_standard_app_version.version.project}" + app_id = "${google_app_engine_standard_app_version.version.project}" + service = "${google_app_engine_standard_app_version.version.service}" \ No newline at end of file diff --git a/templates/terraform/iam/example_config_body/app_engine_version.tf.erb b/templates/terraform/iam/example_config_body/app_engine_version.tf.erb new file mode 100644 index 000000000000..6457774102e7 --- /dev/null +++ b/templates/terraform/iam/example_config_body/app_engine_version.tf.erb @@ -0,0 +1,4 @@ + project = "${google_app_engine_standard_app_version.version.project}" + app_id = "${google_app_engine_standard_app_version.version.project}" + service = "${google_app_engine_standard_app_version.version.service}" + version_id = "${google_app_engine_standard_app_version.version.version_id}" \ No newline at end of file diff --git a/templates/terraform/iam/iam_attributes.go.erb b/templates/terraform/iam/iam_attributes.tf.erb similarity index 100% rename from templates/terraform/iam/iam_attributes.go.erb rename to templates/terraform/iam/iam_attributes.tf.erb diff --git a/templates/terraform/iam/iam_context.go.erb b/templates/terraform/iam/iam_context.go.erb index 3980906483bb..93f68fdaffb0 100644 --- a/templates/terraform/iam/iam_context.go.erb +++ b/templates/terraform/iam/iam_context.go.erb @@ -4,11 +4,7 @@ context := map[string]interface{}{ <% unless object.iam_policy.test_project_name.nil? -%> "project_id" : fmt.Sprintf("<%= object.iam_policy.test_project_name -%>%s", acctest.RandString(10)), <% end -%> -<% example.test_env_vars&.each do |var_name, var_type| -%> -<% if var_type == :ORG_ID -%> - "<%= var_name -%>": getTestOrgFromEnv(t), -<% end -%> -<% end -%> +<%= lines(compile('templates/terraform/env_var_context.go.erb')) -%> <% unless example.test_custom_context.nil? -%> <% example.test_custom_context.each do |var_name, custom_val| -%> "<%= var_name %>": <%= custom_val %>, diff --git a/templates/terraform/iam_policy.go.erb b/templates/terraform/iam_policy.go.erb index 28f12a8ca3a2..e74a336827c0 100644 --- a/templates/terraform/iam_policy.go.erb +++ b/templates/terraform/iam_policy.go.erb @@ -67,41 +67,25 @@ type <%= resource_name -%>IamUpdater struct { Config *Config } +<% provider_default_values = ['project', 'location', 'region', 'zone'] -%> func <%= resource_name -%>IamUpdaterProducer(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) { values := make(map[string]string) -<% if resource_params.include?('project') -%> - project, err := getProject(d, config) - if err != nil { - return nil, err - } - values["project"] = project - -<% end -%> -<% if resource_params.include?('location') -%> - location, err := getLocation(d, config) - if err != nil { - return nil, err - } - values["location"] = location - -<% end -%> -<% if resource_params.include?('zone') -%> - zone, err := getZone(d, config) +<% resource_params.each do |param| -%> +<% if provider_default_values.include?(param) -%> + <%= param -%>, err := get<%= param.capitalize -%>(d, config) if err != nil { return nil, err } - values["zone"] = zone + values["<%= param -%>"] = <%= param -%> -<% end -%> -<% if resource_params.include?('region') -%> - region, err := getRegion(d, config) - if err != nil { - return nil, err +<% else -%> + if v, ok := d.GetOk("<%= param.underscore -%>"); ok { + values["<%= param -%>"] = v.(string) } - values["region"] = region -<% end -%> +<% end # if provider_default_values.include? -%> +<% end # resource_params.each -%> // We may have gotten either a long or short name, so attempt to parse long name if possible m, err := getImportIdQualifiers([]string{"<%= import_id_formats(object).map{|s| format2regex s}.map{|s| s.gsub('', "<#{object.name.underscore}>")}.join('","') -%>"}, d, config, d.Get("<%= resource_params.last.underscore -%>").(string)) @@ -143,38 +127,16 @@ func <%= resource_name -%>IamUpdaterProducer(d *schema.ResourceData, config *Con func <%= resource_name -%>IdParseFunc(d *schema.ResourceData, config *Config) error { values := make(map[string]string) -<% if resource_params.include?('project') -%> - project, err := getProject(d, config) - if err != nil { - return err - } - values["project"] = project - -<% end -%> -<% if resource_params.include?('location') -%> - location, err := getLocation(d, config) - if err != nil { - return err - } - values["location"] = location - -<% end -%> -<% if resource_params.include?('zone') -%> - zone, err := getZone(d, config) - if err != nil { - return err - } - values["zone"] = zone - -<% end -%> -<% if resource_params.include?('region') -%> - region, err := getRegion(d, config) +<% resource_params.each do |param| -%> +<% if provider_default_values.include?(param) -%> + <%= param -%>, err := get<%= param.capitalize -%>(d, config) if err != nil { return err } - values["region"] = region + values["<%= param -%>"] = <%= param -%> -<% end -%> +<% end # if provider_default_values.include? -%> +<% end # resource_params.each -%> m, err := getImportIdQualifiers([]string{"<%= import_id_formats(object).map{|s| format2regex s}.map{|s| s.gsub('', "<#{object.name.underscore}>")}.join('","') -%>"}, d, config, d.Id()) if err != nil { diff --git a/templates/terraform/resource_iam.html.markdown.erb b/templates/terraform/resource_iam.html.markdown.erb index 30e3c51fd391..aa30fb802bd0 100644 --- a/templates/terraform/resource_iam.html.markdown.erb +++ b/templates/terraform/resource_iam.html.markdown.erb @@ -92,7 +92,7 @@ data "google_iam_policy" "admin" { } resource "<%= resource_ns_iam -%>_policy" "editor" { -<%= lines(compile('templates/terraform/iam/iam_attributes.go.erb')) -%> +<%= lines(compile(object.iam_policy.example_config_body)) -%> policy_data = "${data.google_iam_policy.admin.policy_data}" } ``` @@ -101,7 +101,7 @@ resource "<%= resource_ns_iam -%>_policy" "editor" { ```hcl resource "<%= resource_ns_iam -%>_binding" "editor" { -<%= lines(compile('templates/terraform/iam/iam_attributes.go.erb')) -%> +<%= lines(compile(object.iam_policy.example_config_body)) -%> role = "<%= object.iam_policy.allowed_iam_role -%>" members = [ "user:jane@example.com", @@ -113,7 +113,7 @@ resource "<%= resource_ns_iam -%>_binding" "editor" { ```hcl resource "<%= resource_ns_iam -%>_member" "editor" { -<%= lines(compile('templates/terraform/iam/iam_attributes.go.erb')) -%> +<%= lines(compile(object.iam_policy.example_config_body)) -%> role = "<%= object.iam_policy.allowed_iam_role -%>" member = "user:jane@example.com" } diff --git a/third_party/terraform/website-compiled/google.erb b/third_party/terraform/website-compiled/google.erb index df6627592fa7..adfe84f819ec 100644 --- a/third_party/terraform/website-compiled/google.erb +++ b/third_party/terraform/website-compiled/google.erb @@ -992,6 +992,24 @@ google_iap_tunnel_instance_iam_policy <% end -%> + > + google_iap_app_engine_service_iam_binding + + > + google_iap_app_engine_service_iam_member + + > + google_iap_app_engine_service_iam_policy + + > + google_iap_app_engine_version_iam_binding + + > + google_iap_app_engine_version_iam_member + + > + google_iap_app_engine_version_iam_policy + > google_iap_web_backend_service_iam_binding From 6fde5d4aea4e947c336dffea8b0aed0b442f7637 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Mon, 7 Oct 2019 17:03:22 -0700 Subject: [PATCH 05/87] max_pods_per_node is GA now. (#2429) Merged PR #2429. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resource_container_cluster.go.erb | 24 ++++++------ .../resource_container_node_pool.go.erb | 7 ---- .../resource_container_node_pool_test.go.erb | 39 +++---------------- 5 files changed, 19 insertions(+), 55 deletions(-) diff --git a/build/terraform b/build/terraform index 96aa72c6cd3a..95e30b9ca4f5 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 96aa72c6cd3ac197fbd796ed3ce5d6ea7e9c5bbb +Subproject commit 95e30b9ca4f56e44109f378a46be95a918c2ab57 diff --git a/build/terraform-beta b/build/terraform-beta index 03a4b662a5c2..e2ec8bf6745f 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 03a4b662a5c21caa39733f9852d6f3f8adab1bd5 +Subproject commit e2ec8bf6745f56520f485b8b35a142c044d8f297 diff --git a/third_party/terraform/resources/resource_container_cluster.go.erb b/third_party/terraform/resources/resource_container_cluster.go.erb index c8f45d09a391..6f59edef0cce 100644 --- a/third_party/terraform/resources/resource_container_cluster.go.erb +++ b/third_party/terraform/resources/resource_container_cluster.go.erb @@ -711,6 +711,13 @@ func resourceContainerCluster() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, + "default_max_pods_per_node": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Computed: true, + }, + <% unless version == 'ga' -%> "vertical_pod_autoscaling": { Type: schema.TypeList, @@ -745,13 +752,6 @@ func resourceContainerCluster() *schema.Resource { Type: schema.TypeString, }, - "default_max_pods_per_node": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - Computed: true, - }, - "database_encryption": { Type: schema.TypeList, MaxItems: 1, @@ -961,11 +961,9 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er ResourceLabels: expandStringMap(d, "resource_labels"), } -<% unless version == 'ga' -%> if v, ok := d.GetOk("default_max_pods_per_node"); ok { cluster.DefaultMaxPodsConstraint = expandDefaultMaxPodsConstraint(v) } -<% end -%> // Only allow setting node_version on create if it's set to the equivalent master version, // since `InitialClusterVersion` only accepts valid master-style versions. @@ -1208,9 +1206,6 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro if err := d.Set("cluster_autoscaling", flattenClusterAutoscaling(cluster.Autoscaling)); err != nil { return err } - if cluster.DefaultMaxPodsConstraint != nil { - d.Set("default_max_pods_per_node", cluster.DefaultMaxPodsConstraint.MaxPodsPerNode) - } if err := d.Set("authenticator_groups_config", flattenAuthenticatorGroupsConfig(cluster.AuthenticatorGroupsConfig)); err != nil { return err } @@ -1220,6 +1215,9 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro return err } <% end -%> + if cluster.DefaultMaxPodsConstraint != nil { + d.Set("default_max_pods_per_node", cluster.DefaultMaxPodsConstraint.MaxPodsPerNode) + } if err := d.Set("node_config", flattenNodeConfig(cluster.NodeConfig)); err != nil { return err } @@ -2385,7 +2383,6 @@ func expandPodSecurityPolicyConfig(configured interface{}) *containerBeta.PodSec <% end -%> } -<% unless version == 'ga' -%> func expandDefaultMaxPodsConstraint(v interface{}) *containerBeta.MaxPodsConstraint { if v == nil { return nil @@ -2396,6 +2393,7 @@ func expandDefaultMaxPodsConstraint(v interface{}) *containerBeta.MaxPodsConstra } } +<% unless version == 'ga' -%> func expandResourceUsageExportConfig(configured interface{}) *containerBeta.ResourceUsageExportConfig { l := configured.([]interface{}) if len(l) == 0 || l[0] == nil { diff --git a/third_party/terraform/resources/resource_container_node_pool.go.erb b/third_party/terraform/resources/resource_container_node_pool.go.erb index 733131f42e83..5f5630ca446f 100644 --- a/third_party/terraform/resources/resource_container_node_pool.go.erb +++ b/third_party/terraform/resources/resource_container_node_pool.go.erb @@ -99,9 +99,6 @@ var schemaNodePool = map[string]*schema.Schema{ }, "max_pods_per_node": &schema.Schema{ -<% 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.TypeInt, Optional: true, ForceNew: true, @@ -516,13 +513,11 @@ func expandNodePool(d *schema.ResourceData, prefix string) (*containerBeta.NodeP } } - <% unless version == 'ga' -%> if v, ok := d.GetOk(prefix + "max_pods_per_node"); ok { np.MaxPodsConstraint = &containerBeta.MaxPodsConstraint{ MaxPodsPerNode: int64(v.(int)), } } - <% end -%> if v, ok := d.GetOk(prefix + "management"); ok { managementConfig := v.([]interface{})[0].(map[string]interface{}) @@ -583,11 +578,9 @@ func flattenNodePool(d *schema.ResourceData, config *Config, np *containerBeta.N } } - <% unless version == 'ga' -%> if np.MaxPodsConstraint != nil { nodePool["max_pods_per_node"] = np.MaxPodsConstraint.MaxPodsPerNode } - <% end -%> nodePool["management"] = []map[string]interface{}{ { 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 66b05cf1c3f0..20e000c5d3fe 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 @@ -28,7 +28,6 @@ func TestAccContainerNodePool_basic(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) @@ -53,14 +52,12 @@ func TestAccContainerNodePool_nodeLocations(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) } <% end -%> -<% unless version == 'ga' -%> func TestAccContainerNodePool_maxPodsPerNode(t *testing.T) { t.Parallel() @@ -79,12 +76,10 @@ func TestAccContainerNodePool_maxPodsPerNode(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) } -<% end -%> func TestAccContainerNodePool_namePrefix(t *testing.T) { t.Parallel() @@ -103,7 +98,7 @@ func TestAccContainerNodePool_namePrefix(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix", "max_pods_per_node"}, + ImportStateVerifyIgnore: []string{"name_prefix"}, }, }, }) @@ -126,7 +121,6 @@ func TestAccContainerNodePool_noName(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) @@ -152,7 +146,7 @@ func TestAccContainerNodePool_withNodeConfig(t *testing.T) { ImportStateVerify: true, // autoscaling.# = 0 is equivalent to no autoscaling at all, // but will still cause an import diff - ImportStateVerifyIgnore: []string{"autoscaling.#", "max_pods_per_node"}, + ImportStateVerifyIgnore: []string{"autoscaling.#"}, }, resource.TestStep{ Config: testAccContainerNodePool_withNodeConfigUpdate(cluster, nodePool), @@ -163,7 +157,7 @@ func TestAccContainerNodePool_withNodeConfig(t *testing.T) { ImportStateVerify: true, // autoscaling.# = 0 is equivalent to no autoscaling at all, // but will still cause an import diff - ImportStateVerifyIgnore: []string{"autoscaling.#", "max_pods_per_node"}, + ImportStateVerifyIgnore: []string{"autoscaling.#"}, }, }, }) @@ -190,7 +184,7 @@ func TestAccContainerNodePool_withNodeConfigTaints(t *testing.T) { ImportStateVerify: true, // autoscaling.# = 0 is equivalent to no autoscaling at all, // but will still cause an import diff - ImportStateVerifyIgnore: []string{"autoscaling.#", "max_pods_per_node"}, + ImportStateVerifyIgnore: []string{"autoscaling.#"}, }, // Once taints are in GA, consider merging this test with the _withNodeConfig test. }, @@ -222,7 +216,6 @@ func TestAccContainerNodePool_withWorkloadMetadataConfig(t *testing.T) { ImportStateVerifyIgnore: []string{ "node_config.0.workload_metadata_config.#", "node_config.0.workload_metadata_config.0.node_metadata", - "max_pods_per_node", }, }, }, @@ -295,7 +288,6 @@ func TestAccContainerNodePool_withGPU(t *testing.T) { ResourceName: "google_container_node_pool.np_with_gpu", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) @@ -332,7 +324,6 @@ func TestAccContainerNodePool_withManagement(t *testing.T) { ResourceName: "google_container_node_pool.np_with_management", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, resource.TestStep{ Config: testAccContainerNodePool_withManagement(cluster, nodePool, management), @@ -349,7 +340,6 @@ func TestAccContainerNodePool_withManagement(t *testing.T) { ResourceName: "google_container_node_pool.np_with_management", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) @@ -370,7 +360,6 @@ func TestAccContainerNodePool_withNodeConfigScopeAlias(t *testing.T) { ResourceName: "google_container_node_pool.np_with_node_config_scope_alias", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) @@ -399,7 +388,6 @@ func TestAccContainerNodePool_regionalAutoscaling(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, resource.TestStep{ Config: testAccContainerNodePool_updateAutoscaling(cluster, np), @@ -412,7 +400,6 @@ func TestAccContainerNodePool_regionalAutoscaling(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, resource.TestStep{ Config: testAccContainerNodePool_basic(cluster, np), @@ -427,7 +414,7 @@ func TestAccContainerNodePool_regionalAutoscaling(t *testing.T) { ImportStateVerify: true, // autoscaling.# = 0 is equivalent to no autoscaling at all, // but will still cause an import diff - ImportStateVerifyIgnore: []string{"autoscaling.#", "max_pods_per_node"}, + ImportStateVerifyIgnore: []string{"autoscaling.#"}, }, }, }) @@ -455,7 +442,6 @@ func TestAccContainerNodePool_autoscaling(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, resource.TestStep{ Config: testAccContainerNodePool_updateAutoscaling(cluster, np), @@ -468,7 +454,6 @@ func TestAccContainerNodePool_autoscaling(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, resource.TestStep{ Config: testAccContainerNodePool_basic(cluster, np), @@ -483,7 +468,7 @@ func TestAccContainerNodePool_autoscaling(t *testing.T) { ImportStateVerify: true, // autoscaling.# = 0 is equivalent to no autoscaling at all, // but will still cause an import diff - ImportStateVerifyIgnore: []string{"autoscaling.#", "max_pods_per_node"}, + ImportStateVerifyIgnore: []string{"autoscaling.#"}, }, }, }) @@ -510,7 +495,6 @@ func TestAccContainerNodePool_resize(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, { Config: testAccContainerNodePool_resize(cluster, np), @@ -522,7 +506,6 @@ func TestAccContainerNodePool_resize(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) @@ -546,7 +529,6 @@ func TestAccContainerNodePool_version(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, { Config: testAccContainerNodePool_updateVersion(cluster, np), @@ -555,7 +537,6 @@ func TestAccContainerNodePool_version(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, { Config: testAccContainerNodePool_version(cluster, np), @@ -564,7 +545,6 @@ func TestAccContainerNodePool_version(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) @@ -588,7 +568,6 @@ func TestAccContainerNodePool_regionalClusters(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) @@ -612,7 +591,6 @@ func TestAccContainerNodePool_012_ConfigModeAttr(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, { Config: testAccContainerNodePool_012_ConfigModeAttr2(cluster, np), @@ -621,7 +599,6 @@ func TestAccContainerNodePool_012_ConfigModeAttr(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, }, }) @@ -646,7 +623,6 @@ func TestAccContainerNodePool_EmptyGuestAccelerator(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, { // Test alternative way to specify an empty node pool @@ -656,7 +632,6 @@ func TestAccContainerNodePool_EmptyGuestAccelerator(t *testing.T) { ResourceName: "google_container_node_pool.np", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"max_pods_per_node"}, }, { // Assert that changes in count from 1 result in a diff @@ -782,7 +757,6 @@ resource "google_container_node_pool" "np" { } <% end -%> -<% unless version == 'ga' -%> func testAccContainerNodePool_maxPodsPerNode(cluster, np string) string { return fmt.Sprintf(` resource "google_compute_network" "container_network" { @@ -838,7 +812,6 @@ resource "google_container_node_pool" "np" { initial_node_count = 2 }`, cluster, cluster, np) } -<% end -%> func testAccContainerNodePool_regionalClusters(cluster, np string) string { return fmt.Sprintf(` From afb887ffd651c525fddb6ce201bb9e6963ad3ef7 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Tue, 8 Oct 2019 11:04:13 -0700 Subject: [PATCH 06/87] It is convenient for Pulumi if this conflict is bidirectional. (#2413) Merged PR #2413. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resources/resource_compute_instance_template.go | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build/terraform b/build/terraform index 95e30b9ca4f5..6f9d222ee57f 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 95e30b9ca4f56e44109f378a46be95a918c2ab57 +Subproject commit 6f9d222ee57fca7a0dcefeb2a00f4a702adb70c7 diff --git a/build/terraform-beta b/build/terraform-beta index e2ec8bf6745f..88fec38ce79c 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit e2ec8bf6745f56520f485b8b35a142c044d8f297 +Subproject commit 88fec38ce79c1a9dc9947da39f21364ba962e30f diff --git a/third_party/terraform/resources/resource_compute_instance_template.go b/third_party/terraform/resources/resource_compute_instance_template.go index fe9a4d57fd4d..65e8018c85f6 100644 --- a/third_party/terraform/resources/resource_compute_instance_template.go +++ b/third_party/terraform/resources/resource_compute_instance_template.go @@ -37,10 +37,11 @@ func resourceComputeInstanceTemplate() *schema.Resource { }, "name_prefix": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name"}, ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { // https://cloud.google.com/compute/docs/reference/latest/instanceTemplates#resource // uuid is 26 characters, limit the prefix to 37. From 13ea31abc282d27ac62afb1b41ce1f26885bf143 Mon Sep 17 00:00:00 2001 From: Ty Larrabee Date: Tue, 8 Oct 2019 11:57:08 -0700 Subject: [PATCH 07/87] Add custom flattens for passwords (#2376) Merged PR #2376. --- build/terraform-beta | 2 +- products/securityscanner/terraform.yaml | 2 ++ .../custom_flatten/scan_config_auth_custom_password.go.erb | 3 +++ .../custom_flatten/scan_config_auth_google_password.go.erb | 3 +++ 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 templates/terraform/custom_flatten/scan_config_auth_custom_password.go.erb create mode 100644 templates/terraform/custom_flatten/scan_config_auth_google_password.go.erb diff --git a/build/terraform-beta b/build/terraform-beta index 88fec38ce79c..9829eed7951e 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 88fec38ce79c1a9dc9947da39f21364ba962e30f +Subproject commit 9829eed7951e361f425b80d3f3111c137a2301c8 diff --git a/products/securityscanner/terraform.yaml b/products/securityscanner/terraform.yaml index a40e142ff064..66e7a5436f98 100644 --- a/products/securityscanner/terraform.yaml +++ b/products/securityscanner/terraform.yaml @@ -28,8 +28,10 @@ overrides: !ruby/object:Overrides::ResourceOverrides function: 'validation.IntBetween(5, 20)' authentication.googleAccount.password: !ruby/object:Overrides::Terraform::PropertyOverride sensitive: true + custom_flatten: 'templates/terraform/custom_flatten/scan_config_auth_custom_password.go.erb' authentication.customAccount.password: !ruby/object:Overrides::Terraform::PropertyOverride sensitive: true + custom_flatten: 'templates/terraform/custom_flatten/scan_config_auth_google_password.go.erb' schedule: !ruby/object:Overrides::Terraform::PropertyOverride properties: scheduleTime: !ruby/object:Overrides::Terraform::PropertyOverride diff --git a/templates/terraform/custom_flatten/scan_config_auth_custom_password.go.erb b/templates/terraform/custom_flatten/scan_config_auth_custom_password.go.erb new file mode 100644 index 000000000000..977df61ba287 --- /dev/null +++ b/templates/terraform/custom_flatten/scan_config_auth_custom_password.go.erb @@ -0,0 +1,3 @@ +func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData) interface{} { + return d.Get("authentication.0.custom_account.0.password") +} diff --git a/templates/terraform/custom_flatten/scan_config_auth_google_password.go.erb b/templates/terraform/custom_flatten/scan_config_auth_google_password.go.erb new file mode 100644 index 000000000000..0bcd6004ae7c --- /dev/null +++ b/templates/terraform/custom_flatten/scan_config_auth_google_password.go.erb @@ -0,0 +1,3 @@ +func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData) interface{} { + return d.Get("authentication.0.google_account.0.password") +} From b5b4585a2404f1b4e6cd66ec31f7fc3ca7485b72 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 8 Oct 2019 12:27:33 -0700 Subject: [PATCH 08/87] Clean up iap appengine version test (#2442) Merged PR #2442. --- build/terraform | 2 +- build/terraform-beta | 2 +- products/iap/terraform.yaml | 8 +-- .../examples/iap_app_engine_service.tf.erb | 51 +++++++++++++++++++ .../examples/iap_app_engine_version.tf.erb | 28 +--------- 5 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 templates/terraform/examples/iap_app_engine_service.tf.erb diff --git a/build/terraform b/build/terraform index 6f9d222ee57f..454f1cb51626 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 6f9d222ee57fca7a0dcefeb2a00f4a702adb70c7 +Subproject commit 454f1cb51626505d51f6a78a62b9e3aa411d7c83 diff --git a/build/terraform-beta b/build/terraform-beta index 9829eed7951e..0e1c1f2e3b42 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 9829eed7951e361f425b80d3f3111c137a2301c8 +Subproject commit 0e1c1f2e3b4292fd2c25bc86960aa70dec6ea24d diff --git a/products/iap/terraform.yaml b/products/iap/terraform.yaml index 2a24701658c2..6b2afee54b15 100644 --- a/products/iap/terraform.yaml +++ b/products/iap/terraform.yaml @@ -74,7 +74,6 @@ overrides: !ruby/object:Overrides::ResourceOverrides parent_resource_attribute: 'version' fetch_iam_policy_verb: :POST allowed_iam_role: 'roles/iap.httpsResourceAccessor' - test_project_name: "tf-test" example_config_body: 'templates/terraform/iam/example_config_body/app_engine_version.tf.erb' id_format: 'projects/{{project}}/iap_web/appengine-{{appId}}/services/{{service}}/versions/{{versionId}}' import_format: [ @@ -85,10 +84,7 @@ overrides: !ruby/object:Overrides::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "iap_app_engine_version" primary_resource_id: "version" - primary_resource_name: "context[\"project_id\"], \"default\", \"v2\"" - test_env_vars: - org_id: :ORG_ID - billing_account: :BILLING_ACCT + primary_resource_name: "getTestProjectFromEnv(), \"default\", context[\"random_suffix\"]" AppEngineService: !ruby/object:Overrides::Terraform::ResourceOverride iam_policy: !ruby/object:Api::Resource::IamPolicy exclude: false @@ -103,7 +99,7 @@ overrides: !ruby/object:Overrides::ResourceOverrides import_format: ['projects/{{project}}/iap_web/appengine-{{appId}}/services/{{service}}', '{{service}}'] examples: - !ruby/object:Provider::Terraform::Examples - name: "iap_app_engine_version" + name: "iap_app_engine_service" primary_resource_id: "version" primary_resource_name: "context[\"project_id\"], \"default\"" test_env_vars: diff --git a/templates/terraform/examples/iap_app_engine_service.tf.erb b/templates/terraform/examples/iap_app_engine_service.tf.erb new file mode 100644 index 000000000000..48ce29f3d49d --- /dev/null +++ b/templates/terraform/examples/iap_app_engine_service.tf.erb @@ -0,0 +1,51 @@ +resource "google_project" "my_project" { + name = "%{project_id}" + project_id = "%{project_id}" + org_id = "%{org_id}" + billing_account = "%{billing_account}" +} + +resource "google_project_service" "project_service" { + project = "${google_project.my_project.project_id}" + service = "iap.googleapis.com" +} + +resource "google_project_service" "cloudbuild_service" { + project = "${google_project_service.project_service.project}" + service = "cloudbuild.googleapis.com" +} + +resource "google_app_engine_application" "app" { + project = "${google_project_service.cloudbuild_service.project}" + location_id = "us-central" +} + +resource "google_storage_bucket" "bucket" { + project = "${google_app_engine_application.app.project}" + name = "appengine-static-content-%{random_suffix}" +} + +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" "version" { + project = "${google_app_engine_application.app.project}" + version_id = "v2" + service = "default" + 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" + } + } + env_variables = { + port = "8080" + } +} \ No newline at end of file diff --git a/templates/terraform/examples/iap_app_engine_version.tf.erb b/templates/terraform/examples/iap_app_engine_version.tf.erb index 48ce29f3d49d..70f741cfa5ea 100644 --- a/templates/terraform/examples/iap_app_engine_version.tf.erb +++ b/templates/terraform/examples/iap_app_engine_version.tf.erb @@ -1,27 +1,4 @@ -resource "google_project" "my_project" { - name = "%{project_id}" - project_id = "%{project_id}" - org_id = "%{org_id}" - billing_account = "%{billing_account}" -} - -resource "google_project_service" "project_service" { - project = "${google_project.my_project.project_id}" - service = "iap.googleapis.com" -} - -resource "google_project_service" "cloudbuild_service" { - project = "${google_project_service.project_service.project}" - service = "cloudbuild.googleapis.com" -} - -resource "google_app_engine_application" "app" { - project = "${google_project_service.cloudbuild_service.project}" - location_id = "us-central" -} - resource "google_storage_bucket" "bucket" { - project = "${google_app_engine_application.app.project}" name = "appengine-static-content-%{random_suffix}" } @@ -32,11 +9,10 @@ resource "google_storage_bucket_object" "object" { } resource "google_app_engine_standard_app_version" "version" { - project = "${google_app_engine_application.app.project}" - version_id = "v2" + version_id = "%{random_suffix}" service = "default" runtime = "nodejs10" - noop_on_destroy = true + noop_on_destroy = false entrypoint { shell = "node ./app.js" } From 544c070256ad5815b1f9d3a92416a093a3502018 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Tue, 8 Oct 2019 15:31:05 -0700 Subject: [PATCH 09/87] Add enhancement to changelog template and docs (#2444) Merged PR #2444. --- .ci/RELEASE_NOTES_GUIDE.md | 1 + .github/PULL_REQUEST_TEMPLATE.md | 3 ++- build/terraform | 2 +- build/terraform-beta | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.ci/RELEASE_NOTES_GUIDE.md b/.ci/RELEASE_NOTES_GUIDE.md index 3d1dc141955d..437808ddd73d 100644 --- a/.ci/RELEASE_NOTES_GUIDE.md +++ b/.ci/RELEASE_NOTES_GUIDE.md @@ -127,6 +127,7 @@ Don’t write notes like: ## Headings Release notes should be formatted with one of the following headings: +- `release-note:enhancement` - `release-note:bug` - `release-note:note` - `release-note:new-resource` diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 326a1ff712f6..62d16b939089 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,6 +13,7 @@ this PR, but make sure not to indent notes and to leave newlines between code blocks for Markdown's sake. For Terraform PRs, we use the following "release-note:" headings + - release-note:enhancement - release-note:bug - release-note:note - release-note:new-resource @@ -25,4 +26,4 @@ For Terraform PRs, we use the following "release-note:" headings ```release-note:REPLACEME -``` \ No newline at end of file +``` diff --git a/build/terraform b/build/terraform index 454f1cb51626..c812df191bf2 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 454f1cb51626505d51f6a78a62b9e3aa411d7c83 +Subproject commit c812df191bf2e97eafd01a16a59c3e7d8279e862 diff --git a/build/terraform-beta b/build/terraform-beta index 0e1c1f2e3b42..ec275b88c15d 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 0e1c1f2e3b4292fd2c25bc86960aa70dec6ea24d +Subproject commit ec275b88c15d76ce12b490aee4a0e356e9dfd0b4 From 445b7b6dce59fd334e7edc5f91552b4cc26f3a8c Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 9 Oct 2019 09:27:35 -0700 Subject: [PATCH 10/87] Upstream service account docfix (#2445) Merged PR #2445. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../website/docs/r/google_service_account_key.html.markdown | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/terraform b/build/terraform index c812df191bf2..897e609b7ee0 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit c812df191bf2e97eafd01a16a59c3e7d8279e862 +Subproject commit 897e609b7ee06a4d58ab2b51a41db1746f5dff14 diff --git a/build/terraform-beta b/build/terraform-beta index ec275b88c15d..394d7aa55322 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit ec275b88c15d76ce12b490aee4a0e356e9dfd0b4 +Subproject commit 394d7aa55322a61c9b4749a06314706f6861c62e diff --git a/third_party/terraform/website/docs/r/google_service_account_key.html.markdown b/third_party/terraform/website/docs/r/google_service_account_key.html.markdown index 5d3b00036af5..63d2485387ff 100644 --- a/third_party/terraform/website/docs/r/google_service_account_key.html.markdown +++ b/third_party/terraform/website/docs/r/google_service_account_key.html.markdown @@ -38,10 +38,10 @@ resource "google_service_account_key" "mykey" { } resource "kubernetes_secret" "google-application-credentials" { - metadata = { + metadata { name = "google-application-credentials" } - data { + data = { credentials.json = "${base64decode(google_service_account_key.mykey.private_key)}" } } From f10392b5015c78724bdecd8d7364987c73714406 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 9 Oct 2019 14:01:04 -0700 Subject: [PATCH 11/87] Inspec dataproc firewalls IT fix (#2448) Merged PR #2448. --- build/inspec | 2 +- .../inspec/tests/integration/build/gcp-mm.tf | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/build/inspec b/build/inspec index 307ff3b726e4..b6d584fcfec8 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 307ff3b726e4d2fb6372f325f6045b7d7a22f5ea +Subproject commit b6d584fcfec8dd523ed4e67fe3eeb4b7fabb9662 diff --git a/templates/inspec/tests/integration/build/gcp-mm.tf b/templates/inspec/tests/integration/build/gcp-mm.tf index b25f9bd7185d..73c6e6791755 100644 --- a/templates/inspec/tests/integration/build/gcp-mm.tf +++ b/templates/inspec/tests/integration/build/gcp-mm.tf @@ -668,6 +668,29 @@ resource "google_ml_engine_model" "inspec-gcp-model" { online_prediction_console_logging = var.ml_model["online_prediction_console_logging"] } +resource "google_compute_firewall" "dataproc" { + name = "dataproc-firewall" + network = "${google_compute_network.dataproc.name}" + + source_ranges = ["10.128.0.0/9"] + allow { + protocol = "icmp" + } + + allow { + protocol = "tcp" + ports = ["0-65535"] + } + allow { + protocol = "udp" + ports = ["0-65535"] + } +} + +resource "google_compute_network" "dataproc" { + name = "dataproc-network" +} + resource "google_dataproc_cluster" "mycluster" { project = var.gcp_project_id region = var.gcp_location @@ -704,6 +727,7 @@ resource "google_dataproc_cluster" "mycluster" { } gce_cluster_config { + network = google_compute_network.dataproc.self_link tags = [var.dataproc_cluster["config"]["gce_cluster_config"]["tag"]] } } From be81b7da34dc6dc8b33d09f07ac382045d84f0a5 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 10 Oct 2019 08:55:31 -0700 Subject: [PATCH 12/87] Bump image timeout, add service to binauth basic (#2455) Merged PR #2455. --- build/inspec | 2 +- build/terraform | 2 +- build/terraform-beta | 2 +- products/compute/api.yaml | 4 ++++ .../resource_binaryauthorization_policy_test.go.erb | 12 ++++++++++++ 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/build/inspec b/build/inspec index b6d584fcfec8..735f38f2505b 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit b6d584fcfec8dd523ed4e67fe3eeb4b7fabb9662 +Subproject commit 735f38f2505b6fdbeffb5ba0629051a8ac74992b diff --git a/build/terraform b/build/terraform index 897e609b7ee0..fd24aeda1cb7 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 897e609b7ee06a4d58ab2b51a41db1746f5dff14 +Subproject commit fd24aeda1cb71fe9dc79ae17ecdcdaef81599329 diff --git a/build/terraform-beta b/build/terraform-beta index 394d7aa55322..7644c371aad7 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 394d7aa55322a61c9b4749a06314706f6861c62e +Subproject commit 7644c371aad7fac13466486e58999524262be3e8 diff --git a/products/compute/api.yaml b/products/compute/api.yaml index 1012eed1f249..c2b5ff879c46 100644 --- a/products/compute/api.yaml +++ b/products/compute/api.yaml @@ -4021,6 +4021,10 @@ objects: path: 'name' base_url: 'projects/{{project}}/global/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 diff --git a/third_party/terraform/tests/resource_binaryauthorization_policy_test.go.erb b/third_party/terraform/tests/resource_binaryauthorization_policy_test.go.erb index 755b4a068a3f..2dbc5a27d5b8 100644 --- a/third_party/terraform/tests/resource_binaryauthorization_policy_test.go.erb +++ b/third_party/terraform/tests/resource_binaryauthorization_policy_test.go.erb @@ -190,6 +190,11 @@ resource "google_project" "project" { org_id = "%s" billing_account = "%s" } + +resource "google_project_service" "binauthz" { + project = "${google_project.project.project_id}" + service = "binaryauthorization.googleapis.com" +} `, pid, pname, org, billing) } @@ -203,6 +208,11 @@ resource "google_project" "project" { billing_account = "%s" } +resource "google_project_service" "binauthz" { + project = "${google_project.project.project_id}" + service = "binaryauthorization.googleapis.com" +} + resource "google_binary_authorization_policy" "policy" { project = "${google_project.project.project_id}" @@ -214,6 +224,8 @@ resource "google_binary_authorization_policy" "policy" { evaluation_mode = "ALWAYS_DENY" enforcement_mode = "ENFORCED_BLOCK_AND_AUDIT_LOG" } + + depends_on = ["google_project_service.binauthz"] } `, pid, pname, org, billing) } From 349996e79e460fbb3199091b04786e33f066d7f3 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 10 Oct 2019 10:12:31 -0700 Subject: [PATCH 13/87] Clean up markdown for signed_url a bit (#2457) Merged PR #2457. --- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- third_party/terraform/website/docs/d/signed_url.html.markdown | 4 +--- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/build/terraform b/build/terraform index fd24aeda1cb7..0263d6f7f8ec 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit fd24aeda1cb71fe9dc79ae17ecdcdaef81599329 +Subproject commit 0263d6f7f8ecc05d7300afacdc8f04b8a64f588c diff --git a/build/terraform-beta b/build/terraform-beta index 7644c371aad7..f57c0c7371c2 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 7644c371aad7fac13466486e58999524262be3e8 +Subproject commit f57c0c7371c200611c2a50626e05db893afdf0a4 diff --git a/build/terraform-mapper b/build/terraform-mapper index f2e4a08f0339..11538f8e3a8e 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit f2e4a08f03396880afa709d19d08ba07103f0a45 +Subproject commit 11538f8e3a8eaf97ed33c741dd13ce3e6a4399a1 diff --git a/third_party/terraform/website/docs/d/signed_url.html.markdown b/third_party/terraform/website/docs/d/signed_url.html.markdown index 5c2eb10e634c..e5a43d37b2c0 100644 --- a/third_party/terraform/website/docs/d/signed_url.html.markdown +++ b/third_party/terraform/website/docs/d/signed_url.html.markdown @@ -18,12 +18,10 @@ For more info about signed URL's is available [here](https://cloud.google.com/st data "google_storage_object_signed_url" "artifact" { bucket = "install_binaries" path = "path/to/install_file.bin" - } resource "google_compute_instance" "vm" { name = "vm" - ... provisioner "remote-exec" { inline = [ @@ -64,7 +62,7 @@ The following arguments are supported: * `credentials` - (Optional) What Google service account credentials json should be used to sign the URL. This data source checks the following locations for credentials, in order of preference: data source `credentials` attribute, provider `credentials` attribute and finally the GOOGLE_APPLICATION_CREDENTIALS environment variable. -> **NOTE** the default google credentials configured by `gcloud` sdk or the service account associated with a compute instance cannot be used, because these do not include the private key required to sign the URL. A valid `json` service account credentials key file must be used, as generated via Google cloud console. + > **NOTE** the default google credentials configured by `gcloud` sdk or the service account associated with a compute instance cannot be used, because these do not include the private key required to sign the URL. A valid `json` service account credentials key file must be used, as generated via Google cloud console. * `content_type` - (Optional) If you specify this in the datasource, the client must provide the `Content-Type` HTTP header with the same value in its request. * `content_md5` - (Optional) The [MD5 digest](https://cloud.google.com/storage/docs/hashes-etags#_MD5) value in Base64. From 79a9ccee544dcb2cc0767484b500b7ad6238e80f Mon Sep 17 00:00:00 2001 From: Ali Khan Date: Thu, 10 Oct 2019 22:19:47 +0500 Subject: [PATCH 14/87] Support for deleting the service upon destrory (#2331) Merged PR #2331. --- products/appengine/api.yaml | 6 ++ products/appengine/terraform.yaml | 8 ++- .../custom_delete/appversion_delete.go.erb | 72 +++++++++++++++++++ .../noop_on_destroy_appengine_version.go.erb | 49 ------------- .../app_engine_standard_app_version.tf.erb | 37 ++++++++++ 5 files changed, 122 insertions(+), 50 deletions(-) create mode 100644 templates/terraform/custom_delete/appversion_delete.go.erb delete mode 100644 templates/terraform/custom_delete/noop_on_destroy_appengine_version.go.erb diff --git a/products/appengine/api.yaml b/products/appengine/api.yaml index 7a657ca41867..347e7819b4fb 100644 --- a/products/appengine/api.yaml +++ b/products/appengine/api.yaml @@ -440,3 +440,9 @@ objects: name: 'shell' description: | The format should be a shell command that can be fed to bash -c. + - !ruby/object:Api::Type::String + name: 'instanceClass' + description: | + Instance class that is used to run this version. Valid values are + AutomaticScaling F1, F2, F4, F4_1G + (Only AutomaticScaling is supported at the moment) diff --git a/products/appengine/terraform.yaml b/products/appengine/terraform.yaml index a317399d104a..506900f045d9 100644 --- a/products/appengine/terraform.yaml +++ b/products/appengine/terraform.yaml @@ -37,8 +37,12 @@ overrides: !ruby/object:Overrides::ResourceOverrides name: 'noop_on_destroy' description: | If set to `true`, the application version will not be deleted. + - !ruby/object:Provider::Terraform::VirtualFields + name: 'delete_service_on_destroy' + description: | + If set to `true`, the service will be deleted if it is the last version. custom_code: !ruby/object:Provider::Terraform::CustomCode - custom_delete: templates/terraform/custom_delete/noop_on_destroy_appengine_version.go.erb + custom_delete: templates/terraform/custom_delete/appversion_delete.go.erb test_check_destroy: templates/terraform/custom_check_destroy/appengine_version.go.erb properties: id: !ruby/object:Overrides::Terraform::PropertyOverride @@ -51,6 +55,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides ignore_read: true threadsafe: !ruby/object:Overrides::Terraform::PropertyOverride ignore_read: true + instanceClass: !ruby/object:Overrides::Terraform::PropertyOverride + ignore_read: true examples: - !ruby/object:Provider::Terraform::Examples name: "app_engine_standard_app_version" diff --git a/templates/terraform/custom_delete/appversion_delete.go.erb b/templates/terraform/custom_delete/appversion_delete.go.erb new file mode 100644 index 000000000000..e591e6251b95 --- /dev/null +++ b/templates/terraform/custom_delete/appversion_delete.go.erb @@ -0,0 +1,72 @@ + +if d.Get("noop_on_destroy") == true { + log.Printf("[DEBUG] Keeping the StandardAppVersion %q", d.Id()) + return nil +} +config := meta.(*Config) + +project, err := getProject(d, config) +if err != nil { + return err +} + +lockName, err := replaceVars(d, config, "apps/{{project}}/services/{{service}}") +if err != nil { + return err +} +mutexKV.Lock(lockName) +defer mutexKV.Unlock(lockName) + +if d.Get("delete_service_on_destroy") == true { + url, err := replaceVars(d, config, "{{AppEngineBasePath}}apps/{{project}}/services/{{service}}") + if err != nil { + return err + } + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting Service %q", d.Id()) + res, err := sendRequestWithTimeout(config, "DELETE", project, url, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "Service") + } + op := &appengine.Operation{} + err = Convert(res, op) + if err != nil { + return err + } + err = appEngineOperationWaitTime( + config.clientAppEngine, op, project, "Deleting Service", + int(d.Timeout(schema.TimeoutDelete).Minutes())) + + if err != nil { + return err + } + log.Printf("[DEBUG] Finished deleting Service %q: %#v", d.Id(), res) + return nil +} else { + url, err := replaceVars(d, config, "{{AppEngineBasePath}}apps/{{project}}/services/{{service}}/versions/{{version_id}}") + if err != nil { + return err + } + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting StandardAppVersion %q", d.Id()) + res, err := sendRequestWithTimeout(config, "DELETE", project, url, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "StandardAppVersion") + } + op := &appengine.Operation{} + err = Convert(res, op) + if err != nil { + return err + } + err = appEngineOperationWaitTime( + config.clientAppEngine, op, project, "Deleting StandardAppVersion", + int(d.Timeout(schema.TimeoutDelete).Minutes())) + + if err != nil { + return err + } + log.Printf("[DEBUG] Finished deleting StandardAppVersion %q: %#v", d.Id(), res) + return nil + + +} \ No newline at end of file diff --git a/templates/terraform/custom_delete/noop_on_destroy_appengine_version.go.erb b/templates/terraform/custom_delete/noop_on_destroy_appengine_version.go.erb deleted file mode 100644 index 142d187a69fb..000000000000 --- a/templates/terraform/custom_delete/noop_on_destroy_appengine_version.go.erb +++ /dev/null @@ -1,49 +0,0 @@ -if d.Get("noop_on_destroy") == true { - log.Printf("[DEBUG] Keeping the StandardAppVersion %q", d.Id()) - return nil -} -config := meta.(*Config) - -project, err := getProject(d, config) -if err != nil { - return err -} - -lockName, err := replaceVars(d, config, "apps/{{project}}/services/{{service}}") -if err != nil { - return err -} -mutexKV.Lock(lockName) -defer mutexKV.Unlock(lockName) - -url, err := replaceVars(d, config, "{{AppEngineBasePath}}apps/{{project}}/services/{{service}}/versions/{{version_id}}") -if err != nil { - return err -} - -var obj map[string]interface{} -log.Printf("[DEBUG] Deleting StandardAppVersion %q", d.Id()) - -res, err := sendRequestWithTimeout(config, "DELETE", project, url, obj, d.Timeout(schema.TimeoutDelete)) -if err != nil { - return handleNotFoundError(err, d, "StandardAppVersion") -} - -op := &appengine.Operation{} -err = Convert(res, op) -if err != nil { - return err -} - -err = appEngineOperationWaitTime( - config.clientAppEngine, op, project, "Deleting StandardAppVersion", - int(d.Timeout(schema.TimeoutDelete).Minutes())) - -if err != nil { - return err -} - -log.Printf("[DEBUG] Finished deleting StandardAppVersion %q: %#v", d.Id(), res) -return nil - - 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 c8386a18da23..c888d360cd13 100644 --- a/templates/terraform/examples/app_engine_standard_app_version.tf.erb +++ b/templates/terraform/examples/app_engine_standard_app_version.tf.erb @@ -26,3 +26,40 @@ resource "google_app_engine_standard_app_version" "<%= ctx[:primary_resource_id] } } + +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" + 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_app_engine_standard_app_version.myapp_v1"] + +} From c239a5f48929ad64491286921705ce45ed8eeb49 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 10 Oct 2019 10:58:27 -0700 Subject: [PATCH 15/87] Fix syntax highlighting issue from Example Usage (#2456) Merged PR #2456. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../website/docs/r/cloudfunctions_function.html.markdown | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index 0263d6f7f8ec..acc79c19b0ba 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 0263d6f7f8ecc05d7300afacdc8f04b8a64f588c +Subproject commit acc79c19b0bacbb90a1dbd7ddeb3293ba8c81a0f diff --git a/build/terraform-beta b/build/terraform-beta index f57c0c7371c2..0fa767c4d345 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit f57c0c7371c200611c2a50626e05db893afdf0a4 +Subproject commit 0fa767c4d3455a89426ced2d3cbd1683e58e8c05 diff --git a/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown b/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown index a65f806a3215..ccd9cdb9e323 100644 --- a/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown +++ b/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown @@ -22,6 +22,7 @@ for Cloud Functions. ## Example Usage Secured function with a user allowed to invoke: + ```hcl resource "google_storage_bucket" "bucket" { name = "test-bucket" From d005b16ed78ea8857f2fc667d9115f6fc17c2d31 Mon Sep 17 00:00:00 2001 From: Jan Martens <44572196+JanMa@users.noreply.github.com> Date: Thu, 10 Oct 2019 23:10:50 +0200 Subject: [PATCH 16/87] Add datasource for google_compute_resource_policy (#2074) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add datasource for google_compute_resource_policy * Update third_party/terraform/data_sources/data_source_google_compute_resource_policy.go Co-Authored-By: Ty Larrabee * Update third_party/terraform/tests/data_source_google_compute_resource_policy_test.go Co-Authored-By: Ty Larrabee * Update third_party/terraform/tests/data_source_google_compute_resource_policy_test.go Co-Authored-By: Ty Larrabee * Do not generate code in GA provider Move the ´.go` files to `.go.erb` templates which will only be rendered when `unless version == 'ga'` constraint matches. This way we avoid failing tests in the GA provider because of unreferenced code. * Update tracked submodules -> HEAD on Thu Oct 10 20:37:20 UTC 2019 Tracked submodules are build/terraform-beta build/terraform-mapper build/terraform build/ansible build/inspec. --- build/terraform | 2 +- build/terraform-beta | 2 +- ...urce_google_compute_resource_policy.go.erb | 65 ++++++++++ ...google_compute_resource_policy_test.go.erb | 116 ++++++++++++++++++ third_party/terraform/utils/provider.go.erb | 3 + .../terraform/website-compiled/google.erb | 5 + ...ogle_compute_resource_policy.html.markdown | 42 +++++++ 7 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 third_party/terraform/data_sources/data_source_google_compute_resource_policy.go.erb create mode 100644 third_party/terraform/tests/data_source_google_compute_resource_policy_test.go.erb create mode 100644 third_party/terraform/website/docs/d/google_compute_resource_policy.html.markdown diff --git a/build/terraform b/build/terraform index acc79c19b0ba..9ed6246593b9 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit acc79c19b0bacbb90a1dbd7ddeb3293ba8c81a0f +Subproject commit 9ed6246593b9f1151f46ad00c23951cea31c40f0 diff --git a/build/terraform-beta b/build/terraform-beta index 0fa767c4d345..1b2c16107457 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 0fa767c4d3455a89426ced2d3cbd1683e58e8c05 +Subproject commit 1b2c161074579930ff46a591eab325d0f6ed7e64 diff --git a/third_party/terraform/data_sources/data_source_google_compute_resource_policy.go.erb b/third_party/terraform/data_sources/data_source_google_compute_resource_policy.go.erb new file mode 100644 index 000000000000..40dce7229e73 --- /dev/null +++ b/third_party/terraform/data_sources/data_source_google_compute_resource_policy.go.erb @@ -0,0 +1,65 @@ +<% autogen_exception -%> +package google + +<% unless version == 'ga' -%> +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func dataSourceGoogleComputeResourcePolicy() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGoogleComputeResourcePolicyRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "project": { + Type: schema.TypeString, + Optional: true, + }, + + "region": { + Type: schema.TypeString, + Required: true, + }, + + "description": { + Type: schema.TypeString, + Computed: true, + }, + + "self_link": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceGoogleComputeResourcePolicyRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + region, err := getRegion(d, config) + if err != nil { + return err + } + name := d.Get("name").(string) + resourcePolicy, err := config.clientCompute.ResourcePolicies.Get(project, region, name).Do() + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("ResourcePolicy Not Found : %s", name)) + } + d.Set("self_link", resourcePolicy.SelfLink) + d.Set("description", resourcePolicy.Description) + d.SetId(resourcePolicy.Name) + return nil +} +<% end -%> diff --git a/third_party/terraform/tests/data_source_google_compute_resource_policy_test.go.erb b/third_party/terraform/tests/data_source_google_compute_resource_policy_test.go.erb new file mode 100644 index 000000000000..464e787768d7 --- /dev/null +++ b/third_party/terraform/tests/data_source_google_compute_resource_policy_test.go.erb @@ -0,0 +1,116 @@ +<% autogen_exception -%> +package google + +<% unless version == 'ga' -%> +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccDataSourceComputeResourcePolicy(t *testing.T) { + t.Parallel() + + randomSuffix := acctest.RandString(10) + + rsName := "foo_" + randomSuffix + rsFullName := fmt.Sprintf("google_compute_resource_policy.%s", rsName) + dsName := "my_policy_" + randomSuffix + dsFullName := fmt.Sprintf("data.google_compute_resource_policy.%s", dsName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDataSourceComputeResourcePolicyDestroy(rsFullName), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceComputeResourcePolicyConfig(rsName, dsName, randomSuffix), + Check: resource.ComposeTestCheckFunc( + testAccDataSourceComputeResourcePolicyCheck(dsFullName, rsFullName), + ), + }, + }, + }) +} + +func testAccDataSourceComputeResourcePolicyCheck(dataSourceName string, resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[dataSourceName] + if !ok { + return fmt.Errorf("root module has no resource called %s", dataSourceName) + } + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("can't find %s in state", resourceName) + } + + dsAttr := ds.Primary.Attributes + rsAttr := rs.Primary.Attributes + + policyAttrsToTest := []string{ + "self_link", + "name", + } + + for _, attrToCheck := range policyAttrsToTest { + if dsAttr[attrToCheck] != rsAttr[attrToCheck] { + return fmt.Errorf( + "%s is %s; want %s", + attrToCheck, + dsAttr[attrToCheck], + rsAttr[attrToCheck], + ) + } + } + + return nil + } +} + +func testAccCheckDataSourceComputeResourcePolicyDestroy(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("can't find %s in state", resourceName) + } + + policyAttrs := rs.Primary.Attributes + + _, err := config.clientCompute.ResourcePolicies.Get( + config.Project, policyAttrs["region"], policyAttrs["name"]).Do() + if err == nil { + return fmt.Errorf("Resource Policy still exists") + } + + return nil + } +} + +func testAccDataSourceComputeResourcePolicyConfig(rsName, dsName, randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_resource_policy" "%s" { + name = "policy-%s" + region = "us-central1" + snapshot_schedule_policy { + schedule { + daily_schedule { + days_in_cycle = 1 + start_time = "04:00" + } + } + } +} + +data "google_compute_resource_policy" "%s" { + name = "${google_compute_resource_policy.%s.name}" + region = "${google_compute_resource_policy.%s.region}" +} +`, rsName, randomSuffix, dsName, rsName, rsName) +} +<% end -%> diff --git a/third_party/terraform/utils/provider.go.erb b/third_party/terraform/utils/provider.go.erb index b622db021a40..1064e2048b1b 100644 --- a/third_party/terraform/utils/provider.go.erb +++ b/third_party/terraform/utils/provider.go.erb @@ -162,6 +162,9 @@ func Provider() terraform.ResourceProvider { "google_compute_node_types": dataSourceGoogleComputeNodeTypes(), "google_compute_regions": dataSourceGoogleComputeRegions(), "google_compute_region_instance_group": dataSourceGoogleComputeRegionInstanceGroup(), + <% unless version == 'ga' -%> + "google_compute_resource_policy": dataSourceGoogleComputeResourcePolicy(), + <% end -%> "google_compute_ssl_certificate": dataSourceGoogleComputeSslCertificate(), "google_compute_ssl_policy": dataSourceGoogleComputeSslPolicy(), "google_compute_subnetwork": dataSourceGoogleComputeSubnetwork(), diff --git a/third_party/terraform/website-compiled/google.erb b/third_party/terraform/website-compiled/google.erb index adfe84f819ec..2376b2654a8b 100644 --- a/third_party/terraform/website-compiled/google.erb +++ b/third_party/terraform/website-compiled/google.erb @@ -98,6 +98,11 @@ > google_compute_region_instance_group +<% unless version == 'ga' -%> + > + google_compute_resource_policy + +<% end -%> > google_compute_ssl_certificate diff --git a/third_party/terraform/website/docs/d/google_compute_resource_policy.html.markdown b/third_party/terraform/website/docs/d/google_compute_resource_policy.html.markdown new file mode 100644 index 000000000000..d7293952a134 --- /dev/null +++ b/third_party/terraform/website/docs/d/google_compute_resource_policy.html.markdown @@ -0,0 +1,42 @@ +--- +layout: "google" +page_title: "Google: google_compute_resource_policy" +sidebar_current: "docs-google-datasource-compute-resource-policy" +description: |- + Provide access to a Resource Policy's attributes +--- + +# google\_compute\_resource\_policy + +Provide access to a Resource Policy's attributes. For more information see [the official documentation](https://cloud.google.com/compute/docs/disks/scheduled-snapshots) or the [API](https://cloud.google.com/compute/docs/reference/rest/beta/resourcePolicies). + +~> **Warning:** This datasource is in beta, and should be used with the terraform-provider-google-beta provider. +See [Provider Versions](https://terraform.io/docs/providers/google/provider_versions.html) for more details on beta resources. + +```hcl +provider "google-beta" { + region = "us-central1" + zone = "us-central1-a" +} + +data "google_compute_resource_policy" "daily" { + provider = "google-beta" + name = "daily" + region = "us-central1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` (Required) - The name of the Resource Policy. +* `project` (Optional) - Project from which to list the Resource Policy. Defaults to project declared in the provider. +* `region` (Required) - Region where the Resource Policy resides. + +## Attributes Reference + +The following attributes are exported: + +* `description` - Description of this Resource Policy. +* `self_link` - The URI of the resource. From 6cd5b8b51b3cc1f661c148829a8563900dcccee3 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 10 Oct 2019 14:46:24 -0700 Subject: [PATCH 17/87] Correct Cloud Run DomainMapping namespace in encoder (#2458) Merged PR #2458. --- build/terraform | 2 +- build/terraform-beta | 2 +- products/cloudrun/terraform.yaml | 4 ++-- .../encoders/cloud_run_domain_mapping.go.erb | 22 +++++++++++++++++++ ...ud_run.go.erb => cloud_run_service.go.erb} | 0 5 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 templates/terraform/encoders/cloud_run_domain_mapping.go.erb rename templates/terraform/encoders/{cloud_run.go.erb => cloud_run_service.go.erb} (100%) diff --git a/build/terraform b/build/terraform index 9ed6246593b9..b15b6a6e193b 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 9ed6246593b9f1151f46ad00c23951cea31c40f0 +Subproject commit b15b6a6e193b931c16b6741900f3de2b93a433d7 diff --git a/build/terraform-beta b/build/terraform-beta index 1b2c16107457..a71ed23efd91 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 1b2c161074579930ff46a591eab325d0f6ed7e64 +Subproject commit a71ed23efd910dc22efc13c3621165905963a41e diff --git a/products/cloudrun/terraform.yaml b/products/cloudrun/terraform.yaml index ee64ce02f6c8..ae8bb89df7ac 100644 --- a/products/cloudrun/terraform.yaml +++ b/products/cloudrun/terraform.yaml @@ -24,7 +24,7 @@ overrides: !ruby/object:Overrides::ResourceOverrides test_env_vars: namespace: :PROJECT_NAME custom_code: !ruby/object:Provider::Terraform::CustomCode - encoder: 'templates/terraform/encoders/cloud_run.go.erb' + encoder: 'templates/terraform/encoders/cloud_run_domain_mapping.go.erb' decoder: 'templates/terraform/decoders/cloud_run.go.erb' properties: name: !ruby/object:Overrides::Terraform::PropertyOverride @@ -53,7 +53,7 @@ overrides: !ruby/object:Overrides::ResourceOverrides test_env_vars: namespace: :PROJECT_NAME custom_code: !ruby/object:Provider::Terraform::CustomCode - encoder: 'templates/terraform/encoders/cloud_run.go.erb' + encoder: 'templates/terraform/encoders/cloud_run_service.go.erb' decoder: 'templates/terraform/decoders/cloud_run.go.erb' properties: name: !ruby/object:Overrides::Terraform::PropertyOverride diff --git a/templates/terraform/encoders/cloud_run_domain_mapping.go.erb b/templates/terraform/encoders/cloud_run_domain_mapping.go.erb new file mode 100644 index 000000000000..195f26b2b445 --- /dev/null +++ b/templates/terraform/encoders/cloud_run_domain_mapping.go.erb @@ -0,0 +1,22 @@ +<%# The license inside this block applies to this file. + # 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. +-%> +name := d.Get("name").(string) +metadata := obj["metadata"].(map[string]interface{}) +metadata["name"] = name + +// The only acceptable version/kind right now +obj["apiVersion"] = "domains.cloudrun.com/v1alpha1" +obj["kind"] = "<%= "#{object.name}" -%>" +return obj, nil diff --git a/templates/terraform/encoders/cloud_run.go.erb b/templates/terraform/encoders/cloud_run_service.go.erb similarity index 100% rename from templates/terraform/encoders/cloud_run.go.erb rename to templates/terraform/encoders/cloud_run_service.go.erb From d91fdc0e6f14155fc71133414f1032da38f44c4c Mon Sep 17 00:00:00 2001 From: megan07 Date: Fri, 11 Oct 2019 13:32:25 -0500 Subject: [PATCH 18/87] skip TestAccOrganizationIam if env var not set (#2462) Merged PR #2462. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../terraform/tests/resource_google_organization_iam_test.go | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index b15b6a6e193b..f431592a7ddc 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit b15b6a6e193b931c16b6741900f3de2b93a433d7 +Subproject commit f431592a7ddc26f459edd197a60ace3cedcc954c diff --git a/build/terraform-beta b/build/terraform-beta index a71ed23efd91..e18cfe991b82 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit a71ed23efd910dc22efc13c3621165905963a41e +Subproject commit e18cfe991b82fce06b2efa9e241cc76b203bc7a0 diff --git a/third_party/terraform/tests/resource_google_organization_iam_test.go b/third_party/terraform/tests/resource_google_organization_iam_test.go index 2e2af516e16b..fa3dbc939a08 100644 --- a/third_party/terraform/tests/resource_google_organization_iam_test.go +++ b/third_party/terraform/tests/resource_google_organization_iam_test.go @@ -2,6 +2,7 @@ package google import ( "fmt" + "os" "reflect" "sort" "testing" @@ -19,6 +20,10 @@ import ( // serially. // Policies are *not tested*, because testing them will ruin changes made to the test org. func TestAccOrganizationIam(t *testing.T) { + if os.Getenv("TF_RUN_ORG_IAM") != "true" { + t.Skip("Environment variable TF_RUN_ORG_IAM is not set, skipping.") + } + t.Parallel() org := getTestOrgFromEnv(t) From 241eacd9d3eab62ba261d9056e34c225b9401124 Mon Sep 17 00:00:00 2001 From: megan07 Date: Fri, 11 Oct 2019 14:39:21 -0500 Subject: [PATCH 19/87] add node config shielded instance config (#2381) Merged PR #2381. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resource_container_cluster.go.erb | 33 ++++++ .../resource_container_cluster_test.go.erb | 109 ++++++++++++++++++ .../resource_container_node_pool_test.go.erb | 76 ++++++++++++ .../terraform/utils/node_config.go.erb | 42 +++++++ .../docs/r/container_cluster.html.markdown | 14 +++ 7 files changed, 276 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index f431592a7ddc..114d7fed816d 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit f431592a7ddc26f459edd197a60ace3cedcc954c +Subproject commit 114d7fed816d36c2f465df122c7dd95532fab8ac diff --git a/build/terraform-beta b/build/terraform-beta index e18cfe991b82..407f2b0688e4 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit e18cfe991b82fce06b2efa9e241cc76b203bc7a0 +Subproject commit 407f2b0688e4e68c211f5f8ab22a12015a3d36ca diff --git a/third_party/terraform/resources/resource_container_cluster.go.erb b/third_party/terraform/resources/resource_container_cluster.go.erb index 6f59edef0cce..9d10ae89ca65 100644 --- a/third_party/terraform/resources/resource_container_cluster.go.erb +++ b/third_party/terraform/resources/resource_container_cluster.go.erb @@ -344,6 +344,12 @@ func resourceContainerCluster() *schema.Resource { }, <% unless version == 'ga' -%> + "enable_shielded_nodes": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "authenticator_groups_config": { Type: schema.TypeList, Optional: true, @@ -947,6 +953,10 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er IpAllocationPolicy: expandIPAllocationPolicy(d.Get("ip_allocation_policy")), PodSecurityPolicyConfig: expandPodSecurityPolicyConfig(d.Get("pod_security_policy_config")), <% unless version == 'ga' -%> + ShieldedNodes: &containerBeta.ShieldedNodes{ + Enabled: d.Get("enable_shielded_nodes").(bool), + ForceSendFields: []string{"Enabled"}, + }, EnableTpu: d.Get("enable_tpu").(bool), BinaryAuthorization: &containerBeta.BinaryAuthorization{ Enabled: d.Get("enable_binary_authorization").(bool), @@ -1200,6 +1210,7 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro d.Set("network", cluster.NetworkConfig.Network) d.Set("subnetwork", cluster.NetworkConfig.Subnetwork) <% unless version == 'ga' -%> + d.Set("enable_shielded_nodes", cluster.ShieldedNodes.Enabled) d.Set("enable_binary_authorization", cluster.BinaryAuthorization != nil && cluster.BinaryAuthorization.Enabled) d.Set("enable_tpu", cluster.EnableTpu) d.Set("tpu_ipv4_cidr_block", cluster.TpuIpv4CidrBlock) @@ -1353,6 +1364,28 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er } } <% unless version == 'ga' -%> + if d.HasChange("enable_shielded_nodes") { + enabled := d.Get("enable_shielded_nodes").(bool) + req := &containerBeta.UpdateClusterRequest{ + Update: &containerBeta.ClusterUpdate{ + DesiredShieldedNodes: &containerBeta.ShieldedNodes{ + Enabled: enabled, + ForceSendFields: []string{"Enabled"}, + }, + }, + } + + updateF := updateFunc(req, "updating GKE shielded nodes") + // Call update serially. + if err := lockedCall(lockKey, updateF); err != nil { + return err + } + + log.Printf("[INFO] GKE cluster %s's shielded nodes has been updated to %v", d.Id(), enabled) + + d.SetPartial("enable_shielded_nodes") + } + if d.HasChange("enable_binary_authorization") { enabled := d.Get("enable_binary_authorization").(bool) req := &containerBeta.UpdateClusterRequest{ 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 7ee435e00323..7e7538e8748c 100644 --- a/third_party/terraform/tests/resource_container_cluster_test.go.erb +++ b/third_party/terraform/tests/resource_container_cluster_test.go.erb @@ -754,6 +754,29 @@ func TestAccContainerCluster_withNodeConfigTaints(t *testing.T) { } <% end -%> +func TestAccContainerCluster_withNodeConfigShieldedInstanceConfig(t *testing.T) { + t.Parallel() + + clusterName := fmt.Sprintf("cluster-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withNodeConfigShieldedInstanceConfig(clusterName), + }, + { + ResourceName: "google_container_cluster.with_node_config", + ImportStateIdPrefix: "us-central1-f/", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + <% unless version.nil? || version == 'ga' -%> func TestAccContainerCluster_withWorkloadMetadataConfig(t *testing.T) { t.Parallel() @@ -1403,6 +1426,38 @@ func TestAccContainerCluster_withBinaryAuthorization(t *testing.T) { }) } +func TestAccContainerCluster_withShieldedNodes(t *testing.T) { + t.Parallel() + + clusterName := fmt.Sprintf("cluster-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withShieldedNodes(clusterName, true), + }, + { + ResourceName: "google_container_cluster.with_shielded_nodes", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerCluster_withShieldedNodes(clusterName, false), + }, + { + ResourceName: "google_container_cluster.with_shielded_nodes", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccContainerCluster_withFlexiblePodCIDR(t *testing.T) { t.Parallel() @@ -2316,6 +2371,48 @@ resource "google_container_cluster" "with_node_config" { } <% end -%> + +func testAccContainerCluster_withNodeConfigShieldedInstanceConfig(clusterName string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_node_config" { + name = "%s" + zone = "us-central1-f" + initial_node_count = 1 + + node_config { + machine_type = "n1-standard-1" + disk_size_gb = 15 + disk_type = "pd-ssd" + local_ssd_count = 1 + oauth_scopes = [ + "https://www.googleapis.com/auth/monitoring", + "https://www.googleapis.com/auth/compute", + "https://www.googleapis.com/auth/devstorage.read_only", + "https://www.googleapis.com/auth/logging.write" + ] + service_account = "default" + metadata = { + foo = "bar" + disable-legacy-endpoints = "true" + } + labels = { + foo = "bar" + } + tags = ["foo", "bar"] + preemptible = true + min_cpu_platform = "Intel Broadwell" + + // Updatable fields + image_type = "COS" + + shielded_instance_config { + enable_secure_boot = true + enable_integrity_monitoring = true + } + } +}`, clusterName) +} + <% unless version.nil? || version == 'ga' -%> func testAccContainerCluster_withWorkloadMetadataConfig() string { return fmt.Sprintf(` @@ -3124,6 +3221,18 @@ resource "google_container_cluster" "with_binary_authorization" { `, clusterName, enabled) } +func testAccContainerCluster_withShieldedNodes(clusterName string, enabled bool) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_shielded_nodes" { + name = "%s" + zone = "us-central1-a" + initial_node_count = 1 + + enable_shielded_nodes = %v +} +`, clusterName, enabled) +} + func testAccContainerCluster_withFlexiblePodCIDR(cluster string) string { return fmt.Sprintf(` resource "google_compute_network" "container_network" { 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 20e000c5d3fe..802e641a53ad 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 @@ -649,6 +649,39 @@ func TestAccContainerNodePool_EmptyGuestAccelerator(t *testing.T) { }) } +func TestAccContainerNodePool_shieldedInstanceConfig(t *testing.T) { + t.Parallel() + + cluster := fmt.Sprintf("tf-nodepool-test-%s", acctest.RandString(10)) + np := fmt.Sprintf("tf-nodepool-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerNodePoolDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccContainerNodePool_shieldedInstanceConfig(cluster, np), + }, + resource.TestStep{ + ResourceName: "google_container_node_pool.np", + ImportState: true, + 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"}, + }, + }, + }) +} + func testAccCheckContainerNodePoolDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) @@ -1420,3 +1453,46 @@ resource "google_container_node_pool" "np" { } }`, cluster, np) } + +func testAccContainerNodePool_shieldedInstanceConfig(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_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 4116f60d3b53..10cead014348 100644 --- a/third_party/terraform/utils/node_config.go.erb +++ b/third_party/terraform/utils/node_config.go.erb @@ -148,6 +148,27 @@ var schemaNodeConfig = &schema.Schema{ Elem: &schema.Schema{Type: schema.TypeString}, }, + "shielded_instance_config": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable_secure_boot": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "enable_integrity_monitoring": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + }, + }, + }, + "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.", @@ -308,6 +329,15 @@ func expandNodeConfig(v interface{}) *containerBeta.NodeConfig { } nc.Tags = tags } + + if v, ok := nodeConfig["shielded_instance_config"]; ok && len(v.([]interface{})) > 0 { + conf := v.([]interface{})[0].(map[string]interface{}) + nc.ShieldedInstanceConfig = &containerBeta.ShieldedInstanceConfig{ + EnableSecureBoot: conf["enable_secure_boot"].(bool), + EnableIntegrityMonitoring: conf["enable_integrity_monitoring"].(bool), + } + } + // Preemptible Is Optional+Default, so it always has a value nc.Preemptible = nodeConfig["preemptible"].(bool) @@ -371,6 +401,7 @@ func flattenNodeConfig(c *containerBeta.NodeConfig) []map[string]interface{} { "tags": c.Tags, "preemptible": c.Preemptible, "min_cpu_platform": c.MinCpuPlatform, + "shielded_instance_config": flattenShieldedInstanceConfig(c.ShieldedInstanceConfig), <% unless version == 'ga' -%> "taint": flattenTaints(c.Taints), "workload_metadata_config": flattenWorkloadMetadataConfig(c.WorkloadMetadataConfig), @@ -396,6 +427,17 @@ func flattenContainerGuestAccelerators(c []*containerBeta.AcceleratorConfig) []m return result } +func flattenShieldedInstanceConfig(c *containerBeta.ShieldedInstanceConfig) []map[string]interface{} { + result := []map[string]interface{}{} + if c != nil { + result = append(result, map[string]interface{}{ + "enable_secure_boot": c.EnableSecureBoot, + "enable_integrity_monitoring": c.EnableIntegrityMonitoring, + }) + } + return result +} + <% unless version.nil? || version == 'ga' -%> func flattenTaints(c []*containerBeta.NodeTaint) []map[string]interface{} { result := []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 57c49b585a42..708f7715300a 100644 --- a/third_party/terraform/website/docs/r/container_cluster.html.markdown +++ b/third_party/terraform/website/docs/r/container_cluster.html.markdown @@ -191,6 +191,8 @@ for more details. Structure is documented below. will have statically granted permissions beyond those provided by the RBAC configuration or IAM. Defaults to `false` +* `enable_shielded_nodes` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) Enable Shielded Nodes features on all nodes in this cluster. Defaults to `false`. + * `initial_node_count` - (Optional) The number of nodes to create in this cluster's default node pool. In regional or multi-zonal clusters, this is the number of nodes per zone. Must be set if `node_pool` is not set. If you're using @@ -557,6 +559,8 @@ The `node_config` block supports: -> Projects that enable the [Cloud Compute Engine API](https://cloud.google.com/compute/) with Terraform may need these roles added manually to the service account. Projects that enable the API in the Cloud Console should have them added automatically. +* `shielded_instance_config` - (Optional) Shielded Instance options. Structure is documented below. + * `tags` - (Optional) The list of instance tags applied to all nodes. Tags are used to identify valid sources or targets for network firewalls. @@ -629,6 +633,16 @@ resource_usage_export_config { } ``` +The `shielded_instance_config` block supports: + +* `enable_secure_boot` (Optional) - Defines if the instance has Secure Boot enabled. + +Secure Boot helps ensure that the system only runs authentic software by verifying the digital signature of all boot components, and halting the boot process if signature verification fails. Defaults to `false`. + +* `enable_integrity_monitoring` (Optional) - Defines if the instance has integrity monitoring enabled. + +Enables monitoring and attestation of the boot integrity of the instance. The attestation is performed against the integrity policy baseline. This baseline is initially derived from the implicitly trusted boot image when the instance is created. Defaults to `true`. + The `taint` block supports: * `key` (Required) Key for taint. From d03be0b13775ff630370d50ac7fd67d158efcc5a Mon Sep 17 00:00:00 2001 From: Cameron Thornton Date: Fri, 11 Oct 2019 16:19:30 -0700 Subject: [PATCH 20/87] add missing tpu_tensorflow_versions doc (#2463) Merged PR #2463. --- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- .../terraform/website-compiled/google.erb | 3 ++ ...urce_tpu_tensorflow_versions.html.markdown | 48 +++++++++++++++++++ 5 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 third_party/terraform/website/docs/d/datasource_tpu_tensorflow_versions.html.markdown diff --git a/build/terraform b/build/terraform index 114d7fed816d..8730e1dbb985 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 114d7fed816d36c2f465df122c7dd95532fab8ac +Subproject commit 8730e1dbb985f7d1f3e78dbf93b76fcb384e4e63 diff --git a/build/terraform-beta b/build/terraform-beta index 407f2b0688e4..8a99ed3f0a32 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 407f2b0688e4e68c211f5f8ab22a12015a3d36ca +Subproject commit 8a99ed3f0a32145764ed174aecb410987065b259 diff --git a/build/terraform-mapper b/build/terraform-mapper index 11538f8e3a8e..e190929caf0c 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 11538f8e3a8eaf97ed33c741dd13ce3e6a4399a1 +Subproject commit e190929caf0c18826931cd77585a6c1a6e34014c diff --git a/third_party/terraform/website-compiled/google.erb b/third_party/terraform/website-compiled/google.erb index 2376b2654a8b..b57105ae4d61 100644 --- a/third_party/terraform/website-compiled/google.erb +++ b/third_party/terraform/website-compiled/google.erb @@ -199,6 +199,9 @@ > google_storage_transfer_project_service_account + > + google_tpu_tensorflow_versions + diff --git a/third_party/terraform/website/docs/d/datasource_tpu_tensorflow_versions.html.markdown b/third_party/terraform/website/docs/d/datasource_tpu_tensorflow_versions.html.markdown new file mode 100644 index 000000000000..1ba803ebf155 --- /dev/null +++ b/third_party/terraform/website/docs/d/datasource_tpu_tensorflow_versions.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "google" +page_title: "Google: google_tpu_tensorflow_versions" +sidebar_current: "docs-google-datasource-tpu-tensorflow-versions" +description: |- + Get available TensorFlow versions. +--- + +# google\_tpu\_tensorflow\_versions + +Get TensorFlow versions available for a project. For more information see the [official documentation](https://cloud.google.com/tpu/docs/) and [API](https://cloud.google.com/tpu/docs/reference/rest/v1/projects.locations.tensorflowVersions). + +## Example Usage + +```hcl +data "google_tpu_tensorflow_versions" "available" { } +``` + +## Example Usage: Configure Basic TPU Node with available version + +```hcl +data "google_tpu_tensorflow_versions" "available" { } + +resource "google_tpu_node" "tpu" { + name = "test-tpu" + zone = "us-central1-b" + + accelerator_type = "v3-8" + tensorflow_version = "${data.google_tpu_tensorflow_versions.available.versions[0]}" + cidr_block = "10.2.0.0/29" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `project` - (Optional) The project to list versions for. If it + is not provided, the provider project is used. + +* `zone` - (Optional) The zone to list versions for. If it + is not provided, the provider zone is used. + +## Attributes Reference + +The following attributes are exported: + +* `versions` - The list of TensorFlow versions available for the given project and zone. From 211ba0429933ea56d160206b21bc938812cef65b Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Mon, 14 Oct 2019 09:22:47 -0700 Subject: [PATCH 21/87] Implement basepath patches in the provider. (#2443) Merged PR #2443. --- .../custom_expand/compute_full_url.erb | 8 ++++++- .../custom_expand/self_link_from_name.erb | 8 +++++-- .../data_source_google_compute_zones.go | 12 +++++++++-- .../resource_compute_instance_group.go | 2 +- .../resources/resource_compute_target_pool.go | 21 ++++++++++++------- .../resource_container_cluster.go.erb | 12 ++++------- .../resources/resource_google_project.go | 9 +++++--- 7 files changed, 47 insertions(+), 25 deletions(-) diff --git a/templates/terraform/custom_expand/compute_full_url.erb b/templates/terraform/custom_expand/compute_full_url.erb index 9bb1dd785e6c..32dbb4da8be5 100644 --- a/templates/terraform/custom_expand/compute_full_url.erb +++ b/templates/terraform/custom_expand/compute_full_url.erb @@ -20,5 +20,11 @@ func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d T if err != nil { return nil, fmt.Errorf("Invalid value for <%= property.name.underscore -%>: %s", err) } - return "https://www.googleapis.com/compute/v1/" + f.RelativeLink(), nil + + url, err := replaceVars(d, config, "{{ComputeBasePath}}"+f.RelativeLink()) + if err != nil { + return nil, err + } + + return url, nil } diff --git a/templates/terraform/custom_expand/self_link_from_name.erb b/templates/terraform/custom_expand/self_link_from_name.erb index 380369602984..1fa6d99471eb 100644 --- a/templates/terraform/custom_expand/self_link_from_name.erb +++ b/templates/terraform/custom_expand/self_link_from_name.erb @@ -21,8 +21,12 @@ func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d T // Anything that starts with a URL scheme is assumed to be a self link worth using. return v, nil } else if strings.HasPrefix(v.(string), "projects/") { - // If the self link references a project, we'll just stuck the compute v1 prefix on it. - return "https://www.googleapis.com/compute/v1/" + v.(string), nil + // If the self link references a project, we'll just stuck the compute prefix on it + url, err := replaceVars(d, config, "{{ComputeBasePath}}" + v.(string)) + if err != nil { + return "", err + } + return url, nil } else if strings.HasPrefix(v.(string), "regions/") || strings.HasPrefix(v.(string), "zones/") { // For regional or zonal resources which include their region or zone, just put the project in front. url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/") diff --git a/third_party/terraform/data_sources/data_source_google_compute_zones.go b/third_party/terraform/data_sources/data_source_google_compute_zones.go index 38c3ca49e1d3..981026e9ab20 100644 --- a/third_party/terraform/data_sources/data_source_google_compute_zones.go +++ b/third_party/terraform/data_sources/data_source_google_compute_zones.go @@ -51,8 +51,16 @@ func dataSourceGoogleComputeZonesRead(d *schema.ResourceData, meta interface{}) return err } - regionUrl := fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/regions/%s", - project, region) + // we want to share exactly the same base path as the compute client or the + // region string may mismatch, giving us no results + // note that the client's BasePath includes a `projects/` suffix, so that'll + // need to be added to the URL below if the source changes + computeClientBasePath := config.clientCompute.BasePath + + regionUrl, err := replaceVars(d, config, fmt.Sprintf("%s%s/regions/%s", computeClientBasePath, project, region)) + if err != nil { + return err + } filter := fmt.Sprintf("(region eq %s)", regionUrl) if s, ok := d.GetOk("status"); ok { diff --git a/third_party/terraform/resources/resource_compute_instance_group.go b/third_party/terraform/resources/resource_compute_instance_group.go index 00604d854103..d4f9ad3542a9 100644 --- a/third_party/terraform/resources/resource_compute_instance_group.go +++ b/third_party/terraform/resources/resource_compute_instance_group.go @@ -108,7 +108,7 @@ func getInstanceReferences(instanceUrls []string) (refs []*compute.InstanceRefer func validInstanceURLs(instanceUrls []string) bool { for _, v := range instanceUrls { - if !strings.HasPrefix(v, "https://www.googleapis.com/compute/v1/") { + if !strings.HasPrefix(v, "https://") { return false } } diff --git a/third_party/terraform/resources/resource_compute_target_pool.go b/third_party/terraform/resources/resource_compute_target_pool.go index 191684e87e55..cc25a52f21a8 100644 --- a/third_party/terraform/resources/resource_compute_target_pool.go +++ b/third_party/terraform/resources/resource_compute_target_pool.go @@ -136,20 +136,25 @@ func convertHealthChecks(healthChecks []interface{}, d *schema.ResourceData, con // Instances do not need to exist yet, so we simply generate URLs. // Instances can be full URLS or zone/name -func convertInstancesToUrls(project string, names *schema.Set) ([]string, error) { +func convertInstancesToUrls(d *schema.ResourceData, config *Config, project string, names *schema.Set) ([]string, error) { urls := make([]string, len(names.List())) for i, nameI := range names.List() { name := nameI.(string) - if strings.HasPrefix(name, "https://www.googleapis.com/compute/v1/") { + // assume that any URI will start with https:// + if strings.HasPrefix(name, "https://") { urls[i] = name } else { splitName := strings.Split(name, "/") if len(splitName) != 2 { return nil, fmt.Errorf("Invalid instance name, require URL or zone/name: %s", name) } else { - urls[i] = fmt.Sprintf( - "https://www.googleapis.com/compute/v1/projects/%s/zones/%s/instances/%s", - project, splitName[0], splitName[1]) + url, err := replaceVars(d, config, fmt.Sprintf( + "{{ComputeBasePath}}projects/%s/zones/%s/instances/%s", + project, splitName[0], splitName[1])) + if err != nil { + return nil, err + } + urls[i] = url } } } @@ -174,7 +179,7 @@ func resourceComputeTargetPoolCreate(d *schema.ResourceData, meta interface{}) e return err } - instanceUrls, err := convertInstancesToUrls(project, d.Get("instances").(*schema.Set)) + instanceUrls, err := convertInstancesToUrls(d, config, project, d.Get("instances").(*schema.Set)) if err != nil { return err } @@ -310,11 +315,11 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e old := old_.(*schema.Set) new := new_.(*schema.Set) - addUrls, err := convertInstancesToUrls(project, new.Difference(old)) + addUrls, err := convertInstancesToUrls(d, config, project, new.Difference(old)) if err != nil { return err } - removeUrls, err := convertInstancesToUrls(project, old.Difference(new)) + removeUrls, err := convertInstancesToUrls(d, config, project, old.Difference(new)) if err != nil { return err } diff --git a/third_party/terraform/resources/resource_container_cluster.go.erb b/third_party/terraform/resources/resource_container_cluster.go.erb index 9d10ae89ca65..cf09740c5d06 100644 --- a/third_party/terraform/resources/resource_container_cluster.go.erb +++ b/third_party/terraform/resources/resource_container_cluster.go.erb @@ -18,7 +18,7 @@ import ( ) var ( - instanceGroupManagerURL = regexp.MustCompile(fmt.Sprintf("^https://www.googleapis.com/compute/v1/projects/(%s)/zones/([a-z0-9-]*)/instanceGroupManagers/([^/]*)", ProjectRegex)) + instanceGroupManagerURL = regexp.MustCompile(fmt.Sprintf("projects/(%s)/zones/([a-z0-9-]*)/instanceGroupManagers/([^/]*)", ProjectRegex)) networkConfig = &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -2094,13 +2094,9 @@ func waitForContainerClusterReady(config *Config, project, location, clusterName }) } -// container engine's API currently mistakenly returns the instance group manager's -// URL instead of the instance group's URL in its responses. This shim detects that -// error, and corrects it, by fetching the instance group manager URL and retrieving -// the instance group manager, then using that to look up the instance group URL, which -// is then substituted. -// -// This should be removed when the API response is fixed. +// container engine's API returns the instance group manager's URL instead of the instance +// group's URL in its responses, while the field is named as if it should have been the group +// and not the manager. This shim should be supported for backwards compatibility reasons. func getInstanceGroupUrlsFromManagerUrls(config *Config, igmUrls []string) ([]string, error) { instanceGroupURLs := make([]string, 0, len(igmUrls)) for _, u := range igmUrls { diff --git a/third_party/terraform/resources/resource_google_project.go b/third_party/terraform/resources/resource_google_project.go index fff44dfed136..02e7247c2d10 100644 --- a/third_party/terraform/resources/resource_google_project.go +++ b/third_party/terraform/resources/resource_google_project.go @@ -274,7 +274,7 @@ func resourceGoogleProjectCreate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error enabling the Compute Engine API required to delete the default network: %s", err) } - if err = forceDeleteComputeNetwork(project.ProjectId, "default", config); err != nil { + if err = forceDeleteComputeNetwork(d, config, project.ProjectId, "default"); err != nil { if isGoogleApiErrorWithCode(err, 404) { log.Printf("[DEBUG] Default network not found for project %q, no need to delete it", project.ProjectId) } else { @@ -495,8 +495,11 @@ func resourceProjectImportState(d *schema.ResourceData, meta interface{}) ([]*sc } // Delete a compute network along with the firewall rules inside it. -func forceDeleteComputeNetwork(projectId, networkName string, config *Config) error { - networkLink := fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/global/networks/%s", projectId, networkName) +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)) + if err != nil { + return err + } token := "" for paginate := true; paginate; { From c2795f67a48f4ebbcefa2bb03b7a3957b3fa4af6 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Mon, 14 Oct 2019 12:27:36 -0700 Subject: [PATCH 22/87] Update max pods per node docs to indicate it's GA (#2465) Merged PR #2465. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../website/docs/r/container_cluster.html.markdown | 9 ++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build/terraform b/build/terraform index 8730e1dbb985..1db04e9e73ac 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 8730e1dbb985f7d1f3e78dbf93b76fcb384e4e63 +Subproject commit 1db04e9e73acb6bc91ab24ea2e48dd95cd88aa1a diff --git a/build/terraform-beta b/build/terraform-beta index 8a99ed3f0a32..b4b3a5a2ccd2 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 8a99ed3f0a32145764ed174aecb410987065b259 +Subproject commit b4b3a5a2ccd24537f1cd89e07855395b55150c97 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 708f7715300a..b82307a2754e 100644 --- a/third_party/terraform/website/docs/r/container_cluster.html.markdown +++ b/third_party/terraform/website/docs/r/container_cluster.html.markdown @@ -170,11 +170,10 @@ for more details. Structure is documented below. * `description` - (Optional) Description of the cluster. -* `default_max_pods_per_node` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) The default maximum number of pods per node in this cluster. - Note that this does not work on node pools which are "route-based" - that is, node - pools belonging to clusters that do not have IP Aliasing enabled. - See the [official documentation](https://cloud.google.com/kubernetes-engine/docs/how-to/flexible-pod-cidr) - for more information. +* `default_max_pods_per_node` - (Optional) The default maximum number of pods +per node in this cluster. This doesn't work on "routes-based" clusters, clusters +that don't have IP Aliasing enabled. See the [official documentation](https://cloud.google.com/kubernetes-engine/docs/how-to/flexible-pod-cidr) +for more information. * `enable_binary_authorization` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) Enable Binary Authorization for this cluster. If enabled, all container images will be validated by Google Binary Authorization. From 7cdc6b4573c2942ca25c7e0cc135cadd53530c06 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Tue, 15 Oct 2019 09:58:44 -0700 Subject: [PATCH 23/87] Fix formatting on Cloud Functions docs (#2469) Merged PR #2469. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../r/cloudfunctions_function.html.markdown | 32 +++++++++---------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/build/terraform b/build/terraform index 1db04e9e73ac..a0dfdc80c5df 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 1db04e9e73acb6bc91ab24ea2e48dd95cd88aa1a +Subproject commit a0dfdc80c5df22af808f004180695f9e4d578356 diff --git a/build/terraform-beta b/build/terraform-beta index b4b3a5a2ccd2..c39a624b52e6 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit b4b3a5a2ccd24537f1cd89e07855395b55150c97 +Subproject commit c39a624b52e61bf4ff6920e1973fc6f558c275cb diff --git a/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown b/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown index ccd9cdb9e323..4a87b69f6b98 100644 --- a/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown +++ b/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown @@ -19,9 +19,7 @@ to be invoked. See below examples for how to set up the appropriate permissions, or view the [Cloud Functions IAM resources](/docs/providers/google/r/cloudfunctions_cloud_function_iam.html) for Cloud Functions. -## Example Usage - -Secured function with a user allowed to invoke: +## Example Usage - Public Function ```hcl resource "google_storage_bucket" "bucket" { @@ -43,30 +41,21 @@ resource "google_cloudfunctions_function" "function" { source_archive_bucket = "${google_storage_bucket.bucket.name}" source_archive_object = "${google_storage_bucket_object.archive.name}" trigger_http = true - timeout = 60 entry_point = "helloGET" - labels = { - my-label = "my-label-value" - } - - environment_variables = { - MY_ENV_VAR = "my-env-var-value" - } } -# Add IAM member for a user who can invoke the function (no admin actions) +# IAM entry for all users to invoke the function resource "google_cloudfunctions_function_iam_member" "invoker" { project = "${google_cloudfunctions_function.function.project}" region = "${google_cloudfunctions_function.function.region}" cloud_function = "${google_cloudfunctions_function.function.name}" role = "roles/cloudfunctions.invoker" - member = "user:myFunctionInvoker@example.com" + member = "allUsers" } ``` -A publically invocable function (similar behavior to functions created before -private-by-default): +## Example Usage - Single User ```hcl resource "google_storage_bucket" "bucket" { @@ -88,19 +77,28 @@ resource "google_cloudfunctions_function" "function" { source_archive_bucket = "${google_storage_bucket.bucket.name}" source_archive_object = "${google_storage_bucket_object.archive.name}" trigger_http = true + timeout = 60 entry_point = "helloGET" + labels = { + my-label = "my-label-value" + } + + environment_variables = { + MY_ENV_VAR = "my-env-var-value" + } } -# Add IAM member for a user who can invoke the function (no admin actions) +# IAM entry for a single user to invoke the function resource "google_cloudfunctions_function_iam_member" "invoker" { project = "${google_cloudfunctions_function.function.project}" region = "${google_cloudfunctions_function.function.region}" cloud_function = "${google_cloudfunctions_function.function.name}" role = "roles/cloudfunctions.invoker" - member = "allUsers" + member = "user:myFunctionInvoker@example.com" } ``` + ## Argument Reference The following arguments are supported: From 4771a34383e6801d048afb67b7afecba213caa8c Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Tue, 15 Oct 2019 10:04:13 -0700 Subject: [PATCH 24/87] Fixed out of range error (#2464) Merged PR #2464. --- build/terraform-mapper | 2 +- third_party/validator/storage_bucket.go | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/build/terraform-mapper b/build/terraform-mapper index e190929caf0c..1d5c7659d772 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit e190929caf0c18826931cd77585a6c1a6e34014c +Subproject commit 1d5c7659d77251835a1d1430561520189aad38b4 diff --git a/third_party/validator/storage_bucket.go b/third_party/validator/storage_bucket.go index f70b004d6ec4..21dadb4c74e2 100644 --- a/third_party/validator/storage_bucket.go +++ b/third_party/validator/storage_bucket.go @@ -50,9 +50,9 @@ func GetStorageBucketApiObject(d TerraformResourceData, config *Config) (map[str // Create a bucket, setting the labels, location and name. sb := &storage.Bucket{ - Name: bucket, - Labels: expandLabels(d), - Location: location, + Name: bucket, + Labels: expandLabels(d), + Location: location, IamConfiguration: expandIamConfiguration(d), } @@ -77,14 +77,16 @@ func GetStorageBucketApiObject(d TerraformResourceData, config *Config) (map[str sb.Website = &storage.BucketWebsite{} - website := websites[0].(map[string]interface{}) + if len(websites) > 0 { + website := websites[0].(map[string]interface{}) - if v, ok := website["not_found_page"]; ok { - sb.Website.NotFoundPage = v.(string) - } + if v, ok := website["not_found_page"]; ok { + sb.Website.NotFoundPage = v.(string) + } - if v, ok := website["main_page_suffix"]; ok { - sb.Website.MainPageSuffix = v.(string) + if v, ok := website["main_page_suffix"]; ok { + sb.Website.MainPageSuffix = v.(string) + } } } From d2ba5826e67ecf18fd22e9a1743713aa8da16747 Mon Sep 17 00:00:00 2001 From: Chris Sng Date: Wed, 16 Oct 2019 05:21:11 +0800 Subject: [PATCH 25/87] Configure release channel when creating GKE clusters (#2414) Merged PR #2414. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resource_container_cluster.go.erb | 50 ++++++++ .../resource_container_cluster_test.go.erb | 112 ++++++++++++++++++ .../docs/r/container_cluster.html.markdown | 19 ++- 5 files changed, 180 insertions(+), 5 deletions(-) diff --git a/build/terraform b/build/terraform index a0dfdc80c5df..87a05b2fc09c 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit a0dfdc80c5df22af808f004180695f9e4d578356 +Subproject commit 87a05b2fc09ca86b6074e641792bf80dc228eef7 diff --git a/build/terraform-beta b/build/terraform-beta index c39a624b52e6..df6ba8c84acd 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit c39a624b52e61bf4ff6920e1973fc6f558c275cb +Subproject commit df6ba8c84acd790c636bbff74a45fd85bc8934dd diff --git a/third_party/terraform/resources/resource_container_cluster.go.erb b/third_party/terraform/resources/resource_container_cluster.go.erb index cf09740c5d06..7a426b8e7250 100644 --- a/third_party/terraform/resources/resource_container_cluster.go.erb +++ b/third_party/terraform/resources/resource_container_cluster.go.erb @@ -725,6 +725,26 @@ func resourceContainerCluster() *schema.Resource { }, <% unless version == 'ga' -%> + "release_channel": { + Type: schema.TypeList, + ForceNew: true, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "channel": { + Type: schema.TypeString, + Default: "UNSPECIFIED", + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"UNSPECIFIED", "RAPID", "REGULAR", "STABLE"}, false), + DiffSuppressFunc: emptyOrDefaultStringSuppress("UNSPECIFIED"), + }, + }, + }, + }, + "vertical_pod_autoscaling": { Type: schema.TypeList, MaxItems: 1, @@ -957,6 +977,7 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er Enabled: d.Get("enable_shielded_nodes").(bool), ForceSendFields: []string{"Enabled"}, }, + ReleaseChannel: expandReleaseChannel(d.Get("release_channel")), EnableTpu: d.Get("enable_tpu").(bool), BinaryAuthorization: &containerBeta.BinaryAuthorization{ Enabled: d.Get("enable_binary_authorization").(bool), @@ -1220,6 +1241,9 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro if err := d.Set("authenticator_groups_config", flattenAuthenticatorGroupsConfig(cluster.AuthenticatorGroupsConfig)); err != nil { return err } + if err := d.Set("release_channel", flattenReleaseChannel(cluster.ReleaseChannel)); err != nil { + return err + } d.Set("enable_intranode_visibility", cluster.NetworkConfig.EnableIntraNodeVisibility) <% else -%> if err := d.Set("cluster_autoscaling", nil); err != nil { @@ -2358,6 +2382,17 @@ func expandPrivateClusterConfig(configured interface{}) *containerBeta.PrivateCl } <% unless version == 'ga' -%> +func expandReleaseChannel(configured interface{}) *containerBeta.ReleaseChannel { + l := configured.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil + } + config := l[0].(map[string]interface{}) + return &containerBeta.ReleaseChannel{ + Channel: config["channel"].(string), + } +} + func expandDatabaseEncryption(configured interface{}) *containerBeta.DatabaseEncryption { l := configured.([]interface{}) if len(l) == 0 { @@ -2564,6 +2599,21 @@ func flattenPrivateClusterConfig(c *containerBeta.PrivateClusterConfig) []map[st } <% unless version == 'ga' -%> +func flattenReleaseChannel(c *containerBeta.ReleaseChannel) []map[string]interface{} { + result := []map[string]interface{}{} + if c != nil { + result = append(result, map[string]interface{}{ + "channel": c.Channel, + }) + } else { + // Explicitly set the release channel to the default. + result = append(result, map[string]interface{}{ + "channel": "UNSPECIFIED", + }) + } + return result +} + func flattenVerticalPodAutoscaling(c *containerBeta.VerticalPodAutoscaling) []map[string]interface{} { if c == nil { return nil 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 7e7538e8748c..4d7990c33210 100644 --- a/third_party/terraform/tests/resource_container_cluster_test.go.erb +++ b/third_party/terraform/tests/resource_container_cluster_test.go.erb @@ -361,6 +361,93 @@ func TestAccContainerCluster_withNetworkPolicyEnabled(t *testing.T) { }) } +<% unless version == 'ga' -%> +func TestAccContainerCluster_withReleaseChannelEnabled(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("cluster-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "STABLE"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "REGULAR"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "RAPID"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "UNSPECIFIED"), + }, + { + ResourceName: "google_container_cluster.with_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccContainerCluster_withReleaseChannelDefault(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("cluster-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withReleaseChannelDefault(clusterName), + }, + { + ResourceName: "google_container_cluster.with_default_release_channel", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccContainerCluster_withInvalidReleaseChannel(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("cluster-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withReleaseChannelEnabled(clusterName, "CANARY"), + ExpectError: regexp.MustCompile(`config is invalid: expected release_channel\.0\.channel to be one of \[UNSPECIFIED RAPID REGULAR STABLE\], got CANARY`), + }, + }, + }) +} +<% end -%> + func TestAccContainerCluster_withMasterAuthorizedNetworksConfig(t *testing.T) { t.Parallel() @@ -1970,6 +2057,31 @@ resource "google_container_cluster" "with_network_policy_enabled" { }`, clusterName) } +<% unless version == 'ga' -%> +func testAccContainerCluster_withReleaseChannelEnabled(clusterName string, channel string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_release_channel" { + name = "%s" + location = "us-central1-a" + initial_node_count = 1 + + release_channel { + channel = "%s" + } +}`, clusterName, channel) +} + +func testAccContainerCluster_withReleaseChannelDefault(clusterName string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_default_release_channel" { + name = "%s" + location = "us-central1-a" + initial_node_count = 1 + release_channel {} +}`, clusterName) +} +<% end -%> + func testAccContainerCluster_removeNetworkPolicy(clusterName string) string { return fmt.Sprintf(` resource "google_container_cluster" "with_network_policy_enabled" { 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 b82307a2754e..9f7b7ed14810 100644 --- a/third_party/terraform/website/docs/r/container_cluster.html.markdown +++ b/third_party/terraform/website/docs/r/container_cluster.html.markdown @@ -156,7 +156,7 @@ in this cluster in CIDR notation (e.g. 10.96.0.0/14). Leave blank to have one automatically chosen or specify a /14 block in 10.0.0.0/8. This field will only work if your cluster is not VPC-native- when an `ip_allocation_policy` block is not defined, or `ip_allocation_policy.use_ip_aliases` is set to false. If your -cluster is VPC-native, use `ip_allocation_policy.cluster_ipv4_cidr_block`. +cluster is VPC-native, use `ip_allocation_policy.cluster_ipv4_cidr_block`. * `cluster_autoscaling` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) Per-cluster configuration of Node Auto-Provisioning with Cluster Autoscaler to @@ -289,6 +289,10 @@ to the datasource. A `region` can have a different set of supported versions tha * `project` - (Optional) The ID of the project in which the resource belongs. If it is not provided, the provider project is used. +* `release_channel` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) Configuration options for the + [Release channel](https://cloud.google.com/kubernetes-engine/docs/concepts/release-channels) + feature, which provide more control over automatic upgrades of your GKE clusters. Structure is documented below. + * `remove_default_node_pool` - (Optional) If `true`, deletes the default node pool upon cluster creation. If you're using `google_container_node_pool` resources with no default node pool, this should be set to `true`, alongside @@ -433,7 +437,7 @@ to have a range chosen with a specific netmask. Set to a CIDR notation (e.g. 10. from the RFC-1918 private networks (e.g. 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) to pick a specific range to use. This field will only work if your cluster is VPC-native- when `ip_allocation_policy.use_ip_aliases` is undefined or set to -true. If your cluster is not VPC-native, use `cluster_ipv4_cidr`. +true. If your cluster is not VPC-native, use `cluster_ipv4_cidr`. * `node_ipv4_cidr_block` - (Optional) The IP address range of the node IPs in this cluster. This should be set only if `create_subnetwork` is true. @@ -547,7 +551,7 @@ The `node_config` block supports: are preemptible. See the [official documentation](https://cloud.google.com/container-engine/docs/preemptible-vm) for more information. Defaults to false. -* `sandbox_config` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) [GKE Sandbox](https://cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods) configuration. When enabling this feature you must specify `image_type = "COS_CONTAINERD"` and `node_version = "1.12.7-gke.17"` or later to use it. +* `sandbox_config` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) [GKE Sandbox](https://cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods) configuration. When enabling this feature you must specify `image_type = "COS_CONTAINERD"` and `node_version = "1.12.7-gke.17"` or later to use it. Structure is documented below. * `service_account` - (Optional) The service account to be used by the Node VMs. @@ -614,6 +618,15 @@ The `sandbox_type` block supports: * `"gvisor"`: Pods run within a gVisor sandbox. +The `release_channel` block supports: + +* `channel` - (Optional) The selected release channel. Defaults to `UNSPECIFIED`. + Accepted values are: + * UNSPECIFIED: Not set. + * RAPID: Weekly upgrade cadence; Early testers and developers who requires new features. + * REGULAR: Multiple per month upgrade cadence; Production users who need features not yet offered in the Stable channel. + * STABLE: Every few months upgrade cadence; Production users who need stability above all else, and for whom frequent upgrades are too risky. + The `resource_usage_export_config` block supports: * `enable_network_egress_metering` (Optional) - Whether to enable network egress metering for this cluster. If enabled, a daemonset will be created From cc0dc86f7bab750f6bb80c690a714260f2bb8c6a Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 16 Oct 2019 08:59:50 -0700 Subject: [PATCH 26/87] Add custom flatten to transform json to string -> string map for params (#2467) Merged PR #2467. --- build/terraform | 2 +- build/terraform-beta | 2 +- products/bigquerydatatransfer/terraform.yaml | 2 + .../custom_flatten/json_to_string_map.go.erb | 27 +++++++ ...urce_bigquery_data_transfer_config_test.go | 70 ++++++++++++++++++- 5 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 templates/terraform/custom_flatten/json_to_string_map.go.erb diff --git a/build/terraform b/build/terraform index 87a05b2fc09c..e7dac2b6e377 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 87a05b2fc09ca86b6074e641792bf80dc228eef7 +Subproject commit e7dac2b6e377ad0cc01c00f69b4b365bff3de12d diff --git a/build/terraform-beta b/build/terraform-beta index df6ba8c84acd..1eb072f64bcf 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit df6ba8c84acd790c636bbff74a45fd85bc8934dd +Subproject commit 1eb072f64bcf7f80076f66dc83132b66e95732c9 diff --git a/products/bigquerydatatransfer/terraform.yaml b/products/bigquerydatatransfer/terraform.yaml index f6f089fb79d0..fe561caf0a83 100644 --- a/products/bigquerydatatransfer/terraform.yaml +++ b/products/bigquerydatatransfer/terraform.yaml @@ -23,6 +23,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides properties: location: !ruby/object:Overrides::Terraform::PropertyOverride ignore_read: true + params: !ruby/object:Overrides::Terraform::PropertyOverride + custom_flatten: templates/terraform/custom_flatten/json_to_string_map.go.erb examples: - !ruby/object:Provider::Terraform::Examples skip_test: true diff --git a/templates/terraform/custom_flatten/json_to_string_map.go.erb b/templates/terraform/custom_flatten/json_to_string_map.go.erb new file mode 100644 index 000000000000..678b2a5c6de9 --- /dev/null +++ b/templates/terraform/custom_flatten/json_to_string_map.go.erb @@ -0,0 +1,27 @@ +<%- # the license inside this block applies to this file + # 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. +-%> +func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + + kv := v.(map[string]interface{}) + + res := make(map[string]string) + for key, value := range kv { + res[key] = fmt.Sprintf("%v", value) + } + return res +} diff --git a/third_party/terraform/tests/resource_bigquery_data_transfer_config_test.go b/third_party/terraform/tests/resource_bigquery_data_transfer_config_test.go index 5640b9eceb10..8d4c31e0549c 100644 --- a/third_party/terraform/tests/resource_bigquery_data_transfer_config_test.go +++ b/third_party/terraform/tests/resource_bigquery_data_transfer_config_test.go @@ -14,8 +14,9 @@ import ( // but it will get deleted by parallel tests, so they need to be ran serially. func TestAccBigqueryDataTransferConfig(t *testing.T) { testCases := map[string]func(t *testing.T){ - "basic": testAccBigqueryDataTransferConfig_scheduledQuery_basic, - "update": testAccBigqueryDataTransferConfig_scheduledQuery_update, + "basic": testAccBigqueryDataTransferConfig_scheduledQuery_basic, + "update": testAccBigqueryDataTransferConfig_scheduledQuery_update, + "booleanParam": testAccBigqueryDataTransferConfig_copy_booleanParam, } for name, tc := range testCases { @@ -75,6 +76,27 @@ func testAccBigqueryDataTransferConfig_scheduledQuery_update(t *testing.T) { }) } +func testAccBigqueryDataTransferConfig_copy_booleanParam(t *testing.T) { + random_suffix := acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBigqueryDataTransferConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBigqueryDataTransferConfig_booleanParam(random_suffix), + }, + { + ResourceName: "google_bigquery_data_transfer_config.copy_config", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location"}, + }, + }, + }) +} + func testAccCheckBigqueryDataTransferConfigDestroy(s *terraform.State) error { for name, rs := range s.RootModule().Resources { if rs.Type != "google_bigquery_data_transfer_config" { @@ -135,3 +157,47 @@ resource "google_bigquery_data_transfer_config" "query_config" { } `, random_suffix, random_suffix, schedule, letter) } + +func testAccBigqueryDataTransferConfig_booleanParam(random_suffix string) string { + return fmt.Sprintf(` +data "google_project" "project" {} + +resource "google_project_iam_member" "permissions" { + role = "roles/iam.serviceAccountShortTermTokenMinter" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-bigquerydatatransfer.iam.gserviceaccount.com" +} + +resource "google_bigquery_dataset" "source_dataset" { + depends_on = [google_project_iam_member.permissions] + + dataset_id = "source_%s" + friendly_name = "foo" + description = "bar" + location = "asia-northeast1" +} + +resource "google_bigquery_dataset" "destination_dataset" { + depends_on = [google_project_iam_member.permissions] + + dataset_id = "destination_%s" + friendly_name = "foo" + description = "bar" + location = "asia-northeast1" +} + +resource "google_bigquery_data_transfer_config" "copy_config" { + depends_on = [google_project_iam_member.permissions] + + location = "asia-northeast1" + + display_name = "Copy test %s" + data_source_id = "cross_region_copy" + destination_dataset_id = google_bigquery_dataset.destination_dataset.dataset_id + params = { + overwrite_destination_table = "true" + source_dataset_id = google_bigquery_dataset.source_dataset.dataset_id + source_project_id = data.google_project.project.project_id + } +} +`, random_suffix, random_suffix, random_suffix) +} From f0623f2dc80695c89e0c9b3699209dffad1c2864 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Wed, 16 Oct 2019 13:00:57 -0700 Subject: [PATCH 27/87] [Ansible] Stop building deprecated facts modules in collections (#2476) Merged PR #2476. --- build/ansible | 2 +- provider/ansible.rb | 9 --------- templates/ansible/facts.erb | 9 +-------- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/build/ansible b/build/ansible index e5ab367a20e3..bc4c90ad080d 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit e5ab367a20e34b45076858cc2c1e45a8641b36b3 +Subproject commit bc4c90ad080d16e4ff8e402b6edd7a318541fa2e diff --git a/provider/ansible.rb b/provider/ansible.rb index 4bfca7a2e351..58a11aee5e90 100644 --- a/provider/ansible.rb +++ b/provider/ansible.rb @@ -290,15 +290,6 @@ def compile_datasource(data) File.join(target_folder, "plugins/modules/#{name}_info.py"), self) - - # Generate symlink for old `facts` modules. - return if version_added(data.object, :facts) >= '2.9' - - deprecated_facts_path = File.join(target_folder, - "plugins/modules/_#{name}_facts.py") - return if File.exist?(deprecated_facts_path) - - File.symlink "#{name}_info.py", deprecated_facts_path end def generate_objects(output_folder, types) diff --git a/templates/ansible/facts.erb b/templates/ansible/facts.erb index a7b16bbffc23..c7d7506672c4 100644 --- a/templates/ansible/facts.erb +++ b/templates/ansible/facts.erb @@ -26,9 +26,7 @@ DOCUMENTATION = ''' --- <%= ansible_style_yaml({ 'module' => "#{module_name(object)}_info", - 'description' => ["Gather info for GCP #{object.name}", - (version_added(object, :facts) < '2.9' ? "This module was called C(#{module_name(object)}_facts) before Ansible 2.9. The usage has not changed." : nil) - ].compact, + 'description' => ["Gather info for GCP #{object.name}"], 'short_description' => "Gather info for GCP #{object.name}", 'version_added' => version_added(object, :facts), 'author' => "Google Inc. (@googlecloudplatform)", @@ -91,11 +89,6 @@ def main(): })), 12) -%> ) -<% if version_added(object, :facts) < '2.9' -%> - - if module._name == '<%= module_name(object) -%>_facts': - module.deprecate("The '<%= module_name(object) -%>_facts' module has been renamed to '<%= module_name(object) -%>_info'", version='2.13') -<% end -%> if not module.params['scopes']: module.params['scopes'] = <%= python_literal(object.__product.scopes) %> From aa6b6881f41989c3e7cbd4f7a7019dbbdf49c4e9 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 16 Oct 2019 13:27:54 -0700 Subject: [PATCH 28/87] use IsStable in (R)IGM instead of CurrentActions (#2382) Merged PR #2382. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resource_compute_region_instance_group_manager.go.erb | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/terraform b/build/terraform index e7dac2b6e377..33af39fd11b6 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit e7dac2b6e377ad0cc01c00f69b4b365bff3de12d +Subproject commit 33af39fd11b6abaa8d5ece60f388dbb07c677d51 diff --git a/build/terraform-beta b/build/terraform-beta index 1eb072f64bcf..f90234310ce9 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 1eb072f64bcf7f80076f66dc83132b66e95732c9 +Subproject commit f90234310ce9678ac85195d83d85486b9598af79 diff --git a/third_party/terraform/resources/resource_compute_region_instance_group_manager.go.erb b/third_party/terraform/resources/resource_compute_region_instance_group_manager.go.erb index e78916d774a7..9459ebf1bad5 100644 --- a/third_party/terraform/resources/resource_compute_region_instance_group_manager.go.erb +++ b/third_party/terraform/resources/resource_compute_region_instance_group_manager.go.erb @@ -430,10 +430,10 @@ func waitForInstancesRefreshFunc(f getInstanceManagerFunc, d *schema.ResourceDat log.Printf("[WARNING] Error in fetching manager while waiting for instances to come up: %s\n", err) return nil, "error", err } - if done := m.CurrentActions.None; done < m.TargetSize { - return done, "creating", nil + if m.Status.IsStable { + return true, "created", nil } else { - return done, "created", nil + return false, "creating", nil } } } From fd5b4b180149476cadf4feb8445d20ecb8bd3cc2 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Wed, 16 Oct 2019 14:13:05 -0700 Subject: [PATCH 29/87] adding fields to compute#instance.accessConfigs[] (#2437) Merged PR #2437. --- build/ansible | 2 +- build/inspec | 2 +- products/compute/ansible_version_added.yaml | 12 +++++ products/compute/api.yaml | 50 +++++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/build/ansible b/build/ansible index bc4c90ad080d..e0d101beccb2 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit bc4c90ad080d16e4ff8e402b6edd7a318541fa2e +Subproject commit e0d101beccb2e021491ee749bde49dce1c5f0235 diff --git a/build/inspec b/build/inspec index 735f38f2505b..fcc076d77165 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 735f38f2505b6fdbeffb5ba0629051a8ac74992b +Subproject commit fcc076d77165d672797a5063a4501e4fb733118d diff --git a/products/compute/ansible_version_added.yaml b/products/compute/ansible_version_added.yaml index 125cd8ebfa3d..539e36710653 100644 --- a/products/compute/ansible_version_added.yaml +++ b/products/compute/ansible_version_added.yaml @@ -619,6 +619,12 @@ :version_added: '2.6' :type: :version_added: '2.6' + :setPublicPtr: + :version_added: '2.10' + :publicPtrDomainName: + :version_added: '2.10' + :networkTier: + :version_added: '2.10' :aliasIpRanges: :version_added: '2.6' :ipCidrRange: @@ -767,6 +773,12 @@ :version_added: '2.6' :type: :version_added: '2.6' + :setPublicPtr: + :version_added: '2.10' + :publicPtrDomainName: + :version_added: '2.10' + :networkTier: + :version_added: '2.10' :aliasIpRanges: :version_added: '2.6' :ipCidrRange: diff --git a/products/compute/api.yaml b/products/compute/api.yaml index c2b5ff879c46..47065b6558e7 100644 --- a/products/compute/api.yaml +++ b/products/compute/api.yaml @@ -3836,6 +3836,31 @@ objects: values: - :ONE_TO_ONE_NAT required: true + - !ruby/object:Api::Type::Boolean + name: 'setPublicPtr' + description: | + Specifies whether a public DNS PTR record should be + created to map the external IP address of the instance + to a DNS domain name. + - !ruby/object:Api::Type::String + name: 'publicPtrDomainName' + description: | + The DNS domain name for the public PTR record. You can + set this field only if the setPublicPtr field is + enabled. + - !ruby/object:Api::Type::Enum + name: 'networkTier' + description: | + This signifies the networking tier used for configuring + this access configuration. If an AccessConfig is + specified without a valid external IP address, an + ephemeral IP will be created with this networkTier. If an + AccessConfig with a valid external IP address is + specified, it must match that of the networkTier + associated with the Address resource owning that IP. + values: + - :PREMIUM + - :STANDARD - !ruby/object:Api::Type::Array name: 'aliasIpRanges' description: | @@ -4634,6 +4659,31 @@ objects: values: - :ONE_TO_ONE_NAT required: true + - !ruby/object:Api::Type::Boolean + name: 'setPublicPtr' + description: | + Specifies whether a public DNS PTR record should be + created to map the external IP address of the instance + to a DNS domain name. + - !ruby/object:Api::Type::String + name: 'publicPtrDomainName' + description: | + The DNS domain name for the public PTR record. You can + set this field only if the setPublicPtr field is + enabled. + - !ruby/object:Api::Type::Enum + name: 'networkTier' + description: | + This signifies the networking tier used for configuring + this access configuration. If an AccessConfig is + specified without a valid external IP address, an + ephemeral IP will be created with this networkTier. If an + AccessConfig with a valid external IP address is + specified, it must match that of the networkTier + associated with the Address resource owning that IP. + values: + - :PREMIUM + - :STANDARD - !ruby/object:Api::Type::Array name: 'aliasIpRanges' description: | From bb4342d60907f6e21245a37a3c4c49c038590099 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 16 Oct 2019 14:37:36 -0700 Subject: [PATCH 30/87] Add GKE Stackdriver Monitoring changes to upgrade guide (#2472) Merged PR #2472. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../docs/version_3_upgrade.html.markdown | 33 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index 33af39fd11b6..66e689ac6685 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 33af39fd11b6abaa8d5ece60f388dbb07c677d51 +Subproject commit 66e689ac66853db952f05c3ab9535e6a8a3357da diff --git a/build/terraform-beta b/build/terraform-beta index f90234310ce9..075403f9746f 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit f90234310ce9678ac85195d83d85486b9598af79 +Subproject commit 075403f9746f8677c08e50a2fdb6da358988eb38 diff --git a/third_party/terraform/website/docs/version_3_upgrade.html.markdown b/third_party/terraform/website/docs/version_3_upgrade.html.markdown index b4794acb005e..52907f077b41 100644 --- a/third_party/terraform/website/docs/version_3_upgrade.html.markdown +++ b/third_party/terraform/website/docs/version_3_upgrade.html.markdown @@ -50,6 +50,7 @@ so Terraform knows to manage them. ## Upgrade Topics +- [Resource: `google_container_cluster`](#resource-google_container_cluster) - [Resource: `google_project_services`](#resource-google_project_services) @@ -90,6 +91,38 @@ provider "google" { } ``` +## Resource: `google_container_cluster` + +### `logging_service` and `monitoring_service` defaults changed + +GKE Stackdriver Monitoring (the GKE-specific Stackdriver experience) is now +enabled at cluster creation by default, similar to the default in GKE `1.14` +through other tools. + +Terraform will now detect changes out of band when the field(s) are not defined +in config, attempting to return them to their new defaults, and will be clear +about what values will be set when creating a cluster. + +`terraform plan` will report changes upon upgrading if the field was previously +unset. Applying this change will enable the new Stackdriver service without +recreating clusters. Users who wish to use another value should record their +intended value in config; the old default values can be added to a +`google_container_cluster` resource config block to preserve them. + +#### Old Defaults + +```hcl +logging_service = "logging.googleapis.com" +monitoring_service = "monitoring.googleapis.com" +``` + +#### New Defaults + +```hcl +logging_service = "logging.googleapis.com/kubernetes" +monitoring_service = "monitoring.googleapis.com/kubernetes" +``` + ## Resource: `google_project_services` ### `google_project_services` has been removed from the provider From 13c7429a76c2a96ce63258b019ef396110178034 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 16 Oct 2019 14:43:03 -0700 Subject: [PATCH 31/87] Add support for one-field-at-a-time update to Subnetwork (#2475) Merged PR #2475. --- api/type.rb | 14 ++++++++ products/compute/ansible.yaml | 5 +++ products/compute/api.yaml | 9 +++-- .../ansible/service_account_key_template.erb | 2 +- .../helpers/ansible/object_template.erb | 2 +- provider/core.rb | 25 ++++++++++--- spec/provider_terraform_spec.rb | 21 +++++++++-- templates/ansible/resource.erb | 2 +- templates/terraform/resource.erb | 29 ++++++++++++--- .../tests/resource_compute_subnetwork_test.go | 35 +++++++++++++++++++ 10 files changed, 127 insertions(+), 17 deletions(-) diff --git a/api/type.rb b/api/type.rb index 9b67c2c530c0..4e1698ff2c6a 100644 --- a/api/type.rb +++ b/api/type.rb @@ -35,8 +35,20 @@ module Fields attr_reader :input # If set to true value is used only on creation attr_reader :url_param_only # If, true will not be send in request body attr_reader :required + attr_reader :update_verb attr_reader :update_url + # Some updates only allow updating certain fields at once (generally each + # top-level field can be updated one-at-a-time). If this is set, we group + # fields to update by (verb, url, fingerprint, id) instead of just + # (verb, url, fingerprint), to allow multiple fields to reuse the same + # endpoints. + attr_reader :update_id + # THe fingerprint value required to update this field. Downstreams should + # GET the resource and parse the fingerprint value while doing each update + # call. This ensures we can supply the fingerprint to each distinct + # request. + attr_reader :fingerprint_name # If true, we will include the empty value in requests made including # this attribute (both creates and updates). This rarely needs to be # set to true, and corresponds to both the "NullFields" and @@ -91,6 +103,8 @@ def validate default: @__resource&.update_verb check :update_url, type: ::String + check :update_id, type: ::String + check :fingerprint_name, type: ::String check :pattern, type: ::String check_default_value_property diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index d57496681b73..bf95933f6c79 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -272,6 +272,11 @@ overrides: !ruby/object:Overrides::ResourceOverrides * https://www.googleapis.com/compute/v1/projects/project/global/gateways/default-internet-gateway * projects/project/global/gateways/default-internet-gateway * global/gateways/default-internet-gateway + Subnetwork: !ruby/object:Overrides::Ansible::ResourceOverride + properties: + fingerprint: !ruby/object:Overrides::Ansible::PropertyOverride + update_verb: :PATCH + update_url: projects/{{project}}/regions/{{region}}/subnetworks/{{name}} TargetPool: !ruby/object:Overrides::Ansible::ResourceOverride transport: !ruby/object:Overrides::Ansible::Transport encoder: encode_request diff --git a/products/compute/api.yaml b/products/compute/api.yaml index 47065b6558e7..0582c47e414b 100644 --- a/products/compute/api.yaml +++ b/products/compute/api.yaml @@ -8722,14 +8722,15 @@ objects: Whether to enable flow logging for this subnetwork. update_verb: :PATCH update_url: projects/{{project}}/regions/{{region}}/subnetworks/{{name}} + update_id: 'enableFlowLogs' + fingerprint_name: 'fingerprint' send_empty_value: true + # TODO(rileykarson): Work with rambleraptor to remove this field from downstreams. - !ruby/object:Api::Type::Fingerprint name: 'fingerprint' description: | Fingerprint of this resource. This field is used internally during updates of this resource. - update_verb: :PATCH - update_url: projects/{{project}}/regions/{{region}}/subnetworks/{{name}} - !ruby/object:Api::Type::Enum name: 'purpose' min_version: beta @@ -8750,6 +8751,8 @@ objects: min_version: beta update_verb: :PATCH update_url: projects/{{project}}/regions/{{region}}/subnetworks/{{name}} + update_id: 'role' + fingerprint_name: 'fingerprint' values: - :ACTIVE - :BACKUP @@ -8768,6 +8771,8 @@ objects: to either primary or secondary ranges. update_verb: :PATCH update_url: projects/{{project}}/regions/{{region}}/subnetworks/{{name}} + update_id: 'secondaryIpRanges' + fingerprint_name: 'fingerprint' item_type: !ruby/object:Api::Type::NestedObject properties: - !ruby/object:Api::Type::String diff --git a/products/iam/helpers/ansible/service_account_key_template.erb b/products/iam/helpers/ansible/service_account_key_template.erb index 027b06d75607..0c9b69a3a261 100644 --- a/products/iam/helpers/ansible/service_account_key_template.erb +++ b/products/iam/helpers/ansible/service_account_key_template.erb @@ -21,7 +21,7 @@ __metaclass__ = type -%> <% - update_props = properties_by_custom_update(object.all_user_properties) + update_props = properties_by_custom_update(object.all_user_properties, :old) import = 'from ansible.module_utils.gcp_utils import navigate_hash, GcpSession, GcpModule, GcpRequest' import += ', remove_nones_from_dict' unless properties_with_classes(object.all_user_properties).empty? import += ', replace_resource_dict' if nonreadonly_rrefs(object) diff --git a/products/storage/helpers/ansible/object_template.erb b/products/storage/helpers/ansible/object_template.erb index 2b9d1b01c66b..1309077ccb9d 100644 --- a/products/storage/helpers/ansible/object_template.erb +++ b/products/storage/helpers/ansible/object_template.erb @@ -21,7 +21,7 @@ __metaclass__ = type -%> <% - update_props = properties_by_custom_update(object.all_user_properties) + update_props = properties_by_custom_update(object.all_user_properties, :old) import = 'from ansible.module_utils.gcp_utils import navigate_hash, GcpSession, GcpModule, GcpRequest' import += ', remove_nones_from_dict' unless properties_with_classes(object.all_user_properties).empty? import += ', replace_resource_dict' if nonreadonly_rrefs(object) diff --git a/provider/core.rb b/provider/core.rb index a67f1566caa6..8cf273f19ebf 100644 --- a/provider/core.rb +++ b/provider/core.rb @@ -282,12 +282,25 @@ def build_env # Filter the properties to keep only the ones requiring custom update # method and group them by update url & verb. - def properties_by_custom_update(properties) + def properties_by_custom_update(properties, behavior = :new) update_props = properties.reject do |p| p.update_url.nil? || p.update_verb.nil? || p.update_verb == :NOOP end - update_props.group_by do |p| - { update_url: p.update_url, update_verb: p.update_verb } + + # TODO(rambleraptor): Add support to Ansible for one-at-a-time updates. + if behavior == :old + update_props.group_by do |p| + { update_url: p.update_url, update_verb: p.update_verb } + end + else + update_props.group_by do |p| + { + update_url: p.update_url, + update_verb: p.update_verb, + update_id: p.update_id, + fingerprint_name: p.fingerprint_name + } + end end end @@ -295,9 +308,11 @@ def properties_by_custom_update(properties) # that can be updated at that URL. This allows flattened objects # to determine which parent property in the API should be updated with # the contents of the flattened object - def custom_update_properties_by_url(properties, update_url) + def custom_update_properties_by_key(properties, key) properties_by_custom_update(properties).select do |k, _| - k[:update_url] == update_url + k[:update_url] == key[:update_url] && + k[:update_id] == key[:update_id] && + k[:fingerprint_name] == key[:fingerprint_name] end.first.last # .first is to grab the element from the select which returns a list # .last is because properties_by_custom_update returns a list of diff --git a/spec/provider_terraform_spec.rb b/spec/provider_terraform_spec.rb index f7c1cba477ef..76fdff0f9e17 100644 --- a/spec/provider_terraform_spec.rb +++ b/spec/provider_terraform_spec.rb @@ -117,10 +117,25 @@ class << self it do is_expected.to eq( - { update_url: 'url1', update_verb: :POST } => + { + update_url: 'url1', + update_verb: :POST, + update_id: nil, + fingerprint_name: nil + } => [postUrl1, otherPostUrl1], - { update_url: 'url2', update_verb: :POST } => [postUrl2], - { update_url: 'url2', update_verb: :PUT } => [putUrl2] + { + update_url: 'url2', + update_verb: :POST, + update_id: nil, + fingerprint_name: nil + } => [postUrl2], + { + update_url: 'url2', + update_verb: :PUT, + update_id: nil, + fingerprint_name: nil + } => [putUrl2] ) end end diff --git a/templates/ansible/resource.erb b/templates/ansible/resource.erb index 26e6ce161c6f..ec4e12aafb53 100644 --- a/templates/ansible/resource.erb +++ b/templates/ansible/resource.erb @@ -19,7 +19,7 @@ __metaclass__ = type .map(&:resource_ref) .uniq - update_props = properties_by_custom_update(object.all_user_properties) + update_props = properties_by_custom_update(object.all_user_properties, :old) -%> <% diff --git a/templates/terraform/resource.erb b/templates/terraform/resource.erb index 88e4789967a3..0eaf2f530220 100644 --- a/templates/terraform/resource.erb +++ b/templates/terraform/resource.erb @@ -344,7 +344,28 @@ func resource<%= resource_name -%>Update(d *schema.ResourceData, meta interface{ <% properties_by_custom_update(object.root_properties).each do |key, props| -%> if <%= props.map { |prop| "d.HasChange(\"#{prop.name.underscore}\")" }.join ' || ' -%> { obj := make(map[string]interface{}) -<% custom_update_properties_by_url(properties, key[:update_url]).each do |prop| -%> + +<%- unless key[:fingerprint_name] == nil -%> + getUrl, err := replaceVars(d, config, "<%= "{{#{object.__product.name}BasePath}}#{object.self_link_uri}" -%>") + if err != nil { + return err + } + +<% if has_project -%> + project, err := getProject(d, config) + if err != nil { + return err + } +<% end -%> + getRes, err := sendRequest(config, "<%= object.read_verb.to_s.upcase -%>", <% if has_project %>project<% else %>""<% end %>, getUrl, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("<%= resource_name -%> %q", d.Id())) + } + + obj["<%= key[:fingerprint_name] %>"] = getRes["<%= key[:fingerprint_name] %>"] + +<% end # unless key[:fingerprint_name] -%> +<% custom_update_properties_by_key(properties, key).each do |prop| -%> <% schemaPrefix = prop.flatten_object ? "nil" : "d.Get( \"#{prop.name.underscore}\" )" -%> <%= prop.api_name -%>Prop, err := expand<%= resource_name -%><%= titlelize_property(prop) -%>(<%= schemaPrefix -%>, d, config) if err != nil { @@ -398,13 +419,13 @@ if <%= props.map { |prop| "d.HasChange(\"#{prop.name.underscore}\")" }.join ' || } <% if !object.async.nil? && object.async.allow?('update') -%> -<% if object.autogen_async -%> +<% if object.autogen_async -%> err = <%= client_name_camel -%>OperationWaitTime( config, res, <% if has_project -%> project, <% end -%> "Updating <%= object.name -%>", int(d.Timeout(schema.TimeoutUpdate).Minutes())) -<% else -%> +<% else -%> op := &<%= client_name_lower -%>.Operation{} err = Convert(res, op) if err != nil { @@ -415,7 +436,7 @@ if <%= props.map { |prop| "d.HasChange(\"#{prop.name.underscore}\")" }.join ' || config.client<%= client_name_pascal -%>, op, <% if has_project -%> project, <% end -%> "Updating <%= object.name -%>", int(d.Timeout(schema.TimeoutUpdate).Minutes())) -<% end -%> +<% end -%> if err != nil { return err } diff --git a/third_party/terraform/tests/resource_compute_subnetwork_test.go b/third_party/terraform/tests/resource_compute_subnetwork_test.go index 20441163f349..c3294c5343f0 100644 --- a/third_party/terraform/tests/resource_compute_subnetwork_test.go +++ b/third_party/terraform/tests/resource_compute_subnetwork_test.go @@ -123,6 +123,19 @@ func TestAccComputeSubnetwork_update(t *testing.T) { "google_compute_subnetwork.network-with-private-google-access", &subnetwork), ), }, + { + // Add a secondary range and enable flow logs at once + Config: testAccComputeSubnetwork_update3(cnName, "10.2.0.0/24", subnetworkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSubnetworkExists( + "google_compute_subnetwork.network-with-private-google-access", &subnetwork), + ), + }, + { + ResourceName: "google_compute_subnetwork.network-with-private-google-access", + ImportState: true, + ImportStateVerify: true, + }, }, }) @@ -356,6 +369,28 @@ resource "google_compute_subnetwork" "network-with-private-google-access" { `, cnName, subnetworkName, cidrRange) } +func testAccComputeSubnetwork_update3(cnName, cidrRange, subnetworkName string) string { + return fmt.Sprintf(` +resource "google_compute_network" "custom-test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "network-with-private-google-access" { + name = "%s" + ip_cidr_range = "%s" + region = "us-central1" + network = "${google_compute_network.custom-test.self_link}" + + enable_flow_logs = true + secondary_ip_range { + range_name = "tf-test-secondary-range-update" + ip_cidr_range = "192.168.10.0/24" + } +} +`, cnName, subnetworkName, cidrRange) +} + func testAccComputeSubnetwork_secondaryIpRanges_update1(cnName, subnetworkName string) string { return fmt.Sprintf(` resource "google_compute_network" "custom-test" { From 50d6d1ca0fa65e3492ab4d6662fd300b2b636898 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 16 Oct 2019 16:27:05 -0700 Subject: [PATCH 32/87] Remove built-in version from service client basepaths (#2483) Merged PR #2483. --- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- third_party/terraform/utils/config.go.erb | 14 +++++++------- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build/terraform b/build/terraform index 66e689ac6685..f058c31cf97d 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 66e689ac66853db952f05c3ab9535e6a8a3357da +Subproject commit f058c31cf97df1f2dcb04306334668ff332e0c19 diff --git a/build/terraform-beta b/build/terraform-beta index 075403f9746f..2e8d2b2075f3 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 075403f9746f8677c08e50a2fdb6da358988eb38 +Subproject commit 2e8d2b2075f307751114375e1def035c89e9db6c diff --git a/build/terraform-mapper b/build/terraform-mapper index 1d5c7659d772..a5b16ba9fbb0 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 1d5c7659d77251835a1d1430561520189aad38b4 +Subproject commit a5b16ba9fbb0853df76e12bece176aa93df56f61 diff --git a/third_party/terraform/utils/config.go.erb b/third_party/terraform/utils/config.go.erb index d6d507dff358..e08703748e80 100644 --- a/third_party/terraform/utils/config.go.erb +++ b/third_party/terraform/utils/config.go.erb @@ -231,7 +231,7 @@ func (c *Config) LoadAndValidate() error { // while most only want the host URL, some older ones also want the version and some // of those "projects" as well. You can find out if this is required by looking at // the basePath value in the client library file. - computeClientBasePath := removeBasePathVersion(c.ComputeBasePath) + "v1/projects/" + computeClientBasePath := c.ComputeBasePath + "projects/" log.Printf("[INFO] Instantiating GCE client for path %s", computeClientBasePath) c.clientCompute, err = compute.NewService(context, option.WithHTTPClient(client)) if err != nil { @@ -240,7 +240,7 @@ func (c *Config) LoadAndValidate() error { c.clientCompute.UserAgent = userAgent c.clientCompute.BasePath = computeClientBasePath - computeBetaClientBasePath := removeBasePathVersion(c.ComputeBetaBasePath) + "beta/projects/" + computeBetaClientBasePath := c.ComputeBetaBasePath + "projects/" log.Printf("[INFO] Instantiating GCE Beta client for path %s", computeBetaClientBasePath) c.clientComputeBeta, err = computeBeta.NewService(context, option.WithHTTPClient(client)) if err != nil { @@ -267,7 +267,7 @@ func (c *Config) LoadAndValidate() error { c.clientContainerBeta.UserAgent = userAgent c.clientContainerBeta.BasePath = containerBetaClientBasePath - dnsClientBasePath := removeBasePathVersion(c.DNSBasePath) + "v1/projects/" + dnsClientBasePath := c.DNSBasePath + "projects/" log.Printf("[INFO] Instantiating Google Cloud DNS client for path %s", dnsClientBasePath) c.clientDns, err = dns.NewService(context, option.WithHTTPClient(client)) if err != nil { @@ -276,7 +276,7 @@ func (c *Config) LoadAndValidate() error { c.clientDns.UserAgent = userAgent c.clientDns.BasePath = dnsClientBasePath - dnsBetaClientBasePath := removeBasePathVersion(c.DnsBetaBasePath) + "v1beta2/projects/" + dnsBetaClientBasePath := c.DnsBetaBasePath + "projects/" log.Printf("[INFO] Instantiating Google Cloud DNS Beta client for path %s", dnsBetaClientBasePath) c.clientDnsBeta, err = dnsBeta.NewService(context, option.WithHTTPClient(client)) if err != nil { @@ -303,7 +303,7 @@ func (c *Config) LoadAndValidate() error { c.clientLogging.UserAgent = userAgent c.clientLogging.BasePath = loggingClientBasePath - storageClientBasePath := removeBasePathVersion(c.StorageBasePath) + "v1/" + storageClientBasePath := c.StorageBasePath log.Printf("[INFO] Instantiating Google Storage client for path %s", storageClientBasePath) c.clientStorage, err = storage.NewService(context, option.WithHTTPClient(client)) if err != nil { @@ -312,7 +312,7 @@ func (c *Config) LoadAndValidate() error { c.clientStorage.UserAgent = userAgent c.clientStorage.BasePath = storageClientBasePath - sqlClientBasePath := removeBasePathVersion(c.SQLBasePath) + "v1beta4/" + sqlClientBasePath := c.SQLBasePath log.Printf("[INFO] Instantiating Google SqlAdmin client for path %s", sqlClientBasePath) c.clientSqlAdmin, err = sqladmin.NewService(context, option.WithHTTPClient(client)) if err != nil { @@ -432,7 +432,7 @@ func (c *Config) LoadAndValidate() error { c.clientBuild.UserAgent = userAgent c.clientBuild.BasePath = cloudBuildClientBasePath - bigQueryClientBasePath := removeBasePathVersion(c.BigQueryBasePath) + "v2/" + bigQueryClientBasePath := c.BigQueryBasePath log.Printf("[INFO] Instantiating Google Cloud BigQuery client for path %s", bigQueryClientBasePath) c.clientBigQuery, err = bigquery.NewService(context, option.WithHTTPClient(client)) if err != nil { From eb875a443a629b4e7d29b401830900753fb90b2e Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 16 Oct 2019 16:45:00 -0700 Subject: [PATCH 33/87] Add note about using 2.13.0 in google_project_services deprecation messages (#2480) Merged PR #2480. --- .../terraform/resources/resource_google_project_services.go | 3 ++- .../terraform/website/docs/version_3_upgrade.html.markdown | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/third_party/terraform/resources/resource_google_project_services.go b/third_party/terraform/resources/resource_google_project_services.go index e31f85d0a8a8..30c8a0eb11d5 100644 --- a/third_party/terraform/resources/resource_google_project_services.go +++ b/third_party/terraform/resources/resource_google_project_services.go @@ -26,7 +26,8 @@ func resourceGoogleProjectServices() *schema.Resource { DeprecationMessage: "google_project_services is deprecated - many users reported " + "issues with dependent services that were not resolvable. Please use google_project_service or the " + "https://github.com/terraform-google-modules/terraform-google-project-factory/tree/master/modules/project_services" + - " module. This resource will be removed in version 3.0.0.", + " module. It's recommended that you use a provider version of 2.13.0 or higher when you migrate so that requests are" + + " batched to the API, reducing the request rate. This resource will be removed in version 3.0.0.", Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(20 * time.Minute), diff --git a/third_party/terraform/website/docs/version_3_upgrade.html.markdown b/third_party/terraform/website/docs/version_3_upgrade.html.markdown index 52907f077b41..201bcc10dce5 100644 --- a/third_party/terraform/website/docs/version_3_upgrade.html.markdown +++ b/third_party/terraform/website/docs/version_3_upgrade.html.markdown @@ -147,6 +147,11 @@ Users should migrate to using `google_project_service` resources, or using the [`"terraform-google-modules/project-factory/google//modules/project_services"`](https://registry.terraform.io/modules/terraform-google-modules/project-factory/google/3.3.0/submodules/project_services) module for a similar interface to `google_project_services`. +-> Prior to `2.13.0`, each `google_project_service` sent separate API enablement +requests. From `2.13.0` onwards, those requests are batched. It's recommended +that you upgrade to `2.13.0+` before migrating if you encounter quota issues +when you migrate off `google_project_services`. + #### Old Config ```hcl From d76cd0db374c3e7454e15e323578c6d5dc2fd783 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Thu, 17 Oct 2019 10:07:40 -0700 Subject: [PATCH 34/87] Clear members if iam_binding doesn't find a binding (#2481) Merged PR #2481. --- build/terraform | 2 +- build/terraform-beta | 2 +- third_party/terraform/resources/resource_iam_binding.go.erb | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index f058c31cf97d..22f774c05d77 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit f058c31cf97df1f2dcb04306334668ff332e0c19 +Subproject commit 22f774c05d773cb254eb6e5c2d2f6d0e5432c7a5 diff --git a/build/terraform-beta b/build/terraform-beta index 2e8d2b2075f3..c21bc1e1d76e 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 2e8d2b2075f307751114375e1def035c89e9db6c +Subproject commit c21bc1e1d76e7426f9d2167139ad028123563c45 diff --git a/third_party/terraform/resources/resource_iam_binding.go.erb b/third_party/terraform/resources/resource_iam_binding.go.erb index b76e027f4cb0..14fb00e6f516 100644 --- a/third_party/terraform/resources/resource_iam_binding.go.erb +++ b/third_party/terraform/resources/resource_iam_binding.go.erb @@ -107,6 +107,7 @@ func resourceIamBindingRead(newUpdaterFunc newResourceIamUpdaterFunc) schema.Rea 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()) d.Set("role", eBinding.Role) + d.Set("members", nil) return nil } else { d.Set("role", binding.Role) From ac212c3b948a080cdc1e4e709143d205a1e0ef78 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 17 Oct 2019 16:59:23 -0700 Subject: [PATCH 35/87] Correct a bad assumption about versions made in instance template (#2487) Merged PR #2487. --- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- .../resource_compute_instance_template.go | 12 +++----- third_party/terraform/utils/image.go | 28 ++++++++++--------- 5 files changed, 22 insertions(+), 24 deletions(-) diff --git a/build/terraform b/build/terraform index 22f774c05d77..7ba8fef5fc0e 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 22f774c05d773cb254eb6e5c2d2f6d0e5432c7a5 +Subproject commit 7ba8fef5fc0e41b9442a263d2c7716a51ef1dc20 diff --git a/build/terraform-beta b/build/terraform-beta index c21bc1e1d76e..f9c2705788ec 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit c21bc1e1d76e7426f9d2167139ad028123563c45 +Subproject commit f9c2705788ec0273a269fb149a676ca9077229e3 diff --git a/build/terraform-mapper b/build/terraform-mapper index a5b16ba9fbb0..af8ba4e287cf 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit a5b16ba9fbb0853df76e12bece176aa93df56f61 +Subproject commit af8ba4e287cf4b1f5b631dc16ed7b74fb4aff136 diff --git a/third_party/terraform/resources/resource_compute_instance_template.go b/third_party/terraform/resources/resource_compute_instance_template.go index 65e8018c85f6..07607e05c143 100644 --- a/third_party/terraform/resources/resource_compute_instance_template.go +++ b/third_party/terraform/resources/resource_compute_instance_template.go @@ -514,7 +514,7 @@ func resourceComputeInstanceTemplateSourceImageCustomizeDiff(diff *schema.Resour if err != nil { return err } - oldResolved, err = resolvedImageSelfLink(project, oldResolved) + oldResolved, err = resolveImageRefToRelativeURI(project, oldResolved) if err != nil { return err } @@ -522,7 +522,7 @@ func resourceComputeInstanceTemplateSourceImageCustomizeDiff(diff *schema.Resour if err != nil { return err } - newResolved, err = resolvedImageSelfLink(project, newResolved) + newResolved, err = resolveImageRefToRelativeURI(project, newResolved) if err != nil { return err } @@ -781,13 +781,9 @@ func flattenDisk(disk *computeBeta.AttachedDisk, defaultProject string) (map[str diskMap := make(map[string]interface{}) if disk.InitializeParams != nil { if disk.InitializeParams.SourceImage != "" { - selfLink, err := resolvedImageSelfLink(defaultProject, disk.InitializeParams.SourceImage) + path, err := resolveImageRefToRelativeURI(defaultProject, disk.InitializeParams.SourceImage) if err != nil { - return nil, errwrap.Wrapf("Error expanding source image input to self_link: {{err}}", err) - } - path, err := getRelativePath(selfLink) - if err != nil { - return nil, errwrap.Wrapf("Error getting relative path for source image: {{err}}", err) + return nil, errwrap.Wrapf("Error expanding source image input to relative URI: {{err}}", err) } diskMap["source_image"] = path } else { diff --git a/third_party/terraform/utils/image.go b/third_party/terraform/utils/image.go index 6ea945c199d6..920edcbcf731 100644 --- a/third_party/terraform/utils/image.go +++ b/third_party/terraform/utils/image.go @@ -197,38 +197,40 @@ func resolveImage(c *Config, project, name string) (string, error) { return "", fmt.Errorf("Could not find image or family %s", name) } -// resolvedImageSelfLink takes the output of resolveImage and coerces it into a self_link. -// In the event that a global/images/IMAGE or global/images/family/FAMILY reference is -// returned from resolveImage, providerProject will be used as the project for the self_link. -func resolvedImageSelfLink(providerProject, name string) (string, error) { +// resolveImageRefToRelativeURI takes the output of resolveImage and coerces it +// into a relative URI. In the event that a global/images/IMAGE or +// global/images/family/FAMILY reference is returned from resolveImage, +// providerProject will be used as the project for the self_link. +func resolveImageRefToRelativeURI(providerProject, name string) (string, error) { switch { case resolveImageLink.MatchString(name): // https://www.googleapis.com/compute/v1/projects/xyz/global/images/xyz - return name, nil - case resolveImageProjectImage.MatchString(name): // projects/xyz/global/images/xyz - res := resolveImageProjectImage.FindStringSubmatch(name) - if err := sanityTestRegexMatches(2, res, "project image", name); err != nil { + namePath, err := getRelativePath(name) + if err != nil { return "", err } - return fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/global/images/%s", res[1], res[2]), nil + + return namePath, nil + case resolveImageProjectImage.MatchString(name): // projects/xyz/global/images/xyz + return name, nil case resolveImageProjectFamily.MatchString(name): // projects/xyz/global/images/family/xyz res := resolveImageProjectFamily.FindStringSubmatch(name) if err := sanityTestRegexMatches(2, res, "project family", name); err != nil { return "", err } - return fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/global/images/family/%s", res[1], res[2]), nil + return fmt.Sprintf("projects/%s/global/images/family/%s", res[1], res[2]), nil case resolveImageGlobalImage.MatchString(name): // global/images/xyz res := resolveImageGlobalImage.FindStringSubmatch(name) if err := sanityTestRegexMatches(1, res, "global image", name); err != nil { return "", err } - return fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/global/images/%s", providerProject, res[1]), nil + return fmt.Sprintf("projects/%s/global/images/%s", providerProject, res[1]), nil case resolveImageGlobalFamily.MatchString(name): // global/images/family/xyz res := resolveImageGlobalFamily.FindStringSubmatch(name) if err := sanityTestRegexMatches(1, res, "global family", name); err != nil { return "", err } - return fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/global/images/family/%s", providerProject, res[1]), nil + return fmt.Sprintf("projects/%s/global/images/family/%s", providerProject, res[1]), nil } - return "", fmt.Errorf("Could not expand image or family %q into a self_link", name) + return "", fmt.Errorf("Could not expand image or family %q into a relative URI", name) } From a85456787163e8879a32c6fa0e60864d1145a0c0 Mon Sep 17 00:00:00 2001 From: Experience Coder Date: Fri, 18 Oct 2019 13:15:53 -0400 Subject: [PATCH 36/87] Terraform AppEngine Dispatch Resource (#2466) * Dispatch URL change * Dispatch URL change * Update third_party/terraform/website-compiled/google.erb Co-Authored-By: Riley Karson * Update third_party/terraform/website-compiled/google.erb Co-Authored-By: Riley Karson * Added Sync and tested * Dispatch URL change * Update third_party/terraform/website-compiled/google.erb Co-Authored-By: Riley Karson * Update third_party/terraform/website-compiled/google.erb Co-Authored-By: Riley Karson * Added Sync and tested * Added Mutex * changed mutex and also added depend_on * Added failing Destroy one * Added failing Destroy one * Added failing Destroy one * updated custome delete * Removed custome delete * Update products/appengine/terraform.yaml Co-Authored-By: Riley Karson * Update products/appengine/api.yaml Co-Authored-By: Riley Karson * Update products/appengine/api.yaml Co-Authored-By: Riley Karson * Update templates/terraform/examples/app_engine_application_url_dispatch_rules_basic.tf.erb Co-Authored-By: Riley Karson * Update templates/terraform/examples/app_engine_application_url_dispatch_rules_basic.tf.erb Co-Authored-By: Riley Karson * Fixed custom-check-distroy file name * Update products/appengine/api.yaml Co-Authored-By: Riley Karson --- products/appengine/ansible.yaml | 2 + products/appengine/api.yaml | 57 +++++++++++++++++++ products/appengine/inspec.yaml | 2 + products/appengine/terraform.yaml | 17 +++++- ...engine_version.go.erb => appengine.go.erb} | 0 ...pplication_url_dispatch_rules_basic.tf.erb | 43 ++++++++++++++ .../terraform/website-compiled/google.erb | 3 + 7 files changed, 122 insertions(+), 2 deletions(-) rename templates/terraform/custom_check_destroy/{appengine_version.go.erb => appengine.go.erb} (100%) create mode 100644 templates/terraform/examples/app_engine_application_url_dispatch_rules_basic.tf.erb diff --git a/products/appengine/ansible.yaml b/products/appengine/ansible.yaml index 5f4928d134d7..c540de908491 100644 --- a/products/appengine/ansible.yaml +++ b/products/appengine/ansible.yaml @@ -20,6 +20,8 @@ datasources: !ruby/object:Overrides::ResourceOverrides DomainMapping: !ruby/object:Overrides::Ansible::ResourceOverride exclude: true overrides: !ruby/object:Overrides::ResourceOverrides + ApplicationUrlDispatchRules: !ruby/object:Overrides::Ansible::ResourceOverride + exclude: true FirewallRule: !ruby/object:Overrides::Ansible::ResourceOverride # Because of lack of state, Ansible treats identity # differently than Terraform. diff --git a/products/appengine/api.yaml b/products/appengine/api.yaml index 347e7819b4fb..d33bfd8087bc 100644 --- a/products/appengine/api.yaml +++ b/products/appengine/api.yaml @@ -446,3 +446,60 @@ objects: Instance class that is used to run this version. Valid values are AutomaticScaling F1, F2, F4, F4_1G (Only AutomaticScaling is supported at the moment) + - !ruby/object:Api::Resource + name: 'ApplicationUrlDispatchRules' + description: | + Rules to match an HTTP request and dispatch that request to a service. + base_url: 'apps/{{project}}' + create_url: 'apps/{{project}}?updateMask=dispatch_rules' + create_verb: :PATCH + delete_url: 'apps/{{project}}?updateMask=dispatch_rules' + delete_verb: :PATCH + update_url: 'apps/{{project}}?updateMask=dispatch_rules' + update_verb: :PATCH + references: !ruby/object:Api::Resource::ReferenceLinks + api: 'https://cloud.google.com/appengine/docs/admin-api/reference/rest/v1/apps#UrlDispatchRule' + async: !ruby/object:Api::Async + operation: !ruby/object:Api::Async::Operation + path: 'name' + base_url: '{{op_id}}' + wait_ms: 1000 + result: !ruby/object:Api::Async::Result + path: 'response' + status: !ruby/object:Api::Async::Status + path: 'status' + complete: 'DONE' + allowed: + - 'PENDING' + - 'RUNNING' + - 'DONE' + error: !ruby/object:Api::Async::Error + path: 'error/errors' + message: 'message' + properties: + - !ruby/object:Api::Type::Array + name: 'dispatchRules' + required: true + description: | + Rules to match an HTTP request and dispatch that request to a service. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'domain' + default_value: '*' + description: | + Domain name to match against. The wildcard "*" is supported if specified before a period: "*.". + Defaults to matching all domains: "*". + - !ruby/object:Api::Type::String + name: 'path' + description: | + Pathname within the host. Must start with a "/". A single "*" can be included at the end of the path. + The sum of the lengths of the domain and path may not exceed 100 characters. + required: true + - !ruby/object:Api::Type::String + name: 'service' + description: | + Pathname within the host. Must start with a "/". A single "*" can be included at the end of the path. + The sum of the lengths of the domain and path may not exceed 100 characters. + required: true + diff --git a/products/appengine/inspec.yaml b/products/appengine/inspec.yaml index 70f55cc33388..44be027a00e9 100644 --- a/products/appengine/inspec.yaml +++ b/products/appengine/inspec.yaml @@ -14,6 +14,8 @@ --- !ruby/object:Provider::Inspec::Config legacy_name: appengine overrides: !ruby/object:Overrides::ResourceOverrides + ApplicationUrlDispatchRules: !ruby/object:Overrides::Inspec::ResourceOverride + exclude: true DomainMapping: !ruby/object:Overrides::Inspec::ResourceOverride exclude: true FirewallRule: !ruby/object:Overrides::Inspec::ResourceOverride diff --git a/products/appengine/terraform.yaml b/products/appengine/terraform.yaml index 506900f045d9..2dcbb57e9843 100644 --- a/products/appengine/terraform.yaml +++ b/products/appengine/terraform.yaml @@ -27,7 +27,7 @@ overrides: !ruby/object:Overrides::ResourceOverrides StandardAppVersion: !ruby/object:Overrides::Terraform::ResourceOverride id_format: "apps/{{project}}/services/{{service}}/versions/{{version_id}}" import_format: ["apps/{{project}}/services/{{service}}/versions/{{version_id}}"] - mutex: "apps/{{project}}/services/{{service}}" + mutex: "apps/{{project}}" parameters: service: !ruby/object:Overrides::Terraform::PropertyOverride default_from_api: true @@ -43,7 +43,7 @@ overrides: !ruby/object:Overrides::ResourceOverrides If set to `true`, the service will be deleted if it is the last version. custom_code: !ruby/object:Provider::Terraform::CustomCode custom_delete: templates/terraform/custom_delete/appversion_delete.go.erb - test_check_destroy: templates/terraform/custom_check_destroy/appengine_version.go.erb + test_check_destroy: templates/terraform/custom_check_destroy/appengine.go.erb properties: id: !ruby/object:Overrides::Terraform::PropertyOverride name: 'version_id' @@ -93,6 +93,19 @@ overrides: !ruby/object:Overrides::ResourceOverrides update_mask_fields: - "ssl_settings.certificate_id" - "ssl_settings.ssl_management_type" + ApplicationUrlDispatchRules: !ruby/object:Overrides::Terraform::ResourceOverride + id_format: "{{project}}" + import_format: ["{{project}}"] + mutex: "apps/{{project}}" + custom_code: !ruby/object:Provider::Terraform::CustomCode + test_check_destroy: templates/terraform/custom_check_destroy/appengine.go.erb + examples: + - !ruby/object:Provider::Terraform::Examples + name: "app_engine_application_url_dispatch_rules_basic" + primary_resource_id: "service_rules" + vars: + project: "my-project" + bucket_name: "appengine-test-bucket" # This is for copying files over files: !ruby/object:Provider::Config::Files diff --git a/templates/terraform/custom_check_destroy/appengine_version.go.erb b/templates/terraform/custom_check_destroy/appengine.go.erb similarity index 100% rename from templates/terraform/custom_check_destroy/appengine_version.go.erb rename to templates/terraform/custom_check_destroy/appengine.go.erb 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 new file mode 100644 index 000000000000..7fc7237b0b5c --- /dev/null +++ b/templates/terraform/examples/app_engine_application_url_dispatch_rules_basic.tf.erb @@ -0,0 +1,43 @@ +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" "myapp_v1" { + version_id = "v1" + service = "myapp" + 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" + } + } + env_variables = { + port = "8080" + } + depends_on = ["google_storage_bucket_object.object"] + +} + +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}" + } +} diff --git a/third_party/terraform/website-compiled/google.erb b/third_party/terraform/website-compiled/google.erb index b57105ae4d61..57728087c777 100644 --- a/third_party/terraform/website-compiled/google.erb +++ b/third_party/terraform/website-compiled/google.erb @@ -235,6 +235,9 @@ > google_app_engine_standard_app_version + > + google_app_engine_application_url_dispatch_rules + From 70348d62ba917201805678f4ed959e967935d501 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Fri, 18 Oct 2019 10:30:48 -0700 Subject: [PATCH 37/87] Compare relpath and not by version in compute ig (#2493) Merged PR #2493. --- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- .../terraform/resources/resource_compute_instance_group.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/terraform b/build/terraform index 7ba8fef5fc0e..1ed9f43abe5a 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 7ba8fef5fc0e41b9442a263d2c7716a51ef1dc20 +Subproject commit 1ed9f43abe5a1118143bc460594341338ffc598d diff --git a/build/terraform-beta b/build/terraform-beta index f9c2705788ec..0574383c5b92 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit f9c2705788ec0273a269fb149a676ca9077229e3 +Subproject commit 0574383c5b921b76b03bbb3a0791763f8e3155d6 diff --git a/build/terraform-mapper b/build/terraform-mapper index af8ba4e287cf..4700f1232f79 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit af8ba4e287cf4b1f5b631dc16ed7b74fb4aff136 +Subproject commit 4700f1232f799d729d3422bb3e51055522ec4fb0 diff --git a/third_party/terraform/resources/resource_compute_instance_group.go b/third_party/terraform/resources/resource_compute_instance_group.go index d4f9ad3542a9..176a16a2518e 100644 --- a/third_party/terraform/resources/resource_compute_instance_group.go +++ b/third_party/terraform/resources/resource_compute_instance_group.go @@ -49,7 +49,7 @@ func resourceComputeInstanceGroup() *schema.Resource { Optional: true, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, + Set: selfLinkRelativePathHash, }, "named_port": { From 72c6dd8a5c9eb65ad769e7996f26c0b244cb4b84 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Fri, 18 Oct 2019 10:55:41 -0700 Subject: [PATCH 38/87] Fix some TF diff issues in MM 3.0.0 branch. (#2491) Merged PR #2491. --- .ci/ci.yml.tmpl | 4 ++-- .ci/magic-modules/diff-terraform.sh | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.ci/ci.yml.tmpl b/.ci/ci.yml.tmpl index 0c09f3c37a1f..de645d661533 100644 --- a/.ci/ci.yml.tmpl +++ b/.ci/ci.yml.tmpl @@ -192,7 +192,7 @@ jobs: - put: {{v.short_name}}-intermediate params: - repository: terraform-diff/new + repository: terraform-diff/{{k}}/new branch_file: magic-modules-branched/branchname force: true get_params: @@ -200,7 +200,7 @@ jobs: - put: {{v.short_name}}-intermediate params: - repository: terraform-diff/old + repository: terraform-diff/{{k}}/old branch_file: magic-modules-previous/branchname force: true get_params: diff --git a/.ci/magic-modules/diff-terraform.sh b/.ci/magic-modules/diff-terraform.sh index acc398f625ac..596ea881a016 100755 --- a/.ci/magic-modules/diff-terraform.sh +++ b/.ci/magic-modules/diff-terraform.sh @@ -82,5 +82,7 @@ for mm_dir in magic-modules-branched magic-modules-previous; do done -git clone "magic-modules-branched/build/$SHORT_NAME" "./terraform-diff/new" -git clone "magic-modules-previous/build/$SHORT_NAME" "./terraform-diff/old" +mkdir "./terraform-diff/$VERSION" + +git clone "magic-modules-branched/build/$SHORT_NAME" "./terraform-diff/$VERSION/new" +git clone "magic-modules-previous/build/$SHORT_NAME" "./terraform-diff/$VERSION/old" From d2093ddd5fc4b14909d180d600e28f07ab050c23 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Mon, 21 Oct 2019 11:01:09 -0700 Subject: [PATCH 39/87] 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 40/87] 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 41/87] 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 42/87] 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 43/87] 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 44/87] 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 45/87] 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 46/87] 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 47/87] 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 48/87] 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 49/87] 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 50/87] 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 51/87] 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 52/87] 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 53/87] 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 54/87] 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 55/87] 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 56/87] 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 57/87] 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 58/87] 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 59/87] 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 60/87] 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 61/87] 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 62/87] 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 63/87] 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 64/87] 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 65/87] 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 66/87] 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 67/87] 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 68/87] 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 69/87] 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 70/87] 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 71/87] 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 72/87] 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 73/87] 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 74/87] 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 75/87] 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 From 097c1f1af50668d1a3263374dd13190eabd197ef Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Mon, 28 Oct 2019 11:34:15 -0700 Subject: [PATCH 76/87] Eliminate overwriting of GKE maintenance exclusions, and prevent diff. (#2548) Merged PR #2548. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../resource_container_cluster.go.erb | 31 ++++++++++++++++--- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/build/terraform b/build/terraform index b86a93f8ffc2..2c36238a2e69 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit b86a93f8ffc2909f729afb0fbd3793a2a489cb24 +Subproject commit 2c36238a2e69911d3a7f83925357e06486980168 diff --git a/build/terraform-beta b/build/terraform-beta index 67c66aad64f7..d316f5408f59 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 67c66aad64f73b68bbf71551db3393ffb101b695 +Subproject commit d316f5408f5980901436ae7cab1e59f188e95edc diff --git a/third_party/terraform/resources/resource_container_cluster.go.erb b/third_party/terraform/resources/resource_container_cluster.go.erb index 3f600bc5aed2..a6ad01425e38 100644 --- a/third_party/terraform/resources/resource_container_cluster.go.erb +++ b/third_party/terraform/resources/resource_container_cluster.go.erb @@ -57,6 +57,19 @@ func validateRFC3339Date(v interface{}, k string) (warnings []string, errors []e } return } + +func rfc5545RecurrenceDiffSuppress(k, o, n string, d *schema.ResourceData) bool { + // This diff gets applied in the cloud console if you specify + // "FREQ=DAILY" in your config and add a maintenance exclusion. + if o == "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA,SU" && n == "FREQ=DAILY" { + return true + } + // Writing a full diff suppress for identical recurrences would be + // complex and error-prone - it's not a big problem if a user + // changes the recurrence and it's textually difference but semantically + // identical. + return false +} <% end %> func resourceContainerCluster() *schema.Resource { @@ -443,6 +456,7 @@ func resourceContainerCluster() *schema.Resource { "recurrence": { Type: schema.TypeString, Required: true, + DiffSuppressFunc: rfc5545RecurrenceDiffSuppress, }, }, }, @@ -2271,19 +2285,26 @@ func expandMaintenancePolicy(d *schema.ResourceData, meta interface{}) *containe 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 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 } + exclusions := make(map[string]containerBeta.TimeWindow, 0) + if cluster != nil && cluster.MaintenancePolicy != nil && cluster.MaintenancePolicy.Window != nil { + exclusions = cluster.MaintenancePolicy.Window.MaintenanceExclusions + } configured := d.Get("maintenance_policy") l := configured.([]interface{}) if len(l) == 0 || l[0] == nil { return &containerBeta.MaintenancePolicy{ ResourceVersion: resourceVersion, + Window: &containerBeta.MaintenanceWindow{ + MaintenanceExclusions: exclusions, + }, } } maintenancePolicy := l[0].(map[string]interface{}) @@ -2293,6 +2314,7 @@ func expandMaintenancePolicy(d *schema.ResourceData, meta interface{}) *containe startTime := dmw["start_time"].(string) return &containerBeta.MaintenancePolicy{ Window: &containerBeta.MaintenanceWindow{ + MaintenanceExclusions: exclusions, DailyMaintenanceWindow: &containerBeta.DailyMaintenanceWindow{ StartTime: startTime, }, @@ -2305,6 +2327,7 @@ func expandMaintenancePolicy(d *schema.ResourceData, meta interface{}) *containe rw := recurringWindow.([]interface{})[0].(map[string]interface{}) return &containerBeta.MaintenancePolicy{ Window: &containerBeta.MaintenanceWindow{ + MaintenanceExclusions: exclusions, RecurringWindow: &containerBeta.RecurringTimeWindow{ Window: &containerBeta.TimeWindow{ StartTime: rw["start_time"].(string), From 052a8ec878d9ff8e84dacffd0f6b12ab763a0f3f Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Mon, 28 Oct 2019 16:25:16 -0700 Subject: [PATCH 77/87] Add product services resource, make disk tests work with VCR (#2553) Merged PR #2553. --- build/inspec | 2 +- products/serviceusage/api.yaml | 52 +++++++++++++++++++ products/serviceusage/inspec.yaml | 15 ++++++ .../google_compute_disk.erb | 13 ++--- .../google_compute_disk_attributes.erb | 7 +-- .../google_compute_disks.erb | 5 +- .../google_project_service.erb | 6 +++ .../google_project_service_attributes.erb | 2 + .../google_project_services.erb | 9 ++++ .../inspec/tests/integration/build/gcp-mm.tf | 13 ++++- .../configuration/mm-attributes.yml | 7 ++- 11 files changed, 116 insertions(+), 15 deletions(-) create mode 100644 products/serviceusage/api.yaml create mode 100644 products/serviceusage/inspec.yaml create mode 100644 templates/inspec/examples/google_project_service/google_project_service.erb create mode 100644 templates/inspec/examples/google_project_service/google_project_service_attributes.erb create mode 100644 templates/inspec/examples/google_project_service/google_project_services.erb diff --git a/build/inspec b/build/inspec index e046d0b3311c..8e38bbaf755d 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit e046d0b3311c05ff7ec809bb18b167f11371207c +Subproject commit 8e38bbaf755d831821314c0b40890150ee337032 diff --git a/products/serviceusage/api.yaml b/products/serviceusage/api.yaml new file mode 100644 index 000000000000..c6d3084edecd --- /dev/null +++ b/products/serviceusage/api.yaml @@ -0,0 +1,52 @@ +# 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:Api::Product +name: ServiceUsage +display_name: Service Usage +versions: + - !ruby/object:Api::Product::Version + name: ga + base_url: https://serviceusage.googleapis.com/v1/ +scopes: + - https://www.googleapis.com/auth/cloud-platform +apis_required: + - !ruby/object:Api::Product::ApiReference + name: Service Usage API + url: https://console.cloud.google.com/apis/library/serviceusage.googleapis.com/ +objects: + - !ruby/object:Api::Resource + name: Service + base_url: projects/{{project}}/services + self_link: projects/{{project}}/services/{{name}} + references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Getting Started': 'https://cloud.google.com/service-usage/docs/getting-started' + description: | + A service that is available for use + properties: + - !ruby/object:Api::Type::String + name: name + required: true + description: | + The resource name of the service + - !ruby/object:Api::Type::String + name: parent + description: | + The name of the parent of this service. For example: `projects/123` + - !ruby/object:Api::Type::Enum + name: 'state' + description: Whether or not the service has been enabled for use by the consumer. + values: + - STATE_UNSPECIFIED + - DISABLED + - ENABLED diff --git a/products/serviceusage/inspec.yaml b/products/serviceusage/inspec.yaml new file mode 100644 index 000000000000..fff27bce1ea0 --- /dev/null +++ b/products/serviceusage/inspec.yaml @@ -0,0 +1,15 @@ +# 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::Inspec::Config +legacy_name: project +overrides: !ruby/object:Overrides::ResourceOverrides diff --git a/templates/inspec/examples/google_compute_disk/google_compute_disk.erb b/templates/inspec/examples/google_compute_disk/google_compute_disk.erb index 15509c4a9f63..d00ed63614b0 100644 --- a/templates/inspec/examples/google_compute_disk/google_compute_disk.erb +++ b/templates/inspec/examples/google_compute_disk/google_compute_disk.erb @@ -1,19 +1,20 @@ <% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> <% gcp_zone = "#{external_attribute('gcp_zone', doc_generation)}" %> -<% gcp_compute_disk_name = "#{external_attribute('gcp_compute_disk_name', doc_generation)}" -%> -<% gcp_compute_disk_image = "#{external_attribute('gcp_compute_disk_image', doc_generation).gsub('\'', '')}" -%> -<% gcp_compute_disk_type = "#{external_attribute('gcp_compute_disk_type', doc_generation)}" -%> +<% snapshot = grab_attributes['snapshot'] -%> +<% gcp_compute_disk_name = snapshot["disk_name"] -%> +<% gcp_compute_disk_image = snapshot["disk_image"] -%> +<% gcp_compute_disk_type = snapshot["disk_type"] -%> most_recent_image = google_compute_image(project: <%= doc_generation ? "'#{gcp_compute_disk_image.split('/').first}'" : "gcp_compute_disk_image.split('/').first" -%>, name: <%= doc_generation ? "'#{gcp_compute_disk_image.split('/').last}'" : "gcp_compute_disk_image.split('/').last" -%>) -describe google_compute_disk(project: <%= gcp_project_id -%>, name: <%= gcp_compute_disk_name -%>, zone: <%= gcp_zone -%>) do +describe google_compute_disk(project: <%= gcp_project_id -%>, name: <%= doc_generation ? "'#{gcp_compute_disk_name}'" : "gcp_compute_disk_name" -%>, zone: <%= gcp_zone -%>) do it { should exist } # Test that the image is the most recent image for the family its('source_image') { should match most_recent_image.self_link } - its('type') { should match <%= gcp_compute_disk_type -%> } + its('type') { should match <%= doc_generation ? "'#{gcp_compute_disk_type}'" : "gcp_compute_disk_type" -%> } end describe.one do - google_compute_disk(project: <%= gcp_project_id -%>, name: <%= gcp_compute_disk_name -%>, zone: <%= gcp_zone -%>).labels.each_pair do |key, value| + google_compute_disk(project: <%= gcp_project_id -%>, name: <%= doc_generation ? "'#{gcp_compute_disk_name}'" : "gcp_compute_disk_name" -%>, zone: <%= gcp_zone -%>).labels.each_pair do |key, value| describe key do it { should cmp "environment" } end diff --git a/templates/inspec/examples/google_compute_disk/google_compute_disk_attributes.erb b/templates/inspec/examples/google_compute_disk/google_compute_disk_attributes.erb index f8f2ac23908c..9de6cdaa0312 100644 --- a/templates/inspec/examples/google_compute_disk/google_compute_disk_attributes.erb +++ b/templates/inspec/examples/google_compute_disk/google_compute_disk_attributes.erb @@ -1,5 +1,6 @@ gcp_project_id = attribute(:gcp_project_id, default: '<%= external_attribute('gcp_project_id') -%>', description: 'The GCP project identifier.') gcp_zone = attribute(:gcp_zone, default: '<%= external_attribute('gcp_zone') -%>', description: 'The GCP project zone.') -gcp_compute_disk_name = attribute(:gcp_compute_disk_name, default: '<%= external_attribute('gcp_compute_disk_name') -%>', description: 'GCP Compute disk name.') -gcp_compute_disk_image = attribute(:gcp_compute_disk_image, default: '<%= external_attribute('gcp_compute_disk_image') -%>', description: 'GCP Compute image identifier.') -gcp_compute_disk_type = attribute(:gcp_compute_disk_type, default: '<%= external_attribute('gcp_compute_disk_type') -%>', description: 'GCP Compute disk type.') \ No newline at end of file +snapshot = attribute('snapshot', default: <%= JSON.pretty_generate(grab_attributes['snapshot']) -%>, description: 'Disk snapshot description') +gcp_compute_disk_name = snapshot["disk_name"] +gcp_compute_disk_image = snapshot["disk_image"] +gcp_compute_disk_type = snapshot["disk_type"] \ No newline at end of file diff --git a/templates/inspec/examples/google_compute_disk/google_compute_disks.erb b/templates/inspec/examples/google_compute_disk/google_compute_disks.erb index f5e08110c544..f09c940b9de2 100644 --- a/templates/inspec/examples/google_compute_disk/google_compute_disks.erb +++ b/templates/inspec/examples/google_compute_disk/google_compute_disks.erb @@ -1,7 +1,8 @@ -<% gcp_compute_disk_image = "#{external_attribute('gcp_compute_disk_image', doc_generation).gsub('\'', '')}" -%> +<% snapshot = grab_attributes['snapshot'] -%> +<% gcp_compute_disk_image = "#{snapshot["disk_image"].gsub('\'', '')}" -%> most_recent_image = google_compute_image(project: <%= doc_generation ? "'#{gcp_compute_disk_image.split('/').first}'" : "gcp_compute_disk_image.split('/').first" -%>, name: <%= doc_generation ? "'#{gcp_compute_disk_image.split('/').last}'" : "gcp_compute_disk_image.split('/').last" -%>) describe google_compute_disks(project: <%= "#{external_attribute('gcp_project_id', doc_generation)}" -%>, zone: <%= "#{external_attribute('gcp_zone', doc_generation)}" -%>) do it { should exist } - its('names') { should include <%= "#{external_attribute('gcp_compute_disk_name', doc_generation)}" -%> } + its('names') { should include <%= doc_generation ? "'#{snapshot['disk_name']}'" : "snapshot['disk_name']" -%> } its('source_images') { should include most_recent_image.self_link } end \ No newline at end of file diff --git a/templates/inspec/examples/google_project_service/google_project_service.erb b/templates/inspec/examples/google_project_service/google_project_service.erb new file mode 100644 index 000000000000..7e5e67045fc1 --- /dev/null +++ b/templates/inspec/examples/google_project_service/google_project_service.erb @@ -0,0 +1,6 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% service = grab_attributes['service'] -%> +describe google_project_service(project: <%= gcp_project_id -%>, name: <%= doc_generation ? "'#{service['name']}'" : "service['name']" -%>) do + it { should exist } + its('state') { should cmp "ENABLED" } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_project_service/google_project_service_attributes.erb b/templates/inspec/examples/google_project_service/google_project_service_attributes.erb new file mode 100644 index 000000000000..f7b5ec33a865 --- /dev/null +++ b/templates/inspec/examples/google_project_service/google_project_service_attributes.erb @@ -0,0 +1,2 @@ +gcp_project_id = attribute(:gcp_project_id, default: '<%= external_attribute('gcp_project_id') -%>', description: 'The GCP project identifier.') +service = attribute('service', default: <%= JSON.pretty_generate(grab_attributes['service']) -%>, description: 'Service description') \ No newline at end of file diff --git a/templates/inspec/examples/google_project_service/google_project_services.erb b/templates/inspec/examples/google_project_service/google_project_services.erb new file mode 100644 index 000000000000..4494e84fc468 --- /dev/null +++ b/templates/inspec/examples/google_project_service/google_project_services.erb @@ -0,0 +1,9 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% service = grab_attributes['service'] -%> +describe.one do + google_project_services(project: <%= gcp_project_id -%>).names.each do |name| + describe name do + it { should match <%= doc_generation ? "'#{service['name']}'" : "service['name']" -%> } + end + end +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 4e9619e09d48..be9ec4c4816f 100644 --- a/templates/inspec/tests/integration/build/gcp-mm.tf +++ b/templates/inspec/tests/integration/build/gcp-mm.tf @@ -197,6 +197,10 @@ variable "router_nat" { type = "map" } +variable "service" { + type = "map" +} + resource "google_compute_ssl_policy" "custom-ssl-policy" { name = "${var.ssl_policy["name"]}" min_tls_version = "${var.ssl_policy["min_tls_version"]}" @@ -504,9 +508,9 @@ resource "google_compute_router" "gcp-inspec-router" { resource "google_compute_disk" "snapshot-disk" { project = "${var.gcp_project_id}" name = var.snapshot["disk_name"] - type = "${var.gcp_compute_disk_type}" + type = var.snapshot["disk_type"] zone = "${var.gcp_zone}" - image = "${var.gcp_compute_disk_image}" + image = var.snapshot["disk_image"] labels = { environment = "generic_compute_disk_label" } @@ -857,3 +861,8 @@ resource "google_compute_router_nat" "inspec-nat" { filter = var.router_nat["log_config_filter"] } } + +resource "google_project_service" "project" { + project = var.gcp_project_id + service = var.service["name"] +} \ No newline at end of file diff --git a/templates/inspec/tests/integration/configuration/mm-attributes.yml b/templates/inspec/tests/integration/configuration/mm-attributes.yml index 89aabf593b47..d5bd4937fc06 100644 --- a/templates/inspec/tests/integration/configuration/mm-attributes.yml +++ b/templates/inspec/tests/integration/configuration/mm-attributes.yml @@ -149,6 +149,8 @@ router: snapshot: name: inspec-gcp-disk-snapshot disk_name: inspec-snapshot-disk + disk_type: pd-standard + disk_image: debian-cloud/debian-10-buster-v20191014 https_proxy: name: inspec-gcp-https-proxy @@ -323,4 +325,7 @@ router_nat: 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 + log_config_filter: ERRORS_ONLY + +service: + name: maps-android-backend.googleapis.com \ No newline at end of file From 5ea2db1c20dc0b15cbcaec19dccd375535561a6a Mon Sep 17 00:00:00 2001 From: Will Beebe Date: Fri, 18 Oct 2019 16:40:05 +0200 Subject: [PATCH 78/87] adding name to cloud build product --- products/cloudbuild/api.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/products/cloudbuild/api.yaml b/products/cloudbuild/api.yaml index 282e69e815d0..8943af8b8e30 100644 --- a/products/cloudbuild/api.yaml +++ b/products/cloudbuild/api.yaml @@ -50,6 +50,10 @@ objects: description: | The unique identifier for the trigger. output: true + - !ruby/object:Api::Type::String + name: 'name' + description: | + Name of the trigger. Must be unique within the project. - !ruby/object:Api::Type::String name: 'description' description: | From acdaac2dbf3bc130124909073f419ce1447bf6f5 Mon Sep 17 00:00:00 2001 From: Will Beebe Date: Fri, 18 Oct 2019 16:40:17 +0200 Subject: [PATCH 79/87] adding note about ulimit issues to readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7eb835415835..6ddb39d72b3c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ features/tools: * Terraform in Cloud Shell Importantly, Magic Modules *isn't* full code generation. Every change is made -manually; more than a code generator, Magic Modules is a force multiplier for +manually; more than a code generator, Magic Modules is a force multiplier for development. While many Magic Modules resources are defined exactly based on the GCP API, we use Magic Modules to preemptively solve issues across each tool by encoding our field-tested learnings from other tools in those definitions. In @@ -61,6 +61,8 @@ To get started, you'll need: * You can use `rbenv` to manage your Ruby version(s) * [`Bundler`](https://github.com/bundler/bundler) * This can be installed with `gem install bundler` +* If you are getting "Too many open files" ulimit needs to be raised. + * Mac OSX: `ulimit -n 1000` ### Preparing Magic Modules / One-time setup From 19b7bef7e435a9979d2ac511cb672771c33aba7f Mon Sep 17 00:00:00 2001 From: Will Beebe Date: Tue, 29 Oct 2019 01:25:42 -0700 Subject: [PATCH 80/87] name wil default to computed, from API --- products/cloudbuild/terraform.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/products/cloudbuild/terraform.yaml b/products/cloudbuild/terraform.yaml index 5503ae1c45eb..22cb1ad323fb 100644 --- a/products/cloudbuild/terraform.yaml +++ b/products/cloudbuild/terraform.yaml @@ -26,6 +26,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides properties: id: !ruby/object:Overrides::Terraform::PropertyOverride name: 'trigger_id' + name: !ruby/object:Overrides::Terraform::PropertyOverride + default_from_api: true build.steps: !ruby/object:Overrides::Terraform::PropertyOverride name: 'step' triggerTemplate: !ruby/object:Overrides::Terraform::PropertyOverride From ff9c9d70355100577826a6fe597a5a1e4f2397e3 Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Tue, 29 Oct 2019 18:35:44 +0000 Subject: [PATCH 81/87] Update tracked submodules -> HEAD on Tue Oct 29 18:35:44 UTC 2019 Tracked submodules are build/terraform-beta build/terraform-mapper build/terraform build/ansible build/inspec. --- build/ansible | 2 +- build/inspec | 2 +- build/terraform | 2 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/ansible b/build/ansible index 2bbb08735bfb..c5f24a9ef7d4 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit 2bbb08735bfb233645351fee2a988dc0e7d337e3 +Subproject commit c5f24a9ef7d4a64110e66049c39ed1a32ccfb1bf diff --git a/build/inspec b/build/inspec index 8e38bbaf755d..2aa5e47d5b0f 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 8e38bbaf755d831821314c0b40890150ee337032 +Subproject commit 2aa5e47d5b0fb12d7098d24536c96c83bd748e77 diff --git a/build/terraform b/build/terraform index 2c36238a2e69..7c404ed529b9 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 2c36238a2e69911d3a7f83925357e06486980168 +Subproject commit 7c404ed529b9841cb54980d45358c840559bdd38 diff --git a/build/terraform-beta b/build/terraform-beta index d316f5408f59..33dc4444cd6d 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit d316f5408f5980901436ae7cab1e59f188e95edc +Subproject commit 33dc4444cd6de407482f0f1cbe1ba6ac7e682337 diff --git a/build/terraform-mapper b/build/terraform-mapper index 54004ba54ac1..3db481de4ba2 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 54004ba54ac16a9fe5b8284e64cee22a8526392c +Subproject commit 3db481de4ba215229233d8cba520f3e3acf1886c From 4e2b04f2e1761d96920c42b87aea20e90b1134c6 Mon Sep 17 00:00:00 2001 From: Experience Coder Date: Tue, 29 Oct 2019 16:20:47 -0400 Subject: [PATCH 82/87] changes for spanner Insatnce and database (#2519) Merged PR #2519. --- products/spanner/inspec.yaml | 29 +++++++++++++++++++ .../google_spanner_database.erb | 7 +++++ .../google_spanner_database_attributes.erb | 2 ++ .../google_spanner_databases.erb | 10 +++++++ .../google_spanner_instance.erb | 11 +++++++ .../google_spanner_instance_attributes.erb | 2 ++ .../google_spanner_instances.erb | 10 +++++++ .../inspec/tests/integration/build/gcp-mm.tf | 29 ++++++++++++++++++- .../configuration/mm-attributes.yml | 15 +++++++++- 9 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 products/spanner/inspec.yaml create mode 100644 templates/inspec/examples/google_spanner_database/google_spanner_database.erb create mode 100644 templates/inspec/examples/google_spanner_database/google_spanner_database_attributes.erb create mode 100644 templates/inspec/examples/google_spanner_database/google_spanner_databases.erb create mode 100644 templates/inspec/examples/google_spanner_instance/google_spanner_instance.erb create mode 100644 templates/inspec/examples/google_spanner_instance/google_spanner_instance_attributes.erb create mode 100644 templates/inspec/examples/google_spanner_instance/google_spanner_instances.erb diff --git a/products/spanner/inspec.yaml b/products/spanner/inspec.yaml new file mode 100644 index 000000000000..c9974387db39 --- /dev/null +++ b/products/spanner/inspec.yaml @@ -0,0 +1,29 @@ +# 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::Inspec::Config +overrides: !ruby/object:Overrides::ResourceOverrides + InstanceConfig: !ruby/object:Overrides::Inspec::ResourceOverride + exclude: true + Instance: !ruby/object:Overrides::Inspec::ResourceOverride + exclude: false + properties: + state: !ruby/object:Overrides::Inspec::PropertyOverride + exclude: true + Database: !ruby/object:Overrides::Inspec::ResourceOverride + exclude: false + properties: + state: !ruby/object:Overrides::Inspec::PropertyOverride + exclude: true + extraStatements: !ruby/object:Overrides::Inspec::PropertyOverride + exclude: true diff --git a/templates/inspec/examples/google_spanner_database/google_spanner_database.erb b/templates/inspec/examples/google_spanner_database/google_spanner_database.erb new file mode 100644 index 000000000000..487ce68cc262 --- /dev/null +++ b/templates/inspec/examples/google_spanner_database/google_spanner_database.erb @@ -0,0 +1,7 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% spannerdatabase = grab_attributes['spannerdatabase'] -%> + +describe google_spanner_database(project: <%= gcp_project_id -%>, instance: <%= doc_generation ? "'#{spannerdatabase['instance']}'" : "spannerdatabase['instance']" -%>, name: <%= doc_generation ? "'#{spannerdatabase['name']}'" : "spannerdatabase['name']" -%>) do + it { should exist } + its('name') { should match <%= doc_generation ? "'#{spannerdatabase['name']}'" : "spannerdatabase['name']" -%> } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_spanner_database/google_spanner_database_attributes.erb b/templates/inspec/examples/google_spanner_database/google_spanner_database_attributes.erb new file mode 100644 index 000000000000..93a24ba09df1 --- /dev/null +++ b/templates/inspec/examples/google_spanner_database/google_spanner_database_attributes.erb @@ -0,0 +1,2 @@ +gcp_project_id = attribute(:gcp_project_id, default: '<%= external_attribute('gcp_project_id') -%>', description: 'The GCP project identifier.') +spannerdatabase = attribute('spannerdatabase', default: <%= JSON.pretty_generate(grab_attributes['spannerdatabase']) -%>, description: 'Cloud Spanner definition') diff --git a/templates/inspec/examples/google_spanner_database/google_spanner_databases.erb b/templates/inspec/examples/google_spanner_database/google_spanner_databases.erb new file mode 100644 index 000000000000..c29f3cb3c7ab --- /dev/null +++ b/templates/inspec/examples/google_spanner_database/google_spanner_databases.erb @@ -0,0 +1,10 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% spannerdatabase = grab_attributes['spannerdatabase'] -%> + +describe.one do + google_spanner_databases(project: <%= gcp_project_id -%>, instance: <%= doc_generation ? "'#{spannerdatabase['instance']}'" : "spannerdatabase['instance']" -%>).names.each do |name| + describe name do + it { should match <%= doc_generation ? "'#{spannerdatabase['name']}'" : "spannerdatabase['name']" -%> } + end + end +end \ No newline at end of file diff --git a/templates/inspec/examples/google_spanner_instance/google_spanner_instance.erb b/templates/inspec/examples/google_spanner_instance/google_spanner_instance.erb new file mode 100644 index 000000000000..bb488d5eaee0 --- /dev/null +++ b/templates/inspec/examples/google_spanner_instance/google_spanner_instance.erb @@ -0,0 +1,11 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% spannerinstance = grab_attributes['spannerinstance'] -%> + +describe google_spanner_instance(project: <%= gcp_project_id -%>, name: <%= doc_generation ? "'#{spannerinstance['name']}'" : "spannerinstance['name']" -%>, config: <%= doc_generation ? "'#{spannerinstance['config']}'" : "spannerinstance['config']" -%>) do + it { should exist } + its('config') { should match <%= doc_generation ? "'#{spannerinstance['config']}'" : "spannerinstance['config']" -%> } + its('name') { should match <%= doc_generation ? "'#{spannerinstance['name']}'" : "spannerinstance['name']" -%> } + its('display_name') { should eq <%= doc_generation ? "'#{spannerinstance['display_name']}'" : "spannerinstance['display_name']" -%> } + its('node_count') { should eq <%= doc_generation ? "'#{spannerinstance['num_nodes']}'" : "spannerinstance['num_nodes']" -%> } + its('labels') { should include(<%= doc_generation ? "'#{spannerinstance['label_key']}'" : "spannerinstance['label_key']" -%> => <%= doc_generation ? "'#{spannerinstance['label_value']}'" : "spannerinstance['label_value']" -%>) } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_spanner_instance/google_spanner_instance_attributes.erb b/templates/inspec/examples/google_spanner_instance/google_spanner_instance_attributes.erb new file mode 100644 index 000000000000..ba0396d56f0e --- /dev/null +++ b/templates/inspec/examples/google_spanner_instance/google_spanner_instance_attributes.erb @@ -0,0 +1,2 @@ +gcp_project_id = attribute(:gcp_project_id, default: '<%= external_attribute('gcp_project_id') -%>', description: 'The GCP project identifier.') +spannerinstance = attribute('spannerinstance', default: <%= JSON.pretty_generate(grab_attributes['spannerinstance']) -%>, description: 'Cloud Spanner definition') diff --git a/templates/inspec/examples/google_spanner_instance/google_spanner_instances.erb b/templates/inspec/examples/google_spanner_instance/google_spanner_instances.erb new file mode 100644 index 000000000000..3ddaa51ab622 --- /dev/null +++ b/templates/inspec/examples/google_spanner_instance/google_spanner_instances.erb @@ -0,0 +1,10 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% spannerinstance = grab_attributes['spannerinstance'] -%> + +describe.one do + google_spanner_instances(project: <%= gcp_project_id -%>, config: <%= doc_generation ? "'#{spannerinstance['config']}'" : "spannerinstance['config']" -%>).configs.each do |config| + describe config do + it { should match <%= doc_generation ? "'#{spannerinstance['config']}'" : "spannerinstance['config']" -%> } + end + end +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 be9ec4c4816f..21678b63f90b 100644 --- a/templates/inspec/tests/integration/build/gcp-mm.tf +++ b/templates/inspec/tests/integration/build/gcp-mm.tf @@ -201,6 +201,15 @@ variable "service" { type = "map" } +variable "spannerinstance" { + type = "map" +} + +variable "spannerdatabase" { + type = "map" +} + + resource "google_compute_ssl_policy" "custom-ssl-policy" { name = "${var.ssl_policy["name"]}" min_tls_version = "${var.ssl_policy["min_tls_version"]}" @@ -865,4 +874,22 @@ resource "google_compute_router_nat" "inspec-nat" { resource "google_project_service" "project" { project = var.gcp_project_id service = var.service["name"] -} \ No newline at end of file +} + +resource "google_spanner_instance" "spanner_instance" { + project = "${var.gcp_project_id}" + config = "${var.spannerinstance["config"]}" + name = "${var.spannerinstance["name"]}" + display_name = "${var.spannerinstance["display_name"]}" + num_nodes = "${var.spannerinstance["num_nodes"]}" + labels = { + "${var.spannerinstance["label_key"]}" = "${var.spannerinstance["label_value"]}" + } +} + +resource "google_spanner_database" "database" { + project = "${var.gcp_project_id}" + instance = "${google_spanner_instance.spanner_instance.name}" + name = "${var.spannerdatabase["name"]}" + ddl = ["${var.spannerdatabase["ddl"]}"] +} diff --git a/templates/inspec/tests/integration/configuration/mm-attributes.yml b/templates/inspec/tests/integration/configuration/mm-attributes.yml index d5bd4937fc06..f4ed2a05b7df 100644 --- a/templates/inspec/tests/integration/configuration/mm-attributes.yml +++ b/templates/inspec/tests/integration/configuration/mm-attributes.yml @@ -328,4 +328,17 @@ router_nat: log_config_filter: ERRORS_ONLY service: - name: maps-android-backend.googleapis.com \ No newline at end of file + name: maps-android-backend.googleapis.com + +spannerinstance: + config: regional-us-east1 + name: spinstance + display_name: inspectest + num_nodes: 1 + label_key: env + label_value: test + +spannerdatabase: + name: spdatabase + instance: spinstance + ddl: "CREATE TABLE test (test STRING(MAX),) PRIMARY KEY (test)" From 1d3e5f72a145bf96b66018ab46473ade61479297 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Tue, 29 Oct 2019 13:49:04 -0700 Subject: [PATCH 83/87] Add GKE taint changes to the upgrade guide (#2538) Merged PR #2538. --- build/terraform | 2 +- build/terraform-beta | 2 +- .../docs/version_3_upgrade.html.markdown | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index 7c404ed529b9..a0fb67e68c6d 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 7c404ed529b9841cb54980d45358c840559bdd38 +Subproject commit a0fb67e68c6d0d91af90657dfabd9e2f2741c4bf diff --git a/build/terraform-beta b/build/terraform-beta index 33dc4444cd6d..8d3bdd31d309 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 33dc4444cd6de407482f0f1cbe1ba6ac7e682337 +Subproject commit 8d3bdd31d3092eb0b0791d1c4ffa878c9fd01a76 diff --git a/third_party/terraform/website/docs/version_3_upgrade.html.markdown b/third_party/terraform/website/docs/version_3_upgrade.html.markdown index 201bcc10dce5..62a5a1e002e2 100644 --- a/third_party/terraform/website/docs/version_3_upgrade.html.markdown +++ b/third_party/terraform/website/docs/version_3_upgrade.html.markdown @@ -123,6 +123,23 @@ logging_service = "logging.googleapis.com/kubernetes" monitoring_service = "monitoring.googleapis.com/kubernetes" ``` +### `taint` field is now authoritative when set + +The `taint` field inside of `node_config` blocks on `google_container_cluster` +and `google_container_node_pool` will no longer ignore GPU-related values when +set. + +Previously, the field ignored upstream taints when unset and ignored unset GPU +taints when other taints were set. Now it will ignore upstream taints when set +and act authoritatively when set, requiring all taints (including Kubernetes and +GKE-managed ones) to be defined in config. + +Additionally, an empty taint can now be specified with `taint = []`. As a result +of this change, the JSON/state representation of the field has changed, +introducing an incompatibility for users who specify config in JSON instead of +HCL or who use `dynamic` blocks. See more details in the [Attributes as Blocks](https://www.terraform.io/docs/configuration/attr-as-blocks.html) +documentation. + ## Resource: `google_project_services` ### `google_project_services` has been removed from the provider From 503495af6d7d569e2028b931a0f8d085ee13a6ee Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Wed, 30 Oct 2019 10:57:48 -0700 Subject: [PATCH 84/87] support GKE in terraform-mapper (terraform-google-conversion) (#2564) Merged PR #2564. --- .../vars/validator_handwritten_files.txt | 4 +- build/terraform-beta | 2 +- build/terraform-mapper | 2 +- provider/terraform_object_library.rb | 2 + third_party/validator/container.go | 1609 +++++++++++++++++ 5 files changed, 1616 insertions(+), 3 deletions(-) create mode 100644 third_party/validator/container.go diff --git a/.ci/magic-modules/vars/validator_handwritten_files.txt b/.ci/magic-modules/vars/validator_handwritten_files.txt index 4f40ca6f84a8..f0f27294d903 100644 --- a/.ci/magic-modules/vars/validator_handwritten_files.txt +++ b/.ci/magic-modules/vars/validator_handwritten_files.txt @@ -1,7 +1,9 @@ resource_compute_instance.go +resource_container_cluster.go +resource_container_node_pool.go resource_google_project.go resource_sql_database_instance.go.erb resource_storage_bucket.go iam_folder.go iam_organization.go -iam_project.go \ No newline at end of file +iam_project.go diff --git a/build/terraform-beta b/build/terraform-beta index 8d3bdd31d309..855308180f58 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 8d3bdd31d3092eb0b0791d1c4ffa878c9fd01a76 +Subproject commit 855308180f58d7f0d77c6dec2abed608eded7ac5 diff --git a/build/terraform-mapper b/build/terraform-mapper index 3db481de4ba2..c11498b4dc61 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 3db481de4ba215229233d8cba520f3e3acf1886c +Subproject commit c11498b4dc6146fc95f99890e41ece5239a39c53 diff --git a/provider/terraform_object_library.rb b/provider/terraform_object_library.rb index 2567ec02e904..a1005733e695 100644 --- a/provider/terraform_object_library.rb +++ b/provider/terraform_object_library.rb @@ -90,6 +90,8 @@ def copy_common_files(output_folder) 'third_party/validator/project_iam.go'], ['google/folder_iam.go', 'third_party/validator/folder_iam.go'], + ['google/container.go', + 'third_party/validator/container.go'], ['google/image.go', 'third_party/terraform/utils/image.go'], ['google/disk_type.go', diff --git a/third_party/validator/container.go b/third_party/validator/container.go new file mode 100644 index 000000000000..943123878ead --- /dev/null +++ b/third_party/validator/container.go @@ -0,0 +1,1609 @@ +// This file is written manually and is not based from terraform-provider-google. +// There is a huge potential for drift. The longer term plan is to have this +// file generated from the logic in terraform-provider-google. Please +// see https://github.com/GoogleCloudPlatform/magic-modules/pull/2485#issuecomment-545680059 +// for the discussion. + +package google + +import ( + "fmt" + "reflect" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func expandContainerEnabledObject(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + if val := reflect.ValueOf(v); !val.IsValid() || isEmptyValue(val) { + return nil, nil + } + transformed := map[string]interface{}{ + "enabled": v, + } + return transformed, nil +} + +func expandContainerClusterEnableLegacyAbac(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return expandContainerEnabledObject(v, d, config) +} + +func expandContainerClusterEnableBinaryAuthorization(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return expandContainerEnabledObject(v, d, config) +} + +func expandContainerMaxPodsConstraint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + if val := reflect.ValueOf(v); !val.IsValid() || isEmptyValue(val) { + return nil, nil + } + transformed := map[string]interface{}{ + "maxPodsPerNode": v, + } + return transformed, nil +} + +func expandContainerClusterDefaultMaxPodsPerNode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return expandContainerMaxPodsConstraint(v, d, config) +} + +func expandContainerNodePoolMaxPodsPerNode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return expandContainerMaxPodsConstraint(v, d, config) +} + +func expandContainerClusterNetwork(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + fv, err := ParseNetworkFieldValue(v.(string), d, config) + if err != nil { + return nil, err + } + return fv.RelativeLink(), nil +} + +func expandContainerClusterSubnetwork(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + fv, err := ParseNetworkFieldValue(v.(string), d, config) + if err != nil { + return nil, err + } + return fv.RelativeLink(), nil +} + +func canonicalizeServiceScopesFromSet(scopesSet *schema.Set) (interface{}, error) { + scopes := make([]string, scopesSet.Len()) + for i, scope := range scopesSet.List() { + scopes[i] = canonicalizeServiceScope(scope.(string)) + } + return scopes, nil +} + +func expandContainerClusterNodeConfigOauthScopes(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + scopesSet := v.(*schema.Set) + return canonicalizeServiceScopesFromSet(scopesSet) +} + +func expandContainerNodePoolNodeConfigOauthScopes(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + scopesSet := v.(*schema.Set) + return canonicalizeServiceScopesFromSet(scopesSet) +} + +func GetContainerClusterCaiObject(d TerraformResourceData, config *Config) (Asset, error) { + name, err := assetName(d, config, "//container.googleapis.com/projects/{{project}}/locations/{{location}}/clusters/{{name}}") + if err != nil { + return Asset{}, err + } + if obj, err := GetContainerClusterApiObject(d, config); err == nil { + return Asset{ + Name: name, + Type: "container.googleapis.com/Cluster", + Resource: &AssetResource{ + Version: "v1", + DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/container/v1/rest", + DiscoveryName: "Cluster", + Data: obj, + }, + }, nil + } else { + return Asset{}, err + } +} + +func GetContainerClusterApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) { + obj := make(map[string]interface{}) + binaryAuthorizationProp, err := expandContainerClusterEnableBinaryAuthorization(d.Get("enable_binary_authorization"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("enable_binary_authorization"); !isEmptyValue(reflect.ValueOf(binaryAuthorizationProp)) && (ok || !reflect.DeepEqual(v, binaryAuthorizationProp)) { + obj["binaryAuthorization"] = binaryAuthorizationProp + } + enableKubernetesAlphaProp, err := expandContainerClusterEnableKubernetesAlpha(d.Get("enable_kubernetes_alpha"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("enable_kubernetes_alpha"); !isEmptyValue(reflect.ValueOf(enableKubernetesAlphaProp)) && (ok || !reflect.DeepEqual(v, enableKubernetesAlphaProp)) { + obj["enableKubernetesAlpha"] = enableKubernetesAlphaProp + } + podSecurityPolicyConfigProp, err := expandContainerClusterPodSecurityPolicyConfig(d.Get("pod_security_policy_config"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("pod_security_policy_config"); !isEmptyValue(reflect.ValueOf(podSecurityPolicyConfigProp)) && (ok || !reflect.DeepEqual(v, podSecurityPolicyConfigProp)) { + obj["podSecurityPolicyConfig"] = podSecurityPolicyConfigProp + } + nameProp, err := expandContainerClusterName(d.Get("name"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { + obj["name"] = nameProp + } + descriptionProp, err := expandContainerClusterDescription(d.Get("description"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + initialNodeCountProp, err := expandContainerClusterInitialNodeCount(d.Get("initial_node_count"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("initial_node_count"); !isEmptyValue(reflect.ValueOf(initialNodeCountProp)) && (ok || !reflect.DeepEqual(v, initialNodeCountProp)) { + obj["initialNodeCount"] = initialNodeCountProp + } + nodeConfigProp, err := expandContainerClusterNodeConfig(d.Get("node_config"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("node_config"); !isEmptyValue(reflect.ValueOf(nodeConfigProp)) && (ok || !reflect.DeepEqual(v, nodeConfigProp)) { + obj["nodeConfig"] = nodeConfigProp + } + masterAuthProp, err := expandContainerClusterMasterAuth(d.Get("master_auth"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("master_auth"); !isEmptyValue(reflect.ValueOf(masterAuthProp)) && (ok || !reflect.DeepEqual(v, masterAuthProp)) { + obj["masterAuth"] = masterAuthProp + } + loggingServiceProp, err := expandContainerClusterLoggingService(d.Get("logging_service"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("logging_service"); !isEmptyValue(reflect.ValueOf(loggingServiceProp)) && (ok || !reflect.DeepEqual(v, loggingServiceProp)) { + obj["loggingService"] = loggingServiceProp + } + monitoringServiceProp, err := expandContainerClusterMonitoringService(d.Get("monitoring_service"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("monitoring_service"); !isEmptyValue(reflect.ValueOf(monitoringServiceProp)) && (ok || !reflect.DeepEqual(v, monitoringServiceProp)) { + obj["monitoringService"] = monitoringServiceProp + } + networkProp, err := expandContainerClusterNetwork(d.Get("network"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("network"); !isEmptyValue(reflect.ValueOf(networkProp)) && (ok || !reflect.DeepEqual(v, networkProp)) { + obj["network"] = networkProp + } + privateClusterConfigProp, err := expandContainerClusterPrivateClusterConfig(d.Get("private_cluster_config"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("private_cluster_config"); !isEmptyValue(reflect.ValueOf(privateClusterConfigProp)) && (ok || !reflect.DeepEqual(v, privateClusterConfigProp)) { + obj["privateClusterConfig"] = privateClusterConfigProp + } + clusterIpv4CidrProp, err := expandContainerClusterClusterIpv4Cidr(d.Get("cluster_ipv4_cidr"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("cluster_ipv4_cidr"); !isEmptyValue(reflect.ValueOf(clusterIpv4CidrProp)) && (ok || !reflect.DeepEqual(v, clusterIpv4CidrProp)) { + obj["clusterIpv4Cidr"] = clusterIpv4CidrProp + } + addonsConfigProp, err := expandContainerClusterAddonsConfig(d.Get("addons_config"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("addons_config"); !isEmptyValue(reflect.ValueOf(addonsConfigProp)) && (ok || !reflect.DeepEqual(v, addonsConfigProp)) { + obj["addonsConfig"] = addonsConfigProp + } + subnetworkProp, err := expandContainerClusterSubnetwork(d.Get("subnetwork"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("subnetwork"); !isEmptyValue(reflect.ValueOf(subnetworkProp)) && (ok || !reflect.DeepEqual(v, subnetworkProp)) { + obj["subnetwork"] = subnetworkProp + } + locationsProp, err := expandContainerClusterNodeLocations(d.Get("node_locations"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("node_locations"); !isEmptyValue(reflect.ValueOf(locationsProp)) && (ok || !reflect.DeepEqual(v, locationsProp)) { + obj["locations"] = locationsProp + } + resourceLabelsProp, err := expandContainerClusterResourceLabels(d.Get("resource_labels"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("resource_labels"); !isEmptyValue(reflect.ValueOf(resourceLabelsProp)) && (ok || !reflect.DeepEqual(v, resourceLabelsProp)) { + obj["resourceLabels"] = resourceLabelsProp + } + labelFingerprintProp, err := expandContainerClusterLabelFingerprint(d.Get("label_fingerprint"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("label_fingerprint"); !isEmptyValue(reflect.ValueOf(labelFingerprintProp)) && (ok || !reflect.DeepEqual(v, labelFingerprintProp)) { + obj["labelFingerprint"] = labelFingerprintProp + } + legacyAbacProp, err := expandContainerClusterEnableLegacyAbac(d.Get("enable_legacy_abac"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("enable_legacy_abac"); !isEmptyValue(reflect.ValueOf(legacyAbacProp)) && (ok || !reflect.DeepEqual(v, legacyAbacProp)) { + obj["legacyAbac"] = legacyAbacProp + } + networkPolicyProp, err := expandContainerClusterNetworkPolicy(d.Get("network_policy"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("network_policy"); !isEmptyValue(reflect.ValueOf(networkPolicyProp)) && (ok || !reflect.DeepEqual(v, networkPolicyProp)) { + obj["networkPolicy"] = networkPolicyProp + } + defaultMaxPodsConstraintProp, err := expandContainerClusterDefaultMaxPodsPerNode(d.Get("default_max_pods_per_node"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("default_max_pods_per_node"); !isEmptyValue(reflect.ValueOf(defaultMaxPodsConstraintProp)) && (ok || !reflect.DeepEqual(v, defaultMaxPodsConstraintProp)) { + obj["defaultMaxPodsConstraint"] = defaultMaxPodsConstraintProp + } + ipAllocationPolicyProp, err := expandContainerClusterIpAllocationPolicy(d.Get("ip_allocation_policy"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("ip_allocation_policy"); !isEmptyValue(reflect.ValueOf(ipAllocationPolicyProp)) && (ok || !reflect.DeepEqual(v, ipAllocationPolicyProp)) { + obj["ipAllocationPolicy"] = ipAllocationPolicyProp + } + initialClusterVersionProp, err := expandContainerClusterMinMasterVersion(d.Get("min_master_version"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("min_master_version"); !isEmptyValue(reflect.ValueOf(initialClusterVersionProp)) && (ok || !reflect.DeepEqual(v, initialClusterVersionProp)) { + obj["initialClusterVersion"] = initialClusterVersionProp + } + enableTpuProp, err := expandContainerClusterEnableTpu(d.Get("enable_tpu"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("enable_tpu"); !isEmptyValue(reflect.ValueOf(enableTpuProp)) && (ok || !reflect.DeepEqual(v, enableTpuProp)) { + obj["enableTpu"] = enableTpuProp + } + tpuIpv4CidrBlockProp, err := expandContainerClusterTPUIpv4CidrBlock(d.Get("tpu_ipv4_cidr_block"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("tpu_ipv4_cidr_block"); !isEmptyValue(reflect.ValueOf(tpuIpv4CidrBlockProp)) && (ok || !reflect.DeepEqual(v, tpuIpv4CidrBlockProp)) { + obj["tpuIpv4CidrBlock"] = tpuIpv4CidrBlockProp + } + masterAuthorizedNetworksConfigProp, err := expandContainerClusterMasterAuthorizedNetworksConfig(d.Get("master_authorized_networks_config"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("master_authorized_networks_config"); !isEmptyValue(reflect.ValueOf(masterAuthorizedNetworksConfigProp)) && (ok || !reflect.DeepEqual(v, masterAuthorizedNetworksConfigProp)) { + obj["masterAuthorizedNetworksConfig"] = masterAuthorizedNetworksConfigProp + } + locationProp, err := expandContainerClusterLocation(d.Get("location"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("location"); !isEmptyValue(reflect.ValueOf(locationProp)) && (ok || !reflect.DeepEqual(v, locationProp)) { + obj["location"] = locationProp + } + kubectlPathProp, err := expandContainerClusterKubectlPath(d.Get("kubectl_path"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("kubectl_path"); !isEmptyValue(reflect.ValueOf(kubectlPathProp)) && (ok || !reflect.DeepEqual(v, kubectlPathProp)) { + obj["kubectlPath"] = kubectlPathProp + } + kubectlContextProp, err := expandContainerClusterKubectlContext(d.Get("kubectl_context"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("kubectl_context"); !isEmptyValue(reflect.ValueOf(kubectlContextProp)) && (ok || !reflect.DeepEqual(v, kubectlContextProp)) { + obj["kubectlContext"] = kubectlContextProp + } + + return obj, nil +} + +func expandContainerClusterEnableKubernetesAlpha(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterPodSecurityPolicyConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnabled, err := expandContainerClusterPodSecurityPolicyConfigEnabled(original["enabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnabled); val.IsValid() && !isEmptyValue(val) { + transformed["enabled"] = transformedEnabled + } + + return transformed, nil +} + +func expandContainerClusterPodSecurityPolicyConfigEnabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterInitialNodeCount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedWorkloadMetadataConfig, err := expandContainerClusterNodeConfigWorkloadMetadataConfig(original["workload_metadata_config"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedWorkloadMetadataConfig); val.IsValid() && !isEmptyValue(val) { + transformed["workloadMetadataConfig"] = transformedWorkloadMetadataConfig + } + + transformedMachineType, err := expandContainerClusterNodeConfigMachineType(original["machine_type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMachineType); val.IsValid() && !isEmptyValue(val) { + transformed["machineType"] = transformedMachineType + } + + transformedDiskSizeGb, err := expandContainerClusterNodeConfigDiskSizeGb(original["disk_size_gb"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDiskSizeGb); val.IsValid() && !isEmptyValue(val) { + transformed["diskSizeGb"] = transformedDiskSizeGb + } + + transformedOauthScopes, err := expandContainerClusterNodeConfigOauthScopes(original["oauth_scopes"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedOauthScopes); val.IsValid() && !isEmptyValue(val) { + transformed["oauthScopes"] = transformedOauthScopes + } + + transformedServiceAccount, err := expandContainerClusterNodeConfigServiceAccount(original["service_account"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedServiceAccount); val.IsValid() && !isEmptyValue(val) { + transformed["serviceAccount"] = transformedServiceAccount + } + + transformedMetadata, err := expandContainerClusterNodeConfigMetadata(original["metadata"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMetadata); val.IsValid() && !isEmptyValue(val) { + transformed["metadata"] = transformedMetadata + } + + transformedImageType, err := expandContainerClusterNodeConfigImageType(original["image_type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedImageType); val.IsValid() && !isEmptyValue(val) { + transformed["imageType"] = transformedImageType + } + + transformedLabels, err := expandContainerClusterNodeConfigLabels(original["labels"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedLabels); val.IsValid() && !isEmptyValue(val) { + transformed["labels"] = transformedLabels + } + + transformedLocalSsdCount, err := expandContainerClusterNodeConfigLocalSsdCount(original["local_ssd_count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedLocalSsdCount); val.IsValid() && !isEmptyValue(val) { + transformed["localSsdCount"] = transformedLocalSsdCount + } + + transformedTags, err := expandContainerClusterNodeConfigTags(original["tags"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTags); val.IsValid() && !isEmptyValue(val) { + transformed["tags"] = transformedTags + } + + transformedPreemptible, err := expandContainerClusterNodeConfigPreemptible(original["preemptible"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPreemptible); val.IsValid() && !isEmptyValue(val) { + transformed["preemptible"] = transformedPreemptible + } + + transformedGuestAccelerator, err := expandContainerClusterNodeConfigGuestAccelerator(original["guest_accelerator"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedGuestAccelerator); val.IsValid() && !isEmptyValue(val) { + transformed["accelerators"] = transformedGuestAccelerator + } + + transformedDiskType, err := expandContainerClusterNodeConfigDiskType(original["disk_type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDiskType); val.IsValid() && !isEmptyValue(val) { + transformed["diskType"] = transformedDiskType + } + + transformedMinCpuPlatform, err := expandContainerClusterNodeConfigMinCpuPlatform(original["min_cpu_platform"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMinCpuPlatform); val.IsValid() && !isEmptyValue(val) { + transformed["minCpuPlatform"] = transformedMinCpuPlatform + } + + transformedTaint, err := expandContainerClusterNodeConfigTaint(original["taint"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTaint); val.IsValid() && !isEmptyValue(val) { + transformed["taints"] = transformedTaint + } + + return transformed, nil +} + +func expandContainerClusterNodeConfigWorkloadMetadataConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedNodeMetadata, err := expandContainerClusterNodeConfigWorkloadMetadataConfigNodeMetadata(original["node_metadata"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNodeMetadata); val.IsValid() && !isEmptyValue(val) { + transformed["nodeMetadata"] = transformedNodeMetadata + } + + return transformed, nil +} + +func expandContainerClusterNodeConfigWorkloadMetadataConfigNodeMetadata(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigMachineType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigDiskSizeGb(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigServiceAccount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigMetadata(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandContainerClusterNodeConfigImageType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandContainerClusterNodeConfigLocalSsdCount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigTags(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigPreemptible(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigGuestAccelerator(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedCount, err := expandContainerClusterNodeConfigGuestAcceleratorCount(original["count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCount); val.IsValid() && !isEmptyValue(val) { + transformed["acceleratorCount"] = transformedCount + } + + transformedType, err := expandContainerClusterNodeConfigGuestAcceleratorType(original["type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedType); val.IsValid() && !isEmptyValue(val) { + transformed["acceleratorType"] = transformedType + } + + req = append(req, transformed) + } + return req, nil +} + +func expandContainerClusterNodeConfigGuestAcceleratorCount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigGuestAcceleratorType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigDiskType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigMinCpuPlatform(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigTaint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedKey, err := expandContainerClusterNodeConfigTaintKey(original["key"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKey); val.IsValid() && !isEmptyValue(val) { + transformed["key"] = transformedKey + } + + transformedValue, err := expandContainerClusterNodeConfigTaintValue(original["value"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedValue); val.IsValid() && !isEmptyValue(val) { + transformed["value"] = transformedValue + } + + transformedEffect, err := expandContainerClusterNodeConfigTaintEffect(original["effect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEffect); val.IsValid() && !isEmptyValue(val) { + transformed["effect"] = transformedEffect + } + + req = append(req, transformed) + } + return req, nil +} + +func expandContainerClusterNodeConfigTaintKey(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigTaintValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeConfigTaintEffect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMasterAuth(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedUsername, err := expandContainerClusterMasterAuthUsername(original["username"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedUsername); val.IsValid() && !isEmptyValue(val) { + transformed["username"] = transformedUsername + } + + transformedPassword, err := expandContainerClusterMasterAuthPassword(original["password"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPassword); val.IsValid() && !isEmptyValue(val) { + transformed["password"] = transformedPassword + } + + transformedClientCertificateConfig, err := expandContainerClusterMasterAuthClientCertificateConfig(original["client_certificate_config"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedClientCertificateConfig); val.IsValid() && !isEmptyValue(val) { + transformed["clientCertificateConfig"] = transformedClientCertificateConfig + } + + transformedClusterCaCertificate, err := expandContainerClusterMasterAuthClusterCaCertificate(original["cluster_ca_certificate"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedClusterCaCertificate); val.IsValid() && !isEmptyValue(val) { + transformed["clusterCaCertificate"] = transformedClusterCaCertificate + } + + transformedClientCertificate, err := expandContainerClusterMasterAuthClientCertificate(original["client_certificate"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedClientCertificate); val.IsValid() && !isEmptyValue(val) { + transformed["clientCertificate"] = transformedClientCertificate + } + + transformedClientKey, err := expandContainerClusterMasterAuthClientKey(original["client_key"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedClientKey); val.IsValid() && !isEmptyValue(val) { + transformed["clientKey"] = transformedClientKey + } + + return transformed, nil +} + +func expandContainerClusterMasterAuthUsername(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMasterAuthPassword(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMasterAuthClientCertificateConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedIssueClientCertificate, err := expandContainerClusterMasterAuthClientCertificateConfigIssueClientCertificate(original["issue_client_certificate"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedIssueClientCertificate); val.IsValid() && !isEmptyValue(val) { + transformed["issueClientCertificate"] = transformedIssueClientCertificate + } + + return transformed, nil +} + +func expandContainerClusterMasterAuthClientCertificateConfigIssueClientCertificate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMasterAuthClusterCaCertificate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMasterAuthClientCertificate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMasterAuthClientKey(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterLoggingService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMonitoringService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterPrivateClusterConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnablePrivateNodes, err := expandContainerClusterPrivateClusterConfigEnablePrivateNodes(original["enable_private_nodes"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnablePrivateNodes); val.IsValid() && !isEmptyValue(val) { + transformed["enablePrivateNodes"] = transformedEnablePrivateNodes + } + + transformedEnablePrivateEndpoint, err := expandContainerClusterPrivateClusterConfigEnablePrivateEndpoint(original["enable_private_endpoint"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnablePrivateEndpoint); val.IsValid() && !isEmptyValue(val) { + transformed["enablePrivateEndpoint"] = transformedEnablePrivateEndpoint + } + + transformedMasterIpv4CidrBlock, err := expandContainerClusterPrivateClusterConfigMasterIpv4CidrBlock(original["master_ipv4_cidr_block"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMasterIpv4CidrBlock); val.IsValid() && !isEmptyValue(val) { + transformed["masterIpv4CidrBlock"] = transformedMasterIpv4CidrBlock + } + + transformedPrivateEndpoint, err := expandContainerClusterPrivateClusterConfigPrivateEndpoint(original["private_endpoint"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrivateEndpoint); val.IsValid() && !isEmptyValue(val) { + transformed["privateEndpoint"] = transformedPrivateEndpoint + } + + transformedPublicEndpoint, err := expandContainerClusterPrivateClusterConfigPublicEndpoint(original["public_endpoint"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPublicEndpoint); val.IsValid() && !isEmptyValue(val) { + transformed["publicEndpoint"] = transformedPublicEndpoint + } + + return transformed, nil +} + +func expandContainerClusterPrivateClusterConfigEnablePrivateNodes(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterPrivateClusterConfigEnablePrivateEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterPrivateClusterConfigMasterIpv4CidrBlock(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterPrivateClusterConfigPrivateEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterPrivateClusterConfigPublicEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterClusterIpv4Cidr(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterAddonsConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHttpLoadBalancing, err := expandContainerClusterAddonsConfigHttpLoadBalancing(original["http_load_balancing"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpLoadBalancing); val.IsValid() && !isEmptyValue(val) { + transformed["httpLoadBalancing"] = transformedHttpLoadBalancing + } + + transformedHorizontalPodAutoscaling, err := expandContainerClusterAddonsConfigHorizontalPodAutoscaling(original["horizontal_pod_autoscaling"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHorizontalPodAutoscaling); val.IsValid() && !isEmptyValue(val) { + transformed["horizontalPodAutoscaling"] = transformedHorizontalPodAutoscaling + } + + transformedKubernetesDashboard, err := expandContainerClusterAddonsConfigKubernetesDashboard(original["kubernetes_dashboard"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKubernetesDashboard); val.IsValid() && !isEmptyValue(val) { + transformed["kubernetesDashboard"] = transformedKubernetesDashboard + } + + transformedNetworkPolicyConfig, err := expandContainerClusterAddonsConfigNetworkPolicyConfig(original["network_policy_config"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNetworkPolicyConfig); val.IsValid() && !isEmptyValue(val) { + transformed["networkPolicyConfig"] = transformedNetworkPolicyConfig + } + + return transformed, nil +} + +func expandContainerClusterAddonsConfigHttpLoadBalancing(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDisabled, err := expandContainerClusterAddonsConfigHttpLoadBalancingDisabled(original["disabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisabled); val.IsValid() && !isEmptyValue(val) { + transformed["disabled"] = transformedDisabled + } + + return transformed, nil +} + +func expandContainerClusterAddonsConfigHttpLoadBalancingDisabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterAddonsConfigHorizontalPodAutoscaling(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDisabled, err := expandContainerClusterAddonsConfigHorizontalPodAutoscalingDisabled(original["disabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisabled); val.IsValid() && !isEmptyValue(val) { + transformed["disabled"] = transformedDisabled + } + + return transformed, nil +} + +func expandContainerClusterAddonsConfigHorizontalPodAutoscalingDisabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterAddonsConfigKubernetesDashboard(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDisabled, err := expandContainerClusterAddonsConfigKubernetesDashboardDisabled(original["disabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisabled); val.IsValid() && !isEmptyValue(val) { + transformed["disabled"] = transformedDisabled + } + + return transformed, nil +} + +func expandContainerClusterAddonsConfigKubernetesDashboardDisabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterAddonsConfigNetworkPolicyConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDisabled, err := expandContainerClusterAddonsConfigNetworkPolicyConfigDisabled(original["disabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisabled); val.IsValid() && !isEmptyValue(val) { + transformed["disabled"] = transformedDisabled + } + + return transformed, nil +} + +func expandContainerClusterAddonsConfigNetworkPolicyConfigDisabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNodeLocations(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + return v, nil +} + +func expandContainerClusterResourceLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandContainerClusterLabelFingerprint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNetworkPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedProvider, err := expandContainerClusterNetworkPolicyProvider(original["provider"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedProvider); val.IsValid() && !isEmptyValue(val) { + transformed["provider"] = transformedProvider + } + + transformedEnabled, err := expandContainerClusterNetworkPolicyEnabled(original["enabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnabled); val.IsValid() && !isEmptyValue(val) { + transformed["enabled"] = transformedEnabled + } + + return transformed, nil +} + +func expandContainerClusterNetworkPolicyProvider(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterNetworkPolicyEnabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterIpAllocationPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedUseIpAliases, err := expandContainerClusterIpAllocationPolicyUseIpAliases(original["use_ip_aliases"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedUseIpAliases); val.IsValid() && !isEmptyValue(val) { + transformed["useIpAliases"] = transformedUseIpAliases + } + + transformedCreateSubnetwork, err := expandContainerClusterIpAllocationPolicyCreateSubnetwork(original["create_subnetwork"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCreateSubnetwork); val.IsValid() && !isEmptyValue(val) { + transformed["createSubnetwork"] = transformedCreateSubnetwork + } + + transformedSubnetworkName, err := expandContainerClusterIpAllocationPolicySubnetworkName(original["subnetwork_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSubnetworkName); val.IsValid() && !isEmptyValue(val) { + transformed["subnetworkName"] = transformedSubnetworkName + } + + transformedClusterSecondaryRangeName, err := expandContainerClusterIpAllocationPolicyClusterSecondaryRangeName(original["cluster_secondary_range_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedClusterSecondaryRangeName); val.IsValid() && !isEmptyValue(val) { + transformed["clusterSecondaryRangeName"] = transformedClusterSecondaryRangeName + } + + transformedServicesSecondaryRangeName, err := expandContainerClusterIpAllocationPolicyServicesSecondaryRangeName(original["services_secondary_range_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedServicesSecondaryRangeName); val.IsValid() && !isEmptyValue(val) { + transformed["servicesSecondaryRangeName"] = transformedServicesSecondaryRangeName + } + + transformedClusterIpv4CidrBlock, err := expandContainerClusterIpAllocationPolicyClusterIpv4CidrBlock(original["cluster_ipv4_cidr_block"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedClusterIpv4CidrBlock); val.IsValid() && !isEmptyValue(val) { + transformed["clusterIpv4CidrBlock"] = transformedClusterIpv4CidrBlock + } + + transformedNodeIpv4CidrBlock, err := expandContainerClusterIpAllocationPolicyNodeIpv4CidrBlock(original["node_ipv4_cidr_block"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNodeIpv4CidrBlock); val.IsValid() && !isEmptyValue(val) { + transformed["nodeIpv4CidrBlock"] = transformedNodeIpv4CidrBlock + } + + transformedServicesIpv4CidrBlock, err := expandContainerClusterIpAllocationPolicyServicesIpv4CidrBlock(original["services_ipv4_cidr_block"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedServicesIpv4CidrBlock); val.IsValid() && !isEmptyValue(val) { + transformed["servicesIpv4CidrBlock"] = transformedServicesIpv4CidrBlock + } + + transformedTPUIpv4CidrBlock, err := expandContainerClusterIpAllocationPolicyTPUIpv4CidrBlock(original["tpu_ipv4_cidr_block"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTPUIpv4CidrBlock); val.IsValid() && !isEmptyValue(val) { + transformed["tpuIpv4CidrBlock"] = transformedTPUIpv4CidrBlock + } + + return transformed, nil +} + +func expandContainerClusterIpAllocationPolicyUseIpAliases(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterIpAllocationPolicyCreateSubnetwork(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterIpAllocationPolicySubnetworkName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterIpAllocationPolicyClusterSecondaryRangeName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterIpAllocationPolicyServicesSecondaryRangeName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterIpAllocationPolicyClusterIpv4CidrBlock(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterIpAllocationPolicyNodeIpv4CidrBlock(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterIpAllocationPolicyServicesIpv4CidrBlock(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterIpAllocationPolicyTPUIpv4CidrBlock(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMinMasterVersion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterEnableTpu(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterTPUIpv4CidrBlock(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMasterAuthorizedNetworksConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnabled, err := expandContainerClusterMasterAuthorizedNetworksConfigEnabled(original["enabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnabled); val.IsValid() && !isEmptyValue(val) { + transformed["enabled"] = transformedEnabled + } + + transformedCidrBlocks, err := expandContainerClusterMasterAuthorizedNetworksConfigCidrBlocks(original["cidr_blocks"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCidrBlocks); val.IsValid() && !isEmptyValue(val) { + transformed["cidrBlocks"] = transformedCidrBlocks + } + + return transformed, nil +} + +func expandContainerClusterMasterAuthorizedNetworksConfigEnabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMasterAuthorizedNetworksConfigCidrBlocks(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDisplayName, err := expandContainerClusterMasterAuthorizedNetworksConfigCidrBlocksDisplayName(original["display_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisplayName); val.IsValid() && !isEmptyValue(val) { + transformed["displayName"] = transformedDisplayName + } + + transformedCidrBlock, err := expandContainerClusterMasterAuthorizedNetworksConfigCidrBlocksCidrBlock(original["cidr_block"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCidrBlock); val.IsValid() && !isEmptyValue(val) { + transformed["cidrBlock"] = transformedCidrBlock + } + + req = append(req, transformed) + } + return req, nil +} + +func expandContainerClusterMasterAuthorizedNetworksConfigCidrBlocksDisplayName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterMasterAuthorizedNetworksConfigCidrBlocksCidrBlock(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterLocation(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterKubectlPath(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerClusterKubectlContext(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func GetContainerNodePoolCaiObject(d TerraformResourceData, config *Config) (Asset, error) { + name, err := assetName(d, config, "//container.googleapis.com/projects/{{project}}/locations/{{location}}/clusters/{{cluster}}/nodePools/{{name}}") + if err != nil { + return Asset{}, err + } + if obj, err := GetContainerNodePoolApiObject(d, config); err == nil { + return Asset{ + Name: name, + Type: "container.googleapis.com/NodePool", + Resource: &AssetResource{ + Version: "v1", + DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/container/v1/rest", + DiscoveryName: "NodePool", + Data: obj, + }, + }, nil + } else { + return Asset{}, err + } +} + +func GetContainerNodePoolApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) { + obj := make(map[string]interface{}) + nameProp, err := expandContainerNodePoolName(d.Get("name"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { + obj["name"] = nameProp + } + configProp, err := expandContainerNodePoolNodeConfig(d.Get("node_config"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("node_config"); !isEmptyValue(reflect.ValueOf(configProp)) && (ok || !reflect.DeepEqual(v, configProp)) { + obj["config"] = configProp + } + initialNodeCountProp, err := expandContainerNodePoolInitialNodeCount(d.Get("initial_node_count"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("initial_node_count"); !isEmptyValue(reflect.ValueOf(initialNodeCountProp)) && (ok || !reflect.DeepEqual(v, initialNodeCountProp)) { + obj["initialNodeCount"] = initialNodeCountProp + } + versionProp, err := expandContainerNodePoolVersion(d.Get("version"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("version"); !isEmptyValue(reflect.ValueOf(versionProp)) && (ok || !reflect.DeepEqual(v, versionProp)) { + obj["version"] = versionProp + } + autoscalingProp, err := expandContainerNodePoolAutoscaling(d.Get("autoscaling"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("autoscaling"); !isEmptyValue(reflect.ValueOf(autoscalingProp)) && (ok || !reflect.DeepEqual(v, autoscalingProp)) { + obj["autoscaling"] = autoscalingProp + } + managementProp, err := expandContainerNodePoolManagement(d.Get("management"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("management"); !isEmptyValue(reflect.ValueOf(managementProp)) && (ok || !reflect.DeepEqual(v, managementProp)) { + obj["management"] = managementProp + } + maxPodsConstraintProp, err := expandContainerNodePoolMaxPodsPerNode(d.Get("max_pods_per_node"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("max_pods_per_node"); !isEmptyValue(reflect.ValueOf(maxPodsConstraintProp)) && (ok || !reflect.DeepEqual(v, maxPodsConstraintProp)) { + obj["maxPodsConstraint"] = maxPodsConstraintProp + } + clusterProp, err := expandContainerNodePoolCluster(d.Get("cluster"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("cluster"); !isEmptyValue(reflect.ValueOf(clusterProp)) && (ok || !reflect.DeepEqual(v, clusterProp)) { + obj["cluster"] = clusterProp + } + locationProp, err := expandContainerNodePoolLocation(d.Get("location"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("location"); !isEmptyValue(reflect.ValueOf(locationProp)) && (ok || !reflect.DeepEqual(v, locationProp)) { + obj["location"] = locationProp + } + + return obj, nil +} + +func expandContainerNodePoolName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedMachineType, err := expandContainerNodePoolNodeConfigMachineType(original["machine_type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMachineType); val.IsValid() && !isEmptyValue(val) { + transformed["machineType"] = transformedMachineType + } + + transformedDiskSizeGb, err := expandContainerNodePoolNodeConfigDiskSizeGb(original["disk_size_gb"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDiskSizeGb); val.IsValid() && !isEmptyValue(val) { + transformed["diskSizeGb"] = transformedDiskSizeGb + } + + transformedOauthScopes, err := expandContainerNodePoolNodeConfigOauthScopes(original["oauth_scopes"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedOauthScopes); val.IsValid() && !isEmptyValue(val) { + transformed["oauthScopes"] = transformedOauthScopes + } + + transformedServiceAccount, err := expandContainerNodePoolNodeConfigServiceAccount(original["service_account"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedServiceAccount); val.IsValid() && !isEmptyValue(val) { + transformed["serviceAccount"] = transformedServiceAccount + } + + transformedMetadata, err := expandContainerNodePoolNodeConfigMetadata(original["metadata"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMetadata); val.IsValid() && !isEmptyValue(val) { + transformed["metadata"] = transformedMetadata + } + + transformedImageType, err := expandContainerNodePoolNodeConfigImageType(original["image_type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedImageType); val.IsValid() && !isEmptyValue(val) { + transformed["imageType"] = transformedImageType + } + + transformedLabels, err := expandContainerNodePoolNodeConfigLabels(original["labels"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedLabels); val.IsValid() && !isEmptyValue(val) { + transformed["labels"] = transformedLabels + } + + transformedLocalSsdCount, err := expandContainerNodePoolNodeConfigLocalSsdCount(original["local_ssd_count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedLocalSsdCount); val.IsValid() && !isEmptyValue(val) { + transformed["localSsdCount"] = transformedLocalSsdCount + } + + transformedTags, err := expandContainerNodePoolNodeConfigTags(original["tags"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTags); val.IsValid() && !isEmptyValue(val) { + transformed["tags"] = transformedTags + } + + transformedPreemptible, err := expandContainerNodePoolNodeConfigPreemptible(original["preemptible"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPreemptible); val.IsValid() && !isEmptyValue(val) { + transformed["preemptible"] = transformedPreemptible + } + + transformedGuestAccelerator, err := expandContainerNodePoolNodeConfigGuestAccelerator(original["guest_accelerator"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedGuestAccelerator); val.IsValid() && !isEmptyValue(val) { + transformed["accelerators"] = transformedGuestAccelerator + } + + transformedDiskType, err := expandContainerNodePoolNodeConfigDiskType(original["disk_type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDiskType); val.IsValid() && !isEmptyValue(val) { + transformed["diskType"] = transformedDiskType + } + + transformedMinCpuPlatform, err := expandContainerNodePoolNodeConfigMinCpuPlatform(original["min_cpu_platform"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMinCpuPlatform); val.IsValid() && !isEmptyValue(val) { + transformed["minCpuPlatform"] = transformedMinCpuPlatform + } + + transformedTaint, err := expandContainerNodePoolNodeConfigTaint(original["taint"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTaint); val.IsValid() && !isEmptyValue(val) { + transformed["taints"] = transformedTaint + } + + return transformed, nil +} + +func expandContainerNodePoolNodeConfigMachineType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigDiskSizeGb(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigServiceAccount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigMetadata(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandContainerNodePoolNodeConfigImageType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandContainerNodePoolNodeConfigLocalSsdCount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigTags(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigPreemptible(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigGuestAccelerator(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedCount, err := expandContainerNodePoolNodeConfigGuestAcceleratorCount(original["count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCount); val.IsValid() && !isEmptyValue(val) { + transformed["acceleratorCount"] = transformedCount + } + + transformedType, err := expandContainerNodePoolNodeConfigGuestAcceleratorType(original["type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedType); val.IsValid() && !isEmptyValue(val) { + transformed["acceleratorType"] = transformedType + } + + req = append(req, transformed) + } + return req, nil +} + +func expandContainerNodePoolNodeConfigGuestAcceleratorCount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigGuestAcceleratorType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigDiskType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigMinCpuPlatform(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigTaint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedKey, err := expandContainerNodePoolNodeConfigTaintKey(original["key"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKey); val.IsValid() && !isEmptyValue(val) { + transformed["key"] = transformedKey + } + + transformedValue, err := expandContainerNodePoolNodeConfigTaintValue(original["value"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedValue); val.IsValid() && !isEmptyValue(val) { + transformed["value"] = transformedValue + } + + transformedEffect, err := expandContainerNodePoolNodeConfigTaintEffect(original["effect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEffect); val.IsValid() && !isEmptyValue(val) { + transformed["effect"] = transformedEffect + } + + req = append(req, transformed) + } + return req, nil +} + +func expandContainerNodePoolNodeConfigTaintKey(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigTaintValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolNodeConfigTaintEffect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolInitialNodeCount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolVersion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolAutoscaling(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnabled, err := expandContainerNodePoolAutoscalingEnabled(original["enabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnabled); val.IsValid() && !isEmptyValue(val) { + transformed["enabled"] = transformedEnabled + } + + transformedMinNodeCount, err := expandContainerNodePoolAutoscalingMinNodeCount(original["min_node_count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMinNodeCount); val.IsValid() && !isEmptyValue(val) { + transformed["minNodeCount"] = transformedMinNodeCount + } + + transformedMaxNodeCount, err := expandContainerNodePoolAutoscalingMaxNodeCount(original["max_node_count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxNodeCount); val.IsValid() && !isEmptyValue(val) { + transformed["maxNodeCount"] = transformedMaxNodeCount + } + + return transformed, nil +} + +func expandContainerNodePoolAutoscalingEnabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolAutoscalingMinNodeCount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolAutoscalingMaxNodeCount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolManagement(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedAutoUpgrade, err := expandContainerNodePoolManagementAutoUpgrade(original["auto_upgrade"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAutoUpgrade); val.IsValid() && !isEmptyValue(val) { + transformed["autoUpgrade"] = transformedAutoUpgrade + } + + transformedAutoRepair, err := expandContainerNodePoolManagementAutoRepair(original["auto_repair"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAutoRepair); val.IsValid() && !isEmptyValue(val) { + transformed["autoRepair"] = transformedAutoRepair + } + + return transformed, nil +} + +func expandContainerNodePoolManagementAutoUpgrade(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolManagementAutoRepair(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandContainerNodePoolCluster(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseGlobalFieldValue("clusters", v.(string), "project", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for cluster: %s", err) + } + return f.RelativeLink(), nil +} + +func expandContainerNodePoolLocation(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} From b12f9a05ceace04bba8d7b05efd0c9a107e11dee Mon Sep 17 00:00:00 2001 From: Chris Sng Date: Thu, 31 Oct 2019 02:00:47 +0800 Subject: [PATCH 85/87] Fix nil reference to Shielded Nodes (#2555) Merged PR #2555. --- .../terraform/resources/resource_container_cluster.go.erb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/third_party/terraform/resources/resource_container_cluster.go.erb b/third_party/terraform/resources/resource_container_cluster.go.erb index a6ad01425e38..5e8c98a01609 100644 --- a/third_party/terraform/resources/resource_container_cluster.go.erb +++ b/third_party/terraform/resources/resource_container_cluster.go.erb @@ -1287,7 +1287,9 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro d.Set("network", cluster.NetworkConfig.Network) d.Set("subnetwork", cluster.NetworkConfig.Subnetwork) <% unless version == 'ga' -%> - d.Set("enable_shielded_nodes", cluster.ShieldedNodes.Enabled) + if cluster.ShieldedNodes != nil { + d.Set("enable_shielded_nodes", cluster.ShieldedNodes.Enabled) + } d.Set("enable_binary_authorization", cluster.BinaryAuthorization != nil && cluster.BinaryAuthorization.Enabled) d.Set("enable_tpu", cluster.EnableTpu) d.Set("tpu_ipv4_cidr_block", cluster.TpuIpv4CidrBlock) From bb3203c811e5df4bca4699cfc9cae695db49b665 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Wed, 30 Oct 2019 11:24:59 -0700 Subject: [PATCH 86/87] Ansible - Compute Target Instances + Reservations (#2560) Merged PR #2560. --- build/ansible | 2 +- build/inspec | 2 +- products/compute/ansible.yaml | 8 --- products/compute/ansible_version_added.yaml | 48 ++++++++++++++++++ products/compute/api.yaml | 1 + .../compute/examples/ansible/reservation.yaml | 26 ++++++++++ .../examples/ansible/target_instance.yaml | 50 +++++++++++++++++++ 7 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 products/compute/examples/ansible/reservation.yaml create mode 100644 products/compute/examples/ansible/target_instance.yaml diff --git a/build/ansible b/build/ansible index c5f24a9ef7d4..424109a1d013 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit c5f24a9ef7d4a64110e66049c39ed1a32ccfb1bf +Subproject commit 424109a1d0130a75707c008a9b032be379a210e2 diff --git a/build/inspec b/build/inspec index 2aa5e47d5b0f..eb043332ae44 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 2aa5e47d5b0fb12d7098d24536c96c83bd748e77 +Subproject commit eb043332ae4494de652e941ae9b7cfacb2bafca6 diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index bf95933f6c79..e4aebc663da5 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -47,14 +47,10 @@ datasources: !ruby/object:Overrides::ResourceOverrides exclude: true RegionUrlMap: !ruby/object:Overrides::Ansible::ResourceOverride exclude: true - Reservation: !ruby/object:Overrides::Ansible::ResourceOverride - exclude: true ResourcePolicy: !ruby/object:Overrides::Ansible::ResourceOverride exclude: true RouterNat: !ruby/object:Overrides::Ansible::ResourceOverride exclude: true - TargetInstance: !ruby/object:Overrides::Ansible::ResourceOverride - exclude: true ExternalVpnGateway: !ruby/object:Overrides::Ansible::ResourceOverride exclude: true Zone: !ruby/object:Overrides::Ansible::ResourceOverride @@ -318,12 +314,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides exclude: true RegionUrlMap: !ruby/object:Overrides::Ansible::ResourceOverride exclude: true - Reservation: !ruby/object:Overrides::Ansible::ResourceOverride - exclude: true RouterNat: !ruby/object:Overrides::Ansible::ResourceOverride exclude: true - TargetInstance: !ruby/object:Overrides::Ansible::ResourceOverride - exclude: true Zone: !ruby/object:Overrides::Ansible::ResourceOverride exclude: true files: !ruby/object:Provider::Config::Files diff --git a/products/compute/ansible_version_added.yaml b/products/compute/ansible_version_added.yaml index 539e36710653..b16a6b35811c 100644 --- a/products/compute/ansible_version_added.yaml +++ b/products/compute/ansible_version_added.yaml @@ -56,6 +56,8 @@ :version_added: '2.9' :SslCertificate: :version_added: '2.7' + :Reservation: + :version_added: '2.10' :SslPolicy: :version_added: '2.7' :Subnetwork: @@ -64,6 +66,8 @@ :version_added: '2.7' :TargetHttpsProxy: :version_added: '2.7' + :TargetInstance: + :version_added: '2.10' :TargetPool: :version_added: '2.7' :TargetSslProxy: @@ -1065,6 +1069,38 @@ :version_added: '2.6' :privateKey: :version_added: '2.6' + :Reservation: + :version_added: '2.10' + :description: + :version_added: '2.10' + :name: + :version_added: '2.10' + :specificReservationRequired: + :version_added: '2.10' + :specificReservation: + :version_added: '2.10' + :count: + :version_added: '2.10' + :instanceProperties: + :version_added: '2.10' + :machineType: + :version_added: '2.10' + :minCpuPlatform: + :version_added: '2.10' + :guestAccelerators: + :version_added: '2.10' + :acceleratorType: + :version_added: '2.10' + :acceleratorCount: + :version_added: '2.10' + :localSsds: + :version_added: '2.10' + :interface: + :version_added: '2.10' + :diskSizeGb: + :version_added: '2.10' + :zone: + :version_added: '2.10' :SslPolicy: :version_added: '2.7' :description: @@ -1121,6 +1157,18 @@ :version_added: '2.8' :urlMap: :version_added: '2.6' + :TargetInstance: + :version_added: '2.10' + :name: + :version_added: '2.10' + :description: + :version_added: '2.10' + :instance: + :version_added: '2.10' + :natPolicy: + :version_added: '2.10' + :zone: + :version_added: '2.10' :TargetPool: :version_added: '2.6' :backupPool: diff --git a/products/compute/api.yaml b/products/compute/api.yaml index b54ab386f1f7..bf9bec7864c6 100644 --- a/products/compute/api.yaml +++ b/products/compute/api.yaml @@ -9242,6 +9242,7 @@ objects: name: 'TargetInstance' kind: 'compute#targetInstance' base_url: projects/{{project}}/zones/{{zone}}/targetInstances + collection_url_key: 'items' has_self_link: true input: true description: | diff --git a/products/compute/examples/ansible/reservation.yaml b/products/compute/examples/ansible/reservation.yaml new file mode 100644 index 000000000000..e4d2f39319ac --- /dev/null +++ b/products/compute/examples/ansible/reservation.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::Example +task: !ruby/object:Provider::Ansible::Task + name: gcp_compute_reservation + code: + name: <%= ctx[:name] %> + zone: 'us-central1-a' + specific_reservation: + count: 1 + instance_properties: + min_cpu_platform: "Intel Cascade Lake" + machine_type: 'n2-standard-2' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> diff --git a/products/compute/examples/ansible/target_instance.yaml b/products/compute/examples/ansible/target_instance.yaml new file mode 100644 index 000000000000..780a3e57793f --- /dev/null +++ b/products/compute/examples/ansible/target_instance.yaml @@ -0,0 +1,50 @@ +# 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 +dependencies: + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_network + register: network + code: + name: <%= dependency_name('network', 'instance') %> + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_instance + code: + name: <%= ctx[:name] %> + machine_type: n1-standard-1 + disks: + - auto_delete: true + boot: true + initialize_params: + source_image: 'projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts' + labels: + environment: production + network_interfaces: + - network: "{{ network }}" + zone: 'us-central1-a' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + register: instance +task: !ruby/object:Provider::Ansible::Task + name: gcp_compute_target_instance + code: + name: 'target' + instance: "{{ instance }}" + zone: 'us-central1-a' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> From eee0398090a34626ff4bcaea72d6a19e155bea4c Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 30 Oct 2019 12:59:27 -0700 Subject: [PATCH 87/87] Add iamMember to bigquery dataset access, example in InSpec test to use it (#2566) Merged PR #2566. --- products/bigquery/ansible.yaml | 2 ++ products/bigquery/api.yaml | 5 +++++ products/bigquery/terraform.yaml | 2 ++ .../google_bigquery_dataset/google_bigquery_datasets.erb | 9 +++++++++ 4 files changed, 18 insertions(+) diff --git a/products/bigquery/ansible.yaml b/products/bigquery/ansible.yaml index 05cb87d38d5f..2d28224c6640 100644 --- a/products/bigquery/ansible.yaml +++ b/products/bigquery/ansible.yaml @@ -35,6 +35,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides # Override self_link due to GoogleCloudPlatform/magic-modules#2219 self_link: "projects/{project}/datasets/{name}" properties: + access.iamMember: !ruby/object:Overrides::Terraform::PropertyOverride + exclude: true access.view.tableId: !ruby/object:Overrides::Ansible::PropertyOverride # Ansible linter doesn't like the (_) notation. description: | diff --git a/products/bigquery/api.yaml b/products/bigquery/api.yaml index a8b05da8dc4d..e287a628f7eb 100644 --- a/products/bigquery/api.yaml +++ b/products/bigquery/api.yaml @@ -83,6 +83,11 @@ objects: description: | An email address of a user to grant access to. For example: fred@example.com + - !ruby/object:Api::Type::String + name: 'iamMember' + description: | + Some other type of member that appears in the IAM Policy but isn't a user, + group, domain, or special group. For example: `allUsers` - !ruby/object:Api::Type::NestedObject name: 'view' description: | diff --git a/products/bigquery/terraform.yaml b/products/bigquery/terraform.yaml index 7ad39173574a..f1ceb67f45a6 100644 --- a/products/bigquery/terraform.yaml +++ b/products/bigquery/terraform.yaml @@ -43,6 +43,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides access: !ruby/object:Overrides::Terraform::PropertyOverride default_from_api: true is_set: true + access.iamMember: !ruby/object:Overrides::Terraform::PropertyOverride + exclude: true defaultTableExpirationMs: !ruby/object:Overrides::Terraform::PropertyOverride validation: !ruby/object:Provider::Terraform::Validation function: 'validateDefaultTableExpirationMs' diff --git a/templates/inspec/examples/google_bigquery_dataset/google_bigquery_datasets.erb b/templates/inspec/examples/google_bigquery_dataset/google_bigquery_datasets.erb index 5619decc767e..326afb3e0ae7 100644 --- a/templates/inspec/examples/google_bigquery_dataset/google_bigquery_datasets.erb +++ b/templates/inspec/examples/google_bigquery_dataset/google_bigquery_datasets.erb @@ -4,4 +4,13 @@ describe google_bigquery_datasets(project: <%= doc_generation ? "#{gcp_project_i its('count') { should be >= 1 } its('friendly_names') { should include <%= doc_generation ? "'#{dataset['friendly_name']}'" : "dataset['friendly_name']" -%> } its('locations') { should include <%= doc_generation ? "'#{dataset['location']}'" : "dataset['location']" -%> } +end + +google_bigquery_datasets(project: <%= doc_generation ? "#{gcp_project_id}" : "gcp_project_id" -%>).ids.each do |name| + google_bigquery_dataset(project: <%= doc_generation ? "#{gcp_project_id}" : "gcp_project_id" -%>, name: name.split(':').last).access.each do |access| + describe access do + # No bigquery dataset should allow access to allUsers + its('iam_member') { should_not cmp 'allUsers' } + end + end end \ No newline at end of file