Auto-provision the per-domain PDS instance in the domain saga
closedProblem
pdsRegistryProvisioner.EnsureActive (router.go:454-474) only looks up an
existing ACTIVE ZDS registry record and fails loudly if absent (“onboard the
ZDS instance before creating the domain”). The saga therefore cannot create a
domain end-to-end without an operator first standing up the per-domain PDS.
Deliverable
A saga provisioning adapter that calls pdslifecycle.Supervisor.Activate
(internal/pdslifecycle/supervisor.go:245) as the saga’s PDS step: allocate
slug/port, render the instance env, start the zds@ unit, and reload the Caddy
maps. It must be idempotent on retry (reuse an already-active record) and keep
the existing rollback posture: Activate rolls itself back on its own failure,
and the saga deliberately does not auto-Retire a healthy instance on a
later-step failure (that would burn slug/port tombstones).
Acceptance
domain.createprovisions the PDS instance with no operator pre-step.- A crash/retry between PDS activation and the DB insert reuses the instance and converges.
- Tests cover: activate-on-create, reuse-on-retry, activate failure compensation, and the “later step fails → instance left healthy” rule.
2 Comments
Auto-provision the per-domain PDS instance in the domain saga
Problem
pdsRegistryProvisioner(router.go) only looks up an existing ACTIVE ZDS registry record and fails if absent (“onboard the ZDS instance before creating the domain”). Sodomain.createcannot complete end-to-end; an operator must pre-create the per-domain PDS.pdslifecycle.Supervisor.Activate(supervisor.go:245) already implements the full onboarding (allocate slug+port → render env → enable/start → Caddy maps → health gate → active, self-rolling-back on failure). It just isn’t wired to the saga.Design
pds.origin_suffix(envSOVRN_PDS_ORIGIN_SUFFIX, toml[pds] origin_suffix). A cell sets e.g..pds1.eu.sovrn.at; the per-domain origin ispdslifecycle.SlugForDomain(domain) + origin_suffix. Empty suffix keeps the existing lookup-only behavior (dev/test; no silent activation).pdslifecycle.ActivationProvisioner{Supervisor, Secrets, OriginSuffix}implementing the saga’s PDS seam:(Origin, Port)(no mutation);retiring→Supervisor.Activatewith Origin/ServerDID/ HandleDomains/Secrets + the statics already onSupervisor.Config; return the active record;OriginSuffix→ the old lookup-only error, with a message naming the config key (so prod misconfig is obvious).Registry.Allocatereclaims aretiringrow (same slug/port).PDSProvisioner.EnsureActive→EnsureProvisioned(ctx, domain) (origin, port, err); update the saga call andfakePDS.*pdslifecycle.SupervisorinsagaWiringand reuse it for the orphan sweep (single registry handle + one mutex), so concurrent Activate/Retire cannot interleave.Activaterolls itself back on its own failure; the saga deliberately never auto-Retires a healthy instance.Tasks
pds.origin_suffix(+ bindEnv, toml, Ansible var, test).pdslifecycle.OriginForDomain(domain, suffix)helper (+ test).pdslifecycle.ActivationProvisioner(+ tests withFakeRunner/registry).fakePDS.sagaWiring: shared Supervisor +ActivationProvisioner; reuse inorphanSweepDeps.sovrn_pds_origin_suffix(derived default per cell) rendered into[pds] origin_suffix.test-deploygreen.Acceptance
pds.origin_suffixset and supervision on,domain.createprovisions the PDS instance with no operator step; retries converge on the same slug/port; anActivatefailure surfaces the original error and leaves no active record.Implemented and verified (commit 1535d509). New pdslifecycle.ActivationProvisioner{Supervisor, Secrets, OriginSuffix} satisfies the saga’s EnsureProvisioned seam: active row reused; absent/retiring row activated via Supervisor.Activate (allocate slug+port -> env -> start -> Caddy maps -> health gate -> active, self-rolling-back); empty pds.origin_suffix preserves the old lookup-only behavior with an actionable error. Supervisor.EnsureActive/FindActive make check-then-act atomic under the supervisor mutex, and one shared Supervisor is now used by both the saga and the orphan sweep. Config pds.origin_suffix wired (config.go, bindEnv, sovrn.toml, group_vars default empty, mx99 set to .pds1.eu.sovrn.at). Review fixes applied: atomic ensure + concurrency test (-race), OriginForDomain leading-dot validation, retiring-reclaim retry test, detached rollback context, ServerDIDForOrigin/normalized domain. go test ./…, go vet, test-deploy (23), and gofmt all green. Ready for your check; not closing. Follow-up noted in the epic: concurrent same-domain creates now converge via EnsureActive.