Repository navigation
Add experimental branch commands and MCP tools - #265
Closed
nathanjcochran wants to merge 30 commits into
Closed
nathanjcochran wants to merge 30 commits into
nathanjcochran wants to merge 30 commits into
Conversation
Copies the spec from the project-level branches API proposal and regenerates the API client and mocks.
Adds 'tiger branch' (create, list, get, delete, start, stop, resize, rename, update-password) and 'tiger service branch', behind TIGER_EXPERIMENTAL and backed by the preview project-level branch endpoints. Branch commands take branch IDs only for now; create and list take a service by ID or name. A branch starts with its parent's password, which the platform can't return, so create copies the parent's saved password into a new entry for the branch. Branches count as DEV for the read-only gate. The wait loop is shared between services and branches.
Adds branch_create, branch_list, branch_get, branch_delete, branch_start, branch_stop, branch_resize, branch_rename, and branch_update_password, registered only behind TIGER_EXPERIMENTAL. They mirror the branch CLI commands and the existing service tools, taking IDs only. Branches count as DEV, so none of them prompt for confirmation and read_only=prod leaves them usable.
A branch no longer becomes the default on create, so 'tiger branch create' and 'tiger service branch' lose --no-set-default and branch_create loses set_default. Since service_id only ever holds a service ID, the branch commands no longer fall back to it and always take an explicit branch ID.
Syncs openapi.yaml with the API's service-nested branch endpoints and its new resolve endpoint, which turns a ref (ID or name) into the service, branch, or read replica set it names. Every ref now goes through the resolve endpoint, restricted by type: the service commands resolve services, the branch commands branches, and the db commands anything they can connect to. Branch commands now accept names, and get the parent service ID the nested endpoints need from the same lookup. The db commands and the db_query/db_schema tools can connect to branches behind the experimental gate. Service commands no longer accept read replica set IDs. A branch now gets its own initial password on create, which is saved like a new service's, replacing the copy of the parent's password.
The resolve endpoint's SERVICE type doesn't match read replica set IDs, which the service commands accept today, so they go back to resolving through the ref filter on the service list until the CLI's handling of replica sets is reworked. The branch and db commands stay on the resolve endpoint.
Creating a branch gives its tsdbadmin user a new password.
Matches the --iops flag on the service commands: it is sent only when given, and resize accepts an IOPS-only change. The branch resize endpoint requires CPU and memory, so an IOPS-only resize sends the branch's current ones. branch_create and branch_resize take a matching iops parameter.
A branch endpoint without a port is shown as no endpoint at all, in both the CLI table and the MCP branch detail, rather than with 5432 filled in.
A service endpoint or connection pooler without a port is no longer shown with 5432 or 6432 filled in, in the CLI table or the MCP service detail, and pgpass storage, which keys entries by host and port, refuses one rather than assuming 5432.
Password storage and database connections no longer take an api.Service. Password storage takes a PasswordTarget (the project, ID, and endpoint a password is keyed by), and ConnectionTarget carries just what connecting needs (kind, ID, name, status, environment, endpoint, pooler, and whose credentials to use) instead of two services. Services, branches, and read replica sets each convert into these, and BranchAsService is gone.
Brings in the branch API's latest changes: optional CPU and memory on branch resize, an internal endpoint listing every branch in a project, and the corrected 250m / 2 GB default branch size. The resolve endpoint's changes are kept on top. An IOPS-only branch resize now leaves out CPU and memory, which the API keeps, instead of sending the branch's current ones.
Uses the new endpoint listing every branch in a project. Like the service completion, each branch is offered once, by name, falling back to its ID when what is typed matches the ID instead.
Moves the branch ref helpers out of branch.go and into ref_helper.go (renamed from service_ref_helper.go), next to their service counterparts, so the two are easy to find and compare.
branch get fetches the parent service to show its name alongside its ID, and service get counts a fluid service's branches, behind the experimental gate. branch create and list already hold the parent, so they show its name too. Neither extra fetch blocks the output: a failure is a warning, and the field is left out. The branch_get and service_get tools return the same fields. The service and branch output functions now take an options struct instead of a run of positional arguments, and read the output format from the config rather than taking it separately.
The Console doesn't support branches yet, and its prototype puts them at a different path than the one the CLI printed, so the link was likely wrong. Leave it out until the Console's URL is settled.
service list, service backup list, and branch list returned early with a message before checking the output format, so -o json and -o yaml printed nothing for an empty list. They now print [], and the message is shown only for table output. Every empty-result message (including service backup region list and service metrics series, which printed theirs to stdout) goes to stderr from the output function's table case. The output*Table functions now take their writer first, matching util.SerializeToJSON and util.SerializeToYAML. CLAUDE.md spells out both conventions.
The API deletes a fluid service's branches when the service is deleted, without asking. tiger service delete and the service_delete tool now list them: the CLI before its confirmation prompt and after the delete, and the tool in its PROD confirmation prompt and its result message. The listing isn't gated, since the branches are deleted either way; only the help text and tool description mention branches behind the experimental gate. A failed listing is a warning and doesn't stop the delete.
# Conflicts: # internal/cmd/completion_helper.go # internal/cmd/completion_helper_test.go # internal/cmd/service.go # internal/cmd/service_get_test.go # internal/mcp/service_get_test.go # internal/mcp/utils.go
The branch tools returned IOPS as a top-level field because ResourceInfo had none. Now that the service IOPS work added one, put the branch's IOPS there to match the service tools.
Both the service and branch password endpoints answer success with a 204 and nothing else, but the CLI and MCP also accepted a 200. Check for the one status the API sends, as the other 204 endpoints do.
Move withGenerateSecurePassword into main_test.go, with the MCP helper's signature, and use it in the service update-password tests in place of gomock.Any() and length checks. Add a case for a failed password generation to every command and tool that generates one.
NewReplicaConnectionTarget only ever read the primary's credentials, so it takes those (from the new ServiceCredentials) instead of the primary's ConnectionTarget, and callers no longer build a target just to pass it in.
A branch's credentials now record its parent service, and the password reset helpers pick the service or branch password endpoint from them. db psql's recovery menu offers the reset for branches too, and branch update-password goes through the same helpers instead of its own copy.
Behind the experimental gate, each db command's help says it also takes a branch. A paused or unready branch is reported as a branch, not a service, in both the CLI and MCP.
It only aliased the fluid platform's configurations, which are what a branch takes since only fluid services can be branched.
The exported Resolve only filled in resolve's error wording, and had one caller in its own package. Callers now pass resolve what they're looking for directly.
Member
Author
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.
First pass at branches for Fluid Services, in the CLI and MCP. Branch commands and tools are behind
TIGER_EXPERIMENTAL. It's built on the API's service-nested branch endpoints (/projects/{project_id}/services/{service_id}/branches/...) and its new resolve endpoint (/projects/{project_id}/resolve), both synced intoopenapi.yaml. Neither is deployed yet, so this has only been tested against mocks.Resolving refs. The branch and
dbcommands resolve refs (an ID or a name) through the resolve endpoint, viacommon.Resolve, restricted by itstypeparameter:tiger branchcommands resolve only branches. That one call also gives the parent service ID the nested branch endpoints need.tiger dbcommands resolve anything they can connect to: a service, a read replica set, or (behind the experimental gate) a branch.tiger servicecommands still resolve through the ref filter on the service list, unchanged. The resolve endpoint'sSERVICEtype doesn't match read replica set IDs, which those commands accept today.CLI.
tiger branchhascreate,list,get,delete,start/resume,stop/pause,resize,rename, andupdate-password.tiger service branchis the same command astiger branch create, built by one shared builder.service_idonly ever holds a service ID, andcreatenever changes it.createandlisttake a service, falling back to the default service.createsaves the branch's initial tsdbadmin password (returned once by the API) the same wayservice create/forkdo, and ends with "Connect with: tiger db psql ".tiger servicecommands. CPU/memory are checked against the fluid combinations.createandresizetake--iops, matching the flag on the service commands: it's sent only when given, andresizeaccepts an IOPS-only change. The branch's IOPS is shown when the API returns it.get,create, andlistshow the parent service's name with its ID (service_namein JSON/YAML).getfetches the parent for it; a failed fetch is a warning, and the ID is shown alone.tiger service getshows a fluid service's branch count (branch_count), behind the gate. It lists the service's branches to count them; a failed listing is a warning, and the count is left out.MCP. There's a tool for each branch command (
branch_create,branch_list,branch_get,branch_delete,branch_start,branch_stop,branch_resize,branch_rename,branch_update_password), registered only behind the experimental gate.branch_createandbranch_resizetake aniopsparameter, like the flag. The branch tools return a branch's IOPS inresources, as the service tools do.branch_getreturns the parent'sservice_name, andservice_getreturnsbranch_countfor a fluid service behind the gate (the field is left out of its schema otherwise).db_queryanddb_schemaalso go through the resolve endpoint now, so behind the gate they accept branch IDs too.Read-only. A branch has no environment tag and always counts as DEV (
common.BranchEnvironmentTag). Soread_only=prodallows every branch operation, even on branches of a PROD service, andallblocks them all.Supporting changes.
api.Serviceand now need to work with branches too:Save/Get/Remove,SavePasswordWithResult, andGetPassword) takes aPasswordTarget: the project ID, ID, and endpoint a password is keyed by.ServicePasswordTargetandBranchPasswordTargetbuild one. The keyring key format is unchanged.ConnectionTarget, which thedbcommands and thedb_query/db_schematools connect through, held twoapi.Servicevalues (the one to connect to, and the one whose credentials to use). It now holds just what connecting needs: kind (service, read replica, or branch), ID, name, status, environment, endpoint, pooler, and the credential owner. There's a constructor for each kind.GetConnectionDetailstakes aConnectionTarget, andCheckServiceReadybecameCheckReady, taking a status.dbcommands' help (behind the gate) says they take a branch, and their errors name a branch and thetiger branchcommands (orbranch_*tools) for one.db psqlskips its read-replica menu for a branch, and its password recovery resets a branch's password through the branch endpoint.WaitForServiceandWaitForBranch.sizeCompletiontakes a configs func, so the branch commands can complete fluid-only sizes._helper.gofiles because more than one group now uses them.Changes to existing (public) commands
dbcommands now depend on the resolve endpoint (markedx-tigerdata-internal), so this can't be released before the API ships it.tiger service get/listor the MCP service detail. pgpass password storage, which keys entries by host and port, refuses such an endpoint rather than assuming 5432. Branches follow the same rule.service list,service backup list, andbranch listprint[]for an empty list with-o json/yamlinstead of nothing. Empty-result messages appear only in table output, on stderr.service deletelists branches. The API deletes a fluid service's branches along with it.tiger service deletelists them before its confirmation prompt and after the delete, andservice_deletenames them in its PROD prompt and its result. This isn't gated, since the branches go either way; only the help text and tool description mention branches behind the gate.dbmessages. "Not found" now reads "service or read replica set '…' not found". A read replica set that isn't active reads as "not ready" rather than "paused". Under discussion; likely to be reverted to the old behaviour.Follow-ups
tiger replica …or similar), thetiger servicecommands no longer need to accept replica set IDs and can move to the resolve endpoint. Until then they keep the service-list lookup, which is the only way to reach a replica set today (TODO onresolveService).branch logs. The API has no branch logs endpoint, and the PRD puts branch logs at GA./projects/<project>/branches/<branch>/overview, not under its parent service as the PRD has it, so add one once that's settled (TODO inprepareBranchForOutput).tiger service getprints/dashboard/services/<service>, a legacy path the Console redirects into the browser's active project, which may not be the CLI's. The canonical form is/projects/<project>/services/<service>.Fatalfends the handler goroutine without a response.