diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index 76f59f3b3c6..7c58c3e5e9b 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -68,6 +68,11 @@ import ( "k8s.io/kubernetes/third_party/forked/golang/expansion" ) +const ( + managedHostsHeader = "# Kubernetes-managed hosts file.\n" + managedHostsHeaderWithHostNetwork = "# Kubernetes-managed hosts file (host network).\n" +) + // Get a list of pods that have data directories. func (kl *Kubelet) listPodsFromDisk() ([]types.UID, error) { podInfos, err := ioutil.ReadDir(kl.getPodsDir()) @@ -356,15 +361,18 @@ func nodeHostsFileContent(hostsFilePath string, hostAliases []v1.HostAlias) ([]b if err != nil { return nil, err } - hostsFileContent = append(hostsFileContent, hostsEntriesFromHostAliases(hostAliases)...) - return hostsFileContent, nil + var buffer bytes.Buffer + buffer.WriteString(managedHostsHeaderWithHostNetwork) + buffer.Write(hostsFileContent) + buffer.Write(hostsEntriesFromHostAliases(hostAliases)) + return buffer.Bytes(), nil } // managedHostsFileContent generates the content of the managed etc hosts based on Pod IP and other // information. func managedHostsFileContent(hostIP, hostName, hostDomainName string, hostAliases []v1.HostAlias) []byte { var buffer bytes.Buffer - buffer.WriteString("# Kubernetes-managed hosts file.\n") + buffer.WriteString(managedHostsHeader) buffer.WriteString("127.0.0.1\tlocalhost\n") // ipv4 localhost buffer.WriteString("::1\tlocalhost ip6-localhost ip6-loopback\n") // ipv6 localhost buffer.WriteString("fe00::0\tip6-localnet\n") @@ -376,9 +384,8 @@ func managedHostsFileContent(hostIP, hostName, hostDomainName string, hostAliase } else { buffer.WriteString(fmt.Sprintf("%s\t%s\n", hostIP, hostName)) } - hostsFileContent := buffer.Bytes() - hostsFileContent = append(hostsFileContent, hostsEntriesFromHostAliases(hostAliases)...) - return hostsFileContent + buffer.Write(hostsEntriesFromHostAliases(hostAliases)) + return buffer.Bytes() } func hostsEntriesFromHostAliases(hostAliases []v1.HostAlias) []byte { diff --git a/pkg/kubelet/kubelet_pods_test.go b/pkg/kubelet/kubelet_pods_test.go index 92552f5227b..47b8b3e760e 100644 --- a/pkg/kubelet/kubelet_pods_test.go +++ b/pkg/kubelet/kubelet_pods_test.go @@ -563,7 +563,8 @@ fe00::1 ip6-allnodes fe00::2 ip6-allrouters 123.45.67.89 some.domain `, - `# hosts file for testing. + `# Kubernetes-managed hosts file (host network). +# hosts file for testing. 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet @@ -585,7 +586,8 @@ fe00::1 ip6-allnodes fe00::2 ip6-allrouters 12.34.56.78 another.domain `, - `# another hosts file for testing. + `# Kubernetes-managed hosts file (host network). +# another hosts file for testing. 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet @@ -609,7 +611,8 @@ fe00::1 ip6-allnodes fe00::2 ip6-allrouters 123.45.67.89 some.domain `, - `# hosts file for testing. + `# Kubernetes-managed hosts file (host network). +# hosts file for testing. 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet @@ -639,7 +642,8 @@ fe00::1 ip6-allnodes fe00::2 ip6-allrouters 12.34.56.78 another.domain `, - `# another hosts file for testing. + `# Kubernetes-managed hosts file (host network). +# another hosts file for testing. 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet diff --git a/test/e2e/common/kubelet_etc_hosts.go b/test/e2e/common/kubelet_etc_hosts.go index bcce2375a4c..d9d4bdbb3a1 100644 --- a/test/e2e/common/kubelet_etc_hosts.go +++ b/test/e2e/common/kubelet_etc_hosts.go @@ -17,8 +17,6 @@ limitations under the License. package common import ( - "io/ioutil" - "os" "strings" "time" @@ -34,6 +32,8 @@ const ( etcHostsPodName = "test-pod" etcHostsHostNetworkPodName = "test-host-network-pod" etcHostsPartialContent = "# Kubernetes-managed hosts file." + etcHostsPath = "/etc/hosts" + etcHostsOriginalPath = "/etc/hosts-original" ) var etcHostsImageName = imageutils.GetE2EImage(imageutils.Netexec) @@ -42,7 +42,6 @@ type KubeletManagedHostConfig struct { hostNetworkPod *v1.Pod pod *v1.Pod f *framework.Framework - tmpEtcHostFile *os.File } var _ = framework.KubeDescribe("KubeletManagedEtcHosts", func() { @@ -62,8 +61,6 @@ var _ = framework.KubeDescribe("KubeletManagedEtcHosts", func() { By("Running the test") config.verifyEtcHosts() - - config.cleanup() }) }) @@ -81,26 +78,6 @@ func (config *KubeletManagedHostConfig) verifyEtcHosts() { } func (config *KubeletManagedHostConfig) setup() { - etcHostContents := `127.0.0.1 localhost -::1 localhost ip6-localhost ip6-loopback -fe00::0 ip6-localnet -ff00::0 ip6-mcastprefix -ff02::1 ip6-allnodes -ff02::2 ip6-allrouters` - - // Write the data to a temp file. - var err error - config.tmpEtcHostFile, err = ioutil.TempFile("", "etc-hosts") - if err != nil { - framework.Failf("failed to create temp file for /etc/hosts: %v", err) - } - if _, err := config.tmpEtcHostFile.Write([]byte(etcHostContents)); err != nil { - framework.Failf("Failed to write temp file for /etc/hosts data: %v", err) - } - if err := config.tmpEtcHostFile.Close(); err != nil { - framework.Failf("Failed to close temp file: %v", err) - } - By("Creating hostNetwork=false pod") config.createPodWithoutHostNetwork() @@ -108,12 +85,6 @@ ff02::2 ip6-allrouters` config.createPodWithHostNetwork() } -func (config *KubeletManagedHostConfig) cleanup() { - if config.tmpEtcHostFile != nil { - os.Remove(config.tmpEtcHostFile.Name()) - } -} - func (config *KubeletManagedHostConfig) createPodWithoutHostNetwork() { podSpec := config.createPodSpec(etcHostsPodName) config.pod = config.f.PodClient().CreateSync(podSpec) @@ -137,16 +108,24 @@ func assertManagedStatus( etcHostsContent := "" for startTime := time.Now(); time.Since(startTime) < retryTimeout; { - etcHostsContent = config.getEtcHostsContent(podName, name) - isManaged := strings.Contains(etcHostsContent, etcHostsPartialContent) + etcHostsContent = config.getFileContents(podName, name, etcHostsPath) + etcHostsOriginalContent := config.getFileContents(podName, name, etcHostsOriginalPath) - if expectedIsManaged == isManaged { - return + // Make sure there is some content in both files + if len(etcHostsContent) > 0 && len(etcHostsOriginalContent) > 0 { + // if the files match, kubernetes did not touch the file at all + // if the file has the header, kubernetes is not using host network + // and is constructing the file based on Pod IP + isManaged := strings.HasPrefix(etcHostsContent, etcHostsPartialContent) && + etcHostsContent != etcHostsOriginalContent + if expectedIsManaged == isManaged { + return + } } glog.Warningf( - "For pod: %s, name: %s, expected %t, actual %t (/etc/hosts was %q), retryCount: %d", - podName, name, expectedIsManaged, isManaged, etcHostsContent, retryCount) + "For pod: %s, name: %s, expected %t, (/etc/hosts was %q), (/etc/hosts-original was %q), retryCount: %d", + podName, name, expectedIsManaged, etcHostsContent, etcHostsOriginalContent, retryCount) retryCount++ time.Sleep(100 * time.Millisecond) @@ -163,8 +142,8 @@ func assertManagedStatus( } } -func (config *KubeletManagedHostConfig) getEtcHostsContent(podName, containerName string) string { - return config.f.ExecCommandInContainer(podName, containerName, "cat", "/etc/hosts") +func (config *KubeletManagedHostConfig) getFileContents(podName, containerName, path string) string { + return config.f.ExecCommandInContainer(podName, containerName, "cat", path) } func (config *KubeletManagedHostConfig) createPodSpec(podName string) *v1.Pod { @@ -184,6 +163,12 @@ func (config *KubeletManagedHostConfig) createPodSpec(podName string) *v1.Pod { "sleep", "900", }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "host-etc-hosts", + MountPath: etcHostsOriginalPath, + }, + }, }, { Name: "busybox-2", @@ -193,6 +178,12 @@ func (config *KubeletManagedHostConfig) createPodSpec(podName string) *v1.Pod { "sleep", "900", }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "host-etc-hosts", + MountPath: etcHostsOriginalPath, + }, + }, }, { Name: "busybox-3", @@ -205,7 +196,11 @@ func (config *KubeletManagedHostConfig) createPodSpec(podName string) *v1.Pod { VolumeMounts: []v1.VolumeMount{ { Name: "host-etc-hosts", - MountPath: "/etc/hosts", + MountPath: etcHostsPath, + }, + { + Name: "host-etc-hosts", + MountPath: etcHostsOriginalPath, }, }, }, @@ -215,7 +210,7 @@ func (config *KubeletManagedHostConfig) createPodSpec(podName string) *v1.Pod { Name: "host-etc-hosts", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ - Path: config.tmpEtcHostFile.Name(), + Path: etcHostsPath, Type: hostPathType, }, }, @@ -227,6 +222,8 @@ func (config *KubeletManagedHostConfig) createPodSpec(podName string) *v1.Pod { } func (config *KubeletManagedHostConfig) createPodSpecWithHostNetwork(podName string) *v1.Pod { + hostPathType := new(v1.HostPathType) + *hostPathType = v1.HostPathType(string(v1.HostPathFileOrCreate)) pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName, @@ -243,6 +240,12 @@ func (config *KubeletManagedHostConfig) createPodSpecWithHostNetwork(podName str "sleep", "900", }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "host-etc-hosts", + MountPath: etcHostsOriginalPath, + }, + }, }, { Name: "busybox-2", @@ -252,6 +255,23 @@ func (config *KubeletManagedHostConfig) createPodSpecWithHostNetwork(podName str "sleep", "900", }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "host-etc-hosts", + MountPath: etcHostsOriginalPath, + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "host-etc-hosts", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: etcHostsPath, + Type: hostPathType, + }, + }, }, }, },