FAQ | This is a LIVE service | Changelog

Skip to content
Snippets Groups Projects
Commit a647facf authored by Ryan Kowalewski's avatar Ryan Kowalewski :man_dancing:
Browse files

chore: implement integration testing

parent bd0ac388
No related branches found
No related tags found
1 merge request!63Release 9.0
name: gcp-cloud-run-app-testing
services:
test:
image: registry.gitlab.developers.cam.ac.uk/uis/devops/infra/dockerimages/logan-terraform:1.6
entrypoint: ["bash", "-c"]
environment:
# This unsets the GOOGLE_APPLICATION_CREDENTIALS as it is not required but the logan-terraform images sets it.
- GOOGLE_APPLICATION_CREDENTIALS=
- GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=terraform-deploy@infra-testing-int-e2395220.iam.gserviceaccount.com
volumes:
- .:/workdir:rw
- ~/.config/gcloud/application_default_credentials.json:/root/.config/gcloud/application_default_credentials.json:ro
# outputs.tf defines outputs for the module.
output "service" {
description = "Webapp Cloud Run service resource"
value = google_cloud_run_v2_service.webapp
......@@ -10,9 +8,20 @@ output "service_account" {
value = google_service_account.webapp
}
output "network_endpoint_group" {
description = "Network endpoint group for the load balancer."
value = try(google_compute_region_network_endpoint_group.webapp[0], null)
}
output "ssl_policy" {
description = "The ssl_policy object, if one is being created."
value = try(google_compute_ssl_policy.default[0], null)
}
output "load_balancer" {
description = "Load balancer for the webapp"
value = try(module.webapp_http_load_balancer[0], null)
sensitive = true
}
output "static_egress_ip" {
......
#! /usr/bin/env bash
set -e
shopt -s expand_aliases
usage() {
cat << EOF
Usage:
./run_tests.sh
./run_tests.sh -t tests/default.tftest.hcl
./run_tests.sh -t tests/default.tftest.hcl -t tests/cloudsql.tftest.hcl
Options:
-c Run in CI/CD job mode meaning that terraform is executed directly, rather than via
docker compose.
-t <test file path> Execute specific test files only (all tests are executed by default).
This option can be specified multiple times.
-v Add the verbose switch to the terraform test command.
EOF
}
# The sed commands in this script must use gnu-sed on mac as the default sed on MacOS is a steaming pile of...
if [[ "$(uname)" == "Darwin" ]]; then
if [ ! "$(command -v gsed)" ]; then
echo -e "This script requires gnu-sed on Mac OS. Install it with 'brew install gnu-sed'"
exit 1
fi
alias sed="gsed"
fi
cleanup() {
# Uncomment the prevent_destroy lifecycle argument if it has been commented out by the command further in the
# script.
sed -i 's/^ # prevent_destroy = true/ prevent_destroy = true/g' static_egress_ip.tf
# Remove the lockfile file as it is only necessary to enable the test run.
if [[ -f .terraform.lock.hcl ]]; then
rm .terraform.lock.hcl
fi
# Ensure the original versions.tf.json is restored.
if [[ -f versions.tf.json.bak ]]; then
mv versions.tf.json.bak versions.tf.json
fi
}
trap 'cleanup' EXIT INT TERM
while getopts 'cht:v' option; do
case $option in
c) ci_run=1;;
h) usage; exit;;
t) tests+=("$OPTARG");;
v) verbose=1;;
*) all_tests=1;;
esac
done
shift $((OPTIND-1))
if [[ ${#tests[@]} -gt 0 && -n $all_tests ]]; then
echo -e "Error: Cannot specify both -a and -t together.\n"
usage
exit 1
fi
test_args=("--var-file=tests/tests.tfvars")
if [[ "${#tests[@]}" -gt 0 ]]; then
for test in "${tests[@]}"; do
test_args+=("--filter=$test")
done
fi
if [[ -n $verbose ]]; then
test_args+=(--verbose)
fi
# Comment out the prevent_destroy lifecycle argument otherwise the tests will fail as they cannot tear down the
# resource.
sed -i 's/^ prevent_destroy = true/ # prevent_destroy = true/g' static_egress_ip.tf
# The terraform testing framework doesn't yet have a nice way to target specific versions of a provider. Therefore, we
# are having to programatically change the Google provider versions in the versions.tf.json file for the time being.
if [[ -n $GOOGLE_PROVIDER_VERSION_CONSTRAINT ]]; then
mv versions.tf.json versions.tf.json.bak
jq ".terraform.required_providers.google.version |= \"$GOOGLE_PROVIDER_VERSION_CONSTRAINT\"" versions.tf.json.bak \
> versions.tf.json
fi
if [[ -n $ci_run ]]; then
terraform init
terraform test "${test_args[@]}"
else
docker compose run --rm test "$(cat << EOF
terraform init
terraform test ${test_args[@]}
EOF
)"
fi
run "setup" {
variables {
create_test_secrets = true
}
module {
source = "./tests/setup"
}
}
run "test_service_with_default_variable_values" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
}
assert {
condition = google_cloud_run_v2_service.webapp.ingress == "INGRESS_TRAFFIC_ALL"
error_message = "Ingress should be 'INGRESS_TRAFFIC_ALL'."
}
assert {
condition = google_cloud_run_v2_service.webapp.launch_stage == "GA"
error_message = "Launch stage should be 'GA'."
}
assert {
condition = google_cloud_run_v2_service.webapp.location == "europe-west2"
error_message = "Location should be 'europe-west2'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].execution_environment == "EXECUTION_ENVIRONMENT_GEN1"
error_message = "Execution environment should be 'EXECUTION_ENVIRONMENT_GEN1'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].timeout == "300s"
error_message = "Timeout should be '300s'."
}
assert {
condition = length(google_cloud_run_v2_service.webapp.template[0].containers) == 1
error_message = "A single container block should be defined."
}
assert {
condition = google_cloud_run_v2_service_iam_member.webapp_all_users_invoker[0].role == "roles/run.invoker"
error_message = "google_cloud_run_v2_service_iam_member.webapp_all_users_invoker.role should be 'roles/run.invoker'."
}
assert {
condition = google_cloud_run_v2_service_iam_member.webapp_all_users_invoker[0].member == "allUsers"
error_message = "google_cloud_run_v2_service_iam_member.webapp_all_users_invoker.member should be 'allUsers'."
}
assert {
condition = google_service_account.webapp.name != null
error_message = "A dedicated service account should be created for the Cloud Run service."
}
}
run "test_container_resources_block" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
resources = {
startup_cpu_boost = true
cpu_idle = true
limits = {
cpu = 2
}
}
}
}
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].resources[0].startup_cpu_boost == true
error_message = "startup_cpu_boost should be 'true'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].resources[0].cpu_idle == true
error_message = "cpu_idle should be 'true'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].resources[0].limits.cpu == "2"
error_message = "limits.cpu should be '2'."
}
}
run "setup_secret_iam" {
variables {
create_test_secrets = true
create_test_secrets_iam = true
}
module {
source = "./tests/setup"
}
}
run "test_env_vars_and_secrets" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
env = [
{
name = "HELLO",
value = "WORLD!"
},
{
name = "SECRET",
value_source = {
secret_key_ref = {
secret = run.setup.secret_ids[0]
}
}
}
]
volume_mounts = [
{
name = "secret-volume",
mount_path = "/secrets"
}
]
}
}
volumes = [
{
name = "secret-volume",
secret = {
secret = run.setup.secret_ids[1]
items = [
{
version = "1",
path = "my-secret"
}
]
}
}
]
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].env[0].name == "HELLO"
error_message = "A standard environment variable with the name 'HELLO' should be created."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].env[0].value == "WORLD!"
error_message = "A standard environment variable with the value 'WORLD!' should be created."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].env[1].name == "SECRET"
error_message = "A secret-backed environment variable with the name 'SECRET' should be created."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].env[1].value_source[0].secret_key_ref[0].secret == run.setup.secret_ids[0]
error_message = "A secret-backed environment variable referencing the 'run.setup.secret_ids[0]' secret should be created."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].volume_mounts[0].name == "secret-volume"
error_message = "A volume mount with the name 'secret-volume' should be created."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].volume_mounts[0].mount_path == "/secrets"
error_message = "A volume mount with the mount path '/secrets' should be created."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].volumes[0].name == "secret-volume"
error_message = "A volume with the name 'secret-volume' should be created."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].volumes[0].secret[0].secret == run.setup.secret_ids[1]
error_message = "A secret-backed volume referencing the 'run.setup.secret_ids[0]' secret should be created."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].volumes[0].secret[0].items[0].version == "1"
error_message = "Secret version should be '1'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].volumes[0].secret[0].items[0].path == "my-secret"
error_message = "Secret path should be 'my-secret'."
}
}
run "test_service_with_multiple_containers" {
variables {
name = run.setup.random_name
containers = {
webapp1 = {
name = "webapp-1"
image = "us-docker.pkg.dev/cloudrun/container/hello"
ports = [
{
container_port = 8080
}
]
}
webapp2 = {
name = "webapp-2"
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
}
assert {
condition = length(google_cloud_run_v2_service.webapp.template[0].containers) == 2
error_message = "The Cloud Run service should have two containers defined."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].name == "webapp-1"
error_message = "The first container definition should be configured with the name 'webapp-1'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].ports[0].container_port == 8080
error_message = "The first container definition should be configured with a single container_port of 8080."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[1].name == "webapp-2"
error_message = "The second container definition should be configured with the name 'webapp-2'."
}
}
run "test_traffic_distribution_across_multiple_revisions_initial_deploy" {
variables {
name = run.setup.random_name
revision = "${run.setup.random_name}-v1-0-0"
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
traffic = [
{
type = "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION"
revision = "${run.setup.random_name}-v1-0-0"
percent = 100
}
]
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].revision == "${run.setup.random_name}-v1-0-0"
error_message = "The service revision name should be '${run.setup.random_name}-v1-0-0'."
}
assert {
condition = length(google_cloud_run_v2_service.webapp.traffic) == 1
error_message = "There should be a single traffic block configured."
}
assert {
condition = google_cloud_run_v2_service.webapp.traffic[0].type == "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION"
error_message = "There should be a single traffic block with the type 'TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION'."
}
assert {
condition = google_cloud_run_v2_service.webapp.traffic[0].revision == "${run.setup.random_name}-v1-0-0"
error_message = "There should be a single traffic block with the revision '${run.setup.random_name}-v1-0-0'."
}
assert {
condition = google_cloud_run_v2_service.webapp.traffic[0].percent == 100
error_message = "There should be a single traffic block with percent set to 100."
}
}
run "test_traffic_distribution_across_multiple_revisions_split_50_50" {
variables {
name = run.setup.random_name
revision = "${run.setup.random_name}-v1-1-0"
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
traffic = [
{
type = "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION"
revision = "${run.setup.random_name}-v1-0-0"
percent = 50
},
{
type = "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION"
revision = "${run.setup.random_name}-v1-1-0"
percent = 50
}
]
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].revision == "${run.setup.random_name}-v1-1-0"
error_message = "The service revision name should be '${run.setup.random_name}-v1-1-0'."
}
assert {
condition = length(google_cloud_run_v2_service.webapp.traffic) == 2
error_message = "There should be two traffic blocks configured."
}
assert {
condition = google_cloud_run_v2_service.webapp.traffic[0].type == "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION"
error_message = "The first traffic block should be configured with the type 'TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION'."
}
assert {
condition = google_cloud_run_v2_service.webapp.traffic[0].revision == "${run.setup.random_name}-v1-0-0"
error_message = "The first traffic block should be configured with the revision '${run.setup.random_name}-v1-0-0'."
}
assert {
condition = google_cloud_run_v2_service.webapp.traffic[0].percent == 50
error_message = "The first traffic block should be configured with percent set to 50."
}
assert {
condition = google_cloud_run_v2_service.webapp.traffic[1].type == "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION"
error_message = "The second traffic block should be configured with the type 'TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION'."
}
assert {
condition = google_cloud_run_v2_service.webapp.traffic[1].revision == "${run.setup.random_name}-v1-1-0"
error_message = "The second traffic block should be configured with the revision '${run.setup.random_name}-v1-1-0'."
}
assert {
condition = google_cloud_run_v2_service.webapp.traffic[1].percent == 50
error_message = "The second traffic block should be configured with percent set to 50."
}
}
run "setup" {
variables {
create_test_sql_instances = true
}
module {
source = "./tests/setup"
}
}
run "test_cloudsql_mount_using_helper_variable" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
mount_cloudsql_instance = run.setup.instance_connection_names[0]
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].volumes[0].name == "cloudsql"
error_message = "A single volume should be configured named 'cloudsql'."
}
assert {
condition = [
for instance in google_cloud_run_v2_service.webapp.template[0].volumes[0].cloud_sql_instance[0].instances : instance
][0] == run.setup.instance_connection_names[0]
error_message = "The cloudsql volume should be configured to use the run.setup.instance_connection_names[0] instance connection name."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].volume_mounts[0].name == "cloudsql"
error_message = "A single volume mount should be configured with the name 'cloudsql'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].volume_mounts[0].mount_path == "/cloudsql"
error_message = "A single volume mount should be configured with the mount path '/cloudsql'."
}
}
run "test_cloudsql_mount_to_single_container" {
variables {
name = run.setup.random_name
volumes = [
{
name = "cloudsql"
cloud_sql_instance = {
instances = [run.setup.instance_connection_names[0]]
}
}
]
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
volume_mounts = [
{
name = "cloudsql"
mount_path = "/cloudsql"
}
]
}
}
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].volumes[0].name == "cloudsql"
error_message = "A single volume should be configured named 'cloudsql'."
}
assert {
condition = (
[for instance in google_cloud_run_v2_service.webapp.template[0].volumes[0].cloud_sql_instance[0].instances : instance][0]
== run.setup.instance_connection_names[0]
)
error_message = "The cloudsql volume should be configured to use the run.setup.instance_connection_names[0] instance connection name."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].volume_mounts[0].name == "cloudsql"
error_message = "A single volume mount should be configured with the name 'cloudsql'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].volume_mounts[0].mount_path == "/cloudsql"
error_message = "A single volume mount should be configured with the mount path '/cloudsql'."
}
}
run "test_cloudsql_mount_multiple_cloudsql_instances" {
variables {
name = run.setup.random_name
volumes = [
{
name = "cloudsql"
cloud_sql_instance = {
instances = run.setup.instance_connection_names
}
}
]
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
volume_mounts = [
{
name = "cloudsql"
mount_path = "/cloudsql"
}
]
}
}
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].volumes[0].name == "cloudsql"
error_message = "A single volume should be configured named 'cloudsql'."
}
assert {
condition = length(google_cloud_run_v2_service.webapp.template[0].volumes[0].cloud_sql_instance[0].instances) == 2
error_message = "The cloudsql volume should be configured with exactly 2 instances."
}
assert {
condition = (
contains(
google_cloud_run_v2_service.webapp.template[0].volumes[0].cloud_sql_instance[0].instances,
run.setup.instance_connection_names[0]
)
&& contains(
google_cloud_run_v2_service.webapp.template[0].volumes[0].cloud_sql_instance[0].instances,
run.setup.instance_connection_names[1]
)
)
error_message = "The cloudsql volume should be configured with the two instance connection names defined in var.volumes."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].volume_mounts[0].name == "cloudsql"
error_message = "A single volume mount should be configured with the name 'cloudsql'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].containers[0].volume_mounts[0].mount_path == "/cloudsql"
error_message = "A single volume mount should be configured with the mount path '/cloudsql'."
}
}
run "test_cloudsql_mount_to_pre_deploy_job_using_helper_variable" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_pre_deploy_job = true
pre_deploy_job_container = {
image = "golang:latest"
command = ["go"]
args = ["version"]
}
pre_deploy_job_mount_cloudsql_instance = run.setup.instance_connection_names[0]
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].volumes[0].name == "cloudsql"
error_message = "A single volume should be configured named 'cloudsql'."
}
assert {
condition = (
[for instance in google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].volumes[0].cloud_sql_instance[0].instances : instance][0]
== run.setup.instance_connection_names[0]
)
error_message = "The cloudsql volume should be configured to use the run.setup.instance_connection_names[0] instance connection name."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].containers[0].volume_mounts[0].name == "cloudsql"
error_message = "A single volume mount should be configured with the name 'cloudsql'."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].containers[0].volume_mounts[0].mount_path == "/cloudsql"
error_message = "A single volume mount should be configured with the mount path '/cloudsql'."
}
}
run "test_cloudsql_mount_to_pre_deploy_job" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_pre_deploy_job = true
pre_deploy_job_volumes = [
{
name = "cloudsql"
cloud_sql_instance = {
instances = [run.setup.instance_connection_names[0]]
}
}
]
pre_deploy_job_container = {
image = "golang:latest"
command = ["go"]
args = ["version"]
volume_mounts = [
{
name = "cloudsql"
mount_path = "/cloudsql"
}
]
}
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].volumes[0].name == "cloudsql"
error_message = "A single volume should be configured named 'cloudsql'."
}
assert {
condition = (
[for instance in google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].volumes[0].cloud_sql_instance[0].instances : instance][0]
== run.setup.instance_connection_names[0]
)
error_message = "The cloudsql volume should be configured to use the run.setup.instance_connection_names[0] instance connection name."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].containers[0].volume_mounts[0].name == "cloudsql"
error_message = "A single volume mount should be configured with the name 'cloudsql'."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].containers[0].volume_mounts[0].mount_path == "/cloudsql"
error_message = "A single volume mount should be configured with the mount path '/cloudsql'."
}
}
run "setup" {
module {
source = "./tests/setup"
}
}
run "test_service_with_default_variable_values_and_load_balancer_enabled" {
variables {
name = run.setup.random_name
enable_load_balancer = true
dns_names = {
webapp = "${run.setup.random_name}.test.example.gcp.uis.cam.ac.uk"
}
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
}
assert {
condition = google_cloud_run_v2_service.webapp.ingress == "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
error_message = "Ingress should be 'INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER'."
}
assert {
condition = google_cloud_run_v2_service.webapp.launch_stage == "GA"
error_message = "Launch stage should be 'GA'."
}
assert {
condition = google_cloud_run_v2_service.webapp.location == "europe-west2"
error_message = "Location should be 'europe-west2'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].execution_environment == "EXECUTION_ENVIRONMENT_GEN1"
error_message = "Execution environment should be 'EXECUTION_ENVIRONMENT_GEN1'."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].timeout == "300s"
error_message = "Timeout should be '300s'."
}
assert {
condition = google_cloud_run_v2_service_iam_member.webapp_all_users_invoker[0].role == "roles/run.invoker"
error_message = "google_cloud_run_v2_service_iam_member.webapp_all_users_invoker.role should be 'roles/run.invoker'."
}
assert {
condition = google_cloud_run_v2_service_iam_member.webapp_all_users_invoker[0].member == "allUsers"
error_message = "google_cloud_run_v2_service_iam_member.webapp_all_users_invoker.member should be 'allUsers'."
}
assert {
condition = google_service_account.webapp.name != null
error_message = "A dedicated service account should be created for the Cloud Run service."
}
assert {
condition = google_compute_region_network_endpoint_group.webapp[0].network_endpoint_type == "SERVERLESS"
error_message = "Network endpoint group type should be 'SERVERLESS'."
}
assert {
condition = google_compute_region_network_endpoint_group.webapp[0].cloud_run[0].service == google_cloud_run_v2_service.webapp.name
error_message = "Network endpoint group must contain the created Cloud Run service in its definition."
}
assert {
condition = google_compute_ssl_policy.default[0].min_tls_version == "TLS_1_2"
error_message = "Minimum TLS version should be 'TLS_1_2'."
}
assert {
condition = google_compute_ssl_policy.default[0].profile == "MODERN"
error_message = "SSL profile should be 'MODERN'."
}
assert {
condition = module.webapp_http_load_balancer[0].backend_services["default"].protocol == "HTTP"
error_message = "The default backend service protocol should be 'HTTP'."
}
assert {
condition = module.webapp_http_load_balancer[0].backend_services["default"].enable_cdn == false
error_message = "The default backend service should not enable CDN."
}
assert {
condition = module.webapp_http_load_balancer[0].backend_services["default"].log_config[0].enable == true
error_message = "The default backend service log config should be enabled."
}
assert {
condition = module.webapp_http_load_balancer[0].backend_services["default"].log_config[0].sample_rate == 1.0
error_message = "The default backend service log sample rate should be '1.0'."
}
assert {
condition = length(module.webapp_http_load_balancer[0].backend_services["default"].backend) == 1
error_message = "The default backend service should be configured with a single backend block."
}
assert {
condition = contains([
for backend in module.webapp_http_load_balancer[0].backend_services["default"].backend : backend.group
], google_compute_region_network_endpoint_group.webapp[0].self_link)
error_message = "The default backend service group should be configured to use the created network_endpoint_group."
}
assert {
condition = module.webapp_http_load_balancer[0].http_proxy != null
error_message = "A http proxy resource should be created by the load balancer module."
}
assert {
condition = module.webapp_http_load_balancer[0].https_proxy != null
error_message = "A https proxy resource should be created by the load balancer module."
}
}
run "setup" {
module {
source = "./tests/setup"
}
}
run "test_monitoring_with_alert_policies_created_in_default_project" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_monitoring = true
}
assert {
condition = google_cloud_run_v2_service.webapp.ingress == "INGRESS_TRAFFIC_ALL"
error_message = "Ingress should be 'INGRESS_TRAFFIC_ALL'."
}
assert {
condition = module.uptime_monitoring["default"].ssl_cert_expiry_alert_policy.enabled == true
error_message = "SSL cert expiry alert policy should be enabled."
}
assert {
condition = module.uptime_monitoring["default"].ssl_cert_expiry_alert_policy.project == var.project
error_message = "SSL cert expiry alert policy should be created in the project specified in var.project."
}
assert {
condition = module.uptime_monitoring["default"].uptime_check_alert_policy.enabled == true
error_message = "Uptime alert policy should be enabled."
}
assert {
condition = module.uptime_monitoring["default"].uptime_check_alert_policy.project == var.project
error_message = "Uptime alert policy should be created in the project specified in var.project."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.period == "300s"
error_message = "Uptime check should be configured with period == '300s'."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.timeout == "30s"
error_message = "Uptime check should be configured with timeout == '30s'."
}
assert {
condition = length(module.uptime_monitoring["default"].https_uptime_check_config.http_check) == 1
error_message = "A single HTTP check should be configured."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].path == "/"
error_message = "The HTTP check path should be '/'."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].port == 443
error_message = "The HTTP check port should be 443."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].request_method == "GET"
error_message = "The HTTP check request method should be 'GET'."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].use_ssl == true
error_message = "The HTTP check should be configured to use SSL."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].validate_ssl == true
error_message = "The HTTP check should be configured to validate SSL."
}
assert {
condition = (
module.uptime_monitoring["default"].https_uptime_check_config.monitored_resource[0].type == "uptime_url"
)
error_message = "The uptime check should be configured to monitor a URL resource."
}
}
run "test_monitoring_with_alert_policies_created_in_scoping_project" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_monitoring = true
monitoring_scoping_project = "infra-testing-meta-21f09a44"
}
assert {
condition = google_cloud_run_v2_service.webapp.ingress == "INGRESS_TRAFFIC_ALL"
error_message = "Ingress should be 'INGRESS_TRAFFIC_ALL'."
}
assert {
condition = module.uptime_monitoring["default"].ssl_cert_expiry_alert_policy.enabled == true
error_message = "SSL cert expiry alert policy should be enabled."
}
assert {
condition = (
module.uptime_monitoring["default"].ssl_cert_expiry_alert_policy.project == var.monitoring_scoping_project
)
error_message = (
"SSL cert expiry alert policy should be created in the project specified in var.monitoring_scoping_project."
)
}
assert {
condition = module.uptime_monitoring["default"].uptime_check_alert_policy.enabled == true
error_message = "Uptime alert policy should be enabled."
}
assert {
condition = (
module.uptime_monitoring["default"].uptime_check_alert_policy.project == var.monitoring_scoping_project
)
error_message = "Uptime alert policy should be created in the project specified in var.monitoring_scoping_project."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.period == "300s"
error_message = "Uptime check should be configured with period == '300s'."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.timeout == "30s"
error_message = "Uptime check should be configured with timeout == '30s'."
}
assert {
condition = length(module.uptime_monitoring["default"].https_uptime_check_config.http_check) == 1
error_message = "A single HTTP check should be configured."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].path == "/"
error_message = "The HTTP check path should be '/'."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].port == 443
error_message = "The HTTP check port should be 443."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].request_method == "GET"
error_message = "The HTTP check request method should be 'GET'."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].use_ssl == true
error_message = "The HTTP check should be configured to use SSL."
}
assert {
condition = module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].validate_ssl == true
error_message = "The HTTP check should be configured to validate SSL."
}
assert {
condition = (
module.uptime_monitoring["default"].https_uptime_check_config.monitored_resource[0].type == "uptime_url"
)
error_message = "The uptime check should be configured to monitor a URL resource."
}
}
run "test_monitoring_with_auth_proxy" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_monitoring = true
allow_unauthenticated_invocations = false
}
assert {
condition = (
module.uptime_monitoring["default"].https_uptime_check_config.monitored_resource[0].labels.host
== module.uptime_monitoring["default"].auth_proxy_host
)
error_message = "HTTP check path should be configured to use the authentication proxy function host."
}
assert {
condition = (
module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].path
== module.uptime_monitoring["default"].auth_proxy_path
)
error_message = "HTTP check path should be configured to use the authentication proxy function path."
}
assert {
condition = module.uptime_monitoring["default"].auth_proxy_function.ingress_settings == "ALLOW_ALL"
error_message = "The authentication proxy function should be configured to allow all ingress."
}
}
run "test_monitoring_with_auth_proxy_and_vpc_access_connector" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_monitoring = true
allow_unauthenticated_invocations = false
ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY"
}
assert {
condition = (
module.uptime_monitoring["default"].https_uptime_check_config.monitored_resource[0].labels.host
== module.uptime_monitoring["default"].auth_proxy_host
)
error_message = "HTTP check path should be configured to use the authentication proxy function host."
}
assert {
condition = (
module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].path
== module.uptime_monitoring["default"].auth_proxy_path
)
error_message = "HTTP check path should be configured to use the authentication proxy function path."
}
assert {
condition = (
google_compute_subnetwork.vpc_connector[0].network
== "https://www.googleapis.com/compute/v1/projects/${var.project}/global/networks/default"
)
error_message = "A subnet for the VPC connector should be created in the 'default' network."
}
assert {
condition = google_vpc_access_connector.main[0].project == var.project
error_message = "A VPC connector should be created in the specified project."
}
assert {
condition = module.uptime_monitoring["default"].auth_proxy_function.ingress_settings == "ALLOW_ALL"
error_message = "The authentication proxy function should be configured to allow all ingress."
}
assert {
condition = (
module.uptime_monitoring["default"].auth_proxy_function.vpc_connector
== "projects/${var.project}/locations/${var.region}/connectors/${var.name}-conn"
)
error_message = "The authentication proxy function should be configured to use the created VPC connector."
}
assert {
condition = (
module.uptime_monitoring["default"].auth_proxy_function.vpc_connector_egress_settings == "ALL_TRAFFIC"
)
error_message = (
"The authentication proxy function should be configured to route all egress traffic via the VPC connector."
)
}
}
run "setup_vpc_connector" {
variables {
create_vpc_connector = true
}
module {
source = "./tests/setup"
}
}
run "test_monitoring_with_auth_proxy_and_an_existing_vpc_access_connector" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_monitoring = true
allow_unauthenticated_invocations = false
ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY"
vpc_access = {
connector = run.setup_vpc_connector.vpc_connector_id
egress = "ALL_TRAFFIC"
}
}
assert {
condition = (
module.uptime_monitoring["default"].https_uptime_check_config.monitored_resource[0].labels.host
== module.uptime_monitoring["default"].auth_proxy_host
)
error_message = "HTTP check path should be configured to use the authentication proxy function host."
}
assert {
condition = (
module.uptime_monitoring["default"].https_uptime_check_config.http_check[0].path
== module.uptime_monitoring["default"].auth_proxy_path
)
error_message = "HTTP check path should be configured to use the authentication proxy function path."
}
assert {
condition = try(google_compute_subnetwork.vpc_connector[0], null) == null
error_message = "A subnet should not be created."
}
assert {
condition = try(google_vpc_access_connector.main[0], null) == null
error_message = "A VPC connector should not be created."
}
assert {
condition = module.uptime_monitoring["default"].auth_proxy_function.ingress_settings == "ALLOW_ALL"
error_message = "The authentication proxy function should be configured to allow all ingress."
}
assert {
condition = (
module.uptime_monitoring["default"].auth_proxy_function.vpc_connector
== "projects/${var.project}/locations/${var.region}/connectors/${var.name}-setup"
)
error_message = "The authentication proxy function should be configured to use the provided VPC connector."
}
assert {
condition = (
module.uptime_monitoring["default"].auth_proxy_function.vpc_connector_egress_settings == "ALL_TRAFFIC"
)
error_message = (
"The authentication proxy function should be configured to route all egress traffic via the VPC connector."
)
}
}
run "setup" {
variables {
create_test_secrets = true
}
module {
source = "./tests/setup"
}
}
run "test_pre_deploy_job" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_pre_deploy_job = true
pre_deploy_job_container = {
image = "golang:latest"
command = ["go"]
args = ["version"]
}
}
}
run "setup_secret_iam" {
variables {
create_test_secrets = true
create_test_secrets_iam = true
}
module {
source = "./tests/setup"
}
}
run "test_pre_deploy_job_env_vars_and_secrets" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_pre_deploy_job = true
pre_deploy_job_container = {
image = "golang:latest"
command = ["go"]
args = ["version"]
env = [
{
name = "HELLO",
value = "WORLD!"
},
{
name = "SECRET",
value_source = {
secret_key_ref = {
secret = run.setup.secret_ids[0]
}
}
}
]
volume_mounts = [
{
name = "secret-volume",
mount_path = "/secrets"
}
]
}
pre_deploy_job_volumes = [
{
name = "secret-volume",
secret = {
secret = run.setup.secret_ids[1]
items = [
{
version = "1",
path = "my-secret"
}
]
}
}
]
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].containers[0].env[0].name == "HELLO"
error_message = "A standard environment variable with the name 'HELLO' should be created."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].containers[0].env[0].value == "WORLD!"
error_message = "A standard environment variable with the value 'WORLD!' should be created."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].containers[0].env[1].name == "SECRET"
error_message = "A secret-backed environment variable with the name 'SECRET' should be created."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].containers[0].env[1].value_source[0].secret_key_ref[0].secret == run.setup.secret_ids[0]
error_message = "A secret-backed environment variable referencing the 'run.setup.secret_ids[0]' secret should be created."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].containers[0].volume_mounts[0].name == "secret-volume"
error_message = "A volume mount with the name 'secret-volume' should be created."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].containers[0].volume_mounts[0].mount_path == "/secrets"
error_message = "A volume mount with the mount path '/secrets' should be created."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].volumes[0].name == "secret-volume"
error_message = "A volume with the name 'secret-volume' should be created."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].volumes[0].secret[0].secret == run.setup.secret_ids[1]
error_message = "A secret-backed volume referencing the 'run.setup.secret_ids[0]' secret should be created."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].volumes[0].secret[0].items[0].version == "1"
error_message = "Secret version should be '1'."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].volumes[0].secret[0].items[0].path == "my-secret"
error_message = "Secret path should be 'my-secret'."
}
}
resource "random_id" "name" {
byte_length = 2
# "rapp" represents Cloud Run App and is required to ensure any resources created by this repo's tests are easily
# identifiable by the cleanup.sh script. We only have 4 characters to play with given some of Google's naming
# restrictions.
prefix = "test-rapp"
}
module "sql" {
count = var.create_test_sql_instances ? 2 : 0
source = "GoogleCloudPlatform/sql-db/google//modules/postgresql"
version = "~> 17.0"
database_version = "POSTGRES_15"
name = "${random_id.name.hex}-${count.index}"
project_id = var.project
tier = "db-f1-micro"
availability_type = "ZONAL"
region = var.region
zone = "${var.region}-a"
deletion_protection = false
deletion_protection_enabled = false
}
resource "google_secret_manager_secret" "test" {
count = var.create_test_secrets ? 2 : 0
secret_id = "${random_id.name.hex}-secret-version-${count.index}"
project = var.project
replication {
auto {}
}
}
resource "google_secret_manager_secret_version" "test" {
count = var.create_test_secrets ? 2 : 0
secret = google_secret_manager_secret.test[count.index].id
secret_data = "secret-data"
}
resource "google_secret_manager_secret_iam_member" "test" {
count = var.create_test_secrets_iam ? 2 : 0
project = var.project
secret_id = google_secret_manager_secret.test[count.index].id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${random_id.name.hex}-run@${var.project}.iam.gserviceaccount.com"
}
resource "google_compute_subnetwork" "test" {
count = var.create_vpc_connector ? 1 : 0
name = "${random_id.name.hex}-setup"
project = var.project
ip_cidr_range = var.test_ip_cidr_range
network = "default"
region = var.region
}
resource "google_vpc_access_connector" "test" {
count = var.create_vpc_connector ? 1 : 0
name = "${random_id.name.hex}-setup"
project = var.project
region = var.region
subnet {
name = google_compute_subnetwork.test[0].name
}
}
output "random_name" {
description = "A random name to use for the test run."
value = random_id.name.hex
}
output "instance_connection_names" {
description = "A list of created SQL instance connection names."
value = [for k, v in module.sql : v.instance_connection_name]
}
output "secret_ids" {
description = "A list of created secret object IDs."
value = [for k, v in google_secret_manager_secret.test : v.id]
}
output "subnetwork_id" {
description = "The ID of the subnetwork created to test VPC access for the Cloud Run service."
value = var.create_vpc_connector ? google_compute_subnetwork.test[0].id : null
}
output "vpc_connector_id" {
description = "The ID of the VPC Access Connector object created to test VPC access for the Cloud Run service."
value = var.create_vpc_connector ? google_vpc_access_connector.test[0].id : null
}
variable "project" {
description = "The ID of a project to deploy the testing resources to."
type = string
}
variable "region" {
description = "The region/location to deploy test resources to."
type = string
}
variable "create_test_sql_instances" {
description = "If true, two SQL instances will be deployed for testing."
type = bool
default = false
}
variable "create_test_secrets" {
description = "If true, two secret objects will be deployed for testing."
type = bool
default = false
}
variable "create_test_secrets_iam" {
description = <<EOI
If true, IAM bindings will be created to allow the Cloud Run service to access the test secret objects.
EOI
type = bool
default = false
}
variable "create_vpc_connector" {
description = "If true, create a VPC Access Connector and associated subnet for testing."
type = bool
default = false
}
variable "test_ip_cidr_range" {
description = "CIDR range for the subnet which is created for the VPC Access Connector."
type = string
default = "10.0.0.16/28"
}
project = "infra-testing-int-e2395220"
region = "europe-west2"
run "setup" {
variables {
create_vpc_connector = true
}
module {
source = "./tests/setup"
}
}
run "test_service_vpc_access" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
vpc_access = {
connector = run.setup.vpc_connector_id
egress = "PRIVATE_RANGES_ONLY"
}
}
assert {
condition = length(google_cloud_run_v2_service.webapp.template[0].vpc_access) == 1
error_message = "The Cloud Run service should be configured with a single VPC Access Connector."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].vpc_access[0].connector == run.setup.vpc_connector_id
error_message = "The Cloud Run service should be configured to use the run.setup.vpc_connector_id VPC Access Connector."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].vpc_access[0].egress == "PRIVATE_RANGES_ONLY"
error_message = "The Cloud Run service should be configured to route private ranges only to the VPC Access Connector."
}
}
run "test_pre_deploy_job_vpc_access" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_pre_deploy_job = true
pre_deploy_job_container = {
image = "golang:latest"
command = ["go"]
args = ["version"]
}
pre_deploy_job_vpc_access = {
connector = run.setup.vpc_connector_id
egress = "PRIVATE_RANGES_ONLY"
}
}
assert {
condition = length(google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].vpc_access) == 1
error_message = "The Cloud Run service should be configured with a single VPC Access Connector."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].vpc_access[0].connector == run.setup.vpc_connector_id
error_message = "The Cloud Run service should be configured to use the run.setup.vpc_connector_id VPC Access Connector."
}
assert {
condition = google_cloud_run_v2_job.pre_deploy[0].template[0].template[0].vpc_access[0].egress == "PRIVATE_RANGES_ONLY"
error_message = "The Cloud Run service should be configured to route private ranges only to the VPC Access Connector."
}
}
run "test_static_egress_ip_config_with_existing_access_controller" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
vpc_access = {
connector = run.setup.vpc_connector_id
}
enable_static_egress_ip = true
static_egress_ip_subnetwork_id = run.setup.subnetwork_id
}
assert {
condition = length(google_compute_router.static_ip) == 1
error_message = "A single compute router resource should be created."
}
assert {
condition = length(google_compute_address.static_ip) == 1
error_message = "A single compute address resource should be created."
}
assert {
condition = length(google_compute_router_nat.static_ip) == 1
error_message = "A single compute router NAT resource should be created."
}
assert {
condition = google_compute_router_nat.static_ip[0].router == google_compute_router.static_ip[0].name
error_message = "The NAT resource should be configured to use the google_compute_router.static_ip[0] router resource."
}
assert {
condition = length(google_compute_router_nat.static_ip[0].subnetwork) == 1
error_message = "The NAT resource should be configured with a single subnetwork."
}
assert {
condition = [for subnet in google_compute_router_nat.static_ip[0].subnetwork : subnet.name][0] == run.setup.subnetwork_id
error_message = "The NAT resource should be configured to use the run.setup.subnetwork_id subnetwork resource."
}
assert {
condition = length(google_cloud_run_v2_service.webapp.template[0].vpc_access) == 1
error_message = "The Cloud Run service should be configured with a single VPC Access Connector."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].vpc_access[0].connector == run.setup.vpc_connector_id
error_message = "The Cloud Run service should be configured to use the run.setup.vpc_connector_id VPC Access Connector."
}
}
run "test_static_egress_ip_config_with_defaults" {
variables {
name = run.setup.random_name
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
enable_static_egress_ip = true
}
assert {
condition = length(google_compute_subnetwork.vpc_connector) == 1
error_message = "A single subnetwork should be created for the VPC Access Connector to utilise."
}
assert {
condition = length(google_vpc_access_connector.main) == 1
error_message = "A single VPC Access Connector resource should be created."
}
assert {
condition = google_vpc_access_connector.main[0].subnet[0].name == google_compute_subnetwork.vpc_connector[0].name
error_message = "The VPC Access Connector should be configured to use the google_compute_subnetwork.vpc_connector[0] subnetwork."
}
assert {
condition = length(google_compute_router.static_ip) == 1
error_message = "A single compute router resource should be created."
}
assert {
condition = length(google_compute_address.static_ip) == 1
error_message = "A single compute address resource should be created."
}
assert {
condition = length(google_compute_router_nat.static_ip) == 1
error_message = "A single compute router NAT resource should be created."
}
assert {
condition = google_compute_router_nat.static_ip[0].router == google_compute_router.static_ip[0].name
error_message = "The NAT resource should be configured to use the google_compute_router.static_ip[0] router resource."
}
assert {
condition = length(google_compute_router_nat.static_ip[0].subnetwork) == 1
error_message = "The NAT resource should be configured with a single subnetwork."
}
assert {
condition = [for subnet in google_compute_router_nat.static_ip[0].subnetwork : subnet.name][0] == google_compute_subnetwork.vpc_connector[0].id
error_message = "The NAT resource should be configured to use the google_compute_subnetwork.vpc_connector[0] subnetwork resource."
}
assert {
condition = length(google_cloud_run_v2_service.webapp.template[0].vpc_access) == 1
error_message = "The Cloud Run service should be configured with a single VPC Access Connector."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].vpc_access[0].connector == google_vpc_access_connector.main[0].id
error_message = "The Cloud Run service should be configured to use the google_vpc_access_connector.main[0].id resource."
}
assert {
condition = google_cloud_run_v2_service.webapp.template[0].vpc_access[0].egress == "ALL_TRAFFIC"
error_message = "The Cloud Run service should be configured to route all egress traffic to the VPC Access Connector."
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment