From c68bc27f7399274a4a5f7b17e522d6488e1d2d6e Mon Sep 17 00:00:00 2001 From: Claudiu Belu Date: Tue, 21 Mar 2023 09:37:07 +0000 Subject: [PATCH] kubelet: Read DNS Config options from file for Windows A previous commit added the capability to read the DNS configuration options from a Windows host, while removing the capability to read from a resolv.conf-like file. This commit addresses this issue: if the given ``--resolv-conf`` option is not set to ``Host``, it will consider it as a file, preserving the previous behavior. --- pkg/kubelet/network/dns/dns.go | 27 +++++++++ pkg/kubelet/network/dns/dns_other.go | 34 +---------- pkg/kubelet/network/dns/dns_other_test.go | 31 ---------- pkg/kubelet/network/dns/dns_test.go | 19 +++++-- pkg/kubelet/network/dns/dns_windows.go | 63 ++++++++++++++------- pkg/kubelet/network/dns/dns_windows_test.go | 7 --- 6 files changed, 88 insertions(+), 93 deletions(-) diff --git a/pkg/kubelet/network/dns/dns.go b/pkg/kubelet/network/dns/dns.go index d08c7a6cf2b..0bb5ab1bb48 100644 --- a/pkg/kubelet/network/dns/dns.go +++ b/pkg/kubelet/network/dns/dns.go @@ -281,6 +281,33 @@ func parseResolvConf(reader io.Reader) (nameservers []string, searches []string, return nameservers, searches, options, utilerrors.NewAggregate(allErrors) } +// Reads a resolv.conf-like file and returns the DNS config options from it. +// Returns an empty DNSConfig if the given resolverConfigFile is an empty string. +func getDNSConfig(resolverConfigFile string) (*runtimeapi.DNSConfig, error) { + var hostDNS, hostSearch, hostOptions []string + // Get host DNS settings + if resolverConfigFile != "" { + f, err := os.Open(resolverConfigFile) + if err != nil { + klog.ErrorS(err, "Could not open resolv conf file.") + return nil, err + } + defer f.Close() + + hostDNS, hostSearch, hostOptions, err = parseResolvConf(f) + if err != nil { + err := fmt.Errorf("Encountered error while parsing resolv conf file. Error: %w", err) + klog.ErrorS(err, "Could not parse resolv conf file.") + return nil, err + } + } + return &runtimeapi.DNSConfig{ + Servers: hostDNS, + Searches: hostSearch, + Options: hostOptions, + }, nil +} + func getPodDNSType(pod *v1.Pod) (podDNSType, error) { dnsPolicy := pod.Spec.DNSPolicy switch dnsPolicy { diff --git a/pkg/kubelet/network/dns/dns_other.go b/pkg/kubelet/network/dns/dns_other.go index 40c18c289e2..8d5f5caa7d3 100644 --- a/pkg/kubelet/network/dns/dns_other.go +++ b/pkg/kubelet/network/dns/dns_other.go @@ -19,35 +19,5 @@ limitations under the License. package dns -import ( - "fmt" - "os" - - runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" - "k8s.io/klog/v2" -) - -func getHostDNSConfig(resolverConfig string) (*runtimeapi.DNSConfig, error) { - var hostDNS, hostSearch, hostOptions []string - // Get host DNS settings - if resolverConfig != "" { - f, err := os.Open(resolverConfig) - if err != nil { - klog.ErrorS(err, "Could not open resolv conf file.") - return nil, err - } - defer f.Close() - - hostDNS, hostSearch, hostOptions, err = parseResolvConf(f) - if err != nil { - err := fmt.Errorf("Encountered error while parsing resolv conf file. Error: %w", err) - klog.ErrorS(err, "Could not parse resolv conf file.") - return nil, err - } - } - return &runtimeapi.DNSConfig{ - Servers: hostDNS, - Searches: hostSearch, - Options: hostOptions, - }, nil -} +// Read the DNS configuration from a resolv.conf file. +var getHostDNSConfig = getDNSConfig diff --git a/pkg/kubelet/network/dns/dns_other_test.go b/pkg/kubelet/network/dns/dns_other_test.go index daa615ee133..a2440558334 100644 --- a/pkg/kubelet/network/dns/dns_other_test.go +++ b/pkg/kubelet/network/dns/dns_other_test.go @@ -19,39 +19,8 @@ limitations under the License. package dns -import ( - "fmt" - "os" - "testing" -) - var ( defaultResolvConf = "/etc/resolv.conf" // configurer.getHostDNSConfig is faked on Windows, while it is not faked on Linux. fakeGetHostDNSConfigCustom = getHostDNSConfig ) - -// getResolvConf returns a temporary resolv.conf file containing the testHostNameserver nameserver and -// testHostDomain search field, and a cleanup function for the temporary file. -func getResolvConf(t *testing.T) (string, func()) { - resolvConfContent := []byte(fmt.Sprintf("nameserver %s\nsearch %s\n", testHostNameserver, testHostDomain)) - tmpfile, err := os.CreateTemp("", "tmpResolvConf") - if err != nil { - t.Fatal(err) - } - - cleanup := func() { - os.Remove(tmpfile.Name()) - } - - if _, err := tmpfile.Write(resolvConfContent); err != nil { - cleanup() - t.Fatal(err) - } - if err := tmpfile.Close(); err != nil { - cleanup() - t.Fatal(err) - } - - return tmpfile.Name(), cleanup -} diff --git a/pkg/kubelet/network/dns/dns_test.go b/pkg/kubelet/network/dns/dns_test.go index 7f279624c4d..c74fb4e307e 100644 --- a/pkg/kubelet/network/dns/dns_test.go +++ b/pkg/kubelet/network/dns/dns_test.go @@ -19,6 +19,7 @@ package dns import ( "fmt" "net" + "os" "strconv" "strings" "testing" @@ -536,6 +537,7 @@ func testGetPodDNS(t *testing.T) { } configurer = NewConfigurer(recorder, nodeRef, nil, testClusterDNS, testClusterDNSDomain, defaultResolvConf) + configurer.getHostDNSConfig = fakeGetHostDNSConfigCustom for i, pod := range pods { var err error dnsConfig, err := configurer.GetPodDNS(pod) @@ -600,11 +602,20 @@ func TestGetPodDNSCustom(t *testing.T) { }, } - resolvConf, cleanup := getResolvConf(t) - defer cleanup() + resolvConfContent := []byte(fmt.Sprintf("nameserver %s\nsearch %s\n", testHostNameserver, testHostDomain)) + tmpfile, err := os.CreateTemp("", "tmpResolvConf") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpfile.Name()) + if _, err := tmpfile.Write(resolvConfContent); err != nil { + t.Fatal(err) + } + if err := tmpfile.Close(); err != nil { + t.Fatal(err) + } - configurer := NewConfigurer(recorder, nodeRef, nil, []net.IP{netutils.ParseIPSloppy(testClusterNameserver)}, testClusterDNSDomain, resolvConf) - configurer.getHostDNSConfig = fakeGetHostDNSConfigCustom + configurer := NewConfigurer(recorder, nodeRef, nil, []net.IP{netutils.ParseIPSloppy(testClusterNameserver)}, testClusterDNSDomain, tmpfile.Name()) testCases := []struct { desc string diff --git a/pkg/kubelet/network/dns/dns_windows.go b/pkg/kubelet/network/dns/dns_windows.go index 052c4e0eaee..554238790f0 100644 --- a/pkg/kubelet/network/dns/dns_windows.go +++ b/pkg/kubelet/network/dns/dns_windows.go @@ -21,6 +21,7 @@ package dns import ( "fmt" + "os" "strings" "syscall" "unsafe" @@ -63,31 +64,55 @@ var ( procGetNetworkParams = iphlpapidll.MustFindProc("GetNetworkParams") ) +func fileExists(filename string) (bool, error) { + stat, err := os.Stat(filename) + if os.IsNotExist(err) { + return false, nil + } + if err != nil { + return false, err + } + + return stat.Mode().IsRegular(), nil +} + func getHostDNSConfig(resolverConfig string) (*runtimeapi.DNSConfig, error) { - if resolverConfig != "" && resolverConfig != hostResolvConf { - err := fmt.Errorf(`Unexpected resolver config value: "%s". Expected "" or "%s".`, resolverConfig, hostResolvConf) + if resolverConfig == "" { + // This handles "" by returning defaults. + return getDNSConfig(resolverConfig) + } + + isFile, err := fileExists(resolverConfig) + if err != nil { + err = fmt.Errorf(`Unexpected error while getting os.Stat for "%s" resolver config. Error: %w`, resolverConfig, err) + klog.ErrorS(err, "Cannot get host DNS Configuration.") + return nil, err + } + if isFile { + // Get the DNS config from a resolv.conf-like file. + return getDNSConfig(resolverConfig) + } + + if resolverConfig != hostResolvConf { + err := fmt.Errorf(`Unexpected resolver config value: "%s". Expected "", "%s", or a path to an existing resolv.conf file.`, resolverConfig, hostResolvConf) klog.ErrorS(err, "Cannot get host DNS Configuration.") return nil, err } - var ( - hostDNS, hostSearch []string - err error - ) + // If we get here, the resolverConfig == hostResolvConf and that is not actually a file, so + // it means to use the host settings. // Get host DNS settings - if resolverConfig == hostResolvConf { - hostDNS, err = getDNSServerList() - if err != nil { - err = fmt.Errorf("Could not get the host's DNS Server List. Error: %w", err) - klog.ErrorS(err, "Encountered error while getting host's DNS Server List.") - return nil, err - } - hostSearch, err = getDNSSuffixList() - if err != nil { - err = fmt.Errorf("Could not get the host's DNS Suffix List. Error: %w", err) - klog.ErrorS(err, "Encountered error while getting host's DNS Suffix List.") - return nil, err - } + hostDNS, err := getDNSServerList() + if err != nil { + err = fmt.Errorf("Could not get the host's DNS Server List. Error: %w", err) + klog.ErrorS(err, "Encountered error while getting host's DNS Server List.") + return nil, err + } + hostSearch, err := getDNSSuffixList() + if err != nil { + err = fmt.Errorf("Could not get the host's DNS Suffix List. Error: %w", err) + klog.ErrorS(err, "Encountered error while getting host's DNS Suffix List.") + return nil, err } return &runtimeapi.DNSConfig{ Servers: hostDNS, diff --git a/pkg/kubelet/network/dns/dns_windows_test.go b/pkg/kubelet/network/dns/dns_windows_test.go index 5a415c02c5a..5252047199f 100644 --- a/pkg/kubelet/network/dns/dns_windows_test.go +++ b/pkg/kubelet/network/dns/dns_windows_test.go @@ -20,8 +20,6 @@ limitations under the License. package dns import ( - "testing" - runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" ) @@ -35,8 +33,3 @@ func fakeGetHostDNSConfigCustom(resolverConfig string) (*runtimeapi.DNSConfig, e Searches: []string{testHostDomain}, }, nil } - -// getResolvConf returns the hostResolvConf string, which will be used to get the Host's DNS configuration. -func getResolvConf(t *testing.T) (string, func()) { - return hostResolvConf, func() {} -}