/* * SysDeck - Containers Panel (v0.0.35) * Author: Jeremy Anderson (https://dcos.net) * * Podman container management panel. The Kata portion of the * former merged "Containers & VMs" module is now its own sidebar * entry — `SysDeck Kata` (plugins/sysdeck-kata/) — per the v0.0.35 * user directive: "kata containers should be called SysDeck Kata and * moved out of the tools area." * * v0.0.34 was a merged two-tab panel (Podman + Kata iframe). The * v0.0.35 split restores the one-module-one-concern shape: this * panel manages Podman containers only; the SysDeck Kata plugin * hosts the pre-built cockpit-kata React app for Kata sandboxes & VMs. * * Bridge surface (see shared/bridge.js → bridge.containers): * list() → podman ps --format json (normalized) * inspect(id) → podman inspect * action(id, action) → podman stop|restart|rm * count() → derived from list() */ export async function mount(panel, { bridge, EventBus }) { panel.innerHTML = renderSkeleton(); panel.innerHTML = renderShell(); await renderPodmanTable(panel, { bridge, EventBus }); EventBus.emit('containers.loaded', {}); } // ── Shell ────────────────────────────────────────────────────────── function renderShell() { return `

Containers

Podman runtime — list, inspect, stop / restart / remove

Loading containers…

Kata Containers (hardware-virtualized OCI sandboxes) is now a standalone sidebar entry — SysDeck Kata. Open it to manage Kata sandboxes & VMs from the pre-built React app.

`; } async function renderPodmanTable(panel, { bridge, EventBus }) { const host = panel.querySelector('#containers-table-host'); const summary = panel.querySelector('#containers-summary'); if (!host || !summary) return; host.innerHTML = `
`; let containers = []; try { containers = await bridge.containers.list(); } catch (err) { summary.textContent = 'Podman unavailable'; host.innerHTML = renderPodmanError(err); return; } summary.textContent = `Podman runtime — ${containers.length} container${containers.length === 1 ? '' : 's'}`; host.innerHTML = ` ${containers.map(renderPodmanRow).join('') || ''}
IDNameImageStatusPortsActions
No containers. Run `podman run -d --name hello alpine sleep 9999` to create one.
`; host.querySelectorAll('[data-action]').forEach((btn) => { btn.addEventListener('click', async () => { const id = btn.dataset.id; const action = btn.dataset.action; btn.disabled = true; try { await bridge.containers.action(id, action); EventBus.emit('container.action', { id, action }); } catch (err) { EventBus.emit('container.error', { id, error: err.message }); } renderPodmanTable(panel, { bridge, EventBus }); }); }); } function renderPodmanRow(c) { const statusClass = (c.status || '').startsWith('Up') ? 'success' : 'warn'; const id = (c.id || '').substring(0, 12); const name = c.name || '—'; const image = c.image || '—'; const status = c.status || '—'; const ports = c.ports || '—'; return ` ${escapeHtml(id)} ${escapeHtml(name)} ${escapeHtml(image)} ${escapeHtml(status)} ${escapeHtml(ports)} `; } function renderPodmanError(err) { return `

Podman unavailable

${escapeHtml(err.message || String(err))}. Install podman to manage containers from this panel.

Arch: pacman -S podman · Debian: apt install podman · Fedora: dnf install podman

`; } // ── Utilities ─────────────────────────────────────────────────────── function escapeHtml(s) { return String(s == null ? '' : s) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function renderSkeleton() { return `
`; }