Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
CRITICAL FIX: Problem: drift-check.yml was using 'copy' module in check_mode, which compares: - File content ✓ - Permissions (owner, group, mode) ✗ - Ownership ✗ After deploy, files have root:root 0644 permissions. Even though content matches, the copy module marked files as 'changed' because permissions were being compared. This caused false OUT_OF_SYNC reports even when configuration was actually synced. Solution: Use MD5 checksum-based comparison instead: - Compare only file CONTENT using stat checksums - Ignore permissions/ownership differences - This is what matters for config management Also fixed URLs: - Changed back from port 80 to port 5000 (API only) - Updated service name to gitops-status-api Now drift detection only triggers on actual config changes, not permission differences. After successful deploy, should correctly report SYNCED status.
205 lines
7.9 KiB
YAML
205 lines
7.9 KiB
YAML
# =============================================================================
|
||
# Single pipeline – all event types handled via step-level when conditions:
|
||
#
|
||
# pull_request → syntax-check, validate
|
||
# ONLY validates config correctness.
|
||
# Does NOT compare to the live server – a PR branch is
|
||
# expected to differ from the server (not yet deployed),
|
||
# so a drift-check here would always be OUT_OF_SYNC by
|
||
# design and is meaningless as a failure signal.
|
||
#
|
||
# push (master) → syntax-check, validate, deploy, update-gitops-status
|
||
# Deploys to the server, then verifies sync and sends
|
||
# JSON status snapshot to gitops-status-server for Grafana.
|
||
#
|
||
# cron → gitops_sync_check (read-only drift check, no deploy)
|
||
# Continuously verifies that the live server still matches
|
||
# Git even when no push has happened. Detects manual edits
|
||
# made directly on the server. Sends JSON status with
|
||
# detailed file-level drift information to gitops-status-server.
|
||
#
|
||
# NOTE: Woodpecker does not support multiple YAML documents (---) in one file.
|
||
# All pipelines must live in a single document with step-level filtering.
|
||
# =============================================================================
|
||
when:
|
||
- event: pull_request
|
||
- event: push
|
||
branch: master
|
||
- event: cron
|
||
|
||
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
|
||
when:
|
||
- event: pull_request
|
||
- event: push
|
||
branch: master
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 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
|
||
when:
|
||
- event: pull_request
|
||
- event: push
|
||
branch: master
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 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-gitops-status: Post-deploy sync check + JSON status update
|
||
# Runs on push to master only, after deploy succeeds.
|
||
# Generates structured JSON with sync status, drift count, and changed files.
|
||
# Sends JSON to gitops-status-server for Grafana visualization.
|
||
# ---------------------------------------------------------------------------
|
||
update-gitops-status:
|
||
image: alpine/ansible:latest
|
||
depends_on: [deploy]
|
||
environment:
|
||
ANSIBLE_CONFIG: ansible.cfg
|
||
SSH_PRIVATE_KEY:
|
||
from_secret: SSH_PRIVATE_KEY
|
||
GITOPS_STATUS_SERVER_URL: http://gitops-status-api.observability-stack.svc.cluster.local:5000
|
||
REPO_NAME: rsyslog
|
||
SERVER_NAME: rsyslog-lab
|
||
# Optimize Ansible for container environment
|
||
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||
ANSIBLE_FORCE_COLOR: "False"
|
||
ANSIBLE_RETRY_FILES_ENABLED: "False"
|
||
ANSIBLE_UNSAFE_WRITES: "True"
|
||
ANSIBLE_FORKS: "1"
|
||
commands:
|
||
- |
|
||
# Increase file descriptor limit for Ansible (max safe value)
|
||
ulimit -n 65536
|
||
|
||
# Install dependencies: curl for HTTP requests, jq for JSON formatting
|
||
apk add --no-cache curl jq > /dev/null 2>&1
|
||
|
||
# Setup SSH key for Ansible
|
||
mkdir -p ~/.ssh
|
||
printf '%s\n' "$${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa
|
||
chmod 600 ~/.ssh/id_rsa
|
||
|
||
echo "==> Running post-deploy GitOps status check..."
|
||
|
||
# Make script executable and run it
|
||
chmod +x update-gitops-status.sh
|
||
./update-gitops-status.sh
|
||
|
||
echo "==> JSON status update complete. Pipeline always succeeds."
|
||
when:
|
||
branch: master
|
||
event: push
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# gitops_sync_check: ArgoCD-style cron drift check – read-only, no deploy
|
||
# Detects manual changes made directly on the server between pushes.
|
||
# Generates structured JSON with sync status, drift count, and changed files.
|
||
# Sends JSON to gitops-status-server for continuous GitOps monitoring.
|
||
# Pipeline marked FAILED when drift found so it is visible in the UI.
|
||
#
|
||
# ─── Woodpecker Cron UI settings ──────────────────────────────────────────
|
||
# Name: gitops_sync_check
|
||
# Branch: master
|
||
# Schedule: */2 * * * *
|
||
# ---------------------------------------------------------------------------
|
||
gitops_sync_check:
|
||
image: alpine/ansible:latest
|
||
environment:
|
||
ANSIBLE_CONFIG: ansible.cfg
|
||
SSH_PRIVATE_KEY:
|
||
from_secret: SSH_PRIVATE_KEY
|
||
GITOPS_STATUS_SERVER_URL: http://gitops-status-api.observability-stack.svc.cluster.local:5000
|
||
REPO_NAME: rsyslog
|
||
SERVER_NAME: rsyslog-lab
|
||
# Optimize Ansible for container environment
|
||
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||
ANSIBLE_FORCE_COLOR: "False"
|
||
ANSIBLE_RETRY_FILES_ENABLED: "False"
|
||
ANSIBLE_UNSAFE_WRITES: "True"
|
||
ANSIBLE_FORKS: "1"
|
||
commands:
|
||
- |
|
||
# Increase file descriptor limit for Ansible (max safe value)
|
||
ulimit -n 65536
|
||
|
||
# Install dependencies: curl for HTTP requests, jq for JSON formatting
|
||
apk add --no-cache curl jq bash > /dev/null 2>&1
|
||
|
||
# Setup SSH key for Ansible
|
||
mkdir -p ~/.ssh
|
||
printf '%s\n' "$${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa
|
||
chmod 600 ~/.ssh/id_rsa
|
||
|
||
echo "==> [cron] Running continuous GitOps drift check..."
|
||
|
||
# Make script executable and run it
|
||
chmod +x update-gitops-status.sh
|
||
|
||
# Capture exit code to determine if drift was detected
|
||
set +e
|
||
./update-gitops-status.sh
|
||
SCRIPT_RC=$?
|
||
set -e
|
||
|
||
if [ "$SCRIPT_RC" -ne 0 ]; then
|
||
echo "==> ERROR: Status update failed"
|
||
exit 1
|
||
fi
|
||
|
||
# Check sync status to determine pipeline result
|
||
# Read the generated JSON or re-run drift check
|
||
echo "==> Verifying drift status for pipeline result..."
|
||
set +e
|
||
ANSIBLE_FORCE_COLOR=false \
|
||
ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/drift-check.yml > /dev/null 2>&1
|
||
DRIFT_RC=$?
|
||
set -e
|
||
|
||
if [ "$DRIFT_RC" -eq 0 ]; then
|
||
echo "==> Pipeline SUCCESS: Server is SYNCED with Git"
|
||
exit 0
|
||
else
|
||
echo "==> Pipeline FAILED: OUT OF SYNC - manual drift detected on server"
|
||
echo " Check gitops-status-server for detailed file-level drift information"
|
||
exit 1
|
||
fi
|
||
when:
|
||
event: cron
|