Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Retina e2e scale test #720

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions test/e2e/framework/helpers/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package helpers

import (
"context"
"testing"
"time"
)

const safetyTimeout = 24 * time.Hour

// Context returns a context with a deadline set to the test deadline - 1 min to ensure cleanup.
// If the test deadline is not set, a deadline is set to Now + 24h to prevent the test from running indefinitely
func Context(t *testing.T) (context.Context, context.CancelFunc) {
deadline, ok := t.Deadline()
if !ok {
t.Log("Test deadline disabled, deadline set to Now + 24h to prevent test from running indefinitely")
deadline = time.Now().Add(safetyTimeout)
}

// Subtract a minute from the deadline to ensure we have time to cleanup
deadline = deadline.Add(-time.Minute)

return context.WithDeadline(context.Background(), deadline)
}
59 changes: 59 additions & 0 deletions test/e2e/framework/kubernetes/create-namespace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package kubernetes

import (
"context"
"fmt"
"time"

v1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)

type CreateNamespace struct {
Namespace string
KubeConfigFilePath string
}

func (c *CreateNamespace) Run() error {
config, err := clientcmd.BuildConfigFromFlags("", c.KubeConfigFilePath)
if err != nil {
return fmt.Errorf("error building kubeconfig: %w", err)
}

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return fmt.Errorf("error creating Kubernetes client: %w", err)
}

ctx, cancel := context.WithTimeout(context.Background(), defaultTimeoutSeconds*time.Second)
defer cancel()

_, err = clientset.CoreV1().Namespaces().Create(ctx, c.getNamespace(), metaV1.CreateOptions{})
if err != nil {
return fmt.Errorf("failed to create namespace \"%s\": %w", c.Namespace, err)
}

return nil
}

func (c *CreateNamespace) Stop() error {
return nil
}

func (c *CreateNamespace) Prevalidate() error {
return nil
}

func (c *CreateNamespace) getNamespace() *v1.Namespace {
return &v1.Namespace{
TypeMeta: metaV1.TypeMeta{
Kind: "Namespace",
APIVersion: "v1",
},
ObjectMeta: metaV1.ObjectMeta{
Name: c.Namespace,
},
}
}
78 changes: 78 additions & 0 deletions test/e2e/framework/kubernetes/delete-namespace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package kubernetes

import (
"context"
"fmt"
"log"
"time"

"k8s.io/apimachinery/pkg/api/errors"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/retry"
)

type DeleteNamespace struct {
Namespace string
KubeConfigFilePath string
}

func (d *DeleteNamespace) Run() error {
config, err := clientcmd.BuildConfigFromFlags("", d.KubeConfigFilePath)
if err != nil {
return fmt.Errorf("error building kubeconfig: %w", err)
}

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return fmt.Errorf("error creating Kubernetes client: %w", err)
}

ctx, cancel := context.WithTimeout(context.Background(), defaultTimeoutSeconds*time.Second)
defer cancel()

err = clientset.CoreV1().Namespaces().Delete(ctx, d.Namespace, metaV1.DeleteOptions{})
if err != nil {
if !errors.IsNotFound(err) {
return fmt.Errorf("failed to delete namespace \"%s\": %w", d.Namespace, err)
}
}

backoff := wait.Backoff{
Steps: 6,
Duration: 10 * time.Second,
Factor: 2.0,
// Jitter: 0.1,
}

// Check if namespace was deleted
return retry.OnError(backoff,
func(err error) bool {
log.Printf("%v. Checking again soon...", err)

return true
},
func() error {
_, err = clientset.CoreV1().Namespaces().Get(ctx, d.Namespace, metaV1.GetOptions{})
if errors.IsNotFound(err) {
return nil
}

if err == nil {
return fmt.Errorf("namespace \"%s\" still exists", d.Namespace)
}

return err
},
)
}

func (d *DeleteNamespace) Stop() error {
return nil
}

func (d *DeleteNamespace) Prevalidate() error {
return nil
}
40 changes: 40 additions & 0 deletions test/e2e/framework/kubernetes/wait-pod-ready.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)

const (
Expand All @@ -19,6 +20,45 @@ const (
printInterval = 5 // print to stdout every 5 iterations
)

type WaitPodsReady struct {
KubeConfigFilePath string
Namespace string
LabelSelector string
}

// Useful when wanting to do parameter checking, for example
// if a parameter length is known to be required less than 80 characters,
// do this here so we don't find out later on when we run the step
// when possible, try to avoid making external calls, this should be fast and simple
func (w *WaitPodsReady) Prevalidate() error {
return nil
}

// Primary step where test logic is executed
// Returning an error will cause the test to fail
func (w *WaitPodsReady) Run() error {

config, err := clientcmd.BuildConfigFromFlags("", w.KubeConfigFilePath)
if err != nil {
return fmt.Errorf("error building kubeconfig: %w", err)
}

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return fmt.Errorf("error creating Kubernetes client: %w", err)
}

ctx, cancel := context.WithTimeout(context.Background(), defaultTimeoutSeconds*time.Second)
defer cancel()

return WaitForPodReady(ctx, clientset, w.Namespace, w.LabelSelector)
}

// Require for background steps
func (w *WaitPodsReady) Stop() error {
return nil
}

func WaitForPodReady(ctx context.Context, clientset *kubernetes.Clientset, namespace, labelSelector string) error {
podReadyMap := make(map[string]bool)

Expand Down
87 changes: 87 additions & 0 deletions test/e2e/framework/scaletest/add-shared-labels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package scaletest

import (
"context"
"encoding/json"
"fmt"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)

type patchStringValue struct {
Op string `json:"op"`
Path string `json:"path"`
Value string `json:"value"`
}

type AddSharedLabelsToAllPods struct {
KubeConfigFilePath string
NumSharedLabelsPerPod int
Namespace string
}

// Useful when wanting to do parameter checking, for example
// if a parameter length is known to be required less than 80 characters,
// do this here so we don't find out later on when we run the step
// when possible, try to avoid making external calls, this should be fast and simple
func (a *AddSharedLabelsToAllPods) Prevalidate() error {
return nil
}

// Primary step where test logic is executed
// Returning an error will cause the test to fail
func (a *AddSharedLabelsToAllPods) Run() error {

if a.NumSharedLabelsPerPod < 1 {
return nil
}

config, err := clientcmd.BuildConfigFromFlags("", a.KubeConfigFilePath)
if err != nil {
return fmt.Errorf("error building kubeconfig: %w", err)
}

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return fmt.Errorf("error creating Kubernetes client: %w", err)
}

ctx, cancel := context.WithTimeout(context.Background(), defaultTimeoutSeconds*time.Second)
defer cancel()

resources, err := clientset.CoreV1().Pods(a.Namespace).List(ctx, metav1.ListOptions{})

patch := []patchStringValue{}

for i := 0; i < a.NumSharedLabelsPerPod; i++ {
patch = append(patch, patchStringValue{
Op: "add",
Path: "/metadata/labels/shared-lab-" + fmt.Sprintf("%05d", i),
Value: "val",
})
}

patchBytes, err := json.Marshal(patch)
if err != nil {
return fmt.Errorf("error marshalling patch: %w", err)
}

for _, resource := range resources.Items {
clientset.CoreV1().Pods(a.Namespace).Patch(ctx, resource.Name,
types.JSONPatchType,
patchBytes,
metav1.PatchOptions{},
)
}

return nil
}

// Require for background steps
func (a *AddSharedLabelsToAllPods) Stop() error {
return nil
}
84 changes: 84 additions & 0 deletions test/e2e/framework/scaletest/add-unique-labels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package scaletest

import (
"context"
"encoding/json"
"fmt"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)

type AddUniqueLabelsToAllPods struct {
KubeConfigFilePath string
NumUniqueLabelsPerPod int
Namespace string
}

// Useful when wanting to do parameter checking, for example
// if a parameter length is known to be required less than 80 characters,
// do this here so we don't find out later on when we run the step
// when possible, try to avoid making external calls, this should be fast and simple
func (a *AddUniqueLabelsToAllPods) Prevalidate() error {
return nil
}

// Primary step where test logic is executed
// Returning an error will cause the test to fail
func (a *AddUniqueLabelsToAllPods) Run() error {

if a.NumUniqueLabelsPerPod < 1 {
return nil
}

config, err := clientcmd.BuildConfigFromFlags("", a.KubeConfigFilePath)
if err != nil {
return fmt.Errorf("error building kubeconfig: %w", err)
}

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return fmt.Errorf("error creating Kubernetes client: %w", err)
}

ctx, cancel := context.WithTimeout(context.Background(), defaultTimeoutSeconds*time.Second)
defer cancel()

resources, err := clientset.CoreV1().Pods(a.Namespace).List(ctx, metav1.ListOptions{})

count := 0

for _, resource := range resources.Items {
patch := []patchStringValue{}

for i := 0; i < a.NumUniqueLabelsPerPod; i++ {
patch = append(patch, patchStringValue{
Op: "add",
Path: "/metadata/labels/uni-lab-" + fmt.Sprintf("%05d", count),
Value: "val",
})
count++
}

patchBytes, err := json.Marshal(patch)
if err != nil {
return fmt.Errorf("error marshalling patch: %w", err)
}

clientset.CoreV1().Pods(a.Namespace).Patch(ctx, resource.Name,
types.JSONPatchType,
patchBytes,
metav1.PatchOptions{},
)
}

return nil
}

// Require for background steps
func (a *AddUniqueLabelsToAllPods) Stop() error {
return nil
}
Loading