mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-09-21 18:39:05 +02:00
feat: add FRANCIS_HOST to connect to a standalone Francis runtime
FRANCIS_HOST decides where the Francis actor runtime lives. When set to "embedded" (the default), Pocket ID starts the runtime inside its own process. Any other value is the address, or a comma-separated list of addresses, of a standalone Francis runtime. Pocket ID then connects to it as a remote actor host and starts no embedded runtime. Because when using a remote runtime, it's likewise not possible to enforce a single instance of Pocket ID is running at once, the env vars currently have the `EXPERIMENTAL_` prefix, are **undocumented**, and show a warning if used. Notes: - Connecting to a standalone runtime also needs FRANCIS_HOST_PSK or FRANCIS_HOST_JWT_FILE, and optionally (but recommended) FRANCIS_CA. - When connecting to a remote runtime, exporting Pocket ID data does not include the actor state, which will need to be backed up and restored separately
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# This Docker Compose file is used to set up the environment for the tests.
|
||||
# It's the variant where Pocket ID does not embed the Francis actor runtime, but connects to a standalone one instead.
|
||||
services:
|
||||
lldap:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: lldap
|
||||
scim-test-server:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: scim-test-server
|
||||
francis-runtime:
|
||||
# The version comes from the Francis dependency in backend/go.mod, so the runtime always matches the client Pocket ID is built with
|
||||
# Set it with: echo "FRANCIS_VERSION=$(./francis-version.sh)" > .env
|
||||
image: ghcr.io/italypaleale/francis:${FRANCIS_VERSION:?run ./francis-version.sh to set it, see CONTRIBUTING.md}
|
||||
volumes:
|
||||
- ./francis-config.yaml:/etc/francis/config.yaml:ro
|
||||
- francis-test-data:/data
|
||||
# The image ships its own HEALTHCHECK, which probes the runtime over the loopback
|
||||
# It's repeated here so Pocket ID can wait on it, and so a runtime that never comes up fails fast instead of after the default retries
|
||||
healthcheck:
|
||||
test: ["CMD", "/bin/francis", "healthcheck"]
|
||||
interval: 2s
|
||||
timeout: 5s
|
||||
retries: 15
|
||||
start_period: 5s
|
||||
pocket-id:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: pocket-id
|
||||
environment:
|
||||
APP_ENV: test
|
||||
ENCRYPTION_KEY: test-encryption-key
|
||||
FILE_BACKEND: ${FILE_BACKEND}
|
||||
# The runtime's port is UDP, since WebTransport runs over HTTP/3
|
||||
FRANCIS_HOST: francis-runtime:7400
|
||||
# Must match "bootstrap.hostPSK" in francis-config.yaml
|
||||
FRANCIS_HOST_PSK: e2e-host-bootstrap-psk-0123456789
|
||||
# FRANCIS_CA is intentionally unset, so this exercises the same trust-on-first-use path an operator gets without it
|
||||
# The cluster only exists inside this Compose network for the duration of the tests
|
||||
#
|
||||
# Peers reach actors placed on this host at ACTORS_HOST, which the runtime hands out, so it has to be the address other containers resolve rather than the default wildcard
|
||||
ACTORS_HOST: pocket-id
|
||||
depends_on:
|
||||
francis-runtime:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
pocket-id-test-data:
|
||||
francis-test-data:
|
||||
@@ -0,0 +1,26 @@
|
||||
# Configuration for the standalone Francis runtime used by the "remote Francis" E2E variant.
|
||||
# In that variant Pocket ID does not embed the actor runtime: it connects to this one instead, which owns the actor state, placement, and alarms.
|
||||
# These secrets are fixed test values and must match the FRANCIS_HOST_PSK passed to Pocket ID in docker-compose-francis.yml.
|
||||
|
||||
# The WebTransport server runs over HTTP/3, so this port is UDP
|
||||
bind: "0.0.0.0:7400"
|
||||
|
||||
# The runtime PSKs derive the cluster CA that signs every workload certificate
|
||||
runtimePSKs:
|
||||
- "e2e-runtime-psk-0123456789abcdef"
|
||||
|
||||
# Hosts prove they may join by presenting this pre-shared key
|
||||
bootstrap:
|
||||
method: psk
|
||||
hostPSK: "e2e-host-bootstrap-psk-0123456789"
|
||||
|
||||
# The runtime owns its own SQLite store, which is separate from Pocket ID's database
|
||||
# It lives on a volume because the image runs as a non-root user that cannot write to the image filesystem
|
||||
provider:
|
||||
connectionString: "/data/francis.db"
|
||||
|
||||
# A single Pocket ID replica joins the cluster, matching the cap the embedded runtime applies when HA is off
|
||||
maxHosts: 1
|
||||
|
||||
log:
|
||||
level: debug
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
# Prints the Francis version Pocket ID depends on, formatted as the tag of the runtime's container image.
|
||||
#
|
||||
# The Go module is the single source of truth for the version: the standalone runtime the E2E tests run against
|
||||
# has to be the same version as the client Pocket ID is built with, and pinning it in two places lets them drift.
|
||||
#
|
||||
# Usage, from this directory:
|
||||
# echo "FRANCIS_VERSION=$(./francis-version.sh)" > .env
|
||||
#
|
||||
# This is a plain shell script rather than "go list" because the E2E workflow does not set up Go.
|
||||
set -eu
|
||||
|
||||
go_mod="$(CDPATH='' cd -- "$(dirname -- "$0")/../../backend" && pwd)/go.mod"
|
||||
|
||||
if [ ! -f "$go_mod" ]; then
|
||||
echo "francis-version.sh: cannot find $go_mod" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Module versions carry a leading "v" that container tags do not, so it is stripped
|
||||
version=$(sed -n 's|^[[:space:]]*github\.com/italypaleale/francis v\([^[:space:]]*\).*|\1|p' "$go_mod" | head -n 1)
|
||||
|
||||
if [ -z "$version" ]; then
|
||||
echo "francis-version.sh: no github.com/italypaleale/francis requirement found in $go_mod" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$version"
|
||||
Reference in New Issue
Block a user