Fix plugin updates.
This commit is contained in:
@@ -210,7 +210,7 @@ fn-ga-apply-list-change() {
|
||||
return 0
|
||||
fi
|
||||
if ! fn-ga-service-reads-app-lists; then
|
||||
dokku_log_info1 "recreating the auth service so it can read per-app lists"
|
||||
dokku_log_info1 "recreating the auth service so it can read per-app lists (this rebuilds the image if the plugin was updated)"
|
||||
fn-ga-service-start
|
||||
return 0
|
||||
fi
|
||||
@@ -275,7 +275,7 @@ fn-ga-app-set-enabled() {
|
||||
local dir
|
||||
dir="$(fn-ga-app-dir "$APP")"
|
||||
if [[ "$ENABLED" == "true" ]]; then
|
||||
mkdir -p "$dir"
|
||||
dir="$(fn-ga-app-dir-ensure "$APP")"
|
||||
touch "$dir/enabled"
|
||||
else
|
||||
rm -f "$dir/enabled"
|
||||
@@ -326,9 +326,8 @@ fn-ga-validate-pattern() {
|
||||
fn-ga-exclude-add() {
|
||||
declare APP="$1" PATTERN="$2"
|
||||
local dir file
|
||||
dir="$(fn-ga-app-dir "$APP")"
|
||||
dir="$(fn-ga-app-dir-ensure "$APP")"
|
||||
file="$dir/excludes"
|
||||
mkdir -p "$dir"
|
||||
touch "$file"
|
||||
grep -qxF "$PATTERN" "$file" || printf '%s\n' "$PATTERN" >>"$file"
|
||||
}
|
||||
@@ -605,29 +604,103 @@ fn-ga-image-exists() {
|
||||
docker image inspect "$GOOGLE_AUTH_IMAGE" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Fingerprint of everything that ends up in the service image. The image is
|
||||
# labelled with it so an upgraded plugin rebuilds instead of reusing a binary
|
||||
# built from older source. Without this the shell half of the plugin upgrades
|
||||
# the moment the files change — new commands, new nginx config, new env var —
|
||||
# while the binary enforcing them stays whatever was built first, and the
|
||||
# mismatch is invisible from the outside.
|
||||
fn-ga-source-hash() {
|
||||
local dir="$GOOGLE_AUTH_PLUGIN_DIR" file
|
||||
{
|
||||
while IFS= read -r file; do
|
||||
printf '%s ' "${file#"$dir/"}"
|
||||
sha256sum "$file" | awk '{print $1}'
|
||||
done < <(
|
||||
{
|
||||
printf '%s\n' "$dir/Dockerfile" "$dir/go.mod"
|
||||
find "$dir/cmd" "$dir/internal" -type f 2>/dev/null
|
||||
} | LC_ALL=C sort
|
||||
)
|
||||
} | sha256sum | awk '{print $1}'
|
||||
}
|
||||
|
||||
# True when the built image matches the plugin's current source.
|
||||
fn-ga-image-current() {
|
||||
fn-ga-image-exists || return 1
|
||||
local labelled
|
||||
labelled="$(docker image inspect -f '{{index .Config.Labels "google-auth.source-hash"}}' "$GOOGLE_AUTH_IMAGE" 2>/dev/null)"
|
||||
# Images built before this label existed report an empty value or "<no value>",
|
||||
# and are stale by definition.
|
||||
[[ -n "$labelled" && "$labelled" != "<no value>" ]] || return 1
|
||||
[[ "$labelled" == "$(fn-ga-source-hash)" ]]
|
||||
}
|
||||
|
||||
fn-ga-build-image() {
|
||||
command -v docker >/dev/null 2>&1 || dokku_log_fail "docker is required to build the google-auth service image"
|
||||
dokku_log_info1 "Building $GOOGLE_AUTH_IMAGE (first build downloads the Go toolchain image; this can take a few minutes)"
|
||||
docker image build -t "$GOOGLE_AUTH_IMAGE" "$GOOGLE_AUTH_PLUGIN_DIR"
|
||||
docker image build \
|
||||
--label "google-auth.source-hash=$(fn-ga-source-hash)" \
|
||||
-t "$GOOGLE_AUTH_IMAGE" "$GOOGLE_AUTH_PLUGIN_DIR"
|
||||
}
|
||||
|
||||
fn-ga-service-running() {
|
||||
[[ "$(docker container inspect -f '{{.State.Running}}' "$GOOGLE_AUTH_SERVICE_NAME" 2>/dev/null)" == "true" ]]
|
||||
}
|
||||
|
||||
# True when the running container can actually read per-app lists. That takes
|
||||
# both halves: the bind mount, and the environment variable pointing at it.
|
||||
# Either one alone makes the service ignore every per-app list and fall back to
|
||||
# the global one — silently, and in the permissive direction — so both are
|
||||
# checked. A container started by an older version of this plugin has neither;
|
||||
# one recreated from a stale service.env has the mount without the variable.
|
||||
# Asks the running service which per-app config directory it is using, and
|
||||
# prints it. This is the only check that sees the binary rather than the
|
||||
# container around it; a build from before per-app lists existed answers with a
|
||||
# bare "ok" and names no directory. Fails if the service is unreachable or too
|
||||
# old to answer.
|
||||
fn-ga-service-app-config-dir() {
|
||||
command -v curl >/dev/null 2>&1 || return 1
|
||||
local port body dir
|
||||
port="$(fn-ga-global-get port "$GOOGLE_AUTH_DEFAULT_PORT")"
|
||||
body="$(curl -fsS --max-time 3 "http://127.0.0.1:${port}${GOOGLE_AUTH_ROUTE_PREFIX}/healthz" 2>/dev/null)" || return 1
|
||||
dir="$(sed -n 's/.*"app_config_dir":"\([^"]*\)".*/\1/p' <<<"$body")"
|
||||
[[ -n "$dir" ]] || return 1
|
||||
printf '%s' "$dir"
|
||||
}
|
||||
|
||||
# True when the running service can actually read per-app lists. That takes
|
||||
# three things, and missing any of them makes the service ignore every per-app
|
||||
# list and fall back to the global one — silently, and in the permissive
|
||||
# direction — so all three are checked:
|
||||
#
|
||||
# the bind mount — a container from an older plugin has none;
|
||||
# the env var naming it — one recreated from a stale service.env has the
|
||||
# mount without the variable;
|
||||
# a binary that reads both — one recreated from a stale *image* has the
|
||||
# mount and the variable and ignores them, which
|
||||
# no amount of docker metadata can reveal. Only
|
||||
# the service's own answer distinguishes it.
|
||||
fn-ga-service-reads-app-lists() {
|
||||
local inspected
|
||||
inspected="$(docker container inspect \
|
||||
-f '{{range .Mounts}}mount={{println .Destination}}{{end}}{{range .Config.Env}}env={{println .}}{{end}}' \
|
||||
"$GOOGLE_AUTH_SERVICE_NAME" 2>/dev/null)" || return 1
|
||||
grep -qxF "mount=$GOOGLE_AUTH_APP_CONFIG_MOUNT" <<<"$inspected" || return 1
|
||||
grep -qxF "env=GOOGLE_AUTH_APP_CONFIG_DIR=$GOOGLE_AUTH_APP_CONFIG_MOUNT" <<<"$inspected"
|
||||
grep -qxF "env=GOOGLE_AUTH_APP_CONFIG_DIR=$GOOGLE_AUTH_APP_CONFIG_MOUNT" <<<"$inspected" || return 1
|
||||
|
||||
local reported
|
||||
if reported="$(fn-ga-service-app-config-dir)"; then
|
||||
[[ "$reported" == "$GOOGLE_AUTH_APP_CONFIG_MOUNT" ]]
|
||||
return $?
|
||||
fi
|
||||
# No answer (curl missing, or the service is not reachable on the loopback
|
||||
# port): fall back to checking that the image was built from this plugin's
|
||||
# current source and that the container is running that image.
|
||||
fn-ga-image-current && fn-ga-container-runs-current-image
|
||||
}
|
||||
|
||||
# True when the running container was created from the current service image,
|
||||
# rather than from an earlier build still tagged over.
|
||||
fn-ga-container-runs-current-image() {
|
||||
local running current
|
||||
running="$(docker container inspect -f '{{.Image}}' "$GOOGLE_AUTH_SERVICE_NAME" 2>/dev/null)" || return 1
|
||||
current="$(docker image inspect -f '{{.Id}}' "$GOOGLE_AUTH_IMAGE" 2>/dev/null)" || return 1
|
||||
[[ -n "$running" && "$running" == "$current" ]]
|
||||
}
|
||||
|
||||
fn-ga-write-env-file() {
|
||||
@@ -637,8 +710,13 @@ fn-ga-write-env-file() {
|
||||
emails="$(fn-ga-global-get-list allowed-emails | paste -sd, -)"
|
||||
denied="$(fn-ga-global-get-list denied-emails | paste -sd, -)"
|
||||
mkdir -p "$GOOGLE_AUTH_DATA_ROOT"
|
||||
umask 077
|
||||
cat >"$envfile" <<EOF
|
||||
# The umask stays inside this subshell. Leaking it left every directory
|
||||
# created later in the same command at 0700 — including an app's config
|
||||
# directory during google-auth:enable, which the service then could not
|
||||
# traverse to read that app's lists.
|
||||
(
|
||||
umask 077
|
||||
cat >"$envfile" <<EOF
|
||||
GOOGLE_AUTH_CLIENT_ID=$(fn-ga-global-get client-id)
|
||||
GOOGLE_AUTH_CLIENT_SECRET=$(fn-ga-global-get client-secret)
|
||||
GOOGLE_AUTH_COOKIE_SECRET=$(fn-ga-global-get cookie-secret)
|
||||
@@ -652,11 +730,16 @@ GOOGLE_AUTH_SESSION_TTL=$(fn-ga-global-get session-ttl 24h)
|
||||
GOOGLE_AUTH_ALLOW_INSECURE=$(fn-ga-global-get allow-insecure false)
|
||||
GOOGLE_AUTH_LISTEN=:2999
|
||||
EOF
|
||||
)
|
||||
}
|
||||
|
||||
fn-ga-service-start() {
|
||||
fn-ga-configured || dokku_log_fail "google-auth is not fully configured; run: dokku google-auth:configure"
|
||||
fn-ga-image-exists || fn-ga-build-image
|
||||
# Rebuild whenever the source moved, not merely when the image is missing.
|
||||
# Starting the service is how an operator expects to pick up an upgraded
|
||||
# plugin, and reusing the old binary there is what makes a new feature look
|
||||
# broken rather than absent.
|
||||
fn-ga-image-current || fn-ga-build-image
|
||||
fn-ga-write-env-file
|
||||
local port
|
||||
port="$(fn-ga-global-get port "$GOOGLE_AUTH_DEFAULT_PORT")"
|
||||
|
||||
Reference in New Issue
Block a user