cmd/tailscale/cli: return error on duplicate multi-value flags (#15534)
Some CLI flags support multiple values separated by commas. These flags are intended to be declared only once and will silently ignore subsequent instances. This will now throw an error if multiple instances of advertise-tags and advertise-routes are detected. Fixes #6813 Signed-off-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com>
This commit is contained in:
@@ -657,6 +657,13 @@ func upArgsFromOSArgs(goos string, flagArgs ...string) (args upArgsT) {
|
||||
return
|
||||
}
|
||||
|
||||
func newSingleUseStringForTest(v string) singleUseStringFlag {
|
||||
return singleUseStringFlag{
|
||||
set: true,
|
||||
value: v,
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrefsFromUpArgs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -721,14 +728,14 @@ func TestPrefsFromUpArgs(t *testing.T) {
|
||||
{
|
||||
name: "error_advertise_route_invalid_ip",
|
||||
args: upArgsT{
|
||||
advertiseRoutes: "foo",
|
||||
advertiseRoutes: newSingleUseStringForTest("foo"),
|
||||
},
|
||||
wantErr: `"foo" is not a valid IP address or CIDR prefix`,
|
||||
},
|
||||
{
|
||||
name: "error_advertise_route_unmasked_bits",
|
||||
args: upArgsT{
|
||||
advertiseRoutes: "1.2.3.4/16",
|
||||
advertiseRoutes: newSingleUseStringForTest("1.2.3.4/16"),
|
||||
},
|
||||
wantErr: `1.2.3.4/16 has non-address bits set; expected 1.2.0.0/16`,
|
||||
},
|
||||
@@ -749,7 +756,7 @@ func TestPrefsFromUpArgs(t *testing.T) {
|
||||
{
|
||||
name: "error_tag_prefix",
|
||||
args: upArgsT{
|
||||
advertiseTags: "foo",
|
||||
advertiseTags: newSingleUseStringForTest("foo"),
|
||||
},
|
||||
wantErr: `tag: "foo": tags must start with 'tag:'`,
|
||||
},
|
||||
@@ -829,7 +836,7 @@ func TestPrefsFromUpArgs(t *testing.T) {
|
||||
name: "via_route_good",
|
||||
goos: "linux",
|
||||
args: upArgsT{
|
||||
advertiseRoutes: "fd7a:115c:a1e0:b1a::bb:10.0.0.0/112",
|
||||
advertiseRoutes: newSingleUseStringForTest("fd7a:115c:a1e0:b1a::bb:10.0.0.0/112"),
|
||||
netfilterMode: "off",
|
||||
},
|
||||
want: &ipn.Prefs{
|
||||
@@ -848,7 +855,7 @@ func TestPrefsFromUpArgs(t *testing.T) {
|
||||
name: "via_route_good_16_bit",
|
||||
goos: "linux",
|
||||
args: upArgsT{
|
||||
advertiseRoutes: "fd7a:115c:a1e0:b1a::aabb:10.0.0.0/112",
|
||||
advertiseRoutes: newSingleUseStringForTest("fd7a:115c:a1e0:b1a::aabb:10.0.0.0/112"),
|
||||
netfilterMode: "off",
|
||||
},
|
||||
want: &ipn.Prefs{
|
||||
@@ -867,7 +874,7 @@ func TestPrefsFromUpArgs(t *testing.T) {
|
||||
name: "via_route_short_prefix",
|
||||
goos: "linux",
|
||||
args: upArgsT{
|
||||
advertiseRoutes: "fd7a:115c:a1e0:b1a::/64",
|
||||
advertiseRoutes: newSingleUseStringForTest("fd7a:115c:a1e0:b1a::/64"),
|
||||
netfilterMode: "off",
|
||||
},
|
||||
wantErr: "fd7a:115c:a1e0:b1a::/64 4-in-6 prefix must be at least a /96",
|
||||
@@ -876,7 +883,7 @@ func TestPrefsFromUpArgs(t *testing.T) {
|
||||
name: "via_route_short_reserved_siteid",
|
||||
goos: "linux",
|
||||
args: upArgsT{
|
||||
advertiseRoutes: "fd7a:115c:a1e0:b1a:1234:5678::/112",
|
||||
advertiseRoutes: newSingleUseStringForTest("fd7a:115c:a1e0:b1a:1234:5678::/112"),
|
||||
netfilterMode: "off",
|
||||
},
|
||||
wantErr: "route fd7a:115c:a1e0:b1a:1234:5678::/112 contains invalid site ID 12345678; must be 0xffff or less",
|
||||
@@ -1106,6 +1113,7 @@ func TestUpdatePrefs(t *testing.T) {
|
||||
},
|
||||
env: upCheckEnv{backendState: "Running"},
|
||||
},
|
||||
|
||||
{
|
||||
// Issue 3808: explicitly empty --operator= should clear value.
|
||||
name: "explicit_empty_operator",
|
||||
@@ -1598,3 +1606,56 @@ func TestDepsNoCapture(t *testing.T) {
|
||||
}.Check(t)
|
||||
|
||||
}
|
||||
|
||||
func TestSingleUseStringFlag(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setValues []string
|
||||
wantValue string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "set once",
|
||||
setValues: []string{"foo"},
|
||||
wantValue: "foo",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "set twice",
|
||||
setValues: []string{"foo", "bar"},
|
||||
wantValue: "foo",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "set nothing",
|
||||
setValues: []string{},
|
||||
wantValue: "",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var flag singleUseStringFlag
|
||||
var lastErr error
|
||||
|
||||
for _, val := range tt.setValues {
|
||||
lastErr = flag.Set(val)
|
||||
}
|
||||
|
||||
if tt.wantErr {
|
||||
if lastErr == nil {
|
||||
t.Errorf("expected error on final Set, got nil")
|
||||
}
|
||||
} else {
|
||||
if lastErr != nil {
|
||||
t.Errorf("unexpected error on final Set: %v", lastErr)
|
||||
}
|
||||
}
|
||||
|
||||
if got := flag.String(); got != tt.wantValue {
|
||||
t.Errorf("String() = %q, want %q", got, tt.wantValue)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user