Skip to content

Commit

Permalink
cmd/tailscale/cli: don't start WatchIPNBus until after up's initial S…
Browse files Browse the repository at this point in the history
…tart

The CLI "up" command is a historical mess, both on the CLI side and
the LocalBackend side. We're getting closer to cleaning it up, but in
the meantime it was again implicated in flaky tests.

In this case, the background goroutine running WatchIPNBus was very
occasionally running enough to get to its StartLoginInteractive call
before the original goroutine did its Start call. That meant
integration tests were very rarely but sometimes logging in with the
default control plane URL out on the internet
(controlplane.tailscale.com) instead of the localhost control server
for tests.

This also might've affected new Headscale etc users on initial "up".

Fixes #11960
Fixes #11962

Change-Id: I36f8817b69267a99271b5ee78cb7dbf0fcc0bd34
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
  • Loading branch information
bradfitz committed May 6, 2024
1 parent 3831337 commit acf4768
Showing 1 changed file with 50 additions and 49 deletions.
99 changes: 50 additions & 49 deletions cmd/tailscale/cli/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,11 +477,6 @@ func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retE

watchCtx, cancelWatch := context.WithCancel(ctx)
defer cancelWatch()
watcher, err := localClient.WatchIPNBus(watchCtx, 0)
if err != nil {
return err
}
defer watcher.Close()

go func() {
interrupt := make(chan os.Signal, 1)
Expand All @@ -494,7 +489,7 @@ func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retE
}()

running := make(chan bool, 1) // gets value once in state ipn.Running
pumpErr := make(chan error, 1)
watchErr := make(chan error, 1)

// localAPIMu should be held while doing mutable LocalAPI calls
// to the backend. In particular, it prevents StartLoginInteractive from
Expand All @@ -511,12 +506,59 @@ func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retE
localClient.StartLoginInteractive(ctx)
})

// Special case: bare "tailscale up" means to just start
// running, if there's ever been a login.
if simpleUp {
localAPIMu.Lock()
_, err := localClient.EditPrefs(ctx, &ipn.MaskedPrefs{
Prefs: ipn.Prefs{
WantRunning: true,
},
WantRunningSet: true,
})
localAPIMu.Unlock()
if err != nil {
return err
}
} else {
if err := localClient.CheckPrefs(ctx, prefs); err != nil {
return err
}

authKey, err := upArgs.getAuthKey()
if err != nil {
return err
}
authKey, err = resolveAuthKey(ctx, authKey, upArgs.advertiseTags)
if err != nil {
return err
}
localAPIMu.Lock()
err = localClient.Start(ctx, ipn.Options{
AuthKey: authKey,
UpdatePrefs: prefs,
})
localAPIMu.Unlock()
if err != nil {
return err
}
if upArgs.forceReauth {
startLoginInteractive()
}
}

watcher, err := localClient.WatchIPNBus(watchCtx, ipn.NotifyInitialState)
if err != nil {
return err
}
defer watcher.Close()

go func() {
var printed bool // whether we've yet printed anything to stdout or stderr
for {
n, err := watcher.Next()
if err != nil {
pumpErr <- err
watchErr <- err
return
}
if n.ErrMessage != nil {
Expand Down Expand Up @@ -583,47 +625,6 @@ func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retE
}
}()

// Special case: bare "tailscale up" means to just start
// running, if there's ever been a login.
if simpleUp {
localAPIMu.Lock()
_, err := localClient.EditPrefs(ctx, &ipn.MaskedPrefs{
Prefs: ipn.Prefs{
WantRunning: true,
},
WantRunningSet: true,
})
localAPIMu.Unlock()
if err != nil {
return err
}
} else {
if err := localClient.CheckPrefs(ctx, prefs); err != nil {
return err
}

authKey, err := upArgs.getAuthKey()
if err != nil {
return err
}
authKey, err = resolveAuthKey(ctx, authKey, upArgs.advertiseTags)
if err != nil {
return err
}
localAPIMu.Lock()
err = localClient.Start(ctx, ipn.Options{
AuthKey: authKey,
UpdatePrefs: prefs,
})
localAPIMu.Unlock()
if err != nil {
return err
}
if upArgs.forceReauth {
startLoginInteractive()
}
}

// This whole 'up' mechanism is too complicated and results in
// hairy stuff like this select. We're ultimately waiting for
// 'running' to be done, but even in the case where
Expand All @@ -647,7 +648,7 @@ func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retE
default:
}
return watchCtx.Err()
case err := <-pumpErr:
case err := <-watchErr:
select {
case <-running:
return nil
Expand Down

0 comments on commit acf4768

Please sign in to comment.