Files

6.3 KiB
Raw Permalink Blame History

kmsvc-cli — CLI wrapper implementation plan

Context

Fourth sibling repo, alongside kafaka_management_service (server), kmsvc-proto (wire contract), and kmsvc-sdk (Go client SDK). Per design.md §11a, this is a thin CLI over kmsvc-sdk. Used by operators to send/receive/delete messages and inspect/redrive DLQ contents from the terminal.

Queue lifecycle (create/delete/configure) is managed via the Queue CRD on the cluster (kubectl apply/kubectl get queues), not exposed over gRPC by kmsvc-proto — so this CLI has no queue subcommand, only message and dlq.

Dependency: kmsvc-cli imports kmsvc-sdk as a normal Go module dependency — go get forgejo.riotpiao.homelab.com/rock/[email protected] (module path matches the self-hosted Forgejo host, same pattern as kmsvc-proto). While kmsvc-sdk isn't pushed/tagged yet, this repo uses a local replace directive in go.mod pointing at the sibling ../kmsvc-sdk checkout — remove it once a real tag exists.

CLI framework: the user's existing talos-cli (homelab cluster control) is a Rust/clap binary, not Go — there's no existing Go CLI convention in this homelab to literally reuse. We use cobra (the de facto standard for Go CLIs, same family kubectl/helm use) and mirror talos-cli's behavioral conventions instead: simple subcommand tree, env-var-driven config with sane defaults, no mandatory config file.

Repo layout

kmsvc-cli/
  go.mod                          # module forgejo.riotpiao.homelab.com/rock/kmsvc-cli
  cmd/kmsvc/main.go               # entrypoint
  internal/cli/root.go            # root command, global flags (--server, --token, --output)
  internal/cli/config.go          # ~/.kmsvc/config.yaml (optional) + env var overrides, flag overrides env
  internal/cli/client.go          # MessageClient interface + buildClient (lazy kmsvc-sdk.Client construction)
  internal/cli/messages.go        # kmsvc message send|receive|delete|change-visibility
  internal/cli/dlq.go             # kmsvc dlq peek|redrive
  internal/cli/output.go          # table/json rendering, shared across subcommands
  internal/cli/version.go         # kmsvc version (ldflags-injected at release build time)
  internal/cli/*_test.go
  .forgejo/workflows/ci.yaml
  .forgejo/workflows/release.yaml
  README.md

Implementation steps

Step 1 — Command skeleton + config loading done

  • cobra root command kmsvc with persistent flags --server/--token/--output (table|json), each falling back to KMSVC_SERVER/KMSVC_TOKEN/KMSVC_OUTPUT, falling back to an optional ~/.kmsvc/config.yaml (server:, output:), falling back to defaults (output: table). Precedence: flag > env > file > default.
  • Verify: kmsvc --help lists message/dlq/version; a table-driven test asserts file+env+flag precedence resolves correctly.

Step 2 — Auth wiring + client construction done (revised)

  • Commands use the concrete *kmsvc.Client directly — no MessageClient interface or clientFactory abstraction (course-corrected: re-wrapping the SDK's own methods behind an interface purely for test substitution was unnecessary duplication).
  • buildClient constructs a real *kmsvc.Client from resolved config (kmsvc.WithTokenSource(kmsvc.StaticToken(token)) when --token/KMSVC_TOKEN is set), errors if --server/KMSVC_SERVER is unset.
  • Tests spin up a real loopback-TCP grpc.Server backed by a hand-rolled fake of kafkamgmtv1.QueueServiceServer (testserver_test.go), exercised through the unmodified SDK New/Client — fully offline, no interface needed on the CLI side.

Step 3 — Message commands done

  • kmsvc message send --queue --body [--group-id] [--dedup-id] [--delay], receive --queue [--wait] [--max-messages] [--visibility-timeout], delete --queue --receipt-handle, change-visibility --queue --receipt-handle --timeout.
  • Verify: per-command tests against a hand-rolled fake MessageClient; --output json produces machine-parseable output for receive.

Step 4 — DLQ inspection commands done

  • kmsvc dlq peek --queue <dlq-name> [--max-messages] [--visibility-timeout]: receives without deleting (a short visibility timeout lets it become re-visible) — non-destructive inspect.
  • kmsvc dlq redrive --queue <dlq-name> --to <source-queue> [--max-messages]: for each received message — SendMessage to --to, then DeleteMessage from the DLQ. Not atomic (3+ SDK calls): if send succeeds but delete fails, the command reports that message as "sent but not removed from DLQ — may be redelivered" rather than silently continuing, and the command exits non-zero if any entry had a partial failure.
  • Verify: fake-backed test asserting call order (send before delete) and that a forced delete failure surfaces the duplicate-risk warning and a non-zero exit.

Step 5 — Output formatting done

  • internal/cli/output.go: shared table/json renderer for messages and batch results.
  • Verify: tests for both formats against a fixed sample response.

Step 6 — Packaging + CI done (workflows written, release untested — no tag pushed yet)

  • .forgejo/workflows/ci.yaml: go build ./..., go vet ./..., go test ./... on every push/PR.
  • .forgejo/workflows/release.yaml: on a tag push, cross-compile (darwin/linux × amd64/arm64) with -ldflags -X .../internal/cli.version=<tag>, attach to a Forgejo release.
  • Verify: a real tag push produces downloadable binaries; kmsvc version in the built binary reports the tag. — deferred until first tag.

Acceptance criteria (overall)

  • All message/dlq subcommands work against a fake gRPC server in CI — no real network dependency.
  • Config file + env var + flag precedence is flag > env > file > default, tested explicitly.
  • --output json is machine-parseable for every read command (receive, dlq peek).
  • dlq redrive partial-failure (send ok, delete fails) is surfaced, not silently swallowed, and causes non-zero exit.
  • A tagged release produces real downloadable binaries via Forgejo Actions.
  • Manual end-to-end smoke test (send → receive → delete → dlq redrive) passes against a real running server, once one exists.

Sequencing note

Once kmsvc-sdk is pushed and tagged on Forgejo, swap the go.mod replace directive for a real go get forgejo.riotpiao.homelab.com/rock/kmsvc-sdk@<tag>.