repositories / bugabinga.net
bugabinga.net
personal infrastructure for bugabinga!
owned by admin
scripts/remote/setup-garbage-collection.sh
Raw#!/usr/bin/env bash
set -euo pipefail
[[ "$#" -eq 3 ]] || {
echo "usage: setup-garbage-collection.sh SERVICE_USER CI_USER TOAD_USER" >&2
exit 2
}
service_user="$1"
ci_user="$2"
toad_user="$3"
for user in "${service_user}" "${ci_user}" "${toad_user}"; do
[[ "${user}" =~ ^[a-z_][a-z0-9_-]*$ ]] || {
echo "unsafe user name: ${user}" >&2
exit 2
}
id -u "${user}" >/dev/null
loginctl enable-linger "${user}"
systemctl start "user@$(id -u "${user}").service"
done
service_home="$(getent passwd "${service_user}" | cut -d: -f6)"
[[ "${service_home}" =~ ^/[A-Za-z0-9._/-]+$ ]] || {
echo "unsafe user home" >&2
exit 2
}
temporary="$(mktemp -d)"
trap 'rm -rf -- "${temporary}"' EXIT
# Per-owner rootless Podman garbage collection. Image prune deliberately omits
# --all: only dangling images are removed, so tagged images such as the Luci
# rollback image are always retained. Failures reach bugabinga-alert@.
cat >"${temporary}/bugabinga-gc@.service" <<'EOF'
[Unit]
Description=Weekly rootless Podman garbage collection for %i
OnFailure=bugabinga-alert@%n.service
[Service]
Type=oneshot
TimeoutStartSec=30min
ExecStart=/usr/sbin/runuser -u %i -- /usr/bin/bash -c 'export XDG_RUNTIME_DIR="/run/user/$(id -u)" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"; exec /usr/bin/podman container prune --force --filter until=168h'
ExecStart=/usr/sbin/runuser -u %i -- /usr/bin/bash -c 'export XDG_RUNTIME_DIR="/run/user/$(id -u)" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"; exec /usr/bin/podman image prune --force --filter until=168h'
EOF
cat >"${temporary}/bugabinga-gc@.timer" <<'EOF'
[Unit]
Description=Weekly garbage collection timer for %i
[Timer]
OnCalendar=weekly
RandomizedDelaySec=6h
Persistent=true
Unit=bugabinga-gc@%i.service
[Install]
WantedBy=timers.target
EOF
cat >"${temporary}/bugabinga-gc-host.service" <<'EOF'
[Unit]
Description=Weekly host-level log retention cleanup
OnFailure=bugabinga-alert@%n.service
[Service]
Type=oneshot
TimeoutStartSec=30min
ExecStart=/usr/bin/find /data/ci/logs -type f -mtime +90 -delete
ExecStart=/usr/bin/journalctl --vacuum-size=2G --vacuum-time=30d
EOF
cat >"${temporary}/bugabinga-gc-host.timer" <<'EOF'
[Unit]
Description=Weekly host-level cleanup timer
[Timer]
OnCalendar=weekly
RandomizedDelaySec=6h
Persistent=true
Unit=bugabinga-gc-host.service
[Install]
WantedBy=timers.target
EOF
cat >"${temporary}/bugabinga-alert@.service" <<'EOF'
[Unit]
Description=Record failure alert for %i
[Service]
Type=oneshot
ExecStart=/usr/bin/logger -p user.crit -t bugabinga-alert "unit %i failed"
ExecStart=-/usr/bin/sh -c 'if command -v mailx >/dev/null 2>&1; then systemctl status %i --no-pager -n 20 | mailx -s "bugabinga: %i failed" root; fi'
EOF
cat >"${temporary}/bugabinga-maintenance.service" <<'EOF'
[Unit]
Description=Weekly maintenance freshness checks
OnFailure=bugabinga-alert@%n.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/bugabinga-check-maintenance
EOF
cat >"${temporary}/bugabinga-maintenance.timer" <<'EOF'
[Unit]
Description=Weekly maintenance check timer
[Timer]
OnCalendar=weekly
RandomizedDelaySec=3h
Persistent=true
Unit=bugabinga-maintenance.service
[Install]
WantedBy=timers.target
EOF
cat >"${temporary}/journald-retention.conf" <<'EOF'
[Journal]
SystemMaxUse=2G
MaxRetentionSec=30day
EOF
cat >"${temporary}/rsyslog-logrotate" <<'EOF'
/var/log/cron
/var/log/maillog
/var/log/messages
/var/log/secure
/var/log/spooler
{
daily
rotate 7
maxsize 100M
compress
missingok
sharedscripts
postrotate
/usr/bin/systemctl reload rsyslog.service >/dev/null 2>&1 || true
endscript
}
EOF
# The vendor podman-auto-update.service runs "podman image prune -f" after the
# update, which fails whenever any image is still in use and masks the update
# result. An empty ExecStartPost assignment clears that vendor command so the
# update outcome stands alone.
dropin_dir="${service_home}/.config/systemd/user/podman-auto-update.service.d"
mkdir -p "${dropin_dir}"
cat >"${temporary}/no-image-prune.conf" <<'EOF'
[Service]
ExecStartPost=
EOF
install -m 0644 "${temporary}/bugabinga-gc@.service" /etc/systemd/system/bugabinga-gc@.service
install -m 0644 "${temporary}/bugabinga-gc@.timer" /etc/systemd/system/bugabinga-gc@.timer
install -m 0644 "${temporary}/bugabinga-gc-host.service" /etc/systemd/system/bugabinga-gc-host.service
install -m 0644 "${temporary}/bugabinga-gc-host.timer" /etc/systemd/system/bugabinga-gc-host.timer
install -m 0644 "${temporary}/bugabinga-alert@.service" /etc/systemd/system/bugabinga-alert@.service
install -m 0644 "${temporary}/bugabinga-maintenance.service" /etc/systemd/system/bugabinga-maintenance.service
install -m 0644 "${temporary}/bugabinga-maintenance.timer" /etc/systemd/system/bugabinga-maintenance.timer
install -d -m 0755 /etc/systemd/journald.conf.d
install -m 0644 "${temporary}/journald-retention.conf" /etc/systemd/journald.conf.d/bugabinga-retention.conf
install -m 0644 "${temporary}/rsyslog-logrotate" /etc/logrotate.d/rsyslog
install -d -o "${service_user}" -g "${service_user}" -m 0755 "${dropin_dir}"
install -o "${service_user}" -g "${service_user}" -m 0644 "${temporary}/no-image-prune.conf" "${dropin_dir}/no-image-prune.conf"
systemctl disable --now bugabinga-gc.timer >/dev/null 2>&1 || true
rm -f /etc/systemd/system/bugabinga-gc.service /etc/systemd/system/bugabinga-gc.timer
systemctl daemon-reload
systemctl restart systemd-journald.service
systemctl enable --now bugabinga-gc@"${service_user}".timer bugabinga-gc@"${ci_user}".timer bugabinga-gc@"${toad_user}".timer
systemctl enable --now bugabinga-gc-host.timer bugabinga-maintenance.timer
# Seed a first run so freshness checks have a baseline immediately.
systemctl start bugabinga-gc@"${service_user}".service bugabinga-gc@"${ci_user}".service bugabinga-gc@"${toad_user}".service bugabinga-gc-host.service
systemctl start bugabinga-maintenance.service || true
service_runtime="/run/user/$(id -u "${service_user}")"
runuser -u "${service_user}" -- env HOME="${service_home}" USER="${service_user}" LOGNAME="${service_user}" XDG_RUNTIME_DIR="${service_runtime}" DBUS_SESSION_BUS_ADDRESS="unix:path=${service_runtime}/bus" systemctl --user daemon-reload