mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-23 09:06:24 +00:00
control/controlclient,ipn/ipnlocal: replace State enum with boolean flags
Remove the State enum (StateNew, StateNotAuthenticated, etc.) from controlclient and replace it with two explicit boolean fields: - LoginFinished: indicates successful authentication - Synced: indicates we've received at least one netmap This makes the state more composable and easier to reason about, as multiple conditions can be true independently rather than being encoded in a single enum value. The State enum was originally intended as the state machine for the whole client, but that abstraction moved to ipn.Backend long ago. This change continues moving away from the legacy state machine by representing state as a combination of independent facts. Also adds test helpers in ipnlocal that check independent, observable facts (hasValidNetMap, needsLogin, etc.) rather than relying on derived state enums, making tests more robust. Updates #12639 Signed-off-by: James Tucker <james@tailscale.com>
This commit is contained in:
committed by
James Tucker
parent
c5919b4ed1
commit
a96ef432cf
@@ -15,7 +15,6 @@ import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -49,7 +48,7 @@ func fieldsOf(t reflect.Type) (fields []string) {
|
||||
|
||||
func TestStatusEqual(t *testing.T) {
|
||||
// Verify that the Equal method stays in sync with reality
|
||||
equalHandles := []string{"Err", "URL", "NetMap", "Persist", "state"}
|
||||
equalHandles := []string{"Err", "URL", "LoggedIn", "InMapPoll", "NetMap", "Persist"}
|
||||
if have := fieldsOf(reflect.TypeFor[Status]()); !reflect.DeepEqual(have, equalHandles) {
|
||||
t.Errorf("Status.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
|
||||
have, equalHandles)
|
||||
@@ -81,7 +80,7 @@ func TestStatusEqual(t *testing.T) {
|
||||
},
|
||||
{
|
||||
&Status{},
|
||||
&Status{state: StateAuthenticated},
|
||||
&Status{LoggedIn: true, Persist: new(persist.Persist).View()},
|
||||
false,
|
||||
},
|
||||
}
|
||||
@@ -135,8 +134,20 @@ func TestCanSkipStatus(t *testing.T) {
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "s1-state-diff",
|
||||
s1: &Status{state: 123, NetMap: nm1},
|
||||
name: "s1-login-finished-diff",
|
||||
s1: &Status{LoggedIn: true, Persist: new(persist.Persist).View(), NetMap: nm1},
|
||||
s2: &Status{NetMap: nm2},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "s1-login-finished",
|
||||
s1: &Status{LoggedIn: true, Persist: new(persist.Persist).View(), NetMap: nm1},
|
||||
s2: &Status{NetMap: nm2},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "s1-synced-diff",
|
||||
s1: &Status{InMapPoll: true, LoggedIn: true, Persist: new(persist.Persist).View(), NetMap: nm1},
|
||||
s2: &Status{NetMap: nm2},
|
||||
want: false,
|
||||
},
|
||||
@@ -167,10 +178,11 @@ func TestCanSkipStatus(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
want := []string{"Err", "URL", "NetMap", "Persist", "state"}
|
||||
if f := fieldsOf(reflect.TypeFor[Status]()); !slices.Equal(f, want) {
|
||||
t.Errorf("Status fields = %q; this code was only written to handle fields %q", f, want)
|
||||
coveredFields := []string{"Err", "URL", "LoggedIn", "InMapPoll", "NetMap", "Persist"}
|
||||
if have := fieldsOf(reflect.TypeFor[Status]()); !reflect.DeepEqual(have, coveredFields) {
|
||||
t.Errorf("Status fields = %q; this code was only written to handle fields %q", have, coveredFields)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRetryableErrors(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user