e2e network test for multiple protocol services on same port

The test creates a Service exposing two protocols on the same port
and a backend that replies on both protocols.

1. Test that Service with works for both protocol
2. Update Service to expose only the TCP port
3. Verify that TCP works and UDP does not work
4. Update Service to expose only the UDP port
5. Verify that TCP does not work and UDP does work

Change-Id: Ic4f3a6509e332aa5694d20dfc3b223d7063a7871
This commit is contained in:
Antonio Ojea 2023-03-07 16:09:54 +00:00
parent 1af56548af
commit 7cb135a888
2 changed files with 139 additions and 0 deletions

View File

@ -3618,6 +3618,114 @@ var _ = common.SIGDescribe("Services", func() {
framework.Logf("Collection of services has been deleted") framework.Logf("Collection of services has been deleted")
}) })
ginkgo.It("should serve endpoints on same port and different protocols", func(ctx context.Context) {
serviceName := "multiprotocol-test"
testLabels := map[string]string{"app": "multiport"}
ns := f.Namespace.Name
containerPort := 80
svcTCPport := v1.ServicePort{
Name: "tcp-port",
Port: 80,
TargetPort: intstr.FromInt(containerPort),
Protocol: v1.ProtocolTCP,
}
svcUDPport := v1.ServicePort{
Name: "udp-port",
Port: 80,
TargetPort: intstr.FromInt(containerPort),
Protocol: v1.ProtocolUDP,
}
ginkgo.By("creating service " + serviceName + " in namespace " + ns)
testService := v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: serviceName,
Labels: testLabels,
},
Spec: v1.ServiceSpec{
Type: v1.ServiceTypeClusterIP,
Selector: testLabels,
Ports: []v1.ServicePort{svcTCPport, svcUDPport},
},
}
service, err := cs.CoreV1().Services(ns).Create(ctx, &testService, metav1.CreateOptions{})
framework.ExpectNoError(err, "failed to create Service")
containerPorts := []v1.ContainerPort{{
Name: svcTCPport.Name,
ContainerPort: int32(containerPort),
Protocol: v1.ProtocolTCP,
}, {
Name: svcUDPport.Name,
ContainerPort: int32(containerPort),
Protocol: v1.ProtocolUDP,
}}
podname1 := "pod1"
ginkgo.By("creating pod " + podname1 + " in namespace " + ns)
createPodOrFail(ctx, f, ns, podname1, testLabels, containerPorts, "netexec", "--http-port", strconv.Itoa(containerPort), "--udp-port", strconv.Itoa(containerPort))
validateEndpointsPortsWithProtocolsOrFail(cs, ns, serviceName, fullPortsByPodName{podname1: containerPorts})
ginkgo.By("Checking if the Service forwards traffic to the TCP and UDP port")
execPod := e2epod.CreateExecPodOrFail(ctx, cs, ns, "execpod", nil)
err = testEndpointReachability(ctx, service.Spec.ClusterIP, 80, v1.ProtocolTCP, execPod, 30*time.Second)
if err != nil {
framework.Failf("Failed to connect to Service TCP port: %v", err)
}
err = testEndpointReachability(ctx, service.Spec.ClusterIP, 80, v1.ProtocolUDP, execPod, 30*time.Second)
if err != nil {
framework.Failf("Failed to connect to Service UDP port: %v", err)
}
ginkgo.By("Checking if the Service forwards traffic to TCP only")
service, err = cs.CoreV1().Services(ns).Get(ctx, serviceName, metav1.GetOptions{})
if err != nil {
framework.Failf("failed to get Service %q: %v", serviceName, err)
}
service.Spec.Ports = []v1.ServicePort{svcTCPport}
_, err = cs.CoreV1().Services(ns).Update(ctx, service, metav1.UpdateOptions{})
if err != nil {
framework.Failf("failed to get Service %q: %v", serviceName, err)
}
// test reachability
err = testEndpointReachability(ctx, service.Spec.ClusterIP, 80, v1.ProtocolTCP, execPod, 30*time.Second)
if err != nil {
framework.Failf("Failed to connect to Service TCP port: %v", err)
}
// take into account the NetworkProgrammingLatency
// testEndpointReachability tries 3 times every 3 second
// we retry again during 30 seconds to check if the port stops forwarding
gomega.Eventually(ctx, func() error {
return testEndpointReachability(ctx, service.Spec.ClusterIP, 80, v1.ProtocolUDP, execPod, 6*time.Second)
}).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(gomega.BeNil())
ginkgo.By("Checking if the Service forwards traffic to UDP only")
service, err = cs.CoreV1().Services(ns).Get(ctx, serviceName, metav1.GetOptions{})
if err != nil {
framework.Failf("failed to get Service %q: %v", serviceName, err)
}
service.Spec.Ports = []v1.ServicePort{svcUDPport}
_, err = cs.CoreV1().Services(ns).Update(ctx, service, metav1.UpdateOptions{})
if err != nil {
framework.Failf("failed to update Service %q: %v", serviceName, err)
}
// test reachability
err = testEndpointReachability(ctx, service.Spec.ClusterIP, 80, v1.ProtocolUDP, execPod, 30*time.Second)
if err != nil {
framework.Failf("Failed to connect to Service UDP port: %v", err)
}
// take into account the NetworkProgrammingLatency
// testEndpointReachability tries 3 times every 3 second
// we retry again during 30 seconds to check if the port stops forwarding
gomega.Eventually(ctx, func() error {
return testEndpointReachability(ctx, service.Spec.ClusterIP, 80, v1.ProtocolTCP, execPod, 6*time.Second)
}).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(gomega.BeNil())
})
/* /*
Release: v1.26 Release: v1.26
Testname: Service, same ports with different protocols on a Load Balancer Service Testname: Service, same ports with different protocols on a Load Balancer Service

View File

@ -218,3 +218,34 @@ func createSecondNodePortService(ctx context.Context, f *framework.Framework, co
return createdService, httpPort return createdService, httpPort
} }
// testEndpointReachability tests reachability to endpoints (i.e. IP, ServiceName) and ports. Test request is initiated from specified execPod.
// TCP and UDP protocol based service are supported at this moment
func testEndpointReachability(ctx context.Context, endpoint string, port int32, protocol v1.Protocol, execPod *v1.Pod, timeout time.Duration) error {
cmd := ""
switch protocol {
case v1.ProtocolTCP:
cmd = fmt.Sprintf("echo hostName | nc -v -t -w 2 %s %v", endpoint, port)
case v1.ProtocolUDP:
cmd = fmt.Sprintf("echo hostName | nc -v -u -w 2 %s %v", endpoint, port)
default:
return fmt.Errorf("service reachability check is not supported for %v", protocol)
}
err := wait.PollImmediateWithContext(ctx, framework.Poll, timeout, func(ctx context.Context) (bool, error) {
stdout, err := e2eoutput.RunHostCmd(execPod.Namespace, execPod.Name, cmd)
if err != nil {
framework.Logf("Service reachability failing with error: %v\nRetrying...", err)
return false, nil
}
trimmed := strings.TrimSpace(stdout)
if trimmed != "" {
return true, nil
}
return false, nil
})
if err != nil {
return fmt.Errorf("service is not reachable within %v timeout on endpoint %s %d over %s protocol", timeout, endpoint, port, protocol)
}
return nil
}