modules/v2/main.tf (326 lines of code) (raw):
/**
* 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.
*/
data "google_service_account" "existing_sa" {
count = local.create_service_account == false ? 1 : 0
account_id = google_cloud_run_v2_service.main.template[0].service_account
}
locals {
service_account = (
var.service_account != null
? var.service_account
: (
var.create_service_account
? google_service_account.sa[0].email
: null
)
)
create_service_account = var.create_service_account ? var.service_account == null : false
service_account_prefix = substr("${var.service_name}-${var.location}", 0, 27)
service_account_output = local.create_service_account ? {
id = google_service_account.sa[0].account_id,
email = google_service_account.sa[0].email,
member = google_service_account.sa[0].member
} : {
id = data.google_service_account.existing_sa[0].account_id,
email = data.google_service_account.existing_sa[0].email,
member = data.google_service_account.existing_sa[0].member
}
service_account_project_roles = local.create_service_account ? distinct(concat(
var.service_account_project_roles,
var.enable_prometheus_sidecar ? ["roles/monitoring.metricWriter"] : []
)) : []
ingress_container = try(
[for container in var.containers : container if length(try(container.ports, {})) > 0][0],
null
)
prometheus_sidecar_container = [{
container_name = "collector"
container_image = "us-docker.pkg.dev/cloud-ops-agents-artifacts/cloud-run-gmp-sidecar/cloud-run-gmp-sidecar:1.1.1"
# Set default values for the sidecar container
ports = {}
working_dir = null
depends_on_container = [local.ingress_container.container_name]
container_args = null
container_command = null
env_vars = {}
env_secret_vars = {}
volume_mounts = []
resources = {
cpu_idle = true
startup_cpu_boost = false
limits = {}
}
startup_probe = []
liveness_probe = []
}]
}
resource "google_service_account" "sa" {
count = local.create_service_account ? 1 : 0
project = var.project_id
account_id = "${local.service_account_prefix}-sa"
display_name = "Service account for ${var.service_name} in ${var.location}"
}
resource "google_project_iam_member" "roles" {
for_each = toset(local.service_account_project_roles)
project = var.project_id
role = each.value
member = "serviceAccount:${local.service_account}"
}
resource "google_cloud_run_v2_service" "main" {
provider = google-beta
project = var.project_id
name = var.service_name
location = var.location
description = var.description
labels = var.service_labels
deletion_protection = var.cloud_run_deletion_protection
template {
revision = var.revision
labels = var.template_labels
annotations = var.template_annotations
timeout = var.timeout
service_account = local.service_account
execution_environment = var.execution_environment
encryption_key = var.encryption_key
max_instance_request_concurrency = var.max_instance_request_concurrency
session_affinity = var.session_affinity
dynamic "scaling" {
for_each = var.template_scaling[*]
content {
min_instance_count = scaling.value.min_instance_count
max_instance_count = scaling.value.max_instance_count
}
}
dynamic "vpc_access" {
for_each = var.vpc_access[*]
content {
connector = vpc_access.value.connector
egress = vpc_access.value.egress
dynamic "network_interfaces" {
for_each = vpc_access.value.network_interfaces[*]
content {
network = network_interfaces.value.network
subnetwork = network_interfaces.value.subnetwork
tags = network_interfaces.value.tags
}
}
}
}
dynamic "containers" {
for_each = concat(var.containers,
var.enable_prometheus_sidecar ? local.prometheus_sidecar_container : [])
content {
name = containers.value.container_name
image = containers.value.container_image
command = containers.value.container_command
args = containers.value.container_args
working_dir = containers.value.working_dir
depends_on = containers.value.depends_on_container
dynamic "ports" {
for_each = lookup(containers.value, "ports", {}) != {} ? [containers.value.ports] : []
content {
name = ports.value["name"]
container_port = ports.value["container_port"]
}
}
resources {
limits = containers.value.resources.limits
cpu_idle = containers.value.resources.cpu_idle
startup_cpu_boost = containers.value.resources.startup_cpu_boost
}
dynamic "startup_probe" {
for_each = containers.value.startup_probe[*]
content {
failure_threshold = startup_probe.value.failure_threshold
initial_delay_seconds = startup_probe.value.initial_delay_seconds
timeout_seconds = startup_probe.value.timeout_seconds
period_seconds = startup_probe.value.period_seconds
dynamic "http_get" {
for_each = startup_probe.value.http_get[*]
content {
path = http_get.value.path
port = http_get.value.port
dynamic "http_headers" {
for_each = http_get.value.http_headers[*]
content {
name = http_headers.value["name"]
value = http_headers.value["value"]
}
}
}
}
dynamic "tcp_socket" {
for_each = startup_probe.value.tcp_socket[*]
content {
port = tcp_socket.value.port
}
}
dynamic "grpc" {
for_each = startup_probe.value.grpc[*]
content {
port = grpc.value.port
service = grpc.value.service
}
}
}
}
dynamic "liveness_probe" {
for_each = containers.value.liveness_probe[*]
content {
failure_threshold = liveness_probe.value.failure_threshold
initial_delay_seconds = liveness_probe.value.initial_delay_seconds
timeout_seconds = liveness_probe.value.timeout_seconds
period_seconds = liveness_probe.value.period_seconds
dynamic "http_get" {
for_each = liveness_probe.value.http_get[*]
content {
path = http_get.value.path
port = http_get.value.port
dynamic "http_headers" {
for_each = http_get.value.http_headers[*]
content {
name = http_headers.value["name"]
value = http_headers.value["value"]
}
}
}
}
dynamic "tcp_socket" {
for_each = liveness_probe.value.tcp_socket[*]
content {
port = tcp_socket.value.port
}
}
dynamic "grpc" {
for_each = liveness_probe.value.grpc[*]
content {
port = grpc.value.port
service = grpc.value.service
}
}
}
}
dynamic "env" {
for_each = containers.value.env_vars
content {
name = env.key
value = env.value
}
}
dynamic "env" {
for_each = containers.value.env_secret_vars
content {
name = env.key
value_source {
secret_key_ref {
secret = env.value.secret
version = env.value.version
}
}
}
}
dynamic "volume_mounts" {
for_each = containers.value.volume_mounts
content {
name = volume_mounts.value["name"]
mount_path = volume_mounts.value["mount_path"]
}
}
}
} // containers
dynamic "volumes" {
for_each = var.volumes
content {
name = volumes.value["name"]
dynamic "secret" {
for_each = volumes.value.secret[*]
content {
secret = secret.value["secret"]
items {
path = secret.value.items["path"]
version = secret.value.items["version"]
mode = secret.value.items["mode"]
}
}
}
dynamic "cloud_sql_instance" {
for_each = volumes.value.cloud_sql_instance[*]
content {
instances = cloud_sql_instance.value["instances"]
}
}
dynamic "empty_dir" {
for_each = volumes.value.empty_dir[*]
content {
medium = empty_dir.value["medium"]
size_limit = empty_dir.value["size_limit"]
}
}
dynamic "gcs" {
for_each = volumes.value.gcs[*]
content {
bucket = gcs.value["bucket"]
read_only = gcs.value["read_only"]
}
}
dynamic "nfs" {
for_each = volumes.value.nfs[*]
content {
server = nfs.value["server"]
path = nfs.value["path"]
read_only = nfs.value["read_only"]
}
}
}
}
} // template
annotations = var.service_annotations
client = var.client.name
client_version = var.client.version
ingress = var.ingress
launch_stage = var.launch_stage
custom_audiences = var.custom_audiences
dynamic "binary_authorization" {
for_each = var.binary_authorization[*]
content {
breakglass_justification = binary_authorization.value.breakglass_justification
use_default = binary_authorization.value.use_default
}
}
dynamic "scaling" {
for_each = var.service_scaling[*]
content {
min_instance_count = scaling.value.min_instance_count
}
}
dynamic "traffic" {
for_each = var.traffic
content {
percent = traffic.value.percent
type = traffic.value.type
revision = traffic.value.revision
tag = traffic.value.tag
}
}
depends_on = [google_project_iam_member.roles]
}
resource "google_cloud_run_v2_service_iam_member" "authorize" {
for_each = toset(var.members)
location = google_cloud_run_v2_service.main.location
project = google_cloud_run_v2_service.main.project
name = google_cloud_run_v2_service.main.name
role = "roles/run.invoker"
member = each.value
}