I am having some problems while trying to create Cloud Endpoints on a Cloud Run service, mainly because of its “complicated” dependency chain.
So what we would usually do is:
Create a Cloud Run service. Note its URL.
Create a Cloud Endpoints service that refers to URL we got from step 1.
Update the Cloud Run service that we created before, adding an environment variable containing its own URL.
#############
# Cloud Run #
#############
resource "google_cloud_run_service" "cloud-run" {
name = "cloud-run"
provider = "google-beta"
location = "europe-west1"
metadata {
namespace = "${var.gcp_project[var.env]}"
}
spec {
containers {
env { # STEP 3: update environment variable
name = "ENDPOINTS_SERVICE_NAME"
value = "${local.cloud_run_url}" # <--- pseudo-code, will not work because it is self-referential
}
image = "gcr.io/endpoints-release/endpoints-runtime-serverless@sha256:a12b14dd6d31a88637ca7c9e63724ad542226d9509421ba08ed4452a91ce751e"
}
container_concurrency = var.env != "dev" ? 0 : 1
}
}
locals {
# STEP 1: create Cloud Run and get URL
cloud_run_url = google_cloud_run_service.cloud-run.status[0].url
}
output "cloudRunUrl" {
value = local.cloud_run_url
}
###################
# Cloud Endpoints #
###################
resource "google_endpoints_service" "my-api" {
# STEP 2: Create Cloud Endpoints service with Cloud Run's URL
service_name = "${replace(local.cloud_run_url, "https://", "")}"
openapi_config = <<EOF
swagger: '2.0'
info:
title: My-API
description: Some description.
version: 1.0.0
host: "${replace(local.cloud_run_url, "https://", "")}"
security:
- api_key: []
[...]
EOF
depends_on = ["google_cloud_run_service.cloud-run"}
}
So steps 1 and 2 are working, but I am trying to figure out how to add an environment variable to the Cloud Run service after it has been created, without having to write any references to itself in the configuration.
Allright this has been bugging me as well for the last 2 days. I couldn’t find it. But! Google Cloud Functions is switching to image based deployments on April 20th, which means that you’ll (probably) be able to host your serverless endpoints there, and Cloud Functions URLs are known before you create the function, because it’s based on the function name and region without a cluster prefix, so that would fix the problem. To do it on Cloud Run, you would really need to bash-script both the first and third step, as you cannot create a Cloud Run Service that already exists using terraform.
as for step 3, this is the workaround I ended up having in the code:
resource "google_cloud_run_service" "my-cloud-run-service" {
name = "my-cloud-run-service"
provider = google-beta
location = "europe-west1"
metadata {
namespace = var.gcp_project[var.env]
}
spec {
containers {
# Environment variables used by the Cloud Endpoints runtime
env {
# The 'ENDPOINTS_SERVICE_NAME' value will only be known after the initial creation
# of the Cloud Run service and will only be able to be set on a second run of the provisioning
name = "ENDPOINTS_SERVICE_NAME"
value = var.cloud_run_service_name[var.env] # see below where this comes from
}
image = "gcr.io/endpoints-release/endpoints-runtime-serverless@sha256:a12b14dd6d31a88637ca7c9e63724ad542226d9509421ba08ed4452a91ce751e"
}
container_concurrency = var.env != "dev" ? 0 : 1 # in DEV it should be 1
}
}
output "cloudRunUrl" {
value = local.cloud_run_url
}
I would copy-paste the output value “cloudRunUrl” for each environment into a variable, for all following runs:
variable "cloud_run_service_name" {
description = "Cloud Run service name. Returned in the output variable 'cloudRunUrl'."
type = map(string)
default = {
// Here would be the values from the output "cloudRunUrl"
"dev" = "xxx.a.run.app"
"stage" = "yyy.a.run.app"
"prod" = "zzz.a.run.app"
}
}
So basically this was just working from the second run (the first run would obviously fail).
I was creating the Cloud Run services once at the beginning of the project though, so while this solution still sucked, it was not a tragedy.