Skip to content

Commit 4b0f787

Browse files
committed
feat(cli): add coder users delete command
1 parent 36f3151 commit 4b0f787

File tree

4 files changed

+197
-1
lines changed

4 files changed

+197
-1
lines changed

cli/user_delete_test.go

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package cli_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/coder/coder/v2/cli/clitest"
10+
"github.com/coder/coder/v2/coderd/coderdtest"
11+
"github.com/coder/coder/v2/codersdk"
12+
"github.com/coder/coder/v2/cryptorand"
13+
"github.com/coder/coder/v2/pty/ptytest"
14+
)
15+
16+
func TestUserDelete(t *testing.T) {
17+
t.Parallel()
18+
t.Run("Username", func(t *testing.T) {
19+
t.Parallel()
20+
ctx := context.Background()
21+
client := coderdtest.New(t, nil)
22+
coderdtest.CreateFirstUser(t, client)
23+
24+
pw, err := cryptorand.String(16)
25+
require.NoError(t, err)
26+
27+
_, err = client.CreateUser(ctx, codersdk.CreateUserRequest{
28+
Email: "colin5@coder.com",
29+
Username: "coolin",
30+
Password: pw,
31+
UserLoginType: codersdk.LoginTypePassword,
32+
DisableLogin: false,
33+
})
34+
require.NoError(t, err)
35+
36+
inv, root := clitest.New(t, "users", "delete", "coolin")
37+
clitest.SetupConfig(t, client, root)
38+
pty := ptytest.New(t).Attach(inv)
39+
errC := make(chan error)
40+
go func() {
41+
errC <- inv.Run()
42+
}()
43+
require.NoError(t, <-errC)
44+
pty.ExpectMatch("coolin")
45+
})
46+
47+
t.Run("UserID", func(t *testing.T) {
48+
t.Parallel()
49+
ctx := context.Background()
50+
client := coderdtest.New(t, nil)
51+
coderdtest.CreateFirstUser(t, client)
52+
53+
pw, err := cryptorand.String(16)
54+
require.NoError(t, err)
55+
56+
user, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
57+
Email: "colin5@coder.com",
58+
Username: "coolin",
59+
Password: pw,
60+
UserLoginType: codersdk.LoginTypePassword,
61+
DisableLogin: false,
62+
})
63+
require.NoError(t, err)
64+
65+
inv, root := clitest.New(t, "users", "delete", user.ID.String())
66+
clitest.SetupConfig(t, client, root)
67+
pty := ptytest.New(t).Attach(inv)
68+
errC := make(chan error)
69+
go func() {
70+
errC <- inv.Run()
71+
}()
72+
require.NoError(t, <-errC)
73+
pty.ExpectMatch("coolin")
74+
})
75+
76+
t.Run("UserID", func(t *testing.T) {
77+
t.Parallel()
78+
ctx := context.Background()
79+
client := coderdtest.New(t, nil)
80+
coderdtest.CreateFirstUser(t, client)
81+
82+
pw, err := cryptorand.String(16)
83+
require.NoError(t, err)
84+
85+
user, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
86+
Email: "colin5@coder.com",
87+
Username: "coolin",
88+
Password: pw,
89+
UserLoginType: codersdk.LoginTypePassword,
90+
DisableLogin: false,
91+
})
92+
require.NoError(t, err)
93+
94+
inv, root := clitest.New(t, "users", "delete", user.ID.String())
95+
clitest.SetupConfig(t, client, root)
96+
pty := ptytest.New(t).Attach(inv)
97+
errC := make(chan error)
98+
go func() {
99+
errC <- inv.Run()
100+
}()
101+
require.NoError(t, <-errC)
102+
pty.ExpectMatch("coolin")
103+
})
104+
105+
t.Run("NoPerms", func(t *testing.T) {
106+
t.Parallel()
107+
ctx := context.Background()
108+
client := coderdtest.New(t, nil)
109+
admin := coderdtest.CreateFirstUser(t, client)
110+
111+
pw, err := cryptorand.String(16)
112+
require.NoError(t, err)
113+
114+
toDelete, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
115+
Email: "colin5@coder.com",
116+
Username: "coolin",
117+
Password: pw,
118+
UserLoginType: codersdk.LoginTypePassword,
119+
DisableLogin: false,
120+
})
121+
require.NoError(t, err)
122+
123+
uClient, _ := coderdtest.CreateAnotherUser(t, client, admin.OrganizationID, "member")
124+
125+
inv, root := clitest.New(t, "users", "delete", toDelete.ID.String())
126+
clitest.SetupConfig(t, uClient, root)
127+
require.ErrorContains(t, inv.Run(), "You cannot delete yourself!")
128+
})
129+
130+
t.Run("DeleteSelf", func(t *testing.T) {
131+
t.Parallel()
132+
ctx := context.Background()
133+
client := coderdtest.New(t, nil)
134+
admin := coderdtest.CreateFirstUser(t, client)
135+
136+
pw, err := cryptorand.String(16)
137+
require.NoError(t, err)
138+
139+
_, err = client.CreateUser(ctx, codersdk.CreateUserRequest{
140+
Email: "colin5@coder.com",
141+
Username: "coolin",
142+
Password: pw,
143+
UserLoginType: codersdk.LoginTypePassword,
144+
DisableLogin: false,
145+
})
146+
require.NoError(t, err)
147+
148+
coderdtest.CreateAnotherUser(t, client, admin.OrganizationID)
149+
150+
inv, root := clitest.New(t, "users", "delete", "me")
151+
clitest.SetupConfig(t, client, root)
152+
require.ErrorContains(t, inv.Run(), "You cannot delete yourself!")
153+
})
154+
}

cli/userdelete.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
6+
"golang.org/x/xerrors"
7+
8+
"github.com/coder/coder/v2/cli/clibase"
9+
"github.com/coder/coder/v2/cli/cliui"
10+
"github.com/coder/coder/v2/codersdk"
11+
"github.com/coder/pretty"
12+
)
13+
14+
func (r *RootCmd) userDelete() *clibase.Cmd {
15+
client := new(codersdk.Client)
16+
cmd := &clibase.Cmd{
17+
Use: "delete <username|user_id>",
18+
Short: "Delete a user by username or user_id.",
19+
Middleware: clibase.Chain(
20+
clibase.RequireNArgs(1),
21+
r.InitClient(client),
22+
),
23+
Handler: func(inv *clibase.Invocation) error {
24+
ctx := inv.Context()
25+
user, err := client.User(ctx, inv.Args[0])
26+
if err != nil {
27+
return xerrors.Errorf("fetch user: %w", err)
28+
}
29+
30+
err = client.DeleteUser(ctx, user.ID)
31+
if err != nil {
32+
return xerrors.Errorf("delete user: %w", err)
33+
}
34+
35+
_, _ = fmt.Fprintln(inv.Stderr,
36+
"Successfully deleted "+pretty.Sprint(cliui.DefaultStyles.Keyword, user.Username)+".",
37+
)
38+
return nil
39+
},
40+
}
41+
return cmd
42+
}

cli/users.go

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ func (r *RootCmd) users() *clibase.Cmd {
1717
r.userCreate(),
1818
r.userList(),
1919
r.userSingle(),
20+
r.userDelete(),
2021
r.createUserStatusCommand(codersdk.UserStatusActive),
2122
r.createUserStatusCommand(codersdk.UserStatusSuspended),
2223
},

cli/userstatus.go

-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *clibas
2828
case codersdk.UserStatusSuspended:
2929
verb = "suspend"
3030
pastVerb = "suspended"
31-
aliases = []string{"rm", "delete"}
3231
short = "Update a user's status to 'suspended'. A suspended user cannot log into the platform"
3332
default:
3433
panic(fmt.Sprintf("%s is not supported", sdkStatus))

0 commit comments

Comments
 (0)