Skip to content

KSQL-14849: add 'confluent ksql cluster update --csu N' command#3368

Draft
Vedarth Sharma (VedarthConfluent) wants to merge 3 commits into
mainfrom
KSQL-14849
Draft

KSQL-14849: add 'confluent ksql cluster update --csu N' command#3368
Vedarth Sharma (VedarthConfluent) wants to merge 3 commits into
mainfrom
KSQL-14849

Conversation

@VedarthConfluent
Copy link
Copy Markdown
Member

Summary

Adds the `confluent ksql cluster update --csu ` command for self-serve CSU alteration of ksqlDB clusters. Targets the new PATCH `/ksqldbcm/v2/clusters/{id}` endpoint being added in cc-control-plane-ksql PR #313 (KSQL-14845) and gated server-side by the `ksql.self_serve_csu_alteration.enable` LD flag.

What's in the change

  • `internal/ksql/command_cluster_update.go` — cobra command, `--csu` flag, client-side validation, pre-check via Describe, "rolling restart" notice
  • `internal/ksql/command_cluster_update_test.go` — 14 sub-tests covering CSU validation
  • `internal/ksql/command_cluster.go` — wires `update` into the cluster command group (cloud-login branch only)
  • `pkg/ccloudv2/ksql.go` — `UpdateKsqlCluster` shim with the future SDK-call wiring documented inline

Validation behavior

  • Valid values: 4, 8, 12, 16, 20, 24, 28 — mirrors the server-side authoritative list in `cc-control-plane-ksql/internal/service/update_ksql_cluster_resize.go::validCSUSizes`.
  • `--csu` > 28 → returns a customer-safe support-ticket message ("CSU values above 28 require a support ticket. Please contact Confluent Support…") rather than the generic "not a valid CSU size" message.
  • Legacy sizes (1, 2) → rejected as "not a valid CSU size".
  • Same CSU as current → rejected client-side with `"ksqlDB cluster %q is already at %d CSUs; no change requested"`. (The server's `ValidateKSQLClusterResize` also returns 400 `"new CSU size is the same as old CSU size, no-op"` for this case — the client pre-check just produces a clearer message and avoids an unnecessary API round trip. No rolling restart occurs on no-op.)
  • Shrink (target < current) → rejected client-side mirroring the server contract (`"shrinking is not supported"`).

Rolling restart behavior

On a real resize the PATCH returns 202 Accepted immediately; the control plane then performs a rolling restart of the StatefulSet asynchronously (via `expandKsqlCluster` → `syncSpecToPhysicalCluster`). The CLI surfaces this with a pre-flight notice:

```
Resizing ksqlDB cluster "lksqlc-12345" from 4 to 8 CSUs. A rolling restart will be performed
asynchronously; the cluster will continue serving queries during the resize.
```

SDK dependency — actual wire call is gated

The `ccloud-sdk-go-v2/ksql/v2` SDK at `v0.2.0` (current dep in go.mod) does not yet expose `ClustersKsqldbcmV2Api.UpdateKsqldbcmV2Cluster`. That SDK method is generated from the cc-api OpenAPI spec, which is being updated by cc-api PR #2507 (KSQL-14844). Until that lands and the SDK is regenerated:

  • `Client.UpdateKsqlCluster` returns a clear, customer-safe error explaining the feature is pending.
  • The full command logic (flag parsing, validation, pre-check, no-op detection, rolling-restart notice, table output) is wired and unit-tested today.

Unblock procedure is documented as a code-block inside `Client.UpdateKsqlCluster`'s doc comment — it's a one-line replacement of the shim body with the real SDK call once the SDK is regenerated.

Test plan

  • `go test ./internal/ksql/...` — all sub-tests pass (14 validation cases + existing tests)
  • `go vet ./internal/ksql/... ./pkg/ccloudv2/...` — clean
  • `confluent ksql cluster update --csu 8` against staging (after cc-api chore: update repo by service bot #2507 merges, SDK regenerates, and cc-control-plane-ksql ships with the LD flag enabled)
  • CI green

Why draft

Cannot meaningfully run end-to-end against any environment until the SDK is regenerated. Marking ready for review once `ccloud-sdk-go-v2/ksql` ships a release with `UpdateKsqldbcmV2Cluster`.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 26, 2026 15:06
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new confluent ksql cluster update <id> --csu <N> command intended to trigger self-serve ksqlDB cluster resizes via a forthcoming PATCH /ksqldbcm/v2/clusters/{id} control-plane endpoint.

Changes:

  • Adds a new ksql cluster update cobra subcommand with --csu validation, pre-check via Describe, and a rolling-restart notice.
  • Wires the new update subcommand into the existing ksql cluster command group (cloud-login branch).
  • Introduces a Client.UpdateKsqlCluster shim in pkg/ccloudv2 (currently returns a placeholder error until the SDK supports the endpoint), plus unit tests for CSU validation helpers.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
pkg/ccloudv2/ksql.go Adds UpdateKsqlCluster shim for future PATCH support (currently returns placeholder error).
internal/ksql/command_cluster.go Registers the new update subcommand under ksql cluster for cloud-login.
internal/ksql/command_cluster_update.go Implements the ksql cluster update command, CSU validation, pre-check logic, and user messaging.
internal/ksql/command_cluster_update_test.go Adds tests for CSU validation and CSU list formatting helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cmd.AddCommand(c.newDeleteCommand())
cmd.AddCommand(c.newDescribeCommand())
cmd.AddCommand(c.newListCommand())
cmd.AddCommand(c.newUpdateCommand())
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in 9b9eee25: added Hidden: true on the cobra command. The subcommand is still reachable for testing and review, but won't appear in ksql cluster --help or in customer-facing discovery. Drop Hidden when the SDK is regenerated and the shim is replaced with the real PATCH call.

Comment thread internal/ksql/command_cluster_update.go Outdated
Comment on lines +101 to +110
output.ErrPrintf(c.Config.EnableColor,
"Resizing ksqlDB cluster %q from %d to %d CSUs. A rolling restart will be "+
"performed asynchronously; the cluster will continue serving queries during the resize.\n",
clusterId, currentCsu, csu)

cluster, err := c.V2Client.UpdateKsqlCluster(clusterId, environmentId, csu)
if err != nil {
return err
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in 9b9eee25: moved the rolling-restart output.ErrPrintf to AFTER UpdateKsqlCluster returns nil. On failure (including the current shim that always fails) the notice is no longer printed.

Comment on lines +55 to +63
func buildUpdateLongDescription() string {
return fmt.Sprintf(
`Update an existing ksqlDB cluster. Currently only the CSU count may be
modified, and only to larger sizes (shrink is not supported).

Valid CSU values are %s. Larger sizes require a support ticket.
The cluster will undergo a rolling restart to apply the new size; the
command returns once the resize has been accepted by the control plane.`,
formatCsuList(validCsuSizes))
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in 9b9eee25: replaced the raw multi-line string with concatenated string literals and an explicit \n\n paragraph break. No more leading whitespace on continuation lines.

Comment on lines +120 to +123
func validateCsuForUpdate(csu int32) error {
if csu > 28 {
return fmt.Errorf("%d CSUs: %s", csu, csuSupportTicketMessage)
}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in 9b9eee25: added maxSelfServeCSU derived once from validCsuSizes (max over the slice). Both validateCsuForUpdate and csuSupportTicketMessage reference it, so extending validCsuSizes later moves the threshold automatically.

Comment thread pkg/ccloudv2/ksql.go Outdated
Comment on lines +99 to +101
"ksqlDB cluster update is not yet available in this CLI build; " +
"this command is pending a ccloud-sdk-go-v2/ksql regeneration " +
"after cc-api PR #2507 (KSQL-14844) merges. Track KSQL-14849 for status.")
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in 9b9eee25: error is now "ksqlDB cluster update is not yet available in this CLI build. Please upgrade to a newer version of the Confluent CLI and retry." The internal cc-api / KSQL-* references stay in the doc comment on Client.UpdateKsqlCluster for engineers.

@confluent-cla-assistant
Copy link
Copy Markdown

🎉 All Contributor License Agreements have been signed. Ready to merge.
Please push an empty commit if you would like to re-run the checks to verify CLA status for all contributors.

Adds the `confluent ksql cluster update <id> --csu <N>` command for
self-serve CSU alteration of ksqlDB clusters. The command targets the
new public-API PATCH /ksqldbcm/v2/clusters/{id} endpoint being added by
cc-control-plane-ksql PR #313 (KSQL-14845) and gated server-side by the
ksql.self_serve_csu_alteration.enable LD flag.

Behavior:
  - Validates --csu client-side against {4, 8, 12, 16, 20, 24, 28}. The
    server's validCSUSizes map remains authoritative; the client check
    exists to fail fast with a clearer error.
  - CSU > 28 returns a customer-safe support-ticket message rather than
    the generic "not a valid CSU size" error.
  - Pre-checks the current CSU via DescribeKsqlCluster: a no-op resize
    (same CSU) is rejected client-side with "already at N CSUs; no
    change requested" instead of relying on the server's 400 no-op. A
    shrink is also rejected client-side, mirroring the server contract.
  - Prints a clear "rolling restart will be performed asynchronously,
    cluster keeps serving queries" notice before the PATCH.

Files:
  - internal/ksql/command_cluster_update.go: cobra command + validation
  - internal/ksql/command_cluster_update_test.go: validation unit tests
  - internal/ksql/command_cluster.go: wires new subcommand under cluster
  - pkg/ccloudv2/ksql.go: UpdateKsqlCluster shim

SDK dependency (the actual wire call is gated):

The ccloud-sdk-go-v2/ksql/v2 SDK at v0.2.0 does not yet expose
ClustersKsqldbcmV2Api.UpdateKsqldbcmV2Cluster — that method will be
generated once cc-api PR #2507 (KSQL-14844) merges and the SDK is
regenerated. Until then, UpdateKsqlCluster returns a clear,
customer-safe error explaining the feature is pending. The unblock
procedure (one-line SDK call, doc comment in the file) is documented
inline. All other command logic (flag parsing, validation, pre-check,
no-op detection, output formatting) is fully wired and unit-tested today.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ommand

CI failure on #3368 was two integration-test golden-file mismatches:

  - test/fixtures/output/ksql/cluster/help.golden was missing the
    new 'update' line under Available Commands.
  - test/fixtures/output/ksql/cluster/update-help.golden didn't
    exist; the new subcommand needs its own --help fixture.

help-onprem.golden is unchanged: the update subcommand is registered
only on the cloud-login branch in internal/ksql/command_cluster.go,
so on-prem builds don't include it.
@sonarqube-confluent
Copy link
Copy Markdown

Quality Gate failed Quality Gate failed

Failed conditions
52.3% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube

1. Hide subcommand while SDK call is shimmed (Copilot:command_cluster.go:24).
   The 'update' subcommand was wired into the cloud command tree but
   Client.UpdateKsqlCluster always returned an error, so a released
   build would expose a permanently failing command to customers. Add
   Hidden: true on the cobra.Command — the command stays reachable for
   testing and review, but doesn't appear in 'ksql cluster --help' or
   discovery. Drop Hidden when the SDK is regenerated.

2. Move rolling-restart notice to AFTER successful PATCH
   (Copilot:command_cluster_update.go:110). Printing 'Resizing…' before
   the call was misleading on failure (and is always wrong with the
   current shim that always fails). Now printed only once
   UpdateKsqlCluster returns nil error.

3. Fix long-description indentation (Copilot:command_cluster_update.go:63).
   The raw multi-line string had leading whitespace on wrapped lines
   that would render with extra leading spaces in --help. Use a
   concatenated string literal with explicit \n\n paragraph break and
   no embedded indentation.

4. Derive support-ticket threshold from validCsuSizes
   (Copilot:command_cluster_update.go:123). The hard-coded '28' in
   validateCsuForUpdate duplicated knowledge already in the
   validCsuSizes slice. Added maxSelfServeCSU computed once from the
   slice; validateCsuForUpdate and csuSupportTicketMessage both use
   it. If validCsuSizes is extended later, the threshold moves with
   it.

5. Customer-safe error message in SDK shim (Copilot:ksql.go:101).
   The shim error referenced cc-api PR #2507 and KSQL-* Jira IDs,
   which are internal tracking artifacts. Replaced with a generic
   'this command is not yet available in this CLI build; please
   upgrade and retry' message. Internal context stays in the doc
   comment on UpdateKsqlCluster where reviewers and future
   maintainers can find it.

Golden-file impact:
  - help.golden: 'update' no longer listed under Available Commands
    (because Hidden=true).
  - update-help.golden: deleted; testHelp removes fixtures for
    !cmd.IsAvailableCommand(), and Hidden makes update unavailable
    in the parent's listing (own --help still works at runtime).
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.

2 participants