From d8c92c43a7990017062c94a48b7f0c79413bafe1 Mon Sep 17 00:00:00 2001 From: Ludo Date: Sun, 13 Nov 2022 14:21:03 +0100 Subject: [PATCH 01/13] wip --- modules/net-ilb-l7/backend-service.tf | 220 ++++++ modules/net-ilb-l7/backend-services.tf | 133 ---- modules/net-ilb-l7/forwarding-rule.tf | 68 -- modules/net-ilb-l7/health-check.tf | 109 +++ modules/net-ilb-l7/health-checks.tf | 148 ---- modules/net-ilb-l7/main.tf | 77 ++ modules/net-ilb-l7/outputs.tf | 72 +- modules/net-ilb-l7/ssl-certificates.tf | 26 - modules/net-ilb-l7/target-proxy.tf | 48 -- modules/net-ilb-l7/url-map.tf | 708 ------------------- modules/net-ilb-l7/urlmap.tf | 563 +++++++++++++++ modules/net-ilb-l7/variables-health-check.tf | 95 +++ modules/net-ilb-l7/variables-urlmap.tf | 228 ++++++ modules/net-ilb-l7/variables.tf | 320 +++++---- 14 files changed, 1504 insertions(+), 1311 deletions(-) create mode 100644 modules/net-ilb-l7/backend-service.tf delete mode 100644 modules/net-ilb-l7/backend-services.tf delete mode 100644 modules/net-ilb-l7/forwarding-rule.tf create mode 100644 modules/net-ilb-l7/health-check.tf delete mode 100644 modules/net-ilb-l7/health-checks.tf create mode 100644 modules/net-ilb-l7/main.tf delete mode 100644 modules/net-ilb-l7/ssl-certificates.tf delete mode 100644 modules/net-ilb-l7/target-proxy.tf delete mode 100644 modules/net-ilb-l7/url-map.tf create mode 100644 modules/net-ilb-l7/urlmap.tf create mode 100644 modules/net-ilb-l7/variables-health-check.tf create mode 100644 modules/net-ilb-l7/variables-urlmap.tf diff --git a/modules/net-ilb-l7/backend-service.tf b/modules/net-ilb-l7/backend-service.tf new file mode 100644 index 0000000000..ddae9d5b96 --- /dev/null +++ b/modules/net-ilb-l7/backend-service.tf @@ -0,0 +1,220 @@ +/** + * 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 Backend service resources. + +locals { + bsc = var.backend_service_config + hc = { + for k, v in google_compute_health_check.default : k => v.id + } +} + +resource "google_compute_region_backend_service" "default" { + provider = google-beta + for_each = var.backend_service_config + project = var.project_id + region = var.region + name = "${var.name}-${each.key}" + description = var.description + affinity_cookie_ttl_sec = each.value.affinity_cookie_ttl_sec + connection_draining_timeout_sec = each.value.connection_draining_timeout_sec + health_checks = [ + for k in each.value.health_checks : lookup(local.hc, k, k) + ] # not for internet / serverless NEGs + locality_lb_policy = each.value.locality_lb_policy + load_balancing_scheme = "INTERNAL_MANAGED" + network = var.vpc_config.network + port_name = each.value.port_name # defaults to http, not for NEGs + protocol = var.protocol + session_affinity = each.value.session_affinity + timeout_sec = each.value.timeout_sec + + dynamic "backend" { + for_each = { for b in coalesce(var.backends, []) : b.group => b } + content { + group = backend.key + balancing_mode = backend.value.balancing_mode + capacity_scaler = backend.value.capacity_scaler + description = backend.value.description + failover = backend.value.failover + max_connections = try( + backend.value.max_connections.per_group, null + ) + max_connections_per_endpoint = try( + backend.value.max_connections.per_endpoint, null + ) + max_connections_per_instance = try( + backend.value.max_connections.per_instance, null + ) + max_rate = try( + backend.value.max_rate.per_group, null + ) + max_rate_per_endpoint = try( + backend.value.max_rate.per_endpoint, null + ) + max_rate_per_instance = try( + backend.value.max_rate.per_instance, null + ) + max_utilization = backend.value.max_utilization + } + } + + dynamic "circuit_breakers" { + for_each = ( + each.value.circuit_breakers == null ? [] : [each.value.circuit_breakers] + ) + iterator = cb + content { + max_connections = cb.value.max_connections + max_pending_requests = cb.value.max_pending_requests + max_requests = cb.value.max_requests + max_requests_per_connection = cb.value.max_requests_per_connection + max_retries = cb.value.max_retries + dynamic "connect_timeout" { + for_each = ( + cb.value.connect_timeout == null ? [] : [cb.value.connect_timeout] + ) + content { + seconds = connect_timeout.value.seconds + nanos = connect_timeout.value.nanos + } + } + } + } + + dynamic "connection_tracking_policy" { + for_each = ( + each.value.connection_tracking == null + ? [] + : [each.value.connection_tracking] + ) + iterator = cb + content { + connection_persistence_on_unhealthy_backends = ( + cb.value.persist_conn_on_unhealthy != null + ? cb.value.persist_conn_on_unhealthy + : null + ) + idle_timeout_sec = cb.value.idle_timeout_sec + tracking_mode = ( + cb.value.track_per_session != null + ? cb.value.track_per_session + : null + ) + } + } + + dynamic "consistent_hash" { + for_each = ( + each.value.consistent_hash == null ? [] : [each.value.consistent_hash] + ) + iterator = ch + content { + http_header_name = ch.value.http_header_name + minimum_ring_size = ch.value.minimum_ring_size + dynamic "http_cookie" { + for_each = ch.value.http_cookie == null ? [] : [ch.value.http_cookie] + content { + name = http_cookie.value.name + path = http_cookie.value.path + dynamic "ttl" { + for_each = ( + http_cookie.value.ttl == null ? [] : [http_cookie.value.ttl] + ) + content { + seconds = ttl.value.seconds + nanos = ttl.value.nanos + } + } + } + } + } + } + + dynamic "failover_policy" { + for_each = ( + each.value.failover_config == null ? [] : [each.value.failover_config] + ) + iterator = fc + content { + disable_connection_drain_on_failover = fc.value.disable_conn_drain + drop_traffic_if_unhealthy = fc.value.drop_traffic_if_unhealthy + failover_ratio = fc.value.ratio + } + } + + dynamic "iap" { + for_each = each.value.iap_config == null ? [] : [each.value.iap_config] + content { + oauth2_client_id = iap.value.oauth2_client_id + oauth2_client_secret = iap.value.oauth2_client_secret + oauth2_client_secret_sha256 = iap.value.oauth2_client_secret_sha256 + } + } + + dynamic "log_config" { + for_each = var.backend_service_config.log_sample_rate == null ? [] : [""] + content { + enable = true + sample_rate = var.backend_service_config.log_sample_rate + } + } + + dynamic "outlier_detection" { + for_each = ( + each.value.outlier_detection == null ? [] : [each.value.outlier_detection] + ) + iterator = od + content { + consecutive_errors = od.value.consecutive_errors + consecutive_gateway_failure = od.value.consecutive_gateway_failure + enforcing_consecutive_errors = od.value.enforcing_consecutive_errors + enforcing_consecutive_gateway_failure = od.value.enforcing_consecutive_gateway_failure + enforcing_success_rate = od.value.enforcing_success_rate + max_ejection_percent = od.value.max_ejection_percent + success_rate_minimum_hosts = od.value.success_rate_minimum_hosts + success_rate_request_volume = od.value.success_rate_request_volume + success_rate_stdev_factor = od.value.success_rate_stdev_factor + dynamic "base_ejection_time" { + for_each = ( + od.value.base_ejection_time == null ? [] : [od.value.base_ejection_time] + ) + content { + seconds = base_ejection_time.value.seconds + nanos = base_ejection_time.value.nanos + } + } + dynamic "interval" { + for_each = ( + od.value.interval == null ? [] : [od.value.interval] + ) + content { + seconds = interval.value.seconds + nanos = interval.value.nanos + } + } + } + } + + dynamic "subsetting" { + for_each = var.backend_service_config.enable_subsetting == true ? [""] : [] + content { + policy = "CONSISTENT_HASH_SUBSETTING" + } + } + +} diff --git a/modules/net-ilb-l7/backend-services.tf b/modules/net-ilb-l7/backend-services.tf deleted file mode 100644 index 3e8ccc2fec..0000000000 --- a/modules/net-ilb-l7/backend-services.tf +++ /dev/null @@ -1,133 +0,0 @@ -/** - * 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. - -resource "google_compute_region_backend_service" "backend_service" { - for_each = var.backend_services_config - name = "${var.name}-${each.key}" - project = var.project_id - description = "Terraform managed." - affinity_cookie_ttl_sec = try(each.value.options.affinity_cookie_ttl_sec, null) - connection_draining_timeout_sec = try(each.value.options.connection_draining_timeout_sec, null) - load_balancing_scheme = "INTERNAL_MANAGED" - locality_lb_policy = try(each.value.options.locality_lb_policy, null) - port_name = try(each.value.options.port_name, null) - protocol = try(each.value.options.protocol, null) - region = var.region - session_affinity = try(each.value.options.session_affinity, null) - timeout_sec = try(each.value.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.health_checks), 0) == 0 - ? try( - [google_compute_region_health_check.health_check["default"].self_link], - null - ) - : [ - for hc in each.value.health_checks : - try(google_compute_region_health_check.health_check[hc].self_link, hc) - ] - ) - - dynamic "backend" { - for_each = try(each.value.backends, []) - content { - balancing_mode = try(backend.value.options.balancing_mode, "UTILIZATION") - capacity_scaler = try(backend.value.options.capacity_scaler, 1.0) - 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.options.circuit_breakers, null) == null - ? [] - : [each.value.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.options.consistent_hash, null) == null - ? [] - : [each.value.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 "iap" { - for_each = ( - try(each.value.options.iap, null) == null - ? [] - : [each.value.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.log_config, null) == null - ? [] - : [each.value.log_config] - ) - content { - enable = try(log_config.value.enable, null) - sample_rate = try(log_config.value.sample_rate, null) - } - } -} diff --git a/modules/net-ilb-l7/forwarding-rule.tf b/modules/net-ilb-l7/forwarding-rule.tf deleted file mode 100644 index 31ff652563..0000000000 --- a/modules/net-ilb-l7/forwarding-rule.tf +++ /dev/null @@ -1,68 +0,0 @@ -/** - * 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 IP Address and forwarding rule. - -locals { - ip_address = ( - var.static_ip_config.reserve - ? google_compute_address.static_ip.0.id - : null - ) - - port_range = coalesce( - var.forwarding_rule_config.port_range, - var.https ? "443" : "80" - ) - - target = ( - var.https - ? google_compute_region_target_https_proxy.https.0.id - : google_compute_region_target_http_proxy.http.0.id - ) -} - -resource "google_compute_address" "static_ip" { - count = var.static_ip_config.reserve ? 1 : 0 - provider = google-beta - name = var.name - project = var.project_id - description = "Terraform managed." - address_type = "INTERNAL" - address = try(var.static_ip_config.options.address, null) - purpose = "GCE_ENDPOINT" - region = try(var.region, null) - subnetwork = try(var.static_ip_config.options.subnet, var.subnetwork, null) -} - -resource "google_compute_forwarding_rule" "forwarding_rule" { - provider = google-beta - name = var.name - project = var.project_id - description = "Terraform managed." - ip_address = local.ip_address - ip_protocol = "TCP" - labels = try(var.forwarding_rule_config.labels, {}) - load_balancing_scheme = "INTERNAL_MANAGED" - network = try(var.forwarding_rule_config.network, null) - network_tier = var.forwarding_rule_config.network_tier - port_range = local.port_range - ports = [] - region = try(var.region, null) - service_label = try(var.forwarding_rule_config.service_label, null) - subnetwork = try(var.subnetwork, null) - target = local.target -} diff --git a/modules/net-ilb-l7/health-check.tf b/modules/net-ilb-l7/health-check.tf new file mode 100644 index 0000000000..c9df52a229 --- /dev/null +++ b/modules/net-ilb-l7/health-check.tf @@ -0,0 +1,109 @@ +/** + * 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 Health check resource. + +resource "google_compute_health_check" "default" { + provider = google-beta + for_each = var.health_check_configs + project = var.project_id + name = "${var.name}-${each.key}" + description = each.value.description + check_interval_sec = each.value.check_interval_sec + healthy_threshold = each.value.healthy_threshold + timeout_sec = each.value.timeout_sec + unhealthy_threshold = each.value.unhealthy_threshold + + dynamic "grpc_health_check" { + for_each = try(each.value.grpc, null) != null ? [""] : [] + content { + port = each.value.grpc.port + port_name = each.value.grpc.port_name + port_specification = each.value.grpc.port_specification + grpc_service_name = each.value.grpc.service_name + } + } + + dynamic "http_health_check" { + for_each = try(each.value.http, null) != null ? [""] : [] + content { + host = each.value.http.host + port = each.value.http.port + port_name = each.value.http.port_name + port_specification = each.value.http.port_specification + proxy_header = each.value.http.proxy_header + request_path = each.value.http.request_path + response = each.value.http.response + } + } + + dynamic "http2_health_check" { + for_each = try(each.value.http2, null) != null ? [""] : [] + content { + host = each.value.http2.host + port = each.value.http2.port + port_name = each.value.http2.port_name + port_specification = each.value.http2.port_specification + proxy_header = each.value.http2.proxy_header + request_path = each.value.http2.request_path + response = each.value.http2.response + } + } + + dynamic "https_health_check" { + for_each = try(each.value.https, null) != null ? [""] : [] + content { + host = each.value.https.host + port = each.value.https.port + port_name = each.value.https.port_name + port_specification = each.value.https.port_specification + proxy_header = each.value.https.proxy_header + request_path = each.value.https.request_path + response = each.value.https.response + } + } + + dynamic "ssl_health_check" { + for_each = try(each.value.ssl, null) != null ? [""] : [] + content { + port = each.value.ssl.port + port_name = each.value.ssl.port_name + port_specification = each.value.ssl.port_specification + proxy_header = each.value.ssl.proxy_header + request = each.value.ssl.request + response = each.value.ssl.response + } + } + + dynamic "tcp_health_check" { + for_each = try(each.value.tcp, null) != null ? [""] : [] + content { + port = each.value.tcp.port + port_name = each.value.tcp.port_name + port_specification = each.value.tcp.port_specification + proxy_header = each.value.tcp.proxy_header + request = each.value.tcp.request + response = each.value.tcp.response + } + } + + dynamic "log_config" { + for_each = try(each.value.enable_logging, null) == true ? [""] : [] + content { + enable = true + } + } +} diff --git a/modules/net-ilb-l7/health-checks.tf b/modules/net-ilb-l7/health-checks.tf deleted file mode 100644 index d484e6e8f3..0000000000 --- a/modules/net-ilb-l7/health-checks.tf +++ /dev/null @@ -1,148 +0,0 @@ -/** - * 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 Health checks. - -locals { - # Get backend services without health checks defined - _backends_without_hcs = [ - for k, v in coalesce(var.backend_services_config, {}) : - v if( - try(v.health_checks, null) == null - || length(try(v.health_checks, [])) == 0 - ) - ] - - health_checks_config_defaults = ( - try(var.health_checks_config_defaults, null) == null - ? null - : { default = var.health_checks_config_defaults } - ) - - # If at least one group backend service without HC is defined, - # create also a default HC (if default HC is not null) - health_checks_config = ( - length(local._backends_without_hcs) > 0 - ? merge( - coalesce(local.health_checks_config_defaults, {}), - coalesce(var.health_checks_config, {}) - ) - : coalesce(var.health_checks_config, {}) - ) -} - -resource "google_compute_region_health_check" "health_check" { - for_each = local.health_checks_config - provider = google-beta - name = "${var.name}-${each.key}" - project = var.project_id - description = "Terraform managed." - check_interval_sec = try(each.value.options.check_interval_sec, null) - healthy_threshold = try(each.value.options.healthy_threshold, null) - region = var.region - 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-ilb-l7/main.tf b/modules/net-ilb-l7/main.tf new file mode 100644 index 0000000000..cd8c40be1d --- /dev/null +++ b/modules/net-ilb-l7/main.tf @@ -0,0 +1,77 @@ +/** + * 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. + */ + +locals { + fwd_rule_ports = ( + var.protocol == "HTTPS" ? [443] : coalesce(var.ports, [80]) + ) + fwd_rule_target = ( + var.protocol == "HTTPS" + ? google_compute_region_target_https_proxy.default.0.id + : google_compute_region_target_http_proxy.default.0.id + ) + proxy_ssl_certificates = concat( + [for k, v in google_compute_region_ssl_certificate.default : v.id], + [for v in var.ssl_certificates : v.self_link if v.id != null] + ) +} + +resource "google_compute_forwarding_rule" "default" { + provider = google-beta + project = var.project_id + region = var.region + name = var.name + description = var.description + ip_address = var.address + ip_protocol = "TCP" + backend_service = google_compute_region_backend_service.default.self_link + load_balancing_scheme = "INTERNAL_MANAGED" + network = var.vpc_config.network + network_tier = var.network_tier_premium ? "PREMUIM" : "STANDARD" + port_range = local.fwd_rule_ports + subnetwork = var.vpc_config.subnetwork + labels = var.labels + target = local.fwd_rule_target + # service_directory_registrations +} + +resource "google_compute_region_ssl_certificate" "default" { + for_each = { for v in var.ssl_certificates : v.name => v if v.id == null } + project = var.project_id + region = var.region + name = "${var.name}-${each.key}" + certificate = try(each.value.tls_self_signed_cert, null) + private_key = try(each.value.tls_private_key, null) +} + +resource "google_compute_region_target_http_proxy" "default" { + count = var.protocol == "HTTPS" ? 0 : 1 + project = var.project_id + region = var.region + name = var.name + description = var.description + url_map = google_compute_region_url_map.default.id +} + +resource "google_compute_region_target_https_proxy" "default" { + count = var.protocol == "HTTPS" ? 1 : 0 + project = var.project_id + region = var.region + name = var.name + description = var.description + ssl_certificates = local.proxy_ssl_certificates + url_map = google_compute_region_url_map.default.id +} diff --git a/modules/net-ilb-l7/outputs.tf b/modules/net-ilb-l7/outputs.tf index 3ffca24341..500e90260b 100644 --- a/modules/net-ilb-l7/outputs.tf +++ b/modules/net-ilb-l7/outputs.tf @@ -14,45 +14,45 @@ * limitations under the License. */ -output "health_checks" { - description = "Health-check resources." - value = try(google_compute_region_health_check.health_check, []) -} +# output "health_checks" { +# description = "Health-check resources." +# value = try(google_compute_region_health_check.health_check, []) +# } -output "backend_services" { - description = "Backend service resources." - value = { - group = try(google_compute_region_backend_service.backend_service, []) - } -} +# output "backend_services" { +# description = "Backend service resources." +# value = { +# group = try(google_compute_region_backend_service.backend_service, []) +# } +# } -output "url_map" { - description = "The url-map." - value = try(google_compute_region_url_map.url_map, null) -} +# output "url_map" { +# description = "The url-map." +# value = try(google_compute_region_url_map.url_map, null) +# } -output "ssl_certificate_link_ids" { - description = "The SSL certificate." - value = { - for k, v in google_compute_region_ssl_certificate.certificates : k => v.self_link - } -} +# output "ssl_certificate_link_ids" { +# description = "The SSL certificate." +# value = { +# for k, v in google_compute_region_ssl_certificate.certificates : k => v.self_link +# } +# } -output "ip_address" { - description = "The reserved IP address." - value = try(google_compute_forwarding_rule.forwarding_rule.ip_address, null) -} +# output "ip_address" { +# description = "The reserved IP address." +# value = try(google_compute_forwarding_rule.forwarding_rule.ip_address, null) +# } -output "target_proxy" { - description = "The target proxy." - value = try( - google_compute_region_target_https_proxy.https.0, - google_compute_region_target_http_proxy.http.0, - [] - ) -} +# output "target_proxy" { +# description = "The target proxy." +# value = try( +# google_compute_region_target_https_proxy.https.0, +# google_compute_region_target_http_proxy.http.0, +# [] +# ) +# } -output "forwarding_rule" { - description = "The forwarding rule." - value = try(google_compute_forwarding_rule.forwarding_rule, null) -} +# output "forwarding_rule" { +# description = "The forwarding rule." +# value = try(google_compute_forwarding_rule.forwarding_rule, null) +# } diff --git a/modules/net-ilb-l7/ssl-certificates.tf b/modules/net-ilb-l7/ssl-certificates.tf deleted file mode 100644 index 46fafa3d1c..0000000000 --- a/modules/net-ilb-l7/ssl-certificates.tf +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 SSL certificates. - -resource "google_compute_region_ssl_certificate" "certificates" { - for_each = var.ssl_certificates_config - project = var.project_id - name = "${var.name}-${each.key}" - certificate = try(each.value.tls_self_signed_cert, null) - private_key = try(each.value.tls_private_key, null) - region = var.region -} diff --git a/modules/net-ilb-l7/target-proxy.tf b/modules/net-ilb-l7/target-proxy.tf deleted file mode 100644 index b43a64179c..0000000000 --- a/modules/net-ilb-l7/target-proxy.tf +++ /dev/null @@ -1,48 +0,0 @@ -/** - * 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 HTTP and HTTPS target proxies. - -locals { - # Look for the cert in the the ssl_certificates_config map. - # If not found, use the SSL certificate id as is (already existing). - ssl_certificates = [ - for cert in try(var.target_proxy_https_config.ssl_certificates, []) : - try( - google_compute_region_ssl_certificate.certificates[cert].self_link, - cert - ) - ] -} - -resource "google_compute_region_target_http_proxy" "http" { - count = var.https ? 0 : 1 - name = var.name - project = var.project_id - description = "Terraform managed." - region = var.region - url_map = google_compute_region_url_map.url_map.id -} - -resource "google_compute_region_target_https_proxy" "https" { - count = var.https ? 1 : 0 - name = var.name - project = var.project_id - description = "Terraform managed." - region = var.region - url_map = google_compute_region_url_map.url_map.id - ssl_certificates = local.ssl_certificates -} diff --git a/modules/net-ilb-l7/url-map.tf b/modules/net-ilb-l7/url-map.tf deleted file mode 100644 index 1d98b4978a..0000000000 --- a/modules/net-ilb-l7/url-map.tf +++ /dev/null @@ -1,708 +0,0 @@ -/** - * 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 URL maps. - -locals { - # Look for a backend service 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. - _default_service = try( - google_compute_region_backend_service.backend_service[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 - default_service = ( - try(local._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_region_backend_service.backend_service[keys(google_compute_region_backend_service.backend_service)[0]].id, - null - ) - : null - ) -} - -resource "google_compute_region_url_map" "url_map" { - name = var.name - description = "Terraform managed." - project = var.project_id - region = var.region - default_service = local.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_region_backend_service.backend_service[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_region_backend_service.backend_service[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_region_backend_service.backend_service[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_region_backend_service.backend_service[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_region_backend_service.backend_service[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_region_backend_service.backend_service[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_region_backend_service.backend_service[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_region_backend_service.backend_service[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-ilb-l7/urlmap.tf b/modules/net-ilb-l7/urlmap.tf new file mode 100644 index 0000000000..0b379ef881 --- /dev/null +++ b/modules/net-ilb-l7/urlmap.tf @@ -0,0 +1,563 @@ +/** + * 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 URL map resources. + +locals { + backend_ids = { + for k, v in google_compute_region_backend_service.default : k => v.id + } +} + +resource "google_compute_region_url_map" "default" { + project = var.project_id + region = var.region + name = var.name + description = var.description + default_service = var.urlmap_config.default_service + + dynamic "default_url_redirect" { + for_each = ( + var.urlmap_config.default_url_redirect == null + ? [] + : [var.urlmap_config.default_url_redirect] + ) + iterator = r + content { + host_redirect = r.value.host + https_redirect = r.value.https + path_redirect = r.value.path + prefix_redirect = r.value.prefix + redirect_response_code = r.value.response_code + strip_query = r.value.strip_query + } + } + + dynamic "host_rule" { + for_each = ( + var.urlmap_config.host_rule == null + ? [] + : [var.urlmap_config.host_rule] + ) + iterator = r + content { + hosts = r.value.hosts + path_matcher = r.value.path_matcher + description = r.value.description + } + } + + dynamic "path_matcher" { + for_each = coalesce(var.urlmap_config.path_matchers, {}) + iterator = m + content { + default_service = m.value.default_service == null ? null : lookup( + local.backend_ids, m.value.default_service, m.value.default_service + ) + description = pm.value.description + name = pm.key + dynamic "default_url_redirect" { + for_each = ( + each.value.default_url_redirect == null + ? [] + : [each.value.default_url_redirect] + ) + content { + host_redirect = default_url_redirect.value.host + https_redirect = default_url_redirect.value.https + path_redirect = default_url_redirect.value.path + prefix_redirect = default_url_redirect.value.prefix + redirect_response_code = default_url_redirect.value.response_code + strip_query = default_url_redirect.value.strip_query + } + } + dynamic "path_rule" { + for_each = toset(coalesce(each.value.path_rules)) + content { + paths = path_rule.value.paths + service = path_rule.value.default_service == null ? null : lookup( + local.backend_ids, + path_rule.value.default_service, + path_rule.value.default_service + ) + dynamic "route_action" { + for_each = ( + path_rule.value.route_action == null + ? [] + : [path_rule.value.route_action] + ) + content { + dynamic "cors_policy" { + for_each = ( + route_action.value.cors_policy == null + ? [] + : [route_action.value.cors_policy] + ) + content { + allow_credentials = cors_policy.value.allow_credentials + allow_headers = cors_policy.value.allow_headers + allow_methods = cors_policy.value.allow_methods + allow_origin_regexes = cors_policy.value.allow_origin_regexes + allow_origins = cors_policy.value.allow_origins + disabled = cors_policy.value.disabled + expose_headers = cors_policy.value.expose_headers + max_age = cors_policy.value.max_age + } + } + dynamic "fault_injection_policy" { + for_each = ( + route_action.value.fault_injection_policy == null + ? [] + : [route_action.value.fault_injection_policy] + ) + content { + dynamic "abort" { + for_each = ( + fault_injection_policy.value.abort == null + ? [] + : [fault_injection_policy.value.abort] + ) + content { + http_status = abort.value.status + percentage = abort.value.percentage + } + } + dynamic "delay" { + for_each = ( + fault_injection_policy.value.delay == null + ? [] + : [fault_injection_policy.value.delay] + ) + content { + percentage = delay.value.percentage + fixed_delay { + nanos = delay.value.fixed.nanos + seconds = delay.value.fixed.seconds + } + } + } + } + } + dynamic "request_mirror_policy" { + for_each = ( + route_action.value.request_mirror_backend == null + ? [] + : [""] + ) + content { + backend_service = lookup( + local.backend_ids, + route_action.value.request_mirror_backend, + route_action.value.request_mirror_backend + ) + } + } + dynamic "retry_policy" { + for_each = ( + route_action.value.retry_policy == null + ? [] + : [route_action.value.retry_policy] + ) + content { + num_retries = retry_policy.value.num_retries + retry_conditions = retry_policy.value.retry_conditions + dynamic "per_try_timeout" { + for_each = ( + retry_policy.value.per_try_timeout == null + ? [] + : [retry_policy.value.per_try_timeout] + ) + content { + nanos = per_try_timeout.value.nanos + seconds = per_try_timeout.value.seconds + } + } + } + } + dynamic "timeout" { + for_each = ( + route_action.value.timeout == null + ? [] + : [route_action.value.timeout] + ) + content { + nanos = timeout.value.nanos + seconds = timeout.value.seconds + } + } + dynamic "url_rewrite" { + for_each = ( + route_action.value.url_rewrite == null + ? [] + : [route_action.value.url_rewrite] + ) + content { + host_rewrite = url_rewrite.value.host + path_prefix_rewrite = url_rewrite.value.path_prefix + } + } + dynamic "weighted_backend_services" { + for_each = coalesce( + route_action.value.weighted_backend_services, {} + ) + iterator = service + content { + backend_service = lookup( + local.backend_ids, service.key, service.key + ) + weight = service.value.weight + dynamic "header_action" { + for_each = ( + service.value.header_action == null + ? [] + : [service.value.header_action] + ) + iterator = h + content { + request_headers_to_remove = h.value.request_remove + response_headers_to_remove = h.value.response_remove + dynamic "request_headers_to_add" { + for_each = coalesce(h.value.request_add, {}) + content { + header_name = request_headers_to_add.key + header_value = request_headers_to_add.value.value + replace = request_headers_to_add.value.replace + } + } + dynamic "response_headers_to_add" { + for_each = coalesce(h.value.response_add, {}) + content { + header_name = response_headers_to_add.key + header_value = response_headers_to_add.value.value + replace = response_headers_to_add.value.replace + } + } + } + } + } + } + } + } + dynamic "url_redirect" { + for_each = ( + path_rule.value.default_url_redirect == null + ? [] + : [path_rule.value.default_url_redirect] + ) + content { + host_redirect = url_redirect.value.host + https_redirect = url_redirect.value.https + path_redirect = url_redirect.value.path + prefix_redirect = url_redirect.value.prefix + redirect_response_code = url_redirect.value.response_code + strip_query = url_redirect.value.strip_query + } + } + } + } + dynamic "route_rules" { + for_each = toset(coalesce(each.value.route_rules)) + content { + priority = route_rules.value.priority + service = route_rules.value.service == null ? null : lookup( + local.backend_ids, + route_rules.value.service, + route_rules.value.service + ) + dynamic "header_action" { + for_each = ( + route_rules.value.header_action == null + ? [] + : [route_rules.value.header_action] + ) + iterator = h + content { + request_headers_to_remove = h.value.request_remove + response_headers_to_remove = h.value.response_remove + dynamic "request_headers_to_add" { + for_each = coalesce(h.value.request_add, {}) + content { + header_name = request_headers_to_add.key + header_value = request_headers_to_add.value.value + replace = request_headers_to_add.value.replace + } + } + dynamic "response_headers_to_add" { + for_each = coalesce(h.value.response_add, {}) + content { + header_name = response_headers_to_add.key + header_value = response_headers_to_add.value.value + replace = response_headers_to_add.value.replace + } + } + } + } + dynamic "match_rules" { + for_each = toset(coalesce(route_rules.value.match_rules, [])) + content { + ignore_case = match_rules.value.ignore_case + full_path_match = ( + try(match_rules.value.path.type, null) == "full" + ? match_rules.value.path.value + : null + ) + prefix_match = ( + try(match_rules.value.path.type, null) == "prefix" + ? match_rules.value.path.value + : null + ) + regex_match = ( + try(match_rules.value.path.type, null) == "regex" + ? match_rules.value.path.value + : null + ) + dynamic "header_matches" { + for_each = toset(coalesce(match_rules.value.headers, [])) + iterator = h + content { + header_name = h.value.name + exact_match = h.value.type == "exact" ? h.value.value : null + invert_match = h.value.invert_match + prefix_match = h.value.type == "prefix" ? h.value.value : null + present_match = h.value.type == "present" ? h.value.value : null + range_match = h.value.type == "range" ? h.value.value : null + regex_match = h.value.type == "regex" ? h.value.value : null + suffix_match = h.value.type == "suffix" ? h.value.value : null + } + } + dynamic "metadata_filters" { + for_each = toset(coalesce(match_rules.value.metadata_filters, [])) + iterator = m + content { + filter_match_criteria = ( + m.value.match_all ? "MATCH_ALL" : "MATCH_ANY" + ) + dynamic "filter_labels" { + for_each = m.value.labels + content { + name = filter_labels.key + value = filter_labels.value + } + } + } + } + dynamic "query_parameter_matches" { + for_each = toset(coalesce(match_rules.value.query_params, [])) + iterator = q + content { + name = q.value.name + exact_match = ( + q.value.type == "exact" ? q.value.value : null + ) + present_match = ( + q.value.type == "present" ? q.value.value : null + ) + regex_match = ( + q.value.type == "regex" ? q.value.value : null + ) + } + } + } + } + dynamic "route_action" { + for_each = ( + route_rules.value.route_action == null + ? [] + : [route_rules.value.route_action] + ) + content { + dynamic "cors_policy" { + for_each = ( + route_action.value.cors_policy == null + ? [] + : [route_action.value.cors_policy] + ) + content { + allow_credentials = cors_policy.value.allow_credentials + allow_headers = cors_policy.value.allow_headers + allow_methods = cors_policy.value.allow_methods + allow_origin_regexes = cors_policy.value.allow_origin_regexes + allow_origins = cors_policy.value.allow_origins + disabled = cors_policy.value.disabled + expose_headers = cors_policy.value.expose_headers + max_age = cors_policy.value.max_age + } + } + dynamic "fault_injection_policy" { + for_each = ( + route_action.value.fault_injection_policy == null + ? [] + : [route_action.value.fault_injection_policy] + ) + content { + dynamic "abort" { + for_each = ( + fault_injection_policy.value.abort == null + ? [] + : [fault_injection_policy.value.abort] + ) + content { + http_status = abort.value.status + percentage = abort.value.percentage + } + } + dynamic "delay" { + for_each = ( + fault_injection_policy.value.delay == null + ? [] + : [fault_injection_policy.value.delay] + ) + content { + percentage = delay.value.percentage + fixed_delay { + nanos = delay.value.fixed.nanos + seconds = delay.value.fixed.seconds + } + } + } + } + } + dynamic "request_mirror_policy" { + for_each = ( + route_action.value.request_mirror_backend == null + ? [] + : [""] + ) + content { + backend_service = lookup( + local.backend_ids, + route_action.value.request_mirror_backend, + route_action.value.request_mirror_backend + ) + } + } + dynamic "retry_policy" { + for_each = ( + route_action.value.retry_policy == null + ? [] + : [route_action.value.retry_policy] + ) + content { + num_retries = retry_policy.value.num_retries + retry_conditions = retry_policy.value.retry_conditions + dynamic "per_try_timeout" { + for_each = ( + retry_policy.value.per_try_timeout == null + ? [] + : [retry_policy.value.per_try_timeout] + ) + content { + nanos = per_try_timeout.value.nanos + seconds = per_try_timeout.value.seconds + } + } + } + } + dynamic "timeout" { + for_each = ( + route_action.value.timeout == null + ? [] + : [route_action.value.timeout] + ) + content { + nanos = timeout.value.nanos + seconds = timeout.value.seconds + } + } + dynamic "url_rewrite" { + for_each = ( + route_action.value.url_rewrite == null + ? [] + : [route_action.value.url_rewrite] + ) + content { + host_rewrite = url_rewrite.value.host + path_prefix_rewrite = url_rewrite.value.path_prefix + } + } + dynamic "weighted_backend_services" { + for_each = coalesce( + route_action.value.weighted_backend_services, {} + ) + iterator = service + content { + backend_service = lookup( + local.backend_ids, service.key, service.key + ) + weight = service.value.weight + dynamic "header_action" { + for_each = ( + service.value.header_action == null + ? [] + : [service.value.header_action] + ) + iterator = h + content { + request_headers_to_remove = h.value.request_remove + response_headers_to_remove = h.value.response_remove + dynamic "request_headers_to_add" { + for_each = coalesce(h.value.request_add, {}) + content { + header_name = request_headers_to_add.key + header_value = request_headers_to_add.value.value + replace = request_headers_to_add.value.replace + } + } + dynamic "response_headers_to_add" { + for_each = coalesce(h.value.response_add, {}) + content { + header_name = response_headers_to_add.key + header_value = response_headers_to_add.value.value + replace = response_headers_to_add.value.replace + } + } + } + } + } + } + } + } + dynamic "url_redirect" { + for_each = ( + route_rules.value.default_url_redirect == null + ? [] + : [route_rules.value.default_url_redirect] + ) + content { + host_redirect = url_redirect.value.host + https_redirect = url_redirect.value.https + path_redirect = url_redirect.value.path + prefix_redirect = url_redirect.value.prefix + redirect_response_code = url_redirect.value.response_code + strip_query = url_redirect.value.strip_query + } + } + } + } + } + } + + dynamic "test" { + for_each = toset(coalesce(var.urlmap_config.test, [])) + content { + host = test.value.host + path = test.value.path + service = test.value.service + description = test.value.description + } + } + +} diff --git a/modules/net-ilb-l7/variables-health-check.tf b/modules/net-ilb-l7/variables-health-check.tf new file mode 100644 index 0000000000..9b2066a5a9 --- /dev/null +++ b/modules/net-ilb-l7/variables-health-check.tf @@ -0,0 +1,95 @@ +/** + * 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 Health check variable. + +variable "health_check_configs" { + description = "Optional auto-created health check configurations, use the output self-link to set it in the auto healing policy. Refer to examples for usage." + type = map(object({ + check_interval_sec = optional(number) + description = optional(string, "Terraform managed.") + enable_logging = optional(bool, false) + healthy_threshold = optional(number) + timeout_sec = optional(number) + unhealthy_threshold = optional(number) + grpc = optional(object({ + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + service_name = optional(string) + })) + http = optional(object({ + host = optional(string) + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + proxy_header = optional(string) + request_path = optional(string) + response = optional(string) + })) + http2 = optional(object({ + host = optional(string) + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + proxy_header = optional(string) + request_path = optional(string) + response = optional(string) + })) + https = optional(object({ + host = optional(string) + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + proxy_header = optional(string) + request_path = optional(string) + response = optional(string) + })) + tcp = optional(object({ + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + proxy_header = optional(string) + request = optional(string) + response = optional(string) + })) + ssl = optional(object({ + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + proxy_header = optional(string) + request = optional(string) + response = optional(string) + })) + })) + default = { + default = { + http = { + port_specification = "USE_SERVING_PORT" + } + } + } + validation { + condition = alltrue([ + for k, v in var.health_check_configs : ( + (try(v.grpc, null) == null ? 0 : 1) + + (try(v.http, null) == null ? 0 : 1) + + (try(v.tcp, null) == null ? 0 : 1) <= 1 + ) + ]) + error_message = "Only one health check type can be configured at a time." + } +} diff --git a/modules/net-ilb-l7/variables-urlmap.tf b/modules/net-ilb-l7/variables-urlmap.tf new file mode 100644 index 0000000000..7e41a7fa41 --- /dev/null +++ b/modules/net-ilb-l7/variables-urlmap.tf @@ -0,0 +1,228 @@ +/** + * 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 URLmap variable. + +variable "urlmap_config" { + description = "The URL map configuration." + type = object({ + default_service = optional(string) + default_url_redirect = optional(object({ + host = optional(string) + https = optional(bool) + path = optional(string) + prefix = optional(string) + response_code = optional(string) + strip_query = optional(bool) + })) + host_rule = optional(object({ + hosts = list(string) + path_matcher = string + description = optional(string) + })) + path_matchers = optional(map(object({ + description = optional(string) + default_service = optional(string) + default_url_redirect = optional(object({ + host = optional(string) + https = optional(bool) + path = optional(string) + prefix = optional(string) + response_code = optional(string) + strip_query = optional(bool) + })) + path_rules = optional(list(object({ + paths = list(string) + service = optional(string) + route_action = optional(object({ + request_mirror_backend = optional(string) + cors_policy = optional(object({ + allow_credentials = optional(bool) + allow_headers = optional(string) + allow_methods = optional(string) + allow_origin_regexes = list(string) + allow_origins = list(string) + disabled = optional(bool) + expose_headers = optional(string) + max_age = optional(string) + })) + fault_injection_policy = optional(object({ + abort = optional(object({ + percentage = number + status = number + })) + delay = optional(object({ + fixed = object({ + seconds = number + nanos = number + }) + percentage = number + })) + })) + retry_policy = optional(object({ + num_retries = number + retry_conditions = optional(list(string)) + per_try_timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + url_rewrite = optional(object({ + host = optional(string) + path_prefix = optional(string) + })) + weighted_backend_services = optional(map(object({ + weight = number + header_action = optional(object({ + request_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + request_remove = optional(list(string)) + response_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + response_remove = optional(list(string)) + })) + }))) + })) + url_redirect = optional(object({ + host = optional(string) + https = optional(bool) + path = optional(string) + prefix = optional(string) + response_code = optional(string) + strip_query = optional(bool) + })) + }))) + route_rules = optional(list(object({ + priority = number + service = optional(string) + header_action = optional(object({ + request_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + request_remove = optional(list(string)) + response_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + response_remove = optional(list(string)) + })) + match_rules = optional(list(object({ + ignore_case = optional(bool, false) + headers = optional(list(object({ + name = string + value = string + invert_match = optional(bool, false) + type = optional(string, "present") # exact, prefix, suffix, regex, present, range + }))) + metadata_filters = optional(list(object({ + labels = map(string) + match_all = bool # MATCH_ANY, MATCH_ALL + }))) + path = optional(object({ + value = string + type = optional(string, "prefix") # full, prefix, regex + })) + query_params = optional(list(object({ + name = string + value = string + type = optional(string, "present") # exact, present, regex + }))) + }))) + route_action = optional(object({ + request_mirror_backend = optional(string) + cors_policy = optional(object({ + allow_credentials = optional(bool) + allow_headers = optional(string) + allow_methods = optional(string) + allow_origin_regexes = list(string) + allow_origins = list(string) + disabled = optional(bool) + expose_headers = optional(string) + max_age = optional(string) + })) + fault_injection_policy = optional(object({ + abort = optional(object({ + percentage = number + status = number + })) + delay = optional(object({ + fixed = object({ + seconds = number + nanos = number + }) + percentage = number + })) + })) + retry_policy = optional(object({ + num_retries = number + retry_conditions = optional(list(string)) + per_try_timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + url_rewrite = optional(object({ + host = optional(string) + path_prefix = optional(string) + })) + weighted_backend_services = optional(map(object({ + weight = number + header_action = optional(object({ + request_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + request_remove = optional(list(string)) + response_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + response_remove = optional(list(string)) + })) + }))) + })) + url_redirect = optional(object({ + host = optional(string) + https = optional(bool) + path = optional(string) + prefix = optional(string) + response_code = optional(string) + strip_query = optional(bool) + })) + }))) + }))) + test = optional(list(object({ + host = string + path = string + service = string + description = optional(string) + }))) + }) + default = null +} diff --git a/modules/net-ilb-l7/variables.tf b/modules/net-ilb-l7/variables.tf index 7202e3faed..bdb3d2f74d 100644 --- a/modules/net-ilb-l7/variables.tf +++ b/modules/net-ilb-l7/variables.tf @@ -14,146 +14,189 @@ * limitations under the License. */ -variable "name" { - description = "Load balancer name." +variable "address" { + description = "Optional IP address used for the forwarding rule." type = string + default = null } -variable "project_id" { - description = "Project id." +variable "description" { + description = "Optional description used for resources." type = string + default = "Terraform managed." } -variable "backend_services_config" { - description = "The backends services configuration." +variable "backend_service_config" { + description = "Backend service level configuration." type = map(object({ - backends = list(object({ - group = string # The instance group link id - options = object({ - balancing_mode = string # Can be UTILIZATION, RATE - capacity_scaler = number # Valid range is [0.0,1.0] - max_connections = number - max_connections_per_instance = number - max_connections_per_endpoint = number - max_rate = number - max_rate_per_instance = number - max_rate_per_endpoint = number - max_utilization = number - }) + affinity_cookie_ttl_sec = optional(number) + connection_draining_timeout_sec = optional(number) + health_checks = optional(list(string), ["default"]) + locality_lb_policy = optional(string) + log_sample_rate = optional(number) + port_name = optional(string) + session_affinity = optional(string) + timeout_sec = optional(number) + circuit_breakers = optional(object({ + max_connections = optional(number) + max_pending_requests = optional(number) + max_requests = optional(number) + max_requests_per_connection = optional(number) + max_retries = optional(number) + connect_timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + connection_tracking = optional(object({ + idle_timeout_sec = optional(number) + persist_conn_on_unhealthy = optional(string) + track_per_session = optional(bool) + })) + consistent_hash = optional(object({ + http_header_name = optional(string) + minimum_ring_size = optional(number) + http_cookie = optional(object({ + name = optional(string) + path = optional(string) + ttl = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + })) + enable_subsetting = optional(bool) + failover_config = optional(object({ + disable_conn_drain = optional(bool) + drop_traffic_if_unhealthy = optional(bool) + ratio = optional(number) + })) + iap_config = optional(object({ + oauth2_client_id = string + oauth2_client_secret = string + oauth2_client_secret_sha256 = optional(string) + })) + outlier_detection = optional(object({ + consecutive_errors = optional(number) + consecutive_gateway_failure = optional(number) + enforcing_consecutive_errors = optional(number) + enforcing_consecutive_gateway_failure = optional(number) + enforcing_success_rate = optional(number) + max_ejection_percent = optional(number) + success_rate_minimum_hosts = optional(number) + success_rate_request_volume = optional(number) + success_rate_stdev_factor = optional(number) + base_ejection_time = optional(object({ + seconds = number + nanos = optional(number) + })) + interval = optional(object({ + seconds = number + nanos = optional(number) + })) })) - - # Optional health check ids for backend service groups. - # Will lookup for ids in health_chacks_config first, - # then will use the id as is. If no ids are defined - # at all (null, []) health_checks_config_defaults is used - health_checks = list(string) - - log_config = object({ - enable = bool - sample_rate = number # must be in [0, 1] - }) - - options = object({ - affinity_cookie_ttl_sec = number - custom_request_headers = list(string) - custom_response_headers = list(string) - connection_draining_timeout_sec = number - locality_lb_policy = string - port_name = string - protocol = string - session_affinity = string - timeout_sec = number - - circuits_breakers = object({ - max_requests_per_connection = number # Set to 1 to disable keep-alive - max_connections = number # Defaults to 1024 - max_pending_requests = number # Defaults to 1024 - max_requests = number # Defaults to 1024 - max_retries = number # Defaults to 3 - }) - - consistent_hash = object({ - http_header_name = string - minimum_ring_size = string - http_cookie = object({ - name = string - path = string - ttl = object({ - seconds = number - nanos = number - }) - }) - }) - - iap = object({ - oauth2_client_id = string - oauth2_client_secret = string - oauth2_client_secret_sha256 = string - }) - }) })) - default = {} + default = {} + nullable = false + validation { + condition = contains( + [ + "-", "ROUND_ROBIN", "LEAST_REQUEST", "RING_HASH", + "RANDOM", "ORIGINAL_DESTINATION", "MAGLEV" + ], + try(var.backend_service_config.locality_lb_policy, "-") + ) + error_message = "Invalid locality lb policy value." + } + validation { + condition = contains( + [ + "NONE", "CLIENT_IP", "CLIENT_IP_NO_DESTINATION", + "CLIENT_IP_PORT_PROTO", "CLIENT_IP_PROTO" + ], + try(var.backend_service_config.session_affinity, "NONE") + ) + error_message = "Invalid session affinity value." + } } -variable "forwarding_rule_config" { - description = "Forwarding rule configurations." - type = object({ - ip_version = string - labels = map(string) - network_tier = string - port_range = string - service_label = string - }) - default = { - allow_global_access = true - ip_version = "IPV4" - labels = {} - network_tier = "PREMIUM" - # If not specified, 443 if var.https = true; 80 otherwise - port_range = null - service_label = null +variable "backends" { + description = "Load balancer backends, balancing mode is one of 'CONNECTION', 'RATE' or 'UTILIZATION'." + type = list(object({ + group = string + balancing_mode = optional(string, "CONNECTION") + capacity_scaler = optional(number) + description = optional(string, "Terraform managed.") + failover = optional(bool, false) + max_connections = optional(object({ + per_endpoint = optional(number) + per_group = optional(number) + per_instance = optional(number) + })) + max_rate = optional(object({ + per_endpoint = optional(number) + per_group = optional(number) + per_instance = optional(number) + })) + max_utilization = optional(number) + })) + default = [] + nullable = false + validation { + condition = alltrue([ + for b in var.backends : contains( + ["CONNECTION", "RATE", "UTILIZATION"], + coalesce(b.balancing_mode, "CONNECTION") + )]) + error_message = "When specified balancing mode needs to be 'CONNECTION', 'RATE' or 'UTILIZATION'." } } -variable "health_checks_config" { - description = "Custom health checks configuration." +variable "group_configs" { + description = "Optional unmanaged groups to create. Can be referenced in backends via outputs." type = map(object({ - type = string # http https tcp ssl http2 - check = map(any) # actual health check block attributes - options = map(number) # interval, thresholds, timeout - logging = bool + zone = string + instances = optional(list(string), []) + named_ports = optional(map(number), {}) })) - default = {} + default = {} + nullable = false } -variable "health_checks_config_defaults" { - description = "Auto-created health check default configuration." - type = object({ - check = map(any) # actual health check block attributes - logging = bool - options = map(number) # interval, thresholds, timeout - type = string # http https tcp ssl http2 - }) - default = { - type = "http" - logging = false - options = {} - check = { - port_specification = "USE_SERVING_PORT" - } - } +variable "labels" { + description = "Labels set on resources." + type = map(string) + default = {} } -variable "https" { - description = "Whether to enable HTTPS." +variable "name" { + description = "Load balancer name." + type = string +} + +variable "network_tier_premium" { + description = "Use premium network tier. Defaults to true." type = bool - default = false + default = true + nullable = false } -variable "network" { - description = "The network where the ILB is created." +variable "project_id" { + description = "Project id." type = string - default = "default" +} + +variable "ports" { + description = "Optional ports for HTTP load balancer, valid ports are 80 and 8080." + type = list(string) + default = null +} + +variable "protocol" { + description = "IP protocol used, defaults to TCP." + type = string + default = "HTTP" + nullable = false } variable "region" { @@ -161,14 +204,19 @@ variable "region" { type = string } -variable "ssl_certificates_config" { - description = "The SSL certificates configuration." - type = map(object({ - domains = list(string) - tls_private_key = string - tls_self_signed_cert = string +variable "ssl_certificates" { + description = "SSL target proxy certificates (only if protocol is HTTPS). Specify id for existing certificates, create config attributes to create." + type = list(object({ + create_config = optional(object({ + domains = list(string) + name = list(string) + tls_private_key = string + tls_self_signed_cert = string + })) + id = optional(string) })) - default = {} + default = [] + nullable = false } variable "static_ip_config" { @@ -186,27 +234,11 @@ variable "static_ip_config" { } } -variable "subnetwork" { - description = "The subnetwork where the ILB VIP is allocated." - type = string -} - -variable "target_proxy_https_config" { - description = "The HTTPS target proxy configuration." - type = object({ - ssl_certificates = list(string) - }) - default = null -} - -variable "url_map_config" { - description = "The url-map configuration." +variable "vpc_config" { + description = "VPC-level configuration." type = object({ - default_service = string - default_url_redirect = map(any) - host_rules = list(any) - path_matchers = list(any) - tests = list(map(string)) + network = string + subnetwork = string }) - default = null + nullable = false } From c1d473099b5a70363c7c9d2752f43b81971e6116 Mon Sep 17 00:00:00 2001 From: Ludo Date: Sun, 13 Nov 2022 17:09:37 +0100 Subject: [PATCH 02/13] wip --- modules/net-ilb-l7/main.tf | 3 +-- modules/net-ilb-l7/urlmap.tf | 21 ++++++++++++++++----- modules/net-ilb-l7/variables-urlmap.tf | 6 +++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/modules/net-ilb-l7/main.tf b/modules/net-ilb-l7/main.tf index cd8c40be1d..236703da01 100644 --- a/modules/net-ilb-l7/main.tf +++ b/modules/net-ilb-l7/main.tf @@ -37,11 +37,10 @@ resource "google_compute_forwarding_rule" "default" { description = var.description ip_address = var.address ip_protocol = "TCP" - backend_service = google_compute_region_backend_service.default.self_link load_balancing_scheme = "INTERNAL_MANAGED" network = var.vpc_config.network network_tier = var.network_tier_premium ? "PREMUIM" : "STANDARD" - port_range = local.fwd_rule_ports + port_range = join(",", local.fwd_rule_ports) subnetwork = var.vpc_config.subnetwork labels = var.labels target = local.fwd_rule_target diff --git a/modules/net-ilb-l7/urlmap.tf b/modules/net-ilb-l7/urlmap.tf index 0b379ef881..7a29a2f71b 100644 --- a/modules/net-ilb-l7/urlmap.tf +++ b/modules/net-ilb-l7/urlmap.tf @@ -23,6 +23,7 @@ locals { } resource "google_compute_region_url_map" "default" { + provider = google-beta project = var.project_id region = var.region name = var.name @@ -71,9 +72,9 @@ resource "google_compute_region_url_map" "default" { name = pm.key dynamic "default_url_redirect" { for_each = ( - each.value.default_url_redirect == null + m.value.default_url_redirect == null ? [] - : [each.value.default_url_redirect] + : [m.value.default_url_redirect] ) content { host_redirect = default_url_redirect.value.host @@ -85,7 +86,7 @@ resource "google_compute_region_url_map" "default" { } } dynamic "path_rule" { - for_each = toset(coalesce(each.value.path_rules)) + for_each = toset(coalesce(m.value.path_rules)) content { paths = path_rule.value.paths service = path_rule.value.default_service == null ? null : lookup( @@ -269,7 +270,7 @@ resource "google_compute_region_url_map" "default" { } } dynamic "route_rules" { - for_each = toset(coalesce(each.value.route_rules)) + for_each = toset(coalesce(m.value.route_rules)) content { priority = route_rules.value.priority service = route_rules.value.service == null ? null : lookup( @@ -333,9 +334,19 @@ resource "google_compute_region_url_map" "default" { invert_match = h.value.invert_match prefix_match = h.value.type == "prefix" ? h.value.value : null present_match = h.value.type == "present" ? h.value.value : null - range_match = h.value.type == "range" ? h.value.value : null regex_match = h.value.type == "regex" ? h.value.value : null suffix_match = h.value.type == "suffix" ? h.value.value : null + dynamic "range_match" { + for_each = ( + h.value.type != "range" || h.value.range_value == null + ? [] + : [""] + ) + content { + range_end = h.value.range_value.end + range_start = h.value.range_value.start + } + } } } dynamic "metadata_filters" { diff --git a/modules/net-ilb-l7/variables-urlmap.tf b/modules/net-ilb-l7/variables-urlmap.tf index 7e41a7fa41..c6044cc84b 100644 --- a/modules/net-ilb-l7/variables-urlmap.tf +++ b/modules/net-ilb-l7/variables-urlmap.tf @@ -132,9 +132,13 @@ variable "urlmap_config" { ignore_case = optional(bool, false) headers = optional(list(object({ name = string - value = string invert_match = optional(bool, false) type = optional(string, "present") # exact, prefix, suffix, regex, present, range + value = optional(string) + range_value = optional(object({ + end = string + start = string + })) }))) metadata_filters = optional(list(object({ labels = map(string) From 4a31ca8f7584adad103c5e651a866c065b0f3b37 Mon Sep 17 00:00:00 2001 From: Ludo Date: Sun, 13 Nov 2022 17:22:25 +0100 Subject: [PATCH 03/13] minimal example test --- modules/net-ilb-l7/README.md | 26 ++++++------ modules/net-ilb-l7/backend-service.tf | 11 +++-- modules/net-ilb-l7/variables-urlmap.tf | 1 - modules/net-ilb-l7/variables.tf | 56 ++++++++++---------------- 4 files changed, 38 insertions(+), 56 deletions(-) diff --git a/modules/net-ilb-l7/README.md b/modules/net-ilb-l7/README.md index 64a3f7761a..b206faeda6 100644 --- a/modules/net-ilb-l7/README.md +++ b/modules/net-ilb-l7/README.md @@ -15,22 +15,20 @@ module "ilb" { name = "ilb-test" project_id = var.project_id region = "europe-west1" - network = var.vpc.self_link - subnetwork = var.subnet.self_link - - backend_services_config = { - my-backend-svc = { - backends = [ - { - group = "projects/my-project/zones/europe-west1-a/instanceGroups/my-ig" - options = null - } - ] - health_checks = [] - log_config = null - options = null + backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] } } + urlmap_config = { + default_service = "default" + } + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link + } } # tftest modules=1 resources=5 ``` diff --git a/modules/net-ilb-l7/backend-service.tf b/modules/net-ilb-l7/backend-service.tf index ddae9d5b96..bf80b539f9 100644 --- a/modules/net-ilb-l7/backend-service.tf +++ b/modules/net-ilb-l7/backend-service.tf @@ -17,7 +17,6 @@ # tfdoc:file:description Backend service resources. locals { - bsc = var.backend_service_config hc = { for k, v in google_compute_health_check.default : k => v.id } @@ -25,7 +24,7 @@ locals { resource "google_compute_region_backend_service" "default" { provider = google-beta - for_each = var.backend_service_config + for_each = var.backend_service_configs project = var.project_id region = var.region name = "${var.name}-${each.key}" @@ -44,7 +43,7 @@ resource "google_compute_region_backend_service" "default" { timeout_sec = each.value.timeout_sec dynamic "backend" { - for_each = { for b in coalesce(var.backends, []) : b.group => b } + for_each = { for b in coalesce(each.value.backends, []) : b.group => b } content { group = backend.key balancing_mode = backend.value.balancing_mode @@ -167,10 +166,10 @@ resource "google_compute_region_backend_service" "default" { } dynamic "log_config" { - for_each = var.backend_service_config.log_sample_rate == null ? [] : [""] + for_each = each.value.log_sample_rate == null ? [] : [""] content { enable = true - sample_rate = var.backend_service_config.log_sample_rate + sample_rate = each.value.log_sample_rate } } @@ -211,7 +210,7 @@ resource "google_compute_region_backend_service" "default" { } dynamic "subsetting" { - for_each = var.backend_service_config.enable_subsetting == true ? [""] : [] + for_each = each.value.enable_subsetting == true ? [""] : [] content { policy = "CONSISTENT_HASH_SUBSETTING" } diff --git a/modules/net-ilb-l7/variables-urlmap.tf b/modules/net-ilb-l7/variables-urlmap.tf index c6044cc84b..8ed9610aa2 100644 --- a/modules/net-ilb-l7/variables-urlmap.tf +++ b/modules/net-ilb-l7/variables-urlmap.tf @@ -228,5 +228,4 @@ variable "urlmap_config" { description = optional(string) }))) }) - default = null } diff --git a/modules/net-ilb-l7/variables.tf b/modules/net-ilb-l7/variables.tf index bdb3d2f74d..bf2eb90d7e 100644 --- a/modules/net-ilb-l7/variables.tf +++ b/modules/net-ilb-l7/variables.tf @@ -26,7 +26,7 @@ variable "description" { default = "Terraform managed." } -variable "backend_service_config" { +variable "backend_service_configs" { description = "Backend service level configuration." type = map(object({ affinity_cookie_ttl_sec = optional(number) @@ -37,6 +37,24 @@ variable "backend_service_config" { port_name = optional(string) session_affinity = optional(string) timeout_sec = optional(number) + backends = list(object({ + group = string + balancing_mode = optional(string, "CONNECTION") + capacity_scaler = optional(number) + description = optional(string, "Terraform managed.") + failover = optional(bool, false) + max_connections = optional(object({ + per_endpoint = optional(number) + per_group = optional(number) + per_instance = optional(number) + })) + max_rate = optional(object({ + per_endpoint = optional(number) + per_group = optional(number) + per_instance = optional(number) + })) + max_utilization = optional(number) + })) circuit_breakers = optional(object({ max_connections = optional(number) max_pending_requests = optional(number) @@ -104,7 +122,7 @@ variable "backend_service_config" { "-", "ROUND_ROBIN", "LEAST_REQUEST", "RING_HASH", "RANDOM", "ORIGINAL_DESTINATION", "MAGLEV" ], - try(var.backend_service_config.locality_lb_policy, "-") + try(var.backend_service_configs.locality_lb_policy, "-") ) error_message = "Invalid locality lb policy value." } @@ -114,44 +132,12 @@ variable "backend_service_config" { "NONE", "CLIENT_IP", "CLIENT_IP_NO_DESTINATION", "CLIENT_IP_PORT_PROTO", "CLIENT_IP_PROTO" ], - try(var.backend_service_config.session_affinity, "NONE") + try(var.backend_service_configs.session_affinity, "NONE") ) error_message = "Invalid session affinity value." } } -variable "backends" { - description = "Load balancer backends, balancing mode is one of 'CONNECTION', 'RATE' or 'UTILIZATION'." - type = list(object({ - group = string - balancing_mode = optional(string, "CONNECTION") - capacity_scaler = optional(number) - description = optional(string, "Terraform managed.") - failover = optional(bool, false) - max_connections = optional(object({ - per_endpoint = optional(number) - per_group = optional(number) - per_instance = optional(number) - })) - max_rate = optional(object({ - per_endpoint = optional(number) - per_group = optional(number) - per_instance = optional(number) - })) - max_utilization = optional(number) - })) - default = [] - nullable = false - validation { - condition = alltrue([ - for b in var.backends : contains( - ["CONNECTION", "RATE", "UTILIZATION"], - coalesce(b.balancing_mode, "CONNECTION") - )]) - error_message = "When specified balancing mode needs to be 'CONNECTION', 'RATE' or 'UTILIZATION'." - } -} - variable "group_configs" { description = "Optional unmanaged groups to create. Can be referenced in backends via outputs." type = map(object({ From cf2b927dbfa4e0a3116d0d1224133099c3255bf1 Mon Sep 17 00:00:00 2001 From: Ludo Date: Sun, 13 Nov 2022 17:36:01 +0100 Subject: [PATCH 04/13] wip --- modules/net-ilb-l7/README.md | 125 ++++++++--------- .../net-ilb-l7/variables-backend-service.tf | 129 ++++++++++++++++++ modules/net-ilb-l7/variables.tf | 112 --------------- 3 files changed, 193 insertions(+), 173 deletions(-) create mode 100644 modules/net-ilb-l7/variables-backend-service.tf diff --git a/modules/net-ilb-l7/README.md b/modules/net-ilb-l7/README.md index b206faeda6..336eb8553e 100644 --- a/modules/net-ilb-l7/README.md +++ b/modules/net-ilb-l7/README.md @@ -33,7 +33,13 @@ module "ilb" { # tftest modules=1 resources=5 ``` -Network and subnetwork can be entered using their name (if present in the same project) or leveraging their link id. The latter is mandatory if you're trying to deploy an ILB in a shared VPC environment. +### Defining Health Checks + +You can leverage externally defined health checks for backend services, or have the module create them for you. By default a simple HTTP health check is created, and used in backend services. + +Health check configuration is controlled via the `health_check_configs` variable, which behaves in a similar way to other LB modules in this repository. + +Defining different health checks fromt he default is very easy. You can for example replace the default HTTP health check with a TCP one and reference it in you backend service: ```hcl module "ilb" { @@ -41,35 +47,31 @@ module "ilb" { name = "ilb-test" project_id = var.project_id region = "europe-west1" - network = "projects/my-host-project/global/networks/my-shared-vpc" - subnetwork = "projects/my-host-project/regions/europe-west1/subnetworks/my-shared-subnet" - - backend_services_config = { - my-backend-svc = { - backends = [ - { - group = "projects/my-project/zones/europe-west1-a/instanceGroups/my-ig" - options = null - } - ] - health_checks = [] - log_config = null - options = null + backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + health_checks = ["custom-tcp"] } } + health_check_configs = { + custom-tcp = { + tcp = { port = 80 } + } + } + urlmap_config = { + default_service = "default" + } + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link + } } # tftest modules=1 resources=5 ``` -### Defining Health Checks - -If no health checks are specified, a default health check is created and associated to each backend service without health checks already associated. The default health check configuration can be modified through the `health_checks_config_defaults` variable. - -If the `health_checks_config_defaults` variable is set to null, no default health checks will be automatically associted to backend services. - -Alternatively, one or more health checks can be either contextually created or attached, if existing. If the id of the health checks specified is equal to one of the keys of the `health_checks_config` variable, the health check is contextually created; otherwise, the health check id is used as is, assuming an health check with that id alredy exists. - -For example, to contextually create a health check and attach it to the backend service: +To leverage existing health checks without having the module create them, simply pass their self links to backend services and set the `health_check_configs` variable to an empty map: ```hcl module "ilb" { @@ -77,45 +79,55 @@ module "ilb" { name = "ilb-test" project_id = var.project_id region = "europe-west1" - network = var.vpc.self_link - subnetwork = var.subnet.self_link - - backend_services_config = { - my-backend-svc = { - backends = [ - { - group = "projects/my-project/zones/europe-west1-a/instanceGroups/my-ig" - options = null - } - ], - health_checks = ["hc-1"] - log_config = null - options = null + backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + health_checks = ["projects/myprj/global/healthChecks/custom"] } } - - health_checks_config = { - hc-1 = { - type = "http" - logging = true - options = { - timeout_sec = 5 - } - check = { - port_specification = "USE_SERVING_PORT" - } - } + health_check_configs = {} + urlmap_config = { + default_service = "default" + } + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link } } -# tftest modules=1 resources=5 +# tftest modules=1 resources=4 ``` +### Instance Group Management + ### Network Endpoint Groups (NEGs) -Zonal Network Endpoint Groups (NEGs) can also be used, as shown in the example below. +Zonal Network Endpoint Groups (NEGs) can also be used as backends: ```hcl module "ilb" { + source = "./fabric/modules/net-ilb-l7" + name = "ilb-test" + project_id = var.project_id + region = "europe-west1" + backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + health_checks = ["projects/myprj/global/healthChecks/custom"] + } + } + health_check_configs = {} + urlmap_config = { + default_service = "default" + } + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link + } + source = "./fabric/modules/net-ilb-l7" name = "ilb-test" project_id = var.project_id @@ -147,15 +159,6 @@ module "ilb" { } } } - -resource "google_compute_network_endpoint_group" "my-neg" { - name = "my-neg" - project = var.project_id - network = var.vpc.self_link - subnetwork = var.subnet.self_link - default_port = "90" - zone = "europe-west1-b" -} # tftest modules=1 resources=6 ``` --> diff --git a/modules/net-ilb-l7/variables-backend-service.tf b/modules/net-ilb-l7/variables-backend-service.tf new file mode 100644 index 0000000000..e84ebb1101 --- /dev/null +++ b/modules/net-ilb-l7/variables-backend-service.tf @@ -0,0 +1,129 @@ +/** + * 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 Backend services variables. + +variable "backend_service_configs" { + description = "Backend service level configuration." + type = map(object({ + affinity_cookie_ttl_sec = optional(number) + connection_draining_timeout_sec = optional(number) + health_checks = optional(list(string), ["default"]) + locality_lb_policy = optional(string) + log_sample_rate = optional(number) + port_name = optional(string) + session_affinity = optional(string) + timeout_sec = optional(number) + backends = list(object({ + group = string + balancing_mode = optional(string, "CONNECTION") + capacity_scaler = optional(number) + description = optional(string, "Terraform managed.") + failover = optional(bool, false) + max_connections = optional(object({ + per_endpoint = optional(number) + per_group = optional(number) + per_instance = optional(number) + })) + max_rate = optional(object({ + per_endpoint = optional(number) + per_group = optional(number) + per_instance = optional(number) + })) + max_utilization = optional(number) + })) + circuit_breakers = optional(object({ + max_connections = optional(number) + max_pending_requests = optional(number) + max_requests = optional(number) + max_requests_per_connection = optional(number) + max_retries = optional(number) + connect_timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + connection_tracking = optional(object({ + idle_timeout_sec = optional(number) + persist_conn_on_unhealthy = optional(string) + track_per_session = optional(bool) + })) + consistent_hash = optional(object({ + http_header_name = optional(string) + minimum_ring_size = optional(number) + http_cookie = optional(object({ + name = optional(string) + path = optional(string) + ttl = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + })) + enable_subsetting = optional(bool) + failover_config = optional(object({ + disable_conn_drain = optional(bool) + drop_traffic_if_unhealthy = optional(bool) + ratio = optional(number) + })) + iap_config = optional(object({ + oauth2_client_id = string + oauth2_client_secret = string + oauth2_client_secret_sha256 = optional(string) + })) + outlier_detection = optional(object({ + consecutive_errors = optional(number) + consecutive_gateway_failure = optional(number) + enforcing_consecutive_errors = optional(number) + enforcing_consecutive_gateway_failure = optional(number) + enforcing_success_rate = optional(number) + max_ejection_percent = optional(number) + success_rate_minimum_hosts = optional(number) + success_rate_request_volume = optional(number) + success_rate_stdev_factor = optional(number) + base_ejection_time = optional(object({ + seconds = number + nanos = optional(number) + })) + interval = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + })) + default = {} + nullable = false + validation { + condition = contains( + [ + "-", "ROUND_ROBIN", "LEAST_REQUEST", "RING_HASH", + "RANDOM", "ORIGINAL_DESTINATION", "MAGLEV" + ], + try(var.backend_service_configs.locality_lb_policy, "-") + ) + error_message = "Invalid locality lb policy value." + } + validation { + condition = contains( + [ + "NONE", "CLIENT_IP", "CLIENT_IP_NO_DESTINATION", + "CLIENT_IP_PORT_PROTO", "CLIENT_IP_PROTO" + ], + try(var.backend_service_configs.session_affinity, "NONE") + ) + error_message = "Invalid session affinity value." + } +} diff --git a/modules/net-ilb-l7/variables.tf b/modules/net-ilb-l7/variables.tf index bf2eb90d7e..6078222e76 100644 --- a/modules/net-ilb-l7/variables.tf +++ b/modules/net-ilb-l7/variables.tf @@ -26,118 +26,6 @@ variable "description" { default = "Terraform managed." } -variable "backend_service_configs" { - description = "Backend service level configuration." - type = map(object({ - affinity_cookie_ttl_sec = optional(number) - connection_draining_timeout_sec = optional(number) - health_checks = optional(list(string), ["default"]) - locality_lb_policy = optional(string) - log_sample_rate = optional(number) - port_name = optional(string) - session_affinity = optional(string) - timeout_sec = optional(number) - backends = list(object({ - group = string - balancing_mode = optional(string, "CONNECTION") - capacity_scaler = optional(number) - description = optional(string, "Terraform managed.") - failover = optional(bool, false) - max_connections = optional(object({ - per_endpoint = optional(number) - per_group = optional(number) - per_instance = optional(number) - })) - max_rate = optional(object({ - per_endpoint = optional(number) - per_group = optional(number) - per_instance = optional(number) - })) - max_utilization = optional(number) - })) - circuit_breakers = optional(object({ - max_connections = optional(number) - max_pending_requests = optional(number) - max_requests = optional(number) - max_requests_per_connection = optional(number) - max_retries = optional(number) - connect_timeout = optional(object({ - seconds = number - nanos = optional(number) - })) - })) - connection_tracking = optional(object({ - idle_timeout_sec = optional(number) - persist_conn_on_unhealthy = optional(string) - track_per_session = optional(bool) - })) - consistent_hash = optional(object({ - http_header_name = optional(string) - minimum_ring_size = optional(number) - http_cookie = optional(object({ - name = optional(string) - path = optional(string) - ttl = optional(object({ - seconds = number - nanos = optional(number) - })) - })) - })) - enable_subsetting = optional(bool) - failover_config = optional(object({ - disable_conn_drain = optional(bool) - drop_traffic_if_unhealthy = optional(bool) - ratio = optional(number) - })) - iap_config = optional(object({ - oauth2_client_id = string - oauth2_client_secret = string - oauth2_client_secret_sha256 = optional(string) - })) - outlier_detection = optional(object({ - consecutive_errors = optional(number) - consecutive_gateway_failure = optional(number) - enforcing_consecutive_errors = optional(number) - enforcing_consecutive_gateway_failure = optional(number) - enforcing_success_rate = optional(number) - max_ejection_percent = optional(number) - success_rate_minimum_hosts = optional(number) - success_rate_request_volume = optional(number) - success_rate_stdev_factor = optional(number) - base_ejection_time = optional(object({ - seconds = number - nanos = optional(number) - })) - interval = optional(object({ - seconds = number - nanos = optional(number) - })) - })) - })) - default = {} - nullable = false - validation { - condition = contains( - [ - "-", "ROUND_ROBIN", "LEAST_REQUEST", "RING_HASH", - "RANDOM", "ORIGINAL_DESTINATION", "MAGLEV" - ], - try(var.backend_service_configs.locality_lb_policy, "-") - ) - error_message = "Invalid locality lb policy value." - } - validation { - condition = contains( - [ - "NONE", "CLIENT_IP", "CLIENT_IP_NO_DESTINATION", - "CLIENT_IP_PORT_PROTO", "CLIENT_IP_PROTO" - ], - try(var.backend_service_configs.session_affinity, "NONE") - ) - error_message = "Invalid session affinity value." - } -} - variable "group_configs" { description = "Optional unmanaged groups to create. Can be referenced in backends via outputs." type = map(object({ From bea5a1b81327758cf91ac752e36d19d1356b8c82 Mon Sep 17 00:00:00 2001 From: Ludo Date: Sun, 13 Nov 2022 19:06:57 +0100 Subject: [PATCH 05/13] instance group example --- modules/net-ilb-l7/README.md | 80 ++++++++++++++++++--------- modules/net-ilb-l7/backend-service.tf | 9 ++- modules/net-ilb-l7/main.tf | 16 ++++++ 3 files changed, 77 insertions(+), 28 deletions(-) diff --git a/modules/net-ilb-l7/README.md b/modules/net-ilb-l7/README.md index 336eb8553e..896adcd5ab 100644 --- a/modules/net-ilb-l7/README.md +++ b/modules/net-ilb-l7/README.md @@ -10,7 +10,7 @@ It's designed to be a simple match for the [`vpc`](../net-vpc) and the [`compute An HTTP ILB with a backend service pointing to a GCE instance group: ```hcl -module "ilb" { +module "ilb-l7" { source = "./fabric/modules/net-ilb-l7" name = "ilb-test" project_id = var.project_id @@ -42,7 +42,7 @@ Health check configuration is controlled via the `health_check_configs` variable Defining different health checks fromt he default is very easy. You can for example replace the default HTTP health check with a TCP one and reference it in you backend service: ```hcl -module "ilb" { +module "ilb-l7" { source = "./fabric/modules/net-ilb-l7" name = "ilb-test" project_id = var.project_id @@ -74,7 +74,7 @@ module "ilb" { To leverage existing health checks without having the module create them, simply pass their self links to backend services and set the `health_check_configs` variable to an empty map: ```hcl -module "ilb" { +module "ilb-l7" { source = "./fabric/modules/net-ilb-l7" name = "ilb-test" project_id = var.project_id @@ -99,14 +99,54 @@ module "ilb" { # tftest modules=1 resources=4 ``` -### Instance Group Management +### Backends + +#### Instance Group Management + +The module can optionally create unmanaged instance groups, which can then be referred to in backends via their key: + +```hcl +module "ilb-l7" { + source = "./fabric/modules/net-ilb-l7" + name = "ilb-test" + project_id = var.project_id + region = "europe-west1" + backend_service_configs = { + default = { + port_name = "http" + backends = [ + { group = "default" } + ] + } + } + group_configs = { + default = { + zone = "europe-west1-b" + instances = [ + "projects/myprj/zones/europe-west1-b/instances/vm-a" + ] + named_ports = { + http = 80 + } + } + } + urlmap_config = { + default_service = "default" + } + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link + } +} +# tftest modules=1 resources=6 +``` ### Network Endpoint Groups (NEGs) Zonal Network Endpoint Groups (NEGs) can also be used as backends: ```hcl -module "ilb" { +module "ilb-l7" { source = "./fabric/modules/net-ilb-l7" name = "ilb-test" project_id = var.project_id @@ -159,9 +199,9 @@ module "ilb" { } } } -# tftest modules=1 resources=6 ``` ---> + + ### Url-map @@ -175,7 +215,7 @@ Backend services can be specified as needed in the url-map configuration, refere In this example, we're using a backend service as the default backend ```hcl -module "ilb" { +module "ilb-l7" { source = "./fabric/modules/net-ilb-l7" name = "ilb-test" project_id = var.project_id @@ -226,15 +266,15 @@ module "ilb" { } } } -# tftest modules=1 resources=6 ``` + ### Reserve a static IP address Optionally, a static IP address can be reserved: ```hcl -module "ilb" { +module "ilb-l7" { source = "./fabric/modules/net-ilb-l7" name = "ilb-test" project_id = var.project_id @@ -261,8 +301,8 @@ module "ilb" { } } } -# tftest modules=1 resources=6 ``` + ### HTTPS And SSL Certificates @@ -271,7 +311,7 @@ HTTPS is disabled by default but it can be optionally enabled. When HTTPS is enabled, if the ids specified in the `target_proxy_https_config` variable are not found in the `ssl_certificates_config` map, they are used as is, assuming the ssl certificates already exist: ```hcl -module "ilb" { +module "ilb-l7" { source = "./fabric/modules/net-ilb-l7" name = "ilb-test" project_id = var.project_id @@ -301,13 +341,13 @@ module "ilb" { } } } -# tftest modules=1 resources=5 ``` + Otherwise, unmanaged certificates can also be contextually created: ```hcl -module "ilb" { +module "ilb-l7" { source = "./fabric/modules/net-ilb-l7" name = "ilb-test" project_id = var.project_id @@ -368,18 +408,8 @@ resource "tls_self_signed_cert" "self_signed_cert" { organization = "My Test Org" } } -# tftest modules=1 resources=8 ``` - -## Components And Files Mapping - -An Internal HTTP 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. - -- The global load balancer [forwarding rule](forwarding-rule.tf) binds a frontend public Virtual IP (VIP) to an HTTP(S) [target proxy](target-proxy.tf). -- If the target proxy is HTTPS, it requires one or more unmanaged [SSL certificates](ssl-certificates.tf). -- Target proxies leverage [url-maps](url-map.tf): a set of L7 rules that create a mapping between specific hostnames, URIs (and more) to one or more [backends services](backend-services.tf). -- [Backend services](backend-services.tf) link to one or multiple infrastructure groups (GCE instance groups or NEGs). It is assumed in this module that groups have been previously created through other modules, and referenced in the input variables. -- Backend services support one or more [health checks](health-checks.tf), used to verify that the backend is indeed healthy, so that traffic can be forwarded to it. Health checks currently supported in this module are HTTP, HTTPS, HTTP2, SSL, TCP. + diff --git a/modules/net-ilb-l7/backend-service.tf b/modules/net-ilb-l7/backend-service.tf index bf80b539f9..58a5c7a8eb 100644 --- a/modules/net-ilb-l7/backend-service.tf +++ b/modules/net-ilb-l7/backend-service.tf @@ -17,7 +17,10 @@ # tfdoc:file:description Backend service resources. locals { - hc = { + group_ids = { + for k, v in google_compute_instance_group.default : k => v.id + } + hc_ids = { for k, v in google_compute_health_check.default : k => v.id } } @@ -32,7 +35,7 @@ resource "google_compute_region_backend_service" "default" { affinity_cookie_ttl_sec = each.value.affinity_cookie_ttl_sec connection_draining_timeout_sec = each.value.connection_draining_timeout_sec health_checks = [ - for k in each.value.health_checks : lookup(local.hc, k, k) + for k in each.value.health_checks : lookup(local.hc_ids, k, k) ] # not for internet / serverless NEGs locality_lb_policy = each.value.locality_lb_policy load_balancing_scheme = "INTERNAL_MANAGED" @@ -45,7 +48,7 @@ resource "google_compute_region_backend_service" "default" { dynamic "backend" { for_each = { for b in coalesce(each.value.backends, []) : b.group => b } content { - group = backend.key + group = lookup(local.group_ids, backend.key, backend.key) balancing_mode = backend.value.balancing_mode capacity_scaler = backend.value.capacity_scaler description = backend.value.description diff --git a/modules/net-ilb-l7/main.tf b/modules/net-ilb-l7/main.tf index 236703da01..265e6cdbc4 100644 --- a/modules/net-ilb-l7/main.tf +++ b/modules/net-ilb-l7/main.tf @@ -74,3 +74,19 @@ resource "google_compute_region_target_https_proxy" "default" { ssl_certificates = local.proxy_ssl_certificates url_map = google_compute_region_url_map.default.id } + +resource "google_compute_instance_group" "default" { + for_each = var.group_configs + project = var.project_id + zone = each.value.zone + name = "${var.name}-${each.key}" + description = var.description + instances = each.value.instances + dynamic "named_port" { + for_each = each.value.named_ports + content { + name = named_port.key + port = named_port.value + } + } +} From 7a1530646f075378121638fa4010bef869bc3e9f Mon Sep 17 00:00:00 2001 From: Ludo Date: Sun, 13 Nov 2022 20:01:28 +0100 Subject: [PATCH 06/13] neg examples --- modules/net-ilb-l7/README.md | 61 +++++++++++++++++---------------- modules/net-ilb-l7/main.tf | 32 +++++++++++++++++ modules/net-ilb-l7/variables.tf | 22 +++++++++++- 3 files changed, 84 insertions(+), 31 deletions(-) diff --git a/modules/net-ilb-l7/README.md b/modules/net-ilb-l7/README.md index 896adcd5ab..f15a6a715d 100644 --- a/modules/net-ilb-l7/README.md +++ b/modules/net-ilb-l7/README.md @@ -99,8 +99,6 @@ module "ilb-l7" { # tftest modules=1 resources=4 ``` -### Backends - #### Instance Group Management The module can optionally create unmanaged instance groups, which can then be referred to in backends via their key: @@ -143,7 +141,7 @@ module "ilb-l7" { ### Network Endpoint Groups (NEGs) -Zonal Network Endpoint Groups (NEGs) can also be used as backends: +Zonal Network Endpoint Groups (NEGs) can be used as backends, by passing their id as the backend group in a backends service configuration: ```hcl module "ilb-l7" { @@ -154,12 +152,10 @@ module "ilb-l7" { backend_service_configs = { default = { backends = [{ - group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + group = "projects/myprj/zones/europe-west1-a/networkEndpointGroups/my-neg" }] - health_checks = ["projects/myprj/global/healthChecks/custom"] } } - health_check_configs = {} urlmap_config = { default_service = "default" } @@ -167,42 +163,47 @@ module "ilb-l7" { network = var.vpc.self_link subnetwork = var.subnet.self_link } - +} +# tftest modules=1 resources=5 +``` + +Similarly to instance groups, NEGs can also be managed by this module: + +```hcl +module "ilb-l7" { source = "./fabric/modules/net-ilb-l7" name = "ilb-test" project_id = var.project_id region = "europe-west1" - network = var.vpc.self_link - subnetwork = var.subnet.self_link - - backend_services_config = { - my-backend-svc = { + backend_service_configs = { + default = { backends = [ + { group = "my-neg" } + ] + } + } + neg_configs = { + my-neg = { + zone = "europe-west1-b" + endpoints = [ { - group = google_compute_network_endpoint_group.my-neg.id - options = { - balancing_mode = "RATE" - capacity_scaler = 1.0 - max_connections = null - max_connections_per_instance = null - max_connections_per_endpoint = null - max_rate = 100 - max_rate_per_endpoint = null - max_rate_per_instance = null - max_utilization = null - } + ip_address = "10.0.0.10" + instance = "test-1" } - ], - health_checks = [] - log_config = null - options = null + ] } } + urlmap_config = { + default_service = "default" + } + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link + } } +# tftest modules=1 resources=7 ``` - - ### Url-map The url-map can be customized with lots of different configurations. This includes leveraging multiple backends in different parts of the configuration. diff --git a/modules/net-ilb-l7/main.tf b/modules/net-ilb-l7/main.tf index 265e6cdbc4..ddb6507161 100644 --- a/modules/net-ilb-l7/main.tf +++ b/modules/net-ilb-l7/main.tf @@ -15,6 +15,11 @@ */ locals { + _neg_endpoints = flatten([ + for k, v in var.neg_configs : [ + for vv in v.endpoints : merge(vv, { neg = k }) + ] + ]) fwd_rule_ports = ( var.protocol == "HTTPS" ? [443] : coalesce(var.ports, [80]) ) @@ -23,6 +28,10 @@ locals { ? google_compute_region_target_https_proxy.default.0.id : google_compute_region_target_http_proxy.default.0.id ) + neg_endpoints = { + for v in local._neg_endpoints : + "${v.neg}-${v.ip_address}-${coalesce(v.port, "none")}" => v + } proxy_ssl_certificates = concat( [for k, v in google_compute_region_ssl_certificate.default : v.id], [for v in var.ssl_certificates : v.self_link if v.id != null] @@ -90,3 +99,26 @@ resource "google_compute_instance_group" "default" { } } } + +resource "google_compute_network_endpoint_group" "default" { + for_each = var.neg_configs + project = var.project_id + zone = each.value.zone + name = "${var.name}-${each.key}" + description = var.description + network_endpoint_type = each.value.type + network = try(each.value.vpc_config.network, var.vpc_config.network) + subnetwork = try(each.value.vpc_config.subnetwork, var.vpc_config.subnetwork) +} + +resource "google_compute_network_endpoint" "default" { + for_each = local.neg_endpoints + project = var.project_id + network_endpoint_group = ( + google_compute_network_endpoint_group.default[each.value.neg].name + ) + instance = each.value.instance + ip_address = each.value.ip_address + port = each.value.port +} + diff --git a/modules/net-ilb-l7/variables.tf b/modules/net-ilb-l7/variables.tf index 6078222e76..b47f3223c0 100644 --- a/modules/net-ilb-l7/variables.tf +++ b/modules/net-ilb-l7/variables.tf @@ -27,7 +27,7 @@ variable "description" { } variable "group_configs" { - description = "Optional unmanaged groups to create. Can be referenced in backends via outputs." + description = "Optional unmanaged groups to create. Can be referenced in backends via key or outputs." type = map(object({ zone = string instances = optional(list(string), []) @@ -48,6 +48,26 @@ variable "name" { type = string } +variable "neg_configs" { + description = "Optional network endpoint groups to create. Can be referenced in backends via key or outputs." + type = map(object({ + zone = string + type = optional(string, "GCE_VM_IP") # GCE_VM_IP, GCE_VM_IP_PORT, and NON_GCP_PRIVATE_IP_PORT + default_port = optional(number) + endpoints = optional(list(object({ + ip_address = string + instance = optional(string) + port = optional(number) + }))) + vpc_config = optional(object({ + network = optional(string) + subnetwork = optional(string) + })) + })) + default = {} + nullable = false +} + variable "network_tier_premium" { description = "Use premium network tier. Defaults to true." type = bool From f9327379cd6e2af8b0441e7f002b983e330e8cd6 Mon Sep 17 00:00:00 2001 From: Ludo Date: Mon, 14 Nov 2022 00:42:33 +0100 Subject: [PATCH 07/13] example tests --- modules/net-ilb-l7/README.md | 318 ++++++++++--------------- modules/net-ilb-l7/backend-service.tf | 11 +- modules/net-ilb-l7/main.tf | 10 +- modules/net-ilb-l7/outputs.tf | 63 ++--- modules/net-ilb-l7/urlmap.tf | 40 ++-- modules/net-ilb-l7/variables-urlmap.tf | 4 +- modules/net-ilb-l7/variables.tf | 29 ++- 7 files changed, 203 insertions(+), 272 deletions(-) diff --git a/modules/net-ilb-l7/README.md b/modules/net-ilb-l7/README.md index f15a6a715d..49e43287b6 100644 --- a/modules/net-ilb-l7/README.md +++ b/modules/net-ilb-l7/README.md @@ -1,6 +1,7 @@ # Internal (HTTP/S) Load Balancer Module The module allows managing Internal HTTP/HTTPS Load Balancers (HTTP(S) ILBs), integrating the forwarding rule, the url-map, the backends, optional health checks and SSL certificates. + It's designed to be a simple match for the [`vpc`](../net-vpc) and the [`compute-mig`](../compute-mig) modules, which can be used to manage VPCs and instance groups. ## Examples @@ -33,7 +34,39 @@ module "ilb-l7" { # tftest modules=1 resources=5 ``` -### Defining Health Checks +An HTTPS ILB needs a few additional fields: + +```hcl +module "ilb-l7" { + source = "./fabric/modules/net-ilb-l7" + name = "ilb-test" + project_id = var.project_id + region = "europe-west1" + backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + } + } + protocol = "HTTPS" + ssl_certificates = { + certificate_ids = [ + "projects/myprj/regions/europe-west1/sslCertificates/my-cert" + ] + } + urlmap_config = { + default_service = "default" + } + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link + } +} +# tftest modules=1 resources=5 +``` + +### Health Checks You can leverage externally defined health checks for backend services, or have the module create them for you. By default a simple HTTP health check is created, and used in backend services. @@ -99,7 +132,70 @@ module "ilb-l7" { # tftest modules=1 resources=4 ``` -#### Instance Group Management +### SSL Certificates + +Similarly to health checks, SSL certificates can also be created by the module. In this example we are using private key and certificate resources so that the example test only depends on Terraform providers, but in real use those can be replaced by external files. + +```hcl + +resource "tls_private_key" "default" { + algorithm = "RSA" + rsa_bits = 4096 +} + +resource "tls_self_signed_cert" "default" { + private_key_pem = tls_private_key.default.private_key_pem + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + validity_period_hours = 720 + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +module "ilb-l7" { + source = "./fabric/modules/net-ilb-l7" + name = "ilb-test" + project_id = var.project_id + region = "europe-west1" + backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + } + } + health_check_configs = { + default = { + https = { port = 443 } + } + } + protocol = "HTTPS" + ssl_certificates = { + create_configs = { + default = { + # certificate and key could also be read via file() from external files + certificate = tls_self_signed_cert.default.cert_pem + private_key = tls_private_key.default.private_key_pem + } + } + } + urlmap_config = { + default_service = "default" + } + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link + } +} +# tftest modules=1 resources=8 +``` + +### Instance Groups The module can optionally create unmanaged instance groups, which can then be referred to in backends via their key: @@ -204,75 +300,9 @@ module "ilb-l7" { # tftest modules=1 resources=7 ``` -### Url-map - -The url-map can be customized with lots of different configurations. This includes leveraging multiple backends in different parts of the configuration. -Given its complexity, it's left to the user passing the right data structure. - -For simplicity, *if no configurations are given* the first backend service defined (in alphabetical order, with priority to bucket backend services, if any) is used as the *default_service*, thus answering to the root (*/*) path. - -Backend services can be specified as needed in the url-map configuration, referencing the id used to declare them in the backend services map. If a corresponding backend service is found, their object id is automatically used; otherwise, it is assumed that the string passed is the id of an already existing backend and it is given to the provider as it was passed. - -In this example, we're using a backend service as the default backend - -```hcl -module "ilb-l7" { - source = "./fabric/modules/net-ilb-l7" - name = "ilb-test" - project_id = var.project_id - region = "europe-west1" - network = var.vpc.self_link - subnetwork = var.subnet.self_link - - url_map_config = { - default_service = "my-backend-svc" - default_url_redirect = null - tests = null - host_rules = [] - path_matchers = [ - { - name = "my-example-page" - path_rules = [ - { - paths = ["/my-example-page"] - service = "another-group-backend" - } - ] - } - ] - } - - backend_services_config = { - my-backend-svc = { - backends = [ - { - group = "projects/my-project/zones/europe-west1-a/instanceGroups/my-ig" - options = null - } - ], - health_checks = [] - log_config = null - options = null - }, - my-example-page = { - backends = [ - { - group = "projects/my-project/zones/europe-west1-a/instanceGroups/another-ig" - options = null - } - ], - health_checks = [] - log_config = null - options = null - } - } -} -``` - - -### Reserve a static IP address +### URL Map -Optionally, a static IP address can be reserved: +The module exposes the full URL map resource configuration, with some minor changes to the interface to decrease verbosity, and support for aliasing backend services via keys: ```hcl module "ilb-l7" { @@ -280,137 +310,41 @@ module "ilb-l7" { name = "ilb-test" project_id = var.project_id region = "europe-west1" - network = var.vpc.self_link - subnetwork = var.subnet.self_link - - static_ip_config = { - reserve = true - options = null - } - - backend_services_config = { - my-backend-svc = { - backends = [ - { - group = "projects/my-project/zones/europe-west1-a/instanceGroups/my-ig" - options = null - } - ], - health_checks = [] - log_config = null - options = null + backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] } - } -} -``` - - -### HTTPS And SSL Certificates - -HTTPS is disabled by default but it can be optionally enabled. - -When HTTPS is enabled, if the ids specified in the `target_proxy_https_config` variable are not found in the `ssl_certificates_config` map, they are used as is, assuming the ssl certificates already exist: - -```hcl -module "ilb-l7" { - source = "./fabric/modules/net-ilb-l7" - name = "ilb-test" - project_id = var.project_id - region = "europe-west1" - network = var.vpc.self_link - subnetwork = var.subnet.self_link - - https = true - - target_proxy_https_config = { - ssl_certificates = [ - "an-existing-cert" - ] - } - - backend_services_config = { - my-backend-svc = { - backends = [ - { - group = "projects/my-project/zones/europe-west1-a/instanceGroups/my-ig" - options = null - } - ] - health_checks = [] - log_config = null - options = null + video = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig-2" + }] } } -} -``` - - -Otherwise, unmanaged certificates can also be contextually created: - -```hcl -module "ilb-l7" { - source = "./fabric/modules/net-ilb-l7" - name = "ilb-test" - project_id = var.project_id - region = "europe-west1" - network = var.vpc.self_link - subnetwork = var.subnet.self_link - - https = true - - ssl_certificates_config = { - my-domain = { - domains = [ - "my-domain.com" - ], - tls_private_key = tls_private_key.self_signed_key.private_key_pem - tls_self_signed_cert = tls_self_signed_cert.self_signed_cert.cert_pem + urlmap_config = { + default_service = "default" + host_rules = [{ + hosts = ["*"] + path_matcher = "pathmap" + }] + path_matchers = { + pathmap = { + path_rules = [{ + paths = ["/video", "/video/*"] + service = "video" + }] + } } } - - target_proxy_https_config = { - ssl_certificates = [ - "my-domain" - ] - } - - backend_services_config = { - my-backend-svc = { - backends = [ - { - group = "projects/my-project/zones/europe-west1-a/instanceGroups/my-ig" - options = null - } - ], - health_checks = [] - log_config = null - options = null - } + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link } } -resource "tls_private_key" "self_signed_key" { - algorithm = "RSA" - rsa_bits = 2048 -} - -resource "tls_self_signed_cert" "self_signed_cert" { - private_key_pem = tls_private_key.self_signed_key.private_key_pem - validity_period_hours = 12 - early_renewal_hours = 3 - dns_names = ["example.com"] - allowed_uses = [ - "key_encipherment", - "digital_signature", - "server_auth" - ] - subject { - common_name = "example.com" - organization = "My Test Org" - } -} +# tftest modules=1 resources=6 ``` - diff --git a/modules/net-ilb-l7/backend-service.tf b/modules/net-ilb-l7/backend-service.tf index 58a5c7a8eb..d89787bfef 100644 --- a/modules/net-ilb-l7/backend-service.tf +++ b/modules/net-ilb-l7/backend-service.tf @@ -17,9 +17,14 @@ # tfdoc:file:description Backend service resources. locals { - group_ids = { - for k, v in google_compute_instance_group.default : k => v.id - } + group_ids = merge( + { + for k, v in google_compute_instance_group.default : k => v.id + }, + { + for k, v in google_compute_network_endpoint_group.default : k => v.id + } + ) hc_ids = { for k, v in google_compute_health_check.default : k => v.id } diff --git a/modules/net-ilb-l7/main.tf b/modules/net-ilb-l7/main.tf index ddb6507161..ce540e7ad4 100644 --- a/modules/net-ilb-l7/main.tf +++ b/modules/net-ilb-l7/main.tf @@ -33,8 +33,8 @@ locals { "${v.neg}-${v.ip_address}-${coalesce(v.port, "none")}" => v } proxy_ssl_certificates = concat( - [for k, v in google_compute_region_ssl_certificate.default : v.id], - [for v in var.ssl_certificates : v.self_link if v.id != null] + coalesce(var.ssl_certificates.certificate_ids, []), + [for k, v in google_compute_region_ssl_certificate.default : v.id] ) } @@ -57,12 +57,12 @@ resource "google_compute_forwarding_rule" "default" { } resource "google_compute_region_ssl_certificate" "default" { - for_each = { for v in var.ssl_certificates : v.name => v if v.id == null } + for_each = var.ssl_certificates.create_configs project = var.project_id region = var.region name = "${var.name}-${each.key}" - certificate = try(each.value.tls_self_signed_cert, null) - private_key = try(each.value.tls_private_key, null) + certificate = each.value.certificate + private_key = each.value.private_key } resource "google_compute_region_target_http_proxy" "default" { diff --git a/modules/net-ilb-l7/outputs.tf b/modules/net-ilb-l7/outputs.tf index 500e90260b..01ff7dab87 100644 --- a/modules/net-ilb-l7/outputs.tf +++ b/modules/net-ilb-l7/outputs.tf @@ -14,45 +14,30 @@ * limitations under the License. */ -# output "health_checks" { -# description = "Health-check resources." -# value = try(google_compute_region_health_check.health_check, []) -# } +output "backend_service_ids" { + description = "Backend service resources." + value = { + for k, v in google_compute_region_backend_service.default : k => v.id + } +} -# output "backend_services" { -# description = "Backend service resources." -# value = { -# group = try(google_compute_region_backend_service.backend_service, []) -# } -# } +output "group_ids" { + description = "Autogenerated instance group ids." + value = { + for k, v in google_compute_instance_group.default : k => v.id + } +} -# output "url_map" { -# description = "The url-map." -# value = try(google_compute_region_url_map.url_map, null) -# } +output "health_check_ids" { + description = "Autogenerated health check ids." + value = { + for k, v in google_compute_health_check.default : k => v.id + } +} -# output "ssl_certificate_link_ids" { -# description = "The SSL certificate." -# value = { -# for k, v in google_compute_region_ssl_certificate.certificates : k => v.self_link -# } -# } - -# output "ip_address" { -# description = "The reserved IP address." -# value = try(google_compute_forwarding_rule.forwarding_rule.ip_address, null) -# } - -# output "target_proxy" { -# description = "The target proxy." -# value = try( -# google_compute_region_target_https_proxy.https.0, -# google_compute_region_target_http_proxy.http.0, -# [] -# ) -# } - -# output "forwarding_rule" { -# description = "The forwarding rule." -# value = try(google_compute_forwarding_rule.forwarding_rule, null) -# } +output "neg_ids" { + description = "Autogenerated network endpoint group ids." + value = { + for k, v in google_compute_network_endpoint_group.default : k => v.id + } +} diff --git a/modules/net-ilb-l7/urlmap.tf b/modules/net-ilb-l7/urlmap.tf index 7a29a2f71b..c58d64c075 100644 --- a/modules/net-ilb-l7/urlmap.tf +++ b/modules/net-ilb-l7/urlmap.tf @@ -23,12 +23,18 @@ locals { } resource "google_compute_region_url_map" "default" { - provider = google-beta - project = var.project_id - region = var.region - name = var.name - description = var.description - default_service = var.urlmap_config.default_service + provider = google-beta + project = var.project_id + region = var.region + name = var.name + description = var.description + default_service = ( + var.urlmap_config.default_service == null ? null : lookup( + local.backend_ids, + var.urlmap_config.default_service, + var.urlmap_config.default_service + ) + ) dynamic "default_url_redirect" { for_each = ( @@ -48,11 +54,7 @@ resource "google_compute_region_url_map" "default" { } dynamic "host_rule" { - for_each = ( - var.urlmap_config.host_rule == null - ? [] - : [var.urlmap_config.host_rule] - ) + for_each = coalesce(var.urlmap_config.host_rules, []) iterator = r content { hosts = r.value.hosts @@ -68,8 +70,8 @@ resource "google_compute_region_url_map" "default" { default_service = m.value.default_service == null ? null : lookup( local.backend_ids, m.value.default_service, m.value.default_service ) - description = pm.value.description - name = pm.key + description = m.value.description + name = m.key dynamic "default_url_redirect" { for_each = ( m.value.default_url_redirect == null @@ -89,10 +91,10 @@ resource "google_compute_region_url_map" "default" { for_each = toset(coalesce(m.value.path_rules)) content { paths = path_rule.value.paths - service = path_rule.value.default_service == null ? null : lookup( + service = path_rule.value.service == null ? null : lookup( local.backend_ids, - path_rule.value.default_service, - path_rule.value.default_service + path_rule.value.service, + path_rule.value.service ) dynamic "route_action" { for_each = ( @@ -254,9 +256,9 @@ resource "google_compute_region_url_map" "default" { } dynamic "url_redirect" { for_each = ( - path_rule.value.default_url_redirect == null + path_rule.value.url_redirect == null ? [] - : [path_rule.value.default_url_redirect] + : [path_rule.value.url_redirect] ) content { host_redirect = url_redirect.value.host @@ -270,7 +272,7 @@ resource "google_compute_region_url_map" "default" { } } dynamic "route_rules" { - for_each = toset(coalesce(m.value.route_rules)) + for_each = toset(coalesce(m.value.route_rules, [])) content { priority = route_rules.value.priority service = route_rules.value.service == null ? null : lookup( diff --git a/modules/net-ilb-l7/variables-urlmap.tf b/modules/net-ilb-l7/variables-urlmap.tf index 8ed9610aa2..5e9dffeb68 100644 --- a/modules/net-ilb-l7/variables-urlmap.tf +++ b/modules/net-ilb-l7/variables-urlmap.tf @@ -28,11 +28,11 @@ variable "urlmap_config" { response_code = optional(string) strip_query = optional(bool) })) - host_rule = optional(object({ + host_rules = optional(list(object({ hosts = list(string) path_matcher = string description = optional(string) - })) + }))) path_matchers = optional(map(object({ description = optional(string) default_service = optional(string) diff --git a/modules/net-ilb-l7/variables.tf b/modules/net-ilb-l7/variables.tf index b47f3223c0..10d8dde128 100644 --- a/modules/net-ilb-l7/variables.tf +++ b/modules/net-ilb-l7/variables.tf @@ -87,10 +87,17 @@ variable "ports" { } variable "protocol" { - description = "IP protocol used, defaults to TCP." + description = "Protocol supported by this load balancer." type = string default = "HTTP" nullable = false + validation { + condition = ( + var.protocol == null || var.protocol == "HTTP" || var.protocol == "HTTPS" + ) + error_message = "Protocol must be HTTP or HTTPS" + } + } variable "region" { @@ -99,17 +106,15 @@ variable "region" { } variable "ssl_certificates" { - description = "SSL target proxy certificates (only if protocol is HTTPS). Specify id for existing certificates, create config attributes to create." - type = list(object({ - create_config = optional(object({ - domains = list(string) - name = list(string) - tls_private_key = string - tls_self_signed_cert = string - })) - id = optional(string) - })) - default = [] + description = "SSL target proxy certificates (only if protocol is HTTPS)." + type = object({ + certificate_ids = optional(list(string), []) + create_configs = optional(map(object({ + certificate = string + private_key = string + })), {}) + }) + default = {} nullable = false } From 25c720164c13ab19e9c1b796b2cb9cc545ad0726 Mon Sep 17 00:00:00 2001 From: Ludo Date: Mon, 14 Nov 2022 00:45:04 +0100 Subject: [PATCH 08/13] tfdoc --- modules/net-ilb-l7/README.md | 55 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/modules/net-ilb-l7/README.md b/modules/net-ilb-l7/README.md index 49e43287b6..f9b7697ab5 100644 --- a/modules/net-ilb-l7/README.md +++ b/modules/net-ilb-l7/README.md @@ -353,13 +353,14 @@ module "ilb-l7" { | name | description | resources | |---|---|---| -| [backend-services.tf](./backend-services.tf) | Bucket and group backend services. | google_compute_region_backend_service | -| [forwarding-rule.tf](./forwarding-rule.tf) | IP Address and forwarding rule. | google_compute_address · google_compute_forwarding_rule | -| [health-checks.tf](./health-checks.tf) | Health checks. | google_compute_region_health_check | +| [backend-service.tf](./backend-service.tf) | Backend service resources. | google_compute_region_backend_service | +| [health-check.tf](./health-check.tf) | Health check resource. | google_compute_health_check | +| [main.tf](./main.tf) | Module-level locals and resources. | google_compute_forwarding_rule · google_compute_instance_group · google_compute_network_endpoint · google_compute_network_endpoint_group · google_compute_region_ssl_certificate · google_compute_region_target_http_proxy · google_compute_region_target_https_proxy | | [outputs.tf](./outputs.tf) | Module outputs. | | -| [ssl-certificates.tf](./ssl-certificates.tf) | SSL certificates. | google_compute_region_ssl_certificate | -| [target-proxy.tf](./target-proxy.tf) | HTTP and HTTPS target proxies. | google_compute_region_target_http_proxy · google_compute_region_target_https_proxy | -| [url-map.tf](./url-map.tf) | URL maps. | google_compute_region_url_map | +| [urlmap.tf](./urlmap.tf) | URL map resources. | google_compute_region_url_map | +| [variables-backend-service.tf](./variables-backend-service.tf) | Backend services variables. | | +| [variables-health-check.tf](./variables-health-check.tf) | Health check variable. | | +| [variables-urlmap.tf](./variables-urlmap.tf) | URLmap variable. | | | [variables.tf](./variables.tf) | Module variables. | | | [versions.tf](./versions.tf) | Version pins. | | @@ -367,31 +368,31 @@ module "ilb-l7" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L17) | Load balancer name. | string | ✓ | | -| [project_id](variables.tf#L22) | Project id. | string | ✓ | | -| [region](variables.tf#L159) | The region where to allocate the ILB resources. | string | ✓ | | -| [subnetwork](variables.tf#L189) | The subnetwork where the ILB VIP is allocated. | string | ✓ | | -| [backend_services_config](variables.tf#L27) | The backends services configuration. | map(object({…})) | | {} | -| [forwarding_rule_config](variables.tf#L98) | Forwarding rule configurations. | object({…}) | | {…} | -| [health_checks_config](variables.tf#L118) | Custom health checks configuration. | map(object({…})) | | {} | -| [health_checks_config_defaults](variables.tf#L129) | Auto-created health check default configuration. | object({…}) | | {…} | -| [https](variables.tf#L147) | Whether to enable HTTPS. | bool | | false | -| [network](variables.tf#L153) | The network where the ILB is created. | string | | "default" | -| [ssl_certificates_config](variables.tf#L164) | The SSL certificates configuration. | map(object({…})) | | {} | -| [static_ip_config](variables.tf#L174) | Static IP address configuration. | object({…}) | | {…} | -| [target_proxy_https_config](variables.tf#L194) | The HTTPS target proxy configuration. | object({…}) | | null | -| [url_map_config](variables.tf#L202) | The url-map configuration. | object({…}) | | null | +| [name](variables.tf#L46) | Load balancer name. | string | ✓ | | +| [project_id](variables.tf#L78) | Project id. | string | ✓ | | +| [region](variables.tf#L103) | The region where to allocate the ILB resources. | string | ✓ | | +| [urlmap_config](variables-urlmap.tf#L19) | The URL map configuration. | object({…}) | ✓ | | +| [vpc_config](variables.tf#L136) | VPC-level configuration. | object({…}) | ✓ | | +| [address](variables.tf#L17) | Optional IP address used for the forwarding rule. | string | | null | +| [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} | +| [description](variables.tf#L23) | Optional description used for resources. | string | | "Terraform managed." | +| [group_configs](variables.tf#L29) | Optional unmanaged groups to create. Can be referenced in backends via key or outputs. | map(object({…})) | | {} | +| [health_check_configs](variables-health-check.tf#L19) | Optional auto-created health check configurations, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | map(object({…})) | | {…} | +| [labels](variables.tf#L40) | Labels set on resources. | map(string) | | {} | +| [neg_configs](variables.tf#L51) | Optional network endpoint groups to create. Can be referenced in backends via key or outputs. | map(object({…})) | | {} | +| [network_tier_premium](variables.tf#L71) | Use premium network tier. Defaults to true. | bool | | true | +| [ports](variables.tf#L83) | Optional ports for HTTP load balancer, valid ports are 80 and 8080. | list(string) | | null | +| [protocol](variables.tf#L89) | Protocol supported by this load balancer. | string | | "HTTP" | +| [ssl_certificates](variables.tf#L108) | SSL target proxy certificates (only if protocol is HTTPS). | object({…}) | | {} | +| [static_ip_config](variables.tf#L121) | Static IP address configuration. | object({…}) | | {…} | ## Outputs | name | description | sensitive | |---|---|:---:| -| [backend_services](outputs.tf#L22) | Backend service resources. | | -| [forwarding_rule](outputs.tf#L55) | The forwarding rule. | | -| [health_checks](outputs.tf#L17) | Health-check resources. | | -| [ip_address](outputs.tf#L41) | The reserved IP address. | | -| [ssl_certificate_link_ids](outputs.tf#L34) | The SSL certificate. | | -| [target_proxy](outputs.tf#L46) | The target proxy. | | -| [url_map](outputs.tf#L29) | The url-map. | | +| [backend_service_ids](outputs.tf#L17) | Backend service resources. | | +| [group_ids](outputs.tf#L24) | Autogenerated instance group ids. | | +| [health_check_ids](outputs.tf#L31) | Autogenerated health check ids. | | +| [neg_ids](outputs.tf#L38) | Autogenerated network endpoint group ids. | | From 8a68f5b8aacbf9cbbb6ff3da96e26c6df1041f6e Mon Sep 17 00:00:00 2001 From: Ludo Date: Mon, 14 Nov 2022 00:49:01 +0100 Subject: [PATCH 09/13] readme --- modules/net-ilb-l7/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/net-ilb-l7/README.md b/modules/net-ilb-l7/README.md index f9b7697ab5..2274b7ba81 100644 --- a/modules/net-ilb-l7/README.md +++ b/modules/net-ilb-l7/README.md @@ -1,8 +1,6 @@ # Internal (HTTP/S) Load Balancer Module -The module allows managing Internal HTTP/HTTPS Load Balancers (HTTP(S) ILBs), integrating the forwarding rule, the url-map, the backends, optional health checks and SSL certificates. - -It's designed to be a simple match for the [`vpc`](../net-vpc) and the [`compute-mig`](../compute-mig) modules, which can be used to manage VPCs and instance groups. +This module allows managing Internal HTTP/HTTPS Load Balancers (L7 ILBs). It's designed to expose the full configuration of the underlying resources, and to facilitate common usage patterns by providing sensible defaults, and optionally managing prerequisite resources like health checks, instance groups, etc. ## Examples From 63ab47cc4104942f2b125cc6e20a0c971f415b28 Mon Sep 17 00:00:00 2001 From: Ludo Date: Mon, 14 Nov 2022 10:45:06 +0100 Subject: [PATCH 10/13] tested --- modules/net-ilb-l7/README.md | 41 +++++++++++-------- modules/net-ilb-l7/backend-service.tf | 10 ++--- modules/net-ilb-l7/main.tf | 31 +++++++++----- modules/net-ilb-l7/outputs.tf | 10 +++++ modules/net-ilb-l7/urlmap.tf | 2 +- .../net-ilb-l7/variables-backend-service.tf | 5 ++- modules/net-ilb-l7/variables.tf | 7 ++-- 7 files changed, 69 insertions(+), 37 deletions(-) diff --git a/modules/net-ilb-l7/README.md b/modules/net-ilb-l7/README.md index 2274b7ba81..a008840a57 100644 --- a/modules/net-ilb-l7/README.md +++ b/modules/net-ilb-l7/README.md @@ -2,6 +2,8 @@ This module allows managing Internal HTTP/HTTPS Load Balancers (L7 ILBs). It's designed to expose the full configuration of the underlying resources, and to facilitate common usage patterns by providing sensible defaults, and optionally managing prerequisite resources like health checks, instance groups, etc. +Due to the complexity of the underlying resources, changes to the configuration that involve recreation of resources are best applied in stages, starting by disabling the configuration in the urlmap that references the resources that neeed recreation, then doing the same for the backend service, etc. + ## Examples ### Minimal Example @@ -278,7 +280,8 @@ module "ilb-l7" { } neg_configs = { my-neg = { - zone = "europe-west1-b" + zone = "europe-west1-b" + default_port = 80 endpoints = [ { ip_address = "10.0.0.10" @@ -328,6 +331,7 @@ module "ilb-l7" { }] path_matchers = { pathmap = { + default_service = "default" path_rules = [{ paths = ["/video", "/video/*"] service = "video" @@ -354,7 +358,11 @@ module "ilb-l7" { | [backend-service.tf](./backend-service.tf) | Backend service resources. | google_compute_region_backend_service | | [health-check.tf](./health-check.tf) | Health check resource. | google_compute_health_check | | [main.tf](./main.tf) | Module-level locals and resources. | google_compute_forwarding_rule · google_compute_instance_group · google_compute_network_endpoint · google_compute_network_endpoint_group · google_compute_region_ssl_certificate · google_compute_region_target_http_proxy · google_compute_region_target_https_proxy | -| [outputs.tf](./outputs.tf) | Module outputs. | | +| [outputs.tf](./outputs.tf) | Module outputs. | +# value = google_compute_forwarding_rule.default +# } + +output | | [urlmap.tf](./urlmap.tf) | URL map resources. | google_compute_region_url_map | | [variables-backend-service.tf](./variables-backend-service.tf) | Backend services variables. | | | [variables-health-check.tf](./variables-health-check.tf) | Health check variable. | | @@ -367,30 +375,31 @@ module "ilb-l7" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [name](variables.tf#L46) | Load balancer name. | string | ✓ | | -| [project_id](variables.tf#L78) | Project id. | string | ✓ | | -| [region](variables.tf#L103) | The region where to allocate the ILB resources. | string | ✓ | | +| [project_id](variables.tf#L79) | Project id. | string | ✓ | | +| [region](variables.tf#L104) | The region where to allocate the ILB resources. | string | ✓ | | | [urlmap_config](variables-urlmap.tf#L19) | The URL map configuration. | object({…}) | ✓ | | -| [vpc_config](variables.tf#L136) | VPC-level configuration. | object({…}) | ✓ | | +| [vpc_config](variables.tf#L137) | VPC-level configuration. | object({…}) | ✓ | | | [address](variables.tf#L17) | Optional IP address used for the forwarding rule. | string | | null | -| [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} | +| [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} | | [description](variables.tf#L23) | Optional description used for resources. | string | | "Terraform managed." | | [group_configs](variables.tf#L29) | Optional unmanaged groups to create. Can be referenced in backends via key or outputs. | map(object({…})) | | {} | | [health_check_configs](variables-health-check.tf#L19) | Optional auto-created health check configurations, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | map(object({…})) | | {…} | | [labels](variables.tf#L40) | Labels set on resources. | map(string) | | {} | -| [neg_configs](variables.tf#L51) | Optional network endpoint groups to create. Can be referenced in backends via key or outputs. | map(object({…})) | | {} | -| [network_tier_premium](variables.tf#L71) | Use premium network tier. Defaults to true. | bool | | true | -| [ports](variables.tf#L83) | Optional ports for HTTP load balancer, valid ports are 80 and 8080. | list(string) | | null | -| [protocol](variables.tf#L89) | Protocol supported by this load balancer. | string | | "HTTP" | -| [ssl_certificates](variables.tf#L108) | SSL target proxy certificates (only if protocol is HTTPS). | object({…}) | | {} | -| [static_ip_config](variables.tf#L121) | Static IP address configuration. | object({…}) | | {…} | +| [neg_configs](variables.tf#L51) | Optional network endpoint groups to create. Can be referenced in backends via key or outputs. | map(object({…})) | | {} | +| [network_tier_premium](variables.tf#L72) | Use premium network tier. Defaults to true. | bool | | true | +| [ports](variables.tf#L84) | Optional ports for HTTP load balancer, valid ports are 80 and 8080. | list(string) | | null | +| [protocol](variables.tf#L90) | Protocol supported by this load balancer. | string | | "HTTP" | +| [ssl_certificates](variables.tf#L109) | SSL target proxy certificates (only if protocol is HTTPS). | object({…}) | | {} | +| [static_ip_config](variables.tf#L122) | Static IP address configuration. | object({…}) | | {…} | ## Outputs | name | description | sensitive | |---|---|:---:| -| [backend_service_ids](outputs.tf#L17) | Backend service resources. | | -| [group_ids](outputs.tf#L24) | Autogenerated instance group ids. | | -| [health_check_ids](outputs.tf#L31) | Autogenerated health check ids. | | -| [neg_ids](outputs.tf#L38) | Autogenerated network endpoint group ids. | | +| [address](outputs.tf#L17) | Forwarding rule address. | | +| [backend_service_ids](outputs.tf#L22) | Backend service resources. | | +| [group_ids](outputs.tf#L34) | Autogenerated instance group ids. | | +| [health_check_ids](outputs.tf#L41) | Autogenerated health check ids. | | +| [neg_ids](outputs.tf#L48) | Autogenerated network endpoint group ids. | | diff --git a/modules/net-ilb-l7/backend-service.tf b/modules/net-ilb-l7/backend-service.tf index d89787bfef..87e618b5b4 100644 --- a/modules/net-ilb-l7/backend-service.tf +++ b/modules/net-ilb-l7/backend-service.tf @@ -44,11 +44,12 @@ resource "google_compute_region_backend_service" "default" { ] # not for internet / serverless NEGs locality_lb_policy = each.value.locality_lb_policy load_balancing_scheme = "INTERNAL_MANAGED" - network = var.vpc_config.network port_name = each.value.port_name # defaults to http, not for NEGs - protocol = var.protocol - session_affinity = each.value.session_affinity - timeout_sec = each.value.timeout_sec + protocol = ( + each.value.protocol == null ? var.protocol : each.value.protocol + ) + session_affinity = each.value.session_affinity + timeout_sec = each.value.timeout_sec dynamic "backend" { for_each = { for b in coalesce(each.value.backends, []) : b.group => b } @@ -223,5 +224,4 @@ resource "google_compute_region_backend_service" "default" { policy = "CONSISTENT_HASH_SUBSETTING" } } - } diff --git a/modules/net-ilb-l7/main.tf b/modules/net-ilb-l7/main.tf index ce540e7ad4..2845c3479c 100644 --- a/modules/net-ilb-l7/main.tf +++ b/modules/net-ilb-l7/main.tf @@ -17,7 +17,7 @@ locals { _neg_endpoints = flatten([ for k, v in var.neg_configs : [ - for vv in v.endpoints : merge(vv, { neg = k }) + for vv in v.endpoints : merge(vv, { neg = k, zone = v.zone }) ] ]) fwd_rule_ports = ( @@ -48,7 +48,7 @@ resource "google_compute_forwarding_rule" "default" { ip_protocol = "TCP" load_balancing_scheme = "INTERNAL_MANAGED" network = var.vpc_config.network - network_tier = var.network_tier_premium ? "PREMUIM" : "STANDARD" + network_tier = var.network_tier_premium ? "PREMIUM" : "STANDARD" port_range = join(",", local.fwd_rule_ports) subnetwork = var.vpc_config.subnetwork labels = var.labels @@ -101,14 +101,24 @@ resource "google_compute_instance_group" "default" { } resource "google_compute_network_endpoint_group" "default" { - for_each = var.neg_configs - project = var.project_id - zone = each.value.zone - name = "${var.name}-${each.key}" - description = var.description - network_endpoint_type = each.value.type - network = try(each.value.vpc_config.network, var.vpc_config.network) - subnetwork = try(each.value.vpc_config.subnetwork, var.vpc_config.subnetwork) + for_each = var.neg_configs + project = var.project_id + zone = each.value.zone + name = "${var.name}-${each.key}" + # re-enable once provider properly supports this + # default_port = each.value.default_port + description = var.description + network_endpoint_type = ( + each.value.is_hybrid + ? "NON_GCP_PRIVATE_IP_PORT" + : "GCE_VM_IP_PORT" + ) + network = try(each.value.vpc_config.network, var.vpc_config.network) + subnetwork = ( + each.value.is_hybrid + ? null + : try(each.value.vpc_config.subnetwork, var.vpc_config.subnetwork) + ) } resource "google_compute_network_endpoint" "default" { @@ -120,5 +130,6 @@ resource "google_compute_network_endpoint" "default" { instance = each.value.instance ip_address = each.value.ip_address port = each.value.port + zone = each.value.zone } diff --git a/modules/net-ilb-l7/outputs.tf b/modules/net-ilb-l7/outputs.tf index 01ff7dab87..e940fb48e6 100644 --- a/modules/net-ilb-l7/outputs.tf +++ b/modules/net-ilb-l7/outputs.tf @@ -14,6 +14,11 @@ * limitations under the License. */ +output "address" { + description = "Forwarding rule address." + value = google_compute_forwarding_rule.default.ip_address +} + output "backend_service_ids" { description = "Backend service resources." value = { @@ -21,6 +26,11 @@ output "backend_service_ids" { } } +# output "forwarding_rule" { +# description = "Forwarding rule resource" +# value = google_compute_forwarding_rule.default +# } + output "group_ids" { description = "Autogenerated instance group ids." value = { diff --git a/modules/net-ilb-l7/urlmap.tf b/modules/net-ilb-l7/urlmap.tf index c58d64c075..21764ce090 100644 --- a/modules/net-ilb-l7/urlmap.tf +++ b/modules/net-ilb-l7/urlmap.tf @@ -88,7 +88,7 @@ resource "google_compute_region_url_map" "default" { } } dynamic "path_rule" { - for_each = toset(coalesce(m.value.path_rules)) + for_each = toset(coalesce(m.value.path_rules, [])) content { paths = path_rule.value.paths service = path_rule.value.service == null ? null : lookup( diff --git a/modules/net-ilb-l7/variables-backend-service.tf b/modules/net-ilb-l7/variables-backend-service.tf index e84ebb1101..30eee51e76 100644 --- a/modules/net-ilb-l7/variables-backend-service.tf +++ b/modules/net-ilb-l7/variables-backend-service.tf @@ -25,12 +25,13 @@ variable "backend_service_configs" { locality_lb_policy = optional(string) log_sample_rate = optional(number) port_name = optional(string) + protocol = optional(string) session_affinity = optional(string) timeout_sec = optional(number) backends = list(object({ group = string - balancing_mode = optional(string, "CONNECTION") - capacity_scaler = optional(number) + balancing_mode = optional(string, "UTILIZATION") + capacity_scaler = optional(number, 1) description = optional(string, "Terraform managed.") failover = optional(bool, false) max_connections = optional(object({ diff --git a/modules/net-ilb-l7/variables.tf b/modules/net-ilb-l7/variables.tf index 10d8dde128..42e08d4451 100644 --- a/modules/net-ilb-l7/variables.tf +++ b/modules/net-ilb-l7/variables.tf @@ -51,9 +51,10 @@ variable "name" { variable "neg_configs" { description = "Optional network endpoint groups to create. Can be referenced in backends via key or outputs." type = map(object({ - zone = string - type = optional(string, "GCE_VM_IP") # GCE_VM_IP, GCE_VM_IP_PORT, and NON_GCP_PRIVATE_IP_PORT - default_port = optional(number) + zone = string + # re-enable once provider properly support this + # default_port = optional(number) + is_hybrid = optional(bool, false) endpoints = optional(list(object({ ip_address = string instance = optional(string) From 1bdfd203267f2f000762b0e85f60153770a496d5 Mon Sep 17 00:00:00 2001 From: Ludo Date: Mon, 14 Nov 2022 10:57:29 +0100 Subject: [PATCH 11/13] example --- modules/net-ilb-l7/README.md | 192 ++++++++++++++++++++++++++++++++--- 1 file changed, 177 insertions(+), 15 deletions(-) diff --git a/modules/net-ilb-l7/README.md b/modules/net-ilb-l7/README.md index a008840a57..32498b96e8 100644 --- a/modules/net-ilb-l7/README.md +++ b/modules/net-ilb-l7/README.md @@ -219,9 +219,7 @@ module "ilb-l7" { instances = [ "projects/myprj/zones/europe-west1-b/instances/vm-a" ] - named_ports = { - http = 80 - } + named_ports = { http = 80 } } } urlmap_config = { @@ -248,7 +246,9 @@ module "ilb-l7" { backend_service_configs = { default = { backends = [{ - group = "projects/myprj/zones/europe-west1-a/networkEndpointGroups/my-neg" + balancing_mode = "RATE" + group = "projects/myprj/zones/europe-west1-a/networkEndpointGroups/my-neg" + max_rate = { per_endpoint = 1 } }] } } @@ -273,21 +273,59 @@ module "ilb-l7" { region = "europe-west1" backend_service_configs = { default = { - backends = [ - { group = "my-neg" } - ] + backends = [{ + balancing_mode = "RATE" + group = "my-neg" + max_rate = { per_endpoint = 1 } + }] } } neg_configs = { my-neg = { - zone = "europe-west1-b" - default_port = 80 - endpoints = [ - { - ip_address = "10.0.0.10" - instance = "test-1" - } - ] + zone = "europe-west1-b" + endpoints = [{ + ip_address = "10.0.0.10" + instance = "test-1" + port = 80 + }] + } + } + urlmap_config = { + default_service = "default" + } + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link + } +} +# tftest modules=1 resources=7 +``` + +Hybrid NEGs are also supported: + +```hcl +module "ilb-l7" { + source = "./fabric/modules/net-ilb-l7" + name = "ilb-test" + project_id = var.project_id + region = "europe-west1" + backend_service_configs = { + default = { + backends = [{ + balancing_mode = "RATE" + group = "my-neg" + max_rate = { per_endpoint = 1 } + }] + } + } + neg_configs = { + my-neg = { + zone = "europe-west1-b" + is_hybrid = true + endpoints = [{ + ip_address = "10.0.0.10" + port = 80 + }] } } urlmap_config = { @@ -348,6 +386,130 @@ module "ilb-l7" { # tftest modules=1 resources=6 ``` +### Complex example + +This example mixes group and NEG backends, and shows how to set HTTPS for specific backends. + +```hcl +module "ilb-l7" { + source = "./fabric/modules/net-ilb-l7" + name = "ilb-l7-test-0" + project_id = "prj-gce" + region = "europe-west8" + backend_service_configs = { + default = { + backends = [ + { group = "nginx-ew8-b" }, + { group = "nginx-ew8-c" }, + ] + } + gce-neg = { + backends = [{ + balancing_mode = "RATE" + group = "neg-nginx-ew8-c" + max_rate = { per_endpoint = 1 } + }] + } + home = { + backends = [{ + balancing_mode = "RATE" + group = "neg-home-hello" + max_rate = { + per_endpoint = 1 + } + }] + health_checks = ["neg"] + locality_lb_policy = "ROUND_ROBIN" + protocol = "HTTPS" + port_name = "https" + } + } + group_configs = { + nginx-ew8-b = { + zone = "europe-west8-b" + instances = [ + "projects/prj-gce/zones/europe-west8-b/instances/nginx-ew8-b" + ] + named_ports = { http = 80 } + } + nginx-ew8-c = { + zone = "europe-west8-c" + instances = [ + "projects/prj-gce/zones/europe-west8-c/instances/nginx-ew8-c" + ] + named_ports = { http = 80 } + } + } + health_check_configs = { + default = { + http = { + port = 80 + } + } + neg = { + https = { + host = "hello.home.example.com" + port = 443 + } + } + } + neg_configs = { + neg-nginx-ew8-c = { + zone = "europe-west8-c" + endpoints = [ + { + ip_address = "10.24.32.26" + instance = "nginx-ew8-c" + port = 80 + } + ] + } + neg-home-hello = { + zone = "europe-west8-b" + is_hybrid = true + endpoints = [ + { + ip_address = "192.168.0.3" + port = 443 + } + ] + } + } + urlmap_config = { + default_service = "default" + host_rules = [ + { + hosts = ["*"] + path_matcher = "gce" + }, + { + hosts = ["hello.home.example.com"] + path_matcher = "home" + } + ] + path_matchers = { + gce = { + default_service = "default" + path_rules = [ + { + paths = ["/gce-neg", "/gce-neg/*"] + service = "gce-neg" + } + ] + } + home = { + default_service = "home" + } + } + } + vpc_config = { + network = "projects/prj-host/global/networks/shared-vpc" + subnetwork = "projects/prj-host/regions/europe-west8/subnetworks/gce" + } +} +# tftest modules=1 resources=14 +``` + From 7543325183a12a72acaa45dade2ed380bf1b3711 Mon Sep 17 00:00:00 2001 From: Ludo Date: Mon, 14 Nov 2022 12:02:11 +0100 Subject: [PATCH 12/13] default urlmap value, remove stale variable --- modules/net-ilb-l7/README.md | 41 +++++--------------------- modules/net-ilb-l7/variables-urlmap.tf | 3 ++ modules/net-ilb-l7/variables.tf | 16 ---------- 3 files changed, 10 insertions(+), 50 deletions(-) diff --git a/modules/net-ilb-l7/README.md b/modules/net-ilb-l7/README.md index 32498b96e8..c3373e2f17 100644 --- a/modules/net-ilb-l7/README.md +++ b/modules/net-ilb-l7/README.md @@ -23,9 +23,6 @@ module "ilb-l7" { }] } } - urlmap_config = { - default_service = "default" - } vpc_config = { network = var.vpc.self_link subnetwork = var.subnet.self_link @@ -55,9 +52,6 @@ module "ilb-l7" { "projects/myprj/regions/europe-west1/sslCertificates/my-cert" ] } - urlmap_config = { - default_service = "default" - } vpc_config = { network = var.vpc.self_link subnetwork = var.subnet.self_link @@ -93,9 +87,6 @@ module "ilb-l7" { tcp = { port = 80 } } } - urlmap_config = { - default_service = "default" - } vpc_config = { network = var.vpc.self_link subnetwork = var.subnet.self_link @@ -121,9 +112,6 @@ module "ilb-l7" { } } health_check_configs = {} - urlmap_config = { - default_service = "default" - } vpc_config = { network = var.vpc.self_link subnetwork = var.subnet.self_link @@ -184,9 +172,6 @@ module "ilb-l7" { } } } - urlmap_config = { - default_service = "default" - } vpc_config = { network = var.vpc.self_link subnetwork = var.subnet.self_link @@ -222,9 +207,6 @@ module "ilb-l7" { named_ports = { http = 80 } } } - urlmap_config = { - default_service = "default" - } vpc_config = { network = var.vpc.self_link subnetwork = var.subnet.self_link @@ -252,9 +234,6 @@ module "ilb-l7" { }] } } - urlmap_config = { - default_service = "default" - } vpc_config = { network = var.vpc.self_link subnetwork = var.subnet.self_link @@ -290,9 +269,6 @@ module "ilb-l7" { }] } } - urlmap_config = { - default_service = "default" - } vpc_config = { network = var.vpc.self_link subnetwork = var.subnet.self_link @@ -328,9 +304,6 @@ module "ilb-l7" { }] } } - urlmap_config = { - default_service = "default" - } vpc_config = { network = var.vpc.self_link subnetwork = var.subnet.self_link @@ -341,7 +314,9 @@ module "ilb-l7" { ### URL Map -The module exposes the full URL map resource configuration, with some minor changes to the interface to decrease verbosity, and support for aliasing backend services via keys: +The module exposes the full URL map resource configuration, with some minor changes to the interface to decrease verbosity, and support for aliasing backend services via keys. + +The default URL map configuration sets the `default` backend service as the default service for the load balancer as a convenience. Just override the `urlmap_config` variable to change the default behaviour: ```hcl module "ilb-l7" { @@ -421,7 +396,6 @@ module "ilb-l7" { health_checks = ["neg"] locality_lb_policy = "ROUND_ROBIN" protocol = "HTTPS" - port_name = "https" } } group_configs = { @@ -538,9 +512,8 @@ output | |---|---|:---:|:---:|:---:| | [name](variables.tf#L46) | Load balancer name. | string | ✓ | | | [project_id](variables.tf#L79) | Project id. | string | ✓ | | -| [region](variables.tf#L104) | The region where to allocate the ILB resources. | string | ✓ | | -| [urlmap_config](variables-urlmap.tf#L19) | The URL map configuration. | object({…}) | ✓ | | -| [vpc_config](variables.tf#L137) | VPC-level configuration. | object({…}) | ✓ | | +| [region](variables.tf#L103) | The region where to allocate the ILB resources. | string | ✓ | | +| [vpc_config](variables.tf#L121) | VPC-level configuration. | object({…}) | ✓ | | | [address](variables.tf#L17) | Optional IP address used for the forwarding rule. | string | | null | | [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} | | [description](variables.tf#L23) | Optional description used for resources. | string | | "Terraform managed." | @@ -551,8 +524,8 @@ output | | [network_tier_premium](variables.tf#L72) | Use premium network tier. Defaults to true. | bool | | true | | [ports](variables.tf#L84) | Optional ports for HTTP load balancer, valid ports are 80 and 8080. | list(string) | | null | | [protocol](variables.tf#L90) | Protocol supported by this load balancer. | string | | "HTTP" | -| [ssl_certificates](variables.tf#L109) | SSL target proxy certificates (only if protocol is HTTPS). | object({…}) | | {} | -| [static_ip_config](variables.tf#L122) | Static IP address configuration. | object({…}) | | {…} | +| [ssl_certificates](variables.tf#L108) | SSL target proxy certificates (only if protocol is HTTPS). | object({…}) | | {} | +| [urlmap_config](variables-urlmap.tf#L19) | The URL map configuration. | object({…}) | | {…} | ## Outputs diff --git a/modules/net-ilb-l7/variables-urlmap.tf b/modules/net-ilb-l7/variables-urlmap.tf index 5e9dffeb68..cd1869f5ae 100644 --- a/modules/net-ilb-l7/variables-urlmap.tf +++ b/modules/net-ilb-l7/variables-urlmap.tf @@ -228,4 +228,7 @@ variable "urlmap_config" { description = optional(string) }))) }) + default = { + default_service = "default" + } } diff --git a/modules/net-ilb-l7/variables.tf b/modules/net-ilb-l7/variables.tf index 42e08d4451..536fa7d082 100644 --- a/modules/net-ilb-l7/variables.tf +++ b/modules/net-ilb-l7/variables.tf @@ -98,7 +98,6 @@ variable "protocol" { ) error_message = "Protocol must be HTTP or HTTPS" } - } variable "region" { @@ -119,21 +118,6 @@ variable "ssl_certificates" { nullable = false } -variable "static_ip_config" { - description = "Static IP address configuration." - type = object({ - reserve = bool - options = object({ - address = string - subnetwork = string # The subnet id - }) - }) - default = { - reserve = false - options = null - } -} - variable "vpc_config" { description = "VPC-level configuration." type = object({ From 156e113a456b95a835740b6ecc8d1aafe9946a23 Mon Sep 17 00:00:00 2001 From: Ludo Date: Mon, 14 Nov 2022 12:31:57 +0100 Subject: [PATCH 13/13] tests --- tests/modules/net_ilb_l7/fixture/main.tf | 35 ++- .../net_ilb_l7/fixture/test.defaults.tfvars | 7 + .../net_ilb_l7/fixture/test.groups.tfvars | 16 ++ .../fixture/test.health-checks-custom.tfvars | 16 ++ .../test.health-checks-external.tfvars | 10 + .../net_ilb_l7/fixture/test.https.tfvars | 6 + .../net_ilb_l7/fixture/test.negs.tfvars | 17 ++ .../net_ilb_l7/fixture/test.ssl.tfvars | 9 + .../net_ilb_l7/fixture/test.urlmaps.tfvars | 28 ++ tests/modules/net_ilb_l7/fixture/variables.tf | 188 +++--------- tests/modules/net_ilb_l7/test_plan.py | 270 +++++++----------- 11 files changed, 275 insertions(+), 327 deletions(-) create mode 100644 tests/modules/net_ilb_l7/fixture/test.defaults.tfvars create mode 100644 tests/modules/net_ilb_l7/fixture/test.groups.tfvars create mode 100644 tests/modules/net_ilb_l7/fixture/test.health-checks-custom.tfvars create mode 100644 tests/modules/net_ilb_l7/fixture/test.health-checks-external.tfvars create mode 100644 tests/modules/net_ilb_l7/fixture/test.https.tfvars create mode 100644 tests/modules/net_ilb_l7/fixture/test.negs.tfvars create mode 100644 tests/modules/net_ilb_l7/fixture/test.ssl.tfvars create mode 100644 tests/modules/net_ilb_l7/fixture/test.urlmaps.tfvars diff --git a/tests/modules/net_ilb_l7/fixture/main.tf b/tests/modules/net_ilb_l7/fixture/main.tf index 10d747c9db..a9981458a9 100644 --- a/tests/modules/net_ilb_l7/fixture/main.tf +++ b/tests/modules/net_ilb_l7/fixture/main.tf @@ -15,19 +15,24 @@ */ module "test" { - source = "../../../../modules/net-ilb-l7" - project_id = "my-project" - name = "ilb-l7-test" - region = "europe-west1" - network = "projects/my-project/global/networks/default" - subnetwork = "projects/my-project/regions/europe-west1/subnetworks/default" - backend_services_config = var.backend_services_config - forwarding_rule_config = var.forwarding_rule_config - health_checks_config = var.health_checks_config - health_checks_config_defaults = var.health_checks_config_defaults - https = var.https - ssl_certificates_config = var.ssl_certificates_config - static_ip_config = var.static_ip_config - target_proxy_https_config = var.target_proxy_https_config - url_map_config = var.url_map_config + source = "../../../../modules/net-ilb-l7" + project_id = "my-project" + name = "ilb-l7-test" + region = "europe-west1" + vpc_config = { + network = "projects/my-project/global/networks/default" + subnetwork = "projects/my-project/regions/europe-west1/subnetworks/default" + } + address = var.address + backend_service_configs = var.backend_service_configs + description = var.description + group_configs = var.group_configs + health_check_configs = var.health_check_configs + labels = var.labels + neg_configs = var.neg_configs + network_tier_premium = var.network_tier_premium + ports = var.ports + protocol = var.protocol + ssl_certificates = var.ssl_certificates + urlmap_config = var.urlmap_config } diff --git a/tests/modules/net_ilb_l7/fixture/test.defaults.tfvars b/tests/modules/net_ilb_l7/fixture/test.defaults.tfvars new file mode 100644 index 0000000000..f36b1062de --- /dev/null +++ b/tests/modules/net_ilb_l7/fixture/test.defaults.tfvars @@ -0,0 +1,7 @@ +backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + } +} diff --git a/tests/modules/net_ilb_l7/fixture/test.groups.tfvars b/tests/modules/net_ilb_l7/fixture/test.groups.tfvars new file mode 100644 index 0000000000..36e25ea558 --- /dev/null +++ b/tests/modules/net_ilb_l7/fixture/test.groups.tfvars @@ -0,0 +1,16 @@ +backend_service_configs = { + default = { + backends = [{ + group = "custom" + }] + } +} +group_configs = { + custom = { + zone = "europe-west1-b" + instances = [ + "projects/myprj/zones/europe-west1-b/instances/vm-a" + ] + named_ports = { http = 80 } + } +} diff --git a/tests/modules/net_ilb_l7/fixture/test.health-checks-custom.tfvars b/tests/modules/net_ilb_l7/fixture/test.health-checks-custom.tfvars new file mode 100644 index 0000000000..4e267cd23f --- /dev/null +++ b/tests/modules/net_ilb_l7/fixture/test.health-checks-custom.tfvars @@ -0,0 +1,16 @@ +backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + health_checks = ["custom"] + } +} +health_check_configs = { + custom = { + tcp = { + port_specification = "USE_SERVING_PORT" + } + } +} + diff --git a/tests/modules/net_ilb_l7/fixture/test.health-checks-external.tfvars b/tests/modules/net_ilb_l7/fixture/test.health-checks-external.tfvars new file mode 100644 index 0000000000..57fafa93a8 --- /dev/null +++ b/tests/modules/net_ilb_l7/fixture/test.health-checks-external.tfvars @@ -0,0 +1,10 @@ +backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + health_checks = ["projects/myprj/global/healthChecks/custom"] + } +} +health_check_configs = {} + diff --git a/tests/modules/net_ilb_l7/fixture/test.https.tfvars b/tests/modules/net_ilb_l7/fixture/test.https.tfvars new file mode 100644 index 0000000000..7a5f61ddf5 --- /dev/null +++ b/tests/modules/net_ilb_l7/fixture/test.https.tfvars @@ -0,0 +1,6 @@ +protocol = "HTTPS" +ssl_certificates = { + certificate_ids = [ + "projects/myprj/regions/europe-west1/sslCertificates/my-cert" + ] +} diff --git a/tests/modules/net_ilb_l7/fixture/test.negs.tfvars b/tests/modules/net_ilb_l7/fixture/test.negs.tfvars new file mode 100644 index 0000000000..6ffa232a1e --- /dev/null +++ b/tests/modules/net_ilb_l7/fixture/test.negs.tfvars @@ -0,0 +1,17 @@ +backend_service_configs = { + default = { + backends = [{ + group = "custom" + }] + } +} +neg_configs = { + custom = { + zone = "europe-west1-b" + endpoints = [{ + ip_address = "10.0.0.10" + instance = "test-1" + port = 80 + }] + } +} diff --git a/tests/modules/net_ilb_l7/fixture/test.ssl.tfvars b/tests/modules/net_ilb_l7/fixture/test.ssl.tfvars new file mode 100644 index 0000000000..d81d0e6d2c --- /dev/null +++ b/tests/modules/net_ilb_l7/fixture/test.ssl.tfvars @@ -0,0 +1,9 @@ +protocol = "HTTPS" +ssl_certificates = { + create_configs = { + default = { + certificate = "FOO" + private_key = "FOO" + } + } +} diff --git a/tests/modules/net_ilb_l7/fixture/test.urlmaps.tfvars b/tests/modules/net_ilb_l7/fixture/test.urlmaps.tfvars new file mode 100644 index 0000000000..110f65e7c0 --- /dev/null +++ b/tests/modules/net_ilb_l7/fixture/test.urlmaps.tfvars @@ -0,0 +1,28 @@ +backend_service_configs = { + default = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + } + video = { + backends = [{ + group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig-2" + }] + } +} +urlmap_config = { + default_service = "default" + host_rules = [{ + hosts = ["*"] + path_matcher = "pathmap" + }] + path_matchers = { + pathmap = { + default_service = "default" + path_rules = [{ + paths = ["/video", "/video/*"] + service = "video" + }] + } + } +} diff --git a/tests/modules/net_ilb_l7/fixture/variables.tf b/tests/modules/net_ilb_l7/fixture/variables.tf index d513a2a4c4..4c8fff683c 100644 --- a/tests/modules/net_ilb_l7/fixture/variables.tf +++ b/tests/modules/net_ilb_l7/fixture/variables.tf @@ -14,173 +14,71 @@ * limitations under the License. */ -variable "backend_services_config" { - description = "The backends services configuration." - type = map(object({ - backends = list(object({ - group = string # IG FQDN address - options = object({ - balancing_mode = string # Can be UTILIZATION, RATE - capacity_scaler = number # Valid range is [0.0,1.0] - max_connections = number - max_connections_per_instance = number - max_connections_per_endpoint = number - max_rate = number - max_rate_per_instance = number - max_rate_per_endpoint = number - max_utilization = number - }) - })) - - # Optional health check ids for backend service groups. - # Will lookup for ids in health_chacks_config first, - # then will use the id as is. If no ids are defined - # at all (null, []) health_checks_config_defaults is used - health_checks = list(string) - - log_config = object({ - enable = bool - sample_rate = number # must be in [0, 1] - }) - - options = object({ - affinity_cookie_ttl_sec = number - custom_request_headers = list(string) - custom_response_headers = list(string) - connection_draining_timeout_sec = number - locality_lb_policy = string - port_name = string - protocol = string - session_affinity = string - timeout_sec = number - - circuits_breakers = object({ - max_requests_per_connection = number # Set to 1 to disable keep-alive - max_connections = number # Defaults to 1024 - max_pending_requests = number # Defaults to 1024 - max_requests = number # Defaults to 1024 - max_retries = number # Defaults to 3 - }) - - consistent_hash = object({ - http_header_name = string - minimum_ring_size = string - http_cookie = object({ - name = string - path = string - ttl = object({ - seconds = number - nanos = number - }) - }) - }) +variable "address" { + description = "Optional IP address used for the forwarding rule." + type = string + default = null +} - iap = object({ - oauth2_client_id = string - oauth2_client_secret = string - oauth2_client_secret_sha256 = string - }) - }) - })) +variable "backend_service_configs" { + type = any default = {} } -variable "forwarding_rule_config" { - description = "Forwarding rule configurations." - type = object({ - ip_version = string - labels = map(string) - network_tier = string - port_range = string - service_label = string - }) - default = { - allow_global_access = true - ip_version = "IPV4" - labels = {} - network_tier = "PREMIUM" - # If not specified, 443 if var.https = true; 80 otherwise - port_range = null - service_label = null - } +variable "description" { + type = string + default = "Terraform managed." } -variable "health_checks_config" { - description = "Custom health checks configuration." - type = map(object({ - type = string # http https tcp ssl http2 - check = map(any) # actual health check block attributes - options = map(number) # interval, thresholds, timeout - logging = bool - })) +variable "group_configs" { + type = any default = {} } -variable "health_checks_config_defaults" { - description = "Auto-created health check default configuration." - type = object({ - check = map(any) # actual health check block attributes - logging = bool - options = map(number) # interval, thresholds, timeout - type = string # http https tcp ssl http2 - }) +variable "health_check_configs" { + type = any default = { - type = "http" - logging = false - options = {} - check = { - port_specification = "USE_SERVING_PORT" + default = { + http = { + port_specification = "USE_SERVING_PORT" + } } } } -variable "https" { - description = "Whether to enable HTTPS." - type = bool - default = false +variable "labels" { + type = map(string) + default = {} } -variable "ssl_certificates_config" { - description = "The SSL certificate configuration." - type = map(object({ - domains = list(string) - tls_private_key = string - tls_self_signed_cert = string - })) +variable "neg_configs" { + type = any default = {} } -variable "static_ip_config" { - description = "Static IP address configuration." - type = object({ - reserve = bool - options = object({ - address = string - subnetwork = string # The subnet id - }) - }) - default = { - reserve = false - options = null - } +variable "network_tier_premium" { + type = bool + default = true } -variable "target_proxy_https_config" { - description = "The HTTPS target proxy configuration." - type = object({ - ssl_certificates = list(string) - }) +variable "ports" { + type = list(string) default = null } -variable "url_map_config" { - description = "The url-map configuration." - type = object({ - default_service = string - default_url_redirect = map(any) - host_rules = list(any) - path_matchers = list(any) - tests = list(map(string)) - }) - default = null +variable "protocol" { + type = string + default = "HTTP" +} + +variable "ssl_certificates" { + type = any + default = {} +} + +variable "urlmap_config" { + type = any + default = { + default_service = "default" + } } diff --git a/tests/modules/net_ilb_l7/test_plan.py b/tests/modules/net_ilb_l7/test_plan.py index 5e235a13ae..91af3d2a2a 100644 --- a/tests/modules/net_ilb_l7/test_plan.py +++ b/tests/modules/net_ilb_l7/test_plan.py @@ -12,173 +12,109 @@ # See the License for the specific language governing permissions and # limitations under the License. -_BACKEND_SVC_CONFIG = '''{ - my-group = { - backends = [ - { - group = "my_group", - options = null - } - ], - health_checks = [] - log_config = null - options = null +import collections + + +def test_defaults(plan_runner): + "Test with default values." + _, resources = plan_runner(tf_var_file='test.defaults.tfvars') + counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources) + assert counts == { + 'google_compute_forwarding_rule.default': 1, + 'google_compute_health_check.default': 1, + 'google_compute_region_backend_service.default': 1, + 'google_compute_region_target_http_proxy.default': 1, + 'google_compute_region_url_map.default': 1 } -}''' - -_BACKEND_SVC_CONFIG_HC = '''{ - my-group = { - backends = [ - { - group = "my_group", - options = null - } - ], - health_checks = ["hc_1"] - log_config = null - options = null + + +def test_groups(plan_runner): + "Test groups." + _, resources = plan_runner(tf_var_file='test.groups.tfvars') + counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources) + assert counts == { + 'google_compute_forwarding_rule.default': 1, + 'google_compute_health_check.default': 1, + 'google_compute_instance_group.default': 1, + 'google_compute_region_backend_service.default': 1, + 'google_compute_region_target_http_proxy.default': 1, + 'google_compute_region_url_map.default': 1 + } + + +def test_health_checks_external(plan_runner): + "Test external health check." + _, resources = plan_runner(tf_var_file='test.health-checks-external.tfvars') + counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources) + assert counts == { + 'google_compute_forwarding_rule.default': 1, + 'google_compute_region_backend_service.default': 1, + 'google_compute_region_target_http_proxy.default': 1, + 'google_compute_region_url_map.default': 1 } -}''' - -_NAME = 'ilb-l7-test' - -_RESERVED_IP_CONFIG = '''{ - reserve = true - options = null -}''' - -_SSL_CERTIFICATES_CONFIG = '''{ - my-domain = { - domains = [ - "my-domain.com" - ], - tls_private_key = "my-key" - tls_self_signed_cert = "my-cert" + + +def test_health_checks_custom(plan_runner): + "Test custom health check." + _, resources = plan_runner(tf_var_file='test.health-checks-custom.tfvars') + counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources) + assert counts == { + 'google_compute_forwarding_rule.default': 1, + 'google_compute_health_check.default': 1, + 'google_compute_region_backend_service.default': 1, + 'google_compute_region_target_http_proxy.default': 1, + 'google_compute_region_url_map.default': 1 + } + + +def test_https(plan_runner): + "Test HTTPS load balancer." + _, resources = plan_runner(tf_var_file='test.https.tfvars') + counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources) + assert counts == { + 'google_compute_forwarding_rule.default': 1, + 'google_compute_health_check.default': 1, + 'google_compute_region_target_https_proxy.default': 1, + 'google_compute_region_url_map.default': 1 + } + + +def test_negs(plan_runner): + "Test negs." + _, resources = plan_runner(tf_var_file='test.negs.tfvars') + counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources) + assert counts == { + 'google_compute_forwarding_rule.default': 1, + 'google_compute_health_check.default': 1, + 'google_compute_network_endpoint.default': 1, + 'google_compute_network_endpoint_group.default': 1, + 'google_compute_region_backend_service.default': 1, + 'google_compute_region_target_http_proxy.default': 1, + 'google_compute_region_url_map.default': 1 + } + + +def test_ssl_certificates(plan_runner): + "Test HTTPS load balancer with SSL certificates." + _, resources = plan_runner(tf_var_file='test.ssl.tfvars') + counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources) + assert counts == { + 'google_compute_forwarding_rule.default': 1, + 'google_compute_health_check.default': 1, + 'google_compute_region_ssl_certificate.default': 1, + 'google_compute_region_target_https_proxy.default': 1, + 'google_compute_region_url_map.default': 1 + } + + +def test_urlmaps(plan_runner): + "Test URL maps." + _, resources = plan_runner(tf_var_file='test.urlmaps.tfvars') + counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources) + assert counts == { + 'google_compute_forwarding_rule.default': 1, + 'google_compute_health_check.default': 1, + 'google_compute_region_backend_service.default': 2, + 'google_compute_region_target_http_proxy.default': 1, + 'google_compute_region_url_map.default': 1 } -}''' - -_TARGET_PROXY_HTTPS_CONFIG = '''{ - ssl_certificates = [ - "my-domain" - ] -}''' - - -def test_group_default_hc(plan_runner): - "Tests a group backend service with no HC specified." - _, resources = plan_runner(backend_services_config=_BACKEND_SVC_CONFIG) - 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'] == 'INTERNAL_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_SVC_CONFIG, - health_checks_config_defaults='null') - - 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_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 - assert 'google_compute_forwarding_rule' 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_SVC_CONFIG_HC) - 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_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 - assert 'google_compute_forwarding_rule' in resources - - -def test_reserved_ip(plan_runner): - "Tests an IP reservation with a group backend service." - _, resources = plan_runner( - backend_services_config=_BACKEND_SVC_CONFIG, - static_ip_config=_RESERVED_IP_CONFIG - ) - assert len(resources) == 6 - resources = dict((r['type'], r['values']) for r in resources) - - assert 'google_compute_region_backend_service' 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 - assert 'google_compute_address' in resources - assert 'google_compute_forwarding_rule' in resources - - -def test_ssl(plan_runner): - "Tests HTTPS and SSL certificates." - _, resources = plan_runner( - backend_services_config=_BACKEND_SVC_CONFIG, - https="true", - ssl_certificates_config=_SSL_CERTIFICATES_CONFIG, - 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_forwarding_rule'] - assert fwd_rule['port_range'] == '443' - - assert 'google_compute_region_backend_service' in resources - assert 'google_compute_region_ssl_certificate' in resources - assert 'google_compute_region_target_http_proxy' not in resources - assert 'google_compute_region_target_https_proxy' in resources - assert 'google_compute_region_url_map' in resources - assert 'google_compute_forwarding_rule' in resources - - -def test_ssl_existing_cert(plan_runner): - "Tests HTTPS and SSL existing certificate." - _, resources = plan_runner( - backend_services_config=_BACKEND_SVC_CONFIG, - 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_forwarding_rule'] - assert fwd_rule['port_range'] == '443' - - assert 'google_compute_region_backend_service' in resources - assert 'google_compute_region_ssl_certificate' not in resources - assert 'google_compute_region_target_http_proxy' not in resources - assert 'google_compute_region_target_https_proxy' in resources - assert 'google_compute_region_url_map' in resources - assert 'google_compute_forwarding_rule' in resources