Skip to content

Commit c0549a7

Browse files
authored
Merge pull request #1146 from planetscale/piki/reassign-objects
Implement `role reset` and `role reassign`
2 parents f669f44 + 64dd3cb commit c0549a7

File tree

6 files changed

+243
-3
lines changed

6 files changed

+243
-3
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ require (
2525
github.com/mattn/go-shellwords v1.0.12
2626
github.com/mitchellh/go-homedir v1.1.0
2727
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
28-
github.com/planetscale/planetscale-go v0.142.0
28+
github.com/planetscale/planetscale-go v0.143.0
2929
github.com/planetscale/psdb v0.0.0-20250717190954-65c6661ab6e4
3030
github.com/planetscale/psdbproxy v0.0.0-20250728082226-3f4ea3a74ec7
3131
github.com/spf13/cobra v1.10.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjL
175175
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
176176
github.com/planetscale/noglog v0.2.1-0.20210421230640-bea75fcd2e8e h1:MZ8D+Z3m2vvqGZLvoQfpaGg/j1fNDr4j03s3PRz4rVY=
177177
github.com/planetscale/noglog v0.2.1-0.20210421230640-bea75fcd2e8e/go.mod h1:hwAsSPQdvPa3WcfKfzTXxtEq/HlqwLjQasfO6QbGo4Q=
178-
github.com/planetscale/planetscale-go v0.142.0 h1:luApvfbD2xcg8gS9TmUC7nFINl4t3T9qzX7Ed8rYlwQ=
179-
github.com/planetscale/planetscale-go v0.142.0/go.mod h1:PheYDHAwF14wfCBak1M0J64AdPW8NUeyvgPgWqe7zpI=
178+
github.com/planetscale/planetscale-go v0.143.0 h1:3LeJXrPYIkXdxUcGIK5rhitA44D6HMp9ZxmVTy7ozO0=
179+
github.com/planetscale/planetscale-go v0.143.0/go.mod h1:PheYDHAwF14wfCBak1M0J64AdPW8NUeyvgPgWqe7zpI=
180180
github.com/planetscale/psdb v0.0.0-20250717190954-65c6661ab6e4 h1:Xv5pj20Rhfty1Tv0OVcidg4ez4PvGrpKvb6rvUwQgDs=
181181
github.com/planetscale/psdb v0.0.0-20250717190954-65c6661ab6e4/go.mod h1:M52h5IWxAcbdQ1hSZrLAGQC4ZXslxEsK/Wh9nu3wdWs=
182182
github.com/planetscale/psdbproxy v0.0.0-20250728082226-3f4ea3a74ec7 h1:aRd6vdE1fyuSI4RVj7oCr8lFmgqXvpnPUmN85VbZCp8=

internal/cmd/role/reassign.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package role
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
8+
"github.com/AlecAivazis/survey/v2"
9+
"github.com/AlecAivazis/survey/v2/terminal"
10+
"github.com/planetscale/cli/internal/cmdutil"
11+
"github.com/planetscale/cli/internal/printer"
12+
ps "github.com/planetscale/planetscale-go/planetscale"
13+
14+
"github.com/spf13/cobra"
15+
)
16+
17+
func ReassignCmd(ch *cmdutil.Helper) *cobra.Command {
18+
var flags struct {
19+
force bool
20+
successor string
21+
}
22+
23+
cmd := &cobra.Command{
24+
Use: "reassign <database> <branch> <role-id>",
25+
Short: "Reassign objects owned by a role to another role",
26+
Args: cmdutil.RequiredArgs("database", "branch", "role-id"),
27+
RunE: func(cmd *cobra.Command, args []string) error {
28+
ctx := cmd.Context()
29+
database := args[0]
30+
branch := args[1]
31+
roleID := args[2]
32+
33+
if flags.successor == "" {
34+
return fmt.Errorf("--successor flag is required")
35+
}
36+
37+
client, err := ch.Client()
38+
if err != nil {
39+
return err
40+
}
41+
42+
if !flags.force {
43+
if ch.Printer.Format() != printer.Human {
44+
return fmt.Errorf("cannot reassign role objects with the output format %q (run with --force to override)", ch.Printer.Format())
45+
}
46+
47+
confirmationName := fmt.Sprintf("%s/%s/%s", database, branch, roleID)
48+
if !printer.IsTTY {
49+
return fmt.Errorf("cannot confirm object reassignment for role %q (run with --force to override)", confirmationName)
50+
}
51+
52+
confirmationMessage := fmt.Sprintf("%s %s %s", printer.Bold("Please type"),
53+
printer.BoldBlue(confirmationName), printer.Bold("to confirm:"))
54+
55+
prompt := &survey.Input{
56+
Message: confirmationMessage,
57+
}
58+
59+
var userInput string
60+
err := survey.AskOne(prompt, &userInput)
61+
if err != nil {
62+
if err == terminal.InterruptErr {
63+
os.Exit(0)
64+
} else {
65+
return err
66+
}
67+
}
68+
69+
// If the confirmations don't match up, let's return an error.
70+
if userInput != confirmationName {
71+
return errors.New("incorrect role identifier entered, skipping object reassignment")
72+
}
73+
}
74+
75+
end := ch.Printer.PrintProgress(fmt.Sprintf("Reassigning objects from role %s to %s in %s/%s...",
76+
printer.BoldBlue(roleID), printer.BoldBlue(flags.successor), printer.BoldBlue(database), printer.BoldBlue(branch)))
77+
defer end()
78+
79+
err = client.PostgresRoles.ReassignObjects(ctx, &ps.ReassignPostgresRoleObjectsRequest{
80+
Organization: ch.Config.Organization,
81+
Database: database,
82+
Branch: branch,
83+
RoleId: roleID,
84+
Successor: flags.successor,
85+
})
86+
if err != nil {
87+
switch cmdutil.ErrCode(err) {
88+
case ps.ErrNotFound:
89+
return fmt.Errorf("role %s does not exist in branch %s of database %s (organization: %s)",
90+
printer.BoldBlue(roleID), printer.BoldBlue(branch), printer.BoldBlue(database), printer.BoldBlue(ch.Config.Organization))
91+
default:
92+
return cmdutil.HandleError(err)
93+
}
94+
}
95+
96+
end()
97+
98+
if ch.Printer.Format() == printer.Human {
99+
ch.Printer.Printf("Objects owned by role %s were successfully reassigned to %s in %s/%s.\n",
100+
printer.BoldBlue(roleID), printer.BoldBlue(flags.successor), printer.BoldBlue(database), printer.BoldBlue(branch))
101+
return nil
102+
}
103+
104+
return ch.Printer.PrintResource(
105+
map[string]string{
106+
"result": "objects reassigned",
107+
"role_id": roleID,
108+
"successor": flags.successor,
109+
"branch": branch,
110+
},
111+
)
112+
},
113+
}
114+
115+
cmd.Flags().BoolVar(&flags.force, "force", false, "Reassign objects without confirmation")
116+
cmd.Flags().StringVar(&flags.successor, "successor", "", "Role to transfer ownership to (required)")
117+
cmd.MarkFlagRequired("successor") // nolint:errcheck
118+
119+
return cmd
120+
}

internal/cmd/role/reset.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package role
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
8+
"github.com/AlecAivazis/survey/v2"
9+
"github.com/AlecAivazis/survey/v2/terminal"
10+
"github.com/planetscale/cli/internal/cmdutil"
11+
"github.com/planetscale/cli/internal/printer"
12+
ps "github.com/planetscale/planetscale-go/planetscale"
13+
14+
"github.com/spf13/cobra"
15+
)
16+
17+
func ResetCmd(ch *cmdutil.Helper) *cobra.Command {
18+
var flags struct {
19+
force bool
20+
}
21+
22+
cmd := &cobra.Command{
23+
Use: "reset <database> <branch> <role-id>",
24+
Short: "Reset a role's password",
25+
Args: cmdutil.RequiredArgs("database", "branch", "role-id"),
26+
RunE: func(cmd *cobra.Command, args []string) error {
27+
ctx := cmd.Context()
28+
database := args[0]
29+
branch := args[1]
30+
roleID := args[2]
31+
32+
client, err := ch.Client()
33+
if err != nil {
34+
return err
35+
}
36+
37+
if !flags.force {
38+
if ch.Printer.Format() != printer.Human {
39+
return fmt.Errorf("cannot reset role password with the output format %q (run with --force to override)", ch.Printer.Format())
40+
}
41+
42+
confirmationName := fmt.Sprintf("%s/%s/%s", database, branch, roleID)
43+
if !printer.IsTTY {
44+
return fmt.Errorf("cannot confirm password reset for role %q (run with --force to override)", confirmationName)
45+
}
46+
47+
confirmationMessage := fmt.Sprintf("%s %s %s", printer.Bold("Please type"),
48+
printer.BoldBlue(confirmationName), printer.Bold("to confirm:"))
49+
50+
prompt := &survey.Input{
51+
Message: confirmationMessage,
52+
}
53+
54+
var userInput string
55+
err := survey.AskOne(prompt, &userInput)
56+
if err != nil {
57+
if err == terminal.InterruptErr {
58+
os.Exit(0)
59+
} else {
60+
return err
61+
}
62+
}
63+
64+
// If the confirmations don't match up, let's return an error.
65+
if userInput != confirmationName {
66+
return errors.New("incorrect role identifier entered, skipping password reset")
67+
}
68+
}
69+
70+
end := ch.Printer.PrintProgress(fmt.Sprintf("Resetting password for role %s in %s/%s...",
71+
printer.BoldBlue(roleID), printer.BoldBlue(database), printer.BoldBlue(branch)))
72+
defer end()
73+
74+
role, err := client.PostgresRoles.ResetPassword(ctx, &ps.ResetPostgresRolePasswordRequest{
75+
Organization: ch.Config.Organization,
76+
Database: database,
77+
Branch: branch,
78+
RoleId: roleID,
79+
})
80+
if err != nil {
81+
switch cmdutil.ErrCode(err) {
82+
case ps.ErrNotFound:
83+
return fmt.Errorf("role %s does not exist in branch %s of database %s (organization: %s)",
84+
printer.BoldBlue(roleID), printer.BoldBlue(branch), printer.BoldBlue(database), printer.BoldBlue(ch.Config.Organization))
85+
default:
86+
return cmdutil.HandleError(err)
87+
}
88+
}
89+
90+
end()
91+
92+
if ch.Printer.Format() == printer.Human {
93+
ch.Printer.Printf("Password for role %s was successfully reset in %s/%s.\n",
94+
printer.BoldBlue(roleID), printer.BoldBlue(database), printer.BoldBlue(branch))
95+
}
96+
97+
return ch.Printer.PrintResource(toPostgresRole(role))
98+
},
99+
}
100+
101+
cmd.Flags().BoolVar(&flags.force, "force", false, "Reset password without confirmation")
102+
103+
return cmd
104+
}

internal/cmd/role/role.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ func RoleCmd(ch *cmdutil.Helper) *cobra.Command {
2121
DeleteCmd(ch),
2222
GetCmd(ch),
2323
ListCmd(ch),
24+
ReassignCmd(ch),
2425
RenewCmd(ch),
26+
ResetCmd(ch),
2527
ResetDefaultCmd(ch),
2628
UpdateCmd(ch),
2729
)

internal/mock/postgres_roles.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ type PostgresRolesService struct {
2121
DeleteFnInvoked bool
2222
ResetDefaultRoleFn func(context.Context, *ps.ResetDefaultRoleRequest) (*ps.PostgresRole, error)
2323
ResetDefaultRoleFnInvoked bool
24+
ResetPasswordFn func(context.Context, *ps.ResetPostgresRolePasswordRequest) (*ps.PostgresRole, error)
25+
ResetPasswordFnInvoked bool
26+
ReassignObjectsFn func(context.Context, *ps.ReassignPostgresRoleObjectsRequest) error
27+
ReassignObjectsFnInvoked bool
2428
}
2529

2630
func (s *PostgresRolesService) List(ctx context.Context, req *ps.ListPostgresRolesRequest, opts ...ps.ListOption) ([]*ps.PostgresRole, error) {
@@ -57,3 +61,13 @@ func (s *PostgresRolesService) ResetDefaultRole(ctx context.Context, req *ps.Res
5761
s.ResetDefaultRoleFnInvoked = true
5862
return s.ResetDefaultRoleFn(ctx, req)
5963
}
64+
65+
func (s *PostgresRolesService) ResetPassword(ctx context.Context, req *ps.ResetPostgresRolePasswordRequest) (*ps.PostgresRole, error) {
66+
s.ResetPasswordFnInvoked = true
67+
return s.ResetPasswordFn(ctx, req)
68+
}
69+
70+
func (s *PostgresRolesService) ReassignObjects(ctx context.Context, req *ps.ReassignPostgresRoleObjectsRequest) error {
71+
s.ReassignObjectsFnInvoked = true
72+
return s.ReassignObjectsFn(ctx, req)
73+
}

0 commit comments

Comments
 (0)