From 56fe87d349adfcccac16c5eda8e9da5538106f55 Mon Sep 17 00:00:00 2001 From: Claudiu Belu Date: Sat, 5 Oct 2019 13:20:40 -0700 Subject: [PATCH] tests: Adds large requests tests Ensures that requests that require large packets work properly, and that they are not dropped. Adds AgnhostPrivate to test/utils/image/manifest. Some tests are trying to pull the agnhost image from the private registry, meaning that we would need to always build and push the agnhost image to both e2e and private registry whenever we bump its version. Decoupling them would mean that we only need to push the image to the e2e registry. --- test/e2e/framework/network/utils.go | 57 ++++++++++++++++++++++------- test/e2e/network/networking.go | 14 +++++++ 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/test/e2e/framework/network/utils.go b/test/e2e/framework/network/utils.go index 8ffc41af700..f3204dfaefb 100644 --- a/test/e2e/framework/network/utils.go +++ b/test/e2e/framework/network/utils.go @@ -73,6 +73,9 @@ const ( RegexIPv6 = "(?:(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-fA-F]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,1}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,2}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,3}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:[0-9a-fA-F]{1,4})):)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,4}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,5}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,6}(?:(?:[0-9a-fA-F]{1,4})))?::))))" resizeNodeReadyTimeout = 2 * time.Minute resizeNodeNotReadyTimeout = 2 * time.Minute + // netexec dial commands + // the destination will echo its hostname. + echoHostname = "hostname" ) // NetexecImageName is the image name for agnhost. @@ -146,12 +149,30 @@ type NetworkingTestConfig struct { // DialFromEndpointContainer executes a curl via kubectl exec in an endpoint container. func (config *NetworkingTestConfig) DialFromEndpointContainer(protocol, targetIP string, targetPort, maxTries, minTries int, expectedEps sets.String) { - config.DialFromContainer(protocol, config.EndpointPods[0].Status.PodIP, targetIP, EndpointHTTPPort, targetPort, maxTries, minTries, expectedEps) + config.DialFromContainer(protocol, echoHostname, config.EndpointPods[0].Status.PodIP, targetIP, EndpointHTTPPort, targetPort, maxTries, minTries, expectedEps) } // DialFromTestContainer executes a curl via kubectl exec in a test container. func (config *NetworkingTestConfig) DialFromTestContainer(protocol, targetIP string, targetPort, maxTries, minTries int, expectedEps sets.String) { - config.DialFromContainer(protocol, config.TestContainerPod.Status.PodIP, targetIP, testContainerHTTPPort, targetPort, maxTries, minTries, expectedEps) + config.DialFromContainer(protocol, echoHostname, config.TestContainerPod.Status.PodIP, targetIP, testContainerHTTPPort, targetPort, maxTries, minTries, expectedEps) +} + +// DialEchoFromTestContainer executes a curl via kubectl exec in a test container. The response is expected to match the echoMessage. +func (config *NetworkingTestConfig) DialEchoFromTestContainer(protocol, targetIP string, targetPort, maxTries, minTries int, echoMessage string) { + expectedResponse := sets.NewString() + expectedResponse.Insert(echoMessage) + var dialCommand string + + // NOTE(claudiub): netexec /dialCommand will send a request to the given targetIP and targetPort as follows: + // for HTTP: it will send a request to: http://targetIP:targetPort/dialCommand + // for UDP: it will send targetCommand as a message. The consumer receives the data message and looks for + // a few starting strings, including echo, and treats it accordingly. + if protocol == "http" { + dialCommand = fmt.Sprintf("echo?msg=%s", echoMessage) + } else { + dialCommand = fmt.Sprintf("echo%%20%s", echoMessage) + } + config.DialFromContainer(protocol, dialCommand, config.TestContainerPod.Status.PodIP, targetIP, testContainerHTTPPort, targetPort, maxTries, minTries, expectedResponse) } // diagnoseMissingEndpoints prints debug information about the endpoints that @@ -186,23 +207,29 @@ func (config *NetworkingTestConfig) EndpointHostnames() sets.String { // at least once. // - maxTries is the maximum number of curl attempts. If this many attempts pass // and we don't see all expected endpoints, the test fails. -// - expectedEps is the set of endpointnames to wait for. Typically this is also -// the hostname reported by each pod in the service through /hostName. +// - targetIP is the source Pod IP that will dial the given dialCommand using the given protocol. +// - dialCommand is the command that the targetIP will send to the targetIP using the given protocol. +// the dialCommand should be formatted properly for the protocol (http: URL path+parameters, +// udp: command%20parameters, where parameters are optional) +// - expectedResponses is the unordered set of responses to wait for. The responses are based on +// the dialCommand; for example, for the dialCommand "hostname", the expectedResponses +// should contain the hostnames reported by each pod in the service through /hostName. // maxTries == minTries will confirm that we see the expected endpoints and no // more for maxTries. Use this if you want to eg: fail a readiness check on a // pod and confirm it doesn't show up as an endpoint. -func (config *NetworkingTestConfig) DialFromContainer(protocol, containerIP, targetIP string, containerHTTPPort, targetPort, maxTries, minTries int, expectedEps sets.String) { +func (config *NetworkingTestConfig) DialFromContainer(protocol, dialCommand, containerIP, targetIP string, containerHTTPPort, targetPort, maxTries, minTries int, expectedResponses sets.String) { ipPort := net.JoinHostPort(containerIP, strconv.Itoa(containerHTTPPort)) // The current versions of curl included in CentOS and RHEL distros // misinterpret square brackets around IPv6 as globbing, so use the -g // argument to disable globbing to handle the IPv6 case. - cmd := fmt.Sprintf("curl -g -q -s 'http://%s/dial?request=hostName&protocol=%s&host=%s&port=%d&tries=1'", + cmd := fmt.Sprintf("curl -g -q -s 'http://%s/dial?request=%s&protocol=%s&host=%s&port=%d&tries=1'", ipPort, + dialCommand, protocol, targetIP, targetPort) - eps := sets.NewString() + responses := sets.NewString() for i := 0; i < maxTries; i++ { stdout, stderr, err := config.f.ExecShellInPodWithFullOutput(config.HostTestContainerPod.Name, cmd) @@ -219,25 +246,27 @@ func (config *NetworkingTestConfig) DialFromContainer(protocol, containerIP, tar continue } - for _, hostName := range output["responses"] { - trimmed := strings.TrimSpace(hostName) + for _, response := range output["responses"] { + trimmed := strings.TrimSpace(response) if trimmed != "" { - eps.Insert(trimmed) + responses.Insert(trimmed) } } } - framework.Logf("Waiting for endpoints: %v", expectedEps.Difference(eps)) + framework.Logf("Waiting for responses: %v", expectedResponses.Difference(responses)) // Check against i+1 so we exit if minTries == maxTries. - if (eps.Equal(expectedEps) || eps.Len() == 0 && expectedEps.Len() == 0) && i+1 >= minTries { + if (responses.Equal(expectedResponses) || responses.Len() == 0 && expectedResponses.Len() == 0) && i+1 >= minTries { return } // TODO: get rid of this delay #36281 time.Sleep(hitEndpointRetryDelay) } - config.diagnoseMissingEndpoints(eps) - framework.Failf("Failed to find expected endpoints:\nTries %d\nCommand %v\nretrieved %v\nexpected %v\n", maxTries, cmd, eps, expectedEps) + if dialCommand == echoHostname { + config.diagnoseMissingEndpoints(responses) + } + framework.Failf("Failed to find expected responses:\nTries %d\nCommand %v\nretrieved %v\nexpected %v\n", maxTries, cmd, responses, expectedResponses) } // GetEndpointsFromTestContainer executes a curl via kubectl exec in a test container. diff --git a/test/e2e/network/networking.go b/test/e2e/network/networking.go index 7d9345165fd..cdd8e08dfa7 100644 --- a/test/e2e/network/networking.go +++ b/test/e2e/network/networking.go @@ -295,6 +295,20 @@ var _ = SIGDescribe("Networking", func() { framework.Failf("Unexpected endpoints return: %v, expect 1 endpoints", eps) } }) + + ginkgo.It("should be able to handle large requests: http", func() { + config := e2enetwork.NewNetworkingTestConfig(f) + ginkgo.By(fmt.Sprintf("dialing(http) %v --> %v:%v (config.clusterIP)", config.TestContainerPod.Name, config.ClusterIP, e2enetwork.ClusterHTTPPort)) + message := strings.Repeat("42", 1000) + config.DialEchoFromTestContainer("http", config.ClusterIP, e2enetwork.ClusterHTTPPort, config.MaxTries, 0, message) + }) + + ginkgo.It("should be able to handle large requests: udp", func() { + config := e2enetwork.NewNetworkingTestConfig(f) + ginkgo.By(fmt.Sprintf("dialing(udp) %v --> %v:%v (config.clusterIP)", config.TestContainerPod.Name, config.ClusterIP, e2enetwork.ClusterUDPPort)) + message := "n" + strings.Repeat("o", 1999) + config.DialEchoFromTestContainer("udp", config.ClusterIP, e2enetwork.ClusterUDPPort, config.MaxTries, 0, message) + }) }) ginkgo.It("should recreate its iptables rules if they are deleted [Disruptive]", func() {