[ci skip] Phase 3: Create 66 service stacks and migrate state
Generated individual stack directories for all 66 services under stacks/. Each stack has terragrunt.hcl (depends on platform) and main.tf (thin wrapper calling existing module). Migrated all 64 active service states from root terraform.tfstate to individual state files. Root state is now empty. Verified with terragrunt plan on multiple stacks (no changes).
This commit is contained in:
parent
6b7909d94c
commit
c01c2729a3
134 changed files with 2426 additions and 0 deletions
535
scripts/gen_service_stacks.py
Normal file
535
scripts/gen_service_stacks.py
Normal file
|
|
@ -0,0 +1,535 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Generate Terragrunt service stack files for all app-level services."""
|
||||
import os
|
||||
import textwrap
|
||||
|
||||
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# Each service: (module_name, source_dir, [(arg_name, var_expr), ...], tier)
|
||||
# var_expr is what goes on the right side of = in the module call.
|
||||
# If var_expr starts with "var.", it's a variable passthrough and we declare the variable.
|
||||
# If it's a literal string, we inline it.
|
||||
# Special: "LOCAL_TIER" means we use local.tiers.<tier>
|
||||
SERVICES = [
|
||||
("blog", "blog", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("descheduler", "descheduler", []),
|
||||
("drone", "drone", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("github_client_id", "var.drone_github_client_id"),
|
||||
("github_client_secret", "var.drone_github_client_secret"),
|
||||
("rpc_secret", "var.drone_rpc_secret"),
|
||||
("webhook_secret", "var.drone_webhook_secret"),
|
||||
("server_host", '"drone.viktorbarzin.me"'),
|
||||
("server_proto", '"https"'),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("f1-stream", "f1-stream", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
("turn_secret", "var.coturn_turn_secret"),
|
||||
("public_ip", "var.public_ip"),
|
||||
]),
|
||||
("coturn", "coturn", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
("turn_secret", "var.coturn_turn_secret"),
|
||||
("public_ip", "var.public_ip"),
|
||||
]),
|
||||
("hackmd", "hackmd", [
|
||||
("hackmd_db_password", "var.hackmd_db_password"),
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("kms", "kms", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("k8s-dashboard", "k8s-dashboard", [
|
||||
("tier", "LOCAL_TIER:cluster"),
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("client_certificate_secret_name", "var.client_certificate_secret_name"),
|
||||
]),
|
||||
("privatebin", "privatebin", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("reloader", "reloader", [
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("shadowsocks", "shadowsocks", [
|
||||
("password", "var.shadowsocks_password"),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("city-guesser", "city-guesser", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("echo", "echo", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("url", "url-shortener", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("geolite_license_key", "var.url_shortener_geolite_license_key"),
|
||||
("api_key", "var.url_shortener_api_key"),
|
||||
("mysql_password", "var.url_shortener_mysql_password"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("webhook_handler", "webhook_handler", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("webhook_secret", "var.webhook_handler_secret"),
|
||||
("fb_verify_token", "var.webhook_handler_fb_verify_token"),
|
||||
("fb_page_token", "var.webhook_handler_fb_page_token"),
|
||||
("fb_app_secret", "var.webhook_handler_fb_app_secret"),
|
||||
("git_user", "var.webhook_handler_git_user"),
|
||||
("git_token", "var.webhook_handler_git_token"),
|
||||
("ssh_key", "var.webhook_handler_ssh_key"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("excalidraw", "excalidraw", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("travel_blog", "travel_blog", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("dashy", "dashy", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("send", "send", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("ytdlp", "youtube_dl", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
("openrouter_api_key", "var.openrouter_api_key"),
|
||||
("slack_bot_token", "var.slack_bot_token"),
|
||||
("slack_channel", "var.slack_channel"),
|
||||
]),
|
||||
("immich", "immich", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("postgresql_password", "var.immich_postgresql_password"),
|
||||
("frame_api_key", "var.immich_frame_api_key"),
|
||||
("homepage_token", 'var.homepage_credentials["immich"]["token"]'),
|
||||
("tier", "LOCAL_TIER:gpu"),
|
||||
]),
|
||||
("resume", "resume", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
("database_url", "var.resume_database_url"),
|
||||
("auth_secret", "var.resume_auth_secret"),
|
||||
("smtp_password", 'var.mailserver_accounts["info@viktorbarzin.me"]'),
|
||||
]),
|
||||
("calibre", "calibre", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("homepage_username", 'var.homepage_credentials["calibre-web"]["username"]'),
|
||||
("homepage_password", 'var.homepage_credentials["calibre-web"]["password"]'),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("audiobookshelf", "audiobookshelf", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("frigate", "frigate", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:gpu"),
|
||||
]),
|
||||
("paperless-ngx", "paperless-ngx", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("db_password", "var.paperless_db_password"),
|
||||
("homepage_username", 'var.homepage_credentials["paperless-ngx"]["username"]'),
|
||||
("homepage_password", 'var.homepage_credentials["paperless-ngx"]["password"]'),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("jsoncrack", "jsoncrack", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("servarr", "servarr", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
("aiostreams_database_connection_string", "var.aiostreams_database_connection_string"),
|
||||
]),
|
||||
("ollama", "ollama", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:gpu"),
|
||||
("ollama_api_credentials", "var.ollama_api_credentials"),
|
||||
]),
|
||||
("ntfy", "ntfy", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("cyberchef", "cyberchef", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("diun", "diun", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("diun_nfty_token", "var.diun_nfty_token"),
|
||||
("diun_slack_url", "var.diun_slack_url"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("meshcentral", "meshcentral", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("netbox", "netbox", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("nextcloud", "nextcloud", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("db_password", "var.nextcloud_db_password"),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("homepage", "homepage", [
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
]),
|
||||
("matrix", "matrix", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("linkwarden", "linkwarden", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("postgresql_password", "var.linkwarden_postgresql_password"),
|
||||
("authentik_client_id", "var.linkwarden_authentik_client_id"),
|
||||
("authentik_client_secret", "var.linkwarden_authentik_client_secret"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("actualbudget", "actualbudget", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
("credentials", "var.actualbudget_credentials"),
|
||||
]),
|
||||
("owntracks", "owntracks", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("owntracks_credentials", "var.owntracks_credentials"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("dawarich", "dawarich", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("database_password", "var.dawarich_database_password"),
|
||||
("geoapify_api_key", "var.geoapify_api_key"),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("changedetection", "changedetection", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("tandoor", "tandoor", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tandoor_database_password", "var.tandoor_database_password"),
|
||||
("tandoor_email_password", "var.tandoor_email_password"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("n8n", "n8n", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("postgresql_password", "var.n8n_postgresql_password"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("real-estate-crawler", "real-estate-crawler", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("db_password", "var.realestate_crawler_db_password"),
|
||||
("notification_settings", "var.realestate_crawler_notification_settings"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("osm_routing", "osm-routing", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("tor-proxy", "tor-proxy", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("onlyoffice", "onlyoffice", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("db_password", "var.onlyoffice_db_password"),
|
||||
("jwt_token", "var.onlyoffice_jwt_token"),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("forgejo", "forgejo", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("freshrss", "freshrss", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("navidrome", "navidrome", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("networking-toolbox", "networking-toolbox", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("tuya-bridge", "tuya-bridge", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:cluster"),
|
||||
("tiny_tuya_api_key", "var.tiny_tuya_api_key"),
|
||||
("tiny_tuya_api_secret", "var.tiny_tuya_api_secret"),
|
||||
("tiny_tuya_service_secret", "var.tiny_tuya_service_secret"),
|
||||
("slack_url", "var.tiny_tuya_slack_url"),
|
||||
]),
|
||||
("stirling-pdf", "stirling-pdf", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("isponsorblocktv", "isponsorblocktv", [
|
||||
("tier", "LOCAL_TIER:edge"),
|
||||
]),
|
||||
("ebook2audiobook", "ebook2audiobook", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:gpu"),
|
||||
]),
|
||||
("rybbit", "rybbit", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("clickhouse_password", "var.clickhouse_password"),
|
||||
("postgres_password", "var.clickhouse_postgres_password"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("wealthfolio", "wealthfolio", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("wealthfolio_password_hash", "var.wealthfolio_password_hash"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("speedtest", "speedtest", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
("db_password", "var.speedtest_db_password"),
|
||||
]),
|
||||
("freedify", "freedify", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
("additional_credentials", "var.freedify_credentials"),
|
||||
]),
|
||||
("affine", "affine", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("postgresql_password", "var.affine_postgresql_password"),
|
||||
("smtp_password", 'var.mailserver_accounts["info@viktorbarzin.me"]'),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("plotting-book", "plotting-book", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("health", "health", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("postgresql_password", "var.health_postgresql_password"),
|
||||
("secret_key", "var.health_secret_key"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("whisper", "whisper", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("tier", "LOCAL_TIER:gpu"),
|
||||
]),
|
||||
("grampsweb", "grampsweb", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("smtp_password", 'var.mailserver_accounts["info@viktorbarzin.me"]'),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
("openclaw", "openclaw", [
|
||||
("tls_secret_name", "var.tls_secret_name"),
|
||||
("ssh_key", "var.openclaw_ssh_key"),
|
||||
("skill_secrets", "var.openclaw_skill_secrets"),
|
||||
("gemini_api_key", "var.gemini_api_key"),
|
||||
("llama_api_key", "var.llama_api_key"),
|
||||
("brave_api_key", "var.brave_api_key"),
|
||||
("modal_api_key", "var.modal_api_key"),
|
||||
("tier", "LOCAL_TIER:aux"),
|
||||
]),
|
||||
]
|
||||
|
||||
# Variable type overrides (var_name -> type declaration)
|
||||
VAR_TYPES = {
|
||||
"tls_secret_name": "string",
|
||||
"client_certificate_secret_name": "string",
|
||||
"public_ip": "string",
|
||||
"hackmd_db_password": "string",
|
||||
"shadowsocks_password": "string",
|
||||
"openrouter_api_key": "string",
|
||||
"slack_bot_token": "string",
|
||||
"slack_channel": "string",
|
||||
"ollama_api_credentials": "string",
|
||||
"clickhouse_password": "string",
|
||||
"clickhouse_postgres_password": "string",
|
||||
"wealthfolio_password_hash": "string",
|
||||
"speedtest_db_password": "string",
|
||||
"affine_postgresql_password": "string",
|
||||
"health_postgresql_password": "string",
|
||||
"health_secret_key": "string",
|
||||
"gemini_api_key": "string",
|
||||
"llama_api_key": "string",
|
||||
"brave_api_key": "string",
|
||||
"modal_api_key": "string",
|
||||
"coturn_turn_secret": "string",
|
||||
"onlyoffice_db_password": "string",
|
||||
"onlyoffice_jwt_token": "string",
|
||||
"resume_database_url": "string",
|
||||
"resume_auth_secret": "string",
|
||||
"nextcloud_db_password": "string",
|
||||
"paperless_db_password": "string",
|
||||
"diun_nfty_token": "string",
|
||||
"diun_slack_url": "string",
|
||||
"dawarich_database_password": "string",
|
||||
"geoapify_api_key": "string",
|
||||
"tandoor_database_password": "string",
|
||||
"tandoor_email_password": "string",
|
||||
"n8n_postgresql_password": "string",
|
||||
"realestate_crawler_db_password": "string",
|
||||
"immich_postgresql_password": "string",
|
||||
"immich_frame_api_key": "string",
|
||||
"linkwarden_postgresql_password": "string",
|
||||
"linkwarden_authentik_client_id": "string",
|
||||
"linkwarden_authentik_client_secret": "string",
|
||||
"aiostreams_database_connection_string": "string",
|
||||
"tiny_tuya_api_key": "string",
|
||||
"tiny_tuya_api_secret": "string",
|
||||
"tiny_tuya_service_secret": "string",
|
||||
"tiny_tuya_slack_url": "string",
|
||||
"drone_github_client_id": "string",
|
||||
"drone_github_client_secret": "string",
|
||||
"drone_rpc_secret": "string",
|
||||
"drone_webhook_secret": "string",
|
||||
"url_shortener_geolite_license_key": "string",
|
||||
"url_shortener_api_key": "string",
|
||||
"url_shortener_mysql_password": "string",
|
||||
"webhook_handler_secret": "string",
|
||||
"webhook_handler_fb_verify_token": "string",
|
||||
"webhook_handler_fb_page_token": "string",
|
||||
"webhook_handler_fb_app_secret": "string",
|
||||
"webhook_handler_git_user": "string",
|
||||
"webhook_handler_git_token": "string",
|
||||
"webhook_handler_ssh_key": "string",
|
||||
"openclaw_ssh_key": "string",
|
||||
"openclaw_skill_secrets": "map(string)",
|
||||
"actualbudget_credentials": "map(any)",
|
||||
"freedify_credentials": "map(any)",
|
||||
"realestate_crawler_notification_settings": "map(string)",
|
||||
"homepage_credentials": "map(any)",
|
||||
"mailserver_accounts": "map(any)",
|
||||
"owntracks_credentials": "string",
|
||||
}
|
||||
|
||||
TERRAGRUNT_HCL = """\
|
||||
include "root" {
|
||||
path = find_in_parent_folders()
|
||||
}
|
||||
|
||||
dependency "platform" {
|
||||
config_path = "../platform"
|
||||
skip_outputs = true
|
||||
}
|
||||
"""
|
||||
|
||||
TIERS_BLOCK = """\
|
||||
locals {
|
||||
tiers = {
|
||||
core = "0-core"
|
||||
cluster = "1-cluster"
|
||||
gpu = "2-gpu"
|
||||
edge = "3-edge"
|
||||
aux = "4-aux"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def extract_var_name(expr):
|
||||
"""Extract variable name from var.xxx or var.xxx["yyy"]["zzz"]."""
|
||||
if not expr.startswith("var."):
|
||||
return None
|
||||
# Get the base variable name (before any indexing)
|
||||
name = expr[4:]
|
||||
bracket = name.find("[")
|
||||
if bracket != -1:
|
||||
name = name[:bracket]
|
||||
return name
|
||||
|
||||
|
||||
def gen_main_tf(mod_name, source_dir, args):
|
||||
"""Generate main.tf content for a service stack."""
|
||||
lines = []
|
||||
|
||||
# Collect variables needed
|
||||
vars_needed = {}
|
||||
needs_tiers = False
|
||||
for arg_name, var_expr in args:
|
||||
if var_expr.startswith("LOCAL_TIER:"):
|
||||
needs_tiers = True
|
||||
continue
|
||||
vname = extract_var_name(var_expr)
|
||||
if vname and vname not in vars_needed:
|
||||
vtype = VAR_TYPES.get(vname, None)
|
||||
vars_needed[vname] = vtype
|
||||
|
||||
# Variable declarations
|
||||
for vname, vtype in vars_needed.items():
|
||||
if vtype:
|
||||
lines.append(f'variable "{vname}" {{ type = {vtype} }}')
|
||||
else:
|
||||
lines.append(f'variable "{vname}" {{}}')
|
||||
|
||||
if vars_needed:
|
||||
lines.append("")
|
||||
|
||||
# Tiers block if needed
|
||||
if needs_tiers:
|
||||
lines.append(TIERS_BLOCK)
|
||||
|
||||
# Module call
|
||||
lines.append(f'module "{mod_name}" {{')
|
||||
lines.append(f' source = "../../modules/kubernetes/{source_dir}"')
|
||||
for arg_name, var_expr in args:
|
||||
if var_expr.startswith("LOCAL_TIER:"):
|
||||
tier = var_expr.split(":")[1]
|
||||
val = f"local.tiers.{tier}"
|
||||
else:
|
||||
val = var_expr
|
||||
# Pad for alignment
|
||||
lines.append(f" {arg_name:30s} = {val}")
|
||||
lines.append("}")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
stacks_dir = os.path.join(REPO_ROOT, "stacks")
|
||||
|
||||
for mod_name, source_dir, args in SERVICES:
|
||||
# Use source_dir as the stack directory name for consistency
|
||||
# But some modules have different names than source dirs
|
||||
# Use the module name for the stack dir
|
||||
stack_dir = os.path.join(stacks_dir, mod_name)
|
||||
os.makedirs(stack_dir, exist_ok=True)
|
||||
|
||||
# terragrunt.hcl
|
||||
tg_path = os.path.join(stack_dir, "terragrunt.hcl")
|
||||
with open(tg_path, "w") as f:
|
||||
f.write(TERRAGRUNT_HCL)
|
||||
|
||||
# main.tf
|
||||
main_path = os.path.join(stack_dir, "main.tf")
|
||||
with open(main_path, "w") as f:
|
||||
f.write(gen_main_tf(mod_name, source_dir, args))
|
||||
|
||||
# secrets symlink
|
||||
secrets_link = os.path.join(stack_dir, "secrets")
|
||||
if not os.path.exists(secrets_link):
|
||||
os.symlink("../../secrets", secrets_link)
|
||||
|
||||
print(f" Created stacks/{mod_name}/")
|
||||
|
||||
print(f"\nGenerated {len(SERVICES)} service stacks")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
115
scripts/migrate_service_state.sh
Normal file
115
scripts/migrate_service_state.sh
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#!/bin/bash
|
||||
# Phase 3: Migrate all service module state from root to individual stacks
|
||||
# Each module in root state is at: module.kubernetes_cluster.module.<name>["<name>"]
|
||||
# Target: state/stacks/<name>/terraform.tfstate as module.<name>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_STATE="$(pwd)/terraform.tfstate"
|
||||
STATE_DIR="$(pwd)/state/stacks"
|
||||
|
||||
# All 64 service modules currently in root state
|
||||
MODULES=(
|
||||
actualbudget
|
||||
affine
|
||||
audiobookshelf
|
||||
blog
|
||||
calibre
|
||||
changedetection
|
||||
city-guesser
|
||||
coturn
|
||||
cyberchef
|
||||
dashy
|
||||
dawarich
|
||||
descheduler
|
||||
diun
|
||||
drone
|
||||
ebook2audiobook
|
||||
echo
|
||||
excalidraw
|
||||
f1-stream
|
||||
forgejo
|
||||
freedify
|
||||
freshrss
|
||||
frigate
|
||||
hackmd
|
||||
health
|
||||
homepage
|
||||
immich
|
||||
isponsorblocktv
|
||||
jsoncrack
|
||||
kms
|
||||
linkwarden
|
||||
matrix
|
||||
meshcentral
|
||||
n8n
|
||||
navidrome
|
||||
netbox
|
||||
networking-toolbox
|
||||
nextcloud
|
||||
ntfy
|
||||
ollama
|
||||
onlyoffice
|
||||
openclaw
|
||||
osm_routing
|
||||
owntracks
|
||||
paperless-ngx
|
||||
plotting-book
|
||||
privatebin
|
||||
real-estate-crawler
|
||||
reloader
|
||||
resume
|
||||
rybbit
|
||||
send
|
||||
servarr
|
||||
shadowsocks
|
||||
speedtest
|
||||
stirling-pdf
|
||||
tandoor
|
||||
tor-proxy
|
||||
travel_blog
|
||||
tuya-bridge
|
||||
url
|
||||
wealthfolio
|
||||
webhook_handler
|
||||
whisper
|
||||
ytdlp
|
||||
)
|
||||
|
||||
TOTAL=${#MODULES[@]}
|
||||
SUCCESS=0
|
||||
FAIL=0
|
||||
|
||||
echo "=== Phase 3: Service State Migration ==="
|
||||
echo "Migrating $TOTAL modules from root state to individual stacks"
|
||||
echo ""
|
||||
|
||||
for mod in "${MODULES[@]}"; do
|
||||
idx=$((SUCCESS + FAIL + 1))
|
||||
echo "[$idx/$TOTAL] Migrating: $mod"
|
||||
|
||||
# Create state directory
|
||||
mkdir -p "$STATE_DIR/$mod"
|
||||
|
||||
# Source address (with for_each key)
|
||||
SRC="module.kubernetes_cluster.module.${mod}[\"${mod}\"]"
|
||||
DST="module.${mod}"
|
||||
DST_STATE="$STATE_DIR/$mod/terraform.tfstate"
|
||||
|
||||
if terraform state mv \
|
||||
-state="$ROOT_STATE" \
|
||||
-state-out="$DST_STATE" \
|
||||
"$SRC" "$DST" 2>&1; then
|
||||
echo " ✓ $mod migrated successfully"
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
else
|
||||
echo " ✗ $mod FAILED"
|
||||
FAIL=$((FAIL + 1))
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "=== Migration Summary ==="
|
||||
echo "Total: $TOTAL"
|
||||
echo "Success: $SUCCESS"
|
||||
echo "Failed: $FAIL"
|
||||
Loading…
Add table
Add a link
Reference in a new issue