diff --git a/commands/doctor.cmd b/commands/doctor.cmd index 7d3f9f52..132592fd 100644 --- a/commands/doctor.cmd +++ b/commands/doctor.cmd @@ -43,6 +43,22 @@ echo -e "\033[32mWarden global .env:\033[0m" cat ${WARDEN_HOME_DIR}/.env echo +echo -e "\033[32mTraefik version in use:\033[0m" +docker container inspect traefik --format '{{ .Config.Image }} ({{ .State.Status }})' 2>/dev/null \ + || echo "traefik container is not present" +echo + +echo -e "\033[32mTraefik static configuration:\033[0m" +if [[ ! -f "${WARDEN_HOME_DIR}/etc/traefik/traefik.yml" ]]; then + echo "not present; run 'warden svc up'" +elif cmp -s "${WARDEN_DIR}/config/traefik/traefik.yml" "${WARDEN_HOME_DIR}/etc/traefik/traefik.yml"; then + echo "in sync with ${WARDEN_DIR}/config/traefik/traefik.yml" +else + echo "differs from ${WARDEN_DIR}/config/traefik/traefik.yml (shipped < | > in use):" + diff "${WARDEN_DIR}/config/traefik/traefik.yml" "${WARDEN_HOME_DIR}/etc/traefik/traefik.yml" +fi +echo + echo -e "\033[32mWarden service override via Docker compose file:\033[0m" if [[ -f ${WARDEN_HOME_DIR}/docker-compose.yml ]]; then echo -e "\033[33mWarden services have additional service configuration added or overridden via ${WARDEN_HOME_DIR}/docker-compose.yml file.\033[0m" diff --git a/commands/env.cmd b/commands/env.cmd index 9083a02f..1dd924c7 100644 --- a/commands/env.cmd +++ b/commands/env.cmd @@ -10,6 +10,12 @@ if [[ "${WARDEN_PARAMS[0]}" == "up" ]]; then assertSvcRunning fi +## keep Traefik's static configuration current on environment lifecycle commands, since +## these are reached far more often than 'warden svc up' after an upgrade +if containsElement "${WARDEN_PARAMS[0]}" up start restart; then + assertTraefikStaticConfig +fi + HOST_UID=$(id -u) HOST_GID=$(id -g) diff --git a/commands/svc.cmd b/commands/svc.cmd index 29af3e82..c1aaaf26 100644 --- a/commands/svc.cmd +++ b/commands/svc.cmd @@ -67,6 +67,11 @@ if [[ -f "${WARDEN_HOME_DIR}/docker-compose.yml" ]]; then DOCKER_COMPOSE_ARGS+=("${WARDEN_HOME_DIR}/docker-compose.yml") fi +## keep Traefik's static configuration current whenever global services are (re)started +if containsElement "${WARDEN_PARAMS[0]}" up start restart; then + assertTraefikStaticConfig +fi + ## special handling when 'svc up' is run if [[ "${WARDEN_PARAMS[0]}" == "up" ]]; then @@ -84,11 +89,6 @@ if [[ "${WARDEN_PARAMS[0]}" == "up" ]]; then mkdir -p "${WARDEN_HOME_DIR}/etc/traefik" fi - ## copy configuration files into location where they'll be mounted into containers from - if [[ ! -f "${WARDEN_HOME_DIR}/etc/traefik/traefik.yml" ]]; then - cp "${WARDEN_DIR}/config/traefik/traefik.yml" "${WARDEN_HOME_DIR}/etc/traefik/traefik.yml" - fi - ## generate dynamic traefik ssl termination configuration cat > "${WARDEN_HOME_DIR}/etc/traefik/dynamic.yml" <<-EOT tls: diff --git a/utils/svc.sh b/utils/svc.sh index 17305ece..7c6a9fb9 100644 --- a/utils/svc.sh +++ b/utils/svc.sh @@ -1,6 +1,64 @@ #!/usr/bin/env bash [[ ! ${WARDEN_DIR} ]] && >&2 echo -e "\033[31mThis script is not intended to be run directly!\033[0m" && exit 1 +## Warden owns ${WARDEN_HOME_DIR}/etc/traefik/traefik.yml; it is generated configuration, +## not user state. After a Traefik major upgrade some settings, such as the router rule +## syntax, have to be adjusted globally for backward compatibility, and seeding the file +## once meant existing installs never received them. +## +## Override the service's volumes via ${WARDEN_HOME_DIR}/docker-compose.yml if you need +## Traefik to run against a hand-written static config. +## +## Returns 0 when the file was replaced, otherwise 1. +function refreshTraefikStaticConfig() { + local source="${WARDEN_DIR}/config/traefik/traefik.yml" + local target="${WARDEN_HOME_DIR}/etc/traefik/traefik.yml" + + if cmp -s "${source}" "${target}"; then + return 1 + fi + + mkdir -p "$(dirname "${target}")" + + if [[ -f "${target}" ]]; then + ## timestamped so a later refresh cannot clobber an earlier backup + local backup + backup="${target}.$(date +%Y%m%d-%H%M%S).bak" + cp "${target}" "${backup}" + echo "==> Refreshing ${target} from ${source}" + echo " Previous version saved as ${backup}" + fi + + cp "${source}" "${target}" + + return 0 +} + +## Resolve the container id of Warden's own running Traefik. Matching on the Compose +## project labels keeps this scoped to the services 'warden svc' orchestrates, so a +## Traefik belonging to something else on the same daemon is never restarted. +function wardenTraefikContainerId() { + docker container ls -q \ + --filter label=com.docker.compose.project=warden \ + --filter label=com.docker.compose.service=traefik \ + --filter status=running 2>/dev/null || true +} + +## Refresh Traefik's static configuration, restarting Traefik when it changed. Traefik +## never reloads static configuration, and Compose will not recreate the container just +## because a bind mounted file changed, so the restart has to be explicit. +function assertTraefikStaticConfig() { + refreshTraefikStaticConfig || return 0 + + local traefikId + traefikId="$(wardenTraefikContainerId)" + + if [[ -n "${traefikId}" ]]; then + echo "==> Restarting traefik to apply the updated static configuration" + docker restart "${traefikId}" >/dev/null + fi +} + function assertSvcRunning() { ## test for global services running wardenNetworkName=$(cat ${WARDEN_DIR}/docker/docker-compose.yml | grep -A3 'networks:' | tail -n1 | sed -e 's/[[:blank:]]*name:[[:blank:]]*//g')