ipn/ipnlocal: some state_test cleanups.

This doesn't change the actual functionality. Just some additional
comments and fine tuning.

Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
This commit is contained in:
Avery Pennarun 2021-04-30 03:47:21 -04:00
parent ac9cd48c80
commit b0382ca167

View File

@ -18,6 +18,8 @@
"tailscale.com/types/empty" "tailscale.com/types/empty"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/netmap" "tailscale.com/types/netmap"
"tailscale.com/types/persist"
"tailscale.com/types/wgkey"
"tailscale.com/wgengine" "tailscale.com/wgengine"
) )
@ -89,6 +91,8 @@ type mockControl struct {
mu sync.Mutex mu sync.Mutex
calls []string calls []string
authBlocked bool authBlocked bool
persist persist.Persist
machineKey wgkey.Private
} }
func newMockControl() *mockControl { func newMockControl() *mockControl {
@ -102,13 +106,30 @@ func (cc *mockControl) SetStatusFunc(fn func(controlclient.Status)) {
cc.statusFunc = fn cc.statusFunc = fn
} }
func (cc *mockControl) populateKeys() {
cc.mu.Lock()
defer cc.mu.Unlock()
if cc.machineKey.IsZero() {
cc.logf("Copying machineKey.")
cc.machineKey, _ = cc.opts.GetMachinePrivateKey()
}
if cc.persist.PrivateNodeKey.IsZero() {
cc.logf("Generating a new nodekey.")
cc.persist.OldPrivateNodeKey = cc.persist.PrivateNodeKey
cc.persist.PrivateNodeKey, _ = wgkey.NewPrivate()
}
}
// send publishes a controlclient.Status notification upstream. // send publishes a controlclient.Status notification upstream.
// (In our tests here, upstream is the ipnlocal.Local instance.) // (In our tests here, upstream is the ipnlocal.Local instance.)
func (cc *mockControl) send(err error, url string, loginFinished bool, nm *netmap.NetworkMap) { func (cc *mockControl) send(err error, url string, loginFinished bool, nm *netmap.NetworkMap) {
if cc.statusFunc != nil { if cc.statusFunc != nil {
s := controlclient.Status{ s := controlclient.Status{
URL: url, URL: url,
NetMap: nm, NetMap: nm,
Persist: &cc.persist,
} }
if err != nil { if err != nil {
s.Err = err.Error() s.Err = err.Error()
@ -170,6 +191,7 @@ func (cc *mockControl) Shutdown() {
func (cc *mockControl) Login(t *tailcfg.Oauth2Token, flags controlclient.LoginFlags) { func (cc *mockControl) Login(t *tailcfg.Oauth2Token, flags controlclient.LoginFlags) {
cc.logf("Login token=%v flags=%v", t, flags) cc.logf("Login token=%v flags=%v", t, flags)
cc.called("Login") cc.called("Login")
cc.populateKeys()
} }
func (cc *mockControl) StartLogout() { func (cc *mockControl) StartLogout() {
@ -285,6 +307,7 @@ func TestStateMachine(t *testing.T) {
// Start the state machine. // Start the state machine.
// Since !WantRunning by default, it'll create a controlclient, // Since !WantRunning by default, it'll create a controlclient,
// but not ask it to do anything yet. // but not ask it to do anything yet.
t.Logf("\n\nStart")
notifies.expect(2) notifies.expect(2)
assert.Nil(b.Start(ipn.Options{ assert.Nil(b.Start(ipn.Options{
StateKey: ipn.GlobalDaemonStateKey, StateKey: ipn.GlobalDaemonStateKey,
@ -299,7 +322,6 @@ func TestStateMachine(t *testing.T) {
assert.NotNil(nn[1].State) assert.NotNil(nn[1].State)
prefs := *nn[0].Prefs prefs := *nn[0].Prefs
assert.Equal(false, prefs.WantRunning) assert.Equal(false, prefs.WantRunning)
// BUG: there should be a NoState first, to tell the GUI to show "Loading..."
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())
} }
@ -321,9 +343,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)
prefs := *nn[0].Prefs assert.Equal(false, nn[0].Prefs.WantRunning)
assert.Equal(false, prefs.WantRunning)
// BUG: there should be a NoState first, to tell the GUI to show "Loading..."
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())
} }
@ -361,7 +381,7 @@ func TestStateMachine(t *testing.T) {
nn := notifies.drain(1) nn := notifies.drain(1)
// Trying to log in automatically sets WantRunning. // Trying to log in automatically sets WantRunning.
// BUG: this 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(true, nn[0].Prefs.WantRunning) assert.Equal(true, nn[0].Prefs.WantRunning)
} }
@ -510,6 +530,8 @@ func TestStateMachine(t *testing.T) {
// Test the fast-path frontend reconnection. // Test the fast-path frontend reconnection.
// This one is very finicky, so we have to force State==Running. // This one is very finicky, so we have to force State==Running.
// TODO: actually get to State==Running, rather than cheating. // TODO: actually get to State==Running, rather than cheating.
// That'll require spinning up a fake DERP server and putting it in
// the netmap.
t.Logf("\n\nFastpath Start()") t.Logf("\n\nFastpath Start()")
notifies.expect(1) notifies.expect(1)
b.state = ipn.Running b.state = ipn.Running
@ -546,6 +568,7 @@ func TestStateMachine(t *testing.T) {
} }
// Let's make the logout succeed. // Let's make the logout succeed.
t.Logf("\n\nLogout (async) - succeed")
notifies.expect(1) notifies.expect(1)
cc.setAuthBlocked(true) cc.setAuthBlocked(true)
cc.send(nil, "", false, nil) cc.send(nil, "", false, nil)
@ -566,9 +589,11 @@ func TestStateMachine(t *testing.T) {
// 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())
assert.Equal(ipn.NeedsLogin, b.State())
} }
// Let's acknowledge the second logout too. // Let's acknowledge the second logout too.
t.Logf("\n\nLogout2 (async) - succeed")
notifies.expect(1) notifies.expect(1)
cc.setAuthBlocked(true) cc.setAuthBlocked(true)
cc.send(nil, "", false, nil) cc.send(nil, "", false, nil)
@ -578,6 +603,7 @@ func TestStateMachine(t *testing.T) {
assert.NotNil(nn[0].Prefs) assert.NotNil(nn[0].Prefs)
// 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())
} }
// Try the synchronous logout feature. // Try the synchronous logout feature.
@ -591,9 +617,11 @@ func TestStateMachine(t *testing.T) {
assert.Equal([]string{"Logout"}, cc.getCalls()) assert.Equal([]string{"Logout"}, cc.getCalls())
assert.NotNil(nn[0].Prefs) assert.NotNil(nn[0].Prefs)
assert.Equal(false, nn[0].Prefs.WantRunning) assert.Equal(false, nn[0].Prefs.WantRunning)
assert.Equal(ipn.NeedsLogin, b.State())
} }
// Generate the third logout event. // Generate the third logout event.
t.Logf("\n\nLogout3 (sync) - succeed")
notifies.expect(1) notifies.expect(1)
cc.setAuthBlocked(true) cc.setAuthBlocked(true)
cc.send(nil, "", false, nil) cc.send(nil, "", false, nil)
@ -603,6 +631,7 @@ func TestStateMachine(t *testing.T) {
assert.NotNil(nn[0].Prefs) assert.NotNil(nn[0].Prefs)
// 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())
} }
// Shut down the backend. // Shut down the backend.
@ -620,7 +649,7 @@ func TestStateMachine(t *testing.T) {
// Note that it's explicitly okay to call b.Start() over and over // Note that it's explicitly okay to call b.Start() over and over
// again, every time the frontend reconnects. // again, every time the frontend reconnects.
// //
// BUG: WantRunning should be true here (because of the bug above). // BUG: WantRunning is true here (because of the bug above).
// We'll have to adjust the following test's expectations if we // We'll have to adjust the following test's expectations if we
// fix that. // fix that.
@ -645,9 +674,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)
prefs := *nn[0].Prefs assert.Equal(true, nn[0].Prefs.WantRunning)
assert.Equal(true, prefs.WantRunning)
// BUG: there should be a NoState first, to tell the GUI to show "Loading..."
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())
} }
@ -703,7 +730,6 @@ func TestStateMachine(t *testing.T) {
assert.NotNil(nn[1].State) assert.NotNil(nn[1].State)
prefs := *nn[0].Prefs prefs := *nn[0].Prefs
assert.Equal(false, prefs.WantRunning) assert.Equal(false, prefs.WantRunning)
// BUG: there should be a NoState first, to tell the GUI to show "Loading..."
// BUG: NeedsLogin is incorrect. We are already logged in. // BUG: NeedsLogin is incorrect. We are already logged in.
// This is the cause of bug tailscale/corp#1660. // This is the cause of bug tailscale/corp#1660.
// (Whenever we enter the NeedsLogin state, the UI will // (Whenever we enter the NeedsLogin state, the UI will