From 8b981ecdeac08b0309a1f99f8e89256ebdc5d9ff Mon Sep 17 00:00:00 2001 From: Lars Rossen Date: Tue, 1 Sep 2026 22:14:54 +0200 Subject: [PATCH] feat(signage): scaffold bare Debian VM module New src/apps/signage: Debian 13 VM, 1 core / 1G / 16G (vmid 816), deps cluster:vm + templates:debian + backup:vm. update.sh confirms the VM is up and records a version marker; signage software layered on in a later pass. Registered in module-catalog.json. --- src/apps/signage/README.md | 26 ++++++++++++++ src/apps/signage/install.sh | 15 ++++++++ src/apps/signage/signage.json | 29 +++++++++++++++ src/apps/signage/test.sh | 66 +++++++++++++++++++++++++++++++++++ src/apps/signage/update.sh | 58 ++++++++++++++++++++++++++++++ src/module-catalog.json | 9 +++++ 6 files changed, 203 insertions(+) create mode 100644 src/apps/signage/README.md create mode 100755 src/apps/signage/install.sh create mode 100644 src/apps/signage/signage.json create mode 100755 src/apps/signage/test.sh create mode 100755 src/apps/signage/update.sh diff --git a/src/apps/signage/README.md b/src/apps/signage/README.md new file mode 100644 index 0000000..ec01123 --- /dev/null +++ b/src/apps/signage/README.md @@ -0,0 +1,26 @@ +# signage — digital signage host + +A Debian 13 VM intended to run digital-signage software. **Current state: bare +VM scaffold** — the VM shape and module plumbing are in place; the signage +service itself is not chosen/installed yet. + +| | | +|---|---| +| VM | 1 core · 1024 MB · 16 G disk · `tanka1` · `lan` bridge | +| Image | `debian-13-generic-amd64.qcow2` (cloud-init) | +| vmid | 816 | +| Depends on | `cluster:vm`, `templates:debian`, `backup:vm` | +| Target env | `omStaging` (installs as `signage-omStaging`) | + +## Files + +- `signage.json` — module config (VM shape + dependencies) +- `install.sh` — thin wrapper → `update.sh` +- `update.sh` — confirms the VM is up, records a version marker (no service yet) +- `test.sh` — checks the VM resolves, is reachable, and carries the marker + +## Next steps + +Pick the signage software and extend `update.sh`/`test.sh` accordingly. If it +exposes a web UI, add `network:proxy` (and `identity:identity` for OIDC login) +to `dependsOn` and a matching `config` block. diff --git a/src/apps/signage/install.sh b/src/apps/signage/install.sh new file mode 100755 index 0000000..c86c34d --- /dev/null +++ b/src/apps/signage/install.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# +# signage module install — thin wrapper. +# +# The VM is created by the cluster:vm provider (Debian 13 cloud image) and +# OS-prepped by templates:debian. This script applies the module-specific step; +# all real work lives in update.sh (install == update for this module). +# +# Usage: install.sh +# + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec "${SCRIPT_DIR}/update.sh" "$@" diff --git a/src/apps/signage/signage.json b/src/apps/signage/signage.json new file mode 100644 index 0000000..6026b37 --- /dev/null +++ b/src/apps/signage/signage.json @@ -0,0 +1,29 @@ +{ + "description": "Digital signage host — bare Debian VM scaffold (signage software TBD)", + "version": "0.0.1", + "appVersion": "TBD", + "releaseDate": "2026-09-01", + "maintainer": "@larsrossen", + "status": "Development", + "vmname": "signage", + "vmid": 816, + "vmtag": "TAPPaaS,MakerFLOSS", + "ports": [], + "dependsOn": ["cluster:vm", "templates:debian", "backup:vm"], + "provides": [], + "config": { + "cluster:vm": { + "bios": "seabios", + "ostype": "l26", + "cores": 1, + "memory": "1024", + "diskSize": "16G", + "storage": "tanka1", + "imageType": "img", + "image": "debian-13-generic-amd64.qcow2", + "imageLocation": "https://cdimage.debian.org/cdimage/cloud/trixie/latest", + "cloudInit": "true", + "bridge0": "lan" + } + } +} diff --git a/src/apps/signage/test.sh b/src/apps/signage/test.sh new file mode 100755 index 0000000..61cd005 --- /dev/null +++ b/src/apps/signage/test.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# +# signage module test — health checks for the bare Debian VM. +# +# Verifies (from tappaas-cicd, over the Proxmox guest agent + SSH): +# - the VM's IP resolves via the guest agent +# - the VM is reachable over SSH +# - the version marker written by update.sh is present +# +# As the signage software is layered on later, add its checks here. +# +# Usage: test.sh +# + +set -uo pipefail + +. /home/tappaas/bin/common-install-routines.sh + +MODULE="${1:-signage}" +readonly MGMT="mgmt" +readonly MARKER="/etc/tappaas-signage.version" +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}signage 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}" "$@"; } + +if vm "true"; then + ok "VM reachable over SSH" +else + no "VM not reachable over SSH" +fi + +if vm "test -f ${MARKER}"; then + ok "version marker present ($(vm "cat ${MARKER} 2>/dev/null" | tr -d '\r'))" +else + no "version marker ${MARKER} missing (update.sh did not run?)" +fi + +echo "" +info "Result: ${PASS} passed, ${FAIL} failed" +[[ "${FAIL}" -eq 0 ]] diff --git a/src/apps/signage/update.sh b/src/apps/signage/update.sh new file mode 100755 index 0000000..9f2d1b8 --- /dev/null +++ b/src/apps/signage/update.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# +# signage module update — a bare Debian VM (scaffold). +# +# The VM is created by the cluster:vm provider (Debian 13 cloud image) and +# OS-prepped by templates:debian. This module currently only confirms the VM is +# up and records a version marker — the signage software will be layered on in a +# later pass. install == update for this module. +# +# Runs on tappaas-cicd: resolves the VM's IP via the Proxmox guest agent, then +# drives the VM over SSH. Idempotent. +# +# Usage: update.sh +# + +set -euo pipefail + +. /home/tappaas/bin/common-install-routines.sh + +MODULE="${1:-signage}" +readonly MGMT="mgmt" +readonly MARKER="/etc/tappaas-signage.version" # on the VM: module version we applied + +VMNAME="$(get_config_value 'vmname' "${MODULE}")" +VMID="$(get_config_value 'vmid')" +VERSION="$(get_config_value 'version' '0.0.1')" +[[ -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}Updating signage${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 an IPv4 for VM ${VMID} via the guest agent" +info " VM IP: ${IP}" + +vm() { ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 "tappaas@${IP}" "$@"; } + +# Confirm SSH is up and record the version marker. Nothing else to install yet — +# this is a bare Debian scaffold; the signage service comes in a later pass. +vm "true" || die "VM ${VMNAME} (${IP}) not reachable over SSH" +vm "echo '${VERSION}' | sudo tee ${MARKER} >/dev/null" || warn "could not write version marker" + +info " ${GN}✓${CL} signage VM is up (bare Debian scaffold — no service installed yet)" diff --git a/src/module-catalog.json b/src/module-catalog.json index fa3f55c..e96f5e7 100644 --- a/src/module-catalog.json +++ b/src/module-catalog.json @@ -19,6 +19,15 @@ "stack": "infrastructure", "category": "containers", "status": "incomplete" + }, + { + "moduleName": "signage", + "repo": "community", + "moduleJson": "src/apps/signage/signage.json", + "vmid": 816, + "stack": "application", + "category": "signage", + "status": "incomplete" } ], "proxmoxTemplates": [],