Add cloud postgres subcommand tree#127
Merged
Merged
Conversation
Plumbs all 13 ClickHouse Cloud managed Postgres operations into the CLI under `clickhousectl cloud postgres ...` — CRUD, lifecycle (restart/ promote/switchover), CA certs, runtime config (get/replace/patch with --set key=value overrides), password reset, read replica creation, and PITR restore. Lives in its own src/cloud/postgres.rs module with 33 new unit + parse tests and the full write-classification coverage. Closes #116 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the ClickHouse service lifecycle test (create, wait-running, list, certs, config get, PATCH tags, password reset, restart, delete) against the Postgres endpoints, wired into the scheduled Cloud Integration workflow. Password step treats a successful 200 as the pass condition: per the OpenAPI spec, PostgresServicePasswordResource.password is only populated when the request omits `password`, so the supplied-password path returns empty by design. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tables now render using only ASCII `|` and `-` instead of the non-standard rounded box-drawing characters, so output is readable in minimal terminals and log aggregators and pasteable into issues/PRs. Closes #126
4 tasks
Closes #133. Uses the is-ai-agent crate to detect when the CLI is invoked under a known agent (Claude Code, Cursor, Gemini CLI, Codex, Goose, Devin, etc.) and appends an agent=<id> query param to outbound requests to ClickHouse-owned hosts (builds.clickhouse.com, packages.clickhouse.com, api.clickhouse.cloud). GitHub and other third-party hosts are not annotated. The cloud library gains a generic Client::with_extra_query_params builder so the CLI can attach the tag to every request.
Three of the four credential branches in CloudClient::new differed only in which (key, secret) pair they pulled and which AuthSource label to attach. Introduce ResolvedAuth + resolve_auth() to walk the precedence ladder once, then build the lib client and tag it with the agent param at a single site. resolve_active_auth_source becomes a thin wrapper that preserves its lenient half-set CLI-flag behavior for cloud auth status.
The helper is only called from cloud auth status, which never has --api-key/--api-secret to pass (the subcommand doesn't accept them). The half-set lenient branch and its test were protecting a contract no production caller exercises. Inlining the only sensible call removes the dead parameters and the dead branch, leaving a one-line wrapper that documents its actual purpose: peek at credential precedence without erroring on the empty case (which auth status needs to render no-creds-configured correctly).
The local AgentId -> kebab-case match arm was duplicating a mapping the crate already maintains for its AGENT= env var parser. is-ai-agent 0.2.1 exposes AgentId::as_str returning the canonical kebab-case id (claude-code, gemini-cli, etc.) — the inverse of the parser. Switch to it. Replaces our 12-arm match plus exhaustive variant test with a single delegation and a contract test against the upstream lookup. New agents added upstream automatically flow through without code changes here.
…arams Rather than thread an agent search param through every clickhouse.com request (with a URL-domain gate, two helpers, and a generic extra-query-params feature on the cloud library), fold the signal into the User-Agent header that every outbound request already carries: clickhousectl/0.1.18 (agent=claude-code). RFC 7231 allows parenthesised comments in User-Agent, and this matches conventional shapes (Mozilla/5.0 etc). Detection still uses is-ai-agent. The change deletes the agent_signal module, the cloud library extra_query_params API + tests, the URL-host parser, and the per-call-site wrappers in version_manager — net -146 lines vs the previous implementation. Every reqwest::Client::builder() in the codebase already calls user_agent::user_agent(), so the new attribution flows through with zero per-call-site wiring.
It's now a one-line implementation detail of an analytics signal — not user-facing functionality, not configurable, not surprising for a future reader to understand from the user_agent.rs source. Doesn't earn a documentation entry.
Pull in upstream security fixes flagged by Dependabot. Both are transitive dependencies; lockfile-only update, no API or behaviour changes. Build and full workspace tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bump clickhousectl to 0.2.0
Tag clickhouse.com requests with detected AI agent
Switch tabled tables to markdown style
iskakaushik
approved these changes
May 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #116.
Stacked on top of #120 — merge that first.
Summary
Plumbs all 13 ClickHouse Cloud managed Postgres operations into the CLI under
clickhousectl cloud postgres ...:list,get,create,update,deleterestart,promote,switchover(flat subcommands, mirroringservice start|stop)certs get [--output PATH]— raw PEM to stdout or file (mode 0600 on unix), JSON mode wraps in{"certificate": ...}config get|replace|patch— patch accepts either--set key=value(repeatable, parses values as JSON with string fallback) or--file PATH; mutually exclusivereset-password --password VALUE | --generateread-replica create <pg-id> --name …restore <pg-id> --name … --restore-target <ISO8601>All commands support
--json,--debug,--org-id(auto-resolved), and inherit the OAuth/API-key auth-precedence and bearer-write-block that the cloud tree already uses.Design
Everything lives in a single new module
crates/clickhousectl/src/cloud/postgres.rs(~1150 lines) — thePostgresCommandsclap enum, 13 handler functions, internal option structs, helpers, tests, and theis_write()classifier. This is a departure from thecli.rs+commands.rssplit used for Service/Org/Backup, but it keeps the already-2400-linecommands.rsfrom growing another 600 lines of Postgres-specific helpers (parse_pg_config_overrides,render_postgres_service, JSON-file loader, PEM file writer, password generator/validator).cloud/cli.rsadds oneCloudCommands::Postgres { command }variant plus a one-line delegation inis_write_command()→command.is_write(), keeping the exhaustive-match compiler guard intact.cloud/commands.rsunchanged except for bumpingresolve_org_id,parse_tag,parse_tags,parse_serde_enumfrom private topub(super)sopostgres.rscan reuse them.cloud/cli.rs::parse_datetimebumped topub(super)for--restore-targetvalidation.uuidgains thev4feature (for--generatepassword, which concatenates two UUIDv4s and prefixesA1to satisfy upper+lower+digit).UX notes
--sizeaccepts any string (e.g.m7i.2xlarge); server validates. Same posture as--region.--pg-versionconstrained to18|17|16;--ha-typetonone|async|sync;--providerdefaults toaws.update --add-tag K=V/--remove-tag Kmerges against current tags by fetching the pg service first and diffing.Test plan
cargo test -p clickhousectl— 234 passing (27 new inpostgres.rs, plus 14 new write-classification assertions incli.rs::tests)cargo clippy -p clickhousectl --all-targets— cleancargo run -p clickhousectl -- cloud postgres list --org-id …hits the API end-to-end (returns well-formedFORBIDDEN: early access API endpointwhen auth'd via OAuth — expected, since pg is beta-gated to API-key auth)cloud postgres --helprenders the full subcommand tree with theCONTEXT FOR AGENTSblockNew tests
CLI-parse (17): list/get/delete, create minimal/all-flags/rejects-missing/rejects-invalid-version, update tag-diff/no-fields, certs get stdout+output, config get, config replace requires-file, config patch with-set/with-file/rejects-set+file-together, reset-password with-password/with-generate/rejects-both, restore valid-rfc3339/rejects-invalid-datetime, read-replica create, restart+promote+switchover.
Helper unit (6):
parse_pg_config_overridesnumeric/string coercion, malformed rejection, last-wins on duplicates;validate_password_rules;generated_password_is_compliant;merge_tags_adds_and_removes.Write-classification (14 assertions): extends the existing
is_write_command_read_only_commandsandis_write_command_destructive_commandstests with all 14 Postgres variants.🤖 Generated with Claude Code