ipn: remove the last traces of Prefs.AllowSingleHosts

We stopped reading this field nearly two years ago, with a TODO comment
to remove it sometime in 2025.

It is now 2026.

Updates #12058

Change-Id: I8ddf1c2e4c3c428e8d45a6491d3899368ec52c30
Signed-off-by: Alex Chan <alexc@tailscale.com>
This commit is contained in:
Alex Chan 2026-06-26 09:37:39 +01:00 committed by Alex Chan
parent e21fd6b77a
commit 8379d5955f
8 changed files with 2 additions and 67 deletions

View File

@ -1016,9 +1016,6 @@ func TestPrefFlagMapping(t *testing.T) {
continue
}
switch prefName {
case "AllowSingleHosts":
// Fake pref for downgrade compat. See #12058.
continue
case "WantRunning", "Persist", "LoggedOut":
// All explicitly handled (ignored) by checkForAccidentalSettingReverts.
continue

View File

@ -104,7 +104,6 @@ func (src *Prefs) Clone() *Prefs {
DriveShares []*drive.Share
RelayServerPort *uint16
RelayServerStaticEndpoints []netip.AddrPort
AllowSingleHosts marshalAsTrueInJSON
Persist *persist.Persist
}{})

View File

@ -453,16 +453,6 @@ func (v PrefsView) RelayServerStaticEndpoints() views.Slice[netip.AddrPort] {
return views.SliceOf(v.ж.RelayServerStaticEndpoints)
}
// AllowSingleHosts was a legacy field that was always true
// for the past 4.5 years. It controlled whether Tailscale
// peers got /32 or /128 routes for each other.
// As of 2024-05-17 we're starting to ignore it, but to let
// people still downgrade Tailscale versions and not break
// all peer-to-peer networking we still write it to disk (as JSON)
// so it can be loaded back by old versions.
// TODO(bradfitz): delete this in 2025 sometime. See #12058.
func (v PrefsView) AllowSingleHosts() marshalAsTrueInJSON { return v.ж.AllowSingleHosts }
// The Persist field is named 'Config' in the file for backward
// compatibility with earlier versions.
// TODO(apenwarr): We should move this out of here, it's not a pref.
@ -506,7 +496,6 @@ func (v PrefsView) Persist() persist.PersistView { return v.ж.Persist.View() }
DriveShares []*drive.Share
RelayServerPort *uint16
RelayServerStaticEndpoints []netip.AddrPort
AllowSingleHosts marshalAsTrueInJSON
Persist *persist.Persist
}{})

View File

@ -291,16 +291,6 @@ type Prefs struct {
// non-nil.
RelayServerStaticEndpoints []netip.AddrPort `json:",omitempty"`
// AllowSingleHosts was a legacy field that was always true
// for the past 4.5 years. It controlled whether Tailscale
// peers got /32 or /128 routes for each other.
// As of 2024-05-17 we're starting to ignore it, but to let
// people still downgrade Tailscale versions and not break
// all peer-to-peer networking we still write it to disk (as JSON)
// so it can be loaded back by old versions.
// TODO(bradfitz): delete this in 2025 sometime. See #12058.
AllowSingleHosts marshalAsTrueInJSON
// The Persist field is named 'Config' in the file for backward
// compatibility with earlier versions.
// TODO(apenwarr): We should move this out of here, it's not a pref.
@ -331,13 +321,6 @@ func (au1 AutoUpdatePrefs) Equals(au2 AutoUpdatePrefs) bool {
ok1 == ok2
}
type marshalAsTrueInJSON struct{}
var trueJSON = []byte("true")
func (marshalAsTrueInJSON) MarshalJSON() ([]byte, error) { return trueJSON, nil }
func (*marshalAsTrueInJSON) UnmarshalJSON([]byte) error { return nil }
// AppConnectorPrefs are the app connector settings for the node agent.
type AppConnectorPrefs struct {
// Advertise specifies whether the app connector subsystem is advertising

View File

@ -70,7 +70,6 @@ func TestPrefsEqual(t *testing.T) {
"DriveShares",
"RelayServerPort",
"RelayServerStaticEndpoints",
"AllowSingleHosts",
"Persist",
}
if have := fieldsOf(reflect.TypeFor[Prefs]()); !reflect.DeepEqual(have, prefsHandles) {
@ -714,7 +713,7 @@ func TestMaskedPrefsFields(t *testing.T) {
have := map[string]bool{}
for _, f := range fieldsOf(reflect.TypeFor[Prefs]()) {
switch f {
case "Persist", "AllowSingleHosts":
case "Persist":
// These can't be edited.
continue
}
@ -1222,27 +1221,6 @@ func TestNotifyPrefsJSONRoundtrip(t *testing.T) {
}
}
// Verify that our Prefs type writes out an AllowSingleHosts field so we can
// downgrade to older versions that require it.
func TestPrefsDowngrade(t *testing.T) {
var p Prefs
j, err := json.Marshal(p)
if err != nil {
t.Fatal(err)
}
type oldPrefs struct {
AllowSingleHosts bool
}
var op oldPrefs
if err := json.Unmarshal(j, &op); err != nil {
t.Fatal(err)
}
if !op.AllowSingleHosts {
t.Fatal("AllowSingleHosts should be true")
}
}
func TestParseAutoExitNodeString(t *testing.T) {
tests := []struct {
name string

View File

@ -61,7 +61,6 @@ func (src *Prefs) Clone() *Prefs {
PostureChecking prefs.Item[bool]
NetfilterKind prefs.Item[string]
DriveShares prefs.StructList[*drive.Share]
AllowSingleHosts prefs.Item[marshalAsTrueInJSON]
Persist *persist.Persist
}{})

View File

@ -145,7 +145,6 @@ func (v PrefsView) NetfilterKind() prefs.Item[string] { return v.ж.NetfilterKin
func (v PrefsView) DriveShares() prefs.StructListView[*drive.Share, drive.ShareView] {
return prefs.StructListViewOf(&v.ж.DriveShares)
}
func (v PrefsView) AllowSingleHosts() prefs.Item[marshalAsTrueInJSON] { return v.ж.AllowSingleHosts }
// Persist is an internal state rather than a preference.
// It can be kept in the Prefs structure but should not be wrapped
@ -182,7 +181,6 @@ func (v PrefsView) Persist() persist.PersistView { return v.ж.Persist.View() }
PostureChecking prefs.Item[bool]
NetfilterKind prefs.Item[string]
DriveShares prefs.StructList[*drive.Share]
AllowSingleHosts prefs.Item[marshalAsTrueInJSON]
Persist *persist.Persist
}{})

View File

@ -106,8 +106,7 @@ type Prefs struct {
// not be modified after the preference is set.
// Since the item type (*drive.Share) is mutable and implements [views.ViewCloner],
// we need to use [prefs.StructList] instead of [prefs.List].
DriveShares prefs.StructList[*drive.Share] `json:",omitzero"`
AllowSingleHosts prefs.Item[marshalAsTrueInJSON] `json:",omitzero"`
DriveShares prefs.StructList[*drive.Share] `json:",omitzero"`
// Persist is an internal state rather than a preference.
// It can be kept in the Prefs structure but should not be wrapped
@ -162,10 +161,3 @@ func (p Prefs) MarshalJSON() ([]byte, error) {
func (p *Prefs) UnmarshalJSON(b []byte) error {
return jsonv2.Unmarshal(b, p) // uses UnmarshalJSONFrom
}
type marshalAsTrueInJSON struct{}
var trueJSON = []byte("true")
func (marshalAsTrueInJSON) MarshalJSON() ([]byte, error) { return trueJSON, nil }
func (*marshalAsTrueInJSON) UnmarshalJSON([]byte) error { return nil }