@@ -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
}
0 commit comments