Skip to content

Commit 5bb028d

Browse files
committed
Introduce a mechanism for gathering test artifacts during individual test failures (#2442)
* Introduce mechanism for gathering test artifacts during individual test failures Update the testing e2e suite and add a mechanism for gathering test artifacts during individual test failures. Currently, container logs are gathered when deprovisioning upstream kind clusters, yet we lack fine-grain ability to diagnose test failures further. Note: test failures use the `CurrentGinkgoTestDescription.Failed` field to determine failures. Testing artifacts are only gathered when the base $ARTIFACTS_DIR environment variable has been specified. Add a collect-ci-artifacts.sh bash script, responsible for gathering OLM native resources for an individual testing namespace. This bash script will be called when tearing down the generated testing namespace for relevant e2e packages. Currently, the artifact gathering process is restricted to only a single namespace - longer term, it might be possible to instead migrate towards collecting resources that all share a similar label selector, and utilizing that label selector to handle multi-namespace testing scenarios. Introduce another helper function in test/e2e/util_test.go that's responsible for gathering test artifacts (i.e. calling this newly introduced script) when the test case had failed, and in either case, delete the namespace. Signed-off-by: timflannagan <[email protected]> * test/e2e: Refactor the bundle e2e tests to avoid using global test namespace Signed-off-by: timflannagan <[email protected]> Upstream-repository: operator-lifecycle-manager Upstream-commit: 4cc28bb74ff6721eb41491ec57c55cc47bc1ad84
1 parent 6b14127 commit 5bb028d

File tree

5 files changed

+77
-22
lines changed

5 files changed

+77
-22
lines changed

staging/operator-lifecycle-manager/test/e2e/bundle_e2e_test.go

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/json"
77

88
"github.com/ghodss/yaml"
9+
corev1 "k8s.io/api/core/v1"
910
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1011
k8serrors "k8s.io/apimachinery/pkg/api/errors"
1112
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -27,19 +28,23 @@ var vpaCRDRaw []byte
2728

2829
var _ = Describe("Installing bundles with new object types", func() {
2930
var (
30-
kubeClient operatorclient.ClientInterface
31-
operatorClient versioned.Interface
32-
dynamicClient dynamic.Interface
31+
kubeClient operatorclient.ClientInterface
32+
operatorClient versioned.Interface
33+
dynamicClient dynamic.Interface
34+
generatedNamespace corev1.Namespace
3335
)
3436

3537
BeforeEach(func() {
3638
kubeClient = ctx.Ctx().KubeClient()
3739
operatorClient = ctx.Ctx().OperatorClient()
3840
dynamicClient = ctx.Ctx().DynamicClient()
41+
42+
By("creating a test namespace")
43+
generatedNamespace = SetupGeneratedTestNamespace(genName("bundle-e2e-"))
3944
})
4045

4146
AfterEach(func() {
42-
TearDown(testNamespace)
47+
TeardownNamespace(generatedNamespace.GetName())
4348
})
4449

4550
When("a bundle with a pdb, priorityclass, and VPA object is installed", func() {
@@ -66,7 +71,7 @@ var _ = Describe("Installing bundles with new object types", func() {
6671
Expect(err).ToNot(HaveOccurred(), "could not convert vpa crd to unstructured")
6772

6873
Eventually(func() error {
69-
err := ctx.Ctx().Client().Create(context.TODO(), &vpaCRD)
74+
err := ctx.Ctx().Client().Create(context.Background(), &vpaCRD)
7075
if err != nil {
7176
if !k8serrors.IsAlreadyExists(err) {
7277
return err
@@ -77,22 +82,21 @@ var _ = Describe("Installing bundles with new object types", func() {
7782

7883
// ensure vpa crd is established and accepted on the cluster before continuing
7984
Eventually(func() (bool, error) {
80-
crd, err := kubeClient.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), vpaCRD.GetName(), metav1.GetOptions{})
85+
crd, err := kubeClient.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(), vpaCRD.GetName(), metav1.GetOptions{})
8186
if err != nil {
8287
return false, err
8388
}
8489
return crdReady(&crd.Status), nil
8590
}).Should(BeTrue())
8691

87-
var installPlanRef string
8892
source := &v1alpha1.CatalogSource{
8993
TypeMeta: metav1.TypeMeta{
9094
Kind: v1alpha1.CatalogSourceKind,
9195
APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
9296
},
9397
ObjectMeta: metav1.ObjectMeta{
9498
Name: sourceName,
95-
Namespace: testNamespace,
99+
Namespace: generatedNamespace.GetName(),
96100
Labels: map[string]string{"olm.catalogSource": sourceName},
97101
},
98102
Spec: v1alpha1.CatalogSourceSpec{
@@ -102,7 +106,7 @@ var _ = Describe("Installing bundles with new object types", func() {
102106
}
103107

104108
Eventually(func() error {
105-
source, err = operatorClient.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.TODO(), source, metav1.CreateOptions{})
109+
source, err = operatorClient.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
106110
return err
107111
}).Should(Succeed())
108112

@@ -114,13 +118,13 @@ var _ = Describe("Installing bundles with new object types", func() {
114118
_ = createSubscriptionForCatalog(operatorClient, source.GetNamespace(), subName, source.GetName(), packageName, channelName, "", v1alpha1.ApprovalAutomatic)
115119

116120
// Wait for the Subscription to succeed
117-
sub, err := fetchSubscription(operatorClient, testNamespace, subName, subscriptionStateAtLatestChecker)
121+
sub, err := fetchSubscription(operatorClient, generatedNamespace.GetName(), subName, subscriptionStateAtLatestChecker)
118122
Expect(err).ToNot(HaveOccurred(), "could not get subscription at latest status")
119123

120-
installPlanRef = sub.Status.InstallPlanRef.Name
124+
installPlanRef := sub.Status.InstallPlanRef
121125

122126
// Wait for the installplan to complete (5 minute timeout)
123-
_, err = fetchInstallPlan(GinkgoT(), operatorClient, installPlanRef, buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseComplete))
127+
_, err = fetchInstallPlanWithNamespace(GinkgoT(), operatorClient, installPlanRef.Name, installPlanRef.Namespace, buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseComplete))
124128
Expect(err).ToNot(HaveOccurred(), "could not get installplan at complete phase")
125129

126130
ctx.Ctx().Logf("install plan %s completed", installPlanRef)
@@ -144,25 +148,25 @@ var _ = Describe("Installing bundles with new object types", func() {
144148

145149
// confirm extra bundle objects are installed
146150
Eventually(func() error {
147-
_, err := kubeClient.KubernetesInterface().SchedulingV1().PriorityClasses().Get(context.TODO(), priorityClassName, metav1.GetOptions{})
151+
_, err := kubeClient.KubernetesInterface().SchedulingV1().PriorityClasses().Get(context.Background(), priorityClassName, metav1.GetOptions{})
148152
return err
149153
}).Should(Succeed(), "expected no error getting priorityclass object associated with CSV")
150154

151155
Eventually(func() error {
152-
_, err := dynamicClient.Resource(resource).Namespace(testNamespace).Get(context.TODO(), vpaName, metav1.GetOptions{})
156+
_, err := dynamicClient.Resource(resource).Namespace(generatedNamespace.GetName()).Get(context.Background(), vpaName, metav1.GetOptions{})
153157
return err
154158
}).Should(Succeed(), "expected no error finding vpa object associated with csv")
155159

156160
Eventually(func() error {
157-
_, err := kubeClient.KubernetesInterface().PolicyV1().PodDisruptionBudgets(testNamespace).Get(context.TODO(), pdbName, metav1.GetOptions{})
161+
_, err := kubeClient.KubernetesInterface().PolicyV1().PodDisruptionBudgets(generatedNamespace.GetName()).Get(context.Background(), pdbName, metav1.GetOptions{})
158162
return err
159163
}).Should(Succeed(), "expected no error getting pdb object associated with CSV")
160164
})
161165

162166
AfterEach(func() {
163167
By("Deleting the VPA CRD")
164168
Eventually(func() error {
165-
err := ctx.Ctx().Client().Delete(context.TODO(), &vpaCRD)
169+
err := ctx.Ctx().Client().Delete(context.Background(), &vpaCRD)
166170
if k8serrors.IsNotFound(err) {
167171
return nil
168172
}

staging/operator-lifecycle-manager/test/e2e/ctx/ctx.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package ctx
22

33
import (
44
"fmt"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
58
"strings"
69

710
. "github.com/onsi/ginkgo"
@@ -32,6 +35,7 @@ type TestContext struct {
3235
dynamicClient dynamic.Interface
3336
packageClient pversioned.Interface
3437
ssaClient *controllerclient.ServerSideApplier
38+
artifactsDir string
3539

3640
scheme *runtime.Scheme
3741

@@ -86,6 +90,35 @@ func (ctx TestContext) SSAClient() *controllerclient.ServerSideApplier {
8690
return ctx.ssaClient
8791
}
8892

93+
func (ctx TestContext) DumpNamespaceArtifacts(namespace string) error {
94+
if ctx.artifactsDir == "" {
95+
ctx.Logf("$ARTIFACTS_DIR is unset -- not collecting failed test case logs")
96+
return nil
97+
}
98+
ctx.Logf("collecting logs in the %s artifacts directory", ctx.artifactsDir)
99+
100+
logDir := filepath.Join(ctx.artifactsDir, namespace)
101+
if err := os.MkdirAll(logDir, os.ModePerm); err != nil {
102+
return err
103+
}
104+
envvars := []string{
105+
"TEST_NAMESPACE=" + namespace,
106+
"TEST_ARTIFACTS_DIR=" + logDir,
107+
"KUBECONFIG=" + os.Getenv("KUBECONFIG"),
108+
}
109+
110+
// compiled test binary running e2e tests is run from the root ./bin directory
111+
cmd := exec.Command("../test/e2e/collect-ci-artifacts.sh")
112+
cmd.Env = append(cmd.Env, envvars...)
113+
cmd.Stdout = os.Stdout
114+
cmd.Stderr = os.Stderr
115+
if err := cmd.Run(); err != nil {
116+
return err
117+
}
118+
119+
return nil
120+
}
121+
89122
func setDerivedFields(ctx *TestContext) error {
90123
if ctx == nil {
91124
return fmt.Errorf("nil test context")

staging/operator-lifecycle-manager/test/e2e/ctx/provisioner_kind.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,18 @@ func Provision(ctx *TestContext) (func(), error) {
134134
if err != nil {
135135
return nil, fmt.Errorf("error loading kubeconfig: %s", err.Error())
136136
}
137-
138137
ctx.restConfig = restConfig
139138

139+
if artifactsDir := os.Getenv("ARTIFACTS_DIR"); artifactsDir != "" {
140+
ctx.artifactsDir = artifactsDir
141+
}
142+
140143
var once sync.Once
141144
deprovision := func() {
142145
once.Do(func() {
143-
if artifactsDir := os.Getenv("ARTIFACTS_DIR"); artifactsDir != "" {
146+
if ctx.artifactsDir != "" {
144147
ctx.Logf("collecting container logs for the %s cluster", name)
145-
if err := provider.CollectLogs(name, filepath.Join(artifactsDir, logDir)); err != nil {
148+
if err := provider.CollectLogs(name, filepath.Join(ctx.artifactsDir, logDir)); err != nil {
146149
ctx.Logf("failed to collect logs: %v", err)
147150
}
148151
}

staging/operator-lifecycle-manager/test/e2e/subscription_e2e_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,7 @@ var _ = Describe("Subscription", func() {
5959
})
6060

6161
AfterEach(func() {
62-
Eventually(func() error {
63-
return ctx.Ctx().Client().Delete(context.Background(), &generatedNamespace)
64-
}).Should(Succeed())
62+
TeardownNamespace(generatedNamespace.GetName())
6563
})
6664

6765
When("an entry in the middle of a channel does not provide a required GVK", func() {

staging/operator-lifecycle-manager/test/e2e/util_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,3 +936,20 @@ func SetupGeneratedTestNamespace(name string) corev1.Namespace {
936936

937937
return ns
938938
}
939+
940+
func TeardownNamespace(ns string) {
941+
log := ctx.Ctx().Logf
942+
943+
currentTest := CurrentGinkgoTestDescription()
944+
if currentTest.Failed {
945+
log("collecting the %s namespace artifacts as the '%s' test case failed", ns, currentTest.TestText)
946+
if err := ctx.Ctx().DumpNamespaceArtifacts(ns); err != nil {
947+
log("failed to collect namespace artifacts: %v", err)
948+
}
949+
}
950+
951+
log("tearing down the %s namespace", ns)
952+
Eventually(func() error {
953+
return ctx.Ctx().KubeClient().KubernetesInterface().CoreV1().Namespaces().Delete(context.Background(), ns, metav1.DeleteOptions{})
954+
}).Should(Succeed())
955+
}

0 commit comments

Comments
 (0)