aboutsummaryrefslogtreecommitdiffstats
path: root/main.tf
diff options
context:
space:
mode:
Diffstat (limited to 'main.tf')
-rw-r--r--main.tf443
1 files changed, 443 insertions, 0 deletions
diff --git a/main.tf b/main.tf
new file mode 100644
index 0000000..5a4e848
--- /dev/null
+++ b/main.tf
@@ -0,0 +1,443 @@
+# VARIABLES
+
+locals {
+ deploy_ui_cf_name = "deploy-ui-gcs"
+ deploy_ui_topic = "deploy-ui-gcs"
+ artifact_bucket_name = "gh-artifacts"
+ ui_name = "ui"
+ runner_api_name = "runner-api"
+ example_api_name = "example-api"
+ example_db_name = "example-db"
+}
+
+variable "project" {
+ type = string
+}
+variable "region" {
+ type = string
+}
+variable "zone" {
+ type = string
+}
+variable "credentials" {
+ type = string
+}
+variable "cloud_run_location" {
+ type = string
+}
+variable "gcs_location" {
+ type = string
+}
+variable "gh_deploy_sa" {
+ type = string
+}
+variable "runner_api_image" {
+ type = string
+}
+variable "example_api_image" {
+ type = string
+}
+variable "ui_build_revision" {
+ type = string
+}
+variable "ui_dns_name" {
+ type = string
+}
+variable "example_db_api_user_password" {
+ type = string
+ sensitive = true
+}
+variable "nr_license_key" {
+ type = string
+ sensitive = true
+}
+
+# TF BACKEND
+
+terraform {
+ backend "gcs" {
+ bucket = "slam-lang-tf-state"
+ prefix = "terraform/state"
+ credentials = "sa.json"
+ }
+}
+
+provider "google" {
+ project = var.project
+ region = var.region
+ zone = var.zone
+ credentials = var.credentials
+}
+
+# DEPLOYMENT INFRA
+
+resource "google_storage_bucket" "gh-artifacts" {
+ name = "${var.project}-${local.artifact_bucket_name}"
+ location = var.gcs_location
+ force_destroy = true
+
+ uniform_bucket_level_access = true
+}
+
+resource "google_pubsub_schema" "deploy-ui-gcs" {
+ name = "${local.deploy_ui_topic}-schema"
+ type = "AVRO"
+ definition = <<-EOT
+ {
+ "type": "record",
+ "name": "Avro",
+ "fields": [
+ {
+ "name": "revision",
+ "type": "string"
+ }
+ ]
+ }
+ EOT
+}
+
+resource "google_pubsub_topic" "deploy-ui-gcs" {
+ name = local.deploy_ui_topic
+
+ depends_on = [google_pubsub_schema.deploy-ui-gcs]
+ schema_settings {
+ schema = "projects/${var.project}/schemas/${google_pubsub_schema.deploy-ui-gcs.name}"
+ encoding = "JSON"
+ }
+}
+
+resource "google_pubsub_schema" "deploy-ui-gcs-result" {
+ name = "${local.deploy_ui_topic}-result-schema"
+ type = "AVRO"
+ definition = <<-EOT
+ {
+ "type": "record",
+ "name": "Avro",
+ "fields": [
+ {
+ "name": "revision",
+ "type": "string"
+ },
+ {
+ "name": "ok",
+ "type": "boolean"
+ }
+ ]
+ }
+ EOT
+}
+
+resource "google_pubsub_topic" "deploy-ui-gcs-result" {
+ name = "${local.deploy_ui_topic}-result"
+
+ depends_on = [google_pubsub_schema.deploy-ui-gcs-result]
+ schema_settings {
+ schema = "projects/${var.project}/schemas/${google_pubsub_schema.deploy-ui-gcs-result.name}"
+ encoding = "JSON"
+ }
+}
+
+resource "google_pubsub_subscription" "deploy-ui-gcs-result" {
+ name = "${local.deploy_ui_topic}-result-sub"
+ topic = google_pubsub_topic.deploy-ui-gcs-result.name
+
+ message_retention_duration = "604800s" # 7 days
+ retain_acked_messages = false
+
+ ack_deadline_seconds = 20
+
+ expiration_policy {
+ ttl = ""
+ }
+ retry_policy {
+ minimum_backoff = "10s"
+ }
+
+ enable_message_ordering = false
+}
+
+resource "null_resource" "cloud-function-deploy-ui-gcs" {
+ triggers = {
+ version = jsondecode(file("deploy-ui-gcs/package.json")).version
+ name = local.deploy_ui_cf_name
+ region = var.cloud_run_location
+ topic = local.deploy_ui_topic
+ service-account = var.gh_deploy_sa
+ }
+
+ depends_on = [
+ google_storage_bucket.gh-artifacts,
+ google_pubsub_topic.deploy-ui-gcs,
+ google_pubsub_subscription.deploy-ui-gcs-result,
+ ]
+
+ provisioner "local-exec" {
+ command = <<-EOT
+ cd deploy-ui-gcs
+ gcloud functions deploy ${self.triggers.name} \
+ --region ${self.triggers.region} \
+ --trigger-topic ${self.triggers.topic} \
+ --service-account=${self.triggers.service-account} \
+ --runtime=nodejs16
+ EOT
+ }
+
+ provisioner "local-exec" {
+ when = destroy
+ command = <<-EOT
+ cd deploy-ui-gcs
+ gcloud functions delete ${self.triggers.name} \
+ --region ${self.triggers.region} \
+ EOT
+ }
+}
+
+# FRONTEND UI
+
+resource "google_storage_bucket" "ui" {
+ name = "${var.project}-${local.ui_name}"
+ location = var.gcs_location
+ force_destroy = true
+
+ uniform_bucket_level_access = true
+
+ website {
+ main_page_suffix = "index.html"
+ }
+ cors {
+ origin = ["*"]
+ method = ["GET", "HEAD", "PUT", "POST", "DELETE"]
+ response_header = ["*"]
+ max_age_seconds = 3600
+ }
+}
+
+data "google_iam_policy" "gcs_allUsers_viewer" {
+ binding {
+ role = "roles/storage.objectViewer"
+ members = [
+ "allUsers",
+ ]
+ }
+}
+
+resource "google_storage_bucket_iam_policy" "ui" {
+ bucket = google_storage_bucket.ui.name
+ policy_data = data.google_iam_policy.gcs_allUsers_viewer.policy_data
+}
+
+resource "null_resource" "ui-content" {
+ triggers = {
+ ui_build_revision = var.ui_build_revision
+ }
+
+ depends_on = [
+ null_resource.cloud-function-deploy-ui-gcs,
+ ]
+
+ provisioner "local-exec" {
+ command = <<-EOT
+ which npm
+ cd deploy-ui-gcs
+ npm run deploy "${var.ui_build_revision}"
+ EOT
+ }
+}
+
+resource "google_compute_backend_bucket" "ui" {
+ name = local.ui_name
+ description = ""
+ bucket_name = google_storage_bucket.ui.name
+ enable_cdn = false
+}
+
+resource "google_compute_url_map" "ui" {
+ name = local.ui_name
+ project = var.project
+ provider = google-beta
+ default_service = google_compute_backend_bucket.ui.id
+}
+
+resource "google_compute_managed_ssl_certificate" "ui" {
+ name = local.ui_name
+
+ managed {
+ domains = [var.ui_dns_name]
+ }
+}
+
+resource "google_compute_target_https_proxy" "ui" {
+ name = local.ui_name
+ project = var.project
+ url_map = google_compute_url_map.ui.id
+ ssl_certificates = [google_compute_managed_ssl_certificate.ui.id]
+}
+
+resource "google_compute_global_address" "ui" {
+ provider = google-beta
+ project = var.project
+ name = local.ui_name
+}
+
+resource "google_compute_global_forwarding_rule" "ui" {
+ name = local.ui_name
+ project = var.project
+ provider = google-beta
+ ip_protocol = "TCP"
+ load_balancing_scheme = "EXTERNAL"
+ port_range = "443"
+ target = google_compute_target_https_proxy.ui.id
+ ip_address = google_compute_global_address.ui.id
+}
+
+output "ui-ip" {
+ value = google_compute_global_address.ui.address
+}
+
+output "ui-url" {
+ value = "https://${var.ui_dns_name}"
+}
+
+# RUNNER API
+
+resource "google_cloud_run_service" "runner-api" {
+ name = local.runner_api_name
+ project = var.project
+ location = var.cloud_run_location
+
+ template {
+ spec {
+ containers {
+ image = var.runner_api_image
+ env {
+ name = "NEWRELIC_LICENSE_KEY"
+ value = var.nr_license_key
+ }
+ }
+ timeout_seconds = 7 # interpreter timeout = 5
+ }
+
+ metadata {
+ annotations = {
+ "autoscaling.knative.dev/maxScale" = 3
+ }
+ }
+ }
+
+ traffic {
+ percent = 100
+ latest_revision = true
+ }
+}
+
+data "google_iam_policy" "cloud_run_allUsers_invoker" {
+ binding {
+ role = "roles/run.invoker"
+ members = [
+ "allUsers",
+ ]
+ }
+}
+
+resource "google_cloud_run_service_iam_policy" "runner-api" {
+ location = google_cloud_run_service.runner-api.location
+ project = google_cloud_run_service.runner-api.project
+ service = google_cloud_run_service.runner-api.name
+
+ policy_data = data.google_iam_policy.cloud_run_allUsers_invoker.policy_data
+}
+
+# EXAMPLE API
+
+resource "google_cloud_run_service" "example-api" {
+ name = local.example_api_name
+ project = var.project
+ location = var.cloud_run_location
+
+ template {
+ spec {
+ containers {
+ image = var.example_api_image
+ env {
+ name = "PGHOST"
+ value = "/cloudsql/${google_sql_database_instance.example-db.connection_name}"
+ }
+ env {
+ name = "PGDATABASE"
+ value = google_sql_database.example-db.name
+ }
+ env {
+ name = "PGUSER"
+ value = local.example_api_name
+ }
+ env {
+ name = "PGPASSWORD"
+ value = var.example_db_api_user_password
+ }
+ env {
+ name = "NEWRELIC_LICENSE_KEY"
+ value = var.nr_license_key
+ }
+ }
+ timeout_seconds = 5
+ }
+
+ metadata {
+ annotations = {
+ "autoscaling.knative.dev/maxScale" = 3
+ "run.googleapis.com/cloudsql-instances" = google_sql_database_instance.example-db.connection_name
+ }
+ }
+ }
+
+ traffic {
+ percent = 100
+ latest_revision = true
+ }
+
+ depends_on = [
+ google_sql_user.example-db
+ ]
+}
+
+resource "google_cloud_run_service_iam_policy" "example-api" {
+ location = google_cloud_run_service.example-api.location
+ project = google_cloud_run_service.example-api.project
+ service = google_cloud_run_service.example-api.name
+
+ policy_data = data.google_iam_policy.cloud_run_allUsers_invoker.policy_data
+}
+
+# EXAMPLE DB
+
+resource "google_sql_database_instance" "example-db" {
+ name = local.example_db_name
+ database_version = "POSTGRES_14"
+ region = var.region
+
+ settings {
+ tier = "db-f1-micro"
+ availability_type = "ZONAL"
+ location_preference {
+ zone = var.zone
+ }
+ disk_size = 10
+ backup_configuration {
+ enabled = false
+ }
+ ip_configuration {
+ ipv4_enabled = true
+ }
+ }
+}
+
+resource "google_sql_database" "example-db" {
+ name = local.example_db_name
+ instance = google_sql_database_instance.example-db.name
+}
+
+resource "google_sql_user" "example-db" {
+ name = local.example_api_name
+ instance = google_sql_database_instance.example-db.name
+ password = var.example_db_api_user_password
+}