ipn/ipnlocal: use policyclient.Client always, stop using global syspolicy funcs

Step 4 of N. See earlier commits in the series (via the issue) for the
plan.

This adds the missing methods to policyclient.Client and then uses it
everywhere in ipn/ipnlocal and locks it in with a new dep test.

Still plenty of users of the global syspolicy elsewhere in the tree,
but this is a lot of them.

Updates #16998
Updates #12614

Change-Id: I25b136539ae1eedbcba80124de842970db0ca314
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2025-09-01 15:05:06 -07:00
committed by Brad Fitzpatrick
parent 2434bc69fc
commit 1ca4ae598a
6 changed files with 168 additions and 47 deletions
+44 -6
View File
@@ -47,6 +47,7 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/tsd"
"tailscale.com/tstest"
"tailscale.com/tstest/deptest"
"tailscale.com/types/dnstype"
"tailscale.com/types/ipproto"
"tailscale.com/types/key"
@@ -63,6 +64,7 @@ import (
"tailscale.com/util/set"
"tailscale.com/util/syspolicy"
"tailscale.com/util/syspolicy/pkey"
"tailscale.com/util/syspolicy/policyclient"
"tailscale.com/util/syspolicy/setting"
"tailscale.com/util/syspolicy/source"
"tailscale.com/wgengine"
@@ -5541,6 +5543,28 @@ func TestReadWriteRouteInfo(t *testing.T) {
}
}
// staticPolicy maps policy keys to their corresponding values,
// which must be of the correct type (string, []string, bool, etc).
//
// It is used for testing purposes to simulate policy client behavior.
// It panics if the values are the wrong type.
type staticPolicy map[pkey.Key]any
type testPolicy struct {
staticPolicy
policyclient.Client
}
func (sp testPolicy) GetStringArray(key pkey.Key, defaultVal []string) ([]string, error) {
if val, ok := sp.staticPolicy[key]; ok {
if arr, ok := val.([]string); ok {
return arr, nil
}
return nil, fmt.Errorf("key %s is not a []string", key)
}
return defaultVal, nil
}
func TestFillAllowedSuggestions(t *testing.T) {
tests := []struct {
name string
@@ -5571,15 +5595,16 @@ func TestFillAllowedSuggestions(t *testing.T) {
want: []tailcfg.StableNodeID{"ABC", "def", "gHiJ"},
},
}
syspolicy.RegisterWellKnownSettingsForTest(t)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
policyStore := source.NewTestStoreOf(t, source.TestSettingOf(
pkey.AllowedSuggestedExitNodes, tt.allowPolicy,
))
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
polc := testPolicy{
staticPolicy: staticPolicy{
pkey.AllowedSuggestedExitNodes: tt.allowPolicy,
},
}
got := fillAllowedSuggestions()
got := fillAllowedSuggestions(polc)
if got == nil {
if tt.want == nil {
return
@@ -7008,6 +7033,19 @@ func TestDisplayMessageIPNBus(t *testing.T) {
}
}
func TestDeps(t *testing.T) {
deptest.DepChecker{
OnImport: func(pkg string) {
switch pkg {
case "tailscale.com/util/syspolicy",
"tailscale.com/util/syspolicy/setting",
"tailscale.com/util/syspolicy/rsop":
t.Errorf("ipn/ipnlocal: importing syspolicy package %q is not allowed; only policyclient and its deps should be used by ipn/ipnlocal", pkg)
}
},
}.Check(t)
}
func checkError(tb testing.TB, got, want error, fatal bool) {
tb.Helper()
f := tb.Errorf