Reported from a member's daemon failing: "rootless Docker needs these packages on the host: uidmap". They were being installed — but only inside branch [2] "rootless Docker for <owner>" in section 22. The owner's choice is not the only one that matters: every Developer account the platform provisions gets its own rootless daemon whatever the owner picked for themselves. So on a machine where the owner chose the docker group, the host never got them and every member's daemon failed. Moved into install_docker_engine, so they arrive with Docker rather than with one particular answer to a question about the owner. Three packages, not the one in the error. checkDockerPrerequisites in os-user-docker.ts is the authority and wants uidmap (newuidmap, newgidmap) AND docker-ce-rootless-extras (dockerd-rootless-setuptool.sh); dbus-user-session is what keeps a member's systemd --user alive without a login session. rootless-extras is only RECOMMENDED by docker-ce — installed by default, so usually there by luck, and absent on any host configured with --no-install-recommends. Named explicitly. Reproduced on this machine while checking: rootless-extras present via Recommends, uidmap absent, newuidmap and newgidmap missing. Exactly the reported failure, on a box that chose the docker group. The rootless branch still installs uidmap and dbus-user-session behind its pkg_is_installed guard. Redundant now, kept deliberately: it is the only thing that fixes a machine whose Docker was installed by an older run of this script. Verified: bash -n on both files, and all three packages present in the noble archive. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
141 lines
5.8 KiB
Bash
141 lines
5.8 KiB
Bash
#!/bin/bash
|
|
# =============================================================================
|
|
# machine-setup — Docker
|
|
# =============================================================================
|
|
#
|
|
# Definitions only, like the other lib/ files.
|
|
|
|
[[ -n "${MACHINE_SETUP_DOCKER_LOADED:-}" ]] && return 0
|
|
MACHINE_SETUP_DOCKER_LOADED=1
|
|
|
|
DOCKER_NETWORK="${SETUP_DOCKER_NETWORK:-services}"
|
|
|
|
docker_is_installed() { command -v docker &>/dev/null; }
|
|
|
|
# The daemon, not just the binary. `docker --version` answers from the client
|
|
# alone and says nothing about whether there is anything to talk to.
|
|
docker_daemon_ok() { docker info &>/dev/null; }
|
|
|
|
user_in_docker_group() { id -nG "$USERNAME" 2>/dev/null | tr ' ' '\n' | grep -qx docker; }
|
|
|
|
docker_rootless_installed() { [[ -S "/run/user/$(id -u "$USERNAME" 2>/dev/null)/docker.sock" ]]; }
|
|
|
|
# The codename Docker's repository is actually published under.
|
|
#
|
|
# `lsb_release -cs` is what the original used, and it is wrong on every
|
|
# derivative: Mint reports "vanessa", Pop reports its own, and Docker publishes
|
|
# neither — so `apt update` fails on a repository that does not exist. os-release
|
|
# carries UBUNTU_CODENAME on exactly those systems for exactly this reason, so it
|
|
# is preferred and VERSION_CODENAME is the fallback.
|
|
docker_repo_codename() {
|
|
local c
|
|
c="$(os_release UBUNTU_CODENAME || true)"
|
|
[[ -z "$c" ]] && c="$(os_release VERSION_CODENAME || true)"
|
|
echo "$c"
|
|
}
|
|
|
|
# Which upstream to point at. A derivative is Ubuntu or Debian as far as Docker
|
|
# is concerned, and ID_LIKE is how it says which.
|
|
docker_repo_distro() {
|
|
case "$OS" in
|
|
ubuntu | debian) echo "$OS" ;;
|
|
*)
|
|
case " $(os_release ID_LIKE || true) " in
|
|
*" ubuntu "*) echo ubuntu ;;
|
|
*) echo debian ;;
|
|
esac
|
|
;;
|
|
esac
|
|
}
|
|
|
|
install_docker_engine() {
|
|
# Linux only, and never reached on macOS: the Docker step there checks for
|
|
# Docker Desktop and tells the owner to install it rather than doing it — a GUI
|
|
# app that wants opening, permissions and a running window is not a shell
|
|
# script's job, and colima/lima are not worth the evening they cost.
|
|
|
|
local distro codename
|
|
distro="$(docker_repo_distro)"
|
|
codename="$(docker_repo_codename)"
|
|
|
|
[[ -n "$codename" ]] || {
|
|
warn "could not work out this release's codename — cannot add the Docker repository"
|
|
return 1
|
|
}
|
|
|
|
install -m 0755 -d /etc/apt/keyrings
|
|
curl -fsSL "https://download.docker.com/linux/${distro}/gpg" |
|
|
gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg
|
|
chmod a+r /etc/apt/keyrings/docker.gpg
|
|
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/${distro} ${codename} stable" \
|
|
>/etc/apt/sources.list.d/docker.list
|
|
|
|
pkg_refresh >/dev/null
|
|
|
|
# ── The rootless prerequisites go in HERE, not in the rootless branch ──
|
|
#
|
|
# They used to be installed only when the owner picked "[2] rootless Docker for
|
|
# me" in section 22. But the OWNER's choice is not the only one that matters:
|
|
# every Developer account the platform provisions gets its own rootless daemon,
|
|
# whatever the owner picked for themselves. So on a machine where the owner chose
|
|
# the docker group, the host never got these and every member's daemon failed
|
|
# with `rootless Docker needs these packages on the host: uidmap`.
|
|
#
|
|
# `src/servers/os-user-docker.ts` → checkDockerPrerequisites is the authority on
|
|
# this list, and it wants both:
|
|
#
|
|
# uidmap /usr/bin/newuidmap, /usr/bin/newgidmap
|
|
# docker-ce-rootless-extras /usr/bin/dockerd-rootless-setuptool.sh
|
|
#
|
|
# docker-ce only RECOMMENDS rootless-extras. That is installed by default, so it
|
|
# is usually there by luck — and is not on a host configured with
|
|
# --no-install-recommends. Named explicitly so it does not depend on that.
|
|
#
|
|
# dbus-user-session is what lets a member's systemd --user survive without a
|
|
# login session, which is how the daemon stays up.
|
|
pkg_install_now docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \
|
|
docker-ce-rootless-extras uidmap dbus-user-session
|
|
}
|
|
|
|
# A shared network so containers from different compose files can reach each
|
|
# other by name. Harmless if it is already there.
|
|
ensure_docker_network() {
|
|
docker network inspect "$DOCKER_NETWORK" &>/dev/null && return 0
|
|
docker network create "$DOCKER_NETWORK" >/dev/null 2>&1
|
|
}
|
|
|
|
# ── Rootless, for the owner ──
|
|
#
|
|
# Works, and does not work with Officer's app store as it stands. Both are true
|
|
# and the second is the one nobody would find out until a container failed to
|
|
# provision, so it is stated at the prompt rather than left here.
|
|
#
|
|
# The app store spawns `docker` with no environment of its own —
|
|
# app-store/compose.ts, app-store/preflight.ts, api/system-monitor — so it talks
|
|
# to whatever socket the `officer` pm2 process's environment points at. That is
|
|
# /var/run/docker.sock unless DOCKER_HOST says otherwise, and nothing sets
|
|
# DOCKER_HOST for the owner: os-user-docker.ts sets it only for member commands.
|
|
#
|
|
# pm2 started at boot by systemd has no session either, so exporting it in a
|
|
# shell rc does not reach the process that matters.
|
|
install_docker_rootless() {
|
|
local uid
|
|
uid="$(id -u "$USERNAME")"
|
|
|
|
# Without lingering, the user manager stops when the last session ends and
|
|
# takes the daemon with it. Officer's shells are not login sessions.
|
|
loginctl enable-linger "$USERNAME" >/dev/null 2>&1
|
|
|
|
sudo -u "$USERNAME" \
|
|
XDG_RUNTIME_DIR="/run/user/${uid}" \
|
|
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${uid}/bus" \
|
|
PATH="/usr/bin:/usr/sbin:/bin:/sbin" \
|
|
dockerd-rootless-setuptool.sh install >/dev/null 2>&1 || return 1
|
|
|
|
sudo -u "$USERNAME" \
|
|
XDG_RUNTIME_DIR="/run/user/${uid}" \
|
|
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${uid}/bus" \
|
|
systemctl --user enable --now docker >/dev/null 2>&1
|
|
}
|