...

Source file src/k8s.io/kubernetes/pkg/kubelet/network/dns/dns_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/network/dns

     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  }
   718  

View as plain text