7UXZYCGIA4ZKZOQQQXAFUL4OR2XAPV4MDGJ5U3U3BCRY55GV63YAC OFB6PRFOV5Z4HNEQPYRI6K3LXMXRVPORBB23XSEDFRABQYM6EGPAC XWLO2R5JZCHDE5OQPRO6ZDIBK5TDCQQXYVQVM4ELRQCTLAPCDIGAC RNHBTG23XBZEWM6W6ZBQGLDUMGH74NKUORUEATD2Y4AQY4QXXCVAC DPBDWBX5WRGDBFQRBEWJOHSV4TIGJS2EIJC257HBOSZFSQ2J2WNQC ZYD2JAPGU3VJPSH5VMJQFOQFIB64UEPBCI4GPOYNKVHVT7L3FJXQC UMCATODOPQUDCUN5MZIZI24GBQJXCBNMRXBH6BIKVSQNTXDKY3IQC 34U5B56LT3OBJ5HCDAGYWMQPQP56XAYL5TOYEGYKF56HT7FRNYPAC NL24AWTEDDNFQRGAIQI2CXPJGWTPFJDH6HDVI7BTIM3VJMXE2XYAC IPZS5HGY4NRR7XVZD7WCHS43B533YS3KOBMJRU6M2TLVAD35B37AC EPO6PZVIXI5WBDGX2W72Q43VY4GCTKXDX6MJIJMWLLZSOACLN5XQC NZ2NB77XD5DQZQXSVKUYWOU7RBPP3BNSY6NP4Z7LHWOS4MQ3RHPQC # Terraform state files.terraform/*.tfstate*.tfstate.**.tfstate.backup# Terraform variables with sensitive dataterraform.tfvars*.auto.tfvars# Crash log filescrash.logcrash.*.log# CLI configuration files.terraformrcterraform.rc# Provider cache.terraform.lock.hcl# Override filesoverride.tfoverride.tf.json*_override.tf*_override.tf.json# Plan files*.tfplan
terraform {required_version = ">= 1.10"required_providers {}}}}}}}}}}# IAM policy to allow unauthenticated access (public)resource "google_cloud_run_service_iam_member" "public_access" {count = var.allow_unauthenticated ? 1 : 0service = google_cloud_run_service.beats_api.namelocation = google_cloud_run_service.beats_api.locationrole = "roles/run.invoker"member = "allUsers"}# Custom domain mappingresource "google_cloud_run_domain_mapping" "custom_domain" {name = var.custom_domainlocation = var.regionmetadata {namespace = var.project_id}spec {route_name = google_cloud_run_service.beats_api.name}}resource "google_artifact_registry_repository_iam_member" "cloudrun_reader" {project = var.project_idlocation = local.artifact_registry_locationrepository = google_artifact_registry_repository.docker.namerole = "roles/artifactregistry.reader"member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com"}traffic {percent = 100latest_revision = true}lifecycle {ignore_changes = [template[0].spec[0].containers[0].image]}}# Grant Cloud Run default service account access to Artifact Registry# Cloud Run uses the Compute Engine default service accountdata "google_project" "project" {project_id = var.project_idmetadata {annotations = {"autoscaling.knative.dev/minScale" = tostring(var.min_instances)"autoscaling.knative.dev/maxScale" = tostring(var.max_instances)}filename = "cloudbuild.yaml"substitutions = {}service_account = google_service_account.cloudbuild.iddepends_on = [google_project_service.cloudbuild,]}# Cloud Run service for the Beats FastAPI applicationresource "google_cloud_run_service" "beats_api" {name = var.service_namelocation = var.regiontemplate {spec {containers {image = "${local.built_image}:latest"ports {container_port = var.container_port}env {name = "DB_DSN"value = var.db_dsn}env {name = "DB_NAME"value = var.db_name}env {name = "ACCESS_TOKEN"value = var.access_token}resources {limits = {cpu = var.cpu_limitmemory = var.memory_limit}}}container_concurrency = var.container_concurrencytimeout_seconds = var.timeout_secondsgoogle_project_service.artifactregistry,google_artifact_registry_repository.docker,_REGISTRY_LOCATION = local.artifact_registry_location_REPO_NAME = local.artifact_registry_repo_IMAGE_NAME = var.service_name_PROJECT_ID = var.project_id_REGION = var.region}# Enable required APIsresource "google_project_service" "cloudbuild" {service = "cloudbuild.googleapis.com"project = var.project_iddisable_on_destroy = false}# Service account for Cloud Buildresource "google_service_account" "cloudbuild" {account_id = "cloudbuild-${var.service_name}"display_name = "Cloud Build Service Account for ${var.service_name}"project = var.project_id}# Grant permissions to Cloud Build service accountresource "google_project_iam_member" "cloudbuild_run" {project = var.project_idrole = "roles/run.admin"member = "serviceAccount:${google_service_account.cloudbuild.email}"}resource "google_project_iam_member" "cloudbuild_service_account_user" {project = var.project_idrole = "roles/iam.serviceAccountUser"# Artifact Registry repository for Docker imagesresource "google_artifact_registry_repository" "docker" {location = local.artifact_registry_locationrepository_id = local.artifact_registry_repodescription = "Docker repository for ${var.service_name}"format = "DOCKER"project = var.project_iddepends_on = [google_project_service.artifactregistry,]}# Cloud Build trigger for building Docker imagesresource "google_cloudbuild_trigger" "docker_build" {name = "${var.service_name}-build"description = "Build and push Docker image for ${var.service_name}"project = var.project_idgithub {owner = var.github_ownername = var.github_repopush {branch = "^${var.github_branch}$"member = "serviceAccount:${google_service_account.cloudbuild.email}"}resource "google_project_iam_member" "cloudbuild_logging" {project = var.project_idrole = "roles/logging.logWriter"member = "serviceAccount:${google_service_account.cloudbuild.email}"}# Artifact Registry permissions for Cloud Build to push imagesresource "google_project_iam_member" "cloudbuild_artifactregistry" {project = var.project_idrole = "roles/artifactregistry.writer"member = "serviceAccount:${google_service_account.cloudbuild.email}"}# Enable Artifact Registry APIresource "google_project_service" "artifactregistry" {service = "artifactregistry.googleapis.com"project = var.project_iddisable_on_destroy = false}# Build image URL for Artifact Registrybuilt_image = "${local.artifact_registry_location}-docker.pkg.dev/${var.project_id}/${local.artifact_registry_repo}/${var.service_name}"locals {artifact_registry_repo = "docker"# Use europe-west1 for all resources (Artifact Registry and Cloud Run)artifact_registry_location = "europe-west1"provider "google" {project = var.project_idregion = var.regiongoogle = {source = "hashicorp/google"version = "~> 7.9"
output "service_url" {}output "service_name" {}}}output "built_image_url" {description = "Built Docker image URL"value = "${local.built_image}:latest"}output "dashboard_url" {description = "GCP Console URL to view the service"value = "https://console.cloud.google.com/run/detail/${var.region}/${google_cloud_run_service.beats_api.name}?project=${var.project_id}"}output "custom_domain_url" {description = "Custom domain URL"value = "https://${var.custom_domain}"}output "cloud_build_trigger_id" {description = "Cloud Build trigger ID"value = google_cloudbuild_trigger.docker_build.trigger_idoutput "service_location" {description = "Region where the service is deployed"value = google_cloud_run_service.beats_api.locationdescription = "Name of the deployed Cloud Run service"value = google_cloud_run_service.beats_api.namedescription = "Public URL of the deployed Cloud Run service"value = google_cloud_run_service.beats_api.status[0].url
# Example Terraform variables file# Copy this to terraform.tfvars and fill in your actual values# WARNING: Never commit terraform.tfvars with real credentials!# MongoDB connection string from MongoDB Atlas or other providerdb_dsn = "mongodb+srv://username:password@cluster.mongodb.net/?retryWrites=true&w=majority"# Database namedb_name = "beats"# API access token for authenticationaccess_token = "your-secure-random-token-here"# Custom Domaincustom_domain = "beats.elghareeb.space"# Cloud SQL Configuration (optional)# Leave empty if not using Cloud SQL# Format: PROJECT:REGION:INSTANCEcloud_sql_instance = ""# Cloud Build Configurationgithub_owner = "lanterno" # Your GitHub username or orggithub_repo = "beats" # Your repository namegithub_branch = "main" # Branch to trigger builds on# Access Controlallow_unauthenticated = true # Set to false to require authentication# Container Configurationcontainer_port = 8080 # Cloud Run sets PORT env var automaticallycpu_limit = "1000m" # 1 vCPUmemory_limit = "512Mi"container_concurrency = 80 # Requests per instancetimeout_seconds = 300 # 5 minutesmin_instances = 0 # Scale to zero when idlemax_instances = 10# Service Configurationservice_name = "beats-api"# Google Cloud Configurationproject_id = "your-gcp-project-id"region = "us-central1" # Options: us-central1, us-east1, europe-west1, etc.
# Terraform Infrastructure for Beats API## Rerunning BuildsWhile Terraform manages the Cloud Build trigger configuration, it doesn't directly rerun builds. Here are your options:### Option 1: Manual Trigger via gcloud CLI (Recommended)1. Get the trigger ID from Terraform outputs:```bashcd terraformterraform output cloud_build_trigger_id```2. Trigger the build manually:```bashgcloud builds triggers run <TRIGGER_ID> \--project=beats-476914 \--branch=main```Or use the trigger name directly:```bashgcloud builds triggers run beats-api-build \--project=beats-476914 \--branch=main```### Option 2: Push to GitHubThe trigger is configured to automatically run on pushes to the `main` branch. Simply push any commit:```bashgit push origin main```### Option 3: View Build HistoryCheck recent builds:```bashgcloud builds list --project=beats-476914 --limit=10```Or use the Makefile:```bashmake build-status```### Option 4: View Build Logs in Terminal**View logs for the latest build:**```bashmake build-logs```**Stream logs in real-time (follow mode):**```bashmake build-logs-follow```**View logs for a specific build ID:**```bashmake build-logs-id BUILD_ID=<build-id># Example:make build-logs-id BUILD_ID=7b0567e9-b97c-402b-956d-e41fc0c5d9ee```**Using gcloud directly:**```bash# Get build ID from statusBUILD_ID=$(gcloud builds list --project=beats-476914 --limit=1 --format="value(id)")# View logsgcloud builds log $BUILD_ID --project=beats-476914# Stream logs (real-time)gcloud builds log $BUILD_ID --project=beats-476914 --stream```### Option 5: Trigger via Cloud Console1. Go to [Cloud Build Triggers](https://console.cloud.google.com/cloud-build/triggers)2. Find your trigger (`beats-api-build`)3. Click "Run" → Select branch → "Run"
type = string}type = string}# Service Configurationvariable "service_name" {type = stringdefault = "beats-api"}type = string}type = string}}}# Database Configurationvariable "db_dsn" {description = "MongoDB connection string (e.g., mongodb+srv://user:pass@cluster.mongodb.net)"type = stringsensitive = true}variable "db_name" {description = "MongoDB database name"type = stringdefault = "beats"}# Application Configurationvariable "access_token" {description = "API access token for authentication"type = stringsensitive = true}}type = stringdefault = ""}# Cloud Build Configurationvariable "github_owner" {description = "GitHub repository owner (username or organization)"type = stringdefault = "lanterno"}variable "github_repo" {description = "GitHub repository name"type = stringdefault = "beats"}variable "github_branch" {description = "GitHub branch to trigger builds on"type = stringdefault = "main"}# Cloud SQL Configuration (optional)variable "cloud_sql_instance" {description = "Cloud SQL instance connection name (format: PROJECT:REGION:INSTANCE). Leave empty if not using Cloud SQL."# Custom Domain Configurationvariable "custom_domain" {description = "Custom domain to attach to the Cloud Run service (e.g., beats.elghareeb.space)"type = stringvariable "allow_unauthenticated" {description = "Allow unauthenticated access to the service"type = booldefault = truevariable "timeout_seconds" {description = "Request timeout in seconds"type = numberdefault = 300}variable "min_instances" {description = "Minimum number of container instances to keep running"type = numberdefault = 0}variable "max_instances" {description = "Maximum number of container instances"type = numberdefault = 10default = "512Mi"}variable "container_concurrency" {description = "Maximum number of concurrent requests per container instance"type = numberdefault = 80variable "memory_limit" {description = "Memory limit per container instance"default = "1000m" # 1 vCPUvariable "container_port" {description = "Port the container listens on (Cloud Run sets PORT env var automatically)"type = numberdefault = 8080}variable "cpu_limit" {description = "CPU limit per container instance"description = "Name of the Cloud Run service"default = "us-central1"variable "region" {description = "GCP region to deploy to (e.g., us-central1, europe-west1)"# Google Cloud Configurationvariable "project_id" {description = "GCP Project ID"