114 lines
4.5 KiB
YAML
114 lines
4.5 KiB
YAML
variant: flatcar
|
|
version: 1.0.0
|
|
|
|
# Shared by every Flatcar VM: main.tf passes this file as each VM's inline
|
|
# butane snippet. Loaded with file(), not templatefile(), so it is not a
|
|
# template -- ${...} below reaches the VM as written.
|
|
#
|
|
# Keeps /etc/versions.env in sync with versions.env at the root of
|
|
# AtlasHorizon/compose, which holds the image tag of every container on the
|
|
# Flatcar VMs. A version bump merged to the branch below reaches each VM
|
|
# within ~10 minutes with no plan or rebuild; see fetch-versions.sh for how
|
|
# the affected units are found and restarted.
|
|
#
|
|
# To opt a container unit in, in its butane file:
|
|
# [Unit] After=... versions-fetch.service
|
|
# Wants=versions-fetch.service
|
|
# [Service] EnvironmentFile=/etc/versions.env
|
|
# and use the variable, in braces, wherever the image goes (the docker pull
|
|
# and docker run arguments). In a .tftpl file the braces need a doubled
|
|
# dollar sign so templatefile() leaves them alone.
|
|
|
|
systemd:
|
|
units:
|
|
# Not enabled itself: the container units pull it in at boot (Wants= and
|
|
# After=), so they start on the freshest copy, and the timer re-runs it.
|
|
# A failed fetch keeps the last good copy, so a Gitea outage only delays
|
|
# updates -- except on a brand-new VM, whose units cannot start until the
|
|
# first fetch succeeds. Restart= keeps retrying that first fetch.
|
|
- name: versions-fetch.service
|
|
contents: |
|
|
[Unit]
|
|
Description=Fetch image versions from AtlasHorizon/compose
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
[Service]
|
|
Type=oneshot
|
|
# Override with a drop-in to test unreleased versions on one VM.
|
|
Environment=VERSIONS_BRANCH=main
|
|
ExecStart=/opt/versions/fetch-versions.sh
|
|
Restart=on-failure
|
|
RestartSec=30s
|
|
- name: versions-fetch.timer
|
|
enabled: true
|
|
contents: |
|
|
[Unit]
|
|
Description=Periodically re-fetch image versions
|
|
[Timer]
|
|
OnBootSec=10min
|
|
OnUnitActiveSec=10min
|
|
RandomizedDelaySec=1min
|
|
[Install]
|
|
WantedBy=timers.target
|
|
|
|
storage:
|
|
files:
|
|
- path: /opt/versions/fetch-versions.sh
|
|
mode: 0755
|
|
contents:
|
|
inline: |
|
|
#!/usr/bin/bash
|
|
# Fetches versions.env and restarts the units that use a variable
|
|
# whose value changed. A unit "uses" a variable when its unit file
|
|
# contains it in braces (the docker pull/run image arguments), so
|
|
# adding a container needs no change here.
|
|
set -euo pipefail
|
|
|
|
url="https://git.atlashorizon.net/AtlasHorizon/compose/raw/branch/$VERSIONS_BRANCH/versions.env"
|
|
dest=/etc/versions.env
|
|
|
|
tmp=$(mktemp /etc/.versions.env.XXXXXX)
|
|
trap 'rm -f "$tmp"' EXIT
|
|
|
|
curl -fsS --max-time 30 -o "$tmp" "$url"
|
|
|
|
# Only comments, blank lines and VAR=value lines are accepted, and
|
|
# at least one *_IMAGE line must be present, so an error page or a
|
|
# truncated download never replaces the last good copy.
|
|
if grep -qvE '^(#.*|[A-Z0-9_]+=[^[:space:]]+|)$' "$tmp" ||
|
|
! grep -qE '^[A-Z0-9_]+_IMAGE=' "$tmp"; then
|
|
echo "refusing malformed versions.env from $url" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ -f "$dest" ] && cmp -s "$tmp" "$dest"; then
|
|
exit 0
|
|
fi
|
|
|
|
# With no previous copy this is a brand-new VM: its units are waiting
|
|
# on (or already failed without) this file, so start them outright.
|
|
# Otherwise only units that are running get restarted, so a unit
|
|
# stopped by hand stays stopped.
|
|
if [ -f "$dest" ]; then
|
|
action=try-restart
|
|
changed=$(diff <(grep -E '^[A-Z0-9_]+=' "$dest" | sort) \
|
|
<(grep -E '^[A-Z0-9_]+=' "$tmp" | sort) |
|
|
sed -nE 's/^[<>] ([A-Z0-9_]+)=.*/\1/p' | sort -u || true)
|
|
else
|
|
action=restart
|
|
changed=$(sed -nE 's/^([A-Z0-9_]+)=.*/\1/p' "$tmp")
|
|
fi
|
|
|
|
chmod 0644 "$tmp"
|
|
mv -f "$tmp" "$dest"
|
|
|
|
for var in $changed; do
|
|
echo "$var changed"
|
|
pattern=$(printf '${%s}' "$var")
|
|
# --no-block: the units are ordered after this one, so waiting on
|
|
# their jobs from inside it would deadlock at boot.
|
|
for unit in $(grep -lF "$pattern" /etc/systemd/system/*.service || true); do
|
|
systemctl --no-block "$action" "$(basename "$unit")"
|
|
done
|
|
done
|