Compare commits

..

2 Commits

Author SHA1 Message Date
Niklas Ye a314083fa9 Set the chart's placeholder version to 0.46.1
CI / test (push) Successful in 21s
CI / chart (push) Successful in 1s
CI / security (push) Successful in 35s
Release / test (push) Successful in 7s
Release / chart (push) Successful in 2s
Release / binaries (push) Successful in 45s
Release / image (push) Successful in 1m25s
Release / scan-image (push) Successful in 2s
Cosmetic: make helm-package passes --version and --app-version from the tag,
so these fields decide nothing about what is published. They are kept in
step so the tree does not state an older number than the release it heads
for, as in 81197a3 and 6292ad6.

Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
2026-10-13 11:49:05 +02:00
Niklas Ye c28a9b7b49 Let the bootstrap hook wait for a database that is slow to start
The server exits when the database does not answer within about ten
seconds, so while the database is down the pod crash-loops with a restart
backoff that grows to five minutes, and /healthz only answers after it has
connected. The hook gave up after a fixed two minutes per attempt, and Flux's
default five-minute timeout came first anyway. A failed hook fails the
release and nothing runs it again until the next upgrade, so a database that
was merely slow left the install without an administrator.

Each attempt now waits bootstrap.waitSeconds (default 600), logging progress,
and the Job gets an activeDeadlineSeconds derived from it so a database that
never comes up still ends the hook. No change to the binary, API or schema.

Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
2026-10-13 11:49:05 +02:00
4 changed files with 31 additions and 12 deletions
+2 -2
View File
@@ -15,5 +15,5 @@ type: application
# appVersion and image.tag in values.yaml no longer agree, and that is not an oversight:
# image.tag stays "latest", which is what a local install actually pulls. appVersion is
# metadata and drives nothing.
version: 0.46.0
appVersion: "v0.46.0"
version: 0.46.1
appVersion: "v0.46.1"
@@ -13,6 +13,10 @@ metadata:
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
backoffLimit: 3
# Bounds the whole Job, retries included, so a database that never comes up ends the
# hook instead of leaving it pending. Each attempt waits up to bootstrap.waitSeconds
# for the server, and the allowance covers every attempt plus the pod backoff between.
activeDeadlineSeconds: {{ add (mul (int .Values.bootstrap.waitSeconds) 4) 300 }}
template:
metadata:
labels:
@@ -66,18 +70,26 @@ spec:
BODY=$(echo "$_resp" | sed '$d')
}
echo "Waiting for terdut-server to be ready..."
RETRIES=60
while [ "$RETRIES" -gt 0 ]; do
curl -sf "$SERVICE_URL/healthz" > /dev/null 2>&1 && break
RETRIES=$((RETRIES - 1))
# The server exits when the database does not answer within ~10 seconds, so
# while the database is down the pod crash-loops, with a restart backoff that
# grows to five minutes, and /healthz only answers once it has connected. Wait
# for that for as long as bootstrap.waitSeconds allows, not a fixed two minutes:
# a failed hook fails the release and nothing runs it again until the next
# upgrade, so a database that was merely slow would leave no administrator.
WAIT={{ int .Values.bootstrap.waitSeconds }}
echo "Waiting up to ${WAIT}s for terdut-server to be ready (it needs the database)..."
WAITED=0
while ! curl -sf "$SERVICE_URL/healthz" > /dev/null 2>&1; do
if [ "$WAITED" -ge "$WAIT" ]; then
echo "Timed out after ${WAITED}s waiting for the server to be ready."
echo "Check the database and the terdut-server pod's logs, then run helm upgrade again."
exit 1
fi
[ $((WAITED % 30)) -eq 0 ] && [ "$WAITED" -gt 0 ] && echo "Still waiting (${WAITED}s)..."
sleep 2
WAITED=$((WAITED + 2))
done
if [ "$RETRIES" -eq 0 ]; then
echo "Timed out waiting for server to be ready."
exit 1
fi
echo "Server is ready."
echo "Server is ready after ${WAITED}s."
# The Secret is the source of truth for the administrator's password: reuse
# the one it holds, so recreating the database brings the same account back.
+6
View File
@@ -167,6 +167,12 @@ bootstrap:
enabled: true
username: admin
email: admin@example.com
# How long each attempt waits for the server to answer /healthz, which it only does once
# the database is reachable. Raise it when the database starts after the release (a fresh
# cluster, a database operator) -- and keep Flux's HelmRelease spec.timeout / helm
# --timeout above it, because Helm waits for this hook and fails the release when its own
# timeout comes first (the Job still carries on and can finish the bootstrap).
waitSeconds: 600
# secretName overrides the default of <fullname>-admin-key. It holds username, password and
# api-key. The password is generated once and then reused: delete the Secret and the
# database to start over.
+1
View File
@@ -36,6 +36,7 @@ than an `Ingress`. TLS is terminated at the gateway, so the server itself never
| `networking.listener` | `""` | Gateway listener (`sectionName`) to bind to. Empty attaches to every matching listener, **including plaintext HTTP** — set it to the HTTPS listener's name to serve TLS only |
| `networking.servicePort` | `8080` | Port the route forwards to; keep in sync with `service.port` |
| `bootstrap.enabled` | `true` | Runs a post-install/upgrade hook that creates the first administrator with a generated password and stores `username`, `password` and `api-key` in the `<release>-admin-key` Secret. The password is generated once and reused, so recreating the database brings the same account back. Already-bootstrapped servers are left alone. Set `false` to create the first user yourself with `POST /api/bootstrap` |
| `bootstrap.waitSeconds` | `600` | How long each attempt of the hook waits for the server to answer `/healthz`, which it does only once the database is reachable. Keep Helm's `--timeout` (Flux `spec.timeout`) above it; if the timeout comes first the release is marked failed, but the Job keeps running and can still create the administrator |
| `bootstrap.username` / `bootstrap.email` | `admin` / `admin@example.com` | Account the hook creates |
| `bootstrap.secretName` | `""` | Secret to keep the credentials in; empty means `<release>-admin-key` |
| `database.dsn` | `""` | **Required.** Postgres DSN, with no password in it. The chart provisions no database |