New Function: DEV VM
Some checks failed
Deploy / Update Proxmox Dev VMs / Detect changed Proxmox tfvars (push) Successful in 26s
Test / Static Analysis (push) Failing after 25s
Test / Unit Tests — Docker Stack (push) Has been skipped
Test / Unit Tests — K8s Stack (push) Has been skipped
Deploy / Update Proxmox Dev VMs / Provision ${{ matrix.tfvars }} (push) Failing after 32s
Deploy / Update Proxmox Dev VMs / Destroy ${{ matrix.tfvars }} (push) Has been skipped
Test / Integration Test — K8s (k3d) (push) Has been skipped
Some checks failed
Deploy / Update Proxmox Dev VMs / Detect changed Proxmox tfvars (push) Successful in 26s
Test / Static Analysis (push) Failing after 25s
Test / Unit Tests — Docker Stack (push) Has been skipped
Test / Unit Tests — K8s Stack (push) Has been skipped
Deploy / Update Proxmox Dev VMs / Provision ${{ matrix.tfvars }} (push) Failing after 32s
Deploy / Update Proxmox Dev VMs / Destroy ${{ matrix.tfvars }} (push) Has been skipped
Test / Integration Test — K8s (k3d) (push) Has been skipped
This commit is contained in:
300
.gitlab/workflows/deploy-proxmox.gitlab-ci.yml
Normal file
300
.gitlab/workflows/deploy-proxmox.gitlab-ci.yml
Normal file
@@ -0,0 +1,300 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Proxmox dev VM deploy pipeline — mirrors .gitea/workflows/deploy-proxmox.yml
|
||||
#
|
||||
# Triggered on pushes to main when files under proxmox/apps/*.tfvars change.
|
||||
# Two jobs run in sequence:
|
||||
# provision-proxmox — loops over added/modified tfvars, provisions/updates VMs,
|
||||
# stores credentials in gopass, runs chained app deploy
|
||||
# when apps/<devname>-qa.tfvars or k8s/apps/<devname>-qa.tfvars exists
|
||||
# destroy-proxmox — loops over deleted tfvars, destroys VMs, removes gopass entries
|
||||
#
|
||||
# Required GitLab CI/CD variables (in addition to the shared ones):
|
||||
# PROXMOX_ENDPOINT — https://proxmox.example.com:8006/
|
||||
# PROXMOX_API_TOKEN — user@pam!tokenid=xxxxxxxx-... (mark as Masked)
|
||||
# PROXMOX_TLS_INSECURE — "true" when using a self-signed cert (optional)
|
||||
#
|
||||
# State key pattern: apps-proxmox/<devname>.tfstate
|
||||
# gopass paths written by this pipeline:
|
||||
# infra/ssh-keys/<devname> — SSH private key (if auto-generated)
|
||||
# infra/kubeconfigs/<devname> — k3s kubeconfig (if vm_role = "k3s")
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
.proxmox_base:
|
||||
image: ubuntu:22.04
|
||||
variables:
|
||||
GIT_DEPTH: "0"
|
||||
before_script:
|
||||
- apt-get update -qq && apt-get install -y -qq curl git jq gnupg openssh-client
|
||||
- |
|
||||
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"
|
||||
|
||||
# ─── Provision / Update ───────────────────────────────────────────────────────
|
||||
|
||||
provision-proxmox:
|
||||
extends: .proxmox_base
|
||||
stage: deploy
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"'
|
||||
changes:
|
||||
- proxmox/apps/*.tfvars
|
||||
script:
|
||||
- |
|
||||
ADDED_MODIFIED=$(git diff --name-only --diff-filter=ACM \
|
||||
"$CI_COMMIT_BEFORE_SHA" "$CI_COMMIT_SHA" -- 'proxmox/apps/*.tfvars')
|
||||
|
||||
if [ -z "$ADDED_MODIFIED" ]; then
|
||||
echo "No Proxmox tfvars files added or modified. Nothing to provision."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Files to provision: $ADDED_MODIFIED"
|
||||
FAILED=0
|
||||
|
||||
for TFVARS in $ADDED_MODIFIED; do
|
||||
DEV=$(basename "$TFVARS" .tfvars)
|
||||
echo ""
|
||||
echo "══════════════════════════════════════════"
|
||||
echo " Provisioning VM: $DEV"
|
||||
echo "══════════════════════════════════════════"
|
||||
|
||||
if ! (
|
||||
set -e
|
||||
|
||||
export AWS_ACCESS_KEY_ID="$SEAWEED_ACCESS_KEY"
|
||||
export AWS_SECRET_ACCESS_KEY="$SEAWEED_SECRET_KEY"
|
||||
TLS_INSECURE="${PROXMOX_TLS_INSECURE:-false}"
|
||||
|
||||
# ── tofu init with SeaweedFS backend ──
|
||||
cd proxmox
|
||||
tofu init \
|
||||
-backend-config="bucket=tofu-state" \
|
||||
-backend-config="key=apps-proxmox/$DEV.tfstate" \
|
||||
-backend-config="endpoint=$SEAWEED_S3_ENDPOINT" \
|
||||
-backend-config="region=us-east-1" \
|
||||
-backend-config="force_path_style=true" \
|
||||
-reconfigure
|
||||
|
||||
tofu workspace select "$DEV" 2>/dev/null || tofu workspace new "$DEV"
|
||||
|
||||
tofu apply -auto-approve \
|
||||
-var-file="../$TFVARS" \
|
||||
-var="proxmox_endpoint=$PROXMOX_ENDPOINT" \
|
||||
-var="proxmox_api_token=$PROXMOX_API_TOKEN" \
|
||||
-var="proxmox_tls_insecure=$TLS_INSECURE"
|
||||
|
||||
# ── Read VM outputs ──
|
||||
VM_IP=$(tofu output -raw vm_ip)
|
||||
VM_ROLE=$(tofu output -raw vm_role)
|
||||
KEY_GEN=$(tofu output -raw key_was_generated)
|
||||
CI_USER=$(tofu output -raw cloud_init_user)
|
||||
echo " VM IP: $VM_IP Role: $VM_ROLE Key generated: $KEY_GEN"
|
||||
cd ..
|
||||
|
||||
# ── Store auto-generated SSH key in gopass ──
|
||||
if [ "$KEY_GEN" = "true" ]; then
|
||||
cd proxmox
|
||||
SSH_KEY=$(tofu output -raw ssh_private_key)
|
||||
echo "$SSH_KEY" | gopass insert -f "infra/ssh-keys/$DEV"
|
||||
echo " SSH key stored in gopass at infra/ssh-keys/$DEV"
|
||||
cd ..
|
||||
fi
|
||||
|
||||
# ── For k3s: wait for k3s and fetch kubeconfig ──
|
||||
if [ "$VM_ROLE" = "k3s" ]; then
|
||||
gopass show -o "infra/ssh-keys/$DEV" > /tmp/deploy_key
|
||||
chmod 600 /tmp/deploy_key
|
||||
|
||||
echo " Waiting for k3s to become active on $VM_IP..."
|
||||
for i in $(seq 1 30); do
|
||||
if ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 \
|
||||
-i /tmp/deploy_key "$CI_USER@$VM_IP" \
|
||||
"systemctl is-active --quiet k3s" 2>/dev/null; then
|
||||
echo " k3s is active."
|
||||
break
|
||||
fi
|
||||
if [ "$i" -eq 30 ]; then
|
||||
echo "ERROR: k3s did not become active within 5 minutes."
|
||||
exit 1
|
||||
fi
|
||||
echo " Attempt $i/30 — retrying in 10s..."
|
||||
sleep 10
|
||||
done
|
||||
|
||||
ssh -o StrictHostKeyChecking=no -i /tmp/deploy_key "$CI_USER@$VM_IP" \
|
||||
"cat /etc/rancher/k3s/k3s.yaml" \
|
||||
| sed "s|127.0.0.1|$VM_IP|g" \
|
||||
| gopass insert -f "infra/kubeconfigs/$DEV"
|
||||
|
||||
rm -f /tmp/deploy_key
|
||||
echo " kubeconfig stored in gopass at infra/kubeconfigs/$DEV"
|
||||
fi
|
||||
|
||||
# ── Chained Docker deploy ──
|
||||
if [ "$VM_ROLE" = "docker" ] && [ -f "apps/${DEV}-qa.tfvars" ]; then
|
||||
APP="${DEV}-qa"
|
||||
echo " Chained Docker deploy: $APP → $VM_IP"
|
||||
|
||||
gopass show -o "infra/ssh-keys/$DEV" > /tmp/deploy_key
|
||||
chmod 600 /tmp/deploy_key
|
||||
|
||||
DB_PASSWORD=$(gopass show -o "apps/$APP/db_password" 2>/dev/null || echo "")
|
||||
GIT_TOKEN=$(gopass show -o "apps/$APP/git_token" 2>/dev/null || echo "")
|
||||
|
||||
tofu init \
|
||||
-backend-config="bucket=tofu-state" \
|
||||
-backend-config="key=apps/$APP.tfstate" \
|
||||
-backend-config="endpoint=$SEAWEED_S3_ENDPOINT" \
|
||||
-backend-config="region=us-east-1" \
|
||||
-backend-config="force_path_style=true" \
|
||||
-reconfigure
|
||||
|
||||
tofu workspace select "$APP" 2>/dev/null || tofu workspace new "$APP"
|
||||
|
||||
tofu apply -auto-approve \
|
||||
-var-file="apps/${DEV}-qa.tfvars" \
|
||||
-var="ssh_host=$VM_IP" \
|
||||
-var="ssh_key_path=/tmp/deploy_key" \
|
||||
-var="db_password=$DB_PASSWORD" \
|
||||
-var="openresty_git_token=$GIT_TOKEN"
|
||||
|
||||
rm -f /tmp/deploy_key
|
||||
echo " Chained Docker deploy complete: $APP"
|
||||
fi
|
||||
|
||||
# ── Chained K8s deploy ──
|
||||
if [ "$VM_ROLE" = "k3s" ] && [ -f "k8s/apps/${DEV}-qa.tfvars" ]; then
|
||||
APP="${DEV}-qa"
|
||||
echo " Chained K8s deploy: $APP"
|
||||
|
||||
gopass show -o "infra/kubeconfigs/$DEV" > /tmp/kubeconfig
|
||||
chmod 600 /tmp/kubeconfig
|
||||
|
||||
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 "")
|
||||
|
||||
cd k8s
|
||||
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
|
||||
|
||||
tofu workspace select "$APP" 2>/dev/null || tofu workspace new "$APP"
|
||||
|
||||
tofu apply -auto-approve \
|
||||
-var-file="../k8s/apps/${DEV}-qa.tfvars" \
|
||||
-var="kubeconfig_path=/tmp/kubeconfig" \
|
||||
-var="rabbitmq_password=$RABBITMQ_PASSWORD" \
|
||||
-var="loki_auth_token=$LOKI_AUTH_TOKEN"
|
||||
|
||||
cd ..
|
||||
rm -f /tmp/kubeconfig
|
||||
echo " Chained K8s deploy complete: $APP"
|
||||
fi
|
||||
|
||||
echo " Provisioned: $DEV"
|
||||
); then
|
||||
echo "ERROR: Provisioning of $DEV failed — continuing with remaining VMs"
|
||||
FAILED=1
|
||||
rm -f /tmp/deploy_key /tmp/kubeconfig
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $FAILED -ne 0 ]; then
|
||||
echo ""
|
||||
echo "One or more VM provisioning operations failed. See logs above for details."
|
||||
exit 1
|
||||
fi
|
||||
after_script:
|
||||
- rm -f /tmp/deploy_key /tmp/kubeconfig
|
||||
|
||||
# ─── Destroy (tfvars file deleted) ───────────────────────────────────────────
|
||||
|
||||
destroy-proxmox:
|
||||
extends: .proxmox_base
|
||||
stage: destroy
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"'
|
||||
changes:
|
||||
- proxmox/apps/*.tfvars
|
||||
script:
|
||||
- |
|
||||
DELETED=$(git diff --name-only --diff-filter=D \
|
||||
"$CI_COMMIT_BEFORE_SHA" "$CI_COMMIT_SHA" -- 'proxmox/apps/*.tfvars')
|
||||
|
||||
if [ -z "$DELETED" ]; then
|
||||
echo "No Proxmox tfvars files deleted. Nothing to destroy."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Files to destroy: $DELETED"
|
||||
FAILED=0
|
||||
|
||||
for TFVARS in $DELETED; do
|
||||
DEV=$(basename "$TFVARS" .tfvars)
|
||||
echo ""
|
||||
echo "══════════════════════════════════════════"
|
||||
echo " Destroying VM: $DEV"
|
||||
echo "══════════════════════════════════════════"
|
||||
|
||||
if ! (
|
||||
set -e
|
||||
|
||||
# Recover deleted tfvars from git history
|
||||
git show "$CI_COMMIT_BEFORE_SHA:$TFVARS" > /tmp/${DEV}.tfvars
|
||||
|
||||
export AWS_ACCESS_KEY_ID="$SEAWEED_ACCESS_KEY"
|
||||
export AWS_SECRET_ACCESS_KEY="$SEAWEED_SECRET_KEY"
|
||||
TLS_INSECURE="${PROXMOX_TLS_INSECURE:-false}"
|
||||
|
||||
cd proxmox
|
||||
tofu init \
|
||||
-backend-config="bucket=tofu-state" \
|
||||
-backend-config="key=apps-proxmox/$DEV.tfstate" \
|
||||
-backend-config="endpoint=$SEAWEED_S3_ENDPOINT" \
|
||||
-backend-config="region=us-east-1" \
|
||||
-backend-config="force_path_style=true" \
|
||||
-reconfigure
|
||||
|
||||
tofu workspace select "$DEV"
|
||||
|
||||
tofu destroy -auto-approve \
|
||||
-var-file="/tmp/${DEV}.tfvars" \
|
||||
-var="proxmox_endpoint=$PROXMOX_ENDPOINT" \
|
||||
-var="proxmox_api_token=$PROXMOX_API_TOKEN" \
|
||||
-var="proxmox_tls_insecure=$TLS_INSECURE"
|
||||
|
||||
tofu workspace select default
|
||||
tofu workspace delete "$DEV"
|
||||
cd ..
|
||||
|
||||
# Remove credentials from gopass
|
||||
gopass rm -f "infra/ssh-keys/$DEV" 2>/dev/null || true
|
||||
gopass rm -f "infra/kubeconfigs/$DEV" 2>/dev/null || true
|
||||
|
||||
rm -f "/tmp/${DEV}.tfvars"
|
||||
echo " Destroyed: $DEV"
|
||||
); then
|
||||
echo "ERROR: Destroy of $DEV failed — continuing with remaining VMs"
|
||||
FAILED=1
|
||||
rm -f "/tmp/${DEV}.tfvars"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $FAILED -ne 0 ]; then
|
||||
echo ""
|
||||
echo "One or more VM destroy operations failed. See logs above for details."
|
||||
exit 1
|
||||
fi
|
||||
after_script:
|
||||
- rm -f /tmp/deploy_key /tmp/kubeconfig /tmp/*.tfvars
|
||||
Reference in New Issue
Block a user