video-accessibility/infra/cloud-run/main.tf
2025-08-24 16:28:33 -05:00

478 lines
No EOL
11 KiB
HCL

terraform {
required_version = ">= 1.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}
# Variables
variable "project_id" {
description = "Google Cloud Project ID"
type = string
}
variable "region" {
description = "Google Cloud Region"
type = string
default = "us-central1"
}
variable "environment" {
description = "Environment (dev, staging, prod)"
type = string
default = "prod"
}
# Enable required APIs
resource "google_project_service" "required_apis" {
for_each = toset([
"cloudbuild.googleapis.com",
"run.googleapis.com",
"containerregistry.googleapis.com",
"secretmanager.googleapis.com",
"cloudtrace.googleapis.com",
"monitoring.googleapis.com",
"translate.googleapis.com",
"texttospeech.googleapis.com",
"storage.googleapis.com",
"aiplatform.googleapis.com"
])
service = each.value
disable_on_destroy = false
}
# Service Accounts
resource "google_service_account" "api_service_account" {
account_id = "accessible-video-api"
display_name = "Accessible Video API Service Account"
description = "Service account for the API server"
}
resource "google_service_account" "worker_service_account" {
account_id = "accessible-video-worker"
display_name = "Accessible Video Worker Service Account"
description = "Service account for Celery workers"
}
# IAM bindings for API service account
resource "google_project_iam_member" "api_secret_accessor" {
project = var.project_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.api_service_account.email}"
}
resource "google_project_iam_member" "api_storage_admin" {
project = var.project_id
role = "roles/storage.objectAdmin"
member = "serviceAccount:${google_service_account.api_service_account.email}"
}
resource "google_project_iam_member" "api_trace_agent" {
project = var.project_id
role = "roles/cloudtrace.agent"
member = "serviceAccount:${google_service_account.api_service_account.email}"
}
resource "google_project_iam_member" "api_monitoring_writer" {
project = var.project_id
role = "roles/monitoring.metricWriter"
member = "serviceAccount:${google_service_account.api_service_account.email}"
}
# IAM bindings for Worker service account
resource "google_project_iam_member" "worker_secret_accessor" {
project = var.project_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.worker_service_account.email}"
}
resource "google_project_iam_member" "worker_storage_admin" {
project = var.project_id
role = "roles/storage.objectAdmin"
member = "serviceAccount:${google_service_account.worker_service_account.email}"
}
resource "google_project_iam_member" "worker_trace_agent" {
project = var.project_id
role = "roles/cloudtrace.agent"
member = "serviceAccount:${google_service_account.worker_service_account.email}"
}
resource "google_project_iam_member" "worker_monitoring_writer" {
project = var.project_id
role = "roles/monitoring.metricWriter"
member = "serviceAccount:${google_service_account.worker_service_account.email}"
}
resource "google_project_iam_member" "worker_ai_user" {
project = var.project_id
role = "roles/aiplatform.user"
member = "serviceAccount:${google_service_account.worker_service_account.email}"
}
# GCS Bucket for video storage
resource "google_storage_bucket" "video_storage" {
name = "accessible-video-${var.project_id}"
location = var.region
storage_class = "STANDARD"
uniform_bucket_level_access = true
cors {
origin = ["https://your-frontend-domain.com", "http://localhost:5173"]
method = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
response_header = ["Content-Type", "Authorization", "Range"]
max_age_seconds = 3600
}
lifecycle_rule {
condition {
age = 90
}
action {
type = "Delete"
}
}
}
# Cloud Run API Service
resource "google_cloud_run_v2_service" "api_service" {
name = "accessible-video-api"
location = var.region
depends_on = [google_project_service.required_apis]
template {
service_account = google_service_account.api_service_account.email
scaling {
min_instance_count = 1
max_instance_count = 10
}
containers {
image = "gcr.io/${var.project_id}/accessible-video-api:latest"
ports {
container_port = 8000
}
resources {
limits = {
memory = "2Gi"
cpu = "2000m"
}
cpu_idle = false
}
env {
name = "APP_ENV"
value = var.environment
}
env {
name = "PYTHONPATH"
value = "/app"
}
env {
name = "PYTHONUNBUFFERED"
value = "1"
}
env {
name = "GCS_BUCKET_NAME"
value = google_storage_bucket.video_storage.name
}
env {
name = "GOOGLE_CLOUD_PROJECT"
value = var.project_id
}
env {
name = "OTEL_SERVICE_NAME"
value = "accessible-video-api"
}
env {
name = "OTEL_TRACES_EXPORTER"
value = "gcp_trace"
}
env {
name = "SENTRY_ENVIRONMENT"
value = var.environment
}
# Secret environment variables
env {
name = "MONGODB_URL"
value_source {
secret_key_ref {
secret = "mongodb-url"
version = "latest"
}
}
}
env {
name = "REDIS_URL"
value_source {
secret_key_ref {
secret = "redis-url"
version = "latest"
}
}
}
env {
name = "JWT_SECRET_KEY"
value_source {
secret_key_ref {
secret = "jwt-secret"
version = "latest"
}
}
}
env {
name = "GEMINI_API_KEY"
value_source {
secret_key_ref {
secret = "gemini-api-key"
version = "latest"
}
}
}
env {
name = "SENDGRID_API_KEY"
value_source {
secret_key_ref {
secret = "sendgrid-api-key"
version = "latest"
}
}
}
env {
name = "ELEVENLABS_API_KEY"
value_source {
secret_key_ref {
secret = "elevenlabs-api-key"
version = "latest"
}
}
}
env {
name = "SENTRY_DSN"
value_source {
secret_key_ref {
secret = "sentry-dsn"
version = "latest"
}
}
}
liveness_probe {
http_get {
path = "/health"
port = 8000
}
initial_delay_seconds = 30
timeout_seconds = 10
period_seconds = 60
}
startup_probe {
http_get {
path = "/health"
port = 8000
}
initial_delay_seconds = 10
timeout_seconds = 5
period_seconds = 30
}
}
}
}
# Cloud Run Worker Service
resource "google_cloud_run_v2_service" "worker_service" {
name = "accessible-video-worker"
location = var.region
depends_on = [google_project_service.required_apis]
template {
service_account = google_service_account.worker_service_account.email
scaling {
min_instance_count = 0
max_instance_count = 5
}
containers {
image = "gcr.io/${var.project_id}/accessible-video-worker:latest"
resources {
limits = {
memory = "4Gi"
cpu = "4000m"
}
cpu_idle = false
}
env {
name = "APP_ENV"
value = var.environment
}
env {
name = "PYTHONPATH"
value = "/app"
}
env {
name = "PYTHONUNBUFFERED"
value = "1"
}
env {
name = "C_FORCE_ROOT"
value = "1"
}
env {
name = "GCS_BUCKET_NAME"
value = google_storage_bucket.video_storage.name
}
env {
name = "GOOGLE_CLOUD_PROJECT"
value = var.project_id
}
env {
name = "OTEL_SERVICE_NAME"
value = "accessible-video-worker"
}
env {
name = "OTEL_TRACES_EXPORTER"
value = "gcp_trace"
}
env {
name = "SENTRY_ENVIRONMENT"
value = var.environment
}
# Secret environment variables
env {
name = "MONGODB_URL"
value_source {
secret_key_ref {
secret = "mongodb-url"
version = "latest"
}
}
}
env {
name = "REDIS_URL"
value_source {
secret_key_ref {
secret = "redis-url"
version = "latest"
}
}
}
env {
name = "CELERY_BROKER_URL"
value_source {
secret_key_ref {
secret = "redis-url"
version = "latest"
}
}
}
env {
name = "CELERY_RESULT_BACKEND"
value_source {
secret_key_ref {
secret = "redis-url"
version = "latest"
}
}
}
env {
name = "GEMINI_API_KEY"
value_source {
secret_key_ref {
secret = "gemini-api-key"
version = "latest"
}
}
}
env {
name = "ELEVENLABS_API_KEY"
value_source {
secret_key_ref {
secret = "elevenlabs-api-key"
version = "latest"
}
}
}
env {
name = "SENTRY_DSN"
value_source {
secret_key_ref {
secret = "sentry-dsn"
version = "latest"
}
}
}
}
}
}
# IAM for public access to API
resource "google_cloud_run_service_iam_binding" "api_public_access" {
location = google_cloud_run_v2_service.api_service.location
service = google_cloud_run_v2_service.api_service.name
role = "roles/run.invoker"
members = ["allUsers"]
}
# Outputs
output "api_service_url" {
description = "URL of the deployed API service"
value = google_cloud_run_v2_service.api_service.uri
}
output "worker_service_url" {
description = "URL of the deployed worker service"
value = google_cloud_run_v2_service.worker_service.uri
}
output "storage_bucket_name" {
description = "Name of the GCS bucket for video storage"
value = google_storage_bucket.video_storage.name
}