Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,20 @@ pscale keyspace parameters changes show <database> <branch> <keyspace> <change-i
pscale keyspace parameters changes cancel <database> <branch> <keyspace> <change-id> --org <org> --format json
```

Branch VTGate parameters: list them with `branch vtgate parameters`, then change them with `branch vtgate update`. Prefix each parameter with `vtgate.`; `--reset` sets a parameter back to its default. The change rolls out to the branch's VTGates; follow it with `branch vtgate changes list`. Only one unfinished VTGate change can exist on a branch at a time. VTGate size and count are changed with `branch vtgate resize`, not `update`.

```bash
pscale branch vtgate parameters <database> <branch> --org <org> --format json
pscale branch vtgate update <database> <branch> --org <org> --format json \
--parameters vtgate.max_memory_rows=500000 \
--parameters vtgate.query-timeout=30000
pscale branch vtgate update <database> <branch> --org <org> --format json \
--reset vtgate.query-timeout
pscale branch vtgate changes list <database> <branch> --org <org> --format json
pscale branch vtgate changes show <database> <branch> <change-id> --org <org> --format json
pscale branch vtgate changes cancel <database> <branch> <change-id> --org <org> --format json
```

## Vitess MoveTables

Copy tables between keyspaces with `pscale branch vtctld move-tables`. `pscale workflow` is deprecated; use `move-tables` instead. JSON output includes `next_steps` — follow those commands. Typical order: create the target keyspace (`keyspace create` or `keyspace create-external`), create the workflow, poll `status`, switch replica traffic, then primary traffic (ask the user first), then `complete --dry-run` and `complete` after approval. Create with `--auto-start=false` to leave streams stopped, then `start` when you are ready.
Expand Down
5 changes: 4 additions & 1 deletion internal/cmd/branch/vtgate.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,14 @@ type branchVTGateResize struct {
func VtgateCmd(ch *cmdutil.Helper) *cobra.Command {
cmd := &cobra.Command{
Use: "vtgate <command>",
Short: "Manage VTGate size for a Vitess branch",
Short: "Manage VTGate size and parameters for a Vitess branch",
}

cmd.AddCommand(VtgateShowCmd(ch))
cmd.AddCommand(VtgateResizeCmd(ch))
cmd.AddCommand(VtgateParametersCmd(ch))
cmd.AddCommand(VtgateUpdateCmd(ch))
cmd.AddCommand(VtgateChangesCmd(ch))
return cmd
}

Expand Down
276 changes: 276 additions & 0 deletions internal/cmd/branch/vtgate_parameters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
package branch

import (
"fmt"

"github.com/planetscale/cli/internal/cmd/vitessparams"
"github.com/planetscale/cli/internal/cmdutil"
ps "github.com/planetscale/cli/internal/planetscale"
"github.com/planetscale/cli/internal/printer"
"github.com/spf13/cobra"
)

var vtgateParameterNamespaces = []string{"vtgate"}

// VtgateParametersCmd lists the VTGate parameters of a Vitess branch.
func VtgateParametersCmd(ch *cmdutil.Helper) *cobra.Command {
return &cobra.Command{
Use: "parameters <database> <branch>",
Short: "List the VTGate parameters of a Vitess branch",
Long: "List the VTGate parameters of a Vitess branch, including their current and default values.\n\nTo change parameters, use 'pscale branch vtgate update <database> <branch> --parameters vtgate.name=value'.",
Args: cmdutil.RequiredArgs("database", "branch"),
Aliases: []string{"params"},
RunE: func(cmd *cobra.Command, args []string) error {
database, branch := args[0], args[1]

client, err := ch.Client()
if err != nil {
return err
}

end := ch.Printer.PrintProgress(fmt.Sprintf("Fetching VTGate parameters for branch %s in %s...", printer.BoldBlue(branch), printer.BoldBlue(database)))
defer end()

parameters, err := client.DatabaseBranches.ListVTGateParameters(cmd.Context(), &ps.ListVTGateParametersRequest{
Organization: ch.Config.Organization,
Database: database,
Branch: branch,
})
if err != nil {
return vtgateBranchError(ch, err, database, branch)
}
end()

return ch.Printer.PrintResource(vitessparams.ToParameters(parameters))
},
}
}

// VtgateUpdateCmd changes the VTGate parameters of a Vitess branch.
func VtgateUpdateCmd(ch *cmdutil.Helper) *cobra.Command {
var flags struct {
parameters []string
resets []string
}

cmd := &cobra.Command{
Use: "update <database> <branch>",
Short: "Change the VTGate parameters of a Vitess branch",
Long: `Change VTGate parameters on a Vitess branch. Pass each parameter as vtgate.name=value, and pass --reset vtgate.name to set a parameter back to its default.

The change is rolled out to the branch's VTGates. Use 'pscale branch vtgate changes list' to follow the rollout. To change the size or number of VTGates, use 'pscale branch vtgate resize'.`,
Example: ` pscale branch vtgate update <database> <branch> \
--parameters vtgate.max_memory_rows=500000 \
--parameters vtgate.query-timeout=30000

pscale branch vtgate update <database> <branch> --reset vtgate.query-timeout`,
Args: cmdutil.RequiredArgs("database", "branch"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
database, branch := args[0], args[1]

changes, err := vitessparams.ParseChanges(flags.parameters, flags.resets, vtgateParameterNamespaces, "vtgate.max_memory_rows=500000")
if err != nil {
return err
}

client, err := ch.Client()
if err != nil {
return err
}

end := ch.Printer.PrintProgress(fmt.Sprintf("Submitting VTGate parameter changes for branch %s in %s...", printer.BoldBlue(branch), printer.BoldBlue(database)))
defer end()

draft, err := client.DatabaseBranches.CreateVTGateConfigChange(ctx, &ps.CreateVTGateConfigChangeRequest{
Organization: ch.Config.Organization,
Database: database,
Branch: branch,
Options: changes["vtgate"],
})
if err != nil {
return vtgateBranchError(ch, err, database, branch)
}

if err := client.DatabaseBranches.SubmitConfigChanges(ctx, &ps.SubmitConfigChangesRequest{
Organization: ch.Config.Organization,
Database: database,
Branch: branch,
IDs: []string{draft.ID},
}); err != nil {
_ = client.DatabaseBranches.CancelVTGateConfigChange(ctx, &ps.CancelVTGateConfigChangeRequest{
Organization: ch.Config.Organization,
Database: database,
Branch: branch,
ID: draft.ID,
})
return vtgateBranchError(ch, err, database, branch)
}

change, err := client.DatabaseBranches.GetVTGateConfigChange(ctx, &ps.GetVTGateConfigChangeRequest{
Organization: ch.Config.Organization,
Database: database,
Branch: branch,
ID: draft.ID,
})
if err != nil {
change = draft
}
end()

return ch.Printer.PrintResource(vitessparams.ToConfigChange(change))
},
}

cmd.Flags().StringArrayVar(&flags.parameters, "parameters", nil, "Set a parameter as vtgate.name=value (e.g. vtgate.max_memory_rows=500000). Repeatable. Use 'pscale branch vtgate parameters' to see available parameters.")
cmd.Flags().StringArrayVar(&flags.resets, "reset", nil, "Set a parameter back to its default, as vtgate.name (e.g. vtgate.query-timeout). Repeatable.")

return cmd
}

// VtgateChangesCmd manages VTGate parameter changes on a Vitess branch.
func VtgateChangesCmd(ch *cmdutil.Helper) *cobra.Command {
cmd := &cobra.Command{
Use: "changes <command>",
Short: "Manage VTGate parameter changes for a Vitess branch",
}
cmd.AddCommand(vtgateChangesListCmd(ch), vtgateChangesShowCmd(ch), vtgateChangesCancelCmd(ch))
return cmd
}

func vtgateChangesListCmd(ch *cmdutil.Helper) *cobra.Command {
var flags struct {
page int
perPage int
}

cmd := &cobra.Command{
Use: "list <database> <branch>",
Short: "List VTGate parameter changes for a Vitess branch",
Args: cmdutil.RequiredArgs("database", "branch"),
Aliases: []string{"ls"},
RunE: func(cmd *cobra.Command, args []string) error {
database, branch := args[0], args[1]

client, err := ch.Client()
if err != nil {
return err
}

end := ch.Printer.PrintProgress(fmt.Sprintf("Fetching VTGate parameter changes for branch %s in %s...", printer.BoldBlue(branch), printer.BoldBlue(database)))
defer end()

changes, err := client.DatabaseBranches.ListVTGateConfigChanges(cmd.Context(), &ps.ListVTGateConfigChangesRequest{
Organization: ch.Config.Organization,
Database: database,
Branch: branch,
Page: flags.page,
PerPage: flags.perPage,
})
if err != nil {
return vtgateBranchError(ch, err, database, branch)
}
end()

if len(changes) == 0 && ch.Printer.Format() == printer.Human {
ch.Printer.Printf("No VTGate parameter changes found for branch %s in %s.\n", printer.BoldBlue(branch), printer.BoldBlue(database))
return nil
}

return ch.Printer.PrintResource(vitessparams.ToConfigChanges(changes))
},
}

cmd.Flags().IntVar(&flags.page, "page", 0, "Page number to fetch")
cmd.Flags().IntVar(&flags.perPage, "per-page", 25, "Number of results per page")
return cmd
}

func vtgateChangesShowCmd(ch *cmdutil.Helper) *cobra.Command {
return &cobra.Command{
Use: "show <database> <branch> <change-id>",
Short: "Show a VTGate parameter change for a Vitess branch",
Args: cmdutil.RequiredArgs("database", "branch", "change-id"),
Aliases: []string{"get"},
RunE: func(cmd *cobra.Command, args []string) error {
database, branch, changeID := args[0], args[1], args[2]

client, err := ch.Client()
if err != nil {
return err
}

end := ch.Printer.PrintProgress(fmt.Sprintf("Fetching VTGate parameter change %s for branch %s...", printer.BoldBlue(changeID), printer.BoldBlue(branch)))
defer end()

change, err := client.DatabaseBranches.GetVTGateConfigChange(cmd.Context(), &ps.GetVTGateConfigChangeRequest{
Organization: ch.Config.Organization,
Database: database,
Branch: branch,
ID: changeID,
})
if err != nil {
if cmdutil.ErrCode(err) == ps.ErrNotFound {
return vtgateChangeNotFoundError(changeID, database, branch)
}
return cmdutil.HandleError(err)
}
end()

return ch.Printer.PrintResource(vitessparams.ToConfigChange(change))
},
}
}

func vtgateChangesCancelCmd(ch *cmdutil.Helper) *cobra.Command {
return &cobra.Command{
Use: "cancel <database> <branch> <change-id>",
Short: "Cancel a pending VTGate parameter change for a Vitess branch",
Args: cmdutil.RequiredArgs("database", "branch", "change-id"),
RunE: func(cmd *cobra.Command, args []string) error {
database, branch, changeID := args[0], args[1], args[2]

client, err := ch.Client()
if err != nil {
return err
}

end := ch.Printer.PrintProgress(fmt.Sprintf("Canceling VTGate parameter change %s for branch %s...", printer.BoldBlue(changeID), printer.BoldBlue(branch)))
defer end()

if err := client.DatabaseBranches.CancelVTGateConfigChange(cmd.Context(), &ps.CancelVTGateConfigChangeRequest{
Organization: ch.Config.Organization,
Database: database,
Branch: branch,
ID: changeID,
}); err != nil {
if cmdutil.ErrCode(err) == ps.ErrNotFound {
return vtgateChangeNotFoundError(changeID, database, branch)
}
return cmdutil.HandleError(err)
}
end()

if ch.Printer.Format() == printer.Human {
ch.Printer.Printf("Canceled VTGate parameter change %s for branch %s in %s.\n", printer.BoldBlue(changeID), printer.BoldBlue(branch), printer.BoldBlue(database))
return nil
}

return ch.Printer.PrintResource(map[string]string{
"result": "change canceled",
"change_id": changeID,
})
},
}
}

func vtgateBranchError(ch *cmdutil.Helper, err error, database, branch string) error {
if cmdutil.ErrCode(err) == ps.ErrNotFound {
return fmt.Errorf("database %s or branch %s does not exist in organization %s", printer.BoldBlue(database), printer.BoldBlue(branch), printer.BoldBlue(ch.Config.Organization))
}
return cmdutil.HandleError(err)
}

func vtgateChangeNotFoundError(changeID, database, branch string) error {
return fmt.Errorf("VTGate parameter change %s does not exist for branch %s in %s", printer.BoldBlue(changeID), printer.BoldBlue(branch), printer.BoldBlue(database))
}
46 changes: 2 additions & 44 deletions internal/cmd/keyspace/parameters.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package keyspace

import (
"encoding/json"
"fmt"
"slices"
"strings"

"github.com/planetscale/cli/internal/cmd/vitessparams"
"github.com/planetscale/cli/internal/cmdutil"
ps "github.com/planetscale/cli/internal/planetscale"
"github.com/planetscale/cli/internal/printer"
Expand Down Expand Up @@ -67,7 +67,7 @@ To change parameters, use 'pscale keyspace parameters set <database> <branch> <k
selected = append(selected, parameters.MySQL...)
}

return ch.Printer.PrintResource(toKeyspaceParameters(selected))
return ch.Printer.PrintResource(vitessparams.ToParameters(selected))
}

registerFlags := func(cmd *cobra.Command) {
Expand Down Expand Up @@ -100,45 +100,3 @@ To change parameters, use 'pscale keyspace parameters set <database> <branch> <k
func keyspaceNotFoundError(ch *cmdutil.Helper, database, branch, keyspace string) error {
return fmt.Errorf("keyspace %s does not exist in branch %s (database: %s, organization: %s)", printer.BoldBlue(keyspace), printer.BoldBlue(branch), printer.BoldBlue(database), printer.BoldBlue(ch.Config.Organization))
}

type keyspaceParameter struct {
Namespace string `header:"namespace" json:"namespace"`
Name string `header:"name" json:"name"`
Value string `header:"value" json:"value"`
Default string `header:"default" json:"default_value"`
Type string `header:"type" json:"parameter_type"`
Override bool `header:"override" json:"override"`

orig *ps.VitessParameter
}

func toKeyspaceParameters(parameters []*ps.VitessParameter) []*keyspaceParameter {
out := make([]*keyspaceParameter, 0, len(parameters))
for _, param := range parameters {
out = append(out, &keyspaceParameter{
Namespace: param.Component,
Name: param.Name,
Value: stringValue(param.Value),
Default: stringValue(param.DefaultValue),
Type: param.ParameterType,
Override: param.Override,
orig: param,
})
}
return out
}

func (p *keyspaceParameter) MarshalJSON() ([]byte, error) {
return json.MarshalIndent(p.orig, "", " ")
}

func (p *keyspaceParameter) MarshalCSVValue() interface{} {
return []*keyspaceParameter{p}
}

func stringValue(value *string) string {
if value == nil {
return ""
}
return *value
}
Loading
Loading