k8s.io/kubernetes@v1.29.3/pkg/util/flag/flags_test.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package flag 18 19 import ( 20 "fmt" 21 "reflect" 22 "strings" 23 "testing" 24 25 "github.com/spf13/pflag" 26 27 v1 "k8s.io/api/core/v1" 28 apiequality "k8s.io/apimachinery/pkg/api/equality" 29 "k8s.io/apimachinery/pkg/api/resource" 30 kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" 31 ) 32 33 func TestIPVar(t *testing.T) { 34 defaultIP := "0.0.0.0" 35 testCases := []struct { 36 argc string 37 expectErr bool 38 expectVal string 39 }{ 40 41 { 42 argc: "blah --ip=1.2.3.4", 43 expectVal: "1.2.3.4", 44 }, 45 { 46 argc: "blah --ip=1.2.3.4a", 47 expectErr: true, 48 expectVal: defaultIP, 49 }, 50 } 51 for _, tc := range testCases { 52 fs := pflag.NewFlagSet("blah", pflag.PanicOnError) 53 ip := defaultIP 54 fs.Var(&IPVar{&ip}, "ip", "the ip") 55 56 var err error 57 func() { 58 defer func() { 59 if r := recover(); r != nil { 60 err = r.(error) 61 } 62 }() 63 fs.Parse(strings.Split(tc.argc, " ")) 64 }() 65 66 if tc.expectErr && err == nil { 67 t.Errorf("did not observe an expected error") 68 continue 69 } 70 if !tc.expectErr && err != nil { 71 t.Errorf("observed an unexpected error: %v", err) 72 continue 73 } 74 if tc.expectVal != ip { 75 t.Errorf("unexpected ip: expected %q, saw %q", tc.expectVal, ip) 76 } 77 } 78 } 79 80 func TestIPPortVar(t *testing.T) { 81 defaultIPPort := "0.0.0.0:8080" 82 testCases := []struct { 83 desc string 84 argc string 85 expectErr bool 86 expectVal string 87 }{ 88 89 { 90 desc: "valid ipv4 1", 91 argc: "blah --ipport=0.0.0.0", 92 expectVal: "0.0.0.0", 93 }, 94 { 95 desc: "valid ipv4 2", 96 argc: "blah --ipport=127.0.0.1", 97 expectVal: "127.0.0.1", 98 }, 99 100 { 101 desc: "invalid IP", 102 argc: "blah --ipport=invalidip", 103 expectErr: true, 104 expectVal: defaultIPPort, 105 }, 106 { 107 desc: "valid ipv4 with port", 108 argc: "blah --ipport=0.0.0.0:8080", 109 expectVal: "0.0.0.0:8080", 110 }, 111 { 112 desc: "invalid ipv4 with invalid port", 113 argc: "blah --ipport=0.0.0.0:invalidport", 114 expectErr: true, 115 expectVal: defaultIPPort, 116 }, 117 { 118 desc: "invalid IP with port", 119 argc: "blah --ipport=invalidip:8080", 120 expectErr: true, 121 expectVal: defaultIPPort, 122 }, 123 { 124 desc: "valid ipv6 1", 125 argc: "blah --ipport=::1", 126 expectVal: "::1", 127 }, 128 { 129 desc: "valid ipv6 2", 130 argc: "blah --ipport=::", 131 expectVal: "::", 132 }, 133 { 134 desc: "valid ipv6 with port", 135 argc: "blah --ipport=[::1]:8080", 136 expectVal: "[::1]:8080", 137 }, 138 { 139 desc: "invalid ipv6 with port without bracket", 140 argc: "blah --ipport=fd00:f00d:600d:f00d:8080", 141 expectErr: true, 142 expectVal: defaultIPPort, 143 }, 144 } 145 for _, tc := range testCases { 146 fs := pflag.NewFlagSet("blah", pflag.PanicOnError) 147 ipport := defaultIPPort 148 fs.Var(&IPPortVar{&ipport}, "ipport", "the ip:port") 149 150 var err error 151 func() { 152 defer func() { 153 if r := recover(); r != nil { 154 err = r.(error) 155 } 156 }() 157 fs.Parse(strings.Split(tc.argc, " ")) 158 }() 159 160 if tc.expectErr && err == nil { 161 t.Errorf("%q: Did not observe an expected error", tc.desc) 162 continue 163 } 164 if !tc.expectErr && err != nil { 165 t.Errorf("%q: Observed an unexpected error: %v", tc.desc, err) 166 continue 167 } 168 if tc.expectVal != ipport { 169 t.Errorf("%q: Unexpected ipport: expected %q, saw %q", tc.desc, tc.expectVal, ipport) 170 } 171 } 172 } 173 174 func TestReservedMemoryVar(t *testing.T) { 175 resourceNameHugepages1Gi := v1.ResourceName(fmt.Sprintf("%s1Gi", v1.ResourceHugePagesPrefix)) 176 memory1Gi := resource.MustParse("1Gi") 177 testCases := []struct { 178 desc string 179 argc string 180 expectErr bool 181 expectVal []kubeletconfig.MemoryReservation 182 }{ 183 { 184 desc: "valid input", 185 argc: "blah --reserved-memory=0:memory=1Gi", 186 expectVal: []kubeletconfig.MemoryReservation{ 187 { 188 NumaNode: 0, 189 Limits: v1.ResourceList{ 190 v1.ResourceMemory: memory1Gi, 191 }, 192 }, 193 }, 194 }, 195 { 196 desc: "valid input with multiple memory types", 197 argc: "blah --reserved-memory=0:memory=1Gi,hugepages-1Gi=1Gi", 198 expectVal: []kubeletconfig.MemoryReservation{ 199 { 200 NumaNode: 0, 201 Limits: v1.ResourceList{ 202 v1.ResourceMemory: memory1Gi, 203 resourceNameHugepages1Gi: memory1Gi, 204 }, 205 }, 206 }, 207 }, 208 { 209 desc: "valid input with multiple reserved-memory arguments", 210 argc: "blah --reserved-memory=0:memory=1Gi,hugepages-1Gi=1Gi --reserved-memory=1:memory=1Gi", 211 expectVal: []kubeletconfig.MemoryReservation{ 212 { 213 NumaNode: 0, 214 Limits: v1.ResourceList{ 215 v1.ResourceMemory: memory1Gi, 216 resourceNameHugepages1Gi: memory1Gi, 217 }, 218 }, 219 { 220 NumaNode: 1, 221 Limits: v1.ResourceList{ 222 v1.ResourceMemory: memory1Gi, 223 }, 224 }, 225 }, 226 }, 227 { 228 desc: "valid input with ';' as separator for multiple reserved-memory arguments", 229 argc: "blah --reserved-memory=0:memory=1Gi,hugepages-1Gi=1Gi;1:memory=1Gi", 230 expectVal: []kubeletconfig.MemoryReservation{ 231 { 232 NumaNode: 0, 233 Limits: v1.ResourceList{ 234 v1.ResourceMemory: memory1Gi, 235 resourceNameHugepages1Gi: memory1Gi, 236 }, 237 }, 238 { 239 NumaNode: 1, 240 Limits: v1.ResourceList{ 241 v1.ResourceMemory: memory1Gi, 242 }, 243 }, 244 }, 245 }, 246 { 247 desc: "invalid input", 248 argc: "blah --reserved-memory=bad-input", 249 expectVal: nil, 250 expectErr: true, 251 }, 252 { 253 desc: "invalid input without memory types", 254 argc: "blah --reserved-memory=0:", 255 expectVal: nil, 256 expectErr: true, 257 }, 258 { 259 desc: "invalid input with non-integer NUMA node", 260 argc: "blah --reserved-memory=a:memory=1Gi", 261 expectVal: nil, 262 expectErr: true, 263 }, 264 { 265 desc: "invalid input with invalid limit", 266 argc: "blah --reserved-memory=0:memory=", 267 expectVal: nil, 268 expectErr: true, 269 }, 270 { 271 desc: "invalid input with invalid memory type", 272 argc: "blah --reserved-memory=0:type=1Gi", 273 expectVal: nil, 274 expectErr: true, 275 }, 276 { 277 desc: "invalid input with invalid quantity", 278 argc: "blah --reserved-memory=0:memory=1Be", 279 expectVal: nil, 280 expectErr: true, 281 }, 282 } 283 for _, tc := range testCases { 284 fs := pflag.NewFlagSet("blah", pflag.PanicOnError) 285 286 var reservedMemory []kubeletconfig.MemoryReservation 287 fs.Var(&ReservedMemoryVar{Value: &reservedMemory}, "reserved-memory", "--reserved-memory 0:memory=1Gi,hugepages-1M=2Gi") 288 289 var err error 290 func() { 291 defer func() { 292 if r := recover(); r != nil { 293 err = r.(error) 294 } 295 }() 296 fs.Parse(strings.Split(tc.argc, " ")) 297 }() 298 299 if tc.expectErr && err == nil { 300 t.Fatalf("%q: Did not observe an expected error", tc.desc) 301 } 302 if !tc.expectErr && err != nil { 303 t.Fatalf("%q: Observed an unexpected error: %v", tc.desc, err) 304 } 305 if !apiequality.Semantic.DeepEqual(reservedMemory, tc.expectVal) { 306 t.Fatalf("%q: Unexpected reserved-error: expected %v, saw %v", tc.desc, tc.expectVal, reservedMemory) 307 } 308 } 309 } 310 311 func TestTaintsVar(t *testing.T) { 312 cases := []struct { 313 f string 314 err bool 315 t []v1.Taint 316 }{ 317 { 318 f: "", 319 t: []v1.Taint(nil), 320 }, 321 { 322 f: "--t=foo=bar:NoSchedule", 323 t: []v1.Taint{{Key: "foo", Value: "bar", Effect: "NoSchedule"}}, 324 }, 325 { 326 f: "--t=baz:NoSchedule", 327 t: []v1.Taint{{Key: "baz", Value: "", Effect: "NoSchedule"}}, 328 }, 329 { 330 f: "--t=foo=bar:NoSchedule,baz:NoSchedule,bing=bang:PreferNoSchedule,qux=:NoSchedule", 331 t: []v1.Taint{ 332 {Key: "foo", Value: "bar", Effect: v1.TaintEffectNoSchedule}, 333 {Key: "baz", Value: "", Effect: "NoSchedule"}, 334 {Key: "bing", Value: "bang", Effect: v1.TaintEffectPreferNoSchedule}, 335 {Key: "qux", Value: "", Effect: "NoSchedule"}, 336 }, 337 }, 338 { 339 f: "--t=dedicated-for=user1:NoExecute,baz:NoSchedule,foo-bar=:NoSchedule", 340 t: []v1.Taint{ 341 {Key: "dedicated-for", Value: "user1", Effect: "NoExecute"}, 342 {Key: "baz", Value: "", Effect: "NoSchedule"}, 343 {Key: "foo-bar", Value: "", Effect: "NoSchedule"}, 344 }, 345 }, 346 } 347 348 for i, c := range cases { 349 args := append([]string{"test"}, strings.Fields(c.f)...) 350 cli := pflag.NewFlagSet("test", pflag.ContinueOnError) 351 var taints []v1.Taint 352 cli.Var(RegisterWithTaintsVar{Value: &taints}, "t", "bar") 353 354 err := cli.Parse(args) 355 if err == nil && c.err { 356 t.Errorf("[%v] expected error", i) 357 continue 358 } 359 if err != nil && !c.err { 360 t.Errorf("[%v] unexpected error: %v", i, err) 361 continue 362 } 363 if !reflect.DeepEqual(c.t, taints) { 364 t.Errorf("[%v] unexpected taints:\n\texpected:\n\t\t%#v\n\tgot:\n\t\t%#v", i, c.t, taints) 365 } 366 } 367 368 }