Skip to content

Commit 64c4876

Browse files
committed
Add e2e session affinity timeout test
The service session affinity allows to set the maximum session sticky timeout. This commit adds e2e tests to check that the session is sticky before the timeout and is not after.
1 parent e268f03 commit 64c4876

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

test/e2e/network/service.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2369,6 +2369,13 @@ var _ = SIGDescribe("Services", func() {
23692369
execAffinityTestForNonLBService(f, cs, svc)
23702370
})
23712371

2372+
// [LinuxOnly]: Windows does not support session affinity.
2373+
ginkgo.It("should have session affinity timeout work for service with type clusterIP [LinuxOnly]", func() {
2374+
svc := getServeHostnameService("affinity-clusterip-timeout")
2375+
svc.Spec.Type = v1.ServiceTypeClusterIP
2376+
execAffinityTestForSessionAffinityTimeout(f, cs, svc)
2377+
})
2378+
23722379
// [LinuxOnly]: Windows does not support session affinity.
23732380
ginkgo.It("should be able to switch session affinity for service with type clusterIP [LinuxOnly]", func() {
23742381
svc := getServeHostnameService("affinity-clusterip-transition")
@@ -2383,6 +2390,13 @@ var _ = SIGDescribe("Services", func() {
23832390
execAffinityTestForNonLBService(f, cs, svc)
23842391
})
23852392

2393+
// [LinuxOnly]: Windows does not support session affinity.
2394+
ginkgo.It("should have session affinity timeout work for NodePort service [LinuxOnly]", func() {
2395+
svc := getServeHostnameService("affinity-nodeport-timeout")
2396+
svc.Spec.Type = v1.ServiceTypeNodePort
2397+
execAffinityTestForSessionAffinityTimeout(f, cs, svc)
2398+
})
2399+
23862400
// [LinuxOnly]: Windows does not support session affinity.
23872401
ginkgo.It("should be able to switch session affinity for NodePort service [LinuxOnly]", func() {
23882402
svc := getServeHostnameService("affinity-nodeport-transition")
@@ -3042,6 +3056,69 @@ func execSourceipTest(pausePod v1.Pod, serviceAddress string) (string, string) {
30423056
return pausePod.Status.PodIP, host
30433057
}
30443058

3059+
// execAffinityTestForSessionAffinityTimeout is a helper function that wrap the logic of
3060+
// affinity test for non-load-balancer services. Session afinity will be
3061+
// enabled when the service is created and a short timeout will be configured so
3062+
// session affinity must change after the timeout expirese.
3063+
func execAffinityTestForSessionAffinityTimeout(f *framework.Framework, cs clientset.Interface, svc *v1.Service) {
3064+
ns := f.Namespace.Name
3065+
numPods, servicePort, serviceName := 3, defaultServeHostnameServicePort, svc.ObjectMeta.Name
3066+
ginkgo.By("creating service in namespace " + ns)
3067+
serviceType := svc.Spec.Type
3068+
// set an affinity timeout equal to the number of connection requests
3069+
svcSessionAffinityTimeout := int32(AffinityConfirmCount)
3070+
svc.Spec.SessionAffinity = v1.ServiceAffinityClientIP
3071+
svc.Spec.SessionAffinityConfig = &v1.SessionAffinityConfig{
3072+
ClientIP: &v1.ClientIPConfig{TimeoutSeconds: &svcSessionAffinityTimeout},
3073+
}
3074+
_, _, err := StartServeHostnameService(cs, svc, ns, numPods)
3075+
framework.ExpectNoError(err, "failed to create replication controller with service in the namespace: %s", ns)
3076+
defer func() {
3077+
StopServeHostnameService(cs, ns, serviceName)
3078+
}()
3079+
jig := e2eservice.NewTestJig(cs, ns, serviceName)
3080+
svc, err = jig.Client.CoreV1().Services(ns).Get(context.TODO(), serviceName, metav1.GetOptions{})
3081+
framework.ExpectNoError(err, "failed to fetch service: %s in namespace: %s", serviceName, ns)
3082+
var svcIP string
3083+
if serviceType == v1.ServiceTypeNodePort {
3084+
nodes, err := e2enode.GetReadySchedulableNodes(cs)
3085+
framework.ExpectNoError(err)
3086+
addrs := e2enode.CollectAddresses(nodes, v1.NodeInternalIP)
3087+
gomega.Expect(len(addrs)).To(gomega.BeNumerically(">", 0), "ginkgo.Failed to get Node internal IP")
3088+
svcIP = addrs[0]
3089+
servicePort = int(svc.Spec.Ports[0].NodePort)
3090+
} else {
3091+
svcIP = svc.Spec.ClusterIP
3092+
}
3093+
3094+
execPod := e2epod.CreateExecPodOrFail(cs, ns, "execpod-affinity", nil)
3095+
defer func() {
3096+
framework.Logf("Cleaning up the exec pod")
3097+
err := cs.CoreV1().Pods(ns).Delete(context.TODO(), execPod.Name, nil)
3098+
framework.ExpectNoError(err, "failed to delete pod: %s in namespace: %s", execPod.Name, ns)
3099+
}()
3100+
err = jig.CheckServiceReachability(svc, execPod)
3101+
framework.ExpectNoError(err)
3102+
3103+
// the service should be sticky until the timeout expires
3104+
framework.ExpectEqual(checkAffinity(execPod, svcIP, servicePort, true), true)
3105+
// but it should return different hostnames after the timeout expires
3106+
// try several times to avoid the probability that we hit the same pod twice
3107+
hosts := sets.NewString()
3108+
cmd := fmt.Sprintf(`curl -q -s --connect-timeout 2 http://%s/`, net.JoinHostPort(svcIP, strconv.Itoa(servicePort)))
3109+
for i := 0; i < 10; i++ {
3110+
hostname, err := framework.RunHostCmd(execPod.Namespace, execPod.Name, cmd)
3111+
if err == nil {
3112+
hosts.Insert(hostname)
3113+
if hosts.Len() > 1 {
3114+
return
3115+
}
3116+
time.Sleep(time.Duration(svcSessionAffinityTimeout) * time.Second)
3117+
}
3118+
}
3119+
framework.Fail("Session is sticky after reaching the timeout")
3120+
}
3121+
30453122
func execAffinityTestForNonLBServiceWithTransition(f *framework.Framework, cs clientset.Interface, svc *v1.Service) {
30463123
execAffinityTestForNonLBServiceWithOptionalTransition(f, cs, svc, true)
30473124
}

0 commit comments

Comments
 (0)