1
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
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},
70 {"#comment\nnameserver\nsearch", []string{}, []string{}, []string{}, true},
71 {"#comment\nnameserver 1.2.3.4\nsearch", []string{"1.2.3.4"}, []string{}, []string{}, false},
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},
80 {"search .", []string{}, []string{}, []string{}, false},
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
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
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
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 }
718
View as plain text