# ============================================================================= # Pipeline 1: CI/CD # Triggered by: pull_request (validate + drift-check) # push to master (validate + deploy + post-deploy sync metric) # ============================================================================= when: - event: pull_request - event: push branch: master steps: # --------------------------------------------------------------------------- # syntax-check: Lint all playbooks – runs on pull_request and push # --------------------------------------------------------------------------- syntax-check: image: alpine/ansible:latest environment: ANSIBLE_CONFIG: ansible.cfg commands: - ansible-playbook -i ansible/inventory/hosts.yml --syntax-check ansible/playbooks/*.yml # --------------------------------------------------------------------------- # validate: Validate server state – runs on pull_request and push # --------------------------------------------------------------------------- validate: image: alpine/ansible:latest depends_on: [syntax-check] environment: ANSIBLE_CONFIG: ansible.cfg SSH_PRIVATE_KEY: from_secret: SSH_PRIVATE_KEY commands: - mkdir -p ~/.ssh - printf '%s\n' "$${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/validate.yml # --------------------------------------------------------------------------- # drift-check: Compare server config to Git – runs on pull_request only # --------------------------------------------------------------------------- drift-check: image: alpine/ansible:latest depends_on: [syntax-check] environment: ANSIBLE_CONFIG: ansible.cfg SSH_PRIVATE_KEY: from_secret: SSH_PRIVATE_KEY commands: - mkdir -p ~/.ssh - printf '%s\n' "$${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/drift-check.yml when: event: pull_request # --------------------------------------------------------------------------- # deploy: Apply Git configuration to server – runs on push to master only # --------------------------------------------------------------------------- deploy: image: alpine/ansible:latest depends_on: [syntax-check, validate] environment: ANSIBLE_CONFIG: ansible.cfg SSH_PRIVATE_KEY: from_secret: SSH_PRIVATE_KEY commands: - mkdir -p ~/.ssh - printf '%s\n' "$${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/apply.yml when: branch: master event: push # --------------------------------------------------------------------------- # update-sync-metric: Verify post-deploy sync and report to Prometheus # Runs drift-check after deploy; pushes gitops_sync_status metric. # STATUS=1 means SYNCED, STATUS=0 means OUT_OF_SYNC. # Runs on push to master only, after deploy succeeds. # --------------------------------------------------------------------------- update-sync-metric: image: alpine/ansible:latest depends_on: [deploy] environment: ANSIBLE_CONFIG: ansible.cfg SSH_PRIVATE_KEY: from_secret: SSH_PRIVATE_KEY PUSHGATEWAY_URL: http://pushgateway.observability-stack.svc.cluster.local:9091 commands: - | apk add --no-cache curl > /dev/null 2>&1 mkdir -p ~/.ssh printf '%s\n' "$${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa echo "==> Verifying post-deploy sync status..." set +e ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/drift-check.yml DRIFT_RC=$? set -e if [ "$DRIFT_RC" -eq 0 ]; then STATUS=1 echo "==> SYNCED (1) – server configuration matches Git" else STATUS=0 echo "==> OUT OF SYNC (0) – drift detected after deploy" fi printf 'gitops_sync_status{repo="rsyslog",server="rsyslog-lab"} %s\n' "$STATUS" | \ curl --silent --show-error --fail --data-binary @- \ "$${PUSHGATEWAY_URL}/metrics/job/gitops_rsyslog/instance/rsyslog-lab" exit $DRIFT_RC when: branch: master event: push --- # ============================================================================= # Pipeline 2: GitOps Sync Check (ArgoCD-style continuous drift detection) # Triggered by: cron – runs every 2 minutes even without a new push # # Purpose: Detect manual configuration changes made directly on the server # (outside Git). If someone edits /etc/rsyslog.conf by hand, the next cron # run will catch it and mark the system OUT_OF_SYNC in Prometheus. # # This pipeline does NOT deploy anything – it is a read-only drift check. # # ─── Woodpecker Cron Configuration (set in the Woodpecker UI) ─────────────── # Name: gitops-drift-check # Branch: master # Schedule: */2 * * * * # ───────────────────────────────────────────────────────────────────────────── # # Required secrets (Woodpecker → Repository Settings → Secrets): # SSH_PRIVATE_KEY – SSH key to reach the managed server # # Pushgateway URL is hardcoded (internal k8s service, not a secret): # http://pushgateway.observability-stack.svc.cluster.local:9091 # ============================================================================= when: event: cron steps: # --------------------------------------------------------------------------- # gitops_sync_check: Read-only drift check + Prometheus metric push # STATUS=1 → SYNCED (server matches Git) # STATUS=0 → OUT_OF_SYNC (manual drift detected on server) # Pipeline is marked FAILED when out of sync so it is visible in the UI. # --------------------------------------------------------------------------- gitops_sync_check: image: alpine/ansible:latest environment: ANSIBLE_CONFIG: ansible.cfg SSH_PRIVATE_KEY: from_secret: SSH_PRIVATE_KEY PUSHGATEWAY_URL: http://pushgateway.observability-stack.svc.cluster.local:9091 commands: - | apk add --no-cache curl > /dev/null 2>&1 # Prepare SSH key for remote server access mkdir -p ~/.ssh printf '%s\n' "$${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa echo "==> [cron] Running drift check against remote server..." set +e ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/drift-check.yml DRIFT_RC=$? set -e if [ "$DRIFT_RC" -eq 0 ]; then STATUS=1 echo "==> STATUS: SYNCED (1) – server configuration matches Git" else STATUS=0 echo "==> STATUS: OUT OF SYNC (0) – manual drift detected on server" fi echo "==> Pushing metric: gitops_sync_status{repo=\"rsyslog\",server=\"rsyslog-lab\"} $STATUS" printf 'gitops_sync_status{repo="rsyslog",server="rsyslog-lab"} %s\n' "$STATUS" | \ curl --silent --show-error --fail --data-binary @- \ "$${PUSHGATEWAY_URL}/metrics/job/gitops_rsyslog/instance/rsyslog-lab" # Exit non-zero when out of sync so Woodpecker marks the pipeline FAILED exit $DRIFT_RC