feat(ops): systemd units + smoke test

This commit is contained in:
Marco Allegretti 2026-02-24 14:02:04 +01:00
parent cfa74f214c
commit 8007433d5f
5 changed files with 184 additions and 8 deletions

View file

@ -68,6 +68,22 @@ For VPS deployments, it is recommended to bind demo ports to localhost and put a
- Point `API_BASE` at your public domain (for example `https://openlikwid.org`) - Point `API_BASE` at your public domain (for example `https://openlikwid.org`)
- Serve the demo via the reverse proxy on `80/443` - Serve the demo via the reverse proxy on `80/443`
## Start on boot (systemd --user)
This repo includes `systemd --user` unit templates:
- `systemd/likwid-demo.service`
- `systemd/likwid-prod.service`
These units assume the repo is checked out at `~/likwid` for the target user, and they run `podman compose` using the env files under `compose/`.
## Smoke test
An operator smoke test script is provided:
- `scripts/smoke-test.sh demo`
- `scripts/smoke-test.sh prod`
## Demo Instance Details ## Demo Instance Details
### Demo Accounts ### Demo Accounts

View file

@ -44,26 +44,26 @@ If your Podman build does not provide `podman compose`, install the compose inte
git clone https://codeberg.org/likwid/likwid.git ~/likwid git clone https://codeberg.org/likwid/likwid.git ~/likwid
``` ```
2. Create the production env file: 1. Create the production env file:
```bash ```bash
cp ~/likwid/compose/.env.production.example ~/likwid/compose/.env.production cp ~/likwid/compose/.env.production.example ~/likwid/compose/.env.production
``` ```
3. Edit `~/likwid/compose/.env.production`: 1. Edit `~/likwid/compose/.env.production`:
- `POSTGRES_PASSWORD` - `POSTGRES_PASSWORD`
- `JWT_SECRET` - `JWT_SECRET`
- `API_BASE` (should be your public URL, e.g. `https://your.domain`) - `API_BASE` (should be your public URL, e.g. `https://your.domain`)
4. Start services: 1. Start services:
```bash ```bash
cd ~/likwid cd ~/likwid
podman compose --env-file compose/.env.production -f compose/production.yml up -d --build podman compose --env-file compose/.env.production -f compose/production.yml up -d --build
``` ```
5. Create the first admin and complete setup: 1. Create the first admin and complete setup:
- Register the first user at `/register` (first user becomes platform admin) - Register the first user at `/register` (first user becomes platform admin)
- Complete `/setup` - Complete `/setup`
@ -139,10 +139,12 @@ Podman is most reliable on openSUSE when managed as a rootless user service.
sudo loginctl enable-linger deploy sudo loginctl enable-linger deploy
``` ```
2. Create a systemd user unit: 1. Create a systemd user unit:
- File: `~/.config/systemd/user/likwid-demo.service` - File: `~/.config/systemd/user/likwid-demo.service`
- Template: `systemd/likwid-demo.service` (from this repo)
```ini ```ini
[Unit] [Unit]
Description=Likwid demo (podman compose) Description=Likwid demo (podman compose)
@ -153,7 +155,7 @@ After=network-online.target
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
WorkingDirectory=%h/likwid WorkingDirectory=%h/likwid
ExecStart=/usr/bin/podman compose --env-file compose/.env.demo -f compose/demo.yml -f compose/demo.vps.override.yml up -d --build ExecStart=/usr/bin/podman compose --env-file compose/.env.demo -f compose/demo.yml -f compose/demo.vps.override.yml up -d
ExecStop=/usr/bin/podman compose --env-file compose/.env.demo -f compose/demo.yml -f compose/demo.vps.override.yml down ExecStop=/usr/bin/podman compose --env-file compose/.env.demo -f compose/demo.yml -f compose/demo.vps.override.yml down
TimeoutStartSec=0 TimeoutStartSec=0
@ -161,17 +163,23 @@ TimeoutStartSec=0
WantedBy=default.target WantedBy=default.target
``` ```
3. Enable and start it: 1. Enable and start it:
```bash ```bash
systemctl --user daemon-reload systemctl --user daemon-reload
systemctl --user enable --now likwid-demo.service systemctl --user enable --now likwid-demo.service
``` ```
4. Inspect service logs: 1. Inspect service logs:
```bash ```bash
journalctl --user -u likwid-demo.service -f journalctl --user -u likwid-demo.service -f
``` ```
For production, create a separate unit (for example `likwid-prod.service`) with the production env file and compose file. For production, create a separate unit (for example `likwid-prod.service`) with the production env file and compose file.
## Smoke test
After deploy/update, run:
- `./scripts/smoke-test.sh demo`

122
scripts/smoke-test.sh Executable file
View file

@ -0,0 +1,122 @@
#!/bin/bash
set -euo pipefail
MODE="${1:-}"
if [ -z "$MODE" ] || { [ "$MODE" != "demo" ] && [ "$MODE" != "prod" ] && [ "$MODE" != "production" ]; }; then
echo "Usage: ./scripts/smoke-test.sh <demo|prod>" >&2
exit 2
fi
if [ "$MODE" = "production" ]; then
MODE="prod"
fi
if ! command -v curl >/dev/null 2>&1; then
echo "curl is required" >&2
exit 1
fi
if ! command -v podman >/dev/null 2>&1; then
echo "podman is required" >&2
exit 1
fi
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [ "$MODE" = "demo" ]; then
ENV_FILE="$ROOT_DIR/compose/.env.demo"
BACKEND_CONTAINER="likwid-demo-backend"
FRONTEND_CONTAINER="likwid-demo-frontend"
DB_CONTAINER="likwid-demo-db"
else
ENV_FILE="$ROOT_DIR/compose/.env.production"
BACKEND_CONTAINER="likwid-prod-backend"
FRONTEND_CONTAINER="likwid-prod-frontend"
DB_CONTAINER="likwid-prod-db"
fi
if [ ! -f "$ENV_FILE" ]; then
echo "Missing env file: $ENV_FILE" >&2
exit 1
fi
set -a
# shellcheck disable=SC1090
source <(sed 's/\r$//' "$ENV_FILE")
set +a
BACKEND_PORT="${BACKEND_PORT:-3000}"
FRONTEND_PORT="${FRONTEND_PORT:-4321}"
POSTGRES_USER="${POSTGRES_USER:-likwid}"
POSTGRES_DB="${POSTGRES_DB:-likwid_prod}"
if [ "$MODE" = "demo" ]; then
BACKEND_PORT="${BACKEND_PORT:-3001}"
FRONTEND_PORT="${FRONTEND_PORT:-4322}"
POSTGRES_USER="${POSTGRES_USER:-likwid_demo}"
POSTGRES_DB="${POSTGRES_DB:-likwid_demo}"
fi
BACKEND_HEALTH_URL="http://127.0.0.1:${BACKEND_PORT}/health"
FRONTEND_URL="http://127.0.0.1:${FRONTEND_PORT}/"
fail() {
echo "FAIL: $1" >&2
exit 1
}
ok() {
echo "OK: $1"
}
echo "=== Likwid smoke test ($MODE) ==="
echo "[1/4] Containers present"
podman container exists "$BACKEND_CONTAINER" || fail "Missing container: $BACKEND_CONTAINER"
podman container exists "$FRONTEND_CONTAINER" || fail "Missing container: $FRONTEND_CONTAINER"
podman container exists "$DB_CONTAINER" || fail "Missing container: $DB_CONTAINER"
backend_running="$(podman inspect -f '{{.State.Running}}' "$BACKEND_CONTAINER" 2>/dev/null || true)"
frontend_running="$(podman inspect -f '{{.State.Running}}' "$FRONTEND_CONTAINER" 2>/dev/null || true)"
db_running="$(podman inspect -f '{{.State.Running}}' "$DB_CONTAINER" 2>/dev/null || true)"
[ "$backend_running" = "true" ] || fail "$BACKEND_CONTAINER is not running"
[ "$frontend_running" = "true" ] || fail "$FRONTEND_CONTAINER is not running"
[ "$db_running" = "true" ] || fail "$DB_CONTAINER is not running"
ok "containers running"
echo "[2/4] Backend health: $BACKEND_HEALTH_URL"
health_json="$(curl -fsS "$BACKEND_HEALTH_URL")" || fail "backend health check failed"
echo "$health_json"
ok "backend /health"
echo "[3/4] Frontend reachable: $FRONTEND_URL"
http_code="$(curl -fsS -o /dev/null -w "%{http_code}" "$FRONTEND_URL" || true)"
case "$http_code" in
2*|3*) ok "frontend HTTP $http_code";;
*) fail "frontend not reachable (HTTP $http_code)";;
esac
echo "[4/4] Database reachable + migrations"
podman exec "$DB_CONTAINER" pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB" >/dev/null || fail "pg_isready failed"
migration_table_check="$(podman exec "$DB_CONTAINER" psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -tAc "SELECT to_regclass('_sqlx_migrations');" | tr -d '[:space:]')"
if [ "$migration_table_check" != "_sqlx_migrations" ]; then
fail "_sqlx_migrations table not found"
fi
failed_count="$(podman exec "$DB_CONTAINER" psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -tAc "SELECT COUNT(*) FROM _sqlx_migrations WHERE success = false;" | tr -d '[:space:]')"
if [ "$failed_count" != "0" ]; then
fail "found failed migrations: $failed_count"
fi
installed_count="$(podman exec "$DB_CONTAINER" psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -tAc "SELECT COUNT(*) FROM _sqlx_migrations;" | tr -d '[:space:]')"
if [ -z "$installed_count" ] || [ "$installed_count" -lt 1 ]; then
fail "no migrations recorded"
fi
ok "db reachable, migrations applied ($installed_count)"
echo "=== Smoke test passed ==="

View file

@ -0,0 +1,15 @@
[Unit]
Description=Likwid demo (podman compose)
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=%h/likwid
ExecStart=/usr/bin/podman compose --env-file compose/.env.demo -f compose/demo.yml -f compose/demo.vps.override.yml up -d
ExecStop=/usr/bin/podman compose --env-file compose/.env.demo -f compose/demo.yml -f compose/demo.vps.override.yml down
TimeoutStartSec=0
[Install]
WantedBy=default.target

View file

@ -0,0 +1,15 @@
[Unit]
Description=Likwid production (podman compose)
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=%h/likwid
ExecStart=/usr/bin/podman compose --env-file compose/.env.production -f compose/production.yml up -d
ExecStop=/usr/bin/podman compose --env-file compose/.env.production -f compose/production.yml down
TimeoutStartSec=0
[Install]
WantedBy=default.target