Merge pull request #116333 from aojea/multiport_service

e2e network test for multiple protocol services on same port
This commit is contained in:
Kubernetes Prow Robot 2023-03-08 11:27:12 -08:00 committed by GitHub
commit 7598ff36cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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")
})
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
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
}
// 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
}