Add Library Catalog addon, lib/docker.sh and tests
- lib/docker.sh: shared Docker/Compose tooling (on-demand install via apt or get.docker.com, compose wrapper, container state/health, HTTP wait) - menus/addon_library.sh: install/update/uninstall the Koha + Quick Add stack from library/ to /opt/kiosk-library, add the catalogue as a kiosk page (duration 0), choose kiosk-only vs LAN access, show addresses and login - wire into install.sh menu, complete_uninstall and clone_settings - tests/addon_library.test.sh: 41 checks against a stub sudo/docker and a fixture stack (no root, Docker or network needed) Note: the 'network access' choice relies on a BIND_ADDR setting in the library stack, added in a follow-up commit. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
This commit is contained in:
+106
@@ -0,0 +1,106 @@
|
||||
#!/bin/bash
|
||||
################################################################################
|
||||
# lib/docker.sh - Docker / Compose tooling shared by every addon that runs a
|
||||
# containerised service (the Library Catalog addon is the first).
|
||||
#
|
||||
# Everything here goes through `sudo docker ...`: the kiosk admin runs
|
||||
# install.sh as a regular user and is deliberately NOT put in the `docker`
|
||||
# group (docker-group membership is root-equivalent).
|
||||
#
|
||||
# Depends on: lib/menu.sh (log_* helpers) being sourced first.
|
||||
################################################################################
|
||||
|
||||
# Overridable so tests can substitute stubs for the real binaries.
|
||||
: "${DOCKER_BIN:=docker}"
|
||||
|
||||
docker_is_installed() {
|
||||
command -v "$DOCKER_BIN" >/dev/null 2>&1 \
|
||||
&& sudo "$DOCKER_BIN" compose version >/dev/null 2>&1
|
||||
}
|
||||
|
||||
docker_daemon_running() {
|
||||
sudo "$DOCKER_BIN" info >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Install Docker Engine + the Compose plugin if they are missing.
|
||||
#
|
||||
# Ubuntu 24.04+ ships both in its own archive (docker.io, docker-compose-v2):
|
||||
# preferred because it also works through the offline apt bundle's mirror and
|
||||
# gets security updates with everything else. On releases without
|
||||
# docker-compose-v2 (22.04), fall back to Docker's official install script.
|
||||
docker_ensure_installed() {
|
||||
if docker_is_installed; then
|
||||
docker_daemon_running || sudo systemctl enable --now docker >/dev/null 2>&1 || true
|
||||
log_success "Docker $(sudo "$DOCKER_BIN" --version 2>/dev/null | awk '{print $3}' | tr -d ,) is installed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Installing Docker..."
|
||||
if ! sudo apt-get update -qq; then
|
||||
log_error "apt update failed - check the network/package sources and try again"
|
||||
return 1
|
||||
fi
|
||||
if apt-cache show docker-compose-v2 >/dev/null 2>&1; then
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io docker-compose-v2 \
|
||||
|| { log_error "Installing docker.io / docker-compose-v2 failed"; return 1; }
|
||||
else
|
||||
log_info "This Ubuntu release has no docker-compose-v2 package - using Docker's install script"
|
||||
have_internet 2>/dev/null || { log_error "No internet connection - can't download Docker"; return 1; }
|
||||
local script
|
||||
script=$(mktemp)
|
||||
if ! curl -fsSL https://get.docker.com -o "$script"; then
|
||||
rm -f "$script"
|
||||
log_error "Could not download Docker's install script"
|
||||
return 1
|
||||
fi
|
||||
sudo sh "$script" || { rm -f "$script"; log_error "Docker's install script failed"; return 1; }
|
||||
rm -f "$script"
|
||||
fi
|
||||
|
||||
sudo systemctl enable --now docker >/dev/null 2>&1 || true
|
||||
if ! docker_is_installed || ! docker_daemon_running; then
|
||||
log_error "Docker was installed but is not working - check: sudo systemctl status docker"
|
||||
return 1
|
||||
fi
|
||||
log_success "Docker installed"
|
||||
}
|
||||
|
||||
# docker_compose DIR args... -> sudo docker compose --project-directory DIR args...
|
||||
docker_compose() {
|
||||
local dir="$1"; shift
|
||||
sudo "$DOCKER_BIN" compose --project-directory "$dir" "$@"
|
||||
}
|
||||
|
||||
# docker_container_state NAME -> running | exited | restarting | ... | missing
|
||||
docker_container_state() {
|
||||
sudo "$DOCKER_BIN" inspect -f '{{.State.Status}}' "$1" 2>/dev/null || echo "missing"
|
||||
}
|
||||
|
||||
# docker_container_health NAME -> healthy | unhealthy | starting | none | missing
|
||||
docker_container_health() {
|
||||
sudo "$DOCKER_BIN" inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$1" 2>/dev/null \
|
||||
|| echo "missing"
|
||||
}
|
||||
|
||||
# docker_wait_http URL [TIMEOUT_SECONDS]
|
||||
# Polls until URL answers 2xx/3xx; returns 1 on timeout.
|
||||
docker_wait_http() {
|
||||
local url="$1" timeout="${2:-300}" waited=0
|
||||
while (( waited < timeout )); do
|
||||
if curl -fsS -o /dev/null -m 5 "$url" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
sleep 5
|
||||
waited=$((waited + 5))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# docker_free_port START -> prints the first TCP port >= START nothing listens on.
|
||||
docker_free_port() {
|
||||
local port="$1"
|
||||
while ss -tlnH "sport = :${port}" 2>/dev/null | grep -q .; do
|
||||
port=$((port + 1))
|
||||
done
|
||||
echo "$port"
|
||||
}
|
||||
Reference in New Issue
Block a user