# ───────────────────────────────────────────────────────────────────────────── # K8s stack deploy pipeline — mirrors .gitea/workflows/deploy-k8s.yml # # Triggered on pushes to main when files under k8s/apps/*.tfvars change. # Two jobs run in sequence: # deploy-k8s — loops over added/modified tfvars and applies each app # destroy-k8s — loops over deleted tfvars, recovers content from git # history, destroys each app and removes its workspace # # gopass paths used: # infra/kubeconfigs/ — kubeconfig for the target cluster # apps//rabbitmq_password — RabbitMQ admin password # apps//loki_token — optional Loki auth bearer token # ───────────────────────────────────────────────────────────────────────────── # ─── Shared template: OpenTofu + gopass setup ──────────────────────────────── .deploy_k8s_base: image: ubuntu:22.04 variables: GIT_DEPTH: "0" TOFU_WORKING_DIR: "k8s" before_script: - apt-get update -qq && apt-get install -y -qq curl git jq gnupg - | curl -fsSL https://get.opentofu.org/install-opentofu.sh \ | sh -s -- --install-method=standalone --opentofu-version=$TOFU_VERSION - | curl -fsSL https://github.com/gopasspw/gopass/releases/latest/download/gopass-linux-amd64.tar.gz \ | tar xz mv gopass /usr/local/bin/ - | echo "$GOPASS_GPG_KEY" | gpg --batch --import gopass clone "$GOPASS_STORE_REPO" # ─── Deploy / Update ───────────────────────────────────────────────────────── deploy-k8s: extends: .deploy_k8s_base stage: deploy rules: - if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"' changes: - k8s/apps/*.tfvars script: - | ADDED_MODIFIED=$(git diff --name-only --diff-filter=ACM \ "$CI_COMMIT_BEFORE_SHA" "$CI_COMMIT_SHA" -- 'k8s/apps/*.tfvars') if [ -z "$ADDED_MODIFIED" ]; then echo "No K8s tfvars files added or modified. Nothing to deploy." exit 0 fi echo "Files to deploy: $ADDED_MODIFIED" FAILED=0 for TFVARS in $ADDED_MODIFIED; do APP=$(basename "$TFVARS" .tfvars) echo "" echo "══════════════════════════════════════════" echo " Deploying K8s app: $APP" echo "══════════════════════════════════════════" if ! ( set -e # ── Fetch kubeconfig from gopass ── gopass show -o "infra/kubeconfigs/$APP" > /tmp/kubeconfig chmod 600 /tmp/kubeconfig # ── Fetch app secrets from gopass ── RABBITMQ_PASSWORD=$(gopass show -o "apps/$APP/rabbitmq_password" 2>/dev/null || echo "") LOKI_AUTH_TOKEN=$(gopass show -o "apps/$APP/loki_token" 2>/dev/null || echo "") # ── tofu init with SeaweedFS backend ── export AWS_ACCESS_KEY_ID="$SEAWEED_ACCESS_KEY" export AWS_SECRET_ACCESS_KEY="$SEAWEED_SECRET_KEY" cd "$TOFU_WORKING_DIR" tofu init \ -backend-config="bucket=tofu-state" \ -backend-config="key=apps-k8s/$APP.tfstate" \ -backend-config="endpoint=$SEAWEED_S3_ENDPOINT" \ -backend-config="region=us-east-1" \ -backend-config="force_path_style=true" \ -reconfigure # ── Workspace ── tofu workspace select "$APP" 2>/dev/null || tofu workspace new "$APP" # ── Apply ── tofu apply -auto-approve \ -var-file="../$TFVARS" \ -var="kubeconfig_path=/tmp/kubeconfig" \ -var="rabbitmq_password=$RABBITMQ_PASSWORD" \ -var="loki_auth_token=$LOKI_AUTH_TOKEN" rm -f /tmp/kubeconfig echo " Deployed K8s app: $APP" ); then echo "ERROR: Deployment of K8s app $APP failed — continuing with remaining apps" FAILED=1 rm -f /tmp/kubeconfig fi done if [ $FAILED -ne 0 ]; then echo "" echo "One or more K8s deployments failed. See logs above for details." exit 1 fi after_script: - rm -f /tmp/kubeconfig # ─── Destroy (tfvars file deleted) ─────────────────────────────────────────── destroy-k8s: extends: .deploy_k8s_base stage: destroy rules: - if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"' changes: - k8s/apps/*.tfvars script: - | DELETED=$(git diff --name-only --diff-filter=D \ "$CI_COMMIT_BEFORE_SHA" "$CI_COMMIT_SHA" -- 'k8s/apps/*.tfvars') if [ -z "$DELETED" ]; then echo "No K8s tfvars files deleted. Nothing to destroy." exit 0 fi echo "Files to destroy: $DELETED" FAILED=0 for TFVARS in $DELETED; do APP=$(basename "$TFVARS" .tfvars) echo "" echo "══════════════════════════════════════════" echo " Destroying K8s app: $APP" echo "══════════════════════════════════════════" if ! ( set -e # ── Recover deleted tfvars from git history ── git show "$CI_COMMIT_BEFORE_SHA:$TFVARS" > /tmp/${APP}.tfvars # ── Fetch kubeconfig from gopass ── gopass show -o "infra/kubeconfigs/$APP" > /tmp/kubeconfig chmod 600 /tmp/kubeconfig # ── Fetch app secrets from gopass ── RABBITMQ_PASSWORD=$(gopass show -o "apps/$APP/rabbitmq_password" 2>/dev/null || echo "") LOKI_AUTH_TOKEN=$(gopass show -o "apps/$APP/loki_token" 2>/dev/null || echo "") # ── tofu init ── export AWS_ACCESS_KEY_ID="$SEAWEED_ACCESS_KEY" export AWS_SECRET_ACCESS_KEY="$SEAWEED_SECRET_KEY" cd "$TOFU_WORKING_DIR" tofu init \ -backend-config="bucket=tofu-state" \ -backend-config="key=apps-k8s/$APP.tfstate" \ -backend-config="endpoint=$SEAWEED_S3_ENDPOINT" \ -backend-config="region=us-east-1" \ -backend-config="force_path_style=true" \ -reconfigure # ── Select existing workspace ── tofu workspace select "$APP" # ── Destroy ── tofu destroy -auto-approve \ -var-file="/tmp/${APP}.tfvars" \ -var="kubeconfig_path=/tmp/kubeconfig" \ -var="rabbitmq_password=$RABBITMQ_PASSWORD" \ -var="loki_auth_token=$LOKI_AUTH_TOKEN" # ── Remove workspace ── tofu workspace select default tofu workspace delete "$APP" rm -f /tmp/${APP}.tfvars /tmp/kubeconfig echo " Destroyed K8s app: $APP" ); then echo "ERROR: Destroy of K8s app $APP failed — continuing with remaining apps" FAILED=1 rm -f /tmp/${APP}.tfvars /tmp/kubeconfig fi done if [ $FAILED -ne 0 ]; then echo "" echo "One or more K8s destroy operations failed. See logs above for details." exit 1 fi after_script: - rm -f /tmp/kubeconfig /tmp/*.tfvars