ipn: move ParseAutoExitNodeID from ipn/ipnlocal to ipn
So it can be used from the CLI without importing ipnlocal. While there, also remove isAutoExitNodeID, a wrapper around parseAutoExitNodeID that's no longer used. Updates tailscale/corp#29969 Updates #cleanup Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
+3
-30
@@ -1890,7 +1890,7 @@ func (b *LocalBackend) applyExitNodeSysPolicyLocked(prefs *ipn.Prefs) (anyChange
|
|||||||
// and update prefs if it differs from the current one.
|
// and update prefs if it differs from the current one.
|
||||||
// This includes cases where it was previously an expression but no longer is,
|
// This includes cases where it was previously an expression but no longer is,
|
||||||
// or where it wasn't before but now is.
|
// or where it wasn't before but now is.
|
||||||
autoExitNode, useAutoExitNode := parseAutoExitNodeID(exitNodeID)
|
autoExitNode, useAutoExitNode := ipn.ParseAutoExitNodeString(exitNodeID)
|
||||||
if prefs.AutoExitNode != autoExitNode {
|
if prefs.AutoExitNode != autoExitNode {
|
||||||
prefs.AutoExitNode = autoExitNode
|
prefs.AutoExitNode = autoExitNode
|
||||||
anyChange = true
|
anyChange = true
|
||||||
@@ -4292,7 +4292,7 @@ func (b *LocalBackend) SetUseExitNodeEnabled(actor ipnauth.Actor, v bool) (ipn.P
|
|||||||
if v {
|
if v {
|
||||||
mp.ExitNodeIDSet = true
|
mp.ExitNodeIDSet = true
|
||||||
mp.ExitNodeID = p0.InternalExitNodePrior()
|
mp.ExitNodeID = p0.InternalExitNodePrior()
|
||||||
if expr, ok := parseAutoExitNodeID(mp.ExitNodeID); ok {
|
if expr, ok := ipn.ParseAutoExitNodeString(mp.ExitNodeID); ok {
|
||||||
mp.AutoExitNodeSet = true
|
mp.AutoExitNodeSet = true
|
||||||
mp.AutoExitNode = expr
|
mp.AutoExitNode = expr
|
||||||
mp.ExitNodeID = unresolvedExitNodeID
|
mp.ExitNodeID = unresolvedExitNodeID
|
||||||
@@ -4304,7 +4304,7 @@ func (b *LocalBackend) SetUseExitNodeEnabled(actor ipnauth.Actor, v bool) (ipn.P
|
|||||||
mp.AutoExitNode = ""
|
mp.AutoExitNode = ""
|
||||||
mp.InternalExitNodePriorSet = true
|
mp.InternalExitNodePriorSet = true
|
||||||
if p0.AutoExitNode().IsSet() {
|
if p0.AutoExitNode().IsSet() {
|
||||||
mp.InternalExitNodePrior = tailcfg.StableNodeID(autoExitNodePrefix + p0.AutoExitNode())
|
mp.InternalExitNodePrior = tailcfg.StableNodeID(ipn.AutoExitNodePrefix + p0.AutoExitNode())
|
||||||
} else {
|
} else {
|
||||||
mp.InternalExitNodePrior = p0.ExitNodeID()
|
mp.InternalExitNodePrior = p0.ExitNodeID()
|
||||||
}
|
}
|
||||||
@@ -7933,10 +7933,6 @@ func longLatDistance(fromLat, fromLong, toLat, toLong float64) float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// autoExitNodePrefix is the prefix used in [syspolicy.ExitNodeID] values
|
|
||||||
// to indicate that the string following the prefix is an [ipn.ExitNodeExpression].
|
|
||||||
autoExitNodePrefix = "auto:"
|
|
||||||
|
|
||||||
// unresolvedExitNodeID is a special [tailcfg.StableNodeID] value
|
// unresolvedExitNodeID is a special [tailcfg.StableNodeID] value
|
||||||
// used as an exit node ID to install a blackhole route, preventing
|
// used as an exit node ID to install a blackhole route, preventing
|
||||||
// accidental non-exit-node usage until the [ipn.ExitNodeExpression]
|
// accidental non-exit-node usage until the [ipn.ExitNodeExpression]
|
||||||
@@ -7947,29 +7943,6 @@ const (
|
|||||||
unresolvedExitNodeID tailcfg.StableNodeID = "auto:any"
|
unresolvedExitNodeID tailcfg.StableNodeID = "auto:any"
|
||||||
)
|
)
|
||||||
|
|
||||||
// isAutoExitNodeID reports whether the given [tailcfg.StableNodeID] is
|
|
||||||
// actually an "auto:"-prefixed [ipn.ExitNodeExpression].
|
|
||||||
func isAutoExitNodeID(id tailcfg.StableNodeID) bool {
|
|
||||||
_, ok := parseAutoExitNodeID(id)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseAutoExitNodeID attempts to parse the given [tailcfg.StableNodeID]
|
|
||||||
// as an [ExitNodeExpression].
|
|
||||||
//
|
|
||||||
// It returns the parsed expression and true on success,
|
|
||||||
// or an empty string and false if the input does not appear to be
|
|
||||||
// an [ExitNodeExpression] (i.e., it doesn't start with "auto:").
|
|
||||||
//
|
|
||||||
// It is mainly used to parse the [syspolicy.ExitNodeID] value
|
|
||||||
// when it is set to "auto:<expression>" (e.g., auto:any).
|
|
||||||
func parseAutoExitNodeID(id tailcfg.StableNodeID) (_ ipn.ExitNodeExpression, ok bool) {
|
|
||||||
if expr, ok := strings.CutPrefix(string(id), autoExitNodePrefix); ok && expr != "" {
|
|
||||||
return ipn.ExitNodeExpression(expr), true
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAllowedAutoExitNodeID(exitNodeID tailcfg.StableNodeID) bool {
|
func isAllowedAutoExitNodeID(exitNodeID tailcfg.StableNodeID) bool {
|
||||||
if exitNodeID == "" {
|
if exitNodeID == "" {
|
||||||
return false // an exit node is required
|
return false // an exit node is required
|
||||||
|
|||||||
@@ -4873,110 +4873,6 @@ func TestMinLatencyDERPregion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldAutoExitNode(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
exitNodeIDPolicyValue string
|
|
||||||
expectedBool bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "auto:any",
|
|
||||||
exitNodeIDPolicyValue: "auto:any",
|
|
||||||
expectedBool: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no auto prefix",
|
|
||||||
exitNodeIDPolicyValue: "foo",
|
|
||||||
expectedBool: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "auto prefix but empty suffix",
|
|
||||||
exitNodeIDPolicyValue: "auto:",
|
|
||||||
expectedBool: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "auto prefix no colon",
|
|
||||||
exitNodeIDPolicyValue: "auto",
|
|
||||||
expectedBool: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "auto prefix unknown suffix",
|
|
||||||
exitNodeIDPolicyValue: "auto:foo",
|
|
||||||
expectedBool: true, // "auto:{unknown}" is treated as "auto:any"
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
syspolicy.RegisterWellKnownSettingsForTest(t)
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got := isAutoExitNodeID(tailcfg.StableNodeID(tt.exitNodeIDPolicyValue))
|
|
||||||
if got != tt.expectedBool {
|
|
||||||
t.Fatalf("expected %v got %v for %v policy value", tt.expectedBool, got, tt.exitNodeIDPolicyValue)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseAutoExitNodeID(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
exitNodeID string
|
|
||||||
wantOk bool
|
|
||||||
wantExpr ipn.ExitNodeExpression
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty expr",
|
|
||||||
exitNodeID: "",
|
|
||||||
wantOk: false,
|
|
||||||
wantExpr: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no auto prefix",
|
|
||||||
exitNodeID: "foo",
|
|
||||||
wantOk: false,
|
|
||||||
wantExpr: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "auto:any",
|
|
||||||
exitNodeID: "auto:any",
|
|
||||||
wantOk: true,
|
|
||||||
wantExpr: ipn.AnyExitNode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "auto:foo",
|
|
||||||
exitNodeID: "auto:foo",
|
|
||||||
wantOk: true,
|
|
||||||
wantExpr: "foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "auto prefix but empty suffix",
|
|
||||||
exitNodeID: "auto:",
|
|
||||||
wantOk: false,
|
|
||||||
wantExpr: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "auto prefix no colon",
|
|
||||||
exitNodeID: "auto",
|
|
||||||
wantOk: false,
|
|
||||||
wantExpr: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
gotExpr, gotOk := parseAutoExitNodeID(tailcfg.StableNodeID(tt.exitNodeID))
|
|
||||||
if gotOk != tt.wantOk || gotExpr != tt.wantExpr {
|
|
||||||
if tt.wantOk {
|
|
||||||
t.Fatalf("got %v (%q); want %v (%q)", gotOk, gotExpr, tt.wantOk, tt.wantExpr)
|
|
||||||
} else {
|
|
||||||
t.Fatalf("got %v (%q); want false", gotOk, gotExpr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEnableAutoUpdates(t *testing.T) {
|
func TestEnableAutoUpdates(t *testing.T) {
|
||||||
lb := newTestLocalBackend(t)
|
lb := newTestLocalBackend(t)
|
||||||
|
|
||||||
|
|||||||
@@ -1088,3 +1088,25 @@ const AnyExitNode ExitNodeExpression = "any"
|
|||||||
func (e ExitNodeExpression) IsSet() bool {
|
func (e ExitNodeExpression) IsSet() bool {
|
||||||
return e != ""
|
return e != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AutoExitNodePrefix is the prefix used in [syspolicy.ExitNodeID] values and CLI
|
||||||
|
// to indicate that the string following the prefix is an [ipn.ExitNodeExpression].
|
||||||
|
AutoExitNodePrefix = "auto:"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseAutoExitNodeString attempts to parse the given string
|
||||||
|
// as an [ExitNodeExpression].
|
||||||
|
//
|
||||||
|
// It returns the parsed expression and true on success,
|
||||||
|
// or an empty string and false if the input does not appear to be
|
||||||
|
// an [ExitNodeExpression] (i.e., it doesn't start with "auto:").
|
||||||
|
//
|
||||||
|
// It is mainly used to parse the [syspolicy.ExitNodeID] value
|
||||||
|
// when it is set to "auto:<expression>" (e.g., auto:any).
|
||||||
|
func ParseAutoExitNodeString[T ~string](s T) (_ ExitNodeExpression, ok bool) {
|
||||||
|
if expr, ok := strings.CutPrefix(string(s), AutoExitNodePrefix); ok && expr != "" {
|
||||||
|
return ExitNodeExpression(expr), true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|||||||
@@ -1129,3 +1129,62 @@ func TestPrefsDowngrade(t *testing.T) {
|
|||||||
t.Fatal("AllowSingleHosts should be true")
|
t.Fatal("AllowSingleHosts should be true")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseAutoExitNodeString(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
exitNodeID string
|
||||||
|
wantOk bool
|
||||||
|
wantExpr ExitNodeExpression
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty expr",
|
||||||
|
exitNodeID: "",
|
||||||
|
wantOk: false,
|
||||||
|
wantExpr: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no auto prefix",
|
||||||
|
exitNodeID: "foo",
|
||||||
|
wantOk: false,
|
||||||
|
wantExpr: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "auto:any",
|
||||||
|
exitNodeID: "auto:any",
|
||||||
|
wantOk: true,
|
||||||
|
wantExpr: AnyExitNode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "auto:foo",
|
||||||
|
exitNodeID: "auto:foo",
|
||||||
|
wantOk: true,
|
||||||
|
wantExpr: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "auto prefix but empty suffix",
|
||||||
|
exitNodeID: "auto:",
|
||||||
|
wantOk: false,
|
||||||
|
wantExpr: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "auto prefix no colon",
|
||||||
|
exitNodeID: "auto",
|
||||||
|
wantOk: false,
|
||||||
|
wantExpr: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
gotExpr, gotOk := ParseAutoExitNodeString(tt.exitNodeID)
|
||||||
|
if gotOk != tt.wantOk || gotExpr != tt.wantExpr {
|
||||||
|
if tt.wantOk {
|
||||||
|
t.Fatalf("got %v (%q); want %v (%q)", gotOk, gotExpr, tt.wantOk, tt.wantExpr)
|
||||||
|
} else {
|
||||||
|
t.Fatalf("got %v (%q); want false", gotOk, gotExpr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user