diff --git a/mmv1/products/cloudrunv2/Service.yaml b/mmv1/products/cloudrunv2/Service.yaml index 4d0cdfc0d04b..c0d7c5ae63a2 100644 --- a/mmv1/products/cloudrunv2/Service.yaml +++ b/mmv1/products/cloudrunv2/Service.yaml @@ -644,6 +644,7 @@ properties: description: |- Only memory, CPU, and nvidia.com/gpu are supported. Use key `cpu` for CPU limit, `memory` for memory limit, `nvidia.com/gpu` for gpu limit. Note: The only supported values for CPU are '1', '2', '4', '6' and '8'. Setting 4 CPU requires at least 2Gi of memory, setting 6 or more CPU requires at least 4Gi of memory. The values of the map is string form of the 'quantity' k8s type: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go default_from_api: true + diff_suppress_func: 'cloudRunV2ServiceResourceLimitsDiffSuppress' - name: 'cpuIdle' type: Boolean description: |- diff --git a/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_test.go.tmpl b/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_test.go.tmpl index 571ccd839478..f7ccd167505c 100644 --- a/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_test.go.tmpl +++ b/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_test.go.tmpl @@ -2082,3 +2082,60 @@ resource "google_cloud_run_v2_service" "default" { } `, context) } + +// TestAccCloudRunV2Service_noPermadiffWithMemoryLimitOnly verifies that setting +// only the memory key in template.containers.resources.limits does not produce a +// perpetual diff. The API automatically adds a default cpu limit which should be +// silently accepted. Regression test for: +// https://github.com/hashicorp/terraform-provider-google/issues/20399 +func TestAccCloudRunV2Service_noPermadiffWithMemoryLimitOnly(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_memoryLimitOnly(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location", "annotations", "labels", "terraform_labels", "deletion_protection"}, + }, + // Second plan must produce no diff (no permadiff on cpu limit key). + { + Config: testAccCloudRunV2Service_memoryLimitOnly(context), + PlanOnly: true, + }, + }, + }) +} + +func testAccCloudRunV2Service_memoryLimitOnly(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + deletion_protection = false + ingress = "INGRESS_TRAFFIC_ALL" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + resources { + limits = { + memory = "1Gi" + } + } + } + } +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_utils.go b/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_utils.go new file mode 100644 index 000000000000..b29715b81904 --- /dev/null +++ b/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_utils.go @@ -0,0 +1,25 @@ +package cloudrunv2 + +import ( + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// cloudRunV2ServiceResourceLimitsDiffSuppress suppresses diffs for the `cpu` +// key in the container resources.limits map when it was added by the API but +// not specified in the user's config. When only `memory` is set, the Cloud Run +// API automatically populates a default `cpu` value. Without this suppression +// the provider would produce a perpetual diff trying to clear that API-set key. +// +// Only the `cpu` key is suppressed because it is the only key the API +// auto-populates. Other keys such as `nvidia.com/gpu` must never be suppressed +// so that intentional removals (e.g. switching from GPU to non-GPU) are applied. +func cloudRunV2ServiceResourceLimitsDiffSuppress(k, old, new string, d *schema.ResourceData) bool { + // Only suppress the auto-populated cpu key, not user-set keys like nvidia.com/gpu. + if !strings.HasSuffix(k, "limits.cpu") { + return false + } + // Suppress when the user did not set cpu (new == "") but the API populated it (old != ""). + return new == "" && old != "" +}