From b49df4f2eb7220b78c709ca0ac97a77d430f9800 Mon Sep 17 00:00:00 2001 From: juvdiaz Date: Mon, 29 Jun 2026 11:13:29 -0600 Subject: [PATCH] Add Debian Docker root repair command --- README.md | 7 +++ README.md.tmpl | 7 +++ infra/rpi-services/seed-uptime-kuma.js | 52 ++++++++++++++++++++++- jeannie | 59 +++++++++++++++++++++++++- 4 files changed, 123 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 96aabca..45dbfce 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,13 @@ or host storage/networking: ./jeannie preflight ``` +If the Debian Docker root check fails after a reinstall, repair it before +running the full pipeline: + +```bash +./jeannie fix-debian-docker-root +``` + ## Validation Useful checks after a rebuild: diff --git a/README.md.tmpl b/README.md.tmpl index 8eca15e..b43f2d4 100644 --- a/README.md.tmpl +++ b/README.md.tmpl @@ -230,6 +230,13 @@ or host storage/networking: ./{{ main_script }} preflight ``` +If the Debian Docker root check fails after a reinstall, repair it before +running the full pipeline: + +```bash +./{{ main_script }} fix-debian-docker-root +``` + ## Validation Useful checks after a rebuild: diff --git a/infra/rpi-services/seed-uptime-kuma.js b/infra/rpi-services/seed-uptime-kuma.js index 2efb10d..f3c2c29 100755 --- a/infra/rpi-services/seed-uptime-kuma.js +++ b/infra/rpi-services/seed-uptime-kuma.js @@ -2,9 +2,59 @@ "use strict"; const fs = require("fs"); +const path = require("path"); + +function findModuleDirectory(root, moduleName, depth = 0) { + if (depth > 5) { + return ""; + } + let entries; + try { + entries = fs.readdirSync(root, { withFileTypes: true }); + } catch (_error) { + return ""; + } + + for (const entry of entries) { + if (!entry.isDirectory()) { + continue; + } + const fullPath = path.join(root, entry.name); + if (entry.name === moduleName && fs.existsSync(path.join(fullPath, "package.json"))) { + return fullPath; + } + } + + for (const entry of entries) { + if (!entry.isDirectory()) { + continue; + } + const fullPath = path.join(root, entry.name); + const found = findModuleDirectory(fullPath, moduleName, depth + 1); + if (found) { + return found; + } + } + + return ""; +} function loadDatabaseModule() { - for (const moduleName of ["better-sqlite3", "/app/node_modules/better-sqlite3"]) { + const candidates = [ + "better-sqlite3", + "/app/node_modules/better-sqlite3", + "/app/server/node_modules/better-sqlite3", + "/app/src/node_modules/better-sqlite3" + ]; + + for (const root of ["/app/node_modules", "/app/server/node_modules", "/app/src/node_modules"]) { + const found = findModuleDirectory(root, "better-sqlite3"); + if (found) { + candidates.push(found); + } + } + + for (const moduleName of candidates) { try { return require(moduleName); } catch (_error) { diff --git a/jeannie b/jeannie index 1ded1cd..db8a4d7 100755 --- a/jeannie +++ b/jeannie @@ -1081,6 +1081,60 @@ check_debian_docker_root() { fi } +fix_debian_docker_root() { + local expected_root="${LAB_DEBIAN_DOCKER_ROOT:-/data/docker}" + local daemon_file="/etc/docker/daemon.json" + local current_root="" + + require_debian_server "fix-debian-docker-root" + + if ! command -v docker >/dev/null 2>&1; then + echo "docker command was not found on Debian host" >&2 + exit 1 + fi + + current_root="$(sudo docker info --format '{{.DockerRootDir}}' 2>/dev/null || true)" + if [[ "${current_root}" == "${expected_root}" ]]; then + echo "Debian Docker root is already ${expected_root}." + return 0 + fi + + echo "Moving Debian Docker root from ${current_root:-unknown} to ${expected_root}..." + sudo mkdir -p "${expected_root}" /etc/docker + + echo "Stopping Docker services..." + sudo systemctl stop docker 2>/dev/null || true + sudo systemctl stop containerd 2>/dev/null || true + + if [[ -n "${current_root}" && -d "${current_root}" && "${current_root}" != "${expected_root}" ]]; then + echo "Copying existing Docker data to ${expected_root}..." + sudo rsync -aHAX --numeric-ids "${current_root}/" "${expected_root}/" + fi + + sudo python3 - "${daemon_file}" "${expected_root}" <<'PY' +import json +import os +import sys + +path, expected_root = sys.argv[1:3] +document = {} +if os.path.exists(path) and os.path.getsize(path) > 0: + with open(path, encoding="utf-8") as handle: + document = json.load(handle) +document["data-root"] = expected_root +with open(path, "w", encoding="utf-8") as handle: + json.dump(document, handle, indent=2, sort_keys=True) + handle.write("\n") +PY + + echo "Starting Docker services..." + sudo systemctl start containerd + sudo systemctl start docker + + check_debian_docker_root + echo "Debian Docker root is now ${expected_root}." +} + check_debian_tailscale_ip() { local expected_ip="${LAB_DEBIAN_TAILSCALE_IP:-}" @@ -4921,6 +4975,9 @@ case "${1:-}" in preflight) homelab_preflight ;; + fix-debian-docker-root) + fix_debian_docker_root + ;; secrets-init) secrets_init ;; @@ -4943,7 +5000,7 @@ case "${1:-}" in nuke ;; *) - echo "Usage: $0 {up|rebuild-cluster|stop-cluster|start-cluster|status|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-gitea-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|openwrt|nuke}" + echo "Usage: $0 {up|rebuild-cluster|stop-cluster|start-cluster|status|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-gitea-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|fix-debian-docker-root|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|openwrt|nuke}" exit 1 ;; esac