ipn/ipnlocal: fix 'tailscale up' on Windows without GUI
With this, I can now: * install Tailscale * stop the GUI * net stop Tailscale * net start Tailscale * tailscale up --unattended (where the middle three steps simulate what would happen on a Windows Server Core machine without a GUI) Fixes #2137 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>pull/2464/head
parent
7f7a81e5ae
commit
b6d70203d3
|
@ -179,6 +179,7 @@ func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, e wge
|
||||||
gotPortPollRes: make(chan struct{}),
|
gotPortPollRes: make(chan struct{}),
|
||||||
}
|
}
|
||||||
b.statusChanged = sync.NewCond(&b.statusLock)
|
b.statusChanged = sync.NewCond(&b.statusLock)
|
||||||
|
b.e.SetStatusCallback(b.setWgengineStatus)
|
||||||
|
|
||||||
linkMon := e.GetLinkMonitor()
|
linkMon := e.GetLinkMonitor()
|
||||||
b.prevIfState = linkMon.InterfaceState()
|
b.prevIfState = linkMon.InterfaceState()
|
||||||
|
@ -610,8 +611,8 @@ func (b *LocalBackend) setWgengineStatus(s *wgengine.Status, err error) {
|
||||||
|
|
||||||
if cc != nil {
|
if cc != nil {
|
||||||
cc.UpdateEndpoints(0, s.LocalAddrs)
|
cc.UpdateEndpoints(0, s.LocalAddrs)
|
||||||
|
b.stateMachine()
|
||||||
}
|
}
|
||||||
b.stateMachine()
|
|
||||||
|
|
||||||
b.statusLock.Lock()
|
b.statusLock.Lock()
|
||||||
b.statusChanged.Broadcast()
|
b.statusChanged.Broadcast()
|
||||||
|
@ -868,7 +869,6 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.SetStatusFunc(b.setClientStatus)
|
cc.SetStatusFunc(b.setClientStatus)
|
||||||
b.e.SetStatusCallback(b.setWgengineStatus)
|
|
||||||
b.e.SetNetInfoCallback(b.setNetInfo)
|
b.e.SetNetInfoCallback(b.setNetInfo)
|
||||||
|
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
|
|
|
@ -42,6 +42,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
verboseTailscaled = flag.Bool("verbose-tailscaled", false, "verbose tailscaled logging")
|
verboseTailscaled = flag.Bool("verbose-tailscaled", false, "verbose tailscaled logging")
|
||||||
|
verboseTailscale = flag.Bool("verbose-tailscale", false, "verbose tailscale CLI logging")
|
||||||
)
|
)
|
||||||
|
|
||||||
var mainError atomic.Value // of error
|
var mainError atomic.Value // of error
|
||||||
|
@ -358,6 +359,29 @@ func TestNoControlConnectionWhenDown(t *testing.T) {
|
||||||
d2.MustCleanShutdown(t)
|
d2.MustCleanShutdown(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 2137: make sure Windows tailscaled works with the CLI alone,
|
||||||
|
// without the GUI to kick off a Start.
|
||||||
|
func TestOneNodeUp_WindowsStyle(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
bins := BuildTestBinaries(t)
|
||||||
|
|
||||||
|
env := newTestEnv(t, bins)
|
||||||
|
defer env.Close()
|
||||||
|
|
||||||
|
n1 := newTestNode(t, env)
|
||||||
|
n1.upFlagGOOS = "windows"
|
||||||
|
|
||||||
|
d1 := n1.StartDaemonAsIPNGOOS(t, "windows")
|
||||||
|
defer d1.Kill()
|
||||||
|
n1.AwaitResponding(t)
|
||||||
|
n1.MustUp("--unattended")
|
||||||
|
|
||||||
|
t.Logf("Got IP: %v", n1.AwaitIP(t))
|
||||||
|
n1.AwaitRunning(t)
|
||||||
|
|
||||||
|
d1.MustCleanShutdown(t)
|
||||||
|
}
|
||||||
|
|
||||||
// testEnv contains the test environment (set of servers) used by one
|
// testEnv contains the test environment (set of servers) used by one
|
||||||
// or more nodes.
|
// or more nodes.
|
||||||
type testEnv struct {
|
type testEnv struct {
|
||||||
|
@ -434,9 +458,10 @@ func (e *testEnv) Close() error {
|
||||||
type testNode struct {
|
type testNode struct {
|
||||||
env *testEnv
|
env *testEnv
|
||||||
|
|
||||||
dir string // temp dir for sock & state
|
dir string // temp dir for sock & state
|
||||||
sockFile string
|
sockFile string
|
||||||
stateFile string
|
stateFile string
|
||||||
|
upFlagGOOS string // if non-empty, sets TS_DEBUG_UP_FLAG_GOOS for cmd/tailscale CLI
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
onLogLine []func([]byte)
|
onLogLine []func([]byte)
|
||||||
|
@ -595,6 +620,10 @@ func (d *Daemon) MustCleanShutdown(t testing.TB) {
|
||||||
// StartDaemon starts the node's tailscaled, failing if it fails to
|
// StartDaemon starts the node's tailscaled, failing if it fails to
|
||||||
// start.
|
// start.
|
||||||
func (n *testNode) StartDaemon(t testing.TB) *Daemon {
|
func (n *testNode) StartDaemon(t testing.TB) *Daemon {
|
||||||
|
return n.StartDaemonAsIPNGOOS(t, runtime.GOOS)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *testNode) StartDaemonAsIPNGOOS(t testing.TB, ipnGOOS string) *Daemon {
|
||||||
cmd := exec.Command(n.env.Binaries.Daemon,
|
cmd := exec.Command(n.env.Binaries.Daemon,
|
||||||
"--tun=userspace-networking",
|
"--tun=userspace-networking",
|
||||||
"--state="+n.stateFile,
|
"--state="+n.stateFile,
|
||||||
|
@ -605,6 +634,7 @@ func (n *testNode) StartDaemon(t testing.TB) *Daemon {
|
||||||
"TS_LOG_TARGET="+n.env.LogCatcherServer.URL,
|
"TS_LOG_TARGET="+n.env.LogCatcherServer.URL,
|
||||||
"HTTP_PROXY="+n.env.TrafficTrapServer.URL,
|
"HTTP_PROXY="+n.env.TrafficTrapServer.URL,
|
||||||
"HTTPS_PROXY="+n.env.TrafficTrapServer.URL,
|
"HTTPS_PROXY="+n.env.TrafficTrapServer.URL,
|
||||||
|
"TS_DEBUG_TAILSCALED_IPN_GOOS="+ipnGOOS,
|
||||||
)
|
)
|
||||||
cmd.Stderr = &nodeOutputParser{n: n}
|
cmd.Stderr = &nodeOutputParser{n: n}
|
||||||
if *verboseTailscaled {
|
if *verboseTailscaled {
|
||||||
|
@ -619,10 +649,15 @@ func (n *testNode) StartDaemon(t testing.TB) *Daemon {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *testNode) MustUp() {
|
func (n *testNode) MustUp(extraArgs ...string) {
|
||||||
t := n.env.t
|
t := n.env.t
|
||||||
t.Logf("Running up --login-server=%s ...", n.env.ControlServer.URL)
|
args := []string{
|
||||||
if err := n.Tailscale("up", "--login-server="+n.env.ControlServer.URL).Run(); err != nil {
|
"up",
|
||||||
|
"--login-server=" + n.env.ControlServer.URL,
|
||||||
|
}
|
||||||
|
args = append(args, extraArgs...)
|
||||||
|
t.Logf("Running %v ...", args)
|
||||||
|
if err := n.Tailscale(args...).Run(); err != nil {
|
||||||
t.Fatalf("up: %v", err)
|
t.Fatalf("up: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -654,7 +689,10 @@ func (n *testNode) AwaitIPs(t testing.TB) []netaddr.IP {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var addrs []netaddr.IP
|
var addrs []netaddr.IP
|
||||||
if err := tstest.WaitFor(20*time.Second, func() error {
|
if err := tstest.WaitFor(20*time.Second, func() error {
|
||||||
out, err := n.Tailscale("ip").Output()
|
cmd := n.Tailscale("ip")
|
||||||
|
cmd.Stdout = nil // in case --verbose-tailscale was set
|
||||||
|
cmd.Stderr = nil // in case --verbose-tailscale was set
|
||||||
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -709,11 +747,21 @@ func (n *testNode) Tailscale(arg ...string) *exec.Cmd {
|
||||||
cmd := exec.Command(n.env.Binaries.CLI, "--socket="+n.sockFile)
|
cmd := exec.Command(n.env.Binaries.CLI, "--socket="+n.sockFile)
|
||||||
cmd.Args = append(cmd.Args, arg...)
|
cmd.Args = append(cmd.Args, arg...)
|
||||||
cmd.Dir = n.dir
|
cmd.Dir = n.dir
|
||||||
|
cmd.Env = append(os.Environ(),
|
||||||
|
"TS_DEBUG_UP_FLAG_GOOS="+n.upFlagGOOS,
|
||||||
|
)
|
||||||
|
if *verboseTailscale {
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
}
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *testNode) Status() (*ipnstate.Status, error) {
|
func (n *testNode) Status() (*ipnstate.Status, error) {
|
||||||
out, err := n.Tailscale("status", "--json").CombinedOutput()
|
cmd := n.Tailscale("status", "--json")
|
||||||
|
cmd.Stdout = nil // in case --verbose-tailscale was set
|
||||||
|
cmd.Stderr = nil // in case --verbose-tailscale was set
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("running tailscale status: %v, %s", err, out)
|
return nil, fmt.Errorf("running tailscale status: %v, %s", err, out)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue