Change:
When a --resolv-conf (DNS resolver config) is passed to kubelet on a Windows node, the config content is used as part of sandbox initialization (through getHostDNSConfig()). Now with the recent changes (), it seems now to fail with an error like: E0315 10:22:16. 4764 ] "Error syncing pod, skipping" err="failed to "CreatePodSandbox" for "img-pull-(-b40a-459d-9033-)" with CreatePodSandboxError: "Failed to generate sandbox config for pod "img-pull-(-b40a-459d-9033-)": Unexpected resolver config value: "C:etckubernetescni". Expected "" or "Host"."" pod="img-puller-3842/img-pull-" podUID=-b40a-459d-9033- This is showing on all the runs of GCE testgrid (latest): Reading/using the passed resolv config on Windows Run a Windows pool with DNS resolv config being set as part of kubelet params. No response_ 27 alpha.3 1.27 beta.0 master <detailsGCE </details<details</details<details</details<details</details<details</details
/triage accepted /milestone v1.27 /sig node /sig network
/sig windows /cc
/priority criticial-urgent as this is likely a release blocking
The label(s) cannot be applied, because the repository doesn't have them. <detailsIn response to : /priority criticial-urgent Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/priority critical-urgent
Hmm, so you're setting the [1]. This PR introduced the capability to read the DNS config options from the Windows host registries while removing the capability to read from a file, introducing a regression. I've sent a PR addressing this issue: [1]
Is the file you are pointing to a UNIX-style or something else?
Was the file changing the behavior of the node?
Yeah, what used on GCE/GKE, seems to be a Linux-like conf, eg: nameserver 10.128.0.1 search c.<project_name.internal google.internal ` We haven't tried to remove from kubelet to see the behavior.
This Issue/PR has been marked as release blocker. Please have the fix merged by eod today otherwise the feature has to be reverted. For the Issue/PR owner: please have a revert/feature disable PR open today in case the fix failed to get in. Thank you! See the -- Kubernetes 1.27 release manager
I opened up a revert PR in case this PR does not merge by code freeze.
Thanks for the resolution! both 2019 and 2022 grids seems to be back to green now!
Thanks for confirming! I opened to ensure gets updated too

Discussion:
return nameservers, searches, options, utilerrors.NewAggregate(allErrors) } <ins> // 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 } </ins> func getPodDNSType(pod *v1.Pod) (podDNSType, error) { dnsPolicy := pod.Spec.DNSPolicy switch dnsPolicy {

package dns <del> 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 } </del> <ins> // Read the DNS configuration from a resolv.conf file. var getHostDNSConfig = getDNSConfig </ins>

package dns <del> import ( "fmt" "os" "testing" ) </del> var ( defaultResolvConf = "/etc/resolv.conf" // configurer.getHostDNSConfig is faked on Windows, while it is not faked on Linux. fakeGetHostDNSConfigCustom = getHostDNSConfig ) <del> // 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 %snsearch %sn", 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 } </del>

import ( "fmt" "net" <ins> "os" </ins> "strconv" "strings" "testing"

} configurer = NewConfigurer(recorder, nodeRef, nil, testClusterDNS, testClusterDNSDomain, defaultResolvConf) <ins> configurer.getHostDNSConfig = fakeGetHostDNSConfigCustom </ins> for i, pod := range pods { var err error dnsConfig, err := configurer.GetPodDNS(pod)

}, } <del> resolvConf, cleanup := getResolvConf(t) defer cleanup() </del> <ins> resolvConfContent := []byte(fmt.Sprintf("nameserver %snsearch %sn", 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) } </ins> <del> configurer := NewConfigurer(recorder, nodeRef, nil, []net.IP{netutils.ParseIPSloppy(testClusterNameserver)}, testClusterDNSDomain, resolvConf) configurer.getHostDNSConfig = fakeGetHostDNSConfigCustom </del> <ins> configurer := NewConfigurer(recorder, nodeRef, nil, []net.IP{netutils.ParseIPSloppy(testClusterNameserver)}, testClusterDNSDomain, tmpfile.Name()) </ins> testCases := []struct { desc              string

import ( "fmt" <ins> "os" </ins> "strings" "syscall" "unsafe"

procGetNetworkParams = iphlpapidll.MustFindProc("GetNetworkParams") ) <ins> 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 } </ins> func getHostDNSConfig(resolverConfig string) (*runtimeapi.DNSConfig, error) { <del> if resolverConfig != "" && resolverConfig != hostResolvConf { err := fmt.Errorf(`Unexpected resolver config value: "%s". Expected "" or "%s".`, resolverConfig, hostResolvConf) </del> <ins> 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) </ins> klog.ErrorS(err, "Cannot get host DNS Configuration.") return nil, err } <ins> if isFile { // Get the DNS config from a resolv.conf-like file. return getDNSConfig(resolverConfig) } </ins> <del> var ( hostDNS, hostSearch []string err                 error ) </del> <ins> 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 } // If we get here, the resolverConfig == hostResolvConf and that is not actually a file, so // it means to use the host settings. </ins> // Get host DNS settings <del> 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 } </del> <ins> 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 </ins> } return &runtimeapi.DNSConfig{ Servers:  hostDNS,

package dns import ( <del> "testing" </del> runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" )

Searches: []string{testHostDomain}, }, nil } <del> // 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() {} } </del>