mirror of
https://github.com/tailscale/tailscale.git
synced 2025-03-12 08:20:58 +00:00
ipn/ipnlocal: stay out of map poll when down
Fixes #2434 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
798b0da470
commit
6eecf3c9d1
@ -214,6 +214,15 @@ func (b *LocalBackend) SetDirectFileRoot(dir string) {
|
|||||||
b.directFileRoot = dir
|
b.directFileRoot = dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// b.mu must be held.
|
||||||
|
func (b *LocalBackend) maybePauseControlClientLocked() {
|
||||||
|
if b.cc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
networkUp := b.prevIfState.AnyInterfaceUp()
|
||||||
|
b.cc.SetPaused((b.state == ipn.Stopped && b.netMap != nil) || !networkUp)
|
||||||
|
}
|
||||||
|
|
||||||
// linkChange is our link monitor callback, called whenever the network changes.
|
// linkChange is our link monitor callback, called whenever the network changes.
|
||||||
// major is whether ifst is different than earlier.
|
// major is whether ifst is different than earlier.
|
||||||
func (b *LocalBackend) linkChange(major bool, ifst *interfaces.State) {
|
func (b *LocalBackend) linkChange(major bool, ifst *interfaces.State) {
|
||||||
@ -222,11 +231,7 @@ func (b *LocalBackend) linkChange(major bool, ifst *interfaces.State) {
|
|||||||
|
|
||||||
hadPAC := b.prevIfState.HasPAC()
|
hadPAC := b.prevIfState.HasPAC()
|
||||||
b.prevIfState = ifst
|
b.prevIfState = ifst
|
||||||
|
b.maybePauseControlClientLocked()
|
||||||
networkUp := ifst.AnyInterfaceUp()
|
|
||||||
if b.cc != nil {
|
|
||||||
go b.cc.SetPaused((b.state == ipn.Stopped && b.netMap != nil) || !networkUp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the PAC-ness of the network changed, reconfig wireguard+route to
|
// If the PAC-ness of the network changed, reconfig wireguard+route to
|
||||||
// add/remove subnets.
|
// add/remove subnets.
|
||||||
@ -2173,9 +2178,7 @@ func (b *LocalBackend) enterState(newState ipn.State) {
|
|||||||
oldState := b.state
|
oldState := b.state
|
||||||
b.state = newState
|
b.state = newState
|
||||||
prefs := b.prefs
|
prefs := b.prefs
|
||||||
cc := b.cc
|
|
||||||
netMap := b.netMap
|
netMap := b.netMap
|
||||||
networkUp := b.prevIfState.AnyInterfaceUp()
|
|
||||||
activeLogin := b.activeLogin
|
activeLogin := b.activeLogin
|
||||||
authURL := b.authURL
|
authURL := b.authURL
|
||||||
if newState == ipn.Running {
|
if newState == ipn.Running {
|
||||||
@ -2185,6 +2188,7 @@ func (b *LocalBackend) enterState(newState ipn.State) {
|
|||||||
// Transitioning away from running.
|
// Transitioning away from running.
|
||||||
b.closePeerAPIListenersLocked()
|
b.closePeerAPIListenersLocked()
|
||||||
}
|
}
|
||||||
|
b.maybePauseControlClientLocked()
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
|
|
||||||
if oldState == newState {
|
if oldState == newState {
|
||||||
@ -2195,10 +2199,6 @@ func (b *LocalBackend) enterState(newState ipn.State) {
|
|||||||
health.SetIPNState(newState.String(), prefs.WantRunning)
|
health.SetIPNState(newState.String(), prefs.WantRunning)
|
||||||
b.send(ipn.Notify{State: &newState})
|
b.send(ipn.Notify{State: &newState})
|
||||||
|
|
||||||
if cc != nil {
|
|
||||||
cc.SetPaused((newState == ipn.Stopped && netMap != nil) || !networkUp)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch newState {
|
switch newState {
|
||||||
case ipn.NeedsLogin:
|
case ipn.NeedsLogin:
|
||||||
systemd.Status("Needs login: %s", authURL)
|
systemd.Status("Needs login: %s", authURL)
|
||||||
@ -2459,6 +2459,7 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
|
|||||||
b.logf("active login: %v", login)
|
b.logf("active login: %v", login)
|
||||||
b.activeLogin = login
|
b.activeLogin = login
|
||||||
}
|
}
|
||||||
|
b.maybePauseControlClientLocked()
|
||||||
|
|
||||||
// Determine if file sharing is enabled
|
// Determine if file sharing is enabled
|
||||||
fs := hasCapability(nm, tailcfg.CapabilityFileSharing)
|
fs := hasCapability(nm, tailcfg.CapabilityFileSharing)
|
||||||
|
@ -354,7 +354,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
c.Assert(b.Start(ipn.Options{StateKey: ipn.GlobalDaemonStateKey}), qt.IsNil)
|
c.Assert(b.Start(ipn.Options{StateKey: ipn.GlobalDaemonStateKey}), qt.IsNil)
|
||||||
{
|
{
|
||||||
// BUG: strictly, it should pause, not unpause, here, since !WantRunning.
|
// BUG: strictly, it should pause, not unpause, here, since !WantRunning.
|
||||||
c.Assert([]string{"Shutdown", "New", "unpause"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"Shutdown", "unpause", "New", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
|
|
||||||
nn := notifies.drain(2)
|
nn := notifies.drain(2)
|
||||||
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
||||||
@ -389,7 +389,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
url1 := "http://localhost:1/1"
|
url1 := "http://localhost:1/1"
|
||||||
cc.send(nil, url1, false, nil)
|
cc.send(nil, url1, false, nil)
|
||||||
{
|
{
|
||||||
c.Assert(cc.getCalls(), qt.DeepEquals, []string{})
|
c.Assert(cc.getCalls(), qt.DeepEquals, []string{"unpause"})
|
||||||
|
|
||||||
// ...but backend eats that notification, because the user
|
// ...but backend eats that notification, because the user
|
||||||
// didn't explicitly request interactive login yet, and
|
// didn't explicitly request interactive login yet, and
|
||||||
@ -414,7 +414,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
// We're still not logged in so there's nothing we can do
|
// We're still not logged in so there's nothing we can do
|
||||||
// with it. (And empirically, it's providing an empty list
|
// with it. (And empirically, it's providing an empty list
|
||||||
// of endpoints.)
|
// of endpoints.)
|
||||||
c.Assert([]string{"UpdateEndpoints"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"UpdateEndpoints", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
c.Assert(nn[0].BrowseToURL, qt.Not(qt.IsNil))
|
c.Assert(nn[0].BrowseToURL, qt.Not(qt.IsNil))
|
||||||
c.Assert(url1, qt.Equals, *nn[0].BrowseToURL)
|
c.Assert(url1, qt.Equals, *nn[0].BrowseToURL)
|
||||||
}
|
}
|
||||||
@ -440,7 +440,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
cc.send(nil, url2, false, nil)
|
cc.send(nil, url2, false, nil)
|
||||||
{
|
{
|
||||||
// BUG: UpdateEndpoints again, this is getting silly.
|
// BUG: UpdateEndpoints again, this is getting silly.
|
||||||
c.Assert([]string{"UpdateEndpoints"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"UpdateEndpoints", "unpause", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
|
|
||||||
// This time, backend should emit it to the UI right away,
|
// This time, backend should emit it to the UI right away,
|
||||||
// because the UI is anxiously awaiting a new URL to visit.
|
// because the UI is anxiously awaiting a new URL to visit.
|
||||||
@ -470,7 +470,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
// wait until it gets into Starting.
|
// wait until it gets into Starting.
|
||||||
// TODO: (Currently this test doesn't detect that bug, but
|
// TODO: (Currently this test doesn't detect that bug, but
|
||||||
// it's visible in the logs)
|
// it's visible in the logs)
|
||||||
c.Assert([]string{"unpause", "UpdateEndpoints"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"unpause", "unpause", "UpdateEndpoints", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
c.Assert(nn[0].LoginFinished, qt.Not(qt.IsNil))
|
c.Assert(nn[0].LoginFinished, qt.Not(qt.IsNil))
|
||||||
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
|
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
|
||||||
c.Assert(nn[2].State, qt.Not(qt.IsNil))
|
c.Assert(nn[2].State, qt.Not(qt.IsNil))
|
||||||
@ -492,7 +492,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
nn := notifies.drain(1)
|
nn := notifies.drain(1)
|
||||||
c.Assert([]string{"unpause", "UpdateEndpoints"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"unpause", "unpause", "UpdateEndpoints", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
c.Assert(nn[0].State, qt.Not(qt.IsNil))
|
c.Assert(nn[0].State, qt.Not(qt.IsNil))
|
||||||
c.Assert(ipn.Starting, qt.Equals, *nn[0].State)
|
c.Assert(ipn.Starting, qt.Equals, *nn[0].State)
|
||||||
}
|
}
|
||||||
@ -534,7 +534,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
nn := notifies.drain(2)
|
nn := notifies.drain(2)
|
||||||
// BUG: UpdateEndpoints isn't needed here.
|
// BUG: UpdateEndpoints isn't needed here.
|
||||||
// BUG: Login isn't needed here. We never logged out.
|
// BUG: Login isn't needed here. We never logged out.
|
||||||
c.Assert([]string{"Login", "unpause", "UpdateEndpoints"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"Login", "unpause", "UpdateEndpoints", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
// BUG: I would expect Prefs to change first, and state after.
|
// BUG: I would expect Prefs to change first, and state after.
|
||||||
c.Assert(nn[0].State, qt.Not(qt.IsNil))
|
c.Assert(nn[0].State, qt.Not(qt.IsNil))
|
||||||
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
|
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
|
||||||
@ -570,7 +570,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
b.Logout()
|
b.Logout()
|
||||||
{
|
{
|
||||||
nn := notifies.drain(2)
|
nn := notifies.drain(2)
|
||||||
c.Assert([]string{"pause", "StartLogout"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"pause", "StartLogout", "pause"}, qt.DeepEquals, cc.getCalls())
|
||||||
c.Assert(nn[0].State, qt.Not(qt.IsNil))
|
c.Assert(nn[0].State, qt.Not(qt.IsNil))
|
||||||
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
|
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
|
||||||
c.Assert(ipn.Stopped, qt.Equals, *nn[0].State)
|
c.Assert(ipn.Stopped, qt.Equals, *nn[0].State)
|
||||||
@ -587,7 +587,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
cc.send(nil, "", false, nil)
|
cc.send(nil, "", false, nil)
|
||||||
{
|
{
|
||||||
nn := notifies.drain(1)
|
nn := notifies.drain(1)
|
||||||
c.Assert([]string{"unpause"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"unpause", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
c.Assert(nn[0].State, qt.Not(qt.IsNil))
|
c.Assert(nn[0].State, qt.Not(qt.IsNil))
|
||||||
c.Assert(ipn.NeedsLogin, qt.Equals, *nn[0].State)
|
c.Assert(ipn.NeedsLogin, qt.Equals, *nn[0].State)
|
||||||
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
|
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
|
||||||
@ -603,7 +603,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
notifies.drain(0)
|
notifies.drain(0)
|
||||||
// BUG: the backend has already called StartLogout, and we're
|
// BUG: the backend has already called StartLogout, and we're
|
||||||
// still logged out. So it shouldn't call it again.
|
// still logged out. So it shouldn't call it again.
|
||||||
c.Assert([]string{"StartLogout"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"StartLogout", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
||||||
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
|
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
|
||||||
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
|
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
|
||||||
@ -617,8 +617,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
cc.send(nil, "", false, nil)
|
cc.send(nil, "", false, nil)
|
||||||
{
|
{
|
||||||
notifies.drain(0)
|
notifies.drain(0)
|
||||||
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
c.Assert(cc.getCalls(), qt.DeepEquals, []string{"unpause", "unpause"})
|
||||||
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
|
||||||
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
|
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
|
||||||
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
|
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
|
||||||
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
||||||
@ -632,7 +631,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
// I guess, since that's supposed to be synchronous.
|
// I guess, since that's supposed to be synchronous.
|
||||||
{
|
{
|
||||||
notifies.drain(0)
|
notifies.drain(0)
|
||||||
c.Assert([]string{"Logout"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"Logout", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
||||||
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
|
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
|
||||||
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
|
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
|
||||||
@ -646,8 +645,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
cc.send(nil, "", false, nil)
|
cc.send(nil, "", false, nil)
|
||||||
{
|
{
|
||||||
notifies.drain(0)
|
notifies.drain(0)
|
||||||
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
c.Assert(cc.getCalls(), qt.DeepEquals, []string{"unpause", "unpause"})
|
||||||
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
|
||||||
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
|
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
|
||||||
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
|
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
|
||||||
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
||||||
@ -678,7 +676,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
// BUG: We already called Shutdown(), no need to do it again.
|
// BUG: We already called Shutdown(), no need to do it again.
|
||||||
// BUG: Way too soon for UpdateEndpoints.
|
// BUG: Way too soon for UpdateEndpoints.
|
||||||
// BUG: don't unpause because we're not logged in.
|
// BUG: don't unpause because we're not logged in.
|
||||||
c.Assert([]string{"Shutdown", "New", "UpdateEndpoints", "unpause"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"Shutdown", "unpause", "New", "UpdateEndpoints", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
|
|
||||||
nn := notifies.drain(2)
|
nn := notifies.drain(2)
|
||||||
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
||||||
@ -703,7 +701,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
nn := notifies.drain(3)
|
nn := notifies.drain(3)
|
||||||
c.Assert([]string{"unpause"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"unpause", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
c.Assert(nn[0].LoginFinished, qt.Not(qt.IsNil))
|
c.Assert(nn[0].LoginFinished, qt.Not(qt.IsNil))
|
||||||
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
|
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
|
||||||
c.Assert(nn[2].State, qt.Not(qt.IsNil))
|
c.Assert(nn[2].State, qt.Not(qt.IsNil))
|
||||||
@ -743,7 +741,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
// on startup, otherwise UIs can't show the node list, login
|
// on startup, otherwise UIs can't show the node list, login
|
||||||
// name, etc when in state ipn.Stopped.
|
// name, etc when in state ipn.Stopped.
|
||||||
// Arguably they shouldn't try. But they currently do.
|
// Arguably they shouldn't try. But they currently do.
|
||||||
c.Assert([]string{"Shutdown", "New", "UpdateEndpoints", "Login", "unpause"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"Shutdown", "unpause", "New", "UpdateEndpoints", "Login", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
|
|
||||||
nn := notifies.drain(2)
|
nn := notifies.drain(2)
|
||||||
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
||||||
@ -817,7 +815,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
nn := notifies.drain(3)
|
nn := notifies.drain(3)
|
||||||
c.Assert([]string{"unpause"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"unpause", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
c.Assert(nn[0].LoginFinished, qt.Not(qt.IsNil))
|
c.Assert(nn[0].LoginFinished, qt.Not(qt.IsNil))
|
||||||
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
|
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
|
||||||
c.Assert(nn[2].State, qt.Not(qt.IsNil))
|
c.Assert(nn[2].State, qt.Not(qt.IsNil))
|
||||||
@ -836,7 +834,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
{
|
{
|
||||||
// NOTE: cc.Shutdown() is correct here, since we didn't call
|
// NOTE: cc.Shutdown() is correct here, since we didn't call
|
||||||
// b.Shutdown() ourselves.
|
// b.Shutdown() ourselves.
|
||||||
c.Assert([]string{"Shutdown", "New", "UpdateEndpoints", "Login"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"Shutdown", "unpause", "New", "UpdateEndpoints", "Login", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
|
|
||||||
nn := notifies.drain(1)
|
nn := notifies.drain(1)
|
||||||
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
c.Assert(cc.getCalls(), qt.HasLen, 0)
|
||||||
@ -855,7 +853,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
nn := notifies.drain(1)
|
nn := notifies.drain(1)
|
||||||
c.Assert([]string{"unpause"}, qt.DeepEquals, cc.getCalls())
|
c.Assert([]string{"unpause", "unpause"}, qt.DeepEquals, cc.getCalls())
|
||||||
// NOTE: No LoginFinished message since no interactive
|
// NOTE: No LoginFinished message since no interactive
|
||||||
// login was needed.
|
// login was needed.
|
||||||
c.Assert(nn[0].State, qt.Not(qt.IsNil))
|
c.Assert(nn[0].State, qt.Not(qt.IsNil))
|
||||||
|
@ -205,6 +205,13 @@ func (lc *LogCatcher) logsString() string {
|
|||||||
return lc.buf.String()
|
return lc.buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset clears the buffered logs from memory.
|
||||||
|
func (lc *LogCatcher) Reset() {
|
||||||
|
lc.mu.Lock()
|
||||||
|
defer lc.mu.Unlock()
|
||||||
|
lc.buf.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
func (lc *LogCatcher) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (lc *LogCatcher) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
var body io.Reader = r.Body
|
var body io.Reader = r.Body
|
||||||
if r.Header.Get("Content-Encoding") == "zstd" {
|
if r.Header.Get("Content-Encoding") == "zstd" {
|
||||||
|
@ -72,29 +72,9 @@ func TestOneNodeUp_NoAuth(t *testing.T) {
|
|||||||
|
|
||||||
d1 := n1.StartDaemon(t)
|
d1 := n1.StartDaemon(t)
|
||||||
defer d1.Kill()
|
defer d1.Kill()
|
||||||
|
n1.AwaitResponding(t)
|
||||||
n1.AwaitListening(t)
|
|
||||||
|
|
||||||
st := n1.MustStatus(t)
|
|
||||||
t.Logf("Status: %s", st.BackendState)
|
|
||||||
|
|
||||||
if err := tstest.WaitFor(20*time.Second, func() error {
|
|
||||||
const sub = `Program starting: `
|
|
||||||
if !env.LogCatcher.logsContains(mem.S(sub)) {
|
|
||||||
return fmt.Errorf("log catcher didn't see %#q; got %s", sub, env.LogCatcher.logsString())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
n1.MustUp()
|
n1.MustUp()
|
||||||
|
|
||||||
if d, _ := time.ParseDuration(os.Getenv("TS_POST_UP_SLEEP")); d > 0 {
|
|
||||||
t.Logf("Sleeping for %v to give 'up' time to misbehave (https://github.com/tailscale/tailscale/issues/1840) ...", d)
|
|
||||||
time.Sleep(d)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Got IP: %v", n1.AwaitIP(t))
|
t.Logf("Got IP: %v", n1.AwaitIP(t))
|
||||||
n1.AwaitRunning(t)
|
n1.AwaitRunning(t)
|
||||||
|
|
||||||
@ -115,22 +95,7 @@ func TestStateSavedOnStart(t *testing.T) {
|
|||||||
|
|
||||||
d1 := n1.StartDaemon(t)
|
d1 := n1.StartDaemon(t)
|
||||||
defer d1.Kill()
|
defer d1.Kill()
|
||||||
|
n1.AwaitResponding(t)
|
||||||
n1.AwaitListening(t)
|
|
||||||
|
|
||||||
st := n1.MustStatus(t)
|
|
||||||
t.Logf("Status: %s", st.BackendState)
|
|
||||||
|
|
||||||
if err := tstest.WaitFor(20*time.Second, func() error {
|
|
||||||
const sub = `Program starting: `
|
|
||||||
if !env.LogCatcher.logsContains(mem.S(sub)) {
|
|
||||||
return fmt.Errorf("log catcher didn't see %#q; got %s", sub, env.LogCatcher.logsString())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
n1.MustUp()
|
n1.MustUp()
|
||||||
|
|
||||||
t.Logf("Got IP: %v", n1.AwaitIP(t))
|
t.Logf("Got IP: %v", n1.AwaitIP(t))
|
||||||
@ -346,6 +311,53 @@ func TestAddPingRequest(t *testing.T) {
|
|||||||
t.Error("all ping attempts failed")
|
t.Error("all ping attempts failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 2434: when "down" (WantRunning false), tailscaled shouldn't
|
||||||
|
// be connected to control.
|
||||||
|
func TestNoControlConnectionWhenDown(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
bins := BuildTestBinaries(t)
|
||||||
|
|
||||||
|
env := newTestEnv(t, bins)
|
||||||
|
defer env.Close()
|
||||||
|
|
||||||
|
n1 := newTestNode(t, env)
|
||||||
|
|
||||||
|
d1 := n1.StartDaemon(t)
|
||||||
|
defer d1.Kill()
|
||||||
|
n1.AwaitResponding(t)
|
||||||
|
|
||||||
|
// Come up the first time.
|
||||||
|
n1.MustUp()
|
||||||
|
ip1 := n1.AwaitIP(t)
|
||||||
|
n1.AwaitRunning(t)
|
||||||
|
|
||||||
|
// Then bring it down and stop the daemon.
|
||||||
|
n1.MustDown()
|
||||||
|
d1.MustCleanShutdown(t)
|
||||||
|
|
||||||
|
env.LogCatcher.Reset()
|
||||||
|
d2 := n1.StartDaemon(t)
|
||||||
|
defer d2.Kill()
|
||||||
|
n1.AwaitResponding(t)
|
||||||
|
|
||||||
|
st := n1.MustStatus(t)
|
||||||
|
if got, want := st.BackendState, "Stopped"; got != want {
|
||||||
|
t.Fatalf("after restart, state = %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip2 := n1.AwaitIP(t)
|
||||||
|
if ip1 != ip2 {
|
||||||
|
t.Errorf("IPs different: %q vs %q", ip1, ip2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The real test: verify our daemon doesn't have an HTTP request open.:
|
||||||
|
if n := env.Control.InServeMap(); n != 0 {
|
||||||
|
t.Errorf("in serve map = %d; want 0", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
d2.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 {
|
||||||
@ -462,6 +474,26 @@ func (n *testNode) diskPrefs(t testing.TB) *ipn.Prefs {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AwaitResponding waits for n's tailscaled to be up enough to be
|
||||||
|
// responding, but doesn't wait for any particular state.
|
||||||
|
func (n *testNode) AwaitResponding(t testing.TB) {
|
||||||
|
t.Helper()
|
||||||
|
n.AwaitListening(t)
|
||||||
|
|
||||||
|
st := n.MustStatus(t)
|
||||||
|
t.Logf("Status: %s", st.BackendState)
|
||||||
|
|
||||||
|
if err := tstest.WaitFor(20*time.Second, func() error {
|
||||||
|
const sub = `Program starting: `
|
||||||
|
if !n.env.LogCatcher.logsContains(mem.S(sub)) {
|
||||||
|
return fmt.Errorf("log catcher didn't see %#q; got %s", sub, n.env.LogCatcher.logsString())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// addLogLineHook registers a hook f to be called on each tailscaled
|
// addLogLineHook registers a hook f to be called on each tailscaled
|
||||||
// log line output.
|
// log line output.
|
||||||
func (n *testNode) addLogLineHook(f func([]byte)) {
|
func (n *testNode) addLogLineHook(f func([]byte)) {
|
||||||
@ -654,6 +686,7 @@ func (n *testNode) AwaitIP(t testing.TB) netaddr.IP {
|
|||||||
return ips[0]
|
return ips[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AwaitRunning waits for n to reach the IPN state "Running".
|
||||||
func (n *testNode) AwaitRunning(t testing.TB) {
|
func (n *testNode) AwaitRunning(t testing.TB) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if err := tstest.WaitFor(20*time.Second, func() error {
|
if err := tstest.WaitFor(20*time.Second, func() error {
|
||||||
|
@ -51,6 +51,7 @@ type Server struct {
|
|||||||
mux *http.ServeMux
|
mux *http.ServeMux
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
inServeMap int
|
||||||
cond *sync.Cond // lazily initialized by condLocked
|
cond *sync.Cond // lazily initialized by condLocked
|
||||||
pubKey wgkey.Key
|
pubKey wgkey.Key
|
||||||
privKey wgkey.Private
|
privKey wgkey.Private
|
||||||
@ -509,7 +510,22 @@ func (s *Server) UpdateNode(n *tailcfg.Node) (peersToUpdate []tailcfg.NodeID) {
|
|||||||
return peersToUpdate
|
return peersToUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) incrInServeMap(delta int) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
s.inServeMap += delta
|
||||||
|
}
|
||||||
|
|
||||||
|
// InServeMap returns the number of clients currently in a MapRequest HTTP handler.
|
||||||
|
func (s *Server) InServeMap() int {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.inServeMap
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) serveMap(w http.ResponseWriter, r *http.Request, mkey tailcfg.MachineKey) {
|
func (s *Server) serveMap(w http.ResponseWriter, r *http.Request, mkey tailcfg.MachineKey) {
|
||||||
|
s.incrInServeMap(1)
|
||||||
|
defer s.incrInServeMap(-1)
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
req := new(tailcfg.MapRequest)
|
req := new(tailcfg.MapRequest)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user