Files
kodi 417d08b162 Fix: voorkom pods uit .container workloads
/api/pods-dashboard genereerde onterecht 'pod<basename>' entries voor .container Quadlet-bestanden, wat leidde tot lege nep-pods zoals 'podn8n' in de WebUI.

Alleen echte pod-workloads (.pod, evt. .kube) mogen nog een Source:"systemd" pod-row opleveren.

Geen endpoint- of schemawijzigingen. Alleen filtering in control/app_pods.py aangepast.
2026-03-01 08:41:20 +01:00

165 lines
6.5 KiB
Python

import os
from fastapi import APIRouter
from common import (
_build_pod_to_containers_map,
_map_pod_to_unit,
_podman_action_post,
_podman_delete,
_podman_get_json,
_podman_post,
_systemd_then_podman,
)
def init_pods_router(
session,
podman_api_base: str,
workloads_dir: str,
systemctl_func,
) -> APIRouter:
router = APIRouter(tags=["pods"])
def _append_podman_pods_dashboard_rows(dashboard: list, api_pods: list, pod_to_containers: dict):
# preserves original api_pods iteration order
for p in api_pods:
name = p.get("Name")
status = p.get("Status", "unknown")
unit = _map_pod_to_unit(name) if name else ""
dashboard.append({
"Name": name,
"Status": status,
"Containers": pod_to_containers.get(name, []),
"Unit": unit,
"Source": "podman",
})
def _append_defined_pods_dashboard_rows(dashboard: list, by_name: dict, root_dir: str):
# preserves original os.walk order and file iteration order
SUPPORTED_POD_WORKLOAD_EXTENSIONS = {".pod", ".kube"}
for root, _, files in os.walk(root_dir):
for f in files:
_, ext = os.path.splitext(f)
if ext in SUPPORTED_POD_WORKLOAD_EXTENSIONS:
base = os.path.splitext(os.path.basename(f))[0]
pod_name = f"pod{base}"
unit_name = _map_pod_to_unit(pod_name)
if pod_name not in by_name:
code, out = systemctl_func(["systemctl", "--user", "is-active", unit_name])
status = (out or "").strip() or ("active" if code == 0 else "inactive")
dashboard.append({
"Name": pod_name,
"Status": status,
"Containers": [],
"Unit": unit_name,
"Source": "systemd",
})
def try_systemd_pod_action(action: str, podname: str):
# If systemd unit exists/allowed, prefer it.
unit = _map_pod_to_unit(podname)
if not unit:
return None
code, out = systemctl_func(["systemctl", "--user", action, unit])
return {
"method": "systemd",
"pod": podname,
"unit": unit,
"cmd": f"systemctl --user {action} {unit}",
"exit": code,
"output": out,
}
@router.get("/pods")
def list_pods():
# Cruciaal: ?all=true zorgt dat EXIT_STATE pods ook getoond worden
url = f"{podman_api_base}/libpod/pods/json?all=true"
return _podman_get_json(session, url)
@router.post("/actions/{action}/{name}")
def take_action(action: str, name: str):
# Legacy endpoint (keep behavior)
possible_names = [name, f"pod{name}", f"pod-{name}"]
if action == "start":
# STAP 1: Probeer direct de pod te starten (de 'Cockpit' methode)
for target in possible_names:
res = _podman_post(session, f"{podman_api_base}/libpod/pods/{target}/start")
if res.status_code in (200, 204):
return {"status": "started", "target": target, "method": "direct"}
# STAP 2: Als direct starten faalt, probeer dan YAML opnieuw te deployen
target_path = None
for ext in (".yaml", ".yml"):
cand = os.path.join(workloads_dir, f"{name}{ext}")
if os.path.exists(cand):
target_path = cand
break
if target_path:
with open(target_path, 'r') as file:
yaml_content = file.read()
res = _podman_post(session, f"{podman_api_base}/libpod/kube/play", data=yaml_content)
# SPECIALE CASE: Pod bestaat al, forceer dan restart
if res.status_code == 500 and "already exists" in res.text:
print(f"DEBUG: Forceer herstart voor {name} wegens conflict")
for target in possible_names:
_podman_delete(session, f"{podman_api_base}/libpod/pods/{target}?force=true")
# Probeer het nu opnieuw
retry_res = _podman_post(session, f"{podman_api_base}/libpod/kube/play", data=yaml_content)
return retry_res.json()
return res.json()
return {"status": "unknown", "method": "no_yaml_found"}
if action == "stop":
for target in possible_names:
res = _podman_post(session, f"{podman_api_base}/libpod/pods/{target}/stop")
if res.status_code in (200, 204):
return {"status": "stopped", "target": target}
return {"status": "not found"}
return {"status": "unknown"}
@router.get("/pods-dashboard")
def pods_dashboard():
dashboard = []
# 0) Bouw mapping: pod_name -> [container_names...]
containers = _podman_get_json(session, f"{podman_api_base}/libpod/containers/json?all=true")
pod_to_containers = _build_pod_to_containers_map(containers)
# 1) A) echte pods
api_pods = _podman_get_json(session, f"{podman_api_base}/libpod/pods/json?all=true")
by_name = {p.get("Name"): p for p in api_pods}
_append_podman_pods_dashboard_rows(dashboard, api_pods, pod_to_containers)
# 1) B) defined pods via workloads scan
# Based on YAML files in WORKLOADS_DIR; show even if not running.
_append_defined_pods_dashboard_rows(dashboard, by_name, workloads_dir)
return dashboard
@router.post("/pods/actions/{action}/{podname}")
def pod_action_prefer_systemd(action: str, podname: str):
if action not in ("start", "stop", "restart"):
return {"error": "Invalid action"}, 400
def _systemd_call():
return try_systemd_pod_action(action, podname)
def _podman_call(systemd_res):
if systemd_res:
note = "systemd failed; falling back to podman"
podman = _podman_action_post(session, podman_api_base, "pods", podname, action).json()
return {"method": "systemd_then_podman", "note": note, "systemd": systemd_res, "podman": podman}
return {"method": "podman", "result": _podman_action_post(session, podman_api_base, "pods", podname, action).json()}
return _systemd_then_podman(_systemd_call, _podman_call)
return router