From 8c76845b0389e67c5d2d51343d4122d110d279aa Mon Sep 17 00:00:00 2001 From: Anton Protopopov Date: Fri, 17 Jun 2022 12:46:50 +0000 Subject: [PATCH 1/3] test/e2e/network: fix a bug in the hostport e2e test The hostport e2e test (sonobuoy run --e2e-focus 'validates that there is no conflict between pods with same hostPort but different hostIP and protocol') checks, in particular, that two pods with the same hostPort, the same hostIP, but different L4 protocols can coexist on one node. In order to do this, the test creates two pods with the same hostIP:hostPort, one TCP-based, another UDP-based. However, both pods listen on both protocols: netexec --http-port=8080 --udp-port=8080 This can happen that a CNI which doesn't distinguish between TCP and UDP hostPorts forwards all traffic, TCP or UDP, to the same pod. As this pod listens on both protocols it will reply to both requests, and the test will think that everything works properly while the second pod is indeed disconnected. Fix this by executing different commands in different pods: TCP: netexec --http-port=8080 --udp-port=-1 UDP: netexec --http-port=8008 --udp-port=8080 The TCP pod now doesn't listen on UDP, and the UDP pod doesn't listen on TCP on the target hostPort. The UDP pod still needs to listen on TCP on another port so that a pod readiness check can be made. --- test/e2e/network/hostport.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/e2e/network/hostport.go b/test/e2e/network/hostport.go index dec0d359949..4b177f3cc1d 100644 --- a/test/e2e/network/hostport.go +++ b/test/e2e/network/hostport.go @@ -157,6 +157,18 @@ var _ = common.SIGDescribe("HostPort", func() { // create pod which using hostport on the specified node according to the nodeSelector // it starts an http server on the exposed port func createHostPortPodOnNode(f *framework.Framework, podName, ns, hostIP string, port int32, protocol v1.Protocol, nodeName string) { + + var netexecArgs []string + var readinessProbePort int32 + + if protocol == v1.ProtocolTCP { + readinessProbePort = 8080 + netexecArgs = []string{"--http-port=8080", "--udp-port=-1"} + } else { + readinessProbePort = 8008 + netexecArgs = []string{"--http-port=8008", "--udp-port=8080"} + } + hostPortPod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName, @@ -166,7 +178,7 @@ func createHostPortPodOnNode(f *framework.Framework, podName, ns, hostIP string, { Name: "agnhost", Image: imageutils.GetE2EImage(imageutils.Agnhost), - Args: []string{"netexec", "--http-port=8080", "--udp-port=8080"}, + Args: append([]string{"netexec"}, netexecArgs...), Ports: []v1.ContainerPort{ { HostPort: port, @@ -180,7 +192,7 @@ func createHostPortPodOnNode(f *framework.Framework, podName, ns, hostIP string, HTTPGet: &v1.HTTPGetAction{ Path: "/hostname", Port: intstr.IntOrString{ - IntVal: int32(8080), + IntVal: readinessProbePort, }, Scheme: v1.URISchemeHTTP, }, From fd94ff64877ccf8a44de476b791e741720b2082d Mon Sep 17 00:00:00 2001 From: Anton Protopopov Date: Fri, 17 Jun 2022 16:03:33 +0000 Subject: [PATCH 2/3] agnhost/netexec: fix an error message The error message inside the UDP server used a SCTP port instead of a UDP port --- test/images/agnhost/netexec/netexec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/images/agnhost/netexec/netexec.go b/test/images/agnhost/netexec/netexec.go index f1510a0fcc4..a63054f7771 100644 --- a/test/images/agnhost/netexec/netexec.go +++ b/test/images/agnhost/netexec/netexec.go @@ -634,7 +634,7 @@ func redirectHandler(w http.ResponseWriter, r *http.Request) { // udp server supports the hostName, echo and clientIP commands. func startUDPServer(address string, udpPort int) { serverAddress, err := net.ResolveUDPAddr("udp", net.JoinHostPort(address, strconv.Itoa(udpPort))) - assertNoError(err, fmt.Sprintf("failed to resolve UDP address for port %d", sctpPort)) + assertNoError(err, fmt.Sprintf("failed to resolve UDP address for port %d", udpPort)) serverConn, err := net.ListenUDP("udp", serverAddress) assertNoError(err, fmt.Sprintf("failed to create listener for UDP address %v", serverAddress)) defer serverConn.Close() From 4026d355bc38433ca41937215798ee4bd89f47a1 Mon Sep 17 00:00:00 2001 From: Anton Protopopov Date: Fri, 17 Jun 2022 16:06:25 +0000 Subject: [PATCH 3/3] test/e2e/network: add a check to the hostport e2e test As described in 8c76845b038 ("test/e2e/network: fix a bug in the hostport e2e test") if we have two pods with the same hostPort, hostIP, but different protocols, a CNI may be buggy and decide to forward all traffic only to one of these pods. Add a check that we receiving requests from different pods. Co-authored-by: Antonio Ojea --- test/e2e/network/hostport.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/e2e/network/hostport.go b/test/e2e/network/hostport.go index 4b177f3cc1d..c33e781a13d 100644 --- a/test/e2e/network/hostport.go +++ b/test/e2e/network/hostport.go @@ -119,7 +119,7 @@ var _ = common.SIGDescribe("HostPort", func() { // IPv6 doesn't NAT from localhost -> localhost, it doesn't have the route_localnet kernel hack, so we need to specify the source IP cmdPod1 := []string{"/bin/sh", "-c", fmt.Sprintf("curl -g --connect-timeout %v --interface %s http://%s/hostname", timeout, hostIP, net.JoinHostPort(localhost, strconv.Itoa(int(port))))} cmdPod2 := []string{"/bin/sh", "-c", fmt.Sprintf("curl -g --connect-timeout %v http://%s/hostname", timeout, net.JoinHostPort(hostIP, strconv.Itoa(int(port))))} - cmdPod3 := []string{"/bin/sh", "-c", fmt.Sprintf("nc -vuz -w %v %s %d", timeout, hostIP, port)} + cmdPod3 := []string{"/bin/sh", "-c", fmt.Sprintf("echo hostname | nc -u -w %v %s %d", timeout, hostIP, port)} // try 5 times to connect to the exposed ports for i := 0; i < 5; i++ { // check pod1 @@ -143,11 +143,19 @@ var _ = common.SIGDescribe("HostPort", func() { } // check pod3 ginkgo.By(fmt.Sprintf("checking connectivity from pod %s to serverIP: %s, port: %d UDP", hostExecPod.Name, hostIP, port)) - _, _, err = f.ExecCommandInContainerWithFullOutput(hostExecPod.Name, "e2e-host-exec", cmdPod3...) + hostname3, _, err := f.ExecCommandInContainerWithFullOutput(hostExecPod.Name, "e2e-host-exec", cmdPod3...) if err != nil { framework.Logf("Can not connect from %s to pod(pod2) to serverIP: %s, port: %d", hostExecPod.Name, hostIP, port) continue } + if hostname1 == hostname3 { + framework.Logf("pods must have different hostname: pod1 has hostname %s, pod3 has hostname %s", hostname1, hostname3) + continue + } + if hostname2 == hostname3 { + framework.Logf("pods must have different hostname: pod2 has hostname %s, pod3 has hostname %s", hostname2, hostname3) + continue + } return } framework.Failf("Failed to connect to exposed host ports")