1
16
17 package node
18
19 import (
20 "context"
21 "strings"
22 "time"
23
24 "github.com/onsi/ginkgo/v2"
25 v1 "k8s.io/api/core/v1"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/klog/v2"
28 "k8s.io/kubernetes/test/e2e/framework"
29 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
30 admissionapi "k8s.io/pod-security-admission/api"
31 )
32
33 const (
34 etcHostsPodName = "test-pod"
35 etcHostsHostNetworkPodName = "test-host-network-pod"
36 etcHostsPartialContent = "# Kubernetes-managed hosts file."
37 etcHostsPath = "/etc/hosts"
38 etcHostsOriginalPath = "/etc/hosts-original"
39 )
40
41
42 type KubeletManagedHostConfig struct {
43 hostNetworkPod *v1.Pod
44 pod *v1.Pod
45 f *framework.Framework
46 }
47
48 var _ = SIGDescribe("KubeletManagedEtcHosts", func() {
49 f := framework.NewDefaultFramework("e2e-kubelet-etc-hosts")
50 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
51 config := &KubeletManagedHostConfig{
52 f: f,
53 }
54
55
64 framework.ConformanceIt("should test kubelet managed /etc/hosts file [LinuxOnly]", f.WithNodeConformance(), func(ctx context.Context) {
65 ginkgo.By("Setting up the test")
66 config.setup(ctx)
67
68 ginkgo.By("Running the test")
69 config.verifyEtcHosts()
70 })
71 })
72
73 func (config *KubeletManagedHostConfig) verifyEtcHosts() {
74 ginkgo.By("Verifying /etc/hosts of container is kubelet-managed for pod with hostNetwork=false")
75 assertManagedStatus(config, etcHostsPodName, true, "busybox-1")
76 assertManagedStatus(config, etcHostsPodName, true, "busybox-2")
77
78 ginkgo.By("Verifying /etc/hosts of container is not kubelet-managed since container specifies /etc/hosts mount")
79 assertManagedStatus(config, etcHostsPodName, false, "busybox-3")
80
81 ginkgo.By("Verifying /etc/hosts content of container is not kubelet-managed for pod with hostNetwork=true")
82 assertManagedStatus(config, etcHostsHostNetworkPodName, false, "busybox-1")
83 assertManagedStatus(config, etcHostsHostNetworkPodName, false, "busybox-2")
84 }
85
86 func (config *KubeletManagedHostConfig) setup(ctx context.Context) {
87 ginkgo.By("Creating hostNetwork=false pod")
88 config.createPodWithoutHostNetwork(ctx)
89
90 ginkgo.By("Creating hostNetwork=true pod")
91 config.createPodWithHostNetwork(ctx)
92 }
93
94 func (config *KubeletManagedHostConfig) createPodWithoutHostNetwork(ctx context.Context) {
95 podSpec := config.createPodSpec(etcHostsPodName)
96 config.pod = e2epod.NewPodClient(config.f).CreateSync(ctx, podSpec)
97 }
98
99 func (config *KubeletManagedHostConfig) createPodWithHostNetwork(ctx context.Context) {
100 podSpec := config.createPodSpecWithHostNetwork(etcHostsHostNetworkPodName)
101 config.hostNetworkPod = e2epod.NewPodClient(config.f).CreateSync(ctx, podSpec)
102 }
103
104 func assertManagedStatus(
105 config *KubeletManagedHostConfig, podName string, expectedIsManaged bool, name string) {
106
107
108
109
110
111 const retryTimeout = 30 * time.Second
112
113 retryCount := 0
114 etcHostsContent := ""
115
116 for startTime := time.Now(); time.Since(startTime) < retryTimeout; {
117 etcHostsContent = config.getFileContents(podName, name, etcHostsPath)
118 etcHostsOriginalContent := config.getFileContents(podName, name, etcHostsOriginalPath)
119
120
121 if len(etcHostsContent) > 0 && len(etcHostsOriginalContent) > 0 {
122
123
124
125 isManaged := strings.HasPrefix(etcHostsContent, etcHostsPartialContent) &&
126 etcHostsContent != etcHostsOriginalContent
127 if expectedIsManaged == isManaged {
128 return
129 }
130 }
131
132 klog.Warningf(
133 "For pod: %s, name: %s, expected %t, (/etc/hosts was %q), (/etc/hosts-original was %q), retryCount: %d",
134 podName, name, expectedIsManaged, etcHostsContent, etcHostsOriginalContent, retryCount)
135
136 retryCount++
137 time.Sleep(100 * time.Millisecond)
138 }
139
140 if expectedIsManaged {
141 framework.Failf(
142 "/etc/hosts file should be kubelet managed (name: %s, retries: %d). /etc/hosts contains %q",
143 name, retryCount, etcHostsContent)
144 } else {
145 framework.Failf(
146 "/etc/hosts file should no be kubelet managed (name: %s, retries: %d). /etc/hosts contains %q",
147 name, retryCount, etcHostsContent)
148 }
149 }
150
151 func (config *KubeletManagedHostConfig) getFileContents(podName, containerName, path string) string {
152 return e2epod.ExecCommandInContainer(config.f, podName, containerName, "cat", path)
153 }
154
155 func (config *KubeletManagedHostConfig) createPodSpec(podName string) *v1.Pod {
156 hostPathType := new(v1.HostPathType)
157 *hostPathType = v1.HostPathType(string(v1.HostPathFileOrCreate))
158 mounts := []v1.VolumeMount{
159 {
160 Name: "host-etc-hosts",
161 MountPath: etcHostsOriginalPath,
162 },
163 }
164 multipleMounts := []v1.VolumeMount{
165 mounts[0],
166 {
167 Name: "host-etc-hosts",
168 MountPath: etcHostsPath,
169 },
170 }
171 pod := &v1.Pod{
172 ObjectMeta: metav1.ObjectMeta{
173 Name: podName,
174 },
175 Spec: v1.PodSpec{
176 Containers: []v1.Container{
177 e2epod.NewAgnhostContainer("busybox-1", mounts, nil),
178 e2epod.NewAgnhostContainer("busybox-2", mounts, nil),
179 e2epod.NewAgnhostContainer("busybox-3", multipleMounts, nil),
180 },
181 Volumes: []v1.Volume{
182 {
183 Name: "host-etc-hosts",
184 VolumeSource: v1.VolumeSource{
185 HostPath: &v1.HostPathVolumeSource{
186 Path: etcHostsPath,
187 Type: hostPathType,
188 },
189 },
190 },
191 },
192 },
193 }
194
195 return pod
196 }
197
198 func (config *KubeletManagedHostConfig) createPodSpecWithHostNetwork(podName string) *v1.Pod {
199 hostPathType := new(v1.HostPathType)
200 *hostPathType = v1.HostPathType(string(v1.HostPathFileOrCreate))
201 mounts := []v1.VolumeMount{
202 {
203 Name: "host-etc-hosts",
204 MountPath: etcHostsOriginalPath,
205 },
206 }
207 pod := &v1.Pod{
208 ObjectMeta: metav1.ObjectMeta{
209 Name: podName,
210 },
211 Spec: v1.PodSpec{
212 HostNetwork: true,
213 SecurityContext: &v1.PodSecurityContext{},
214 Containers: []v1.Container{
215 e2epod.NewAgnhostContainer("busybox-1", mounts, nil),
216 e2epod.NewAgnhostContainer("busybox-2", mounts, nil),
217 },
218 Volumes: []v1.Volume{
219 {
220 Name: "host-etc-hosts",
221 VolumeSource: v1.VolumeSource{
222 HostPath: &v1.HostPathVolumeSource{
223 Path: etcHostsPath,
224 Type: hostPathType,
225 },
226 },
227 },
228 },
229 },
230 }
231 return pod
232 }
233
View as plain text