Add a service-scoped repair path for stale query keys - #538
Conversation
|
Carry-over review from PR #508 (old PR for #454). This PR is technically strong: it meets all five acceptance criteria, the subcommand surface (
|
4e9fc0c to
4f3a621
Compare
4f3a621 to
9054803
Compare
9054803 to
f66ac32
Compare
ee88f2f to
4e37187
Compare
4e37187 to
3f090d8
Compare
3f090d8 to
f732b85
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7ab6c91. Configure here.
| bindings: {rollback_error}. Newly created API key {new_api_key_id} was retained \ | ||
| because the endpoint may still reference it" | ||
| ); | ||
| return Err(repair_error); |
There was a problem hiding this comment.
Rollback failure desyncs credentials
Medium Severity
When endpoint bind succeeds but local persistence fails, and restore_repair_endpoint then also fails, fail_after_repair_binding keeps the replacement key in the cloud while discarding the in-memory credentials that match that binding. Local state still points at the unbound superseded key, so queries keep failing. A later repair rotates again and leaves the retained key ID in openApiKeys with no cleanup path.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 7ab6c91. Configure here.
| client | ||
| .delete_query_endpoint_if_exists(org_id, service_id) | ||
| .await?; | ||
| } |
There was a problem hiding this comment.
Missing rollback deletes concurrent endpoint
Medium Severity
For RepairEndpointState::Missing, any failure after the repair upsert—including when create_query_endpoint itself returns an error—runs delete_query_endpoint_if_exists. If another process created an endpoint after the initial 404 GET, that rollback deletes their configuration. Provisioning only deletes the new API key on bind failure and does not remove the endpoint.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 7ab6c91. Configure here.


Summary
cloud service repair-query-key <service-id>write operation with human and JSON outputDesign notes
The dedicated subcommand intentionally supersedes the over-engineering concern raised on #508. Issue #454 calls for an explicit recovery operation, and keeping it as a separate write command avoids mutating local credentials as a side effect of a failed read. Although users can create a key and update a query endpoint with existing commands, that manual sequence does not provide the repair flow ownership checks, rollback ordering, exact pending-cleanup metadata, or same-project repair serialization. Repair is never triggered automatically.
Concurrent repairs in the same project snapshot the expected stale credential before waiting and reuse a changed lock winner without rotating it again. A repair explicitly started while a newly provisioned endpoint is still converging can still rotate that new key; the README tells users to wait for first-use provisioning and readiness to finish.
The endpoint API has no conditional update operation. Repair therefore reads and rewrites the complete endpoint configuration while replacing only the owned key ID. It preserves the configuration it read, but a concurrent endpoint update can be overwritten; this limitation and the required operational serialization are documented in the README.
Tests
cargo test -p clickhousectl cloud::service_query::testscargo test -p clickhousectl --test cli_request_shape_test repair -- --nocapturecargo test -p clickhousectl --no-fail-fastcargo check -p clickhousectl --all-targetscargo clippy -p clickhousectl --all-targets -- -D warningscargo fmt --all --checkCloses #454