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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 64 additions & 17 deletions
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

Lines changed: 2 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy