From 4482d1c2f4900632e106b4857a1f054265d8a4ce Mon Sep 17 00:00:00 2001 From: Antonio Ojea Date: Fri, 25 Nov 2022 18:14:45 +0000 Subject: [PATCH] e2e support Services with multiple EndpointSlices A Service can use multiple EndpointSlices for its backend, when using custom Endpoint Slices, the data plane should forward traffic to any of the endpoints in the Endpointslices that belong to the Service. Change-Id: I80b42522bf6ab443050697a29b94d8245943526f --- test/e2e/network/endpointslice.go | 205 ++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/test/e2e/network/endpointslice.go b/test/e2e/network/endpointslice.go index ff04d19495f..74fd48aa374 100644 --- a/test/e2e/network/endpointslice.go +++ b/test/e2e/network/endpointslice.go @@ -20,6 +20,7 @@ import ( "context" "encoding/json" "fmt" + "net" "time" v1 "k8s.io/api/core/v1" @@ -38,6 +39,7 @@ import ( "k8s.io/kubernetes/test/e2e/network/common" imageutils "k8s.io/kubernetes/test/utils/image" admissionapi "k8s.io/pod-security-admission/api" + "k8s.io/utils/pointer" "github.com/onsi/ginkgo/v2" ) @@ -524,6 +526,209 @@ var _ = common.SIGDescribe("EndpointSlice", func() { framework.ExpectNoError(err) framework.ExpectEqual(len(epsList.Items), 0, "filtered list should have 0 items") }) + + ginkgo.It("should support a Service with multiple ports specified in multiple EndpointSlices", func(ctx context.Context) { + ns := f.Namespace.Name + svc := createServiceReportErr(ctx, cs, f.Namespace.Name, &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-custom-endpoints", + }, + Spec: v1.ServiceSpec{ + Ports: []v1.ServicePort{ + { + Name: "port80", + Port: 80, + Protocol: v1.ProtocolTCP, + }, + { + Name: "port81", + Port: 81, + Protocol: v1.ProtocolTCP, + }, + }, + }, + }) + + // Add a backend pod to the service in the other node + port8090 := []v1.ContainerPort{ + { + ContainerPort: 8090, + Protocol: v1.ProtocolTCP, + }, + } + port9090 := []v1.ContainerPort{ + { + ContainerPort: 9090, + Protocol: v1.ProtocolTCP, + }, + } + + serverPod := e2epod.NewAgnhostPodFromContainers( + "", "pod-handle-http-request", nil, + e2epod.NewAgnhostContainer("container-handle-8090-request", nil, port8090, "netexec", "--http-port", "8090", "--udp-port", "-1"), + e2epod.NewAgnhostContainer("container-handle-9090-request", nil, port9090, "netexec", "--http-port", "9090", "--udp-port", "-1"), + ) + + pod := e2epod.NewPodClient(f).CreateSync(ctx, serverPod) + + if pod.Status.PodIP == "" { + framework.Failf("PodIP not assigned for pod %s", pod.Name) + } + + addressType := discoveryv1.AddressTypeIPv4 + if framework.TestContext.ClusterIsIPv6() { + addressType = discoveryv1.AddressTypeIPv6 + } + + // create custom endpoint slices + tcpProtocol := v1.ProtocolTCP + epsTemplate := &discoveryv1.EndpointSlice{ + ObjectMeta: metav1.ObjectMeta{GenerateName: "e2e-custom-slice", + Labels: map[string]string{ + discoveryv1.LabelServiceName: svc.Name, + discoveryv1.LabelManagedBy: "e2e-test" + ns, + }}, + AddressType: addressType, + Endpoints: []discoveryv1.Endpoint{ + {Addresses: []string{pod.Status.PodIP}}, + }, + } + + ginkgo.By("creating") + eps1 := epsTemplate.DeepCopy() + eps1.Ports = []discoveryv1.EndpointPort{{ + Name: pointer.String("port80"), + Port: pointer.Int32(8090), + Protocol: &tcpProtocol, + }} + + _, err := f.ClientSet.DiscoveryV1().EndpointSlices(ns).Create(ctx, eps1, metav1.CreateOptions{}) + framework.ExpectNoError(err) + eps2 := epsTemplate.DeepCopy() + eps2.Ports = []discoveryv1.EndpointPort{{ + Name: pointer.String("port81"), + Port: pointer.Int32(9090), + Protocol: &tcpProtocol, + }} + + _, err = f.ClientSet.DiscoveryV1().EndpointSlices(ns).Create(ctx, eps2, metav1.CreateOptions{}) + framework.ExpectNoError(err) + + // connect to the service must work + ginkgo.By("Creating a pause pods that will try to connect to the webserver") + pausePod0 := e2epod.NewAgnhostPod(ns, "pause-pod-0", nil, nil, nil) + e2epod.NewPodClient(f).CreateSync(ctx, pausePod0) + + dest1 := net.JoinHostPort(svc.Spec.ClusterIP, "80") + dest2 := net.JoinHostPort(svc.Spec.ClusterIP, "81") + execHostnameTest(*pausePod0, dest1, serverPod.Name) + execHostnameTest(*pausePod0, dest2, serverPod.Name) + + }) + + ginkgo.It("should support a Service with multiple endpoint IPs specified in multiple EndpointSlices", func(ctx context.Context) { + ns := f.Namespace.Name + svc := createServiceReportErr(ctx, cs, f.Namespace.Name, &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-custom-endpoints", + }, + Spec: v1.ServiceSpec{ + Ports: []v1.ServicePort{ + { + Name: "port80", + Port: 80, + Protocol: v1.ProtocolTCP, + }, + { + Name: "port81", + Port: 81, + Protocol: v1.ProtocolTCP, + }, + }, + }, + }) + + // Add a backend pod to the service in the other node + port8090 := []v1.ContainerPort{ + { + ContainerPort: 8090, + Protocol: v1.ProtocolTCP, + }, + } + + serverPod1 := e2epod.NewAgnhostPodFromContainers( + "", "pod1-handle-http-request", nil, + e2epod.NewAgnhostContainer("container-handle-8090-request", nil, port8090, "netexec", "--http-port", "8090", "--udp-port", "-1"), + ) + pod1 := e2epod.NewPodClient(f).CreateSync(ctx, serverPod1) + + if pod1.Status.PodIP == "" { + framework.Failf("PodIP not assigned for pod %s", pod1.Name) + } + + serverPod2 := e2epod.NewAgnhostPodFromContainers( + "", "pod2-handle-http-request", nil, + e2epod.NewAgnhostContainer("container-handle-8090-request", nil, port8090, "netexec", "--http-port", "8090", "--udp-port", "-1"), + ) + pod2 := e2epod.NewPodClient(f).CreateSync(ctx, serverPod2) + + if pod2.Status.PodIP == "" { + framework.Failf("PodIP not assigned for pod %s", pod2.Name) + } + + addressType := discoveryv1.AddressTypeIPv4 + if framework.TestContext.ClusterIsIPv6() { + addressType = discoveryv1.AddressTypeIPv6 + } + + // create custom endpoint slices + tcpProtocol := v1.ProtocolTCP + epsTemplate := &discoveryv1.EndpointSlice{ + ObjectMeta: metav1.ObjectMeta{GenerateName: "e2e-custom-slice", + Labels: map[string]string{ + discoveryv1.LabelServiceName: svc.Name, + discoveryv1.LabelManagedBy: "e2e-test" + ns, + }}, + AddressType: addressType, + } + + ginkgo.By("creating") + eps1 := epsTemplate.DeepCopy() + eps1.Endpoints = []discoveryv1.Endpoint{ + {Addresses: []string{pod1.Status.PodIP}}, + } + eps1.Ports = []discoveryv1.EndpointPort{{ + Name: pointer.String("port80"), + Port: pointer.Int32(8090), + Protocol: &tcpProtocol, + }} + + _, err := f.ClientSet.DiscoveryV1().EndpointSlices(ns).Create(context.TODO(), eps1, metav1.CreateOptions{}) + framework.ExpectNoError(err) + eps2 := epsTemplate.DeepCopy() + eps2.Endpoints = []discoveryv1.Endpoint{ + {Addresses: []string{pod2.Status.PodIP}}, + } + eps2.Ports = []discoveryv1.EndpointPort{{ + Name: pointer.String("port81"), + Port: pointer.Int32(8090), + Protocol: &tcpProtocol, + }} + _, err = f.ClientSet.DiscoveryV1().EndpointSlices(ns).Create(context.TODO(), eps2, metav1.CreateOptions{}) + framework.ExpectNoError(err) + + // connect to the service must work + ginkgo.By("Creating a pause pods that will try to connect to the webserver") + pausePod0 := e2epod.NewAgnhostPod(ns, "pause-pod-0", nil, nil, nil) + e2epod.NewPodClient(f).CreateSync(ctx, pausePod0) + + dest1 := net.JoinHostPort(svc.Spec.ClusterIP, "80") + dest2 := net.JoinHostPort(svc.Spec.ClusterIP, "81") + execHostnameTest(*pausePod0, dest1, serverPod1.Name) + execHostnameTest(*pausePod0, dest2, serverPod2.Name) + + }) + }) // expectEndpointsAndSlices verifies that Endpoints and EndpointSlices exist for