Own watcher (docker:28-cli + handler.sh): attaches traefik to every docker network labelled traefik.enable=true (daemon-side filter), 5-min periodic resync + traefik-restart healing. dawarich/teslamate back on private networks with the net label set.
129 lines
3.8 KiB
Bash
129 lines
3.8 KiB
Bash
#!/bin/sh
|
|
#
|
|
# traefik-network-watcher
|
|
# Koppelt Traefik automatisch aan elke Docker-network die het label
|
|
# "traefik.enable=true" draagt. De filtering gebeurt daemon-side
|
|
# (docker network ls --filter label=...), dus zonder label gebeurt er
|
|
# niets: geen inspect-call, geen connect.
|
|
#
|
|
# Gedrag:
|
|
# - bij opstart: eenmalige scan over alle netwerken met het label
|
|
# - live: reactie op Docker-events (network create/connect)
|
|
# - herstelt zichzelf als de Traefik-container opnieuw opgestart wordt
|
|
# - optioneel: periodieke resync (SYNC_INTERVAL, standaard 300s)
|
|
# - optioneel: detachen van netwerken die het label verloren hebben
|
|
# (DETACH_UNLABELED=true, standaard uit)
|
|
#
|
|
set -u
|
|
|
|
TRAEFIK="${TRAEFIK_CONTAINER:-traefik}"
|
|
LABEL_SELECTOR="${TRAEFIK_NETWORK_LABEL:-label=traefik.enable=true}"
|
|
EXCLUDED="${EXCLUDED_NETWORKS:-bridge host none ingress docker_gwbridge}"
|
|
SYNC_INTERVAL="${SYNC_INTERVAL:-300}"
|
|
DETACH_UNLABELED="${DETACH_UNLABELED:-false}"
|
|
|
|
log() { echo "[watcher] $*"; }
|
|
warn() { echo "[watcher] $*" >&2; }
|
|
|
|
is_excluded() {
|
|
for n in $EXCLUDED; do
|
|
[ "$1" = "$n" ] && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
is_labeled() {
|
|
# True alleen als de daemon zelf dit netwerk met het label teruggeeft.
|
|
# Dit is de single source of truth; nergens anders wordt op het label
|
|
# gecontroleerd of geparst.
|
|
[ "$(docker network ls --filter "name=^$1\$" --filter "$LABEL_SELECTOR" --format '{{.Name}}')" = "$1" ]
|
|
}
|
|
|
|
networks_of() {
|
|
docker inspect "$TRAEFIK" --format \
|
|
'{{range $name, $_ := .NetworkSettings.Networks}}{{$name}}
|
|
{{end}}' 2>/dev/null
|
|
}
|
|
|
|
is_connected() {
|
|
networks_of | grep -qxF "$1"
|
|
}
|
|
|
|
attach() {
|
|
net="$1"
|
|
is_excluded "$net" && return 0
|
|
is_connected "$net" && return 0
|
|
log "attach: $TRAEFIK -> $net"
|
|
docker network connect "$net" "$TRAEFIK" >/dev/null 2>&1 || warn "attach $net mislukt"
|
|
}
|
|
|
|
detach() {
|
|
net="$1"
|
|
is_excluded "$net" && return 0
|
|
is_connected "$net" || return 0
|
|
log "detach: $TRAEFIK -/- $net"
|
|
docker network disconnect "$net" "$TRAEFIK" >/dev/null 2>&1 || warn "detach $net mislukt"
|
|
}
|
|
|
|
sync() {
|
|
# Alles met het label verbinden (idempotent, connect-check zit in attach)
|
|
docker network ls --filter "$LABEL_SELECTOR" --format '{{.Name}}' | while read -r net; do
|
|
attach "$net"
|
|
done
|
|
|
|
# Optioneel: alles zonder label weer loskoppelen
|
|
if [ "$DETACH_UNLABELED" = "true" ]; then
|
|
networks_of | while read -r net; do
|
|
is_excluded "$net" && continue
|
|
is_labeled "$net" || detach "$net"
|
|
done
|
|
fi
|
|
}
|
|
|
|
# --- opstart ------------------------------------------------------------
|
|
|
|
docker inspect "$TRAEFIK" >/dev/null 2>&1 || {
|
|
warn "container '$TRAEFIK' niet gevonden; restart:unless-stopped probeert het opnieuw";
|
|
exit 1;
|
|
}
|
|
|
|
log "traefik='$TRAEFIK' label-selector='$LABEL_SELECTOR' sync-interval=${SYNC_INTERVAL}s"
|
|
|
|
if [ "$SYNC_INTERVAL" != "0" ]; then
|
|
(
|
|
while :; do
|
|
sleep "$SYNC_INTERVAL"
|
|
sync
|
|
done
|
|
) &
|
|
SYNC_PID=$!
|
|
trap 'kill "$SYNC_PID" 2>/dev/null; exit 0' INT TERM
|
|
fi
|
|
|
|
# 1. bestaande netwerken nalopen
|
|
log "eerste scan..."
|
|
sync
|
|
|
|
# 2. live events volgen
|
|
# - network create/connect -> label-check op het betrokken netwerk,
|
|
# connect = connect (handmatige detaches worden ook gerepareerd)
|
|
# - container start (traefik zelf) -> volledige resync, zo herstellen
|
|
# de koppelingen na een 'docker compose up -d' van de traefik-stack,
|
|
# omdat die het container-object vernieuwt en alle endpoints wist
|
|
docker events \
|
|
--filter type=network \
|
|
--filter type=container \
|
|
--format '{{.Type}} {{.Action}} {{.Actor.Attributes.name}}' |
|
|
while read -r type action name; do
|
|
case "$type:$action" in
|
|
network:create | network:connect)
|
|
is_labeled "$name" && attach "$name"
|
|
;;
|
|
container:start)
|
|
if [ "$name" = "$TRAEFIK" ]; then
|
|
log "traefik opnieuw gestart, resync"
|
|
sync
|
|
fi
|
|
;;
|
|
esac
|
|
done |