From 6db9b2563311ce8068b4b84c3c32c3e915c67274 Mon Sep 17 00:00:00 2001 From: Abhishek Tiwari Date: Mon, 9 Jun 2025 14:47:20 +0000 Subject: [PATCH] feat: add support for backend bucket to modules/backend --- .../external-lb-backend-bucket/gcp-logo.svg | 142 ++++++++++++++++++ examples/external-lb-backend-bucket/main.tf | 54 +++++++ .../external-lb-backend-bucket/outputs.tf | 19 +++ .../external-lb-backend-bucket/variables.tf | 19 +++ metadata.yaml | 2 + modules/backend/README.md | 1 + modules/backend/main.tf | 54 +++++++ modules/backend/metadata.yaml | 12 ++ modules/backend/outputs.tf | 12 +- modules/backend/variables.tf | 6 + modules/dynamic_backends/metadata.yaml | 2 + modules/frontend/metadata.yaml | 2 + modules/serverless_negs/metadata.yaml | 2 + .../external_lb_backend_bucket_test.go | 38 +++++ 14 files changed, 362 insertions(+), 3 deletions(-) create mode 100644 examples/external-lb-backend-bucket/gcp-logo.svg create mode 100644 examples/external-lb-backend-bucket/main.tf create mode 100644 examples/external-lb-backend-bucket/outputs.tf create mode 100644 examples/external-lb-backend-bucket/variables.tf create mode 100644 test/integration/external-lb-backend-bucket/external_lb_backend_bucket_test.go diff --git a/examples/external-lb-backend-bucket/gcp-logo.svg b/examples/external-lb-backend-bucket/gcp-logo.svg new file mode 100644 index 00000000..45c38c4e --- /dev/null +++ b/examples/external-lb-backend-bucket/gcp-logo.svg @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/external-lb-backend-bucket/main.tf b/examples/external-lb-backend-bucket/main.tf new file mode 100644 index 00000000..f394f7be --- /dev/null +++ b/examples/external-lb-backend-bucket/main.tf @@ -0,0 +1,54 @@ +/** + * Copyright 2025 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. + */ + +module "lb-frontend" { + source = "terraform-google-modules/lb-http/google//modules/frontend" + version = "~> 12.0" + + project_id = var.project_id + name = "global-lb-fe-bucket" + url_map_input = module.lb-backend.backend_service_info +} + +module "lb-backend" { + source = "terraform-google-modules/lb-http/google//modules/backend" + version = "~> 12.0" + + project_id = var.project_id + name = "global-lb-be-bucket" + backend_bucket_name = module.gcs.name + enable_cdn = true +} + +module "gcs" { + source = "terraform-google-modules/cloud-storage/google//modules/simple_bucket" + version = "~> 10.0" + + project_id = var.project_id + location = "us-central1" + name = "gcs-bucket" + force_destroy = true + iam_members = [{ member = "allUsers", role = "roles/storage.objectViewer" }] +} + +// The image object in Cloud Storage. +// Note that the path in the bucket matches the paths in the url map path rule above. +resource "google_storage_bucket_object" "image" { + name = "assets/gcp-logo.svg" + content = file("./gcp-logo.svg") + content_type = "image/svg+xml" + bucket = module.gcs.name +} diff --git a/examples/external-lb-backend-bucket/outputs.tf b/examples/external-lb-backend-bucket/outputs.tf new file mode 100644 index 00000000..456878ac --- /dev/null +++ b/examples/external-lb-backend-bucket/outputs.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2025 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. + */ + +output "load-balancer-ip" { + value = module.lb-frontend.external_ip +} diff --git a/examples/external-lb-backend-bucket/variables.tf b/examples/external-lb-backend-bucket/variables.tf new file mode 100644 index 00000000..419e3a19 --- /dev/null +++ b/examples/external-lb-backend-bucket/variables.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2025 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. + */ + +variable "project_id" { + type = string +} diff --git a/metadata.yaml b/metadata.yaml index ddf68ac9..078d7fb0 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -52,6 +52,8 @@ spec: location: examples/cross-project-mig-backend - name: dynamic-backend location: examples/dynamic-backend + - name: external-lb-backend-bucket + location: examples/external-lb-backend-bucket - name: gke-node-port location: examples/https-gke/gke-node-port - name: https-gke diff --git a/modules/backend/README.md b/modules/backend/README.md index c8b59193..bd6f5555 100644 --- a/modules/backend/README.md +++ b/modules/backend/README.md @@ -7,6 +7,7 @@ This module creates `google_compute_backend_service` resource and its dependenci | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | affinity\_cookie\_ttl\_sec | Lifetime of cookies in seconds if session\_affinity is GENERATED\_COOKIE. | `number` | `null` | no | +| backend\_bucket\_name | The name of GCS bucket which serves the traffic. | `string` | `""` | no | | cdn\_policy | Cloud CDN configuration for this BackendService. |
object({
cache_mode = optional(string)
signed_url_cache_max_age_sec = optional(string)
default_ttl = optional(number)
max_ttl = optional(number)
client_ttl = optional(number)
negative_caching = optional(bool)
serve_while_stale = optional(number)
bypass_cache_on_request_headers = optional(list(string))
negative_caching_policy = optional(object({
code = optional(number)
ttl = optional(number)
}))
cache_key_policy = optional(object({
include_host = optional(bool)
include_protocol = optional(bool)
include_query_string = optional(bool)
query_string_blacklist = optional(list(string))
query_string_whitelist = optional(list(string))
include_http_headers = optional(list(string))
include_named_cookies = optional(list(string))
}))
})
| `{}` | no | | compression\_mode | Compress text responses using Brotli or gzip compression. | `string` | `"DISABLED"` | no | | connection\_draining\_timeout\_sec | Time for which instance will be drained (not accept new connections, but still work to finish started). | `number` | `null` | no | diff --git a/modules/backend/main.tf b/modules/backend/main.tf index bbae368b..ede11825 100644 --- a/modules/backend/main.tf +++ b/modules/backend/main.tf @@ -14,8 +14,13 @@ * limitations under the License. */ +locals { + is_backend_bucket = var.backend_bucket_name != null && var.backend_bucket_name != "" +} + resource "google_compute_backend_service" "default" { provider = google-beta + count = !local.is_backend_bucket ? 1 : 0 project = var.project_id name = var.name @@ -310,3 +315,52 @@ resource "google_compute_firewall" "allow_proxy" { protocol = "tcp" } } + +resource "google_compute_backend_bucket" "default" { + provider = google-beta + count = local.is_backend_bucket ? 1 : 0 + + project = var.project_id + name = var.name + bucket_name = var.backend_bucket_name + enable_cdn = var.enable_cdn + + description = var.description + + # CDN policy configuration, if CDN is enabled + dynamic "cdn_policy" { + for_each = var.enable_cdn ? [1] : [] + content { + cache_mode = var.cdn_policy.cache_mode + signed_url_cache_max_age_sec = var.cdn_policy.signed_url_cache_max_age_sec + default_ttl = var.cdn_policy.default_ttl + max_ttl = var.cdn_policy.max_ttl + client_ttl = var.cdn_policy.client_ttl + negative_caching = var.cdn_policy.negative_caching + serve_while_stale = var.cdn_policy.serve_while_stale + + dynamic "negative_caching_policy" { + for_each = var.cdn_policy.negative_caching_policy != null ? [1] : [] + content { + code = var.cdn_policy.negative_caching_policy.code + ttl = var.cdn_policy.negative_caching_policy.ttl + } + } + + dynamic "cache_key_policy" { + for_each = var.cdn_policy.cache_key_policy != null ? [1] : [] + content { + query_string_whitelist = var.cdn_policy.cache_key_policy.query_string_whitelist + include_http_headers = var.cdn_policy.cache_key_policy.include_http_headers + } + } + + dynamic "bypass_cache_on_request_headers" { + for_each = var.cdn_policy.bypass_cache_on_request_headers != null && try(length(var.cdn_policy.bypass_cache_on_request_headers), 0) > 0 ? toset(var.cdn_policy.bypass_cache_on_request_headers) : [] + content { + header_name = bypass_cache_on_request_headers.value + } + } + } + } +} diff --git a/modules/backend/metadata.yaml b/modules/backend/metadata.yaml index ca4082f6..91525b18 100644 --- a/modules/backend/metadata.yaml +++ b/modules/backend/metadata.yaml @@ -44,6 +44,8 @@ spec: location: examples/cross-project-mig-backend - name: dynamic-backend location: examples/dynamic-backend + - name: external-lb-backend-bucket + location: examples/external-lb-backend-bucket - name: gke-node-port location: examples/https-gke/gke-node-port - name: https-gke @@ -176,6 +178,16 @@ spec: version: ">= 0.13" spec: outputExpr: "{\"region\": location, \"service_name\": service_name, \"type\": \"cloud-run\", \"service_version\": \"\"}" + - name: backend_bucket_name + description: The name of GCS bucket which serves the traffic. + varType: string + defaultValue: "" + connections: + - source: + source: github.com/terraform-google-modules/terraform-google-cloud-storage//modules/simple_bucket + version: ">= 8.0" + spec: + outputExpr: name - name: iap_config description: Settings for enabling Cloud Identity Aware Proxy Structure. varType: |- diff --git a/modules/backend/outputs.tf b/modules/backend/outputs.tf index c8ed10f1..bb09320e 100644 --- a/modules/backend/outputs.tf +++ b/modules/backend/outputs.tf @@ -16,11 +16,17 @@ output "backend_service_info" { description = "Host, path and backend service mapping" - value = [ + value = concat(!local.is_backend_bucket ? [ for mapping in var.host_path_mappings : { host = mapping.host path = mapping.path - backend_service = google_compute_backend_service.default.self_link + backend_service = google_compute_backend_service.default[0].self_link } - ] + ] : [], local.is_backend_bucket ? [for mapping in var.host_path_mappings : { + host = mapping.host + path = mapping.path + backend_service = google_compute_backend_bucket.default[0].self_link + } + ] : [] + ) } diff --git a/modules/backend/variables.tf b/modules/backend/variables.tf index a79cfae3..9c9c0b3c 100644 --- a/modules/backend/variables.tf +++ b/modules/backend/variables.tf @@ -147,6 +147,12 @@ variable "serverless_neg_backends" { } } +variable "backend_bucket_name" { + description = "The name of GCS bucket which serves the traffic." + type = string + default = "" +} + variable "iap_config" { description = "Settings for enabling Cloud Identity Aware Proxy Structure." type = object({ diff --git a/modules/dynamic_backends/metadata.yaml b/modules/dynamic_backends/metadata.yaml index 58de844d..9ba5066a 100644 --- a/modules/dynamic_backends/metadata.yaml +++ b/modules/dynamic_backends/metadata.yaml @@ -44,6 +44,8 @@ spec: location: examples/cross-project-mig-backend - name: dynamic-backend location: examples/dynamic-backend + - name: external-lb-backend-bucket + location: examples/external-lb-backend-bucket - name: gke-node-port location: examples/https-gke/gke-node-port - name: https-gke diff --git a/modules/frontend/metadata.yaml b/modules/frontend/metadata.yaml index 1fa724d1..4526e454 100644 --- a/modules/frontend/metadata.yaml +++ b/modules/frontend/metadata.yaml @@ -44,6 +44,8 @@ spec: location: examples/cross-project-mig-backend - name: dynamic-backend location: examples/dynamic-backend + - name: external-lb-backend-bucket + location: examples/external-lb-backend-bucket - name: gke-node-port location: examples/https-gke/gke-node-port - name: https-gke diff --git a/modules/serverless_negs/metadata.yaml b/modules/serverless_negs/metadata.yaml index 68d6007f..a37e19fa 100644 --- a/modules/serverless_negs/metadata.yaml +++ b/modules/serverless_negs/metadata.yaml @@ -44,6 +44,8 @@ spec: location: examples/cross-project-mig-backend - name: dynamic-backend location: examples/dynamic-backend + - name: external-lb-backend-bucket + location: examples/external-lb-backend-bucket - name: gke-node-port location: examples/https-gke/gke-node-port - name: https-gke diff --git a/test/integration/external-lb-backend-bucket/external_lb_backend_bucket_test.go b/test/integration/external-lb-backend-bucket/external_lb_backend_bucket_test.go new file mode 100644 index 00000000..0f0c56df --- /dev/null +++ b/test/integration/external-lb-backend-bucket/external_lb_backend_bucket_test.go @@ -0,0 +1,38 @@ +// Copyright 2024 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. + +package external_lb_backend_bucket + +import ( + "testing" + + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" + "github.com/stretchr/testify/assert" + + test "github.com/terraform-google-modules/terraform-google-lb-http/test/integration" +) + +func TestExternalLbBackendBucket(t *testing.T) { + bpt := tft.NewTFBlueprintTest(t) + + bpt.DefineVerify(func(assert *assert.Assertions) { + bpt.DefaultVerify(assert) + + loadBalancerIp := bpt.GetStringOutput("load-balancer-ip") + + test.AssertResponseStatus(t, assert, "http://"+loadBalancerIp+"/assets/gcp-logo.svg", 200) + }) + + bpt.Test() +}