diff --git a/envs/gcp/prod/main.tf b/envs/gcp/prod/main.tf index 99ad38b..74034be 100755 --- a/envs/gcp/prod/main.tf +++ b/envs/gcp/prod/main.tf @@ -7,20 +7,41 @@ locals { enable_apps = var.enable_apps enable_private_networking = var.enable_private_networking - vpc_connector_name = var.vpc_connector_name != "" ? var.vpc_connector_name : "${var.name_prefix}-cr-conn" + + # Strictly use app_name for resource naming (Convention over Configuration) + network_name = "${var.app_name}-vpc" + subnet_name = "${var.app_name}-subnet" + artifact_registry_repo = var.artifact_registry_repo != "" ? var.artifact_registry_repo : "${var.app_name}-repo" + gcs_bucket = "${var.app_name}-storage" + cloud_sql_instance_name = var.cloud_sql_instance_name != "" ? var.cloud_sql_instance_name : "${var.app_name}-db" + redis_instance_name = var.redis_instance_name != "" ? var.redis_instance_name : "${var.app_name}-redis" + filestore_instance_name = var.filestore_instance_name != "" ? var.filestore_instance_name : "${var.app_name}-fs" + app_service_name = "${var.app_name}-backend" + ui_service_name = "${var.app_name}-ui" + orion_service_name = "${var.app_name}-orion" + campsite_service_name = "${var.app_name}-campsite" + vpc_connector_name = "${var.app_name}-cr-conn" enable_lb = var.enable_lb lb_routing_plan = { domain = var.base_domain - default_backend = (var.ui_service_name != "" ? "ui" : "backend") + default_backend = "ui" backends = { backend = { - cloud_run_service = var.app_service_name + cloud_run_service = local.app_service_name region = var.region } ui = { - cloud_run_service = var.ui_service_name + cloud_run_service = local.ui_service_name + region = var.region + } + orion = { + cloud_run_service = local.orion_service_name + region = var.region + } + campsite = { + cloud_run_service = local.campsite_service_name region = var.region } } @@ -33,41 +54,44 @@ locals { } } -# GKE related modules disabled/removed, Cloud Run introduced for app service - +# Artifact Registry module module "artifact_registry" { count = local.enable_build_env ? 1 : 0 source = "../../../modules/gcp/artifact_registry" location = var.artifact_registry_location - repo_name = var.artifact_registry_repo + repo_name = local.artifact_registry_repo } +# Network module module "network" { count = local.enable_private_networking ? 1 : 0 source = "../../../modules/gcp/network" - name_prefix = var.name_prefix + app_name = var.app_name region = var.region - network_name = var.network_name - subnet_name = var.subnet_name + network_name = local.network_name + subnet_name = local.subnet_name subnet_cidr = var.subnet_cidr pods_secondary_range = var.pods_secondary_range services_secondary_range = var.services_secondary_range } +# IAM module module "iam" { source = "../../../modules/gcp/iam" project_id = var.project_id - prefix = coalesce(var.app_suffix, var.name_prefix) + app_name = coalesce(var.app_suffix, var.app_name) service_accounts = var.iam_service_accounts } +# Monitoring module module "monitoring" { source = "../../../modules/gcp/monitoring" project_id = var.project_id + app_name = var.app_name enable_logging = var.enable_logging enable_monitoring = var.enable_monitoring enable_alerts = var.enable_alerts @@ -76,21 +100,23 @@ module "monitoring" { log_sink_destination = var.log_sink_destination } +# GCS module module "gcs" { count = local.enable_gcs ? 1 : 0 source = "../../../modules/gcp/gcs" - name = var.gcs_bucket + name = local.gcs_bucket location = var.region force_destroy = var.gcs_force_destroy uniform_bucket_level_access = var.gcs_uniform_bucket_level_access } +# Cloud SQL module module "cloud_sql" { count = local.enable_cloud_sql ? 1 : 0 source = "../../../modules/gcp/cloud_sql" - name = var.cloud_sql_instance_name + name = local.cloud_sql_instance_name database_version = var.cloud_sql_database_version region = var.region tier = var.cloud_sql_tier @@ -108,11 +134,12 @@ module "cloud_sql" { deletion_protection = var.cloud_sql_deletion_protection } +# Redis module module "redis" { count = local.enable_redis ? 1 : 0 source = "../../../modules/gcp/redis" - name = var.redis_instance_name + name = local.redis_instance_name region = var.region tier = var.redis_tier memory_size_gb = var.redis_memory_size_gb @@ -120,20 +147,21 @@ module "redis" { transit_encryption_mode = var.redis_transit_encryption_mode } +# Filestore module module "filestore" { count = local.enable_filestore ? 1 : 0 source = "../../../modules/gcp/filestore" - name = var.filestore_instance_name + name = local.filestore_instance_name location = var.zone != "" ? var.zone : "${var.region}-b" - network = local.enable_private_networking ? module.network[0].network_self_link : "" + network = local.enable_private_networking ? module.network[0].network_id : "" tier = var.filestore_tier capacity_gb = var.filestore_capacity_gb file_share_name = var.filestore_file_share_name reserved_ip_range = var.filestore_reserved_ip_range } -# Serverless VPC Access Connector for Cloud Run private egress +# Serverless VPC Access Connector module "vpc_connector" { count = local.enable_private_networking ? 1 : 0 source = "../../../modules/gcp/vpc_connector" @@ -144,14 +172,14 @@ module "vpc_connector" { ip_cidr_range = var.vpc_connector_cidr } -# Cloud Run module for application service +# Cloud Run: Backend module "app_cloud_run" { count = local.enable_apps ? 1 : 0 source = "../../../modules/gcp/cloud_run" project_id = var.project_id region = var.region - service_name = var.app_service_name + service_name = local.app_service_name image = var.app_image env_vars = var.app_env @@ -160,18 +188,20 @@ module "app_cloud_run" { min_instances = var.app_min_instances max_instances = var.app_max_instances allow_unauth = var.app_allow_unauth + container_port = 8000 vpc_connector = local.enable_private_networking ? module.vpc_connector[0].name : null vpc_egress = var.cloud_run_vpc_egress } +# Cloud Run: UI module "ui_cloud_run" { - count = local.enable_apps && var.ui_service_name != "" ? 1 : 0 + count = local.enable_apps ? 1 : 0 source = "../../../modules/gcp/cloud_run" project_id = var.project_id region = var.region - service_name = var.ui_service_name + service_name = local.ui_service_name image = var.ui_image env_vars = var.ui_env_vars @@ -180,26 +210,71 @@ module "ui_cloud_run" { min_instances = var.ui_min_instances max_instances = var.ui_max_instances allow_unauth = var.ui_allow_unauth + container_port = 3000 + + vpc_connector = local.enable_private_networking ? module.vpc_connector[0].name : null + vpc_egress = var.cloud_run_vpc_egress +} + +# Cloud Run: Orion Server +module "orion_cloud_run" { + count = local.enable_apps && var.orion_image != "" ? 1 : 0 + source = "../../../modules/gcp/cloud_run" + + project_id = var.project_id + region = var.region + service_name = local.orion_service_name + image = var.orion_image + env_vars = var.orion_env_vars + + cpu = "1" + memory = "512Mi" + min_instances = 0 + max_instances = 10 + allow_unauth = true + container_port = 8000 vpc_connector = local.enable_private_networking ? module.vpc_connector[0].name : null vpc_egress = var.cloud_run_vpc_egress } +# Cloud Run: Campsite +module "campsite_cloud_run" { + count = local.enable_apps && var.campsite_image != "" ? 1 : 0 + source = "../../../modules/gcp/cloud_run" + + project_id = var.project_id + region = var.region + service_name = local.campsite_service_name + image = var.campsite_image + env_vars = var.campsite_env_vars + + cpu = "1" + memory = "512Mi" + min_instances = 0 + max_instances = 10 + allow_unauth = true + container_port = 8000 + + vpc_connector = local.enable_private_networking ? module.vpc_connector[0].name : null + vpc_egress = var.cloud_run_vpc_egress +} + +# Load Balancer module module "lb_backends" { count = var.enable_lb ? 1 : 0 source = "../../../modules/gcp/load_balancer" project_id = var.project_id region = var.region - name_prefix = var.name_prefix - backend_service_name = var.app_service_name - ui_service_name = var.ui_service_name + app_name = var.app_name + backend_service_name = local.app_service_name + ui_service_name = local.ui_service_name lb_domain = var.base_domain api_path_prefixes = var.lb_api_path_prefixes } -# Outputs adjusted (removed GKE related ones) - +# Outputs output "artifact_registry_repo" { value = local.enable_build_env ? module.artifact_registry[0].repository : null } @@ -241,7 +316,15 @@ output "app_cloud_run_url" { } output "ui_cloud_run_url" { - value = local.enable_apps && var.ui_service_name != "" ? module.ui_cloud_run[0].url : null + value = local.enable_apps ? module.ui_cloud_run[0].url : null +} + +output "orion_cloud_run_url" { + value = local.enable_apps && var.orion_image != "" ? module.orion_cloud_run[0].url : null +} + +output "campsite_cloud_run_url" { + value = local.enable_apps && var.campsite_image != "" ? module.campsite_cloud_run[0].url : null } output "lb_backend_backend_service" { diff --git a/envs/gcp/prod/terraform.tfvars.example b/envs/gcp/prod/terraform.tfvars.example index 67da18d..3b8cbc8 100755 --- a/envs/gcp/prod/terraform.tfvars.example +++ b/envs/gcp/prod/terraform.tfvars.example @@ -3,23 +3,31 @@ # Required project and domain project_id = "infra-20250121-20260121-0235" -base_domain = "buck2hub.com" +app_name = "mega" +base_domain = "buck2hub.com" # used for certificates / app config + +# Region / zone (optional overrides) +# region = "us-central1" +# zone = "us-central1-b" # GCS (object storage) gcs_bucket = "mega-prod-storage" +gcs_force_destroy = false # Artifact Registry (container images) enable_build_env = true artifact_registry_repo = "mega-prod" -# Cloud Run backend application -app_service_name = "mega-backend" -# Image path format: [LOCATION]-docker.pkg.dev/[PROJECT_ID]/[REPOSITORY_ID]/[IMAGE_NAME]:[TAG] -app_image = "us-central1-docker.pkg.dev/your-gcp-project-id/mega-prod/mega-backend:mono-0.1.0-pre-release" +# Cloud Run Images +# Note: GitHub Actions will push images directly to ${app_name}-repo. +app_image = "us-central1-docker.pkg.dev/your-gcp-project-id/mega-prod/mega:mono-0.1.0-pre-release" +ui_image = "us-central1-docker.pkg.dev/your-gcp-project-id/mega-prod/mega:mega-ui-staging-0.1.0-pre-release" +orion_image = "us-central1-docker.pkg.dev/your-gcp-project-id/mega-prod/mega:orion-server-0.1.0-pre-release" +campsite_image = "us-central1-docker.pkg.dev/your-gcp-project-id/mega-prod/mega:campsite-0.1.0-pre-release" + app_env = { RAILS_ENV = "production" RACK_ENV = "production" - # Optimized for SQLite on Cloud Run to avoid PoolTimedOut panic MEGA_DATABASE__DB_TYPE = "sqlite" MEGA_DATABASE__DB_PATH = "/tmp/mega.db" MEGA_DATABASE__MIN_CONNECTION = "1" @@ -27,16 +35,12 @@ app_env = { MEGA_DATABASE__ACQUIRE_TIMEOUT = "30" } -# Cloud Run UI (Next.js SSR) -ui_service_name = "mega-ui" -# Image path format: [LOCATION]-docker.pkg.dev/[PROJECT_ID]/[REPOSITORY_ID]/[IMAGE_NAME]:[TAG] -ui_image = "us-central1-docker.pkg.dev/your-gcp-project-id/mega-prod/mega-ui:mega-ui-staging-0.1.0-pre-release" ui_env_vars = { APP_ENV = "staging" } # Database (Cloud SQL) -cloud_sql_instance_name = "mega-prod-db" +cloud_sql_instance_name = "mega-db" cloud_sql_db_name = "mega" db_username = "mega_user" db_password = "your-prod-db-password" @@ -49,9 +53,12 @@ cloud_sql_enable_public_ip = true vpc_connector_cidr = "10.8.0.0/28" # Redis (Memorystore) -redis_instance_name = "mega-prod-redis" +redis_instance_name = "mega-redis" redis_tier = "BASIC" # no HA redis_memory_size_gb = 1 +# Filestore +filestore_instance_name = "mega-fs" + # Rails secrets (sensitive) rails_master_key = "your-prod-rails-master-key" diff --git a/envs/gcp/prod/variables.tf b/envs/gcp/prod/variables.tf index 8e7b829..4a4773b 100755 --- a/envs/gcp/prod/variables.tf +++ b/envs/gcp/prod/variables.tf @@ -1,7 +1,13 @@ -# NOTE: cleaned up variables, removed k8s/ingress legacy blocks - +# --- Project & App Identity --- variable "project_id" { - type = string + type = string + description = "GCP Project ID" +} + +variable "app_name" { + type = string + description = "The name of the application, used as a prefix for all resources" + default = "mega" } variable "region" { @@ -15,34 +21,19 @@ variable "zone" { default = "" } -variable "name_prefix" { - type = string - default = "mega-prod" -} - variable "base_domain" { - type = string - default = "" + type = string + description = "The FQDN for the application (e.g., buck2hub.com)" + default = "buck2hub.com" } +# --- Feature Flags --- variable "enable_build_env" { type = bool - description = "Enable Artifact Registry build environment (repository for Cloud Run images)." + description = "Enable Artifact Registry build environment." default = false } -variable "artifact_registry_location" { - type = string - default = "us-central1" -} - -variable "artifact_registry_repo" { - type = string - description = "Artifact Registry repository name" - default = "mega-prod" -} - - variable "enable_gcs" { type = bool default = true @@ -68,47 +59,13 @@ variable "enable_apps" { default = true } -variable "enable_logging" { - type = bool - default = true -} - -variable "enable_monitoring" { - type = bool - default = true -} - -variable "enable_alerts" { - type = bool - default = true -} - -variable "alert_notification_channels" { - type = list(string) - default = [] - description = "List of notification channel IDs for alerts" -} - -variable "log_sink_name" { - type = string - default = "" -} - -variable "log_sink_destination" { - type = string - default = "" -} - -variable "network_name" { - type = string - default = "mega-prod-net" -} - -variable "subnet_name" { - type = string - default = "mega-prod-subnet" +variable "enable_lb" { + type = bool + description = "Whether to enable Global HTTPS Load Balancer" + default = true } +# --- Network Configuration --- variable "subnet_cidr" { type = string default = "10.40.0.0/16" @@ -124,20 +81,14 @@ variable "services_secondary_range" { default = "10.42.0.0/16" } -# Private networking for Cloud SQL / Redis (Cloud Run -> VPC Connector) variable "enable_private_networking" { type = bool default = true } -variable "vpc_connector_name" { - type = string - default = "" -} - variable "vpc_connector_cidr" { type = string - default = null + default = "10.8.0.0/28" } variable "cloud_run_vpc_egress" { @@ -145,64 +96,70 @@ variable "cloud_run_vpc_egress" { default = "private-ranges-only" } +# --- Artifact Registry --- +variable "artifact_registry_location" { + type = string + default = "us-central1" +} + +variable "artifact_registry_repo" { + type = string + description = "Optional Artifact Registry repository name override." + default = "" +} + +# --- Storage (GCS) --- variable "gcs_bucket" { type = string - description = "GCS bucket name" + description = "Optional GCS bucket override. If empty, Terraform will derive a default name from app_name." default = "" } variable "gcs_force_destroy" { - type = bool - description = "Allow force deletion of bucket objects" - default = false + type = bool + default = false } variable "gcs_uniform_bucket_level_access" { - type = bool - description = "Enable uniform bucket-level access" - default = true + type = bool + default = true } +# --- Database (Cloud SQL) --- variable "cloud_sql_instance_name" { type = string - description = "Cloud SQL instance name" + description = "Optional Cloud SQL instance name override." default = "" } variable "cloud_sql_database_version" { - type = string - description = "Cloud SQL database version" - default = "POSTGRES_17" + type = string + default = "POSTGRES_17" } variable "cloud_sql_tier" { - type = string - description = "Cloud SQL instance tier" - default = "db-f1-micro" # smallest tier for this project + type = string + default = "db-f1-micro" } variable "cloud_sql_disk_size" { - type = number - description = "Cloud SQL disk size in GB" - default = 10 + type = number + default = 10 } variable "cloud_sql_disk_type" { - type = string - description = "Cloud SQL disk type" - default = "PD_SSD" + type = string + default = "PD_SSD" } variable "cloud_sql_availability_type" { - type = string - description = "Cloud SQL availability type" - default = "ZONAL" + type = string + default = "ZONAL" } variable "cloud_sql_private_ip_prefix_length" { - type = number - description = "Prefix length for private services range" - default = 16 + type = number + default = 16 } variable "cloud_sql_enable_private_service_connection" { @@ -217,7 +174,7 @@ variable "cloud_sql_enable_public_ip" { variable "cloud_sql_db_name" { type = string - default = "" + default = "mega" } variable "cloud_sql_backup_enabled" { @@ -230,9 +187,10 @@ variable "cloud_sql_deletion_protection" { default = true } +# --- Redis (Memorystore) --- variable "redis_instance_name" { type = string - description = "Memorystore instance name" + description = "Optional Redis instance name override." default = "" } @@ -251,9 +209,11 @@ variable "redis_transit_encryption_mode" { default = "DISABLED" } +# --- Filestore --- variable "filestore_instance_name" { - type = string - default = "" + type = string + description = "Optional Filestore instance name override." + default = "" } variable "filestore_tier" { @@ -276,23 +236,15 @@ variable "filestore_reserved_ip_range" { default = null } -# Cloud Run application variables -variable "app_service_name" { - type = string - description = "Cloud Run service name" - default = "" -} - +# --- Cloud Run: Backend App --- variable "app_image" { - type = string - description = "Container image" - default = "" + type = string + default = "" } variable "app_env" { - type = map(string) - description = "Environment variables for Cloud Run" - default = {} + type = map(string) + default = {} } variable "app_cpu" { @@ -320,128 +272,149 @@ variable "app_allow_unauth" { default = true } -variable "storage_key" { - type = string - sensitive = true - default = "" +# --- Cloud Run: UI --- +variable "ui_image" { + type = string + default = "" } -variable "storage_secret_key" { - type = string - sensitive = true - default = "" +variable "ui_env_vars" { + type = map(string) + default = {} } -variable "storage_bucket" { +variable "ui_cpu" { type = string - default = "" + default = "1" } -variable "db_username" { - type = string - sensitive = true - default = "" +variable "ui_memory" { + type = string + default = "512Mi" } -variable "db_password" { - type = string - sensitive = true - default = "" +variable "ui_min_instances" { + type = number + default = 0 } -variable "db_schema" { - type = string - default = "" +variable "ui_max_instances" { + type = number + default = 10 } -variable "rails_master_key" { - type = string - sensitive = true - default = "" +variable "ui_allow_unauth" { + type = bool + default = true } -variable "rails_env" { +# --- Cloud Run: Orion Server --- +variable "orion_image" { type = string default = "" } -variable "ui_env" { +variable "orion_env_vars" { + type = map(string) + default = {} +} + +# --- Cloud Run: Campsite --- +variable "campsite_image" { type = string default = "" } -# Cloud Run UI variables -variable "ui_service_name" { - type = string - description = "Cloud Run service name for UI" - default = "" +variable "campsite_env_vars" { + type = map(string) + default = {} } -variable "ui_image" { - type = string - description = "Container image for UI" - default = "" +# --- IAM & Service Accounts --- +variable "app_suffix" { + type = string + default = "" } -variable "ui_env_vars" { - type = map(string) - description = "Environment variables for UI Cloud Run" +variable "iam_service_accounts" { + type = map(object({ + display_name = optional(string) + description = optional(string) + roles = optional(list(string), []) + wi_bindings = optional(list(object({ + namespace = string + k8s_service_account_name = string + })), []) + })) default = {} + description = "Service accounts to create and their IAM roles" } -variable "ui_cpu" { - type = string - default = "1" +# --- Monitoring & Logging --- +variable "enable_logging" { + type = bool + default = true } -variable "ui_memory" { - type = string - default = "512Mi" +variable "enable_monitoring" { + type = bool + default = true } -variable "ui_min_instances" { - type = number - default = 0 +variable "enable_alerts" { + type = bool + default = true } -variable "ui_max_instances" { - type = number - default = 10 +variable "alert_notification_channels" { + type = list(string) + default = [] } -variable "ui_allow_unauth" { - type = bool - default = true +variable "log_sink_name" { + type = string + default = "" } -# HTTPS Load Balancer & Routing Strategy (Milestone A) -variable "enable_lb" { - type = bool - description = "Whether to enable Global HTTPS Load Balancer" - default = true +variable "log_sink_destination" { + type = string + default = "" } - +# --- Load Balancer Routing --- variable "lb_api_path_prefixes" { type = list(string) description = "URL path prefixes to be routed to the backend service" default = ["/api/v1", "/info/lfs"] } -variable "app_suffix" { - type = string - default = "" +# --- Secrets (Sensitive Variables) --- +variable "db_username" { + type = string + sensitive = true + default = "" } -variable "iam_service_accounts" { - type = map(object({ - display_name = optional(string) - description = optional(string) - roles = optional(list(string), []) - wi_bindings = optional(list(object({ - namespace = string - k8s_service_account_name = string - })), []) - })) - default = {} +variable "db_password" { + type = string + sensitive = true + default = "" +} + +variable "rails_master_key" { + type = string + sensitive = true + default = "" +} + +variable "storage_key" { + type = string + sensitive = true + default = "" +} + +variable "storage_secret_key" { + type = string + sensitive = true + default = "" } diff --git a/modules/gcp/cloud_run/main.tf b/modules/gcp/cloud_run/main.tf index 00d9549..7a1aa2d 100755 --- a/modules/gcp/cloud_run/main.tf +++ b/modules/gcp/cloud_run/main.tf @@ -1,7 +1,19 @@ -variable "project_id" { type = string } -variable "service_name" { type = string } -variable "region" { type = string } -variable "image" { type = string } +variable "project_id" { + type = string +} + +variable "service_name" { + type = string +} + +variable "region" { + type = string +} + +variable "image" { + type = string +} + variable "env_vars" { type = map(string) default = {} @@ -37,6 +49,11 @@ variable "allow_unauth" { default = true } +variable "container_port" { + type = number + default = 8080 +} + variable "vpc_connector" { type = string default = null @@ -57,6 +74,9 @@ resource "google_cloud_run_service" "this" { spec { containers { image = var.image + ports { + container_port = var.container_port + } resources { limits = { cpu = var.cpu @@ -75,8 +95,8 @@ resource "google_cloud_run_service" "this" { metadata { annotations = merge( { - "autoscaling.knative.dev/minScale" = tostring(var.min_instances) - "autoscaling.knative.dev/maxScale" = tostring(var.max_instances) + "autoscaling.knative.dev/minScale" = tostring(var.min_instances) + "autoscaling.knative.dev/maxScale" = tostring(var.max_instances) }, var.vpc_connector != null ? { "run.googleapis.com/vpc-access-connector" = var.vpc_connector @@ -106,4 +126,4 @@ resource "google_cloud_run_service_iam_member" "invoker" { output "url" { value = google_cloud_run_service.this.status[0].url -} +} \ No newline at end of file diff --git a/modules/gcp/iam/main.tf b/modules/gcp/iam/main.tf index ab2f3ce..ed7b2d3 100755 --- a/modules/gcp/iam/main.tf +++ b/modules/gcp/iam/main.tf @@ -5,7 +5,7 @@ locals { resource "google_service_account" "this" { for_each = local.service_accounts - account_id = "${var.prefix}-${each.key}" + account_id = "${var.app_name}-${each.key}" display_name = coalesce(try(each.value.display_name, null), each.key) description = try(each.value.description, null) } diff --git a/modules/gcp/iam/variables.tf b/modules/gcp/iam/variables.tf index 54e5369..48acfe1 100755 --- a/modules/gcp/iam/variables.tf +++ b/modules/gcp/iam/variables.tf @@ -3,10 +3,9 @@ variable "project_id" { description = "GCP project ID" } -variable "prefix" { +variable "app_name" { type = string - description = "Prefix for resource names" - default = "mega" + description = "The name of the application, used as a prefix for all resources" } variable "service_accounts" { diff --git a/modules/gcp/load_balancer/main.tf b/modules/gcp/load_balancer/main.tf index b10f139..9cf8b8f 100755 --- a/modules/gcp/load_balancer/main.tf +++ b/modules/gcp/load_balancer/main.tf @@ -6,8 +6,9 @@ variable "region" { type = string } -variable "name_prefix" { - type = string +variable "app_name" { + type = string + description = "The name of the application, used as a prefix for all resources" } variable "backend_service_name" { @@ -35,7 +36,7 @@ variable "api_path_prefixes" { resource "google_compute_region_network_endpoint_group" "backend" { project = var.project_id region = var.region - name = "${var.name_prefix}-backend-neg" + name = "${var.app_name}-backend-neg" network_endpoint_type = "SERVERLESS" cloud_run { @@ -47,7 +48,7 @@ resource "google_compute_region_network_endpoint_group" "ui" { count = var.ui_service_name != "" ? 1 : 0 project = var.project_id region = var.region - name = "${var.name_prefix}-ui-neg" + name = "${var.app_name}-ui-neg" network_endpoint_type = "SERVERLESS" cloud_run { @@ -57,7 +58,7 @@ resource "google_compute_region_network_endpoint_group" "ui" { resource "google_compute_backend_service" "backend" { project = var.project_id - name = "${var.name_prefix}-backend-bs" + name = "${var.app_name}-backend-bs" protocol = "HTTP" load_balancing_scheme = "EXTERNAL_MANAGED" @@ -69,7 +70,7 @@ resource "google_compute_backend_service" "backend" { resource "google_compute_backend_service" "ui" { count = var.ui_service_name != "" ? 1 : 0 project = var.project_id - name = "${var.name_prefix}-ui-bs" + name = "${var.app_name}-ui-bs" protocol = "HTTP" load_balancing_scheme = "EXTERNAL_MANAGED" @@ -80,7 +81,7 @@ resource "google_compute_backend_service" "ui" { resource "google_compute_url_map" "this" { project = var.project_id - name = "${var.name_prefix}-urlmap" + name = "${var.app_name}-urlmap" default_service = var.ui_service_name != "" ? google_compute_backend_service.ui[0].self_link : google_compute_backend_service.backend.self_link @@ -105,19 +106,19 @@ resource "google_compute_url_map" "this" { resource "google_compute_global_address" "this" { project = var.project_id - name = "${var.name_prefix}-lb-ip" + name = "${var.app_name}-lb-ip" } resource "google_compute_target_https_proxy" "this" { project = var.project_id - name = "${var.name_prefix}-https-proxy" + name = "${var.app_name}-https-proxy" url_map = google_compute_url_map.this.id certificate_map = "//certificatemanager.googleapis.com/${google_certificate_manager_certificate_map.this.id}" } resource "google_compute_global_forwarding_rule" "https" { project = var.project_id - name = "${var.name_prefix}-https-fr" + name = "${var.app_name}-https-fr" target = google_compute_target_https_proxy.this.id port_range = "443" ip_address = google_compute_global_address.this.address @@ -126,14 +127,14 @@ resource "google_compute_global_forwarding_rule" "https" { resource "google_certificate_manager_dns_authorization" "this" { project = var.project_id - name = "${var.name_prefix}-dns-auth" + name = "${var.app_name}-dns-auth" domain = var.lb_domain description = "DNS authorization for ${var.lb_domain}" } resource "google_certificate_manager_certificate" "this" { project = var.project_id - name = "${var.name_prefix}-cert" + name = "${var.app_name}-cert" description = "Google-managed cert for ${var.lb_domain}" scope = "DEFAULT" managed { @@ -146,13 +147,13 @@ resource "google_certificate_manager_certificate" "this" { resource "google_certificate_manager_certificate_map" "this" { project = var.project_id - name = "${var.name_prefix}-cert-map" + name = "${var.app_name}-cert-map" description = "Certificate map for ${var.lb_domain}" } resource "google_certificate_manager_certificate_map_entry" "this" { project = var.project_id - name = "${var.name_prefix}-cert-map-entry" + name = "${var.app_name}-cert-map-entry" map = google_certificate_manager_certificate_map.this.name certificates = [google_certificate_manager_certificate.this.id] hostname = var.lb_domain diff --git a/modules/gcp/monitoring/main.tf b/modules/gcp/monitoring/main.tf index 9965bae..8d20024 100755 --- a/modules/gcp/monitoring/main.tf +++ b/modules/gcp/monitoring/main.tf @@ -17,44 +17,44 @@ resource "google_project_service" "monitoring" { } resource "google_logging_project_sink" "this" { - count = var.log_sink_name != "" && var.log_sink_destination != "" ? 1 : 0 - name = var.log_sink_name + count = var.enable_logging && var.log_sink_destination != "" ? 1 : 0 + name = var.log_sink_name != "" ? var.log_sink_name : "${var.app_name}-log-sink" project = var.project_id destination = var.log_sink_destination - filter = "resource.type=\"k8s_container\"" + filter = "resource.type=\"cloud_run_revision\"" } resource "google_monitoring_alert_policy" "pod_restart_high" { count = local.enable_alerts ? 1 : 0 - display_name = "GKE Pod Restart Rate High" + display_name = "${var.app_name} Cloud Run Instance Restart Rate High" combiner = "OR" enabled = true notification_channels = var.alert_notification_channels conditions { - display_name = "Pod restart rate > 5/min" + display_name = "Container restart rate > 5/min" condition_threshold { - filter = "metric.type=\"kubernetes.io/container/restart_count\" resource.type=\"k8s_container\"" + filter = "metric.type=\"run.googleapis.com/container/instance_count\" resource.type=\"cloud_run_revision\"" duration = "300s" comparison = "COMPARISON_GT" threshold_value = 5 aggregations { alignment_period = "60s" - per_series_aligner = "ALIGN_RATE" - cross_series_reducer = "REDUCE_SUM" - group_by_fields = ["resource.label.namespace_name", "resource.label.pod_name", "resource.label.container_name"] + per_series_aligner = "ALIGN_MEAN" + cross_series_reducer = "REDUCE_MAX" + group_by_fields = ["resource.label.service_name", "resource.label.location"] } } } documentation { - content = "High pod restart rate detected. Check pod logs and events." + content = "High instance restart rate detected for ${var.app_name}. Check Cloud Run logs and events." } } resource "google_monitoring_alert_policy" "sql_connection_failures" { count = local.enable_alerts ? 1 : 0 - display_name = "Cloud SQL Connection Failures" + display_name = "${var.app_name} Cloud SQL Connection Failures" combiner = "OR" enabled = true notification_channels = var.alert_notification_channels diff --git a/modules/gcp/monitoring/variables.tf b/modules/gcp/monitoring/variables.tf index 1fe5f97..b875a5b 100755 --- a/modules/gcp/monitoring/variables.tf +++ b/modules/gcp/monitoring/variables.tf @@ -3,6 +3,11 @@ variable "project_id" { description = "GCP project ID" } +variable "app_name" { + type = string + description = "The name of the application, used as a prefix for all resources" +} + variable "enable_logging" { type = bool default = true diff --git a/modules/gcp/network/main.tf b/modules/gcp/network/main.tf index eb14e5f..2c0cef0 100755 --- a/modules/gcp/network/main.tf +++ b/modules/gcp/network/main.tf @@ -6,7 +6,7 @@ locals { public_subnet_map = { for idx, cidr in var.public_subnet_cidrs : idx => cidr } private_subnet_map = { for idx, cidr in var.private_subnet_cidrs : idx => cidr } - default_gke_node_tags = ["${var.name_prefix}-gke"] + default_gke_node_tags = ["${var.app_name}-gke"] effective_gke_node_tags = length(var.gke_node_tags) > 0 ? var.gke_node_tags : local.default_gke_node_tags health_check_port_numbers = [for p in var.health_check_ports : tonumber(p)] @@ -27,12 +27,12 @@ resource "google_compute_subnetwork" "this" { network = google_compute_network.this.id secondary_ip_range { - range_name = "${var.name_prefix}-pods" + range_name = "${var.app_name}-pods" ip_cidr_range = var.pods_secondary_range } secondary_ip_range { - range_name = "${var.name_prefix}-services" + range_name = "${var.app_name}-services" ip_cidr_range = var.services_secondary_range } } @@ -59,7 +59,7 @@ resource "google_compute_subnetwork" "private" { resource "google_compute_router" "this" { count = var.create_nat && (local.use_multi_subnets ? local.has_private_subnets : true) ? 1 : 0 - name = "${var.name_prefix}-gke-router" + name = "${var.app_name}-router" network = google_compute_network.this.id region = var.region } @@ -67,7 +67,7 @@ resource "google_compute_router" "this" { resource "google_compute_router_nat" "this" { count = var.create_nat && (local.use_multi_subnets ? local.has_private_subnets : true) ? 1 : 0 - name = "${var.name_prefix}-gke-nat" + name = "${var.app_name}-nat" router = google_compute_router.this[0].name region = var.region nat_ip_allocate_option = "AUTO_ONLY" diff --git a/modules/gcp/network/outputs.tf b/modules/gcp/network/outputs.tf index 3eb63ad..6ead68b 100755 --- a/modules/gcp/network/outputs.tf +++ b/modules/gcp/network/outputs.tf @@ -21,16 +21,16 @@ output "subnetwork_name" { output "pods_secondary_range_name" { value = local.use_multi_subnets ? ( length(google_compute_subnetwork.private) > 0 ? ( - length(google_compute_subnetwork.private[0].secondary_ip_range) > 0 ? google_compute_subnetwork.private[0].secondary_ip_range[0].range_name : "${var.name_prefix}-pods" - ) : "${var.name_prefix}-pods" + length(google_compute_subnetwork.private[0].secondary_ip_range) > 0 ? google_compute_subnetwork.private[0].secondary_ip_range[0].range_name : "${var.app_name}-pods" + ) : "${var.app_name}-pods" ) : google_compute_subnetwork.this[0].secondary_ip_range[0].range_name } output "services_secondary_range_name" { value = local.use_multi_subnets ? ( length(google_compute_subnetwork.private) > 0 ? ( - length(google_compute_subnetwork.private[0].secondary_ip_range) > 1 ? google_compute_subnetwork.private[0].secondary_ip_range[1].range_name : "${var.name_prefix}-services" - ) : "${var.name_prefix}-services" + length(google_compute_subnetwork.private[0].secondary_ip_range) > 1 ? google_compute_subnetwork.private[0].secondary_ip_range[1].range_name : "${var.app_name}-services" + ) : "${var.app_name}-services" ) : google_compute_subnetwork.this[0].secondary_ip_range[1].range_name } diff --git a/modules/gcp/network/variables.tf b/modules/gcp/network/variables.tf index 038463d..9be92dc 100755 --- a/modules/gcp/network/variables.tf +++ b/modules/gcp/network/variables.tf @@ -1,4 +1,4 @@ -variable "name_prefix" { +variable "app_name" { type = string } diff --git a/scripts/sync_images_to_ar.sh b/scripts/sync_images_to_ar.sh deleted file mode 100755 index 5b91e1f..0000000 --- a/scripts/sync_images_to_ar.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -PROJECT_ID="infra-20250121-20260121-0235" -LOCATION="us-central1" -REPO="mega-prod" - -# Source images (ECR Public) -BACKEND_SRC="public.ecr.aws/m8q5m4u3/mega:mono-0.1.0-pre-release" -UI_SRC="public.ecr.aws/m8q5m4u3/mega:mega-ui-staging-0.1.0-pre-release" - -# Target images (Artifact Registry) -BACKEND_DST="${LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/mega-backend:mono-0.1.0-pre-release" -UI_DST="${LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/mega-ui:mega-ui-staging-0.1.0-pre-release" - -# Ensure auth for Artifact Registry -gcloud auth configure-docker "${LOCATION}-docker.pkg.dev" --quiet - -echo "Pulling source images..." -docker pull "${BACKEND_SRC}" -docker pull "${UI_SRC}" - -echo "Tagging for Artifact Registry..." -docker tag "${BACKEND_SRC}" "${BACKEND_DST}" -docker tag "${UI_SRC}" "${UI_DST}" - -echo "Pushing to Artifact Registry..." -docker push "${BACKEND_DST}" -docker push "${UI_DST}" - -echo "Done." -echo "Backend image: ${BACKEND_DST}" -echo "UI image: ${UI_DST}" -