Skip to content

Commit b5e95fc

Browse files
authored
Merge pull request kubernetes#88409 from aojea/affinity
deflake e2e session affinity tests
2 parents 46fcbcf + 64c4876 commit b5e95fc

File tree

1 file changed

+90
-7
lines changed

1 file changed

+90
-7
lines changed

test/e2e/network/service.go

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,20 +114,26 @@ type portsByPodName map[string][]int
114114
// return false only in case of unexpected errors.
115115
func checkAffinity(execPod *v1.Pod, serviceIP string, servicePort int, shouldHold bool) bool {
116116
serviceIPPort := net.JoinHostPort(serviceIP, strconv.Itoa(servicePort))
117-
cmd := fmt.Sprintf(`curl -q -s --connect-timeout 2 http://%s/`, serviceIPPort)
117+
curl := fmt.Sprintf(`curl -q -s --connect-timeout 2 http://%s/`, serviceIPPort)
118+
cmd := fmt.Sprintf("for i in $(seq 0 %d); do echo; %s ; done", AffinityConfirmCount, curl)
118119
timeout := AffinityTimeout
119120
if execPod == nil {
120121
timeout = LoadBalancerPollTimeout
121122
}
122123
var tracker affinityTracker
123-
if pollErr := wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
124+
// interval considering a maximum of 2 seconds per connection
125+
interval := 2 * AffinityConfirmCount * time.Second
126+
if pollErr := wait.PollImmediate(interval, timeout, func() (bool, error) {
124127
if execPod != nil {
125128
stdout, err := framework.RunHostCmd(execPod.Namespace, execPod.Name, cmd)
126129
if err != nil {
127130
framework.Logf("Failed to get response from %s. Retry until timeout", serviceIPPort)
128131
return false, nil
129132
}
130-
tracker.recordHost(stdout)
133+
hosts := strings.Split(stdout, "\n")
134+
for _, host := range hosts {
135+
tracker.recordHost(strings.TrimSpace(host))
136+
}
131137
} else {
132138
rawResponse := GetHTTPContent(serviceIP, servicePort, timeout, "")
133139
tracker.recordHost(rawResponse.String())
@@ -2357,28 +2363,42 @@ var _ = SIGDescribe("Services", func() {
23572363
})
23582364

23592365
// [LinuxOnly]: Windows does not support session affinity.
2360-
ginkgo.It("should have session affinity work for service with type clusterIP [LinuxOnly] [Flaky]", func() {
2366+
ginkgo.It("should have session affinity work for service with type clusterIP [LinuxOnly]", func() {
23612367
svc := getServeHostnameService("affinity-clusterip")
23622368
svc.Spec.Type = v1.ServiceTypeClusterIP
23632369
execAffinityTestForNonLBService(f, cs, svc)
23642370
})
23652371

23662372
// [LinuxOnly]: Windows does not support session affinity.
2367-
ginkgo.It("should be able to switch session affinity for service with type clusterIP [LinuxOnly] [Flaky]", func() {
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+
2379+
// [LinuxOnly]: Windows does not support session affinity.
2380+
ginkgo.It("should be able to switch session affinity for service with type clusterIP [LinuxOnly]", func() {
23682381
svc := getServeHostnameService("affinity-clusterip-transition")
23692382
svc.Spec.Type = v1.ServiceTypeClusterIP
23702383
execAffinityTestForNonLBServiceWithTransition(f, cs, svc)
23712384
})
23722385

23732386
// [LinuxOnly]: Windows does not support session affinity.
2374-
ginkgo.It("should have session affinity work for NodePort service [LinuxOnly] [Flaky]", func() {
2387+
ginkgo.It("should have session affinity work for NodePort service [LinuxOnly]", func() {
23752388
svc := getServeHostnameService("affinity-nodeport")
23762389
svc.Spec.Type = v1.ServiceTypeNodePort
23772390
execAffinityTestForNonLBService(f, cs, svc)
23782391
})
23792392

23802393
// [LinuxOnly]: Windows does not support session affinity.
2381-
ginkgo.It("should be able to switch session affinity for NodePort service [LinuxOnly] [Flaky]", func() {
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+
2400+
// [LinuxOnly]: Windows does not support session affinity.
2401+
ginkgo.It("should be able to switch session affinity for NodePort service [LinuxOnly]", func() {
23822402
svc := getServeHostnameService("affinity-nodeport-transition")
23832403
svc.Spec.Type = v1.ServiceTypeNodePort
23842404
execAffinityTestForNonLBServiceWithTransition(f, cs, svc)
@@ -3036,6 +3056,69 @@ func execSourceipTest(pausePod v1.Pod, serviceAddress string) (string, string) {
30363056
return pausePod.Status.PodIP, host
30373057
}
30383058

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+
30393122
func execAffinityTestForNonLBServiceWithTransition(f *framework.Framework, cs clientset.Interface, svc *v1.Service) {
30403123
execAffinityTestForNonLBServiceWithOptionalTransition(f, cs, svc, true)
30413124
}

0 commit comments

Comments
 (0)