#!/bin/sh
# Prepara el registro privado de Hermes sin exponer la credencial en argv,
# ficheros del paquete ni logs. Solo autoriza el servicio despues de un pull
# real de la referencia fijada por digest.
set -eu
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH

REGISTRY=registry.lopezalex.app
REGISTRY_USER=filmos
SERVICE_ENV=/etc/filmos/hermes/service.env
COMPOSE_FILE=/etc/filmos/hermes/compose.yml
CONTROL_DIR=/var/lib/filmos/hermes-control
READY_FILE=$CONTROL_DIR/registry-ready
DOCKER_CONFIG_DIR=/root/.docker
DOCKER_BIN=/usr/bin/docker
SYSTEMCTL_BIN=/usr/bin/systemctl
ID_BIN=/usr/bin/id
CHOWN_BIN=/usr/bin/chown

fail() {
    echo "filmos-hermes-setup: $*" >&2
    exit 1
}

usage() {
    cat <<'EOF'
Uso: sudo filmos-hermes-setup

Pide una vez la contraseña del registro, sin eco, y se la entrega a Docker por
la entrada estándar. No se admiten contraseñas como argumento.
EOF
}

read_image() {
    [ -r "$SERVICE_ENV" ] || fail "no se puede leer $SERVICE_ENV"

    image=$(sed -n 's/^FILMOS_HERMES_IMAGE=//p' "$SERVICE_ENV")
    [ -n "$image" ] || fail "falta FILMOS_HERMES_IMAGE en $SERVICE_ENV"
    case "$image" in
        *'
'*) fail "FILMOS_HERMES_IMAGE aparece mas de una vez" ;;
    esac

    prefix="$REGISTRY/filmos/hermes:"
    case "$image" in
        "$prefix"*@sha256:*) ;;
        *) fail "la imagen debe pertenecer al registro de filmos y estar fijada por digest" ;;
    esac

    tagged=${image%@sha256:*}
    tag=${tagged#"$prefix"}
    digest=${image##*@sha256:}
    case "$tag" in
        ''|*[!A-Za-z0-9._-]*) fail "la etiqueta de la imagen no es valida" ;;
    esac
    case "$digest" in
        *[!0-9a-f]*) fail "el digest sha256 no es valido" ;;
    esac
    [ "${#digest}" -eq 64 ] || fail "el digest sha256 debe tener 64 caracteres"

    printf '%s\n' "$image"
}

check_ready() {
    image=$(read_image)
    [ -r "$READY_FILE" ] || return 1
    ready=$(cat "$READY_FILE")
    [ "$ready" = "$image" ] || return 1

    # El marcador demuestra que hubo un pull correcto, pero la imagen puede
    # desaparecer despues por un `docker image prune`. La condicion de systemd
    # comprueba tambien el efecto presente y omite la unidad si ya no existe.
    [ -x "$DOCKER_BIN" ] || return 1
    "$DOCKER_BIN" --config "$DOCKER_CONFIG_DIR" image inspect "$image" \
        >/dev/null 2>&1
}

restore_tty() {
    if [ -n "${TTY_STATE:-}" ]; then
        stty "$TTY_STATE"
        TTY_STATE=
        printf '\n' >&2
    fi
}

login_registry() {
    if [ -t 0 ]; then
        printf 'Contraseña de %s para el usuario %s: ' "$REGISTRY" "$REGISTRY_USER" >&2
        TTY_STATE=$(stty -g)
        trap 'restore_tty' EXIT HUP INT TERM
        stty -echo
        if ! IFS= read -r registry_password; then
            restore_tty
            trap - EXIT HUP INT TERM
            fail "no se recibio ninguna contraseña"
        fi
        restore_tty
        trap - EXIT HUP INT TERM
        [ -n "$registry_password" ] || fail "la contraseña esta vacia"

        if ! printf '%s\n' "$registry_password" | \
            "$DOCKER_BIN" --config "$DOCKER_CONFIG_DIR" login "$REGISTRY" \
                --username "$REGISTRY_USER" --password-stdin; then
            unset registry_password
            fail "el registro rechazo las credenciales"
        fi
        unset registry_password
    else
        # Para automatizacion segura: el llamador escribe el secreto en stdin.
        # Nunca se acepta como argumento ni se copia a una variable del shell.
        if ! "$DOCKER_BIN" --config "$DOCKER_CONFIG_DIR" login "$REGISTRY" \
            --username "$REGISTRY_USER" --password-stdin; then
            fail "el registro rechazo las credenciales"
        fi
    fi
}

case "${1:-}" in
    --check-ready)
        [ "$#" -eq 1 ] || exit 2
        check_ready
        exit $?
        ;;
    -h|--help)
        usage
        exit 0
        ;;
    '') ;;
    *)
        echo "filmos-hermes-setup: no se admiten argumentos" >&2
        usage >&2
        exit 2
        ;;
esac

[ "$#" -eq 0 ] || exit 2
[ "$("$ID_BIN" -u)" = 0 ] || fail "debe ejecutarse como root (usa sudo)"
[ -x "$DOCKER_BIN" ] || fail "no se encuentra Docker en $DOCKER_BIN"
[ -x "$SYSTEMCTL_BIN" ] || fail "no se encuentra systemctl en $SYSTEMCTL_BIN"
[ -r "$COMPOSE_FILE" ] || fail "no se puede leer $COMPOSE_FILE"

image=$(read_image)

# Se comprueban Docker y el compose antes de pedir el secreto.
"$DOCKER_BIN" info >/dev/null || fail "el demonio de Docker no responde"
FILMOS_HERMES_IMAGE="$image" \
    "$DOCKER_BIN" compose -f "$COMPOSE_FILE" config --quiet >/dev/null || \
    fail "la configuracion de Compose no es valida"

umask 077
mkdir -p "$DOCKER_CONFIG_DIR" "$CONTROL_DIR"
chmod 0700 "$DOCKER_CONFIG_DIR"
"$CHOWN_BIN" 0:0 "$CONTROL_DIR"
chmod 0750 "$CONTROL_DIR"

login_registry

if [ -f "$DOCKER_CONFIG_DIR/config.json" ]; then
    chmod 0600 "$DOCKER_CONFIG_DIR/config.json"
fi

echo "filmos-hermes-setup: descargando la imagen fijada por digest..."
"$DOCKER_BIN" --config "$DOCKER_CONFIG_DIR" pull "$image" || \
    fail "no se pudo descargar la imagen fijada"
"$DOCKER_BIN" --config "$DOCKER_CONFIG_DIR" image inspect "$image" >/dev/null || \
    fail "Docker no encuentra localmente la imagen descargada"

# Evita autorizar una referencia antigua si otro administrador cambia el
# conffile durante una descarga larga.
current_image=$(read_image)
[ "$current_image" = "$image" ] || \
    fail "service.env cambio durante la descarga; vuelve a ejecutar el asistente"

ready_tmp="$READY_FILE.tmp.$$"
trap 'rm -f "$ready_tmp"' EXIT HUP INT TERM
printf '%s\n' "$image" > "$ready_tmp"
chmod 0600 "$ready_tmp"
mv -f "$ready_tmp" "$READY_FILE"
trap - EXIT HUP INT TERM

"$SYSTEMCTL_BIN" daemon-reload
# `restart` tambien aplica una referencia nueva si el asistente se ejecuta tras
# actualizar service.env; un `start` sobre la unidad oneshot ya activa seria un
# no-op y dejaria vivo el contenedor anterior.
if ! "$SYSTEMCTL_BIN" restart filmos-hermes.service; then
    # Un fallo de la aplicacion tampoco debe dejar el aterrizaje en rojo ni
    # habilitar un error para el siguiente reinicio.
    "$SYSTEMCTL_BIN" stop filmos-hermes.service >/dev/null 2>&1 || true
    "$SYSTEMCTL_BIN" reset-failed filmos-hermes.service >/dev/null 2>&1 || true
    fail "la imagen esta preparada, pero Hermes no supero su arranque saludable"
fi
if ! "$SYSTEMCTL_BIN" enable filmos-hermes.service; then
    "$SYSTEMCTL_BIN" stop filmos-hermes.service >/dev/null 2>&1 || true
    fail "Hermes arranco, pero systemd no pudo habilitarlo para el proximo reinicio"
fi

echo "filmos-hermes-setup: login, descarga y arranque completados."
