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

Keep k8s resources on test failure #236

Merged
merged 4 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
14 changes: 9 additions & 5 deletions docs/01-development/01-contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,14 +205,12 @@ func Test_E2E(t *testing.T) {

// Execute test on cluster
t.Run("Test", func(t *testing.T){
// create namespace for test
namespace, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), "test")
// create namespace for test. Will be deleted automatically when the test ends
namespace, err := namespace.CreateNamespace(context.TODO(), t, k8s.Client())
if err != nil {
t.Errorf("error creating test namespace: %v", err)
return
}
// delete test resources when test ends
defer k8s.Client().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})

// create test resources using k8s fixtures

Expand Down Expand Up @@ -260,4 +258,10 @@ It is possible to specify that we want to reuse a test cluster is one exists wit
When you don't longer needs the cluster, you can delete it using kind:
```sh
kind delete cluster --name=<cluster name>
```
```

### Keeping Kubernetes resources for debugging

By default, test namespaces created with `CreateTestNamespace` are automatically deleted when a test ends. This is inconvenient for debugging failed tests.

This behavior is controlled by passing the `WithKeepOnFail` option when creating the namespace or by setting `E2E_KEEPONFAIL=true` in the environment when running an e2e test.
18 changes: 8 additions & 10 deletions e2e/agent/agent_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import (
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/cluster"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/deploy"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/fixtures"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/kubernetes/namespace"
"github.com/grafana/xk6-disruptor/pkg/testutils/kubernetes/builders"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)

Expand Down Expand Up @@ -222,12 +222,12 @@ func Test_Agent(t *testing.T) {
tc := tc
t.Run(tc.title, func(t *testing.T) {
t.Parallel()
namespace, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), "test-")

namespace, err := namespace.CreateTestNamespace(context.TODO(), t, k8s.Client())
if err != nil {
t.Errorf("error creating test namespace: %v", err)
t.Errorf("failed to create test namespace: %v", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a discussion for another PR, but t.Fatalf would be preferred in this cases, as it protects us from forgetting the return line below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to unify this in another PR.

return
}
defer k8s.Client().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})

err = deploy.ExposeApp(
k8s,
Expand All @@ -254,12 +254,11 @@ func Test_Agent(t *testing.T) {
t.Run("Prevent execution of multiple commands", func(t *testing.T) {
t.Parallel()

namespace, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), "test-")
namespace, err := namespace.CreateTestNamespace(context.TODO(), t, k8s.Client())
if err != nil {
t.Errorf("error creating test namespace: %v", err)
t.Errorf("failed to create test namespace: %v", err)
return
}
defer k8s.Client().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})

err = deploy.RunPod(
k8s,
Expand Down Expand Up @@ -290,12 +289,11 @@ func Test_Agent(t *testing.T) {
t.Run("Non-transparent proxy to upstream service", func(t *testing.T) {
t.Parallel()

namespace, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), "test-")
namespace, err := namespace.CreateTestNamespace(context.TODO(), t, k8s.Client())
if err != nil {
t.Errorf("error creating test namespace: %v", err)
t.Errorf("failed to create test namespace: %v", err)
return
}
defer k8s.Client().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})

err = deploy.ExposeApp(
k8s,
Expand Down
7 changes: 3 additions & 4 deletions e2e/disruptors/pod_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"time"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"

"github.com/grafana/xk6-disruptor/pkg/disruptors"
Expand All @@ -18,6 +17,7 @@ import (
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/cluster"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/deploy"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/fixtures"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/kubernetes/namespace"
)

func Test_PodDisruptor(t *testing.T) {
Expand Down Expand Up @@ -111,12 +111,11 @@ func Test_PodDisruptor(t *testing.T) {
t.Run(tc.title, func(t *testing.T) {
t.Parallel()

namespace, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), "test-pods")
namespace, err := namespace.CreateTestNamespace(context.TODO(), t, k8s.Client())
if err != nil {
t.Errorf("error creating test namespace: %v", err)
t.Errorf("failed to create test namespace: %v", err)
return
}
defer k8s.Client().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})

err = deploy.ExposeApp(
k8s,
Expand Down
8 changes: 4 additions & 4 deletions e2e/disruptors/service_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"time"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"

"github.com/grafana/xk6-disruptor/pkg/disruptors"
Expand All @@ -18,6 +17,8 @@ import (
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/cluster"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/deploy"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/fixtures"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/kubernetes/namespace"

)

func Test_ServiceDisruptor(t *testing.T) {
Expand Down Expand Up @@ -80,12 +81,11 @@ func Test_ServiceDisruptor(t *testing.T) {
t.Run(tc.title, func(t *testing.T) {
t.Parallel()

namespace, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), "test-pods")
namespace, err := namespace.CreateTestNamespace(context.TODO(), t, k8s.Client())
if err != nil {
t.Errorf("error creating test namespace: %v", err)
t.Errorf("failed to create test namespace: %v", err)
return
}
defer k8s.Client().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})

err = deploy.ExposeApp(
k8s,
Expand Down
42 changes: 9 additions & 33 deletions e2e/kubernetes/kubernetes_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package e2e

import (
"context"
"strings"
"testing"
"time"

Expand All @@ -15,6 +14,7 @@ import (
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/cluster"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/deploy"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/fixtures"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/kubernetes/namespace"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -38,34 +38,13 @@ func Test_Kubernetes(t *testing.T) {
return
}

kubeconfig := cluster.Kubeconfig()

// Test Creating a random namespace
t.Run("Create Random Namespace", func(t *testing.T) {
k8s, err := kubernetes.NewFromKubeconfig(kubeconfig)
if err != nil {
t.Errorf("error creating kubernetes client: %v", err)
return
}
prefix := "test"
ns, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), prefix)
if err != nil {
t.Errorf("unexpected error creating namespace: %v", err)
return
}
if !strings.HasPrefix(ns, prefix) {
t.Errorf("returned namespace does not have expected prefix '%s': %s", prefix, ns)
}
})

// Test Wait Pod Running
t.Run("Wait Pod is Running", func(t *testing.T) {
namespace, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), "test-")
namespace, err := namespace.CreateTestNamespace(context.TODO(), t, k8s.Client())
if err != nil {
t.Errorf("error creating test namespace: %v", err)
t.Errorf("failed to create test namespace: %v", err)
return
}
defer k8s.Client().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})

// Deploy nginx
_, err = k8s.Client().CoreV1().Pods(namespace).Create(
Expand All @@ -92,12 +71,11 @@ func Test_Kubernetes(t *testing.T) {

// Test Wait Service Ready helper
t.Run("Wait Service Ready", func(t *testing.T) {
namespace, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), "test-")
namespace, err := namespace.CreateTestNamespace(context.TODO(), t, k8s.Client())
if err != nil {
t.Errorf("error creating test namespace: %v", err)
t.Errorf("failed to create test namespace: %v", err)
return
}
defer k8s.Client().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})

// Deploy nginx and expose it as a service. Intentionally not using e2e fixures
// because these functions rely on WaitPodRunnin and WaitServiceReady which we
Expand Down Expand Up @@ -131,12 +109,11 @@ func Test_Kubernetes(t *testing.T) {
})

t.Run("Exec Command", func(t *testing.T) {
namespace, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), "test-")
namespace, err := namespace.CreateTestNamespace(context.TODO(), t, k8s.Client())
if err != nil {
t.Errorf("error creating test namespace: %v", err)
t.Errorf("failed to create test namespace: %v", err)
return
}
defer k8s.Client().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})

err = deploy.RunPod(k8s, namespace, fixtures.BuildBusyBoxPod(), 10*time.Second)
if err != nil {
Expand All @@ -163,12 +140,11 @@ func Test_Kubernetes(t *testing.T) {
})

t.Run("Attach Ephemeral Container", func(t *testing.T) {
namespace, err := k8s.NamespaceHelper().CreateRandomNamespace(context.TODO(), "test-")
namespace, err := namespace.CreateTestNamespace(context.TODO(), t, k8s.Client())
if err != nil {
t.Errorf("error creating test namespace: %v", err)
t.Errorf("failed to create test namespace: %v", err)
return
}
defer k8s.Client().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})

err = deploy.RunPod(k8s, namespace, fixtures.BuildPausedPod(), 10*time.Second)
if err != nil {
Expand Down
7 changes: 0 additions & 7 deletions pkg/kubernetes/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,6 @@ func (f *FakeKubernetes) ServiceHelper(namespace string) helpers.ServiceHelper {
)
}

// NamespaceHelper returns a NamespaceHelper for the given namespace
func (f *FakeKubernetes) NamespaceHelper() helpers.NamespaceHelper {
return helpers.NewFakeNamespaceHelper(
f.client,
)
}

// Client return a kubernetes client
func (f *FakeKubernetes) Client() kubernetes.Interface {
return f.client
Expand Down
14 changes: 0 additions & 14 deletions pkg/kubernetes/helpers/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,6 @@ func NewFakeServiceHelper(
}
}

// fakeNamespaceHelper is an fake instance of a NamespaceHelper. It can delegate to the actual
// helper the execution of actions or override them when needed
type fakeNamespaceHelper struct {
NamespaceHelper
}

// NewFakeNamespaceHelper creates a set of a NamespaceHelper on the default namespace
func NewFakeNamespaceHelper(client kubernetes.Interface) NamespaceHelper {
h := NewNamespaceHelper(client)
return &fakeNamespaceHelper{
h,
}
}

// FakeHTTPClient implement a fake HTTPClient that returns a fixed response.
// When invoked, it records the request it received
type FakeHTTPClient struct {
Expand Down
43 changes: 0 additions & 43 deletions pkg/kubernetes/helpers/namespaces.go

This file was deleted.

9 changes: 0 additions & 9 deletions pkg/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ type Kubernetes interface {
ServiceHelper(namespace string) helpers.ServiceHelper
// PodHelper returns a helpers.PodHelper scoped for the given namespace
PodHelper(namespace string) helpers.PodHelper
// NamespaceHelper returns a namespace helper
NamespaceHelper() helpers.NamespaceHelper
}

// k8s Holds the reference to the helpers for interacting with kubernetes
Expand Down Expand Up @@ -127,13 +125,6 @@ func (k *k8s) PodHelper(namespace string) helpers.PodHelper {
)
}

// NamespaceHelper returns a NamespaceHelper
func (k *k8s) NamespaceHelper() helpers.NamespaceHelper {
return helpers.NewNamespaceHelper(
k.Interface,
)
}

func (k *k8s) Client() kubernetes.Interface {
return k.Interface
}
2 changes: 2 additions & 0 deletions pkg/testutils/e2e/kubernetes/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package kubernetes implements helper functions for handling k8s resources in e2e tests
package kubernetes
43 changes: 43 additions & 0 deletions pkg/testutils/e2e/kubernetes/namespace/keeponfail_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//go:build keeponfail
// +build keeponfail

package namespace

import (
"context"
"testing"

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

// Test_KeepOnFail is expected to fail as it tests the preservation of namespace
// in case of a test failure
// It only runs if the TEST_KEEPONFAIL=true is specified.
func Test_KeepOnFail(t *testing.T) {
client := fake.NewSimpleClientset()

t.Run("Sub-test", func(t *testing.T) {
_, err := CreateTestNamespace(
context.TODO(),
t,
client,
WithKeepOnFail(true),
WithName("testns"),
)
if err != nil {
t.Errorf("unexpected error %v", err)
return
}

t.Fail()
})

_, err := client.CoreV1().Namespaces().Get(context.TODO(), "testns", metav1.GetOptions{})
if err != nil {
t.Errorf("namespace could not be accessed after test failed: %v", err)
return
}

t.Logf("test succeeded. Namespace %s was preserved after subtest failed", "testns")
}
Loading