mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-16 03:31:39 +00:00
syspolicy: add exit node related policies (#10172)
Adds policy keys ExitNodeID and ExitNodeIP. Uses the policy keys to determine the exit node in preferences. Fixes tailscale/corp#15683 Signed-off-by: Claire Wang <claire@tailscale.com>
This commit is contained in:
parent
ecd1ccb917
commit
8af503b0c5
@ -530,7 +530,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
regexp/syntax from regexp
|
regexp/syntax from regexp
|
||||||
runtime/debug from github.com/klauspost/compress/zstd+
|
runtime/debug from github.com/klauspost/compress/zstd+
|
||||||
runtime/pprof from tailscale.com/ipn/ipnlocal+
|
runtime/pprof from tailscale.com/ipn/ipnlocal+
|
||||||
runtime/trace from net/http/pprof
|
runtime/trace from net/http/pprof+
|
||||||
slices from tailscale.com/wgengine/magicsock+
|
slices from tailscale.com/wgengine/magicsock+
|
||||||
sort from compress/flate+
|
sort from compress/flate+
|
||||||
strconv from compress/flate+
|
strconv from compress/flate+
|
||||||
@ -538,6 +538,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
sync from compress/flate+
|
sync from compress/flate+
|
||||||
sync/atomic from context+
|
sync/atomic from context+
|
||||||
syscall from crypto/rand+
|
syscall from crypto/rand+
|
||||||
|
testing from tailscale.com/util/syspolicy
|
||||||
text/tabwriter from runtime/pprof
|
text/tabwriter from runtime/pprof
|
||||||
text/template from html/template
|
text/template from html/template
|
||||||
text/template/parse from html/template+
|
text/template/parse from html/template+
|
||||||
|
@ -88,6 +88,7 @@ import (
|
|||||||
"tailscale.com/util/osshare"
|
"tailscale.com/util/osshare"
|
||||||
"tailscale.com/util/rands"
|
"tailscale.com/util/rands"
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
|
"tailscale.com/util/syspolicy"
|
||||||
"tailscale.com/util/systemd"
|
"tailscale.com/util/systemd"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
"tailscale.com/util/uniq"
|
"tailscale.com/util/uniq"
|
||||||
@ -1311,6 +1312,24 @@ func (b *LocalBackend) updateNetmapDeltaLocked(muts []netmap.NodeMutation) (hand
|
|||||||
// setExitNodeID updates prefs to reference an exit node by ID, rather
|
// setExitNodeID updates prefs to reference an exit node by ID, rather
|
||||||
// than by IP. It returns whether prefs was mutated.
|
// than by IP. It returns whether prefs was mutated.
|
||||||
func setExitNodeID(prefs *ipn.Prefs, nm *netmap.NetworkMap) (prefsChanged bool) {
|
func setExitNodeID(prefs *ipn.Prefs, nm *netmap.NetworkMap) (prefsChanged bool) {
|
||||||
|
if exitNodeIDStr, _ := syspolicy.GetString(syspolicy.ExitNodeID, ""); exitNodeIDStr != "" {
|
||||||
|
exitNodeID := tailcfg.StableNodeID(exitNodeIDStr)
|
||||||
|
changed := prefs.ExitNodeID != exitNodeID || prefs.ExitNodeIP.IsValid()
|
||||||
|
prefs.ExitNodeID = exitNodeID
|
||||||
|
prefs.ExitNodeIP = netip.Addr{}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
oldExitNodeID := prefs.ExitNodeID
|
||||||
|
if exitNodeIPStr, _ := syspolicy.GetString(syspolicy.ExitNodeIP, ""); exitNodeIPStr != "" {
|
||||||
|
exitNodeIP, err := netip.ParseAddr(exitNodeIPStr)
|
||||||
|
if exitNodeIP.IsValid() && err == nil {
|
||||||
|
prefsChanged = prefs.ExitNodeID != "" || prefs.ExitNodeIP != exitNodeIP
|
||||||
|
prefs.ExitNodeID = ""
|
||||||
|
prefs.ExitNodeIP = exitNodeIP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if nm == nil {
|
if nm == nil {
|
||||||
// No netmap, can't resolve anything.
|
// No netmap, can't resolve anything.
|
||||||
return false
|
return false
|
||||||
@ -1338,7 +1357,7 @@ func setExitNodeID(prefs *ipn.Prefs, nm *netmap.NetworkMap) (prefsChanged bool)
|
|||||||
// reference it directly for next time.
|
// reference it directly for next time.
|
||||||
prefs.ExitNodeID = peer.StableID()
|
prefs.ExitNodeID = peer.StableID()
|
||||||
prefs.ExitNodeIP = netip.Addr{}
|
prefs.ExitNodeIP = netip.Addr{}
|
||||||
return true
|
return oldExitNodeID != prefs.ExitNodeID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ import (
|
|||||||
"tailscale.com/util/mak"
|
"tailscale.com/util/mak"
|
||||||
"tailscale.com/util/must"
|
"tailscale.com/util/must"
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
|
"tailscale.com/util/syspolicy"
|
||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
"tailscale.com/wgengine/filter"
|
"tailscale.com/wgengine/filter"
|
||||||
"tailscale.com/wgengine/wgcfg"
|
"tailscale.com/wgengine/wgcfg"
|
||||||
@ -1330,3 +1331,272 @@ func (rc *routeCollector) AdvertiseRoute(pfx netip.Prefix) error {
|
|||||||
rc.routes = append(rc.routes, pfx)
|
rc.routes = append(rc.routes, pfx)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockSyspolicyHandler struct {
|
||||||
|
t *testing.T
|
||||||
|
key syspolicy.Key
|
||||||
|
s string
|
||||||
|
exitNodeIDKey bool
|
||||||
|
exitNodeIPKey bool
|
||||||
|
exitNodeID string
|
||||||
|
exitNodeIP string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *mockSyspolicyHandler) ReadString(key string) (string, error) {
|
||||||
|
if h.exitNodeIDKey && key == string(syspolicy.ExitNodeID) {
|
||||||
|
return h.exitNodeID, nil
|
||||||
|
}
|
||||||
|
if h.exitNodeIPKey && key == string(syspolicy.ExitNodeIP) {
|
||||||
|
return h.exitNodeIP, nil
|
||||||
|
}
|
||||||
|
return "", syspolicy.ErrNoSuchKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *mockSyspolicyHandler) ReadUInt64(key string) (uint64, error) {
|
||||||
|
h.t.Errorf("ReadUInt64(%q) unexpectedly called", key)
|
||||||
|
return 0, syspolicy.ErrNoSuchKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *mockSyspolicyHandler) ReadBoolean(key string) (bool, error) {
|
||||||
|
h.t.Errorf("ReadBoolean(%q) unexpectedly called", key)
|
||||||
|
return false, syspolicy.ErrNoSuchKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetExitNodeIDPolicy(t *testing.T) {
|
||||||
|
pfx := netip.MustParsePrefix
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
exitNodeIPKey bool
|
||||||
|
exitNodeIDKey bool
|
||||||
|
exitNodeID string
|
||||||
|
exitNodeIP string
|
||||||
|
prefs *ipn.Prefs
|
||||||
|
exitNodeIPWant string
|
||||||
|
exitNodeIDWant string
|
||||||
|
prefsChanged bool
|
||||||
|
nm *netmap.NetworkMap
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ExitNodeID key is set",
|
||||||
|
exitNodeIDKey: true,
|
||||||
|
exitNodeID: "123",
|
||||||
|
exitNodeIDWant: "123",
|
||||||
|
prefsChanged: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ExitNodeID key not set",
|
||||||
|
exitNodeIDKey: true,
|
||||||
|
exitNodeIDWant: "",
|
||||||
|
prefsChanged: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ExitNodeID key set, ExitNodeIP preference set",
|
||||||
|
exitNodeIDKey: true,
|
||||||
|
exitNodeID: "123",
|
||||||
|
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
|
||||||
|
exitNodeIDWant: "123",
|
||||||
|
prefsChanged: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ExitNodeID key not set, ExitNodeIP key set",
|
||||||
|
exitNodeIPKey: true,
|
||||||
|
exitNodeIP: "127.0.0.1",
|
||||||
|
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
|
||||||
|
exitNodeIPWant: "127.0.0.1",
|
||||||
|
prefsChanged: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ExitNodeIP key set, existing ExitNodeIP pref",
|
||||||
|
exitNodeIPKey: true,
|
||||||
|
exitNodeIP: "127.0.0.1",
|
||||||
|
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
|
||||||
|
exitNodeIPWant: "127.0.0.1",
|
||||||
|
prefsChanged: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "existing preferences match policy",
|
||||||
|
exitNodeIDKey: true,
|
||||||
|
exitNodeID: "123",
|
||||||
|
prefs: &ipn.Prefs{ExitNodeID: tailcfg.StableNodeID("123")},
|
||||||
|
exitNodeIDWant: "123",
|
||||||
|
prefsChanged: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ExitNodeIP set if net map does not have corresponding node",
|
||||||
|
exitNodeIPKey: true,
|
||||||
|
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
|
||||||
|
exitNodeIP: "127.0.0.1",
|
||||||
|
exitNodeIPWant: "127.0.0.1",
|
||||||
|
prefsChanged: false,
|
||||||
|
nm: &netmap.NetworkMap{
|
||||||
|
Name: "foo.tailnet",
|
||||||
|
SelfNode: (&tailcfg.Node{
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("100.102.103.104/32"),
|
||||||
|
pfx("100::123/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
Peers: []tailcfg.NodeView{
|
||||||
|
(&tailcfg.Node{
|
||||||
|
Name: "a.tailnet",
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("100.0.0.201/32"),
|
||||||
|
pfx("100::201/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
(&tailcfg.Node{
|
||||||
|
Name: "b.tailnet",
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("100::202/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ExitNodeIP cleared if net map has corresponding node - policy matches prefs",
|
||||||
|
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
|
||||||
|
exitNodeIPKey: true,
|
||||||
|
exitNodeIP: "127.0.0.1",
|
||||||
|
exitNodeIPWant: "",
|
||||||
|
exitNodeIDWant: "123",
|
||||||
|
prefsChanged: true,
|
||||||
|
nm: &netmap.NetworkMap{
|
||||||
|
Name: "foo.tailnet",
|
||||||
|
SelfNode: (&tailcfg.Node{
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("100.102.103.104/32"),
|
||||||
|
pfx("100::123/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
Peers: []tailcfg.NodeView{
|
||||||
|
(&tailcfg.Node{
|
||||||
|
Name: "a.tailnet",
|
||||||
|
StableID: tailcfg.StableNodeID("123"),
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("127.0.0.1/32"),
|
||||||
|
pfx("100::201/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
(&tailcfg.Node{
|
||||||
|
Name: "b.tailnet",
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("100::202/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ExitNodeIP cleared if net map has corresponding node - no policy set",
|
||||||
|
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
|
||||||
|
exitNodeIPWant: "",
|
||||||
|
exitNodeIDWant: "123",
|
||||||
|
prefsChanged: true,
|
||||||
|
nm: &netmap.NetworkMap{
|
||||||
|
Name: "foo.tailnet",
|
||||||
|
SelfNode: (&tailcfg.Node{
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("100.102.103.104/32"),
|
||||||
|
pfx("100::123/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
Peers: []tailcfg.NodeView{
|
||||||
|
(&tailcfg.Node{
|
||||||
|
Name: "a.tailnet",
|
||||||
|
StableID: tailcfg.StableNodeID("123"),
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("127.0.0.1/32"),
|
||||||
|
pfx("100::201/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
(&tailcfg.Node{
|
||||||
|
Name: "b.tailnet",
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("100::202/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ExitNodeIP cleared if net map has corresponding node - different exit node IP in policy",
|
||||||
|
exitNodeIPKey: true,
|
||||||
|
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
|
||||||
|
exitNodeIP: "100.64.5.6",
|
||||||
|
exitNodeIPWant: "",
|
||||||
|
exitNodeIDWant: "123",
|
||||||
|
prefsChanged: true,
|
||||||
|
nm: &netmap.NetworkMap{
|
||||||
|
Name: "foo.tailnet",
|
||||||
|
SelfNode: (&tailcfg.Node{
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("100.102.103.104/32"),
|
||||||
|
pfx("100::123/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
Peers: []tailcfg.NodeView{
|
||||||
|
(&tailcfg.Node{
|
||||||
|
Name: "a.tailnet",
|
||||||
|
StableID: tailcfg.StableNodeID("123"),
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("100.64.5.6/32"),
|
||||||
|
pfx("100::201/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
(&tailcfg.Node{
|
||||||
|
Name: "b.tailnet",
|
||||||
|
Addresses: []netip.Prefix{
|
||||||
|
pfx("100::202/128"),
|
||||||
|
},
|
||||||
|
}).View(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
b := newTestBackend(t)
|
||||||
|
syspolicy.SetHandlerForTest(t, &mockSyspolicyHandler{
|
||||||
|
t: t,
|
||||||
|
exitNodeID: test.exitNodeID,
|
||||||
|
exitNodeIP: test.exitNodeIP,
|
||||||
|
exitNodeIDKey: test.exitNodeIDKey,
|
||||||
|
exitNodeIPKey: test.exitNodeIPKey,
|
||||||
|
})
|
||||||
|
if test.nm == nil {
|
||||||
|
test.nm = new(netmap.NetworkMap)
|
||||||
|
}
|
||||||
|
if test.prefs == nil {
|
||||||
|
test.prefs = ipn.NewPrefs()
|
||||||
|
}
|
||||||
|
pm := must.Get(newProfileManager(new(mem.Store), t.Logf))
|
||||||
|
pm.prefs = test.prefs.View()
|
||||||
|
b.netMap = test.nm
|
||||||
|
b.pm = pm
|
||||||
|
changed := setExitNodeID(b.pm.prefs.AsStruct(), test.nm)
|
||||||
|
b.SetPrefs(pm.CurrentPrefs().AsStruct())
|
||||||
|
if test.exitNodeIDKey {
|
||||||
|
got := b.pm.prefs.ExitNodeID()
|
||||||
|
if got != tailcfg.StableNodeID(test.exitNodeIDWant) {
|
||||||
|
t.Errorf("got %v want %v", got, test.exitNodeIDWant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if test.exitNodeIPKey {
|
||||||
|
got := b.pm.prefs.ExitNodeIP()
|
||||||
|
if test.exitNodeIPWant == "" {
|
||||||
|
if got.String() != "invalid IP" {
|
||||||
|
t.Errorf("got %v want invalid IP", got)
|
||||||
|
}
|
||||||
|
} else if got.String() != test.exitNodeIPWant {
|
||||||
|
t.Errorf("got %v want %v", got, test.exitNodeIPWant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed != test.prefsChanged {
|
||||||
|
t.Errorf("wanted prefs changed %v, got prefs changed %v", test.prefsChanged, changed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@ package syspolicy
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -56,3 +57,10 @@ func RegisterHandler(h Handler) {
|
|||||||
panic("handler was already used before registration")
|
panic("handler was already used before registration")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetHandlerForTest(tb testing.TB, h Handler) {
|
||||||
|
tb.Helper()
|
||||||
|
oldHandler := handler
|
||||||
|
handler = h
|
||||||
|
tb.Cleanup(func() { handler = oldHandler })
|
||||||
|
}
|
||||||
|
@ -10,6 +10,11 @@ const (
|
|||||||
ControlURL Key = "LoginURL" // default ""; if blank, ipn uses ipn.DefaultControlURL.
|
ControlURL Key = "LoginURL" // default ""; if blank, ipn uses ipn.DefaultControlURL.
|
||||||
LogTarget Key = "LogTarget" // default ""; if blank logging uses logtail.DefaultHost.
|
LogTarget Key = "LogTarget" // default ""; if blank logging uses logtail.DefaultHost.
|
||||||
Tailnet Key = "Tailnet" // default ""; if blank, no tailnet name is sent to the server.
|
Tailnet Key = "Tailnet" // default ""; if blank, no tailnet name is sent to the server.
|
||||||
|
// ExitNodeID is the exit node's node id. default ""; if blank, no exit node is forced.
|
||||||
|
// Exit node ID takes precedence over exit node IP.
|
||||||
|
// To find the node ID, go to /api.md#device.
|
||||||
|
ExitNodeID Key = "ExitNodeID"
|
||||||
|
ExitNodeIP Key = "ExitNodeIP" // default ""; if blank, no exit node is forced. Value is exit node IP.
|
||||||
|
|
||||||
// Keys with a string value that specifies an option: "always", "never", "user-decides".
|
// Keys with a string value that specifies an option: "always", "never", "user-decides".
|
||||||
// The default is "user-decides" unless otherwise stated.
|
// The default is "user-decides" unless otherwise stated.
|
||||||
|
@ -24,13 +24,6 @@ type testHandler struct {
|
|||||||
|
|
||||||
var someOtherError = errors.New("error other than not found")
|
var someOtherError = errors.New("error other than not found")
|
||||||
|
|
||||||
func setHandlerForTest(tb testing.TB, h Handler) {
|
|
||||||
tb.Helper()
|
|
||||||
oldHandler := handler
|
|
||||||
handler = h
|
|
||||||
tb.Cleanup(func() { handler = oldHandler })
|
|
||||||
}
|
|
||||||
|
|
||||||
func (th *testHandler) ReadString(key string) (string, error) {
|
func (th *testHandler) ReadString(key string) (string, error) {
|
||||||
if key != string(th.key) {
|
if key != string(th.key) {
|
||||||
th.t.Errorf("ReadString(%q) want %q", key, th.key)
|
th.t.Errorf("ReadString(%q) want %q", key, th.key)
|
||||||
@ -95,7 +88,7 @@ func TestGetString(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
setHandlerForTest(t, &testHandler{
|
SetHandlerForTest(t, &testHandler{
|
||||||
t: t,
|
t: t,
|
||||||
key: tt.key,
|
key: tt.key,
|
||||||
s: tt.handlerValue,
|
s: tt.handlerValue,
|
||||||
@ -152,7 +145,7 @@ func TestGetUint64(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
setHandlerForTest(t, &testHandler{
|
SetHandlerForTest(t, &testHandler{
|
||||||
t: t,
|
t: t,
|
||||||
key: tt.key,
|
key: tt.key,
|
||||||
u64: tt.handlerValue,
|
u64: tt.handlerValue,
|
||||||
@ -204,7 +197,7 @@ func TestGetBoolean(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
setHandlerForTest(t, &testHandler{
|
SetHandlerForTest(t, &testHandler{
|
||||||
t: t,
|
t: t,
|
||||||
key: tt.key,
|
key: tt.key,
|
||||||
b: tt.handlerValue,
|
b: tt.handlerValue,
|
||||||
@ -265,7 +258,7 @@ func TestGetPreferenceOption(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
setHandlerForTest(t, &testHandler{
|
SetHandlerForTest(t, &testHandler{
|
||||||
t: t,
|
t: t,
|
||||||
key: tt.key,
|
key: tt.key,
|
||||||
s: tt.handlerValue,
|
s: tt.handlerValue,
|
||||||
@ -322,7 +315,7 @@ func TestGetVisibility(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
setHandlerForTest(t, &testHandler{
|
SetHandlerForTest(t, &testHandler{
|
||||||
t: t,
|
t: t,
|
||||||
key: tt.key,
|
key: tt.key,
|
||||||
s: tt.handlerValue,
|
s: tt.handlerValue,
|
||||||
@ -389,7 +382,7 @@ func TestGetDuration(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
setHandlerForTest(t, &testHandler{
|
SetHandlerForTest(t, &testHandler{
|
||||||
t: t,
|
t: t,
|
||||||
key: tt.key,
|
key: tt.key,
|
||||||
s: tt.handlerValue,
|
s: tt.handlerValue,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user