k8s.io/kubernetes@v1.29.3/pkg/kubelet/network/dns/dns_test.go (about) 1 /* 2 Copyright 2017 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 dns 18 19 import ( 20 "fmt" 21 "net" 22 "os" 23 "strconv" 24 "strings" 25 "testing" 26 27 v1 "k8s.io/api/core/v1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/types" 30 "k8s.io/apimachinery/pkg/util/sets" 31 "k8s.io/client-go/tools/record" 32 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 33 "k8s.io/kubernetes/pkg/apis/core/validation" 34 netutils "k8s.io/utils/net" 35 36 "github.com/stretchr/testify/assert" 37 "github.com/stretchr/testify/require" 38 ) 39 40 var ( 41 // testHostNameserver and testHostDomain are also being used in dns_windows_test.go. 42 testHostNameserver = "8.8.8.8" 43 testHostDomain = "host.domain" 44 fetchEvent = func(recorder *record.FakeRecorder) string { 45 select { 46 case event := <-recorder.Events: 47 return event 48 default: 49 return "" 50 } 51 } 52 ) 53 54 func TestParseResolvConf(t *testing.T) { 55 testCases := []struct { 56 data string 57 nameservers []string 58 searches []string 59 options []string 60 isErr bool 61 }{ 62 {"", []string{}, []string{}, []string{}, false}, 63 {" ", []string{}, []string{}, []string{}, false}, 64 {"\n", []string{}, []string{}, []string{}, false}, 65 {"\t\n\t", []string{}, []string{}, []string{}, false}, 66 {"#comment\n", []string{}, []string{}, []string{}, false}, 67 {" #comment\n", []string{}, []string{}, []string{}, false}, 68 {"#comment\n#comment", []string{}, []string{}, []string{}, false}, 69 {"#comment\nnameserver", []string{}, []string{}, []string{}, true}, // nameserver empty 70 {"#comment\nnameserver\nsearch", []string{}, []string{}, []string{}, true}, // nameserver and search empty 71 {"#comment\nnameserver 1.2.3.4\nsearch", []string{"1.2.3.4"}, []string{}, []string{}, false}, // nameserver specified and search empty 72 {"nameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}, false}, 73 {" nameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}, false}, 74 {"\tnameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}, false}, 75 {"nameserver\t1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}, false}, 76 {"nameserver \t 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}, false}, 77 {"nameserver 1.2.3.4\nnameserver 5.6.7.8", []string{"1.2.3.4", "5.6.7.8"}, []string{}, []string{}, false}, 78 {"nameserver 1.2.3.4 #comment", []string{"1.2.3.4"}, []string{}, []string{}, false}, 79 {"search ", []string{}, []string{}, []string{}, false}, // search empty 80 {"search .", []string{}, []string{}, []string{}, false}, // ignore lone dot 81 {"search . foo", []string{}, []string{"foo"}, []string{}, false}, 82 {"search foo .", []string{}, []string{"foo"}, []string{}, false}, 83 {"search foo . bar", []string{}, []string{"foo", "bar"}, []string{}, false}, 84 {"search foo", []string{}, []string{"foo"}, []string{}, false}, 85 {"search foo bar", []string{}, []string{"foo", "bar"}, []string{}, false}, 86 {"search foo. bar", []string{}, []string{"foo", "bar"}, []string{}, false}, 87 {"search foo bar bat\n", []string{}, []string{"foo", "bar", "bat"}, []string{}, false}, 88 {"search foo\nsearch bar", []string{}, []string{"bar"}, []string{}, false}, 89 {"nameserver 1.2.3.4\nsearch foo bar", []string{"1.2.3.4"}, []string{"foo", "bar"}, []string{}, false}, 90 {"nameserver 1.2.3.4\nsearch foo\nnameserver 5.6.7.8\nsearch bar", []string{"1.2.3.4", "5.6.7.8"}, []string{"bar"}, []string{}, false}, 91 {"#comment\nnameserver 1.2.3.4\n#comment\nsearch foo\ncomment", []string{"1.2.3.4"}, []string{"foo"}, []string{}, false}, 92 {"options ", []string{}, []string{}, []string{}, false}, 93 {"options ndots:5 attempts:2", []string{}, []string{}, []string{"ndots:5", "attempts:2"}, false}, 94 {"options ndots:1 ndots:5 attempts:2", []string{}, []string{}, []string{"ndots:5", "attempts:2"}, false}, 95 {"options ndots:1 edns0 attempts:2 single-request-reopen", []string{}, []string{}, []string{"edns0", "attempts:2", "single-request-reopen", "ndots:1"}, false}, 96 {"options ndots:1\noptions ndots:5 attempts:3", []string{}, []string{}, []string{"ndots:5", "attempts:3"}, false}, 97 {"options ndots:1 timeout:3 timeout:1 attempts:3\noptions ndots:5", []string{}, []string{}, []string{"ndots:5", "timeout:1", "attempts:3"}, false}, 98 {"options ndots:1 attempts:3\noptions ndots:1 attempts:3 ndots:5", []string{}, []string{}, []string{"ndots:5", "attempts:3"}, false}, 99 {"nameserver 1.2.3.4\nsearch foo\nnameserver 5.6.7.8\nsearch bar\noptions ndots:5 attempts:4", []string{"1.2.3.4", "5.6.7.8"}, []string{"bar"}, []string{"ndots:5", "attempts:4"}, false}, 100 } 101 for i, tc := range testCases { 102 ns, srch, opts, err := parseResolvConf(strings.NewReader(tc.data)) 103 if !tc.isErr { 104 require.NoError(t, err) 105 assert.EqualValues(t, tc.nameservers, ns, "test case [%d]: name servers", i) 106 assert.EqualValues(t, tc.searches, srch, "test case [%d] searches", i) 107 assert.EqualValues(t, sets.NewString(tc.options...), sets.NewString(opts...), "test case [%d] options", i) 108 } else { 109 require.Error(t, err, "tc.searches %v", tc.searches) 110 } 111 } 112 } 113 114 func TestFormDNSSearchFitsLimits(t *testing.T) { 115 searchPathList2048Chars := []string{ 116 // 2048 = 128 + 127 * 15 + 15 117 strings.Repeat("A", 128), 118 strings.Repeat("A", 127), 119 strings.Repeat("A", 127), 120 strings.Repeat("A", 127), 121 strings.Repeat("A", 127), 122 strings.Repeat("A", 127), 123 strings.Repeat("A", 127), 124 strings.Repeat("A", 127), 125 strings.Repeat("A", 127), 126 strings.Repeat("A", 127), 127 strings.Repeat("A", 127), 128 strings.Repeat("A", 127), 129 strings.Repeat("A", 127), 130 strings.Repeat("A", 127), 131 strings.Repeat("A", 127), 132 strings.Repeat("A", 127), 133 } 134 135 recorder := record.NewFakeRecorder(20) 136 nodeRef := &v1.ObjectReference{ 137 Kind: "Node", 138 Name: string("testNode"), 139 UID: types.UID("testNode"), 140 Namespace: "", 141 } 142 testClusterDNSDomain := "TEST" 143 144 configurer := NewConfigurer(recorder, nodeRef, nil, nil, testClusterDNSDomain, "") 145 146 pod := &v1.Pod{ 147 ObjectMeta: metav1.ObjectMeta{ 148 UID: "", 149 Name: "test_pod", 150 Namespace: "testNS", 151 Annotations: map[string]string{}, 152 }, 153 } 154 155 testCases := []struct { 156 desc string 157 hostNames []string 158 resultSearch []string 159 events []string 160 }{ 161 { 162 desc: "valid: 3 search paths", 163 hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST"}, 164 resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST"}, 165 events: []string{}, 166 }, 167 168 { 169 desc: "valid: 5 search paths", 170 hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"}, 171 resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"}, 172 events: []string{}, 173 }, 174 175 { 176 desc: "invalid: longer than 256 characters in search path list", 177 hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", strings.Repeat("B", 256), "BBB"}, 178 resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"}, 179 events: []string{"Search Line limits were exceeded, some search paths have been omitted, the applied search line is: testNS.svc.TEST svc.TEST TEST AAA BBB"}, 180 }, 181 182 { 183 desc: "valid: 2048 characters in search path list", 184 hostNames: searchPathList2048Chars, 185 resultSearch: searchPathList2048Chars, 186 events: []string{}, 187 }, 188 189 { 190 desc: "invalid: 2050 characters in search path list", 191 hostNames: append(searchPathList2048Chars, "B"), 192 resultSearch: searchPathList2048Chars, 193 events: []string{fmt.Sprintf("Search Line limits were exceeded, some search paths have been omitted, the applied search line is: %s", strings.Join(searchPathList2048Chars, " "))}, 194 }, 195 196 { 197 desc: "invalid: 256 characters search path", 198 hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", strings.Repeat("B", 256), "BBB"}, 199 resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"}, 200 events: []string{"Search Line limits were exceeded, some search paths have been omitted, the applied search line is: testNS.svc.TEST svc.TEST TEST AAA BBB"}, 201 }, 202 203 { 204 desc: "valid: 7 search paths", 205 hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB", "CCC", "DDD"}, 206 resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB", "CCC", "DDD"}, 207 events: []string{}, 208 }, 209 210 { 211 desc: "valid: 32 search paths", 212 hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"}, 213 resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"}, 214 events: []string{}, 215 }, 216 217 { 218 desc: "invalid: 33 search paths", 219 hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33"}, 220 resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"}, 221 events: []string{"Search Line limits were exceeded, some search paths have been omitted, the applied search line is: testNS.svc.TEST svc.TEST TEST 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32"}, 222 }, 223 } 224 225 for i, tc := range testCases { 226 t.Run(tc.desc, func(t *testing.T) { 227 dnsSearch := configurer.formDNSSearchFitsLimits(tc.hostNames, pod) 228 assert.EqualValues(t, tc.resultSearch, dnsSearch, "test [%d]", i) 229 for _, expectedEvent := range tc.events { 230 expected := fmt.Sprintf("%s %s %s", v1.EventTypeWarning, "DNSConfigForming", expectedEvent) 231 event := fetchEvent(recorder) 232 assert.Equal(t, expected, event, "test [%d]", i) 233 } 234 }) 235 } 236 } 237 238 func TestFormDNSNameserversFitsLimits(t *testing.T) { 239 recorder := record.NewFakeRecorder(20) 240 nodeRef := &v1.ObjectReference{ 241 Kind: "Node", 242 Name: string("testNode"), 243 UID: types.UID("testNode"), 244 Namespace: "", 245 } 246 testClusterDNSDomain := "TEST" 247 248 configurer := NewConfigurer(recorder, nodeRef, nil, nil, testClusterDNSDomain, "") 249 250 pod := &v1.Pod{ 251 ObjectMeta: metav1.ObjectMeta{ 252 UID: "", 253 Name: "test_pod", 254 Namespace: "testNS", 255 Annotations: map[string]string{}, 256 }, 257 } 258 259 testCases := []struct { 260 desc string 261 nameservers []string 262 expectedNameserver []string 263 expectedEvent bool 264 }{ 265 { 266 desc: "valid: 1 nameserver", 267 nameservers: []string{"127.0.0.1"}, 268 expectedNameserver: []string{"127.0.0.1"}, 269 expectedEvent: false, 270 }, 271 { 272 desc: "valid: 3 nameservers", 273 nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"}, 274 expectedNameserver: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"}, 275 expectedEvent: false, 276 }, 277 { 278 desc: "invalid: 4 nameservers, trimmed to 3", 279 nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8", "1.2.3.4"}, 280 expectedNameserver: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"}, 281 expectedEvent: true, 282 }, 283 } 284 285 for _, tc := range testCases { 286 appliedNameservers := configurer.formDNSNameserversFitsLimits(tc.nameservers, pod) 287 assert.EqualValues(t, tc.expectedNameserver, appliedNameservers, tc.desc) 288 event := fetchEvent(recorder) 289 if tc.expectedEvent && len(event) == 0 { 290 t.Errorf("%s: formDNSNameserversFitsLimits(%v) expected event, got no event.", tc.desc, tc.nameservers) 291 } else if !tc.expectedEvent && len(event) > 0 { 292 t.Errorf("%s: formDNSNameserversFitsLimits(%v) expected no event, got event: %v", tc.desc, tc.nameservers, event) 293 } 294 } 295 } 296 297 func TestMergeDNSOptions(t *testing.T) { 298 testOptionValue := "3" 299 300 testCases := []struct { 301 desc string 302 existingDNSConfigOptions []string 303 dnsConfigOptions []v1.PodDNSConfigOption 304 expectedOptions []string 305 }{ 306 { 307 desc: "Empty dnsConfigOptions", 308 existingDNSConfigOptions: []string{"ndots:5", "debug"}, 309 dnsConfigOptions: nil, 310 expectedOptions: []string{"ndots:5", "debug"}, 311 }, 312 { 313 desc: "No duplicated entries", 314 existingDNSConfigOptions: []string{"ndots:5", "debug"}, 315 dnsConfigOptions: []v1.PodDNSConfigOption{ 316 {Name: "single-request"}, 317 {Name: "attempts", Value: &testOptionValue}, 318 }, 319 expectedOptions: []string{"ndots:5", "debug", "single-request", "attempts:3"}, 320 }, 321 { 322 desc: "Overwrite duplicated entries", 323 existingDNSConfigOptions: []string{"ndots:5", "debug"}, 324 dnsConfigOptions: []v1.PodDNSConfigOption{ 325 {Name: "ndots", Value: &testOptionValue}, 326 {Name: "debug"}, 327 {Name: "single-request"}, 328 {Name: "attempts", Value: &testOptionValue}, 329 }, 330 expectedOptions: []string{"ndots:3", "debug", "single-request", "attempts:3"}, 331 }, 332 } 333 334 for _, tc := range testCases { 335 options := mergeDNSOptions(tc.existingDNSConfigOptions, tc.dnsConfigOptions) 336 // Options order may be changed after conversion. 337 if !sets.NewString(options...).Equal(sets.NewString(tc.expectedOptions...)) { 338 t.Errorf("%s: mergeDNSOptions(%v, %v)=%v, want %v", tc.desc, tc.existingDNSConfigOptions, tc.dnsConfigOptions, options, tc.expectedOptions) 339 } 340 } 341 } 342 343 func TestGetPodDNSType(t *testing.T) { 344 recorder := record.NewFakeRecorder(20) 345 nodeRef := &v1.ObjectReference{ 346 Kind: "Node", 347 Name: string("testNode"), 348 UID: types.UID("testNode"), 349 Namespace: "", 350 } 351 testClusterDNSDomain := "TEST" 352 clusterNS := "203.0.113.1" 353 testClusterDNS := []net.IP{netutils.ParseIPSloppy(clusterNS)} 354 355 configurer := NewConfigurer(recorder, nodeRef, nil, nil, testClusterDNSDomain, "") 356 357 pod := &v1.Pod{ 358 ObjectMeta: metav1.ObjectMeta{ 359 UID: "", 360 Name: "test_pod", 361 Namespace: "testNS", 362 Annotations: map[string]string{}, 363 }, 364 } 365 366 testCases := []struct { 367 desc string 368 hasClusterDNS bool 369 hostNetwork bool 370 dnsPolicy v1.DNSPolicy 371 expectedDNSType podDNSType 372 expectedError bool 373 }{ 374 { 375 desc: "valid DNSClusterFirst without hostnetwork", 376 hasClusterDNS: true, 377 dnsPolicy: v1.DNSClusterFirst, 378 expectedDNSType: podDNSCluster, 379 }, 380 { 381 desc: "valid DNSClusterFirstWithHostNet with hostnetwork", 382 hasClusterDNS: true, 383 hostNetwork: true, 384 dnsPolicy: v1.DNSClusterFirstWithHostNet, 385 expectedDNSType: podDNSCluster, 386 }, 387 { 388 desc: "valid DNSClusterFirstWithHostNet without hostnetwork", 389 hasClusterDNS: true, 390 dnsPolicy: v1.DNSClusterFirstWithHostNet, 391 expectedDNSType: podDNSCluster, 392 }, 393 { 394 desc: "valid DNSDefault without hostnetwork", 395 dnsPolicy: v1.DNSDefault, 396 expectedDNSType: podDNSHost, 397 }, 398 { 399 desc: "valid DNSDefault with hostnetwork", 400 hostNetwork: true, 401 dnsPolicy: v1.DNSDefault, 402 expectedDNSType: podDNSHost, 403 }, 404 { 405 desc: "DNSClusterFirst with hostnetwork, fallback to DNSDefault", 406 hasClusterDNS: true, 407 hostNetwork: true, 408 dnsPolicy: v1.DNSClusterFirst, 409 expectedDNSType: podDNSHost, 410 }, 411 { 412 desc: "valid DNSNone", 413 dnsPolicy: v1.DNSNone, 414 expectedDNSType: podDNSNone, 415 }, 416 { 417 desc: "invalid DNS policy, should return error", 418 dnsPolicy: "invalidPolicy", 419 expectedError: true, 420 }, 421 } 422 423 for _, tc := range testCases { 424 t.Run(tc.desc, func(t *testing.T) { 425 if tc.hasClusterDNS { 426 configurer.clusterDNS = testClusterDNS 427 } else { 428 configurer.clusterDNS = nil 429 } 430 pod.Spec.DNSPolicy = tc.dnsPolicy 431 pod.Spec.HostNetwork = tc.hostNetwork 432 433 resType, err := getPodDNSType(pod) 434 if tc.expectedError { 435 if err == nil { 436 t.Errorf("%s: GetPodDNSType(%v) got no error, want error", tc.desc, pod) 437 } 438 return 439 } 440 if resType != tc.expectedDNSType { 441 t.Errorf("%s: GetPodDNSType(%v)=%v, want %v", tc.desc, pod, resType, tc.expectedDNSType) 442 } 443 }) 444 } 445 } 446 447 func TestGetPodDNS(t *testing.T) { 448 recorder := record.NewFakeRecorder(20) 449 nodeRef := &v1.ObjectReference{ 450 Kind: "Node", 451 Name: string("testNode"), 452 UID: types.UID("testNode"), 453 Namespace: "", 454 } 455 clusterNS := "203.0.113.1" 456 testClusterDNSDomain := "kubernetes.io" 457 testClusterDNS := []net.IP{netutils.ParseIPSloppy(clusterNS)} 458 459 configurer := NewConfigurer(recorder, nodeRef, nil, testClusterDNS, testClusterDNSDomain, "") 460 461 pods := newTestPods(4) 462 pods[0].Spec.DNSPolicy = v1.DNSClusterFirstWithHostNet 463 pods[1].Spec.DNSPolicy = v1.DNSClusterFirst 464 pods[2].Spec.DNSPolicy = v1.DNSClusterFirst 465 pods[2].Spec.HostNetwork = false 466 pods[3].Spec.DNSPolicy = v1.DNSDefault 467 468 options := make([]struct { 469 DNS []string 470 DNSSearch []string 471 }, 4) 472 for i, pod := range pods { 473 var err error 474 dnsConfig, err := configurer.GetPodDNS(pod) 475 if err != nil { 476 t.Fatalf("failed to generate container options: %v", err) 477 } 478 options[i].DNS, options[i].DNSSearch = dnsConfig.Servers, dnsConfig.Searches 479 } 480 if len(options[0].DNS) != 1 || options[0].DNS[0] != clusterNS { 481 t.Errorf("expected nameserver %s, got %+v", clusterNS, options[0].DNS) 482 } 483 if len(options[0].DNSSearch) == 0 || options[0].DNSSearch[0] != ".svc."+configurer.ClusterDomain { 484 t.Errorf("expected search %s, got %+v", ".svc."+configurer.ClusterDomain, options[0].DNSSearch) 485 } 486 if len(options[1].DNS) != 1 || options[1].DNS[0] != "127.0.0.1" { 487 t.Errorf("expected nameserver 127.0.0.1, got %+v", options[1].DNS) 488 } 489 if len(options[1].DNSSearch) != 1 || options[1].DNSSearch[0] != "." { 490 t.Errorf("expected search \".\", got %+v", options[1].DNSSearch) 491 } 492 if len(options[2].DNS) != 1 || options[2].DNS[0] != clusterNS { 493 t.Errorf("expected nameserver %s, got %+v", clusterNS, options[2].DNS) 494 } 495 if len(options[2].DNSSearch) == 0 || options[2].DNSSearch[0] != ".svc."+configurer.ClusterDomain { 496 t.Errorf("expected search %s, got %+v", ".svc."+configurer.ClusterDomain, options[2].DNSSearch) 497 } 498 if len(options[3].DNS) != 1 || options[3].DNS[0] != "127.0.0.1" { 499 t.Errorf("expected nameserver 127.0.0.1, got %+v", options[3].DNS) 500 } 501 if len(options[3].DNSSearch) != 1 || options[3].DNSSearch[0] != "." { 502 t.Errorf("expected search \".\", got %+v", options[3].DNSSearch) 503 } 504 505 configurer = NewConfigurer(recorder, nodeRef, nil, testClusterDNS, testClusterDNSDomain, defaultResolvConf) 506 configurer.getHostDNSConfig = fakeGetHostDNSConfigCustom 507 for i, pod := range pods { 508 var err error 509 dnsConfig, err := configurer.GetPodDNS(pod) 510 if err != nil { 511 t.Fatalf("failed to generate container options: %v", err) 512 } 513 options[i].DNS, options[i].DNSSearch = dnsConfig.Servers, dnsConfig.Searches 514 } 515 t.Logf("nameservers %+v", options[1].DNS) 516 if len(options[0].DNS) != 1 { 517 t.Errorf("expected cluster nameserver only, got %+v", options[0].DNS) 518 } else if options[0].DNS[0] != clusterNS { 519 t.Errorf("expected nameserver %s, got %v", clusterNS, options[0].DNS[0]) 520 } 521 expLength := len(options[1].DNSSearch) + 3 522 523 maxDNSSearchPaths := validation.MaxDNSSearchPaths 524 525 if expLength > maxDNSSearchPaths { 526 expLength = maxDNSSearchPaths 527 } 528 if len(options[0].DNSSearch) != expLength { 529 t.Errorf("expected prepend of cluster domain, got %+v", options[0].DNSSearch) 530 } else if options[0].DNSSearch[0] != ".svc."+configurer.ClusterDomain { 531 t.Errorf("expected domain %s, got %s", ".svc."+configurer.ClusterDomain, options[0].DNSSearch) 532 } 533 if len(options[2].DNS) != 1 { 534 t.Errorf("expected cluster nameserver only, got %+v", options[2].DNS) 535 } else if options[2].DNS[0] != clusterNS { 536 t.Errorf("expected nameserver %s, got %v", clusterNS, options[2].DNS[0]) 537 } 538 if len(options[2].DNSSearch) != expLength { 539 t.Errorf("expected prepend of cluster domain, got %+v", options[2].DNSSearch) 540 } else if options[2].DNSSearch[0] != ".svc."+configurer.ClusterDomain { 541 t.Errorf("expected domain %s, got %s", ".svc."+configurer.ClusterDomain, options[0].DNSSearch) 542 } 543 } 544 545 func TestGetPodDNSCustom(t *testing.T) { 546 recorder := record.NewFakeRecorder(20) 547 nodeRef := &v1.ObjectReference{ 548 Kind: "Node", 549 Name: string("testNode"), 550 UID: types.UID("testNode"), 551 Namespace: "", 552 } 553 554 testPodNamespace := "testNS" 555 testClusterNameserver := "10.0.0.10" 556 testClusterDNSDomain := "kubernetes.io" 557 testSvcDomain := fmt.Sprintf("svc.%s", testClusterDNSDomain) 558 testNsSvcDomain := fmt.Sprintf("%s.svc.%s", testPodNamespace, testClusterDNSDomain) 559 testNdotsOptionValue := "3" 560 561 testPod := &v1.Pod{ 562 ObjectMeta: metav1.ObjectMeta{ 563 Name: "test_pod", 564 Namespace: testPodNamespace, 565 }, 566 } 567 568 resolvConfContent := []byte(fmt.Sprintf("nameserver %s\nsearch %s\n", testHostNameserver, testHostDomain)) 569 tmpfile, err := os.CreateTemp("", "tmpResolvConf") 570 if err != nil { 571 t.Fatal(err) 572 } 573 defer os.Remove(tmpfile.Name()) 574 if _, err := tmpfile.Write(resolvConfContent); err != nil { 575 t.Fatal(err) 576 } 577 if err := tmpfile.Close(); err != nil { 578 t.Fatal(err) 579 } 580 581 configurer := NewConfigurer(recorder, nodeRef, nil, []net.IP{netutils.ParseIPSloppy(testClusterNameserver)}, testClusterDNSDomain, tmpfile.Name()) 582 583 testCases := []struct { 584 desc string 585 hostnetwork bool 586 dnsPolicy v1.DNSPolicy 587 dnsConfig *v1.PodDNSConfig 588 expectedDNSConfig *runtimeapi.DNSConfig 589 }{ 590 { 591 desc: "DNSNone without DNSConfig should have empty DNS settings", 592 dnsPolicy: v1.DNSNone, 593 expectedDNSConfig: &runtimeapi.DNSConfig{}, 594 }, 595 { 596 desc: "DNSNone with DNSConfig should have a merged DNS settings", 597 dnsPolicy: v1.DNSNone, 598 dnsConfig: &v1.PodDNSConfig{ 599 Nameservers: []string{"203.0.113.1"}, 600 Searches: []string{"my.domain", "second.domain"}, 601 Options: []v1.PodDNSConfigOption{ 602 {Name: "ndots", Value: &testNdotsOptionValue}, 603 {Name: "debug"}, 604 }, 605 }, 606 expectedDNSConfig: &runtimeapi.DNSConfig{ 607 Servers: []string{"203.0.113.1"}, 608 Searches: []string{"my.domain", "second.domain"}, 609 Options: []string{"ndots:3", "debug"}, 610 }, 611 }, 612 { 613 desc: "DNSClusterFirst with DNSConfig should have a merged DNS settings", 614 dnsPolicy: v1.DNSClusterFirst, 615 dnsConfig: &v1.PodDNSConfig{ 616 Nameservers: []string{"10.0.0.11"}, 617 Searches: []string{"my.domain"}, 618 Options: []v1.PodDNSConfigOption{ 619 {Name: "ndots", Value: &testNdotsOptionValue}, 620 {Name: "debug"}, 621 }, 622 }, 623 expectedDNSConfig: &runtimeapi.DNSConfig{ 624 Servers: []string{testClusterNameserver, "10.0.0.11"}, 625 Searches: []string{testNsSvcDomain, testSvcDomain, testClusterDNSDomain, testHostDomain, "my.domain"}, 626 Options: []string{"ndots:3", "debug"}, 627 }, 628 }, 629 { 630 desc: "DNSClusterFirstWithHostNet with DNSConfig should have a merged DNS settings", 631 hostnetwork: true, 632 dnsPolicy: v1.DNSClusterFirstWithHostNet, 633 dnsConfig: &v1.PodDNSConfig{ 634 Nameservers: []string{"10.0.0.11"}, 635 Searches: []string{"my.domain"}, 636 Options: []v1.PodDNSConfigOption{ 637 {Name: "ndots", Value: &testNdotsOptionValue}, 638 {Name: "debug"}, 639 }, 640 }, 641 expectedDNSConfig: &runtimeapi.DNSConfig{ 642 Servers: []string{testClusterNameserver, "10.0.0.11"}, 643 Searches: []string{testNsSvcDomain, testSvcDomain, testClusterDNSDomain, testHostDomain, "my.domain"}, 644 Options: []string{"ndots:3", "debug"}, 645 }, 646 }, 647 { 648 desc: "DNSDefault with DNSConfig should have a merged DNS settings", 649 dnsPolicy: v1.DNSDefault, 650 dnsConfig: &v1.PodDNSConfig{ 651 Nameservers: []string{"10.0.0.11"}, 652 Searches: []string{"my.domain"}, 653 Options: []v1.PodDNSConfigOption{ 654 {Name: "ndots", Value: &testNdotsOptionValue}, 655 {Name: "debug"}, 656 }, 657 }, 658 expectedDNSConfig: &runtimeapi.DNSConfig{ 659 Servers: []string{testHostNameserver, "10.0.0.11"}, 660 Searches: []string{testHostDomain, "my.domain"}, 661 Options: []string{"ndots:3", "debug"}, 662 }, 663 }, 664 } 665 666 for _, tc := range testCases { 667 t.Run(tc.desc, func(t *testing.T) { 668 testPod.Spec.HostNetwork = tc.hostnetwork 669 testPod.Spec.DNSConfig = tc.dnsConfig 670 testPod.Spec.DNSPolicy = tc.dnsPolicy 671 672 resDNSConfig, err := configurer.GetPodDNS(testPod) 673 if err != nil { 674 t.Errorf("%s: GetPodDNS(%v), unexpected error: %v", tc.desc, testPod, err) 675 } 676 if !dnsConfigsAreEqual(resDNSConfig, tc.expectedDNSConfig) { 677 t.Errorf("%s: GetPodDNS(%v)=%v, want %v", tc.desc, testPod, resDNSConfig, tc.expectedDNSConfig) 678 } 679 }) 680 } 681 } 682 683 func dnsConfigsAreEqual(resConfig, expectedConfig *runtimeapi.DNSConfig) bool { 684 if len(resConfig.Servers) != len(expectedConfig.Servers) || 685 len(resConfig.Searches) != len(expectedConfig.Searches) || 686 len(resConfig.Options) != len(expectedConfig.Options) { 687 return false 688 } 689 for i, server := range resConfig.Servers { 690 if expectedConfig.Servers[i] != server { 691 return false 692 } 693 } 694 for i, search := range resConfig.Searches { 695 if expectedConfig.Searches[i] != search { 696 return false 697 } 698 } 699 // Options order may be changed after conversion. 700 return sets.NewString(resConfig.Options...).Equal(sets.NewString(expectedConfig.Options...)) 701 } 702 703 func newTestPods(count int) []*v1.Pod { 704 pods := make([]*v1.Pod, count) 705 for i := 0; i < count; i++ { 706 pods[i] = &v1.Pod{ 707 Spec: v1.PodSpec{ 708 HostNetwork: true, 709 }, 710 ObjectMeta: metav1.ObjectMeta{ 711 UID: types.UID(strconv.Itoa(10000 + i)), 712 Name: fmt.Sprintf("pod%d", i), 713 }, 714 } 715 } 716 return pods 717 }