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

Commit c3d86f1

Browse files
authored
feat: add provider name and image name to envs ls (#286)
1 parent 9d75db2 commit c3d86f1

File tree

6 files changed

+113
-10
lines changed

6 files changed

+113
-10
lines changed

coder-sdk/env.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ type Environment struct {
2525
CPUCores float32 `json:"cpu_cores" table:"CPUCores"`
2626
MemoryGB float32 `json:"memory_gb" table:"MemoryGB"`
2727
DiskGB int `json:"disk_gb" table:"DiskGB"`
28-
GPUs int `json:"gpus" table:"GPUs"`
29-
Updating bool `json:"updating" table:"Updating"`
28+
GPUs int `json:"gpus" table:"-"`
29+
Updating bool `json:"updating" table:"-"`
3030
LatestStat EnvironmentStat `json:"latest_stat" table:"Status"`
3131
RebuildMessages []RebuildMessage `json:"rebuild_messages" table:"-"`
3232
CreatedAt time.Time `json:"created_at" table:"-"`

coder-sdk/image.go

+9
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ func (c *DefaultClient) ImportImage(ctx context.Context, req ImportImageReq) (*I
6666
return &img, nil
6767
}
6868

69+
// ImageByID returns an image entity, fetched by its ID.
70+
func (c *DefaultClient) ImageByID(ctx context.Context, id string) (*Image, error) {
71+
var img Image
72+
if err := c.requestBody(ctx, http.MethodGet, "/api/v0/images/"+id, nil, &img); err != nil {
73+
return nil, err
74+
}
75+
return &img, nil
76+
}
77+
6978
// OrganizationImages returns all of the images imported for orgID.
7079
func (c *DefaultClient) OrganizationImages(ctx context.Context, orgID string) ([]Image, error) {
7180
var (

coder-sdk/interface.go

+3
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ type Client interface {
136136
// ImportImage creates a new image and optionally a new registry.
137137
ImportImage(ctx context.Context, req ImportImageReq) (*Image, error)
138138

139+
// ImageByID returns an image entity, fetched by its ID.
140+
ImageByID(ctx context.Context, id string) (*Image, error)
141+
139142
// OrganizationImages returns all of the images imported for orgID.
140143
OrganizationImages(ctx context.Context, orgID string) ([]Image, error)
141144

coder-sdk/workspace_providers.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ type WorkspaceProviders struct {
1212

1313
// KubernetesProvider defines an entity capable of deploying and acting as an ingress for Coder environments.
1414
type KubernetesProvider struct {
15-
ID string `json:"id" table:"-"`
16-
Name string `json:"name" table:"Name"`
17-
Status WorkspaceProviderStatus `json:"status" table:"Status"`
18-
BuiltIn bool `json:"built_in" table:"-"`
19-
EnvproxyAccessURL string `json:"envproxy_access_url" validate:"required" table:"Access URL"`
20-
DevurlHost string `json:"devurl_host" table:"Devurl Host"`
21-
OrgWhitelist []string `json:"org_whitelist" table:"-"`
15+
ID string `json:"id" table:"-"`
16+
Name string `json:"name" table:"Name"`
17+
Status WorkspaceProviderStatus `json:"status" table:"Status"`
18+
BuiltIn bool `json:"built_in" table:"-"`
19+
EnvproxyAccessURL string `json:"envproxy_access_url" table:"Access URL" validate:"required"`
20+
DevurlHost string `json:"devurl_host" table:"Devurl Host"`
21+
OrgWhitelist []string `json:"org_whitelist" table:"-"`
2222
KubeProviderConfig `json:"config" table:"_"`
2323
}
2424

internal/cmd/envs.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ func lsEnvsCommand() *cobra.Command {
8282

8383
switch outputFmt {
8484
case humanOutput:
85-
err := tablewriter.WriteTable(cmd.OutOrStdout(), len(envs), func(i int) interface{} {
85+
envs, err := coderutil.EnvsHumanTable(ctx, client, envs)
86+
if err != nil {
87+
return err
88+
}
89+
err = tablewriter.WriteTable(cmd.OutOrStdout(), len(envs), func(i int) interface{} {
8690
return envs[i]
8791
})
8892
if err != nil {

internal/coderutil/env.go

+87
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package coderutil
22

33
import (
44
"context"
5+
"fmt"
56
"net/url"
7+
"sync"
68

79
"golang.org/x/xerrors"
810
"nhooyr.io/websocket"
911

1012
"cdr.dev/coder-cli/coder-sdk"
13+
"cdr.dev/coder-cli/pkg/clog"
1114
)
1215

1316
// DialEnvWsep dials the executor endpoint using the https://github.com/cdr/wsep message protocol.
@@ -72,3 +75,87 @@ func DefaultWorkspaceProvider(ctx context.Context, c coder.Client) (*coder.Kuber
7275
}
7376
return nil, coder.ErrNotFound
7477
}
78+
79+
// EnvTable defines an Environment-like structure with associated entities composed in a human
80+
// readable form.
81+
type EnvTable 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"`
90+
}
91+
92+
// EnvsHumanTable performs the composition of each Environment with its associated ProviderName and ImageRepo.
93+
func EnvsHumanTable(ctx context.Context, client coder.Client, envs []coder.Environment) ([]EnvTable, error) {
94+
imageMap, err := makeImageMap(ctx, client, envs)
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
pooledEnvs := make([]EnvTable, 0, len(envs))
100+
providers, err := client.WorkspaceProviders(ctx)
101+
if err != nil {
102+
return nil, err
103+
}
104+
providerMap := make(map[string]coder.KubernetesProvider, len(providers.Kubernetes))
105+
for _, p := range providers.Kubernetes {
106+
providerMap[p.ID] = p
107+
}
108+
for _, e := range envs {
109+
envProvider, ok := providerMap[e.ResourcePoolID]
110+
if !ok {
111+
return nil, xerrors.Errorf("fetch env workspace provider: %w", coder.ErrNotFound)
112+
}
113+
pooledEnvs = append(pooledEnvs, EnvTable{
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+
Provider: envProvider.Name,
121+
CVM: e.UseContainerVM,
122+
})
123+
}
124+
return pooledEnvs, nil
125+
}
126+
127+
func makeImageMap(ctx context.Context, client coder.Client, envs []coder.Environment) (map[string]*coder.Image, error) {
128+
var (
129+
mu sync.Mutex
130+
egroup = clog.LoggedErrGroup()
131+
)
132+
imageMap := make(map[string]*coder.Image)
133+
for _, e := range envs {
134+
// put all the image IDs into a map to remove duplicates
135+
imageMap[e.ImageID] = nil
136+
}
137+
ids := make([]string, 0, len(imageMap))
138+
for id := range imageMap {
139+
// put the deduplicated back into a slice
140+
// so we can write to the map while iterating
141+
ids = append(ids, id)
142+
}
143+
for _, id := range ids {
144+
id := id
145+
egroup.Go(func() error {
146+
img, err := client.ImageByID(ctx, id)
147+
if err != nil {
148+
return err
149+
}
150+
mu.Lock()
151+
defer mu.Unlock()
152+
imageMap[id] = img
153+
154+
return nil
155+
})
156+
}
157+
if err := egroup.Wait(); err != nil {
158+
return nil, err
159+
}
160+
return imageMap, nil
161+
}

0 commit comments

Comments
 (0)