mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Merge pull request #88409 from aojea/affinity
deflake e2e session affinity tests
This commit is contained in:
commit
b5e95fc73d
@ -114,20 +114,26 @@ type portsByPodName map[string][]int
|
|||||||
// return false only in case of unexpected errors.
|
// return false only in case of unexpected errors.
|
||||||
func checkAffinity(execPod *v1.Pod, serviceIP string, servicePort int, shouldHold bool) bool {
|
func checkAffinity(execPod *v1.Pod, serviceIP string, servicePort int, shouldHold bool) bool {
|
||||||
serviceIPPort := net.JoinHostPort(serviceIP, strconv.Itoa(servicePort))
|
serviceIPPort := net.JoinHostPort(serviceIP, strconv.Itoa(servicePort))
|
||||||
cmd := fmt.Sprintf(`curl -q -s --connect-timeout 2 http://%s/`, serviceIPPort)
|
curl := fmt.Sprintf(`curl -q -s --connect-timeout 2 http://%s/`, serviceIPPort)
|
||||||
|
cmd := fmt.Sprintf("for i in $(seq 0 %d); do echo; %s ; done", AffinityConfirmCount, curl)
|
||||||
timeout := AffinityTimeout
|
timeout := AffinityTimeout
|
||||||
if execPod == nil {
|
if execPod == nil {
|
||||||
timeout = LoadBalancerPollTimeout
|
timeout = LoadBalancerPollTimeout
|
||||||
}
|
}
|
||||||
var tracker affinityTracker
|
var tracker affinityTracker
|
||||||
if pollErr := wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
|
// interval considering a maximum of 2 seconds per connection
|
||||||
|
interval := 2 * AffinityConfirmCount * time.Second
|
||||||
|
if pollErr := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||||
if execPod != nil {
|
if execPod != nil {
|
||||||
stdout, err := framework.RunHostCmd(execPod.Namespace, execPod.Name, cmd)
|
stdout, err := framework.RunHostCmd(execPod.Namespace, execPod.Name, cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
framework.Logf("Failed to get response from %s. Retry until timeout", serviceIPPort)
|
framework.Logf("Failed to get response from %s. Retry until timeout", serviceIPPort)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
tracker.recordHost(stdout)
|
hosts := strings.Split(stdout, "\n")
|
||||||
|
for _, host := range hosts {
|
||||||
|
tracker.recordHost(strings.TrimSpace(host))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rawResponse := GetHTTPContent(serviceIP, servicePort, timeout, "")
|
rawResponse := GetHTTPContent(serviceIP, servicePort, timeout, "")
|
||||||
tracker.recordHost(rawResponse.String())
|
tracker.recordHost(rawResponse.String())
|
||||||
@ -2357,28 +2363,42 @@ var _ = SIGDescribe("Services", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// [LinuxOnly]: Windows does not support session affinity.
|
// [LinuxOnly]: Windows does not support session affinity.
|
||||||
ginkgo.It("should have session affinity work for service with type clusterIP [LinuxOnly] [Flaky]", func() {
|
ginkgo.It("should have session affinity work for service with type clusterIP [LinuxOnly]", func() {
|
||||||
svc := getServeHostnameService("affinity-clusterip")
|
svc := getServeHostnameService("affinity-clusterip")
|
||||||
svc.Spec.Type = v1.ServiceTypeClusterIP
|
svc.Spec.Type = v1.ServiceTypeClusterIP
|
||||||
execAffinityTestForNonLBService(f, cs, svc)
|
execAffinityTestForNonLBService(f, cs, svc)
|
||||||
})
|
})
|
||||||
|
|
||||||
// [LinuxOnly]: Windows does not support session affinity.
|
// [LinuxOnly]: Windows does not support session affinity.
|
||||||
ginkgo.It("should be able to switch session affinity for service with type clusterIP [LinuxOnly] [Flaky]", func() {
|
ginkgo.It("should have session affinity timeout work for service with type clusterIP [LinuxOnly]", func() {
|
||||||
|
svc := getServeHostnameService("affinity-clusterip-timeout")
|
||||||
|
svc.Spec.Type = v1.ServiceTypeClusterIP
|
||||||
|
execAffinityTestForSessionAffinityTimeout(f, cs, svc)
|
||||||
|
})
|
||||||
|
|
||||||
|
// [LinuxOnly]: Windows does not support session affinity.
|
||||||
|
ginkgo.It("should be able to switch session affinity for service with type clusterIP [LinuxOnly]", func() {
|
||||||
svc := getServeHostnameService("affinity-clusterip-transition")
|
svc := getServeHostnameService("affinity-clusterip-transition")
|
||||||
svc.Spec.Type = v1.ServiceTypeClusterIP
|
svc.Spec.Type = v1.ServiceTypeClusterIP
|
||||||
execAffinityTestForNonLBServiceWithTransition(f, cs, svc)
|
execAffinityTestForNonLBServiceWithTransition(f, cs, svc)
|
||||||
})
|
})
|
||||||
|
|
||||||
// [LinuxOnly]: Windows does not support session affinity.
|
// [LinuxOnly]: Windows does not support session affinity.
|
||||||
ginkgo.It("should have session affinity work for NodePort service [LinuxOnly] [Flaky]", func() {
|
ginkgo.It("should have session affinity work for NodePort service [LinuxOnly]", func() {
|
||||||
svc := getServeHostnameService("affinity-nodeport")
|
svc := getServeHostnameService("affinity-nodeport")
|
||||||
svc.Spec.Type = v1.ServiceTypeNodePort
|
svc.Spec.Type = v1.ServiceTypeNodePort
|
||||||
execAffinityTestForNonLBService(f, cs, svc)
|
execAffinityTestForNonLBService(f, cs, svc)
|
||||||
})
|
})
|
||||||
|
|
||||||
// [LinuxOnly]: Windows does not support session affinity.
|
// [LinuxOnly]: Windows does not support session affinity.
|
||||||
ginkgo.It("should be able to switch session affinity for NodePort service [LinuxOnly] [Flaky]", func() {
|
ginkgo.It("should have session affinity timeout work for NodePort service [LinuxOnly]", func() {
|
||||||
|
svc := getServeHostnameService("affinity-nodeport-timeout")
|
||||||
|
svc.Spec.Type = v1.ServiceTypeNodePort
|
||||||
|
execAffinityTestForSessionAffinityTimeout(f, cs, svc)
|
||||||
|
})
|
||||||
|
|
||||||
|
// [LinuxOnly]: Windows does not support session affinity.
|
||||||
|
ginkgo.It("should be able to switch session affinity for NodePort service [LinuxOnly]", func() {
|
||||||
svc := getServeHostnameService("affinity-nodeport-transition")
|
svc := getServeHostnameService("affinity-nodeport-transition")
|
||||||
svc.Spec.Type = v1.ServiceTypeNodePort
|
svc.Spec.Type = v1.ServiceTypeNodePort
|
||||||
execAffinityTestForNonLBServiceWithTransition(f, cs, svc)
|
execAffinityTestForNonLBServiceWithTransition(f, cs, svc)
|
||||||
@ -3036,6 +3056,69 @@ func execSourceipTest(pausePod v1.Pod, serviceAddress string) (string, string) {
|
|||||||
return pausePod.Status.PodIP, host
|
return pausePod.Status.PodIP, host
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// execAffinityTestForSessionAffinityTimeout is a helper function that wrap the logic of
|
||||||
|
// affinity test for non-load-balancer services. Session afinity will be
|
||||||
|
// enabled when the service is created and a short timeout will be configured so
|
||||||
|
// session affinity must change after the timeout expirese.
|
||||||
|
func execAffinityTestForSessionAffinityTimeout(f *framework.Framework, cs clientset.Interface, svc *v1.Service) {
|
||||||
|
ns := f.Namespace.Name
|
||||||
|
numPods, servicePort, serviceName := 3, defaultServeHostnameServicePort, svc.ObjectMeta.Name
|
||||||
|
ginkgo.By("creating service in namespace " + ns)
|
||||||
|
serviceType := svc.Spec.Type
|
||||||
|
// set an affinity timeout equal to the number of connection requests
|
||||||
|
svcSessionAffinityTimeout := int32(AffinityConfirmCount)
|
||||||
|
svc.Spec.SessionAffinity = v1.ServiceAffinityClientIP
|
||||||
|
svc.Spec.SessionAffinityConfig = &v1.SessionAffinityConfig{
|
||||||
|
ClientIP: &v1.ClientIPConfig{TimeoutSeconds: &svcSessionAffinityTimeout},
|
||||||
|
}
|
||||||
|
_, _, err := StartServeHostnameService(cs, svc, ns, numPods)
|
||||||
|
framework.ExpectNoError(err, "failed to create replication controller with service in the namespace: %s", ns)
|
||||||
|
defer func() {
|
||||||
|
StopServeHostnameService(cs, ns, serviceName)
|
||||||
|
}()
|
||||||
|
jig := e2eservice.NewTestJig(cs, ns, serviceName)
|
||||||
|
svc, err = jig.Client.CoreV1().Services(ns).Get(context.TODO(), serviceName, metav1.GetOptions{})
|
||||||
|
framework.ExpectNoError(err, "failed to fetch service: %s in namespace: %s", serviceName, ns)
|
||||||
|
var svcIP string
|
||||||
|
if serviceType == v1.ServiceTypeNodePort {
|
||||||
|
nodes, err := e2enode.GetReadySchedulableNodes(cs)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
addrs := e2enode.CollectAddresses(nodes, v1.NodeInternalIP)
|
||||||
|
gomega.Expect(len(addrs)).To(gomega.BeNumerically(">", 0), "ginkgo.Failed to get Node internal IP")
|
||||||
|
svcIP = addrs[0]
|
||||||
|
servicePort = int(svc.Spec.Ports[0].NodePort)
|
||||||
|
} else {
|
||||||
|
svcIP = svc.Spec.ClusterIP
|
||||||
|
}
|
||||||
|
|
||||||
|
execPod := e2epod.CreateExecPodOrFail(cs, ns, "execpod-affinity", nil)
|
||||||
|
defer func() {
|
||||||
|
framework.Logf("Cleaning up the exec pod")
|
||||||
|
err := cs.CoreV1().Pods(ns).Delete(context.TODO(), execPod.Name, nil)
|
||||||
|
framework.ExpectNoError(err, "failed to delete pod: %s in namespace: %s", execPod.Name, ns)
|
||||||
|
}()
|
||||||
|
err = jig.CheckServiceReachability(svc, execPod)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
// the service should be sticky until the timeout expires
|
||||||
|
framework.ExpectEqual(checkAffinity(execPod, svcIP, servicePort, true), true)
|
||||||
|
// but it should return different hostnames after the timeout expires
|
||||||
|
// try several times to avoid the probability that we hit the same pod twice
|
||||||
|
hosts := sets.NewString()
|
||||||
|
cmd := fmt.Sprintf(`curl -q -s --connect-timeout 2 http://%s/`, net.JoinHostPort(svcIP, strconv.Itoa(servicePort)))
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
hostname, err := framework.RunHostCmd(execPod.Namespace, execPod.Name, cmd)
|
||||||
|
if err == nil {
|
||||||
|
hosts.Insert(hostname)
|
||||||
|
if hosts.Len() > 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(svcSessionAffinityTimeout) * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
framework.Fail("Session is sticky after reaching the timeout")
|
||||||
|
}
|
||||||
|
|
||||||
func execAffinityTestForNonLBServiceWithTransition(f *framework.Framework, cs clientset.Interface, svc *v1.Service) {
|
func execAffinityTestForNonLBServiceWithTransition(f *framework.Framework, cs clientset.Interface, svc *v1.Service) {
|
||||||
execAffinityTestForNonLBServiceWithOptionalTransition(f, cs, svc, true)
|
execAffinityTestForNonLBServiceWithOptionalTransition(f, cs, svc, true)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user