mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-01 14:05:39 +00:00
8a7d35594d
Previously, there was no server round trip required to log out, so when you asked ipnlocal to Logout(), it could clear the netmap immediately and switch to NeedsLogin state. In v1.8, we added a true Logout operation. ipn.Logout() would trigger an async cc.StartLogout() and *also* immediately switch to NeedsLogin. Unfortunately, some frontends would see NeedsLogin and immediately trigger a new StartInteractiveLogin() operation, before the controlclient auth state machine actually acted on the Logout command, thus accidentally invalidating the entire logout operation, retaining the netmap, and violating the user's expectations. Instead, add a new LogoutFinished signal from controlclient (paralleling LoginFinished) and, upon starting a logout, don't update the ipn state machine until it's received. Updates: #1918 (BUG-2) Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
85 lines
1.6 KiB
Go
85 lines
1.6 KiB
Go
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package controlclient
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"tailscale.com/types/empty"
|
|
)
|
|
|
|
func fieldsOf(t reflect.Type) (fields []string) {
|
|
for i := 0; i < t.NumField(); i++ {
|
|
if name := t.Field(i).Name; name != "_" {
|
|
fields = append(fields, name)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func TestStatusEqual(t *testing.T) {
|
|
// Verify that the Equal method stays in sync with reality
|
|
equalHandles := []string{"LoginFinished", "LogoutFinished", "Err", "URL", "NetMap", "State", "Persist", "Hostinfo"}
|
|
if have := fieldsOf(reflect.TypeOf(Status{})); !reflect.DeepEqual(have, equalHandles) {
|
|
t.Errorf("Status.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
|
|
have, equalHandles)
|
|
}
|
|
|
|
tests := []struct {
|
|
a, b *Status
|
|
want bool
|
|
}{
|
|
{
|
|
&Status{},
|
|
nil,
|
|
false,
|
|
},
|
|
{
|
|
nil,
|
|
&Status{},
|
|
false,
|
|
},
|
|
{
|
|
nil,
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
&Status{},
|
|
&Status{},
|
|
true,
|
|
},
|
|
{
|
|
&Status{State: StateNew},
|
|
&Status{State: StateNew},
|
|
true,
|
|
},
|
|
{
|
|
&Status{State: StateNew},
|
|
&Status{State: StateAuthenticated},
|
|
false,
|
|
},
|
|
{
|
|
&Status{LoginFinished: nil},
|
|
&Status{LoginFinished: new(empty.Message)},
|
|
false,
|
|
},
|
|
}
|
|
for i, tt := range tests {
|
|
got := tt.a.Equal(tt.b)
|
|
if got != tt.want {
|
|
t.Errorf("%d. Equal = %v; want %v", i, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOSVersion(t *testing.T) {
|
|
if osVersion == nil {
|
|
t.Skip("not available for OS")
|
|
}
|
|
t.Logf("Got: %#q", osVersion())
|
|
}
|