diff --git a/cmd/tailscale/cli/serve_v2.go b/cmd/tailscale/cli/serve_v2.go index 756ef97c6..76677a299 100644 --- a/cmd/tailscale/cli/serve_v2.go +++ b/cmd/tailscale/cli/serve_v2.go @@ -271,7 +271,13 @@ func (e *serveEnv) runServeCombined(subcmd serveMode) execFunc { } var watcher *tailscale.IPNBusWatcher - if !e.bg && !turnOff { + wantFg := !e.bg && !turnOff + if wantFg { + // validate the config before creating a WatchIPNBus session + if err := e.validateConfig(parentSC, srvPort, srvType); err != nil { + return err + } + // if foreground mode, create a WatchIPNBus session // and use the nested config for all following operations // TODO(marwan-at-work): nested-config validations should happen here or previous to this point. @@ -334,6 +340,8 @@ func (e *serveEnv) runServeCombined(subcmd serveMode) execFunc { } } +const backgroundExistsMsg = "background configuration already exists, use `tailscale %s --%s=%d off` to remove the existing configuration" + func (e *serveEnv) validateConfig(sc *ipn.ServeConfig, port uint16, wantServe serveType) error { sc, isFg := findConfig(sc, port) if sc == nil { @@ -343,7 +351,7 @@ func (e *serveEnv) validateConfig(sc *ipn.ServeConfig, port uint16, wantServe se return errors.New("foreground already exists under this port") } if !e.bg { - return errors.New("background serve already exists under this port") + return fmt.Errorf(backgroundExistsMsg, infoMap[e.subcmd].Name, wantServe.String(), port) } existingServe := serveFromPortHandler(sc.TCP[port]) if wantServe != existingServe { diff --git a/cmd/tailscale/cli/serve_v2_test.go b/cmd/tailscale/cli/serve_v2_test.go index 9ce752276..e795ff286 100644 --- a/cmd/tailscale/cli/serve_v2_test.go +++ b/cmd/tailscale/cli/serve_v2_test.go @@ -792,6 +792,26 @@ func TestServeDevConfigMutations(t *testing.T) { }, }, }, + { + name: "forground_with_bg_conflict", + steps: []step{ + { + command: cmd("serve --bg --http=3000 localhost:3000"), + want: &ipn.ServeConfig{ + TCP: map[uint16]*ipn.TCPPortHandler{3000: {HTTP: true}}, + Web: map[ipn.HostPort]*ipn.WebServerConfig{ + "foo.test.ts.net:3000": {Handlers: map[string]*ipn.HTTPHandler{ + "/": {Proxy: "http://127.0.0.1:3000"}, + }}, + }, + }, + }, + { + command: cmd("serve --http=3000 localhost:3000"), + wantErr: exactErrMsg(fmt.Errorf(backgroundExistsMsg, "serve", "http", 3000)), + }, + }, + }, } for _, group := range groups { @@ -1330,6 +1350,6 @@ func exactErrMsg(want error) func(error) string { if got.Error() == want.Error() { return "" } - return fmt.Sprintf("got error %v, want %v", got, want) + return fmt.Sprintf("\ngot: %v\nwant: %v\n", got, want) } }