@@ -2,12 +2,15 @@ package coderutil
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
5
6
"net/url"
7
+ "sync"
6
8
7
9
"golang.org/x/xerrors"
8
10
"nhooyr.io/websocket"
9
11
10
12
"cdr.dev/coder-cli/coder-sdk"
13
+ "cdr.dev/coder-cli/pkg/clog"
11
14
)
12
15
13
16
// 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
72
75
}
73
76
return nil , coder .ErrNotFound
74
77
}
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