Skip to content

feat(encryption): Stage 6D-6b - elastickv-admin enable-storage-envelope CLI subcommand#820

Merged
bootjp merged 2 commits into
mainfrom
feat/encryption-6d6b-cli-enable-storage-envelope
May 24, 2026
Merged

feat(encryption): Stage 6D-6b - elastickv-admin enable-storage-envelope CLI subcommand#820
bootjp merged 2 commits into
mainfrom
feat/encryption-6d6b-cli-enable-storage-envelope

Conversation

@bootjp
Copy link
Copy Markdown
Owner

@bootjp bootjp commented May 23, 2026

Summary

  • Stage 6D-6b adds the operator-facing CLI surface for the EnableStorageEnvelope RPC that landed in 6D-6a. 6D-6c will wire the cipher + capability-fanout closure in main.go and add the end-to-end integration test.
  • New elastickv-admin encryption enable-storage-envelope subcommand. The runner dispatches the RPC and renders the §3.1 response with shell-friendly stable line prefixes:
    • Fresh success: enabled applied_index=N + per-member capability summary: block
    • §6.4 idempotent retry (was_already_active=true): already-active applied_index=N (where N is the ORIGINAL StorageEnvelopeCutoverIndex, not this call's Raft index); summary intentionally omitted per §3.1
    • §6.4 defensive fallback (cutover_index_unknown=true): preceding warning: cutover_index_unknown=true … line so operators grepping on the warning can flag clusters that need investigation
  • CLI-side validation duplicates the §6.1 proposer_node_id != 0 sentinel and §4.1 16-bit proposer_local_epoch bound so an operator with a misconfigured shell variable fails fast before the round-trip; the server re-validates as the source of truth.
  • encryptionMain dispatch refactored to a name→handler map so adding the new subcommand keeps the function under the cyclop budget.

Test plan

  • go test -race -timeout 60s ./cmd/elastickv-admin/... — all green
  • golangci-lint run (full repo) — 0 issues
  • TestRunEncryptionEnableStorageEnvelope_HappyPath — fresh-success prefix + capability summary rows + wire-level proto request matches flag inputs
  • TestRunEncryptionEnableStorageEnvelope_IdempotentRetryalready-active prefix; no summary header; no warning
  • TestRunEncryptionEnableStorageEnvelope_DefensiveCutoverIndexUnknown — warning line precedes the already-active result
  • TestRunEncryptionEnableStorageEnvelope_RejectsZeroProposerNodeID — §6.1 sentinel rejection at the CLI side before any RPC
  • TestRunEncryptionEnableStorageEnvelope_RejectsBadEpoch — §4.1 16-bit bound at the CLI side
  • TestEncryptionMain_EnableStorageEnvelopeSubcommand — dispatch routing pin

Caller audit (semantic change)

This PR is purely additive:

  • The new subcommand is reachable only via elastickv-admin encryption enable-storage-envelope — no existing dispatch path is touched.
  • stubMutatorServer gains two new fields with zero-value defaults; existing rotate / register / bootstrap test fixtures continue to use the same struct unchanged.
  • main.go does NOT wire the server method's WithEncryptionAdminCapabilityFanout closure yet (lands in 6D-6c), so the new subcommand will refuse with FailedPrecondition until 6D-6c ships. Operator-inert at this stage.

Refs: design doc §3.1 (Stage 6D-6b slice; 6D-6c remains).

Summary by CodeRabbit

  • New Features

    • Added elastickv-admin encryption enable-storage-envelope subcommand to enable storage envelope with specified proposer identity parameters. Output displays success status, applied index, and capability information with warnings when applicable.
  • Documentation

    • Updated design documentation to reflect completion of the CLI subcommand implementation milestone.

Review Change Stack

…pe CLI subcommand

Stage 6D-6b adds the operator-facing CLI surface for the
EnableStorageEnvelope RPC that landed in 6D-6a. The remaining
6D-6c slice will wire the cipher + capability-fanout closure in
main.go and add the end-to-end integration test.

## CLI wiring (cmd/elastickv-admin/)

- encryption.go dispatch picks up `enable-storage-envelope`;
  the help string in the -h/--help/help branches lists the new
  subcommand.
- encryption_mutators.go ships runEncryptionEnableStorageEnvelope
  + parseEnableStorageEnvelopeArgs +
  printEnableStorageEnvelopeResult.
- CLI-side validation duplicates the §6.1 proposer_node_id != 0
  sentinel and §4.1 16-bit proposer_local_epoch bound so an
  operator with a misconfigured shell variable fails fast
  before the round-trip; the server re-validates as the source
  of truth.

## Output shape (shell-friendly)

Stable line prefixes so automation can switch on column 1 of
the result line:

- Fresh success: `enabled applied_index=N` followed by a
  `capability summary:` block listing each probed member.
- Idempotent retry (§6.4 was_already_active=true):
  `already-active applied_index=N` where N is the ORIGINAL
  StorageEnvelopeCutoverIndex (NOT this call's Raft index).
  Capability summary intentionally omitted per §3.1.
- Defensive fallback (§6.4 cutover_index_unknown=true): a
  `warning: cutover_index_unknown=true (sidecar may have been
  hand-edited or rolled back)` line BEFORE the already-active
  result so operators grepping on the warning substring can
  flag clusters that need investigation.

## Tests (cmd/elastickv-admin/encryption_test.go)

- stubMutatorServer extended with EnableStorageEnvelope: new
  enableEnvelopeCalls slice captures the proto requests for
  wire-level assertions; new enableEnvelopeResp field lets
  tests pin the exact response shape (fresh-success vs
  idempotent vs defensive).
- TestRunEncryptionEnableStorageEnvelope_HappyPath - fresh
  success: asserts the `enabled` prefix, the per-member verdict
  rows in the capability summary, and the wire-level proto
  request matches the flag inputs.
- TestRunEncryptionEnableStorageEnvelope_IdempotentRetry -
  was_already_active=true response: asserts the
  `already-active` prefix, the absence of the capability
  summary header, and the absence of any warning.
- TestRunEncryptionEnableStorageEnvelope_DefensiveCutoverIndexUnknown
  - was_already_active=true + cutover_index_unknown=true:
  asserts the warning line precedes the already-active result.
- TestRunEncryptionEnableStorageEnvelope_RejectsZeroProposerNodeID
  - §6.1 sentinel rejection at the CLI side before any RPC.
- TestRunEncryptionEnableStorageEnvelope_RejectsBadEpoch - §4.1
  16-bit bound at the CLI side.
- TestEncryptionMain_EnableStorageEnvelopeSubcommand - dispatch
  routing pin: a typo in encryptionMain's switch statement
  would route to the default branch and the user would see
  "unknown subcommand" rather than the runner's
  --proposer-node-id error.

## Caller audit (semantic change)

This PR is purely additive:
- The new subcommand is reachable only via
  `elastickv-admin encryption enable-storage-envelope` — no
  existing dispatch path is touched.
- stubMutatorServer gains two new fields with zero-value
  defaults; existing tests (rotate-dek, register-writer,
  bootstrap) continue to use the same fixture unchanged.
- main.go does NOT wire the EnableStorageEnvelope server
  method's capability-fanout closure yet (lands in 6D-6c), so
  the new subcommand will refuse with FailedPrecondition until
  6D-6c ships. Operator-inert at this stage.

## Verification

- go test -race -timeout 60s ./cmd/elastickv-admin/... - all green
- golangci-lint --new-from-rev=origin/main - 0 issues

Refs: design doc §3.1 (Stage 6D-6b slice of the §7
decomposition table; 6D-6c remains).
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

Warning

Review limit reached

@bootjp, we couldn't start this review because you've used your available PR reviews for now.

Your plan currently allows 1 review/hour. Refill in 42 minutes and 18 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1adbf9e9-111f-4436-8afd-15ccc3677c74

📥 Commits

Reviewing files that changed from the base of the PR and between 27374f8 and 099cbd8.

📒 Files selected for processing (2)
  • cmd/elastickv-admin/encryption.go
  • cmd/elastickv-admin/encryption_mutators.go
📝 Walkthrough

Walkthrough

This PR implements the encryption enable-storage-envelope CLI subcommand for elastickv-admin. The dispatcher is refactored to use a map-based lookup, the command handler parses proposer identity flags, invokes the RPC with proper timeout, and renders responses that distinguish fresh success from idempotent retry. Comprehensive tests validate output formatting, CLI input validation, and dispatcher routing. Design documentation is updated to reflect the shipped milestone.

Changes

Enable Storage Envelope CLI Feature

Layer / File(s) Summary
Subcommand dispatcher refactoring
cmd/elastickv-admin/encryption.go
encryptionMain switches from a hardcoded switch statement to a centralized encryptionSubcommands() map-based dispatcher. Help text and "unknown subcommand" errors are updated to list the expanded supported set including enable-storage-envelope and probe-node-id.
Enable-storage-envelope command implementation
cmd/elastickv-admin/encryption_mutators.go
runEncryptionEnableStorageEnvelope parses proposer node ID and local epoch flags, creates a timed RPC client, invokes EnableStorageEnvelope, and renders the response. parseEnableStorageEnvelopeArgs validates non-zero proposer ID and uint16-bounded epoch; printEnableStorageEnvelopeResult formats output as "enabled" or "already-active", includes a warning when cutover_index_unknown is set, and renders the capability summary table when present.
Test infrastructure and test suite
cmd/elastickv-admin/encryption_test.go
stubMutatorServer is extended to track EnableStorageEnvelope requests and optionally return custom responses. New test functions validate fresh-success output (applied index and capability verdicts), idempotent-retry rendering (already-active prefix without capability summary), cutover-index-unknown warning behavior, CLI-side input validation for zero proposer ID and out-of-bounds epoch, and subcommand dispatcher routing to the correct handler.
Design documentation
docs/design/2026_05_18_partial_6d_enable_storage_envelope.md
Stage 6D-6b (CLI subcommand) milestone is marked as shipped. The shipped-milestone section documents the command location, response output format for fresh success vs. idempotent retry, the cutover-index-unknown warning, and CLI-side validation that mirrors server validation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • bootjp/elastickv#812: Adds the EnableStorageEnvelope RPC endpoint and response contract that this PR's CLI command directly invokes and renders.
  • bootjp/elastickv#809: Introduces the storage-layer write gate that the enable-storage-envelope command controls via the EnableStorageEnvelope RPC.
  • bootjp/elastickv#754: Earlier encryption CLI implementation that this PR extends with the dispatcher refactoring and new subcommand routing.

Poem

🐰 A rabbit hops through envelope gates,
Parsing proposer nodes with care,
RPC calls dispatch with perfect fate,
Tests validate each nook and lair.
The CLI now commands with flair!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding an enable-storage-envelope CLI subcommand as part of Stage 6D-6b, which is directly reflected in all the modified files.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/encryption-6d6b-cli-enable-storage-envelope

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the enable-storage-envelope subcommand to the elastickv-admin encryption CLI, facilitating the transition to storage envelope encryption. The changes include a refactored subcommand dispatch mechanism, the implementation of the new command's logic with robust flag validation, and comprehensive unit tests covering various operational outcomes. Documentation was also updated to reflect the completion of this milestone. Feedback was provided to align the docstring examples in encryption_mutators.go with the actual output format implemented in the code.

Comment on lines +122 to +126
// fresh: "enabled storage envelope applied_index=N
// capability summary: ..."
// already-on: "storage envelope already active (idempotent
// retry) applied_index=N"
// defensive: "warning: cutover_index_unknown=true"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The docstring for runEncryptionEnableStorageEnvelope contains example output strings that do not match the actual implementation in printEnableStorageEnvelopeResult. Specifically, the implementation uses more concise prefixes (enabled and already-active) and includes additional detail in the warning message. Updating the docstring to match the implementation will prevent confusion for future maintainers.

Suggested change
// fresh: "enabled storage envelope applied_index=N
// capability summary: ..."
// already-on: "storage envelope already active (idempotent
// retry) applied_index=N"
// defensive: "warning: cutover_index_unknown=true"
// fresh: "enabled applied_index=N
// capability summary: ..."
// already-on: "already-active applied_index=N"
// defensive: "warning: cutover_index_unknown=true (sidecar may have been hand-edited or rolled back)"

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
cmd/elastickv-admin/encryption_mutators.go (1)

145-148: ⚡ Quick win

Use structured slog for the close-error path in this new subcommand.

This newly-added logging path emits unstructured stderr text. Please switch to slog with stable keys to match repo logging policy.

♻️ Proposed change
 import (
 	"context"
 	"encoding/base64"
 	"flag"
 	"fmt"
 	"io"
+	"log/slog"
 	"math"
 	"os"
 	"strconv"
 	"strings"
@@
 	defer func() {
 		if err := closeFn(); err != nil {
-			fmt.Fprintf(os.Stderr, "encryption: close connection: %v\n", err)
+			slog.Error("encryption close connection",
+				"subcommand", "enable-storage-envelope",
+				"endpoint", *endpoint.endpoint,
+				"error", err,
+			)
 		}
 	}()

As per coding guidelines, "Use structured slog logging with stable keys (key, commit_ts, route_id, etc.) for logging throughout the codebase."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/elastickv-admin/encryption_mutators.go` around lines 145 - 148, Replace
the unstructured fmt.Fprintf call inside the defer that invokes closeFn() with
structured slog logging: import log/slog if not already present, and in the
defer's error branch call slog.Error (or slog.Log with LevelError) with a clear
message and stable keys such as "component":"encryption",
"action":"close_connection" and "error":err (or "err":err) so the close error is
emitted as structured logs; update the defer that calls closeFn() (reference
closeFn in encryption_mutators.go) accordingly.
cmd/elastickv-admin/encryption.go (1)

46-53: ⚡ Quick win

Derive the supported-subcommand list from the dispatch table.

The help text and unknown-subcommand error each hardcode command names, separate from encryptionSubcommands(). This can drift on the next subcommand addition/removal.

♻️ Proposed refactor
 import (
 	"context"
 	"flag"
 	"fmt"
 	"io"
 	"os"
 	"slices"
 	"strconv"
+	"strings"
 	"time"
@@
+func encryptionSupportedSubcommands() []string {
+	keys := make([]string, 0, len(encryptionSubcommands()))
+	for k := range encryptionSubcommands() {
+		keys = append(keys, k)
+	}
+	slices.Sort(keys)
+	return keys
+}
+
 func encryptionMain(args []string) error {
@@
 	if sub == "-h" || sub == "--help" || sub == "help" {
+		supported := strings.Join(encryptionSupportedSubcommands(), "\n  ")
-		_, err := fmt.Fprintln(os.Stdout, "usage: elastickv-admin encryption <subcommand> [flags]\n\nsubcommands:\n  status\n  rotate-dek\n  register-writer\n  bootstrap\n  enable-storage-envelope\n  probe-node-id")
+		_, err := fmt.Fprintf(os.Stdout, "usage: elastickv-admin encryption <subcommand> [flags]\n\nsubcommands:\n  %s\n", supported)
 		if err != nil {
 			return errors.Wrap(err, "write usage")
 		}
 		return nil
 	}
-	return errors.Errorf("encryption: unknown subcommand %q (supported: status, rotate-dek, register-writer, bootstrap, enable-storage-envelope, probe-node-id)", sub)
+	return errors.Errorf("encryption: unknown subcommand %q (supported: %s)", sub, strings.Join(encryptionSupportedSubcommands(), ", "))
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/elastickv-admin/encryption.go` around lines 46 - 53, The usage text and
unknown-subcommand error should be generated from the dispatch table returned by
encryptionSubcommands() instead of hardcoding names; query
encryptionSubcommands(), collect its keys into a slice (sort them for stable
output), join them with ", " (or format into the multiline usage string) and use
that generated string in both the fmt.Fprintln usage and the errors.Errorf
unknown-subcommand message (referencing the existing local variable sub for the
unknown value). This ensures the displayed supported-subcommand list is always
derived from encryptionSubcommands().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/elastickv-admin/encryption.go`:
- Around line 30-32: The Stage-6 comment is out of sync: it mentions
`enable-raft-envelope` but this PR actually wires the `probe-node-id` CLI flag;
update the comment in encryption.go to describe that Stage-6 adds
`probe-node-id` (and retains `enable-storage-envelope`/ResyncSidecar behavior)
so the text matches the implemented CLI surface and references the
`probe-node-id` symbol instead of `enable-raft-envelope`.

---

Nitpick comments:
In `@cmd/elastickv-admin/encryption_mutators.go`:
- Around line 145-148: Replace the unstructured fmt.Fprintf call inside the
defer that invokes closeFn() with structured slog logging: import log/slog if
not already present, and in the defer's error branch call slog.Error (or
slog.Log with LevelError) with a clear message and stable keys such as
"component":"encryption", "action":"close_connection" and "error":err (or
"err":err) so the close error is emitted as structured logs; update the defer
that calls closeFn() (reference closeFn in encryption_mutators.go) accordingly.

In `@cmd/elastickv-admin/encryption.go`:
- Around line 46-53: The usage text and unknown-subcommand error should be
generated from the dispatch table returned by encryptionSubcommands() instead of
hardcoding names; query encryptionSubcommands(), collect its keys into a slice
(sort them for stable output), join them with ", " (or format into the multiline
usage string) and use that generated string in both the fmt.Fprintln usage and
the errors.Errorf unknown-subcommand message (referencing the existing local
variable sub for the unknown value). This ensures the displayed
supported-subcommand list is always derived from encryptionSubcommands().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: add537ea-28f2-478e-9342-fd6e3640a094

📥 Commits

Reviewing files that changed from the base of the PR and between 122eadf and 27374f8.

📒 Files selected for processing (4)
  • cmd/elastickv-admin/encryption.go
  • cmd/elastickv-admin/encryption_mutators.go
  • cmd/elastickv-admin/encryption_test.go
  • docs/design/2026_05_18_partial_6d_enable_storage_envelope.md

Comment thread cmd/elastickv-admin/encryption.go Outdated
…string drift

Round-1 review surfaced two documentation-accuracy findings plus
one quick-win style suggestion. Addressing the docs; skipping
the slog suggestion because the other mutator runners
(runEncryptionRotateDEK, runEncryptionRegisterWriter,
runEncryptionBootstrap) all use the same fmt.Fprintf(os.Stderr,
...) close-error pattern - changing only the new runner would
introduce local style drift.

## gemini medium - runEncryptionEnableStorageEnvelope docstring drift

Pre-fix: the docstring example output strings referenced
'enabled storage envelope applied_index=N' / 'storage envelope
already active (idempotent retry)' but the actual
printEnableStorageEnvelopeResult implementation uses the more
concise 'enabled' / 'already-active' prefixes that the CLI tests
pin as the wire-level shell-script contract.

Fix: rewrite the docstring's example block to match the
implementation verbatim. The new block names all three
outcomes (fresh / already-on / defensive) with the exact
prefixes a script will see, including the parenthetical
'(sidecar may have been hand-edited or rolled back)' suffix on
the warning line.

## coderabbit minor - encryption.go Stage-6 comment drift

Pre-fix: the dispatch comment said 'Stage 6 adds
enable-storage-envelope and enable-raft-envelope' but this PR
wires only enable-storage-envelope; enable-raft-envelope is a
§7.1 Phase 2 surface that lands in Stage 6E.

Fix: rewrite the dispatch comment's lifecycle history to match
shipped reality. Calls out 6D-2 (probe-node-id) which was
missing from the original comment, names 6D-6b as the PR that
wires enable-storage-envelope, and defers enable-raft-envelope
to Stage 6E.

## coderabbit quick-win (skipped) - slog vs fmt.Fprintf in close-err path

Pre-fix and current state: the new
runEncryptionEnableStorageEnvelope uses the same
fmt.Fprintf(os.Stderr, ...) close-error path as
runEncryptionRotateDEK / runEncryptionRegisterWriter /
runEncryptionBootstrap. Coderabbit suggested swapping in
slog.Error with structured keys.

Skipped for this PR: changing only this one site creates local
style drift across the four mutator runners. The other three
runners would need the same conversion, and that scope is
better landed as a separate cleanup PR that touches all four
together. Recording the suggestion here so a future
'CLI logging consistency' pass can pick it up.

## Caller audit

Docstring-only changes; no behaviour change, no test impact.

## Verification

- go test -race -timeout 60s ./cmd/elastickv-admin/... - all green
- golangci-lint --new-from-rev=origin/main - 0 issues
@bootjp
Copy link
Copy Markdown
Owner Author

bootjp commented May 23, 2026

@claude review

Round-1 (commit 099cbd8b): addressed gemini medium + coderabbit minor docstring-accuracy findings. Skipped the slog quick-win to avoid local style drift (see below).

gemini medium — runEncryptionEnableStorageEnvelope docstring drift

Pre-fix: docstring example output strings referenced 'enabled storage envelope applied_index=N' / 'storage envelope already active (idempotent retry)' but the actual printEnableStorageEnvelopeResult implementation uses the more concise enabled / already-active prefixes that the CLI tests pin as the wire-level shell-script contract.

Fix: rewrote the docstring's example block to match the implementation verbatim. New block names all three outcomes (fresh / already-on / defensive) with the exact prefixes a script will see, including the (sidecar may have been hand-edited or rolled back) suffix.

coderabbit minor — encryption.go Stage-6 dispatch comment drift

Pre-fix: dispatch comment said 'Stage 6 adds enable-storage-envelope and enable-raft-envelope' but this PR wires only enable-storage-envelope; enable-raft-envelope is a §7.1 Phase 2 surface that lands in Stage 6E.

Fix: rewrote the lifecycle history to match shipped reality. Now calls out 6D-2 (probe-node-id), names 6D-6b as the PR that wires enable-storage-envelope, and defers enable-raft-envelope to Stage 6E.

coderabbit quick-win (skipped) — slog vs fmt.Fprintf in close-err path

The new runner uses the same fmt.Fprintf(os.Stderr, …) close-error pattern as runEncryptionRotateDEK / runEncryptionRegisterWriter / runEncryptionBootstrap. Changing only the new site creates local style drift across the four mutator runners. Better landed as a separate cleanup PR that touches all four together. Suggestion recorded in the commit message for a future 'CLI logging consistency' pass.

Caller audit

Docstring-only changes; no behaviour change, no test impact.

Verification

  • go test -race -timeout 60s ./cmd/elastickv-admin/... — all green
  • golangci-lint --new-from-rev=origin/main — 0 issues

@claude
Copy link
Copy Markdown

claude Bot commented May 23, 2026

Claude finished @bootjp's task in 2m 1s —— View job


Review — Round-1 fixes verified, no new blockers

  • Load tools
  • Read changed files
  • Review all changed files
  • Post final review

Round-1 fixes

Both Round-1 fixes land correctly.

Gemini docstring fix (encryption_mutators.go:108-128): the docstring's example block now matches the implementation verbatim — enabled, already-active, and the warning: cutover_index_unknown=true (sidecar may have been hand-edited or rolled back) suffix all agree with printEnableStorageEnvelopeResult. ✅

CodeRabbit comment drift (encryption.go:28-33): the lifecycle history now accurately names 6D-2 (probe-node-id), 6D-6b (enable-storage-envelope), and defers enable-raft-envelope to Stage 6E. No more enable-raft-envelope ghost in the current-stage block. ✅

slog quick-win (deferred): the rationale — changing only the new site while the existing three runners use fmt.Fprintf would introduce local style drift — is sound. Worth landing as a unified cleanup PR. ✅


Findings from this review pass

No correctness blockers. Two minor observations worth flagging:

1. Missing help-flag test for the new subcommand

TestRunEncryptionProbeNodeID_HelpFlagExitsZero (line 569) explicitly pins that -h returns nil on the probe subcommand. The enable-storage-envelope runner has the same flag.ErrHelp → return nil branch in parseEnableStorageEnvelopeArgs (lines 165-168) but there is no corresponding TestRunEncryptionEnableStorageEnvelope_HelpFlagExitsZero test. All other runners that were added after the help-flag convention was established have this test; skipping it here means the branch is covered only by code inspection.

Fix this →

2. Stale §6.1 section reference in the error message and flag usage string

parseEnableStorageEnvelopeArgs (lines 163, 175) says (0 is the §6.1 not-capable sentinel) in both the flag usage string and the error message. In the design doc, §6.1 is the storage-layer read path ("Read path: already done"). The proposer_node_id != 0 sentinel is defined in §3.2 (step 1 of the server-side sequence). The §6.1 reference in comments and strings should be §3.2. This is a doc-accuracy issue, not a correctness issue; operators reading the error message would get a wrong section hint.

Fix this →


What looks good

  • Dispatch refactor (encryption.go:61-70): the switch → map migration is clean; cyclomatic complexity stays flat as new subcommands land. Map allocation on each call is fine for a CLI (not a hot path).
  • parseEnableStorageEnvelopeArgs (lines 160-183): § sentinel and 16-bit epoch bound validation follow the exact same pattern as parseRotateDEKArgs; consistent and correct.
  • printEnableStorageEnvelopeResult (lines 191-225): the three output shapes (fresh / already-active / defensive) are unambiguous and the warning always precedes the already-active line so scripts grepping on either prefix get a consistent column-1 token.
  • stubMutatorServer extension (lines 179-224): the nil-check fallback for enableEnvelopeResp is correct and keeps existing rotate/register/bootstrap tests unchanged (zero-value fields; no behaviour change on those fixtures).
  • Test coverage: six focused tests cover all documented outcomes. Wire-level proto assertions (call.ProposerNodeId, call.ProposerLocalEpoch) pin the request shape so the CLI cannot silently mis-marshal the proposer identity fields.
  • Design doc (2026_05_18_partial_6d_enable_storage_envelope.md): the 6D-6b milestone entry is accurate and the output-format spec matches the implementation.

Both observations are minor. The PR is otherwise clean and well-tested.

@bootjp bootjp merged commit 77b2690 into main May 24, 2026
9 checks passed
@bootjp bootjp deleted the feat/encryption-6d6b-cli-enable-storage-envelope branch May 24, 2026 00:00
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