From 5196e7840f46514ce43c338de20b821b5c77feb9 Mon Sep 17 00:00:00 2001 From: kodi Date: Mon, 23 Mar 2026 09:50:31 +0100 Subject: [PATCH] fix (helper): verplaats socket naar dedicated submap /run/podman-mvp/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vervangt file bind-mount door directory mount om stale inode probleem op te lossen: bij file bind-mounts bindt Podman de inode op run-tijd; als podman-helper stopt en de socket verwijdert, wijst de container nog steeds naar de verwijderde inode. Een directory mount lost altijd op naar de huidige mapinhoud inclusief nieuwe inodes. Wijzigingen: - podman-helper.py: SOCKET_PATH → XDG_RUNTIME_DIR/podman-mvp/podman-helper.sock - common.py: HELPER_SOCKET → /run/podman-mvp/podman-helper.sock - CLAUDE.md: run-commando gebruikt -v /run/user/1000/podman-mvp:/run/podman-mvp Deploy: kopieer podman-helper.py naar host, daemon-reload, restart helper, rebuild backend image, herstart container met nieuwe mount. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 110 +++++++++++++++++++++++++++++++++ control/common.py | 2 +- podman-helper/podman-helper.py | 2 +- 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a4cb6d5 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,110 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +podman-mvp is a Portainer-like web dashboard for managing rootless user-session Podman containers. It runs as a two-container Podman pod: a FastAPI backend (`mvp-backend`) that talks to Podman over a Unix socket, and a static Apache frontend (`mvp-webui`) that reverse-proxies `/api/` to the backend. + +## Architecture + +### Backend — FastAPI modular monolith (`control/`) + +| File | Role | +|---|---| +| `app.py` | Bootstrap only — creates FastAPI app, wires routers, no feature logic | +| `common.py` | Shared helpers: Podman HTTP, systemctl, utilities | +| `app_system.py` | System/platform router: `/health`, `/daemon-reload`, systemctl unit actions | +| `app_containers.py` | Containers router: dashboard, inspect, logs, stats stream, exec sessions | +| `app_pods.py` | Pods router: dashboard, pod actions | +| `app_networks.py` | Networks router | +| `app_images.py` | Images router | +| `app_files.py` | Files/workloads router: tree, read, save | + +Backend communicates with Podman through the Unix socket at `/run/user/1000/podman/podman.sock` using `requests_unixsocket`. Podman API base: `http+unix://%2Frun%2Frun%2Fuser%2F1000%2Fpodman%2Fpodman.sock/v5.4.2`. + +### Frontend — Static Apache (`webui/`) + +- `webui/html/index.html` — single-page app shell +- `webui/html/assets/js/tabs/` — per-tab JavaScript modules (containers, networks, images, files) +- `webui/conf/httpd.conf` — Apache config, proxies `/api/` → `http://127.0.0.1:8000/api/` + +## Build & Deploy + +```bash +# Build backend image +podman build -t mvp-control:latest control/ + +# Create pod +podman pod create --name mvp-pod -p 8080:8000 -p 8081:8081 --userns=keep-id + +# Run backend +podman run -d --pod mvp-pod --name mvp-backend \ + --ipc=host --pid=host \ + -e XDG_RUNTIME_DIR=/run/user/1000 \ + -e DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus \ + -v /run/user/1000/podman/podman.sock:/run/user/1000/podman/podman.sock:rw \ + -v /run/user/1000/podman-mvp:/run/podman-mvp \ + -v /home/kodi/.config/containers:/app/workloads:rw \ + mvp-control:latest + +# Run frontend +podman run -d --pod mvp-pod --name mvp-webui \ + -v $HOME/.config/podman-mvp/webui/html:/usr/local/apache2/htdocs:ro \ + -v $HOME/.config/podman-mvp/webui/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro \ + docker.io/library/httpd:2.4 +``` + +## Verification Commands + +```bash +# Syntax check all backend modules +python3 -m py_compile control/app.py control/common.py control/app_system.py \ + control/app_containers.py control/app_pods.py control/app_networks.py \ + control/app_files.py control/app_images.py + +# Smoke test key endpoints (all via proxy on :8081) +curl -fsS http://127.0.0.1:8081/api/health | jq +curl -fsS http://127.0.0.1:8081/api/containers-dashboard >/dev/null && echo OK +curl -fsS http://127.0.0.1:8081/api/pods-dashboard >/dev/null && echo OK +curl -fsS http://127.0.0.1:8081/api/files/tree >/dev/null && echo OK +curl -fsS http://127.0.0.1:8081/api/networks/meta | jq +``` + +All test/verification URLs must target `127.0.0.1:8081` (the proxy), not port 8000 directly. + +## Hard Rules + +### Module placement +- `app.py` is bootstrap-only — no endpoints, no feature logic, no Podman/systemctl calls. +- New system/platform endpoints → `app_system.py`. +- New domain feature endpoints → the corresponding `app_.py`. +- Shared helpers → `common.py`, never duplicated into routers. +- `allow_list` / `allowed_units.txt` has been removed and must NOT be reintroduced. +- `app_system.py` broad wildcard routes (`/{action}/{unit}`) must be defined **last**. + +### API contract (`contracts/API_GOLDEN.md`) +- Never remove or rename existing JSON response keys. +- Never change existing key data types. +- Extend via new optional fields or new endpoints only. +- UI-critical endpoints requiring pre-approval before any change: `/containers-dashboard`, `/pods-dashboard`, `/images`, `/networks/meta`. + +### Security +- No `shell=True` in subprocess calls. +- All subprocess commands must be explicit lists. + +### Infrastructure (propose before changing) +- Pod name, port mappings, `userns=keep-id`. +- DBus/XDG_RUNTIME_DIR mounts, Podman socket path, host PID/IPC namespaces. +- `control/Containerfile`, `webui/conf/httpd.conf`. + +## Change Workflow + +For non-trivial changes, follow PR_RULES.md: +1. Analyse existing behaviour with curl. +2. Propose minimal plan identifying affected files. +3. Confirm API contract safety. +4. Provide curl validation commands showing expected output change. +5. Implement after agreement. + +Minimize diff size. Do not reformat unrelated code. No large rewrites or hidden refactors. diff --git a/control/common.py b/control/common.py index ec3a6c6..88ade60 100644 --- a/control/common.py +++ b/control/common.py @@ -4,7 +4,7 @@ import subprocess from fastapi import HTTPException -HELPER_SOCKET = "/run/podman-helper.sock" +HELPER_SOCKET = "/run/podman-mvp/podman-helper.sock" def _helper_call(action: str, unit: str) -> tuple[int, str]: diff --git a/podman-helper/podman-helper.py b/podman-helper/podman-helper.py index c9df758..f0f91b9 100644 --- a/podman-helper/podman-helper.py +++ b/podman-helper/podman-helper.py @@ -27,7 +27,7 @@ import sys # ── Configuratie ───────────────────────────────────────────────────────────── SOCKET_PATH = os.getenv( "HELPER_SOCKET", - os.path.join(os.getenv("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}"), "podman-helper.sock") + os.path.join(os.getenv("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}"), "podman-mvp", "podman-helper.sock") ) LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO") TIMEOUT = 30 # seconden maximaal per systemctl aanroep