@@ -3,6 +3,7 @@ package cli
3
3
import (
4
4
"bufio"
5
5
"bytes"
6
+ "context"
6
7
"errors"
7
8
"fmt"
8
9
"io"
@@ -15,6 +16,7 @@ import (
15
16
"strings"
16
17
17
18
"github.com/cli/safeexec"
19
+ "github.com/google/uuid"
18
20
"github.com/pkg/diff"
19
21
"github.com/pkg/diff/write"
20
22
"github.com/spf13/cobra"
@@ -97,6 +99,65 @@ func (o sshCoderConfigOptions) asList() (list []string) {
97
99
return list
98
100
}
99
101
102
+ type sshWorkspaceConfig struct {
103
+ Name string
104
+ Hosts []string
105
+ }
106
+
107
+ func sshPrepareWorkspaceConfigs (ctx context.Context ,client * codersdk.Client ,organizationID uuid.UUID ) (receive func () ([]sshWorkspaceConfig ,error )) {
108
+ wcC := make (chan []sshWorkspaceConfig ,1 )
109
+ errC := make (chan error ,1 )
110
+ go func () {
111
+ wc ,err := func () ([]sshWorkspaceConfig ,error ) {
112
+ workspaces ,err := client .WorkspacesByOwner (ctx ,organizationID ,codersdk .Me )
113
+ if err != nil {
114
+ return nil ,err
115
+ }
116
+
117
+ var errGroup errgroup.Group
118
+ workspaceConfigs := make ([]sshWorkspaceConfig ,len (workspaces ))
119
+ for i ,workspace := range workspaces {
120
+ i := i
121
+ workspace := workspace
122
+ errGroup .Go (func ()error {
123
+ resources ,err := client .TemplateVersionResources (ctx ,workspace .LatestBuild .TemplateVersionID )
124
+ if err != nil {
125
+ return err
126
+ }
127
+
128
+ wc := sshWorkspaceConfig {Name :workspace .Name }
129
+ for _ ,resource := range resources {
130
+ if resource .Transition != codersdk .WorkspaceTransitionStart {
131
+ continue
132
+ }
133
+ for _ ,agent := range resource .Agents {
134
+ hostname := workspace .Name
135
+ if len (resource .Agents )> 1 {
136
+ hostname += "." + agent .Name
137
+ }
138
+ wc .Hosts = append (wc .Hosts ,hostname )
139
+ }
140
+ }
141
+ workspaceConfigs [i ]= wc
142
+
143
+ return nil
144
+ })
145
+ }
146
+ err = errGroup .Wait ()
147
+ if err != nil {
148
+ return nil ,err
149
+ }
150
+
151
+ return workspaceConfigs ,nil
152
+ }()
153
+ wcC <- wc
154
+ errC <- err
155
+ }()
156
+ return func () ([]sshWorkspaceConfig ,error ) {
157
+ return <- wcC ,<- errC
158
+ }
159
+ }
160
+
100
161
func configSSH ()* cobra.Command {
101
162
var (
102
163
coderConfig sshCoderConfigOptions
@@ -136,11 +197,7 @@ func configSSH() *cobra.Command {
136
197
return err
137
198
}
138
199
139
- // Early check for workspaces to ensure API key has not expired.
140
- workspaces ,err := client .WorkspacesByOwner (cmd .Context (),organization .ID ,codersdk .Me )
141
- if err != nil {
142
- return err
143
- }
200
+ recvWorkspaceConfigs := sshPrepareWorkspaceConfigs (cmd .Context (),client ,organization .ID )
144
201
145
202
out := cmd .OutOrStdout ()
146
203
if showDiff {
@@ -174,6 +231,7 @@ func configSSH() *cobra.Command {
174
231
coderConfigExists := true
175
232
coderConfigRaw ,err := os .ReadFile (coderConfigFile )
176
233
if err != nil {
234
+ //nolint: revive // Inverting this if statement doesn't improve readability.
177
235
if errors .Is (err ,fs .ErrNotExist ) {
178
236
coderConfigExists = false
179
237
}else {
@@ -234,43 +292,6 @@ func configSSH() *cobra.Command {
234
292
}
235
293
236
294
root := createConfig (cmd )
237
- var errGroup errgroup.Group
238
- type workspaceConfig struct {
239
- Name string
240
- Hosts []string
241
- }
242
- workspaceConfigs := make ([]workspaceConfig ,len (workspaces ))
243
- for i ,workspace := range workspaces {
244
- i := i
245
- workspace := workspace
246
- errGroup .Go (func ()error {
247
- resources ,err := client .TemplateVersionResources (cmd .Context (),workspace .LatestBuild .TemplateVersionID )
248
- if err != nil {
249
- return err
250
- }
251
-
252
- wc := workspaceConfig {Name :workspace .Name }
253
- for _ ,resource := range resources {
254
- if resource .Transition != codersdk .WorkspaceTransitionStart {
255
- continue
256
- }
257
- for _ ,agent := range resource .Agents {
258
- hostname := workspace .Name
259
- if len (resource .Agents )> 1 {
260
- hostname += "." + agent .Name
261
- }
262
- wc .Hosts = append (wc .Hosts ,hostname )
263
- }
264
- }
265
- workspaceConfigs [i ]= wc
266
-
267
- return nil
268
- })
269
- }
270
- err = errGroup .Wait ()
271
- if err != nil {
272
- return err
273
- }
274
295
275
296
buf := & bytes.Buffer {}
276
297
@@ -281,8 +302,12 @@ func configSSH() *cobra.Command {
281
302
return xerrors .Errorf ("write coder config header failed: %w" ,err )
282
303
}
283
304
305
+ workspaceConfigs ,err := recvWorkspaceConfigs ()
306
+ if err != nil {
307
+ return xerrors .Errorf ("fetch workspace configs failed: %w" ,err )
308
+ }
284
309
// Ensure stable sorting of output.
285
- slices .SortFunc (workspaceConfigs ,func (a ,b workspaceConfig )bool {
310
+ slices .SortFunc (workspaceConfigs ,func (a ,b sshWorkspaceConfig )bool {
286
311
return a .Name < b .Name
287
312
})
288
313
for _ ,wc := range workspaceConfigs {
@@ -379,9 +404,9 @@ func configSSH() *cobra.Command {
379
404
}
380
405
}
381
406
382
- if len (workspaces )> 0 {
407
+ if len (workspaceConfigs )> 0 {
383
408
_ ,_ = fmt .Fprintln (out ,"You should now be able to ssh into your workspace." )
384
- _ ,_ = fmt .Fprintf (out ,"For example, try running:\n \n \t $ ssh coder.%s\n \n " ,workspaces [0 ].Name )
409
+ _ ,_ = fmt .Fprintf (out ,"For example, try running:\n \n \t $ ssh coder.%s\n \n " ,workspaceConfigs [0 ].Name )
385
410
}else {
386
411
_ ,_ = fmt .Fprint (out ,"You don't have any workspaces yet, try creating one with:\n \n \t $ coder create <workspace>\n \n " )
387
412
}