eletrotupi / tcc/ ansible/scripts/pull-live-configs.shc732afc720dd4f3c2603ff1f301efdf4cb1f8431
3.8 KB | 119 lines Raw
1
#!/usr/bin/env bash
2
#
3
# Pull the live deployment configs from the VPS and diff them against the repo
4
# Read-only on the server. Exits non-zero if anything has drifted
5
#
6
# INTERNAL tool, run locally (or via `make deploy-diff`). It is deliberately
7
# not wired into CI: it is more useful interactively than as a scheduled job
8
#
9
#   ansible/scripts/pull-live-configs.sh
10
#
11
# Connection details are read from ansible/inventory/hosts.yml (git-ignored), or
12
# from the VPS_HOST / VPS_USER / VPS_KEY environment variables
13

14
set -euo pipefail
15

16
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
17
ansible_dir="$repo_root/ansible"
18
inventory="$ansible_dir/inventory/hosts.yml"
19

20
# first value of a key in the (flat) inventory
21
field() { awk -F': *' -v k="^[[:space:]]*$1" '$0 ~ k {print $2; exit}' "$inventory"; }
22

23
HOST="${VPS_HOST:-}"
24
USER="${VPS_USER:-}"
25
KEY="${VPS_KEY:-}"
26

27
if [ -f "$inventory" ]; then
28
  [ -n "$HOST" ] || HOST="$(field 'ansible_host:')"
29
  [ -n "$USER" ] || USER="$(field 'ansible_user:')"
30
  [ -n "$KEY" ]  || KEY="$(field 'ansible_ssh_private_key_file:')"
31
fi
32

33
KEY="${KEY/#\~/$HOME}"
34
[ -n "$HOST" ] || { echo "no host: set VPS_HOST or provide ansible/inventory/hosts.yml" >&2; exit 2; }
35
[ -n "$USER" ] || { echo "no user: set VPS_USER or provide ansible/inventory/hosts.yml" >&2; exit 2; }
36
KEY="${KEY:-$HOME/.ssh/id_ed25519}"
37

38
tmp="$(mktemp -d)"
39
trap 'rm -rf "$tmp"' EXIT
40

41
# First vhost from vars/vhosts.yml, e.g. "usenexo.xyz"
42
vhost="$(awk '/^vhosts:/{f=1;next} f&&/^[[:space:]]*- /{print $2; exit}' "$ansible_dir/vars/vhosts.yml")"
43
[ -n "$vhost" ] || { echo "could not determine vhost from vars/vhosts.yml" >&2; exit 2; }
44

45
ssh_opts=(-i "$KEY" -o StrictHostKeyChecking=accept-new)
46

47
echo "Pulling live configs from ${USER}@${HOST} ..."
48

49
pull() { # <remote path> <local name>
50
  if ssh "${ssh_opts[@]}" "${USER}@${HOST}" "cat '$1'" >"$tmp/$2" 2>/dev/null; then
51
    echo "  pulled $1"
52
  else
53
    echo "  MISSING on server: $1" >&2
54
    rm -f "$tmp/$2"
55
  fi
56
}
57

58
pull "/etc/nginx/http.d/${vhost}.conf"    nginx.conf
59
pull /srv/app/production/docker-compose.yml docker-compose.yml
60
pull /srv/app/production/valkey.conf        valkey.conf
61

62
# The nginx config is templated; render it the same way the nginx role would
63
render_nginx() {
64
  sed \
65
    -e "s/{{ vhosts\[0\] }}/${vhost}/g" \
66
    -e 's/{{ nginx_cert_dir }}/\/etc\/ssl\/uacme/g' \
67
    -e 's/{{ nginx_static_root }}/\/srv\/static/g' \
68
    -e 's/{{ nginx_proxy_pass }}/http:\/\/127.0.0.1:3000/g' \
69
    -e 's/{{ nginx_logs_proxy_pass }}/http:\/\/127.0.0.1:9428/g' \
70
    -e 's/{{ nginx_logs_htpasswd_file }}/\/etc\/nginx\/.htpasswd-logs/g' \
71
    "$ansible_dir/roles/nginx/templates/site.https.j2"
72
}
73

74
status=0
75

76
report() { # <diff exit status> <label>
77
  if [ "$1" -eq 0 ]; then
78
    echo "ok   $2"
79
  else
80
    echo "DRIFT $2"; status=1
81
  fi
82
}
83

84
show_diff() { # <expected file> <live file>
85
  diff -u "$1" "$2" | sed 's/^/     /' || true
86
}
87

88
echo
89
if [ -f "$tmp/nginx.conf" ]; then
90
  render_nginx >"$tmp/nginx.expected"
91
  if diff -q "$tmp/nginx.expected" "$tmp/nginx.conf" >/dev/null; then
92
    report 0 "nginx server block"
93
  else
94
    report 1 "nginx server block"; show_diff "$tmp/nginx.expected" "$tmp/nginx.conf"
95
  fi
96
else
97
  echo "??   nginx server block — no live copy"
98
fi
99

100
for pair in \
101
  "ansible/roles/app/files/docker-compose.production.yml:docker-compose.yml:production compose" \
102
  "ansible/roles/app/files/valkey.conf:valkey.conf:valkey config"; do
103
  IFS=: read -r repo_rel live name <<<"$pair"
104
  if [ ! -f "$tmp/$live" ]; then
105
    echo "??   $name — no live copy"
106
  elif diff -q "$repo_root/$repo_rel" "$tmp/$live" >/dev/null; then
107
    report 0 "$name"
108
  else
109
    report 1 "$name"; show_diff "$repo_root/$repo_rel" "$tmp/$live"
110
  fi
111
done
112

113
echo
114
if [ "$status" -eq 0 ]; then
115
  echo "No drift."
116
else
117
  echo "Drift detected: re-run the matching playbook (deploy.yml / app.yml)"
118
fi
119
exit "$status"