From 84703b8aef6849a08eb954497350c4a6d71102d4 Mon Sep 17 00:00:00 2001 From: Niklas van Schrick Date: Sat, 3 Oct 2026 22:24:53 +0200 Subject: [PATCH] Setup staging crater and cygnus --- .github/workflows/ci.yml | 1 + envs/server_staging/main.tf | 23 +++- envs/server_staging/variables.tf | 5 + manifests/teleport-config/applications.yaml | 14 ++ manifests/teleport-config/roles.yaml | 2 +- modules/docker/cygnus/cygnus.tf | 36 +++-- modules/docker/cygnus/network.tf | 2 +- modules/docker/cygnus/postgres.tf | 4 +- modules/docker/cygnus/variables.tf | 39 +++++- renovate.json | 3 +- system/administration/main.tf | 16 ++- system/staging/main.tf | 141 +++++++++++++++++++- system/staging/variables.tf | 5 + system/teleport/main.tf | 4 +- 14 files changed, 261 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44fc56a8..02033ce5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,7 @@ jobs: GLPA_TF_VAR_server_administration_ssh_port: ${{ secrets.SERVER_ADMINISTRATION_SSH_PORT }} GLPA_TF_VAR_server_staging_ip: ${{ secrets.SERVER_STAGING_IP }} GLPA_TF_VAR_server_staging_ssh_port: ${{ secrets.SERVER_STAGING_SSH_PORT }} + GLPA_TF_VAR_stripe_staging_api_key: ${{ secrets.STRIPE_STAGING_API_KEY }} - name: Find existing comment uses: peter-evans/find-comment@v4 diff --git a/envs/server_staging/main.tf b/envs/server_staging/main.tf index ce8f4661..11057d6f 100644 --- a/envs/server_staging/main.tf +++ b/envs/server_staging/main.tf @@ -7,13 +7,17 @@ terraform { version = "5.25.0" } gitlab = { - source = "gitlabhq/gitlab" + source = "gitlabhq/gitlab" version = "19.4.0" } docker = { source = "kreuzwerker/docker" version = "4.6.0" } + stripe = { + source = "stripe/stripe" + version = "0.3.0" + } } } @@ -22,7 +26,7 @@ provider "cloudflare" { } provider "gitlab" { - token = var.gitlab_api_token + token = var.gitlab_api_token base_url = "https://gitlab.com/api/v4/" } @@ -30,11 +34,22 @@ provider "docker" { host = "ssh://pipeline@${var.server_staging_ip}:${var.server_staging_ssh_port}" cert_path = "" + + registry_auth { + address = "registry.gitlab.com" + username = "gitlab-ci-token" + password = var.gitlab_api_token + } +} + +provider "stripe" { + api_key = var.stripe_staging_api_key } module "staging" { source = "../../system/staging" - cloudflare_account_id = var.cloudflare_account_id - server_staging_ip = var.server_staging_ip + cloudflare_account_id = var.cloudflare_account_id + server_staging_ip = var.server_staging_ip + stripe_staging_api_key = var.stripe_staging_api_key } diff --git a/envs/server_staging/variables.tf b/envs/server_staging/variables.tf index 735a1497..1146d27c 100644 --- a/envs/server_staging/variables.tf +++ b/envs/server_staging/variables.tf @@ -13,6 +13,11 @@ variable "gitlab_api_token" { sensitive = true } +variable "stripe_staging_api_key" { + type = string + sensitive = true +} + variable "server_staging_ip" { type = string sensitive = true diff --git a/manifests/teleport-config/applications.yaml b/manifests/teleport-config/applications.yaml index 12682de3..0d3bacdd 100644 --- a/manifests/teleport-config/applications.yaml +++ b/manifests/teleport-config/applications.yaml @@ -9,3 +9,17 @@ metadata: spec: uri: http://127.0.0.1:15242 public_addr: staging.codezero.build +--- +apiVersion: resources.teleport.dev/v1 +kind: TeleportAppV3 +metadata: + name: cygnus-staging + labels: + application: cygnus + environment: staging + server: staging +spec: + uri: http://127.0.0.1:15243 + public_addr: staging-cygnus.codezero.build + required_app_names: + - codezero-staging diff --git a/manifests/teleport-config/roles.yaml b/manifests/teleport-config/roles.yaml index 6a964b95..a20bf048 100644 --- a/manifests/teleport-config/roles.yaml +++ b/manifests/teleport-config/roles.yaml @@ -9,7 +9,7 @@ spec: logins: - teleport-user app_labels: - "application": "codezero" + "application": ["codezero", "cygnus"] "environment": "staging" --- apiVersion: resources.teleport.dev/v1 diff --git a/modules/docker/cygnus/cygnus.tf b/modules/docker/cygnus/cygnus.tf index 31bbd6c8..2642c2d9 100644 --- a/modules/docker/cygnus/cygnus.tf +++ b/modules/docker/cygnus/cygnus.tf @@ -1,10 +1,6 @@ -data "docker_registry_image" "cygnus" { - name = "ghcr.io/code0-tech/cygnus:2141" -} - resource "docker_image" "cygnus" { - name = data.docker_registry_image.cygnus.name - pull_triggers = [data.docker_registry_image.cygnus.sha256_digest] + name = var.cygnus_image.name + pull_triggers = [var.cygnus_image.sha256_digest] } resource "random_password" "payload_secret" { @@ -16,7 +12,7 @@ resource "random_password" "payload_user_password" { } resource "random_password" "actions_import_secret" { - length = 64 + length = 64 } data "gitlab_project_variable" "ga_measurement_id" { @@ -53,7 +49,7 @@ locals { "DATABASE_URL=postgresql://cygnus:${random_password.db.result}@${docker_container.postgres.hostname}:5432/payload", "HOSTNAME=0.0.0.0", "NEXT_PUBLIC_GA_MEASUREMENT_ID=${sensitive(data.gitlab_project_variable.ga_measurement_id.value)}", - "NEXT_PUBLIC_APP_URL=${var.web_urls[0]}", + "PAYLOAD_SERVER_URL=https://${var.web_urls[0]}", "ACTIONS_IMPORT_SECRET=${random_password.actions_import_secret.result}", # Cygnus SMTP @@ -70,15 +66,15 @@ locals { } resource "docker_volume" "cygnus_media" { - name = "cygnus_media" + name = "${var.docker_name_prefix}cygnus_media" } resource "docker_container" "cygnus" { image = docker_image.cygnus.image_id - name = "cygnus_cygnus" + name = "${var.docker_name_prefix}cygnus_cygnus" restart = "always" - env = local.cygnus_env + env = concat(local.cygnus_env, var.additional_envs) network_mode = "bridge" @@ -86,8 +82,22 @@ resource "docker_container" "cygnus" { name = docker_network.cygnus.name } - networks_advanced { - name = var.docker_proxy_network_id + dynamic "networks_advanced" { + for_each = compact([var.docker_additional_network_id]) + + content { + name = networks_advanced.value + } + } + + dynamic "ports" { + for_each = compact([var.http_port]) + + content { + internal = 3000 + external = ports.value + ip = var.bind_ip + } } volumes { diff --git a/modules/docker/cygnus/network.tf b/modules/docker/cygnus/network.tf index d5f81742..6ebd7b45 100644 --- a/modules/docker/cygnus/network.tf +++ b/modules/docker/cygnus/network.tf @@ -1,3 +1,3 @@ resource "docker_network" "cygnus" { - name = "cygnus" + name = "${var.docker_name_prefix}cygnus" } diff --git a/modules/docker/cygnus/postgres.tf b/modules/docker/cygnus/postgres.tf index 0522cb8c..0780214a 100644 --- a/modules/docker/cygnus/postgres.tf +++ b/modules/docker/cygnus/postgres.tf @@ -8,7 +8,7 @@ resource "docker_image" "postgres" { } resource "docker_volume" "pgdata" { - name = "cygnus_pgdata" + name = "${var.docker_name_prefix}cygnus_pgdata" } resource "random_password" "db" { @@ -28,7 +28,7 @@ locals { //noinspection HILUnresolvedReference resource "docker_container" "postgres" { image = docker_image.postgres.image_id - name = "cygnus_postgres" + name = "${var.docker_name_prefix}cygnus_postgres" restart = "always" env = local.postgres_env diff --git a/modules/docker/cygnus/variables.tf b/modules/docker/cygnus/variables.tf index 76ef1edd..ddebe206 100644 --- a/modules/docker/cygnus/variables.tf +++ b/modules/docker/cygnus/variables.tf @@ -1,7 +1,42 @@ -variable "docker_proxy_network_id" { - type = string +variable "docker_name_prefix" { + type = string + default = "" +} + +variable "docker_additional_network_id" { + type = string + default = null } variable "web_urls" { type = list(string) } + +variable "bind_ip" { + description = <<-EOT + Host IP address to bind the published cygnus ports to. Defaults to null, + which binds on all interfaces (0.0.0.0). Set to "127.0.0.1" to expose the + ports on localhost only (e.g. when running behind an external reverse + proxy). + EOT + type = string + default = null +} + +variable "http_port" { + description = "Host port mapped to cygnus' internal port 3000. Set to null to not publish HTTP." + type = number + default = null +} + +variable "cygnus_image" { + type = object({ + name = string + sha256_digest = string + }) +} + +variable "additional_envs" { + type = list(string) + default = [] +} diff --git a/renovate.json b/renovate.json index 9b2cdf2e..eb2ff02f 100644 --- a/renovate.json +++ b/renovate.json @@ -18,7 +18,8 @@ }, { "matchFileNames": [ - "modules/docker/**" + "modules/docker/**", + "system/**" ], "matchDatasources": [ "docker" diff --git a/system/administration/main.tf b/system/administration/main.tf index 06635efc..c74239d5 100644 --- a/system/administration/main.tf +++ b/system/administration/main.tf @@ -53,19 +53,27 @@ module "outline" { docker_proxy_network_id = module.proxy.docker_proxy_network_id } +data "docker_registry_image" "cygnus" { + name = "ghcr.io/code0-tech/cygnus:2141" +} + module "cygnus" { source = "../../modules/docker/cygnus" - web_urls = ["codezero.build"] - docker_proxy_network_id = module.proxy.docker_proxy_network_id + web_urls = ["codezero.build"] + docker_additional_network_id = module.proxy.docker_proxy_network_id + cygnus_image = { + name = data.docker_registry_image.cygnus.name + sha256_digest = data.docker_registry_image.cygnus.sha256_digest + } } module "sculptor_playground" { source = "../../modules/docker/sculptor-playground" - hostname = "playground.codezero.build" + hostname = "playground.codezero.build" playground_frame_ancestors = "'self' https://codezero.build http://localhost:3000" - docker_proxy_network_id = module.proxy.docker_proxy_network_id + docker_proxy_network_id = module.proxy.docker_proxy_network_id } module "pyxis" { diff --git a/system/staging/main.tf b/system/staging/main.tf index a2e0119e..1cf3c719 100644 --- a/system/staging/main.tf +++ b/system/staging/main.tf @@ -8,6 +8,14 @@ terraform { source = "kreuzwerker/docker" version = "4.6.0" } + gitlab = { + source = "gitlabhq/gitlab" + version = "19.4.0" + } + stripe = { + source = "stripe/stripe" + version = "0.3.0" + } } } @@ -51,11 +59,27 @@ resource "cloudflare_dns_record" "server_cname_code0_tech" { comment = "Managed by Terraform" } +resource "cloudflare_dns_record" "server_cname_codezero_build" { + for_each = toset([ + "crater-staging.codezero.build", + ]) + + name = each.value + type = "CNAME" + ttl = 1 + zone_id = data.cloudflare_zones.codezero_build_domain.result[0].id + content = cloudflare_dns_record.server_ip.name + proxied = true + + comment = "Managed by Terraform" +} + module "proxy" { source = "../../modules/docker/proxy" certificate_hostnames = [ "signoz.code0.tech", + "crater-staging.codezero.build", ] } @@ -64,19 +88,40 @@ resource "random_password" "codezero_initial_root_password" { special = false } +resource "random_password" "crater_jwt_secret" { + length = 32 + special = false +} + +resource "docker_network" "codezero_staging" { + name = "codezero_staging" +} + module "codezero" { - source = "github.com/code0-tech/reticulum//terraform/docker?ref=161d4aba28edbbfd795a7135a004bc43ea59432b" + source = "github.com/code0-tech/reticulum//terraform/docker?ref=04363b68c6d922bd91529f07a463403027bb8ac6" hostname = "staging.codezero.build" initial_root_mail = "root@code0.tech" initial_root_password = random_password.codezero_initial_root_password.result - image_tag = "0.0.0-canary-2821608682-64f60183ed488c060e58140d9fac1f4f59fa3a74" + image_tag = "0.0.0-experimental-2924424765-d26d9c8654878105c267deabbc80efc9099529d3" image_edition = "cloud" image_registry = "ghcr.io/code0-tech/reticulum/ci-builds" - http_port = 15242 - https_port = null - nginx_bind_ip = "127.0.0.1" + http_port = 15242 + https_port = null + nginx_bind_ip = "127.0.0.1" + nginx_additional_network = docker_network.codezero_staging.name + + additional_sagittarius_config = yamlencode({ + crater = { + jwt_secret = random_password.crater_jwt_secret.result + } + }) + + sculptor_env = [ + "SUBSCRIPTION_URL=https://staging-cygnus.codezero.build/licenses", + "CHECKOUT_URL=https://staging-cygnus.codezero.build/subscription", + ] } module "signoz" { @@ -85,3 +130,89 @@ module "signoz" { proxy_network = module.proxy.docker_proxy_network_name hostname = "signoz.code0.tech" } + +module "stripe_config" { + source = "git::ssh://git@github.com/code0-tech/mensa-private//modules/stripe/config?ref=cfcc21fe714aad14d06d6044f37bddb1fabf58a0" + + webhook_url = "https://crater-staging.codezero.build/webhooks/stripe" +} + +resource "gitlab_project_variable" "stripe_webhook_secret" { + project = "code0-tech/secret-manager" + key = "STRIPE_STAGING_WEBHOOK_SECRET" + value = module.stripe_config.webhook_secret + + lifecycle { + ignore_changes = [value] + } +} + +data "gitlab_project_variable" "stripe_webhook_secret" { + project = gitlab_project_variable.stripe_webhook_secret.project + key = gitlab_project_variable.stripe_webhook_secret.key +} + +data "docker_registry_image" "crater" { + name = "registry.gitlab.com/code0-tech/development/crater:2910747396" +} + +data "gitlab_project_secure_file" "license_encryption_key" { + project = "code0-tech/secret-manager" + name = "license_encryption_key_production.key" +} + +module "crater" { + source = "git::ssh://git@github.com/code0-tech/mensa-private//modules/docker/crater?ref=74b62554368f7eae52151d996d3a90f0f685bda9" + + docker_name_prefix = "staging-" + docker_additional_network_ids = [docker_network.codezero_staging.name, module.proxy.docker_proxy_network_name] + virtual_host = "crater-staging.codezero.build" + crater_image = { + name = data.docker_registry_image.crater.name + sha256_digest = data.docker_registry_image.crater.sha256_digest + } + stripe = { + api_key = var.stripe_staging_api_key + webhook_secret = data.gitlab_project_variable.stripe_webhook_secret.value + } + additional_crater_config = yamlencode({ + rails = { + web = { + force_ssl = false + } + } + sagittarius = { + host = "http://${module.codezero.nginx_container_hostname}" + jwt_secret = random_password.crater_jwt_secret.result + } + checkout = { + allowed_return_origins = [ + "https://staging-cygnus.codezero.build" + ] + prices = module.stripe_config.price_ids + } + }) + license_encryption_key = data.gitlab_project_secure_file.license_encryption_key.content +} + +data "docker_registry_image" "cygnus" { + name = "ghcr.io/code0-tech/cygnus:2226-crater-test" +} + +module "cygnus" { + source = "../../modules/docker/cygnus" + + web_urls = ["staging-cygnus.codezero.build"] + docker_additional_network_id = docker_network.codezero_staging.name + docker_name_prefix = "staging_" + bind_ip = "127.0.0.1" + http_port = 15243 + cygnus_image = { + name = data.docker_registry_image.cygnus.name + sha256_digest = data.docker_registry_image.cygnus.sha256_digest + } + additional_envs = [ + "CRATER_GRAPHQL_URL=http://${module.crater.container_hostname}:3000/graphql", + "PAYLOAD_SKIP_EMAIL_VERIFY=true", + ] +} diff --git a/system/staging/variables.tf b/system/staging/variables.tf index 574ebbd3..38d295d7 100644 --- a/system/staging/variables.tf +++ b/system/staging/variables.tf @@ -7,3 +7,8 @@ variable "server_staging_ip" { type = string sensitive = true } + +variable "stripe_staging_api_key" { + type = string + sensitive = true +} diff --git a/system/teleport/main.tf b/system/teleport/main.tf index 392fae22..6df7d155 100644 --- a/system/teleport/main.tf +++ b/system/teleport/main.tf @@ -17,6 +17,7 @@ terraform { locals { application_hostnames = [ "staging.codezero.build", + "staging-cygnus.codezero.build", ] teleport_hostnames = concat([var.hostname], local.application_hostnames) @@ -94,7 +95,8 @@ resource "cloudflare_dns_record" "teleport" { resource "cloudflare_dns_record" "teleport_cname_codezero_build" { for_each = toset([ - "staging.codezero.build" + "staging.codezero.build", + "staging-cygnus.codezero.build", ]) name = each.value