@@ -258,12 +258,12 @@ type DialWorkspaceAgentOptions struct {
258
258
BlockEndpoints bool
259
259
}
260
260
261
- func (c * Client ) DialWorkspaceAgent (ctx context.Context , agentID uuid.UUID , options * DialWorkspaceAgentOptions ) (agentConn * WorkspaceAgentConn , err error ) {
261
+ func (c * Client ) DialWorkspaceAgent (dialCtx context.Context , agentID uuid.UUID , options * DialWorkspaceAgentOptions ) (agentConn * WorkspaceAgentConn , err error ) {
262
262
if options == nil {
263
263
options = & DialWorkspaceAgentOptions {}
264
264
}
265
265
266
- connInfo , err := c .WorkspaceAgentConnectionInfo (ctx , agentID )
266
+ connInfo , err := c .WorkspaceAgentConnectionInfo (dialCtx , agentID )
267
267
if err != nil {
268
268
return nil , xerrors .Errorf ("get connection info: %w" , err )
269
269
}
@@ -302,7 +302,10 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
302
302
tokenHeader = c .SessionTokenHeader
303
303
}
304
304
headers .Set (tokenHeader , c .SessionToken ())
305
- ctx , cancel := context .WithCancel (ctx )
305
+
306
+ // New context, separate from dialCtx. We don't want to cancel the
307
+ // connection if dialCtx is canceled.
308
+ ctx , cancel := context .WithCancel (context .Background ())
306
309
defer func () {
307
310
if err != nil {
308
311
cancel ()
@@ -314,7 +317,9 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
314
317
return nil , xerrors .Errorf ("parse url: %w" , err )
315
318
}
316
319
closedCoordinator := make (chan struct {})
317
- firstCoordinator := make (chan error )
320
+ // Must only ever be used once, send error OR close to avoid
321
+ // reassignment race. Buffered so we don't hang in goroutine.
322
+ firstCoordinator := make (chan error , 1 )
318
323
go func () {
319
324
defer close (closedCoordinator )
320
325
isFirst := true
@@ -366,7 +371,9 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
366
371
return nil , xerrors .Errorf ("parse url: %w" , err )
367
372
}
368
373
closedDerpMap := make (chan struct {})
369
- firstDerpMap := make (chan error )
374
+ // Must only ever be used once, send error OR close to avoid
375
+ // reassignment race. Buffered so we don't hang in goroutine.
376
+ firstDerpMap := make (chan error , 1 )
370
377
go func () {
371
378
defer close (closedDerpMap )
372
379
isFirst := true
@@ -420,13 +427,21 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
420
427
}
421
428
}()
422
429
423
- err = <- firstCoordinator
424
- if err != nil {
425
- return nil , err
426
- }
427
- err = <- firstDerpMap
428
- if err != nil {
429
- return nil , err
430
+ for firstCoordinator != nil || firstDerpMap != nil {
431
+ select {
432
+ case <- dialCtx .Done ():
433
+ return nil , xerrors .Errorf ("timed out waiting for coordinator and derp map: %w" , dialCtx .Err ())
434
+ case err = <- firstCoordinator :
435
+ if err != nil {
436
+ return nil , xerrors .Errorf ("start coordinator: %w" , err )
437
+ }
438
+ firstCoordinator = nil
439
+ case err = <- firstDerpMap :
440
+ if err != nil {
441
+ return nil , xerrors .Errorf ("receive derp map: %w" , err )
442
+ }
443
+ firstDerpMap = nil
444
+ }
430
445
}
431
446
432
447
agentConn = NewWorkspaceAgentConn (conn , WorkspaceAgentConnOptions {
@@ -444,9 +459,9 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
444
459
},
445
460
})
446
461
447
- if ! agentConn .AwaitReachable (ctx ) {
462
+ if ! agentConn .AwaitReachable (dialCtx ) {
448
463
_ = agentConn .Close ()
449
- return nil , xerrors .Errorf ("timed out waiting for agent to become reachable: %w" , ctx .Err ())
464
+ return nil , xerrors .Errorf ("timed out waiting for agent to become reachable: %w" , dialCtx .Err ())
450
465
}
451
466
452
467
return agentConn , nil
0 commit comments