Skip to content

feat: wire cmd/duckgres-controlplane via shared CLIInputs flag registrar#523

Merged
fuziontech merged 1 commit into
mainfrom
feat/wire-controlplane-binary
May 5, 2026
Merged

feat: wire cmd/duckgres-controlplane via shared CLIInputs flag registrar#523
fuziontech merged 1 commit into
mainfrom
feat/wire-controlplane-binary

Conversation

@fuziontech
Copy link
Copy Markdown
Member

Summary

Wires cmd/duckgres-controlplane to actually run, and lifts the ~50 CLIInputs-backed flag declarations out of root main.go into a shared registrar so the two binaries can never drift on the resolver's CLI surface.

configresolve.RegisterCLIInputsFlags(*flag.FlagSet) func() CLIInputs registers every CLIInputs-backed CLI flag onto the provided FlagSet and returns a harvest closure that produces a populated CLIInputs (with Set) post-Parse. Each binary calls this then adds its own bespoke (b)-bucket flags. The new TestRegisterCLIInputsFlagsCoversEveryCLIBackedField test uses reflection to assert every CLIInputs field has a corresponding flag — adding a new field without a flag now fails CI loudly instead of silently doing nothing. The 6 env-only K8s pod-scheduling fields (CLAUDE.md-documented) are explicitly excluded by name.

cmd/duckgres-controlplane is no longer a stub. It:

  • Registers the shared CLIInputs flags.
  • Adds bespoke --config, --log-level, --mode, --socket-dir, --version, --help. (--mode accepts only control-plane; symmetric with cmd/duckgres-worker's --mode duckdb-service. Loud rejection on misuse, exit 2.)
  • Loads config, resolves via configresolve.ResolveEffective, ensures TLS certs (or hands off to ACME), starts the metrics server, builds controlplane.ControlPlaneConfig, and calls controlplane.RunControlPlane.
  • Mirrors the all-in-one's --mode control-plane branch and does not import duckdbservice — CI guard ci: guard cmd/duckgres-controlplane against duckdb-go regressions #499 (go list -deps ./cmd/duckgres-controlplane | grep duckdb-go empty) is preserved.

Root main.go drops ~75 lines of inline flag.* declarations and the corresponding ~55-line CLIInputs{...} literal in favour of harvestCLIInputs := configresolve.RegisterCLIInputsFlags(flag.CommandLine) + cli := harvestCLIInputs(). Bespoke flags stay inline.

cliboot.InitMetrics is lifted from root main.go so both binaries share the Prometheus metrics-server bring-up loop with identical retry behaviour. The all-in-one binary calls it; the CP-only binary calls it; the worker correctly does not (workers in --mode duckdb-service would all fight over :9090).

just test-unit now also runs ./configresolve/... so the drift guard fires on every CI pass.

Why this exists

Phase B of the post-#521 validation pass concluded that two ~60-flag tables would inevitably drift over time, while the (b)-bucket of mode/runtime/transport flags doesn't fit a single unified registrar. The shape we landed — shared registrar for the (a) bucket, bespoke flags per binary for (b) — keeps the resolver's CLI surface honest without forcing unnatural coupling on --mode, --repl, --psql, --duckdb-*, etc.

This unblocks one of two latent gaps surfaced in the validation pass: cmd/duckgres-controlplane was a crash-loop stub in every matrix-CD image until now. Pre-existing cmd/duckgres-worker fixes (#521, #522) handle the worker side.

Test plan

  • go build ./... clean
  • go vet ./... clean
  • just test-unit green (now includes ./configresolve/...)
  • go test -count=1 ./controlplane/... green
  • TestRegisterCLIInputsFlagsCoversEveryCLIBackedField passes — every CLI-backed CLIInputs field maps to a registered flag, every registered flag maps to a CLIInputs field
  • TestRegisterCLIInputsFlagsHarvestPropagatesValuesAndSet passes — values and Set map populated correctly post-Parse
  • CP binary --version / --help / --mode standalone (rejected) all behave correctly
  • CP binary smoke run with --worker-backend process: binds PG-wire (127.0.0.1:25432), serves Prometheus on :9090, accepts TCP handshake, generates self-signed certs
  • go list -deps ./cmd/duckgres-controlplane | grep duckdb-go is empty (CI guard ci: guard cmd/duckgres-controlplane against duckdb-go regressions #499 preserved)
  • Deferred to user-side: validate the live multitenant CP picks up the new image after a fresh CD run

🤖 Generated with Claude Code

Lifts the ~50 CLIInputs-backed CLI flag declarations out of root main.go
into configresolve.RegisterCLIInputsFlags so the all-in-one duckgres
binary and cmd/duckgres-controlplane share one flag table — adding a
new field to CLIInputs without registering a flag fails CI loudly via
the new TestRegisterCLIInputsFlagsCoversEveryCLIBackedField drift
guard, instead of silently doing nothing.

cmd/duckgres-controlplane is no longer a stub: it parses flags via
the shared registrar plus its own bespoke set (--config, --log-level,
--mode, --socket-dir, --version, --help), resolves config via
configresolve.ResolveEffective, ensures TLS certs (or hands off to
ACME), starts the metrics server, and calls
controlplane.RunControlPlane. Local smoke run confirms it binds the
PG-wire port + :9090, accepts TCP, and serves real Prometheus
metrics. The libduckdb-free invariant (CI guard #499) is preserved —
go list -deps reports no duckdb-go in the dep tree.

Worker stays bespoke (its 14 flags ARE the documented worker-pod
surface), and a --mode control-plane accept-and-validate flag mirrors
the worker's --mode duckdb-service for symmetry. cliboot.InitMetrics
is shared, and just test-unit now also runs ./configresolve/... so
the drift guard fires on every CI pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@fuziontech fuziontech merged commit 5ee1c7b into main May 5, 2026
22 checks passed
@fuziontech fuziontech deleted the feat/wire-controlplane-binary branch May 5, 2026 05:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant