@@ -33,6 +33,7 @@ import (
33
33
"golang.org/x/mod/semver"
34
34
"golang.org/x/oauth2"
35
35
xgithub "golang.org/x/oauth2/github"
36
+ "golang.org/x/sync/errgroup"
36
37
"golang.org/x/xerrors"
37
38
"google.golang.org/api/idtoken"
38
39
"google.golang.org/api/option"
@@ -86,7 +87,7 @@ func server() *cobra.Command {
86
87
tlsKeyFile string
87
88
tlsMinVersion string
88
89
turnRelayAddress string
89
- tunnel bool
90
+ shouldTunnel bool
90
91
stunServers []string
91
92
trace bool
92
93
secureAuthCookie bool
@@ -165,18 +166,18 @@ func server() *cobra.Command {
165
166
accessURL = localURL .String ()
166
167
} else {
167
168
// If an access URL is specified, always skip tunneling.
168
- tunnel = false
169
+ shouldTunnel = false
169
170
}
170
171
171
172
var (
172
- tunnelErrChan <- chan error
173
173
ctxTunnel , closeTunnel = context .WithCancel (cmd .Context ())
174
+ tunnel = & devtunnel.Tunnel {ErrorChan : make (chan error , 1 )}
174
175
)
175
176
defer closeTunnel ()
176
177
177
178
// If we're attempting to tunnel in dev-mode, the access URL
178
179
// needs to be changed to use the tunnel.
179
- if dev && tunnel {
180
+ if dev && shouldTunnel {
180
181
_ , _ = fmt .Fprintln (cmd .ErrOrStderr (), cliui .Styles .Wrap .Render (
181
182
"Coder requires a URL accessible by workspaces you provision. " +
182
183
"A free tunnel can be created for simple setup. This will " +
@@ -195,7 +196,7 @@ func server() *cobra.Command {
195
196
}
196
197
}
197
198
if err == nil {
198
- accessURL , tunnelErrChan , err = devtunnel .New (ctxTunnel , localURL )
199
+ tunnel , err = devtunnel .New (ctxTunnel , logger . Named ( "devtunnel" ) )
199
200
if err != nil {
200
201
return xerrors .Errorf ("create tunnel: %w" , err )
201
202
}
@@ -327,7 +328,25 @@ func server() *cobra.Command {
327
328
return shutdownConnsCtx
328
329
},
329
330
}
330
- errCh <- server .Serve (listener )
331
+
332
+ wg := errgroup.Group {}
333
+ wg .Go (func () error {
334
+ if shouldTunnel {
335
+ defer tunnel .Listener .Close ()
336
+ }
337
+
338
+ return server .Serve (listener )
339
+ })
340
+
341
+ if shouldTunnel {
342
+ wg .Go (func () error {
343
+ defer listener .Close ()
344
+
345
+ return server .Serve (tunnel .Listener )
346
+ })
347
+ }
348
+
349
+ errCh <- wg .Wait ()
331
350
}()
332
351
333
352
config := createConfig (cmd )
@@ -393,7 +412,7 @@ func server() *cobra.Command {
393
412
case <- cmd .Context ().Done ():
394
413
coderAPI .Close ()
395
414
return cmd .Context ().Err ()
396
- case err := <- tunnelErrChan :
415
+ case err := <- tunnel . ErrorChan :
397
416
if err != nil {
398
417
return err
399
418
}
@@ -455,10 +474,10 @@ func server() *cobra.Command {
455
474
spin .Stop ()
456
475
}
457
476
458
- if dev && tunnel {
477
+ if dev && shouldTunnel {
459
478
_ , _ = fmt .Fprintf (cmd .OutOrStdout (), cliui .Styles .Prompt .String ()+ "Waiting for dev tunnel to close...\n " )
460
479
closeTunnel ()
461
- <- tunnelErrChan
480
+ <- tunnel . ErrorChan
462
481
}
463
482
464
483
_ , _ = fmt .Fprintf (cmd .OutOrStdout (), cliui .Styles .Prompt .String ()+ "Waiting for WebSocket connections to close...\n " )
@@ -504,7 +523,7 @@ func server() *cobra.Command {
504
523
"Specifies the path to the private key for the certificate. It requires a PEM-encoded file" )
505
524
cliflag .StringVarP (root .Flags (), & tlsMinVersion , "tls-min-version" , "" , "CODER_TLS_MIN_VERSION" , "tls12" ,
506
525
`Specifies the minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13"` )
507
- cliflag .BoolVarP (root .Flags (), & tunnel , "tunnel" , "" , "CODER_DEV_TUNNEL" , true ,
526
+ cliflag .BoolVarP (root .Flags (), & shouldTunnel , "tunnel" , "" , "CODER_DEV_TUNNEL" , true ,
508
527
"Specifies whether the dev tunnel will be enabled or not. If specified, the interactive prompt will not display." )
509
528
cliflag .StringArrayVarP (root .Flags (), & stunServers , "stun-server" , "" , "CODER_STUN_SERVERS" , []string {
510
529
"stun:stun.l.google.com:19302" ,
0 commit comments