Seed the MakerFLOSS devops repo with the podman module
README, a module-catalog.json validated against TAPPaaS's module-catalog-fields.json, and the podman module copied verbatim from Community/src/larsrossen/containers/podman into a flat src/containers/ layout (the catalog carries the explicit moduleJson path, so layout is free).
This commit is contained in:
commit
1dba304085
9 changed files with 482 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
.DS_Store
|
||||||
35
README.md
Normal file
35
README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# makerfloss
|
||||||
|
|
||||||
|
The MakerFLOSS **DevOps repository** for experimental TAPPaaS modules.
|
||||||
|
|
||||||
|
Modules are developed and tried out here, on the TAPPaaS system at
|
||||||
|
[makerfloss.eu](https://makerfloss.eu), before they are proposed upstream to
|
||||||
|
[TAPPaaS](https://codeberg.org/TAPPaaS/TAPPaaS) or
|
||||||
|
[Community](https://codeberg.org/TAPPaaS/Community). Expect things to be
|
||||||
|
half-built, renamed, or removed.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/module-catalog.json # the registry a TAPPaaS instance reads
|
||||||
|
src/containers/podman/ # one directory per module
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using it from a TAPPaaS instance
|
||||||
|
|
||||||
|
Register the repository on the `tappaas-cicd` mothership, then install a module
|
||||||
|
from it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
site-manager repository add makerfloss \
|
||||||
|
--url https://forgejo.makerfloss.eu/TAPPaaS/makerfloss.git \
|
||||||
|
--branch main
|
||||||
|
|
||||||
|
module-manager module add podman --environment <env>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
|
||||||
|
| Module | What it is | Status |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| [podman](src/containers/podman) | Debian 13 VM with rootless Podman + the Cockpit web console | incomplete |
|
||||||
110
src/containers/podman/INSTALL.md
Normal file
110
src/containers/podman/INSTALL.md
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
# Podman container host — Installation
|
||||||
|
|
||||||
|
Primary audience: TAPPaaS admin. Steps the scripts cannot automate.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- `tappaas@tappaas-cicd` with the usual cluster SSH/sudo access.
|
||||||
|
- The dependencies are pulled in automatically (`cluster:vm`, `templates:debian`,
|
||||||
|
`backup:vm`, `network:proxy`).
|
||||||
|
- For the friendly HTTPS name to present a valid certificate, the TAPPaaS wildcard must be in
|
||||||
|
OPNsense Trust (run `acme-setup.sh` once — see the platform INSTALL). Until then the internal
|
||||||
|
endpoint still works, with a cert warning (Cockpit also ships a self-signed cert).
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/tappaas/Community/src/larsrossen/containers/podman
|
||||||
|
install-module.sh podman
|
||||||
|
```
|
||||||
|
|
||||||
|
This automatically:
|
||||||
|
- creates a Debian 13 VM (vmid 812, 2 vCPU / 2 GB / 20 GB) and OS-preps it;
|
||||||
|
- installs `podman`, `podman-compose`, `cockpit` and `cockpit-podman` from apt;
|
||||||
|
- enables the rootless podman socket (with linger) and the Cockpit console on `:9090`;
|
||||||
|
- publishes the internal reverse-proxy name `podman.<domain>` (console reachable from the
|
||||||
|
`mgmt` zone only, no internet).
|
||||||
|
|
||||||
|
### Placement / zone
|
||||||
|
|
||||||
|
The module JSON sets **no `zone0`**, so the VM lands in the **default environment's zone**
|
||||||
|
(ADR-007 P5: with `zone0` unset, install resolves the zone from
|
||||||
|
`config/environments/<env>.json → network.zone`). To place it in a specific environment — and
|
||||||
|
therefore its associated zone — pass `--environment` at install time:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
install-module.sh podman --environment <env> # VM joins <env>'s network.zone
|
||||||
|
```
|
||||||
|
|
||||||
|
An explicit `zone0` in the JSON would override this, so it is intentionally omitted.
|
||||||
|
|
||||||
|
## Post-install (manual — required for the web login)
|
||||||
|
|
||||||
|
The Cockpit console authenticates against **Linux (PAM) accounts on the VM**. The cloud-init
|
||||||
|
`tappaas` user has SSH-key auth and **no password**, so it cannot log in to the web console
|
||||||
|
until you give it one (or create a dedicated admin user).
|
||||||
|
|
||||||
|
**Option A — give the `tappaas` user a password:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh tappaas@<vm-ip> 'sudo passwd tappaas'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option B — create a dedicated admin user (recommended for shared access):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh tappaas@<vm-ip>
|
||||||
|
sudo useradd -m -s /bin/bash -G sudo podadmin && sudo passwd podadmin
|
||||||
|
sudo loginctl enable-linger podadmin # keep its rootless podman socket alive
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open **`https://podman.<domain>`** (or `https://<vm-ip>:9090`), log in with that account,
|
||||||
|
and choose **Podman containers** in the left menu (Cockpit starts the user's podman service on
|
||||||
|
first use).
|
||||||
|
|
||||||
|
## Using it
|
||||||
|
|
||||||
|
- CLI: `ssh tappaas@<vm-ip>` then `podman run …`, `podman ps`, `podman images`.
|
||||||
|
- Compose files: `podman-compose up -d` in a directory with a `compose.yaml`.
|
||||||
|
- Web: manage/start/stop containers, pull images and inspect logs from the Cockpit
|
||||||
|
**Podman containers** page.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test-module.sh podman
|
||||||
|
```
|
||||||
|
|
||||||
|
Manual checks:
|
||||||
|
|
||||||
|
| Check | Expected |
|
||||||
|
|-------|----------|
|
||||||
|
| `ssh tappaas@<vm-ip> podman --version` | prints the Podman version |
|
||||||
|
| `https://podman.<domain>` from a `mgmt` browser | Cockpit login page loads |
|
||||||
|
| Same URL from an out-of-policy zone or the internet | 403 (blocked) |
|
||||||
|
| Cockpit → **Podman containers** after login | the Podman page renders |
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Web login rejected for `tappaas`**
|
||||||
|
The account has no password by default — set one (Option A above) or use a dedicated admin
|
||||||
|
user (Option B). Cockpit cannot log in a key-only account.
|
||||||
|
|
||||||
|
**Podman page in Cockpit says the service is not running**
|
||||||
|
Cockpit starts the *per-user* podman service on first open; if it does not, on the VM run
|
||||||
|
`systemctl --user start podman.socket` as that user, and ensure `loginctl enable-linger <user>`
|
||||||
|
was set so the socket survives logout.
|
||||||
|
|
||||||
|
**Friendly name unreachable**
|
||||||
|
Confirm you are in the `mgmt` zone (others get 403 by design). Check the Caddy route:
|
||||||
|
`network:proxy test-service.sh podman`. Re-apply with `install-module.sh podman --force`.
|
||||||
|
|
||||||
|
**Friendly name loads a blank page (valid cert, no UI)**
|
||||||
|
Cockpit rides a WebSocket; behind a TLS reverse proxy Caddy must talk HTTP/1.1 to the upstream.
|
||||||
|
This module sets `proxyUpstreamHttp1: true` (os-caddy `HttpVersion=http1`). If you see a blank
|
||||||
|
page, verify the handler has HTTP Version = HTTP/1.1 and re-apply:
|
||||||
|
`install-module.sh podman --force`. The direct console `https://<vm-ip>:9090` always works.
|
||||||
|
|
||||||
|
**Re-run / upgrade**
|
||||||
|
`install-module.sh podman --force` is idempotent: apt re-runs (a no-op when already current)
|
||||||
|
and the version marker `/etc/tappaas-podman.version` is refreshed.
|
||||||
60
src/containers/podman/README.md
Normal file
60
src/containers/podman/README.md
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Podman — container host with a web console
|
||||||
|
|
||||||
|
Primary audience: TAPPaaS operator who wants a plain place to run OCI containers.
|
||||||
|
|
||||||
|
A **Debian 13 (trixie) VM with [Podman](https://podman.io) installed** — the daemonless,
|
||||||
|
rootless-capable container engine (a drop-in for `docker`). On top of the engine it adds the
|
||||||
|
**Cockpit web console** with the **`cockpit-podman`** plugin, so containers, images and pods
|
||||||
|
can be managed from a browser instead of only the CLI. Cockpit's console has a **login
|
||||||
|
screen**, so per the module brief it is published internally as `https://podman.<domain>` via
|
||||||
|
`network:proxy`.
|
||||||
|
|
||||||
|
## What you get
|
||||||
|
|
||||||
|
| Capability | Access from | How |
|
||||||
|
|------------|-------------|-----|
|
||||||
|
| Podman container engine (rootless + CLI) | on the VM | `ssh tappaas@<vm-ip>` then `podman …` |
|
||||||
|
| Cockpit web console (login + Podman page) | `mgmt` zone | `https://podman.<domain>` (internal) |
|
||||||
|
| Direct console | LAN reachable from `mgmt` | `https://<vm-ip>:9090` |
|
||||||
|
| `podman-compose` for compose files | on the VM | `podman-compose up -d` |
|
||||||
|
|
||||||
|
## What is not included
|
||||||
|
|
||||||
|
- **No pre-loaded containers.** This is a clean host — you bring the images/compose files.
|
||||||
|
- **No internet exposure.** The friendly name is reachable only from the `mgmt` zone; it is
|
||||||
|
not published to the internet (no inbound NAT/port-forward).
|
||||||
|
- **No Docker daemon / Kubernetes.** Podman is daemonless; there is no `dockerd` and no k8s
|
||||||
|
control plane here (Podman can generate/play Kubernetes YAML, but that is out of scope).
|
||||||
|
- **No default web login.** The console authenticates against Linux (PAM) accounts on the VM;
|
||||||
|
the cloud-init `tappaas` user has no password until you set one — see [INSTALL.md](./INSTALL.md).
|
||||||
|
|
||||||
|
## Access (internal only)
|
||||||
|
|
||||||
|
`network:proxy` publishes **`https://podman.<domain>`** (split-horizon DNS → Caddy → the VM's
|
||||||
|
`:9090`, HTTPS upstream), with an access-list permitting only the **`mgmt`** zone — **not
|
||||||
|
reachable from the internet**. Cockpit rides WebSockets, so the route is created with
|
||||||
|
`proxyUpstreamHttp1: true` (Caddy talks HTTP/1.1 to the upstream); the direct
|
||||||
|
`https://<vm-ip>:9090` always works too.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
| Depends on | Purpose |
|
||||||
|
|------------|---------|
|
||||||
|
| `cluster:vm` | Creates the Debian 13 VM from the cloud image |
|
||||||
|
| `templates:debian` | OS prep (apt update/upgrade + qemu-guest-agent) |
|
||||||
|
| `backup:vm` | Scheduled VM backup to PBS |
|
||||||
|
| `network:proxy` | Internal-only reverse proxy for `podman.<domain>` |
|
||||||
|
|
||||||
|
## Placement
|
||||||
|
|
||||||
|
The module sets no explicit `zone0`, so the VM is placed in the **default environment's zone**;
|
||||||
|
install it with `--environment <env>` to put it in a specific environment and its associated
|
||||||
|
zone instead (see [INSTALL.md](./INSTALL.md#placement--zone)).
|
||||||
|
|
||||||
|
## Sizing
|
||||||
|
|
||||||
|
2 vCPU / **2 GB** RAM / 20 GB disk — enough for the engine, Cockpit and a handful of light
|
||||||
|
containers. Bump memory/disk to match whatever you actually run on it (container images and
|
||||||
|
volumes land on the VM disk, thin-provisioned on ZFS).
|
||||||
|
|
||||||
|
For installation steps see [INSTALL.md](./INSTALL.md).
|
||||||
17
src/containers/podman/install.sh
Executable file
17
src/containers/podman/install.sh
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# podman module install — thin wrapper.
|
||||||
|
#
|
||||||
|
# The VM itself is created by the cluster:vm provider (Debian 13 cloud image),
|
||||||
|
# then OS-prepped by the templates:debian provider (apt update + qemu-guest-agent
|
||||||
|
# via update-os.sh). This script applies the module-specific step: installing
|
||||||
|
# Podman + the Cockpit web console onto the Debian VM. All real work lives in
|
||||||
|
# update.sh (install == update for this module).
|
||||||
|
#
|
||||||
|
# Usage: install.sh <module-name>
|
||||||
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
exec "${SCRIPT_DIR}/update.sh" "$@"
|
||||||
37
src/containers/podman/podman.json
Normal file
37
src/containers/podman/podman.json
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"description": "Podman container host — a Debian 13 VM with rootless Podman + the Cockpit web console (cockpit-podman) for managing containers, images and pods from a browser",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"appVersion": "5.4",
|
||||||
|
"releaseDate": "2026-08-18",
|
||||||
|
"maintainer": "@larsrossen",
|
||||||
|
"status": "Development",
|
||||||
|
"vmname": "podman",
|
||||||
|
"vmid": 812,
|
||||||
|
"vmtag": "TAPPaaS,Community,Containers",
|
||||||
|
"ports": [
|
||||||
|
{ "port": 9090, "protocol": "TCP", "description": "Cockpit web console UI + login (HTTPS)" }
|
||||||
|
],
|
||||||
|
"dependsOn": ["cluster:vm", "templates:debian", "backup:vm", "network:proxy"],
|
||||||
|
"provides": [],
|
||||||
|
"config": {
|
||||||
|
"cluster:vm": {
|
||||||
|
"bios": "seabios",
|
||||||
|
"ostype": "l26",
|
||||||
|
"cores": 2,
|
||||||
|
"memory": "2048",
|
||||||
|
"diskSize": "20G",
|
||||||
|
"storage": "tanka1",
|
||||||
|
"imageType": "img",
|
||||||
|
"image": "debian-13-generic-amd64.qcow2",
|
||||||
|
"imageLocation": "https://cdimage.debian.org/cdimage/cloud/trixie/latest",
|
||||||
|
"cloudInit": "true",
|
||||||
|
"bridge0": "lan"
|
||||||
|
},
|
||||||
|
"network:proxy": {
|
||||||
|
"proxyPort": 9090,
|
||||||
|
"proxyUpstreamTls": true,
|
||||||
|
"proxyUpstreamHttp1": true,
|
||||||
|
"proxyAllowedZones": ["mgmt"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
81
src/containers/podman/test.sh
Executable file
81
src/containers/podman/test.sh
Executable file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# podman module test — health checks for the Podman container-host VM.
|
||||||
|
#
|
||||||
|
# Verifies (from tappaas-cicd, over the Proxmox guest agent + SSH):
|
||||||
|
# - the VM is reachable
|
||||||
|
# - podman is installed (version marker + working binary)
|
||||||
|
# - the cockpit-podman plugin is present
|
||||||
|
# - the Cockpit web console answers (HTTPS, :9090)
|
||||||
|
#
|
||||||
|
# Usage: test.sh <module-name>
|
||||||
|
#
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
. /home/tappaas/bin/common-install-routines.sh
|
||||||
|
|
||||||
|
MODULE="${1:-podman}"
|
||||||
|
readonly MGMT="mgmt"
|
||||||
|
VMID="$(get_config_value 'vmid')"
|
||||||
|
VMNAME="$(get_config_value 'vmname' "${MODULE}")"
|
||||||
|
|
||||||
|
PASS=0; FAIL=0
|
||||||
|
ok() { info " ${GN}✓${CL} $1"; PASS=$((PASS+1)); }
|
||||||
|
no() { error " ✗ $1"; FAIL=$((FAIL+1)); }
|
||||||
|
|
||||||
|
[[ -n "${VMID}" && "${VMID}" != "null" ]] || die "no vmid for ${MODULE}"
|
||||||
|
|
||||||
|
PRIMARY="$(get_primary_node_fqdn 2>/dev/null || echo "tappaas1.${MGMT}.internal")"
|
||||||
|
NODE="$(ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PRIMARY}" \
|
||||||
|
"pvesh get /cluster/resources --type vm --output-format json 2>/dev/null" \
|
||||||
|
| jq -r --arg v "${VMID}" '.[] | select(.vmid==($v|tonumber)) | .node' 2>/dev/null | head -1)"
|
||||||
|
[[ -n "${NODE}" ]] || NODE="$(get_config_value 'node' "$(get_node_hostname 0)")"
|
||||||
|
|
||||||
|
IP="$(ssh -o BatchMode=yes "root@${NODE}.${MGMT}.internal" \
|
||||||
|
"qm guest cmd ${VMID} network-get-interfaces" 2>/dev/null \
|
||||||
|
| jq -r '.[] | select(.name | test("^lo$") | not) | ."ip-addresses"[]? | select(."ip-address-type"=="ipv4") | ."ip-address"' 2>/dev/null \
|
||||||
|
| grep -v '^127\.' | head -1)"
|
||||||
|
|
||||||
|
info "${BOLD}podman test${CL} (${VMNAME}, VM ${VMID} on ${NODE})"
|
||||||
|
if [[ -z "${IP}" ]]; then
|
||||||
|
no "could not resolve VM IP via guest agent"
|
||||||
|
info "Result: ${PASS} passed, ${FAIL} failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ok "VM IP resolved (${IP})"
|
||||||
|
|
||||||
|
vm() { ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 "tappaas@${IP}" "$@"; }
|
||||||
|
|
||||||
|
# Version marker + working podman binary
|
||||||
|
GOT_VER="$(vm "cat /etc/tappaas-podman.version 2>/dev/null" || true)"
|
||||||
|
if [[ -n "${GOT_VER}" ]]; then
|
||||||
|
ok "podman version marker present (${GOT_VER})"
|
||||||
|
else
|
||||||
|
no "podman version marker missing (not installed?)"
|
||||||
|
fi
|
||||||
|
if vm "command -v podman >/dev/null 2>&1 && podman --version >/dev/null 2>&1"; then
|
||||||
|
ok "podman binary works ($(vm "podman --version 2>/dev/null" || echo '?'))"
|
||||||
|
else
|
||||||
|
no "podman binary not working"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# cockpit-podman plugin installed
|
||||||
|
if vm "dpkg -s cockpit-podman >/dev/null 2>&1"; then
|
||||||
|
ok "cockpit-podman plugin installed"
|
||||||
|
else
|
||||||
|
no "cockpit-podman plugin not installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cockpit web console answers on :9090 (login page pre-auth is a pass).
|
||||||
|
# Plain -s (no --fail): --fail exits 22 on 4xx and would corrupt the -w code.
|
||||||
|
code="$(vm "curl -sk -o /dev/null -w '%{http_code}' https://localhost:9090/ 2>/dev/null" || echo 000)"
|
||||||
|
if [[ "${code}" =~ ^(200|302|401|403)$ ]]; then
|
||||||
|
ok "Cockpit console answers on :9090 (HTTP ${code})"
|
||||||
|
else
|
||||||
|
no "Cockpit console did not answer on :9090 (HTTP ${code})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
info "Result: ${PASS} passed, ${FAIL} failed"
|
||||||
|
[[ "${FAIL}" -eq 0 ]]
|
||||||
124
src/containers/podman/update.sh
Executable file
124
src/containers/podman/update.sh
Executable file
|
|
@ -0,0 +1,124 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# podman module update — install/upgrade Podman + the Cockpit web console on the
|
||||||
|
# Debian 13 VM.
|
||||||
|
#
|
||||||
|
# Podman is the daemonless, rootless-capable container engine; it ships in the
|
||||||
|
# Debian 13 (trixie) main repo, so no external download is needed. To give it the
|
||||||
|
# browser interface the module promises, we also install Cockpit and its
|
||||||
|
# cockpit-podman plugin — Cockpit serves an HTTPS admin console with a PAM LOGIN
|
||||||
|
# SCREEN on :9090, and cockpit-podman adds the "Podman containers" page for
|
||||||
|
# managing containers, images and pods. network:proxy publishes that console as
|
||||||
|
# https://podman.<domain> (internal, mgmt zone only).
|
||||||
|
#
|
||||||
|
# This runs on tappaas-cicd: it resolves the VM's IP via the Proxmox guest agent,
|
||||||
|
# then over SSH installs the packages from apt. Idempotent: it re-runs apt every
|
||||||
|
# time (apt is a no-op when already current) and records a version marker.
|
||||||
|
#
|
||||||
|
# Usage: update.sh <module-name>
|
||||||
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
. /home/tappaas/bin/common-install-routines.sh
|
||||||
|
|
||||||
|
MODULE="${1:-podman}"
|
||||||
|
readonly MGMT="mgmt"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
readonly MARKER="/etc/tappaas-podman.version" # on the VM: records installed podman version
|
||||||
|
|
||||||
|
VMNAME="$(get_config_value 'vmname' "${MODULE}")"
|
||||||
|
VMID="$(get_config_value 'vmid')"
|
||||||
|
[[ -n "${VMID}" && "${VMID}" != "null" ]] || die "no vmid for ${MODULE}"
|
||||||
|
|
||||||
|
# ── Locate the node hosting the VM (HA-safe) and resolve its IP ───────
|
||||||
|
PRIMARY="$(get_primary_node_fqdn 2>/dev/null || echo "tappaas1.${MGMT}.internal")"
|
||||||
|
NODE="$(ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PRIMARY}" \
|
||||||
|
"pvesh get /cluster/resources --type vm --output-format json 2>/dev/null" \
|
||||||
|
| jq -r --arg v "${VMID}" '.[] | select(.vmid==($v|tonumber)) | .node' 2>/dev/null | head -1)"
|
||||||
|
[[ -n "${NODE}" ]] || NODE="$(get_config_value 'node' "$(get_node_hostname 0)")"
|
||||||
|
[[ -n "${NODE}" ]] || die "could not locate the node hosting VM ${VMID}"
|
||||||
|
|
||||||
|
get_vm_ip() {
|
||||||
|
ssh -o BatchMode=yes "root@${NODE}.${MGMT}.internal" \
|
||||||
|
"qm guest cmd ${VMID} network-get-interfaces" 2>/dev/null \
|
||||||
|
| jq -r '.[] | select(.name | test("^lo$") | not) | ."ip-addresses"[]? | select(."ip-address-type"=="ipv4") | ."ip-address"' 2>/dev/null \
|
||||||
|
| grep -v '^127\.' | head -1
|
||||||
|
}
|
||||||
|
|
||||||
|
info "${BOLD}Installing Podman + Cockpit${CL} on ${VMNAME} (VM ${VMID}, node ${NODE})"
|
||||||
|
|
||||||
|
IP=""
|
||||||
|
for _ in $(seq 1 18); do IP="$(get_vm_ip)"; [[ -n "${IP}" ]] && break; sleep 10; done
|
||||||
|
[[ -n "${IP}" ]] || die "could not resolve ${VMNAME} IP via guest agent (is qemu-guest-agent up? templates:debian installs it)"
|
||||||
|
info " VM IP: ${IP}"
|
||||||
|
|
||||||
|
# SSH helper: run a command on the VM as the tappaas user.
|
||||||
|
vm() { ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 "tappaas@${IP}" "$@"; }
|
||||||
|
|
||||||
|
# Wait for SSH (cloud-init may still be finishing).
|
||||||
|
for _ in $(seq 1 40); do vm "exit 0" 2>/dev/null && break; sleep 3; done
|
||||||
|
vm "exit 0" 2>/dev/null || die "SSH to tappaas@${IP} not available"
|
||||||
|
|
||||||
|
# ── Friendly URL for the operator ────────────────────────────────────
|
||||||
|
DOMAIN="$(get_variant_config "" 2>/dev/null | jq -r '.domain // empty' || true)"
|
||||||
|
PROXY_DOMAIN="$(get_config_value 'proxyDomain' "${VMNAME}${DOMAIN:+.${DOMAIN}}")"
|
||||||
|
COCKPIT_DIRECT_URL="https://${IP}:9090"
|
||||||
|
if [[ -n "${PROXY_DOMAIN}" ]] && getent hosts "${PROXY_DOMAIN}" >/dev/null 2>&1; then
|
||||||
|
COCKPIT_URL="https://${PROXY_DOMAIN}"
|
||||||
|
else
|
||||||
|
[[ -n "${PROXY_DOMAIN}" ]] && info " ${PROXY_DOMAIN} does not resolve yet — using the direct URL"
|
||||||
|
COCKPIT_URL="${COCKPIT_DIRECT_URL}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 1. Install podman + cockpit + cockpit-podman (apt) ───────────────
|
||||||
|
# podman-compose/slirp4netns give rootless networking + compose-file support;
|
||||||
|
# cockpit + cockpit-podman provide the web console with a login screen on :9090.
|
||||||
|
info " Installing podman, podman-compose, cockpit, cockpit-podman (apt)..."
|
||||||
|
vm "sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq" || die "apt-get update failed"
|
||||||
|
vm "sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||||
|
podman podman-compose slirp4netns uidmap \
|
||||||
|
cockpit cockpit-podman \
|
||||||
|
curl ca-certificates" \
|
||||||
|
|| die "failed to install podman/cockpit packages"
|
||||||
|
|
||||||
|
PODMAN_VER="$(vm "podman --version 2>/dev/null | awk '{print \$3}'" || true)"
|
||||||
|
[[ -n "${PODMAN_VER}" ]] || die "podman did not install correctly (no version reported)"
|
||||||
|
info " podman version: ${PODMAN_VER}"
|
||||||
|
|
||||||
|
# ── 2. Enable podman socket + Cockpit web console ────────────────────
|
||||||
|
# Rootless podman API socket for the tappaas user (lets cockpit-podman talk to it
|
||||||
|
# without root), and the Cockpit HTTPS console on :9090.
|
||||||
|
info " Enabling the rootless podman socket + Cockpit web console..."
|
||||||
|
vm "systemctl --user enable --now podman.socket 2>/dev/null || true"
|
||||||
|
vm "sudo loginctl enable-linger tappaas 2>/dev/null || true" # keep the user socket alive after logout
|
||||||
|
vm "sudo systemctl enable --now cockpit.socket" || die "failed to enable cockpit.socket"
|
||||||
|
|
||||||
|
# Record version marker.
|
||||||
|
vm "echo '${PODMAN_VER}' | sudo tee ${MARKER} >/dev/null" || warn "could not write version marker"
|
||||||
|
|
||||||
|
# ── 3. Wait for the Cockpit console to answer (port 9090) ────────────
|
||||||
|
info " Waiting for the Cockpit console to come up (https://${IP}:9090)..."
|
||||||
|
UP=0
|
||||||
|
for _ in $(seq 1 18); do
|
||||||
|
code="$(vm "curl -fsk -o /dev/null -w '%{http_code}' https://localhost:9090/ 2>/dev/null" || echo 000)"
|
||||||
|
[[ "${code}" =~ ^(200|302|401|403)$ ]] && { UP=1; break; }
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [[ "${UP}" -eq 1 ]]; then
|
||||||
|
info "${GN}✓ Podman ${PODMAN_VER} + Cockpit console installed${CL}"
|
||||||
|
else
|
||||||
|
warn "Packages installed but the Cockpit console did not answer yet — it may still be starting."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
info "${BOLD}═══ Next steps ═══${CL}"
|
||||||
|
info " ${BOLD}Console:${CL} ${COCKPIT_URL} (direct: https://${IP}:9090)"
|
||||||
|
info " Log in with a Linux account on the VM. The cloud-init ${BOLD}tappaas${CL} user has"
|
||||||
|
info " SSH-key auth and no password, so set one first for the web login:"
|
||||||
|
info " ssh tappaas@${IP} 'sudo passwd tappaas'"
|
||||||
|
info " Then open the console and pick ${BOLD}Podman containers${CL} in the left menu."
|
||||||
|
info " See INSTALL.md for the details (and how to add a dedicated admin user instead)."
|
||||||
17
src/module-catalog.json
Normal file
17
src/module-catalog.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"description": "MakerFLOSS DevOps Module Registry — experimental TAPPaaS modules",
|
||||||
|
"foundationModules": [],
|
||||||
|
"applicationModules": [
|
||||||
|
{
|
||||||
|
"moduleName": "podman",
|
||||||
|
"repo": "community",
|
||||||
|
"moduleJson": "src/containers/podman/podman.json",
|
||||||
|
"vmid": 812,
|
||||||
|
"stack": "infrastructure",
|
||||||
|
"category": "containers",
|
||||||
|
"status": "incomplete"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"proxmoxTemplates": [],
|
||||||
|
"testModules": []
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue