diff --git a/mmv1/products/compute/BackendService.yaml b/mmv1/products/compute/BackendService.yaml index 4d2cf6077102..4b3aa5b00b01 100644 --- a/mmv1/products/compute/BackendService.yaml +++ b/mmv1/products/compute/BackendService.yaml @@ -775,6 +775,7 @@ properties: Specifies the default TTL for cached content served by this origin for responses that do not have an existing valid TTL (max-age or s-max-age). default_from_api: true + custom_expand: 'templates/terraform/custom_expand/compute_backend_service_cdn_policy_ttl.go.tmpl' - name: 'maxTtl' type: Integer description: | @@ -785,6 +786,7 @@ properties: description: | Specifies the maximum allowed TTL for cached content served by this origin. default_from_api: true + custom_expand: 'templates/terraform/custom_expand/compute_backend_service_cdn_policy_ttl.go.tmpl' - name: 'negativeCaching' type: Boolean description: | diff --git a/mmv1/products/compute/RegionBackendService.yaml b/mmv1/products/compute/RegionBackendService.yaml index 4ff2bf72e371..a2fc2d5ff44d 100644 --- a/mmv1/products/compute/RegionBackendService.yaml +++ b/mmv1/products/compute/RegionBackendService.yaml @@ -761,6 +761,7 @@ properties: Specifies the default TTL for cached content served by this origin for responses that do not have an existing valid TTL (max-age or s-max-age). default_from_api: true + custom_expand: 'templates/terraform/custom_expand/compute_backend_service_cdn_policy_ttl.go.tmpl' - name: 'maxTtl' type: Integer description: | @@ -771,6 +772,7 @@ properties: description: | Specifies the maximum allowed TTL for cached content served by this origin. default_from_api: true + custom_expand: 'templates/terraform/custom_expand/compute_backend_service_cdn_policy_ttl.go.tmpl' - name: 'negativeCaching' type: Boolean description: | diff --git a/mmv1/templates/terraform/custom_expand/compute_backend_service_cdn_policy_ttl.go.tmpl b/mmv1/templates/terraform/custom_expand/compute_backend_service_cdn_policy_ttl.go.tmpl new file mode 100644 index 000000000000..91aeecc4023d --- /dev/null +++ b/mmv1/templates/terraform/custom_expand/compute_backend_service_cdn_policy_ttl.go.tmpl @@ -0,0 +1,56 @@ +{{/* + The license inside this block applies to this file + Copyright 2024 Google LLC. All Rights Reserved. + + 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. +*/ -}} + +// expand{{$.GetPrefix}}{{$.TitlelizeProperty}} uses raw config inspection so that an explicitly +// configured `{{ underscore $.Name }} = 0` reaches the API, while an unconfigured field stays out +// of the request entirely. +// +// `{{ underscore $.Name }}` is Optional + Computed, and 0 is the zero value for ints in Terraform +// SDKv2, so resource data alone cannot distinguish "the practitioner asked for 0" from "the +// practitioner said nothing". Neither of the two simple options works: +// +// - Omitting every 0 (the default expander behavior) drops a configured 0, the API substitutes +// its own default, and the next plan shows a permanent diff. +// - Sending every 0 (send_empty_value) makes unconfigured TTLs explicit, which both overrides the +// API-side default and breaks cache modes that forbid TTLs, e.g. the API rejects +// "default_ttl cannot be specified with USE_ORIGIN_HEADERS cache_mode". +// +// Returning a *int64 threads the needle: a non-nil pointer is not an empty value, so a configured 0 +// is included in the request, while nil leaves the field out. +func expand{{$.GetPrefix}}{{$.TitlelizeProperty}}(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + ttl, ok := v.(int) + if !ok { + return nil, nil + } + if ttl != 0 { + return ttl, nil + } +{{- if not (contains $.ResourceMetadata.ProductMetadata.Compiler "terraformgoogleconversion") }} + + // A 0 is only meaningful when it came from the configuration rather than from the zero value of + // an unset field. + if rd, ok := d.(*schema.ResourceData); ok { + path := cty.GetAttrPath("cdn_policy").IndexInt(0).GetAttr("{{ underscore $.Name }}") + if val, _ := rd.GetRawConfigAt(path); !val.IsNull() && val.IsKnown() { + zero := int64(0) + return &zero, nil + } + } +{{- end }} + + return nil, nil +} diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_backend_service_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_backend_service_test.go.tmpl index e12f7cc3b798..0438b91e9d6b 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_backend_service_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_backend_service_test.go.tmpl @@ -1938,9 +1938,17 @@ func testAccComputeBackendService_withCdnPolicy(serviceName, checkName string) s resource "google_compute_backend_service" "foobar" { name = "%s" health_checks = [google_compute_http_health_check.zero.self_link] + enable_cdn = true cdn_policy { - negative_caching = false + // client_ttl and default_ttl are explicitly 0 here. They are Optional + Computed, so an + // explicit 0 has to be told apart from an unset field: dropping it makes the API apply its own + // default and produces a permadiff, while sending it unconditionally breaks cache modes that + // forbid TTLs. testAccComputeBackendService_withCdnPolicyUseOriginHeaders covers the unset side. + cache_mode = "CACHE_ALL_STATIC" + client_ttl = 0 + default_ttl = 0 + negative_caching = false serve_while_stale = 0 cache_key_policy { include_protocol = true