From 87123c49a002564ebdb59c4ee87d9ad879e198f3 Mon Sep 17 00:00:00 2001 From: Claudiu Belu Date: Wed, 17 Nov 2021 02:25:35 -0800 Subject: [PATCH] tests: Enables a few Conformance tests for Windows Some of these tests could not be run previously, especially on Windows Docker containers. But now, by using Windows Containerd, we can finally run them: - HostNetwork=true tests: This can now be enabled on Windows Privileged Containers. - /etc/hosts related tests: These were not supported because it required single file mappings, which is possible in Containerd. - termination message as non-root user: Requires RunAsUsername, and single file mappings. --- test/conformance/testdata/conformance.yaml | 39 ++++++++++------------ test/e2e/common/network/networking.go | 6 ++-- test/e2e/common/node/runtime.go | 5 +-- test/e2e/framework/network/utils.go | 5 +++ test/e2e/framework/pod/resource.go | 22 ++++++++++++ test/e2e/network/dns.go | 4 +-- test/e2e/network/dns_common.go | 7 +++- 7 files changed, 55 insertions(+), 33 deletions(-) diff --git a/test/conformance/testdata/conformance.yaml b/test/conformance/testdata/conformance.yaml index a0dd5f43692..fe5252adf2c 100755 --- a/test/conformance/testdata/conformance.yaml +++ b/test/conformance/testdata/conformance.yaml @@ -1191,8 +1191,7 @@ release: v1.20 file: test/e2e/instrumentation/core_events.go - testname: DNS, cluster - codename: '[sig-network] DNS should provide /etc/hosts entries for the cluster [LinuxOnly] - [Conformance]' + codename: '[sig-network] DNS should provide /etc/hosts entries for the cluster [Conformance]' description: When a Pod is created, the pod MUST be able to resolve cluster dns entries such as kubernetes.default via /etc/hosts. release: v1.14 @@ -1206,8 +1205,7 @@ release: v1.15 file: test/e2e/network/dns.go - testname: DNS, resolve the hostname - codename: '[sig-network] DNS should provide DNS for pods for Hostname [LinuxOnly] - [Conformance]' + codename: '[sig-network] DNS should provide DNS for pods for Hostname [Conformance]' description: Create a headless service with label. Create a Pod with label to match service's label, with hostname and a subdomain same as service name. Pod MUST be able to resolve its fully qualified domain name as well as hostname by serving @@ -1358,7 +1356,7 @@ file: test/e2e/common/network/networking.go - testname: Networking, intra pod http, from node codename: '[sig-network] Networking Granular Checks: Pods should function for node-pod - communication: http [LinuxOnly] [NodeConformance] [Conformance]' + communication: http [NodeConformance] [Conformance]' description: Create a hostexec pod that is capable of curl to netcat commands. Create a test Pod that will act as a webserver front end exposing ports 8080 for tcp and 8081 for udp. The netserver service proxies are created on specified number @@ -1366,13 +1364,12 @@ the each of service proxy endpoints in the cluster using a http post(protocol=tcp) and the request MUST be successful. Container will execute curl command to reach the service port within specified max retry limit and MUST result in reporting unique - hostnames. This test is marked LinuxOnly since HostNetwork is not supported on - other platforms like Windows. + hostnames. release: v1.9 file: test/e2e/common/network/networking.go - testname: Networking, intra pod http, from node codename: '[sig-network] Networking Granular Checks: Pods should function for node-pod - communication: udp [LinuxOnly] [NodeConformance] [Conformance]' + communication: udp [NodeConformance] [Conformance]' description: Create a hostexec pod that is capable of curl to netcat commands. Create a test Pod that will act as a webserver front end exposing ports 8080 for tcp and 8081 for udp. The netserver service proxies are created on specified number @@ -1380,8 +1377,7 @@ the each of service proxy endpoints in the cluster using a http post(protocol=udp) and the request MUST be successful. Container will execute curl command to reach the service port within specified max retry limit and MUST result in reporting unique - hostnames. This test is marked LinuxOnly since HostNetwork is not supported on - other platforms like Windows. + hostnames. release: v1.9 file: test/e2e/common/network/networking.go - testname: Proxy, validate Proxy responses @@ -1690,18 +1686,6 @@ server pod to validate that the pre-stop is executed. release: v1.9 file: test/e2e/common/node/lifecycle_hook.go -- testname: Container Runtime, TerminationMessagePath, non-root user and non-default - path - codename: '[sig-node] Container Runtime blackbox test on terminated container should - report termination message [LinuxOnly] if TerminationMessagePath is set as non-root - user and at a non-default path [NodeConformance] [Conformance]' - description: 'Create a pod with a container to run it as a non-root user with a - custom TerminationMessagePath set. Pod redirects the output to the provided path - successfully. When the container is terminated, the termination message MUST match - the expected output logged in the provided custom path. [LinuxOnly]: Tagged LinuxOnly - due to use of ''uid'' and unable to mount files in Windows Containers.' - release: v1.15 - file: test/e2e/common/node/runtime.go - testname: Container Runtime, TerminationMessage, from log output of succeeding container codename: '[sig-node] Container Runtime blackbox test on terminated container should report termination message as empty when pod succeeds and TerminationMessagePolicy @@ -1730,6 +1714,17 @@ MUST match the expected output recorded from container's log. release: v1.15 file: test/e2e/common/node/runtime.go +- testname: Container Runtime, TerminationMessagePath, non-root user and non-default + path + codename: '[sig-node] Container Runtime blackbox test on terminated container should + report termination message if TerminationMessagePath is set as non-root user and + at a non-default path [NodeConformance] [Conformance]' + description: Create a pod with a container to run it as a non-root user with a custom + TerminationMessagePath set. Pod redirects the output to the provided path successfully. + When the container is terminated, the termination message MUST match the expected + output logged in the provided custom path. + release: v1.15 + file: test/e2e/common/node/runtime.go - testname: Container Runtime, Restart Policy, Pod Phases codename: '[sig-node] Container Runtime blackbox test when starting a container that exits should run with the expected status [NodeConformance] [Conformance]' diff --git a/test/e2e/common/network/networking.go b/test/e2e/common/network/networking.go index 79f9c0f2355..6625f3aea00 100644 --- a/test/e2e/common/network/networking.go +++ b/test/e2e/common/network/networking.go @@ -98,9 +98,8 @@ var _ = SIGDescribe("Networking", func() { Testname: Networking, intra pod http, from node Description: Create a hostexec pod that is capable of curl to netcat commands. Create a test Pod that will act as a webserver front end exposing ports 8080 for tcp and 8081 for udp. The netserver service proxies are created on specified number of nodes. The kubectl exec on the webserver container MUST reach a http port on the each of service proxy endpoints in the cluster using a http post(protocol=tcp) and the request MUST be successful. Container will execute curl command to reach the service port within specified max retry limit and MUST result in reporting unique hostnames. - This test is marked LinuxOnly since HostNetwork is not supported on other platforms like Windows. */ - framework.ConformanceIt("should function for node-pod communication: http [LinuxOnly] [NodeConformance]", func() { + framework.ConformanceIt("should function for node-pod communication: http [NodeConformance]", func() { config := e2enetwork.NewCoreNetworkingTestConfig(f, true) for _, endpointPod := range config.EndpointPods { err := config.DialFromNode("http", endpointPod.Status.PodIP, e2enetwork.EndpointHTTPPort, config.MaxTries, 0, sets.NewString(endpointPod.Name)) @@ -115,9 +114,8 @@ var _ = SIGDescribe("Networking", func() { Testname: Networking, intra pod http, from node Description: Create a hostexec pod that is capable of curl to netcat commands. Create a test Pod that will act as a webserver front end exposing ports 8080 for tcp and 8081 for udp. The netserver service proxies are created on specified number of nodes. The kubectl exec on the webserver container MUST reach a http port on the each of service proxy endpoints in the cluster using a http post(protocol=udp) and the request MUST be successful. Container will execute curl command to reach the service port within specified max retry limit and MUST result in reporting unique hostnames. - This test is marked LinuxOnly since HostNetwork is not supported on other platforms like Windows. */ - framework.ConformanceIt("should function for node-pod communication: udp [LinuxOnly] [NodeConformance]", func() { + framework.ConformanceIt("should function for node-pod communication: udp [NodeConformance]", func() { config := e2enetwork.NewCoreNetworkingTestConfig(f, true) for _, endpointPod := range config.EndpointPods { err := config.DialFromNode("udp", endpointPod.Status.PodIP, e2enetwork.EndpointUDPPort, config.MaxTries, 0, sets.NewString(endpointPod.Name)) diff --git a/test/e2e/common/node/runtime.go b/test/e2e/common/node/runtime.go index ebc72498a4c..9f5756dfd30 100644 --- a/test/e2e/common/node/runtime.go +++ b/test/e2e/common/node/runtime.go @@ -188,11 +188,8 @@ while true; do sleep 1; done Release: v1.15 Testname: Container Runtime, TerminationMessagePath, non-root user and non-default path Description: Create a pod with a container to run it as a non-root user with a custom TerminationMessagePath set. Pod redirects the output to the provided path successfully. When the container is terminated, the termination message MUST match the expected output logged in the provided custom path. - [LinuxOnly]: Tagged LinuxOnly due to use of 'uid' and unable to mount files in Windows Containers. */ - framework.ConformanceIt("should report termination message [LinuxOnly] if TerminationMessagePath is set as non-root user and at a non-default path [NodeConformance]", func() { - // TODO(claudiub): Remove [LinuxOnly] tag once Containerd becomes the default - // container runtime on Windows + framework.ConformanceIt("should report termination message if TerminationMessagePath is set as non-root user and at a non-default path [NodeConformance]", func() { container := v1.Container{ Image: framework.BusyBoxImage, Command: []string{"/bin/sh", "-c"}, diff --git a/test/e2e/framework/network/utils.go b/test/e2e/framework/network/utils.go index 61eedab2152..3f68c1bf604 100644 --- a/test/e2e/framework/network/utils.go +++ b/test/e2e/framework/network/utils.go @@ -848,6 +848,11 @@ func (config *NetworkingTestConfig) createNetProxyPods(podName string, selector pod := config.createNetShellPodSpec(podName, hostname) pod.ObjectMeta.Labels = selector pod.Spec.HostNetwork = config.EndpointsHostNetwork + + // NOTE(claudiub): In order to use HostNetwork on Windows, we need to use Privileged Containers. + if pod.Spec.HostNetwork && framework.NodeOSDistroIs("windows") { + e2epod.WithWindowsHostProcess(pod, "") + } createdPod := config.createPod(pod) createdPods = append(createdPods, createdPod) } diff --git a/test/e2e/framework/pod/resource.go b/test/e2e/framework/pod/resource.go index 083d0590300..de167fa6407 100644 --- a/test/e2e/framework/pod/resource.go +++ b/test/e2e/framework/pod/resource.go @@ -576,6 +576,28 @@ func CreateExecPodOrFail(client clientset.Interface, ns, generateName string, tw return execPod } +// WithWindowsHostProcess sets the Pod's Windows HostProcess option to true. When this option is set, +// HostNetwork can be enabled. +// Containers running as HostProcess will require certain usernames to be set, otherwise the Pod will +// not start: NT AUTHORITY\SYSTEM, NT AUTHORITY\Local service, NT AUTHORITY\NetworkService. +// If the given username is empty, NT AUTHORITY\SYSTEM will be used instead. +// See: https://kubernetes.io/docs/tasks/configure-pod-container/create-hostprocess-pod/ +func WithWindowsHostProcess(pod *v1.Pod, username string) { + if pod.Spec.SecurityContext == nil { + pod.Spec.SecurityContext = &v1.PodSecurityContext{} + } + if pod.Spec.SecurityContext.WindowsOptions == nil { + pod.Spec.SecurityContext.WindowsOptions = &v1.WindowsSecurityContextOptions{} + } + + trueVar := true + if username == "" { + username = "NT AUTHORITY\\SYSTEM" + } + pod.Spec.SecurityContext.WindowsOptions.HostProcess = &trueVar + pod.Spec.SecurityContext.WindowsOptions.RunAsUserName = &username +} + // CheckPodsRunningReady returns whether all pods whose names are listed in // podNames in namespace ns are running and ready, using c and waiting at most // timeout. diff --git a/test/e2e/network/dns.go b/test/e2e/network/dns.go index 1dc3bb75950..7e0b565118b 100644 --- a/test/e2e/network/dns.go +++ b/test/e2e/network/dns.go @@ -112,7 +112,7 @@ var _ = common.SIGDescribe("DNS", func() { Testname: DNS, cluster Description: When a Pod is created, the pod MUST be able to resolve cluster dns entries such as kubernetes.default via /etc/hosts. */ - framework.ConformanceIt("should provide /etc/hosts entries for the cluster [LinuxOnly]", func() { + framework.ConformanceIt("should provide /etc/hosts entries for the cluster", func() { hostFQDN := fmt.Sprintf("%s.%s.%s.svc.%s", dnsTestPodHostName, dnsTestServiceName, f.Namespace.Name, framework.TestContext.ClusterDNSDomain) hostEntries := []string{hostFQDN, dnsTestPodHostName} // TODO: Validate both IPv4 and IPv6 families for dual-stack @@ -243,7 +243,7 @@ var _ = common.SIGDescribe("DNS", func() { Description: Create a headless service with label. Create a Pod with label to match service's label, with hostname and a subdomain same as service name. Pod MUST be able to resolve its fully qualified domain name as well as hostname by serving an A record at that name. */ - framework.ConformanceIt("should provide DNS for pods for Hostname [LinuxOnly]", func() { + framework.ConformanceIt("should provide DNS for pods for Hostname", func() { // Create a test headless service. ginkgo.By("Creating a test headless service") testServiceSelector := map[string]string{ diff --git a/test/e2e/network/dns_common.go b/test/e2e/network/dns_common.go index 5c95a97957c..51de229a141 100644 --- a/test/e2e/network/dns_common.go +++ b/test/e2e/network/dns_common.go @@ -406,10 +406,15 @@ func createProbeCommand(namesToResolve []string, hostEntries []string, ptrLookup probeCmd += fmt.Sprintf(`check="$$(dig +tcp +noall +answer +search %s)" && test -n "$$check" && echo OK > /results/%s;`, lookup, fileName) } + hostEntryCmd := `test -n "$$(getent hosts %s)" && echo OK > /results/%s;` + if framework.NodeOSDistroIs("windows") { + // We don't have getent on Windows, but we can still check the hosts file. + hostEntryCmd = `test -n "$$(grep '%s' C:/Windows/System32/drivers/etc/hosts)" && echo OK > /results/%s;` + } for _, name := range hostEntries { fileName := fmt.Sprintf("%s_hosts@%s", fileNamePrefix, name) fileNames = append(fileNames, fileName) - probeCmd += fmt.Sprintf(`test -n "$$(getent hosts %s)" && echo OK > /results/%s;`, name, fileName) + probeCmd += fmt.Sprintf(hostEntryCmd, name, fileName) } if len(ptrLookupIP) > 0 {