Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 3a6edf6

Browse files
authored
feat: add username to 'coder ws ls' (#486)
* feat: add username to 'coder ws ls' * make lint * make lint
1 parent 9a9a1f3 commit 3a6edf6

File tree

3 files changed

+68
-19
lines changed

3 files changed

+68
-19
lines changed

internal/cmd/login.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func pingAPI(ctx context.Context, workspaceURL *url.URL, token string) error {
118118
return nil
119119
}
120120

121-
// isWSL determines if coder-cli is running within Windows Subsystem for Linux
121+
// isWSL determines if coder-cli is running within Windows Subsystem for Linux.
122122
func isWSL() (bool, error) {
123123
if runtime.GOOS == goosDarwin || runtime.GOOS == goosWindows {
124124
return false, nil
@@ -130,7 +130,7 @@ func isWSL() (bool, error) {
130130
return strings.Contains(strings.ToLower(string(data)), "microsoft"), nil
131131
}
132132

133-
// openURL opens the provided URL via user's default browser
133+
// openURL opens the provided URL via user's default browser.
134134
func openURL(url string) error {
135135
var cmd string
136136
var args []string

internal/coderutil/workspace.go

+64-17
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,15 @@ func DefaultWorkspaceProvider(ctx context.Context, c coder.Client) (*coder.Kuber
7979
// WorkspaceTable defines an Workspace-like structure with associated entities composed in a human
8080
// readable form.
8181
type WorkspaceTable struct {
82-
Name string `table:"Name"`
83-
Image string `table:"Image"`
84-
CPU float32 `table:"vCPU"`
85-
MemoryGB float32 `table:"MemoryGB"`
86-
DiskGB int `table:"DiskGB"`
87-
Status string `table:"Status"`
88-
Provider string `table:"Provider"`
89-
CVM bool `table:"CVM"`
82+
Name string `table:"Name" json:"name"`
83+
Image string `table:"Image" json:"image"`
84+
CPU float32 `table:"vCPU" json:"cpu"`
85+
MemoryGB float32 `table:"MemoryGB" json:"memory_gb"`
86+
DiskGB int `table:"DiskGB" json:"disk_gb"`
87+
Status string `table:"Status" json:"status"`
88+
Provider string `table:"Provider" json:"provider"`
89+
CVM bool `table:"CVM" json:"cvm"`
90+
Username string `table:"Username" json:"username"`
9091
}
9192

9293
// WorkspacesHumanTable performs the composition of each Workspace with its associated ProviderName and ImageRepo.
@@ -96,6 +97,11 @@ func WorkspacesHumanTable(ctx context.Context, client coder.Client, workspaces [
9697
return nil, err
9798
}
9899

100+
userMap, err := MakeUserMap(ctx, client, workspaces)
101+
if err != nil {
102+
return nil, err
103+
}
104+
99105
pooledWorkspaces := make([]WorkspaceTable, 0, len(workspaces))
100106
providers, err := client.WorkspaceProviders(ctx)
101107
if err != nil {
@@ -105,25 +111,66 @@ func WorkspacesHumanTable(ctx context.Context, client coder.Client, workspaces [
105111
for _, p := range providers.Kubernetes {
106112
providerMap[p.ID] = p
107113
}
108-
for _, e := range workspaces {
109-
workspaceProvider, ok := providerMap[e.ResourcePoolID]
114+
for _, ws := range workspaces {
115+
workspaceProvider, ok := providerMap[ws.ResourcePoolID]
110116
if !ok {
111117
return nil, xerrors.Errorf("fetch workspace workspace provider: %w", coder.ErrNotFound)
112118
}
113119
pooledWorkspaces = append(pooledWorkspaces, WorkspaceTable{
114-
Name: e.Name,
115-
Image: fmt.Sprintf("%s:%s", imageMap[e.ImageID].Repository, e.ImageTag),
116-
CPU: e.CPUCores,
117-
MemoryGB: e.MemoryGB,
118-
DiskGB: e.DiskGB,
119-
Status: string(e.LatestStat.ContainerStatus),
120+
Name: ws.Name,
121+
Image: fmt.Sprintf("%s:%s", imageMap[ws.ImageID].Repository, ws.ImageTag),
122+
CPU: ws.CPUCores,
123+
MemoryGB: ws.MemoryGB,
124+
DiskGB: ws.DiskGB,
125+
Status: string(ws.LatestStat.ContainerStatus),
120126
Provider: workspaceProvider.Name,
121-
CVM: e.UseContainerVM,
127+
CVM: ws.UseContainerVM,
128+
Username: userMap[ws.UserID].Username,
122129
})
123130
}
124131
return pooledWorkspaces, nil
125132
}
126133

134+
func MakeUserMap(ctx context.Context, client coder.Client, workspaces []coder.Workspace) (map[string]*coder.User, error) {
135+
var (
136+
mu sync.Mutex
137+
egroup = clog.LoggedErrGroup()
138+
)
139+
140+
userMap := map[string]*coder.User{}
141+
142+
// Iterate over all the workspaces to get a list of unique User IDs.
143+
for _, ws := range workspaces {
144+
userMap[ws.UserID] = nil
145+
}
146+
147+
fetchIds := make([]string, 0, len(userMap))
148+
for id := range userMap {
149+
fetchIds = append(fetchIds, id)
150+
}
151+
152+
for _, id := range fetchIds {
153+
id := id
154+
egroup.Go(func() error {
155+
user, err := client.UserByID(ctx, id)
156+
if err != nil {
157+
return xerrors.Errorf("get user by id: %w", err)
158+
}
159+
mu.Lock()
160+
defer mu.Unlock()
161+
162+
userMap[id] = user
163+
return nil
164+
})
165+
}
166+
167+
if err := egroup.Wait(); err != nil {
168+
return nil, xerrors.Errorf("fetch all workspace users: %w", err)
169+
}
170+
171+
return userMap, nil
172+
}
173+
127174
// MakeImageMap fetches all image entities specified in the slice of workspaces, then places them into an ID map.
128175
func MakeImageMap(ctx context.Context, client coder.Client, workspaces []coder.Workspace) (map[string]*coder.Image, error) {
129176
var (

wsnet/listen.go

+2
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ func (l *listener) dial(ctx context.Context) (<-chan error, error) {
116116
_ = l.ws.Close(websocket.StatusNormalClosure, "new connection inbound")
117117
}
118118

119+
// websocket lib documents that the response does not need to be closed.
120+
// nolint
119121
conn, resp, err := websocket.Dial(ctx, l.broker, nil)
120122
if err != nil {
121123
if resp != nil {

0 commit comments

Comments
 (0)