From e6428ae214e349c7f38663cda60cea8b13954122 Mon Sep 17 00:00:00 2001 From: Swetha Repakula Date: Tue, 25 Aug 2020 11:27:42 -0700 Subject: [PATCH] Add e2e multiple endpoint services test - ensures that when multiple services with the same selector will function even if one of the services is deleted --- test/e2e/framework/network/utils.go | 7 ++--- test/e2e/network/networking.go | 32 ++++++++++++++++++++++ test/e2e/network/util.go | 42 +++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/test/e2e/framework/network/utils.go b/test/e2e/framework/network/utils.go index c66cb3cf47a..a3d00304940 100644 --- a/test/e2e/framework/network/utils.go +++ b/test/e2e/framework/network/utils.go @@ -611,11 +611,11 @@ func (config *NetworkingTestConfig) createNodePortServiceSpec(svcName string, se } func (config *NetworkingTestConfig) createNodePortService(selector map[string]string) { - config.NodePortService = config.createService(config.createNodePortServiceSpec(nodePortServiceName, selector, false)) + config.NodePortService = config.CreateService(config.createNodePortServiceSpec(nodePortServiceName, selector, false)) } func (config *NetworkingTestConfig) createSessionAffinityService(selector map[string]string) { - config.SessionAffinityService = config.createService(config.createNodePortServiceSpec(sessionAffinityServiceName, selector, true)) + config.SessionAffinityService = config.CreateService(config.createNodePortServiceSpec(sessionAffinityServiceName, selector, true)) } // DeleteNodePortService deletes NodePort service. @@ -651,7 +651,8 @@ func (config *NetworkingTestConfig) createTestPods() { } } -func (config *NetworkingTestConfig) createService(serviceSpec *v1.Service) *v1.Service { +// CreateService creates the provided service in config.Namespace and returns created service +func (config *NetworkingTestConfig) CreateService(serviceSpec *v1.Service) *v1.Service { _, err := config.getServiceClient().Create(context.TODO(), serviceSpec, metav1.CreateOptions{}) framework.ExpectNoError(err, fmt.Sprintf("Failed to create %s service: %v", serviceSpec.Name, err)) diff --git a/test/e2e/network/networking.go b/test/e2e/network/networking.go index 07891bcfbe6..316f579c792 100644 --- a/test/e2e/network/networking.go +++ b/test/e2e/network/networking.go @@ -226,6 +226,38 @@ var _ = SIGDescribe("Networking", func() { config.DialFromEndpointContainer("udp", config.NodeIP, config.NodeUDPPort, config.MaxTries, 0, config.EndpointHostnames()) }) + // This test ensures that in a situation where multiple services exist with the same selector, + // deleting one of the services does not affect the connectivity of the remaining service + ginkgo.It("should function for multiple endpoint-Services with same selector", func() { + config := e2enetwork.NewNetworkingTestConfig(f, false, false) + ginkgo.By("creating a second service with same selector") + svc2, httpPort := createSecondNodePortService(f, config) + + // original service should work + ginkgo.By(fmt.Sprintf("dialing(http) %v (endpoint) --> %v:%v (config.clusterIP)", config.EndpointPods[0].Name, config.ClusterIP, e2enetwork.ClusterHTTPPort)) + config.DialFromEndpointContainer("http", config.ClusterIP, e2enetwork.ClusterHTTPPort, config.MaxTries, 0, config.EndpointHostnames()) + + ginkgo.By(fmt.Sprintf("dialing(http) %v (endpoint) --> %v:%v (nodeIP)", config.EndpointPods[0].Name, config.NodeIP, config.NodeHTTPPort)) + config.DialFromEndpointContainer("http", config.NodeIP, config.NodeHTTPPort, config.MaxTries, 0, config.EndpointHostnames()) + + // Dial second service + ginkgo.By(fmt.Sprintf("dialing(http) %v (endpoint) --> %v:%v (svc2.clusterIP)", config.EndpointPods[0].Name, svc2.Spec.ClusterIP, e2enetwork.ClusterHTTPPort)) + config.DialFromEndpointContainer("http", svc2.Spec.ClusterIP, e2enetwork.ClusterHTTPPort, config.MaxTries, 0, config.EndpointHostnames()) + + ginkgo.By(fmt.Sprintf("dialing(http) %v (endpoint) --> %v:%v (nodeIP)", config.EndpointPods[0].Name, config.NodeIP, httpPort)) + config.DialFromEndpointContainer("http", config.NodeIP, httpPort, config.MaxTries, 0, config.EndpointHostnames()) + + ginkgo.By("deleting the original node port service") + config.DeleteNodePortService() + + // Second service should continue to function unaffected + ginkgo.By(fmt.Sprintf("dialing(http) %v (endpoint) --> %v:%v (svc2.clusterIP)", config.EndpointPods[0].Name, svc2.Spec.ClusterIP, e2enetwork.ClusterHTTPPort)) + config.DialFromEndpointContainer("http", svc2.Spec.ClusterIP, e2enetwork.ClusterHTTPPort, config.MaxTries, 0, config.EndpointHostnames()) + + ginkgo.By(fmt.Sprintf("dialing(http) %v (endpoint) --> %v:%v (nodeIP)", config.EndpointPods[0].Name, config.NodeIP, httpPort)) + config.DialFromEndpointContainer("http", config.NodeIP, httpPort, config.MaxTries, 0, config.EndpointHostnames()) + }) + ginkgo.It("should update endpoints: http", func() { config := e2enetwork.NewNetworkingTestConfig(f, false, false) ginkgo.By(fmt.Sprintf("dialing(http) %v --> %v:%v (config.clusterIP)", config.TestContainerPod.Name, config.ClusterIP, e2enetwork.ClusterHTTPPort)) diff --git a/test/e2e/network/util.go b/test/e2e/network/util.go index 6deaabfbf68..7c75749c7d2 100644 --- a/test/e2e/network/util.go +++ b/test/e2e/network/util.go @@ -26,6 +26,7 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/kubernetes/test/e2e/framework" e2enetwork "k8s.io/kubernetes/test/e2e/framework/network" @@ -33,6 +34,9 @@ import ( imageutils "k8s.io/kubernetes/test/utils/image" ) +// secondNodePortSvcName is the name of the secondary node port service +const secondNodePortSvcName = "second-node-port-service" + var ( // agnHostImage is the image URI of AgnHost agnHostImage = imageutils.GetE2EImage(imageutils.Agnhost) @@ -162,3 +166,41 @@ func execSourceIPTest(sourcePod v1.Pod, targetAddr string) (string, string) { } return sourcePod.Status.PodIP, host } + +// createSecondNodePortService creates a service with the same selector as config.NodePortService and same HTTP Port +func createSecondNodePortService(f *framework.Framework, config *e2enetwork.NetworkingTestConfig) (*v1.Service, int) { + svc := &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: secondNodePortSvcName, + }, + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeNodePort, + Ports: []v1.ServicePort{ + { + Port: e2enetwork.ClusterHTTPPort, + Name: "http", + Protocol: v1.ProtocolTCP, + TargetPort: intstr.FromInt(e2enetwork.EndpointHTTPPort), + }, + }, + Selector: config.NodePortService.Spec.Selector, + }, + } + + createdService := config.CreateService(svc) + + err := framework.WaitForServiceEndpointsNum(f.ClientSet, config.Namespace, secondNodePortSvcName, len(config.EndpointPods), time.Second, wait.ForeverTestTimeout) + framework.ExpectNoError(err, "failed to validate endpoints for service %s in namespace: %s", secondNodePortSvcName, config.Namespace) + + var httpPort int + for _, p := range createdService.Spec.Ports { + switch p.Protocol { + case v1.ProtocolTCP: + httpPort = int(p.NodePort) + default: + continue + } + } + + return createdService, httpPort +}