mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
Add prefs.LoggedOut to fix several state machine bugs.
Fixes: tailscale/corp#1660 Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
This commit is contained in:
parent
b0382ca167
commit
2a4d1cf9e2
@ -445,6 +445,10 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
|
|||||||
// Auth completed, unblock the engine
|
// Auth completed, unblock the engine
|
||||||
b.blockEngineUpdates(false)
|
b.blockEngineUpdates(false)
|
||||||
b.authReconfig()
|
b.authReconfig()
|
||||||
|
b.EditPrefs(&ipn.MaskedPrefs{
|
||||||
|
LoggedOutSet: true,
|
||||||
|
Prefs: ipn.Prefs{LoggedOut: false},
|
||||||
|
})
|
||||||
b.send(ipn.Notify{LoginFinished: &empty.Message{}})
|
b.send(ipn.Notify{LoginFinished: &empty.Message{}})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,7 +647,6 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
return errors.New("no state key or prefs provided")
|
return errors.New("no state key or prefs provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer b.stateMachine()
|
|
||||||
if opts.Prefs != nil {
|
if opts.Prefs != nil {
|
||||||
b.logf("Start: %v", opts.Prefs.Pretty())
|
b.logf("Start: %v", opts.Prefs.Pretty())
|
||||||
} else {
|
} else {
|
||||||
@ -707,6 +710,8 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loggedOut := b.prefs.LoggedOut
|
||||||
|
|
||||||
b.inServerMode = b.prefs.ForceDaemon
|
b.inServerMode = b.prefs.ForceDaemon
|
||||||
b.serverURL = b.prefs.ControlURLOrDefault()
|
b.serverURL = b.prefs.ControlURLOrDefault()
|
||||||
hostinfo.RoutableIPs = append(hostinfo.RoutableIPs, b.prefs.AdvertiseRoutes...)
|
hostinfo.RoutableIPs = append(hostinfo.RoutableIPs, b.prefs.AdvertiseRoutes...)
|
||||||
@ -804,8 +809,16 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
b.send(ipn.Notify{BackendLogID: &blid})
|
b.send(ipn.Notify{BackendLogID: &blid})
|
||||||
b.send(ipn.Notify{Prefs: prefs})
|
b.send(ipn.Notify{Prefs: prefs})
|
||||||
|
|
||||||
if wantRunning {
|
if wantRunning && !loggedOut {
|
||||||
cc.Login(nil, controlclient.LoginDefault)
|
cc.Login(nil, controlclient.LoginDefault)
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
b.state = ipn.Starting
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
b.send(ipn.Notify{State: &b.state})
|
||||||
|
} else {
|
||||||
|
b.stateMachine()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2118,12 +2131,17 @@ func (b *LocalBackend) nextState() ipn.State {
|
|||||||
netMap = b.netMap
|
netMap = b.netMap
|
||||||
state = b.state
|
state = b.state
|
||||||
wantRunning = b.prefs.WantRunning
|
wantRunning = b.prefs.WantRunning
|
||||||
|
loggedOut = b.prefs.LoggedOut
|
||||||
|
hasNodeKey = b.prefs.Persist != nil &&
|
||||||
|
!b.prefs.Persist.PrivateNodeKey.IsZero()
|
||||||
)
|
)
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
case !wantRunning && !loggedOut && hasNodeKey:
|
||||||
|
return ipn.Stopped
|
||||||
case netMap == nil:
|
case netMap == nil:
|
||||||
if cc.AuthCantContinue() {
|
if cc.AuthCantContinue() || loggedOut {
|
||||||
// Auth was interrupted or waiting for URL visit,
|
// Auth was interrupted or waiting for URL visit,
|
||||||
// so it won't proceed without human help.
|
// so it won't proceed without human help.
|
||||||
return ipn.NeedsLogin
|
return ipn.NeedsLogin
|
||||||
@ -2238,7 +2256,8 @@ func (b *LocalBackend) logout(ctx context.Context, sync bool) error {
|
|||||||
|
|
||||||
b.EditPrefs(&ipn.MaskedPrefs{
|
b.EditPrefs(&ipn.MaskedPrefs{
|
||||||
WantRunningSet: true,
|
WantRunningSet: true,
|
||||||
Prefs: ipn.Prefs{WantRunning: false},
|
LoggedOutSet: true,
|
||||||
|
Prefs: ipn.Prefs{WantRunning: false, LoggedOut: true},
|
||||||
})
|
})
|
||||||
|
|
||||||
if cc == nil {
|
if cc == nil {
|
||||||
|
@ -321,6 +321,10 @@ func TestStateMachine(t *testing.T) {
|
|||||||
assert.NotNil(nn[0].Prefs)
|
assert.NotNil(nn[0].Prefs)
|
||||||
assert.NotNil(nn[1].State)
|
assert.NotNil(nn[1].State)
|
||||||
prefs := *nn[0].Prefs
|
prefs := *nn[0].Prefs
|
||||||
|
// Note: a totally fresh system has Prefs.LoggedOut=false by
|
||||||
|
// default. We are logged out, but not because the user asked
|
||||||
|
// for it, so it doesn't count as Prefs.LoggedOut==true.
|
||||||
|
assert.Equal(false, nn[0].Prefs.LoggedOut)
|
||||||
assert.Equal(false, prefs.WantRunning)
|
assert.Equal(false, prefs.WantRunning)
|
||||||
assert.Equal(ipn.NeedsLogin, *nn[1].State)
|
assert.Equal(ipn.NeedsLogin, *nn[1].State)
|
||||||
assert.Equal(ipn.NeedsLogin, b.State())
|
assert.Equal(ipn.NeedsLogin, b.State())
|
||||||
@ -343,6 +347,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
assert.Equal([]string{}, cc.getCalls())
|
assert.Equal([]string{}, cc.getCalls())
|
||||||
assert.NotNil(nn[0].Prefs)
|
assert.NotNil(nn[0].Prefs)
|
||||||
assert.NotNil(nn[1].State)
|
assert.NotNil(nn[1].State)
|
||||||
|
assert.Equal(false, nn[0].Prefs.LoggedOut)
|
||||||
assert.Equal(false, nn[0].Prefs.WantRunning)
|
assert.Equal(false, nn[0].Prefs.WantRunning)
|
||||||
assert.Equal(ipn.NeedsLogin, *nn[1].State)
|
assert.Equal(ipn.NeedsLogin, *nn[1].State)
|
||||||
assert.Equal(ipn.NeedsLogin, b.State())
|
assert.Equal(ipn.NeedsLogin, b.State())
|
||||||
@ -383,6 +388,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
// Trying to log in automatically sets WantRunning.
|
// Trying to log in automatically sets WantRunning.
|
||||||
// BUG: that should have happened right after Login().
|
// BUG: that should have happened right after Login().
|
||||||
assert.NotNil(nn[0].Prefs)
|
assert.NotNil(nn[0].Prefs)
|
||||||
|
assert.Equal(false, nn[0].Prefs.LoggedOut)
|
||||||
assert.Equal(true, nn[0].Prefs.WantRunning)
|
assert.Equal(true, nn[0].Prefs.WantRunning)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,16 +561,18 @@ func TestStateMachine(t *testing.T) {
|
|||||||
|
|
||||||
// User wants to logout.
|
// User wants to logout.
|
||||||
t.Logf("\n\nLogout (async)")
|
t.Logf("\n\nLogout (async)")
|
||||||
notifies.expect(1)
|
notifies.expect(2)
|
||||||
b.Logout()
|
b.Logout()
|
||||||
{
|
{
|
||||||
nn := notifies.drain(1)
|
nn := notifies.drain(2)
|
||||||
assert.Equal([]string{"StartLogout"}, cc.getCalls())
|
// BUG: now is not the time to unpause.
|
||||||
assert.NotNil(nn[0].Prefs)
|
assert.Equal([]string{"unpause", "StartLogout"}, cc.getCalls())
|
||||||
// NOTE: WantRunning is false here, which is right. But
|
assert.NotNil(nn[0].State)
|
||||||
// it changes to true in subsequent logout attempts, which is
|
assert.NotNil(nn[1].Prefs)
|
||||||
// wrong in those attempts.
|
assert.Equal(ipn.NeedsLogin, *nn[0].State)
|
||||||
assert.Equal(false, nn[0].Prefs.WantRunning)
|
assert.Equal(true, nn[1].Prefs.LoggedOut)
|
||||||
|
assert.Equal(false, nn[1].Prefs.WantRunning)
|
||||||
|
assert.Equal(ipn.NeedsLogin, b.State())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's make the logout succeed.
|
// Let's make the logout succeed.
|
||||||
@ -574,21 +582,27 @@ func TestStateMachine(t *testing.T) {
|
|||||||
cc.send(nil, "", false, nil)
|
cc.send(nil, "", false, nil)
|
||||||
{
|
{
|
||||||
nn := notifies.drain(1)
|
nn := notifies.drain(1)
|
||||||
// BUG: now is not the time to unpause.
|
assert.Equal([]string{}, cc.getCalls())
|
||||||
assert.Equal([]string{"unpause"}, cc.getCalls())
|
assert.NotNil(nn[0].Prefs)
|
||||||
assert.NotNil(nn[0].State)
|
assert.Equal(true, nn[0].Prefs.LoggedOut)
|
||||||
assert.Equal(ipn.NeedsLogin, *nn[0].State)
|
// BUG: WantRunning should be false after manual logout.
|
||||||
|
assert.Equal(true, nn[0].Prefs.WantRunning)
|
||||||
|
assert.Equal(ipn.NeedsLogin, b.State())
|
||||||
}
|
}
|
||||||
|
|
||||||
// A second logout should do nothing, since the prefs haven't changed.
|
// A second logout should do nothing, since the prefs haven't changed.
|
||||||
t.Logf("\n\nLogout2 (async)")
|
t.Logf("\n\nLogout2 (async)")
|
||||||
notifies.expect(0)
|
notifies.expect(1)
|
||||||
b.Logout()
|
b.Logout()
|
||||||
{
|
{
|
||||||
notifies.drain(0)
|
nn := notifies.drain(1)
|
||||||
// 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.
|
||||||
assert.Equal([]string{"StartLogout"}, cc.getCalls())
|
assert.Equal([]string{"StartLogout"}, cc.getCalls())
|
||||||
|
// BUG: Prefs should not change here. Already logged out.
|
||||||
|
assert.NotNil(nn[0].Prefs)
|
||||||
|
assert.Equal(true, nn[0].Prefs.LoggedOut)
|
||||||
|
assert.Equal(false, nn[0].Prefs.WantRunning)
|
||||||
assert.Equal(ipn.NeedsLogin, b.State())
|
assert.Equal(ipn.NeedsLogin, b.State())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,6 +615,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
nn := notifies.drain(1)
|
nn := notifies.drain(1)
|
||||||
assert.Equal([]string{}, cc.getCalls())
|
assert.Equal([]string{}, cc.getCalls())
|
||||||
assert.NotNil(nn[0].Prefs)
|
assert.NotNil(nn[0].Prefs)
|
||||||
|
assert.Equal(true, nn[0].Prefs.LoggedOut)
|
||||||
// BUG: second logout shouldn't cause WantRunning->true !!
|
// BUG: second logout shouldn't cause WantRunning->true !!
|
||||||
assert.Equal(true, nn[0].Prefs.WantRunning)
|
assert.Equal(true, nn[0].Prefs.WantRunning)
|
||||||
assert.Equal(ipn.NeedsLogin, b.State())
|
assert.Equal(ipn.NeedsLogin, b.State())
|
||||||
@ -616,6 +631,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
nn := notifies.drain(1)
|
nn := notifies.drain(1)
|
||||||
assert.Equal([]string{"Logout"}, cc.getCalls())
|
assert.Equal([]string{"Logout"}, cc.getCalls())
|
||||||
assert.NotNil(nn[0].Prefs)
|
assert.NotNil(nn[0].Prefs)
|
||||||
|
assert.Equal(true, nn[0].Prefs.LoggedOut)
|
||||||
assert.Equal(false, nn[0].Prefs.WantRunning)
|
assert.Equal(false, nn[0].Prefs.WantRunning)
|
||||||
assert.Equal(ipn.NeedsLogin, b.State())
|
assert.Equal(ipn.NeedsLogin, b.State())
|
||||||
}
|
}
|
||||||
@ -629,6 +645,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
nn := notifies.drain(1)
|
nn := notifies.drain(1)
|
||||||
assert.Equal([]string{}, cc.getCalls())
|
assert.Equal([]string{}, cc.getCalls())
|
||||||
assert.NotNil(nn[0].Prefs)
|
assert.NotNil(nn[0].Prefs)
|
||||||
|
assert.Equal(true, nn[0].Prefs.LoggedOut)
|
||||||
// BUG: third logout shouldn't cause WantRunning->true !!
|
// BUG: third logout shouldn't cause WantRunning->true !!
|
||||||
assert.Equal(true, nn[0].Prefs.WantRunning)
|
assert.Equal(true, nn[0].Prefs.WantRunning)
|
||||||
assert.Equal(ipn.NeedsLogin, b.State())
|
assert.Equal(ipn.NeedsLogin, b.State())
|
||||||
@ -664,16 +681,14 @@ 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: We've forgotten that we logged out earlier.
|
// BUG: don't unpause because we're not logged in.
|
||||||
// Shouldn't cc.Login() until the user engages, or else we
|
assert.Equal([]string{"Shutdown", "New", "UpdateEndpoints", "unpause"}, cc.getCalls())
|
||||||
// defeat the "no traffic until `tailscale up`" logic.
|
|
||||||
// BUG: strictly, it should pause, not unpause, here.
|
|
||||||
assert.Equal([]string{"Shutdown", "New", "UpdateEndpoints", "Login", "unpause"}, cc.getCalls())
|
|
||||||
|
|
||||||
nn := notifies.drain(2)
|
nn := notifies.drain(2)
|
||||||
assert.Equal([]string{}, cc.getCalls())
|
assert.Equal([]string{}, cc.getCalls())
|
||||||
assert.NotNil(nn[0].Prefs)
|
assert.NotNil(nn[0].Prefs)
|
||||||
assert.NotNil(nn[1].State)
|
assert.NotNil(nn[1].State)
|
||||||
|
assert.Equal(true, nn[0].Prefs.LoggedOut)
|
||||||
assert.Equal(true, nn[0].Prefs.WantRunning)
|
assert.Equal(true, nn[0].Prefs.WantRunning)
|
||||||
assert.Equal(ipn.NeedsLogin, *nn[1].State)
|
assert.Equal(ipn.NeedsLogin, *nn[1].State)
|
||||||
assert.Equal(ipn.NeedsLogin, b.State())
|
assert.Equal(ipn.NeedsLogin, b.State())
|
||||||
@ -684,17 +699,19 @@ func TestStateMachine(t *testing.T) {
|
|||||||
// (This simulates an admin reviving a key that you previously
|
// (This simulates an admin reviving a key that you previously
|
||||||
// disabled.)
|
// disabled.)
|
||||||
t.Logf("\n\nLoginFinished3")
|
t.Logf("\n\nLoginFinished3")
|
||||||
notifies.expect(2)
|
notifies.expect(3)
|
||||||
cc.setAuthBlocked(false)
|
cc.setAuthBlocked(false)
|
||||||
cc.send(nil, "", true, &netmap.NetworkMap{
|
cc.send(nil, "", true, &netmap.NetworkMap{
|
||||||
MachineStatus: tailcfg.MachineAuthorized,
|
MachineStatus: tailcfg.MachineAuthorized,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
nn := notifies.drain(2)
|
nn := notifies.drain(3)
|
||||||
assert.Equal([]string{"unpause"}, cc.getCalls())
|
assert.Equal([]string{"unpause"}, cc.getCalls())
|
||||||
assert.NotNil(nn[0].LoginFinished)
|
assert.NotNil(nn[0].Prefs)
|
||||||
assert.NotNil(nn[1].State)
|
assert.NotNil(nn[1].LoginFinished)
|
||||||
assert.Equal(ipn.Starting, *nn[1].State)
|
assert.NotNil(nn[2].State)
|
||||||
|
assert.Equal(false, nn[0].Prefs.LoggedOut)
|
||||||
|
assert.Equal(ipn.Starting, *nn[2].State)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we've logged in successfully. Let's disconnect.
|
// Now we've logged in successfully. Let's disconnect.
|
||||||
@ -711,6 +728,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
assert.NotNil(nn[0].State)
|
assert.NotNil(nn[0].State)
|
||||||
assert.NotNil(nn[1].Prefs)
|
assert.NotNil(nn[1].Prefs)
|
||||||
assert.Equal(ipn.Stopped, *nn[0].State)
|
assert.Equal(ipn.Stopped, *nn[0].State)
|
||||||
|
assert.Equal(false, nn[1].Prefs.LoggedOut)
|
||||||
}
|
}
|
||||||
|
|
||||||
// One more restart, this time with a valid key, but WantRunning=false.
|
// One more restart, this time with a valid key, but WantRunning=false.
|
||||||
@ -722,20 +740,16 @@ 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() explicitly ourselves.
|
// b.Shutdown() explicitly ourselves.
|
||||||
assert.Equal([]string{"Shutdown", "New", "UpdateEndpoints", "unpause"}, cc.getCalls())
|
// BUG: UpdateEndpoints should be called here since we're not WantRunning.
|
||||||
|
assert.Equal([]string{"Shutdown", "New", "UpdateEndpoints", "pause"}, cc.getCalls())
|
||||||
|
|
||||||
nn := notifies.drain(2)
|
nn := notifies.drain(2)
|
||||||
assert.Equal([]string{}, cc.getCalls())
|
assert.Equal([]string{}, cc.getCalls())
|
||||||
assert.NotNil(nn[0].Prefs)
|
assert.NotNil(nn[0].Prefs)
|
||||||
assert.NotNil(nn[1].State)
|
assert.NotNil(nn[1].State)
|
||||||
prefs := *nn[0].Prefs
|
assert.Equal(false, nn[0].Prefs.WantRunning)
|
||||||
assert.Equal(false, prefs.WantRunning)
|
assert.Equal(false, nn[0].Prefs.LoggedOut)
|
||||||
// BUG: NeedsLogin is incorrect. We are already logged in.
|
assert.Equal(ipn.Stopped, *nn[1].State)
|
||||||
// This is the cause of bug tailscale/corp#1660.
|
|
||||||
// (Whenever we enter the NeedsLogin state, the UI will
|
|
||||||
// sent the user a notification that they should log in.)
|
|
||||||
assert.Equal(ipn.NeedsLogin, *nn[1].State)
|
|
||||||
assert.Equal(ipn.NeedsLogin, b.State())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This time, let's have the control server spontaneously
|
// This time, let's have the control server spontaneously
|
||||||
@ -744,25 +758,35 @@ func TestStateMachine(t *testing.T) {
|
|||||||
// it. (Normally we should call b.Login() first, but we already
|
// it. (Normally we should call b.Login() first, but we already
|
||||||
// tested that up above.)
|
// tested that up above.)
|
||||||
t.Logf("\n\nLoginFinished4")
|
t.Logf("\n\nLoginFinished4")
|
||||||
notifies.expect(3)
|
notifies.expect(1)
|
||||||
cc.setAuthBlocked(false)
|
cc.setAuthBlocked(false)
|
||||||
cc.send(nil, "", true, &netmap.NetworkMap{
|
cc.send(nil, "", true, &netmap.NetworkMap{
|
||||||
MachineStatus: tailcfg.MachineAuthorized,
|
MachineStatus: tailcfg.MachineAuthorized,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
nn := notifies.drain(3)
|
nn := notifies.drain(1)
|
||||||
assert.Equal([]string{"unpause"}, cc.getCalls())
|
// NOTE: No call to Login() here since !WantRunning at
|
||||||
|
// startup time.
|
||||||
|
assert.Equal([]string{}, cc.getCalls())
|
||||||
assert.NotNil(nn[0].LoginFinished)
|
assert.NotNil(nn[0].LoginFinished)
|
||||||
|
// State is still ipn.Stopped, as expected.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request connection.
|
||||||
|
// The state machine didn't call Login() earlier, so now it needs to.
|
||||||
|
t.Logf("\n\nWantRunning4 -> true")
|
||||||
|
notifies.expect(2)
|
||||||
|
b.EditPrefs(&ipn.MaskedPrefs{
|
||||||
|
WantRunningSet: true,
|
||||||
|
Prefs: ipn.Prefs{WantRunning: true},
|
||||||
|
})
|
||||||
|
{
|
||||||
|
nn := notifies.drain(2)
|
||||||
|
assert.Equal([]string{"unpause", "Login"}, cc.getCalls())
|
||||||
|
// BUG: I would expect Prefs to change first, and state after.
|
||||||
|
assert.NotNil(nn[0].State)
|
||||||
assert.NotNil(nn[1].Prefs)
|
assert.NotNil(nn[1].Prefs)
|
||||||
assert.NotNil(nn[2].State)
|
assert.Equal(ipn.Starting, *nn[0].State)
|
||||||
// BUG: WantRunning spontaneously shifted to true.
|
|
||||||
// This should happen if a user calls b.Login(), but not if
|
|
||||||
// a login spontaneously finishes.
|
|
||||||
// This could lead to a bug where the control server could
|
|
||||||
// remotely cause a node to re-connect when it's supposed to
|
|
||||||
// be in the Stopped state.
|
|
||||||
assert.Equal(true, nn[1].Prefs.WantRunning)
|
|
||||||
assert.Equal(ipn.Starting, *nn[2].State)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The last test case is the most common one: restarting when both
|
// The last test case is the most common one: restarting when both
|
||||||
@ -775,40 +799,32 @@ 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.
|
||||||
assert.Equal([]string{"Shutdown", "New", "UpdateEndpoints", "Login", "unpause"}, cc.getCalls())
|
assert.Equal([]string{"Shutdown", "New", "UpdateEndpoints", "Login"}, cc.getCalls())
|
||||||
|
|
||||||
nn := notifies.drain(2)
|
nn := notifies.drain(2)
|
||||||
assert.Equal([]string{}, cc.getCalls())
|
assert.Equal([]string{}, cc.getCalls())
|
||||||
assert.NotNil(nn[0].Prefs)
|
assert.NotNil(nn[0].Prefs)
|
||||||
assert.NotNil(nn[1].State)
|
assert.NotNil(nn[1].State)
|
||||||
prefs := *nn[0].Prefs
|
assert.Equal(false, nn[0].Prefs.LoggedOut)
|
||||||
assert.Equal(true, prefs.WantRunning)
|
assert.Equal(true, nn[0].Prefs.WantRunning)
|
||||||
// BUG: NeedsLogin is very wrong, it could trigger a login
|
assert.Equal(ipn.Starting, *nn[1].State)
|
||||||
// notification. (It goes away quickly once the control
|
assert.Equal(ipn.Starting, b.State())
|
||||||
// server gets back to us, but this race condition is
|
|
||||||
// enough to probably cause spurious notifications. I think
|
|
||||||
// I've seen this on Windows.)
|
|
||||||
//
|
|
||||||
// Probably we should go to Starting right away here, since
|
|
||||||
// WantRunning==true and we believe (so far) that our key
|
|
||||||
// is valid.
|
|
||||||
assert.Equal(ipn.NeedsLogin, *nn[1].State)
|
|
||||||
assert.Equal(ipn.NeedsLogin, b.State())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control server accepts our valid key from before.
|
// Control server accepts our valid key from before.
|
||||||
t.Logf("\n\nLoginFinished5")
|
t.Logf("\n\nLoginFinished5")
|
||||||
notifies.expect(2)
|
notifies.expect(1)
|
||||||
cc.setAuthBlocked(false)
|
cc.setAuthBlocked(false)
|
||||||
cc.send(nil, "", true, &netmap.NetworkMap{
|
cc.send(nil, "", true, &netmap.NetworkMap{
|
||||||
MachineStatus: tailcfg.MachineAuthorized,
|
MachineStatus: tailcfg.MachineAuthorized,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
nn := notifies.drain(2)
|
nn := notifies.drain(1)
|
||||||
assert.Equal([]string{"unpause"}, cc.getCalls())
|
assert.Equal([]string{}, cc.getCalls())
|
||||||
assert.NotNil(nn[0].LoginFinished)
|
assert.NotNil(nn[0].LoginFinished)
|
||||||
// NOTE: No prefs change this time. WantRunning stays true.
|
// NOTE: No prefs change this time. WantRunning stays true.
|
||||||
assert.NotNil(nn[1].State)
|
// We were in Starting in the first place, so that doesn't
|
||||||
assert.Equal(ipn.Starting, *nn[1].State)
|
// change either.
|
||||||
|
assert.Equal(ipn.Starting, b.State())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
ipn/prefs.go
13
ipn/prefs.go
@ -87,6 +87,14 @@ type Prefs struct {
|
|||||||
// this node.
|
// this node.
|
||||||
WantRunning bool
|
WantRunning bool
|
||||||
|
|
||||||
|
// LoggedOut indicates whether the user intends to be logged out.
|
||||||
|
// There are other reasons we may be logged out, including no valid
|
||||||
|
// keys.
|
||||||
|
// We need to remember this state so that, on next startup, we can
|
||||||
|
// generate the "Login" vs "Connect" buttons correctly, without having
|
||||||
|
// to contact the server to confirm our nodekey status first.
|
||||||
|
LoggedOut bool
|
||||||
|
|
||||||
// ShieldsUp indicates whether to block all incoming connections,
|
// ShieldsUp indicates whether to block all incoming connections,
|
||||||
// regardless of the control-provided packet filter. If false, we
|
// regardless of the control-provided packet filter. If false, we
|
||||||
// use the packet filter as provided. If true, we block incoming
|
// use the packet filter as provided. If true, we block incoming
|
||||||
@ -177,6 +185,7 @@ type MaskedPrefs struct {
|
|||||||
ExitNodeAllowLANAccessSet bool `json:",omitempty"`
|
ExitNodeAllowLANAccessSet bool `json:",omitempty"`
|
||||||
CorpDNSSet bool `json:",omitempty"`
|
CorpDNSSet bool `json:",omitempty"`
|
||||||
WantRunningSet bool `json:",omitempty"`
|
WantRunningSet bool `json:",omitempty"`
|
||||||
|
LoggedOutSet bool `json:",omitempty"`
|
||||||
ShieldsUpSet bool `json:",omitempty"`
|
ShieldsUpSet bool `json:",omitempty"`
|
||||||
AdvertiseTagsSet bool `json:",omitempty"`
|
AdvertiseTagsSet bool `json:",omitempty"`
|
||||||
HostnameSet bool `json:",omitempty"`
|
HostnameSet bool `json:",omitempty"`
|
||||||
@ -246,6 +255,9 @@ func (p *Prefs) pretty(goos string) string {
|
|||||||
sb.WriteString("mesh=false ")
|
sb.WriteString("mesh=false ")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&sb, "dns=%v want=%v ", p.CorpDNS, p.WantRunning)
|
fmt.Fprintf(&sb, "dns=%v want=%v ", p.CorpDNS, p.WantRunning)
|
||||||
|
if p.LoggedOut {
|
||||||
|
sb.WriteString("loggedout=true ")
|
||||||
|
}
|
||||||
if p.ForceDaemon {
|
if p.ForceDaemon {
|
||||||
sb.WriteString("server=true ")
|
sb.WriteString("server=true ")
|
||||||
}
|
}
|
||||||
@ -315,6 +327,7 @@ func (p *Prefs) Equals(p2 *Prefs) bool {
|
|||||||
p.ExitNodeAllowLANAccess == p2.ExitNodeAllowLANAccess &&
|
p.ExitNodeAllowLANAccess == p2.ExitNodeAllowLANAccess &&
|
||||||
p.CorpDNS == p2.CorpDNS &&
|
p.CorpDNS == p2.CorpDNS &&
|
||||||
p.WantRunning == p2.WantRunning &&
|
p.WantRunning == p2.WantRunning &&
|
||||||
|
p.LoggedOut == p2.LoggedOut &&
|
||||||
p.NotepadURLs == p2.NotepadURLs &&
|
p.NotepadURLs == p2.NotepadURLs &&
|
||||||
p.ShieldsUp == p2.ShieldsUp &&
|
p.ShieldsUp == p2.ShieldsUp &&
|
||||||
p.NoSNAT == p2.NoSNAT &&
|
p.NoSNAT == p2.NoSNAT &&
|
||||||
|
@ -41,6 +41,7 @@ func (src *Prefs) Clone() *Prefs {
|
|||||||
ExitNodeAllowLANAccess bool
|
ExitNodeAllowLANAccess bool
|
||||||
CorpDNS bool
|
CorpDNS bool
|
||||||
WantRunning bool
|
WantRunning bool
|
||||||
|
LoggedOut bool
|
||||||
ShieldsUp bool
|
ShieldsUp bool
|
||||||
AdvertiseTags []string
|
AdvertiseTags []string
|
||||||
Hostname string
|
Hostname string
|
||||||
|
@ -42,6 +42,7 @@ func TestPrefsEqual(t *testing.T) {
|
|||||||
"ExitNodeAllowLANAccess",
|
"ExitNodeAllowLANAccess",
|
||||||
"CorpDNS",
|
"CorpDNS",
|
||||||
"WantRunning",
|
"WantRunning",
|
||||||
|
"LoggedOut",
|
||||||
"ShieldsUp",
|
"ShieldsUp",
|
||||||
"AdvertiseTags",
|
"AdvertiseTags",
|
||||||
"Hostname",
|
"Hostname",
|
||||||
|
Loading…
Reference in New Issue
Block a user