rsyslog/ansible/playbooks/drift-check.yml
dvirlabs 1cbe3d4de7
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
fix: Use checksum-based content comparison to avoid permission-based drift false positives
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.
2026-04-23 13:21:20 +03:00

213 lines
9.7 KiB
YAML

---
- name: Check rsyslog configuration drift
hosts: rsyslog_servers
gather_facts: false
# NOTE: This playbook compares file CONTENT ONLY using md5 checksums.
# It ignores permissions/ownership differences.
# Permissions are enforced during deploy (apply.yml)
tasks:
# -------------------------------------------------------------------------
# Checksum-based content comparison (ignores permissions/ownership)
# This is the only reliable way to detect actual config changes
# after deployment when permissions have been set.
# -------------------------------------------------------------------------
- name: Get MD5 checksum of Git version of rsyslog.conf
stat:
path: "{{ playbook_dir }}/../../files/rsyslog.conf"
delegate_to: localhost
register: git_rsyslog_conf_stat
- name: Get MD5 checksum of server version of rsyslog.conf
stat:
path: "{{ rsyslog_main_config }}"
register: server_rsyslog_conf_stat
- name: Compare rsyslog.conf content
set_fact:
main_config_check:
changed: "{{ git_rsyslog_conf_stat.stat.checksum != server_rsyslog_conf_stat.stat.checksum }}"
checksum_git: "{{ git_rsyslog_conf_stat.stat.checksum }}"
checksum_server: "{{ server_rsyslog_conf_stat.stat.checksum }}"
- name: Get checksums for rsyslog.d directory files
block:
- name: Find Git rsyslog.d files
find:
paths: "{{ playbook_dir }}/../../files/rsyslog.d"
patterns: "*.conf"
recurse: false
delegate_to: localhost
register: git_confd_files
- name: Find server rsyslog.d files
find:
paths: "{{ rsyslog_config_dir }}"
patterns: "*.conf"
recurse: false
register: server_confd_files
- name: Get checksums for all Git rsyslog.d files
stat:
path: "{{ item.path }}"
delegate_to: localhost
loop: "{{ git_confd_files.files }}"
register: git_confd_checksums
- name: Get checksums for all server rsyslog.d files
stat:
path: "{{ item.path }}"
loop: "{{ server_confd_files.files }}"
register: server_confd_checksums
- name: Compare rsyslog.d file checksums
set_fact:
rsyslogd_check:
changed: "{{ git_confd_checksums.results | map(attribute='stat.checksum') | list != server_confd_checksums.results | map(attribute='stat.checksum') | list }}"
- name: Check for extra files on server not present in Git
block:
- name: Find config files on server
ansible.builtin.find:
paths: "{{ rsyslog_config_dir }}"
patterns: "*.conf"
recurse: false
register: server_configs
- name: Find config files in Git (controller)
ansible.builtin.find:
paths: "{{ playbook_dir }}/../../files/rsyslog.d"
patterns: "*.conf"
recurse: false
delegate_to: localhost
register: repo_configs
- name: Build list of Git-managed filenames
ansible.builtin.set_fact:
git_filenames: "{{ repo_configs.files | map(attribute='path') | map('basename') | list }}"
- name: Build list of server filenames
ansible.builtin.set_fact:
server_filenames: "{{ server_configs.files | map(attribute='path') | map('basename') | list }}"
- name: Find server files that are managed by Git but missing on server
ansible.builtin.set_fact:
missing_on_server: "{{ git_filenames | difference(server_filenames) }}"
- name: Show missing files
ansible.builtin.debug:
msg: "Files in Git but missing on server: {{ missing_on_server }}"
when: missing_on_server | length > 0
# Initialize missing_on_server with default empty list to avoid undefined variable errors
- name: Initialize missing files tracking
ansible.builtin.set_fact:
missing_on_server: []
- name: Set overall drift flag
ansible.builtin.set_fact:
# Drift detected if: main config changed OR rsyslog.d changed OR any git-managed files missing from server
# Using | default([]) to safely handle undefined variables in container environment
drift_detected: "{{ main_config_check.changed or rsyslogd_check.changed or (missing_on_server | default([]) | length > 0) }}"
# ─────────────────────────────────────────────────────────────────────────
# Debug: Show WHAT changed (for troubleshooting)
# ─────────────────────────────────────────────────────────────────────────
- name: Show main config change status
ansible.builtin.debug:
msg: "Main config (rsyslog.conf) changed: {{ main_config_check.changed }}"
- name: Show rsyslog.d change status
ansible.builtin.debug:
msg: "rsyslog.d directory changed: {{ rsyslogd_check.changed }}"
- name: Show main config diff if changed
ansible.builtin.debug:
var: main_config_check.diff
when: main_config_check.changed and main_config_check.diff is defined
- name: Show rsyslog.d diff if changed
ansible.builtin.debug:
var: rsyslogd_check.diff
when: rsyslogd_check.changed and rsyslogd_check.diff is defined
- name: Show rsyslog.d diff list
ansible.builtin.debug:
msg: "rsyslogd_check details: {{ rsyslogd_check }}"
when: rsyslogd_check.changed
- name: Debug rsyslogd_check.diff structure
ansible.builtin.debug:
msg: |
rsyslogd_check.diff is list: {{ rsyslogd_check.diff is iterable and rsyslogd_check.diff is not string }}
rsyslogd_check.diff length: {{ rsyslogd_check.diff | length if rsyslogd_check.diff is iterable else 'N/A' }}
rsyslogd_check.diff first item: {{ rsyslogd_check.diff[0] if rsyslogd_check.diff is iterable and rsyslogd_check.diff | length > 0 else 'empty' }}
Full diff content: {{ rsyslogd_check.diff }}
when: rsyslogd_check.changed and rsyslogd_check.diff is defined
# ─────────────────────────────────────────────────────────────────────────
# Build structured list of changed files for GitOps status server
# This data is parsed by the update-gitops-status.sh wrapper script
# ─────────────────────────────────────────────────────────────────────────
- name: Initialize list of drifted files
ansible.builtin.set_fact:
drifted_files: []
- name: Add main config to drifted files if changed
ansible.builtin.set_fact:
drifted_files: "{{ drifted_files + ['/etc/rsyslog.conf'] }}"
when: main_config_check.changed
- name: Mark rsyslog.d directory as changed (simplified)
ansible.builtin.set_fact:
drifted_files: "{{ drifted_files + ['/etc/rsyslog.d/'] }}"
when: rsyslogd_check.changed
# NOTE: missing_on_server files are tracked in drift_detected flag but not in drifted_files list
# This is intentional - they indicate missing deployed files, which is a drift condition
# ─────────────────────────────────────────────────────────────────────────
# Debug output: Show structured drifted files for parsing
# Format: DRIFTED_FILES=file1,file2,file3 (or empty if no drift)
# This makes it easy for update-gitops-status.sh to extract changed files
# ALWAYS output this line for reliable parsing, even when empty
# ─────────────────────────────────────────────────────────────────────────
- name: Output structured list of drifted files for GitOps status server
ansible.builtin.debug:
msg: "DRIFTED_FILES={{ drifted_files | join(',') if drifted_files | length > 0 else '' }}"
- name: Output sync status marker for parsing
ansible.builtin.debug:
msg: "SYNC_STATUS=SYNCED"
when: not drift_detected
- name: Output sync status marker for parsing
ansible.builtin.debug:
msg: "SYNC_STATUS=OUT_OF_SYNC"
when: drift_detected
- name: Print SYNCED status
ansible.builtin.debug:
msg: |
╭─────────────────────────────╮
│ ✓ SYNCED │
│ Configuration is up-to-date │
╰─────────────────────────────╯
when: not drift_detected
- name: Print OUT OF SYNC status
ansible.builtin.debug:
msg: |
╭─────────────────────────────╮
│ ✗ OUT OF SYNC │
│ Configuration has drifted │
╰─────────────────────────────╯
when: drift_detected
- name: Fail if drift detected
ansible.builtin.fail:
msg: "Configuration drift detected. Live system does not match repository."
when: drift_detected