mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-03-26 19:03:08 +00:00
feat(ops): systemd units + smoke test
This commit is contained in:
parent
cfa74f214c
commit
8007433d5f
5 changed files with 184 additions and 8 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
122
scripts/smoke-test.sh
Executable 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 ==="
|
||||||
15
systemd/likwid-demo.service
Normal file
15
systemd/likwid-demo.service
Normal 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
|
||||||
15
systemd/likwid-prod.service
Normal file
15
systemd/likwid-prod.service
Normal 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
|
||||||
Loading…
Reference in a new issue