diff --git a/blueprints/cloud-operations/network-quota-monitoring/deploy-cloudrun-job/main.tf b/blueprints/cloud-operations/network-quota-monitoring/deploy-cloudrun-job/main.tf index 478e17d54..038f72510 100644 --- a/blueprints/cloud-operations/network-quota-monitoring/deploy-cloudrun-job/main.tf +++ b/blueprints/cloud-operations/network-quota-monitoring/deploy-cloudrun-job/main.tf @@ -68,7 +68,7 @@ module "cr-job" { project_id = module.project.project_id name = var.name region = var.region - create_job = true + type = "JOB" containers = { netmon = { image = "${module.ar.url}/${var.name}" @@ -97,10 +97,8 @@ module "cr-job" { module.sa-invoker.iam_email ] } - revision = { - job = { - max_retries = 0 - } + job_config = { + max_retries = 0 } service_account = module.sa.email deletion_protection = false diff --git a/blueprints/networking/psc-glb-and-armor/modules/producer/main.tf b/blueprints/networking/psc-glb-and-armor/modules/producer/main.tf index 3e25d9b14..ba199c7c0 100644 --- a/blueprints/networking/psc-glb-and-armor/modules/producer/main.tf +++ b/blueprints/networking/psc-glb-and-armor/modules/producer/main.tf @@ -43,7 +43,9 @@ module "app" { } } } - ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" + service_config = { + ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" + } service_account_create = true } diff --git a/blueprints/serverless/cloud-run-microservices/cloudrun.tf b/blueprints/serverless/cloud-run-microservices/cloudrun.tf index 0c40af619..657dd2299 100644 --- a/blueprints/serverless/cloud-run-microservices/cloudrun.tf +++ b/blueprints/serverless/cloud-run-microservices/cloudrun.tf @@ -24,7 +24,9 @@ module "cloud-run-svc-a" { project_id = module.main-project.project_id name = local.svc_a_name region = var.region - ingress = "INGRESS_TRAFFIC_ALL" + service_config = { + ingress = "INGRESS_TRAFFIC_ALL" + } # Direct VPC Egress is currently in Beta. Checking its use to avoid permadiff, # when in GA it will need to be removed to avoid permadiff again. launch_stage = local.two_projects == true ? "BETA" : "GA" @@ -62,7 +64,9 @@ module "cloud-run-svc-b" { project_id = try(module.service-project[0].project_id, module.main-project.project_id) name = local.svc_b_name region = var.region - ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY" + service_config = { + ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY" + } containers = { default = { image = var.image_configs.svc_b diff --git a/modules/cloud-run-v2/README.md b/modules/cloud-run-v2/README.md index 86c4f1a68..9bbb4408e 100644 --- a/modules/cloud-run-v2/README.md +++ b/modules/cloud-run-v2/README.md @@ -56,7 +56,7 @@ module "cloud_run" { } deletion_protection = false } -# tftest modules=2 resources=5 fixtures=fixtures/secret-credentials.tf inventory=service-iam-env.yaml e2e +# tftest fixtures=fixtures/secret-credentials.tf inventory=service-iam-env.yaml e2e ``` ## Mounting secrets as volumes @@ -86,7 +86,7 @@ module "cloud_run" { } deletion_protection = false } -# tftest modules=2 resources=4 fixtures=fixtures/secret-credentials.tf inventory=service-volume-secretes.yaml e2e +# tftest fixtures=fixtures/secret-credentials.tf inventory=service-volume-secretes.yaml e2e ``` ## Mounting GCS buckets @@ -105,7 +105,7 @@ module "cloud_run" { } } } - revision = { + service_config = { gen2_execution_environment = true } volumes = { @@ -165,18 +165,20 @@ module "cloud_run" { } } revision = { - gen2_execution_environment = true - max_instance_count = 20 vpc_access = { egress = "ALL_TRAFFIC" subnet = var.subnet.name tags = ["tag1", "tag2", "tag3"] } } + service_config = { + gen2_execution_environment = true + max_instance_count = 20 + } deletion_protection = false } # E2E test disabled due to b/332419038 -# tftest modules=1 resources=1 inventory=service-direct-vpc.yaml +# tftest inventory=service-direct-vpc.yaml ``` ## VPC Access Connector @@ -194,7 +196,7 @@ module "cloud_run" { image = "us-docker.pkg.dev/cloudrun/container/hello" } } - revision = { + service_config = { vpc_access = { connector = google_vpc_access_connector.connector.id egress = "ALL_TRAFFIC" @@ -205,7 +207,7 @@ module "cloud_run" { # tftest modules=1 resources=2 fixtures=fixtures/vpc-connector.tf inventory=service-vpc-access-connector.yaml e2e ``` -If creation of the VPC Access Connector is required, use the `vpc_connector_create` variable which also supports optional attributes like number of instances, machine type, or throughput. The connector will be used automatically. +If creation of the VPC Access Connector is required, use the `vpc_connector_create` variable which also supports optional attributes like number of instances, machine type, or throughput. The connector will be used automatically by Cloud Run Service and Job. Worker Pool does not support connector. ```hcl module "cloud_run" { @@ -228,7 +230,7 @@ module "cloud_run" { } deletion_protection = false } -# tftest modules=1 resources=2 inventory=service-vpc-access-connector-create.yaml e2e +# tftest inventory=service-vpc-access-connector-create.yaml e2e ``` Note that if you are using a Shared VPC for the connector, you need to specify a subnet and the host project if this is not where the Cloud Run service is deployed. @@ -257,7 +259,7 @@ module "cloud_run" { } deletion_protection = false } -# tftest modules=4 resources=59 fixtures=fixtures/shared-vpc.tf inventory=service-vpc-access-connector-create-sharedvpc.yaml e2e +# tftest fixtures=fixtures/shared-vpc.tf inventory=service-vpc-access-connector-create-sharedvpc.yaml e2e ``` ## Using Customer-Managed Encryption Key @@ -554,7 +556,7 @@ module "cloud_run" { } deletion_protection = false } -# tftest modules=2 resources=4 files=otel-config inventory=service-otel-sidecar.yaml e2e +# tftest files=otel-config inventory=service-otel-sidecar.yaml e2e ``` ## Eventarc triggers @@ -574,14 +576,16 @@ module "cloud_run" { image = "us-docker.pkg.dev/cloudrun/container/hello" } } - eventarc_triggers = { - pubsub = { - topic-1 = module.pubsub.topic.name + service_config = { + eventarc_triggers = { + pubsub = { + topic-1 = module.pubsub.topic.name + } } } deletion_protection = false } -# tftest modules=2 resources=4 fixtures=fixtures/pubsub.tf inventory=service-eventarc-pubsub.yaml e2e +# tftest fixtures=fixtures/pubsub.tf inventory=service-eventarc-pubsub.yaml e2e ``` ### Audit logs @@ -599,18 +603,20 @@ module "cloud_run" { image = "us-docker.pkg.dev/cloudrun/container/hello" } } - eventarc_triggers = { - audit_log = { - setiampolicy = { - method = "SetIamPolicy" - service = "cloudresourcemanager.googleapis.com" + service_config = { + eventarc_triggers = { + audit_log = { + setiampolicy = { + method = "SetIamPolicy" + service = "cloudresourcemanager.googleapis.com" + } } + service_account_create = true } - service_account_create = true } deletion_protection = false } -# tftest modules=1 resources=4 inventory=service-eventarc-auditlogs-sa-create.yaml +# tftest inventory=service-eventarc-auditlogs-sa-create.yaml ``` ### GCS bucket @@ -628,17 +634,19 @@ module "cloud_run" { image = "us-docker.pkg.dev/cloudrun/container/hello" } } - eventarc_triggers = { - storage = { - bucket-upload = { - bucket = module.gcs.name - path = "/webhook" # optional: URL path for the Cloud Run service + service_config = { + eventarc_triggers = { + storage = { + bucket-upload = { + bucket = module.gcs.name + path = "/webhook" # optional: URL path for the Cloud Run service + } } } } deletion_protection = false } -# tftest modules=2 resources=4 fixtures=fixtures/gcs.tf inventory=service-eventarc-storage.yaml e2e +# tftest fixtures=fixtures/gcs.tf inventory=service-eventarc-storage.yaml e2e ``` ### Using custom service accounts for triggers @@ -658,17 +666,19 @@ module "cloud_run" { image = "us-docker.pkg.dev/cloudrun/container/hello" } } - eventarc_triggers = { - audit_log = { - setiampolicy = { - method = "SetIamPolicy" - service = "cloudresourcemanager.googleapis.com" + service_config = { + eventarc_triggers = { + audit_log = { + setiampolicy = { + method = "SetIamPolicy" + service = "cloudresourcemanager.googleapis.com" + } } + service_account_email = "cloud-run-trigger@my-project.iam.gserviceaccount.com" } - service_account_email = "cloud-run-trigger@my-project.iam.gserviceaccount.com" } } -# tftest modules=1 resources=2 inventory=service-eventarc-auditlogs-external-sa.yaml +# tftest inventory=service-eventarc-auditlogs-external-sa.yaml ``` Example using automatically created service account: @@ -684,15 +694,17 @@ module "cloud_run" { image = "us-docker.pkg.dev/cloudrun/container/hello" } } - eventarc_triggers = { - pubsub = { - topic-1 = module.pubsub.topic.name + service_config = { + eventarc_triggers = { + pubsub = { + topic-1 = module.pubsub.topic.name + } + service_account_create = true } - service_account_create = true } deletion_protection = false } -# tftest modules=2 resources=6 fixtures=fixtures/pubsub.tf inventory=service-eventarc-pubsub-sa-create.yaml e2e +# tftest fixtures=fixtures/pubsub.tf inventory=service-eventarc-pubsub-sa-create.yaml e2e ``` Example using automatically created service account for storage triggers: @@ -708,14 +720,16 @@ module "cloud_run" { image = "us-docker.pkg.dev/cloudrun/container/hello" } } - eventarc_triggers = { - storage = { - bucket-upload = { - bucket = module.gcs.name - path = "/webhook" # optional: URL path for the Cloud Run service + service_config = { + eventarc_triggers = { + storage = { + bucket-upload = { + bucket = module.gcs.name + path = "/webhook" # optional: URL path for the Cloud Run service + } } + service_account_create = true } - service_account_create = true } deletion_protection = false } @@ -737,10 +751,12 @@ module "cloud_run" { image = "us-docker.pkg.dev/cloudrun/container/hello" } } - invoker_iam_disabled = true - deletion_protection = false + service_config = { + invoker_iam_disabled = true + } + deletion_protection = false } -# tftest modules=1 resources=1 inventory=service-invoker-iam-disable.yaml e2e +# tftest inventory=service-invoker-iam-disable.yaml e2e ``` ## Cloud Run Service Account @@ -761,7 +777,7 @@ module "cloud_run" { service_account_create = true deletion_protection = false } -# tftest modules=1 resources=2 inventory=service-sa-create.yaml e2e +# tftest inventory=service-sa-create.yaml e2e ``` To use an externally managed service account, use its email in `service_account` and leave `service_account_create` to `false` (default). @@ -785,7 +801,7 @@ module "cloud_run" { ## Creating Cloud Run Jobs -To create a job instead of service set `create_job` to `true`. Jobs support all functions above apart from triggers. +To create a job instead of service set `type` to `JOB`. Jobs support all functions above apart from triggers. Unsupported variables / attributes: @@ -797,13 +813,18 @@ Unsupported variables / attributes: - containers.resources.cpu_idle - containers.resources.startup_cpu_boost +Additional configuration can be passwed as `job_config`: +* max_retries - maximum of retries per task +* task_count - desired number of tasks +* timeout - max allowed time per task, in seconds with up to nine fractional digits, ending with 's'. Example: `3.5s` + ```hcl module "cloud_run" { source = "./fabric/modules/cloud-run-v2" project_id = var.project_id name = "hello" region = var.region - create_job = true + type = "JOB" containers = { hello = { image = "us-docker.pkg.dev/cloudrun/container/hello" @@ -819,19 +840,26 @@ module "cloud_run" { deletion_protection = false } -# tftest modules=1 resources=2 inventory=job-iam-env.yaml e2e +# tftest inventory=job-iam-env.yaml e2e ``` ## Tag bindings -Tag bindings are not yet supported for jobs. Refer to the [Creating and managing tags](https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing) documentation for details on usage. +Tag bindings are not yet supported for Worker Pool. Refer to the [Creating and managing tags](https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing) documentation for details on usage. ```hcl -module "org" { - source = "./fabric/modules/organization" - organization_id = var.organization_id +module "project" { + source = "./fabric/modules/project" + name = var.project_id + project_reuse = { + use_data_source = false + attributes = { + name = var.project_id + number = var.project_number + } + } tags = { - environment = { + run_environment = { description = "Environment specification." values = { dev = {} @@ -842,85 +870,92 @@ module "org" { } } -module "cloud_run" { +module "cloud_run_service" { source = "./fabric/modules/cloud-run-v2" project_id = var.project_id - name = "hello" + name = "hello-service" region = var.region containers = { hello = { image = "us-docker.pkg.dev/cloudrun/container/hello" - env = { - VAR1 = "VALUE1" - VAR2 = "VALUE2" - } } } - iam = { - "roles/run.invoker" = ["allUsers"] + tag_bindings = { + env-sandbox = module.project.tag_values["run_environment/sandbox"].id + } + deletion_protection = false +} + +module "cloud_run_job" { + source = "./fabric/modules/cloud-run-v2" + project_id = var.project_id + name = "hello-job" + region = var.region + type = "JOB" + containers = { + hello = { + image = "us-docker.pkg.dev/cloudrun/container/hello" + } } tag_bindings = { - env-sandbox = module.org.tag_values["environment/sandbox"].id + env-sandbox = module.project.tag_values["run_environment/sandbox"].id } + deletion_protection = false } -# tftest modules=2 resources=7 + +# tftest inventory=tags.yaml e2e ``` ## IAP Configuration -IAP is only supported for service. Refer to the [Configure IAP directly on cloud run](https://cloud.google.com/run/docs/securing/identity-aware-proxy-cloud-run) documentation for details on usage. +IAP is only supported for service. Refer to the [Configure IAP directly on cloud run](https://cloud.google.com/run/docs/securing/identity-aware-proxy-cloud-run) documentation for details on usage. ```hcl module "cloud_run" { - source = "./fabric/modules/cloud-run-v2" - project_id = var.project_id - name = "hello" - region = var.region + source = "./fabric/modules/cloud-run-v2" + project_id = var.project_id + name = "hello" + region = var.region + launch_stage = "BETA" containers = { hello = { image = "us-docker.pkg.dev/cloudrun/container/hello" - env = { - VAR1 = "VALUE1" - VAR2 = "VALUE2" - } } } - - iap_config = { - iam = ["group:abc@domain.com"] + service_config = { + iap_config = { + iam = ["group:${var.group_email}"] + } } - + deletion_protection = false } -# tftest modules=1 resources=2 +# tftest modules=1 resources=2 e2e ``` ## Variables | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L210) | Name used for Cloud Run service. | string | ✓ | | -| [project_id](variables.tf#L225) | Project id used for all resources. | string | ✓ | | -| [region](variables.tf#L230) | Region used for all resources. | string | ✓ | | +| [name](variables.tf#L143) | Name used for Cloud Run service. | string | ✓ | | +| [project_id](variables.tf#L148) | Project id used for all resources. | string | ✓ | | +| [region](variables.tf#L153) | Region used for all resources. | string | ✓ | | | [containers](variables.tf#L17) | Containers in name => attributes format. | map(object({…})) | | {} | -| [create_job](variables.tf#L80) | Create Cloud Run Job instead of Service. | bool | | false | -| [custom_audiences](variables.tf#L86) | Custom audiences for service. | list(string) | | null | -| [deletion_protection](variables.tf#L92) | Deletion protection setting for this Cloud Run service. | string | | null | -| [encryption_key](variables.tf#L98) | The full resource name of the Cloud KMS CryptoKey. | string | | null | -| [eventarc_triggers](variables.tf#L104) | Event arc triggers for different sources. | object({…}) | | {} | -| [iam](variables.tf#L126) | IAM bindings for Cloud Run service in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| [iap_config](variables.tf#L132) | If present, turns on Identity-Aware Proxy (IAP) for the Cloud Run service. | object({…}) | | null | -| [ingress](variables.tf#L157) | Ingress settings. | string | | null | -| [invoker_iam_disabled](variables.tf#L174) | Disables IAM permission check for run.routes.invoke for callers of this service. | bool | | false | -| [labels](variables.tf#L180) | Resource labels. | map(string) | | {} | -| [launch_stage](variables.tf#L186) | The launch stage as defined by Google Cloud Platform Launch Stages. | string | | null | -| [managed_revision](variables.tf#L203) | Whether the Terraform module should control the deployment of revisions. | bool | | true | -| [prefix](variables.tf#L215) | Optional prefix used for resource names. | string | | null | -| [revision](variables.tf#L235) | Revision template configurations. | object({…}) | | {} | -| [service_account](variables.tf#L274) | Service account email. Unused if service account is auto-created. | string | | null | -| [service_account_create](variables.tf#L280) | Auto-create service account. | bool | | false | -| [tag_bindings](variables.tf#L286) | Tag bindings for this service, in key => tag value id format. | map(string) | | {} | -| [volumes](variables.tf#L293) | Named volumes in containers in name => attributes format. | map(object({…})) | | {} | +| [deletion_protection](variables.tf#L80) | Deletion protection setting for this Cloud Run service. | string | | null | +| [encryption_key](variables.tf#L86) | The full resource name of the Cloud KMS CryptoKey. | string | | null | +| [iam](variables.tf#L92) | IAM bindings for Cloud Run service in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| [job_config](variables.tf#L98) | Cloud Run Job specific configuration. | object({…}) | | {} | +| [labels](variables.tf#L113) | Resource labels. | map(string) | | {} | +| [launch_stage](variables.tf#L119) | The launch stage as defined by Google Cloud Platform Launch Stages. | string | | null | +| [managed_revision](variables.tf#L136) | Whether the Terraform module should control the deployment of revisions. | bool | | true | +| [revision](variables.tf#L158) | Revision template configurations. | object({…}) | | {} | +| [service_account](variables.tf#L214) | Service account email. Unused if service account is auto-created. | string | | null | +| [service_account_create](variables.tf#L220) | Auto-create service account. | bool | | false | +| [service_config](variables.tf#L226) | Cloud Run service specific configuration options. | object({…}) | | {} | +| [tag_bindings](variables.tf#L290) | Tag bindings for this service, in key => tag value id format. | map(string) | | {} | +| [type](variables.tf#L297) | Type of Cloud Run resource to deploy: JOB, SERVICE or WORKERPOOL. | string | | "SERVICE" | +| [volumes](variables.tf#L307) | Named volumes in containers in name => attributes format. | map(object({…})) | | {} | | [vpc_connector_create](variables-vpcconnector.tf#L17) | Populate this to create a Serverless VPC Access connector. | object({…}) | | null | +| [workerpool_config](variables.tf#L341) | Cloud Run Worker Pool specific configuration. | object({…}) | | {} | ## Outputs @@ -928,14 +963,16 @@ module "cloud_run" { |---|---|:---:| | [id](outputs.tf#L17) | Fully qualified job or service id. | | | [invoke_command](outputs.tf#L22) | Command to invoke Cloud Run Service / submit job. | | -| [job](outputs.tf#L40) | Cloud Run Job. | | -| [service](outputs.tf#L45) | Cloud Run Service. | | -| [service_account](outputs.tf#L50) | Service account resource. | | -| [service_account_email](outputs.tf#L55) | Service account email. | | -| [service_account_iam_email](outputs.tf#L60) | Service account email. | | -| [service_name](outputs.tf#L68) | Cloud Run service name. | | -| [service_uri](outputs.tf#L73) | Main URI in which the service is serving traffic. | | -| [vpc_connector](outputs.tf#L78) | VPC connector resource if created. | | +| [job](outputs.tf#L27) | Cloud Run Job. | | +| [resource](outputs.tf#L32) | Cloud Run resource (job, service or worker_pool). | | +| [resource_name](outputs.tf#L37) | Cloud Run resource (job, service or workerpool) service name. | | +| [service](outputs.tf#L42) | Cloud Run Service. | | +| [service_account](outputs.tf#L46) | Service account resource. | | +| [service_account_email](outputs.tf#L51) | Service account email. | | +| [service_account_iam_email](outputs.tf#L56) | Service account email. | | +| [service_name](outputs.tf#L64) | Cloud Run service name. | | +| [service_uri](outputs.tf#L69) | Main URI in which the service is serving traffic. | | +| [vpc_connector](outputs.tf#L74) | VPC connector resource if created. | | ## Fixtures diff --git a/modules/cloud-run-v2/job.tf b/modules/cloud-run-v2/job.tf index 3d3995440..0cb87c2eb 100644 --- a/modules/cloud-run-v2/job.tf +++ b/modules/cloud-run-v2/job.tf @@ -15,21 +15,17 @@ */ resource "google_cloud_run_v2_job" "job" { - count = ( - var.create_job && - var.managed_revision - ? 1 : 0 - ) + count = var.type == "JOB" && var.managed_revision ? 1 : 0 provider = google-beta project = var.project_id location = var.region - name = "${local.prefix}${var.name}" + name = var.name labels = var.labels launch_stage = var.launch_stage deletion_protection = var.deletion_protection template { labels = var.revision.labels - task_count = var.revision.job.task_count + task_count = var.job_config.task_count template { encryption_key = var.encryption_key dynamic "vpc_access" { @@ -50,16 +46,17 @@ resource "google_cloud_run_v2_job" "job" { } } } - max_retries = var.revision.job.max_retries - timeout = var.revision.timeout + max_retries = var.job_config.max_retries + timeout = var.job_config.timeout service_account = local.service_account_email dynamic "containers" { for_each = var.containers content { - name = containers.key - image = containers.value.image - command = containers.value.command - args = containers.value.args + name = containers.key + image = containers.value.image + depends_on = containers.value.depends_on + command = containers.value.command + args = containers.value.args dynamic "env" { for_each = coalesce(containers.value.env, tomap({})) content { @@ -107,6 +104,42 @@ resource "google_cloud_run_v2_job" "job" { mount_path = volume_mounts.value } } + dynamic "startup_probe" { + for_each = containers.value.startup_probe == null ? [] : [""] + content { + initial_delay_seconds = containers.value.startup_probe.initial_delay_seconds + timeout_seconds = containers.value.startup_probe.timeout_seconds + period_seconds = containers.value.startup_probe.period_seconds + failure_threshold = containers.value.startup_probe.failure_threshold + dynamic "http_get" { + for_each = containers.value.startup_probe.http_get == null ? [] : [""] + content { + path = containers.value.startup_probe.http_get.path + port = containers.value.startup_probe.http_get.port + dynamic "http_headers" { + for_each = coalesce(containers.value.startup_probe.http_get.http_headers, tomap({})) + content { + name = http_headers.key + value = http_headers.value + } + } + } + } + dynamic "tcp_socket" { + for_each = containers.value.startup_probe.tcp_socket == null ? [] : [""] + content { + port = containers.value.startup_probe.tcp_socket.port + } + } + dynamic "grpc" { + for_each = containers.value.startup_probe.grpc == null ? [] : [""] + content { + port = containers.value.startup_probe.grpc.port + service = containers.value.startup_probe.grpc.service + } + } + } + } } } dynamic "volumes" { @@ -177,21 +210,17 @@ resource "google_cloud_run_v2_job" "job" { } resource "google_cloud_run_v2_job" "job_unmanaged" { - count = ( - var.create_job && - !var.managed_revision - ? 1 : 0 - ) + count = var.type == "JOB" && !var.managed_revision ? 1 : 0 provider = google-beta project = var.project_id location = var.region - name = "${local.prefix}${var.name}" + name = var.name labels = var.labels launch_stage = var.launch_stage deletion_protection = var.deletion_protection template { labels = var.revision.labels - task_count = var.revision.job.task_count + task_count = var.job_config.task_count template { encryption_key = var.encryption_key dynamic "vpc_access" { @@ -212,16 +241,17 @@ resource "google_cloud_run_v2_job" "job_unmanaged" { } } } - max_retries = var.revision.job.max_retries - timeout = var.revision.timeout + max_retries = var.job_config.max_retries + timeout = var.job_config.timeout service_account = local.service_account_email dynamic "containers" { for_each = var.containers content { - name = containers.key - image = containers.value.image - command = containers.value.command - args = containers.value.args + name = containers.key + image = containers.value.image + depends_on = containers.value.depends_on + command = containers.value.command + args = containers.value.args dynamic "env" { for_each = coalesce(containers.value.env, tomap({})) content { @@ -269,6 +299,42 @@ resource "google_cloud_run_v2_job" "job_unmanaged" { mount_path = volume_mounts.value } } + dynamic "startup_probe" { + for_each = containers.value.startup_probe == null ? [] : [""] + content { + initial_delay_seconds = containers.value.startup_probe.initial_delay_seconds + timeout_seconds = containers.value.startup_probe.timeout_seconds + period_seconds = containers.value.startup_probe.period_seconds + failure_threshold = containers.value.startup_probe.failure_threshold + dynamic "http_get" { + for_each = containers.value.startup_probe.http_get == null ? [] : [""] + content { + path = containers.value.startup_probe.http_get.path + port = containers.value.startup_probe.http_get.port + dynamic "http_headers" { + for_each = coalesce(containers.value.startup_probe.http_get.http_headers, tomap({})) + content { + name = http_headers.key + value = http_headers.value + } + } + } + } + dynamic "tcp_socket" { + for_each = containers.value.startup_probe.tcp_socket == null ? [] : [""] + content { + port = containers.value.startup_probe.tcp_socket.port + } + } + dynamic "grpc" { + for_each = containers.value.startup_probe.grpc == null ? [] : [""] + content { + port = containers.value.startup_probe.grpc.port + service = containers.value.startup_probe.grpc.service + } + } + } + } } } dynamic "volumes" { @@ -343,10 +409,10 @@ resource "google_cloud_run_v2_job" "job_unmanaged" { } resource "google_cloud_run_v2_job_iam_binding" "binding" { - for_each = var.create_job ? var.iam : {} - project = local.service.project - location = local.service.location - name = local.service.name + for_each = var.type == "JOB" ? var.iam : {} + project = local.resource.project + location = local.resource.location + name = local.resource.name role = each.key members = each.value } diff --git a/modules/cloud-run-v2/main.tf b/modules/cloud-run-v2/main.tf index d92a1697b..c17f417ad 100644 --- a/modules/cloud-run-v2/main.tf +++ b/modules/cloud-run-v2/main.tf @@ -20,42 +20,60 @@ locals { ? google_vpc_access_connector.connector[0].id : try(var.revision.vpc_access.connector, null) ) - prefix = var.prefix == null ? "" : "${var.prefix}-" + _invoke_command = { + JOB = <<-EOT + gcloud run jobs execute \ + --project ${var.project_id} \ + --region ${var.region} \ + --wait ${local.resource.name} \ + --args= + EOT + WORKERPOOL = "" + SERVICE = <<-EOT + curl -H "Authorization: bearer $(gcloud auth print-identity-token)" \ + ${local.resource.uri} \ + -X POST -d 'data' + EOT + } + invoke_command = local._invoke_command[var.type] + revision_name = ( var.revision.name == null ? null : "${var.name}-${var.revision.name}" ) service_account_email = ( var.service_account_create - ? ( - length(google_service_account.service_account) > 0 - ? google_service_account.service_account[0].email - : null - ) + ? google_service_account.service_account[0].email : var.service_account ) - service = ( - var.create_job - ? ( - var.managed_revision - ? google_cloud_run_v2_job.job[0] - : google_cloud_run_v2_job.job_unmanaged[0] + _resource = { + "JOB" : ( + var.managed_revision ? + try(google_cloud_run_v2_job.job[0], null) : try(google_cloud_run_v2_job.job_unmanaged[0], null) ) - : ( - var.managed_revision - ? google_cloud_run_v2_service.service[0] - : google_cloud_run_v2_service.service_unmanaged[0] + "WORKERPOOL" : ( + var.managed_revision ? + try(google_cloud_run_v2_worker_pool.default_managed[0], null) : try(google_cloud_run_v2_worker_pool.default_unmanaged[0], null) ) - ) + "SERVICE" : ( + var.managed_revision ? + try(google_cloud_run_v2_service.service[0], null) : try(google_cloud_run_v2_service.service_unmanaged[0], null) + ) + } + resource = { + id = local._resource[var.type].id + location = local._resource[var.type].location + name = local._resource[var.type].name + project = local._resource[var.type].project + uri = var.type == "SERVICE" ? local._resource[var.type].uri : "" + } trigger_sa_create = try( - var.eventarc_triggers.service_account_create, false + var.service_config.eventarc_triggers.service_account_create, false ) trigger_sa_email = try( google_service_account.trigger_service_account[0].email, - var.eventarc_triggers.service_account_email, + var.service_config.eventarc_triggers.service_account_email, null ) - - iap_enabled = var.iap_config != null } resource "google_cloud_run_v2_service_iam_member" "default" { @@ -80,8 +98,8 @@ resource "google_service_account" "service_account" { } resource "google_eventarc_trigger" "audit_log_triggers" { - for_each = coalesce(var.eventarc_triggers.audit_log, tomap({})) - name = "${local.prefix}audit-log-${each.key}" + for_each = coalesce(var.service_config.eventarc_triggers.audit_log, tomap({})) + name = "audit-log-${each.key}" location = google_cloud_run_v2_service.service[0].location project = google_cloud_run_v2_service.service[0].project matching_criteria { @@ -106,8 +124,8 @@ resource "google_eventarc_trigger" "audit_log_triggers" { } resource "google_eventarc_trigger" "pubsub_triggers" { - for_each = coalesce(var.eventarc_triggers.pubsub, tomap({})) - name = "${local.prefix}pubsub-${each.key}" + for_each = coalesce(var.service_config.eventarc_triggers.pubsub, tomap({})) + name = "pubsub-${each.key}" location = google_cloud_run_v2_service.service[0].location project = google_cloud_run_v2_service.service[0].project matching_criteria { @@ -129,8 +147,8 @@ resource "google_eventarc_trigger" "pubsub_triggers" { } resource "google_eventarc_trigger" "storage_triggers" { - for_each = coalesce(var.eventarc_triggers.storage, tomap({})) - name = "${local.prefix}storage-${each.key}" + for_each = coalesce(var.service_config.eventarc_triggers.storage, tomap({})) + name = "storage-${each.key}" location = google_cloud_run_v2_service.service[0].location project = google_cloud_run_v2_service.service[0].project matching_criteria { diff --git a/modules/cloud-run-v2/outputs.tf b/modules/cloud-run-v2/outputs.tf index 47e28b44d..6f3d56318 100644 --- a/modules/cloud-run-v2/outputs.tf +++ b/modules/cloud-run-v2/outputs.tf @@ -16,37 +16,33 @@ output "id" { description = "Fully qualified job or service id." - value = local.service.id + value = local.resource.id } output "invoke_command" { description = "Command to invoke Cloud Run Service / submit job." - value = ( - var.create_job ? <<-EOT - gcloud run jobs execute \ - --project ${var.project_id} \ - --region ${var.region} \ - --wait ${local.service.name} \ - --args= - EOT - : <<-EOT - curl -H "Authorization: bearer $(gcloud auth print-identity-token)" \ - ${local.service.uri} \ - -X POST -d 'data' - EOT - ) + value = local.invoke_command } output "job" { description = "Cloud Run Job." - value = var.create_job ? local.service : null + value = var.type == "JOB" ? local._resource[var.type] : null +} + +output "resource" { + description = "Cloud Run resource (job, service or worker_pool)." + value = local._resource[var.type] +} + +output "resource_name" { + description = "Cloud Run resource (job, service or workerpool) service name." + value = local.resource.name } output "service" { description = "Cloud Run Service." - value = var.create_job ? null : local.service + value = var.type == "SERVICE" ? local._resource[var.type] : null } - output "service_account" { description = "Service account resource." value = try(google_service_account.service_account[0], null) @@ -67,12 +63,12 @@ output "service_account_iam_email" { output "service_name" { description = "Cloud Run service name." - value = var.create_job ? null : local.service.name + value = var.type == "SERVICE" ? local.resource.name : null } output "service_uri" { description = "Main URI in which the service is serving traffic." - value = var.create_job ? null : local.service.uri + value = local.resource.uri } output "vpc_connector" { diff --git a/modules/cloud-run-v2/service.tf b/modules/cloud-run-v2/service.tf index b7e89992c..07948963e 100644 --- a/modules/cloud-run-v2/service.tf +++ b/modules/cloud-run-v2/service.tf @@ -15,37 +15,33 @@ */ resource "google_cloud_run_v2_service" "service" { - count = ( - !var.create_job && - var.managed_revision - ? 1 : 0 - ) + count = var.type == "SERVICE" && var.managed_revision ? 1 : 0 provider = google-beta project = var.project_id location = var.region - name = "${local.prefix}${var.name}" - ingress = var.ingress - invoker_iam_disabled = var.invoker_iam_disabled + name = var.name + ingress = var.service_config.ingress + invoker_iam_disabled = var.service_config.invoker_iam_disabled labels = var.labels launch_stage = var.launch_stage - custom_audiences = var.custom_audiences + custom_audiences = var.service_config.custom_audiences deletion_protection = var.deletion_protection - iap_enabled = local.iap_enabled + iap_enabled = var.service_config.iap_config != null template { labels = var.revision.labels encryption_key = var.encryption_key revision = local.revision_name execution_environment = ( - var.revision.gen2_execution_environment == true + var.service_config.gen2_execution_environment ? "EXECUTION_ENVIRONMENT_GEN2" : "EXECUTION_ENVIRONMENT_GEN1" ) - max_instance_request_concurrency = var.revision.max_concurrency + max_instance_request_concurrency = var.service_config.max_concurrency dynamic "scaling" { - for_each = (var.revision.max_instance_count == null && var.revision.min_instance_count == null) ? [] : [""] + for_each = var.service_config.scaling == null ? [] : [""] content { - max_instance_count = var.revision.max_instance_count - min_instance_count = var.revision.min_instance_count + max_instance_count = var.service_config.scaling.max_instance_count + min_instance_count = var.service_config.scaling.min_instance_count } } dynamic "vpc_access" { @@ -66,7 +62,7 @@ resource "google_cloud_run_v2_service" "service" { } } } - timeout = var.revision.timeout + timeout = var.service_config.timeout service_account = local.service_account_email dynamic "containers" { for_each = var.containers @@ -262,37 +258,33 @@ resource "google_cloud_run_v2_service" "service" { } resource "google_cloud_run_v2_service" "service_unmanaged" { - count = ( - !var.create_job && - !var.managed_revision - ? 1 : 0 - ) + count = var.type == "SERVICE" && !var.managed_revision ? 1 : 0 provider = google-beta project = var.project_id location = var.region - name = "${local.prefix}${var.name}" - ingress = var.ingress - invoker_iam_disabled = var.invoker_iam_disabled + name = var.name + ingress = var.service_config.ingress + invoker_iam_disabled = var.service_config.invoker_iam_disabled labels = var.labels launch_stage = var.launch_stage - custom_audiences = var.custom_audiences + custom_audiences = var.service_config.custom_audiences deletion_protection = var.deletion_protection - iap_enabled = local.iap_enabled + iap_enabled = var.service_config.iap_config != null template { labels = var.revision.labels encryption_key = var.encryption_key revision = local.revision_name execution_environment = ( - var.revision.gen2_execution_environment == true + var.service_config.gen2_execution_environment ? "EXECUTION_ENVIRONMENT_GEN2" : "EXECUTION_ENVIRONMENT_GEN1" ) - max_instance_request_concurrency = var.revision.max_concurrency + max_instance_request_concurrency = var.service_config.max_concurrency dynamic "scaling" { - for_each = (var.revision.max_instance_count == null && var.revision.min_instance_count == null) ? [] : [""] + for_each = var.service_config.scaling == null ? [] : [""] content { - max_instance_count = var.revision.max_instance_count - min_instance_count = var.revision.min_instance_count + max_instance_count = var.service_config.scaling.max_instance_count + min_instance_count = var.service_config.scaling.min_instance_count } } dynamic "vpc_access" { @@ -313,7 +305,7 @@ resource "google_cloud_run_v2_service" "service_unmanaged" { } } } - timeout = var.revision.timeout + timeout = var.service_config.timeout service_account = local.service_account_email dynamic "containers" { for_each = var.containers @@ -512,10 +504,10 @@ resource "google_cloud_run_v2_service" "service_unmanaged" { } resource "google_cloud_run_v2_service_iam_binding" "binding" { - for_each = var.create_job ? {} : var.iam - project = local.service.project - location = local.service.location - name = local.service.name + for_each = var.type == "SERVICE" ? var.iam : {} + project = local.resource.project + location = local.resource.location + name = local.resource.name role = each.key members = ( each.key != "roles/run.invoker" || !local.trigger_sa_create @@ -527,27 +519,23 @@ resource "google_cloud_run_v2_service_iam_binding" "binding" { ) } -locals { - - iap_iam_additive = local.iap_enabled ? var.iap_config.iam_additive : [] - iap_iam = local.iap_enabled ? var.iap_config.iam : [] - -} - resource "google_iap_web_cloud_run_service_iam_member" "member" { - for_each = toset(local.iap_iam_additive) - project = local.service.project - location = local.service.location - cloud_run_service_name = local.service.name + for_each = var.service_config.iap_config == null ? toset([]) : toset(var.service_config.iap_config.iam_additive) + project = local.resource.project + location = local.resource.location + cloud_run_service_name = local.resource.name role = "roles/iap.httpsResourceAccessor" member = each.key } resource "google_iap_web_cloud_run_service_iam_binding" "binding" { - for_each = length(local.iap_iam) == 0 ? {} : { 1 = 1 } - project = local.service.project - location = local.service.location - cloud_run_service_name = local.service.name + for_each = ( + var.service_config.iap_config == null ? {} + : length(var.service_config.iap_config.iam) == 0 ? {} : { 1 = 1 } + ) + project = local.resource.project + location = local.resource.location + cloud_run_service_name = local.resource.name role = "roles/iap.httpsResourceAccessor" - members = local.iap_iam -} \ No newline at end of file + members = var.service_config.iap_config.iam +} diff --git a/modules/cloud-run-v2/tags.tf b/modules/cloud-run-v2/tags.tf index 4a9f24da1..67b3c68a4 100644 --- a/modules/cloud-run-v2/tags.tf +++ b/modules/cloud-run-v2/tags.tf @@ -14,10 +14,18 @@ * limitations under the License. */ +locals { + resource_types = { + JOB = "jobs" + SERVICE = "services" + # WORKERPOOL = "worker-pools" # not yet supported for Worker Pools + } +} + resource "google_tags_location_tag_binding" "binding" { - for_each = var.create_job ? {} : var.tag_bindings + for_each = var.tag_bindings parent = ( - "//run.googleapis.com/projects/${var.project_id}/locations/${var.region}/services/${local.service.name}" + "//run.googleapis.com/projects/${var.project_id}/locations/${var.region}/${local.resource_types[var.type]}/${local.resource.name}" ) tag_value = each.value location = var.region diff --git a/modules/cloud-run-v2/variables.tf b/modules/cloud-run-v2/variables.tf index d270a6799..38b800006 100644 --- a/modules/cloud-run-v2/variables.tf +++ b/modules/cloud-run-v2/variables.tf @@ -77,18 +77,6 @@ variable "containers" { nullable = false } -variable "create_job" { - description = "Create Cloud Run Job instead of Service." - type = bool - default = false -} - -variable "custom_audiences" { - description = "Custom audiences for service." - type = list(string) - default = null -} - variable "deletion_protection" { description = "Deletion protection setting for this Cloud Run service." type = string @@ -101,80 +89,25 @@ variable "encryption_key" { default = null } -variable "eventarc_triggers" { - description = "Event arc triggers for different sources." - type = object({ - audit_log = optional(map(object({ - method = string - service = string - }))) - pubsub = optional(map(string)) - storage = optional(map(object({ - bucket = string - path = optional(string) - }))) - service_account_email = optional(string) - service_account_create = optional(bool, false) - }) - default = {} - validation { - condition = var.eventarc_triggers.audit_log == null || (var.eventarc_triggers.audit_log != null && (var.eventarc_triggers.service_account_email != null || var.eventarc_triggers.service_account_create)) - error_message = "When setting var.eventarc_triggers.audit_log provide either service_account_email or set service_account_create to true" - } -} - variable "iam" { description = "IAM bindings for Cloud Run service in {ROLE => [MEMBERS]} format." type = map(list(string)) default = {} } -variable "iap_config" { - description = "If present, turns on Identity-Aware Proxy (IAP) for the Cloud Run service." +variable "job_config" { + description = "Cloud Run Job specific configuration." type = object({ - iam = optional(list(string), []) - iam_additive = optional(list(string), []) + max_retries = optional(number) + task_count = optional(number) + timeout = optional(string) }) - default = null - + default = {} + nullable = false validation { - condition = !(length(try(var.iap_config.iam, [])) > 0 && length(try(var.iap_config.iam_additive, [])) > 0) - - error_message = "Providing both 'iam' and 'iam_additive' in iap_config is not supported." + condition = var.job_config.timeout == null ? true : endswith(var.job_config.timeout, "s") + error_message = "Timeout should follow format of number with up to nine fractional digits, ending with 's'. Example: '3.5s'." } - - validation { - condition = var.iap_config == null || !var.create_job - error_message = "IAP is only supported for Cloud Run services, not Cloud Run jobs. Set create_job to false when using iap_config." - } - - validation { - condition = var.iap_config == null || var.launch_stage != "GA" - error_message = "iap is currently not supported in GA. Set launch_stage to 'BETA' or lower." - } -} - -variable "ingress" { - description = "Ingress settings." - type = string - default = null - validation { - condition = ( - var.ingress == null ? true : contains( - ["INGRESS_TRAFFIC_ALL", "INGRESS_TRAFFIC_INTERNAL_ONLY", - "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"], var.ingress) - ) - error_message = < 0 && length(try(var.service_config.iap_config.iam_additive, [])) > 0) + error_message = "Providing both 'iam' and 'iam_additive' in iap_config is not supported." + } + + validation { + condition = var.service_config.iap_config == null || var.launch_stage != "GA" + error_message = "iap is currently not supported in GA. Set launch_stage to 'BETA' or lower." + } + + validation { + condition = ( + var.service_config.ingress == null ? true : contains( + ["INGRESS_TRAFFIC_ALL", "INGRESS_TRAFFIC_INTERNAL_ONLY", + "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"], var.service_config.ingress) + ) + error_message = < v if k != "cloudsql" } + content { + name = volume_mounts.key + mount_path = volume_mounts.value + } + } + # CloudSQL is the last mount in the list returned by API + dynamic "volume_mounts" { + for_each = { for k, v in coalesce(containers.value.volume_mounts, tomap({})) : k => v if k == "cloudsql" } + content { + name = volume_mounts.key + mount_path = volume_mounts.value + } + } + } + } + dynamic "volumes" { + for_each = { for k, v in var.volumes : k => v if v.cloud_sql_instances == null } + content { + name = volumes.key + dynamic "secret" { + for_each = volumes.value.secret == null ? [] : [""] + content { + secret = volumes.value.secret.name + default_mode = volumes.value.secret.default_mode + dynamic "items" { + for_each = volumes.value.secret.path == null ? [] : [""] + content { + path = volumes.value.secret.path + version = volumes.value.secret.version + mode = volumes.value.secret.mode + } + } + } + } + + dynamic "empty_dir" { + for_each = volumes.value.empty_dir_size == null ? [] : [""] + content { + medium = "MEMORY" + size_limit = volumes.value.empty_dir_size + } + } + dynamic "gcs" { + for_each = volumes.value.gcs == null ? [] : [""] + content { + bucket = volumes.value.gcs.bucket + read_only = volumes.value.gcs.is_read_only + } + } + dynamic "nfs" { + for_each = volumes.value.nfs == null ? [] : [""] + content { + server = volumes.value.nfs.server + path = volumes.value.nfs.path + read_only = volumes.value.nfs.is_read_only + } + } + } + } + # CloudSQL is the last volume in the list returned by API + dynamic "volumes" { + for_each = { for k, v in var.volumes : k => v if v.cloud_sql_instances != null } + content { + name = volumes.key + dynamic "cloud_sql_instance" { + for_each = length(coalesce(volumes.value.cloud_sql_instances, [])) == 0 ? [] : [""] + content { + instances = volumes.value.cloud_sql_instances + } + } + } + } + } + + lifecycle { + ignore_changes = [ + client, + client_version, + template[0].annotations["run.googleapis.com/operation-id"], + ] + } +} + +resource "google_cloud_run_v2_worker_pool" "default_unmanaged" { + count = var.type == "WORKERPOOL" && var.managed_revision ? 1 : 0 + provider = google-beta + project = var.project_id + location = var.region + name = var.name + labels = var.labels + launch_stage = var.launch_stage + deletion_protection = var.deletion_protection + + dynamic "scaling" { + for_each = var.workerpool_config.scaling == null ? [] : [""] + content { + scaling_mode = var.workerpool_config.scaling.mode + max_instance_count = var.workerpool_config.scaling.max_instance_count + min_instance_count = var.workerpool_config.scaling.min_instance_count + manual_instance_count = var.workerpool_config.scaling.manual_instance_count + } + } + + template { + labels = var.revision.labels + encryption_key = var.encryption_key + revision = local.revision_name + + # Serverless VPC connector is not supported + # dynamic "vpc_access" { + # for_each = local.connector == null ? [] : [""] + # content { + # connector = local.connector + # egress = try(var.revision.vpc_access.egress, null) + # } + # } + dynamic "vpc_access" { + for_each = var.revision.vpc_access.subnet == null && var.revision.vpc_access.network == null ? [] : [""] + content { + egress = var.revision.vpc_access.egress + network_interfaces { + subnetwork = var.revision.vpc_access.subnet + network = var.revision.vpc_access.network + tags = var.revision.vpc_access.tags + } + } + } + service_account = local.service_account_email + dynamic "containers" { + for_each = var.containers + content { + name = containers.key + image = containers.value.image + depends_on = containers.value.depends_on + command = containers.value.command + args = containers.value.args + dynamic "env" { + for_each = coalesce(containers.value.env, tomap({})) + content { + name = env.key + value = env.value + } + } + dynamic "env" { + for_each = coalesce(containers.value.env_from_key, tomap({})) + content { + name = env.key + value_source { + secret_key_ref { + secret = env.value.secret + version = env.value.version + } + } + } + } + dynamic "resources" { + for_each = containers.value.resources == null ? [] : [""] + content { + limits = containers.value.resources.limits + } + } + dynamic "volume_mounts" { + for_each = { for k, v in coalesce(containers.value.volume_mounts, tomap({})) : k => v if k != "cloudsql" } + content { + name = volume_mounts.key + mount_path = volume_mounts.value + } + } + # CloudSQL is the last mount in the list returned by API + dynamic "volume_mounts" { + for_each = { for k, v in coalesce(containers.value.volume_mounts, tomap({})) : k => v if k == "cloudsql" } + content { + name = volume_mounts.key + mount_path = volume_mounts.value + } + } + } + } + dynamic "volumes" { + for_each = { for k, v in var.volumes : k => v if v.cloud_sql_instances == null } + content { + name = volumes.key + dynamic "secret" { + for_each = volumes.value.secret == null ? [] : [""] + content { + secret = volumes.value.secret.name + default_mode = volumes.value.secret.default_mode + dynamic "items" { + for_each = volumes.value.secret.path == null ? [] : [""] + content { + path = volumes.value.secret.path + version = volumes.value.secret.version + mode = volumes.value.secret.mode + } + } + } + } + + dynamic "empty_dir" { + for_each = volumes.value.empty_dir_size == null ? [] : [""] + content { + medium = "MEMORY" + size_limit = volumes.value.empty_dir_size + } + } + dynamic "gcs" { + for_each = volumes.value.gcs == null ? [] : [""] + content { + bucket = volumes.value.gcs.bucket + read_only = volumes.value.gcs.is_read_only + } + } + dynamic "nfs" { + for_each = volumes.value.nfs == null ? [] : [""] + content { + server = volumes.value.nfs.server + path = volumes.value.nfs.path + read_only = volumes.value.nfs.is_read_only + } + } + } + } + # CloudSQL is the last volume in the list returned by API + dynamic "volumes" { + for_each = { for k, v in var.volumes : k => v if v.cloud_sql_instances != null } + content { + name = volumes.key + dynamic "cloud_sql_instance" { + for_each = length(coalesce(volumes.value.cloud_sql_instances, [])) == 0 ? [] : [""] + content { + instances = volumes.value.cloud_sql_instances + } + } + } + } + } + lifecycle { + ignore_changes = [ + client, + client_version, + template[0].annotations["run.googleapis.com/operation-id"], + template[0].containers, + template[0].labels + ] + } +} + +resource "google_cloud_run_v2_worker_pool_iam_binding" "binding" { + for_each = var.type == "WORKERPOOL" ? var.iam : {} + project = local.resource.project + location = local.resource.location + name = local.resource.name + role = each.key + members = each.value +} diff --git a/tests/modules/cloud_run_v2/examples/job-iam-env.yaml b/tests/modules/cloud_run_v2/examples/job-iam-env.yaml index e0f1aef4d..daedaeb9b 100644 --- a/tests/modules/cloud_run_v2/examples/job-iam-env.yaml +++ b/tests/modules/cloud_run_v2/examples/job-iam-env.yaml @@ -39,11 +39,8 @@ values: vpc_access: [] module.cloud_run.google_cloud_run_v2_job_iam_binding.binding["roles/run.invoker"]: condition: [] - location: europe-west8 members: - group:organization-admins@example.org - name: hello - project: project-id role: roles/run.invoker counts: diff --git a/tests/modules/cloud_run_v2/examples/service-iam-env.yaml b/tests/modules/cloud_run_v2/examples/service-iam-env.yaml index 3572428ef..2bb377a28 100644 --- a/tests/modules/cloud_run_v2/examples/service-iam-env.yaml +++ b/tests/modules/cloud_run_v2/examples/service-iam-env.yaml @@ -64,11 +64,8 @@ values: timeouts: null module.cloud_run.google_cloud_run_v2_service_iam_binding.binding["roles/run.invoker"]: condition: [] - location: europe-west8 members: - allUsers - name: hello - project: project-id role: roles/run.invoker module.secret-manager.google_secret_manager_secret.default["credentials"]: annotations: null diff --git a/tests/modules/cloud_run_v2/examples/tags.yaml b/tests/modules/cloud_run_v2/examples/tags.yaml new file mode 100644 index 000000000..25b3e9d43 --- /dev/null +++ b/tests/modules/cloud_run_v2/examples/tags.yaml @@ -0,0 +1,131 @@ +# 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. + +values: + module.cloud_run_job.google_cloud_run_v2_job.job[0]: + annotations: null + binary_authorization: [] + client: null + client_version: null + deletion_protection: false + effective_labels: + goog-terraform-provisioned: 'true' + labels: null + location: europe-west8 + name: hello-job + project: project-id + run_execution_token: null + start_execution_token: null + template: + - annotations: null + labels: null + template: + - containers: + - args: null + command: null + depends_on: null + env: [] + image: us-docker.pkg.dev/cloudrun/container/hello + name: hello + ports: [] + volume_mounts: [] + working_dir: null + encryption_key: null + max_retries: 3 + node_selector: [] + volumes: [] + vpc_access: [] + terraform_labels: + goog-terraform-provisioned: 'true' + timeouts: null + module.cloud_run_job.google_tags_location_tag_binding.binding["env-sandbox"]: + location: europe-west8 + timeouts: null + module.cloud_run_service.google_cloud_run_v2_service.service[0]: + annotations: null + binary_authorization: [] + build_config: [] + client: null + client_version: null + custom_audiences: null + default_uri_disabled: null + deletion_protection: false + description: null + effective_labels: + goog-terraform-provisioned: 'true' + iap_enabled: false + invoker_iam_disabled: false + labels: null + location: europe-west8 + name: hello-service + project: project-id + scaling: [] + template: + - annotations: null + containers: + - args: null + base_image_uri: null + command: null + depends_on: null + env: [] + image: us-docker.pkg.dev/cloudrun/container/hello + liveness_probe: [] + name: hello + volume_mounts: [] + working_dir: null + encryption_key: null + execution_environment: EXECUTION_ENVIRONMENT_GEN1 + labels: null + node_selector: [] + revision: null + service_mesh: [] + session_affinity: null + volumes: [] + vpc_access: [] + terraform_labels: + goog-terraform-provisioned: 'true' + timeouts: null + module.cloud_run_service.google_tags_location_tag_binding.binding["env-sandbox"]: + location: europe-west8 + timeouts: null + module.project.google_tags_tag_key.default["run_environment"]: + description: Environment specification. + parent: projects/project-id + purpose: null + purpose_data: null + short_name: run_environment + timeouts: null + module.project.google_tags_tag_value.default["run_environment/dev"]: + description: Managed by the Terraform project module. + short_name: dev + timeouts: null + module.project.google_tags_tag_value.default["run_environment/prod"]: + description: Managed by the Terraform project module. + short_name: prod + timeouts: null + module.project.google_tags_tag_value.default["run_environment/sandbox"]: + description: Managed by the Terraform project module. + short_name: sandbox + timeouts: null + +counts: + google_cloud_run_v2_job: 1 + google_cloud_run_v2_service: 1 + google_tags_location_tag_binding: 2 + google_tags_tag_key: 1 + google_tags_tag_value: 3 + modules: 3 + resources: 8 + +outputs: {}