diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index ea080476..87927201 100755 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -33,11 +33,11 @@ Terragrunt-based infrastructure repository managing a home Kubernetes cluster on ## Static File Paths (NEVER CHANGE) - **Main config**: `terraform.tfvars` - All secrets, DNS, Cloudflare config, WireGuard peers - **Root Terragrunt**: `terragrunt.hcl` - Root Terragrunt config (providers, backend, var loading) -- **Service stacks**: `stacks//` - Individual service stacks (each has `terragrunt.hcl` + `main.tf` + `module/`) +- **Service stacks**: `stacks//` - Individual service stacks (each has `terragrunt.hcl` + `main.tf` with resources inline) - **Infra stack**: `stacks/infra/` - Proxmox VM resources (templates, docker-registry, VMs) - **Platform stack**: `stacks/platform/` - Core infrastructure services (22 modules in `modules/` subdir) - **Per-stack state**: `state/stacks//terraform.tfstate` - Per-stack state files (gitignored) -- **Service modules**: `stacks//module/` - Service module definitions (collocated with stack) +- **Service resources**: `stacks//main.tf` - Service resources defined directly in stack root - **Platform modules**: `stacks/platform/modules//` - Platform service modules - **Shared modules**: `modules/kubernetes/ingress_factory/`, `modules/kubernetes/setup_tls_secret/`, `modules/kubernetes/dockerhub_secret/`, `modules/kubernetes/oauth-proxy/` - **Secrets**: `secrets/` - git-crypt encrypted TLS certs and keys @@ -79,8 +79,7 @@ Terragrunt-based infrastructure repository managing a home Kubernetes cluster on - `stacks/` - Individual Terragrunt stacks (one per service) - `stacks/infra/` - Proxmox VM resources (templates, docker-registry) - `stacks/platform/` - Core infrastructure (22 services in `stacks/platform/modules/`) -- `stacks//` - Individual service stacks (wrapper `main.tf` + `module/` subdir) -- `stacks//module/` - Service module source code (moved from `modules/kubernetes/`) +- `stacks//` - Individual service stacks (resources directly in `main.tf`) - `stacks/platform/modules//` - Platform service module source code - `modules/kubernetes/` - **Only shared utility modules**: `ingress_factory/`, `setup_tls_secret/`, `dockerhub_secret/`, `oauth-proxy/` - `modules/create-vm/` - Proxmox VM creation module @@ -121,7 +120,7 @@ To add a new NFS exported directory: ### Factory Pattern (for multi-user services) Used when a service needs one instance per user. Structure: ``` -modules/kubernetes// +stacks// ├── main.tf # Namespace, TLS secret, user module calls └── factory/ └── main.tf # Deployment, service, ingress templates with ${var.name} @@ -162,7 +161,7 @@ When configuring services to use the mailserver: ### Terragrunt Architecture - Root `terragrunt.hcl` provides DRY provider, backend, and variable loading for all stacks -- Each stack contains its module source: `stacks//main.tf` calls `source = "./module"`, with actual resources in `stacks//module/` +- Each stack contains its resources directly: `stacks//main.tf` has variable declarations, locals, and all Terraform resources inline - Platform modules live at `stacks/platform/modules//`, referenced as `source = "./modules/"` - Shared utility modules (`ingress_factory`, `setup_tls_secret`, `dockerhub_secret`, `oauth-proxy`) remain at `modules/kubernetes/` and are referenced with relative paths from each module - State isolation: each stack has its own state file at `state/stacks//terraform.tfstate` @@ -178,8 +177,7 @@ When configuring services to use the mailserver: When adding a new service to the cluster: 1. Create `stacks//` directory with: - `terragrunt.hcl` - Include root config, declare `platform` dependency - - `main.tf` - Thin wrapper calling `source = "./module"` - - `module/` - Service module directory containing `main.tf` with actual resources + - `main.tf` - All resources defined directly (variables, locals, namespace, deployments, services, ingress) - `secrets` - Symlink to `../../secrets` (for TLS cert path resolution) 2. Add Cloudflare DNS record in `terraform.tfvars` (`cloudflare_proxied_names` or `cloudflare_non_proxied_names`) 3. Apply the cloudflared stack: `cd stacks/platform && terragrunt apply --non-interactive` @@ -248,7 +246,7 @@ Adding a name to `cloudflare_non_proxied_names` or `cloudflare_proxied_names` in Terragrunt stacks under `stacks/`: - `stacks/infra/` - Proxmox VMs, templates, docker-registry - `stacks/platform/` - Core infrastructure (~22 services in `modules/` subdir) -- `stacks//` - Individual service stacks (wrapper `main.tf` + `module/` with actual resources) +- `stacks//` - Individual service stacks (resources directly in `main.tf`) Each stack's `terragrunt.hcl` includes the root `terragrunt.hcl` which provides: - Kubernetes + Helm providers (configured from `terraform.tfvars`) diff --git a/stacks/actualbudget/module/factory/main.tf b/stacks/actualbudget/factory/main.tf similarity index 98% rename from stacks/actualbudget/module/factory/main.tf rename to stacks/actualbudget/factory/main.tf index e173d3fb..35d2e722 100644 --- a/stacks/actualbudget/module/factory/main.tf +++ b/stacks/actualbudget/factory/main.tf @@ -89,7 +89,7 @@ resource "kubernetes_service" "actualbudget" { } module "ingress" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = "actualbudget" name = "budget-${var.name}" tls_secret_name = var.tls_secret_name diff --git a/stacks/actualbudget/main.tf b/stacks/actualbudget/main.tf index ddba7250..a9a99446 100644 --- a/stacks/actualbudget/main.tf +++ b/stacks/actualbudget/main.tf @@ -11,9 +11,62 @@ locals { } } -module "actualbudget" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.edge - credentials = var.actualbudget_credentials +# To create a new deployment: +/** + 1. Export a new nfs share with {name} in truenas + 2. Add {name} as proxied cloudflare route (tfvars) + 3. Add module here +*/ + +resource "kubernetes_namespace" "actualbudget" { + metadata { + name = "actualbudget" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.edge + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.actualbudget.metadata[0].name + tls_secret_name = var.tls_secret_name +} + + +# https://budget-viktor.viktorbarzin.me/ +module "viktor" { + source = "./factory" + name = "viktor" + tag = "edge" + tls_secret_name = var.tls_secret_name + depends_on = [kubernetes_namespace.actualbudget] + tier = local.tiers.edge + budget_encryption_password = lookup(var.actualbudget_credentials["viktor"], "password", null) + sync_id = lookup(var.actualbudget_credentials["viktor"], "sync_id", null) +} + +# https://budget-anca.viktorbarzin.me/ +module "anca" { + source = "./factory" + name = "anca" + tag = "edge" + tls_secret_name = var.tls_secret_name + depends_on = [kubernetes_namespace.actualbudget] + tier = local.tiers.edge + budget_encryption_password = lookup(var.actualbudget_credentials["anca"], "password", null) + sync_id = lookup(var.actualbudget_credentials["anca"], "sync_id", null) +} + +# https://budget-emo.viktorbarzin.me/ +module "emo" { + source = "./factory" + name = "emo" + tag = "edge" + tls_secret_name = var.tls_secret_name + depends_on = [kubernetes_namespace.actualbudget] + tier = local.tiers.edge + budget_encryption_password = lookup(var.actualbudget_credentials["emo"], "password", null) + sync_id = lookup(var.actualbudget_credentials["emo"], "sync_id", null) } diff --git a/stacks/actualbudget/module/main.tf b/stacks/actualbudget/module/main.tf deleted file mode 100644 index eaa4ebd4..00000000 --- a/stacks/actualbudget/module/main.tf +++ /dev/null @@ -1,63 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "credentials" { type = map(any) } - -# To create a new deployment: -/** - 1. Export a new nfs share with {name} in truenas - 2. Add {name} as proxied cloudflare route (tfvars) - 3. Add module here -*/ - -resource "kubernetes_namespace" "actualbudget" { - metadata { - name = "actualbudget" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.actualbudget.metadata[0].name - tls_secret_name = var.tls_secret_name -} - - -# https://budget-viktor.viktorbarzin.me/ -module "viktor" { - source = "./factory" - name = "viktor" - tag = "edge" - tls_secret_name = var.tls_secret_name - depends_on = [kubernetes_namespace.actualbudget] - tier = var.tier - budget_encryption_password = lookup(var.credentials["viktor"], "password", null) - sync_id = lookup(var.credentials["viktor"], "sync_id", null) -} - -# https://budget-anca.viktorbarzin.me/ -module "anca" { - source = "./factory" - name = "anca" - tag = "edge" - tls_secret_name = var.tls_secret_name - depends_on = [kubernetes_namespace.actualbudget] - tier = var.tier - budget_encryption_password = lookup(var.credentials["anca"], "password", null) - sync_id = lookup(var.credentials["anca"], "sync_id", null) -} - -# https://budget-emo.viktorbarzin.me/ -module "emo" { - source = "./factory" - name = "emo" - tag = "edge" - tls_secret_name = var.tls_secret_name - depends_on = [kubernetes_namespace.actualbudget] - tier = var.tier - budget_encryption_password = lookup(var.credentials["emo"], "password", null) - sync_id = lookup(var.credentials["emo"], "sync_id", null) -} diff --git a/stacks/affine/main.tf b/stacks/affine/main.tf index 0043e2a5..07406113 100644 --- a/stacks/affine/main.tf +++ b/stacks/affine/main.tf @@ -12,10 +12,215 @@ locals { } } -module "affine" { - source = "./module" - tls_secret_name = var.tls_secret_name - postgresql_password = var.affine_postgresql_password - smtp_password = var.mailserver_accounts["info@viktorbarzin.me"] - tier = local.tiers.aux +resource "kubernetes_namespace" "affine" { + metadata { + name = "affine" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.affine.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +locals { + common_env = [ + { + name = "DATABASE_URL" + value = "postgresql://affine:${var.affine_postgresql_password}@postgresql.dbaas.svc.cluster.local:5432/affine" + }, + { + name = "REDIS_SERVER_HOST" + value = "redis.redis.svc.cluster.local" + }, + { + name = "AFFINE_INDEXER_ENABLED" + value = "false" + }, + { + name = "NODE_OPTIONS" + value = "--max-old-space-size=4096" + }, + # Server URL configuration + { + name = "AFFINE_SERVER_EXTERNAL_URL" + value = "https://affine.viktorbarzin.me" + }, + { + name = "AFFINE_SERVER_HTTPS" + value = "true" + }, + # Email/SMTP configuration + { + name = "MAILER_HOST" + value = "mailserver.viktorbarzin.me" + }, + { + name = "MAILER_PORT" + value = "587" + }, + { + name = "MAILER_USER" + value = "info@viktorbarzin.me" + }, + { + name = "MAILER_PASSWORD" + value = var.mailserver_accounts["info@viktorbarzin.me"] + }, + { + name = "MAILER_SENDER" + value = "AFFiNE " + }, + ] +} + +resource "kubernetes_deployment" "affine" { + metadata { + name = "affine" + namespace = kubernetes_namespace.affine.metadata[0].name + labels = { + app = "affine" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "affine" + } + } + template { + metadata { + labels = { + app = "affine" + } + } + spec { + # Init container to run database migrations + init_container { + name = "migration" + image = "ghcr.io/toeverything/affine:stable" + command = ["sh", "-c", "npx prisma migrate deploy && SERVER_FLAVOR=script node ./dist/main.js run"] + + dynamic "env" { + for_each = local.common_env + content { + name = env.value.name + value = env.value.value + } + } + + volume_mount { + name = "data" + mount_path = "/root/.affine/storage" + sub_path = "storage" + } + volume_mount { + name = "data" + mount_path = "/root/.affine/config" + sub_path = "config" + } + } + + container { + name = "affine" + image = "ghcr.io/toeverything/affine:stable" + + port { + container_port = 3010 + } + + dynamic "env" { + for_each = local.common_env + content { + name = env.value.name + value = env.value.value + } + } + + volume_mount { + name = "data" + mount_path = "/root/.affine/storage" + sub_path = "storage" + } + volume_mount { + name = "data" + mount_path = "/root/.affine/config" + sub_path = "config" + } + + resources { + requests = { + memory = "512Mi" + cpu = "100m" + } + limits = { + memory = "4Gi" + cpu = "2" + } + } + + liveness_probe { + http_get { + path = "/info" + port = 3010 + } + initial_delay_seconds = 120 + period_seconds = 30 + timeout_seconds = 10 + } + readiness_probe { + http_get { + path = "/info" + port = 3010 + } + initial_delay_seconds = 60 + period_seconds = 10 + timeout_seconds = 5 + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/affine" + } + } + } + } + } +} + +resource "kubernetes_service" "affine" { + metadata { + name = "affine" + namespace = kubernetes_namespace.affine.metadata[0].name + labels = { + app = "affine" + } + } + + spec { + selector = { + app = "affine" + } + port { + name = "http" + port = 80 + target_port = 3010 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.affine.metadata[0].name + name = "affine" + tls_secret_name = var.tls_secret_name + max_body_size = "500m" } diff --git a/stacks/affine/module/main.tf b/stacks/affine/module/main.tf deleted file mode 100644 index 8d73ec85..00000000 --- a/stacks/affine/module/main.tf +++ /dev/null @@ -1,217 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "postgresql_password" {} -variable "smtp_password" { type = string } - -resource "kubernetes_namespace" "affine" { - metadata { - name = "affine" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.affine.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -locals { - common_env = [ - { - name = "DATABASE_URL" - value = "postgresql://affine:${var.postgresql_password}@postgresql.dbaas.svc.cluster.local:5432/affine" - }, - { - name = "REDIS_SERVER_HOST" - value = "redis.redis.svc.cluster.local" - }, - { - name = "AFFINE_INDEXER_ENABLED" - value = "false" - }, - { - name = "NODE_OPTIONS" - value = "--max-old-space-size=4096" - }, - # Server URL configuration - { - name = "AFFINE_SERVER_EXTERNAL_URL" - value = "https://affine.viktorbarzin.me" - }, - { - name = "AFFINE_SERVER_HTTPS" - value = "true" - }, - # Email/SMTP configuration - { - name = "MAILER_HOST" - value = "mailserver.viktorbarzin.me" - }, - { - name = "MAILER_PORT" - value = "587" - }, - { - name = "MAILER_USER" - value = "info@viktorbarzin.me" - }, - { - name = "MAILER_PASSWORD" - value = var.smtp_password - }, - { - name = "MAILER_SENDER" - value = "AFFiNE " - }, - ] -} - -resource "kubernetes_deployment" "affine" { - metadata { - name = "affine" - namespace = kubernetes_namespace.affine.metadata[0].name - labels = { - app = "affine" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "affine" - } - } - template { - metadata { - labels = { - app = "affine" - } - } - spec { - # Init container to run database migrations - init_container { - name = "migration" - image = "ghcr.io/toeverything/affine:stable" - command = ["sh", "-c", "npx prisma migrate deploy && SERVER_FLAVOR=script node ./dist/main.js run"] - - dynamic "env" { - for_each = local.common_env - content { - name = env.value.name - value = env.value.value - } - } - - volume_mount { - name = "data" - mount_path = "/root/.affine/storage" - sub_path = "storage" - } - volume_mount { - name = "data" - mount_path = "/root/.affine/config" - sub_path = "config" - } - } - - container { - name = "affine" - image = "ghcr.io/toeverything/affine:stable" - - port { - container_port = 3010 - } - - dynamic "env" { - for_each = local.common_env - content { - name = env.value.name - value = env.value.value - } - } - - volume_mount { - name = "data" - mount_path = "/root/.affine/storage" - sub_path = "storage" - } - volume_mount { - name = "data" - mount_path = "/root/.affine/config" - sub_path = "config" - } - - resources { - requests = { - memory = "512Mi" - cpu = "100m" - } - limits = { - memory = "4Gi" - cpu = "2" - } - } - - liveness_probe { - http_get { - path = "/info" - port = 3010 - } - initial_delay_seconds = 120 - period_seconds = 30 - timeout_seconds = 10 - } - readiness_probe { - http_get { - path = "/info" - port = 3010 - } - initial_delay_seconds = 60 - period_seconds = 10 - timeout_seconds = 5 - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/affine" - } - } - } - } - } -} - -resource "kubernetes_service" "affine" { - metadata { - name = "affine" - namespace = kubernetes_namespace.affine.metadata[0].name - labels = { - app = "affine" - } - } - - spec { - selector = { - app = "affine" - } - port { - name = "http" - port = 80 - target_port = 3010 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.affine.metadata[0].name - name = "affine" - tls_secret_name = var.tls_secret_name - max_body_size = "500m" -} diff --git a/stacks/audiobookshelf/main.tf b/stacks/audiobookshelf/main.tf index d09071b9..b3015430 100644 --- a/stacks/audiobookshelf/main.tf +++ b/stacks/audiobookshelf/main.tf @@ -10,8 +10,134 @@ locals { } } -module "audiobookshelf" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "audiobookshelf" { + metadata { + name = "audiobookshelf" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.audiobookshelf.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "audiobookshelf" { + metadata { + name = "audiobookshelf" + namespace = kubernetes_namespace.audiobookshelf.metadata[0].name + labels = { + app = "audiobookshelf" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "audiobookshelf" + } + } + template { + metadata { + labels = { + app = "audiobookshelf" + } + } + spec { + container { + image = "ghcr.io/advplyr/audiobookshelf:2.32.1" + name = "audiobookshelf" + + port { + container_port = 80 + } + volume_mount { + name = "audiobooks" + mount_path = "/audiobooks" + } + volume_mount { + name = "podcasts" + mount_path = "/podcasts" + } + volume_mount { + name = "config" + mount_path = "/config" + } + volume_mount { + name = "metadata" + mount_path = "/metadata" + } + } + volume { + name = "audiobooks" + nfs { + path = "/mnt/main/audiobookshelf/audiobooks" + server = "10.0.10.15" + } + } + volume { + name = "podcasts" + nfs { + path = "/mnt/main/audiobookshelf/podcasts" + server = "10.0.10.15" + } + } + volume { + name = "config" + nfs { + path = "/mnt/main/audiobookshelf/config" + server = "10.0.10.15" + } + } + volume { + name = "metadata" + nfs { + path = "/mnt/main/audiobookshelf/metadata" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "audiobookshelf" { + metadata { + name = "audiobookshelf" + namespace = kubernetes_namespace.audiobookshelf.metadata[0].name + labels = { + "app" = "audiobookshelf" + } + } + + spec { + selector = { + app = "audiobookshelf" + } + port { + name = "http" + target_port = 80 + port = 80 + protocol = "TCP" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.audiobookshelf.metadata[0].name + name = "audiobookshelf" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "b38fda4285df" } diff --git a/stacks/audiobookshelf/module/main.tf b/stacks/audiobookshelf/module/main.tf deleted file mode 100644 index db3280aa..00000000 --- a/stacks/audiobookshelf/module/main.tf +++ /dev/null @@ -1,135 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "audiobookshelf" { - metadata { - name = "audiobookshelf" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.audiobookshelf.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "audiobookshelf" { - metadata { - name = "audiobookshelf" - namespace = kubernetes_namespace.audiobookshelf.metadata[0].name - labels = { - app = "audiobookshelf" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "audiobookshelf" - } - } - template { - metadata { - labels = { - app = "audiobookshelf" - } - } - spec { - container { - image = "ghcr.io/advplyr/audiobookshelf:2.32.1" - name = "audiobookshelf" - - port { - container_port = 80 - } - volume_mount { - name = "audiobooks" - mount_path = "/audiobooks" - } - volume_mount { - name = "podcasts" - mount_path = "/podcasts" - } - volume_mount { - name = "config" - mount_path = "/config" - } - volume_mount { - name = "metadata" - mount_path = "/metadata" - } - } - volume { - name = "audiobooks" - nfs { - path = "/mnt/main/audiobookshelf/audiobooks" - server = "10.0.10.15" - } - } - volume { - name = "podcasts" - nfs { - path = "/mnt/main/audiobookshelf/podcasts" - server = "10.0.10.15" - } - } - volume { - name = "config" - nfs { - path = "/mnt/main/audiobookshelf/config" - server = "10.0.10.15" - } - } - volume { - name = "metadata" - nfs { - path = "/mnt/main/audiobookshelf/metadata" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "audiobookshelf" { - metadata { - name = "audiobookshelf" - namespace = kubernetes_namespace.audiobookshelf.metadata[0].name - labels = { - "app" = "audiobookshelf" - } - } - - spec { - selector = { - app = "audiobookshelf" - } - port { - name = "http" - target_port = 80 - port = 80 - protocol = "TCP" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.audiobookshelf.metadata[0].name - name = "audiobookshelf" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "b38fda4285df" -} - diff --git a/stacks/blog/main.tf b/stacks/blog/main.tf index 6a0ec217..6cebc2c7 100644 --- a/stacks/blog/main.tf +++ b/stacks/blog/main.tf @@ -10,8 +10,131 @@ locals { } } -module "blog" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +# variable "dockerhub_password" {} + +resource "kubernetes_namespace" "website" { + metadata { + name = "website" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.website.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +# module "dockerhub_creds" { +# source = "../../modules/kubernetes/dockerhub_secret" +# namespace = kubernetes_namespace.website.metadata[0].name +# password = var.dockerhub_password +# } + +resource "kubernetes_deployment" "blog" { + metadata { + name = "blog" + namespace = kubernetes_namespace.website.metadata[0].name + labels = { + run = "blog" + tier = local.tiers.aux + } + } + spec { + replicas = 3 + selector { + match_labels = { + run = "blog" + } + } + template { + metadata { + labels = { + run = "blog" + } + } + spec { + container { + image = "viktorbarzin/blog:latest" + name = "blog" + resources { + limits = { + cpu = "0.5" + memory = "512Mi" + } + requests = { + cpu = "250m" + memory = "50Mi" + } + } + port { + container_port = 80 + } + } + + container { + image = "nginx/nginx-prometheus-exporter" + name = "nginx-exporter" + args = ["-nginx.scrape-uri", "http://127.0.0.1:8080/nginx_status"] + port { + container_port = 9113 + } + } + } + } + } +} + +resource "kubernetes_service" "blog" { + metadata { + name = "blog" + namespace = kubernetes_namespace.website.metadata[0].name + labels = { + "run" = "blog" + } + annotations = { + "prometheus.io/scrape" = "true" + "prometheus.io/path" = "/metrics" + "prometheus.io/port" = "9113" + } + } + + spec { + selector = { + run = "blog" + } + port { + name = "http" + port = "80" + target_port = "80" + } + port { + name = "prometheus" + port = "9113" + target_port = "9113" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.website.metadata[0].name + name = "blog" + service_name = "blog" + full_host = "viktorbarzin.me" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "da853a2438d0" +} + +module "ingress-www" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.website.metadata[0].name + name = "blog-www" + service_name = "blog" + full_host = "www.viktorbarzin.me" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "da853a2438d0" } diff --git a/stacks/blog/module/main.tf b/stacks/blog/module/main.tf deleted file mode 100644 index e01b2d10..00000000 --- a/stacks/blog/module/main.tf +++ /dev/null @@ -1,130 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -# variable "dockerhub_password" {} - -resource "kubernetes_namespace" "website" { - metadata { - name = "website" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.website.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -# module "dockerhub_creds" { -# source = "../../../modules/kubernetes/dockerhub_secret" -# namespace = kubernetes_namespace.website.metadata[0].name -# password = var.dockerhub_password -# } - -resource "kubernetes_deployment" "blog" { - metadata { - name = "blog" - namespace = kubernetes_namespace.website.metadata[0].name - labels = { - run = "blog" - tier = var.tier - } - } - spec { - replicas = 3 - selector { - match_labels = { - run = "blog" - } - } - template { - metadata { - labels = { - run = "blog" - } - } - spec { - container { - image = "viktorbarzin/blog:latest" - name = "blog" - resources { - limits = { - cpu = "0.5" - memory = "512Mi" - } - requests = { - cpu = "250m" - memory = "50Mi" - } - } - port { - container_port = 80 - } - } - - container { - image = "nginx/nginx-prometheus-exporter" - name = "nginx-exporter" - args = ["-nginx.scrape-uri", "http://127.0.0.1:8080/nginx_status"] - port { - container_port = 9113 - } - } - } - } - } -} - -resource "kubernetes_service" "blog" { - metadata { - name = "blog" - namespace = kubernetes_namespace.website.metadata[0].name - labels = { - "run" = "blog" - } - annotations = { - "prometheus.io/scrape" = "true" - "prometheus.io/path" = "/metrics" - "prometheus.io/port" = "9113" - } - } - - spec { - selector = { - run = "blog" - } - port { - name = "http" - port = "80" - target_port = "80" - } - port { - name = "prometheus" - port = "9113" - target_port = "9113" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.website.metadata[0].name - name = "blog" - service_name = "blog" - full_host = "viktorbarzin.me" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "da853a2438d0" -} - -module "ingress-www" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.website.metadata[0].name - name = "blog-www" - service_name = "blog" - full_host = "www.viktorbarzin.me" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "da853a2438d0" -} diff --git a/stacks/calibre/main.tf b/stacks/calibre/main.tf index 8e2434ec..b3f26691 100644 --- a/stacks/calibre/main.tf +++ b/stacks/calibre/main.tf @@ -11,10 +11,329 @@ locals { } } -module "calibre" { - source = "./module" - 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.tiers.edge +resource "kubernetes_namespace" "calibre" { + metadata { + name = "calibre" + labels = { + tier = local.tiers.edge + } + # labels = { + # "istio-injection" : "enabled" + # } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.calibre.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +# resource "kubernetes_deployment" "calibre" { +# metadata { +# name = "calibre" +# namespace = kubernetes_namespace.calibre.metadata[0].name +# labels = { +# app = "calibre" +# } +# annotations = { +# "reloader.stakater.com/search" = "true" +# } +# } +# spec { +# replicas = 1 +# strategy { +# type = "Recreate" +# } +# selector { +# match_labels = { +# app = "calibre" +# } +# } +# template { +# metadata { +# annotations = { +# # "diun.enable" = "true" +# "diun.enable" = "false" +# "diun.include_tags" = "^\\d+(?:\\.\\d+)?(?:\\.\\d+)?$" +# } +# labels = { +# app = "calibre" +# } +# } +# spec { +# container { +# image = "lscr.io/linuxserver/calibre-web:latest" +# name = "calibre" +# env { +# name = "PUID" +# value = 1000 +# } +# env { +# name = "PGID" +# value = 1000 +# } +# env { +# name = "DOCKER_MODS" +# value = "linuxserver/mods:universal-calibre" +# } + +# port { +# container_port = 8083 +# } +# volume_mount { +# name = "data" +# mount_path = "/config" +# } +# volume_mount { +# name = "data" +# mount_path = "/books" +# } +# } +# volume { +# name = "data" +# nfs { +# path = "/mnt/main/calibre" +# server = "10.0.10.15" +# } +# } +# } +# } +# } +# } + +resource "kubernetes_deployment" "calibre-web-automated" { + metadata { + name = "calibre-web-automated" + namespace = kubernetes_namespace.calibre.metadata[0].name + labels = { + app = "calibre-web-automated" + tier = local.tiers.edge + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "calibre-web-automated" + } + } + template { + metadata { + annotations = { + # "diun.enable" = "true" + "diun.enable" = "false" + "diun.include_tags" = "^\\d+(?:\\.\\d+)?(?:\\.\\d+)?$" + } + labels = { + app = "calibre-web-automated" + } + } + spec { + container { + image = "crocodilestick/calibre-web-automated:latest" + name = "calibre-web-automated" + env { + name = "PUID" + value = 1000 + } + env { + name = "PGID" + value = 1000 + } + env { + name = "DOCKER_MODS" + value = "linuxserver/mods:universal-calibre" + } + env { + # If your library is on a network share (e.g., NFS/SMB), disable WAL to reduce locking issues + name = "NETWORK_SHARE_MODE" + value = "true" + } + env { + name = "CALIBRE_PORT" + value = "8083" + } + + port { + container_port = 8083 + } + volume_mount { + name = "config" + mount_path = "/config" + } + volume_mount { + name = "library" + mount_path = "/calibre-library" + } + volume_mount { + name = "ingest" + mount_path = "/cwa-book-ingest" + } + } + volume { + name = "library" + nfs { + path = "/mnt/main/calibre-web-automated/calibre-library" + server = "10.0.10.15" + } + } + volume { + name = "config" + nfs { + path = "/mnt/main/calibre-web-automated/config" + server = "10.0.10.15" + } + } + volume { + name = "ingest" + nfs { + path = "/mnt/main/calibre-web-automated/cwa-book-ingest" + server = "10.0.10.15" + } + } + } + } + } +} +resource "kubernetes_service" "calibre" { + metadata { + name = "calibre" + namespace = kubernetes_namespace.calibre.metadata[0].name + labels = { + "app" = "calibre" + } + } + + spec { + selector = { + # app = "calibre" + app = "calibre-web-automated" + } + port { + name = "http" + target_port = 8083 + port = 80 + protocol = "TCP" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.calibre.metadata[0].name + name = "calibre" + tls_secret_name = var.tls_secret_name + extra_annotations = { + "gethomepage.dev/enabled" = "true" + "gethomepage.dev/description" = "Book library" + # gethomepage.dev/group: Media + "gethomepage.dev/icon" : "calibre-web.png" + "gethomepage.dev/name" = "Calibre" + "gethomepage.dev/widget.type" = "calibreweb" + "gethomepage.dev/widget.url" = "https://calibre.viktorbarzin.me" + "gethomepage.dev/widget.username" = var.homepage_credentials["calibre-web"]["username"] + "gethomepage.dev/widget.password" = var.homepage_credentials["calibre-web"]["password"] + "gethomepage.dev/pod-selector" = "" + # gethomepage.dev/weight: 10 # optional + # gethomepage.dev/instance: "public" # optional + } + rybbit_site_id = "17a5c7fbb077" + custom_content_security_policy = "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://rybbit.viktorbarzin.me" +} + +# Stacks - Anna's Archive Download Manager + +resource "kubernetes_deployment" "annas-archive-stacks" { + metadata { + name = "annas-archive-stacks" + namespace = kubernetes_namespace.calibre.metadata[0].name + labels = { + app = "annas-archive-stacks" + tier = local.tiers.edge + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "annas-archive-stacks" + } + } + template { + metadata { + labels = { + app = "annas-archive-stacks" + } + } + spec { + container { + image = "zelest/stacks:latest" + name = "annas-archive-stacks" + port { + container_port = 7788 + } + volume_mount { + name = "config" + mount_path = "/opt/stacks/config" + } + volume_mount { + name = "ingest" + mount_path = "/opt/stacks/download" # this must be the same as CWA ingest dir to auto ingest + } + } + volume { + name = "config" + nfs { + path = "/mnt/main/calibre-web-automated/stacks" + server = "10.0.10.15" + } + } + volume { + name = "ingest" + nfs { + path = "/mnt/main/calibre-web-automated/cwa-book-ingest" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "annas-archive-stacks" { + metadata { + name = "annas-archive-stacks" + namespace = kubernetes_namespace.calibre.metadata[0].name + labels = { + "app" = "annas-archive-stacks" + } + } + + spec { + selector = { + app = "annas-archive-stacks" + } + port { + name = "http" + port = "80" + target_port = 7788 + } + } +} + +module "stacks-ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.calibre.metadata[0].name + name = "stacks" + service_name = "annas-archive-stacks" + tls_secret_name = var.tls_secret_name + protected = true + rybbit_site_id = "ce5f8aed6bbb" } diff --git a/stacks/calibre/module/main.tf b/stacks/calibre/module/main.tf deleted file mode 100644 index fe444ed0..00000000 --- a/stacks/calibre/module/main.tf +++ /dev/null @@ -1,335 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "homepage_username" { - default = "" -} -variable "homepage_password" { - default = "" -} - -resource "kubernetes_namespace" "calibre" { - metadata { - name = "calibre" - labels = { - tier = var.tier - } - # labels = { - # "istio-injection" : "enabled" - # } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.calibre.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -# resource "kubernetes_deployment" "calibre" { -# metadata { -# name = "calibre" -# namespace = kubernetes_namespace.calibre.metadata[0].name -# labels = { -# app = "calibre" -# } -# annotations = { -# "reloader.stakater.com/search" = "true" -# } -# } -# spec { -# replicas = 1 -# strategy { -# type = "Recreate" -# } -# selector { -# match_labels = { -# app = "calibre" -# } -# } -# template { -# metadata { -# annotations = { -# # "diun.enable" = "true" -# "diun.enable" = "false" -# "diun.include_tags" = "^\\d+(?:\\.\\d+)?(?:\\.\\d+)?$" -# } -# labels = { -# app = "calibre" -# } -# } -# spec { -# container { -# image = "lscr.io/linuxserver/calibre-web:latest" -# name = "calibre" -# env { -# name = "PUID" -# value = 1000 -# } -# env { -# name = "PGID" -# value = 1000 -# } -# env { -# name = "DOCKER_MODS" -# value = "linuxserver/mods:universal-calibre" -# } - -# port { -# container_port = 8083 -# } -# volume_mount { -# name = "data" -# mount_path = "/config" -# } -# volume_mount { -# name = "data" -# mount_path = "/books" -# } -# } -# volume { -# name = "data" -# nfs { -# path = "/mnt/main/calibre" -# server = "10.0.10.15" -# } -# } -# } -# } -# } -# } - -resource "kubernetes_deployment" "calibre-web-automated" { - metadata { - name = "calibre-web-automated" - namespace = kubernetes_namespace.calibre.metadata[0].name - labels = { - app = "calibre-web-automated" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "calibre-web-automated" - } - } - template { - metadata { - annotations = { - # "diun.enable" = "true" - "diun.enable" = "false" - "diun.include_tags" = "^\\d+(?:\\.\\d+)?(?:\\.\\d+)?$" - } - labels = { - app = "calibre-web-automated" - } - } - spec { - container { - image = "crocodilestick/calibre-web-automated:latest" - name = "calibre-web-automated" - env { - name = "PUID" - value = 1000 - } - env { - name = "PGID" - value = 1000 - } - env { - name = "DOCKER_MODS" - value = "linuxserver/mods:universal-calibre" - } - env { - # If your library is on a network share (e.g., NFS/SMB), disable WAL to reduce locking issues - name = "NETWORK_SHARE_MODE" - value = "true" - } - env { - name = "CALIBRE_PORT" - value = "8083" - } - - port { - container_port = 8083 - } - volume_mount { - name = "config" - mount_path = "/config" - } - volume_mount { - name = "library" - mount_path = "/calibre-library" - } - volume_mount { - name = "ingest" - mount_path = "/cwa-book-ingest" - } - } - volume { - name = "library" - nfs { - path = "/mnt/main/calibre-web-automated/calibre-library" - server = "10.0.10.15" - } - } - volume { - name = "config" - nfs { - path = "/mnt/main/calibre-web-automated/config" - server = "10.0.10.15" - } - } - volume { - name = "ingest" - nfs { - path = "/mnt/main/calibre-web-automated/cwa-book-ingest" - server = "10.0.10.15" - } - } - } - } - } -} -resource "kubernetes_service" "calibre" { - metadata { - name = "calibre" - namespace = kubernetes_namespace.calibre.metadata[0].name - labels = { - "app" = "calibre" - } - } - - spec { - selector = { - # app = "calibre" - app = "calibre-web-automated" - } - port { - name = "http" - target_port = 8083 - port = 80 - protocol = "TCP" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.calibre.metadata[0].name - name = "calibre" - tls_secret_name = var.tls_secret_name - extra_annotations = { - "gethomepage.dev/enabled" = "true" - "gethomepage.dev/description" = "Book library" - # gethomepage.dev/group: Media - "gethomepage.dev/icon" : "calibre-web.png" - "gethomepage.dev/name" = "Calibre" - "gethomepage.dev/widget.type" = "calibreweb" - "gethomepage.dev/widget.url" = "https://calibre.viktorbarzin.me" - "gethomepage.dev/widget.username" = var.homepage_username - "gethomepage.dev/widget.password" = var.homepage_password - "gethomepage.dev/pod-selector" = "" - # gethomepage.dev/weight: 10 # optional - # gethomepage.dev/instance: "public" # optional - } - rybbit_site_id = "17a5c7fbb077" - custom_content_security_policy = "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://rybbit.viktorbarzin.me" -} - -# Stacks - Anna's Archive Download Manager - -resource "kubernetes_deployment" "annas-archive-stacks" { - metadata { - name = "annas-archive-stacks" - namespace = kubernetes_namespace.calibre.metadata[0].name - labels = { - app = "annas-archive-stacks" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "annas-archive-stacks" - } - } - template { - metadata { - labels = { - app = "annas-archive-stacks" - } - } - spec { - container { - image = "zelest/stacks:latest" - name = "annas-archive-stacks" - port { - container_port = 7788 - } - volume_mount { - name = "config" - mount_path = "/opt/stacks/config" - } - volume_mount { - name = "ingest" - mount_path = "/opt/stacks/download" # this must be the same as CWA ingest dir to auto ingest - } - } - volume { - name = "config" - nfs { - path = "/mnt/main/calibre-web-automated/stacks" - server = "10.0.10.15" - } - } - volume { - name = "ingest" - nfs { - path = "/mnt/main/calibre-web-automated/cwa-book-ingest" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "annas-archive-stacks" { - metadata { - name = "annas-archive-stacks" - namespace = kubernetes_namespace.calibre.metadata[0].name - labels = { - "app" = "annas-archive-stacks" - } - } - - spec { - selector = { - app = "annas-archive-stacks" - } - port { - name = "http" - port = "80" - target_port = 7788 - } - } -} - -module "stacks-ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.calibre.metadata[0].name - name = "stacks" - service_name = "annas-archive-stacks" - tls_secret_name = var.tls_secret_name - protected = true - rybbit_site_id = "ce5f8aed6bbb" -} diff --git a/stacks/changedetection/main.tf b/stacks/changedetection/main.tf index 0be18513..b05307ed 100644 --- a/stacks/changedetection/main.tf +++ b/stacks/changedetection/main.tf @@ -10,8 +10,132 @@ locals { } } -module "changedetection" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "changedetection" { + metadata { + name = "changedetection" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.changedetection.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "changedetection" { + metadata { + name = "changedetection" + namespace = kubernetes_namespace.changedetection.metadata[0].name + labels = { + app = "changedetection" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "changedetection" + } + } + template { + metadata { + labels = { + app = "changedetection" + } + } + spec { + container { + name = "sockpuppetbrowser" + image = "dgtlmoon/sockpuppetbrowser:latest" + image_pull_policy = "IfNotPresent" + port { + name = "ws" + container_port = 3000 + protocol = "TCP" + } + security_context { + capabilities { + add = ["SYS_ADMIN"] + } + } + } + + container { + name = "changedetection" + image = "ghcr.io/dgtlmoon/changedetection.io:latest" # latest is latest stable + env { + name = "PLAYWRIGHT_DRIVER_URL" + value = "ws://localhost:3000" + } + env { + name = "BASE_URL" + value = "https://changedetection.viktorbarzin.me" + } + env { + name = "LOGGER_LEVEL" + value = "WARNING" + } + env { + name = "TZ" + value = "Europe/Sofia" + } + volume_mount { + name = "data" + mount_path = "/datastore" + } + port { + name = "http" + container_port = 5000 + protocol = "TCP" + } + } + # security_context { + # fs_group = "1500" + # } + volume { + name = "data" + nfs { + path = "/mnt/main/changedetection" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "changedetection" { + metadata { + name = "changedetection" + namespace = kubernetes_namespace.changedetection.metadata[0].name + labels = { + "app" = "changedetection" + } + } + + spec { + selector = { + app = "changedetection" + } + port { + port = 80 + target_port = 5000 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.changedetection.metadata[0].name + name = "changedetection" + tls_secret_name = var.tls_secret_name + protected = true } diff --git a/stacks/changedetection/module/main.tf b/stacks/changedetection/module/main.tf deleted file mode 100644 index b5c99704..00000000 --- a/stacks/changedetection/module/main.tf +++ /dev/null @@ -1,132 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "changedetection" { - metadata { - name = "changedetection" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.changedetection.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "changedetection" { - metadata { - name = "changedetection" - namespace = kubernetes_namespace.changedetection.metadata[0].name - labels = { - app = "changedetection" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "changedetection" - } - } - template { - metadata { - labels = { - app = "changedetection" - } - } - spec { - container { - name = "sockpuppetbrowser" - image = "dgtlmoon/sockpuppetbrowser:latest" - image_pull_policy = "IfNotPresent" - port { - name = "ws" - container_port = 3000 - protocol = "TCP" - } - security_context { - capabilities { - add = ["SYS_ADMIN"] - } - } - } - - container { - name = "changedetection" - image = "ghcr.io/dgtlmoon/changedetection.io:latest" # latest is latest stable - env { - name = "PLAYWRIGHT_DRIVER_URL" - value = "ws://localhost:3000" - } - env { - name = "BASE_URL" - value = "https://changedetection.viktorbarzin.me" - } - env { - name = "LOGGER_LEVEL" - value = "WARNING" - } - env { - name = "TZ" - value = "Europe/Sofia" - } - volume_mount { - name = "data" - mount_path = "/datastore" - } - port { - name = "http" - container_port = 5000 - protocol = "TCP" - } - } - # security_context { - # fs_group = "1500" - # } - volume { - name = "data" - nfs { - path = "/mnt/main/changedetection" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "changedetection" { - metadata { - name = "changedetection" - namespace = kubernetes_namespace.changedetection.metadata[0].name - labels = { - "app" = "changedetection" - } - } - - spec { - selector = { - app = "changedetection" - } - port { - port = 80 - target_port = 5000 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.changedetection.metadata[0].name - name = "changedetection" - tls_secret_name = var.tls_secret_name - protected = true -} diff --git a/stacks/city-guesser/main.tf b/stacks/city-guesser/main.tf index 422497b4..d946f936 100644 --- a/stacks/city-guesser/main.tf +++ b/stacks/city-guesser/main.tf @@ -10,8 +10,154 @@ locals { } } -module "city-guesser" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "city-guesser" { + metadata { + name = "city-guesser" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } } + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = "city-guesser" + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "city-guesser" { + metadata { + name = "city-guesser" + namespace = "city-guesser" + labels = { + run = "city-guesser" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + run = "city-guesser" + } + } + template { + metadata { + labels = { + run = "city-guesser" + } + } + spec { + container { + image = "viktorbarzin/city-guesser:latest" + name = "city-guesser" + resources { + limits = { + cpu = "0.5" + memory = "512Mi" + } + requests = { + cpu = "250m" + memory = "50Mi" + } + } + port { + container_port = 80 + } + } + } + } + } +} + +resource "kubernetes_service" "city-guesser" { + metadata { + name = "city-guesser" + namespace = "city-guesser" + labels = { + "run" = "city-guesser" + } + } + + spec { + selector = { + run = "city-guesser" + } + port { + name = "http" + port = "80" + target_port = "80" + } + } +} +# resource "kubernetes_service" "city-guesser-oauth" { +# metadata { +# name = "city-guesser-oauth" +# namespace = "city-guesser" +# labels = { +# "run" = "city-guesser-oauth" +# } +# } + +# spec { +# type = "ExternalName" +# external_name = "oauth-proxy.oauth.svc.cluster.local" + +# # port { +# # name = "tcp" +# # port = "80" +# # target_port = "80" +# # } +# } +# } + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = "city-guesser" + name = "city-guesser" + tls_secret_name = var.tls_secret_name + protected = true +} + +# resource "kubernetes_ingress_v1" "city-guesser-oauth" { +# metadata { +# name = "city-guesser-ingress-oauth" +# namespace = "city-guesser" +# annotations = { +# "kubernetes.io/ingress.class" = "nginx" +# } +# } + +# spec { +# tls { +# hosts = ["city-guesser.viktorbarzin.me"] +# secret_name = var.tls_secret_name +# } +# rule { +# host = "city-guesser.viktorbarzin.me" +# http { +# path { +# path = "/oauth2" +# backend { +# service_name = "city-guesser-oauth" +# service_port = "80" +# } +# } +# } +# } +# } +# } + + +# module "oauth" { +# source = "../../modules/kubernetes/oauth-proxy" +# # oauth_client_id = "3d8ce4bf7b893899d967" +# # oauth_client_secret = "REDACTED_OAUTH_SECRET" +# client_id = "3d8ce4bf7b893899d967" +# client_secret = "REDACTED_OAUTH_SECRET" +# namespace = "city-guesser" +# host = "city-guesser.viktorbarzin.me" +# tls_secret_name = var.tls_secret_name +# svc_name = "city-guesser-oauth" +# } diff --git a/stacks/city-guesser/module/main.tf b/stacks/city-guesser/module/main.tf deleted file mode 100644 index b1ece574..00000000 --- a/stacks/city-guesser/module/main.tf +++ /dev/null @@ -1,154 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "city-guesser" { - metadata { - name = "city-guesser" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = "city-guesser" - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "city-guesser" { - metadata { - name = "city-guesser" - namespace = "city-guesser" - labels = { - run = "city-guesser" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - run = "city-guesser" - } - } - template { - metadata { - labels = { - run = "city-guesser" - } - } - spec { - container { - image = "viktorbarzin/city-guesser:latest" - name = "city-guesser" - resources { - limits = { - cpu = "0.5" - memory = "512Mi" - } - requests = { - cpu = "250m" - memory = "50Mi" - } - } - port { - container_port = 80 - } - } - } - } - } -} - -resource "kubernetes_service" "city-guesser" { - metadata { - name = "city-guesser" - namespace = "city-guesser" - labels = { - "run" = "city-guesser" - } - } - - spec { - selector = { - run = "city-guesser" - } - port { - name = "http" - port = "80" - target_port = "80" - } - } -} -# resource "kubernetes_service" "city-guesser-oauth" { -# metadata { -# name = "city-guesser-oauth" -# namespace = "city-guesser" -# labels = { -# "run" = "city-guesser-oauth" -# } -# } - -# spec { -# type = "ExternalName" -# external_name = "oauth-proxy.oauth.svc.cluster.local" - -# # port { -# # name = "tcp" -# # port = "80" -# # target_port = "80" -# # } -# } -# } - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = "city-guesser" - name = "city-guesser" - tls_secret_name = var.tls_secret_name - protected = true -} - -# resource "kubernetes_ingress_v1" "city-guesser-oauth" { -# metadata { -# name = "city-guesser-ingress-oauth" -# namespace = "city-guesser" -# annotations = { -# "kubernetes.io/ingress.class" = "nginx" -# } -# } - -# spec { -# tls { -# hosts = ["city-guesser.viktorbarzin.me"] -# secret_name = var.tls_secret_name -# } -# rule { -# host = "city-guesser.viktorbarzin.me" -# http { -# path { -# path = "/oauth2" -# backend { -# service_name = "city-guesser-oauth" -# service_port = "80" -# } -# } -# } -# } -# } -# } - - -# module "oauth" { -# source = "../../../modules/kubernetes/oauth-proxy" -# # oauth_client_id = "3d8ce4bf7b893899d967" -# # oauth_client_secret = "REDACTED_OAUTH_SECRET" -# client_id = "3d8ce4bf7b893899d967" -# client_secret = "REDACTED_OAUTH_SECRET" -# namespace = "city-guesser" -# host = "city-guesser.viktorbarzin.me" -# tls_secret_name = var.tls_secret_name -# svc_name = "city-guesser-oauth" -# } diff --git a/stacks/coturn/main.tf b/stacks/coturn/main.tf index d1ceadff..4085dabc 100644 --- a/stacks/coturn/main.tf +++ b/stacks/coturn/main.tf @@ -12,10 +12,192 @@ locals { } } -module "coturn" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.edge - turn_secret = var.coturn_turn_secret - public_ip = var.public_ip +locals { + turn_realm = "viktorbarzin.me" + turn_port = 3478 + # Small relay range — 100 ports is plenty for a home lab (~50 concurrent streams) + min_port = 49152 + max_port = 49252 +} + +resource "kubernetes_namespace" "coturn" { + metadata { + name = "coturn" + labels = { + tier = local.tiers.edge + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.coturn.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_config_map" "coturn_config" { + metadata { + name = "coturn-config" + namespace = kubernetes_namespace.coturn.metadata[0].name + } + + data = { + "turnserver.conf" = <<-EOF + # TURN server configuration + listening-port=${local.turn_port} + fingerprint + lt-cred-mech + use-auth-secret + static-auth-secret=${var.coturn_turn_secret} + realm=${local.turn_realm} + server-name=turn.${local.turn_realm} + + # Network — use 0.0.0.0, coturn auto-detects pod IP + listening-ip=0.0.0.0 + external-ip=${var.public_ip} + + # Media relay port range (narrow — 100 ports) + min-port=${local.min_port} + max-port=${local.max_port} + + # Logging + verbose + no-stdout-log + syslog + + # Security + no-multicast-peers + no-cli + no-tlsv1 + no-tlsv1_1 + + # Performance + total-quota=100 + stale-nonce=600 + max-bps=0 + EOF + } +} + +resource "kubernetes_deployment" "coturn" { + metadata { + name = "coturn" + namespace = kubernetes_namespace.coturn.metadata[0].name + labels = { + app = "coturn" + tier = local.tiers.edge + } + } + + spec { + replicas = 1 + strategy { + type = "RollingUpdate" + rolling_update { + max_unavailable = 0 + max_surge = 1 + } + } + selector { + match_labels = { + app = "coturn" + } + } + + template { + metadata { + labels = { + app = "coturn" + } + } + + spec { + container { + name = "coturn" + image = "coturn/coturn:latest" + args = ["-c", "/etc/turnserver/turnserver.conf"] + + # STUN/TURN signaling port + port { + name = "turn-udp" + container_port = local.turn_port + protocol = "UDP" + } + port { + name = "turn-tcp" + container_port = local.turn_port + protocol = "TCP" + } + + volume_mount { + name = "config" + mount_path = "/etc/turnserver" + read_only = true + } + + resources { + requests = { + cpu = "100m" + memory = "128Mi" + } + limits = { + cpu = "1" + memory = "512Mi" + } + } + } + + volume { + name = "config" + config_map { + name = kubernetes_config_map.coturn_config.metadata[0].name + } + } + } + } + } +} + +# LoadBalancer service with MetalLB — exposes STUN/TURN signaling + relay ports +resource "kubernetes_service" "coturn" { + metadata { + name = "coturn" + namespace = kubernetes_namespace.coturn.metadata[0].name + annotations = { + "metallb.universe.tf/loadBalancerIPs" = "10.0.20.200" + "metallb.universe.tf/allow-shared-ip" = "shared" + } + } + + spec { + type = "LoadBalancer" + selector = { + app = "coturn" + } + + # STUN/TURN signaling + port { + name = "turn-udp" + port = local.turn_port + target_port = local.turn_port + protocol = "UDP" + } + port { + name = "turn-tcp" + port = local.turn_port + target_port = local.turn_port + protocol = "TCP" + } + + # Relay port range (49152-49252) + dynamic "port" { + for_each = range(local.min_port, local.max_port + 1) + content { + name = "relay-${port.value}" + port = port.value + target_port = port.value + protocol = "UDP" + } + } + } } diff --git a/stacks/coturn/module/main.tf b/stacks/coturn/module/main.tf deleted file mode 100644 index abbd5ecf..00000000 --- a/stacks/coturn/module/main.tf +++ /dev/null @@ -1,194 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "turn_secret" { type = string } -variable "public_ip" { type = string } - -locals { - turn_realm = "viktorbarzin.me" - turn_port = 3478 - # Small relay range — 100 ports is plenty for a home lab (~50 concurrent streams) - min_port = 49152 - max_port = 49252 -} - -resource "kubernetes_namespace" "coturn" { - metadata { - name = "coturn" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.coturn.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_config_map" "coturn_config" { - metadata { - name = "coturn-config" - namespace = kubernetes_namespace.coturn.metadata[0].name - } - - data = { - "turnserver.conf" = <<-EOF - # TURN server configuration - listening-port=${local.turn_port} - fingerprint - lt-cred-mech - use-auth-secret - static-auth-secret=${var.turn_secret} - realm=${local.turn_realm} - server-name=turn.${local.turn_realm} - - # Network — use 0.0.0.0, coturn auto-detects pod IP - listening-ip=0.0.0.0 - external-ip=${var.public_ip} - - # Media relay port range (narrow — 100 ports) - min-port=${local.min_port} - max-port=${local.max_port} - - # Logging - verbose - no-stdout-log - syslog - - # Security - no-multicast-peers - no-cli - no-tlsv1 - no-tlsv1_1 - - # Performance - total-quota=100 - stale-nonce=600 - max-bps=0 - EOF - } -} - -resource "kubernetes_deployment" "coturn" { - metadata { - name = "coturn" - namespace = kubernetes_namespace.coturn.metadata[0].name - labels = { - app = "coturn" - tier = var.tier - } - } - - spec { - replicas = 1 - strategy { - type = "RollingUpdate" - rolling_update { - max_unavailable = 0 - max_surge = 1 - } - } - selector { - match_labels = { - app = "coturn" - } - } - - template { - metadata { - labels = { - app = "coturn" - } - } - - spec { - container { - name = "coturn" - image = "coturn/coturn:latest" - args = ["-c", "/etc/turnserver/turnserver.conf"] - - # STUN/TURN signaling port - port { - name = "turn-udp" - container_port = local.turn_port - protocol = "UDP" - } - port { - name = "turn-tcp" - container_port = local.turn_port - protocol = "TCP" - } - - volume_mount { - name = "config" - mount_path = "/etc/turnserver" - read_only = true - } - - resources { - requests = { - cpu = "100m" - memory = "128Mi" - } - limits = { - cpu = "1" - memory = "512Mi" - } - } - } - - volume { - name = "config" - config_map { - name = kubernetes_config_map.coturn_config.metadata[0].name - } - } - } - } - } -} - -# LoadBalancer service with MetalLB — exposes STUN/TURN signaling + relay ports -resource "kubernetes_service" "coturn" { - metadata { - name = "coturn" - namespace = kubernetes_namespace.coturn.metadata[0].name - annotations = { - "metallb.universe.tf/loadBalancerIPs" = "10.0.20.200" - "metallb.universe.tf/allow-shared-ip" = "shared" - } - } - - spec { - type = "LoadBalancer" - selector = { - app = "coturn" - } - - # STUN/TURN signaling - port { - name = "turn-udp" - port = local.turn_port - target_port = local.turn_port - protocol = "UDP" - } - port { - name = "turn-tcp" - port = local.turn_port - target_port = local.turn_port - protocol = "TCP" - } - - # Relay port range (49152-49252) - dynamic "port" { - for_each = range(local.min_port, local.max_port + 1) - content { - name = "relay-${port.value}" - port = port.value - target_port = port.value - protocol = "UDP" - } - } - } -} diff --git a/stacks/cyberchef/main.tf b/stacks/cyberchef/main.tf index ad338c05..4fcd450c 100644 --- a/stacks/cyberchef/main.tf +++ b/stacks/cyberchef/main.tf @@ -10,8 +10,89 @@ locals { } } -module "cyberchef" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "cyberchef" { + metadata { + name = "cyberchef" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.cyberchef.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "cyberchef" { + metadata { + name = "cyberchef" + namespace = kubernetes_namespace.cyberchef.metadata[0].name + labels = { + app = "cyberchef" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "RollingUpdate" + } + selector { + match_labels = { + app = "cyberchef" + } + } + template { + metadata { + labels = { + app = "cyberchef" + } + } + spec { + container { + image = "mpepping/cyberchef:latest" + name = "cyberchef" + + port { + container_port = 8000 + } + } + } + } + } +} + +resource "kubernetes_service" "cyberchef" { + metadata { + name = "cc" + namespace = kubernetes_namespace.cyberchef.metadata[0].name + labels = { + "app" = "cyberchef" + } + } + + spec { + selector = { + app = "cyberchef" + } + port { + name = "http" + target_port = 8000 + port = 80 + } + } +} + + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.cyberchef.metadata[0].name + name = "cc" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "7c460afc68c4" } diff --git a/stacks/cyberchef/module/main.tf b/stacks/cyberchef/module/main.tf deleted file mode 100644 index e0c5970b..00000000 --- a/stacks/cyberchef/module/main.tf +++ /dev/null @@ -1,88 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -resource "kubernetes_namespace" "cyberchef" { - metadata { - name = "cyberchef" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.cyberchef.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "cyberchef" { - metadata { - name = "cyberchef" - namespace = kubernetes_namespace.cyberchef.metadata[0].name - labels = { - app = "cyberchef" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "RollingUpdate" - } - selector { - match_labels = { - app = "cyberchef" - } - } - template { - metadata { - labels = { - app = "cyberchef" - } - } - spec { - container { - image = "mpepping/cyberchef:latest" - name = "cyberchef" - - port { - container_port = 8000 - } - } - } - } - } -} - -resource "kubernetes_service" "cyberchef" { - metadata { - name = "cc" - namespace = kubernetes_namespace.cyberchef.metadata[0].name - labels = { - "app" = "cyberchef" - } - } - - spec { - selector = { - app = "cyberchef" - } - port { - name = "http" - target_port = 8000 - port = 80 - } - } -} - - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.cyberchef.metadata[0].name - name = "cc" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "7c460afc68c4" -} diff --git a/stacks/dashy/module/conf.yml b/stacks/dashy/conf.yml similarity index 100% rename from stacks/dashy/module/conf.yml rename to stacks/dashy/conf.yml diff --git a/stacks/dashy/main.tf b/stacks/dashy/main.tf index 40b7eb84..8b9bbbe5 100644 --- a/stacks/dashy/main.tf +++ b/stacks/dashy/main.tf @@ -10,8 +10,124 @@ locals { } } -module "dashy" { - source = "./module" +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.dashy.metadata[0].name tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +} + +resource "kubernetes_namespace" "dashy" { + metadata { + name = "dashy" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +resource "kubernetes_config_map" "config" { + metadata { + name = "config" + namespace = kubernetes_namespace.dashy.metadata[0].name + + annotations = { + "reloader.stakater.com/match" = "true" + } + } + + data = { + "conf.yml" = file("${path.module}/conf.yml") + } +} + +resource "kubernetes_deployment" "dashy" { + metadata { + name = "dashy" + namespace = kubernetes_namespace.dashy.metadata[0].name + labels = { + app = "dashy" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "dashy" + } + } + template { + metadata { + annotations = { + # "diun.enable" = "true" + } + labels = { + app = "dashy" + } + } + spec { + container { + image = "lissy93/dashy:latest" + name = "dashy" + + resources { + requests = { + cpu = "50m" + memory = "256Mi" + } + limits = { + cpu = "1" + memory = "2Gi" + } + } + port { + container_port = 8080 + } + volume_mount { + name = "config" + mount_path = "/app/user-data/" + } + } + volume { + name = "config" + config_map { + name = "config" + } + } + } + } + } +} + +resource "kubernetes_service" "dashy" { + metadata { + name = "dashy" + namespace = kubernetes_namespace.dashy.metadata[0].name + labels = { + app = "dashy" + } + } + + spec { + selector = { + app = "dashy" + } + port { + name = "http" + port = 80 + target_port = 8080 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.dashy.metadata[0].name + name = "dashy" + tls_secret_name = var.tls_secret_name + protected = true # hidden as we use homepage now } diff --git a/stacks/dashy/module/main.tf b/stacks/dashy/module/main.tf deleted file mode 100644 index f4100cac..00000000 --- a/stacks/dashy/module/main.tf +++ /dev/null @@ -1,126 +0,0 @@ - -variable "tls_secret_name" {} -variable "tier" { type = string } - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.dashy.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_namespace" "dashy" { - metadata { - name = "dashy" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -resource "kubernetes_config_map" "config" { - metadata { - name = "config" - namespace = kubernetes_namespace.dashy.metadata[0].name - - annotations = { - "reloader.stakater.com/match" = "true" - } - } - - data = { - "conf.yml" = file("${path.module}/conf.yml") - } -} - -resource "kubernetes_deployment" "dashy" { - metadata { - name = "dashy" - namespace = kubernetes_namespace.dashy.metadata[0].name - labels = { - app = "dashy" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "dashy" - } - } - template { - metadata { - annotations = { - # "diun.enable" = "true" - } - labels = { - app = "dashy" - } - } - spec { - container { - image = "lissy93/dashy:latest" - name = "dashy" - - resources { - requests = { - cpu = "50m" - memory = "256Mi" - } - limits = { - cpu = "1" - memory = "2Gi" - } - } - port { - container_port = 8080 - } - volume_mount { - name = "config" - mount_path = "/app/user-data/" - } - } - volume { - name = "config" - config_map { - name = "config" - } - } - } - } - } -} - -resource "kubernetes_service" "dashy" { - metadata { - name = "dashy" - namespace = kubernetes_namespace.dashy.metadata[0].name - labels = { - app = "dashy" - } - } - - spec { - selector = { - app = "dashy" - } - port { - name = "http" - port = 80 - target_port = 8080 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.dashy.metadata[0].name - name = "dashy" - tls_secret_name = var.tls_secret_name - protected = true # hidden as we use homepage now -} - diff --git a/stacks/dawarich/main.tf b/stacks/dawarich/main.tf index d4b9bc5e..1a3e351b 100644 --- a/stacks/dawarich/main.tf +++ b/stacks/dawarich/main.tf @@ -12,10 +12,324 @@ locals { } } -module "dawarich" { - source = "./module" - tls_secret_name = var.tls_secret_name - database_password = var.dawarich_database_password - geoapify_api_key = var.geoapify_api_key - tier = local.tiers.edge +variable "image_version" { + type = string + default = "0.37.1" +} + +resource "kubernetes_namespace" "dawarich" { + metadata { + name = "dawarich" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.edge + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.dawarich.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "dawarich" { + metadata { + name = "dawarich" + namespace = kubernetes_namespace.dawarich.metadata[0].name + labels = { + app = "dawarich" + tier = local.tiers.edge + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "dawarich" + } + } + template { + metadata { + labels = { + app = "dawarich" + } + annotations = { + # "diun.enable" = "true" + # "diun.include_tags" = "latest" + } + } + spec { + + container { + image = "freikin/dawarich:${var.image_version}" + name = "dawarich" + port { + name = "http" + container_port = 3000 + } + port { + name = "prometheus" + container_port = 9394 + } + command = ["web-entrypoint.sh"] + args = ["bin/dev"] + env { + name = "REDIS_URL" + value = "redis://redis.redis.svc.cluster.local:6379" + } + env { + name = "DATABASE_HOST" + value = "postgresql.dbaas" + } + env { + name = "DATABASE_USERNAME" + value = "dawarich" + } + env { + name = "DATABASE_PASSWORD" + value = var.dawarich_database_password + } + env { + name = "DATABASE_NAME" + value = "dawarich" + } + env { + name = "MIN_MINUTES_SPENT_IN_CITY" + value = "60" + } + env { + name = "TIME_ZONE" + value = "Europe/London" + } + env { + name = "DISTANCE_UNIT" + value = "km" + } + env { + name = "ENABLE_TELEMETRY" + value = "true" + } + env { + name = "APPLICATION_HOSTS" + value = "dawarich.viktorbarzin.me" + } + # env { + # name = "PROMETHEUS_EXPORTER_ENABLED" + # value = "true" + # } + # env { + # name = "PROMETHEUS_EXPORTER_PORT" + # value = "9394" + # } + # env { + # name = "PROMETHEUS_EXPORTER_HOST" + # value = "0.0.0.0" + # } + env { + name = "SELF_HOSTED" + value = "true" + } + # env { + # name = "PHOTON_API_HOST" + # value = "photon.dawarich" + # } + + + # volume_mount { + # name = "watched" + # mount_path = "/var/app/tmp/imports/watched" + # } + } + # container { + # image = "freikin/dawarich:${var.image_version}" + # name = "dawarich-sidekiq" + # command = ["sidekiq-entrypoint.sh"] + # args = ["bundle exec sidekiq"] + # env { + # name = "REDIS_URL" + # value = "redis://redis.redis.svc.cluster.local:6379" + # } + # env { + # name = "DATABASE_HOST" + # value = "postgresql.dbaas" + # } + # env { + # name = "DATABASE_USERNAME" + # value = "dawarich" + # } + # env { + # name = "DATABASE_PASSWORD" + # value = var.dawarich_database_password + # } + # env { + # name = "DATABASE_NAME" + # value = "dawarich" + # } + # env { + # name = "MIN_MINUTES_SPENT_IN_CITY" + # value = "60" + # } + # env { + # name = "BACKGROUND_PROCESSING_CONCURRENCY" + # value = "10" + # } + # env { + # name = "ENABLE_TELEMETRY" + # value = "true" + # } + # env { + # name = "APPLICATION_HOST" + # value = "dawarich.viktorbarzin.me" + # } + # # env { + # # name = "PROMETHEUS_EXPORTER_ENABLED" + # # value = "false" + # # } + # # env { + # # name = "PROMETHEUS_EXPORTER_HOST" + # # value = "dawarich.dawarich" + # # } + # # env { + # # name = "PHOTON_API_HOST" + # # value = "photon.dawarich:2322" + # # # value = "photon.komoot.io" + # # } + # # env { + # # name = "PHOTON_API_USE_HTTPS" + # # value = "false" + # # } + # env { + # name = "GEOAPIFY_API_KEY" + # value = var.geoapify_api_key + # } + # env { + # name = "SELF_HOSTED" + # value = "true" + # } + + # # volume_mount { + # # name = "watched" + # # mount_path = "/var/app/tmp/imports/watched" + # # } + # } + } + } + } +} + + +# resource "kubernetes_deployment" "photon" { +# metadata { +# name = "photon" +# namespace = kubernetes_namespace.dawarich.metadata[0].name +# labels = { +# app = "photon" +# } +# } +# spec { +# replicas = 1 +# strategy { +# type = "Recreate" +# } +# selector { +# match_labels = { +# app = "photon" +# } +# } +# template { +# metadata { +# labels = { +# app = "photon" +# } +# } +# spec { + +# container { +# image = "rtuszik/photon-docker:latest" +# name = "photon" +# port { +# name = "tcp" +# container_port = 2322 +# } +# env { +# name = "COUNTRY_CODE" +# value = "bg" +# } + +# volume_mount { +# name = "data" +# mount_path = "/photon/photon_data" +# } +# } +# volume { +# name = "data" +# nfs { +# path = "/mnt/main/photon" +# server = "10.0.10.15" +# } +# } +# } + +# } +# } +# } + + + +resource "kubernetes_service" "dawarich" { + metadata { + name = "dawarich" + namespace = kubernetes_namespace.dawarich.metadata[0].name + labels = { + "app" = "dawarich" + } + } + + spec { + selector = { + app = "dawarich" + } + port { + name = "http" + port = 80 + target_port = 3000 + protocol = "TCP" + } + } +} + +# resource "kubernetes_service" "photon" { +# metadata { +# name = "photon" +# namespace = kubernetes_namespace.dawarich.metadata[0].name +# labels = { +# "app" = "photon" +# } +# } + +# spec { +# selector = { +# app = "photon" +# } +# port { +# name = "http" +# port = 2322 +# target_port = 2322 +# protocol = "TCP" +# } +# } +# } +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.dawarich.metadata[0].name + name = "dawarich" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "0abfd409f2fb" } diff --git a/stacks/dawarich/module/main.tf b/stacks/dawarich/module/main.tf deleted file mode 100644 index 6b9ce1fd..00000000 --- a/stacks/dawarich/module/main.tf +++ /dev/null @@ -1,325 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "database_password" {} -variable "geoapify_api_key" {} -variable "image_version" { - type = string - default = "0.37.1" -} - -resource "kubernetes_namespace" "dawarich" { - metadata { - name = "dawarich" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.dawarich.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "dawarich" { - metadata { - name = "dawarich" - namespace = kubernetes_namespace.dawarich.metadata[0].name - labels = { - app = "dawarich" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "dawarich" - } - } - template { - metadata { - labels = { - app = "dawarich" - } - annotations = { - # "diun.enable" = "true" - # "diun.include_tags" = "latest" - } - } - spec { - - container { - image = "freikin/dawarich:${var.image_version}" - name = "dawarich" - port { - name = "http" - container_port = 3000 - } - port { - name = "prometheus" - container_port = 9394 - } - command = ["web-entrypoint.sh"] - args = ["bin/dev"] - env { - name = "REDIS_URL" - value = "redis://redis.redis.svc.cluster.local:6379" - } - env { - name = "DATABASE_HOST" - value = "postgresql.dbaas" - } - env { - name = "DATABASE_USERNAME" - value = "dawarich" - } - env { - name = "DATABASE_PASSWORD" - value = var.database_password - } - env { - name = "DATABASE_NAME" - value = "dawarich" - } - env { - name = "MIN_MINUTES_SPENT_IN_CITY" - value = "60" - } - env { - name = "TIME_ZONE" - value = "Europe/London" - } - env { - name = "DISTANCE_UNIT" - value = "km" - } - env { - name = "ENABLE_TELEMETRY" - value = "true" - } - env { - name = "APPLICATION_HOSTS" - value = "dawarich.viktorbarzin.me" - } - # env { - # name = "PROMETHEUS_EXPORTER_ENABLED" - # value = "true" - # } - # env { - # name = "PROMETHEUS_EXPORTER_PORT" - # value = "9394" - # } - # env { - # name = "PROMETHEUS_EXPORTER_HOST" - # value = "0.0.0.0" - # } - env { - name = "SELF_HOSTED" - value = "true" - } - # env { - # name = "PHOTON_API_HOST" - # value = "photon.dawarich" - # } - - - # volume_mount { - # name = "watched" - # mount_path = "/var/app/tmp/imports/watched" - # } - } - # container { - # image = "freikin/dawarich:${var.image_version}" - # name = "dawarich-sidekiq" - # command = ["sidekiq-entrypoint.sh"] - # args = ["bundle exec sidekiq"] - # env { - # name = "REDIS_URL" - # value = "redis://redis.redis.svc.cluster.local:6379" - # } - # env { - # name = "DATABASE_HOST" - # value = "postgresql.dbaas" - # } - # env { - # name = "DATABASE_USERNAME" - # value = "dawarich" - # } - # env { - # name = "DATABASE_PASSWORD" - # value = var.database_password - # } - # env { - # name = "DATABASE_NAME" - # value = "dawarich" - # } - # env { - # name = "MIN_MINUTES_SPENT_IN_CITY" - # value = "60" - # } - # env { - # name = "BACKGROUND_PROCESSING_CONCURRENCY" - # value = "10" - # } - # env { - # name = "ENABLE_TELEMETRY" - # value = "true" - # } - # env { - # name = "APPLICATION_HOST" - # value = "dawarich.viktorbarzin.me" - # } - # # env { - # # name = "PROMETHEUS_EXPORTER_ENABLED" - # # value = "false" - # # } - # # env { - # # name = "PROMETHEUS_EXPORTER_HOST" - # # value = "dawarich.dawarich" - # # } - # # env { - # # name = "PHOTON_API_HOST" - # # value = "photon.dawarich:2322" - # # # value = "photon.komoot.io" - # # } - # # env { - # # name = "PHOTON_API_USE_HTTPS" - # # value = "false" - # # } - # env { - # name = "GEOAPIFY_API_KEY" - # value = var.geoapify_api_key - # } - # env { - # name = "SELF_HOSTED" - # value = "true" - # } - - # # volume_mount { - # # name = "watched" - # # mount_path = "/var/app/tmp/imports/watched" - # # } - # } - } - } - } -} - - -# resource "kubernetes_deployment" "photon" { -# metadata { -# name = "photon" -# namespace = kubernetes_namespace.dawarich.metadata[0].name -# labels = { -# app = "photon" -# } -# } -# spec { -# replicas = 1 -# strategy { -# type = "Recreate" -# } -# selector { -# match_labels = { -# app = "photon" -# } -# } -# template { -# metadata { -# labels = { -# app = "photon" -# } -# } -# spec { - -# container { -# image = "rtuszik/photon-docker:latest" -# name = "photon" -# port { -# name = "tcp" -# container_port = 2322 -# } -# env { -# name = "COUNTRY_CODE" -# value = "bg" -# } - -# volume_mount { -# name = "data" -# mount_path = "/photon/photon_data" -# } -# } -# volume { -# name = "data" -# nfs { -# path = "/mnt/main/photon" -# server = "10.0.10.15" -# } -# } -# } - -# } -# } -# } - - - -resource "kubernetes_service" "dawarich" { - metadata { - name = "dawarich" - namespace = kubernetes_namespace.dawarich.metadata[0].name - labels = { - "app" = "dawarich" - } - } - - spec { - selector = { - app = "dawarich" - } - port { - name = "http" - port = 80 - target_port = 3000 - protocol = "TCP" - } - } -} - -# resource "kubernetes_service" "photon" { -# metadata { -# name = "photon" -# namespace = kubernetes_namespace.dawarich.metadata[0].name -# labels = { -# "app" = "photon" -# } -# } - -# spec { -# selector = { -# app = "photon" -# } -# port { -# name = "http" -# port = 2322 -# target_port = 2322 -# protocol = "TCP" -# } -# } -# } -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.dawarich.metadata[0].name - name = "dawarich" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "0abfd409f2fb" -} diff --git a/stacks/descheduler/main.tf b/stacks/descheduler/main.tf index a56cbbcf..0f62eea2 100644 --- a/stacks/descheduler/main.tf +++ b/stacks/descheduler/main.tf @@ -1,3 +1,89 @@ -module "descheduler" { - source = "./module" + + +resource "kubernetes_namespace" "descheduler" { + metadata { + name = "descheduler" + } +} + +resource "kubernetes_cluster_role" "descheduler" { + metadata { + name = "descheduler-cluster-role" + } + rule { + api_groups = [""] + resources = ["events"] + verbs = ["create", "update"] + } + rule { + api_groups = ["metrics.k8s.io"] + resources = ["nodes"] + verbs = ["get", "watch", "list"] + } + rule { + api_groups = [""] + resources = ["namespaces"] + verbs = ["get", "list", "watch"] + } + rule { + api_groups = ["metrics.k8s.io"] + resources = ["pods"] + verbs = ["get", "watch", "list", "delete"] + } + rule { + api_groups = [""] + resources = ["pods/eviction"] + verbs = ["create"] + } + rule { + api_groups = [""] + resources = ["scheduling.k8s.io"] + verbs = ["get", "watch", "list"] + } + rule { + api_groups = ["scheduling.k8s.io"] + resources = ["priorityclasses"] + verbs = ["get", "list", "watch"] + } + rule { + api_groups = ["policy"] + resources = ["poddisruptionbudgets"] + verbs = ["get", "list", "watch"] + } +} + +resource "kubernetes_service_account" "descheduler" { + metadata { + name = "descheduler-sa" + namespace = kubernetes_namespace.descheduler.metadata[0].name + } +} + +resource "kubernetes_cluster_role_binding" "descheduler" { + metadata { + name = "descheduler-cluster-role-binding" + + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = "descheduler-cluster-role" + } + subject { + name = "descheduler-sa" + kind = "ServiceAccount" + namespace = kubernetes_namespace.descheduler.metadata[0].name + } +} + +resource "helm_release" "descheduler" { # rename me + namespace = kubernetes_namespace.descheduler.metadata[0].name + name = "descheduler" + + repository = "https://kubernetes-sigs.github.io/descheduler/" + chart = "descheduler" + + + + values = [templatefile("${path.module}/values.yaml", {})] } diff --git a/stacks/descheduler/module/main.tf b/stacks/descheduler/module/main.tf deleted file mode 100644 index e7fed580..00000000 --- a/stacks/descheduler/module/main.tf +++ /dev/null @@ -1,87 +0,0 @@ -resource "kubernetes_namespace" "descheduler" { - metadata { - name = "descheduler" - } -} - -resource "kubernetes_cluster_role" "descheduler" { - metadata { - name = "descheduler-cluster-role" - } - rule { - api_groups = [""] - resources = ["events"] - verbs = ["create", "update"] - } - rule { - api_groups = ["metrics.k8s.io"] - resources = ["nodes"] - verbs = ["get", "watch", "list"] - } - rule { - api_groups = [""] - resources = ["namespaces"] - verbs = ["get", "list", "watch"] - } - rule { - api_groups = ["metrics.k8s.io"] - resources = ["pods"] - verbs = ["get", "watch", "list", "delete"] - } - rule { - api_groups = [""] - resources = ["pods/eviction"] - verbs = ["create"] - } - rule { - api_groups = [""] - resources = ["scheduling.k8s.io"] - verbs = ["get", "watch", "list"] - } - rule { - api_groups = ["scheduling.k8s.io"] - resources = ["priorityclasses"] - verbs = ["get", "list", "watch"] - } - rule { - api_groups = ["policy"] - resources = ["poddisruptionbudgets"] - verbs = ["get", "list", "watch"] - } -} - -resource "kubernetes_service_account" "descheduler" { - metadata { - name = "descheduler-sa" - namespace = kubernetes_namespace.descheduler.metadata[0].name - } -} - -resource "kubernetes_cluster_role_binding" "descheduler" { - metadata { - name = "descheduler-cluster-role-binding" - - } - role_ref { - api_group = "rbac.authorization.k8s.io" - kind = "ClusterRole" - name = "descheduler-cluster-role" - } - subject { - name = "descheduler-sa" - kind = "ServiceAccount" - namespace = kubernetes_namespace.descheduler.metadata[0].name - } -} - -resource "helm_release" "descheduler" { # rename me - namespace = kubernetes_namespace.descheduler.metadata[0].name - name = "descheduler" - - repository = "https://kubernetes-sigs.github.io/descheduler/" - chart = "descheduler" - - - - values = [templatefile("${path.module}/values.yaml", {})] -} diff --git a/stacks/descheduler/module/values.yaml b/stacks/descheduler/values.yaml similarity index 100% rename from stacks/descheduler/module/values.yaml rename to stacks/descheduler/values.yaml diff --git a/stacks/diun/main.tf b/stacks/diun/main.tf index 332bf1b4..8f0d2d1b 100644 --- a/stacks/diun/main.tf +++ b/stacks/diun/main.tf @@ -12,10 +12,174 @@ locals { } } -module "diun" { - source = "./module" - tls_secret_name = var.tls_secret_name - diun_nfty_token = var.diun_nfty_token - diun_slack_url = var.diun_slack_url - tier = local.tiers.aux +resource "kubernetes_namespace" "diun" { + metadata { + name = "diun" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.diun.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_service_account" "diun" { + metadata { + name = "diun" + namespace = kubernetes_namespace.diun.metadata[0].name + } +} + +resource "kubernetes_cluster_role" "diun" { + metadata { + name = "diun" + } + rule { + api_groups = [""] + resources = ["pods"] + verbs = ["get", "watch", "list"] + } +} +resource "kubernetes_cluster_role_binding" "diun" { + metadata { + name = "diun" + + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = "diun" + } + subject { + kind = "ServiceAccount" + name = "diun" + namespace = kubernetes_namespace.diun.metadata[0].name + } +} + +resource "kubernetes_deployment" "diun" { + metadata { + name = "diun" + namespace = kubernetes_namespace.diun.metadata[0].name + labels = { + app = "diun" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + "diun.enable" = "true" + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "diun" + } + } + template { + metadata { + labels = { + app = "diun" + } + } + spec { + service_account_name = "diun" + container { + image = "crazymax/diun:latest" + name = "diun" + args = ["serve"] + env { + name = "TZ" + value = "Europe/Sofia" + } + env { + name = "DIUN_WATCH_WORKERS" + value = "20" + } + env { + name = "DIUN_WATCH_SCHEDULE" + value = "0 */6 * * *" + } + env { + name = "DIUN_WATCH_JITTER" + value = "30s" + } + env { + name = "DIUN_PROVIDERS_KUBERNETES" + value = "true" + } + # env { + # name = "DIUN_DEFAULTS_EXCLUDETAGS" + # value = "^.*nightly.*$" + # } + # env { + # name = "DIUN_DEFAULTS_INCLUDETAGS" + # value = "^\\d+\\.\\d+\\.\\d+$" + # } + env { + name = "DIUN_DEFAULTS_WATCHREPO" + value = "true" + # value = "false" + } + env { + name = "DIUN_DEFAULTS_MAXTAGS" + value = "3" + } + env { + name = "DIUN_DEFAULTS_SORTTAGS" + value = "reverse" + } + # DIUN_PROVIDERS_KUBERNETES_WATCHBYDEFAULT = "true" ?? + + // ntfy settings + # env { // disabled as if this fails, no other notifications are sent + # name = "DIUN_NOTIF_NTFY_ENDPOINT" + # value = "https://ntfy.viktorbarzin.me" + # } + # env { + # name = "DIUN_NOTIF_NTFY_TOPIC" + # value = "diun-updates" + # } + # env { + # name = "DIUN_NOTIF_NTFY_TOKEN" + # value = var.diun_nfty_token + # } + env { + name = "DIUN_NOTIF_SLACK_WEBHOOKURL" + value = var.diun_slack_url + } + env { + name = "LOG_LEVEL" + # value = "info" + value = "debug" + } + # env { + # name = "DIUN_WATCH_FIRSTCHECKNOTIF" + # value = "true" # send notfication on start; subsequent checks check for newer versions and is what you need + # } + # env { + # name = "DIUN_NOTIF_NTFY_TIMEOUT" + # value = "10s" + # } + volume_mount { + name = "data" + mount_path = "/data" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/diun" + server = "10.0.10.15" + } + } + } + } + } } diff --git a/stacks/diun/module/main.tf b/stacks/diun/module/main.tf deleted file mode 100644 index ed7809f0..00000000 --- a/stacks/diun/module/main.tf +++ /dev/null @@ -1,177 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "diun_nfty_token" {} -variable "diun_slack_url" {} - -resource "kubernetes_namespace" "diun" { - metadata { - name = "diun" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.diun.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_service_account" "diun" { - metadata { - name = "diun" - namespace = kubernetes_namespace.diun.metadata[0].name - } -} - -resource "kubernetes_cluster_role" "diun" { - metadata { - name = "diun" - } - rule { - api_groups = [""] - resources = ["pods"] - verbs = ["get", "watch", "list"] - } -} -resource "kubernetes_cluster_role_binding" "diun" { - metadata { - name = "diun" - - } - role_ref { - api_group = "rbac.authorization.k8s.io" - kind = "ClusterRole" - name = "diun" - } - subject { - kind = "ServiceAccount" - name = "diun" - namespace = kubernetes_namespace.diun.metadata[0].name - } -} - -resource "kubernetes_deployment" "diun" { - metadata { - name = "diun" - namespace = kubernetes_namespace.diun.metadata[0].name - labels = { - app = "diun" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - "diun.enable" = "true" - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "diun" - } - } - template { - metadata { - labels = { - app = "diun" - } - } - spec { - service_account_name = "diun" - container { - image = "crazymax/diun:latest" - name = "diun" - args = ["serve"] - env { - name = "TZ" - value = "Europe/Sofia" - } - env { - name = "DIUN_WATCH_WORKERS" - value = "20" - } - env { - name = "DIUN_WATCH_SCHEDULE" - value = "0 */6 * * *" - } - env { - name = "DIUN_WATCH_JITTER" - value = "30s" - } - env { - name = "DIUN_PROVIDERS_KUBERNETES" - value = "true" - } - # env { - # name = "DIUN_DEFAULTS_EXCLUDETAGS" - # value = "^.*nightly.*$" - # } - # env { - # name = "DIUN_DEFAULTS_INCLUDETAGS" - # value = "^\\d+\\.\\d+\\.\\d+$" - # } - env { - name = "DIUN_DEFAULTS_WATCHREPO" - value = "true" - # value = "false" - } - env { - name = "DIUN_DEFAULTS_MAXTAGS" - value = "3" - } - env { - name = "DIUN_DEFAULTS_SORTTAGS" - value = "reverse" - } - # DIUN_PROVIDERS_KUBERNETES_WATCHBYDEFAULT = "true" ?? - - // ntfy settings - # env { // disabled as if this fails, no other notifications are sent - # name = "DIUN_NOTIF_NTFY_ENDPOINT" - # value = "https://ntfy.viktorbarzin.me" - # } - # env { - # name = "DIUN_NOTIF_NTFY_TOPIC" - # value = "diun-updates" - # } - # env { - # name = "DIUN_NOTIF_NTFY_TOKEN" - # value = var.diun_nfty_token - # } - env { - name = "DIUN_NOTIF_SLACK_WEBHOOKURL" - value = var.diun_slack_url - } - env { - name = "LOG_LEVEL" - # value = "info" - value = "debug" - } - # env { - # name = "DIUN_WATCH_FIRSTCHECKNOTIF" - # value = "true" # send notfication on start; subsequent checks check for newer versions and is what you need - # } - # env { - # name = "DIUN_NOTIF_NTFY_TIMEOUT" - # value = "10s" - # } - volume_mount { - name = "data" - mount_path = "/data" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/diun" - server = "10.0.10.15" - } - } - } - } - } -} - diff --git a/stacks/drone/main.tf b/stacks/drone/main.tf index 7a2cb194..471553a2 100644 --- a/stacks/drone/main.tf +++ b/stacks/drone/main.tf @@ -14,15 +14,413 @@ locals { } } -module "drone" { - source = "./module" - tls_secret_name = var.tls_secret_name - git_crypt_key_base64 = filebase64("${path.root}/../../.git/git-crypt/keys/default") - 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.tiers.edge +variable "rpc_host" { + default = "drone.drone.svc.cluster.local" } +variable "allowed_users" { + # comma separated list + default = "viktorbarzin,ancamilea" +} + +resource "kubernetes_namespace" "drone" { + metadata { + name = "drone" + labels = { + "resource-governance/custom-quota" = "true" + tier = local.tiers.edge + } + } +} + +resource "kubernetes_resource_quota" "drone" { + metadata { + name = "tier-quota" + namespace = kubernetes_namespace.drone.metadata[0].name + } + spec { + hard = { + "requests.cpu" = "16" + "requests.memory" = "16Gi" + "limits.cpu" = "64" + "limits.memory" = "128Gi" + pods = "60" + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.drone.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_config_map" "git_crypt_key" { + metadata { + name = "git-crypt-key" + namespace = kubernetes_namespace.drone.metadata[0].name + } + + data = { + "key" = filebase64("${path.root}/../../.git/git-crypt/keys/default") + } +} + +resource "kubernetes_deployment" "drone_server" { + metadata { + name = "drone-server" + namespace = kubernetes_namespace.drone.metadata[0].name + labels = { + app = "drone" + tier = local.tiers.edge + } + } + spec { + strategy { + type = "Recreate" + } + replicas = 1 + selector { + match_labels = { + app = "drone" + } + } + template { + metadata { + labels = { + app = "drone" + } + } + spec { + container { + image = "drone/drone:2.27.0" + name = "drone-server" + # resources { + # limits = { + # cpu = "1" + # memory = "1Gi" + # } + # requests = { + # cpu = "500m" + # memory = "1Gi" + # } + # } + port { + container_port = 80 + } + volume_mount { + name = "data" + mount_path = "/data" + } + + env { + name = "DRONE_GITHUB_CLIENT_ID" + value = var.drone_github_client_id + } + env { + name = "DRONE_GITHUB_CLIENT_SECRET" + value = var.drone_github_client_secret + } + env { + name = "DRONE_RPC_SECRET" + value = var.drone_rpc_secret + } + env { + name = "DRONE_WEBHOOK_SECRET" + value = var.drone_webhook_secret + } + env { + name = "DRONE_SERVER_HOST" + value = "drone.viktorbarzin.me" + } + env { + name = "DRONE_SERVER_PROTO" + value = "https" + } + env { + name = "DRONE_USER_FILTER" + value = var.allowed_users + } + env { + name = "DRONE_CRON_INTERVAL" + value = "1m" + } + env { + name = "DRONE_LOGS_TRACE" + value = "true" + } + env { + name = "DRONE_LOGS_PRETTY" + value = "true" + } + env { + name = "DRONE_LOGS_TEXT" + value = "true" + } + + } + volume { + name = "data" + nfs { + path = "/mnt/main/drone" + server = "10.0.10.15" + } + # iscsi { + # target_portal = "iscsi.viktorbarzin.lan:3260" + # fs_type = "ext4" + # iqn = "iqn.2020-12.lan.viktorbarzin:storage:drone" + # lun = 0 + # read_only = false + # } + } + } + } + } +} + +resource "kubernetes_service" "drone" { + metadata { + name = "drone" + namespace = kubernetes_namespace.drone.metadata[0].name + labels = { + app = "drone" + } + } + + spec { + selector = { + app = "drone" + } + port { + name = "http" + port = "80" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.drone.metadata[0].name + name = "drone" + tls_secret_name = var.tls_secret_name + # protected = true +} + + +# Setup drone runner +resource "kubernetes_cluster_role" "drone" { + metadata { + name = "drone" + } + rule { + api_groups = [""] + resources = ["configmaps"] + verbs = ["get", "list", "update", "patch"] + } + rule { + api_groups = [""] + resources = ["secrets"] + verbs = ["get", "list", "create", "delete"] + } + rule { + api_groups = [""] + resources = ["pods", "pods/log"] + verbs = ["get", "create", "delete", "list", "watch", "update"] + } + rule { + api_groups = ["apps"] + resources = ["deployments"] + verbs = ["get", "create", "delete", "list", "watch", "update", "patch"] + } +} + +resource "kubernetes_cluster_role_binding" "drone" { + metadata { + name = "drone" + } + subject { + kind = "ServiceAccount" + name = "default" + namespace = kubernetes_namespace.drone.metadata[0].name + } + role_ref { + kind = "ClusterRole" + # name = "drone" + name = "cluster-admin" + api_group = "rbac.authorization.k8s.io" + } +} + +resource "kubernetes_deployment" "drone_runner" { + metadata { + name = "drone-runner" + namespace = kubernetes_namespace.drone.metadata[0].name + labels = { + app = "drone-runner" + tier = local.tiers.edge + } + } + spec { + strategy { + type = "Recreate" + } + replicas = 4 + selector { + match_labels = { + app = "drone-runner" + } + } + template { + metadata { + labels = { + app = "drone-runner" + } + } + spec { + container { + image = "drone/drone-runner-kube:latest" + name = "drone-runner" + # resources { + # limits = { + # cpu = "1" + # memory = "1Gi" + # } + # requests = { + # cpu = "500m" + # memory = "1Gi" + # } + # } + env { + name = "DRONE_RPC_HOST" + value = var.rpc_host + } + env { + name = "DRONE_RPC_PROTO" + value = "http" + } + env { + name = "DRONE_RPC_SECRET" + value = var.drone_rpc_secret + } + env { + name = "DRONE_NAMESPACE_DEFAULT" + value = "drone" + } + env { + name = "SECRET_KEY" + value = var.drone_rpc_secret + } + env { + name = "DRONE_SECRET_PLUGIN_ENDPOINT" + value = "http://drone-runner-secret.drone.svc.cluster.local:3000" + } + env { + name = "DRONE_SECRET_PLUGIN_TOKEN" + value = var.drone_rpc_secret + } + env { + name = "DRONE_DEBUG" + value = "true" + } + env { + name = "DRONE_IMAGES_CLONE" + value = "alpine/git:latest" + } + } + } + } + } +} +resource "kubernetes_deployment" "drone_runner_secret" { + metadata { + name = "drone-runner-secret" + namespace = kubernetes_namespace.drone.metadata[0].name + labels = { + app = "drone-runner-secret" + tier = local.tiers.edge + } + } + spec { + strategy { + type = "Recreate" + } + replicas = 1 + selector { + match_labels = { + app = "drone-runner-secret" + } + } + template { + metadata { + labels = { + app = "drone-runner-secret" + } + } + spec { + container { + name = "secret" + image = "drone/kubernetes-secrets:latest" + port { + container_port = 3000 + } + env { + name = "SECRET_KEY" + value = var.drone_rpc_secret + } + env { + name = "DEBUG" + value = "true" + } + env { + name = "KUBERNETES_NAMESPACE" + value = "drone" + } + // Custom variable to start terraform as prod + env { + name = "TF_VAR_prod" + value = true + } + } + } + } + } +} + + +resource "kubernetes_service" "drone_runner_secret" { + metadata { + name = "drone-runner-secret" + namespace = kubernetes_namespace.drone.metadata[0].name + labels = { + app = "drone-runner-secret" + } + } + + spec { + selector = { + app = "drone-runner-secret" + } + port { + name = "http" + port = "3000" + } + } +} + +# SQL to delete last N builds (n = 1000) +# PRAGMA foreign_keys = ON; + +# WITH n_build_ids_per_repo as ( +# SELECT build_id +# FROM ( +# SELECT +# build_id, +# build_repo_id, +# DENSE_RANK() OVER (PARTITION BY build_repo_id ORDER BY build_id DESC) AS rank +# FROM builds +# ) AS t +# WHERE t.rank <= 1000 +# ) +# DELETE FROM +# builds +# WHERE +# builds.build_id NOT IN (SELECT build_id FROM n_build_ids_per_repo); diff --git a/stacks/drone/module/main.tf b/stacks/drone/module/main.tf deleted file mode 100644 index 9a84de24..00000000 --- a/stacks/drone/module/main.tf +++ /dev/null @@ -1,419 +0,0 @@ -variable "tls_secret_name" {} -variable "git_crypt_key_base64" { type = string } -variable "tier" { type = string } -variable "github_client_id" {} -variable "github_client_secret" {} -variable "rpc_secret" {} -variable "webhook_secret" {} -variable "server_host" {} -variable "server_proto" {} -variable "rpc_host" { - default = "drone.drone.svc.cluster.local" -} -variable "allowed_users" { - # comma separated list - default = "viktorbarzin,ancamilea" -} - -resource "kubernetes_namespace" "drone" { - metadata { - name = "drone" - labels = { - "resource-governance/custom-quota" = "true" - tier = var.tier - } - } -} - -resource "kubernetes_resource_quota" "drone" { - metadata { - name = "tier-quota" - namespace = kubernetes_namespace.drone.metadata[0].name - } - spec { - hard = { - "requests.cpu" = "16" - "requests.memory" = "16Gi" - "limits.cpu" = "64" - "limits.memory" = "128Gi" - pods = "60" - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.drone.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_config_map" "git_crypt_key" { - metadata { - name = "git-crypt-key" - namespace = kubernetes_namespace.drone.metadata[0].name - } - - data = { - "key" = var.git_crypt_key_base64 - } -} - -resource "kubernetes_deployment" "drone_server" { - metadata { - name = "drone-server" - namespace = kubernetes_namespace.drone.metadata[0].name - labels = { - app = "drone" - tier = var.tier - } - } - spec { - strategy { - type = "Recreate" - } - replicas = 1 - selector { - match_labels = { - app = "drone" - } - } - template { - metadata { - labels = { - app = "drone" - } - } - spec { - container { - image = "drone/drone:2.27.0" - name = "drone-server" - # resources { - # limits = { - # cpu = "1" - # memory = "1Gi" - # } - # requests = { - # cpu = "500m" - # memory = "1Gi" - # } - # } - port { - container_port = 80 - } - volume_mount { - name = "data" - mount_path = "/data" - } - - env { - name = "DRONE_GITHUB_CLIENT_ID" - value = var.github_client_id - } - env { - name = "DRONE_GITHUB_CLIENT_SECRET" - value = var.github_client_secret - } - env { - name = "DRONE_RPC_SECRET" - value = var.rpc_secret - } - env { - name = "DRONE_WEBHOOK_SECRET" - value = var.webhook_secret - } - env { - name = "DRONE_SERVER_HOST" - value = var.server_host - } - env { - name = "DRONE_SERVER_PROTO" - value = var.server_proto - } - env { - name = "DRONE_USER_FILTER" - value = var.allowed_users - } - env { - name = "DRONE_CRON_INTERVAL" - value = "1m" - } - env { - name = "DRONE_LOGS_TRACE" - value = "true" - } - env { - name = "DRONE_LOGS_PRETTY" - value = "true" - } - env { - name = "DRONE_LOGS_TEXT" - value = "true" - } - - } - volume { - name = "data" - nfs { - path = "/mnt/main/drone" - server = "10.0.10.15" - } - # iscsi { - # target_portal = "iscsi.viktorbarzin.lan:3260" - # fs_type = "ext4" - # iqn = "iqn.2020-12.lan.viktorbarzin:storage:drone" - # lun = 0 - # read_only = false - # } - } - } - } - } -} - -resource "kubernetes_service" "drone" { - metadata { - name = "drone" - namespace = kubernetes_namespace.drone.metadata[0].name - labels = { - app = "drone" - } - } - - spec { - selector = { - app = "drone" - } - port { - name = "http" - port = "80" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.drone.metadata[0].name - name = "drone" - tls_secret_name = var.tls_secret_name - # protected = true -} - - -# Setup drone runner -resource "kubernetes_cluster_role" "drone" { - metadata { - name = "drone" - } - rule { - api_groups = [""] - resources = ["configmaps"] - verbs = ["get", "list", "update", "patch"] - } - rule { - api_groups = [""] - resources = ["secrets"] - verbs = ["get", "list", "create", "delete"] - } - rule { - api_groups = [""] - resources = ["pods", "pods/log"] - verbs = ["get", "create", "delete", "list", "watch", "update"] - } - rule { - api_groups = ["apps"] - resources = ["deployments"] - verbs = ["get", "create", "delete", "list", "watch", "update", "patch"] - } -} - -resource "kubernetes_cluster_role_binding" "drone" { - metadata { - name = "drone" - } - subject { - kind = "ServiceAccount" - name = "default" - namespace = kubernetes_namespace.drone.metadata[0].name - } - role_ref { - kind = "ClusterRole" - # name = "drone" - name = "cluster-admin" - api_group = "rbac.authorization.k8s.io" - } -} - -resource "kubernetes_deployment" "drone_runner" { - metadata { - name = "drone-runner" - namespace = kubernetes_namespace.drone.metadata[0].name - labels = { - app = "drone-runner" - tier = var.tier - } - } - spec { - strategy { - type = "Recreate" - } - replicas = 4 - selector { - match_labels = { - app = "drone-runner" - } - } - template { - metadata { - labels = { - app = "drone-runner" - } - } - spec { - container { - image = "drone/drone-runner-kube:latest" - name = "drone-runner" - # resources { - # limits = { - # cpu = "1" - # memory = "1Gi" - # } - # requests = { - # cpu = "500m" - # memory = "1Gi" - # } - # } - env { - name = "DRONE_RPC_HOST" - value = var.rpc_host - } - env { - name = "DRONE_RPC_PROTO" - value = "http" - } - env { - name = "DRONE_RPC_SECRET" - value = var.rpc_secret - } - env { - name = "DRONE_NAMESPACE_DEFAULT" - value = "drone" - } - env { - name = "SECRET_KEY" - value = var.rpc_secret - } - env { - name = "DRONE_SECRET_PLUGIN_ENDPOINT" - value = "http://drone-runner-secret.drone.svc.cluster.local:3000" - } - env { - name = "DRONE_SECRET_PLUGIN_TOKEN" - value = var.rpc_secret - } - env { - name = "DRONE_DEBUG" - value = "true" - } - env { - name = "DRONE_IMAGES_CLONE" - value = "alpine/git:latest" - } - } - } - } - } -} -resource "kubernetes_deployment" "drone_runner_secret" { - metadata { - name = "drone-runner-secret" - namespace = kubernetes_namespace.drone.metadata[0].name - labels = { - app = "drone-runner-secret" - tier = var.tier - } - } - spec { - strategy { - type = "Recreate" - } - replicas = 1 - selector { - match_labels = { - app = "drone-runner-secret" - } - } - template { - metadata { - labels = { - app = "drone-runner-secret" - } - } - spec { - container { - name = "secret" - image = "drone/kubernetes-secrets:latest" - port { - container_port = 3000 - } - env { - name = "SECRET_KEY" - value = var.rpc_secret - } - env { - name = "DEBUG" - value = "true" - } - env { - name = "KUBERNETES_NAMESPACE" - value = "drone" - } - // Custom variable to start terraform as prod - env { - name = "TF_VAR_prod" - value = true - } - } - } - } - } -} - - -resource "kubernetes_service" "drone_runner_secret" { - metadata { - name = "drone-runner-secret" - namespace = kubernetes_namespace.drone.metadata[0].name - labels = { - app = "drone-runner-secret" - } - } - - spec { - selector = { - app = "drone-runner-secret" - } - port { - name = "http" - port = "3000" - } - } -} - -# SQL to delete last N builds (n = 1000) -# PRAGMA foreign_keys = ON; - -# WITH n_build_ids_per_repo as ( -# SELECT build_id -# FROM ( -# SELECT -# build_id, -# build_repo_id, -# DENSE_RANK() OVER (PARTITION BY build_repo_id ORDER BY build_id DESC) AS rank -# FROM builds -# ) AS t -# WHERE t.rank <= 1000 -# ) -# DELETE FROM -# builds -# WHERE -# builds.build_id NOT IN (SELECT build_id FROM n_build_ids_per_repo); diff --git a/stacks/ebook2audiobook/module/audiblez-web b/stacks/ebook2audiobook/audiblez-web similarity index 100% rename from stacks/ebook2audiobook/module/audiblez-web rename to stacks/ebook2audiobook/audiblez-web diff --git a/stacks/ebook2audiobook/main.tf b/stacks/ebook2audiobook/main.tf index ee3c57db..8fd9864d 100644 --- a/stacks/ebook2audiobook/main.tf +++ b/stacks/ebook2audiobook/main.tf @@ -10,8 +10,408 @@ locals { } } -module "ebook2audiobook" { - source = "./module" +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name tls_secret_name = var.tls_secret_name - tier = local.tiers.gpu +} + +resource "kubernetes_namespace" "ebook2audiobook" { + metadata { + name = "ebook2audiobook" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.gpu + } + } +} + + +resource "kubernetes_deployment" "ebook2audiobook" { + metadata { + name = "ebook2audiobook" + namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name + labels = { + app = "ebook2audiobook" + tier = local.tiers.gpu + } + } + spec { + replicas = 0 # Disabled - using audiblez instead + strategy { + type = "Recreate" + } + + selector { + match_labels = { + app = "ebook2audiobook" + } + } + + template { + metadata { + labels = { + app = "ebook2audiobook" + } + } + + spec { + node_selector = { + "gpu" : "true" + } + toleration { + key = "nvidia.com/gpu" + operator = "Equal" + value = "true" + effect = "NoSchedule" + } + + container { + name = "ebook2audiobook" + image = "docker.io/athomasson2/ebook2audiobook:v25.12.30-cu128" + + tty = true + stdin = true + + port { + container_port = 7860 + } + + # LD_LIBRARY_PATH needed for CUDA detection - libcudart.so is in non-standard location + env { + name = "LD_LIBRARY_PATH" + value = "/usr/local/lib/python3.12/site-packages/nvidia/cuda_runtime/lib:/usr/local/lib/python3.12/site-packages/nvidia/cudnn/lib" + } + + volume_mount { + mount_path = "/home/user" + name = "data" + } + + resources { + limits = { + "nvidia.com/gpu" = "1" + } + } + } + + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/ebook2audiobook" + } + } + } + } + } +} + + +resource "kubernetes_service" "ebook2audiobook" { + metadata { + name = "ebook2audiobook" + namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name + labels = { + "app" = "ebook2audiobook" + } + } + + spec { + selector = { + app = "ebook2audiobook" + } + port { + name = "http" + port = 80 + target_port = 7860 + } + } +} + +# resource "kubernetes_deployment" "piper" { +# metadata { +# name = "piper" +# namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name +# labels = { +# app = "piper" +# } +# } +# spec { +# replicas = 1 +# strategy { +# type = "Recreate" +# } + +# selector { +# match_labels = { +# app = "piper" +# } +# } + +# template { +# metadata { +# labels = { +# app = "piper" +# } +# } + +# spec { +# container { +# name = "piper" +# # image = "lscr.io/linuxserver/piper:gpu" +# # image = "piper-tts-wyoming:latest" +# image = "viktorbarzin/piper" +# # image = "nvidia/cuda:12.8.1-cudnn-devel-ubuntu24.04" + +# # working_dir = "/app" +# command = ["sleep", "3600"] + +# volume_mount { +# mount_path = "/config" +# name = "data" +# } + +# resources { +# limits = { +# "nvidia.com/gpu" = "1" +# } +# } +# # env { +# # name = "PIPER_VOICE" +# # value = "en_US-lessac-medium" +# # } + +# env { +# name = "VOICE_MODEL" +# value = "en_US-lessac-medium" +# } +# env { +# name = "LOG_LEVEL" +# value = "DEBUG" +# } +# port { +# name = "web" +# container_port = 10200 +# } +# } + +# volume { +# name = "data" +# nfs { +# server = "10.0.10.15" +# path = "/mnt/main/piper" +# } +# } +# } +# } +# } +# } + +# resource "kubernetes_service" "piper" { +# metadata { +# name = "piper" +# namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name +# labels = { +# "app" = "piper" +# } +# } + +# spec { +# selector = { +# app = "piper" +# } +# port { +# name = "http" +# port = 80 +# target_port = 10200 +# } +# } +# } + + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name + name = "ebook2audiobook" + tls_secret_name = var.tls_secret_name + protected = true +} + + +resource "kubernetes_deployment" "audiblez" { + metadata { + name = "audiblez" + namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name + labels = { + app = "audiblez" + tier = local.tiers.gpu + } + } + spec { + replicas = 0 # Disabled - using audiblez-web instead + selector { + match_labels = { + app = "audiblez" + } + } + template { + metadata { + labels = { + app = "audiblez" + } + } + spec { + node_selector = { + "gpu" : "true" + } + toleration { + key = "nvidia.com/gpu" + operator = "Equal" + value = "true" + effect = "NoSchedule" + } + container { + image = "viktorbarzin/audiblez:latest" + name = "audiblez" + command = ["/usr/bin/sleep", "infinity"] + volume_mount { + name = "data" + mount_path = "/mnt" + } + resources { + limits = { + "nvidia.com/gpu" = "1" + } + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/audiblez" + } + } + } + } + } +} + + +# Audiblez Web UI +resource "kubernetes_deployment" "audiblez-web" { + metadata { + name = "audiblez-web" + namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name + labels = { + app = "audiblez-web" + tier = local.tiers.gpu + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "audiblez-web" + } + } + template { + metadata { + labels = { + app = "audiblez-web" + } + } + spec { + node_selector = { + "gpu" : "true" + } + toleration { + key = "nvidia.com/gpu" + operator = "Equal" + value = "true" + effect = "NoSchedule" + } + container { + # Use digest to bypass local registry cache + image = "docker.io/viktorbarzin/audiblez-web@sha256:eb6d13e6372b931bcac45ca389c063dfadc7b3fc2a607127fc76c5627b13a34c" + image_pull_policy = "Always" + name = "audiblez-web" + + port { + container_port = 8000 + } + + volume_mount { + name = "data" + mount_path = "/mnt" + } + + resources { + limits = { + "nvidia.com/gpu" = "1" + } + } + + # liveness_probe { + # http_get { + # path = "/health" + # port = 8000 + # } + # initial_delay_seconds = 10 + # period_seconds = 30 + # } + + # readiness_probe { + # http_get { + # path = "/health" + # port = 8000 + # } + # initial_delay_seconds = 5 + # period_seconds = 10 + # } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/audiblez" + } + } + } + } + } +} + +resource "kubernetes_service" "audiblez-web" { + metadata { + name = "audiblez-web" + namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name + labels = { + "app" = "audiblez-web" + } + } + + spec { + selector = { + app = "audiblez-web" + } + port { + name = "http" + port = 80 + target_port = 8000 + } + } +} + +module "audiblez-web-ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name + name = "audiblez-web" + host = "audiblez" + tls_secret_name = var.tls_secret_name + protected = true + max_body_size = "500m" # Allow large EPUB uploads } diff --git a/stacks/ebook2audiobook/module/main.tf b/stacks/ebook2audiobook/module/main.tf deleted file mode 100644 index 8f0e609f..00000000 --- a/stacks/ebook2audiobook/module/main.tf +++ /dev/null @@ -1,410 +0,0 @@ - -variable "tls_secret_name" {} -variable "tier" { type = string } - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_namespace" "ebook2audiobook" { - metadata { - name = "ebook2audiobook" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - - -resource "kubernetes_deployment" "ebook2audiobook" { - metadata { - name = "ebook2audiobook" - namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name - labels = { - app = "ebook2audiobook" - tier = var.tier - } - } - spec { - replicas = 0 # Disabled - using audiblez instead - strategy { - type = "Recreate" - } - - selector { - match_labels = { - app = "ebook2audiobook" - } - } - - template { - metadata { - labels = { - app = "ebook2audiobook" - } - } - - spec { - node_selector = { - "gpu" : "true" - } - toleration { - key = "nvidia.com/gpu" - operator = "Equal" - value = "true" - effect = "NoSchedule" - } - - container { - name = "ebook2audiobook" - image = "docker.io/athomasson2/ebook2audiobook:v25.12.30-cu128" - - tty = true - stdin = true - - port { - container_port = 7860 - } - - # LD_LIBRARY_PATH needed for CUDA detection - libcudart.so is in non-standard location - env { - name = "LD_LIBRARY_PATH" - value = "/usr/local/lib/python3.12/site-packages/nvidia/cuda_runtime/lib:/usr/local/lib/python3.12/site-packages/nvidia/cudnn/lib" - } - - volume_mount { - mount_path = "/home/user" - name = "data" - } - - resources { - limits = { - "nvidia.com/gpu" = "1" - } - } - } - - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/ebook2audiobook" - } - } - } - } - } -} - - -resource "kubernetes_service" "ebook2audiobook" { - metadata { - name = "ebook2audiobook" - namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name - labels = { - "app" = "ebook2audiobook" - } - } - - spec { - selector = { - app = "ebook2audiobook" - } - port { - name = "http" - port = 80 - target_port = 7860 - } - } -} - -# resource "kubernetes_deployment" "piper" { -# metadata { -# name = "piper" -# namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name -# labels = { -# app = "piper" -# } -# } -# spec { -# replicas = 1 -# strategy { -# type = "Recreate" -# } - -# selector { -# match_labels = { -# app = "piper" -# } -# } - -# template { -# metadata { -# labels = { -# app = "piper" -# } -# } - -# spec { -# container { -# name = "piper" -# # image = "lscr.io/linuxserver/piper:gpu" -# # image = "piper-tts-wyoming:latest" -# image = "viktorbarzin/piper" -# # image = "nvidia/cuda:12.8.1-cudnn-devel-ubuntu24.04" - -# # working_dir = "/app" -# command = ["sleep", "3600"] - -# volume_mount { -# mount_path = "/config" -# name = "data" -# } - -# resources { -# limits = { -# "nvidia.com/gpu" = "1" -# } -# } -# # env { -# # name = "PIPER_VOICE" -# # value = "en_US-lessac-medium" -# # } - -# env { -# name = "VOICE_MODEL" -# value = "en_US-lessac-medium" -# } -# env { -# name = "LOG_LEVEL" -# value = "DEBUG" -# } -# port { -# name = "web" -# container_port = 10200 -# } -# } - -# volume { -# name = "data" -# nfs { -# server = "10.0.10.15" -# path = "/mnt/main/piper" -# } -# } -# } -# } -# } -# } - -# resource "kubernetes_service" "piper" { -# metadata { -# name = "piper" -# namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name -# labels = { -# "app" = "piper" -# } -# } - -# spec { -# selector = { -# app = "piper" -# } -# port { -# name = "http" -# port = 80 -# target_port = 10200 -# } -# } -# } - - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name - name = "ebook2audiobook" - tls_secret_name = var.tls_secret_name - protected = true -} - - -resource "kubernetes_deployment" "audiblez" { - metadata { - name = "audiblez" - namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name - labels = { - app = "audiblez" - tier = var.tier - } - } - spec { - replicas = 0 # Disabled - using audiblez-web instead - selector { - match_labels = { - app = "audiblez" - } - } - template { - metadata { - labels = { - app = "audiblez" - } - } - spec { - node_selector = { - "gpu" : "true" - } - toleration { - key = "nvidia.com/gpu" - operator = "Equal" - value = "true" - effect = "NoSchedule" - } - container { - image = "viktorbarzin/audiblez:latest" - name = "audiblez" - command = ["/usr/bin/sleep", "infinity"] - volume_mount { - name = "data" - mount_path = "/mnt" - } - resources { - limits = { - "nvidia.com/gpu" = "1" - } - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/audiblez" - } - } - } - } - } -} - - -# Audiblez Web UI -resource "kubernetes_deployment" "audiblez-web" { - metadata { - name = "audiblez-web" - namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name - labels = { - app = "audiblez-web" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "audiblez-web" - } - } - template { - metadata { - labels = { - app = "audiblez-web" - } - } - spec { - node_selector = { - "gpu" : "true" - } - toleration { - key = "nvidia.com/gpu" - operator = "Equal" - value = "true" - effect = "NoSchedule" - } - container { - # Use digest to bypass local registry cache - image = "docker.io/viktorbarzin/audiblez-web@sha256:eb6d13e6372b931bcac45ca389c063dfadc7b3fc2a607127fc76c5627b13a34c" - image_pull_policy = "Always" - name = "audiblez-web" - - port { - container_port = 8000 - } - - volume_mount { - name = "data" - mount_path = "/mnt" - } - - resources { - limits = { - "nvidia.com/gpu" = "1" - } - } - - # liveness_probe { - # http_get { - # path = "/health" - # port = 8000 - # } - # initial_delay_seconds = 10 - # period_seconds = 30 - # } - - # readiness_probe { - # http_get { - # path = "/health" - # port = 8000 - # } - # initial_delay_seconds = 5 - # period_seconds = 10 - # } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/audiblez" - } - } - } - } - } -} - -resource "kubernetes_service" "audiblez-web" { - metadata { - name = "audiblez-web" - namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name - labels = { - "app" = "audiblez-web" - } - } - - spec { - selector = { - app = "audiblez-web" - } - port { - name = "http" - port = 80 - target_port = 8000 - } - } -} - -module "audiblez-web-ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.ebook2audiobook.metadata[0].name - name = "audiblez-web" - host = "audiblez" - tls_secret_name = var.tls_secret_name - protected = true - max_body_size = "500m" # Allow large EPUB uploads -} - diff --git a/stacks/echo/main.tf b/stacks/echo/main.tf index 946c6230..cfc98271 100644 --- a/stacks/echo/main.tf +++ b/stacks/echo/main.tf @@ -10,8 +10,84 @@ locals { } } -module "echo" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.edge +resource "kubernetes_namespace" "echo" { + metadata { + name = "echo" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.edge + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.echo.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "echo" { + metadata { + name = "echo" + namespace = kubernetes_namespace.echo.metadata[0].name + labels = { + app = "echo" + tier = local.tiers.edge + } + } + spec { + replicas = 5 + selector { + match_labels = { + app = "echo" + } + } + template { + metadata { + labels = { + app = "echo" + } + } + spec { + container { + image = "mendhak/http-https-echo" + name = "echo" + port { + container_port = 8080 + } + port { + container_port = 8443 + } + } + } + } + } +} + +resource "kubernetes_service" "echo" { + metadata { + name = "echo" + namespace = kubernetes_namespace.echo.metadata[0].name + labels = { + "app" = "echo" + } + } + + spec { + selector = { + app = "echo" + } + port { + name = "http" + port = "80" + target_port = "8080" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.echo.metadata[0].name + name = "echo" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/echo/module/main.tf b/stacks/echo/module/main.tf deleted file mode 100644 index e13c4e09..00000000 --- a/stacks/echo/module/main.tf +++ /dev/null @@ -1,84 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "echo" { - metadata { - name = "echo" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.echo.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "echo" { - metadata { - name = "echo" - namespace = kubernetes_namespace.echo.metadata[0].name - labels = { - app = "echo" - tier = var.tier - } - } - spec { - replicas = 5 - selector { - match_labels = { - app = "echo" - } - } - template { - metadata { - labels = { - app = "echo" - } - } - spec { - container { - image = "mendhak/http-https-echo" - name = "echo" - port { - container_port = 8080 - } - port { - container_port = 8443 - } - } - } - } - } -} - -resource "kubernetes_service" "echo" { - metadata { - name = "echo" - namespace = kubernetes_namespace.echo.metadata[0].name - labels = { - "app" = "echo" - } - } - - spec { - selector = { - app = "echo" - } - port { - name = "http" - port = "80" - target_port = "8080" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.echo.metadata[0].name - name = "echo" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/excalidraw/main.tf b/stacks/excalidraw/main.tf index cfbf6fb8..f13d8039 100644 --- a/stacks/excalidraw/main.tf +++ b/stacks/excalidraw/main.tf @@ -10,8 +10,107 @@ locals { } } -module "excalidraw" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "excalidraw" { + metadata { + name = "excalidraw" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.excalidraw.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "excalidraw" { + metadata { + name = "excalidraw" + namespace = kubernetes_namespace.excalidraw.metadata[0].name + labels = { + app = "excalidraw" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "excalidraw" + } + } + template { + metadata { + labels = { + app = "excalidraw" + } + annotations = { + "diun.enable" = "true" + "diun.include_tags" = "^latest$" + } + } + spec { + container { + image = "viktorbarzin/excalidraw-library:v4" + image_pull_policy = "IfNotPresent" + name = "excalidraw" + port { + container_port = 8080 + } + env { + name = "DATA_DIR" + value = "/data" + } + env { + name = "PORT" + value = "8080" + } + volume_mount { + name = "data" + mount_path = "/data" + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/excalidraw" + } + } + } + } + } +} + +resource "kubernetes_service" "draw" { + metadata { + name = "draw" + namespace = kubernetes_namespace.excalidraw.metadata[0].name + labels = { + app = "excalidraw" + } + } + + spec { + selector = { + app = "excalidraw" + } + port { + name = "http" + port = 80 + target_port = 8080 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.excalidraw.metadata[0].name + name = "draw" + tls_secret_name = var.tls_secret_name + protected = true } diff --git a/stacks/excalidraw/module/main.tf b/stacks/excalidraw/module/main.tf deleted file mode 100644 index 4c410692..00000000 --- a/stacks/excalidraw/module/main.tf +++ /dev/null @@ -1,107 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "excalidraw" { - metadata { - name = "excalidraw" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.excalidraw.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "excalidraw" { - metadata { - name = "excalidraw" - namespace = kubernetes_namespace.excalidraw.metadata[0].name - labels = { - app = "excalidraw" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "excalidraw" - } - } - template { - metadata { - labels = { - app = "excalidraw" - } - annotations = { - "diun.enable" = "true" - "diun.include_tags" = "^latest$" - } - } - spec { - container { - image = "viktorbarzin/excalidraw-library:v4" - image_pull_policy = "IfNotPresent" - name = "excalidraw" - port { - container_port = 8080 - } - env { - name = "DATA_DIR" - value = "/data" - } - env { - name = "PORT" - value = "8080" - } - volume_mount { - name = "data" - mount_path = "/data" - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/excalidraw" - } - } - } - } - } -} - -resource "kubernetes_service" "draw" { - metadata { - name = "draw" - namespace = kubernetes_namespace.excalidraw.metadata[0].name - labels = { - app = "excalidraw" - } - } - - spec { - selector = { - app = "excalidraw" - } - port { - name = "http" - port = 80 - target_port = 8080 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.excalidraw.metadata[0].name - name = "draw" - tls_secret_name = var.tls_secret_name - protected = true -} diff --git a/stacks/excalidraw/module/project/.gitignore b/stacks/excalidraw/project/.gitignore similarity index 100% rename from stacks/excalidraw/module/project/.gitignore rename to stacks/excalidraw/project/.gitignore diff --git a/stacks/excalidraw/module/project/Dockerfile b/stacks/excalidraw/project/Dockerfile similarity index 100% rename from stacks/excalidraw/module/project/Dockerfile rename to stacks/excalidraw/project/Dockerfile diff --git a/stacks/excalidraw/module/project/README.md b/stacks/excalidraw/project/README.md similarity index 100% rename from stacks/excalidraw/module/project/README.md rename to stacks/excalidraw/project/README.md diff --git a/stacks/excalidraw/module/project/go.mod b/stacks/excalidraw/project/go.mod similarity index 100% rename from stacks/excalidraw/module/project/go.mod rename to stacks/excalidraw/project/go.mod diff --git a/stacks/excalidraw/module/project/main.go b/stacks/excalidraw/project/main.go similarity index 100% rename from stacks/excalidraw/module/project/main.go rename to stacks/excalidraw/project/main.go diff --git a/stacks/excalidraw/module/project/static/editor.html b/stacks/excalidraw/project/static/editor.html similarity index 100% rename from stacks/excalidraw/module/project/static/editor.html rename to stacks/excalidraw/project/static/editor.html diff --git a/stacks/f1-stream/module/files/.claude/internet-mode-used_DO_NOT_REMOVE_MANUALLY_SECURITY_RISK b/stacks/f1-stream/files/.claude/internet-mode-used_DO_NOT_REMOVE_MANUALLY_SECURITY_RISK similarity index 100% rename from stacks/f1-stream/module/files/.claude/internet-mode-used_DO_NOT_REMOVE_MANUALLY_SECURITY_RISK rename to stacks/f1-stream/files/.claude/internet-mode-used_DO_NOT_REMOVE_MANUALLY_SECURITY_RISK diff --git a/stacks/f1-stream/module/files/.dockerignore b/stacks/f1-stream/files/.dockerignore similarity index 100% rename from stacks/f1-stream/module/files/.dockerignore rename to stacks/f1-stream/files/.dockerignore diff --git a/stacks/f1-stream/module/files/.planning/PROJECT.md b/stacks/f1-stream/files/.planning/PROJECT.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/PROJECT.md rename to stacks/f1-stream/files/.planning/PROJECT.md diff --git a/stacks/f1-stream/module/files/.planning/REQUIREMENTS.md b/stacks/f1-stream/files/.planning/REQUIREMENTS.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/REQUIREMENTS.md rename to stacks/f1-stream/files/.planning/REQUIREMENTS.md diff --git a/stacks/f1-stream/module/files/.planning/ROADMAP.md b/stacks/f1-stream/files/.planning/ROADMAP.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/ROADMAP.md rename to stacks/f1-stream/files/.planning/ROADMAP.md diff --git a/stacks/f1-stream/module/files/.planning/STATE.md b/stacks/f1-stream/files/.planning/STATE.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/STATE.md rename to stacks/f1-stream/files/.planning/STATE.md diff --git a/stacks/f1-stream/module/files/.planning/codebase/ARCHITECTURE.md b/stacks/f1-stream/files/.planning/codebase/ARCHITECTURE.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/codebase/ARCHITECTURE.md rename to stacks/f1-stream/files/.planning/codebase/ARCHITECTURE.md diff --git a/stacks/f1-stream/module/files/.planning/codebase/CONCERNS.md b/stacks/f1-stream/files/.planning/codebase/CONCERNS.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/codebase/CONCERNS.md rename to stacks/f1-stream/files/.planning/codebase/CONCERNS.md diff --git a/stacks/f1-stream/module/files/.planning/codebase/CONVENTIONS.md b/stacks/f1-stream/files/.planning/codebase/CONVENTIONS.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/codebase/CONVENTIONS.md rename to stacks/f1-stream/files/.planning/codebase/CONVENTIONS.md diff --git a/stacks/f1-stream/module/files/.planning/codebase/INTEGRATIONS.md b/stacks/f1-stream/files/.planning/codebase/INTEGRATIONS.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/codebase/INTEGRATIONS.md rename to stacks/f1-stream/files/.planning/codebase/INTEGRATIONS.md diff --git a/stacks/f1-stream/module/files/.planning/codebase/STACK.md b/stacks/f1-stream/files/.planning/codebase/STACK.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/codebase/STACK.md rename to stacks/f1-stream/files/.planning/codebase/STACK.md diff --git a/stacks/f1-stream/module/files/.planning/codebase/STRUCTURE.md b/stacks/f1-stream/files/.planning/codebase/STRUCTURE.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/codebase/STRUCTURE.md rename to stacks/f1-stream/files/.planning/codebase/STRUCTURE.md diff --git a/stacks/f1-stream/module/files/.planning/codebase/TESTING.md b/stacks/f1-stream/files/.planning/codebase/TESTING.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/codebase/TESTING.md rename to stacks/f1-stream/files/.planning/codebase/TESTING.md diff --git a/stacks/f1-stream/module/files/.planning/config.json b/stacks/f1-stream/files/.planning/config.json similarity index 100% rename from stacks/f1-stream/module/files/.planning/config.json rename to stacks/f1-stream/files/.planning/config.json diff --git a/stacks/f1-stream/module/files/.planning/phases/01-scraper-validation/01-01-PLAN.md b/stacks/f1-stream/files/.planning/phases/01-scraper-validation/01-01-PLAN.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/01-scraper-validation/01-01-PLAN.md rename to stacks/f1-stream/files/.planning/phases/01-scraper-validation/01-01-PLAN.md diff --git a/stacks/f1-stream/module/files/.planning/phases/01-scraper-validation/01-01-SUMMARY.md b/stacks/f1-stream/files/.planning/phases/01-scraper-validation/01-01-SUMMARY.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/01-scraper-validation/01-01-SUMMARY.md rename to stacks/f1-stream/files/.planning/phases/01-scraper-validation/01-01-SUMMARY.md diff --git a/stacks/f1-stream/module/files/.planning/phases/01-scraper-validation/01-RESEARCH.md b/stacks/f1-stream/files/.planning/phases/01-scraper-validation/01-RESEARCH.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/01-scraper-validation/01-RESEARCH.md rename to stacks/f1-stream/files/.planning/phases/01-scraper-validation/01-RESEARCH.md diff --git a/stacks/f1-stream/module/files/.planning/phases/01-scraper-validation/01-VERIFICATION.md b/stacks/f1-stream/files/.planning/phases/01-scraper-validation/01-VERIFICATION.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/01-scraper-validation/01-VERIFICATION.md rename to stacks/f1-stream/files/.planning/phases/01-scraper-validation/01-VERIFICATION.md diff --git a/stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-01-PLAN.md b/stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-01-PLAN.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-01-PLAN.md rename to stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-01-PLAN.md diff --git a/stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-01-SUMMARY.md b/stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-01-SUMMARY.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-01-SUMMARY.md rename to stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-01-SUMMARY.md diff --git a/stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-02-PLAN.md b/stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-02-PLAN.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-02-PLAN.md rename to stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-02-PLAN.md diff --git a/stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-02-SUMMARY.md b/stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-02-SUMMARY.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-02-SUMMARY.md rename to stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-02-SUMMARY.md diff --git a/stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-RESEARCH.md b/stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-RESEARCH.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-RESEARCH.md rename to stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-RESEARCH.md diff --git a/stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-VERIFICATION.md b/stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-VERIFICATION.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/02-health-check-infrastructure/02-VERIFICATION.md rename to stacks/f1-stream/files/.planning/phases/02-health-check-infrastructure/02-VERIFICATION.md diff --git a/stacks/f1-stream/module/files/.planning/phases/03-auto-publish-pipeline/03-01-PLAN.md b/stacks/f1-stream/files/.planning/phases/03-auto-publish-pipeline/03-01-PLAN.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/03-auto-publish-pipeline/03-01-PLAN.md rename to stacks/f1-stream/files/.planning/phases/03-auto-publish-pipeline/03-01-PLAN.md diff --git a/stacks/f1-stream/module/files/.planning/phases/03-auto-publish-pipeline/03-01-SUMMARY.md b/stacks/f1-stream/files/.planning/phases/03-auto-publish-pipeline/03-01-SUMMARY.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/03-auto-publish-pipeline/03-01-SUMMARY.md rename to stacks/f1-stream/files/.planning/phases/03-auto-publish-pipeline/03-01-SUMMARY.md diff --git a/stacks/f1-stream/module/files/.planning/phases/03-auto-publish-pipeline/03-VERIFICATION.md b/stacks/f1-stream/files/.planning/phases/03-auto-publish-pipeline/03-VERIFICATION.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/03-auto-publish-pipeline/03-VERIFICATION.md rename to stacks/f1-stream/files/.planning/phases/03-auto-publish-pipeline/03-VERIFICATION.md diff --git a/stacks/f1-stream/module/files/.planning/phases/04-video-extraction-native-playback/04-01-PLAN.md b/stacks/f1-stream/files/.planning/phases/04-video-extraction-native-playback/04-01-PLAN.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/04-video-extraction-native-playback/04-01-PLAN.md rename to stacks/f1-stream/files/.planning/phases/04-video-extraction-native-playback/04-01-PLAN.md diff --git a/stacks/f1-stream/module/files/.planning/phases/04-video-extraction-native-playback/04-01-SUMMARY.md b/stacks/f1-stream/files/.planning/phases/04-video-extraction-native-playback/04-01-SUMMARY.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/04-video-extraction-native-playback/04-01-SUMMARY.md rename to stacks/f1-stream/files/.planning/phases/04-video-extraction-native-playback/04-01-SUMMARY.md diff --git a/stacks/f1-stream/module/files/.planning/phases/04-video-extraction-native-playback/04-02-PLAN.md b/stacks/f1-stream/files/.planning/phases/04-video-extraction-native-playback/04-02-PLAN.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/04-video-extraction-native-playback/04-02-PLAN.md rename to stacks/f1-stream/files/.planning/phases/04-video-extraction-native-playback/04-02-PLAN.md diff --git a/stacks/f1-stream/module/files/.planning/phases/04-video-extraction-native-playback/04-02-SUMMARY.md b/stacks/f1-stream/files/.planning/phases/04-video-extraction-native-playback/04-02-SUMMARY.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/04-video-extraction-native-playback/04-02-SUMMARY.md rename to stacks/f1-stream/files/.planning/phases/04-video-extraction-native-playback/04-02-SUMMARY.md diff --git a/stacks/f1-stream/module/files/.planning/phases/04-video-extraction-native-playback/04-VERIFICATION.md b/stacks/f1-stream/files/.planning/phases/04-video-extraction-native-playback/04-VERIFICATION.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/04-video-extraction-native-playback/04-VERIFICATION.md rename to stacks/f1-stream/files/.planning/phases/04-video-extraction-native-playback/04-VERIFICATION.md diff --git a/stacks/f1-stream/module/files/.planning/phases/05-sandbox-proxy-hardening/05-01-PLAN.md b/stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-01-PLAN.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/05-sandbox-proxy-hardening/05-01-PLAN.md rename to stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-01-PLAN.md diff --git a/stacks/f1-stream/module/files/.planning/phases/05-sandbox-proxy-hardening/05-01-SUMMARY.md b/stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-01-SUMMARY.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/05-sandbox-proxy-hardening/05-01-SUMMARY.md rename to stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-01-SUMMARY.md diff --git a/stacks/f1-stream/module/files/.planning/phases/05-sandbox-proxy-hardening/05-02-PLAN.md b/stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-02-PLAN.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/05-sandbox-proxy-hardening/05-02-PLAN.md rename to stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-02-PLAN.md diff --git a/stacks/f1-stream/module/files/.planning/phases/05-sandbox-proxy-hardening/05-02-SUMMARY.md b/stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-02-SUMMARY.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/05-sandbox-proxy-hardening/05-02-SUMMARY.md rename to stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-02-SUMMARY.md diff --git a/stacks/f1-stream/module/files/.planning/phases/05-sandbox-proxy-hardening/05-VERIFICATION.md b/stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-VERIFICATION.md similarity index 100% rename from stacks/f1-stream/module/files/.planning/phases/05-sandbox-proxy-hardening/05-VERIFICATION.md rename to stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-VERIFICATION.md diff --git a/stacks/f1-stream/module/files/Dockerfile b/stacks/f1-stream/files/Dockerfile similarity index 100% rename from stacks/f1-stream/module/files/Dockerfile rename to stacks/f1-stream/files/Dockerfile diff --git a/stacks/f1-stream/module/files/go.mod b/stacks/f1-stream/files/go.mod similarity index 100% rename from stacks/f1-stream/module/files/go.mod rename to stacks/f1-stream/files/go.mod diff --git a/stacks/f1-stream/module/files/go.sum b/stacks/f1-stream/files/go.sum similarity index 100% rename from stacks/f1-stream/module/files/go.sum rename to stacks/f1-stream/files/go.sum diff --git a/stacks/f1-stream/module/files/index.html b/stacks/f1-stream/files/index.html similarity index 100% rename from stacks/f1-stream/module/files/index.html rename to stacks/f1-stream/files/index.html diff --git a/stacks/f1-stream/module/files/internal/auth/auth.go b/stacks/f1-stream/files/internal/auth/auth.go similarity index 100% rename from stacks/f1-stream/module/files/internal/auth/auth.go rename to stacks/f1-stream/files/internal/auth/auth.go diff --git a/stacks/f1-stream/module/files/internal/auth/context.go b/stacks/f1-stream/files/internal/auth/context.go similarity index 100% rename from stacks/f1-stream/module/files/internal/auth/context.go rename to stacks/f1-stream/files/internal/auth/context.go diff --git a/stacks/f1-stream/module/files/internal/extractor/browser.go b/stacks/f1-stream/files/internal/extractor/browser.go similarity index 100% rename from stacks/f1-stream/module/files/internal/extractor/browser.go rename to stacks/f1-stream/files/internal/extractor/browser.go diff --git a/stacks/f1-stream/module/files/internal/extractor/capture.go b/stacks/f1-stream/files/internal/extractor/capture.go similarity index 100% rename from stacks/f1-stream/module/files/internal/extractor/capture.go rename to stacks/f1-stream/files/internal/extractor/capture.go diff --git a/stacks/f1-stream/module/files/internal/extractor/session.go b/stacks/f1-stream/files/internal/extractor/session.go similarity index 100% rename from stacks/f1-stream/module/files/internal/extractor/session.go rename to stacks/f1-stream/files/internal/extractor/session.go diff --git a/stacks/f1-stream/module/files/internal/extractor/webrtc.go b/stacks/f1-stream/files/internal/extractor/webrtc.go similarity index 100% rename from stacks/f1-stream/module/files/internal/extractor/webrtc.go rename to stacks/f1-stream/files/internal/extractor/webrtc.go diff --git a/stacks/f1-stream/module/files/internal/healthcheck/healthcheck.go b/stacks/f1-stream/files/internal/healthcheck/healthcheck.go similarity index 100% rename from stacks/f1-stream/module/files/internal/healthcheck/healthcheck.go rename to stacks/f1-stream/files/internal/healthcheck/healthcheck.go diff --git a/stacks/f1-stream/module/files/internal/hlsproxy/hlsproxy.go b/stacks/f1-stream/files/internal/hlsproxy/hlsproxy.go similarity index 100% rename from stacks/f1-stream/module/files/internal/hlsproxy/hlsproxy.go rename to stacks/f1-stream/files/internal/hlsproxy/hlsproxy.go diff --git a/stacks/f1-stream/module/files/internal/models/models.go b/stacks/f1-stream/files/internal/models/models.go similarity index 100% rename from stacks/f1-stream/module/files/internal/models/models.go rename to stacks/f1-stream/files/internal/models/models.go diff --git a/stacks/f1-stream/module/files/internal/playerconfig/playerconfig.go b/stacks/f1-stream/files/internal/playerconfig/playerconfig.go similarity index 100% rename from stacks/f1-stream/module/files/internal/playerconfig/playerconfig.go rename to stacks/f1-stream/files/internal/playerconfig/playerconfig.go diff --git a/stacks/f1-stream/module/files/internal/proxy/proxy.go b/stacks/f1-stream/files/internal/proxy/proxy.go similarity index 100% rename from stacks/f1-stream/module/files/internal/proxy/proxy.go rename to stacks/f1-stream/files/internal/proxy/proxy.go diff --git a/stacks/f1-stream/module/files/internal/scraper/reddit.go b/stacks/f1-stream/files/internal/scraper/reddit.go similarity index 100% rename from stacks/f1-stream/module/files/internal/scraper/reddit.go rename to stacks/f1-stream/files/internal/scraper/reddit.go diff --git a/stacks/f1-stream/module/files/internal/scraper/scraper.go b/stacks/f1-stream/files/internal/scraper/scraper.go similarity index 100% rename from stacks/f1-stream/module/files/internal/scraper/scraper.go rename to stacks/f1-stream/files/internal/scraper/scraper.go diff --git a/stacks/f1-stream/module/files/internal/scraper/validate.go b/stacks/f1-stream/files/internal/scraper/validate.go similarity index 100% rename from stacks/f1-stream/module/files/internal/scraper/validate.go rename to stacks/f1-stream/files/internal/scraper/validate.go diff --git a/stacks/f1-stream/module/files/internal/scraper/validate_test.go b/stacks/f1-stream/files/internal/scraper/validate_test.go similarity index 100% rename from stacks/f1-stream/module/files/internal/scraper/validate_test.go rename to stacks/f1-stream/files/internal/scraper/validate_test.go diff --git a/stacks/f1-stream/module/files/internal/server/middleware.go b/stacks/f1-stream/files/internal/server/middleware.go similarity index 100% rename from stacks/f1-stream/module/files/internal/server/middleware.go rename to stacks/f1-stream/files/internal/server/middleware.go diff --git a/stacks/f1-stream/module/files/internal/server/server.go b/stacks/f1-stream/files/internal/server/server.go similarity index 100% rename from stacks/f1-stream/module/files/internal/server/server.go rename to stacks/f1-stream/files/internal/server/server.go diff --git a/stacks/f1-stream/module/files/internal/store/health.go b/stacks/f1-stream/files/internal/store/health.go similarity index 100% rename from stacks/f1-stream/module/files/internal/store/health.go rename to stacks/f1-stream/files/internal/store/health.go diff --git a/stacks/f1-stream/module/files/internal/store/scraped.go b/stacks/f1-stream/files/internal/store/scraped.go similarity index 100% rename from stacks/f1-stream/module/files/internal/store/scraped.go rename to stacks/f1-stream/files/internal/store/scraped.go diff --git a/stacks/f1-stream/module/files/internal/store/sessions.go b/stacks/f1-stream/files/internal/store/sessions.go similarity index 100% rename from stacks/f1-stream/module/files/internal/store/sessions.go rename to stacks/f1-stream/files/internal/store/sessions.go diff --git a/stacks/f1-stream/module/files/internal/store/store.go b/stacks/f1-stream/files/internal/store/store.go similarity index 100% rename from stacks/f1-stream/module/files/internal/store/store.go rename to stacks/f1-stream/files/internal/store/store.go diff --git a/stacks/f1-stream/module/files/internal/store/streams.go b/stacks/f1-stream/files/internal/store/streams.go similarity index 100% rename from stacks/f1-stream/module/files/internal/store/streams.go rename to stacks/f1-stream/files/internal/store/streams.go diff --git a/stacks/f1-stream/module/files/internal/store/users.go b/stacks/f1-stream/files/internal/store/users.go similarity index 100% rename from stacks/f1-stream/module/files/internal/store/users.go rename to stacks/f1-stream/files/internal/store/users.go diff --git a/stacks/f1-stream/module/files/main.go b/stacks/f1-stream/files/main.go similarity index 100% rename from stacks/f1-stream/module/files/main.go rename to stacks/f1-stream/files/main.go diff --git a/stacks/f1-stream/module/files/node_modules/.package-lock.json b/stacks/f1-stream/files/node_modules/.package-lock.json similarity index 100% rename from stacks/f1-stream/module/files/node_modules/.package-lock.json rename to stacks/f1-stream/files/node_modules/.package-lock.json diff --git a/stacks/f1-stream/module/files/package-lock.json b/stacks/f1-stream/files/package-lock.json similarity index 100% rename from stacks/f1-stream/module/files/package-lock.json rename to stacks/f1-stream/files/package-lock.json diff --git a/stacks/f1-stream/module/files/package.json b/stacks/f1-stream/files/package.json similarity index 100% rename from stacks/f1-stream/module/files/package.json rename to stacks/f1-stream/files/package.json diff --git a/stacks/f1-stream/module/files/redeploy.sh b/stacks/f1-stream/files/redeploy.sh similarity index 100% rename from stacks/f1-stream/module/files/redeploy.sh rename to stacks/f1-stream/files/redeploy.sh diff --git a/stacks/f1-stream/module/files/static/css/custom.css b/stacks/f1-stream/files/static/css/custom.css similarity index 100% rename from stacks/f1-stream/module/files/static/css/custom.css rename to stacks/f1-stream/files/static/css/custom.css diff --git a/stacks/f1-stream/module/files/static/css/pico.min.css b/stacks/f1-stream/files/static/css/pico.min.css similarity index 100% rename from stacks/f1-stream/module/files/static/css/pico.min.css rename to stacks/f1-stream/files/static/css/pico.min.css diff --git a/stacks/f1-stream/module/files/static/index.html b/stacks/f1-stream/files/static/index.html similarity index 100% rename from stacks/f1-stream/module/files/static/index.html rename to stacks/f1-stream/files/static/index.html diff --git a/stacks/f1-stream/module/files/static/js/app.js b/stacks/f1-stream/files/static/js/app.js similarity index 100% rename from stacks/f1-stream/module/files/static/js/app.js rename to stacks/f1-stream/files/static/js/app.js diff --git a/stacks/f1-stream/module/files/static/js/auth.js b/stacks/f1-stream/files/static/js/auth.js similarity index 100% rename from stacks/f1-stream/module/files/static/js/auth.js rename to stacks/f1-stream/files/static/js/auth.js diff --git a/stacks/f1-stream/module/files/static/js/player.js b/stacks/f1-stream/files/static/js/player.js similarity index 100% rename from stacks/f1-stream/module/files/static/js/player.js rename to stacks/f1-stream/files/static/js/player.js diff --git a/stacks/f1-stream/module/files/static/js/streams.js b/stacks/f1-stream/files/static/js/streams.js similarity index 100% rename from stacks/f1-stream/module/files/static/js/streams.js rename to stacks/f1-stream/files/static/js/streams.js diff --git a/stacks/f1-stream/module/files/static/js/utils.js b/stacks/f1-stream/files/static/js/utils.js similarity index 100% rename from stacks/f1-stream/module/files/static/js/utils.js rename to stacks/f1-stream/files/static/js/utils.js diff --git a/stacks/f1-stream/main.tf b/stacks/f1-stream/main.tf index faae5095..27650931 100644 --- a/stacks/f1-stream/main.tf +++ b/stacks/f1-stream/main.tf @@ -12,10 +12,133 @@ locals { } } -module "f1-stream" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux - turn_secret = var.coturn_turn_secret - public_ip = var.public_ip +resource "kubernetes_namespace" "f1-stream" { + metadata { + name = "f1-stream" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +resource "kubernetes_deployment" "f1-stream" { + metadata { + name = "f1-stream" + namespace = kubernetes_namespace.f1-stream.metadata[0].name + labels = { + app = "f1-stream" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "f1-stream" + } + } + template { + metadata { + labels = { + app = "f1-stream" + } + } + spec { + container { + image = "viktorbarzin/f1-stream:v1.3.1" + name = "f1-stream" + resources { + limits = { + cpu = "1" + memory = "512Mi" + } + requests = { + cpu = "50m" + memory = "128Mi" + } + } + port { + container_port = 8080 + } + env { + name = "WEBAUTHN_RPID" + value = "f1.viktorbarzin.me" + } + env { + name = "WEBAUTHN_ORIGIN" + value = "https://f1.viktorbarzin.me" + } + env { + name = "WEBAUTHN_DISPLAY_NAME" + value = "F1 Stream" + } + env { + name = "HEADLESS_EXTRACT_ENABLED" + value = "true" + } + env { + name = "TURN_URL" + value = "turn:${var.public_ip}:3478" + } + env { + name = "TURN_SHARED_SECRET" + value = var.coturn_turn_secret + } + env { + name = "TURN_INTERNAL_URL" + value = "turn:coturn.coturn.svc.cluster.local:3478" + } + volume_mount { + name = "data" + mount_path = "/data" + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/f1-stream" + } + } + } + } + } +} + + +resource "kubernetes_service" "f1-stream" { + metadata { + name = "f1" + namespace = kubernetes_namespace.f1-stream.metadata[0].name + labels = { + "app" = "f1-stream" + } + } + + spec { + selector = { + app = "f1-stream" + } + port { + port = "80" + target_port = "8080" + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.f1-stream.metadata[0].name + tls_secret_name = var.tls_secret_name +} + + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.f1-stream.metadata[0].name + name = "f1" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "7e69786f66d5" + exclude_crowdsec = true } diff --git a/stacks/f1-stream/module/main.tf b/stacks/f1-stream/module/main.tf deleted file mode 100644 index dbd8cdb7..00000000 --- a/stacks/f1-stream/module/main.tf +++ /dev/null @@ -1,135 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "turn_secret" { type = string } -variable "public_ip" { type = string } - -resource "kubernetes_namespace" "f1-stream" { - metadata { - name = "f1-stream" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -resource "kubernetes_deployment" "f1-stream" { - metadata { - name = "f1-stream" - namespace = kubernetes_namespace.f1-stream.metadata[0].name - labels = { - app = "f1-stream" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "f1-stream" - } - } - template { - metadata { - labels = { - app = "f1-stream" - } - } - spec { - container { - image = "viktorbarzin/f1-stream:v1.3.1" - name = "f1-stream" - resources { - limits = { - cpu = "1" - memory = "512Mi" - } - requests = { - cpu = "50m" - memory = "128Mi" - } - } - port { - container_port = 8080 - } - env { - name = "WEBAUTHN_RPID" - value = "f1.viktorbarzin.me" - } - env { - name = "WEBAUTHN_ORIGIN" - value = "https://f1.viktorbarzin.me" - } - env { - name = "WEBAUTHN_DISPLAY_NAME" - value = "F1 Stream" - } - env { - name = "HEADLESS_EXTRACT_ENABLED" - value = "true" - } - env { - name = "TURN_URL" - value = "turn:${var.public_ip}:3478" - } - env { - name = "TURN_SHARED_SECRET" - value = var.turn_secret - } - env { - name = "TURN_INTERNAL_URL" - value = "turn:coturn.coturn.svc.cluster.local:3478" - } - volume_mount { - name = "data" - mount_path = "/data" - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/f1-stream" - } - } - } - } - } -} - - -resource "kubernetes_service" "f1-stream" { - metadata { - name = "f1" - namespace = kubernetes_namespace.f1-stream.metadata[0].name - labels = { - "app" = "f1-stream" - } - } - - spec { - selector = { - app = "f1-stream" - } - port { - port = "80" - target_port = "8080" - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.f1-stream.metadata[0].name - tls_secret_name = var.tls_secret_name -} - - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.f1-stream.metadata[0].name - name = "f1" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "7e69786f66d5" - exclude_crowdsec = true -} diff --git a/stacks/forgejo/main.tf b/stacks/forgejo/main.tf index aa79d7cb..1fbc0cad 100644 --- a/stacks/forgejo/main.tf +++ b/stacks/forgejo/main.tf @@ -10,8 +10,103 @@ locals { } } -module "forgejo" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.edge +resource "kubernetes_namespace" "forgejo" { + metadata { + name = "forgejo" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.edge + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.forgejo.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "forgejo" { + metadata { + name = "forgejo" + namespace = kubernetes_namespace.forgejo.metadata[0].name + labels = { + app = "forgejo" + tier = local.tiers.edge + } + } + spec { + replicas = 1 + strategy { + type = "RollingUpdate" # DB is external so we can roll + } + selector { + match_labels = { + app = "forgejo" + } + } + template { + metadata { + labels = { + app = "forgejo" + } + } + spec { + container { + name = "forgejo" + image = "codeberg.org/forgejo/forgejo:11" + env { + name = "USER_UID" + value = 1000 + } + env { + name = "USER_GID" + value = 1000 + } + volume_mount { + name = "data" + mount_path = "/data" + } + port { + name = "http" + container_port = 3000 + protocol = "TCP" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/forgejo" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "forgejo" { + metadata { + name = "forgejo" + namespace = kubernetes_namespace.forgejo.metadata[0].name + labels = { + "app" = "forgejo" + } + } + + spec { + selector = { + app = "forgejo" + } + port { + port = 80 + target_port = 3000 + } + } +} +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.forgejo.metadata[0].name + name = "forgejo" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/forgejo/module/main.tf b/stacks/forgejo/module/main.tf deleted file mode 100644 index 5f80e0d6..00000000 --- a/stacks/forgejo/module/main.tf +++ /dev/null @@ -1,103 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "forgejo" { - metadata { - name = "forgejo" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.forgejo.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "forgejo" { - metadata { - name = "forgejo" - namespace = kubernetes_namespace.forgejo.metadata[0].name - labels = { - app = "forgejo" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "RollingUpdate" # DB is external so we can roll - } - selector { - match_labels = { - app = "forgejo" - } - } - template { - metadata { - labels = { - app = "forgejo" - } - } - spec { - container { - name = "forgejo" - image = "codeberg.org/forgejo/forgejo:11" - env { - name = "USER_UID" - value = 1000 - } - env { - name = "USER_GID" - value = 1000 - } - volume_mount { - name = "data" - mount_path = "/data" - } - port { - name = "http" - container_port = 3000 - protocol = "TCP" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/forgejo" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "forgejo" { - metadata { - name = "forgejo" - namespace = kubernetes_namespace.forgejo.metadata[0].name - labels = { - "app" = "forgejo" - } - } - - spec { - selector = { - app = "forgejo" - } - port { - port = 80 - target_port = 3000 - } - } -} -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.forgejo.metadata[0].name - name = "forgejo" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/freedify/module/factory/main.tf b/stacks/freedify/factory/main.tf similarity index 97% rename from stacks/freedify/module/factory/main.tf rename to stacks/freedify/factory/main.tf index b3ab3eda..15504c02 100755 --- a/stacks/freedify/module/factory/main.tf +++ b/stacks/freedify/factory/main.tf @@ -141,7 +141,7 @@ resource "kubernetes_service" "freedify" { } module "ingress" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = "freedify" name = "music-${var.name}" tls_secret_name = var.tls_secret_name diff --git a/stacks/freedify/main.tf b/stacks/freedify/main.tf index 23803df0..1ab6ffe6 100644 --- a/stacks/freedify/main.tf +++ b/stacks/freedify/main.tf @@ -11,9 +11,54 @@ locals { } } -module "freedify" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux - additional_credentials = var.freedify_credentials +# To create a new deployment: +/** + 1. Export a new nfs share with {name} in truenas at /mnt/main/freedify/{name} + 2. Add {name} as proxied cloudflare route (tfvars) + 3. Add module here +*/ + +resource "kubernetes_namespace" "freedify" { + metadata { + name = "freedify" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.freedify.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +# https://music-viktor.viktorbarzin.me/ +module "viktor" { + source = "./factory" + name = "viktor" + tag = "latest" + tls_secret_name = var.tls_secret_name + depends_on = [kubernetes_namespace.freedify] + tier = local.tiers.aux + protected = true + listenbrainz_token = lookup(var.freedify_credentials["viktor"], "listenbrainz_token", null) + genius_token = lookup(var.freedify_credentials["viktor"], "genius_token", null) + dab_session = lookup(var.freedify_credentials["viktor"], "dab_session", null) + dab_visitor_id = lookup(var.freedify_credentials["viktor"], "dab_visitor_id", null) + gemini_api_key = lookup(var.freedify_credentials["viktor"], "gemini_api_key", null) +} + +# https://music-emo.viktorbarzin.me/ +module "emo" { + source = "./factory" + name = "emo" + tag = "latest" + tls_secret_name = var.tls_secret_name + depends_on = [kubernetes_namespace.freedify] + tier = local.tiers.aux + protected = true + genius_token = lookup(var.freedify_credentials["emo"], "genius_token", null) + gemini_api_key = lookup(var.freedify_credentials["emo"], "gemini_api_key", null) } diff --git a/stacks/freedify/module/main.tf b/stacks/freedify/module/main.tf deleted file mode 100755 index 241647d3..00000000 --- a/stacks/freedify/module/main.tf +++ /dev/null @@ -1,55 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "additional_credentials" { type = map(any) } - -# To create a new deployment: -/** - 1. Export a new nfs share with {name} in truenas at /mnt/main/freedify/{name} - 2. Add {name} as proxied cloudflare route (tfvars) - 3. Add module here -*/ - -resource "kubernetes_namespace" "freedify" { - metadata { - name = "freedify" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.freedify.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -# https://music-viktor.viktorbarzin.me/ -module "viktor" { - source = "./factory" - name = "viktor" - tag = "latest" - tls_secret_name = var.tls_secret_name - depends_on = [kubernetes_namespace.freedify] - tier = var.tier - protected = true - listenbrainz_token = lookup(var.additional_credentials["viktor"], "listenbrainz_token", null) - genius_token = lookup(var.additional_credentials["viktor"], "genius_token", null) - dab_session = lookup(var.additional_credentials["viktor"], "dab_session", null) - dab_visitor_id = lookup(var.additional_credentials["viktor"], "dab_visitor_id", null) - gemini_api_key = lookup(var.additional_credentials["viktor"], "gemini_api_key", null) -} - -# https://music-emo.viktorbarzin.me/ -module "emo" { - source = "./factory" - name = "emo" - tag = "latest" - tls_secret_name = var.tls_secret_name - depends_on = [kubernetes_namespace.freedify] - tier = var.tier - protected = true - genius_token = lookup(var.additional_credentials["emo"], "genius_token", null) - gemini_api_key = lookup(var.additional_credentials["emo"], "gemini_api_key", null) -} diff --git a/stacks/freshrss/main.tf b/stacks/freshrss/main.tf index 581a07c2..8ba17aa6 100644 --- a/stacks/freshrss/main.tf +++ b/stacks/freshrss/main.tf @@ -10,8 +10,122 @@ locals { } } -module "freshrss" { - source = "./module" +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = "freshrss" + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_namespace" "immich" { + metadata { + name = "freshrss" + labels = { + tier = local.tiers.aux + } + } +} + + +resource "kubernetes_deployment" "freshrss" { + metadata { + name = "freshrss" + namespace = "freshrss" + labels = { + app = "freshrss" + "kubernetes.io/cluster-service" = "true" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "freshrss" + } + } + template { + metadata { + labels = { + app = "freshrss" + "kubernetes.io/cluster-service" = "true" + } + } + spec { + + container { + name = "freshrss" + image = "freshrss/freshrss" + env { + name = "CRON_MIN" + value = "0,30" + } + env { + name = "BASE_URL" + value = "https://rss.viktorbarzin.me" + } + env { + name = "PUBLISHED_PORT" + value = 80 + } + volume_mount { + name = "data" + mount_path = "/var/www/FreshRSS/data" + } + volume_mount { + name = "extensions" + mount_path = "/var/www/FreshRSS/extensions" + } + port { + name = "http" + container_port = 80 + protocol = "TCP" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/freshrss/data" + server = "10.0.10.15" + } + } + volume { + name = "extensions" + nfs { + path = "/mnt/main/freshrss/extensions" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "freshrss" { + metadata { + name = "freshrss" + namespace = "freshrss" + labels = { + "app" = "freshrss" + } + } + + spec { + selector = { + app = "freshrss" + } + port { + port = "80" + target_port = "80" + } + } +} +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = "freshrss" + name = "rss" + service_name = "freshrss" tls_secret_name = var.tls_secret_name - tier = local.tiers.aux } diff --git a/stacks/freshrss/module/main.tf b/stacks/freshrss/module/main.tf deleted file mode 100644 index 18754033..00000000 --- a/stacks/freshrss/module/main.tf +++ /dev/null @@ -1,122 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = "freshrss" - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_namespace" "immich" { - metadata { - name = "freshrss" - labels = { - tier = var.tier - } - } -} - - -resource "kubernetes_deployment" "freshrss" { - metadata { - name = "freshrss" - namespace = "freshrss" - labels = { - app = "freshrss" - "kubernetes.io/cluster-service" = "true" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "freshrss" - } - } - template { - metadata { - labels = { - app = "freshrss" - "kubernetes.io/cluster-service" = "true" - } - } - spec { - - container { - name = "freshrss" - image = "freshrss/freshrss" - env { - name = "CRON_MIN" - value = "0,30" - } - env { - name = "BASE_URL" - value = "https://rss.viktorbarzin.me" - } - env { - name = "PUBLISHED_PORT" - value = 80 - } - volume_mount { - name = "data" - mount_path = "/var/www/FreshRSS/data" - } - volume_mount { - name = "extensions" - mount_path = "/var/www/FreshRSS/extensions" - } - port { - name = "http" - container_port = 80 - protocol = "TCP" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/freshrss/data" - server = "10.0.10.15" - } - } - volume { - name = "extensions" - nfs { - path = "/mnt/main/freshrss/extensions" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "freshrss" { - metadata { - name = "freshrss" - namespace = "freshrss" - labels = { - "app" = "freshrss" - } - } - - spec { - selector = { - app = "freshrss" - } - port { - port = "80" - target_port = "80" - } - } -} -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = "freshrss" - name = "rss" - service_name = "freshrss" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/frigate/main.tf b/stacks/frigate/main.tf index 0b2aed34..264850d8 100644 --- a/stacks/frigate/main.tf +++ b/stacks/frigate/main.tf @@ -10,8 +10,215 @@ locals { } } -module "frigate" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.gpu +resource "kubernetes_namespace" "frigate" { + metadata { + name = "frigate" + labels = { + tier = local.tiers.gpu + } + # labels = { + # "istio-injection" : "enabled" + # } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.frigate.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "frigate" { + metadata { + name = "frigate" + namespace = kubernetes_namespace.frigate.metadata[0].name + labels = { + app = "frigate" + tier = local.tiers.gpu + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 # Temporarily disabled due to high power consumption + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "frigate" + } + } + template { + metadata { + labels = { + app = "frigate" + } + } + spec { + node_selector = { + "gpu" : true + } + toleration { + key = "nvidia.com/gpu" + operator = "Equal" + value = "true" + effect = "NoSchedule" + } + container { + # image = "ghcr.io/blakeblackshear/frigate:stable" + # image = "ghcr.io/blakeblackshear/frigate:stable-tensorrt" + image = "ghcr.io/blakeblackshear/frigate:0.17.0-beta1-tensorrt" + name = "frigate" + + resources { + limits = { + "nvidia.com/gpu" = "1" + } + } + env { + name = "FRIGATE_RTSP_PASSWORD" + value = "password" + } + port { + container_port = 5000 + } + port { + container_port = 8554 + } + port { + container_port = 8555 + protocol = "TCP" + } + port { + container_port = 8555 + protocol = "UDP" + } + volume_mount { + name = "config" + mount_path = "/config" + } + volume_mount { + name = "dri" + mount_path = "/dev/dri" + } + volume_mount { + name = "dshm" + mount_path = "/dev/shm" + } + volume_mount { + name = "media" + mount_path = "/media/frigate" + } + security_context { + privileged = true + } + } + + volume { + name = "config" + nfs { + path = "/mnt/main/frigate/config" + server = "10.0.10.15" + } + } + volume { + name = "dshm" + empty_dir { + medium = "Memory" + size_limit = "1Gi" + } + } + volume { + name = "media" + nfs { + path = "/mnt/main/frigate/media" + server = "10.0.10.15" + } + } + volume { + name = "dri" + host_path { + path = "/dev/dri" + type = "Directory" + } + } + } + } + } +} + +resource "kubernetes_service" "frigate" { + metadata { + name = "frigate" + namespace = kubernetes_namespace.frigate.metadata[0].name + labels = { + "app" = "frigate" + } + } + + spec { + selector = { + app = "frigate" + } + port { + name = "http" + target_port = 5000 + port = 80 + protocol = "TCP" + } + } +} + +resource "kubernetes_service" "frigate-rtsp" { + metadata { + name = "frigate-rtsp" + namespace = kubernetes_namespace.frigate.metadata[0].name + labels = { + "app" = "frigate" + } + } + + spec { + type = "NodePort" # Should always live on node1 where the gpu is + selector = { + app = "frigate" + } + port { + name = "rtsp-tcp" + target_port = 8554 + port = 8554 + protocol = "TCP" + node_port = 30554 + } + port { + name = "rtsp-udp" + target_port = 8554 + port = 8554 + protocol = "UDP" + node_port = 30554 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.frigate.metadata[0].name + name = "frigate" + tls_secret_name = var.tls_secret_name + protected = true + rybbit_site_id = "0d4044069ff5" +} + +module "ingress-internal" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.frigate.metadata[0].name + name = "frigate-lan" + host = "frigate-lan" + root_domain = "viktorbarzin.lan" + service_name = "frigate" + tls_secret_name = var.tls_secret_name + allow_local_access_only = true + ssl_redirect = false } diff --git a/stacks/frigate/module/main.tf b/stacks/frigate/module/main.tf deleted file mode 100644 index 94a70ce3..00000000 --- a/stacks/frigate/module/main.tf +++ /dev/null @@ -1,215 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "frigate" { - metadata { - name = "frigate" - labels = { - tier = var.tier - } - # labels = { - # "istio-injection" : "enabled" - # } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.frigate.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "frigate" { - metadata { - name = "frigate" - namespace = kubernetes_namespace.frigate.metadata[0].name - labels = { - app = "frigate" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 # Temporarily disabled due to high power consumption - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "frigate" - } - } - template { - metadata { - labels = { - app = "frigate" - } - } - spec { - node_selector = { - "gpu" : true - } - toleration { - key = "nvidia.com/gpu" - operator = "Equal" - value = "true" - effect = "NoSchedule" - } - container { - # image = "ghcr.io/blakeblackshear/frigate:stable" - # image = "ghcr.io/blakeblackshear/frigate:stable-tensorrt" - image = "ghcr.io/blakeblackshear/frigate:0.17.0-beta1-tensorrt" - name = "frigate" - - resources { - limits = { - "nvidia.com/gpu" = "1" - } - } - env { - name = "FRIGATE_RTSP_PASSWORD" - value = "password" - } - port { - container_port = 5000 - } - port { - container_port = 8554 - } - port { - container_port = 8555 - protocol = "TCP" - } - port { - container_port = 8555 - protocol = "UDP" - } - volume_mount { - name = "config" - mount_path = "/config" - } - volume_mount { - name = "dri" - mount_path = "/dev/dri" - } - volume_mount { - name = "dshm" - mount_path = "/dev/shm" - } - volume_mount { - name = "media" - mount_path = "/media/frigate" - } - security_context { - privileged = true - } - } - - volume { - name = "config" - nfs { - path = "/mnt/main/frigate/config" - server = "10.0.10.15" - } - } - volume { - name = "dshm" - empty_dir { - medium = "Memory" - size_limit = "1Gi" - } - } - volume { - name = "media" - nfs { - path = "/mnt/main/frigate/media" - server = "10.0.10.15" - } - } - volume { - name = "dri" - host_path { - path = "/dev/dri" - type = "Directory" - } - } - } - } - } -} - -resource "kubernetes_service" "frigate" { - metadata { - name = "frigate" - namespace = kubernetes_namespace.frigate.metadata[0].name - labels = { - "app" = "frigate" - } - } - - spec { - selector = { - app = "frigate" - } - port { - name = "http" - target_port = 5000 - port = 80 - protocol = "TCP" - } - } -} - -resource "kubernetes_service" "frigate-rtsp" { - metadata { - name = "frigate-rtsp" - namespace = kubernetes_namespace.frigate.metadata[0].name - labels = { - "app" = "frigate" - } - } - - spec { - type = "NodePort" # Should always live on node1 where the gpu is - selector = { - app = "frigate" - } - port { - name = "rtsp-tcp" - target_port = 8554 - port = 8554 - protocol = "TCP" - node_port = 30554 - } - port { - name = "rtsp-udp" - target_port = 8554 - port = 8554 - protocol = "UDP" - node_port = 30554 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.frigate.metadata[0].name - name = "frigate" - tls_secret_name = var.tls_secret_name - protected = true - rybbit_site_id = "0d4044069ff5" -} - -module "ingress-internal" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.frigate.metadata[0].name - name = "frigate-lan" - host = "frigate-lan" - root_domain = "viktorbarzin.lan" - service_name = "frigate" - tls_secret_name = var.tls_secret_name - allow_local_access_only = true - ssl_redirect = false -} diff --git a/stacks/grampsweb/main.tf b/stacks/grampsweb/main.tf index d68d3bec..f4e827fb 100644 --- a/stacks/grampsweb/main.tf +++ b/stacks/grampsweb/main.tf @@ -11,9 +11,269 @@ locals { } } -module "grampsweb" { - source = "./module" - tls_secret_name = var.tls_secret_name - smtp_password = var.mailserver_accounts["info@viktorbarzin.me"] - tier = local.tiers.aux +resource "kubernetes_namespace" "grampsweb" { + metadata { + name = "grampsweb" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.grampsweb.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "random_password" "secret_key" { + length = 64 + special = false +} + +locals { + common_env = [ + { + name = "GRAMPSWEB_TREE" + value = "Gramps Web" + }, + { + name = "GRAMPSWEB_SECRET_KEY" + value = random_password.secret_key.result + }, + { + name = "GRAMPSWEB_CELERY_CONFIG__broker_url" + value = "redis://redis.redis.svc.cluster.local:6379/2" + }, + { + name = "GRAMPSWEB_CELERY_CONFIG__result_backend" + value = "redis://redis.redis.svc.cluster.local:6379/2" + }, + { + name = "GRAMPSWEB_RATELIMIT_STORAGE_URI" + value = "redis://redis.redis.svc.cluster.local:6379/3" + }, + { + name = "GRAMPSWEB_BASE_URL" + value = "https://family.viktorbarzin.me" + }, + { + name = "GRAMPSWEB_REGISTRATION_DISABLED" + value = "True" + }, + { + name = "GRAMPSWEB_EMAIL_HOST" + value = "mail.viktorbarzin.me" + }, + { + name = "GRAMPSWEB_EMAIL_PORT" + value = "587" + }, + { + name = "GRAMPSWEB_EMAIL_HOST_USER" + value = "info@viktorbarzin.me" + }, + { + name = "GRAMPSWEB_EMAIL_HOST_PASSWORD" + value = var.mailserver_accounts["info@viktorbarzin.me"] + }, + { + name = "GRAMPSWEB_EMAIL_USE_SSL" + value = "False" + }, + { + name = "GRAMPSWEB_EMAIL_USE_STARTTLS" + value = "True" + }, + { + name = "GRAMPSWEB_DEFAULT_FROM_EMAIL" + value = "info@viktorbarzin.me" + }, + { + name = "GRAMPSWEB_LLM_BASE_URL" + value = "http://ollama.ollama.svc.cluster.local:11434/v1" + }, + { + name = "GRAMPSWEB_LLM_MODEL" + value = "llama3.1" + }, + ] +} + +resource "kubernetes_deployment" "grampsweb" { + metadata { + name = "grampsweb" + namespace = kubernetes_namespace.grampsweb.metadata[0].name + labels = { + app = "grampsweb" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "grampsweb" + } + } + template { + metadata { + labels = { + app = "grampsweb" + } + } + spec { + container { + name = "grampsweb" + image = "ghcr.io/gramps-project/grampsweb:latest" + + port { + container_port = 5000 + } + + dynamic "env" { + for_each = local.common_env + content { + name = env.value.name + value = env.value.value + } + } + + volume_mount { + name = "data" + mount_path = "/app/users" + sub_path = "users" + } + volume_mount { + name = "data" + mount_path = "/app/indexdir" + sub_path = "indexdir" + } + volume_mount { + name = "data" + mount_path = "/app/thumbnail_cache" + sub_path = "thumbnail_cache" + } + volume_mount { + name = "data" + mount_path = "/app/cache" + sub_path = "cache" + } + volume_mount { + name = "data" + mount_path = "/app/secret" + sub_path = "secret" + } + volume_mount { + name = "data" + mount_path = "/root/.gramps/grampsdb" + sub_path = "grampsdb" + } + volume_mount { + name = "data" + mount_path = "/app/media" + sub_path = "media" + } + volume_mount { + name = "data" + mount_path = "/tmp" + sub_path = "tmp" + } + } + + container { + name = "grampsweb-celery" + image = "ghcr.io/gramps-project/grampsweb:latest" + command = ["celery", "-A", "gramps_webapi.celery", "worker", "--loglevel=INFO", "--concurrency=2"] + + dynamic "env" { + for_each = local.common_env + content { + name = env.value.name + value = env.value.value + } + } + + volume_mount { + name = "data" + mount_path = "/app/users" + sub_path = "users" + } + volume_mount { + name = "data" + mount_path = "/app/indexdir" + sub_path = "indexdir" + } + volume_mount { + name = "data" + mount_path = "/app/thumbnail_cache" + sub_path = "thumbnail_cache" + } + volume_mount { + name = "data" + mount_path = "/app/cache" + sub_path = "cache" + } + volume_mount { + name = "data" + mount_path = "/app/secret" + sub_path = "secret" + } + volume_mount { + name = "data" + mount_path = "/root/.gramps/grampsdb" + sub_path = "grampsdb" + } + volume_mount { + name = "data" + mount_path = "/app/media" + sub_path = "media" + } + volume_mount { + name = "data" + mount_path = "/tmp" + sub_path = "tmp" + } + } + + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/grampsweb" + } + } + } + } + } +} + +resource "kubernetes_service" "grampsweb" { + metadata { + name = "grampsweb" + namespace = kubernetes_namespace.grampsweb.metadata[0].name + labels = { + app = "grampsweb" + } + } + + spec { + selector = { + app = "grampsweb" + } + port { + name = "http" + port = 80 + target_port = 5000 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.grampsweb.metadata[0].name + name = "family" + service_name = "grampsweb" + tls_secret_name = var.tls_secret_name + max_body_size = "500m" } diff --git a/stacks/grampsweb/module/main.tf b/stacks/grampsweb/module/main.tf deleted file mode 100644 index 547a2a5f..00000000 --- a/stacks/grampsweb/module/main.tf +++ /dev/null @@ -1,270 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "smtp_password" { type = string } - -resource "kubernetes_namespace" "grampsweb" { - metadata { - name = "grampsweb" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.grampsweb.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "random_password" "secret_key" { - length = 64 - special = false -} - -locals { - common_env = [ - { - name = "GRAMPSWEB_TREE" - value = "Gramps Web" - }, - { - name = "GRAMPSWEB_SECRET_KEY" - value = random_password.secret_key.result - }, - { - name = "GRAMPSWEB_CELERY_CONFIG__broker_url" - value = "redis://redis.redis.svc.cluster.local:6379/2" - }, - { - name = "GRAMPSWEB_CELERY_CONFIG__result_backend" - value = "redis://redis.redis.svc.cluster.local:6379/2" - }, - { - name = "GRAMPSWEB_RATELIMIT_STORAGE_URI" - value = "redis://redis.redis.svc.cluster.local:6379/3" - }, - { - name = "GRAMPSWEB_BASE_URL" - value = "https://family.viktorbarzin.me" - }, - { - name = "GRAMPSWEB_REGISTRATION_DISABLED" - value = "True" - }, - { - name = "GRAMPSWEB_EMAIL_HOST" - value = "mail.viktorbarzin.me" - }, - { - name = "GRAMPSWEB_EMAIL_PORT" - value = "587" - }, - { - name = "GRAMPSWEB_EMAIL_HOST_USER" - value = "info@viktorbarzin.me" - }, - { - name = "GRAMPSWEB_EMAIL_HOST_PASSWORD" - value = var.smtp_password - }, - { - name = "GRAMPSWEB_EMAIL_USE_SSL" - value = "False" - }, - { - name = "GRAMPSWEB_EMAIL_USE_STARTTLS" - value = "True" - }, - { - name = "GRAMPSWEB_DEFAULT_FROM_EMAIL" - value = "info@viktorbarzin.me" - }, - { - name = "GRAMPSWEB_LLM_BASE_URL" - value = "http://ollama.ollama.svc.cluster.local:11434/v1" - }, - { - name = "GRAMPSWEB_LLM_MODEL" - value = "llama3.1" - }, - ] -} - -resource "kubernetes_deployment" "grampsweb" { - metadata { - name = "grampsweb" - namespace = kubernetes_namespace.grampsweb.metadata[0].name - labels = { - app = "grampsweb" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "grampsweb" - } - } - template { - metadata { - labels = { - app = "grampsweb" - } - } - spec { - container { - name = "grampsweb" - image = "ghcr.io/gramps-project/grampsweb:latest" - - port { - container_port = 5000 - } - - dynamic "env" { - for_each = local.common_env - content { - name = env.value.name - value = env.value.value - } - } - - volume_mount { - name = "data" - mount_path = "/app/users" - sub_path = "users" - } - volume_mount { - name = "data" - mount_path = "/app/indexdir" - sub_path = "indexdir" - } - volume_mount { - name = "data" - mount_path = "/app/thumbnail_cache" - sub_path = "thumbnail_cache" - } - volume_mount { - name = "data" - mount_path = "/app/cache" - sub_path = "cache" - } - volume_mount { - name = "data" - mount_path = "/app/secret" - sub_path = "secret" - } - volume_mount { - name = "data" - mount_path = "/root/.gramps/grampsdb" - sub_path = "grampsdb" - } - volume_mount { - name = "data" - mount_path = "/app/media" - sub_path = "media" - } - volume_mount { - name = "data" - mount_path = "/tmp" - sub_path = "tmp" - } - } - - container { - name = "grampsweb-celery" - image = "ghcr.io/gramps-project/grampsweb:latest" - command = ["celery", "-A", "gramps_webapi.celery", "worker", "--loglevel=INFO", "--concurrency=2"] - - dynamic "env" { - for_each = local.common_env - content { - name = env.value.name - value = env.value.value - } - } - - volume_mount { - name = "data" - mount_path = "/app/users" - sub_path = "users" - } - volume_mount { - name = "data" - mount_path = "/app/indexdir" - sub_path = "indexdir" - } - volume_mount { - name = "data" - mount_path = "/app/thumbnail_cache" - sub_path = "thumbnail_cache" - } - volume_mount { - name = "data" - mount_path = "/app/cache" - sub_path = "cache" - } - volume_mount { - name = "data" - mount_path = "/app/secret" - sub_path = "secret" - } - volume_mount { - name = "data" - mount_path = "/root/.gramps/grampsdb" - sub_path = "grampsdb" - } - volume_mount { - name = "data" - mount_path = "/app/media" - sub_path = "media" - } - volume_mount { - name = "data" - mount_path = "/tmp" - sub_path = "tmp" - } - } - - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/grampsweb" - } - } - } - } - } -} - -resource "kubernetes_service" "grampsweb" { - metadata { - name = "grampsweb" - namespace = kubernetes_namespace.grampsweb.metadata[0].name - labels = { - app = "grampsweb" - } - } - - spec { - selector = { - app = "grampsweb" - } - port { - name = "http" - port = 80 - target_port = 5000 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.grampsweb.metadata[0].name - name = "family" - service_name = "grampsweb" - tls_secret_name = var.tls_secret_name - max_body_size = "500m" -} diff --git a/stacks/hackmd/main.tf b/stacks/hackmd/main.tf index 9c93242e..fb5f8d86 100644 --- a/stacks/hackmd/main.tf +++ b/stacks/hackmd/main.tf @@ -11,9 +11,153 @@ locals { } } -module "hackmd" { - source = "./module" - hackmd_db_password = var.hackmd_db_password - tls_secret_name = var.tls_secret_name - tier = local.tiers.edge +resource "kubernetes_namespace" "hackmd" { + metadata { + name = "hackmd" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.edge + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.hackmd.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "hackmd" { + metadata { + name = "hackmd" + namespace = kubernetes_namespace.hackmd.metadata[0].name + labels = { + app = "hackmd" + "kubernetes.io/cluster-service" = "true" + tier = local.tiers.edge + } + } + spec { + replicas = 1 + strategy { + type = "RollingUpdate" # DB is external so we can roll + } + selector { + match_labels = { + app = "hackmd" + } + } + template { + metadata { + labels = { + app = "hackmd" + "kubernetes.io/cluster-service" = "true" + } + } + spec { + # container { + # image = "postgres:11.6-alpine" + # name = "postgres" + # image_pull_policy = "IfNotPresent" + # env { + # name = "POSTGRES_USER" + # value = "codimd" + # } + # env { + # name = "POSTGRES_PASSWORD" + # value = var.hackmd_db_password + # } + # env { + # name = "POSTGRES_DB" + # value = "codimd" + # } + # resources { + # limits = { + # cpu = "1" + # memory = "1Gi" + # } + # requests = { + # cpu = "1" + # memory = "1Gi" + # } + # } + # port { + # container_port = 80 + # } + # volume_mount { + # name = "data" + # mount_path = "/var/lib/postgresql/data" + # sub_path = "postgres" + # } + # } + + container { + name = "codimd" + image = "hackmdio/hackmd" + env { + name = "CMD_DB_URL" + # value = format("%s%s%s", "postgres://codimd:", var.hackmd_db_password, "@localhost/codimd") + value = format("%s%s%s", "mysql://codimd:", var.hackmd_db_password, "@mysql.dbaas/codimd") + } + env { + name = "CMD_USECDN" + value = "false" + } + volume_mount { + name = "data" + mount_path = "/home/hackmd/app/public/uploads" + sub_path = "hackmd" + } + port { + name = "http" + container_port = 3000 + protocol = "TCP" + } + } + security_context { + fs_group = "1500" + } + volume { + name = "data" + nfs { + path = "/mnt/main/hackmd" + server = "10.0.10.15" + } + # iscsi { + # target_portal = "iscsi.viktorbarzin.lan:3260" + # fs_type = "ext4" + # iqn = "iqn.2020-12.lan.viktorbarzin:storage:hackmd" + # lun = 0 + # read_only = false + # } + } + } + } + } +} + +resource "kubernetes_service" "hackmd" { + metadata { + name = "hackmd" + namespace = kubernetes_namespace.hackmd.metadata[0].name + labels = { + "app" = "hackmd" + } + } + + spec { + selector = { + app = "hackmd" + } + port { + port = "80" + target_port = "3000" + } + } +} +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.hackmd.metadata[0].name + name = "hackmd" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/hackmd/module/main.tf b/stacks/hackmd/module/main.tf deleted file mode 100644 index 8053bec7..00000000 --- a/stacks/hackmd/module/main.tf +++ /dev/null @@ -1,154 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "hackmd_db_password" {} - -resource "kubernetes_namespace" "hackmd" { - metadata { - name = "hackmd" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.hackmd.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "hackmd" { - metadata { - name = "hackmd" - namespace = kubernetes_namespace.hackmd.metadata[0].name - labels = { - app = "hackmd" - "kubernetes.io/cluster-service" = "true" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "RollingUpdate" # DB is external so we can roll - } - selector { - match_labels = { - app = "hackmd" - } - } - template { - metadata { - labels = { - app = "hackmd" - "kubernetes.io/cluster-service" = "true" - } - } - spec { - # container { - # image = "postgres:11.6-alpine" - # name = "postgres" - # image_pull_policy = "IfNotPresent" - # env { - # name = "POSTGRES_USER" - # value = "codimd" - # } - # env { - # name = "POSTGRES_PASSWORD" - # value = var.hackmd_db_password - # } - # env { - # name = "POSTGRES_DB" - # value = "codimd" - # } - # resources { - # limits = { - # cpu = "1" - # memory = "1Gi" - # } - # requests = { - # cpu = "1" - # memory = "1Gi" - # } - # } - # port { - # container_port = 80 - # } - # volume_mount { - # name = "data" - # mount_path = "/var/lib/postgresql/data" - # sub_path = "postgres" - # } - # } - - container { - name = "codimd" - image = "hackmdio/hackmd" - env { - name = "CMD_DB_URL" - # value = format("%s%s%s", "postgres://codimd:", var.hackmd_db_password, "@localhost/codimd") - value = format("%s%s%s", "mysql://codimd:", var.hackmd_db_password, "@mysql.dbaas/codimd") - } - env { - name = "CMD_USECDN" - value = "false" - } - volume_mount { - name = "data" - mount_path = "/home/hackmd/app/public/uploads" - sub_path = "hackmd" - } - port { - name = "http" - container_port = 3000 - protocol = "TCP" - } - } - security_context { - fs_group = "1500" - } - volume { - name = "data" - nfs { - path = "/mnt/main/hackmd" - server = "10.0.10.15" - } - # iscsi { - # target_portal = "iscsi.viktorbarzin.lan:3260" - # fs_type = "ext4" - # iqn = "iqn.2020-12.lan.viktorbarzin:storage:hackmd" - # lun = 0 - # read_only = false - # } - } - } - } - } -} - -resource "kubernetes_service" "hackmd" { - metadata { - name = "hackmd" - namespace = kubernetes_namespace.hackmd.metadata[0].name - labels = { - "app" = "hackmd" - } - } - - spec { - selector = { - app = "hackmd" - } - port { - port = "80" - target_port = "3000" - } - } -} -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.hackmd.metadata[0].name - name = "hackmd" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/health/main.tf b/stacks/health/main.tf index f5ef4459..428a865e 100644 --- a/stacks/health/main.tf +++ b/stacks/health/main.tf @@ -12,10 +12,130 @@ locals { } } -module "health" { - source = "./module" - tls_secret_name = var.tls_secret_name - postgresql_password = var.health_postgresql_password - secret_key = var.health_secret_key - tier = local.tiers.aux +resource "kubernetes_namespace" "health" { + metadata { + name = "health" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.health.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "health" { + metadata { + name = "health" + namespace = kubernetes_namespace.health.metadata[0].name + labels = { + app = "health" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "health" + } + } + template { + metadata { + labels = { + app = "health" + } + } + spec { + container { + name = "health" + image = "viktorbarzin/health:latest" + + port { + container_port = 3000 + } + + env { + name = "DATABASE_URL" + value = "postgresql+asyncpg://health:${var.health_postgresql_password}@postgresql.dbaas.svc.cluster.local:5432/health" + } + env { + name = "SECRET_KEY" + value = var.health_secret_key + } + env { + name = "UPLOAD_DIR" + value = "/data/uploads" + } + env { + name = "WEBAUTHN_RP_ID" + value = "health.viktorbarzin.me" + } + env { + name = "WEBAUTHN_ORIGIN" + value = "https://health.viktorbarzin.me" + } + env { + name = "COOKIE_SECURE" + value = "true" + } + + volume_mount { + name = "uploads" + mount_path = "/data/uploads" + } + + resources { + requests = { + memory = "256Mi" + cpu = "100m" + } + limits = { + memory = "1Gi" + cpu = "1" + } + } + } + volume { + name = "uploads" + nfs { + server = "10.0.10.15" + path = "/mnt/main/health" + } + } + } + } + } +} + +resource "kubernetes_service" "health" { + metadata { + name = "health" + namespace = kubernetes_namespace.health.metadata[0].name + labels = { + app = "health" + } + } + + spec { + selector = { + app = "health" + } + port { + name = "http" + port = 80 + target_port = 3000 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.health.metadata[0].name + name = "health" + tls_secret_name = var.tls_secret_name + max_body_size = "100m" } diff --git a/stacks/health/module/main.tf b/stacks/health/module/main.tf deleted file mode 100644 index 50cd3c88..00000000 --- a/stacks/health/module/main.tf +++ /dev/null @@ -1,132 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "postgresql_password" {} -variable "secret_key" { type = string } - -resource "kubernetes_namespace" "health" { - metadata { - name = "health" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.health.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "health" { - metadata { - name = "health" - namespace = kubernetes_namespace.health.metadata[0].name - labels = { - app = "health" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "health" - } - } - template { - metadata { - labels = { - app = "health" - } - } - spec { - container { - name = "health" - image = "viktorbarzin/health:latest" - - port { - container_port = 3000 - } - - env { - name = "DATABASE_URL" - value = "postgresql+asyncpg://health:${var.postgresql_password}@postgresql.dbaas.svc.cluster.local:5432/health" - } - env { - name = "SECRET_KEY" - value = var.secret_key - } - env { - name = "UPLOAD_DIR" - value = "/data/uploads" - } - env { - name = "WEBAUTHN_RP_ID" - value = "health.viktorbarzin.me" - } - env { - name = "WEBAUTHN_ORIGIN" - value = "https://health.viktorbarzin.me" - } - env { - name = "COOKIE_SECURE" - value = "true" - } - - volume_mount { - name = "uploads" - mount_path = "/data/uploads" - } - - resources { - requests = { - memory = "256Mi" - cpu = "100m" - } - limits = { - memory = "1Gi" - cpu = "1" - } - } - } - volume { - name = "uploads" - nfs { - server = "10.0.10.15" - path = "/mnt/main/health" - } - } - } - } - } -} - -resource "kubernetes_service" "health" { - metadata { - name = "health" - namespace = kubernetes_namespace.health.metadata[0].name - labels = { - app = "health" - } - } - - spec { - selector = { - app = "health" - } - port { - name = "http" - port = 80 - target_port = 3000 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.health.metadata[0].name - name = "health" - tls_secret_name = var.tls_secret_name - max_body_size = "100m" -} diff --git a/stacks/homepage/main.tf b/stacks/homepage/main.tf index d7b0094d..107da13c 100644 --- a/stacks/homepage/main.tf +++ b/stacks/homepage/main.tf @@ -10,8 +10,30 @@ locals { } } -module "homepage" { - source = "./module" - tier = local.tiers.aux +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.homepage.metadata[0].name tls_secret_name = var.tls_secret_name } + +resource "kubernetes_namespace" "homepage" { + metadata { + name = "homepage" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +resource "helm_release" "homepage" { + namespace = kubernetes_namespace.homepage.metadata[0].name + create_namespace = false + name = "homepage" + atomic = true + + repository = "http://jameswynn.github.io/helm-charts" + chart = "homepage" + + values = [templatefile("${path.module}/values.yaml", { tls_secret_name = var.tls_secret_name })] +} diff --git a/stacks/homepage/module/main.tf b/stacks/homepage/module/main.tf deleted file mode 100644 index ecb6dbce..00000000 --- a/stacks/homepage/module/main.tf +++ /dev/null @@ -1,30 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.homepage.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_namespace" "homepage" { - metadata { - name = "homepage" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -resource "helm_release" "homepage" { - namespace = kubernetes_namespace.homepage.metadata[0].name - create_namespace = false - name = "homepage" - atomic = true - - repository = "http://jameswynn.github.io/helm-charts" - chart = "homepage" - - values = [templatefile("${path.module}/values.yaml", { tls_secret_name = var.tls_secret_name })] -} diff --git a/stacks/homepage/module/values.yaml b/stacks/homepage/values.yaml similarity index 100% rename from stacks/homepage/module/values.yaml rename to stacks/homepage/values.yaml diff --git a/stacks/immich/module/chart_values.tpl b/stacks/immich/chart_values.tpl similarity index 100% rename from stacks/immich/module/chart_values.tpl rename to stacks/immich/chart_values.tpl diff --git a/stacks/immich/module/frame.tf b/stacks/immich/frame.tf similarity index 92% rename from stacks/immich/module/frame.tf rename to stacks/immich/frame.tf index 70835c5d..4ab61bbc 100644 --- a/stacks/immich/module/frame.tf +++ b/stacks/immich/frame.tf @@ -1,6 +1,3 @@ -variable "frame_api_key" { - type = string -} resource "kubernetes_config_map" "mailserver_config" { metadata { @@ -26,7 +23,7 @@ resource "kubernetes_config_map" "mailserver_config" { ShowProgressBar: false Accounts: - ImmichServerUrl: http://immich.viktorbarzin.me - ApiKey: ${var.frame_api_key} + ApiKey: ${var.immich_frame_api_key} Albums: - 1aa98849-bbd5-452b-aac0-310b210a8597 # china EOF @@ -42,7 +39,7 @@ resource "kubernetes_deployment" "immich-frame" { "reloader.stakater.com/search" = "true" } labels = { - tier = var.tier + tier = local.tiers.gpu } } @@ -110,7 +107,7 @@ resource "kubernetes_service" "immich-frame" { } module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" + source = "../../modules/kubernetes/ingress_factory" namespace = "immich" name = "highlights-immich" tls_secret_name = var.tls_secret_name diff --git a/stacks/immich/main.tf b/stacks/immich/main.tf index 9b336074..4769ff40 100644 --- a/stacks/immich/main.tf +++ b/stacks/immich/main.tf @@ -13,11 +13,648 @@ locals { } } -module "immich" { - source = "./module" - 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.tiers.gpu +variable "immich_version" { + type = string + # Change me to upgrade + default = "v2.5.6" } + + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.immich.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_namespace" "immich" { + metadata { + name = "immich" + labels = { + tier = local.tiers.gpu + } + } +} + +resource "kubernetes_deployment" "immich_server" { + metadata { + name = "immich-server" + namespace = kubernetes_namespace.immich.metadata[0].name + + labels = { + app = "immich-server" + tier = local.tiers.gpu + } + } + + spec { + replicas = 1 + progress_deadline_seconds = 600 + + selector { + match_labels = { + app = "immich-server" + } + } + + strategy { + type = "RollingUpdate" + } + + template { + metadata { + labels = { + app = "immich-server" + } + annotations = { + "diun.enable" = "true" + "diun.include_tags" = "^\\d+\\.\\d+\\.\\d+$" + } + } + + spec { + container { + name = "immich-server" + image = "ghcr.io/immich-app/immich-server:${var.immich_version}" + + port { + name = "http" + container_port = 2283 + protocol = "TCP" + } + + env { + name = "DB_DATABASE_NAME" + value = "immich" + } + env { + name = "DB_HOSTNAME" + value = "immich-postgresql.immich.svc.cluster.local" + } + env { + name = "DB_USERNAME" + value = "immich" + } + env { + name = "DB_PASSWORD" + value = var.immich_postgresql_password + } + env { + name = "IMMICH_MACHINE_LEARNING_URL" + value = "http://immich-machine-learning:3003" + } + env { + name = "REDIS_HOSTNAME" + value = "redis.redis.svc.cluster.local" + } + + liveness_probe { + http_get { + path = "/api/server/ping" + port = "http" + } + initial_delay_seconds = 0 + period_seconds = 10 + timeout_seconds = 1 + failure_threshold = 3 + success_threshold = 1 + } + + readiness_probe { + http_get { + path = "/api/server/ping" + port = "http" + } + period_seconds = 10 + timeout_seconds = 1 + failure_threshold = 3 + success_threshold = 1 + } + + startup_probe { + http_get { + path = "/api/server/ping" + port = "http" + } + period_seconds = 10 + timeout_seconds = 1 + failure_threshold = 30 + success_threshold = 1 + } + + # volume_mount { + # name = "library-old" + # mount_path = "/usr/src/app/upload" + # } + + # Mount them 1 by 1 to enable thumbs in ssd + volume_mount { + name = "backups" + mount_path = "/usr/src/app/upload/backups" + } + volume_mount { + name = "encoded-video" + mount_path = "/usr/src/app/upload/encoded-video" + } + volume_mount { + name = "library" + mount_path = "/usr/src/app/upload/library" + } + volume_mount { + name = "profile" + mount_path = "/usr/src/app/upload/profile" + } + volume_mount { + name = "thumbs" + mount_path = "/usr/src/app/upload/thumbs" + } + volume_mount { + name = "upload" + mount_path = "/usr/src/app/upload/upload" + } + } + + # volume { + # name = "library-old" + # nfs { + # server = "10.0.10.15" + # path = "/mnt/main/immich/immich/" + # } + # } + + volume { + name = "backups" + nfs { + server = "10.0.10.15" + path = "/mnt/main/immich/immich/backups" + } + } + volume { + name = "encoded-video" + nfs { + server = "10.0.10.15" + path = "/mnt/main/immich/immich/encoded-video" + } + } + volume { + name = "library" + nfs { + server = "10.0.10.15" + path = "/mnt/main/immich/immich/library" + } + } + volume { + name = "profile" + nfs { + server = "10.0.10.15" + path = "/mnt/main/immich/immich/profile" + } + } + volume { + name = "thumbs" + nfs { + server = "10.0.10.15" + path = "/mnt/ssd/immich/thumbs" + } + } + volume { + name = "upload" + nfs { + server = "10.0.10.15" + path = "/mnt/main/immich/immich/upload" + } + } + } + } + } +} + +resource "kubernetes_service" "immich-server" { + metadata { + name = "immich-server" + namespace = kubernetes_namespace.immich.metadata[0].name + labels = { + "app" = "immich-server" + } + } + + spec { + selector = { + app = "immich-server" + } + port { + port = 2283 + } + } +} + +resource "kubernetes_deployment" "immich-postgres" { + metadata { + name = "immich-postgresql" + namespace = kubernetes_namespace.immich.metadata[0].name + labels = { + tier = local.tiers.gpu + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "immich-postgresql" + } + } + strategy { + type = "Recreate" + } + template { + metadata { + labels = { + app = "immich-postgresql" + } + } + spec { + container { + image = "ghcr.io/immich-app/postgres:15-vectorchord0.3.0-pgvectors0.2.0" + name = "immich-postgresql" + port { + container_port = 5432 + protocol = "TCP" + name = "postgresql" + } + env { + name = "POSTGRES_PASSWORD" + value = var.immich_postgresql_password + } + env { + name = "POSTGRES_USER" + value = "immich" + } + env { + name = "POSTGRES_DB" + value = "immich" + } + env { + name = "DB_STORAGE_TYPE" + value = "HDD" + } + volume_mount { + name = "postgresql-persistent-storage" + mount_path = "/var/lib/postgresql/data" + } + } + volume { + name = "postgresql-persistent-storage" + nfs { + path = "/mnt/main/immich/data-immich-postgresql" + server = "10.0.10.15" + } + } + } + } + } +} + + +resource "kubernetes_service" "immich-postgresql" { + metadata { + name = "immich-postgresql" + namespace = kubernetes_namespace.immich.metadata[0].name + labels = { + "app" = "immich-postgresql" + } + } + + spec { + selector = { + app = "immich-postgresql" + } + port { + port = 5432 + } + } +} + + +# If you're having issuewith typesens container exiting prematurely, increase liveliness check +# resource "helm_release" "immich" { +# namespace = kubernetes_namespace.immich.metadata[0].name +# name = "immich" + +# repository = "https://immich-app.github.io/immich-charts" +# chart = "immich" +# atomic = true +# version = "0.9.3" +# timeout = 6000 + +# values = [templatefile("${path.module}/chart_values.tpl", { postgresql_password = var.immich_postgresql_password, version = var.immich_version })] +# } + +# The helm one cannot be customized to use affinity settings to use the gpu node +resource "kubernetes_deployment" "immich-machine-learning" { + metadata { + name = "immich-machine-learning" + namespace = kubernetes_namespace.immich.metadata[0].name + labels = { + tier = local.tiers.gpu + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "immich-machine-learning" + } + } + strategy { + type = "RollingUpdate" + } + template { + metadata { + labels = { + app = "immich-machine-learning" + } + } + spec { + node_selector = { + "gpu" : "true" + } + toleration { + key = "nvidia.com/gpu" + operator = "Equal" + value = "true" + effect = "NoSchedule" + } + container { + # image = "ghcr.io/immich-app/immich-machine-learning:${var.immich_version}" + image = "ghcr.io/immich-app/immich-machine-learning:${var.immich_version}-cuda" + name = "immich-machine-learning" + port { + container_port = 3003 + protocol = "TCP" + name = "immich-ml" + } + env { + name = "MACHINE_LEARNING_MODEL_TTL" + value = "0" + } + env { + name = "TRANSFORMERS_CACHE" + value = "/cache" + } + env { + name = "HF_XET_CACHE" + value = "/cache/huggingface-xet" + } + env { + name = "MPLCONFIGDIR" + value = "/cache/matplotlib-config" + } + # Preload CLIP models (for smart search) + env { + name = "MACHINE_LEARNING_PRELOAD__CLIP__TEXTUAL" + value = "ViT-B-16-SigLIP2__webli" + } + env { + name = "MACHINE_LEARNING_PRELOAD__CLIP__VISUAL" + value = "ViT-B-16-SigLIP2__webli" + } + # Preload facial recognition models + env { + name = "MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION" + value = "buffalo_l" + } + env { + name = "MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION" + value = "buffalo_l" + } + + volume_mount { + name = "cache" + mount_path = "/cache" + } + resources { + limits = { + "nvidia.com/gpu" = "1" # Used for inference + } + } + } + volume { + name = "cache" + nfs { + # path = "/mnt/main/immich/machine-learning" + path = "/mnt/ssd/immich/machine-learning" # load cache from ssd + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "immich-machine-learning" { + metadata { + name = "immich-machine-learning" + namespace = kubernetes_namespace.immich.metadata[0].name + labels = { + "app" = "immich-machine-learning" + } + } + + spec { + selector = { + app = "immich-machine-learning" + } + port { + port = 3003 + } + } +} + +module "ingress-immich" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.immich.metadata[0].name + name = "immich" + service_name = "immich-server" + port = 2283 + tls_secret_name = var.tls_secret_name + rybbit_site_id = "35eedb7a3d2b" + skip_default_rate_limit = true + extra_middlewares = ["traefik-immich-rate-limit@kubernetescrd"] + extra_annotations = { + "gethomepage.dev/enabled" = "true" + "gethomepage.dev/description" = "Photos library" + "gethomepage.dev/icon" = "immich.png" + "gethomepage.dev/name" = "Immich" + "gethomepage.dev/widget.type" = "immich" + "gethomepage.dev/widget.url" = "https://immich.viktorbarzin.me" + "gethomepage.dev/pod-selector" = "" + "gethomepage.dev/widget.key" = var.homepage_credentials["immich"]["token"] + } +} + + +resource "kubernetes_cron_job_v1" "postgresql-backup" { + metadata { + name = "postgresql-backup" + namespace = kubernetes_namespace.immich.metadata[0].name + } + spec { + concurrency_policy = "Replace" + failed_jobs_history_limit = 5 + schedule = "0 0 * * *" + # schedule = "* * * * *" + starting_deadline_seconds = 10 + successful_jobs_history_limit = 10 + job_template { + metadata {} + spec { + backoff_limit = 3 + ttl_seconds_after_finished = 10 + template { + metadata {} + spec { + container { + name = "postgresql-backup" + image = "postgres:16.4-bullseye" + command = ["/bin/sh", "-c", <<-EOT + export now=$(date +"%Y_%m_%d_%H_%M") + PGPASSWORD=${var.immich_postgresql_password} pg_dumpall -h immich-postgresql -U immich > /backup/dump_$now.sql + + # Rotate - delete last log file + cd /backup + find . -name "dump_*.sql" -type f -mtime +14 -delete # 14 day retention of backups + EOT + ] + volume_mount { + name = "postgresql-backup" + mount_path = "/backup" + } + } + volume { + name = "postgresql-backup" + nfs { + path = "/mnt/main/immich/data-immich-postgresql" + server = "10.0.10.15" + } + } + } + } + } + } + } +} + +# POWER TOOLS + +# resource "kubernetes_deployment" "powertools" { +# metadata { +# name = "immich-powertools" +# namespace = kubernetes_namespace.immich.metadata[0].name +# labels = { +# app = "immich-powertools" +# } +# annotations = { +# "reloader.stakater.com/search" = "true" +# } +# } +# spec { +# replicas = 1 +# strategy { +# type = "Recreate" +# } +# selector { +# match_labels = { +# app = "immich-powertools" +# } +# } +# template { +# metadata { +# labels = { +# app = "immich-powertools" +# } +# annotations = { +# "diun.enable" = "true" +# "diun.include_tags" = "latest" +# } +# } +# spec { + +# container { +# image = "ghcr.io/varun-raj/immich-power-tools:latest" +# name = "owntracks" +# port { +# name = "http" +# container_port = 3000 +# } +# env { +# name = "IMMICH_API_KEY" +# value = "" +# } +# env { +# name = "IMMICH_URL" +# value = "http://immich-server.immich.svc.cluster.local" +# } +# env { +# name = "EXTERNAL_IMMICH_URL" +# value = "https://immich.viktorbarzin.me" +# } +# env { +# name = "DB_USERNAME" +# value = "immich" +# } +# env { +# name = "DB_PASSWORD" +# value = var.immich_postgresql_password +# } +# env { +# name = "DB_HOST" +# value = "immich-postgresql.immich.svc.cluster.local" +# } +# # env { +# # name = "DB_PORT" +# # value = "5432" +# # } +# env { +# name = "DB_DATABASE_NAME" +# value = "immich" +# } +# env { +# name = "NODE_ENV" +# value = "development" +# } + +# } +# } +# } +# } +# } + + +# resource "kubernetes_service" "powertools" { +# metadata { +# name = "immich-powertools" +# namespace = kubernetes_namespace.immich.metadata[0].name +# labels = { +# "app" = "immich-powertools" +# } +# } + +# spec { +# selector = { +# app = "immich-powertools" +# } +# port { +# name = "http" +# port = 80 +# target_port = 3000 +# protocol = "TCP" +# } +# } +# } + +# module "ingress-powertools" { +# source = "../../modules/kubernetes/ingress_factory" +# namespace = kubernetes_namespace.immich.metadata[0].name +# name = "immich-powertools" +# tls_secret_name = var.tls_secret_name +# protected = true +# } diff --git a/stacks/immich/module/main.tf b/stacks/immich/module/main.tf deleted file mode 100644 index 28939154..00000000 --- a/stacks/immich/module/main.tf +++ /dev/null @@ -1,650 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "postgresql_password" {} -variable "homepage_token" {} -variable "immich_version" { - type = string - # Change me to upgrade - default = "v2.5.6" -} - - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.immich.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_namespace" "immich" { - metadata { - name = "immich" - labels = { - tier = var.tier - } - } -} - -resource "kubernetes_deployment" "immich_server" { - metadata { - name = "immich-server" - namespace = kubernetes_namespace.immich.metadata[0].name - - labels = { - app = "immich-server" - tier = var.tier - } - } - - spec { - replicas = 1 - progress_deadline_seconds = 600 - - selector { - match_labels = { - app = "immich-server" - } - } - - strategy { - type = "RollingUpdate" - } - - template { - metadata { - labels = { - app = "immich-server" - } - annotations = { - "diun.enable" = "true" - "diun.include_tags" = "^\\d+\\.\\d+\\.\\d+$" - } - } - - spec { - container { - name = "immich-server" - image = "ghcr.io/immich-app/immich-server:${var.immich_version}" - - port { - name = "http" - container_port = 2283 - protocol = "TCP" - } - - env { - name = "DB_DATABASE_NAME" - value = "immich" - } - env { - name = "DB_HOSTNAME" - value = "immich-postgresql.immich.svc.cluster.local" - } - env { - name = "DB_USERNAME" - value = "immich" - } - env { - name = "DB_PASSWORD" - value = var.postgresql_password - } - env { - name = "IMMICH_MACHINE_LEARNING_URL" - value = "http://immich-machine-learning:3003" - } - env { - name = "REDIS_HOSTNAME" - value = "redis.redis.svc.cluster.local" - } - - liveness_probe { - http_get { - path = "/api/server/ping" - port = "http" - } - initial_delay_seconds = 0 - period_seconds = 10 - timeout_seconds = 1 - failure_threshold = 3 - success_threshold = 1 - } - - readiness_probe { - http_get { - path = "/api/server/ping" - port = "http" - } - period_seconds = 10 - timeout_seconds = 1 - failure_threshold = 3 - success_threshold = 1 - } - - startup_probe { - http_get { - path = "/api/server/ping" - port = "http" - } - period_seconds = 10 - timeout_seconds = 1 - failure_threshold = 30 - success_threshold = 1 - } - - # volume_mount { - # name = "library-old" - # mount_path = "/usr/src/app/upload" - # } - - # Mount them 1 by 1 to enable thumbs in ssd - volume_mount { - name = "backups" - mount_path = "/usr/src/app/upload/backups" - } - volume_mount { - name = "encoded-video" - mount_path = "/usr/src/app/upload/encoded-video" - } - volume_mount { - name = "library" - mount_path = "/usr/src/app/upload/library" - } - volume_mount { - name = "profile" - mount_path = "/usr/src/app/upload/profile" - } - volume_mount { - name = "thumbs" - mount_path = "/usr/src/app/upload/thumbs" - } - volume_mount { - name = "upload" - mount_path = "/usr/src/app/upload/upload" - } - } - - # volume { - # name = "library-old" - # nfs { - # server = "10.0.10.15" - # path = "/mnt/main/immich/immich/" - # } - # } - - volume { - name = "backups" - nfs { - server = "10.0.10.15" - path = "/mnt/main/immich/immich/backups" - } - } - volume { - name = "encoded-video" - nfs { - server = "10.0.10.15" - path = "/mnt/main/immich/immich/encoded-video" - } - } - volume { - name = "library" - nfs { - server = "10.0.10.15" - path = "/mnt/main/immich/immich/library" - } - } - volume { - name = "profile" - nfs { - server = "10.0.10.15" - path = "/mnt/main/immich/immich/profile" - } - } - volume { - name = "thumbs" - nfs { - server = "10.0.10.15" - path = "/mnt/ssd/immich/thumbs" - } - } - volume { - name = "upload" - nfs { - server = "10.0.10.15" - path = "/mnt/main/immich/immich/upload" - } - } - } - } - } -} - -resource "kubernetes_service" "immich-server" { - metadata { - name = "immich-server" - namespace = kubernetes_namespace.immich.metadata[0].name - labels = { - "app" = "immich-server" - } - } - - spec { - selector = { - app = "immich-server" - } - port { - port = 2283 - } - } -} - -resource "kubernetes_deployment" "immich-postgres" { - metadata { - name = "immich-postgresql" - namespace = kubernetes_namespace.immich.metadata[0].name - labels = { - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "immich-postgresql" - } - } - strategy { - type = "Recreate" - } - template { - metadata { - labels = { - app = "immich-postgresql" - } - } - spec { - container { - image = "ghcr.io/immich-app/postgres:15-vectorchord0.3.0-pgvectors0.2.0" - name = "immich-postgresql" - port { - container_port = 5432 - protocol = "TCP" - name = "postgresql" - } - env { - name = "POSTGRES_PASSWORD" - value = var.postgresql_password - } - env { - name = "POSTGRES_USER" - value = "immich" - } - env { - name = "POSTGRES_DB" - value = "immich" - } - env { - name = "DB_STORAGE_TYPE" - value = "HDD" - } - volume_mount { - name = "postgresql-persistent-storage" - mount_path = "/var/lib/postgresql/data" - } - } - volume { - name = "postgresql-persistent-storage" - nfs { - path = "/mnt/main/immich/data-immich-postgresql" - server = "10.0.10.15" - } - } - } - } - } -} - - -resource "kubernetes_service" "immich-postgresql" { - metadata { - name = "immich-postgresql" - namespace = kubernetes_namespace.immich.metadata[0].name - labels = { - "app" = "immich-postgresql" - } - } - - spec { - selector = { - app = "immich-postgresql" - } - port { - port = 5432 - } - } -} - - -# If you're having issuewith typesens container exiting prematurely, increase liveliness check -# resource "helm_release" "immich" { -# namespace = kubernetes_namespace.immich.metadata[0].name -# name = "immich" - -# repository = "https://immich-app.github.io/immich-charts" -# chart = "immich" -# atomic = true -# version = "0.9.3" -# timeout = 6000 - -# values = [templatefile("${path.module}/chart_values.tpl", { postgresql_password = var.postgresql_password, version = var.immich_version })] -# } - -# The helm one cannot be customized to use affinity settings to use the gpu node -resource "kubernetes_deployment" "immich-machine-learning" { - metadata { - name = "immich-machine-learning" - namespace = kubernetes_namespace.immich.metadata[0].name - labels = { - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "immich-machine-learning" - } - } - strategy { - type = "RollingUpdate" - } - template { - metadata { - labels = { - app = "immich-machine-learning" - } - } - spec { - node_selector = { - "gpu" : "true" - } - toleration { - key = "nvidia.com/gpu" - operator = "Equal" - value = "true" - effect = "NoSchedule" - } - container { - # image = "ghcr.io/immich-app/immich-machine-learning:${var.immich_version}" - image = "ghcr.io/immich-app/immich-machine-learning:${var.immich_version}-cuda" - name = "immich-machine-learning" - port { - container_port = 3003 - protocol = "TCP" - name = "immich-ml" - } - env { - name = "MACHINE_LEARNING_MODEL_TTL" - value = "0" - } - env { - name = "TRANSFORMERS_CACHE" - value = "/cache" - } - env { - name = "HF_XET_CACHE" - value = "/cache/huggingface-xet" - } - env { - name = "MPLCONFIGDIR" - value = "/cache/matplotlib-config" - } - # Preload CLIP models (for smart search) - env { - name = "MACHINE_LEARNING_PRELOAD__CLIP__TEXTUAL" - value = "ViT-B-16-SigLIP2__webli" - } - env { - name = "MACHINE_LEARNING_PRELOAD__CLIP__VISUAL" - value = "ViT-B-16-SigLIP2__webli" - } - # Preload facial recognition models - env { - name = "MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION" - value = "buffalo_l" - } - env { - name = "MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION" - value = "buffalo_l" - } - - volume_mount { - name = "cache" - mount_path = "/cache" - } - resources { - limits = { - "nvidia.com/gpu" = "1" # Used for inference - } - } - } - volume { - name = "cache" - nfs { - # path = "/mnt/main/immich/machine-learning" - path = "/mnt/ssd/immich/machine-learning" # load cache from ssd - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "immich-machine-learning" { - metadata { - name = "immich-machine-learning" - namespace = kubernetes_namespace.immich.metadata[0].name - labels = { - "app" = "immich-machine-learning" - } - } - - spec { - selector = { - app = "immich-machine-learning" - } - port { - port = 3003 - } - } -} - -module "ingress-immich" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.immich.metadata[0].name - name = "immich" - service_name = "immich-server" - port = 2283 - tls_secret_name = var.tls_secret_name - rybbit_site_id = "35eedb7a3d2b" - skip_default_rate_limit = true - extra_middlewares = ["traefik-immich-rate-limit@kubernetescrd"] - extra_annotations = { - "gethomepage.dev/enabled" = "true" - "gethomepage.dev/description" = "Photos library" - "gethomepage.dev/icon" = "immich.png" - "gethomepage.dev/name" = "Immich" - "gethomepage.dev/widget.type" = "immich" - "gethomepage.dev/widget.url" = "https://immich.viktorbarzin.me" - "gethomepage.dev/pod-selector" = "" - "gethomepage.dev/widget.key" = var.homepage_token - } -} - - -resource "kubernetes_cron_job_v1" "postgresql-backup" { - metadata { - name = "postgresql-backup" - namespace = kubernetes_namespace.immich.metadata[0].name - } - spec { - concurrency_policy = "Replace" - failed_jobs_history_limit = 5 - schedule = "0 0 * * *" - # schedule = "* * * * *" - starting_deadline_seconds = 10 - successful_jobs_history_limit = 10 - job_template { - metadata {} - spec { - backoff_limit = 3 - ttl_seconds_after_finished = 10 - template { - metadata {} - spec { - container { - name = "postgresql-backup" - image = "postgres:16.4-bullseye" - command = ["/bin/sh", "-c", <<-EOT - export now=$(date +"%Y_%m_%d_%H_%M") - PGPASSWORD=${var.postgresql_password} pg_dumpall -h immich-postgresql -U immich > /backup/dump_$now.sql - - # Rotate - delete last log file - cd /backup - find . -name "dump_*.sql" -type f -mtime +14 -delete # 14 day retention of backups - EOT - ] - volume_mount { - name = "postgresql-backup" - mount_path = "/backup" - } - } - volume { - name = "postgresql-backup" - nfs { - path = "/mnt/main/immich/data-immich-postgresql" - server = "10.0.10.15" - } - } - } - } - } - } - } -} - -# POWER TOOLS - -# resource "kubernetes_deployment" "powertools" { -# metadata { -# name = "immich-powertools" -# namespace = kubernetes_namespace.immich.metadata[0].name -# labels = { -# app = "immich-powertools" -# } -# annotations = { -# "reloader.stakater.com/search" = "true" -# } -# } -# spec { -# replicas = 1 -# strategy { -# type = "Recreate" -# } -# selector { -# match_labels = { -# app = "immich-powertools" -# } -# } -# template { -# metadata { -# labels = { -# app = "immich-powertools" -# } -# annotations = { -# "diun.enable" = "true" -# "diun.include_tags" = "latest" -# } -# } -# spec { - -# container { -# image = "ghcr.io/varun-raj/immich-power-tools:latest" -# name = "owntracks" -# port { -# name = "http" -# container_port = 3000 -# } -# env { -# name = "IMMICH_API_KEY" -# value = "" -# } -# env { -# name = "IMMICH_URL" -# value = "http://immich-server.immich.svc.cluster.local" -# } -# env { -# name = "EXTERNAL_IMMICH_URL" -# value = "https://immich.viktorbarzin.me" -# } -# env { -# name = "DB_USERNAME" -# value = "immich" -# } -# env { -# name = "DB_PASSWORD" -# value = var.postgresql_password -# } -# env { -# name = "DB_HOST" -# value = "immich-postgresql.immich.svc.cluster.local" -# } -# # env { -# # name = "DB_PORT" -# # value = "5432" -# # } -# env { -# name = "DB_DATABASE_NAME" -# value = "immich" -# } -# env { -# name = "NODE_ENV" -# value = "development" -# } - -# } -# } -# } -# } -# } - - -# resource "kubernetes_service" "powertools" { -# metadata { -# name = "immich-powertools" -# namespace = kubernetes_namespace.immich.metadata[0].name -# labels = { -# "app" = "immich-powertools" -# } -# } - -# spec { -# selector = { -# app = "immich-powertools" -# } -# port { -# name = "http" -# port = 80 -# target_port = 3000 -# protocol = "TCP" -# } -# } -# } - -# module "ingress-powertools" { -# source = "../../../modules/kubernetes/ingress_factory" -# namespace = kubernetes_namespace.immich.metadata[0].name -# name = "immich-powertools" -# tls_secret_name = var.tls_secret_name -# protected = true -# } - diff --git a/stacks/isponsorblocktv/main.tf b/stacks/isponsorblocktv/main.tf index 70feedca..21b555ba 100644 --- a/stacks/isponsorblocktv/main.tf +++ b/stacks/isponsorblocktv/main.tf @@ -8,7 +8,58 @@ locals { } } -module "isponsorblocktv" { - source = "./module" - tier = local.tiers.edge +resource "kubernetes_namespace" "isponsorblocktv" { + metadata { + name = "isponsorblocktv" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.edge + } + } +} +# Before running, setup config using +# docker run --rm -it -v ./youtube:/app/data -e TERM=$TERM -e COLORTERM=$COLORTERM ghcr.io/dmunozv04/isponsorblocktv --setup + +# Mute and skip ads for vermont smart tv +resource "kubernetes_deployment" "isponsorblocktv-vermont" { + metadata { + name = "isponsorblocktv-vermont" + namespace = kubernetes_namespace.isponsorblocktv.metadata[0].name + labels = { + app = "isponsorblocktv-vermont" + tier = local.tiers.edge + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "isponsorblocktv-vermont" + } + } + template { + metadata { + labels = { + app = "isponsorblocktv-vermont" + } + } + spec { + container { + image = "ghcr.io/dmunozv04/isponsorblocktv" + name = "isponsorblocktv-vermont" + volume_mount { + name = "data" + mount_path = "/app/data" + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/isponsorblocktv/vermont" + } + } + } + } + } } diff --git a/stacks/isponsorblocktv/module/main.tf b/stacks/isponsorblocktv/module/main.tf deleted file mode 100644 index 7a5bf4fc..00000000 --- a/stacks/isponsorblocktv/module/main.tf +++ /dev/null @@ -1,59 +0,0 @@ -# https://github.com/dmunozv04/iSponsorBlockTV -variable "tier" { type = string } - -resource "kubernetes_namespace" "isponsorblocktv" { - metadata { - name = "isponsorblocktv" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} -# Before running, setup config using -# docker run --rm -it -v ./youtube:/app/data -e TERM=$TERM -e COLORTERM=$COLORTERM ghcr.io/dmunozv04/isponsorblocktv --setup - -# Mute and skip ads for vermont smart tv -resource "kubernetes_deployment" "isponsorblocktv-vermont" { - metadata { - name = "isponsorblocktv-vermont" - namespace = kubernetes_namespace.isponsorblocktv.metadata[0].name - labels = { - app = "isponsorblocktv-vermont" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "isponsorblocktv-vermont" - } - } - template { - metadata { - labels = { - app = "isponsorblocktv-vermont" - } - } - spec { - container { - image = "ghcr.io/dmunozv04/isponsorblocktv" - name = "isponsorblocktv-vermont" - volume_mount { - name = "data" - mount_path = "/app/data" - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/isponsorblocktv/vermont" - } - } - } - } - } -} - diff --git a/stacks/jsoncrack/main.tf b/stacks/jsoncrack/main.tf index c343e0e4..16777af7 100644 --- a/stacks/jsoncrack/main.tf +++ b/stacks/jsoncrack/main.tf @@ -10,8 +10,81 @@ locals { } } -module "jsoncrack" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "jsoncrack" { + metadata { + name = "jsoncrack" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.jsoncrack.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "jsoncrack" { + metadata { + name = "jsoncrack" + namespace = kubernetes_namespace.jsoncrack.metadata[0].name + labels = { + app = "jsoncrack" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "jsoncrack" + } + } + template { + metadata { + labels = { + app = "jsoncrack" + } + } + spec { + container { + image = "viktorbarzin/jsoncrack:latest" + name = "jsoncrack" + port { + container_port = 8080 + } + } + } + } + } +} + +resource "kubernetes_service" "jsoncrack" { + metadata { + name = "json" + namespace = kubernetes_namespace.jsoncrack.metadata[0].name + labels = { + "app" = "jsoncrack" + } + } + + spec { + selector = { + app = "jsoncrack" + } + port { + name = "http" + target_port = 8080 + port = 80 + protocol = "TCP" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.jsoncrack.metadata[0].name + name = "json" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/jsoncrack/module/main.tf b/stacks/jsoncrack/module/main.tf deleted file mode 100644 index d8f3e10e..00000000 --- a/stacks/jsoncrack/module/main.tf +++ /dev/null @@ -1,81 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "jsoncrack" { - metadata { - name = "jsoncrack" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.jsoncrack.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "jsoncrack" { - metadata { - name = "jsoncrack" - namespace = kubernetes_namespace.jsoncrack.metadata[0].name - labels = { - app = "jsoncrack" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "jsoncrack" - } - } - template { - metadata { - labels = { - app = "jsoncrack" - } - } - spec { - container { - image = "viktorbarzin/jsoncrack:latest" - name = "jsoncrack" - port { - container_port = 8080 - } - } - } - } - } -} - -resource "kubernetes_service" "jsoncrack" { - metadata { - name = "json" - namespace = kubernetes_namespace.jsoncrack.metadata[0].name - labels = { - "app" = "jsoncrack" - } - } - - spec { - selector = { - app = "jsoncrack" - } - port { - name = "http" - target_port = 8080 - port = 80 - protocol = "TCP" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.jsoncrack.metadata[0].name - name = "json" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/k8s-dashboard/main.tf b/stacks/k8s-dashboard/main.tf index c475dfa6..0061837a 100644 --- a/stacks/k8s-dashboard/main.tf +++ b/stacks/k8s-dashboard/main.tf @@ -11,9 +11,237 @@ locals { } } -module "k8s-dashboard" { - source = "./module" - tier = local.tiers.cluster - tls_secret_name = var.tls_secret_name - client_certificate_secret_name = var.client_certificate_secret_name +resource "random_password" "csrf_token" { + length = 16 + special = true + override_special = "_%@" +} + +# instructions on deploying: +# https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#accessing-the-dashboard-ui + +# module "dashboard" { +# # source = "cookielab/dashboard/kubernetes" +# source = "ViktorBarzin/dashboard/kubernetes" +# version = "0.13.2" +# kubernetes_dashboard_csrf = random_password.csrf_token.result +# kubernetes_dashboard_deployment_args = tolist([ +# "--auto-generate-certificates", +# "--token-ttl=0" +# ]) +# } +resource "kubernetes_namespace" "k8s-dashboard" { + metadata { + name = "kubernetes-dashboard" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.cluster + } + } +} +# } + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "helm_release" "kubernetes-dashboard" { + namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name + name = "kubernetes-dashboard" + + repository = "https://kubernetes.github.io/dashboard/" + chart = "kubernetes-dashboard" + atomic = true + version = "7.12.0" + + # values = [templatefile("${path.module}/chart_values.tpl", { postgresql_password = var.postgresql_password })] +} + +# # locals { +# # resources = split("---\n", file("${path.module}/recommended.yaml")) +# # } +# # resource "k8s_manifest" "kubernetes-dashboard-manifests" { +# # count = length(local.resources) - 1 +# # # count = 2 +# # # content = local.resources[1 + count.index] +# # # content = file("${path.module}/recommended.yaml") +# # content = local.resources[1] +# # depends_on = [kubernetes_namespace.kubernetes-dashboard] +# # } +# resource "kubectl_manifest" "kubernetes-dashboard-manifests" { +# yaml_body = file("${path.module}/recommended.yaml") +# force_new = true +# depends_on = [kubernetes_namespace.kubernetes-dashboard] +# } + +# resource "kubernetes_secret" "dashboard-token" { +# metadata { +# name = "dashboard-secret" +# namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name +# annotations = { +# "kubernetes.io/service-account.name" : "kubernetes-dashboard" +# } +# } +# type = "kubernetes.io/service-account-token" +# } + + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name + name = "kubernetes-dashboard" + service_name = "kubernetes-dashboard-kong-proxy" + host = "k8s" + tls_secret_name = var.tls_secret_name + protected = true + backend_protocol = "HTTPS" + port = 443 +} + +# create token with +# kb create token --duration=0s kubernetes-dashboard +resource "kubernetes_service_account" "kubernetes-dashboard" { + metadata { + name = "kubernetes-dashboard" + namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name + } +} + +# Give cluster-admin permissions to dashboard +resource "kubernetes_cluster_role_binding" "kubernetes-dashboard" { + metadata { + name = "admin-user" + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = "cluster-admin" + } + subject { + kind = "ServiceAccount" + name = "kubernetes-dashboard" + namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name + } + # depends_on = [module.dashboard] +} + +resource "kubernetes_secret" "kubernetes-dashboard-admin-token" { + metadata { + name = "kubernetes-dashboard-admin" + namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name + annotations = { + "kubernetes.io/service-account.name" : "kubernetes-dashboard" + } + } + type = "kubernetes.io/service-account-token" +} + +## Readonly RBAC +resource "kubernetes_cluster_role" "kubernetes-dashboard-viewonly" { + metadata { + name = "kubernetes-dashboard-viewonly" + } + + rule { + api_groups = [""] + resources = ["configmaps", "endpoints", "persistentvolumeclaims", "pods", "replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", "services", "nodes", "persistentvolumeclaims", "persistentvolumes"] + verbs = ["get", "list", "watch"] + } + + rule { + api_groups = [""] + resources = ["bindings", "events", "limitranges", "namespaces/status", "pods/log", "pods/status", "replicationcontrollers/status", "resourcequotas", "resourcequotas/status"] + verbs = ["get", "list", "watch"] + } + + rule { + api_groups = [""] + resources = ["namespaces"] + verbs = ["get", "list", "watch"] + } + + rule { + api_groups = ["apps"] + resources = ["daemonsets", "deployments", "deployments/scale", "replicasets", "replicasets/scale", "statefulsets"] + verbs = ["get", "list", "watch"] + } + + rule { + api_groups = ["autoscaling"] + resources = ["horizontalpodautoscalers"] + verbs = ["get", "list", "watch"] + } + + rule { + api_groups = ["batch"] + resources = ["cronjobs", "jobs"] + verbs = ["get", "list", "watch"] + } + + rule { + api_groups = ["extensions"] + resources = ["daemonsets", "deployments", "deployments/scale", "ingresses", "networkpolicies", "replicasets", "replicasets/scale", "replicationcontrollers/scale"] + verbs = ["get", "list", "watch"] + } + + rule { + api_groups = ["policy"] + resources = ["poddisruptionbudgets"] + verbs = ["get", "list", "watch"] + } + + rule { + api_groups = ["networking.k8s.io"] + resources = ["networkpolicies"] + verbs = ["get", "list", "watch"] + } + + rule { + api_groups = ["storage.k8s.io"] + resources = ["storageclasses", "volumeattachments"] + verbs = ["get", "list", "watch"] + } + + rule { + api_groups = ["rbac.authorization.k8s.io"] + resources = ["clusterrolebindings", "clusterroles", "roles", "rolebindings"] + verbs = ["get", "list", "watch"] + } +} + +resource "kubernetes_cluster_role_binding" "kubernetes-dashboard-viewonly" { + metadata { + name = "kubernetes-dashboard-viewonly" + } + + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = "kubernetes-dashboard-viewonly" + } + subject { + kind = "ServiceAccount" + name = "kubernetes-dashboard-viewonly" + namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name + } +} + +resource "kubernetes_service_account" "kubernetes-dashboard-viewonly" { + metadata { + name = "kubernetes-dashboard-viewonly" + namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name + } +} + +resource "kubernetes_secret" "kubernetes-dashboard-viewonly-token" { + metadata { + name = "kubernetes-dashboard-viewonly" + namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name + annotations = { + "kubernetes.io/service-account.name" : "kubernetes-dashboard-viewonly" + } + } + type = "kubernetes.io/service-account-token" } diff --git a/stacks/k8s-dashboard/module/main.tf b/stacks/k8s-dashboard/module/main.tf deleted file mode 100644 index 992f2ae7..00000000 --- a/stacks/k8s-dashboard/module/main.tf +++ /dev/null @@ -1,238 +0,0 @@ -variable "tls_secret_name" {} -variable "client_certificate_secret_name" {} -variable "tier" { type = string } - -resource "random_password" "csrf_token" { - length = 16 - special = true - override_special = "_%@" -} - -# instructions on deploying: -# https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#accessing-the-dashboard-ui - -# module "dashboard" { -# # source = "cookielab/dashboard/kubernetes" -# source = "ViktorBarzin/dashboard/kubernetes" -# version = "0.13.2" -# kubernetes_dashboard_csrf = random_password.csrf_token.result -# kubernetes_dashboard_deployment_args = tolist([ -# "--auto-generate-certificates", -# "--token-ttl=0" -# ]) -# } -resource "kubernetes_namespace" "k8s-dashboard" { - metadata { - name = "kubernetes-dashboard" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} -# } - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "helm_release" "kubernetes-dashboard" { - namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name - name = "kubernetes-dashboard" - - repository = "https://kubernetes.github.io/dashboard/" - chart = "kubernetes-dashboard" - atomic = true - version = "7.12.0" - - # values = [templatefile("${path.module}/chart_values.tpl", { postgresql_password = var.postgresql_password })] -} - -# # locals { -# # resources = split("---\n", file("${path.module}/recommended.yaml")) -# # } -# # resource "k8s_manifest" "kubernetes-dashboard-manifests" { -# # count = length(local.resources) - 1 -# # # count = 2 -# # # content = local.resources[1 + count.index] -# # # content = file("${path.module}/recommended.yaml") -# # content = local.resources[1] -# # depends_on = [kubernetes_namespace.kubernetes-dashboard] -# # } -# resource "kubectl_manifest" "kubernetes-dashboard-manifests" { -# yaml_body = file("${path.module}/recommended.yaml") -# force_new = true -# depends_on = [kubernetes_namespace.kubernetes-dashboard] -# } - -# resource "kubernetes_secret" "dashboard-token" { -# metadata { -# name = "dashboard-secret" -# namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name -# annotations = { -# "kubernetes.io/service-account.name" : "kubernetes-dashboard" -# } -# } -# type = "kubernetes.io/service-account-token" -# } - - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name - name = "kubernetes-dashboard" - service_name = "kubernetes-dashboard-kong-proxy" - host = "k8s" - tls_secret_name = var.tls_secret_name - protected = true - backend_protocol = "HTTPS" - port = 443 -} - -# create token with -# kb create token --duration=0s kubernetes-dashboard -resource "kubernetes_service_account" "kubernetes-dashboard" { - metadata { - name = "kubernetes-dashboard" - namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name - } -} - -# Give cluster-admin permissions to dashboard -resource "kubernetes_cluster_role_binding" "kubernetes-dashboard" { - metadata { - name = "admin-user" - } - role_ref { - api_group = "rbac.authorization.k8s.io" - kind = "ClusterRole" - name = "cluster-admin" - } - subject { - kind = "ServiceAccount" - name = "kubernetes-dashboard" - namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name - } - # depends_on = [module.dashboard] -} - -resource "kubernetes_secret" "kubernetes-dashboard-admin-token" { - metadata { - name = "kubernetes-dashboard-admin" - namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name - annotations = { - "kubernetes.io/service-account.name" : "kubernetes-dashboard" - } - } - type = "kubernetes.io/service-account-token" -} - -## Readonly RBAC -resource "kubernetes_cluster_role" "kubernetes-dashboard-viewonly" { - metadata { - name = "kubernetes-dashboard-viewonly" - } - - rule { - api_groups = [""] - resources = ["configmaps", "endpoints", "persistentvolumeclaims", "pods", "replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", "services", "nodes", "persistentvolumeclaims", "persistentvolumes"] - verbs = ["get", "list", "watch"] - } - - rule { - api_groups = [""] - resources = ["bindings", "events", "limitranges", "namespaces/status", "pods/log", "pods/status", "replicationcontrollers/status", "resourcequotas", "resourcequotas/status"] - verbs = ["get", "list", "watch"] - } - - rule { - api_groups = [""] - resources = ["namespaces"] - verbs = ["get", "list", "watch"] - } - - rule { - api_groups = ["apps"] - resources = ["daemonsets", "deployments", "deployments/scale", "replicasets", "replicasets/scale", "statefulsets"] - verbs = ["get", "list", "watch"] - } - - rule { - api_groups = ["autoscaling"] - resources = ["horizontalpodautoscalers"] - verbs = ["get", "list", "watch"] - } - - rule { - api_groups = ["batch"] - resources = ["cronjobs", "jobs"] - verbs = ["get", "list", "watch"] - } - - rule { - api_groups = ["extensions"] - resources = ["daemonsets", "deployments", "deployments/scale", "ingresses", "networkpolicies", "replicasets", "replicasets/scale", "replicationcontrollers/scale"] - verbs = ["get", "list", "watch"] - } - - rule { - api_groups = ["policy"] - resources = ["poddisruptionbudgets"] - verbs = ["get", "list", "watch"] - } - - rule { - api_groups = ["networking.k8s.io"] - resources = ["networkpolicies"] - verbs = ["get", "list", "watch"] - } - - rule { - api_groups = ["storage.k8s.io"] - resources = ["storageclasses", "volumeattachments"] - verbs = ["get", "list", "watch"] - } - - rule { - api_groups = ["rbac.authorization.k8s.io"] - resources = ["clusterrolebindings", "clusterroles", "roles", "rolebindings"] - verbs = ["get", "list", "watch"] - } -} - -resource "kubernetes_cluster_role_binding" "kubernetes-dashboard-viewonly" { - metadata { - name = "kubernetes-dashboard-viewonly" - } - - role_ref { - api_group = "rbac.authorization.k8s.io" - kind = "ClusterRole" - name = "kubernetes-dashboard-viewonly" - } - subject { - kind = "ServiceAccount" - name = "kubernetes-dashboard-viewonly" - namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name - } -} - -resource "kubernetes_service_account" "kubernetes-dashboard-viewonly" { - metadata { - name = "kubernetes-dashboard-viewonly" - namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name - } -} - -resource "kubernetes_secret" "kubernetes-dashboard-viewonly-token" { - metadata { - name = "kubernetes-dashboard-viewonly" - namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name - annotations = { - "kubernetes.io/service-account.name" : "kubernetes-dashboard-viewonly" - } - } - type = "kubernetes.io/service-account-token" -} diff --git a/stacks/kms/main.tf b/stacks/kms/main.tf index 38e851bd..f9a52f46 100644 --- a/stacks/kms/main.tf +++ b/stacks/kms/main.tf @@ -10,8 +10,189 @@ locals { } } -module "kms" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "kms" { + metadata { + name = "kms" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.kms.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_config_map" "kms-web-page" { + metadata { + name = "kms-web-page-config" + namespace = kubernetes_namespace.kms.metadata[0].name + } + data = { + "index.html" = var.index_html + } +} + +resource "kubernetes_deployment" "kms-web-page" { + metadata { + name = "kms-web-page" + namespace = kubernetes_namespace.kms.metadata[0].name + labels = { + "app" = "kms-web-page" + "kubernetes.io/cluster-service" = "true" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + "app" = "kms-web-page" + } + } + template { + metadata { + labels = { + "app" = "kms-web-page" + "kubernetes.io/cluster-service" = "true" + } + } + spec { + container { + image = "nginx" + name = "kms-web-page" + image_pull_policy = "IfNotPresent" + resources { + limits = { + cpu = "0.5" + memory = "512Mi" + } + requests = { + cpu = "0.5" + memory = "512Mi" + } + } + port { + container_port = 80 + protocol = "TCP" + } + volume_mount { + name = "config" + mount_path = "/usr/share/nginx/html/" + } + } + + volume { + name = "config" + config_map { + name = "kms-web-page-config" + items { + key = "index.html" + path = "index.html" + } + } + } + } + } + } + depends_on = [kubernetes_config_map.kms-web-page] +} + +resource "kubernetes_service" "kms-web-page" { + metadata { + name = "kms" + namespace = kubernetes_namespace.kms.metadata[0].name + labels = { + "app" = "kms-web-page" + } + } + + spec { + selector = { + "app" = "kms-web-page" + } + port { + port = "80" + protocol = "TCP" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.kms.metadata[0].name + name = "kms" + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "windows_kms" { + metadata { + name = "kms" + namespace = kubernetes_namespace.kms.metadata[0].name + labels = { + app = "kms-service" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "kms-service" + } + } + template { + metadata { + labels = { + app = "kms-service" + } + } + spec { + container { + image = "kebe/vlmcsd:latest" + name = "windows-kms" + resources { + limits = { + cpu = "1" + memory = "512Mi" + } + requests = { + cpu = "1" + memory = "50Mi" + } + } + port { + container_port = 1688 + } + } + } + } + } +} + +resource "kubernetes_service" "windows_kms" { + metadata { + name = "windows-kms" + namespace = kubernetes_namespace.kms.metadata[0].name + labels = { + app = "kms-service" + } + annotations = { + "metallb.universe.tf/allow-shared-ip" = "shared" + } + } + + spec { + type = "LoadBalancer" + external_traffic_policy = "Cluster" + selector = { + app = "kms-service" + } + port { + port = "1688" + } + } } diff --git a/stacks/kms/module/main.tf b/stacks/kms/module/main.tf deleted file mode 100644 index f97cc1ac..00000000 --- a/stacks/kms/module/main.tf +++ /dev/null @@ -1,189 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "kms" { - metadata { - name = "kms" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.kms.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_config_map" "kms-web-page" { - metadata { - name = "kms-web-page-config" - namespace = kubernetes_namespace.kms.metadata[0].name - } - data = { - "index.html" = var.index_html - } -} - -resource "kubernetes_deployment" "kms-web-page" { - metadata { - name = "kms-web-page" - namespace = kubernetes_namespace.kms.metadata[0].name - labels = { - "app" = "kms-web-page" - "kubernetes.io/cluster-service" = "true" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - "app" = "kms-web-page" - } - } - template { - metadata { - labels = { - "app" = "kms-web-page" - "kubernetes.io/cluster-service" = "true" - } - } - spec { - container { - image = "nginx" - name = "kms-web-page" - image_pull_policy = "IfNotPresent" - resources { - limits = { - cpu = "0.5" - memory = "512Mi" - } - requests = { - cpu = "0.5" - memory = "512Mi" - } - } - port { - container_port = 80 - protocol = "TCP" - } - volume_mount { - name = "config" - mount_path = "/usr/share/nginx/html/" - } - } - - volume { - name = "config" - config_map { - name = "kms-web-page-config" - items { - key = "index.html" - path = "index.html" - } - } - } - } - } - } - depends_on = [kubernetes_config_map.kms-web-page] -} - -resource "kubernetes_service" "kms-web-page" { - metadata { - name = "kms" - namespace = kubernetes_namespace.kms.metadata[0].name - labels = { - "app" = "kms-web-page" - } - } - - spec { - selector = { - "app" = "kms-web-page" - } - port { - port = "80" - protocol = "TCP" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.kms.metadata[0].name - name = "kms" - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "windows_kms" { - metadata { - name = "kms" - namespace = kubernetes_namespace.kms.metadata[0].name - labels = { - app = "kms-service" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "kms-service" - } - } - template { - metadata { - labels = { - app = "kms-service" - } - } - spec { - container { - image = "kebe/vlmcsd:latest" - name = "windows-kms" - resources { - limits = { - cpu = "1" - memory = "512Mi" - } - requests = { - cpu = "1" - memory = "50Mi" - } - } - port { - container_port = 1688 - } - } - } - } - } -} - -resource "kubernetes_service" "windows_kms" { - metadata { - name = "windows-kms" - namespace = kubernetes_namespace.kms.metadata[0].name - labels = { - app = "kms-service" - } - annotations = { - "metallb.universe.tf/allow-shared-ip" = "shared" - } - } - - spec { - type = "LoadBalancer" - external_traffic_policy = "Cluster" - selector = { - app = "kms-service" - } - port { - port = "1688" - } - } -} diff --git a/stacks/kms/module/variables.tf b/stacks/kms/variables.tf similarity index 100% rename from stacks/kms/module/variables.tf rename to stacks/kms/variables.tf diff --git a/stacks/linkwarden/main.tf b/stacks/linkwarden/main.tf index 01b34b67..5cb025d7 100644 --- a/stacks/linkwarden/main.tf +++ b/stacks/linkwarden/main.tf @@ -13,11 +13,121 @@ locals { } } -module "linkwarden" { - source = "./module" - 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.tiers.aux +resource "kubernetes_namespace" "linkwarden" { + metadata { + name = "linkwarden" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.linkwarden.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "random_string" "secret" { + length = 32 + special = true + override_special = "/@£$" +} + +resource "kubernetes_deployment" "linkwarden" { + metadata { + name = "linkwarden" + namespace = kubernetes_namespace.linkwarden.metadata[0].name + labels = { + app = "linkwarden" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "linkwarden" + } + } + template { + metadata { + labels = { + app = "linkwarden" + } + annotations = { + "diun.enable" = "false" + "diun.include_tags" = "latest" + } + } + spec { + container { + image = "ghcr.io/linkwarden/linkwarden:latest" + name = "linkwarden" + + port { + container_port = 3000 + } + env { + name = "DATABASE_URL" + value = "postgresql://linkwarden:${var.linkwarden_postgresql_password}@postgresql.dbaas.svc.cluster.local:5432/linkwarden" + } + env { + name = "NEXT_PUBLIC_AUTHENTIK_ENABLED" + value = "true" + } + env { + name = "NEXTAUTH_SECRET" + value = random_string.secret.result + } + env { + name = "NEXTAUTH_URL" + value = "https://linkwarden.viktorbarzin.me/api/v1/auth" + } + env { + name = "AUTHENTIK_ISSUER" + value = "https://authentik.viktorbarzin.me/application/o/linkwarden" + } + env { + name = "AUTHENTIK_CLIENT_ID" + value = var.linkwarden_authentik_client_id + } + env { + name = "AUTHENTIK_CLIENT_SECRET" + value = var.linkwarden_authentik_client_secret + } + } + } + } + } +} +resource "kubernetes_service" "linkwarden" { + metadata { + name = "linkwarden" + namespace = kubernetes_namespace.linkwarden.metadata[0].name + labels = { + app = "linkwarden" + } + } + + spec { + selector = { + app = "linkwarden" + } + port { + name = "linkwarden" + port = 80 + target_port = 3000 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.linkwarden.metadata[0].name + name = "linkwarden" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/linkwarden/module/main.tf b/stacks/linkwarden/module/main.tf deleted file mode 100644 index d50a6a9a..00000000 --- a/stacks/linkwarden/module/main.tf +++ /dev/null @@ -1,124 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "postgresql_password" {} -variable "authentik_client_id" {} -variable "authentik_client_secret" {} - -resource "kubernetes_namespace" "linkwarden" { - metadata { - name = "linkwarden" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.linkwarden.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "random_string" "secret" { - length = 32 - special = true - override_special = "/@£$" -} - -resource "kubernetes_deployment" "linkwarden" { - metadata { - name = "linkwarden" - namespace = kubernetes_namespace.linkwarden.metadata[0].name - labels = { - app = "linkwarden" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "linkwarden" - } - } - template { - metadata { - labels = { - app = "linkwarden" - } - annotations = { - "diun.enable" = "false" - "diun.include_tags" = "latest" - } - } - spec { - container { - image = "ghcr.io/linkwarden/linkwarden:latest" - name = "linkwarden" - - port { - container_port = 3000 - } - env { - name = "DATABASE_URL" - value = "postgresql://linkwarden:${var.postgresql_password}@postgresql.dbaas.svc.cluster.local:5432/linkwarden" - } - env { - name = "NEXT_PUBLIC_AUTHENTIK_ENABLED" - value = "true" - } - env { - name = "NEXTAUTH_SECRET" - value = random_string.secret.result - } - env { - name = "NEXTAUTH_URL" - value = "https://linkwarden.viktorbarzin.me/api/v1/auth" - } - env { - name = "AUTHENTIK_ISSUER" - value = "https://authentik.viktorbarzin.me/application/o/linkwarden" - } - env { - name = "AUTHENTIK_CLIENT_ID" - value = var.authentik_client_id - } - env { - name = "AUTHENTIK_CLIENT_SECRET" - value = var.authentik_client_secret - } - } - } - } - } -} -resource "kubernetes_service" "linkwarden" { - metadata { - name = "linkwarden" - namespace = kubernetes_namespace.linkwarden.metadata[0].name - labels = { - app = "linkwarden" - } - } - - spec { - selector = { - app = "linkwarden" - } - port { - name = "linkwarden" - port = 80 - target_port = 3000 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.linkwarden.metadata[0].name - name = "linkwarden" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/matrix/main.tf b/stacks/matrix/main.tf index f37ed763..8e3c4087 100644 --- a/stacks/matrix/main.tf +++ b/stacks/matrix/main.tf @@ -10,8 +10,100 @@ locals { } } -module "matrix" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "matrix" { + metadata { + name = "matrix" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.matrix.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "matrix" { + metadata { + name = "matrix" + namespace = kubernetes_namespace.matrix.metadata[0].name + labels = { + app = "matrix" + tier = local.tiers.aux + } + } + spec { + replicas = 0 + selector { + match_labels = { + app = "matrix" + } + } + template { + metadata { + labels = { + app = "matrix" + } + } + spec { + container { + image = "matrixdotorg/synapse:latest" + name = "matrix" + port { + container_port = 8008 + } + env { + name = "SYNAPSE_SERVER_NAME" + value = "matrix.viktorbarzin.me" + } + env { + name = "SYNAPSE_REPORT_STATS" + value = "yes" + } + volume_mount { + name = "data" + mount_path = "/data" + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/matrix" + } + } + } + } + } +} + +resource "kubernetes_service" "matrix" { + metadata { + name = "matrix" + namespace = kubernetes_namespace.matrix.metadata[0].name + labels = { + "app" = "matrix" + } + } + + spec { + selector = { + app = "matrix" + } + port { + name = "http" + port = "80" + target_port = "8008" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.matrix.metadata[0].name + name = "matrix" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/matrix/module/main.tf b/stacks/matrix/module/main.tf deleted file mode 100644 index 710cdcab..00000000 --- a/stacks/matrix/module/main.tf +++ /dev/null @@ -1,100 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "matrix" { - metadata { - name = "matrix" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.matrix.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "matrix" { - metadata { - name = "matrix" - namespace = kubernetes_namespace.matrix.metadata[0].name - labels = { - app = "matrix" - tier = var.tier - } - } - spec { - replicas = 0 - selector { - match_labels = { - app = "matrix" - } - } - template { - metadata { - labels = { - app = "matrix" - } - } - spec { - container { - image = "matrixdotorg/synapse:latest" - name = "matrix" - port { - container_port = 8008 - } - env { - name = "SYNAPSE_SERVER_NAME" - value = "matrix.viktorbarzin.me" - } - env { - name = "SYNAPSE_REPORT_STATS" - value = "yes" - } - volume_mount { - name = "data" - mount_path = "/data" - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/matrix" - } - } - } - } - } -} - -resource "kubernetes_service" "matrix" { - metadata { - name = "matrix" - namespace = kubernetes_namespace.matrix.metadata[0].name - labels = { - "app" = "matrix" - } - } - - spec { - selector = { - app = "matrix" - } - port { - name = "http" - port = "80" - target_port = "8008" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.matrix.metadata[0].name - name = "matrix" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/meshcentral/main.tf b/stacks/meshcentral/main.tf index 0a8b0b37..6ccd8bc9 100644 --- a/stacks/meshcentral/main.tf +++ b/stacks/meshcentral/main.tf @@ -10,8 +10,150 @@ locals { } } -module "meshcentral" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "meshcentral" { + metadata { + name = "meshcentral" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.meshcentral.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "meshcentral" { + metadata { + name = "meshcentral" + namespace = kubernetes_namespace.meshcentral.metadata[0].name + labels = { + app = "meshcentral" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + "meshcentral.enable" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "meshcentral" + } + } + template { + metadata { + labels = { + app = "meshcentral" + } + annotations = { + "diun.enable" = "true" + "diun.include_tags" = "^\\d+(?:\\.\\d+)?(?:\\.\\d+)?$,latest" + } + } + spec { + + container { + image = "typhonragewind/meshcentral:latest" + name = "meshcentral" + port { + name = "http" + container_port = 443 + } + env { + name = "TZ" + value = "Europe/Sofia" + } + env { + name = "HOSTNAME" + value = "meshcentral.viktorbarzin.me" + } + env { + name = "REVERSE_PROXY" + value = "true" + } + env { + name = "ALLOW_NEW_ACCOUNTS" + value = "true" + } + env { + name = "WEBRTC" + value = "false" + } + + volume_mount { + name = "data" + mount_path = "/opt/meshcentral/meshcentral-data" + } + volume_mount { + name = "files" + mount_path = "/opt/meshcentral/meshcentral-files" + } + volume_mount { + name = "backups" + mount_path = "/opt/meshcentral/meshcentral-backups" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/meshcentral/meshcentral-data" + server = "10.0.10.15" + } + } + volume { + name = "files" + nfs { + path = "/mnt/main/meshcentral/meshcentral-files" + server = "10.0.10.15" + } + } + volume { + name = "backups" + nfs { + path = "/mnt/main/meshcentral/meshcentral-backups" + server = "10.0.10.15" + } + } + } + } + } +} + + +resource "kubernetes_service" "meshcentral" { + metadata { + name = "meshcentral" + namespace = kubernetes_namespace.meshcentral.metadata[0].name + labels = { + "app" = "meshcentral" + } + } + + spec { + selector = { + app = "meshcentral" + } + port { + name = "http" + port = 443 + protocol = "TCP" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.meshcentral.metadata[0].name + name = "meshcentral" + tls_secret_name = var.tls_secret_name + port = 443 } diff --git a/stacks/meshcentral/module/main.tf b/stacks/meshcentral/module/main.tf deleted file mode 100644 index e20bcf8f..00000000 --- a/stacks/meshcentral/module/main.tf +++ /dev/null @@ -1,150 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "meshcentral" { - metadata { - name = "meshcentral" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.meshcentral.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "meshcentral" { - metadata { - name = "meshcentral" - namespace = kubernetes_namespace.meshcentral.metadata[0].name - labels = { - app = "meshcentral" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - "meshcentral.enable" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "meshcentral" - } - } - template { - metadata { - labels = { - app = "meshcentral" - } - annotations = { - "diun.enable" = "true" - "diun.include_tags" = "^\\d+(?:\\.\\d+)?(?:\\.\\d+)?$,latest" - } - } - spec { - - container { - image = "typhonragewind/meshcentral:latest" - name = "meshcentral" - port { - name = "http" - container_port = 443 - } - env { - name = "TZ" - value = "Europe/Sofia" - } - env { - name = "HOSTNAME" - value = "meshcentral.viktorbarzin.me" - } - env { - name = "REVERSE_PROXY" - value = "true" - } - env { - name = "ALLOW_NEW_ACCOUNTS" - value = "true" - } - env { - name = "WEBRTC" - value = "false" - } - - volume_mount { - name = "data" - mount_path = "/opt/meshcentral/meshcentral-data" - } - volume_mount { - name = "files" - mount_path = "/opt/meshcentral/meshcentral-files" - } - volume_mount { - name = "backups" - mount_path = "/opt/meshcentral/meshcentral-backups" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/meshcentral/meshcentral-data" - server = "10.0.10.15" - } - } - volume { - name = "files" - nfs { - path = "/mnt/main/meshcentral/meshcentral-files" - server = "10.0.10.15" - } - } - volume { - name = "backups" - nfs { - path = "/mnt/main/meshcentral/meshcentral-backups" - server = "10.0.10.15" - } - } - } - } - } -} - - -resource "kubernetes_service" "meshcentral" { - metadata { - name = "meshcentral" - namespace = kubernetes_namespace.meshcentral.metadata[0].name - labels = { - "app" = "meshcentral" - } - } - - spec { - selector = { - app = "meshcentral" - } - port { - name = "http" - port = 443 - protocol = "TCP" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.meshcentral.metadata[0].name - name = "meshcentral" - tls_secret_name = var.tls_secret_name - port = 443 -} diff --git a/stacks/n8n/main.tf b/stacks/n8n/main.tf index aab58841..9410d432 100644 --- a/stacks/n8n/main.tf +++ b/stacks/n8n/main.tf @@ -11,9 +11,139 @@ locals { } } -module "n8n" { - source = "./module" - tls_secret_name = var.tls_secret_name - postgresql_password = var.n8n_postgresql_password - tier = local.tiers.aux +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.n8n.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_namespace" "n8n" { + metadata { + name = "n8n" + labels = { + tier = local.tiers.aux + } + } +} + +resource "kubernetes_deployment" "n8n" { + metadata { + name = "n8n" + namespace = kubernetes_namespace.n8n.metadata[0].name + labels = { + app = "n8n" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "n8n" + } + } + template { + metadata { + labels = { + app = "n8n" + } + } + spec { + container { + name = "n8n" + image = "docker.n8n.io/n8nio/n8n" + env { + name = "DB_TYPE" + value = "postgresdb" + } + env { + name = "DB_POSTGRESDB_DATABASE" + value = "n8n" + } + env { + name = "DB_POSTGRESDB_HOST" + value = "postgresql.dbaas" + } + env { + name = "DB_POSTGRESDB_PORT" + value = "5432" + } + env { + name = "DB_POSTGRESDB_USER" + value = "n8n" + } + env { + name = "DB_POSTGRESDB_PASSWORD" + value = var.n8n_postgresql_password + } + env { + name = "GENERIC_TIMEZONE" + value = "Europe/Sofia" + } + env { + name = "TZ" + value = "Europe/Sofia" + } + env { + name = "DOMAIN_NAME" + value = "viktorbarzin.me" + } + env { + name = "DOMAIN_NAME" + value = "n8n" + } + env { + name = "N8N_EDITOR_BASE_URL" + value = "https://n8n.viktorbarzin.me" + } + env { + name = "WEBHOOK_URL" + value = "https://n8n.viktorbarzin.me" + } + volume_mount { + name = "data" + mount_path = "/home/node/.n8n" + } + port { + name = "http" + container_port = 5678 + protocol = "TCP" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/n8n" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "n8n" { + metadata { + name = "n8n" + namespace = kubernetes_namespace.n8n.metadata[0].name + labels = { + "app" = "n8n" + } + } + + spec { + selector = { + app = "n8n" + } + port { + port = "80" + target_port = "5678" + } + } +} +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.n8n.metadata[0].name + name = "n8n" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/n8n/module/main.tf b/stacks/n8n/module/main.tf deleted file mode 100644 index d136308e..00000000 --- a/stacks/n8n/module/main.tf +++ /dev/null @@ -1,140 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "postgresql_password" {} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.n8n.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_namespace" "n8n" { - metadata { - name = "n8n" - labels = { - tier = var.tier - } - } -} - -resource "kubernetes_deployment" "n8n" { - metadata { - name = "n8n" - namespace = kubernetes_namespace.n8n.metadata[0].name - labels = { - app = "n8n" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "n8n" - } - } - template { - metadata { - labels = { - app = "n8n" - } - } - spec { - container { - name = "n8n" - image = "docker.n8n.io/n8nio/n8n" - env { - name = "DB_TYPE" - value = "postgresdb" - } - env { - name = "DB_POSTGRESDB_DATABASE" - value = "n8n" - } - env { - name = "DB_POSTGRESDB_HOST" - value = "postgresql.dbaas" - } - env { - name = "DB_POSTGRESDB_PORT" - value = "5432" - } - env { - name = "DB_POSTGRESDB_USER" - value = "n8n" - } - env { - name = "DB_POSTGRESDB_PASSWORD" - value = var.postgresql_password - } - env { - name = "GENERIC_TIMEZONE" - value = "Europe/Sofia" - } - env { - name = "TZ" - value = "Europe/Sofia" - } - env { - name = "DOMAIN_NAME" - value = "viktorbarzin.me" - } - env { - name = "DOMAIN_NAME" - value = "n8n" - } - env { - name = "N8N_EDITOR_BASE_URL" - value = "https://n8n.viktorbarzin.me" - } - env { - name = "WEBHOOK_URL" - value = "https://n8n.viktorbarzin.me" - } - volume_mount { - name = "data" - mount_path = "/home/node/.n8n" - } - port { - name = "http" - container_port = 5678 - protocol = "TCP" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/n8n" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "n8n" { - metadata { - name = "n8n" - namespace = kubernetes_namespace.n8n.metadata[0].name - labels = { - "app" = "n8n" - } - } - - spec { - selector = { - app = "n8n" - } - port { - port = "80" - target_port = "5678" - } - } -} -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.n8n.metadata[0].name - name = "n8n" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/navidrome/main.tf b/stacks/navidrome/main.tf index abb1c8d9..5927ed12 100644 --- a/stacks/navidrome/main.tf +++ b/stacks/navidrome/main.tf @@ -10,8 +10,120 @@ locals { } } -module "navidrome" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "navidrome" { + metadata { + name = "navidrome" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.navidrome.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "navidrome" { + metadata { + name = "navidrome" + namespace = kubernetes_namespace.navidrome.metadata[0].name + labels = { + app = "navidrome" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "navidrome" + } + } + template { + metadata { + labels = { + app = "navidrome" + } + } + spec { + container { + name = "navidrome" + image = "deluan/navidrome:latest" + volume_mount { + name = "data" + mount_path = "/data" + } + volume_mount { + name = "music" + mount_path = "/music" + read_only = true + } + volume_mount { + name = "lidarr" + mount_path = "/lidarr" + read_only = true + } + port { + name = "http" + container_port = 4533 + protocol = "TCP" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/navidrome" + server = "10.0.10.15" + } + } + volume { + name = "music" + nfs { + path = "/volume1/music" + server = "192.168.1.13" + } + } + volume { + name = "lidarr" + nfs { + path = "/mnt/main/servarr/lidarr" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "navidrome" { + metadata { + name = "navidrome" + namespace = kubernetes_namespace.navidrome.metadata[0].name + labels = { + "app" = "navidrome" + } + } + + spec { + selector = { + app = "navidrome" + } + port { + port = "80" + target_port = "4533" + } + } +} +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.navidrome.metadata[0].name + name = "navidrome" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "8a3844ff75ba" } diff --git a/stacks/navidrome/module/main.tf b/stacks/navidrome/module/main.tf deleted file mode 100644 index 8e9cf3e7..00000000 --- a/stacks/navidrome/module/main.tf +++ /dev/null @@ -1,120 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "navidrome" { - metadata { - name = "navidrome" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.navidrome.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "navidrome" { - metadata { - name = "navidrome" - namespace = kubernetes_namespace.navidrome.metadata[0].name - labels = { - app = "navidrome" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "navidrome" - } - } - template { - metadata { - labels = { - app = "navidrome" - } - } - spec { - container { - name = "navidrome" - image = "deluan/navidrome:latest" - volume_mount { - name = "data" - mount_path = "/data" - } - volume_mount { - name = "music" - mount_path = "/music" - read_only = true - } - volume_mount { - name = "lidarr" - mount_path = "/lidarr" - read_only = true - } - port { - name = "http" - container_port = 4533 - protocol = "TCP" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/navidrome" - server = "10.0.10.15" - } - } - volume { - name = "music" - nfs { - path = "/volume1/music" - server = "192.168.1.13" - } - } - volume { - name = "lidarr" - nfs { - path = "/mnt/main/servarr/lidarr" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "navidrome" { - metadata { - name = "navidrome" - namespace = kubernetes_namespace.navidrome.metadata[0].name - labels = { - "app" = "navidrome" - } - } - - spec { - selector = { - app = "navidrome" - } - port { - port = "80" - target_port = "4533" - } - } -} -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.navidrome.metadata[0].name - name = "navidrome" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "8a3844ff75ba" -} diff --git a/stacks/netbox/main.tf b/stacks/netbox/main.tf index 22a0fea1..1796fdd8 100644 --- a/stacks/netbox/main.tf +++ b/stacks/netbox/main.tf @@ -10,8 +10,175 @@ locals { } } -module "netbox" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "netbox" { + metadata { + name = "netbox" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.netbox.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "random_string" "random" { + length = 50 + lower = true +} +resource "random_string" "api_token_pepper" { + length = 50 + lower = true +} + +resource "kubernetes_deployment" "netbox" { + metadata { + name = "netbox" + namespace = kubernetes_namespace.netbox.metadata[0].name + labels = { + app = "netbox" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "netbox" + } + } + template { + metadata { + annotations = { + "diun.enable" = "true" + } + labels = { + app = "netbox" + } + } + spec { + container { + image = "netboxcommunity/netbox:v4.5.0-beta1" + name = "netbox" + env { + name = "DB_USER" + value = "netbox" + } + env { + name = "DB_PASSWORD" + value = "ttPSBjF9oPLb49XZst3sGF" + } + env { + name = "DB_HOST" + value = "postgresql.dbaas.svc.cluster.local" + } + env { + name = "DB_NAME" + value = "netbox" + } + env { + name = "DB_WAIT_DEBUG" + value = "1" + } + env { + name = "SECRET_KEY" + value = random_string.random.result + } + env { + name = "API_TOKEN_PEPPERS" + value = random_string.api_token_pepper.result + } + env { + name = "REDIS_HOST" + value = "redis.redis" + } + env { + name = "ALLOWED_HOST" + value = "netbox.viktorbarzin.me" + } + env { + name = "SUPERUSER_EMAIL" + value = "me@viktorbarzin.me" + } + env { + name = "SUPERUSER_PASSWORD" + value = "ttPSBjF9oPLb49XZst3sGFasdf" + } + env { + name = "REMOTE_AUTH_ENABLED" + value = "True" + } + env { + name = "REMOTE_AUTH_AUTO_CREATE_USER" + value = "True" + } + + env { + name = "PUID" + value = 1000 + } + env { + name = "PGID" + value = 1000 + } + env { + name = "TZ" + value = "Europe/Sofia" + } + + port { + container_port = 8080 + } + # volume_mount { + # name = "data" + # mount_path = "/books" + # } + } + # volume { + # name = "data" + # nfs { + # path = "/mnt/main/netbox" + # server = "10.0.10.15" + # } + # } + } + } + } +} +resource "kubernetes_service" "netbox" { + metadata { + name = "netbox" + namespace = kubernetes_namespace.netbox.metadata[0].name + labels = { + "app" = "netbox" + } + } + + spec { + selector = { + app = "netbox" + } + port { + name = "http" + target_port = 8080 + port = 80 + protocol = "TCP" + } + } +} +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.netbox.metadata[0].name + name = "netbox" + tls_secret_name = var.tls_secret_name + protected = true } diff --git a/stacks/netbox/module/main.tf b/stacks/netbox/module/main.tf deleted file mode 100644 index 0d633d1a..00000000 --- a/stacks/netbox/module/main.tf +++ /dev/null @@ -1,175 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "netbox" { - metadata { - name = "netbox" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.netbox.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "random_string" "random" { - length = 50 - lower = true -} -resource "random_string" "api_token_pepper" { - length = 50 - lower = true -} - -resource "kubernetes_deployment" "netbox" { - metadata { - name = "netbox" - namespace = kubernetes_namespace.netbox.metadata[0].name - labels = { - app = "netbox" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "netbox" - } - } - template { - metadata { - annotations = { - "diun.enable" = "true" - } - labels = { - app = "netbox" - } - } - spec { - container { - image = "netboxcommunity/netbox:v4.5.0-beta1" - name = "netbox" - env { - name = "DB_USER" - value = "netbox" - } - env { - name = "DB_PASSWORD" - value = "ttPSBjF9oPLb49XZst3sGF" - } - env { - name = "DB_HOST" - value = "postgresql.dbaas.svc.cluster.local" - } - env { - name = "DB_NAME" - value = "netbox" - } - env { - name = "DB_WAIT_DEBUG" - value = "1" - } - env { - name = "SECRET_KEY" - value = random_string.random.result - } - env { - name = "API_TOKEN_PEPPERS" - value = random_string.api_token_pepper.result - } - env { - name = "REDIS_HOST" - value = "redis.redis" - } - env { - name = "ALLOWED_HOST" - value = "netbox.viktorbarzin.me" - } - env { - name = "SUPERUSER_EMAIL" - value = "me@viktorbarzin.me" - } - env { - name = "SUPERUSER_PASSWORD" - value = "ttPSBjF9oPLb49XZst3sGFasdf" - } - env { - name = "REMOTE_AUTH_ENABLED" - value = "True" - } - env { - name = "REMOTE_AUTH_AUTO_CREATE_USER" - value = "True" - } - - env { - name = "PUID" - value = 1000 - } - env { - name = "PGID" - value = 1000 - } - env { - name = "TZ" - value = "Europe/Sofia" - } - - port { - container_port = 8080 - } - # volume_mount { - # name = "data" - # mount_path = "/books" - # } - } - # volume { - # name = "data" - # nfs { - # path = "/mnt/main/netbox" - # server = "10.0.10.15" - # } - # } - } - } - } -} -resource "kubernetes_service" "netbox" { - metadata { - name = "netbox" - namespace = kubernetes_namespace.netbox.metadata[0].name - labels = { - "app" = "netbox" - } - } - - spec { - selector = { - app = "netbox" - } - port { - name = "http" - target_port = 8080 - port = 80 - protocol = "TCP" - } - } -} -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.netbox.metadata[0].name - name = "netbox" - tls_secret_name = var.tls_secret_name - protected = true -} diff --git a/stacks/networking-toolbox/main.tf b/stacks/networking-toolbox/main.tf index de605acd..c4744cc3 100644 --- a/stacks/networking-toolbox/main.tf +++ b/stacks/networking-toolbox/main.tf @@ -10,8 +10,83 @@ locals { } } -module "networking-toolbox" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "networking-toolbox" { + metadata { + name = "networking-toolbox" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.networking-toolbox.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "networking-toolbox" { + metadata { + name = "networking-toolbox" + namespace = kubernetes_namespace.networking-toolbox.metadata[0].name + labels = { + app = "networking-toolbox" + tier = local.tiers.aux + } + } + spec { + replicas = 3 + selector { + match_labels = { + app = "networking-toolbox" + } + } + template { + metadata { + labels = { + app = "networking-toolbox" + } + } + spec { + container { + image = "lissy93/networking-toolbox" + name = "networking-toolbox" + port { + container_port = 3000 + } + } + } + } + } +} + +resource "kubernetes_service" "networking-toolbox" { + metadata { + name = "networking-toolbox" + namespace = kubernetes_namespace.networking-toolbox.metadata[0].name + labels = { + "app" = "networking-toolbox" + } + } + + spec { + selector = { + app = "networking-toolbox" + } + port { + name = "http" + port = "80" + target_port = "3000" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.networking-toolbox.metadata[0].name + name = "networking-toolbox" + tls_secret_name = var.tls_secret_name + protected = true + rybbit_site_id = "50e38577e41c" } diff --git a/stacks/networking-toolbox/module/main.tf b/stacks/networking-toolbox/module/main.tf deleted file mode 100644 index 171c1d99..00000000 --- a/stacks/networking-toolbox/module/main.tf +++ /dev/null @@ -1,83 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "networking-toolbox" { - metadata { - name = "networking-toolbox" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.networking-toolbox.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "networking-toolbox" { - metadata { - name = "networking-toolbox" - namespace = kubernetes_namespace.networking-toolbox.metadata[0].name - labels = { - app = "networking-toolbox" - tier = var.tier - } - } - spec { - replicas = 3 - selector { - match_labels = { - app = "networking-toolbox" - } - } - template { - metadata { - labels = { - app = "networking-toolbox" - } - } - spec { - container { - image = "lissy93/networking-toolbox" - name = "networking-toolbox" - port { - container_port = 3000 - } - } - } - } - } -} - -resource "kubernetes_service" "networking-toolbox" { - metadata { - name = "networking-toolbox" - namespace = kubernetes_namespace.networking-toolbox.metadata[0].name - labels = { - "app" = "networking-toolbox" - } - } - - spec { - selector = { - app = "networking-toolbox" - } - port { - name = "http" - port = "80" - target_port = "3000" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.networking-toolbox.metadata[0].name - name = "networking-toolbox" - tls_secret_name = var.tls_secret_name - protected = true - rybbit_site_id = "50e38577e41c" -} diff --git a/stacks/nextcloud/module/chart_values.yaml b/stacks/nextcloud/chart_values.yaml similarity index 100% rename from stacks/nextcloud/module/chart_values.yaml rename to stacks/nextcloud/chart_values.yaml diff --git a/stacks/nextcloud/main.tf b/stacks/nextcloud/main.tf index dc3ca20e..b8a07e4e 100644 --- a/stacks/nextcloud/main.tf +++ b/stacks/nextcloud/main.tf @@ -11,9 +11,316 @@ locals { } } -module "nextcloud" { - source = "./module" - tls_secret_name = var.tls_secret_name - db_password = var.nextcloud_db_password - tier = local.tiers.edge +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.nextcloud.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_namespace" "nextcloud" { + metadata { + name = "nextcloud" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.edge + } + } +} + +resource "helm_release" "nextcloud" { + namespace = kubernetes_namespace.nextcloud.metadata[0].name + name = "nextcloud" + + repository = "https://nextcloud.github.io/helm/" + chart = "nextcloud" + atomic = true + version = "8.8.1" + + values = [templatefile("${path.module}/chart_values.yaml", { tls_secret_name = var.tls_secret_name, db_password = var.nextcloud_db_password })] + timeout = 6000 +} + +# resource "kubernetes_config_map" "config" { +# metadata { +# name = "config" +# namespace = kubernetes_namespace.nextcloud.metadata[0].name + +# annotations = { +# "reloader.stakater.com/match" = "true" +# } +# } + +# data = { +# "conf.yml" = file("${path.module}/conf.yml") +# } +# } + +resource "kubernetes_deployment" "whiteboard" { + metadata { + name = "whiteboard" + namespace = kubernetes_namespace.nextcloud.metadata[0].name + labels = { + app = "whiteboard" + tier = local.tiers.edge + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "whiteboard" + } + } + template { + metadata { + labels = { + app = "whiteboard" + } + } + spec { + priority_class_name = "tier-3-edge" + container { + image = "ghcr.io/nextcloud-releases/whiteboard:release" + name = "whiteboard" + + port { + container_port = 3002 + } + env { + name = "NEXTCLOUD_URL" + value = "http://nextcloud:8080" + } + env { + name = "JWT_SECRET_KEY" + value = var.nextcloud_db_password # anything secret is fine + } + } + } + } + } +} + +resource "kubernetes_service" "whiteboard" { + metadata { + name = "whiteboard" + namespace = kubernetes_namespace.nextcloud.metadata[0].name + labels = { + app = "whiteboard" + } + } + + spec { + selector = { + app = "whiteboard" + } + port { + name = "http" + port = 80 + target_port = 3002 + } + } +} + +resource "kubernetes_persistent_volume" "nextcloud-data-pv" { + metadata { + name = "nextcloud-data-pv" + } + spec { + capacity = { + "storage" = "100Gi" + } + access_modes = ["ReadWriteOnce"] + persistent_volume_source { + nfs { + path = "/mnt/main/nextcloud" + server = "10.0.10.15" + } + } + } +} + +resource "kubernetes_persistent_volume_claim" "nextcloud-data-pvc" { + metadata { + name = "nextcloud-data-pvc" + namespace = kubernetes_namespace.nextcloud.metadata[0].name + } + spec { + access_modes = ["ReadWriteOnce"] + resources { + requests = { + "storage" = "100Gi" + } + } + volume_name = "nextcloud-data-pv" + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.nextcloud.metadata[0].name + name = "nextcloud" + tls_secret_name = var.tls_secret_name + port = 8080 + rybbit_site_id = "5a3bfe59a3fe" +} + +module "whiteboard_ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.nextcloud.metadata[0].name + name = "whiteboard" + tls_secret_name = var.tls_secret_name + port = 80 +} + +resource "kubernetes_config_map" "backup-script" { + metadata { + name = "nextcloud-backup-script" + namespace = kubernetes_namespace.nextcloud.metadata[0].name + } + + data = { + "backup.sh" = <<-EOF + #!/bin/bash + set -e + + BACKUP_DIR="/backup" + DATA_DIR="/nextcloud-data" + DATE=$(date +%Y%m%d_%H%M%S) + BACKUP_PATH="$BACKUP_DIR/$DATE" + + echo "Starting Nextcloud backup at $(date)" + + # Note: Maintenance mode is skipped because occ is not available in the NFS mount. + # For a proper backup with maintenance mode, exec into the nextcloud pod: + # kubectl exec -n nextcloud deployment/nextcloud -- php occ maintenance:mode --on + + # Create backup directory + mkdir -p "$BACKUP_PATH" + + # Backup everything (config, data, custom_apps, themes, etc.) + echo "Backing up Nextcloud installation..." + rsync -a "$DATA_DIR/" "$BACKUP_PATH/" + + # Keep only last 7 backups + echo "Cleaning old backups..." + cd "$BACKUP_DIR" + ls -dt */ | tail -n +8 | xargs -r rm -rf + + echo "Backup completed at $(date)" + echo "Backup stored at: $BACKUP_PATH" + EOF + + "restore.sh" = <<-EOF + #!/bin/bash + # Restore script - run manually when needed + # Usage: ./restore.sh + # Example: ./restore.sh 20250117_030000 + # + # Before restoring, enable maintenance mode: + # kubectl exec -n nextcloud deployment/nextcloud -- php occ maintenance:mode --on + # After restoring, disable it: + # kubectl exec -n nextcloud deployment/nextcloud -- php occ maintenance:mode --off + + set -e + + if [ -z "$1" ]; then + echo "Usage: $0 " + echo "Available backups:" + ls -1 /backup/ + exit 1 + fi + + BACKUP_PATH="/backup/$1" + DATA_DIR="/nextcloud-data" + + if [ ! -d "$BACKUP_PATH" ]; then + echo "Backup not found: $BACKUP_PATH" + exit 1 + fi + + echo "Restoring from $BACKUP_PATH" + + # Restore everything + echo "Restoring Nextcloud installation..." + rsync -a "$BACKUP_PATH/" "$DATA_DIR/" + + echo "Restore completed!" + echo "Remember to run: kubectl exec -n nextcloud deployment/nextcloud -- php occ maintenance:mode --off" + EOF + } +} + +resource "kubernetes_cron_job_v1" "nextcloud-backup" { + metadata { + name = "nextcloud-backup" + namespace = kubernetes_namespace.nextcloud.metadata[0].name + } + + spec { + schedule = "0 3 * * 0" # Sunday at 3 AM + successful_jobs_history_limit = 3 + failed_jobs_history_limit = 3 + concurrency_policy = "Forbid" + + job_template { + metadata {} + spec { + template { + metadata {} + spec { + restart_policy = "OnFailure" + + container { + name = "backup" + image = "alpine:latest" + + command = ["/bin/sh", "-c", "apk add --no-cache rsync bash && /scripts/backup.sh"] + + volume_mount { + name = "nextcloud-data" + mount_path = "/nextcloud-data" + } + + volume_mount { + name = "backup" + mount_path = "/backup" + } + + volume_mount { + name = "scripts" + mount_path = "/scripts" + } + } + + volume { + name = "nextcloud-data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/nextcloud" + } + } + + volume { + name = "backup" + nfs { + server = "10.0.10.15" + path = "/mnt/main/nextcloud-backup" + } + } + + volume { + name = "scripts" + config_map { + name = kubernetes_config_map.backup-script.metadata[0].name + default_mode = "0755" + } + } + } + } + } + } + } } diff --git a/stacks/nextcloud/module/main.tf b/stacks/nextcloud/module/main.tf deleted file mode 100755 index 4b378d81..00000000 --- a/stacks/nextcloud/module/main.tf +++ /dev/null @@ -1,317 +0,0 @@ -variable "tls_secret_name" {} -variable "db_password" {} -variable "tier" { type = string } - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.nextcloud.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_namespace" "nextcloud" { - metadata { - name = "nextcloud" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -resource "helm_release" "nextcloud" { - namespace = kubernetes_namespace.nextcloud.metadata[0].name - name = "nextcloud" - - repository = "https://nextcloud.github.io/helm/" - chart = "nextcloud" - atomic = true - version = "8.8.1" - - values = [templatefile("${path.module}/chart_values.yaml", { tls_secret_name = var.tls_secret_name, db_password = var.db_password })] - timeout = 6000 -} - -# resource "kubernetes_config_map" "config" { -# metadata { -# name = "config" -# namespace = kubernetes_namespace.nextcloud.metadata[0].name - -# annotations = { -# "reloader.stakater.com/match" = "true" -# } -# } - -# data = { -# "conf.yml" = file("${path.module}/conf.yml") -# } -# } - -resource "kubernetes_deployment" "whiteboard" { - metadata { - name = "whiteboard" - namespace = kubernetes_namespace.nextcloud.metadata[0].name - labels = { - app = "whiteboard" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "whiteboard" - } - } - template { - metadata { - labels = { - app = "whiteboard" - } - } - spec { - priority_class_name = "tier-3-edge" - container { - image = "ghcr.io/nextcloud-releases/whiteboard:release" - name = "whiteboard" - - port { - container_port = 3002 - } - env { - name = "NEXTCLOUD_URL" - value = "http://nextcloud:8080" - } - env { - name = "JWT_SECRET_KEY" - value = var.db_password # anything secret is fine - } - } - } - } - } -} - -resource "kubernetes_service" "whiteboard" { - metadata { - name = "whiteboard" - namespace = kubernetes_namespace.nextcloud.metadata[0].name - labels = { - app = "whiteboard" - } - } - - spec { - selector = { - app = "whiteboard" - } - port { - name = "http" - port = 80 - target_port = 3002 - } - } -} - -resource "kubernetes_persistent_volume" "nextcloud-data-pv" { - metadata { - name = "nextcloud-data-pv" - } - spec { - capacity = { - "storage" = "100Gi" - } - access_modes = ["ReadWriteOnce"] - persistent_volume_source { - nfs { - path = "/mnt/main/nextcloud" - server = "10.0.10.15" - } - } - } -} - -resource "kubernetes_persistent_volume_claim" "nextcloud-data-pvc" { - metadata { - name = "nextcloud-data-pvc" - namespace = kubernetes_namespace.nextcloud.metadata[0].name - } - spec { - access_modes = ["ReadWriteOnce"] - resources { - requests = { - "storage" = "100Gi" - } - } - volume_name = "nextcloud-data-pv" - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.nextcloud.metadata[0].name - name = "nextcloud" - tls_secret_name = var.tls_secret_name - port = 8080 - rybbit_site_id = "5a3bfe59a3fe" -} - -module "whiteboard_ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.nextcloud.metadata[0].name - name = "whiteboard" - tls_secret_name = var.tls_secret_name - port = 80 -} - -resource "kubernetes_config_map" "backup-script" { - metadata { - name = "nextcloud-backup-script" - namespace = kubernetes_namespace.nextcloud.metadata[0].name - } - - data = { - "backup.sh" = <<-EOF - #!/bin/bash - set -e - - BACKUP_DIR="/backup" - DATA_DIR="/nextcloud-data" - DATE=$(date +%Y%m%d_%H%M%S) - BACKUP_PATH="$BACKUP_DIR/$DATE" - - echo "Starting Nextcloud backup at $(date)" - - # Note: Maintenance mode is skipped because occ is not available in the NFS mount. - # For a proper backup with maintenance mode, exec into the nextcloud pod: - # kubectl exec -n nextcloud deployment/nextcloud -- php occ maintenance:mode --on - - # Create backup directory - mkdir -p "$BACKUP_PATH" - - # Backup everything (config, data, custom_apps, themes, etc.) - echo "Backing up Nextcloud installation..." - rsync -a "$DATA_DIR/" "$BACKUP_PATH/" - - # Keep only last 7 backups - echo "Cleaning old backups..." - cd "$BACKUP_DIR" - ls -dt */ | tail -n +8 | xargs -r rm -rf - - echo "Backup completed at $(date)" - echo "Backup stored at: $BACKUP_PATH" - EOF - - "restore.sh" = <<-EOF - #!/bin/bash - # Restore script - run manually when needed - # Usage: ./restore.sh - # Example: ./restore.sh 20250117_030000 - # - # Before restoring, enable maintenance mode: - # kubectl exec -n nextcloud deployment/nextcloud -- php occ maintenance:mode --on - # After restoring, disable it: - # kubectl exec -n nextcloud deployment/nextcloud -- php occ maintenance:mode --off - - set -e - - if [ -z "$1" ]; then - echo "Usage: $0 " - echo "Available backups:" - ls -1 /backup/ - exit 1 - fi - - BACKUP_PATH="/backup/$1" - DATA_DIR="/nextcloud-data" - - if [ ! -d "$BACKUP_PATH" ]; then - echo "Backup not found: $BACKUP_PATH" - exit 1 - fi - - echo "Restoring from $BACKUP_PATH" - - # Restore everything - echo "Restoring Nextcloud installation..." - rsync -a "$BACKUP_PATH/" "$DATA_DIR/" - - echo "Restore completed!" - echo "Remember to run: kubectl exec -n nextcloud deployment/nextcloud -- php occ maintenance:mode --off" - EOF - } -} - -resource "kubernetes_cron_job_v1" "nextcloud-backup" { - metadata { - name = "nextcloud-backup" - namespace = kubernetes_namespace.nextcloud.metadata[0].name - } - - spec { - schedule = "0 3 * * 0" # Sunday at 3 AM - successful_jobs_history_limit = 3 - failed_jobs_history_limit = 3 - concurrency_policy = "Forbid" - - job_template { - metadata {} - spec { - template { - metadata {} - spec { - restart_policy = "OnFailure" - - container { - name = "backup" - image = "alpine:latest" - - command = ["/bin/sh", "-c", "apk add --no-cache rsync bash && /scripts/backup.sh"] - - volume_mount { - name = "nextcloud-data" - mount_path = "/nextcloud-data" - } - - volume_mount { - name = "backup" - mount_path = "/backup" - } - - volume_mount { - name = "scripts" - mount_path = "/scripts" - } - } - - volume { - name = "nextcloud-data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/nextcloud" - } - } - - volume { - name = "backup" - nfs { - server = "10.0.10.15" - path = "/mnt/main/nextcloud-backup" - } - } - - volume { - name = "scripts" - config_map { - name = kubernetes_config_map.backup-script.metadata[0].name - default_mode = "0755" - } - } - } - } - } - } - } -} diff --git a/stacks/ntfy/main.tf b/stacks/ntfy/main.tf index 285de7fb..a01ebf84 100644 --- a/stacks/ntfy/main.tf +++ b/stacks/ntfy/main.tf @@ -10,8 +10,133 @@ locals { } } -module "ntfy" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "ntfy" { + metadata { + name = "ntfy" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.ntfy.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "ntfy" { + metadata { + name = "ntfy" + namespace = kubernetes_namespace.ntfy.metadata[0].name + labels = { + app = "ntfy" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "RollingUpdate" + } + selector { + match_labels = { + app = "ntfy" + } + } + template { + metadata { + labels = { + app = "ntfy" + } + } + spec { + container { + image = "binwiederhier/ntfy" + name = "ntfy" + args = ["serve"] + + port { + container_port = 80 + } + env { + name = "NTFY_BASE_URL" + value = "https://ntfy.viktorbarzin.me" + } + env { + name = "NTFY_UPSTREAM_BASE_URL" + # value = "https://ntfy.viktorbarzin.me" + value = "https://ntfy.sh" + } + env { + name = "NTFY_BEHIND_PROXY" + value = true + } + env { + name = "NTFY_ENABLE_LOGIN" + value = true + } + env { + name = "NTFY_AUTH_FILE" + value = "/var/lib/ntfy/user.db" + } + env { + name = "NTFY_AUTH_DEFAULT_ACCESS" + value = "deny-all" + } + env { + name = "NTFY_ENABLE_METRICS" + value = true + } + volume_mount { + name = "data" + mount_path = "/var/lib/ntfy/" + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/ntfy" + } + } + } + } + } +} + +resource "kubernetes_service" "ntfy" { + metadata { + name = "ntfy" + namespace = kubernetes_namespace.ntfy.metadata[0].name + labels = { + "app" = "ntfy" + } + annotations = { + "prometheus.io/scrape" = "true" + "prometheus.io/path" = "/metrics" + "prometheus.io/port" = "80" + } + } + + spec { + selector = { + app = "ntfy" + } + port { + name = "http" + target_port = 80 + port = 80 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.ntfy.metadata[0].name + name = "ntfy" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/ntfy/module/main.tf b/stacks/ntfy/module/main.tf deleted file mode 100644 index 6826ef49..00000000 --- a/stacks/ntfy/module/main.tf +++ /dev/null @@ -1,133 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -resource "kubernetes_namespace" "ntfy" { - metadata { - name = "ntfy" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.ntfy.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "ntfy" { - metadata { - name = "ntfy" - namespace = kubernetes_namespace.ntfy.metadata[0].name - labels = { - app = "ntfy" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "RollingUpdate" - } - selector { - match_labels = { - app = "ntfy" - } - } - template { - metadata { - labels = { - app = "ntfy" - } - } - spec { - container { - image = "binwiederhier/ntfy" - name = "ntfy" - args = ["serve"] - - port { - container_port = 80 - } - env { - name = "NTFY_BASE_URL" - value = "https://ntfy.viktorbarzin.me" - } - env { - name = "NTFY_UPSTREAM_BASE_URL" - # value = "https://ntfy.viktorbarzin.me" - value = "https://ntfy.sh" - } - env { - name = "NTFY_BEHIND_PROXY" - value = true - } - env { - name = "NTFY_ENABLE_LOGIN" - value = true - } - env { - name = "NTFY_AUTH_FILE" - value = "/var/lib/ntfy/user.db" - } - env { - name = "NTFY_AUTH_DEFAULT_ACCESS" - value = "deny-all" - } - env { - name = "NTFY_ENABLE_METRICS" - value = true - } - volume_mount { - name = "data" - mount_path = "/var/lib/ntfy/" - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/ntfy" - } - } - } - } - } -} - -resource "kubernetes_service" "ntfy" { - metadata { - name = "ntfy" - namespace = kubernetes_namespace.ntfy.metadata[0].name - labels = { - "app" = "ntfy" - } - annotations = { - "prometheus.io/scrape" = "true" - "prometheus.io/path" = "/metrics" - "prometheus.io/port" = "80" - } - } - - spec { - selector = { - app = "ntfy" - } - port { - name = "http" - target_port = 80 - port = 80 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.ntfy.metadata[0].name - name = "ntfy" - tls_secret_name = var.tls_secret_name -} - diff --git a/stacks/ollama/main.tf b/stacks/ollama/main.tf index 079ffd3f..b336bab0 100644 --- a/stacks/ollama/main.tf +++ b/stacks/ollama/main.tf @@ -11,9 +11,299 @@ locals { } } -module "ollama" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.gpu - ollama_api_credentials = var.ollama_api_credentials +resource "kubernetes_namespace" "ollama" { + metadata { + name = "ollama" + labels = { + tier = local.tiers.gpu + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.ollama.metadata[0].name + tls_secret_name = var.tls_secret_name +} +resource "kubernetes_persistent_volume_claim" "ollama-pvc" { + metadata { + name = "ollama-pvc" + namespace = kubernetes_namespace.ollama.metadata[0].name + } + + spec { + access_modes = ["ReadWriteOnce"] + resources { + requests = { + storage = "30Gi" + } + } + volume_name = "ollama-pv" + } +} + +resource "kubernetes_persistent_volume" "ollama-pv" { + metadata { + name = "ollama-pv" + } + spec { + capacity = { + "storage" = "30Gi" + } + access_modes = ["ReadWriteOnce"] + persistent_volume_source { + nfs { + path = "/mnt/main/ollama" + server = "10.0.10.15" + } + } + } +} + +# resource "helm_release" "ollama" { +# namespace = kubernetes_namespace.ollama.metadata[0].name +# name = "ollama" + +# repository = "https://otwld.github.io/ollama-helm/" +# chart = "ollama" +# atomic = true + +# values = [templatefile("${path.module}/values.yaml", {})] +# timeout = 2400 +# } + + +resource "kubernetes_deployment" "ollama" { + metadata { + name = "ollama" + namespace = kubernetes_namespace.ollama.metadata[0].name + labels = { + app = "ollama" + tier = local.tiers.gpu + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "ollama" + } + } + template { + metadata { + labels = { + app = "ollama" + } + } + spec { + container { + image = "ollama/ollama:latest" + name = "ollama" + env { + name = "OLLAMA_HOST" + value = "0.0.0.0:11434" + } + env { + name = "PATH" + value = "/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + } + env { + name = "OLLAMA_KEEP_ALIVE" + value = "1h" + } + + port { + container_port = 11434 + } + volume_mount { + name = "ollama-data" + mount_path = "/root/.ollama" + } + resources { + limits = { + "nvidia.com/gpu" = "1" + } + } + } + volume { + name = "ollama-data" + nfs { + # path = "/mnt/main/ollama" + path = "/mnt/ssd/ollama" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "ollama" { + metadata { + name = "ollama" + namespace = kubernetes_namespace.ollama.metadata[0].name + labels = { + app = "ollama" + } + } + + spec { + selector = { + app = "ollama" + } + port { + name = "http" + port = 11434 + } + } +} + +# Allow ollama to be connected to from external apps (internal LAN only) +module "ollama-ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.ollama.metadata[0].name + name = "ollama-server" + service_name = "ollama" + root_domain = "viktorbarzin.lan" + tls_secret_name = var.tls_secret_name + allow_local_access_only = true + ssl_redirect = false + port = 11434 +} + +# Ollama API ingress for external access (basicAuth protected) +locals { + ollama_api_htpasswd = join("\n", [for name, pass in var.ollama_api_credentials : "${name}:${bcrypt(pass, 10)}"]) +} + +resource "kubernetes_secret" "ollama_api_basic_auth" { + count = length(var.ollama_api_credentials) > 0 ? 1 : 0 + metadata { + name = "ollama-api-basic-auth-secret" + namespace = kubernetes_namespace.ollama.metadata[0].name + } + + data = { + auth = local.ollama_api_htpasswd + } + + type = "Opaque" + lifecycle { + ignore_changes = [data] + } +} + +resource "kubernetes_manifest" "ollama_api_basic_auth_middleware" { + count = length(var.ollama_api_credentials) > 0 ? 1 : 0 + manifest = { + apiVersion = "traefik.io/v1alpha1" + kind = "Middleware" + metadata = { + name = "ollama-api-basic-auth" + namespace = kubernetes_namespace.ollama.metadata[0].name + } + spec = { + basicAuth = { + secret = kubernetes_secret.ollama_api_basic_auth[0].metadata[0].name + } + } + } +} + +module "ollama-api-ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.ollama.metadata[0].name + name = "ollama-api" + service_name = "ollama" + root_domain = "viktorbarzin.me" + tls_secret_name = var.tls_secret_name + ssl_redirect = true + port = 11434 + extra_annotations = { + "traefik.ingress.kubernetes.io/router.middlewares" = "ollama-ollama-api-basic-auth@kubernetescrd,traefik-rate-limit@kubernetescrd,traefik-crowdsec@kubernetescrd" + } +} + +# Web UI +resource "kubernetes_deployment" "ollama-ui" { + metadata { + name = "ollama-ui" + namespace = kubernetes_namespace.ollama.metadata[0].name + labels = { + app = "ollama-ui" + tier = local.tiers.gpu + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "ollama-ui" + } + } + template { + metadata { + labels = { + app = "ollama-ui" + } + } + spec { + container { + # image = "ghcr.io/open-webui/open-webui:main" + image = "ghcr.io/open-webui/open-webui:v0.7.2" + name = "ollama-ui" + env { + name = "OLLAMA_BASE_URL" + value = "http://ollama.ollama.svc.cluster.local:11434" + } + + port { + container_port = 8080 + } + volume_mount { + name = "data" + mount_path = "/app/backend/data" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/ollama" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "ollama-ui" { + metadata { + name = "ollama-ui" + namespace = kubernetes_namespace.ollama.metadata[0].name + labels = { + app = "dashy" + } + } + + spec { + selector = { + app = "ollama-ui" + } + port { + name = "http" + port = 80 + target_port = 8080 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.ollama.metadata[0].name + name = "ollama" + service_name = "ollama-ui" + tls_secret_name = var.tls_secret_name + port = 80 + rybbit_site_id = "e73bebea399f" } diff --git a/stacks/ollama/module/main.tf b/stacks/ollama/module/main.tf deleted file mode 100644 index 70a70f42..00000000 --- a/stacks/ollama/module/main.tf +++ /dev/null @@ -1,303 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "ollama_api_credentials" { - type = map(string) - default = {} -} - -resource "kubernetes_namespace" "ollama" { - metadata { - name = "ollama" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.ollama.metadata[0].name - tls_secret_name = var.tls_secret_name -} -resource "kubernetes_persistent_volume_claim" "ollama-pvc" { - metadata { - name = "ollama-pvc" - namespace = kubernetes_namespace.ollama.metadata[0].name - } - - spec { - access_modes = ["ReadWriteOnce"] - resources { - requests = { - storage = "30Gi" - } - } - volume_name = "ollama-pv" - } -} - -resource "kubernetes_persistent_volume" "ollama-pv" { - metadata { - name = "ollama-pv" - } - spec { - capacity = { - "storage" = "30Gi" - } - access_modes = ["ReadWriteOnce"] - persistent_volume_source { - nfs { - path = "/mnt/main/ollama" - server = "10.0.10.15" - } - } - } -} - -# resource "helm_release" "ollama" { -# namespace = kubernetes_namespace.ollama.metadata[0].name -# name = "ollama" - -# repository = "https://otwld.github.io/ollama-helm/" -# chart = "ollama" -# atomic = true - -# values = [templatefile("${path.module}/values.yaml", {})] -# timeout = 2400 -# } - - -resource "kubernetes_deployment" "ollama" { - metadata { - name = "ollama" - namespace = kubernetes_namespace.ollama.metadata[0].name - labels = { - app = "ollama" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "ollama" - } - } - template { - metadata { - labels = { - app = "ollama" - } - } - spec { - container { - image = "ollama/ollama:latest" - name = "ollama" - env { - name = "OLLAMA_HOST" - value = "0.0.0.0:11434" - } - env { - name = "PATH" - value = "/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - } - env { - name = "OLLAMA_KEEP_ALIVE" - value = "1h" - } - - port { - container_port = 11434 - } - volume_mount { - name = "ollama-data" - mount_path = "/root/.ollama" - } - resources { - limits = { - "nvidia.com/gpu" = "1" - } - } - } - volume { - name = "ollama-data" - nfs { - # path = "/mnt/main/ollama" - path = "/mnt/ssd/ollama" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "ollama" { - metadata { - name = "ollama" - namespace = kubernetes_namespace.ollama.metadata[0].name - labels = { - app = "ollama" - } - } - - spec { - selector = { - app = "ollama" - } - port { - name = "http" - port = 11434 - } - } -} - -# Allow ollama to be connected to from external apps (internal LAN only) -module "ollama-ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.ollama.metadata[0].name - name = "ollama-server" - service_name = "ollama" - root_domain = "viktorbarzin.lan" - tls_secret_name = var.tls_secret_name - allow_local_access_only = true - ssl_redirect = false - port = 11434 -} - -# Ollama API ingress for external access (basicAuth protected) -locals { - ollama_api_htpasswd = join("\n", [for name, pass in var.ollama_api_credentials : "${name}:${bcrypt(pass, 10)}"]) -} - -resource "kubernetes_secret" "ollama_api_basic_auth" { - count = length(var.ollama_api_credentials) > 0 ? 1 : 0 - metadata { - name = "ollama-api-basic-auth-secret" - namespace = kubernetes_namespace.ollama.metadata[0].name - } - - data = { - auth = local.ollama_api_htpasswd - } - - type = "Opaque" - lifecycle { - ignore_changes = [data] - } -} - -resource "kubernetes_manifest" "ollama_api_basic_auth_middleware" { - count = length(var.ollama_api_credentials) > 0 ? 1 : 0 - manifest = { - apiVersion = "traefik.io/v1alpha1" - kind = "Middleware" - metadata = { - name = "ollama-api-basic-auth" - namespace = kubernetes_namespace.ollama.metadata[0].name - } - spec = { - basicAuth = { - secret = kubernetes_secret.ollama_api_basic_auth[0].metadata[0].name - } - } - } -} - -module "ollama-api-ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.ollama.metadata[0].name - name = "ollama-api" - service_name = "ollama" - root_domain = "viktorbarzin.me" - tls_secret_name = var.tls_secret_name - ssl_redirect = true - port = 11434 - extra_annotations = { - "traefik.ingress.kubernetes.io/router.middlewares" = "ollama-ollama-api-basic-auth@kubernetescrd,traefik-rate-limit@kubernetescrd,traefik-crowdsec@kubernetescrd" - } -} - -# Web UI -resource "kubernetes_deployment" "ollama-ui" { - metadata { - name = "ollama-ui" - namespace = kubernetes_namespace.ollama.metadata[0].name - labels = { - app = "ollama-ui" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "ollama-ui" - } - } - template { - metadata { - labels = { - app = "ollama-ui" - } - } - spec { - container { - # image = "ghcr.io/open-webui/open-webui:main" - image = "ghcr.io/open-webui/open-webui:v0.7.2" - name = "ollama-ui" - env { - name = "OLLAMA_BASE_URL" - value = "http://ollama.ollama.svc.cluster.local:11434" - } - - port { - container_port = 8080 - } - volume_mount { - name = "data" - mount_path = "/app/backend/data" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/ollama" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "ollama-ui" { - metadata { - name = "ollama-ui" - namespace = kubernetes_namespace.ollama.metadata[0].name - labels = { - app = "dashy" - } - } - - spec { - selector = { - app = "ollama-ui" - } - port { - name = "http" - port = 80 - target_port = 8080 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.ollama.metadata[0].name - name = "ollama" - service_name = "ollama-ui" - tls_secret_name = var.tls_secret_name - port = 80 - rybbit_site_id = "e73bebea399f" -} diff --git a/stacks/ollama/module/values.yaml b/stacks/ollama/values.yaml similarity index 100% rename from stacks/ollama/module/values.yaml rename to stacks/ollama/values.yaml diff --git a/stacks/onlyoffice/main.tf b/stacks/onlyoffice/main.tf index 031b50f0..e9df99e2 100644 --- a/stacks/onlyoffice/main.tf +++ b/stacks/onlyoffice/main.tf @@ -12,10 +12,139 @@ locals { } } -module "onlyoffice" { - source = "./module" - tls_secret_name = var.tls_secret_name - db_password = var.onlyoffice_db_password - jwt_token = var.onlyoffice_jwt_token - tier = local.tiers.edge +resource "kubernetes_namespace" "onlyoffice" { + metadata { + name = "onlyoffice" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.edge + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.onlyoffice.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "onlyoffice-document-server" { + metadata { + name = "onlyoffice-document-server" + namespace = kubernetes_namespace.onlyoffice.metadata[0].name + labels = { + app = "onlyoffice-document-server" + tier = local.tiers.edge + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "onlyoffice-document-server" + } + } + template { + metadata { + labels = { + app = "onlyoffice-document-server" + } + } + spec { + container { + name = "onlyoffice-document-server" + image = "onlyoffice/documentserver:8.2.3" + resources { + requests = { + cpu = "100m" + memory = "512Mi" + } + limits = { + cpu = "2" + memory = "4Gi" + } + } + port { + name = "http" + container_port = 80 + protocol = "TCP" + } + env { + name = "DB_TYPE" + value = "mariadb" + } + env { + name = "DB_HOST" + value = "mysql.dbaas" + } + env { + name = "DB_PORT" + value = 3306 + } + env { + name = "DB_NAME" + value = "onlyoffice" + } + env { + name = "DB_USER" + value = "onlyoffice" + } + env { + name = "DB_PWD" + value = var.onlyoffice_db_password + } + env { + name = "REDIS_SERVER_HOST" + value = "redis.redis" + } + env { + name = "REDIS_SERVER_PORT" + value = 6379 + } + env { + name = "JWT_SECRET" + value = var.onlyoffice_jwt_token + } + + volume_mount { + name = "data" + mount_path = "/var/www/onlyoffice/Data" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/onlyoffice" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "onlyoffice" { + metadata { + name = "onlyoffice-document-server" + namespace = kubernetes_namespace.onlyoffice.metadata[0].name + labels = { + "app" = "onlyoffice-document-server" + } + } + + spec { + selector = { + app = "onlyoffice-document-server" + } + port { + port = "80" + } + } +} +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.onlyoffice.metadata[0].name + name = "onlyoffice" + service_name = "onlyoffice-document-server" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/onlyoffice/module/main.tf b/stacks/onlyoffice/module/main.tf deleted file mode 100644 index a4c99709..00000000 --- a/stacks/onlyoffice/module/main.tf +++ /dev/null @@ -1,141 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "db_password" { type = string } -variable "jwt_token" { type = string } - -resource "kubernetes_namespace" "onlyoffice" { - metadata { - name = "onlyoffice" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.onlyoffice.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "onlyoffice-document-server" { - metadata { - name = "onlyoffice-document-server" - namespace = kubernetes_namespace.onlyoffice.metadata[0].name - labels = { - app = "onlyoffice-document-server" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "onlyoffice-document-server" - } - } - template { - metadata { - labels = { - app = "onlyoffice-document-server" - } - } - spec { - container { - name = "onlyoffice-document-server" - image = "onlyoffice/documentserver:8.2.3" - resources { - requests = { - cpu = "100m" - memory = "512Mi" - } - limits = { - cpu = "2" - memory = "4Gi" - } - } - port { - name = "http" - container_port = 80 - protocol = "TCP" - } - env { - name = "DB_TYPE" - value = "mariadb" - } - env { - name = "DB_HOST" - value = "mysql.dbaas" - } - env { - name = "DB_PORT" - value = 3306 - } - env { - name = "DB_NAME" - value = "onlyoffice" - } - env { - name = "DB_USER" - value = "onlyoffice" - } - env { - name = "DB_PWD" - value = var.db_password - } - env { - name = "REDIS_SERVER_HOST" - value = "redis.redis" - } - env { - name = "REDIS_SERVER_PORT" - value = 6379 - } - env { - name = "JWT_SECRET" - value = var.jwt_token - } - - volume_mount { - name = "data" - mount_path = "/var/www/onlyoffice/Data" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/onlyoffice" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "onlyoffice" { - metadata { - name = "onlyoffice-document-server" - namespace = kubernetes_namespace.onlyoffice.metadata[0].name - labels = { - "app" = "onlyoffice-document-server" - } - } - - spec { - selector = { - app = "onlyoffice-document-server" - } - port { - port = "80" - } - } -} -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.onlyoffice.metadata[0].name - name = "onlyoffice" - service_name = "onlyoffice-document-server" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/openclaw/main.tf b/stacks/openclaw/main.tf index ea75ddb6..609fba1e 100644 --- a/stacks/openclaw/main.tf +++ b/stacks/openclaw/main.tf @@ -16,15 +16,581 @@ locals { } } -module "openclaw" { - source = "./module" - tls_secret_name = var.tls_secret_name - git_crypt_key_base64 = filebase64("${path.root}/../../.git/git-crypt/keys/default") - 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.tiers.aux +resource "kubernetes_namespace" "openclaw" { + metadata { + name = "openclaw" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.openclaw.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_service_account" "openclaw" { + metadata { + name = "openclaw" + namespace = kubernetes_namespace.openclaw.metadata[0].name + } +} + +resource "kubernetes_cluster_role_binding" "openclaw" { + metadata { + name = "openclaw-cluster-admin" + } + subject { + kind = "ServiceAccount" + name = kubernetes_service_account.openclaw.metadata[0].name + namespace = kubernetes_namespace.openclaw.metadata[0].name + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = "cluster-admin" + } +} + +resource "kubernetes_secret" "ssh_key" { + metadata { + name = "ssh-key" + namespace = kubernetes_namespace.openclaw.metadata[0].name + } + data = { + "id_rsa" = var.openclaw_ssh_key + } + type = "generic" +} + +resource "kubernetes_config_map" "git_crypt_key" { + metadata { + name = "git-crypt-key" + namespace = kubernetes_namespace.openclaw.metadata[0].name + } + data = { + "key" = filebase64("${path.root}/../../.git/git-crypt/keys/default") + } +} + +resource "kubernetes_config_map" "openclaw_config" { + metadata { + name = "openclaw-config" + namespace = kubernetes_namespace.openclaw.metadata[0].name + } + data = { + "openclaw.json" = jsonencode({ + gateway = { + bind = "lan" + trustedProxies = ["10.0.0.0/8"] + controlUi = { + dangerouslyDisableDeviceAuth = true + } + } + agents = { + defaults = { + contextTokens = 1000000 + bootstrapMaxChars = 30000 + model = { + primary = "modal/zai-org/GLM-5-FP8" + fallbacks = ["gemini/gemini-2.5-flash", "llama-as-openai/Llama-3.3-70B-Instruct"] + } + models = { + "modal/zai-org/GLM-5-FP8" = { streaming = false } + "gemini/gemini-2.5-flash" = {} + "llama-as-openai/Llama-3.3-70B-Instruct" = {} + "llama-as-openai/Llama-4-Scout-17B-16E-Instruct-FP8" = {} + } + } + } + tools = { + profile = "full" + deny = ["sessions_spawn", "sessions_list", "sessions_history", "sessions_send", "subagents", "browser"] + exec = { + host = "sandbox" + security = "full" + ask = "off" + pathPrepend = ["/tools", "/workspace/infra"] + } + web = { + search = { + enabled = true + provider = "brave" + apiKey = var.brave_api_key + maxResults = 5 + } + fetch = { + enabled = true + maxChars = 50000 + timeoutSeconds = 30 + } + } + } + models = { + mode = "merge" + providers = { + modal = { + baseUrl = "https://api.us-west-2.modal.direct/v1" + api = "openai-completions" + apiKey = var.modal_api_key + models = [ + { id = "zai-org/GLM-5-FP8", name = "GLM-5", reasoning = false, input = ["text"], contextWindow = 128000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, + ] + } + gemini = { + baseUrl = "https://generativelanguage.googleapis.com/v1beta" + api = "google-generative-ai" + apiKey = var.gemini_api_key + models = [ + { id = "gemini-2.5-flash", name = "gemini-2.5-flash", reasoning = true, input = ["text", "image"], contextWindow = 1048576, maxTokens = 65536, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, + ] + } + ollama = { + baseUrl = "http://ollama.ollama.svc.cluster.local:11434/v1" + api = "openai-completions" + apiKey = "ollama" + models = [ + { id = "qwen2.5-coder:14b", name = "qwen2.5-coder:14b", reasoning = false, input = ["text"], contextWindow = 128000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, + { id = "qwen2.5:14b", name = "qwen2.5:14b", reasoning = false, input = ["text"], contextWindow = 128000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, + { id = "deepseek-r1:14b", name = "deepseek-r1:14b", reasoning = true, input = ["text"], contextWindow = 128000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, + ] + } + llama-as-openai = { + baseUrl = "https://api.llama.com/compat/v1" + apiKey = var.llama_api_key + api = "openai-completions" + models = [ + { id = "Llama-3.3-70B-Instruct", name = "Llama-3.3-70B-Instruct", reasoning = false, input = ["text"], contextWindow = 128000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, + { id = "Llama-4-Scout-17B-16E-Instruct-FP8", name = "Llama-4-Scout-17B-16E-Instruct-FP8", reasoning = false, input = ["text"], contextWindow = 200000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, + { id = "Llama-4-Maverick-17B-128E-Instruct-FP8", name = "Llama-4-Maverick-17B-128E-Instruct-FP8", reasoning = false, input = ["text"], contextWindow = 200000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, + ] + } + } + } + }) + } +} + +resource "random_password" "gateway_token" { + length = 32 + special = false +} + +resource "kubernetes_deployment" "openclaw" { + metadata { + name = "openclaw" + namespace = kubernetes_namespace.openclaw.metadata[0].name + labels = { + app = "openclaw" + tier = local.tiers.aux + } + } + spec { + strategy { + type = "Recreate" + } + replicas = 1 + selector { + match_labels = { + app = "openclaw" + } + } + template { + metadata { + labels = { + app = "openclaw" + } + } + spec { + service_account_name = kubernetes_service_account.openclaw.metadata[0].name + + # Init container: Download tools + clone repo + terraform init (parallelized) + init_container { + name = "setup" + image = "alpine:3.20" + command = ["sh", "-c", <<-EOF + set -e + apk add --no-cache curl unzip git-crypt openssh-client git bash + + # Install pip and Python packages for skills + python3 -m ensurepip 2>/dev/null || apk add --no-cache py3-pip + pip3 install --break-system-packages --target=/tools/python-libs requests caldav icalendar uptime-kuma-api + + # Copy OpenClaw config to writable home dir + cp /openclaw-config-src/openclaw.json /openclaw-home/openclaw.json + + # Setup SSH key + mkdir -p /root/.ssh + cp /ssh/id_rsa /root/.ssh/id_rsa + chmod 600 /root/.ssh/id_rsa + ssh-keyscan github.com >> /root/.ssh/known_hosts 2>/dev/null + + # --- Run downloads and clone in parallel --- + # kubectl + (curl -sL --retry 3 --retry-delay 5 "https://dl.k8s.io/release/v1.34.2/bin/linux/amd64/kubectl" -o /tools/kubectl && chmod +x /tools/kubectl) & + PID_KUBECTL=$! + + # terraform + (curl -sL --retry 3 --retry-delay 5 "https://releases.hashicorp.com/terraform/1.14.5/terraform_1.14.5_linux_amd64.zip" -o /tmp/tf.zip && unzip -q /tmp/tf.zip -d /tools && chmod +x /tools/terraform && rm /tmp/tf.zip) & + PID_TF=$! + + # git-crypt (already installed via apk) + cp /usr/bin/git-crypt /tools/git-crypt + + # Clone/pull repo + if [ ! -d /workspace/infra/.git ]; then + git clone git@github.com:ViktorBarzin/infra.git /workspace/infra & + PID_GIT=$! + else + (cd /workspace/infra && git pull --ff-only || true) & + PID_GIT=$! + fi + + # Wait for all parallel tasks + wait $PID_KUBECTL || { echo "kubectl download failed"; exit 1; } + wait $PID_GIT || { echo "git clone/pull failed"; exit 1; } + + # Unlock git-crypt (needs clone done) + cd /workspace/infra + echo "$GIT_CRYPT_KEY" | base64 -d > /tmp/git-crypt-key + git-crypt unlock /tmp/git-crypt-key || true + rm /tmp/git-crypt-key + + # Mark repo as safe for the node user (different UID from init container) + git config --global --add safe.directory /workspace/infra + cp /root/.gitconfig /openclaw-home/.gitconfig 2>/dev/null || true + + # Symlink Claude skills into OpenClaw skills directory + ln -sfn /workspace/infra/.claude/skills /openclaw-home/skills + + # Generate kubeconfig from in-cluster ServiceAccount credentials + SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + SA_CA=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + cat > /openclaw-home/kubeconfig <<-KUBEEOF + apiVersion: v1 + kind: Config + clusters: + - cluster: + certificate-authority-data: $(base64 < "$SA_CA" | tr -d '\n') + server: https://kubernetes.default.svc + name: in-cluster + contexts: + - context: + cluster: in-cluster + user: openclaw + name: in-cluster + current-context: in-cluster + users: + - name: openclaw + user: + token: $SA_TOKEN + KUBEEOF + + # Terraform init (needs terraform + clone done) + wait $PID_TF || { echo "terraform download failed"; exit 1; } + /tools/terraform init + EOF + ] + env { + name = "GIT_CRYPT_KEY" + value_from { + config_map_key_ref { + name = kubernetes_config_map.git_crypt_key.metadata[0].name + key = "key" + } + } + } + volume_mount { + name = "tools" + mount_path = "/tools" + } + volume_mount { + name = "workspace" + mount_path = "/workspace" + } + volume_mount { + name = "ssh-key" + mount_path = "/ssh" + } + volume_mount { + name = "openclaw-home" + mount_path = "/openclaw-home" + } + volume_mount { + name = "openclaw-config" + mount_path = "/openclaw-config-src" + } + } + + # Main container: OpenClaw + container { + name = "openclaw" + image = "ghcr.io/openclaw/openclaw:2026.2.9" + command = ["node", "openclaw.mjs", "gateway", "--allow-unconfigured", "--bind", "lan"] + port { + container_port = 18789 + } + env { + name = "OPENCLAW_GATEWAY_TOKEN" + value = random_password.gateway_token.result + } + env { + name = "PATH" + value = "/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + } + env { + name = "TF_VAR_prod" + value = "true" + } + env { + name = "KUBECONFIG" + value = "/home/node/.openclaw/kubeconfig" + } + env { + name = "GIT_CONFIG_GLOBAL" + value = "/home/node/.openclaw/.gitconfig" + } + env { + name = "GEMINI_API_KEY" + value = var.gemini_api_key + } + # Skill secrets - Home Assistant + env { + name = "HOME_ASSISTANT_URL" + value = "https://ha-london.viktorbarzin.me" + } + env { + name = "HOME_ASSISTANT_TOKEN" + value = var.openclaw_skill_secrets["home_assistant_token"] + } + env { + name = "HOME_ASSISTANT_SOFIA_URL" + value = "https://ha-sofia.viktorbarzin.me" + } + env { + name = "HOME_ASSISTANT_SOFIA_TOKEN" + value = var.openclaw_skill_secrets["home_assistant_sofia_token"] + } + # Skill secrets - Uptime Kuma + env { + name = "UPTIME_KUMA_PASSWORD" + value = var.openclaw_skill_secrets["uptime_kuma_password"] + } + # Skill secrets - Slack + env { + name = "SLACK_WEBHOOK_URL" + value = var.openclaw_skill_secrets["slack_webhook"] + } + # Python packages path for skills + env { + name = "PYTHONPATH" + value = "/tools/python-libs" + } + volume_mount { + name = "tools" + mount_path = "/tools" + } + volume_mount { + name = "workspace" + mount_path = "/workspace" + } + volume_mount { + name = "data" + mount_path = "/data" + } + volume_mount { + name = "ssh-key" + mount_path = "/ssh" + } + volume_mount { + name = "openclaw-home" + mount_path = "/home/node/.openclaw" + } + resources { + limits = { + memory = "4Gi" + } + requests = { + memory = "256Mi" + } + } + } + + volume { + name = "tools" + empty_dir {} + } + volume { + name = "openclaw-home" + empty_dir {} + } + volume { + name = "workspace" + nfs { + server = "10.0.10.15" + path = "/mnt/main/openclaw/workspace" + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/openclaw/data" + } + } + volume { + name = "ssh-key" + secret { + secret_name = kubernetes_secret.ssh_key.metadata[0].name + default_mode = "0600" + } + } + volume { + name = "openclaw-config" + config_map { + name = kubernetes_config_map.openclaw_config.metadata[0].name + } + } + } + } + } +} + +resource "kubernetes_service" "openclaw" { + metadata { + name = "openclaw" + namespace = kubernetes_namespace.openclaw.metadata[0].name + labels = { + app = "openclaw" + } + } + spec { + selector = { + app = "openclaw" + } + port { + port = 80 + target_port = 18789 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.openclaw.metadata[0].name + name = "openclaw" + tls_secret_name = var.tls_secret_name + port = 80 + protected = true +} + +# --- CronJob: Scheduled cluster health check --- + +resource "kubernetes_service_account" "healthcheck" { + metadata { + name = "cluster-healthcheck" + namespace = kubernetes_namespace.openclaw.metadata[0].name + } +} + +resource "kubernetes_role" "healthcheck_exec" { + metadata { + name = "healthcheck-pod-exec" + namespace = kubernetes_namespace.openclaw.metadata[0].name + } + rule { + api_groups = [""] + resources = ["pods"] + verbs = ["get", "list"] + } + rule { + api_groups = [""] + resources = ["pods/exec"] + verbs = ["create"] + } +} + +resource "kubernetes_role_binding" "healthcheck_exec" { + metadata { + name = "healthcheck-pod-exec" + namespace = kubernetes_namespace.openclaw.metadata[0].name + } + subject { + kind = "ServiceAccount" + name = kubernetes_service_account.healthcheck.metadata[0].name + namespace = kubernetes_namespace.openclaw.metadata[0].name + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "Role" + name = kubernetes_role.healthcheck_exec.metadata[0].name + } +} + +resource "kubernetes_cron_job_v1" "cluster_healthcheck" { + metadata { + name = "cluster-healthcheck" + namespace = kubernetes_namespace.openclaw.metadata[0].name + labels = { + app = "cluster-healthcheck" + tier = local.tiers.aux + } + } + spec { + schedule = "*/30 * * * *" + concurrency_policy = "Forbid" + failed_jobs_history_limit = 3 + successful_jobs_history_limit = 3 + + job_template { + metadata { + labels = { + app = "cluster-healthcheck" + } + } + spec { + active_deadline_seconds = 300 + backoff_limit = 0 + template { + metadata { + labels = { + app = "cluster-healthcheck" + } + } + spec { + service_account_name = kubernetes_service_account.healthcheck.metadata[0].name + restart_policy = "Never" + + container { + name = "healthcheck" + image = "bitnami/kubectl:latest" + command = ["bash", "-c", <<-EOF + # Find the openclaw pod + POD=$(kubectl get pods -n openclaw -l app=openclaw -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) + if [ -z "$POD" ]; then + echo "ERROR: OpenClaw pod not found" + exit 1 + fi + echo "Executing health check in pod $POD..." + kubectl exec -n openclaw "$POD" -c openclaw -- bash /workspace/infra/.claude/cluster-health.sh + EOF + ] + + resources { + requests = { + cpu = "50m" + memory = "64Mi" + } + limits = { + memory = "128Mi" + } + } + } + } + } + } + } + } } diff --git a/stacks/openclaw/module/main.tf b/stacks/openclaw/module/main.tf deleted file mode 100644 index 12b88f7e..00000000 --- a/stacks/openclaw/module/main.tf +++ /dev/null @@ -1,588 +0,0 @@ -variable "tls_secret_name" {} -variable "git_crypt_key_base64" { type = string } -variable "tier" { type = string } -variable "ssh_key" {} -variable "gemini_api_key" { type = string } -variable "llama_api_key" { type = string } -variable "brave_api_key" { type = string } -variable "modal_api_key" { type = string } -variable "skill_secrets" { type = map(string) } - -resource "kubernetes_namespace" "openclaw" { - metadata { - name = "openclaw" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.openclaw.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_service_account" "openclaw" { - metadata { - name = "openclaw" - namespace = kubernetes_namespace.openclaw.metadata[0].name - } -} - -resource "kubernetes_cluster_role_binding" "openclaw" { - metadata { - name = "openclaw-cluster-admin" - } - subject { - kind = "ServiceAccount" - name = kubernetes_service_account.openclaw.metadata[0].name - namespace = kubernetes_namespace.openclaw.metadata[0].name - } - role_ref { - api_group = "rbac.authorization.k8s.io" - kind = "ClusterRole" - name = "cluster-admin" - } -} - -resource "kubernetes_secret" "ssh_key" { - metadata { - name = "ssh-key" - namespace = kubernetes_namespace.openclaw.metadata[0].name - } - data = { - "id_rsa" = var.ssh_key - } - type = "generic" -} - -resource "kubernetes_config_map" "git_crypt_key" { - metadata { - name = "git-crypt-key" - namespace = kubernetes_namespace.openclaw.metadata[0].name - } - data = { - "key" = var.git_crypt_key_base64 - } -} - -resource "kubernetes_config_map" "openclaw_config" { - metadata { - name = "openclaw-config" - namespace = kubernetes_namespace.openclaw.metadata[0].name - } - data = { - "openclaw.json" = jsonencode({ - gateway = { - bind = "lan" - trustedProxies = ["10.0.0.0/8"] - controlUi = { - dangerouslyDisableDeviceAuth = true - } - } - agents = { - defaults = { - contextTokens = 1000000 - bootstrapMaxChars = 30000 - model = { - primary = "modal/zai-org/GLM-5-FP8" - fallbacks = ["gemini/gemini-2.5-flash", "llama-as-openai/Llama-3.3-70B-Instruct"] - } - models = { - "modal/zai-org/GLM-5-FP8" = { streaming = false } - "gemini/gemini-2.5-flash" = {} - "llama-as-openai/Llama-3.3-70B-Instruct" = {} - "llama-as-openai/Llama-4-Scout-17B-16E-Instruct-FP8" = {} - } - } - } - tools = { - profile = "full" - deny = ["sessions_spawn", "sessions_list", "sessions_history", "sessions_send", "subagents", "browser"] - exec = { - host = "sandbox" - security = "full" - ask = "off" - pathPrepend = ["/tools", "/workspace/infra"] - } - web = { - search = { - enabled = true - provider = "brave" - apiKey = var.brave_api_key - maxResults = 5 - } - fetch = { - enabled = true - maxChars = 50000 - timeoutSeconds = 30 - } - } - } - models = { - mode = "merge" - providers = { - modal = { - baseUrl = "https://api.us-west-2.modal.direct/v1" - api = "openai-completions" - apiKey = var.modal_api_key - models = [ - { id = "zai-org/GLM-5-FP8", name = "GLM-5", reasoning = false, input = ["text"], contextWindow = 128000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, - ] - } - gemini = { - baseUrl = "https://generativelanguage.googleapis.com/v1beta" - api = "google-generative-ai" - apiKey = var.gemini_api_key - models = [ - { id = "gemini-2.5-flash", name = "gemini-2.5-flash", reasoning = true, input = ["text", "image"], contextWindow = 1048576, maxTokens = 65536, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, - ] - } - ollama = { - baseUrl = "http://ollama.ollama.svc.cluster.local:11434/v1" - api = "openai-completions" - apiKey = "ollama" - models = [ - { id = "qwen2.5-coder:14b", name = "qwen2.5-coder:14b", reasoning = false, input = ["text"], contextWindow = 128000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, - { id = "qwen2.5:14b", name = "qwen2.5:14b", reasoning = false, input = ["text"], contextWindow = 128000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, - { id = "deepseek-r1:14b", name = "deepseek-r1:14b", reasoning = true, input = ["text"], contextWindow = 128000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, - ] - } - llama-as-openai = { - baseUrl = "https://api.llama.com/compat/v1" - apiKey = var.llama_api_key - api = "openai-completions" - models = [ - { id = "Llama-3.3-70B-Instruct", name = "Llama-3.3-70B-Instruct", reasoning = false, input = ["text"], contextWindow = 128000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, - { id = "Llama-4-Scout-17B-16E-Instruct-FP8", name = "Llama-4-Scout-17B-16E-Instruct-FP8", reasoning = false, input = ["text"], contextWindow = 200000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, - { id = "Llama-4-Maverick-17B-128E-Instruct-FP8", name = "Llama-4-Maverick-17B-128E-Instruct-FP8", reasoning = false, input = ["text"], contextWindow = 200000, maxTokens = 8192, cost = { input = 0, output = 0, cacheRead = 0, cacheWrite = 0 } }, - ] - } - } - } - }) - } -} - -resource "random_password" "gateway_token" { - length = 32 - special = false -} - -resource "kubernetes_deployment" "openclaw" { - metadata { - name = "openclaw" - namespace = kubernetes_namespace.openclaw.metadata[0].name - labels = { - app = "openclaw" - tier = var.tier - } - } - spec { - strategy { - type = "Recreate" - } - replicas = 1 - selector { - match_labels = { - app = "openclaw" - } - } - template { - metadata { - labels = { - app = "openclaw" - } - } - spec { - service_account_name = kubernetes_service_account.openclaw.metadata[0].name - - # Init container: Download tools + clone repo + terraform init (parallelized) - init_container { - name = "setup" - image = "alpine:3.20" - command = ["sh", "-c", <<-EOF - set -e - apk add --no-cache curl unzip git-crypt openssh-client git bash - - # Install pip and Python packages for skills - python3 -m ensurepip 2>/dev/null || apk add --no-cache py3-pip - pip3 install --break-system-packages --target=/tools/python-libs requests caldav icalendar uptime-kuma-api - - # Copy OpenClaw config to writable home dir - cp /openclaw-config-src/openclaw.json /openclaw-home/openclaw.json - - # Setup SSH key - mkdir -p /root/.ssh - cp /ssh/id_rsa /root/.ssh/id_rsa - chmod 600 /root/.ssh/id_rsa - ssh-keyscan github.com >> /root/.ssh/known_hosts 2>/dev/null - - # --- Run downloads and clone in parallel --- - # kubectl - (curl -sL --retry 3 --retry-delay 5 "https://dl.k8s.io/release/v1.34.2/bin/linux/amd64/kubectl" -o /tools/kubectl && chmod +x /tools/kubectl) & - PID_KUBECTL=$! - - # terraform - (curl -sL --retry 3 --retry-delay 5 "https://releases.hashicorp.com/terraform/1.14.5/terraform_1.14.5_linux_amd64.zip" -o /tmp/tf.zip && unzip -q /tmp/tf.zip -d /tools && chmod +x /tools/terraform && rm /tmp/tf.zip) & - PID_TF=$! - - # git-crypt (already installed via apk) - cp /usr/bin/git-crypt /tools/git-crypt - - # Clone/pull repo - if [ ! -d /workspace/infra/.git ]; then - git clone git@github.com:ViktorBarzin/infra.git /workspace/infra & - PID_GIT=$! - else - (cd /workspace/infra && git pull --ff-only || true) & - PID_GIT=$! - fi - - # Wait for all parallel tasks - wait $PID_KUBECTL || { echo "kubectl download failed"; exit 1; } - wait $PID_GIT || { echo "git clone/pull failed"; exit 1; } - - # Unlock git-crypt (needs clone done) - cd /workspace/infra - echo "$GIT_CRYPT_KEY" | base64 -d > /tmp/git-crypt-key - git-crypt unlock /tmp/git-crypt-key || true - rm /tmp/git-crypt-key - - # Mark repo as safe for the node user (different UID from init container) - git config --global --add safe.directory /workspace/infra - cp /root/.gitconfig /openclaw-home/.gitconfig 2>/dev/null || true - - # Symlink Claude skills into OpenClaw skills directory - ln -sfn /workspace/infra/.claude/skills /openclaw-home/skills - - # Generate kubeconfig from in-cluster ServiceAccount credentials - SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) - SA_CA=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt - cat > /openclaw-home/kubeconfig <<-KUBEEOF - apiVersion: v1 - kind: Config - clusters: - - cluster: - certificate-authority-data: $(base64 < "$SA_CA" | tr -d '\n') - server: https://kubernetes.default.svc - name: in-cluster - contexts: - - context: - cluster: in-cluster - user: openclaw - name: in-cluster - current-context: in-cluster - users: - - name: openclaw - user: - token: $SA_TOKEN - KUBEEOF - - # Terraform init (needs terraform + clone done) - wait $PID_TF || { echo "terraform download failed"; exit 1; } - /tools/terraform init - EOF - ] - env { - name = "GIT_CRYPT_KEY" - value_from { - config_map_key_ref { - name = kubernetes_config_map.git_crypt_key.metadata[0].name - key = "key" - } - } - } - volume_mount { - name = "tools" - mount_path = "/tools" - } - volume_mount { - name = "workspace" - mount_path = "/workspace" - } - volume_mount { - name = "ssh-key" - mount_path = "/ssh" - } - volume_mount { - name = "openclaw-home" - mount_path = "/openclaw-home" - } - volume_mount { - name = "openclaw-config" - mount_path = "/openclaw-config-src" - } - } - - # Main container: OpenClaw - container { - name = "openclaw" - image = "ghcr.io/openclaw/openclaw:2026.2.9" - command = ["node", "openclaw.mjs", "gateway", "--allow-unconfigured", "--bind", "lan"] - port { - container_port = 18789 - } - env { - name = "OPENCLAW_GATEWAY_TOKEN" - value = random_password.gateway_token.result - } - env { - name = "PATH" - value = "/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - } - env { - name = "TF_VAR_prod" - value = "true" - } - env { - name = "KUBECONFIG" - value = "/home/node/.openclaw/kubeconfig" - } - env { - name = "GIT_CONFIG_GLOBAL" - value = "/home/node/.openclaw/.gitconfig" - } - env { - name = "GEMINI_API_KEY" - value = var.gemini_api_key - } - # Skill secrets - Home Assistant - env { - name = "HOME_ASSISTANT_URL" - value = "https://ha-london.viktorbarzin.me" - } - env { - name = "HOME_ASSISTANT_TOKEN" - value = var.skill_secrets["home_assistant_token"] - } - env { - name = "HOME_ASSISTANT_SOFIA_URL" - value = "https://ha-sofia.viktorbarzin.me" - } - env { - name = "HOME_ASSISTANT_SOFIA_TOKEN" - value = var.skill_secrets["home_assistant_sofia_token"] - } - # Skill secrets - Uptime Kuma - env { - name = "UPTIME_KUMA_PASSWORD" - value = var.skill_secrets["uptime_kuma_password"] - } - # Skill secrets - Slack - env { - name = "SLACK_WEBHOOK_URL" - value = var.skill_secrets["slack_webhook"] - } - # Python packages path for skills - env { - name = "PYTHONPATH" - value = "/tools/python-libs" - } - volume_mount { - name = "tools" - mount_path = "/tools" - } - volume_mount { - name = "workspace" - mount_path = "/workspace" - } - volume_mount { - name = "data" - mount_path = "/data" - } - volume_mount { - name = "ssh-key" - mount_path = "/ssh" - } - volume_mount { - name = "openclaw-home" - mount_path = "/home/node/.openclaw" - } - resources { - limits = { - memory = "4Gi" - } - requests = { - memory = "256Mi" - } - } - } - - volume { - name = "tools" - empty_dir {} - } - volume { - name = "openclaw-home" - empty_dir {} - } - volume { - name = "workspace" - nfs { - server = "10.0.10.15" - path = "/mnt/main/openclaw/workspace" - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/openclaw/data" - } - } - volume { - name = "ssh-key" - secret { - secret_name = kubernetes_secret.ssh_key.metadata[0].name - default_mode = "0600" - } - } - volume { - name = "openclaw-config" - config_map { - name = kubernetes_config_map.openclaw_config.metadata[0].name - } - } - } - } - } -} - -resource "kubernetes_service" "openclaw" { - metadata { - name = "openclaw" - namespace = kubernetes_namespace.openclaw.metadata[0].name - labels = { - app = "openclaw" - } - } - spec { - selector = { - app = "openclaw" - } - port { - port = 80 - target_port = 18789 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.openclaw.metadata[0].name - name = "openclaw" - tls_secret_name = var.tls_secret_name - port = 80 - protected = true -} - -# --- CronJob: Scheduled cluster health check --- - -resource "kubernetes_service_account" "healthcheck" { - metadata { - name = "cluster-healthcheck" - namespace = kubernetes_namespace.openclaw.metadata[0].name - } -} - -resource "kubernetes_role" "healthcheck_exec" { - metadata { - name = "healthcheck-pod-exec" - namespace = kubernetes_namespace.openclaw.metadata[0].name - } - rule { - api_groups = [""] - resources = ["pods"] - verbs = ["get", "list"] - } - rule { - api_groups = [""] - resources = ["pods/exec"] - verbs = ["create"] - } -} - -resource "kubernetes_role_binding" "healthcheck_exec" { - metadata { - name = "healthcheck-pod-exec" - namespace = kubernetes_namespace.openclaw.metadata[0].name - } - subject { - kind = "ServiceAccount" - name = kubernetes_service_account.healthcheck.metadata[0].name - namespace = kubernetes_namespace.openclaw.metadata[0].name - } - role_ref { - api_group = "rbac.authorization.k8s.io" - kind = "Role" - name = kubernetes_role.healthcheck_exec.metadata[0].name - } -} - -resource "kubernetes_cron_job_v1" "cluster_healthcheck" { - metadata { - name = "cluster-healthcheck" - namespace = kubernetes_namespace.openclaw.metadata[0].name - labels = { - app = "cluster-healthcheck" - tier = var.tier - } - } - spec { - schedule = "*/30 * * * *" - concurrency_policy = "Forbid" - failed_jobs_history_limit = 3 - successful_jobs_history_limit = 3 - - job_template { - metadata { - labels = { - app = "cluster-healthcheck" - } - } - spec { - active_deadline_seconds = 300 - backoff_limit = 0 - template { - metadata { - labels = { - app = "cluster-healthcheck" - } - } - spec { - service_account_name = kubernetes_service_account.healthcheck.metadata[0].name - restart_policy = "Never" - - container { - name = "healthcheck" - image = "bitnami/kubectl:latest" - command = ["bash", "-c", <<-EOF - # Find the openclaw pod - POD=$(kubectl get pods -n openclaw -l app=openclaw -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) - if [ -z "$POD" ]; then - echo "ERROR: OpenClaw pod not found" - exit 1 - fi - echo "Executing health check in pod $POD..." - kubectl exec -n openclaw "$POD" -c openclaw -- bash /workspace/infra/.claude/cluster-health.sh - EOF - ] - - resources { - requests = { - cpu = "50m" - memory = "64Mi" - } - limits = { - memory = "128Mi" - } - } - } - } - } - } - } - } -} diff --git a/stacks/osm_routing/main.tf b/stacks/osm_routing/main.tf index 9aa138f5..1bb8d9b6 100644 --- a/stacks/osm_routing/main.tf +++ b/stacks/osm_routing/main.tf @@ -10,8 +10,228 @@ locals { } } -module "osm_routing" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "osm-routing" { + metadata { + name = "osm-routing" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +# --- OSRM Foot --- +resource "kubernetes_deployment" "osrm-foot" { + metadata { + name = "osrm-foot" + namespace = kubernetes_namespace.osm-routing.metadata[0].name + labels = { + app = "osrm-foot" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "osrm-foot" + } + } + template { + metadata { + labels = { + app = "osrm-foot" + } + } + spec { + container { + name = "osrm-foot" + image = "ghcr.io/project-osrm/osrm-backend:latest" + command = ["osrm-routed", "--algorithm", "MLD", "/data/foot/greater-london-latest.osrm"] + port { + name = "http" + container_port = 5000 + protocol = "TCP" + } + volume_mount { + name = "osrm-data" + mount_path = "/data" + } + } + volume { + name = "osrm-data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/osm-routing/osrm-data" + } + } + } + } + } +} + +resource "kubernetes_service" "osrm-foot" { + metadata { + name = "osrm-foot" + namespace = kubernetes_namespace.osm-routing.metadata[0].name + labels = { + app = "osrm-foot" + } + } + spec { + selector = { + app = "osrm-foot" + } + port { + port = 5000 + target_port = 5000 + } + } +} + +# --- OSRM Bicycle --- +resource "kubernetes_deployment" "osrm-bicycle" { + metadata { + name = "osrm-bicycle" + namespace = kubernetes_namespace.osm-routing.metadata[0].name + labels = { + app = "osrm-bicycle" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "osrm-bicycle" + } + } + template { + metadata { + labels = { + app = "osrm-bicycle" + } + } + spec { + container { + name = "osrm-bicycle" + image = "ghcr.io/project-osrm/osrm-backend:latest" + command = ["osrm-routed", "--algorithm", "MLD", "/data/bicycle/greater-london-latest.osrm"] + port { + name = "http" + container_port = 5000 + protocol = "TCP" + } + volume_mount { + name = "osrm-data" + mount_path = "/data" + } + } + volume { + name = "osrm-data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/osm-routing/osrm-data" + } + } + } + } + } +} + +resource "kubernetes_service" "osrm-bicycle" { + metadata { + name = "osrm-bicycle" + namespace = kubernetes_namespace.osm-routing.metadata[0].name + labels = { + app = "osrm-bicycle" + } + } + spec { + selector = { + app = "osrm-bicycle" + } + port { + port = 5000 + target_port = 5000 + } + } +} + +# --- OTP (OpenTripPlanner) --- +resource "kubernetes_deployment" "otp" { + metadata { + name = "otp" + namespace = kubernetes_namespace.osm-routing.metadata[0].name + labels = { + app = "otp" + tier = local.tiers.aux + } + } + spec { + replicas = 0 # Scaled down: TfL GTFS data expired, OTP crash-loops on build + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "otp" + } + } + template { + metadata { + labels = { + app = "otp" + } + } + spec { + container { + name = "otp" + image = "opentripplanner/opentripplanner:2.6.0" + args = ["--build", "--save"] + port { + name = "http" + container_port = 8080 + protocol = "TCP" + } + volume_mount { + name = "otp-data" + mount_path = "/var/opentripplanner" + } + } + volume { + name = "otp-data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/osm-routing/otp-data" + } + } + } + } + } +} + +resource "kubernetes_service" "otp" { + metadata { + name = "otp" + namespace = kubernetes_namespace.osm-routing.metadata[0].name + labels = { + app = "otp" + } + } + spec { + selector = { + app = "otp" + } + port { + port = 8080 + target_port = 8080 + } + } } diff --git a/stacks/osm_routing/module/main.tf b/stacks/osm_routing/module/main.tf deleted file mode 100644 index fa421db8..00000000 --- a/stacks/osm_routing/module/main.tf +++ /dev/null @@ -1,228 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "osm-routing" { - metadata { - name = "osm-routing" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -# --- OSRM Foot --- -resource "kubernetes_deployment" "osrm-foot" { - metadata { - name = "osrm-foot" - namespace = kubernetes_namespace.osm-routing.metadata[0].name - labels = { - app = "osrm-foot" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "osrm-foot" - } - } - template { - metadata { - labels = { - app = "osrm-foot" - } - } - spec { - container { - name = "osrm-foot" - image = "ghcr.io/project-osrm/osrm-backend:latest" - command = ["osrm-routed", "--algorithm", "MLD", "/data/foot/greater-london-latest.osrm"] - port { - name = "http" - container_port = 5000 - protocol = "TCP" - } - volume_mount { - name = "osrm-data" - mount_path = "/data" - } - } - volume { - name = "osrm-data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/osm-routing/osrm-data" - } - } - } - } - } -} - -resource "kubernetes_service" "osrm-foot" { - metadata { - name = "osrm-foot" - namespace = kubernetes_namespace.osm-routing.metadata[0].name - labels = { - app = "osrm-foot" - } - } - spec { - selector = { - app = "osrm-foot" - } - port { - port = 5000 - target_port = 5000 - } - } -} - -# --- OSRM Bicycle --- -resource "kubernetes_deployment" "osrm-bicycle" { - metadata { - name = "osrm-bicycle" - namespace = kubernetes_namespace.osm-routing.metadata[0].name - labels = { - app = "osrm-bicycle" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "osrm-bicycle" - } - } - template { - metadata { - labels = { - app = "osrm-bicycle" - } - } - spec { - container { - name = "osrm-bicycle" - image = "ghcr.io/project-osrm/osrm-backend:latest" - command = ["osrm-routed", "--algorithm", "MLD", "/data/bicycle/greater-london-latest.osrm"] - port { - name = "http" - container_port = 5000 - protocol = "TCP" - } - volume_mount { - name = "osrm-data" - mount_path = "/data" - } - } - volume { - name = "osrm-data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/osm-routing/osrm-data" - } - } - } - } - } -} - -resource "kubernetes_service" "osrm-bicycle" { - metadata { - name = "osrm-bicycle" - namespace = kubernetes_namespace.osm-routing.metadata[0].name - labels = { - app = "osrm-bicycle" - } - } - spec { - selector = { - app = "osrm-bicycle" - } - port { - port = 5000 - target_port = 5000 - } - } -} - -# --- OTP (OpenTripPlanner) --- -resource "kubernetes_deployment" "otp" { - metadata { - name = "otp" - namespace = kubernetes_namespace.osm-routing.metadata[0].name - labels = { - app = "otp" - tier = var.tier - } - } - spec { - replicas = 0 # Scaled down: TfL GTFS data expired, OTP crash-loops on build - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "otp" - } - } - template { - metadata { - labels = { - app = "otp" - } - } - spec { - container { - name = "otp" - image = "opentripplanner/opentripplanner:2.6.0" - args = ["--build", "--save"] - port { - name = "http" - container_port = 8080 - protocol = "TCP" - } - volume_mount { - name = "otp-data" - mount_path = "/var/opentripplanner" - } - } - volume { - name = "otp-data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/osm-routing/otp-data" - } - } - } - } - } -} - -resource "kubernetes_service" "otp" { - metadata { - name = "otp" - namespace = kubernetes_namespace.osm-routing.metadata[0].name - labels = { - app = "otp" - } - } - spec { - selector = { - app = "otp" - } - port { - port = 8080 - target_port = 8080 - } - } -} diff --git a/stacks/owntracks/main.tf b/stacks/owntracks/main.tf index e2c6e99c..b28c0151 100644 --- a/stacks/owntracks/main.tf +++ b/stacks/owntracks/main.tf @@ -11,9 +11,156 @@ locals { } } -module "owntracks" { - source = "./module" - tls_secret_name = var.tls_secret_name - owntracks_credentials = var.owntracks_credentials - tier = local.tiers.aux +resource "kubernetes_namespace" "owntracks" { + metadata { + name = "owntracks" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.owntracks.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +locals { + username = "owntracks" + htpasswd = join("\n", [for name, pass in var.owntracks_credentials : "${name}:${bcrypt(pass, 10)}"]) +} + +resource "kubernetes_secret" "basic_auth" { + metadata { + name = "basic-auth-secret" + namespace = kubernetes_namespace.owntracks.metadata[0].name + } + + data = { + auth = local.htpasswd + } + + type = "Opaque" + lifecycle { + ignore_changes = [data] + } +} + +resource "kubernetes_deployment" "owntracks" { + metadata { + name = "owntracks" + namespace = kubernetes_namespace.owntracks.metadata[0].name + labels = { + app = "owntracks" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "owntracks" + } + } + template { + metadata { + labels = { + app = "owntracks" + } + annotations = { + "diun.enable" = "true" + "diun.include_tags" = "^\\d+(?:\\.\\d+)?(?:\\.\\d+)?$" + } + } + spec { + + container { + image = "owntracks/recorder:0.9.9" + name = "owntracks" + port { + name = "https" + container_port = 8083 + } + env { + name = "OTR_PORT" + value = "0" + } + + volume_mount { + name = "data" + mount_path = "/store" + } + volume_mount { + name = "data" + mount_path = "/config" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/owntracks" + server = "10.0.10.15" + } + } + } + } + } +} + + +resource "kubernetes_service" "owntracks" { + metadata { + name = "owntracks" + namespace = kubernetes_namespace.owntracks.metadata[0].name + labels = { + "app" = "owntracks" + } + } + + spec { + selector = { + app = "owntracks" + } + port { + name = "https" + port = 443 + target_port = 8083 + protocol = "TCP" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.owntracks.metadata[0].name + name = "owntracks" + tls_secret_name = var.tls_secret_name + port = 443 + extra_annotations = { + "traefik.ingress.kubernetes.io/router.middlewares" = "owntracks-basic-auth@kubernetescrd,traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd" + } +} + +resource "kubernetes_manifest" "basic_auth_middleware" { + manifest = { + apiVersion = "traefik.io/v1alpha1" + kind = "Middleware" + metadata = { + name = "basic-auth" + namespace = kubernetes_namespace.owntracks.metadata[0].name + } + spec = { + basicAuth = { + secret = kubernetes_secret.basic_auth.metadata[0].name + } + } + } } diff --git a/stacks/owntracks/module/main.tf b/stacks/owntracks/module/main.tf deleted file mode 100644 index c2b99b4d..00000000 --- a/stacks/owntracks/module/main.tf +++ /dev/null @@ -1,162 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "owntracks_credentials" { - type = map(string) - default = { - "foo" = "bar" // example format for username and password - } -} - -resource "kubernetes_namespace" "owntracks" { - metadata { - name = "owntracks" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.owntracks.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -locals { - username = "owntracks" - htpasswd = join("\n", [for name, pass in var.owntracks_credentials : "${name}:${bcrypt(pass, 10)}"]) -} - -resource "kubernetes_secret" "basic_auth" { - metadata { - name = "basic-auth-secret" - namespace = kubernetes_namespace.owntracks.metadata[0].name - } - - data = { - auth = local.htpasswd - } - - type = "Opaque" - lifecycle { - ignore_changes = [data] - } -} - -resource "kubernetes_deployment" "owntracks" { - metadata { - name = "owntracks" - namespace = kubernetes_namespace.owntracks.metadata[0].name - labels = { - app = "owntracks" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "owntracks" - } - } - template { - metadata { - labels = { - app = "owntracks" - } - annotations = { - "diun.enable" = "true" - "diun.include_tags" = "^\\d+(?:\\.\\d+)?(?:\\.\\d+)?$" - } - } - spec { - - container { - image = "owntracks/recorder:0.9.9" - name = "owntracks" - port { - name = "https" - container_port = 8083 - } - env { - name = "OTR_PORT" - value = "0" - } - - volume_mount { - name = "data" - mount_path = "/store" - } - volume_mount { - name = "data" - mount_path = "/config" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/owntracks" - server = "10.0.10.15" - } - } - } - } - } -} - - -resource "kubernetes_service" "owntracks" { - metadata { - name = "owntracks" - namespace = kubernetes_namespace.owntracks.metadata[0].name - labels = { - "app" = "owntracks" - } - } - - spec { - selector = { - app = "owntracks" - } - port { - name = "https" - port = 443 - target_port = 8083 - protocol = "TCP" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.owntracks.metadata[0].name - name = "owntracks" - tls_secret_name = var.tls_secret_name - port = 443 - extra_annotations = { - "traefik.ingress.kubernetes.io/router.middlewares" = "owntracks-basic-auth@kubernetescrd,traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd" - } -} - -resource "kubernetes_manifest" "basic_auth_middleware" { - manifest = { - apiVersion = "traefik.io/v1alpha1" - kind = "Middleware" - metadata = { - name = "basic-auth" - namespace = kubernetes_namespace.owntracks.metadata[0].name - } - spec = { - basicAuth = { - secret = kubernetes_secret.basic_auth.metadata[0].name - } - } - } -} diff --git a/stacks/paperless-ngx/main.tf b/stacks/paperless-ngx/main.tf index 187c9259..c61db63e 100644 --- a/stacks/paperless-ngx/main.tf +++ b/stacks/paperless-ngx/main.tf @@ -12,11 +12,171 @@ locals { } } -module "paperless-ngx" { - source = "./module" - 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.tiers.edge +resource "kubernetes_namespace" "paperless-ngx" { + metadata { + name = "paperless-ngx" + labels = { + tier = local.tiers.edge + } + # labels = { + # "istio-injection" : "enabled" + # } + } +} +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.paperless-ngx.metadata[0].name + tls_secret_name = var.tls_secret_name +} + + +resource "kubernetes_deployment" "paperless-ngx" { + metadata { + name = "paperless-ngx" + namespace = kubernetes_namespace.paperless-ngx.metadata[0].name + labels = { + app = "paperless-ngx" + tier = local.tiers.edge + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "paperless-ngx" + } + } + template { + metadata { + labels = { + app = "paperless-ngx" + } + annotations = { + "diun.enable" = "false" + "diun.include_tags" = "^\\d+(?:\\.\\d+)?(?:\\.\\d+)?$" + } + } + spec { + container { + image = "ghcr.io/paperless-ngx/paperless-ngx:latest" + name = "paperless-ngx" + env { + name = "PAPERLESS_REDIS" + // If redis gets stuck, try deleting the locks files in log dir + value = "redis://redis.redis" + } + env { + name = "PAPERLESS_REDIS_PREFIX" + value = "paperless-ngx" + } + env { + name = "PAPERLESS_DBENGINE" + value = "mariadb" + } + env { + name = "PAPERLESS_DBHOST" + value = "mysql.dbaas" + } + env { + name = "PAPERLESS_DBNAME" + value = "paperless-ngx" + } + env { + name = "PAPERLESS_DBUSER" + value = "paperless-ngx" + } + env { + name = "PAPERLESS_DBPASS" + value = var.paperless_db_password + } + env { + name = "PAPERLESS_CSRF_TRUSTED_ORIGINS" + value = "https://paperless-ngx.viktorbarzin.me,https://pdf.viktorbarzin.me" + } + env { + name = "PAPERLESS_DEBUG" + value = "false" + } + env { + name = "PAPERLESS_MEDIA_ROOT" + value = "../data" + } + env { + name = "PAPERLESS_OCR_USER_ARGS" + value = "{\"invalidate_digital_signatures\": true}" + } + volume_mount { + name = "data" + mount_path = "/usr/src/paperless/data" + } + + port { + container_port = 8000 + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/paperless-ngx" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "paperless-ngx" { + metadata { + name = "paperless-ngx" + namespace = kubernetes_namespace.paperless-ngx.metadata[0].name + labels = { + "app" = "paperless-ngx" + } + } + + spec { + selector = { + app = "paperless-ngx" + } + port { + name = "http" + target_port = 8000 + port = 80 + protocol = "TCP" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.paperless-ngx.metadata[0].name + name = "paperless-ngx" + service_name = "paperless-ngx" + host = "pdf" + tls_secret_name = var.tls_secret_name + port = 80 + extra_annotations = { + "gethomepage.dev/enabled" = "true" + "gethomepage.dev/description" = "Document library" + # gethomepage.dev/group: Media + "gethomepage.dev/icon" : "paperless-ngx.png" + "gethomepage.dev/name" = "Paperless-ngx" + "gethomepage.dev/widget.type" = "paperlessngx" + "gethomepage.dev/widget.url" = "https://pdf.viktorbarzin.me" + # "gethomepage.dev/widget.token" = var.homepage_token + "gethomepage.dev/widget.username" = var.homepage_credentials["paperless-ngx"]["username"] + "gethomepage.dev/widget.password" = var.homepage_credentials["paperless-ngx"]["password"] + "gethomepage.dev/widget.fields" = "[\"total\"]" + "gethomepage.dev/pod-selector" = "" + # gethomepage.dev/weight: 10 # optional + # gethomepage.dev/instance: "public" # optional + } + rybbit_site_id = "be6d140cbed8" } diff --git a/stacks/paperless-ngx/module/main.tf b/stacks/paperless-ngx/module/main.tf deleted file mode 100644 index 00119916..00000000 --- a/stacks/paperless-ngx/module/main.tf +++ /dev/null @@ -1,177 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "db_password" {} -# variable "homepage_token" {} -variable "homepage_username" {} -variable "homepage_password" {} - - -resource "kubernetes_namespace" "paperless-ngx" { - metadata { - name = "paperless-ngx" - labels = { - tier = var.tier - } - # labels = { - # "istio-injection" : "enabled" - # } - } -} -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.paperless-ngx.metadata[0].name - tls_secret_name = var.tls_secret_name -} - - -resource "kubernetes_deployment" "paperless-ngx" { - metadata { - name = "paperless-ngx" - namespace = kubernetes_namespace.paperless-ngx.metadata[0].name - labels = { - app = "paperless-ngx" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "paperless-ngx" - } - } - template { - metadata { - labels = { - app = "paperless-ngx" - } - annotations = { - "diun.enable" = "false" - "diun.include_tags" = "^\\d+(?:\\.\\d+)?(?:\\.\\d+)?$" - } - } - spec { - container { - image = "ghcr.io/paperless-ngx/paperless-ngx:latest" - name = "paperless-ngx" - env { - name = "PAPERLESS_REDIS" - // If redis gets stuck, try deleting the locks files in log dir - value = "redis://redis.redis" - } - env { - name = "PAPERLESS_REDIS_PREFIX" - value = "paperless-ngx" - } - env { - name = "PAPERLESS_DBENGINE" - value = "mariadb" - } - env { - name = "PAPERLESS_DBHOST" - value = "mysql.dbaas" - } - env { - name = "PAPERLESS_DBNAME" - value = "paperless-ngx" - } - env { - name = "PAPERLESS_DBUSER" - value = "paperless-ngx" - } - env { - name = "PAPERLESS_DBPASS" - value = var.db_password - } - env { - name = "PAPERLESS_CSRF_TRUSTED_ORIGINS" - value = "https://paperless-ngx.viktorbarzin.me,https://pdf.viktorbarzin.me" - } - env { - name = "PAPERLESS_DEBUG" - value = "false" - } - env { - name = "PAPERLESS_MEDIA_ROOT" - value = "../data" - } - env { - name = "PAPERLESS_OCR_USER_ARGS" - value = "{\"invalidate_digital_signatures\": true}" - } - volume_mount { - name = "data" - mount_path = "/usr/src/paperless/data" - } - - port { - container_port = 8000 - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/paperless-ngx" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "paperless-ngx" { - metadata { - name = "paperless-ngx" - namespace = kubernetes_namespace.paperless-ngx.metadata[0].name - labels = { - "app" = "paperless-ngx" - } - } - - spec { - selector = { - app = "paperless-ngx" - } - port { - name = "http" - target_port = 8000 - port = 80 - protocol = "TCP" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.paperless-ngx.metadata[0].name - name = "paperless-ngx" - service_name = "paperless-ngx" - host = "pdf" - tls_secret_name = var.tls_secret_name - port = 80 - extra_annotations = { - "gethomepage.dev/enabled" = "true" - "gethomepage.dev/description" = "Document library" - # gethomepage.dev/group: Media - "gethomepage.dev/icon" : "paperless-ngx.png" - "gethomepage.dev/name" = "Paperless-ngx" - "gethomepage.dev/widget.type" = "paperlessngx" - "gethomepage.dev/widget.url" = "https://pdf.viktorbarzin.me" - # "gethomepage.dev/widget.token" = var.homepage_token - "gethomepage.dev/widget.username" = var.homepage_username - "gethomepage.dev/widget.password" = var.homepage_password - "gethomepage.dev/widget.fields" = "[\"total\"]" - "gethomepage.dev/pod-selector" = "" - # gethomepage.dev/weight: 10 # optional - # gethomepage.dev/instance: "public" # optional - } - rybbit_site_id = "be6d140cbed8" -} - diff --git a/stacks/platform/main.tf b/stacks/platform/main.tf index 7c60075e..b03dd6ca 100644 --- a/stacks/platform/main.tf +++ b/stacks/platform/main.tf @@ -137,7 +137,7 @@ module "metallb" { # DBaaS — MySQL + PostgreSQL + pgAdmin # ----------------------------------------------------------------------------- module "dbaas" { - source = "./modules/dbaas" + source = "./modules/dbaas" prod = var.prod tls_secret_name = var.tls_secret_name dbaas_root_password = var.dbaas_root_password @@ -150,7 +150,7 @@ module "dbaas" { # Redis — Shared Redis instance # ----------------------------------------------------------------------------- module "redis" { - source = "./modules/redis" + source = "./modules/redis" tls_secret_name = var.tls_secret_name tier = local.tiers.cluster } @@ -159,7 +159,7 @@ module "redis" { # Traefik — Ingress controller (Helm) # ----------------------------------------------------------------------------- module "traefik" { - source = "./modules/traefik" + source = "./modules/traefik" tier = local.tiers.core crowdsec_api_key = var.ingress_crowdsec_api_key tls_secret_name = var.tls_secret_name @@ -169,7 +169,7 @@ module "traefik" { # Technitium — DNS server # ----------------------------------------------------------------------------- module "technitium" { - source = "./modules/technitium" + source = "./modules/technitium" tls_secret_name = var.tls_secret_name homepage_token = var.homepage_credentials["technitium"]["token"] technitium_db_password = var.technitium_db_password @@ -180,7 +180,7 @@ module "technitium" { # Headscale — Tailscale control server # ----------------------------------------------------------------------------- module "headscale" { - source = "./modules/headscale" + source = "./modules/headscale" tls_secret_name = var.tls_secret_name headscale_config = var.headscale_config headscale_acl = var.headscale_acl @@ -191,7 +191,7 @@ module "headscale" { # Authentik — Identity provider (SSO) # ----------------------------------------------------------------------------- module "authentik" { - source = "./modules/authentik" + source = "./modules/authentik" tier = local.tiers.cluster tls_secret_name = var.tls_secret_name secret_key = var.authentik_secret_key @@ -202,7 +202,7 @@ module "authentik" { # RBAC — Kubernetes OIDC RBAC (depends on Authentik) # ----------------------------------------------------------------------------- module "rbac" { - source = "./modules/rbac" + source = "./modules/rbac" tier = local.tiers.cluster tls_secret_name = var.tls_secret_name k8s_users = var.k8s_users @@ -213,7 +213,7 @@ module "rbac" { # K8s Portal — Self-service Kubernetes portal (depends on Authentik) # ----------------------------------------------------------------------------- module "k8s-portal" { - source = "./modules/k8s-portal" + source = "./modules/k8s-portal" tier = local.tiers.edge tls_secret_name = var.tls_secret_name } @@ -222,7 +222,7 @@ module "k8s-portal" { # CrowdSec — Security/WAF # ----------------------------------------------------------------------------- module "crowdsec" { - source = "./modules/crowdsec" + source = "./modules/crowdsec" tier = local.tiers.cluster tls_secret_name = var.tls_secret_name homepage_username = var.homepage_credentials["crowdsec"]["username"] @@ -239,25 +239,25 @@ module "crowdsec" { # Monitoring — Prometheus / Grafana / Loki stack # ----------------------------------------------------------------------------- module "monitoring" { - source = "./modules/monitoring" - tls_secret_name = var.tls_secret_name + source = "./modules/monitoring" + tls_secret_name = var.tls_secret_name alertmanager_account_password = var.alertmanager_account_password - idrac_username = var.monitoring_idrac_username - idrac_password = var.monitoring_idrac_password - alertmanager_slack_api_url = var.alertmanager_slack_api_url - tiny_tuya_service_secret = var.tiny_tuya_service_secret - haos_api_token = var.haos_api_token - pve_password = var.pve_password - grafana_db_password = var.grafana_db_password - grafana_admin_password = var.grafana_admin_password - tier = local.tiers.cluster + idrac_username = var.monitoring_idrac_username + idrac_password = var.monitoring_idrac_password + alertmanager_slack_api_url = var.alertmanager_slack_api_url + tiny_tuya_service_secret = var.tiny_tuya_service_secret + haos_api_token = var.haos_api_token + pve_password = var.pve_password + grafana_db_password = var.grafana_db_password + grafana_admin_password = var.grafana_admin_password + tier = local.tiers.cluster } # ----------------------------------------------------------------------------- # Vaultwarden — Password manager # ----------------------------------------------------------------------------- module "vaultwarden" { - source = "./modules/vaultwarden" + source = "./modules/vaultwarden" tls_secret_name = var.tls_secret_name smtp_password = var.vaultwarden_smtp_password tier = local.tiers.edge @@ -267,7 +267,7 @@ module "vaultwarden" { # Reverse Proxy — Generic reverse proxy # ----------------------------------------------------------------------------- module "reverse-proxy" { - source = "./modules/reverse_proxy" + source = "./modules/reverse_proxy" tls_secret_name = var.tls_secret_name truenas_homepage_token = var.homepage_credentials["reverse_proxy"]["truenas_token"] pfsense_homepage_token = var.homepage_credentials["reverse_proxy"]["pfsense_token"] @@ -277,7 +277,7 @@ module "reverse-proxy" { # Metrics Server — Kubernetes metrics # ----------------------------------------------------------------------------- module "metrics-server" { - source = "./modules/metrics-server" + source = "./modules/metrics-server" tier = local.tiers.cluster tls_secret_name = var.tls_secret_name } @@ -286,7 +286,7 @@ module "metrics-server" { # NVIDIA — GPU device plugin # ----------------------------------------------------------------------------- module "nvidia" { - source = "./modules/nvidia" + source = "./modules/nvidia" tls_secret_name = var.tls_secret_name tier = local.tiers.gpu } @@ -302,7 +302,7 @@ module "kyverno" { # Uptime Kuma — Status monitoring # ----------------------------------------------------------------------------- module "uptime-kuma" { - source = "./modules/uptime-kuma" + source = "./modules/uptime-kuma" tls_secret_name = var.tls_secret_name tier = local.tiers.cluster } @@ -311,7 +311,7 @@ module "uptime-kuma" { # WireGuard — VPN server # ----------------------------------------------------------------------------- module "wireguard" { - source = "./modules/wireguard" + source = "./modules/wireguard" tls_secret_name = var.tls_secret_name wg_0_conf = var.wireguard_wg_0_conf wg_0_key = var.wireguard_wg_0_key @@ -323,7 +323,7 @@ module "wireguard" { # Xray — Proxy/tunnel # ----------------------------------------------------------------------------- module "xray" { - source = "./modules/xray" + source = "./modules/xray" tls_secret_name = var.tls_secret_name tier = local.tiers.core @@ -336,7 +336,7 @@ module "xray" { # Mailserver — docker-mailserver # ----------------------------------------------------------------------------- module "mailserver" { - source = "./modules/mailserver" + source = "./modules/mailserver" tls_secret_name = var.tls_secret_name mailserver_accounts = var.mailserver_accounts postfix_account_aliases = var.mailserver_aliases @@ -350,7 +350,7 @@ module "mailserver" { # Cloudflared — Cloudflare tunnel + DNS records # ----------------------------------------------------------------------------- module "cloudflared" { - source = "./modules/cloudflared" + source = "./modules/cloudflared" tier = local.tiers.core tls_secret_name = var.tls_secret_name @@ -369,7 +369,7 @@ module "cloudflared" { # Infra Maintenance — Automated maintenance jobs # ----------------------------------------------------------------------------- module "infra-maintenance" { - source = "./modules/infra-maintenance" + source = "./modules/infra-maintenance" git_user = var.webhook_handler_git_user git_token = var.webhook_handler_git_token technitium_username = var.technitium_username diff --git a/stacks/platform/modules/authentik/main.tf b/stacks/platform/modules/authentik/main.tf index 5cda7cf1..9f220a99 100644 --- a/stacks/platform/modules/authentik/main.tf +++ b/stacks/platform/modules/authentik/main.tf @@ -14,7 +14,7 @@ resource "kubernetes_namespace" "authentik" { metadata { name = "authentik" labels = { - tier = var.tier + tier = var.tier "resource-governance/custom-quota" = "true" } } diff --git a/stacks/platform/modules/kyverno/resource-governance.tf b/stacks/platform/modules/kyverno/resource-governance.tf index 49f04e3f..403b64e1 100644 --- a/stacks/platform/modules/kyverno/resource-governance.tf +++ b/stacks/platform/modules/kyverno/resource-governance.tf @@ -721,12 +721,12 @@ resource "kubernetes_manifest" "mutate_priority_from_tier" { mutate = { patchesJson6902 = yamlencode([ { - op = "remove" - path = "/spec/priority" + op = "remove" + path = "/spec/priority" }, { - op = "remove" - path = "/spec/preemptionPolicy" + op = "remove" + path = "/spec/preemptionPolicy" }, { op = "add" diff --git a/stacks/plotting-book/main.tf b/stacks/plotting-book/main.tf index 2c88f3d1..bc91ea0c 100644 --- a/stacks/plotting-book/main.tf +++ b/stacks/plotting-book/main.tf @@ -10,8 +10,100 @@ locals { } } -module "plotting-book" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "plotting-book" { + metadata { + name = "plotting-book" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.plotting-book.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "plotting-book" { + metadata { + name = "plotting-book" + namespace = kubernetes_namespace.plotting-book.metadata[0].name + labels = { + app = "plotting-book" + tier = local.tiers.aux + } + } + lifecycle { + ignore_changes = [ + spec[0].template[0].spec[0].container[0].image, + ] + } + spec { + replicas = 1 + selector { + match_labels = { + app = "plotting-book" + } + } + template { + metadata { + labels = { + app = "plotting-book" + } + } + spec { + container { + image = "ancamilea/book-plotter:latest" + # image = "viktorbarzin/book-plotter:7" + name = "plotting-book" + image_pull_policy = "Always" + port { + container_port = 3001 + } + resources { + requests = { + memory = "128Mi" + cpu = "50m" + } + limits = { + memory = "512Mi" + cpu = "500m" + } + } + } + } + } + } +} + +resource "kubernetes_service" "plotting-book" { + metadata { + name = "plotting-book" + namespace = kubernetes_namespace.plotting-book.metadata[0].name + labels = { + "app" = "plotting-book" + } + } + + spec { + selector = { + app = "plotting-book" + } + port { + name = "http" + port = 80 + target_port = 3001 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.plotting-book.metadata[0].name + name = "plotting-book" + tls_secret_name = var.tls_secret_name + + custom_content_security_policy = "default-src 'self' blob: data:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; worker-src 'self' blob:; connect-src 'self' blob:; frame-ancestors 'self' *.viktorbarzin.me viktorbarzin.me" } diff --git a/stacks/plotting-book/module/main.tf b/stacks/plotting-book/module/main.tf deleted file mode 100644 index 882fe9ac..00000000 --- a/stacks/plotting-book/module/main.tf +++ /dev/null @@ -1,100 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "plotting-book" { - metadata { - name = "plotting-book" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.plotting-book.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "plotting-book" { - metadata { - name = "plotting-book" - namespace = kubernetes_namespace.plotting-book.metadata[0].name - labels = { - app = "plotting-book" - tier = var.tier - } - } - lifecycle { - ignore_changes = [ - spec[0].template[0].spec[0].container[0].image, - ] - } - spec { - replicas = 1 - selector { - match_labels = { - app = "plotting-book" - } - } - template { - metadata { - labels = { - app = "plotting-book" - } - } - spec { - container { - image = "ancamilea/book-plotter:latest" - # image = "viktorbarzin/book-plotter:7" - name = "plotting-book" - image_pull_policy = "Always" - port { - container_port = 3001 - } - resources { - requests = { - memory = "128Mi" - cpu = "50m" - } - limits = { - memory = "512Mi" - cpu = "500m" - } - } - } - } - } - } -} - -resource "kubernetes_service" "plotting-book" { - metadata { - name = "plotting-book" - namespace = kubernetes_namespace.plotting-book.metadata[0].name - labels = { - "app" = "plotting-book" - } - } - - spec { - selector = { - app = "plotting-book" - } - port { - name = "http" - port = 80 - target_port = 3001 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.plotting-book.metadata[0].name - name = "plotting-book" - tls_secret_name = var.tls_secret_name - - custom_content_security_policy = "default-src 'self' blob: data:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; worker-src 'self' blob:; connect-src 'self' blob:; frame-ancestors 'self' *.viktorbarzin.me viktorbarzin.me" -} diff --git a/stacks/privatebin/main.tf b/stacks/privatebin/main.tf index 1599f81c..517324ee 100644 --- a/stacks/privatebin/main.tf +++ b/stacks/privatebin/main.tf @@ -10,8 +10,100 @@ locals { } } -module "privatebin" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.edge +resource "kubernetes_namespace" "privatebin" { + metadata { + name = "privatebin" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.edge + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.privatebin.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "privatebin" { + metadata { + name = "privatebin" + namespace = kubernetes_namespace.privatebin.metadata[0].name + labels = { + app = "privatebin" + tier = local.tiers.edge + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "privatebin" + } + } + template { + metadata { + labels = { + app = "privatebin" + } + } + spec { + container { + image = "privatebin/nginx-fpm-alpine" + name = "privatebin" + image_pull_policy = "IfNotPresent" + port { + container_port = 8080 + } + volume_mount { + name = "data" + mount_path = "/srv/data" + sub_path = "data" + } + } + + volume { + name = "data" + nfs { + path = "/mnt/main/privatebin" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "privatebin" { + metadata { + name = "privatebin" + namespace = kubernetes_namespace.privatebin.metadata[0].name + labels = { + "app" = "privatebin" + } + } + + spec { + selector = { + app = "privatebin" + } + port { + port = "80" + target_port = "8080" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.privatebin.metadata[0].name + name = "privatebin" + host = "pb" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "3ae810b0476d" + custom_content_security_policy = "script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval' https://rybbit.viktorbarzin.me" } diff --git a/stacks/privatebin/module/main.tf b/stacks/privatebin/module/main.tf deleted file mode 100644 index 9d894e79..00000000 --- a/stacks/privatebin/module/main.tf +++ /dev/null @@ -1,100 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "privatebin" { - metadata { - name = "privatebin" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.privatebin.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "privatebin" { - metadata { - name = "privatebin" - namespace = kubernetes_namespace.privatebin.metadata[0].name - labels = { - app = "privatebin" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "privatebin" - } - } - template { - metadata { - labels = { - app = "privatebin" - } - } - spec { - container { - image = "privatebin/nginx-fpm-alpine" - name = "privatebin" - image_pull_policy = "IfNotPresent" - port { - container_port = 8080 - } - volume_mount { - name = "data" - mount_path = "/srv/data" - sub_path = "data" - } - } - - volume { - name = "data" - nfs { - path = "/mnt/main/privatebin" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "privatebin" { - metadata { - name = "privatebin" - namespace = kubernetes_namespace.privatebin.metadata[0].name - labels = { - "app" = "privatebin" - } - } - - spec { - selector = { - app = "privatebin" - } - port { - port = "80" - target_port = "8080" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.privatebin.metadata[0].name - name = "privatebin" - host = "pb" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "3ae810b0476d" - custom_content_security_policy = "script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval' https://rybbit.viktorbarzin.me" -} diff --git a/stacks/real-estate-crawler/main.tf b/stacks/real-estate-crawler/main.tf index bf25f5ee..7aa16e2c 100644 --- a/stacks/real-estate-crawler/main.tf +++ b/stacks/real-estate-crawler/main.tf @@ -12,10 +12,424 @@ locals { } } -module "real-estate-crawler" { - source = "./module" - tls_secret_name = var.tls_secret_name - db_password = var.realestate_crawler_db_password - notification_settings = var.realestate_crawler_notification_settings - tier = local.tiers.aux +resource "kubernetes_namespace" "realestate-crawler" { + metadata { + name = "realestate-crawler" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "realestate-crawler-ui" { + metadata { + name = "realestate-crawler-ui" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + labels = { + app = "realestate-crawler-ui" + tier = local.tiers.aux + } + } + spec { + replicas = 2 + strategy { + type = "RollingUpdate" + rolling_update { + max_unavailable = 0 + max_surge = 1 + } + } + selector { + match_labels = { + app = "realestate-crawler-ui" + } + } + template { + metadata { + labels = { + app = "realestate-crawler-ui" + } + } + spec { + container { + name = "realestate-crawler-ui" + image = "viktorbarzin/immoweb:latest" + port { + name = "http" + container_port = 8080 + protocol = "TCP" + } + env { + name = "ENV" + value = "prod" + } + } + } + } + } + lifecycle { + ignore_changes = [ + spec[0].template[0].spec[0].container[0].image + ] + } +} + +resource "kubernetes_service" "realestate-crawler-ui" { + metadata { + name = "realestate-crawler-ui" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + labels = { + "app" = "realestate-crawler-ui" + } + } + + spec { + selector = { + app = "realestate-crawler-ui" + } + port { + port = 80 + target_port = 8080 + } + } +} + +resource "kubernetes_deployment" "realestate-crawler-api" { + metadata { + name = "realestate-crawler-api" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + labels = { + app = "realestate-crawler-api" + tier = local.tiers.aux + } + } + spec { + replicas = 2 + strategy { + type = "RollingUpdate" + rolling_update { + max_unavailable = 0 + max_surge = 1 + } + } + selector { + match_labels = { + app = "realestate-crawler-api" + } + } + template { + metadata { + labels = { + app = "realestate-crawler-api" + "kubernetes.io/cluster-service" = "true" + } + } + spec { + container { + name = "realestate-crawler-api" + image = "viktorbarzin/realestatecrawler:latest" + image_pull_policy = "Always" + env { + name = "ENV" + value = "prod" + } + env { + name = "DB_CONNECTION_STRING" + value = "mysql://wrongmove:${var.realestate_crawler_db_password}@mysql.dbaas.svc.cluster.local:3306/wrongmove" + + } + # env { + # name = "HTTP_PROXY" + # value = "http://tor-proxy.tor-proxy:8118" + # } + # env { + # name = "HTTPS_PROXY" + # value = "http://tor-proxy.tor-proxy:8118" + # } + env { + name = "CELERY_BROKER_URL" + value = "redis://redis.redis.svc.cluster.local:6379/0" + } + env { + name = "CELERY_RESULT_BACKEND" + value = "redis://redis.redis.svc.cluster.local:6379/1" + } + + env { + name = "UVICORN_LOG_LEVEL" + value = "debug" + } + env { + name = "OSRM_FOOT_URL" + value = "http://osrm-foot.osm-routing.svc.cluster.local:5000" + } + env { + name = "OSRM_BICYCLE_URL" + value = "http://osrm-bicycle.osm-routing.svc.cluster.local:5000" + } + env { + name = "OTP_URL" + value = "http://otp.osm-routing.svc.cluster.local:8080" + } + env { + name = "SLACK_WEBHOOK_URL" + value = var.realestate_crawler_notification_settings["slack"] + } + env { + name = "WEBAUTHN_RP_ID" + value = "wrongmove.viktorbarzin.me" + } + env { + name = "WEBAUTHN_ORIGIN" + value = "https://wrongmove.viktorbarzin.me" + } + port { + name = "http" + container_port = 5001 + protocol = "TCP" + } + volume_mount { + name = "data" + mount_path = "/app/data" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/real-estate-crawler" + server = "10.0.10.15" + } + } + } + } + } + lifecycle { + ignore_changes = [ + spec[0].template[0].spec[0].container[0].image + ] + } +} +resource "kubernetes_service" "realestate-crawler-api" { + metadata { + name = "realestate-crawler-api" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + labels = { + "app" = "realestate-crawler-api" + } + } + + spec { + selector = { + app = "realestate-crawler-api" + } + port { + port = 80 + target_port = 5001 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + name = "wrongmove" + service_name = "realestate-crawler-ui" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "edee05de453d" +} + +module "ingress-api" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + name = "wrongmove-api" + host = "wrongmove" + service_name = "realestate-crawler-api" + ingress_path = ["/api"] + tls_secret_name = var.tls_secret_name +} + + +# Celery worker for background task processing +resource "kubernetes_deployment" "realestate-crawler-celery" { + metadata { + name = "realestate-crawler-celery" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + labels = { + app = "realestate-crawler-celery" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + strategy { + type = "RollingUpdate" + rolling_update { + max_unavailable = 0 + max_surge = 1 + } + } + selector { + match_labels = { + app = "realestate-crawler-celery" + } + } + template { + metadata { + labels = { + app = "realestate-crawler-celery" + } + } + spec { + container { + name = "celery-worker" + image = "viktorbarzin/realestatecrawler:latest" + image_pull_policy = "Always" + command = ["python", "-m", "celery", "-A", "celery_app", "worker", "--loglevel=info"] + port { + name = "metrics" + container_port = 9090 + protocol = "TCP" + } + env { + name = "ENV" + value = "prod" + } + env { + name = "DB_CONNECTION_STRING" + value = "mysql://wrongmove:${var.realestate_crawler_db_password}@mysql.dbaas.svc.cluster.local:3306/wrongmove" + } + env { + name = "CELERY_BROKER_URL" + value = "redis://redis.redis.svc.cluster.local:6379/0" + } + env { + name = "CELERY_RESULT_BACKEND" + value = "redis://redis.redis.svc.cluster.local:6379/1" + } + env { + name = "SLACK_WEBHOOK_URL" + value = lookup(var.realestate_crawler_notification_settings, "slack", "") + } + env { + name = "OSRM_FOOT_URL" + value = "http://osrm-foot.osm-routing.svc.cluster.local:5000" + } + env { + name = "OSRM_BICYCLE_URL" + value = "http://osrm-bicycle.osm-routing.svc.cluster.local:5000" + } + env { + name = "OTP_URL" + value = "http://otp.osm-routing.svc.cluster.local:8080" + } + volume_mount { + name = "data" + mount_path = "/app/data" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/real-estate-crawler" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "realestate-crawler-celery-metrics" { + metadata { + name = "realestate-crawler-celery-metrics" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + labels = { + "app" = "realestate-crawler-celery" + } + } + + spec { + selector = { + app = "realestate-crawler-celery" + } + port { + port = 9090 + target_port = 9090 + } + } +} + +# Celery beat for scheduled task management +resource "kubernetes_deployment" "realestate-crawler-celery-beat" { + metadata { + name = "realestate-crawler-celery-beat" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + labels = { + app = "realestate-crawler-celery-beat" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" # Only one beat instance should run at a time + } + selector { + match_labels = { + app = "realestate-crawler-celery-beat" + } + } + template { + metadata { + labels = { + app = "realestate-crawler-celery-beat" + } + } + spec { + container { + name = "celery-beat" + image = "viktorbarzin/realestatecrawler:latest" + command = ["python", "-m", "celery", "-A", "celery_app", "beat", "--loglevel=info"] + env { + name = "ENV" + value = "prod" + } + env { + name = "DB_CONNECTION_STRING" + value = "mysql://wrongmove:${var.realestate_crawler_db_password}@mysql.dbaas.svc.cluster.local:3306/wrongmove" + } + env { + name = "CELERY_BROKER_URL" + value = "redis://redis.redis.svc.cluster.local:6379/0" + } + env { + name = "CELERY_RESULT_BACKEND" + value = "redis://redis.redis.svc.cluster.local:6379/1" + } + env { + name = "SCRAPE_SCHEDULES" + value = lookup(var.realestate_crawler_notification_settings, "scrape_schedules", "") + } + volume_mount { + name = "data" + mount_path = "/app/data" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/real-estate-crawler" + server = "10.0.10.15" + } + } + } + } + } } diff --git a/stacks/real-estate-crawler/module/main.tf b/stacks/real-estate-crawler/module/main.tf deleted file mode 100644 index e77423b3..00000000 --- a/stacks/real-estate-crawler/module/main.tf +++ /dev/null @@ -1,431 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "notification_settings" { - type = map(string) - default = { - } -} -variable "db_password" {} - -resource "kubernetes_namespace" "realestate-crawler" { - metadata { - name = "realestate-crawler" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "realestate-crawler-ui" { - metadata { - name = "realestate-crawler-ui" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - labels = { - app = "realestate-crawler-ui" - tier = var.tier - } - } - spec { - replicas = 2 - strategy { - type = "RollingUpdate" - rolling_update { - max_unavailable = 0 - max_surge = 1 - } - } - selector { - match_labels = { - app = "realestate-crawler-ui" - } - } - template { - metadata { - labels = { - app = "realestate-crawler-ui" - } - } - spec { - container { - name = "realestate-crawler-ui" - image = "viktorbarzin/immoweb:latest" - port { - name = "http" - container_port = 8080 - protocol = "TCP" - } - env { - name = "ENV" - value = "prod" - } - } - } - } - } - lifecycle { - ignore_changes = [ - spec[0].template[0].spec[0].container[0].image - ] - } -} - -resource "kubernetes_service" "realestate-crawler-ui" { - metadata { - name = "realestate-crawler-ui" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - labels = { - "app" = "realestate-crawler-ui" - } - } - - spec { - selector = { - app = "realestate-crawler-ui" - } - port { - port = 80 - target_port = 8080 - } - } -} - -resource "kubernetes_deployment" "realestate-crawler-api" { - metadata { - name = "realestate-crawler-api" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - labels = { - app = "realestate-crawler-api" - tier = var.tier - } - } - spec { - replicas = 2 - strategy { - type = "RollingUpdate" - rolling_update { - max_unavailable = 0 - max_surge = 1 - } - } - selector { - match_labels = { - app = "realestate-crawler-api" - } - } - template { - metadata { - labels = { - app = "realestate-crawler-api" - "kubernetes.io/cluster-service" = "true" - } - } - spec { - container { - name = "realestate-crawler-api" - image = "viktorbarzin/realestatecrawler:latest" - image_pull_policy = "Always" - env { - name = "ENV" - value = "prod" - } - env { - name = "DB_CONNECTION_STRING" - value = "mysql://wrongmove:${var.db_password}@mysql.dbaas.svc.cluster.local:3306/wrongmove" - - } - # env { - # name = "HTTP_PROXY" - # value = "http://tor-proxy.tor-proxy:8118" - # } - # env { - # name = "HTTPS_PROXY" - # value = "http://tor-proxy.tor-proxy:8118" - # } - env { - name = "CELERY_BROKER_URL" - value = "redis://redis.redis.svc.cluster.local:6379/0" - } - env { - name = "CELERY_RESULT_BACKEND" - value = "redis://redis.redis.svc.cluster.local:6379/1" - } - - env { - name = "UVICORN_LOG_LEVEL" - value = "debug" - } - env { - name = "OSRM_FOOT_URL" - value = "http://osrm-foot.osm-routing.svc.cluster.local:5000" - } - env { - name = "OSRM_BICYCLE_URL" - value = "http://osrm-bicycle.osm-routing.svc.cluster.local:5000" - } - env { - name = "OTP_URL" - value = "http://otp.osm-routing.svc.cluster.local:8080" - } - env { - name = "SLACK_WEBHOOK_URL" - value = var.notification_settings["slack"] - } - env { - name = "WEBAUTHN_RP_ID" - value = "wrongmove.viktorbarzin.me" - } - env { - name = "WEBAUTHN_ORIGIN" - value = "https://wrongmove.viktorbarzin.me" - } - port { - name = "http" - container_port = 5001 - protocol = "TCP" - } - volume_mount { - name = "data" - mount_path = "/app/data" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/real-estate-crawler" - server = "10.0.10.15" - } - } - } - } - } - lifecycle { - ignore_changes = [ - spec[0].template[0].spec[0].container[0].image - ] - } -} -resource "kubernetes_service" "realestate-crawler-api" { - metadata { - name = "realestate-crawler-api" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - labels = { - "app" = "realestate-crawler-api" - } - } - - spec { - selector = { - app = "realestate-crawler-api" - } - port { - port = 80 - target_port = 5001 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - name = "wrongmove" - service_name = "realestate-crawler-ui" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "edee05de453d" -} - -module "ingress-api" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - name = "wrongmove-api" - host = "wrongmove" - service_name = "realestate-crawler-api" - ingress_path = ["/api"] - tls_secret_name = var.tls_secret_name -} - - -# Celery worker for background task processing -resource "kubernetes_deployment" "realestate-crawler-celery" { - metadata { - name = "realestate-crawler-celery" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - labels = { - app = "realestate-crawler-celery" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "RollingUpdate" - rolling_update { - max_unavailable = 0 - max_surge = 1 - } - } - selector { - match_labels = { - app = "realestate-crawler-celery" - } - } - template { - metadata { - labels = { - app = "realestate-crawler-celery" - } - } - spec { - container { - name = "celery-worker" - image = "viktorbarzin/realestatecrawler:latest" - image_pull_policy = "Always" - command = ["python", "-m", "celery", "-A", "celery_app", "worker", "--loglevel=info"] - port { - name = "metrics" - container_port = 9090 - protocol = "TCP" - } - env { - name = "ENV" - value = "prod" - } - env { - name = "DB_CONNECTION_STRING" - value = "mysql://wrongmove:${var.db_password}@mysql.dbaas.svc.cluster.local:3306/wrongmove" - } - env { - name = "CELERY_BROKER_URL" - value = "redis://redis.redis.svc.cluster.local:6379/0" - } - env { - name = "CELERY_RESULT_BACKEND" - value = "redis://redis.redis.svc.cluster.local:6379/1" - } - env { - name = "SLACK_WEBHOOK_URL" - value = lookup(var.notification_settings, "slack", "") - } - env { - name = "OSRM_FOOT_URL" - value = "http://osrm-foot.osm-routing.svc.cluster.local:5000" - } - env { - name = "OSRM_BICYCLE_URL" - value = "http://osrm-bicycle.osm-routing.svc.cluster.local:5000" - } - env { - name = "OTP_URL" - value = "http://otp.osm-routing.svc.cluster.local:8080" - } - volume_mount { - name = "data" - mount_path = "/app/data" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/real-estate-crawler" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "realestate-crawler-celery-metrics" { - metadata { - name = "realestate-crawler-celery-metrics" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - labels = { - "app" = "realestate-crawler-celery" - } - } - - spec { - selector = { - app = "realestate-crawler-celery" - } - port { - port = 9090 - target_port = 9090 - } - } -} - -# Celery beat for scheduled task management -resource "kubernetes_deployment" "realestate-crawler-celery-beat" { - metadata { - name = "realestate-crawler-celery-beat" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - labels = { - app = "realestate-crawler-celery-beat" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" # Only one beat instance should run at a time - } - selector { - match_labels = { - app = "realestate-crawler-celery-beat" - } - } - template { - metadata { - labels = { - app = "realestate-crawler-celery-beat" - } - } - spec { - container { - name = "celery-beat" - image = "viktorbarzin/realestatecrawler:latest" - command = ["python", "-m", "celery", "-A", "celery_app", "beat", "--loglevel=info"] - env { - name = "ENV" - value = "prod" - } - env { - name = "DB_CONNECTION_STRING" - value = "mysql://wrongmove:${var.db_password}@mysql.dbaas.svc.cluster.local:3306/wrongmove" - } - env { - name = "CELERY_BROKER_URL" - value = "redis://redis.redis.svc.cluster.local:6379/0" - } - env { - name = "CELERY_RESULT_BACKEND" - value = "redis://redis.redis.svc.cluster.local:6379/1" - } - env { - name = "SCRAPE_SCHEDULES" - value = lookup(var.notification_settings, "scrape_schedules", "") - } - volume_mount { - name = "data" - mount_path = "/app/data" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/real-estate-crawler" - server = "10.0.10.15" - } - } - } - } - } -} - diff --git a/stacks/reloader/main.tf b/stacks/reloader/main.tf index 16cce949..da1395ab 100644 --- a/stacks/reloader/main.tf +++ b/stacks/reloader/main.tf @@ -8,7 +8,20 @@ locals { } } -module "reloader" { - source = "./module" - tier = local.tiers.aux +resource "kubernetes_namespace" "crowdsec" { + metadata { + name = "reloader" + labels = { + tier = local.tiers.aux + } + } +} +resource "helm_release" "reloader" { + namespace = kubernetes_namespace.crowdsec.metadata[0].name + create_namespace = false + name = "reloader" + atomic = true + + repository = "https://stakater.github.io/stakater-charts" + chart = "reloader" } diff --git a/stacks/reloader/module/main.tf b/stacks/reloader/module/main.tf deleted file mode 100644 index f220b799..00000000 --- a/stacks/reloader/module/main.tf +++ /dev/null @@ -1,19 +0,0 @@ -variable "tier" { type = string } - -resource "kubernetes_namespace" "crowdsec" { - metadata { - name = "reloader" - labels = { - tier = var.tier - } - } -} -resource "helm_release" "reloader" { - namespace = kubernetes_namespace.crowdsec.metadata[0].name - create_namespace = false - name = "reloader" - atomic = true - - repository = "https://stakater.github.io/stakater-charts" - chart = "reloader" -} diff --git a/stacks/resume/main.tf b/stacks/resume/main.tf index 5b8403f3..f091fdc0 100644 --- a/stacks/resume/main.tf +++ b/stacks/resume/main.tf @@ -13,11 +13,280 @@ locals { } } -module "resume" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux - database_url = var.resume_database_url - auth_secret = var.resume_auth_secret - smtp_password = var.mailserver_accounts["info@viktorbarzin.me"] +locals { + namespace = "resume" + app_url = "https://resume.viktorbarzin.me" +} + +resource "kubernetes_namespace" "resume" { + metadata { + name = local.namespace + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.resume.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +# Printer service (browserless chromium for PDF generation) +resource "kubernetes_deployment" "printer" { + metadata { + name = "printer" + namespace = kubernetes_namespace.resume.metadata[0].name + labels = { + app = "printer" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "printer" + } + } + template { + metadata { + labels = { + app = "printer" + } + } + spec { + container { + name = "printer" + image = "ghcr.io/browserless/chromium:latest" + + port { + container_port = 3000 + } + + env { + name = "HEALTH" + value = "true" + } + env { + name = "CONCURRENT" + value = "20" + } + env { + name = "QUEUED" + value = "10" + } + + resources { + requests = { + memory = "256Mi" + cpu = "100m" + } + limits = { + memory = "2Gi" + cpu = "2" + } + } + + liveness_probe { + http_get { + path = "/pressure" + port = 3000 + } + initial_delay_seconds = 30 + period_seconds = 10 + timeout_seconds = 5 + } + readiness_probe { + http_get { + path = "/pressure" + port = 3000 + } + initial_delay_seconds = 10 + period_seconds = 10 + timeout_seconds = 5 + } + } + } + } + } +} + +resource "kubernetes_service" "printer" { + metadata { + name = "printer" + namespace = kubernetes_namespace.resume.metadata[0].name + } + spec { + selector = { + app = "printer" + } + port { + port = 3000 + target_port = 3000 + } + } +} + +# Reactive Resume app +resource "kubernetes_deployment" "resume" { + metadata { + name = "resume" + namespace = kubernetes_namespace.resume.metadata[0].name + labels = { + app = "resume" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "resume" + } + } + template { + metadata { + labels = { + app = "resume" + } + } + spec { + container { + name = "resume" + image = "amruthpillai/reactive-resume:v5.0.3" + + port { + container_port = 3000 + } + + # Required env vars + env { + name = "APP_URL" + value = local.app_url + } + env { + name = "DATABASE_URL" + value = var.resume_database_url + } + env { + name = "PRINTER_ENDPOINT" + value = "ws://printer.${local.namespace}.svc.cluster.local:3000" + } + env { + name = "PRINTER_APP_URL" + value = "http://resume.${local.namespace}.svc.cluster.local" + } + env { + name = "AUTH_SECRET" + value = var.resume_auth_secret + } + + # Server config + env { + name = "TZ" + value = "Etc/UTC" + } + + # SMTP config for password reset emails + env { + name = "SMTP_HOST" + value = "mail.viktorbarzin.me" + } + env { + name = "SMTP_PORT" + value = "587" + } + env { + name = "SMTP_USER" + value = "info@viktorbarzin.me" + } + env { + name = "SMTP_PASS" + value = var.mailserver_accounts["info@viktorbarzin.me"] + } + env { + name = "SMTP_FROM" + value = "Reactive Resume " + } + env { + name = "SMTP_SECURE" + value = "false" + } + + # Feature flags + env { + name = "FLAG_DISABLE_SIGNUPS" + value = "false" # toggle me + } + + volume_mount { + name = "data" + mount_path = "/app/data" + } + + resources { + requests = { + memory = "256Mi" + cpu = "100m" + } + limits = { + memory = "1Gi" + cpu = "1" + } + } + + liveness_probe { + http_get { + path = "/api/health" + port = 3000 + } + initial_delay_seconds = 60 + period_seconds = 30 + timeout_seconds = 10 + } + readiness_probe { + http_get { + path = "/api/health" + port = 3000 + } + initial_delay_seconds = 30 + period_seconds = 10 + timeout_seconds = 5 + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/resume" + } + } + } + } + } +} + +resource "kubernetes_service" "resume" { + metadata { + name = "resume" + namespace = kubernetes_namespace.resume.metadata[0].name + } + spec { + selector = { + app = "resume" + } + port { + port = 80 + target_port = 3000 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.resume.metadata[0].name + name = "resume" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/resume/module/main.tf b/stacks/resume/module/main.tf deleted file mode 100644 index 8c2c623c..00000000 --- a/stacks/resume/module/main.tf +++ /dev/null @@ -1,283 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "database_url" { type = string } -variable "auth_secret" { type = string } -variable "smtp_password" { type = string } - -locals { - namespace = "resume" - app_url = "https://resume.viktorbarzin.me" -} - -resource "kubernetes_namespace" "resume" { - metadata { - name = local.namespace - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.resume.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -# Printer service (browserless chromium for PDF generation) -resource "kubernetes_deployment" "printer" { - metadata { - name = "printer" - namespace = kubernetes_namespace.resume.metadata[0].name - labels = { - app = "printer" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "printer" - } - } - template { - metadata { - labels = { - app = "printer" - } - } - spec { - container { - name = "printer" - image = "ghcr.io/browserless/chromium:latest" - - port { - container_port = 3000 - } - - env { - name = "HEALTH" - value = "true" - } - env { - name = "CONCURRENT" - value = "20" - } - env { - name = "QUEUED" - value = "10" - } - - resources { - requests = { - memory = "256Mi" - cpu = "100m" - } - limits = { - memory = "2Gi" - cpu = "2" - } - } - - liveness_probe { - http_get { - path = "/pressure" - port = 3000 - } - initial_delay_seconds = 30 - period_seconds = 10 - timeout_seconds = 5 - } - readiness_probe { - http_get { - path = "/pressure" - port = 3000 - } - initial_delay_seconds = 10 - period_seconds = 10 - timeout_seconds = 5 - } - } - } - } - } -} - -resource "kubernetes_service" "printer" { - metadata { - name = "printer" - namespace = kubernetes_namespace.resume.metadata[0].name - } - spec { - selector = { - app = "printer" - } - port { - port = 3000 - target_port = 3000 - } - } -} - -# Reactive Resume app -resource "kubernetes_deployment" "resume" { - metadata { - name = "resume" - namespace = kubernetes_namespace.resume.metadata[0].name - labels = { - app = "resume" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "resume" - } - } - template { - metadata { - labels = { - app = "resume" - } - } - spec { - container { - name = "resume" - image = "amruthpillai/reactive-resume:v5.0.3" - - port { - container_port = 3000 - } - - # Required env vars - env { - name = "APP_URL" - value = local.app_url - } - env { - name = "DATABASE_URL" - value = var.database_url - } - env { - name = "PRINTER_ENDPOINT" - value = "ws://printer.${local.namespace}.svc.cluster.local:3000" - } - env { - name = "PRINTER_APP_URL" - value = "http://resume.${local.namespace}.svc.cluster.local" - } - env { - name = "AUTH_SECRET" - value = var.auth_secret - } - - # Server config - env { - name = "TZ" - value = "Etc/UTC" - } - - # SMTP config for password reset emails - env { - name = "SMTP_HOST" - value = "mail.viktorbarzin.me" - } - env { - name = "SMTP_PORT" - value = "587" - } - env { - name = "SMTP_USER" - value = "info@viktorbarzin.me" - } - env { - name = "SMTP_PASS" - value = var.smtp_password - } - env { - name = "SMTP_FROM" - value = "Reactive Resume " - } - env { - name = "SMTP_SECURE" - value = "false" - } - - # Feature flags - env { - name = "FLAG_DISABLE_SIGNUPS" - value = "false" # toggle me - } - - volume_mount { - name = "data" - mount_path = "/app/data" - } - - resources { - requests = { - memory = "256Mi" - cpu = "100m" - } - limits = { - memory = "1Gi" - cpu = "1" - } - } - - liveness_probe { - http_get { - path = "/api/health" - port = 3000 - } - initial_delay_seconds = 60 - period_seconds = 30 - timeout_seconds = 10 - } - readiness_probe { - http_get { - path = "/api/health" - port = 3000 - } - initial_delay_seconds = 30 - period_seconds = 10 - timeout_seconds = 5 - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/resume" - } - } - } - } - } -} - -resource "kubernetes_service" "resume" { - metadata { - name = "resume" - namespace = kubernetes_namespace.resume.metadata[0].name - } - spec { - selector = { - app = "resume" - } - port { - port = 80 - target_port = 3000 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.resume.metadata[0].name - name = "resume" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/rybbit/main.tf b/stacks/rybbit/main.tf index bbb5906c..794fbe36 100644 --- a/stacks/rybbit/main.tf +++ b/stacks/rybbit/main.tf @@ -12,10 +12,307 @@ locals { } } -module "rybbit" { - source = "./module" - tls_secret_name = var.tls_secret_name - clickhouse_password = var.clickhouse_password - postgres_password = var.clickhouse_postgres_password - tier = local.tiers.aux +resource "kubernetes_namespace" "rybbit" { + metadata { + name = "rybbit" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.rybbit.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "random_string" "random" { + length = 32 + lower = true +} + +locals { + clickhouse_db = "clickhouse" +} + + +resource "kubernetes_deployment" "clickhouse" { + metadata { + name = "clickhouse" + namespace = kubernetes_namespace.rybbit.metadata[0].name + labels = { + app = "clickhouse" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "clickhouse" + } + } + template { + metadata { + labels = { + app = "clickhouse" + } + } + spec { + container { + name = "clickhouse" + image = "clickhouse/clickhouse-server:25.4.2" + env { + name = "CLICKHOUSE_DB" + value = local.clickhouse_db + } + # env { + # name = "CLICKHOUSE_USER" + # value = "clickhouse" + # } + env { + name = "CLICKHOUSE_PASSWORD" + value = var.clickhouse_password + } + port { + name = "clickhouse" + protocol = "TCP" + container_port = 8123 + } + volume_mount { + name = "data" + mount_path = "/var/lib/clickhouse" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/clickhouse" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "clickhouse" { + metadata { + name = "clickhouse" + namespace = kubernetes_namespace.rybbit.metadata[0].name + labels = { + "app" = "clickhouse" + } + } + + spec { + selector = { + app = "clickhouse" + } + port { + name = "http" + target_port = 8123 + port = 8123 + protocol = "TCP" + } + } +} + +resource "kubernetes_deployment" "rybbit" { + metadata { + name = "rybbit" + namespace = kubernetes_namespace.rybbit.metadata[0].name + labels = { + app = "rybbit" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "rybbit" + } + } + template { + metadata { + labels = { + app = "rybbit" + } + } + spec { + container { + image = "ghcr.io/rybbit-io/rybbit-backend:latest" + name = "rybbit" + + env { + name = "NODE_ENV" + value = "production" + } + env { + name = "CLICKHOUSE_HOST" + value = "http://clickhouse.rybbit.svc.cluster.local:8123" + } + env { + name = "CLICKHOUSE_DB" + value = local.clickhouse_db + } + env { + name = "CLICKHOUSE_USER" + value = "default" + } + env { + name = "CLICKHOUSE_PASSWORD" + value = var.clickhouse_password + } + env { + name = "POSTGRES_HOST" + value = "postgresql.dbaas.svc.cluster.local" + } + env { + name = "POSTGRES_PORT" + value = "5432" + } + env { + name = "POSTGRES_DB" + value = "rybbit" + } + env { + name = "POSTGRES_USER" + value = "rybbit" + } + env { + name = "POSTGRES_PASSWORD" + value = var.clickhouse_postgres_password + } + env { + name = "BASE_URL" + value = "https://rybbit.viktorbarzin.me" + } + env { + name = "DISABLE_SIGNUP" + value = true + } + env { + name = "BETTER_AUTH_SECRET" + value = random_string.random.result + } + env { + name = "AUTH_ENABLED" + value = true + } + port { + container_port = 3001 + } + } + } + } + } +} + +resource "kubernetes_service" "rybbit" { + metadata { + name = "rybbit" + namespace = kubernetes_namespace.rybbit.metadata[0].name + labels = { + "app" = "rybbit" + } + } + + spec { + selector = { + "app" = "rybbit" + } + port { + name = "http" + port = 80 + target_port = 3001 + } + } +} + +resource "kubernetes_deployment" "rybbit-client" { + metadata { + name = "rybbit-client" + namespace = kubernetes_namespace.rybbit.metadata[0].name + labels = { + app = "rybbit-client" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "rybbit-client" + } + } + template { + metadata { + labels = { + app = "rybbit-client" + } + } + spec { + container { + name = "rybbit-client" + image = "ghcr.io/rybbit-io/rybbit-client:latest" + env { + name = "NODE_ENV" + value = "production" + } + env { + name = "DISABLE_SIGNUP" + value = true + } + port { + name = "rybbit-client" + protocol = "TCP" + container_port = 3002 + } + } + } + } + } +} + +resource "kubernetes_service" "rybbit-client" { + metadata { + name = "rybbit-client" + namespace = kubernetes_namespace.rybbit.metadata[0].name + labels = { + "app" = "rybbit-client" + } + } + + spec { + selector = { + "app" = "rybbit-client" + } + port { + name = "http" + port = 80 + target_port = 3002 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.rybbit.metadata[0].name + name = "rybbit" + service_name = "rybbit-client" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "3c476801a777" +} + +module "ingress-api" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.rybbit.metadata[0].name + name = "rybbit-api" + host = "rybbit" + service_name = "rybbit" + ingress_path = ["/api"] + tls_secret_name = var.tls_secret_name } diff --git a/stacks/rybbit/module/main.tf b/stacks/rybbit/module/main.tf deleted file mode 100644 index d713a666..00000000 --- a/stacks/rybbit/module/main.tf +++ /dev/null @@ -1,309 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "clickhouse_password" { type = string } -variable "postgres_password" { type = string } - -resource "kubernetes_namespace" "rybbit" { - metadata { - name = "rybbit" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.rybbit.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "random_string" "random" { - length = 32 - lower = true -} - -locals { - clickhouse_db = "clickhouse" -} - - -resource "kubernetes_deployment" "clickhouse" { - metadata { - name = "clickhouse" - namespace = kubernetes_namespace.rybbit.metadata[0].name - labels = { - app = "clickhouse" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "clickhouse" - } - } - template { - metadata { - labels = { - app = "clickhouse" - } - } - spec { - container { - name = "clickhouse" - image = "clickhouse/clickhouse-server:25.4.2" - env { - name = "CLICKHOUSE_DB" - value = local.clickhouse_db - } - # env { - # name = "CLICKHOUSE_USER" - # value = "clickhouse" - # } - env { - name = "CLICKHOUSE_PASSWORD" - value = var.clickhouse_password - } - port { - name = "clickhouse" - protocol = "TCP" - container_port = 8123 - } - volume_mount { - name = "data" - mount_path = "/var/lib/clickhouse" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/clickhouse" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "clickhouse" { - metadata { - name = "clickhouse" - namespace = kubernetes_namespace.rybbit.metadata[0].name - labels = { - "app" = "clickhouse" - } - } - - spec { - selector = { - app = "clickhouse" - } - port { - name = "http" - target_port = 8123 - port = 8123 - protocol = "TCP" - } - } -} - -resource "kubernetes_deployment" "rybbit" { - metadata { - name = "rybbit" - namespace = kubernetes_namespace.rybbit.metadata[0].name - labels = { - app = "rybbit" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "rybbit" - } - } - template { - metadata { - labels = { - app = "rybbit" - } - } - spec { - container { - image = "ghcr.io/rybbit-io/rybbit-backend:latest" - name = "rybbit" - - env { - name = "NODE_ENV" - value = "production" - } - env { - name = "CLICKHOUSE_HOST" - value = "http://clickhouse.rybbit.svc.cluster.local:8123" - } - env { - name = "CLICKHOUSE_DB" - value = local.clickhouse_db - } - env { - name = "CLICKHOUSE_USER" - value = "default" - } - env { - name = "CLICKHOUSE_PASSWORD" - value = var.clickhouse_password - } - env { - name = "POSTGRES_HOST" - value = "postgresql.dbaas.svc.cluster.local" - } - env { - name = "POSTGRES_PORT" - value = "5432" - } - env { - name = "POSTGRES_DB" - value = "rybbit" - } - env { - name = "POSTGRES_USER" - value = "rybbit" - } - env { - name = "POSTGRES_PASSWORD" - value = var.postgres_password - } - env { - name = "BASE_URL" - value = "https://rybbit.viktorbarzin.me" - } - env { - name = "DISABLE_SIGNUP" - value = true - } - env { - name = "BETTER_AUTH_SECRET" - value = random_string.random.result - } - env { - name = "AUTH_ENABLED" - value = true - } - port { - container_port = 3001 - } - } - } - } - } -} - -resource "kubernetes_service" "rybbit" { - metadata { - name = "rybbit" - namespace = kubernetes_namespace.rybbit.metadata[0].name - labels = { - "app" = "rybbit" - } - } - - spec { - selector = { - "app" = "rybbit" - } - port { - name = "http" - port = 80 - target_port = 3001 - } - } -} - -resource "kubernetes_deployment" "rybbit-client" { - metadata { - name = "rybbit-client" - namespace = kubernetes_namespace.rybbit.metadata[0].name - labels = { - app = "rybbit-client" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "rybbit-client" - } - } - template { - metadata { - labels = { - app = "rybbit-client" - } - } - spec { - container { - name = "rybbit-client" - image = "ghcr.io/rybbit-io/rybbit-client:latest" - env { - name = "NODE_ENV" - value = "production" - } - env { - name = "DISABLE_SIGNUP" - value = true - } - port { - name = "rybbit-client" - protocol = "TCP" - container_port = 3002 - } - } - } - } - } -} - -resource "kubernetes_service" "rybbit-client" { - metadata { - name = "rybbit-client" - namespace = kubernetes_namespace.rybbit.metadata[0].name - labels = { - "app" = "rybbit-client" - } - } - - spec { - selector = { - "app" = "rybbit-client" - } - port { - name = "http" - port = 80 - target_port = 3002 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.rybbit.metadata[0].name - name = "rybbit" - service_name = "rybbit-client" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "3c476801a777" -} - -module "ingress-api" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.rybbit.metadata[0].name - name = "rybbit-api" - host = "rybbit" - service_name = "rybbit" - ingress_path = ["/api"] - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/send/main.tf b/stacks/send/main.tf index 85ce0e9f..f1b43931 100644 --- a/stacks/send/main.tf +++ b/stacks/send/main.tf @@ -10,8 +10,119 @@ locals { } } -module "send" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "send" { + metadata { + name = "send" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.send.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "send" { + metadata { + name = "send" + namespace = kubernetes_namespace.send.metadata[0].name + labels = { + app = "send" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "send" + } + } + template { + metadata { + labels = { + app = "send" + } + } + spec { + container { + image = "registry.gitlab.com/timvisee/send:latest" + name = "send" + + port { + container_port = 1443 + } + env { + name = "FILE_DIR" + value = "/uploads" + } + env { + name = "BASE_URL" + value = "https://send.viktorbarzin.me" + } + env { + name = "MAX_FILE_SIZE" + value = "5368709120" + } + env { + name = "MAX_DOWNLOADS" + value = 10 # try to minimize abusive behaviour + } + env { + name = "MAX_EXPIRE_SECONDS" + value = 7 * 24 * 3600 + } + env { + name = "REDIS_HOST" + value = "redis.redis.svc.cluster.local" + } + volume_mount { + name = "data" + mount_path = "/uploads" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/send" + server = "10.0.10.15" + } + } + } + } + } +} +resource "kubernetes_service" "send" { + metadata { + name = "send" + namespace = kubernetes_namespace.send.metadata[0].name + labels = { + app = "send" + } + } + + spec { + selector = { + app = "send" + } + port { + name = "http" + port = 1443 + } + } +} +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.send.metadata[0].name + name = "send" + tls_secret_name = var.tls_secret_name + port = 1443 + rybbit_site_id = "c1b8f8aa831b" } diff --git a/stacks/send/module/main.tf b/stacks/send/module/main.tf deleted file mode 100644 index b3090aba..00000000 --- a/stacks/send/module/main.tf +++ /dev/null @@ -1,119 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "send" { - metadata { - name = "send" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.send.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "send" { - metadata { - name = "send" - namespace = kubernetes_namespace.send.metadata[0].name - labels = { - app = "send" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "send" - } - } - template { - metadata { - labels = { - app = "send" - } - } - spec { - container { - image = "registry.gitlab.com/timvisee/send:latest" - name = "send" - - port { - container_port = 1443 - } - env { - name = "FILE_DIR" - value = "/uploads" - } - env { - name = "BASE_URL" - value = "https://send.viktorbarzin.me" - } - env { - name = "MAX_FILE_SIZE" - value = "5368709120" - } - env { - name = "MAX_DOWNLOADS" - value = 10 # try to minimize abusive behaviour - } - env { - name = "MAX_EXPIRE_SECONDS" - value = 7 * 24 * 3600 - } - env { - name = "REDIS_HOST" - value = "redis.redis.svc.cluster.local" - } - volume_mount { - name = "data" - mount_path = "/uploads" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/send" - server = "10.0.10.15" - } - } - } - } - } -} -resource "kubernetes_service" "send" { - metadata { - name = "send" - namespace = kubernetes_namespace.send.metadata[0].name - labels = { - app = "send" - } - } - - spec { - selector = { - app = "send" - } - port { - name = "http" - port = 1443 - } - } -} -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.send.metadata[0].name - name = "send" - tls_secret_name = var.tls_secret_name - port = 1443 - rybbit_site_id = "c1b8f8aa831b" -} diff --git a/stacks/servarr/module/aiostreams/main.tf b/stacks/servarr/aiostreams/main.tf similarity index 96% rename from stacks/servarr/module/aiostreams/main.tf rename to stacks/servarr/aiostreams/main.tf index cbcbd957..1ec5e4ae 100644 --- a/stacks/servarr/module/aiostreams/main.tf +++ b/stacks/servarr/aiostreams/main.tf @@ -95,7 +95,7 @@ resource "kubernetes_service" "aiostreams" { } module "ingress" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = kubernetes_namespace.aiostreams.metadata[0].name name = "aiostreams" tls_secret_name = var.tls_secret_name diff --git a/stacks/servarr/module/flaresolverr/main.tf b/stacks/servarr/flaresolverr/main.tf similarity index 95% rename from stacks/servarr/module/flaresolverr/main.tf rename to stacks/servarr/flaresolverr/main.tf index 41f205c6..6fa648ab 100644 --- a/stacks/servarr/module/flaresolverr/main.tf +++ b/stacks/servarr/flaresolverr/main.tf @@ -62,7 +62,7 @@ resource "kubernetes_service" "flaresolverr" { } module "ingress" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = "servarr" name = "flaresolverr" tls_secret_name = var.tls_secret_name diff --git a/stacks/servarr/module/lidarr/main.tf b/stacks/servarr/lidarr/main.tf similarity index 95% rename from stacks/servarr/module/lidarr/main.tf rename to stacks/servarr/lidarr/main.tf index 19df0491..b851ca18 100644 --- a/stacks/servarr/module/lidarr/main.tf +++ b/stacks/servarr/lidarr/main.tf @@ -143,7 +143,7 @@ resource "kubernetes_service" "deemix" { module "ingress" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = "servarr" name = "lidarr" tls_secret_name = var.tls_secret_name @@ -155,7 +155,7 @@ module "ingress" { } module "ingress-deemix" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = "servarr" name = "deemix" tls_secret_name = var.tls_secret_name diff --git a/stacks/servarr/module/listenarr/main.tf b/stacks/servarr/listenarr/main.tf similarity index 95% rename from stacks/servarr/module/listenarr/main.tf rename to stacks/servarr/listenarr/main.tf index b5f090f2..2b371421 100644 --- a/stacks/servarr/module/listenarr/main.tf +++ b/stacks/servarr/listenarr/main.tf @@ -82,7 +82,7 @@ resource "kubernetes_service" "listenarr" { module "ingress" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = "servarr" name = "listenarr" tls_secret_name = var.tls_secret_name diff --git a/stacks/servarr/main.tf b/stacks/servarr/main.tf index e997e20d..8498fcc8 100644 --- a/stacks/servarr/main.tf +++ b/stacks/servarr/main.tf @@ -11,9 +11,67 @@ locals { } } -module "servarr" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux - aiostreams_database_connection_string = var.aiostreams_database_connection_string +resource "kubernetes_namespace" "servarr" { + metadata { + name = "servarr" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.servarr.metadata[0].name + tls_secret_name = var.tls_secret_name +} + + +# module "readarr" { +# source = "./readarr" +# tls_secret_name = var.tls_secret_name +# tier = local.tiers.aux +# } + +module "prowlarr" { + source = "./prowlarr" + tls_secret_name = var.tls_secret_name + tier = local.tiers.aux +} + +module "qbittorrent" { + source = "./qbittorrent" + tls_secret_name = var.tls_secret_name + tier = local.tiers.aux +} + +module "flaresolverr" { + source = "./flaresolverr" + tls_secret_name = var.tls_secret_name + tier = local.tiers.aux +} + +# module "lidarr" { +# source = "./lidarr" +# tls_secret_name = var.tls_secret_name +# tier = local.tiers.aux +# } + +# module "soulseek" { +# source = "./soulseek" +# tls_secret_name = var.tls_secret_name +# tier = local.tiers.aux +# } + +module "listenarr" { + source = "./listenarr" + tls_secret_name = var.tls_secret_name + tier = local.tiers.aux +} + +module "aiostreams" { + source = "./aiostreams" + tls_secret_name = var.tls_secret_name + aiostreams_database_connection_string = var.aiostreams_database_connection_string + tier = local.tiers.aux } diff --git a/stacks/servarr/module/main.tf b/stacks/servarr/module/main.tf deleted file mode 100644 index 6d802450..00000000 --- a/stacks/servarr/module/main.tf +++ /dev/null @@ -1,68 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "aiostreams_database_connection_string" { type = string } - -resource "kubernetes_namespace" "servarr" { - metadata { - name = "servarr" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.servarr.metadata[0].name - tls_secret_name = var.tls_secret_name -} - - -# module "readarr" { -# source = "./readarr" -# tls_secret_name = var.tls_secret_name -# tier = var.tier -# } - -module "prowlarr" { - source = "./prowlarr" - tls_secret_name = var.tls_secret_name - tier = var.tier -} - -module "qbittorrent" { - source = "./qbittorrent" - tls_secret_name = var.tls_secret_name - tier = var.tier -} - -module "flaresolverr" { - source = "./flaresolverr" - tls_secret_name = var.tls_secret_name - tier = var.tier -} - -# module "lidarr" { -# source = "./lidarr" -# tls_secret_name = var.tls_secret_name -# tier = var.tier -# } - -# module "soulseek" { -# source = "./soulseek" -# tls_secret_name = var.tls_secret_name -# tier = var.tier -# } - -module "listenarr" { - source = "./listenarr" - tls_secret_name = var.tls_secret_name - tier = var.tier -} - -module "aiostreams" { - source = "./aiostreams" - tls_secret_name = var.tls_secret_name - aiostreams_database_connection_string = var.aiostreams_database_connection_string - tier = var.tier -} diff --git a/stacks/servarr/module/prowlarr/main.tf b/stacks/servarr/prowlarr/main.tf similarity index 96% rename from stacks/servarr/module/prowlarr/main.tf rename to stacks/servarr/prowlarr/main.tf index 0e9c60f6..78bfebfc 100644 --- a/stacks/servarr/module/prowlarr/main.tf +++ b/stacks/servarr/prowlarr/main.tf @@ -102,7 +102,7 @@ resource "kubernetes_service" "prowlarr" { module "ingress" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = "servarr" name = "prowlarr" tls_secret_name = var.tls_secret_name diff --git a/stacks/servarr/module/qbittorrent/main.tf b/stacks/servarr/qbittorrent/main.tf similarity index 97% rename from stacks/servarr/module/qbittorrent/main.tf rename to stacks/servarr/qbittorrent/main.tf index 5bc71817..41976bfd 100644 --- a/stacks/servarr/module/qbittorrent/main.tf +++ b/stacks/servarr/qbittorrent/main.tf @@ -135,7 +135,7 @@ resource "kubernetes_service" "qbittorrent-torrenting" { module "ingress" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = "servarr" name = "qbittorrent" tls_secret_name = var.tls_secret_name diff --git a/stacks/servarr/module/readarr/main.tf b/stacks/servarr/readarr/main.tf similarity index 94% rename from stacks/servarr/module/readarr/main.tf rename to stacks/servarr/readarr/main.tf index 987aeb7f..e58dc4db 100644 --- a/stacks/servarr/module/readarr/main.tf +++ b/stacks/servarr/readarr/main.tf @@ -11,7 +11,7 @@ resource "kubernetes_namespace" "readarr" { module "tls_secret" { - source = "../../../../modules/kubernetes/setup_tls_secret" + source = "../../../modules/kubernetes/setup_tls_secret" namespace = "readarr" tls_secret_name = var.tls_secret_name } @@ -119,7 +119,7 @@ resource "kubernetes_service" "readarr" { } module "ingress" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = "readarr" name = "readarr" port = 8787 diff --git a/stacks/servarr/module/soulseek/main.tf b/stacks/servarr/soulseek/main.tf similarity index 96% rename from stacks/servarr/module/soulseek/main.tf rename to stacks/servarr/soulseek/main.tf index 79c8447e..eaf11b79 100644 --- a/stacks/servarr/module/soulseek/main.tf +++ b/stacks/servarr/soulseek/main.tf @@ -97,7 +97,7 @@ resource "kubernetes_service" "soulseek" { module "ingress" { - source = "../../../../modules/kubernetes/ingress_factory" + source = "../../../modules/kubernetes/ingress_factory" namespace = "servarr" name = "soulseek" tls_secret_name = var.tls_secret_name diff --git a/stacks/shadowsocks/main.tf b/stacks/shadowsocks/main.tf index 7b6fafd8..9a036079 100644 --- a/stacks/shadowsocks/main.tf +++ b/stacks/shadowsocks/main.tf @@ -10,8 +10,107 @@ locals { } } -module "shadowsocks" { - source = "./module" - password = var.shadowsocks_password - tier = local.tiers.edge +variable "method" { + default = "chacha20-ietf-poly1305" +} + +resource "kubernetes_namespace" "shadowsocks" { + metadata { + name = "shadowsocks" + labels = { + tier = local.tiers.edge + } + # TLS termination seems iffy - I get pfsense MiTM-ing + # labels = { + # "istio-injection" : "enabled" + # } + } +} + +resource "kubernetes_deployment" "shadowsocks" { + metadata { + name = "shadowsocks" + namespace = kubernetes_namespace.shadowsocks.metadata[0].name + labels = { + "app" = "shadowsocks" + tier = local.tiers.edge + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = "1" + selector { + match_labels = { + "app" = "shadowsocks" + } + } + template { + metadata { + labels = { + "app" = "shadowsocks" + } + } + spec { + container { + name = "shadowsocks" + image = "shadowsocks/shadowsocks-libev" + image_pull_policy = "IfNotPresent" + env { + name = "METHOD" + value = var.method + } + env { + name = "PASSWORD" + value = var.shadowsocks_password + } + port { + container_port = 8388 + protocol = "TCP" + } + port { + container_port = 8388 + protocol = "UDP" + } + } + } + } + } +} + +resource "kubernetes_service" "mailserver" { # rename me + metadata { + name = "shadowsocks" + namespace = kubernetes_namespace.shadowsocks.metadata[0].name + + labels = { + app = "shadowsocks" + } + annotations = { + "metallb.universe.tf/allow-shared-ip" = "shared" + } + } + + spec { + type = "LoadBalancer" + external_traffic_policy = "Cluster" + selector = { + app = "shadowsocks" + } + + port { + name = "shadowsocks-tcp" + protocol = "TCP" + port = 8388 + target_port = "8388" + } + + port { + name = "shadowsocks-udp" + protocol = "UDP" + port = 8388 + target_port = "8388" + } + } } diff --git a/stacks/shadowsocks/module/main.tf b/stacks/shadowsocks/module/main.tf deleted file mode 100644 index dbd5ad15..00000000 --- a/stacks/shadowsocks/module/main.tf +++ /dev/null @@ -1,106 +0,0 @@ -variable "password" {} -variable "tier" { type = string } -variable "method" { - default = "chacha20-ietf-poly1305" -} - -resource "kubernetes_namespace" "shadowsocks" { - metadata { - name = "shadowsocks" - labels = { - tier = var.tier - } - # TLS termination seems iffy - I get pfsense MiTM-ing - # labels = { - # "istio-injection" : "enabled" - # } - } -} - -resource "kubernetes_deployment" "shadowsocks" { - metadata { - name = "shadowsocks" - namespace = kubernetes_namespace.shadowsocks.metadata[0].name - labels = { - "app" = "shadowsocks" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = "1" - selector { - match_labels = { - "app" = "shadowsocks" - } - } - template { - metadata { - labels = { - "app" = "shadowsocks" - } - } - spec { - container { - name = "shadowsocks" - image = "shadowsocks/shadowsocks-libev" - image_pull_policy = "IfNotPresent" - env { - name = "METHOD" - value = var.method - } - env { - name = "PASSWORD" - value = var.password - } - port { - container_port = 8388 - protocol = "TCP" - } - port { - container_port = 8388 - protocol = "UDP" - } - } - } - } - } -} - -resource "kubernetes_service" "mailserver" { # rename me - metadata { - name = "shadowsocks" - namespace = kubernetes_namespace.shadowsocks.metadata[0].name - - labels = { - app = "shadowsocks" - } - annotations = { - "metallb.universe.tf/allow-shared-ip" = "shared" - } - } - - spec { - type = "LoadBalancer" - external_traffic_policy = "Cluster" - selector = { - app = "shadowsocks" - } - - port { - name = "shadowsocks-tcp" - protocol = "TCP" - port = 8388 - target_port = "8388" - } - - port { - name = "shadowsocks-udp" - protocol = "UDP" - port = 8388 - target_port = "8388" - } - } -} diff --git a/stacks/shadowsocks/module/shadowsocks_chart_values.tpl b/stacks/shadowsocks/shadowsocks_chart_values.tpl similarity index 100% rename from stacks/shadowsocks/module/shadowsocks_chart_values.tpl rename to stacks/shadowsocks/shadowsocks_chart_values.tpl diff --git a/stacks/speedtest/main.tf b/stacks/speedtest/main.tf index c626ee6d..0357f801 100644 --- a/stacks/speedtest/main.tf +++ b/stacks/speedtest/main.tf @@ -11,9 +11,150 @@ locals { } } -module "speedtest" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux - db_password = var.speedtest_db_password +resource "kubernetes_namespace" "speedtest" { + metadata { + name = "speedtest" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.speedtest.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "random_id" "secret_key" { + byte_length = 32 # 32 bytes × 2 hex chars = 64 hex characters +} + +resource "kubernetes_deployment" "speedtest" { + metadata { + name = "speedtest" + namespace = kubernetes_namespace.speedtest.metadata[0].name + labels = { + app = "speedtest" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "speedtest" + } + } + template { + metadata { + labels = { + app = "speedtest" + } + } + spec { + container { + image = "lscr.io/linuxserver/speedtest-tracker:latest" + name = "speedtest" + port { + container_port = 80 + } + env { + name = "PUID" + value = 1000 + } + env { + name = "PGID" + value = 1000 + } + env { + name = "APP_KEY" + value = "base64:${random_id.secret_key.b64_std}" + } + env { + name = "SPEEDTEST_SCHEDULE" + value = "0 * * * *" + } + # env { + # name = "SPEEDTEST_SERVERS" + # # Sofia speedtest servers - https://c.speedtest.net/speedtest-servers-static.php + # value = "7617,17787,11348,37980,54640,27843,57118,10754,20191,29617" + # } + env { + name = "APP_URL" + value = "https://speedtest.viktorbarzin.me" + } + env { + name = "DB_CONNECTION" + value = "mysql" + } + env { + name = "DB_HOST" + value = "mysql.dbaas.svc.cluster.local" + } + env { + name = "DB_DATABASE" + value = "speedtest" + } + env { + name = "DB_USERNAME" + value = "speedtest" + } + env { + name = "DB_PASSWORD" + value = var.speedtest_db_password + } + env { + name = "APP_TIMEZONE" + value = "Europe/Sofia" + } + volume_mount { + name = "config" + mount_path = "/config" + } + } + volume { + name = "config" + nfs { + server = "10.0.10.15" + path = "/mnt/main/speedtest" + } + } + } + } + } +} + +resource "kubernetes_service" "speedtest" { + metadata { + name = "speedtest" + namespace = kubernetes_namespace.speedtest.metadata[0].name + labels = { + "app" = "speedtest" + } + annotations = { + "prometheus.io/scrape" = "true" + "prometheus.io/path" = "/prometheus" + "prometheus.io/port" = "80" + } + } + + spec { + selector = { + app = "speedtest" + } + port { + name = "http" + port = 80 + target_port = 80 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.speedtest.metadata[0].name + name = "speedtest" + tls_secret_name = var.tls_secret_name + protected = true } diff --git a/stacks/speedtest/module/main.tf b/stacks/speedtest/module/main.tf deleted file mode 100644 index 728c5e26..00000000 --- a/stacks/speedtest/module/main.tf +++ /dev/null @@ -1,152 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "db_password" { type = string } - - -resource "kubernetes_namespace" "speedtest" { - metadata { - name = "speedtest" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.speedtest.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "random_id" "secret_key" { - byte_length = 32 # 32 bytes × 2 hex chars = 64 hex characters -} - -resource "kubernetes_deployment" "speedtest" { - metadata { - name = "speedtest" - namespace = kubernetes_namespace.speedtest.metadata[0].name - labels = { - app = "speedtest" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "speedtest" - } - } - template { - metadata { - labels = { - app = "speedtest" - } - } - spec { - container { - image = "lscr.io/linuxserver/speedtest-tracker:latest" - name = "speedtest" - port { - container_port = 80 - } - env { - name = "PUID" - value = 1000 - } - env { - name = "PGID" - value = 1000 - } - env { - name = "APP_KEY" - value = "base64:${random_id.secret_key.b64_std}" - } - env { - name = "SPEEDTEST_SCHEDULE" - value = "0 * * * *" - } - # env { - # name = "SPEEDTEST_SERVERS" - # # Sofia speedtest servers - https://c.speedtest.net/speedtest-servers-static.php - # value = "7617,17787,11348,37980,54640,27843,57118,10754,20191,29617" - # } - env { - name = "APP_URL" - value = "https://speedtest.viktorbarzin.me" - } - env { - name = "DB_CONNECTION" - value = "mysql" - } - env { - name = "DB_HOST" - value = "mysql.dbaas.svc.cluster.local" - } - env { - name = "DB_DATABASE" - value = "speedtest" - } - env { - name = "DB_USERNAME" - value = "speedtest" - } - env { - name = "DB_PASSWORD" - value = var.db_password - } - env { - name = "APP_TIMEZONE" - value = "Europe/Sofia" - } - volume_mount { - name = "config" - mount_path = "/config" - } - } - volume { - name = "config" - nfs { - server = "10.0.10.15" - path = "/mnt/main/speedtest" - } - } - } - } - } -} - -resource "kubernetes_service" "speedtest" { - metadata { - name = "speedtest" - namespace = kubernetes_namespace.speedtest.metadata[0].name - labels = { - "app" = "speedtest" - } - annotations = { - "prometheus.io/scrape" = "true" - "prometheus.io/path" = "/prometheus" - "prometheus.io/port" = "80" - } - } - - spec { - selector = { - app = "speedtest" - } - port { - name = "http" - port = 80 - target_port = 80 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.speedtest.metadata[0].name - name = "speedtest" - tls_secret_name = var.tls_secret_name - protected = true -} diff --git a/stacks/stirling-pdf/main.tf b/stacks/stirling-pdf/main.tf index e0a344d9..48bf69bd 100644 --- a/stacks/stirling-pdf/main.tf +++ b/stacks/stirling-pdf/main.tf @@ -10,8 +10,93 @@ locals { } } -module "stirling-pdf" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "stirling-pdf" { + metadata { + name = "stirling-pdf" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.stirling-pdf.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "stirling-pdf" { + metadata { + name = "stirling-pdf" + namespace = kubernetes_namespace.stirling-pdf.metadata[0].name + labels = { + app = "stirling-pdf" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "stirling-pdf" + } + } + template { + metadata { + labels = { + app = "stirling-pdf" + } + } + spec { + container { + image = "stirlingtools/stirling-pdf:latest" + name = "stirling-pdf" + port { + container_port = 8080 + } + volume_mount { + name = "configs" + mount_path = "/configs" + } + } + volume { + name = "configs" + nfs { + server = "10.0.10.15" + path = "/mnt/main/stirling-pdf" + } + } + } + } + } +} + +resource "kubernetes_service" "stirling-pdf" { + metadata { + name = "stirling-pdf" + namespace = kubernetes_namespace.stirling-pdf.metadata[0].name + labels = { + "app" = "stirling-pdf" + } + } + + spec { + selector = { + app = "stirling-pdf" + } + port { + name = "http" + port = 80 + target_port = 8080 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.stirling-pdf.metadata[0].name + name = "stirling-pdf" + tls_secret_name = var.tls_secret_name + rybbit_site_id = "a55ac54ec749" } diff --git a/stacks/stirling-pdf/module/main.tf b/stacks/stirling-pdf/module/main.tf deleted file mode 100644 index 438611c0..00000000 --- a/stacks/stirling-pdf/module/main.tf +++ /dev/null @@ -1,93 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "stirling-pdf" { - metadata { - name = "stirling-pdf" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.stirling-pdf.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "stirling-pdf" { - metadata { - name = "stirling-pdf" - namespace = kubernetes_namespace.stirling-pdf.metadata[0].name - labels = { - app = "stirling-pdf" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "stirling-pdf" - } - } - template { - metadata { - labels = { - app = "stirling-pdf" - } - } - spec { - container { - image = "stirlingtools/stirling-pdf:latest" - name = "stirling-pdf" - port { - container_port = 8080 - } - volume_mount { - name = "configs" - mount_path = "/configs" - } - } - volume { - name = "configs" - nfs { - server = "10.0.10.15" - path = "/mnt/main/stirling-pdf" - } - } - } - } - } -} - -resource "kubernetes_service" "stirling-pdf" { - metadata { - name = "stirling-pdf" - namespace = kubernetes_namespace.stirling-pdf.metadata[0].name - labels = { - "app" = "stirling-pdf" - } - } - - spec { - selector = { - app = "stirling-pdf" - } - port { - name = "http" - port = 80 - target_port = 8080 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.stirling-pdf.metadata[0].name - name = "stirling-pdf" - tls_secret_name = var.tls_secret_name - rybbit_site_id = "a55ac54ec749" -} diff --git a/stacks/tandoor/main.tf b/stacks/tandoor/main.tf index c692764e..54d3c09e 100644 --- a/stacks/tandoor/main.tf +++ b/stacks/tandoor/main.tf @@ -15,10 +15,170 @@ locals { } } -module "tandoor" { - source = "./module" - tls_secret_name = var.tls_secret_name - tandoor_database_password = var.tandoor_database_password - tandoor_email_password = var.tandoor_email_password - tier = local.tiers.aux +resource "kubernetes_namespace" "tandoor" { + metadata { + name = "tandoor" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} +resource "random_password" "secret_key" { + length = 128 + special = false +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.tandoor.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "tandoor" { + metadata { + name = "tandoor" + namespace = kubernetes_namespace.tandoor.metadata[0].name + labels = { + app = "tandoor" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "tandoor" + } + } + template { + metadata { + labels = { + app = "tandoor" + } + } + spec { + container { + name = "recipes" + image = "vabene1111/recipes" + image_pull_policy = "IfNotPresent" + env { + name = "SECRET_KEY" + value = base64encode(random_password.secret_key.result) + } + env { + name = "DB_ENGINE" + value = "django.db.backends.postgresql" + } + env { + name = "POSTGRES_HOST" + value = "postgresql.dbaas.svc.cluster.local" + } + env { + name = "POSTGRES_PORT" + value = 5432 + } + env { + name = "POSTGRES_USER" + value = "tandoor" + } + env { + name = "POSTGRES_PASSWORD" + value = var.tandoor_database_password + } + env { + name = "TANDOOR_PORT" + value = 8080 + } + env { + name = "ENABLE_SIGNUP" + value = 1 + } + env { + name = "ALLOWED_HOSTS" + value = "tandoor.viktorbarzin.me" + } + env { + name = "POSTGRES_DB" + value = "tandoor" + } + env { + name = "EMAIL_HOST" + value = "mail.viktorbarzin.me" + } + env { + name = "EMAIL_HOST_USER" + value = "info@viktorbarzin.me" + } + env { + name = "EMAIL_HOST_PASSWORD" + value = var.tandoor_email_password + } + env { + name = "EMAIL_USE_TLS" + value = "1" + } + env { + name = "DEFAULT_FROM_EMAIL" + value = "info@viktorbarzin.me" + } + env { + name = "EMAIL_PORT" + value = 587 + } + port { + name = "http" + container_port = 8080 + protocol = "TCP" + } + volume_mount { + name = "data" + mount_path = "/opt/recipes/mediafiles" + } + volume_mount { + name = "data" + mount_path = "/opt/recipes/staticfiles" + } + } + + volume { + name = "data" + nfs { + path = "/mnt/main/tandoor" + server = "10.0.10.15" + } + } + } + } + } +} + +resource "kubernetes_service" "tandoor" { + metadata { + name = "tandoor" + namespace = kubernetes_namespace.tandoor.metadata[0].name + labels = { + "app" = "tandoor" + } + } + + spec { + selector = { + app = "tandoor" + } + port { + port = 80 + target_port = 8080 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.tandoor.metadata[0].name + name = "tandoor" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/tandoor/module/main.tf b/stacks/tandoor/module/main.tf deleted file mode 100644 index ca638afa..00000000 --- a/stacks/tandoor/module/main.tf +++ /dev/null @@ -1,172 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "tandoor_database_password" {} -variable "tandoor_email_password" {} - -resource "kubernetes_namespace" "tandoor" { - metadata { - name = "tandoor" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} -resource "random_password" "secret_key" { - length = 128 - special = false -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.tandoor.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "tandoor" { - metadata { - name = "tandoor" - namespace = kubernetes_namespace.tandoor.metadata[0].name - labels = { - app = "tandoor" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "tandoor" - } - } - template { - metadata { - labels = { - app = "tandoor" - } - } - spec { - container { - name = "recipes" - image = "vabene1111/recipes" - image_pull_policy = "IfNotPresent" - env { - name = "SECRET_KEY" - value = base64encode(random_password.secret_key.result) - } - env { - name = "DB_ENGINE" - value = "django.db.backends.postgresql" - } - env { - name = "POSTGRES_HOST" - value = "postgresql.dbaas.svc.cluster.local" - } - env { - name = "POSTGRES_PORT" - value = 5432 - } - env { - name = "POSTGRES_USER" - value = "tandoor" - } - env { - name = "POSTGRES_PASSWORD" - value = var.tandoor_database_password - } - env { - name = "TANDOOR_PORT" - value = 8080 - } - env { - name = "ENABLE_SIGNUP" - value = 1 - } - env { - name = "ALLOWED_HOSTS" - value = "tandoor.viktorbarzin.me" - } - env { - name = "POSTGRES_DB" - value = "tandoor" - } - env { - name = "EMAIL_HOST" - value = "mail.viktorbarzin.me" - } - env { - name = "EMAIL_HOST_USER" - value = "info@viktorbarzin.me" - } - env { - name = "EMAIL_HOST_PASSWORD" - value = var.tandoor_email_password - } - env { - name = "EMAIL_USE_TLS" - value = "1" - } - env { - name = "DEFAULT_FROM_EMAIL" - value = "info@viktorbarzin.me" - } - env { - name = "EMAIL_PORT" - value = 587 - } - port { - name = "http" - container_port = 8080 - protocol = "TCP" - } - volume_mount { - name = "data" - mount_path = "/opt/recipes/mediafiles" - } - volume_mount { - name = "data" - mount_path = "/opt/recipes/staticfiles" - } - } - - volume { - name = "data" - nfs { - path = "/mnt/main/tandoor" - server = "10.0.10.15" - } - } - } - } - } -} - -resource "kubernetes_service" "tandoor" { - metadata { - name = "tandoor" - namespace = kubernetes_namespace.tandoor.metadata[0].name - labels = { - "app" = "tandoor" - } - } - - spec { - selector = { - app = "tandoor" - } - port { - port = 80 - target_port = 8080 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.tandoor.metadata[0].name - name = "tandoor" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/tor-proxy/main.tf b/stacks/tor-proxy/main.tf index 3ee61aff..0c48104d 100644 --- a/stacks/tor-proxy/main.tf +++ b/stacks/tor-proxy/main.tf @@ -10,8 +10,115 @@ locals { } } -module "tor-proxy" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "tor-proxy" { + metadata { + name = "tor-proxy" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = "tor-proxy" + tls_secret_name = var.tls_secret_name +} + +# resource "kubernetes_config_map" "tor_config" { +# metadata { +# name = "tor-config" +# namespace = "tor-proxy" +# annotations = { +# "reloader.stakater.com/match" = "true" +# } +# } + +# data = { +# "torrc" = file("${path.module}/.torrc") +# } +# } + +resource "kubernetes_deployment" "tor-proxy" { + metadata { + name = "tor-proxy" + namespace = "tor-proxy" + labels = { + app = "tor-proxy" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "RollingUpdate" + } + selector { + match_labels = { + app = "tor-proxy" + } + } + template { + metadata { + labels = { + app = "tor-proxy" + } + } + spec { + container { + name = "tor-proxy" + image = "dperson/torproxy:latest" + port { + name = "http" + container_port = 8118 + protocol = "TCP" + } + port { + name = "tor" + container_port = 9050 + protocol = "TCP" + } + # volume_mount { + # name = "tor-config" + # mount_path = "/etc/tor/torrc" + # sub_path = "torrc" + # } + } + # volume { + # name = "tor-config" + # config_map { + # name = kubernetes_config_map.tor_config.metadata[0].name + # } + # } + } + } + } +} + +resource "kubernetes_service" "tor-proxy" { + metadata { + name = "tor-proxy" + namespace = "tor-proxy" + labels = { + "app" = "tor-proxy" + } + } + + spec { + selector = { + app = "tor-proxy" + } + port { + name = "http" + port = 8118 + } + port { + name = "tor" + port = 9050 + } + } } diff --git a/stacks/tor-proxy/module/main.tf b/stacks/tor-proxy/module/main.tf deleted file mode 100644 index e2bf3afd..00000000 --- a/stacks/tor-proxy/module/main.tf +++ /dev/null @@ -1,115 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "tor-proxy" { - metadata { - name = "tor-proxy" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = "tor-proxy" - tls_secret_name = var.tls_secret_name -} - -# resource "kubernetes_config_map" "tor_config" { -# metadata { -# name = "tor-config" -# namespace = "tor-proxy" -# annotations = { -# "reloader.stakater.com/match" = "true" -# } -# } - -# data = { -# "torrc" = file("${path.module}/.torrc") -# } -# } - -resource "kubernetes_deployment" "tor-proxy" { - metadata { - name = "tor-proxy" - namespace = "tor-proxy" - labels = { - app = "tor-proxy" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "RollingUpdate" - } - selector { - match_labels = { - app = "tor-proxy" - } - } - template { - metadata { - labels = { - app = "tor-proxy" - } - } - spec { - container { - name = "tor-proxy" - image = "dperson/torproxy:latest" - port { - name = "http" - container_port = 8118 - protocol = "TCP" - } - port { - name = "tor" - container_port = 9050 - protocol = "TCP" - } - # volume_mount { - # name = "tor-config" - # mount_path = "/etc/tor/torrc" - # sub_path = "torrc" - # } - } - # volume { - # name = "tor-config" - # config_map { - # name = kubernetes_config_map.tor_config.metadata[0].name - # } - # } - } - } - } -} - -resource "kubernetes_service" "tor-proxy" { - metadata { - name = "tor-proxy" - namespace = "tor-proxy" - labels = { - "app" = "tor-proxy" - } - } - - spec { - selector = { - app = "tor-proxy" - } - port { - name = "http" - port = 8118 - } - port { - name = "tor" - port = 9050 - } - } -} diff --git a/stacks/travel_blog/main.tf b/stacks/travel_blog/main.tf index 80d02c31..185a39c3 100644 --- a/stacks/travel_blog/main.tf +++ b/stacks/travel_blog/main.tf @@ -10,8 +10,107 @@ locals { } } -module "travel_blog" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux +resource "kubernetes_namespace" "travel-blog" { + metadata { + name = "travel-blog" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.travel-blog.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +# module "dockerhub_creds" { +# source = "../../modules/kubernetes/dockerhub_secret" +# namespace = kubernetes_namespace.travel.metadata[0].name +# password = var.dockerhub_password +# } + +resource "kubernetes_deployment" "blog" { + metadata { + name = "travel-blog" + namespace = kubernetes_namespace.travel-blog.metadata[0].name + labels = { + app = "travel-blog" + tier = local.tiers.aux + } + } + spec { + replicas = 3 + selector { + match_labels = { + app = "travel-blog" + } + } + template { + metadata { + labels = { + app = "travel-blog" + } + } + spec { + container { + image = "viktorbarzin/travel_blog:latest" + name = "travel-blog" + resources { + limits = { + cpu = "0.5" + memory = "512Mi" + } + requests = { + cpu = "250m" + memory = "50Mi" + } + } + port { + container_port = 80 + } + } + + # container { + # image = "nginx/nginx-prometheus-exporter" + # name = "nginx-exporter" + # args = ["-nginx.scrape-uri", "http://127.0.0.1:8080/nginx_status"] + # port { + # container_port = 9113 + # } + # } + } + } + } +} + +resource "kubernetes_service" "travel-blog" { + metadata { + name = "travel-blog" + namespace = kubernetes_namespace.travel-blog.metadata[0].name + labels = { + app = "travel-blog" + } + } + + spec { + selector = { + app = "travel-blog" + } + port { + name = "http" + port = "80" + target_port = "80" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.travel-blog.metadata[0].name + name = "travel" + tls_secret_name = var.tls_secret_name + service_name = "travel-blog" } diff --git a/stacks/travel_blog/module/main.tf b/stacks/travel_blog/module/main.tf deleted file mode 100644 index f68bc8c0..00000000 --- a/stacks/travel_blog/module/main.tf +++ /dev/null @@ -1,107 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "travel-blog" { - metadata { - name = "travel-blog" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.travel-blog.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -# module "dockerhub_creds" { -# source = "../../../modules/kubernetes/dockerhub_secret" -# namespace = kubernetes_namespace.travel.metadata[0].name -# password = var.dockerhub_password -# } - -resource "kubernetes_deployment" "blog" { - metadata { - name = "travel-blog" - namespace = kubernetes_namespace.travel-blog.metadata[0].name - labels = { - app = "travel-blog" - tier = var.tier - } - } - spec { - replicas = 3 - selector { - match_labels = { - app = "travel-blog" - } - } - template { - metadata { - labels = { - app = "travel-blog" - } - } - spec { - container { - image = "viktorbarzin/travel_blog:latest" - name = "travel-blog" - resources { - limits = { - cpu = "0.5" - memory = "512Mi" - } - requests = { - cpu = "250m" - memory = "50Mi" - } - } - port { - container_port = 80 - } - } - - # container { - # image = "nginx/nginx-prometheus-exporter" - # name = "nginx-exporter" - # args = ["-nginx.scrape-uri", "http://127.0.0.1:8080/nginx_status"] - # port { - # container_port = 9113 - # } - # } - } - } - } -} - -resource "kubernetes_service" "travel-blog" { - metadata { - name = "travel-blog" - namespace = kubernetes_namespace.travel-blog.metadata[0].name - labels = { - app = "travel-blog" - } - } - - spec { - selector = { - app = "travel-blog" - } - port { - name = "http" - port = "80" - target_port = "80" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.travel-blog.metadata[0].name - name = "travel" - tls_secret_name = var.tls_secret_name - service_name = "travel-blog" -} diff --git a/stacks/tuya-bridge/main.tf b/stacks/tuya-bridge/main.tf index a3e9fd25..54fadc53 100644 --- a/stacks/tuya-bridge/main.tf +++ b/stacks/tuya-bridge/main.tf @@ -14,12 +14,97 @@ locals { } } -module "tuya-bridge" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.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 +resource "kubernetes_namespace" "tuya-bridge" { + metadata { + name = "tuya-bridge" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.cluster + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.tuya-bridge.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "tuya-bridge" { + metadata { + name = "tuya-bridge" + namespace = kubernetes_namespace.tuya-bridge.metadata[0].name + labels = { + app = "tuya-bridge" + tier = local.tiers.cluster + } + } + spec { + replicas = 3 + selector { + match_labels = { + app = "tuya-bridge" + } + } + template { + metadata { + labels = { + app = "tuya-bridge" + } + } + spec { + container { + image = "viktorbarzin/tuya_bridge:latest" + name = "tuya-bridge" + port { + container_port = 8080 + } + env { + name = "TINYTUYA_API_KEY" + value = var.tiny_tuya_api_key + } + env { + name = "TINYTUYA_API_SECRET" + value = var.tiny_tuya_api_secret + } + env { + name = "SERVICE_API_KEY" # used for auth the API endpoint + value = var.tiny_tuya_service_secret + } + env { + name = "SLACK_URL" + value = var.tiny_tuya_slack_url + } + } + } + } + } +} + +resource "kubernetes_service" "tuya-bridge" { + metadata { + name = "tuya-bridge" + namespace = kubernetes_namespace.tuya-bridge.metadata[0].name + labels = { + "app" = "tuya-bridge" + } + } + + spec { + selector = { + app = "tuya-bridge" + } + port { + name = "http" + port = "80" + target_port = "8080" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.tuya-bridge.metadata[0].name + name = "tuya-bridge" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/tuya-bridge/module/main.tf b/stacks/tuya-bridge/module/main.tf deleted file mode 100644 index c13ee82b..00000000 --- a/stacks/tuya-bridge/module/main.tf +++ /dev/null @@ -1,101 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "tiny_tuya_api_key" { type = string } -variable "tiny_tuya_api_secret" { type = string } -variable "tiny_tuya_service_secret" { type = string } -variable "slack_url" { type = string } - -resource "kubernetes_namespace" "tuya-bridge" { - metadata { - name = "tuya-bridge" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.tuya-bridge.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "tuya-bridge" { - metadata { - name = "tuya-bridge" - namespace = kubernetes_namespace.tuya-bridge.metadata[0].name - labels = { - app = "tuya-bridge" - tier = var.tier - } - } - spec { - replicas = 3 - selector { - match_labels = { - app = "tuya-bridge" - } - } - template { - metadata { - labels = { - app = "tuya-bridge" - } - } - spec { - container { - image = "viktorbarzin/tuya_bridge:latest" - name = "tuya-bridge" - port { - container_port = 8080 - } - env { - name = "TINYTUYA_API_KEY" - value = var.tiny_tuya_api_key - } - env { - name = "TINYTUYA_API_SECRET" - value = var.tiny_tuya_api_secret - } - env { - name = "SERVICE_API_KEY" # used for auth the API endpoint - value = var.tiny_tuya_service_secret - } - env { - name = "SLACK_URL" - value = var.slack_url - } - } - } - } - } -} - -resource "kubernetes_service" "tuya-bridge" { - metadata { - name = "tuya-bridge" - namespace = kubernetes_namespace.tuya-bridge.metadata[0].name - labels = { - "app" = "tuya-bridge" - } - } - - spec { - selector = { - app = "tuya-bridge" - } - port { - name = "http" - port = "80" - target_port = "8080" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.tuya-bridge.metadata[0].name - name = "tuya-bridge" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/url/main.tf b/stacks/url/main.tf index 6ee695a6..421f21f2 100644 --- a/stacks/url/main.tf +++ b/stacks/url/main.tf @@ -13,11 +13,292 @@ locals { } } -module "url" { - source = "./module" - 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.tiers.aux +## Setup +## Need to manually add +## user: shlink +## password: var.url_shortener_mysql_password +## to the mysql tier + +variable "domain" { + default = "url.viktorbarzin.me" +} + +resource "kubernetes_namespace" "shlink" { + metadata { + name = "url" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.shlink.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_secret" "mysql_config" { + metadata { + name = "mysql-config" + namespace = kubernetes_namespace.shlink.metadata[0].name + annotations = { + "reloader.stakater.com/match" = "true" + } + } + data = { + "DB_USER" = "shlink" + "DB_PASSWORD" = var.url_shortener_mysql_password + } +} + +# this depends on the mysql installation +# resource "kubectl_manifest" "mysql-user" { +# yaml_body = <<-YAML +# apiVersion: mysql.presslabs.org/v1alpha1 +# kind: MysqlUser +# metadata: +# name: shlink +# namespace = kubernetes_namespace.shlink.metadata[0].name +# spec: +# user: shlink +# clusterRef: +# name: mysql-cluster +# namespace = kubernetes_namespace.shlink.metadata[0].name +# password: +# name: mysql-config +# key: password +# allowedHosts: +# - '%' +# YAML +# # permissions: +# # - schema: db-name-in-mysql +# # tables: ["table1", "table2"] +# # permissions: +# # - SELECT +# # - UPDATE +# # - CREATE +# # allowedHosts: +# # - localhost +# } + +resource "kubernetes_deployment" "shlink" { + metadata { + name = "shlink" + namespace = kubernetes_namespace.shlink.metadata[0].name + labels = { + run = "shlink" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + run = "shlink" + } + } + template { + metadata { + labels = { + run = "shlink" + } + } + spec { + container { + image = "shlinkio/shlink:stable" + name = "shlink" + env { + name = "DEFAULT_DOMAIN" + value = var.domain + } + env { + name = "SHORT_DOMAIN_SCHEMA" + value = "https" + } + env { + name = "GEOLITE_LICENSE_KEY" + value = var.url_shortener_geolite_license_key + } + # DB config + env { + name = "DB_DRIVER" + value = "mysql" + } + env { + name = "DB_HOST" + value = "mysql.dbaas.svc.cluster.local" + } + # env { + # name = "DB_USER" + # value = "shlink" + # } + env_from { + secret_ref { + name = "mysql-config" + } + } + # env { + # name = "DB_PASSWORD" + # value = var.url_shortener_mysql_password + # } + # resources { + # limits = { + # cpu = "0.5" + # memory = "512Mi" + # } + # requests = { + # cpu = "250m" + # memory = "50Mi" + # } + # } + port { + container_port = 8080 + } + } + } + } + } +} + +resource "kubernetes_service" "shlink" { + metadata { + name = "shlink" + namespace = kubernetes_namespace.shlink.metadata[0].name + labels = { + "run" = "shlink" + } + } + + spec { + selector = { + run = "shlink" + } + port { + name = "http" + port = "80" + target_port = "8080" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.shlink.metadata[0].name + name = "url" + service_name = "shlink" + tls_secret_name = var.tls_secret_name + extra_annotations = {} +} + + +# Shlink web client + +resource "kubernetes_config_map" "shlink-web" { + metadata { + name = "shlink-web-servers" + namespace = kubernetes_namespace.shlink.metadata[0].name + + annotations = { + "reloader.stakater.com/match" = "true" + } + } + + data = { + "servers.json" = jsonencode([{ + name = "Main" + url = "https://url.viktorbarzin.me" + apiKey = var.url_shortener_api_key + }]) + } +} + +resource "kubernetes_deployment" "shlink-web" { + metadata { + name = "shlink-web" + namespace = kubernetes_namespace.shlink.metadata[0].name + labels = { + run = "shlink-web" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + selector { + match_labels = { + run = "shlink-web" + } + } + template { + metadata { + labels = { + run = "shlink-web" + } + } + spec { + container { + image = "shlinkio/shlink-web-client" + name = "shlink-web" + volume_mount { + mount_path = "/usr/share/nginx/html/servers.json" + sub_path = "servers.json" + name = "config" + } + resources { + limits = { + cpu = "0.5" + memory = "512Mi" + } + requests = { + cpu = "250m" + memory = "50Mi" + } + } + port { + container_port = 8080 + } + } + volume { + name = "config" + config_map { + name = "shlink-web-servers" + } + } + } + } + } +} + +resource "kubernetes_service" "shlink-web" { + metadata { + name = "shlink-web" + namespace = kubernetes_namespace.shlink.metadata[0].name + labels = { + "run" = "shlink-web" + } + } + + spec { + selector = { + run = "shlink-web" + } + port { + name = "http" + port = 80 + target_port = 8080 + } + } +} + +module "ingress-web" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.shlink.metadata[0].name + name = "shlink" + service_name = "shlink-web" + tls_secret_name = var.tls_secret_name + protected = true } diff --git a/stacks/url/module/main.tf b/stacks/url/module/main.tf deleted file mode 100644 index 35a068f4..00000000 --- a/stacks/url/module/main.tf +++ /dev/null @@ -1,294 +0,0 @@ -## Setup -## Need to manually add -## user: shlink -## password: var.mysql_password -## to the mysql tier - -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "geolite_license_key" {} -variable "api_key" {} -variable "mysql_password" {} -variable "domain" { - default = "url.viktorbarzin.me" -} - -resource "kubernetes_namespace" "shlink" { - metadata { - name = "url" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.shlink.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_secret" "mysql_config" { - metadata { - name = "mysql-config" - namespace = kubernetes_namespace.shlink.metadata[0].name - annotations = { - "reloader.stakater.com/match" = "true" - } - } - data = { - "DB_USER" = "shlink" - "DB_PASSWORD" = var.mysql_password - } -} - -# this depends on the mysql installation -# resource "kubectl_manifest" "mysql-user" { -# yaml_body = <<-YAML -# apiVersion: mysql.presslabs.org/v1alpha1 -# kind: MysqlUser -# metadata: -# name: shlink -# namespace = kubernetes_namespace.shlink.metadata[0].name -# spec: -# user: shlink -# clusterRef: -# name: mysql-cluster -# namespace = kubernetes_namespace.shlink.metadata[0].name -# password: -# name: mysql-config -# key: password -# allowedHosts: -# - '%' -# YAML -# # permissions: -# # - schema: db-name-in-mysql -# # tables: ["table1", "table2"] -# # permissions: -# # - SELECT -# # - UPDATE -# # - CREATE -# # allowedHosts: -# # - localhost -# } - -resource "kubernetes_deployment" "shlink" { - metadata { - name = "shlink" - namespace = kubernetes_namespace.shlink.metadata[0].name - labels = { - run = "shlink" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - run = "shlink" - } - } - template { - metadata { - labels = { - run = "shlink" - } - } - spec { - container { - image = "shlinkio/shlink:stable" - name = "shlink" - env { - name = "DEFAULT_DOMAIN" - value = var.domain - } - env { - name = "SHORT_DOMAIN_SCHEMA" - value = "https" - } - env { - name = "GEOLITE_LICENSE_KEY" - value = var.geolite_license_key - } - # DB config - env { - name = "DB_DRIVER" - value = "mysql" - } - env { - name = "DB_HOST" - value = "mysql.dbaas.svc.cluster.local" - } - # env { - # name = "DB_USER" - # value = "shlink" - # } - env_from { - secret_ref { - name = "mysql-config" - } - } - # env { - # name = "DB_PASSWORD" - # value = var.mysql_password - # } - # resources { - # limits = { - # cpu = "0.5" - # memory = "512Mi" - # } - # requests = { - # cpu = "250m" - # memory = "50Mi" - # } - # } - port { - container_port = 8080 - } - } - } - } - } -} - -resource "kubernetes_service" "shlink" { - metadata { - name = "shlink" - namespace = kubernetes_namespace.shlink.metadata[0].name - labels = { - "run" = "shlink" - } - } - - spec { - selector = { - run = "shlink" - } - port { - name = "http" - port = "80" - target_port = "8080" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.shlink.metadata[0].name - name = "url" - service_name = "shlink" - tls_secret_name = var.tls_secret_name - extra_annotations = {} -} - - -# Shlink web client - -resource "kubernetes_config_map" "shlink-web" { - metadata { - name = "shlink-web-servers" - namespace = kubernetes_namespace.shlink.metadata[0].name - - annotations = { - "reloader.stakater.com/match" = "true" - } - } - - data = { - "servers.json" = jsonencode([{ - name = "Main" - url = "https://url.viktorbarzin.me" - apiKey = var.api_key - }]) - } -} - -resource "kubernetes_deployment" "shlink-web" { - metadata { - name = "shlink-web" - namespace = kubernetes_namespace.shlink.metadata[0].name - labels = { - run = "shlink-web" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - selector { - match_labels = { - run = "shlink-web" - } - } - template { - metadata { - labels = { - run = "shlink-web" - } - } - spec { - container { - image = "shlinkio/shlink-web-client" - name = "shlink-web" - volume_mount { - mount_path = "/usr/share/nginx/html/servers.json" - sub_path = "servers.json" - name = "config" - } - resources { - limits = { - cpu = "0.5" - memory = "512Mi" - } - requests = { - cpu = "250m" - memory = "50Mi" - } - } - port { - container_port = 8080 - } - } - volume { - name = "config" - config_map { - name = "shlink-web-servers" - } - } - } - } - } -} - -resource "kubernetes_service" "shlink-web" { - metadata { - name = "shlink-web" - namespace = kubernetes_namespace.shlink.metadata[0].name - labels = { - "run" = "shlink-web" - } - } - - spec { - selector = { - run = "shlink-web" - } - port { - name = "http" - port = 80 - target_port = 8080 - } - } -} - -module "ingress-web" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.shlink.metadata[0].name - name = "shlink" - service_name = "shlink-web" - tls_secret_name = var.tls_secret_name - protected = true -} diff --git a/stacks/url/module/versions.tf b/stacks/url/versions.tf similarity index 100% rename from stacks/url/module/versions.tf rename to stacks/url/versions.tf diff --git a/stacks/wealthfolio/main.tf b/stacks/wealthfolio/main.tf index 8327e11a..e458b89c 100644 --- a/stacks/wealthfolio/main.tf +++ b/stacks/wealthfolio/main.tf @@ -11,9 +11,129 @@ locals { } } -module "wealthfolio" { - source = "./module" - tls_secret_name = var.tls_secret_name - wealthfolio_password_hash = var.wealthfolio_password_hash - tier = local.tiers.aux +# To refresh transactions use finance db positions exporters: +# +# workon finace-app && cd ~/code/finance && python main.py fetch position --imap-user=$IMAP_USER --imap-password=$IMAP_PASSWORD --trading212-api-keys=$TRADING212_API_KEYS --output-file positions.csv && mv positions.csv /home/wizard/code/infra/modules/kubernetes/wealthfolio/updated_trades.csv +# +# Then upload updated_trades.csv +# Note that currently wealthfolio doesn't dedup (https://github.com/afadil/wealthfolio/issues/476) + +resource "kubernetes_namespace" "wealthfolio" { + metadata { + name = "wealthfolio" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.wealthfolio.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "random_string" "random" { + length = 32 + lower = true +} + +resource "kubernetes_deployment" "wealthfolio" { + metadata { + name = "wealthfolio" + namespace = kubernetes_namespace.wealthfolio.metadata[0].name + labels = { + app = "wealthfolio" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "wealthfolio" + } + } + template { + metadata { + labels = { + app = "wealthfolio" + } + } + spec { + container { + image = "afadil/wealthfolio:latest" + name = "wealthfolio" + port { + container_port = 8080 + } + env { + name = "WF_LISTEN_ADDR" + value = "0.0.0.0:8080" + } + env { + name = "WF_AUTH_PASSWORD_HASH" + value = var.wealthfolio_password_hash + } + env { + name = "WF_DB_PATH" + value = "/data/wealthfolio.db" + } + env { + name = "WF_CORS_ALLOW_ORIGINS" + value = "https://authentik.viktorbarzin.me" + } + env { + name = "WF_AUTH_TOKEN_TTL_MINUTES" + value = "10080" + } + env { + name = "WF_SECRET_KEY" + value = random_string.random.result + } + volume_mount { + name = "data" + mount_path = "/data" + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/wealthfolio" + } + } + } + } + } +} + +resource "kubernetes_service" "wealthfolio" { + metadata { + name = "wealthfolio" + namespace = kubernetes_namespace.wealthfolio.metadata[0].name + labels = { + "app" = "wealthfolio" + } + } + + spec { + selector = { + app = "wealthfolio" + } + port { + name = "http" + port = 80 + target_port = 8080 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.wealthfolio.metadata[0].name + name = "wealthfolio" + tls_secret_name = var.tls_secret_name + protected = true } diff --git a/stacks/wealthfolio/module/main.tf b/stacks/wealthfolio/module/main.tf deleted file mode 100644 index c6acdec2..00000000 --- a/stacks/wealthfolio/module/main.tf +++ /dev/null @@ -1,130 +0,0 @@ -# To refresh transactions use finance db positions exporters: -# -# workon finace-app && cd ~/code/finance && python main.py fetch position --imap-user=$IMAP_USER --imap-password=$IMAP_PASSWORD --trading212-api-keys=$TRADING212_API_KEYS --output-file positions.csv && mv positions.csv /home/wizard/code/infra/modules/kubernetes/wealthfolio/updated_trades.csv -# -# Then upload updated_trades.csv -# Note that currently wealthfolio doesn't dedup (https://github.com/afadil/wealthfolio/issues/476) - -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "wealthfolio_password_hash" {} - -resource "kubernetes_namespace" "wealthfolio" { - metadata { - name = "wealthfolio" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.wealthfolio.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "random_string" "random" { - length = 32 - lower = true -} - -resource "kubernetes_deployment" "wealthfolio" { - metadata { - name = "wealthfolio" - namespace = kubernetes_namespace.wealthfolio.metadata[0].name - labels = { - app = "wealthfolio" - tier = var.tier - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "wealthfolio" - } - } - template { - metadata { - labels = { - app = "wealthfolio" - } - } - spec { - container { - image = "afadil/wealthfolio:latest" - name = "wealthfolio" - port { - container_port = 8080 - } - env { - name = "WF_LISTEN_ADDR" - value = "0.0.0.0:8080" - } - env { - name = "WF_AUTH_PASSWORD_HASH" - value = var.wealthfolio_password_hash - } - env { - name = "WF_DB_PATH" - value = "/data/wealthfolio.db" - } - env { - name = "WF_CORS_ALLOW_ORIGINS" - value = "https://authentik.viktorbarzin.me" - } - env { - name = "WF_AUTH_TOKEN_TTL_MINUTES" - value = "10080" - } - env { - name = "WF_SECRET_KEY" - value = random_string.random.result - } - volume_mount { - name = "data" - mount_path = "/data" - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/wealthfolio" - } - } - } - } - } -} - -resource "kubernetes_service" "wealthfolio" { - metadata { - name = "wealthfolio" - namespace = kubernetes_namespace.wealthfolio.metadata[0].name - labels = { - "app" = "wealthfolio" - } - } - - spec { - selector = { - app = "wealthfolio" - } - port { - name = "http" - port = 80 - target_port = 8080 - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.wealthfolio.metadata[0].name - name = "wealthfolio" - tls_secret_name = var.tls_secret_name - protected = true -} diff --git a/stacks/webhook_handler/main.tf b/stacks/webhook_handler/main.tf index fb258422..79f5b1e6 100644 --- a/stacks/webhook_handler/main.tf +++ b/stacks/webhook_handler/main.tf @@ -17,15 +17,193 @@ locals { } } -module "webhook_handler" { - source = "./module" - 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.tiers.aux +resource "kubernetes_namespace" "webhook-handler" { + metadata { + name = "webhook-handler" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.webhook-handler.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_cluster_role" "deployment_updater" { + metadata { + name = "deployment-updater" + } + + rule { + verbs = ["create", "update", "get", "patch", "list"] + api_groups = ["extensions", "apps", ""] + resources = ["deployments", "namespaces", "pods", "services"] + } +} + +resource "kubernetes_cluster_role_binding" "update_deployment_binding" { + metadata { + name = "update-deployment-binding" + } + + subject { + kind = "ServiceAccount" + name = "default" + namespace = kubernetes_namespace.webhook-handler.metadata[0].name + } + + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = "deployment-updater" + } +} + + +resource "kubernetes_secret" "ssh-key" { + metadata { + name = "ssh-key" + namespace = kubernetes_namespace.webhook-handler.metadata[0].name + + annotations = { + "reloader.stakater.com/match" = "true" + } + } + data = { + "id_rsa" = var.webhook_handler_ssh_key + } + type = "generic" +} +resource "kubernetes_deployment" "webhook_handler" { + metadata { + name = "webhook-handler" + namespace = kubernetes_namespace.webhook-handler.metadata[0].name + labels = { + app = "webhook-handler" + tier = local.tiers.aux + } + annotations = { + "reloader.stakater.com/search" = "true" + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "webhook-handler" + } + } + template { + metadata { + labels = { + app = "webhook-handler" + } + } + spec { + container { + # security_context { + # run_as_user = 1000 + # } + # lifecycle { + # post_start { + # exec { + # # Must be kept in sycn with webhook_handler dockerfile + # command = ["echo", "\"$SSH_KEY\"", ">", "/opt/id_rsa", "&&", "chown", "appuser", "/opt/id_rsa", "&&", "chmod", "600", "/opt/id_rsa"] + # } + # } + # } + image = "viktorbarzin/webhook-handler:latest" + name = "webhook-handler" + resources { + limits = { + cpu = "0.5" + memory = "512Mi" + } + requests = { + cpu = "250m" + memory = "50Mi" + } + } + port { + container_port = 80 + } + volume_mount { + name = "id-rsa" + mount_path = "/opt/id_rsa" + sub_path = "id_rsa" + } + env { + name = "WEBHOOKSECRET" + value = var.webhook_handler_secret + } + env { + name = "FB_APP_SECRET" + value = var.webhook_handler_fb_app_secret + } + env { + name = "FB_VERIFY_TOKEN" + value = var.webhook_handler_fb_verify_token + } + env { + name = "FB_PAGE_TOKEN" + value = var.webhook_handler_fb_page_token + } + env { + name = "CONFIG" + value = "./chatbot/config/viktorwebservices.yaml" + } + env { + name = "GIT_USER" + value = var.webhook_handler_git_user + } + env { + name = "GIT_TOKEN" + value = var.webhook_handler_git_token + } + env { + name = "SSH_KEY" + value = "/opt/id_rsa" + } + } + volume { + name = "id-rsa" + secret { + secret_name = "ssh-key" + } + + } + } + } + } +} + +resource "kubernetes_service" "webhook_handler" { + metadata { + name = "webhook-handler" + namespace = kubernetes_namespace.webhook-handler.metadata[0].name + labels = { + "app" = "webhook-handler" + } + } + + spec { + selector = { + app = "webhook-handler" + } + port { + port = "80" + target_port = "3000" + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.webhook-handler.metadata[0].name + name = "webhook-handler" + host = "webhook" + tls_secret_name = var.tls_secret_name } diff --git a/stacks/webhook_handler/module/main.tf b/stacks/webhook_handler/module/main.tf deleted file mode 100644 index e5792081..00000000 --- a/stacks/webhook_handler/module/main.tf +++ /dev/null @@ -1,201 +0,0 @@ - -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "webhook_secret" {} -variable "fb_verify_token" {} -variable "fb_page_token" {} -variable "fb_app_secret" {} -variable "git_user" {} -variable "git_token" {} -variable "ssh_key" {} - -resource "kubernetes_namespace" "webhook-handler" { - metadata { - name = "webhook-handler" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.webhook-handler.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_cluster_role" "deployment_updater" { - metadata { - name = "deployment-updater" - } - - rule { - verbs = ["create", "update", "get", "patch", "list"] - api_groups = ["extensions", "apps", ""] - resources = ["deployments", "namespaces", "pods", "services"] - } -} - -resource "kubernetes_cluster_role_binding" "update_deployment_binding" { - metadata { - name = "update-deployment-binding" - } - - subject { - kind = "ServiceAccount" - name = "default" - namespace = kubernetes_namespace.webhook-handler.metadata[0].name - } - - role_ref { - api_group = "rbac.authorization.k8s.io" - kind = "ClusterRole" - name = "deployment-updater" - } -} - - -resource "kubernetes_secret" "ssh-key" { - metadata { - name = "ssh-key" - namespace = kubernetes_namespace.webhook-handler.metadata[0].name - - annotations = { - "reloader.stakater.com/match" = "true" - } - } - data = { - "id_rsa" = var.ssh_key - } - type = "generic" -} -resource "kubernetes_deployment" "webhook_handler" { - metadata { - name = "webhook-handler" - namespace = kubernetes_namespace.webhook-handler.metadata[0].name - labels = { - app = "webhook-handler" - tier = var.tier - } - annotations = { - "reloader.stakater.com/search" = "true" - } - } - spec { - replicas = 1 - selector { - match_labels = { - app = "webhook-handler" - } - } - template { - metadata { - labels = { - app = "webhook-handler" - } - } - spec { - container { - # security_context { - # run_as_user = 1000 - # } - # lifecycle { - # post_start { - # exec { - # # Must be kept in sycn with webhook_handler dockerfile - # command = ["echo", "\"$SSH_KEY\"", ">", "/opt/id_rsa", "&&", "chown", "appuser", "/opt/id_rsa", "&&", "chmod", "600", "/opt/id_rsa"] - # } - # } - # } - image = "viktorbarzin/webhook-handler:latest" - name = "webhook-handler" - resources { - limits = { - cpu = "0.5" - memory = "512Mi" - } - requests = { - cpu = "250m" - memory = "50Mi" - } - } - port { - container_port = 80 - } - volume_mount { - name = "id-rsa" - mount_path = "/opt/id_rsa" - sub_path = "id_rsa" - } - env { - name = "WEBHOOKSECRET" - value = var.webhook_secret - } - env { - name = "FB_APP_SECRET" - value = var.fb_app_secret - } - env { - name = "FB_VERIFY_TOKEN" - value = var.fb_verify_token - } - env { - name = "FB_PAGE_TOKEN" - value = var.fb_page_token - } - env { - name = "CONFIG" - value = "./chatbot/config/viktorwebservices.yaml" - } - env { - name = "GIT_USER" - value = var.git_user - } - env { - name = "GIT_TOKEN" - value = var.git_token - } - env { - name = "SSH_KEY" - value = "/opt/id_rsa" - } - } - volume { - name = "id-rsa" - secret { - secret_name = "ssh-key" - } - - } - } - } - } -} - -resource "kubernetes_service" "webhook_handler" { - metadata { - name = "webhook-handler" - namespace = kubernetes_namespace.webhook-handler.metadata[0].name - labels = { - "app" = "webhook-handler" - } - } - - spec { - selector = { - app = "webhook-handler" - } - port { - port = "80" - target_port = "3000" - } - } -} - -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.webhook-handler.metadata[0].name - name = "webhook-handler" - host = "webhook" - tls_secret_name = var.tls_secret_name -} diff --git a/stacks/whisper/main.tf b/stacks/whisper/main.tf index 8e175e0c..1c099c16 100644 --- a/stacks/whisper/main.tf +++ b/stacks/whisper/main.tf @@ -10,8 +10,259 @@ locals { } } -module "whisper" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.gpu +resource "kubernetes_namespace" "whisper" { + metadata { + name = "whisper" + labels = { + tier = local.tiers.gpu + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.whisper.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "whisper" { + metadata { + name = "whisper" + namespace = kubernetes_namespace.whisper.metadata[0].name + labels = { + app = "whisper" + tier = local.tiers.gpu + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "whisper" + } + } + template { + metadata { + labels = { + app = "whisper" + } + } + spec { + node_selector = { + "gpu" : "true" + } + toleration { + key = "nvidia.com/gpu" + operator = "Equal" + value = "true" + effect = "NoSchedule" + } + + container { + name = "whisper" + image = "rhasspy/wyoming-whisper:latest" + args = ["--model", "small-int8", "--language", "en", "--beam-size", "1"] + + port { + container_port = 10300 + protocol = "TCP" + } + + volume_mount { + name = "data" + mount_path = "/data" + } + } + + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/whisper" + } + } + } + } + } +} + +resource "kubernetes_service" "whisper" { + metadata { + name = "whisper" + namespace = kubernetes_namespace.whisper.metadata[0].name + labels = { + app = "whisper" + } + } + + spec { + selector = { + app = "whisper" + } + port { + name = "wyoming" + port = 10300 + target_port = 10300 + protocol = "TCP" + } + } +} + +# TCP passthrough from Traefik to whisper service +resource "kubernetes_manifest" "whisper_tcp_ingressroute" { + manifest = { + apiVersion = "traefik.io/v1alpha1" + kind = "IngressRouteTCP" + metadata = { + name = "whisper-tcp" + namespace = "traefik" + } + spec = { + entryPoints = ["whisper-tcp"] + routes = [{ + match = "HostSNI(`*`)" + services = [{ + name = "whisper" + namespace = "whisper" + port = 10300 + }] + }] + } + } +} + +# Piper TTS +resource "kubernetes_deployment" "piper" { + metadata { + name = "piper" + namespace = kubernetes_namespace.whisper.metadata[0].name + labels = { + app = "piper" + tier = local.tiers.gpu + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "piper" + } + } + template { + metadata { + labels = { + app = "piper" + } + } + spec { + node_selector = { + "gpu" : "true" + } + toleration { + key = "nvidia.com/gpu" + operator = "Equal" + value = "true" + effect = "NoSchedule" + } + + container { + name = "piper" + image = "rhasspy/wyoming-piper:latest" + args = ["--voice", "en_US-lessac-medium"] + + port { + container_port = 10200 + protocol = "TCP" + } + + volume_mount { + name = "data" + mount_path = "/data" + } + } + + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/whisper" + } + } + } + } + } +} + +resource "kubernetes_service" "piper" { + metadata { + name = "piper" + namespace = kubernetes_namespace.whisper.metadata[0].name + labels = { + app = "piper" + } + } + + spec { + selector = { + app = "piper" + } + port { + name = "wyoming" + port = 10200 + target_port = 10200 + protocol = "TCP" + } + } +} + +# TCP passthrough from Traefik to piper service +resource "kubernetes_manifest" "piper_tcp_ingressroute" { + manifest = { + apiVersion = "traefik.io/v1alpha1" + kind = "IngressRouteTCP" + metadata = { + name = "piper-tcp" + namespace = "traefik" + } + spec = { + entryPoints = ["piper-tcp"] + routes = [{ + match = "HostSNI(`*`)" + services = [{ + name = "piper" + namespace = "whisper" + port = 10200 + }] + }] + } + } +} + +# TCP passthrough from Traefik to ollama service (for HA voice pipeline) +resource "kubernetes_manifest" "ollama_tcp_ingressroute" { + manifest = { + apiVersion = "traefik.io/v1alpha1" + kind = "IngressRouteTCP" + metadata = { + name = "ollama-tcp" + namespace = "traefik" + } + spec = { + entryPoints = ["ollama-tcp"] + routes = [{ + match = "HostSNI(`*`)" + services = [{ + name = "ollama" + namespace = "ollama" + port = 11434 + }] + }] + } + } } diff --git a/stacks/whisper/module/main.tf b/stacks/whisper/module/main.tf deleted file mode 100644 index a9aa69f3..00000000 --- a/stacks/whisper/module/main.tf +++ /dev/null @@ -1,259 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } - -resource "kubernetes_namespace" "whisper" { - metadata { - name = "whisper" - labels = { - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.whisper.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "whisper" { - metadata { - name = "whisper" - namespace = kubernetes_namespace.whisper.metadata[0].name - labels = { - app = "whisper" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "whisper" - } - } - template { - metadata { - labels = { - app = "whisper" - } - } - spec { - node_selector = { - "gpu" : "true" - } - toleration { - key = "nvidia.com/gpu" - operator = "Equal" - value = "true" - effect = "NoSchedule" - } - - container { - name = "whisper" - image = "rhasspy/wyoming-whisper:latest" - args = ["--model", "small-int8", "--language", "en", "--beam-size", "1"] - - port { - container_port = 10300 - protocol = "TCP" - } - - volume_mount { - name = "data" - mount_path = "/data" - } - } - - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/whisper" - } - } - } - } - } -} - -resource "kubernetes_service" "whisper" { - metadata { - name = "whisper" - namespace = kubernetes_namespace.whisper.metadata[0].name - labels = { - app = "whisper" - } - } - - spec { - selector = { - app = "whisper" - } - port { - name = "wyoming" - port = 10300 - target_port = 10300 - protocol = "TCP" - } - } -} - -# TCP passthrough from Traefik to whisper service -resource "kubernetes_manifest" "whisper_tcp_ingressroute" { - manifest = { - apiVersion = "traefik.io/v1alpha1" - kind = "IngressRouteTCP" - metadata = { - name = "whisper-tcp" - namespace = "traefik" - } - spec = { - entryPoints = ["whisper-tcp"] - routes = [{ - match = "HostSNI(`*`)" - services = [{ - name = "whisper" - namespace = "whisper" - port = 10300 - }] - }] - } - } -} - -# Piper TTS -resource "kubernetes_deployment" "piper" { - metadata { - name = "piper" - namespace = kubernetes_namespace.whisper.metadata[0].name - labels = { - app = "piper" - tier = var.tier - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "piper" - } - } - template { - metadata { - labels = { - app = "piper" - } - } - spec { - node_selector = { - "gpu" : "true" - } - toleration { - key = "nvidia.com/gpu" - operator = "Equal" - value = "true" - effect = "NoSchedule" - } - - container { - name = "piper" - image = "rhasspy/wyoming-piper:latest" - args = ["--voice", "en_US-lessac-medium"] - - port { - container_port = 10200 - protocol = "TCP" - } - - volume_mount { - name = "data" - mount_path = "/data" - } - } - - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/whisper" - } - } - } - } - } -} - -resource "kubernetes_service" "piper" { - metadata { - name = "piper" - namespace = kubernetes_namespace.whisper.metadata[0].name - labels = { - app = "piper" - } - } - - spec { - selector = { - app = "piper" - } - port { - name = "wyoming" - port = 10200 - target_port = 10200 - protocol = "TCP" - } - } -} - -# TCP passthrough from Traefik to piper service -resource "kubernetes_manifest" "piper_tcp_ingressroute" { - manifest = { - apiVersion = "traefik.io/v1alpha1" - kind = "IngressRouteTCP" - metadata = { - name = "piper-tcp" - namespace = "traefik" - } - spec = { - entryPoints = ["piper-tcp"] - routes = [{ - match = "HostSNI(`*`)" - services = [{ - name = "piper" - namespace = "whisper" - port = 10200 - }] - }] - } - } -} - -# TCP passthrough from Traefik to ollama service (for HA voice pipeline) -resource "kubernetes_manifest" "ollama_tcp_ingressroute" { - manifest = { - apiVersion = "traefik.io/v1alpha1" - kind = "IngressRouteTCP" - metadata = { - name = "ollama-tcp" - namespace = "traefik" - } - spec = { - entryPoints = ["ollama-tcp"] - routes = [{ - match = "HostSNI(`*`)" - services = [{ - name = "ollama" - namespace = "ollama" - port = 11434 - }] - }] - } - } -} diff --git a/stacks/ytdlp/main.tf b/stacks/ytdlp/main.tf index 727e2711..484891d6 100644 --- a/stacks/ytdlp/main.tf +++ b/stacks/ytdlp/main.tf @@ -13,11 +13,318 @@ locals { } } -module "ytdlp" { - source = "./module" - tls_secret_name = var.tls_secret_name - tier = local.tiers.aux - openrouter_api_key = var.openrouter_api_key - slack_bot_token = var.slack_bot_token - slack_channel = var.slack_channel +resource "kubernetes_namespace" "ytdlp" { + metadata { + name = "ytdlp" + labels = { + "istio-injection" : "disabled" + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.ytdlp.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +resource "kubernetes_deployment" "ytdlp" { + # resource "kubernetes_daemonset" "technitium" { + metadata { + name = "ytdlp" + namespace = kubernetes_namespace.ytdlp.metadata[0].name + labels = { + app = "ytdlp" + tier = local.tiers.aux + } + annotations = { + "diun.enable" = "true" + } + } + spec { + # strategy { + # type = "Recreate" + # } + # replicas = 1 + selector { + match_labels = { + app = "ytdlp" + } + } + template { + metadata { + labels = { + app = "ytdlp" + } + } + spec { + container { + image = "tzahi12345/youtubedl-material:nightly" + name = "ytdlp" + # resources { + # limits = { + # cpu = "1" + # memory = "1Gi" + # } + # requests = { + # cpu = "1" + # memory = "1Gi" + # } + # } + port { + container_port = 17442 + } + volume_mount { + mount_path = "/app/appdata" + name = "data" + } + volume_mount { + mount_path = "/app/audio" + name = "data" + } + volume_mount { + mount_path = "/app/video" + name = "data" + } + volume_mount { + mount_path = "/app/users" + name = "data" + } + volume_mount { + mount_path = "/app/subscriptions" + name = "data" + } + } + volume { + name = "data" + nfs { + path = "/mnt/main/ytdlp" + server = "10.0.10.15" + } + } + # } + } + } + } +} + +resource "kubernetes_service" "ytdlp" { + metadata { + name = "ytdlp" + namespace = kubernetes_namespace.ytdlp.metadata[0].name + labels = { + "app" = "ytdlp" + } + } + + spec { + selector = { + app = "ytdlp" + } + port { + name = "ytdlp" + port = 80 + target_port = 17442 + protocol = "TCP" + } + } +} +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.ytdlp.metadata[0].name + name = "ytdlp" + tls_secret_name = var.tls_secret_name + host = "yt" +} + +# ---------------------- +# yt-highlights service +# ---------------------- + +resource "kubernetes_secret" "openrouter" { + metadata { + name = "openrouter-credentials" + namespace = kubernetes_namespace.ytdlp.metadata[0].name + } + data = { + "api-key" = var.openrouter_api_key + } +} + +resource "kubernetes_secret" "slack" { + metadata { + name = "slack-credentials" + namespace = kubernetes_namespace.ytdlp.metadata[0].name + } + data = { + "bot-token" = var.slack_bot_token + "channel" = var.slack_channel + } +} + +resource "kubernetes_deployment" "yt_highlights" { + metadata { + name = "yt-highlights" + namespace = kubernetes_namespace.ytdlp.metadata[0].name + labels = { + app = "yt-highlights" + tier = local.tiers.aux + } + annotations = { + "diun.enable" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "yt-highlights" + } + } + template { + metadata { + labels = { + app = "yt-highlights" + } + } + spec { + node_selector = { + "gpu" : "true" + } + container { + name = "yt-highlights" + image = "viktorbarzin/yt-highlights:v20-20260127" + image_pull_policy = "Always" + port { + container_port = 8000 + } + env { + name = "ASR_MODEL" + value = "large-v3" + } + env { + name = "ASR_DEVICE" + value = "cuda" + } + env { + name = "OPENROUTER_MODEL" + value = "deepseek/deepseek-r1-0528:free" + } + env { + name = "OPENROUTER_API_KEY" + value_from { + secret_key_ref { + name = kubernetes_secret.openrouter.metadata[0].name + key = "api-key" + } + } + } + env { + name = "DATA_PATH" + value = "/data" + } + env { + name = "SLACK_BOT_TOKEN" + value_from { + secret_key_ref { + name = kubernetes_secret.slack.metadata[0].name + key = "bot-token" + } + } + } + env { + name = "SLACK_CHANNEL" + value_from { + secret_key_ref { + name = kubernetes_secret.slack.metadata[0].name + key = "channel" + } + } + } + env { + name = "REDIS_URL" + value = "redis://redis.redis.svc.cluster.local:6379/0" + } + # Store model cache on NFS to avoid ephemeral storage eviction + env { + name = "HF_HOME" + value = "/data/cache/huggingface" + } + env { + name = "TORCH_HOME" + value = "/data/cache/torch" + } + # Ollama fallback for when OpenRouter models fail + env { + name = "OLLAMA_URL" + value = "http://ollama.ollama.svc.cluster.local:11434" + } + env { + name = "OLLAMA_MODEL" + value = "qwen2.5:14b" + } + volume_mount { + name = "data" + mount_path = "/data" + } + resources { + limits = { + "nvidia.com/gpu" = "1" + } + } + liveness_probe { + http_get { + path = "/health" + port = 8000 + } + initial_delay_seconds = 180 + period_seconds = 60 + timeout_seconds = 60 + failure_threshold = 10 + } + } + volume { + name = "data" + nfs { + server = "10.0.10.15" + path = "/mnt/main/ytdlp-highlights" + } + } + } + } + } +} + +resource "kubernetes_service" "yt_highlights" { + metadata { + name = "yt-highlights" + namespace = kubernetes_namespace.ytdlp.metadata[0].name + labels = { + "app" = "yt-highlights" + } + } + spec { + selector = { + app = "yt-highlights" + } + port { + name = "http" + port = 80 + target_port = 8000 + protocol = "TCP" + } + } +} + +module "highlights_ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.ytdlp.metadata[0].name + name = "yt-highlights" + tls_secret_name = var.tls_secret_name + host = "yt-highlights" + protected = true } diff --git a/stacks/ytdlp/module/main.tf b/stacks/ytdlp/module/main.tf deleted file mode 100644 index 63f96a88..00000000 --- a/stacks/ytdlp/module/main.tf +++ /dev/null @@ -1,321 +0,0 @@ -variable "tls_secret_name" {} -variable "tier" { type = string } -variable "openrouter_api_key" { type = string } -variable "slack_bot_token" { type = string } -variable "slack_channel" { type = string } - -resource "kubernetes_namespace" "ytdlp" { - metadata { - name = "ytdlp" - labels = { - "istio-injection" : "disabled" - tier = var.tier - } - } -} - -module "tls_secret" { - source = "../../../modules/kubernetes/setup_tls_secret" - namespace = kubernetes_namespace.ytdlp.metadata[0].name - tls_secret_name = var.tls_secret_name -} - -resource "kubernetes_deployment" "ytdlp" { - # resource "kubernetes_daemonset" "technitium" { - metadata { - name = "ytdlp" - namespace = kubernetes_namespace.ytdlp.metadata[0].name - labels = { - app = "ytdlp" - tier = var.tier - } - annotations = { - "diun.enable" = "true" - } - } - spec { - # strategy { - # type = "Recreate" - # } - # replicas = 1 - selector { - match_labels = { - app = "ytdlp" - } - } - template { - metadata { - labels = { - app = "ytdlp" - } - } - spec { - container { - image = "tzahi12345/youtubedl-material:nightly" - name = "ytdlp" - # resources { - # limits = { - # cpu = "1" - # memory = "1Gi" - # } - # requests = { - # cpu = "1" - # memory = "1Gi" - # } - # } - port { - container_port = 17442 - } - volume_mount { - mount_path = "/app/appdata" - name = "data" - } - volume_mount { - mount_path = "/app/audio" - name = "data" - } - volume_mount { - mount_path = "/app/video" - name = "data" - } - volume_mount { - mount_path = "/app/users" - name = "data" - } - volume_mount { - mount_path = "/app/subscriptions" - name = "data" - } - } - volume { - name = "data" - nfs { - path = "/mnt/main/ytdlp" - server = "10.0.10.15" - } - } - # } - } - } - } -} - -resource "kubernetes_service" "ytdlp" { - metadata { - name = "ytdlp" - namespace = kubernetes_namespace.ytdlp.metadata[0].name - labels = { - "app" = "ytdlp" - } - } - - spec { - selector = { - app = "ytdlp" - } - port { - name = "ytdlp" - port = 80 - target_port = 17442 - protocol = "TCP" - } - } -} -module "ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.ytdlp.metadata[0].name - name = "ytdlp" - tls_secret_name = var.tls_secret_name - host = "yt" -} - -# ---------------------- -# yt-highlights service -# ---------------------- - -resource "kubernetes_secret" "openrouter" { - metadata { - name = "openrouter-credentials" - namespace = kubernetes_namespace.ytdlp.metadata[0].name - } - data = { - "api-key" = var.openrouter_api_key - } -} - -resource "kubernetes_secret" "slack" { - metadata { - name = "slack-credentials" - namespace = kubernetes_namespace.ytdlp.metadata[0].name - } - data = { - "bot-token" = var.slack_bot_token - "channel" = var.slack_channel - } -} - -resource "kubernetes_deployment" "yt_highlights" { - metadata { - name = "yt-highlights" - namespace = kubernetes_namespace.ytdlp.metadata[0].name - labels = { - app = "yt-highlights" - tier = var.tier - } - annotations = { - "diun.enable" = "true" - } - } - spec { - replicas = 1 - strategy { - type = "Recreate" - } - selector { - match_labels = { - app = "yt-highlights" - } - } - template { - metadata { - labels = { - app = "yt-highlights" - } - } - spec { - node_selector = { - "gpu" : "true" - } - container { - name = "yt-highlights" - image = "viktorbarzin/yt-highlights:v20-20260127" - image_pull_policy = "Always" - port { - container_port = 8000 - } - env { - name = "ASR_MODEL" - value = "large-v3" - } - env { - name = "ASR_DEVICE" - value = "cuda" - } - env { - name = "OPENROUTER_MODEL" - value = "deepseek/deepseek-r1-0528:free" - } - env { - name = "OPENROUTER_API_KEY" - value_from { - secret_key_ref { - name = kubernetes_secret.openrouter.metadata[0].name - key = "api-key" - } - } - } - env { - name = "DATA_PATH" - value = "/data" - } - env { - name = "SLACK_BOT_TOKEN" - value_from { - secret_key_ref { - name = kubernetes_secret.slack.metadata[0].name - key = "bot-token" - } - } - } - env { - name = "SLACK_CHANNEL" - value_from { - secret_key_ref { - name = kubernetes_secret.slack.metadata[0].name - key = "channel" - } - } - } - env { - name = "REDIS_URL" - value = "redis://redis.redis.svc.cluster.local:6379/0" - } - # Store model cache on NFS to avoid ephemeral storage eviction - env { - name = "HF_HOME" - value = "/data/cache/huggingface" - } - env { - name = "TORCH_HOME" - value = "/data/cache/torch" - } - # Ollama fallback for when OpenRouter models fail - env { - name = "OLLAMA_URL" - value = "http://ollama.ollama.svc.cluster.local:11434" - } - env { - name = "OLLAMA_MODEL" - value = "qwen2.5:14b" - } - volume_mount { - name = "data" - mount_path = "/data" - } - resources { - limits = { - "nvidia.com/gpu" = "1" - } - } - liveness_probe { - http_get { - path = "/health" - port = 8000 - } - initial_delay_seconds = 180 - period_seconds = 60 - timeout_seconds = 60 - failure_threshold = 10 - } - } - volume { - name = "data" - nfs { - server = "10.0.10.15" - path = "/mnt/main/ytdlp-highlights" - } - } - } - } - } -} - -resource "kubernetes_service" "yt_highlights" { - metadata { - name = "yt-highlights" - namespace = kubernetes_namespace.ytdlp.metadata[0].name - labels = { - "app" = "yt-highlights" - } - } - spec { - selector = { - app = "yt-highlights" - } - port { - name = "http" - port = 80 - target_port = 8000 - protocol = "TCP" - } - } -} - -module "highlights_ingress" { - source = "../../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.ytdlp.metadata[0].name - name = "yt-highlights" - tls_secret_name = var.tls_secret_name - host = "yt-highlights" - protected = true -} diff --git a/stacks/ytdlp/module/yt-highlights/Dockerfile b/stacks/ytdlp/yt-highlights/Dockerfile similarity index 100% rename from stacks/ytdlp/module/yt-highlights/Dockerfile rename to stacks/ytdlp/yt-highlights/Dockerfile diff --git a/stacks/ytdlp/module/yt-highlights/app/__init__.py b/stacks/ytdlp/yt-highlights/app/__init__.py similarity index 100% rename from stacks/ytdlp/module/yt-highlights/app/__init__.py rename to stacks/ytdlp/yt-highlights/app/__init__.py diff --git a/stacks/ytdlp/module/yt-highlights/app/main.py b/stacks/ytdlp/yt-highlights/app/main.py similarity index 100% rename from stacks/ytdlp/module/yt-highlights/app/main.py rename to stacks/ytdlp/yt-highlights/app/main.py diff --git a/stacks/ytdlp/module/yt-highlights/app/static/index.html b/stacks/ytdlp/yt-highlights/app/static/index.html similarity index 100% rename from stacks/ytdlp/module/yt-highlights/app/static/index.html rename to stacks/ytdlp/yt-highlights/app/static/index.html diff --git a/stacks/ytdlp/module/yt-highlights/requirements.txt b/stacks/ytdlp/yt-highlights/requirements.txt similarity index 100% rename from stacks/ytdlp/module/yt-highlights/requirements.txt rename to stacks/ytdlp/yt-highlights/requirements.txt