internal/service/cluster/provisioner/README.md
The provisioner package manages per-instance MariaDB schemas and users for cluster deployments. It derives deterministic identifiers from the cluster UUID and technical node name (NodeName) using a configurable prefix (default cluster_), creates or rotates credentials via the admin DSN, and exposes helpers (EnsureCredentials, DropCredentials, GenerateCredentials) that API and CLI layers can reuse when onboarding or rotating instances.
database.go. The admin connection string is ProvisionDSN (default root:photoprism@tcp(mariadb:4001)/photoprism). Query parameters are optional when configuring the portal flag/env (database-provision-dsn), for example charset=utf8mb4,utf8&collation=utf8mb4_unicode_ci&parseTime=true&timeout=15s.EnsureCredentials accepts the technical node UUID/name identifiers, creates the schema if needed, and returns credentials plus rotation metadata. DropCredentials revokes grants, drops the user, and removes the schema. Both functions require a context; prefer context.WithTimeout in callers.GenerateCredentials. Call it instead of handcrafting database or user names so tests, CLI, and API stay aligned. The resulting identifiers follow <prefix>d<hmac11> for schemas and <prefix>u<hmac11> for users. Portal deployments may override the prefix via the database-provision-prefix flag; defaults are cluster_d… / cluster_u….go test ./internal/service/cluster/provisioner -count=1 for both unit coverage and the lightweight MariaDB integration checks. No environment variables are required; tests connect to the static ProvisionDSN and will skip themselves only if that connection is unavailable.EnsureCredentials, register a t.Cleanup callback to invoke DropCredentials. Example:
creds, _, err := provisioner.EnsureCredentials(ctx, conf, nodeUUID, nodeName, true)
require.NoError(t, err)
t.Cleanup(func() {
dropCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
require.NoError(t, provisioner.DropCredentials(dropCtx, creds.Name, creds.User))
})
GenerateCredentials(conf, uuid, name) before scheduling cleanup.mariadb (already configured to reach mariadb:4001). Common snippets:
cat <<'SQL' | mariadb
SHOW DATABASES LIKE 'cluster_d%'; -- adjust prefix if database-provision-prefix overrides the default
SQL
cat <<'SQL' | mariadb
SELECT User, Host FROM mysql.user WHERE User LIKE 'cluster_u%'; -- adjust prefix if needed
SQL
for db in $(cat <<'SQL' | mariadb --batch --skip-column-names
SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE 'cluster_d%';
SQL
); do
printf 'DROP DATABASE IF EXISTS `%s`;\\n' "$db" | mariadb
done
for user in $(cat <<'SQL' | mariadb --batch --skip-column-names
SELECT User FROM mysql.user WHERE User LIKE 'cluster_u%';
SQL
); do
cat <<SQL | mariadb
DROP USER IF EXISTS '$user'@'%';
SQL
done
t.Cleanup runs, and rerun the suite after manual cleanup to confirm the fix.DropCredentials inside t.Cleanup for tests and defer blocks for ad-hoc scripts.NodeUUID/NodeName) from the response and call GenerateCredentials to identify which schema/user to drop once finished.SHOW DATABASES LIKE 'cluster_d%'; and SELECT User FROM mysql.user WHERE User LIKE 'cluster_u%'; to verify the MariaDB instance is clean.go test ./internal/service/cluster/provisioner -count=1go test ./internal/api -run 'ClusterNodesRegister' -count=1 (ensures the API cleanup helper stays aligned with the provisioner)Use ProxySQL to verify instance provisioning stays in sync with the proxy in addition to MariaDB. The unit test suite ships with an opt-in integration test (TestEnsureCredentials_ProxySQLIntegration) that exercises the full flow once ProxySQL is available locally.
cd /tmp
curl -fL -o proxysql_3.0.2-debian12_amd64.deb https://github.com/sysown/proxysql/releases/download/v3.0.2/proxysql_3.0.2-debian12_amd64.deb
sudo dpkg -i proxysql_3.0.2-debian12_amd64.deb
admin:admin):
sudo proxysql --config /etc/proxysql.cnf --pidfile /tmp/proxysql.pid --daemon
sudo mysql --protocol=TCP --host=127.0.0.1 --port=6032 --user=admin --password=admin -e 'SELECT 1'
The bundled MariaDB instance (credentials in .my.cnf at the repo root) is sufficient as a backend; no extra ProxySQL configuration is required for the integration test.
PHOTOPRISM_TEST_PROXYSQL=1 go test ./internal/service/cluster/provisioner -run TestEnsureCredentials_ProxySQLIntegration -count=1
PHOTOPRISM_TEST_PROXYSQL_DSN=user:pass@tcp(host:6032)/ if you changed the default credentials or port.mysql_users row, reruns the idempotent ensure path, and exercises DropCredentials. Cleanup hooks remove both the MariaDB schema/user and the ProxySQL account.sudo kill "$(cat /tmp/proxysql.pid)"
/var/lib/proxysql.