(chore) setup kmsvc-cli
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# 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)
|
||||
- [x] All `message`/`dlq` subcommands work against a fake gRPC server in CI — no real network dependency.
|
||||
- [x] Config file + env var + flag precedence is flag > env > file > default, tested explicitly.
|
||||
- [x] `--output json` is machine-parseable for every read command (`receive`, `dlq peek`).
|
||||
- [x] `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>`.
|
||||
Reference in New Issue
Block a user