Skip to content

Commit

Permalink
add e2e test for slr (#2841)
Browse files Browse the repository at this point in the history
  • Loading branch information
qiutingjun committed May 26, 2023
1 parent 20b2036 commit a80b375
Show file tree
Hide file tree
Showing 5 changed files with 620 additions and 0 deletions.
155 changes: 155 additions & 0 deletions test/e2e/framework/endpoints.go
@@ -0,0 +1,155 @@
package framework

import (
"context"
"errors"
"fmt"
"time"

"github.com/kubeovn/kube-ovn/pkg/util"
"github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/kubernetes/test/e2e/framework"
)

// EndpointsClient is a struct for endpoint client.
type EndpointsClient struct {
f *Framework
v1core.EndpointsInterface
}

func (f *Framework) EndpointClient() *EndpointsClient {
return f.EndpointsClientNS(f.Namespace.Name)
}

func (f *Framework) EndpointsClientNS(namespace string) *EndpointsClient {
return &EndpointsClient{
f: f,
EndpointsInterface: f.ClientSet.CoreV1().Endpoints(namespace),
}
}

func (c *EndpointsClient) Get(name string) *corev1.Endpoints {
endpoints, err := c.EndpointsInterface.Get(context.TODO(), name, metav1.GetOptions{})
ExpectNoError(err)
return endpoints
}

// Create creates a new endpoints according to the framework specifications
func (c *EndpointsClient) Create(endpoints *corev1.Endpoints) *corev1.Endpoints {
e, err := c.EndpointsInterface.Create(context.TODO(), endpoints, metav1.CreateOptions{})
ExpectNoError(err, "Error creating endpoints")
return e.DeepCopy()
}

// CreateSync creates a new endpoints according to the framework specifications, and waits for it to be updated.
func (c *EndpointsClient) CreateSync(endpoints *corev1.Endpoints, cond func(s *corev1.Endpoints) (bool, error), condDesc string) *corev1.Endpoints {
_ = c.Create(endpoints)
return c.WaitUntil(endpoints.Name, cond, condDesc, 2*time.Second, timeout)
}

// Patch patches the endpoints
func (c *EndpointsClient) Patch(original, modified *corev1.Endpoints) *corev1.Endpoints {
patch, err := util.GenerateMergePatchPayload(original, modified)
ExpectNoError(err)

var patchedEndpoints *corev1.Endpoints
err = wait.PollUntilContextTimeout(context.Background(), 2*time.Second, timeout, true, func(ctx context.Context) (bool, error) {
s, err := c.EndpointsInterface.Patch(context.TODO(), original.Name, types.MergePatchType, patch, metav1.PatchOptions{}, "")
if err != nil {
return handleWaitingAPIError(err, false, "patch endpoints %q", original.Name)
}
patchedEndpoints = s
return true, nil
})
if err == nil {
return patchedEndpoints.DeepCopy()
}

if errors.Is(err, context.DeadlineExceeded) {
Failf("timed out while retrying to patch endpoints %s", original.Name)
}
Failf("error occurred while retrying to patch endpoints %s: %v", original.Name, err)

return nil
}

// PatchSync patches the endpoints and waits the endpoints to meet the condition
func (c *EndpointsClient) PatchSync(original, modified *corev1.Endpoints, cond func(s *corev1.Endpoints) (bool, error), condDesc string) *corev1.Endpoints {
_ = c.Patch(original, modified)
return c.WaitUntil(original.Name, cond, condDesc, 2*time.Second, timeout)
}

// Delete deletes a endpoints if the endpoints exists
func (c *EndpointsClient) Delete(name string) {
err := c.EndpointsInterface.Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil && !apierrors.IsNotFound(err) {
Failf("Failed to delete endpoints %q: %v", name, err)
}
}

// DeleteSync deletes the endpoints and waits for the endpoints to disappear for `timeout`.
// If the endpoints doesn't disappear before the timeout, it will fail the test.
func (c *EndpointsClient) DeleteSync(name string) {
c.Delete(name)
gomega.Expect(c.WaitToDisappear(name, 2*time.Second, timeout)).To(gomega.Succeed(), "wait for endpoints %q to disappear", name)
}

// WaitUntil waits the given timeout duration for the specified condition to be met.
func (c *EndpointsClient) WaitUntil(name string, cond func(s *corev1.Endpoints) (bool, error), condDesc string, interval, timeout time.Duration) *corev1.Endpoints {
var endpoints *corev1.Endpoints
err := wait.PollUntilContextTimeout(context.Background(), 2*time.Second, timeout, true, func(ctx context.Context) (bool, error) {
Logf("Waiting for endpoints %s to meet condition %q", name, condDesc)
endpoints = c.Get(name).DeepCopy()
met, err := cond(endpoints)
if err != nil {
return false, fmt.Errorf("failed to check condition for endpoints %s: %v", name, err)
}
if met {
Logf("endpoints %s met condition %q", name, condDesc)
} else {
Logf("endpoints %s not met condition %q", name, condDesc)
}
return met, nil
})
if err == nil {
return endpoints
}

if errors.Is(err, context.DeadlineExceeded) {
Failf("timed out while retrying to patch endpoints %s", name)
}
Failf("error occurred while retrying to patch endpoints %s: %v", name, err)

return nil
}

// WaitToDisappear waits the given timeout duration for the specified endpoints to disappear.
func (c *EndpointsClient) WaitToDisappear(name string, interval, timeout time.Duration) error {
err := framework.Gomega().Eventually(context.Background(), framework.HandleRetry(func(ctx context.Context) (*corev1.Endpoints, error) {
svc, err := c.EndpointsInterface.Get(ctx, name, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return nil, nil
}
return svc, err
})).WithTimeout(timeout).Should(gomega.BeNil())
if err != nil {
return fmt.Errorf("expected endpoints %s to not be found: %w", name, err)
}
return nil
}

func MakeEndpoints(name string, annotations map[string]string, subset []corev1.EndpointSubset) *corev1.Endpoints {
return &corev1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Annotations: annotations,
},
Subsets: subset,
}
}
165 changes: 165 additions & 0 deletions test/e2e/framework/switch-lb-rule.go
@@ -0,0 +1,165 @@
package framework

import (
"context"
"errors"
"fmt"
"time"

corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/test/e2e/framework"

apiv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
v1 "github.com/kubeovn/kube-ovn/pkg/client/clientset/versioned/typed/kubeovn/v1"
"github.com/kubeovn/kube-ovn/pkg/util"
"github.com/onsi/gomega"
)

// SwitchLBRuleClient is a struct for switch-lb-rule client.
type SwitchLBRuleClient struct {
f *Framework
v1.SwitchLBRuleInterface
}

func (f *Framework) SwitchLBRuleClient() *SwitchLBRuleClient {
return f.SwitchLBRuleClientNS(f.Namespace.Name)
}

func (f *Framework) SwitchLBRuleClientNS(namespace string) *SwitchLBRuleClient {
return &SwitchLBRuleClient{
f: f,
SwitchLBRuleInterface: f.KubeOVNClientSet.KubeovnV1().SwitchLBRules(),
}
}

func (c *SwitchLBRuleClient) Get(name string) *apiv1.SwitchLBRule {
rules, err := c.SwitchLBRuleInterface.Get(context.TODO(), name, metav1.GetOptions{})
ExpectNoError(err)
return rules
}

// Create creates a new switch-lb-rule according to the framework specifications
func (c *SwitchLBRuleClient) Create(rule *apiv1.SwitchLBRule) *apiv1.SwitchLBRule {
e, err := c.SwitchLBRuleInterface.Create(context.TODO(), rule, metav1.CreateOptions{})
ExpectNoError(err, "error creating switch-lb-rule")
return e.DeepCopy()
}

// CreateSync creates a new switch-lb-rule according to the framework specifications, and waits for it to be updated.
func (c *SwitchLBRuleClient) CreateSync(rule *apiv1.SwitchLBRule, cond func(s *apiv1.SwitchLBRule) (bool, error), condDesc string) *apiv1.SwitchLBRule {
_ = c.Create(rule)
return c.WaitUntil(rule.Name, cond, condDesc, 2*time.Second, timeout)
}

// Patch patches the switch-lb-rule
func (c *SwitchLBRuleClient) Patch(original, modified *apiv1.SwitchLBRule) *apiv1.SwitchLBRule {
patch, err := util.GenerateMergePatchPayload(original, modified)
ExpectNoError(err)

var patchedService *apiv1.SwitchLBRule
err = wait.PollUntilContextTimeout(context.Background(), 2*time.Second, timeout, true, func(ctx context.Context) (bool, error) {
s, err := c.SwitchLBRuleInterface.Patch(context.TODO(), original.Name, types.MergePatchType, patch, metav1.PatchOptions{}, "")
if err != nil {
return handleWaitingAPIError(err, false, "patch switch-lb-rule %q", original.Name)
}
patchedService = s
return true, nil
})
if err == nil {
return patchedService.DeepCopy()
}

if errors.Is(err, context.DeadlineExceeded) {
Failf("timed out while retrying to patch switch-lb-rule %s", original.Name)
}
Failf("error occurred while retrying to patch switch-lb-rule %s: %v", original.Name, err)

return nil
}

// PatchSync patches the switch-lb-rule and waits the switch-lb-rule to meet the condition
func (c *SwitchLBRuleClient) PatchSync(original, modified *apiv1.SwitchLBRule, cond func(s *apiv1.SwitchLBRule) (bool, error), condDesc string) *apiv1.SwitchLBRule {
_ = c.Patch(original, modified)
return c.WaitUntil(original.Name, cond, condDesc, 2*time.Second, timeout)
}

// Delete deletes a switch-lb-rule if the switch-lb-rule exists
func (c *SwitchLBRuleClient) Delete(name string) {
err := c.SwitchLBRuleInterface.Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil && !apierrors.IsNotFound(err) {
Failf("Failed to delete switch-lb-rule %q: %v", name, err)
}
}

// DeleteSync deletes the switch-lb-rule and waits for the switch-lb-rule to disappear for `timeout`.
// If the switch-lb-rule doesn't disappear before the timeout, it will fail the test.
func (c *SwitchLBRuleClient) DeleteSync(name string) {
c.Delete(name)
gomega.Expect(c.WaitToDisappear(name, 2*time.Second, timeout)).To(gomega.Succeed(), "wait for switch-lb-rule %q to disappear", name)
}

// WaitUntil waits the given timeout duration for the specified condition to be met.
func (c *SwitchLBRuleClient) WaitUntil(name string, cond func(s *apiv1.SwitchLBRule) (bool, error), condDesc string, interval, timeout time.Duration) *apiv1.SwitchLBRule {
var rules *apiv1.SwitchLBRule
err := wait.PollUntilContextTimeout(context.Background(), 2*time.Second, timeout, true, func(ctx context.Context) (bool, error) {
Logf("Waiting for switch-lb-rule %s to meet condition %q", name, condDesc)
rules = c.Get(name).DeepCopy()
met, err := cond(rules)
if err != nil {
return false, fmt.Errorf("failed to check condition for switch-lb-rule %s: %v", name, err)
}
if met {
Logf("switch-lb-rule %s met condition %q", name, condDesc)
} else {
Logf("switch-lb-rule %s not met condition %q", name, condDesc)
}
return met, nil
})
if err == nil {
return rules
}

if errors.Is(err, context.DeadlineExceeded) {
Failf("timed out while retrying to patch switch-lb-rule %s", name)
}
Failf("error occurred while retrying to patch switch-lb-rule %s: %v", name, err)

return nil
}

// WaitToDisappear waits the given timeout duration for the specified switch-lb-rule to disappear.
func (c *SwitchLBRuleClient) WaitToDisappear(name string, interval, timeout time.Duration) error {
err := framework.Gomega().Eventually(context.Background(), framework.HandleRetry(func(ctx context.Context) (*apiv1.SwitchLBRule, error) {
svc, err := c.SwitchLBRuleInterface.Get(ctx, name, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return nil, nil
}
return svc, err
})).WithTimeout(timeout).Should(gomega.BeNil())
if err != nil {
return fmt.Errorf("expected endpoints %s to not be found: %w", name, err)
}
return nil
}

func MakeSwitchLBRule(name, namespace, vip string, sessionAffinity corev1.ServiceAffinity, annotations map[string]string, slector, endpoints []string, ports []apiv1.SlrPort) *apiv1.SwitchLBRule {
return &apiv1.SwitchLBRule{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Annotations: annotations,
},
Spec: apiv1.SwitchLBRuleSpec{
Vip: vip,
Namespace: namespace,
Selector: slector,
Endpoints: endpoints,
SessionAffinity: string(sessionAffinity),
Ports: ports,
},
}
}
1 change: 1 addition & 0 deletions test/e2e/kube-ovn/e2e_test.go
Expand Up @@ -22,6 +22,7 @@ import (
_ "github.com/kubeovn/kube-ovn/test/e2e/kube-ovn/qos"
_ "github.com/kubeovn/kube-ovn/test/e2e/kube-ovn/service"
_ "github.com/kubeovn/kube-ovn/test/e2e/kube-ovn/subnet"
_ "github.com/kubeovn/kube-ovn/test/e2e/kube-ovn/switch_lb_rule"
_ "github.com/kubeovn/kube-ovn/test/e2e/kube-ovn/underlay"
)

Expand Down

0 comments on commit a80b375

Please sign in to comment.