Introduction
Today I will demonstrate how to deploy Google Cloud Endpoints for Cloud Run using Terraform. This is a “getting started” style tutorial. We are going to bypasses Google’s recommendation of building an ESP image on every Endpoints configuration change and break the circular dependency between Endpoints configuration resource and ESP Cloud Run resource.
Problems
ESP image build
Google recommends to build a new ESP image on every Endpoints configuration change.
In other words if you want to manage your OpenAPI configuration and ESP deployment from the same terraform project it is not possible. You need to split configuration management from ESP image build. You can find some good suggestions here.
Solution
For purposes of a prototype or a small project we can work around that using the unofficial ENDPOINTS_SERVICE_NAME
environment variable.
From the ESPv2 source code:
Note this deployment mode is not officially supported.
But it is used by the project internally e.g. for the end to end tests.
Circular dependency
We get circular dependency between Endpoints configuration and Cloud Run resource because CLOUD_RUN_HOSTNAME
. It represents the hostname portion of the URL that Cloud Run creates during deployment. I am using the same name from the official tutorial.
google_endpoints_service
requires CLOUD_RUN_HOSTNAME
to be used as host
in openapi.json
before creating a configuration. CLOUD_RUN_HOSTNAME
is known only after google_cloud_run_service
deployment. google_cloud_run_service
requires google_endpoints_service
configuration.
Solution
To solve that circular dependency we can use the cloud.goog
domain instead CLOUD_RUN_HOSTNAME
. It is not mentioned in the main Endpoints tutorial but explained here.
Working example
Now we can use both google_endpoints_service
and google_cloud_run_service
in one terraform file and create and destroy both resources on demand e.g. in your continuous integration and deployment pipeline.
Just simple terraform apply
.
Full terraform configuration below. You can test it with:
$ terraform plan
$ terraform apply
Prerequisites:
- create a project on Google Cloud Platform;
- update
variable.tf
.
variable "project_id" {
default = "<your-project-id>"
}
variable "project_number" {
default = "<your-project-number>"
}
variable "suffix" {
default = "<your-random-suffix>"
}
variable "region" {
default = "us-central1"
}
{
"swagger": "2.0",
"info": {
"title": "Service API",
"version": "0.0.1",
"description": "Service API"
},
"basePath": "/v1",
"host": "${endpoints_host}",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"schemes": [
"https"
],
"paths": {
}
}
##############################################################################
# Cloud Endpoints
##############################################################################
locals {
endpoints_host = "api-${var.suffix}.endpoints.${var.project_id}.cloud.goog"
}
# Create/update endpoints configuration based on OpenAPI
resource "google_endpoints_service" "api" {
project = var.project_id
service_name = local.endpoints_host
openapi_config = templatefile("openapi.json", {
project_id = var.project_id
endpoints_host = local.endpoints_host
})
}
# Enable service
resource "google_project_service" "api-project-service" {
service = google_endpoints_service.api.service_name
project = var.project_id
depends_on = [google_endpoints_service.api]
}
# Create/update Cloud Run service
resource "google_cloud_run_service" "endpoints-cloud-run" {
name = "endpoints-${var.suffix}"
location = var.region
project = var.project_id
template {
spec {
containers {
image = "gcr.io/endpoints-release/endpoints-runtime-serverless:2"
env {
name = "ENDPOINTS_SERVICE_NAME"
value = local.endpoints_host
}
}
}
}
depends_on = [google_endpoints_service.api]
}
# Create public access
data "google_iam_policy" "noauth" {
binding {
role = "roles/run.invoker"
members = [
"allUsers",
]
}
}
# Enable public access on endpoints service
resource "google_cloud_run_service_iam_policy" "noauth-endpoints" {
location = google_cloud_run_service.endpoints-cloud-run.location
project = google_cloud_run_service.endpoints-cloud-run.project
service = google_cloud_run_service.endpoints-cloud-run.name
policy_data = data.google_iam_policy.noauth.policy_data
}
output "endpoints-url" {
value = google_cloud_run_service.endpoints-cloud-run.status[0].url
description = "API endpoint URL"
}