From c798625868a89dcb9079b7f1d6909c3017e77002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taneli=20Lepp=C3=A4?= Date: Sat, 27 Aug 2022 22:58:11 +0200 Subject: [PATCH] net-glb: Added support for regional external HTTP(s) load balancing. (#775) --- modules/net-glb/README.md | 64 +- modules/net-glb/backend-services.tf | 3 +- modules/net-glb/global-forwarding-rule.tf | 13 +- modules/net-glb/health-checks.tf | 105 ++- modules/net-glb/outputs.tf | 11 +- modules/net-glb/regional-backend-services.tf | 164 +++++ modules/net-glb/regional-forwarding-rule.tf | 62 ++ modules/net-glb/regional-url-map.tf | 717 +++++++++++++++++++ modules/net-glb/target-proxy.tf | 27 +- modules/net-glb/url-map.tf | 2 + modules/net-glb/variables.tf | 27 + tests/modules/net_glb/fixture/main.tf | 3 + tests/modules/net_glb/fixture/variables.tf | 27 + tests/modules/net_glb/test_plan.py | 296 ++++---- 14 files changed, 1367 insertions(+), 154 deletions(-) create mode 100644 modules/net-glb/regional-backend-services.tf create mode 100644 modules/net-glb/regional-forwarding-rule.tf create mode 100644 modules/net-glb/regional-url-map.tf diff --git a/modules/net-glb/README.md b/modules/net-glb/README.md index d754ae66..c15229ab 100644 --- a/modules/net-glb/README.md +++ b/modules/net-glb/README.md @@ -481,6 +481,40 @@ resource "tls_self_signed_cert" "self_signed_cert" { # tftest modules=1 resources=6 ``` +## Regional Load Balancing + +You can also use regional load balancing by specifying a `region` parameter: + +```hcl +module "glb" { + source = "./modules/net-glb" + name = "glb-test" + project_id = var.project_id + region = var.region + + backend_services_config = { + my-group-backend = { + bucket_config = null + enable_cdn = false + cdn_config = null + group_config = { + backends = [ + { + group = "my_test_group" + options = null + } + ], + health_checks = [] + log_config = null + options = null + } + } + } +} +# tftest modules=1 resources=5 +``` + + ## Components And Files Mapping An External Global Load Balancer is made of multiple components, that change depending on the configurations. Sometimes, it may be tricky to understand what they are, and how they relate to each other. Following, we provide a very brief overview to become more familiar with them. @@ -500,10 +534,13 @@ Target proxies leverage [url-maps](url-map.tf): set of L7 rules, which create a |---|---|---| | [backend-services.tf](./backend-services.tf) | Bucket and group backend services. | google_compute_backend_bucket · google_compute_backend_service | | [global-forwarding-rule.tf](./global-forwarding-rule.tf) | Global address and forwarding rule. | google_compute_global_address · google_compute_global_forwarding_rule | -| [health-checks.tf](./health-checks.tf) | Health checks. | google_compute_health_check | +| [health-checks.tf](./health-checks.tf) | Health checks. | google_compute_health_check · google_compute_region_health_check | | [outputs.tf](./outputs.tf) | Module outputs. | | +| [regional-backend-services.tf](./regional-backend-services.tf) | Bucket and group backend services for regional load balancers. | google_compute_region_backend_service | +| [regional-forwarding-rule.tf](./regional-forwarding-rule.tf) | Global address and forwarding rule. | google_compute_address · google_compute_forwarding_rule | +| [regional-url-map.tf](./regional-url-map.tf) | regional URL maps. | google_compute_region_url_map | | [ssl-certificates.tf](./ssl-certificates.tf) | SSL certificates. | google_compute_managed_ssl_certificate · google_compute_ssl_certificate | -| [target-proxy.tf](./target-proxy.tf) | HTTP and HTTPS target proxies. | google_compute_target_http_proxy · google_compute_target_https_proxy | +| [target-proxy.tf](./target-proxy.tf) | HTTP and HTTPS target proxies. | google_compute_region_target_http_proxy · google_compute_region_target_https_proxy · google_compute_target_http_proxy · google_compute_target_https_proxy | | [url-map.tf](./url-map.tf) | URL maps. | google_compute_url_map | | [variables.tf](./variables.tf) | Module variables. | | | [versions.tf](./versions.tf) | Version pins. | | @@ -514,22 +551,25 @@ Target proxies leverage [url-maps](url-map.tf): set of L7 rules, which create a |---|---|:---:|:---:|:---:| | [name](variables.tf#L17) | Load balancer name. | string | ✓ | | | [project_id](variables.tf#L22) | Project id. | string | ✓ | | -| [backend_services_config](variables.tf#L56) | The backends services configuration. | map(object({…})) | | {} | -| [global_forwarding_rule_config](variables.tf#L202) | Global forwarding rule configurations. | object({…}) | | {…} | -| [health_checks_config](variables.tf#L45) | Custom health checks configuration. | map(object({…})) | | {} | -| [health_checks_config_defaults](variables.tf#L27) | Auto-created health check default configuration. | object({…}) | | {…} | -| [https](variables.tf#L220) | Whether to enable HTTPS. | bool | | false | -| [reserve_ip_address](variables.tf#L226) | Whether to reserve a static global IP address. | bool | | false | -| [ssl_certificates_config](variables.tf#L165) | The SSL certificate configuration. | map(object({…})) | | {} | -| [ssl_certificates_config_defaults](variables.tf#L178) | The SSL certificate default configuration. | object({…}) | | {…} | -| [target_proxy_https_config](variables.tf#L194) | The HTTPS target proxy configuration. | object({…}) | | null | -| [url_map_config](variables.tf#L151) | The url-map configuration. | object({…}) | | null | +| [backend_services_config](variables.tf#L62) | The backends services configuration. | map(object({…})) | | {} | +| [forwarding_rule_config](variables.tf#L226) | Regional forwarding rule configurations. | object({…}) | | {…} | +| [global_forwarding_rule_config](variables.tf#L208) | Global forwarding rule configurations. | object({…}) | | {…} | +| [health_checks_config](variables.tf#L51) | Custom health checks configuration. | map(object({…})) | | {} | +| [health_checks_config_defaults](variables.tf#L33) | Auto-created health check default configuration. | object({…}) | | {…} | +| [https](variables.tf#L247) | Whether to enable HTTPS. | bool | | false | +| [region](variables.tf#L27) | Create a regional load balancer in this region. | string | | null | +| [reserve_ip_address](variables.tf#L253) | Whether to reserve a static global IP address. | bool | | false | +| [ssl_certificates_config](variables.tf#L171) | The SSL certificate configuration. | map(object({…})) | | {} | +| [ssl_certificates_config_defaults](variables.tf#L184) | The SSL certificate default configuration. | object({…}) | | {…} | +| [target_proxy_https_config](variables.tf#L200) | The HTTPS target proxy configuration. | object({…}) | | null | +| [url_map_config](variables.tf#L157) | The url-map configuration. | object({…}) | | null | ## Outputs | name | description | sensitive | |---|---|:---:| | [backend_services](outputs.tf#L22) | Backend service resources. | | +| [forwarding_rule](outputs.tf#L67) | The regional forwarding rule. | | | [global_forwarding_rule](outputs.tf#L62) | The global forwarding rule. | | | [health_checks](outputs.tf#L17) | Health-check resources. | | | [ip_address](outputs.tf#L44) | The reserved global IP address. | | diff --git a/modules/net-glb/backend-services.tf b/modules/net-glb/backend-services.tf index 9bb92501..3ca93da8 100644 --- a/modules/net-glb/backend-services.tf +++ b/modules/net-glb/backend-services.tf @@ -60,7 +60,7 @@ resource "google_compute_backend_bucket" "bucket" { } resource "google_compute_backend_service" "group" { - for_each = local.backend_services_group + for_each = var.region == null ? local.backend_services_group : {} name = "${var.name}-${each.key}" project = var.project_id description = "Terraform managed." @@ -208,3 +208,4 @@ resource "google_compute_backend_service" "group" { } } } + diff --git a/modules/net-glb/global-forwarding-rule.tf b/modules/net-glb/global-forwarding-rule.tf index b4d525c9..54c268f0 100644 --- a/modules/net-glb/global-forwarding-rule.tf +++ b/modules/net-glb/global-forwarding-rule.tf @@ -17,26 +17,26 @@ # tfdoc:file:description Global address and forwarding rule. locals { - ip_address = ( - var.reserve_ip_address + ip_address = var.region == null ? ( + var.region == null && var.reserve_ip_address ? google_compute_global_address.static_ip.0.id : null - ) + ) : null port_range = coalesce( var.global_forwarding_rule_config.port_range, var.https ? "443" : "80" ) - target = ( + target = var.region == null ? ( var.https ? google_compute_target_https_proxy.https.0.id : google_compute_target_http_proxy.http.0.id - ) + ) : null } resource "google_compute_global_address" "static_ip" { - count = var.reserve_ip_address ? 1 : 0 + count = var.region == null && var.reserve_ip_address ? 1 : 0 provider = google-beta name = var.name project = var.project_id @@ -44,6 +44,7 @@ resource "google_compute_global_address" "static_ip" { } resource "google_compute_global_forwarding_rule" "forwarding_rule" { + count = var.region == null ? 1 : 0 provider = google-beta name = var.name project = var.project_id diff --git a/modules/net-glb/health-checks.tf b/modules/net-glb/health-checks.tf index 88a29548..0c0aad63 100644 --- a/modules/net-glb/health-checks.tf +++ b/modules/net-glb/health-checks.tf @@ -48,7 +48,7 @@ locals { } resource "google_compute_health_check" "health_check" { - for_each = local.health_checks_config + for_each = var.region == null ? local.health_checks_config : {} provider = google-beta name = "${var.name}-${each.key}" project = var.project_id @@ -148,3 +148,106 @@ resource "google_compute_health_check" "health_check" { } } } + +resource "google_compute_region_health_check" "health_check" { + for_each = var.region != null ? local.health_checks_config : {} + provider = google-beta + name = "${var.name}-${each.key}" + project = var.project_id + region = var.region + description = "Terraform managed." + check_interval_sec = try(each.value.options.check_interval_sec, null) + healthy_threshold = try(each.value.options.healthy_threshold, null) + timeout_sec = try(each.value.options.timeout_sec, null) + unhealthy_threshold = try(each.value.options.unhealthy_threshold, null) + + dynamic "http_health_check" { + for_each = ( + try(each.value.type, null) == "http" || try(each.value.type, null) == null + ? { 1 = 1 } + : {} + ) + content { + host = try(each.value.check.host, null) + port = try(each.value.check.port, null) + port_name = try(each.value.check.port_name, null) + port_specification = try(each.value.check.port_specification, null) + proxy_header = try(each.value.check.proxy_header, null) + request_path = try(each.value.check.request_path, null) + response = try(each.value.check.response, null) + } + } + + dynamic "https_health_check" { + for_each = ( + try(each.value.type, null) == "https" || try(each.value.type, null) == null + ? { 1 = 1 } + : {} + ) + content { + host = try(each.value.check.host, null) + port = try(each.value.check.port, null) + port_name = try(each.value.check.port_name, null) + port_specification = try(each.value.check.port_specification, null) + proxy_header = try(each.value.check.proxy_header, null) + request_path = try(each.value.check.request_path, null) + response = try(each.value.check.response, null) + } + } + + dynamic "tcp_health_check" { + for_each = ( + try(each.value.type, null) == "tcp" || try(each.value.type, null) == null + ? { 1 = 1 } + : {} + ) + content { + port = try(each.value.check.port, null) + port_name = try(each.value.check.port_name, null) + port_specification = try(each.value.check.port_specification, null) + proxy_header = try(each.value.check.proxy_header, null) + request = try(each.value.check.request, null) + response = try(each.value.check.response, null) + } + } + + dynamic "ssl_health_check" { + for_each = ( + try(each.value.type, null) == "ssl" || try(each.value.type, null) == null + ? { 1 = 1 } + : {} + ) + content { + port = try(each.value.check.port, null) + port_name = try(each.value.check.port_name, null) + port_specification = try(each.value.check.port_specification, null) + proxy_header = try(each.value.check.proxy_header, null) + request = try(each.value.check.request, null) + response = try(each.value.check.response, null) + } + } + + dynamic "http2_health_check" { + for_each = ( + try(each.value.type, null) == "http2" || try(each.value.type, null) == null + ? { 1 = 1 } + : {} + ) + content { + host = try(each.value.check.host, null) + port = try(each.value.check.port, null) + port_name = try(each.value.check.port_name, null) + port_specification = try(each.value.check.port_specification, null) + proxy_header = try(each.value.check.proxy_header, null) + request_path = try(each.value.check.request_path, null) + response = try(each.value.check.response, null) + } + } + + dynamic "log_config" { + for_each = try(each.value.logging, false) ? { 0 = 0 } : {} + content { + enable = true + } + } +} diff --git a/modules/net-glb/outputs.tf b/modules/net-glb/outputs.tf index 490378f2..8e2e4e58 100644 --- a/modules/net-glb/outputs.tf +++ b/modules/net-glb/outputs.tf @@ -43,12 +43,12 @@ output "ssl_certificates" { output "ip_address" { description = "The reserved global IP address." - value = try(google_compute_global_address.static_ip[0].address, null) + value = var.region == null ? try(google_compute_global_address.static_ip.0.address, null) : try(google_compute_address.static_ip.0.address, null) } output "ip_address_self_link" { description = "The URI of the reserved global IP address." - value = google_compute_global_forwarding_rule.forwarding_rule.ip_address + value = var.region == null ? google_compute_global_forwarding_rule.forwarding_rule.0.ip_address : google_compute_forwarding_rule.forwarding_rule.0.ip_address } output "target_proxy" { @@ -61,5 +61,10 @@ output "target_proxy" { output "global_forwarding_rule" { description = "The global forwarding rule." - value = google_compute_global_forwarding_rule.forwarding_rule + value = var.region == null ? google_compute_global_forwarding_rule.forwarding_rule.0 : null +} + +output "forwarding_rule" { + description = "The regional forwarding rule." + value = var.region == null ? google_compute_global_forwarding_rule.forwarding_rule.0 : google_compute_forwarding_rule.forwarding_rule.0 } diff --git a/modules/net-glb/regional-backend-services.tf b/modules/net-glb/regional-backend-services.tf new file mode 100644 index 00000000..228c958a --- /dev/null +++ b/modules/net-glb/regional-backend-services.tf @@ -0,0 +1,164 @@ +/** + * Copyright 2022 Google LLC + * + * 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. + */ + +# tfdoc:file:description Bucket and group backend services for regional load balancers. + +resource "google_compute_region_backend_service" "group" { + for_each = var.region != null ? local.backend_services_group : {} + name = "${var.name}-${each.key}" + project = var.project_id + region = var.region + description = "Terraform managed." + affinity_cookie_ttl_sec = try(each.value.group_config.options.affinity_cookie_ttl_sec, null) + enable_cdn = try(each.value.enable_cdn, null) + connection_draining_timeout_sec = try(each.value.group_config.options.connection_draining_timeout_sec, null) + load_balancing_scheme = try(each.value.group_config.options.load_balancing_scheme, null) + locality_lb_policy = try(each.value.group_config.options.locality_lb_policy, null) + port_name = try(each.value.group_config.options.port_name, null) + protocol = try(each.value.group_config.options.protocol, null) + session_affinity = try(each.value.group_config.options.session_affinity, null) + timeout_sec = try(each.value.group_config.options.timeout_sec, null) + + # If no health checks are defined, use the default one. + # Otherwise, look in the health_checks_config map. + # Otherwise, use the health_check id as is (already existing). + health_checks = ( + try(length(each.value.group_config.health_checks), 0) == 0 + ? try( + [google_compute_region_health_check.health_check["default"].id], + null + ) + : [ + for hc in each.value.group_config.health_checks : + try(google_compute_region_health_check.health_check[hc].id, hc) + ] + ) + + dynamic "backend" { + for_each = try(each.value.group_config.backends, []) + content { + balancing_mode = try(backend.value.options.balancing_mode, null) + capacity_scaler = try(backend.value.options.capacity_scaler, null) + group = try(backend.value.group, null) + max_connections = try(backend.value.options.max_connections, null) + max_connections_per_instance = try(backend.value.options.max_connections_per_instance, null) + max_connections_per_endpoint = try(backend.value.options.max_connections_per_endpoint, null) + max_rate = try(backend.value.options.max_rate, null) + max_rate_per_instance = try(backend.value.options.max_rate_per_instance, null) + max_rate_per_endpoint = try(backend.value.options.max_rate_per_endpoint, null) + max_utilization = try(backend.value.options.max_utilization, null) + } + } + + dynamic "circuit_breakers" { + for_each = ( + try(each.value.group_config.options.circuit_breakers, null) == null + ? [] + : [each.value.group_config.options.circuit_breakers] + ) + iterator = cb + content { + max_requests_per_connection = try(cb.value.max_requests_per_connection, null) + max_connections = try(cb.value.max_connections, null) + max_pending_requests = try(cb.value.max_pending_requests, null) + max_requests = try(cb.value.max_requests, null) + max_retries = try(cb.value.max_retries, null) + } + } + + dynamic "consistent_hash" { + for_each = ( + try(each.value.group_config.options.consistent_hash, null) == null + ? [] + : [each.value.group_config.options.consistent_hash] + ) + content { + http_header_name = try(consistent_hash.value.http_header_name, null) + minimum_ring_size = try(consistent_hash.value.minimum_ring_size, null) + + dynamic "http_cookie" { + for_each = try(consistent_hash.value.http_cookie, null) == null ? [] : [consistent_hash.value.http_cookie] + content { + name = try(http_cookie.value.name, null) + path = try(http_cookie.value.path, null) + + dynamic "ttl" { + for_each = try(consistent_hash.value.ttl, null) == null ? [] : [consistent_hash.value.ttl] + content { + seconds = try(ttl.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive + nanos = try(ttl.value.nanos, null) # Must be from 0 to 999,999,999 inclusive + } + } + } + } + } + } + + dynamic "cdn_policy" { + for_each = ( + try(each.value.cdn_policy, null) == null + ? [] + : [each.value.cdn_policy] + ) + iterator = cdn_policy + content { + signed_url_cache_max_age_sec = try(cdn_policy.value.signed_url_cache_max_age_sec, null) + default_ttl = try(cdn_policy.value.default_ttl, null) + max_ttl = try(cdn_policy.value.max_ttl, null) + client_ttl = try(cdn_policy.value.client_ttl, null) + negative_caching = try(cdn_policy.value.negative_caching, null) + cache_mode = try(cdn_policy.value.cache_mode, null) + serve_while_stale = try(cdn_policy.value.serve_while_stale, null) + + dynamic "negative_caching_policy" { + for_each = ( + try(cdn_policy.value.negative_caching_policy, null) == null + ? [] + : [cdn_policy.value.negative_caching_policy] + ) + iterator = ncp + content { + code = try(ncp.value.code, null) + } + } + } + } + + dynamic "iap" { + for_each = ( + try(each.value.group_config.options.iap, null) == null + ? [] + : [each.value.group_config.options.iap] + ) + content { + oauth2_client_id = try(iap.value.oauth2_client_id, null) + oauth2_client_secret = try(iap.value.oauth2_client_secret, null) # sensitive + oauth2_client_secret_sha256 = try(iap.value.oauth2_client_secret_sha256, null) # sensitive + } + } + + dynamic "log_config" { + for_each = ( + try(each.value.group_config.log_config, null) == null + ? [] + : [each.value.group_config.log_config] + ) + content { + enable = try(log_config.value.enable, null) + sample_rate = try(log_config.value.sample_rate, null) + } + } +} diff --git a/modules/net-glb/regional-forwarding-rule.tf b/modules/net-glb/regional-forwarding-rule.tf new file mode 100644 index 00000000..5f4e3d71 --- /dev/null +++ b/modules/net-glb/regional-forwarding-rule.tf @@ -0,0 +1,62 @@ +/** + * Copyright 2022 Google LLC + * + * 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. + */ + +# tfdoc:file:description Global address and forwarding rule. + +locals { + regional_ip_address = var.region != null ? ( + var.reserve_ip_address + ? google_compute_address.static_ip.0.id + : null + ) : null + + regional_port_range = coalesce( + var.forwarding_rule_config.port_range, + var.https ? "443" : "80" + ) + + regional_target = var.region != null ? ( + var.https + ? google_compute_region_target_https_proxy.https.0.id + : google_compute_region_target_http_proxy.http.0.id + ) : null +} + +resource "google_compute_address" "static_ip" { + count = var.region != null && var.reserve_ip_address ? 1 : 0 + provider = google-beta + name = var.name + project = var.project_id + region = var.region + description = "Terraform managed." + address_type = "EXTERNAL" +} + +resource "google_compute_forwarding_rule" "forwarding_rule" { + count = var.region != null ? 1 : 0 + provider = google-beta + name = var.name + project = var.project_id + region = var.region + description = "Terraform managed." + ip_protocol = var.forwarding_rule_config.ip_protocol + load_balancing_scheme = var.forwarding_rule_config.load_balancing_scheme + port_range = local.regional_port_range + target = local.regional_target + ip_address = local.regional_ip_address + network_tier = var.forwarding_rule_config.network_tier + network = var.forwarding_rule_config.network +} diff --git a/modules/net-glb/regional-url-map.tf b/modules/net-glb/regional-url-map.tf new file mode 100644 index 00000000..a836bb69 --- /dev/null +++ b/modules/net-glb/regional-url-map.tf @@ -0,0 +1,717 @@ +/** + * Copyright 2022 Google LLC + * + * 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. + */ + +# tfdoc:file:description regional URL maps. +locals { + # Look for a backend services in the config whose id is + # the default_service given in the url-map. + # If not found, use the default_service id as given + # (assuming it's already existing). + # If the variable is null, will be set to null. + _regional_default_service = try( + google_compute_backend_bucket.bucket[var.url_map_config.default_service].id, + google_compute_region_backend_service.group[var.url_map_config.default_service].id, + var.url_map_config.default_service, + null + ) + + # If no backend services are specified, + # the first backend service defined is associated + regional_default_service = ( + try(local._regional_default_service, null) == null + && try(var.url_map_config.default_route_action.weighted_backend_services, null) == null + && try(var.url_map_config.default_url_redirect, null) == null + ? try( + google_compute_backend_bucket.bucket[keys(google_compute_backend_bucket.bucket)[0]].id, + google_compute_region_backend_service.group[keys(google_compute_region_backend_service.group)[0]].id, + null + ) + : null + ) +} + +resource "google_compute_region_url_map" "url_map" { + count = var.region != null ? 1 : 0 + name = var.name + description = "Terraform managed." + project = var.project_id + region = var.region + default_service = local.regional_default_service + + dynamic "host_rule" { + for_each = ( + try(var.url_map_config.host_rules, null) == null + ? [] + : var.url_map_config.host_rules + ) + content { + description = try(host_rule.value.description, null) + hosts = try(host_rule.value.hosts, null) + path_matcher = try(host_rule.value.path_matcher, null) + } + } + + dynamic "path_matcher" { + for_each = ( + try(var.url_map_config.path_matchers, null) == null + ? [] + : var.url_map_config.path_matchers + ) + content { + name = try(path_matcher.value.name, null) + description = try(path_matcher.value.description, null) + default_service = try( + google_compute_backend_bucket.bucket[var.url_map_config.default_service].id, + google_compute_region_backend_service.group[var.url_map_config.default_service].id, + path_matcher.value.default_service, + null + ) + + dynamic "path_rule" { + for_each = ( + try(path_matcher.value.path_rules, null) == null + ? [] + : path_matcher.value.path_rules + ) + content { + paths = try(path_rule.value.paths, null) + service = try( + google_compute_backend_bucket.bucket[path_rule.value.service].id, + google_compute_region_backend_service.group[path_rule.value.service].id, + path_rule.value.service, + null + ) + + dynamic "route_action" { + for_each = ( + try(path_rule.value.route_action, null) == null + ? [] + : [path_rule.value.route_action] + ) + content { + + dynamic "cors_policy" { + for_each = ( + try(route_action.value.cors_policy, null) == null + ? [] + : [route_action.value.cors_policy] + ) + content { + allow_credentials = try(cors_policy.value.allow_credentials, null) + allow_headers = try(cors_policy.value.allow_headers, null) + allow_methods = try(cors_policy.value.allow_methods, null) + allow_origin_regexes = try(cors_policy.value.allow_origin_regexes, null) + allow_origins = try(cors_policy.value.allow_origins, null) + disabled = try(cors_policy.value.disabled, null) + expose_headers = try(cors_policy.value.expose_headers, null) + max_age = try(cors_policy.value.max_age, null) + } + } + + dynamic "fault_injection_policy" { + for_each = ( + try(route_action.value.fault_injection_policy, null) == null + ? [] + : [route_action.value.fault_injection_policy] + ) + iterator = policy + content { + + dynamic "abort" { + for_each = ( + try(policy.value.abort, null) == null + ? [] + : [policy.value.abort] + ) + content { + http_status = try(abort.value.http_status, null) # Must be between 200 and 599 inclusive + percentage = try(abort.value.percentage, null) # Must be between 0.0 and 100.0 inclusive + } + } + + dynamic "delay" { + for_each = ( + try(policy.value.delay, null) == null + ? [] + : [policy.value.delay] + ) + content { + percentage = try(delay.value.percentage, null) # Must be between 0.0 and 100.0 inclusive + + dynamic "fixed_delay" { + for_each = ( + try(delay.value.fixed_delay, null) == null + ? [] + : [delay.value.fixed_delay] + ) + content { + nanos = try(fixed_delay.value.nanos, null) # Must be from 0 to 999,999,999 inclusive + seconds = try(fixed_delay.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive + } + } + } + } + } + } + + dynamic "request_mirror_policy" { + for_each = ( + try(route_action.value.request_mirror_policy, null) == null + ? [] + : [route_action.value.request_mirror_policy] + ) + iterator = policy + content { + backend_service = try( + google_compute_backend_bucket.bucket[policy.value.backend_service].id, + google_compute_region_backend_service.group[policy.value.backend_service].id, + policy.value.backend_service, + null + ) + } + } + + dynamic "retry_policy" { + for_each = ( + try(route_action.value.retry_policy, null) == null + ? [] + : [route_action.value.retry_policy] + ) + iterator = policy + content { + num_retries = try(policy.num_retries, null) # Must be > 0 + # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#retry_conditions + retry_conditions = try(policy.retry_conditions, null) + + dynamic "per_try_timeout" { + for_each = ( + try(policy.value.per_try_timeout, null) == null + ? [] + : [policy.value.per_try_timeout] + ) + iterator = timeout + content { + nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive + seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive + } + } + } + } + + dynamic "timeout" { + for_each = ( + try(route_action.value.timeout, null) == null + ? [] + : [route_action.value.timeout] + ) + content { + nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive + seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive + } + } + + dynamic "url_rewrite" { + for_each = ( + try(route_action.value.url_rewrite, null) == null + ? [] + : [route_action.value.url_rewrite] + ) + content { + host_rewrite = try(url_rewrite.value.host_rewrite, null) # Must be between 1 and 255 characters + path_prefix_rewrite = try(url_rewrite.value.path_prefix_rewrite, null) # Must be between 1 and 1024 characters + } + } + + dynamic "weighted_backend_services" { + for_each = ( + try(route_action.value.weighted_backend_services, null) == null + ? [] + : route_action.value.weighted_backend_services + ) + iterator = weighted + content { + weight = try(weighted.value.weigth, null) + backend_service = try( + google_compute_backend_bucket.bucket[weighted.value.backend_service].id, + google_compute_backend_service.group[weighted.value.backend_service].id, + policy.value.backend_service, + null + ) + dynamic "header_action" { + for_each = ( + try(path_matcher.value.header_action, null) == null + ? [] + : [path_matcher.value.header_action] + ) + content { + request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) + response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) + + dynamic "request_headers_to_add" { + for_each = ( + try(header_action.value.request_headers_to_add, null) == null + ? [] : + [header_action.value.request_headers_to_add] + ) + content { + header_name = try(request_headers_to_add.value.header_name, null) + header_value = try(request_headers_to_add.value.header_value, null) + replace = try(request_headers_to_add.value.replace, null) + } + } + + dynamic "response_headers_to_add" { + for_each = ( + try(header_action.response_headers_to_add, null) == null + ? [] + : [header_action.response_headers_to_add] + ) + content { + header_name = try(response_headers_to_add.value.header_name, null) + header_value = try(response_headers_to_add.value.header_value, null) + replace = try(response_headers_to_add.value.replace, null) + } + } + } + } + } + } + } + } + + dynamic "url_redirect" { + for_each = ( + try(path_rule.value.url_redirect, null) == null + ? [] + : path_rule.value.url_redirect + ) + content { + host_redirect = try(url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters + https_redirect = try(url_redirect.value.https_redirect, null) + path_redirect = try(url_redirect.value.path_redirect, null) + prefix_redirect = try(url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters + # Valid valus at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code + redirect_response_code = try(url_redirect.value.redirect_response_code, null) + strip_query = try(url_redirect.value.strip_query, null) + } + } + } + } + + dynamic "route_rules" { + for_each = ( + try(path_matcher.value.route_rules, null) == null + ? [] + : path_matcher.value.route_rules + ) + content { + priority = try(route_rules.value.priority, null) + service = try( + google_compute_backend_bucket.bucket[route_rules.value.service].id, + google_compute_backend_service.group[route_rules.value.service].id, + route_rules.value.service, + null + ) + + dynamic "header_action" { + for_each = ( + try(path_matcher.value.header_action, null) == null + ? [] + : [path_matcher.value.header_action] + ) + content { + request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) + response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) + + dynamic "request_headers_to_add" { + for_each = ( + try(header_action.value.request_headers_to_add, null) == null + ? [] + : [header_action.value.request_headers_to_add] + ) + content { + header_name = try(request_headers_to_add.value.header_name, null) + header_value = try(request_headers_to_add.value.header_value, null) + replace = try(request_headers_to_add.value.replace, null) + } + } + + dynamic "response_headers_to_add" { + for_each = ( + try(header_action.response_headers_to_add, null) == null + ? [] + : [header_action.response_headers_to_add] + ) + content { + header_name = try(response_headers_to_add.value.header_name, null) + header_value = try(response_headers_to_add.value.header_value, null) + replace = try(response_headers_to_add.value.replace, null) + } + } + } + } + + dynamic "match_rules" { + for_each = ( + try(path_matcher.value.match_rules, null) == null + ? [] + : path_matcher.value.match_rules + ) + content { + full_path_match = try(match_rules.value.full_path_match, null) # Must be between 1 and 1024 characters + ignore_case = try(match_rules.value.ignore_case, null) + prefix_match = try(match_rules.value.prefix_match, null) + regex_match = try(match_rules.value.regex_match, null) + + dynamic "header_matches" { + for_each = ( + try(match_rules.value.header_matches, null) == null + ? [] + : [match_rules.value.header_matches] + ) + content { + exact_match = try(header_matches.value.exact_match, null) + header_name = try(header_matches.value.header_name, null) + invert_match = try(header_matches.value.invert_match, null) + prefix_match = try(header_matches.value.prefix_match, null) + present_match = try(header_matches.value.present_match, null) + regex_match = try(header_matches.value.regex_match, null) + suffix_match = try(header_matches.value, null) + + dynamic "range_match" { + for_each = try(header_matches.value.range_match, null) == null ? [] : [header_matches.value.range_match] + content { + range_end = try(range_match.value.range_end, null) + range_start = try(range_match.value.range_start, null) + } + } + } + } + + dynamic "metadata_filters" { + for_each = ( + try(match_rules.value.metadata_filters, null) == null + ? [] + : [match_rules.value.metadata_filters] + ) + content { + # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#filter_match_criteria + filter_match_criteria = try(metadata_filters.value.filter_match_criteria, null) + + dynamic "filter_labels" { + for_each = ( + try(metadata_filters.value.filter_labels, null) == null + ? [] + : metadata_filters.value.filter_labels + ) + content { + name = try(filter_labels.value.name, null) # Must be between 1 and 1024 characters + value = try(filter_labels.value.value, null) # Must be between 1 and 1024 characters + } + } + } + } + + dynamic "query_parameter_matches" { + for_each = ( + try(match_rules.value.query_parameter_matches, null) == null + ? [] + : [match_rules.value.query_parameter_matches] + ) + iterator = query + content { + exact_match = try(query.value.exact_match, null) + name = try(query.value.name, null) + present_match = try(query.value.present_match, null) + regex_match = try(query.value.regex_match, null) + } + } + } + } + + dynamic "route_action" { + for_each = ( + try(route_rules.value.route_action, null) == null + ? [] + : [route_rules.value.route_action] + ) + content { + + dynamic "cors_policy" { + for_each = ( + try(route_action.value.cors_policy, null) == null + ? [] + : [route_action.value.cors_policy] + ) + content { + allow_credentials = try(cors_policy.value.allow_credentials, null) + allow_headers = try(cors_policy.value.allow_headers, null) + allow_methods = try(cors_policy.value.allow_methods, null) + allow_origin_regexes = try(cors_policy.value.allow_origin_regexes, null) + allow_origins = try(cors_policy.value.allow_origins, null) + disabled = try(cors_policy.value.disabled, null) + expose_headers = try(cors_policy.value.expose_headers, null) + max_age = try(cors_policy.value.max_age, null) + } + } + + dynamic "fault_injection_policy" { + for_each = ( + try(route_action.value.fault_injection_policy, null) == null + ? [] + : [route_action.value.fault_injection_policy] + ) + iterator = policy + content { + + dynamic "abort" { + for_each = ( + try(policy.value.abort, null) == null + ? [] + : [policy.value.abort] + ) + content { + http_status = try(abort.value.http_status, null) # Must be between 200 and 599 inclusive + percentage = try(abort.value.percentage, null) # Must be between 0.0 and 100.0 inclusive + } + } + + dynamic "delay" { + for_each = ( + try(policy.value.delay, null) == null + ? [] + : [policy.value.delay] + ) + content { + percentage = try(delay.value.percentage, null) # Must be between 0.0 and 100.0 inclusive + + dynamic "fixed_delay" { + for_each = ( + try(delay.value.fixed_delay, null) == null + ? [] + : [delay.value.fixed_delay] + ) + content { + nanos = try(fixed_delay.value.nanos, null) # Must be from 0 to 999,999,999 inclusive + seconds = try(fixed_delay.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive + } + } + } + } + } + } + + dynamic "request_mirror_policy" { + for_each = ( + try(route_action.value.request_mirror_policy, null) == null + ? [] + : [route_action.value.request_mirror_policy] + ) + iterator = policy + content { + backend_service = try( + google_compute_backend_bucket.bucket[policy.value.backend_service].id, + google_compute_backend_service.group[policy.value.backend_service].id, + policy.value.backend_service, + null + ) + } + } + + dynamic "retry_policy" { + for_each = ( + try(route_action.value.retry_policy, null) == null + ? [] + : [route_action.value.retry_policy] + ) + iterator = policy + content { + num_retries = try(policy.num_retries, null) # Must be > 0 + # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#retry_conditions + retry_conditions = try(policy.retry_conditions, null) + + dynamic "per_try_timeout" { + for_each = ( + try(policy.value.per_try_timeout, null) == null + ? [] + : [policy.value.per_try_timeout] + ) + iterator = timeout + content { + nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive + seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive + } + } + } + } + + dynamic "timeout" { + for_each = ( + try(route_action.value.timeout, null) == null + ? [] + : [route_action.value.timeout] + ) + content { + nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive + seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive + } + } + + dynamic "url_rewrite" { + for_each = ( + try(route_action.value.url_rewrite, null) == null + ? [] + : [route_action.value.url_rewrite] + ) + content { + host_rewrite = try(url_rewrite.value.host_rewrite, null) # Must be between 1 and 255 characters + path_prefix_rewrite = try(url_rewrite.value.path_prefix_rewrite, null) # Must be between 1 and 1024 characters + } + } + + dynamic "weighted_backend_services" { + for_each = ( + try(route_action.value.weighted_backend_services, null) == null + ? [] + : [route_action.value.url_rewrite] + ) + iterator = weighted + content { + weight = try(weighted.value.weigth, null) + backend_service = try( + google_compute_backend_bucket.bucket[weighted.value.backend_service].id, + google_compute_backend_service.group[weighted.value.backend_service].id, + weighted.value.backend_service, + null + ) + + dynamic "header_action" { + for_each = ( + try(path_matcher.value.header_action, null) == null + ? [] : + [path_matcher.value.header_action] + ) + content { + request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) + response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) + + dynamic "request_headers_to_add" { + for_each = ( + try(header_action.value.request_headers_to_add, null) == null + ? [] + : [header_action.value.request_headers_to_add] + ) + content { + header_name = try(request_headers_to_add.value.header_name, null) + header_value = try(request_headers_to_add.value.header_value, null) + replace = try(request_headers_to_add.value.replace, null) + } + } + + dynamic "response_headers_to_add" { + for_each = ( + try(header_action.response_headers_to_add, null) == null + ? [] + : [header_action.response_headers_to_add] + ) + content { + header_name = try(response_headers_to_add.value.header_name, null) + header_value = try(response_headers_to_add.value.header_value, null) + replace = try(response_headers_to_add.value.replace, null) + } + } + } + } + } + } + } + } + + dynamic "url_redirect" { + for_each = ( + try(route_rules.value.url_redirect, null) == null + ? [] + : route_rules.value.url_redirect + ) + content { + host_redirect = try(url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters + https_redirect = try(url_redirect.value.https_redirect, null) + path_redirect = try(url_redirect.value.path_redirect, null) + prefix_redirect = try(url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters + # Valid valus at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code + redirect_response_code = try(url_redirect.value.redirect_response_code, null) + strip_query = try(url_redirect.value.strip_query, null) + } + } + } + } + + dynamic "default_url_redirect" { + for_each = ( + try(path_matcher.value.default_url_redirect, null) == null + ? [] + : path_matcher.value.default_url_redirect + ) + content { + host_redirect = try(default_url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters + https_redirect = try(default_url_redirect.value.https_redirect, null) + path_redirect = try(default_url_redirect.value.path_redirect, null) # Must be between 1 and 1024 characters + prefix_redirect = try(default_url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters + # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code + redirect_response_code = try(default_url_redirect.value.redirect_response_code, null) + strip_query = try(default_url_redirect.value.strip_query, null) + } + } + } + } + + # Up to 100 tests per url_map + dynamic "test" { + for_each = ( + try(var.url_map_config.tests, null) == null + ? [] + : var.url_map_config.tests + ) + content { + description = try(test.value.description, null) + host = try(test.value.host, null) + path = try(test.value.path, null) + service = try( + google_compute_backend_bucket.bucket[test.value.service].id, + google_compute_backend_service.group[test.value.service].id, + test.value.service, + null + ) + } + } + + dynamic "default_url_redirect" { + for_each = ( + try(var.url_map_config.default_url_redirect, null) == null + ? [] + : [var.url_map_config.default_url_redirect] + ) + content { + host_redirect = try(default_url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters + https_redirect = try(default_url_redirect.value.https_redirect, null) + path_redirect = try(default_url_redirect.value.path_redirect, null) # Must be between 1 and 1024 characters + prefix_redirect = try(default_url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters + # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code + redirect_response_code = try(default_url_redirect.value.redirect_response_code, null) + strip_query = try(default_url_redirect.value.strip_query, null) + } + } +} diff --git a/modules/net-glb/target-proxy.tf b/modules/net-glb/target-proxy.tf index c3ede228..ddfbe454 100644 --- a/modules/net-glb/target-proxy.tf +++ b/modules/net-glb/target-proxy.tf @@ -40,18 +40,37 @@ locals { } resource "google_compute_target_http_proxy" "http" { - count = var.https ? 0 : 1 + count = var.https ? 0 : (var.region == null ? 1 : 0) name = var.name project = var.project_id description = "Terraform managed." - url_map = google_compute_url_map.url_map.id + url_map = google_compute_url_map.url_map.0.id } resource "google_compute_target_https_proxy" "https" { - count = var.https ? 1 : 0 + count = var.https ? (var.region == null ? 1 : 0) : 0 name = var.name project = var.project_id description = "Terraform managed." - url_map = google_compute_url_map.url_map.id + url_map = google_compute_url_map.url_map.0.id + ssl_certificates = local.ssl_certificates +} + +resource "google_compute_region_target_http_proxy" "http" { + count = var.https ? 0 : (var.region != null ? 1 : 0) + name = var.name + project = var.project_id + region = var.region + description = "Terraform managed." + url_map = google_compute_region_url_map.url_map.0.id +} + +resource "google_compute_region_target_https_proxy" "https" { + count = var.https ? (var.region != null ? 1 : 0) : 0 + name = var.name + project = var.project_id + region = var.region + description = "Terraform managed." + url_map = google_compute_region_url_map.url_map.0.id ssl_certificates = local.ssl_certificates } diff --git a/modules/net-glb/url-map.tf b/modules/net-glb/url-map.tf index 90cb44e3..a473c349 100644 --- a/modules/net-glb/url-map.tf +++ b/modules/net-glb/url-map.tf @@ -45,6 +45,7 @@ locals { } resource "google_compute_url_map" "url_map" { + count = var.region == null ? 1 : 0 name = var.name description = "Terraform managed." project = var.project_id @@ -1180,3 +1181,4 @@ resource "google_compute_url_map" "url_map" { } } } + diff --git a/modules/net-glb/variables.tf b/modules/net-glb/variables.tf index a53c113b..3260a923 100644 --- a/modules/net-glb/variables.tf +++ b/modules/net-glb/variables.tf @@ -24,6 +24,12 @@ variable "project_id" { type = string } +variable "region" { + description = "Create a regional load balancer in this region." + type = string + default = null +} + variable "health_checks_config_defaults" { description = "Auto-created health check default configuration." type = object({ @@ -217,6 +223,27 @@ variable "global_forwarding_rule_config" { } } +variable "forwarding_rule_config" { + description = "Regional forwarding rule configurations." + type = object({ + ip_protocol = string + ip_version = string + load_balancing_scheme = string + port_range = string + network_tier = string + network = string + }) + default = { + load_balancing_scheme = "EXTERNAL_MANAGED" + ip_protocol = "TCP" + ip_version = "IPV4" + network_tier = "STANDARD" + network = "default" + # If not specified, 80 for https = false, 443 otherwise + port_range = null + } +} + variable "https" { description = "Whether to enable HTTPS." type = bool diff --git a/tests/modules/net_glb/fixture/main.tf b/tests/modules/net_glb/fixture/main.tf index 544804d0..b2c68e03 100644 --- a/tests/modules/net_glb/fixture/main.tf +++ b/tests/modules/net_glb/fixture/main.tf @@ -18,6 +18,7 @@ module "test" { source = "../../../../modules/net-glb" project_id = "my-project" name = "glb-test" + region = var.region health_checks_config_defaults = var.health_checks_config_defaults health_checks_config = var.health_checks_config backend_services_config = var.backend_services_config @@ -28,4 +29,6 @@ module "test" { target_proxy_https_config = var.target_proxy_https_config reserve_ip_address = var.reserve_ip_address global_forwarding_rule_config = var.global_forwarding_rule_config + forwarding_rule_config = var.forwarding_rule_config } + diff --git a/tests/modules/net_glb/fixture/variables.tf b/tests/modules/net_glb/fixture/variables.tf index 0067cce5..6b818405 100644 --- a/tests/modules/net_glb/fixture/variables.tf +++ b/tests/modules/net_glb/fixture/variables.tf @@ -14,6 +14,12 @@ * limitations under the License. */ +variable "region" { + description = "Create a regional load balancer." + type = string + default = null +} + variable "health_checks_config_defaults" { description = "Auto-created health check default configuration." type = object({ @@ -207,6 +213,27 @@ variable "global_forwarding_rule_config" { } } +variable "forwarding_rule_config" { + description = "Global forwarding rule configurations." + type = object({ + ip_protocol = string + ip_version = string + load_balancing_scheme = string + port_range = string + network_tier = string + network = string + }) + default = { + load_balancing_scheme = "EXTERNAL_MANAGED" + ip_protocol = "TCP" + ip_version = "IPV4" + # If not specified, 80 for https = false, 443 otherwise + port_range = null + network_tier = "STANDARD" + network = "default" + } +} + variable "https" { description = "Whether to enable HTTPS." type = bool diff --git a/tests/modules/net_glb/test_plan.py b/tests/modules/net_glb/test_plan.py index 802b083f..758ea508 100644 --- a/tests/modules/net_glb/test_plan.py +++ b/tests/modules/net_glb/test_plan.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - _BACKEND_BUCKET = '''{ my-bucket = { bucket_config = { @@ -94,168 +93,211 @@ _TARGET_PROXY_HTTPS_CONFIG = '''{ def test_bucket(plan_runner): - "Tests a bucket backend service." - _, resources = plan_runner(backend_services_config=_BACKEND_BUCKET) - assert len(resources) == 4 - resources = dict((r['type'], r['values']) for r in resources) + "Tests a bucket backend service." + _, resources = plan_runner(backend_services_config=_BACKEND_BUCKET) + assert len(resources) == 4 + resources = dict((r['type'], r['values']) for r in resources) - fwd_rule = resources['google_compute_global_forwarding_rule'] - assert fwd_rule['load_balancing_scheme'] == 'EXTERNAL' - assert fwd_rule['port_range'] == '80' - assert fwd_rule['ip_protocol'] == 'TCP' + fwd_rule = resources['google_compute_global_forwarding_rule'] + assert fwd_rule['load_balancing_scheme'] == 'EXTERNAL' + assert fwd_rule['port_range'] == '80' + assert fwd_rule['ip_protocol'] == 'TCP' - bucket = resources['google_compute_backend_bucket'] - assert bucket['name'] == _NAME + '-my-bucket' - assert bucket['enable_cdn'] is False + bucket = resources['google_compute_backend_bucket'] + assert bucket['name'] == _NAME + '-my-bucket' + assert bucket['enable_cdn'] is False - assert 'google_compute_health_check' not in resources - assert 'google_compute_target_http_proxy' in resources - assert 'google_compute_url_map' in resources + assert 'google_compute_health_check' not in resources + assert 'google_compute_target_http_proxy' in resources + assert 'google_compute_url_map' in resources def test_group_default_hc(plan_runner): - "Tests a group backend service with no HC specified." - _, resources = plan_runner(backend_services_config=_BACKEND_GROUP) - assert len(resources) == 5 - resources = dict((r['type'], r['values']) for r in resources) + "Tests a group backend service with no HC specified." + _, resources = plan_runner(backend_services_config=_BACKEND_GROUP) + assert len(resources) == 5 + resources = dict((r['type'], r['values']) for r in resources) - fwd_rule = resources['google_compute_global_forwarding_rule'] - assert fwd_rule['load_balancing_scheme'] == 'EXTERNAL' - assert fwd_rule['port_range'] == '80' - assert fwd_rule['ip_protocol'] == 'TCP' + fwd_rule = resources['google_compute_global_forwarding_rule'] + assert fwd_rule['load_balancing_scheme'] == 'EXTERNAL' + assert fwd_rule['port_range'] == '80' + assert fwd_rule['ip_protocol'] == 'TCP' - group = resources['google_compute_backend_service'] - assert len(group['backend']) == 1 - assert group['backend'][0]['group'] == 'my_group' + group = resources['google_compute_backend_service'] + assert len(group['backend']) == 1 + assert group['backend'][0]['group'] == 'my_group' - health_check = resources['google_compute_health_check'] - assert health_check['name'] == _NAME + '-default' - assert len(health_check['http_health_check']) > 0 - assert len(health_check['https_health_check']) == 0 - assert len(health_check['http2_health_check']) == 0 - assert len(health_check['tcp_health_check']) == 0 - assert health_check['http_health_check'][0]['port_specification'] == 'USE_SERVING_PORT' - assert health_check['http_health_check'][0]['proxy_header'] == 'NONE' - assert health_check['http_health_check'][0]['request_path'] == '/' + health_check = resources['google_compute_health_check'] + assert health_check['name'] == _NAME + '-default' + assert len(health_check['http_health_check']) > 0 + assert len(health_check['https_health_check']) == 0 + assert len(health_check['http2_health_check']) == 0 + assert len(health_check['tcp_health_check']) == 0 + assert health_check['http_health_check'][0][ + 'port_specification'] == 'USE_SERVING_PORT' + assert health_check['http_health_check'][0]['proxy_header'] == 'NONE' + assert health_check['http_health_check'][0]['request_path'] == '/' - assert 'google_compute_target_http_proxy' in resources - assert 'google_compute_target_https_proxy' not in resources - assert 'google_compute_url_map' in resources + assert 'google_compute_target_http_proxy' in resources + assert 'google_compute_target_https_proxy' not in resources + assert 'google_compute_url_map' in resources + + +def test_group_default_hc_regional(plan_runner): + "Tests a group backend service with no HC specified." + _, resources = plan_runner(backend_services_config=_BACKEND_GROUP, + region="europe-west4") + assert len(resources) == 5 + resources = dict((r['type'], r['values']) for r in resources) + + fwd_rule = resources['google_compute_forwarding_rule'] + assert fwd_rule['load_balancing_scheme'] == 'EXTERNAL_MANAGED' + assert fwd_rule['port_range'] == '80' + assert fwd_rule['ip_protocol'] == 'TCP' + + group = resources['google_compute_region_backend_service'] + assert len(group['backend']) == 1 + assert group['backend'][0]['group'] == 'my_group' + + health_check = resources['google_compute_region_health_check'] + assert health_check['name'] == _NAME + '-default' + assert len(health_check['http_health_check']) > 0 + assert len(health_check['https_health_check']) == 0 + assert len(health_check['http2_health_check']) == 0 + assert len(health_check['tcp_health_check']) == 0 + assert health_check['http_health_check'][0][ + 'port_specification'] == 'USE_SERVING_PORT' + assert health_check['http_health_check'][0]['proxy_header'] == 'NONE' + assert health_check['http_health_check'][0]['request_path'] == '/' + + assert 'google_compute_region_target_http_proxy' in resources + assert 'google_compute_region_target_https_proxy' not in resources + assert 'google_compute_region_url_map' in resources def test_group_no_hc(plan_runner): - "Tests a group backend service without HCs (including no default HC)." - _, resources = plan_runner(backend_services_config=_BACKEND_GROUP, - health_checks_config_defaults='null') + "Tests a group backend service without HCs (including no default HC)." + _, resources = plan_runner(backend_services_config=_BACKEND_GROUP, + health_checks_config_defaults='null') - assert len(resources) == 4 - resources = dict((r['type'], r['values']) for r in resources) + assert len(resources) == 4 + resources = dict((r['type'], r['values']) for r in resources) - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_health_check' not in resources - assert 'google_compute_target_http_proxy' in resources - assert 'google_compute_target_https_proxy' not in resources - assert 'google_compute_url_map' in resources + assert 'google_compute_backend_service' in resources + assert 'google_compute_global_forwarding_rule' in resources + assert 'google_compute_health_check' not in resources + assert 'google_compute_target_http_proxy' in resources + assert 'google_compute_target_https_proxy' not in resources + assert 'google_compute_url_map' in resources def test_group_existing_hc(plan_runner): - "Tests a group backend service with referencing an existing HC." - _, resources = plan_runner(backend_services_config=_BACKEND_GROUP_HC) - assert len(resources) == 4 - resources = dict((r['type'], r['values']) for r in resources) + "Tests a group backend service with referencing an existing HC." + _, resources = plan_runner(backend_services_config=_BACKEND_GROUP_HC) + assert len(resources) == 4 + resources = dict((r['type'], r['values']) for r in resources) - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_health_check' not in resources - assert 'google_compute_target_http_proxy' in resources - assert 'google_compute_target_https_proxy' not in resources - assert 'google_compute_url_map' in resources + assert 'google_compute_backend_service' in resources + assert 'google_compute_global_forwarding_rule' in resources + assert 'google_compute_health_check' not in resources + assert 'google_compute_target_http_proxy' in resources + assert 'google_compute_target_https_proxy' not in resources + assert 'google_compute_url_map' in resources + + +def test_group_existing_hc_regional(plan_runner): + "Tests a group backend service with referencing an existing HC." + _, resources = plan_runner(backend_services_config=_BACKEND_GROUP_HC, + region="europe-west4") + assert len(resources) == 4 + resources = dict((r['type'], r['values']) for r in resources) + + assert 'google_compute_region_backend_service' in resources + assert 'google_compute_forwarding_rule' in resources + assert 'google_compute_region_health_check' not in resources + assert 'google_compute_region_target_http_proxy' in resources + assert 'google_compute_region_target_https_proxy' not in resources + assert 'google_compute_region_url_map' in resources def test_reserved_ip(plan_runner): - "Tests an IP reservation with a group backend service." - _, resources = plan_runner( - backend_services_config=_BACKEND_GROUP, - reserve_ip_address="true" - ) - assert len(resources) == 6 - resources = dict((r['type'], r['values']) for r in resources) + "Tests an IP reservation with a group backend service." + _, resources = plan_runner(backend_services_config=_BACKEND_GROUP, + reserve_ip_address="true") + assert len(resources) == 6 + resources = dict((r['type'], r['values']) for r in resources) - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_address' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_target_http_proxy' in resources - assert 'google_compute_target_https_proxy' not in resources - assert 'google_compute_url_map' in resources + assert 'google_compute_backend_service' in resources + assert 'google_compute_global_address' in resources + assert 'google_compute_global_forwarding_rule' in resources + assert 'google_compute_target_http_proxy' in resources + assert 'google_compute_target_https_proxy' not in resources + assert 'google_compute_url_map' in resources def test_ssl_managed(plan_runner): - "Tests HTTPS and SSL managed certificates." - _, resources = plan_runner( - backend_services_config=_BACKEND_GROUP, - https='true', - ssl_certificates_config=_SSL_CERTIFICATES_CONFIG_MANAGED, - target_proxy_https_config=_TARGET_PROXY_HTTPS_CONFIG - ) - assert len(resources) == 6 - resources = dict((r['type'], r['values']) for r in resources) + "Tests HTTPS and SSL managed certificates." + _, resources = plan_runner( + backend_services_config=_BACKEND_GROUP, + https='true', + ssl_certificates_config=_SSL_CERTIFICATES_CONFIG_MANAGED, + target_proxy_https_config=_TARGET_PROXY_HTTPS_CONFIG) + assert len(resources) == 6 + resources = dict((r['type'], r['values']) for r in resources) - fwd_rule = resources['google_compute_global_forwarding_rule'] - assert fwd_rule['port_range'] == '443' + fwd_rule = resources['google_compute_global_forwarding_rule'] + assert fwd_rule['port_range'] == '443' - ssl_cert = resources['google_compute_managed_ssl_certificate'] - assert ssl_cert['type'] == "MANAGED" - assert ssl_cert['managed'][0]['domains'][0] == 'my-domain.com' + ssl_cert = resources['google_compute_managed_ssl_certificate'] + assert ssl_cert['type'] == "MANAGED" + assert ssl_cert['managed'][0]['domains'][0] == 'my-domain.com' - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_ssl_certificate' not in resources - assert 'google_compute_target_http_proxy' not in resources - assert 'google_compute_target_https_proxy' in resources - assert 'google_compute_url_map' in resources + assert 'google_compute_backend_service' in resources + assert 'google_compute_global_forwarding_rule' in resources + assert 'google_compute_ssl_certificate' not in resources + assert 'google_compute_target_http_proxy' not in resources + assert 'google_compute_target_https_proxy' in resources + assert 'google_compute_url_map' in resources def test_ssl_unmanaged(plan_runner): - "Tests HTTPS and SSL unmanaged certificates." - _, resources = plan_runner( - backend_services_config=_BACKEND_GROUP, - https="true", - ssl_certificates_config=_SSL_CERTIFICATES_CONFIG_UNMANAGED, - target_proxy_https_config=_TARGET_PROXY_HTTPS_CONFIG - ) - assert len(resources) == 6 - resources = dict((r['type'], r['values']) for r in resources) + "Tests HTTPS and SSL unmanaged certificates." + _, resources = plan_runner( + backend_services_config=_BACKEND_GROUP, + https="true", + ssl_certificates_config=_SSL_CERTIFICATES_CONFIG_UNMANAGED, + target_proxy_https_config=_TARGET_PROXY_HTTPS_CONFIG) + assert len(resources) == 6 + resources = dict((r['type'], r['values']) for r in resources) - fwd_rule = resources['google_compute_global_forwarding_rule'] - assert fwd_rule['port_range'] == '443' + fwd_rule = resources['google_compute_global_forwarding_rule'] + assert fwd_rule['port_range'] == '443' - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_managed_ssl_certificate' not in resources - assert 'google_compute_ssl_certificate' in resources - assert 'google_compute_target_http_proxy' not in resources - assert 'google_compute_target_https_proxy' in resources - assert 'google_compute_url_map' in resources + assert 'google_compute_backend_service' in resources + assert 'google_compute_global_forwarding_rule' in resources + assert 'google_compute_managed_ssl_certificate' not in resources + assert 'google_compute_ssl_certificate' in resources + assert 'google_compute_target_http_proxy' not in resources + assert 'google_compute_target_https_proxy' in resources + assert 'google_compute_url_map' in resources def test_ssl_existing_cert(plan_runner): - "Tests HTTPS and SSL existing certificate." - _, resources = plan_runner( - backend_services_config=_BACKEND_GROUP, - https="true", - target_proxy_https_config=_TARGET_PROXY_HTTPS_CONFIG - ) - assert len(resources) == 5 - resources = dict((r['type'], r['values']) for r in resources) + "Tests HTTPS and SSL existing certificate." + _, resources = plan_runner( + backend_services_config=_BACKEND_GROUP, + https="true", + target_proxy_https_config=_TARGET_PROXY_HTTPS_CONFIG) + assert len(resources) == 5 + resources = dict((r['type'], r['values']) for r in resources) - fwd_rule = resources['google_compute_global_forwarding_rule'] - assert fwd_rule['port_range'] == '443' + fwd_rule = resources['google_compute_global_forwarding_rule'] + assert fwd_rule['port_range'] == '443' - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_managed_ssl_certificate' not in resources - assert 'google_compute_ssl_certificate' not in resources - assert 'google_compute_target_http_proxy' not in resources - assert 'google_compute_target_https_proxy' in resources - assert 'google_compute_url_map' in resources + assert 'google_compute_backend_service' in resources + assert 'google_compute_global_forwarding_rule' in resources + assert 'google_compute_managed_ssl_certificate' not in resources + assert 'google_compute_ssl_certificate' not in resources + assert 'google_compute_target_http_proxy' not in resources + assert 'google_compute_target_https_proxy' in resources + assert 'google_compute_url_map' in resources