From ea913f38ead3fb97c1e9086f65b01c680e078ed8 Mon Sep 17 00:00:00 2001 From: David Eads Date: Wed, 11 Dec 2019 15:53:00 -0500 Subject: [PATCH 1/3] allow configuration of customized AfterEach functions for all tests --- test/e2e/framework/framework.go | 42 ++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index 1f3ad75ea87..3f9e5fef8df 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -102,6 +102,11 @@ type Framework struct { // should abort, the AfterSuite hook should run all Cleanup actions. cleanupHandle CleanupActionHandle + // afterEaches is a map of name to function to be called after each test. These are not + // cleared. The call order is randomized so that no dependencies can grow between + // the various afterEaches + afterEaches map[string]AfterEachActionFunc + // configuration for framework's client Options Options @@ -113,6 +118,9 @@ type Framework struct { clusterAutoscalerMetricsBeforeTest e2emetrics.Collection } +// AfterEachActionFunc is a function that can be called after each test +type AfterEachActionFunc func(f *Framework, failed bool) + // TestDataSummary is an interface for managing test data. type TestDataSummary interface { SummaryKind() string @@ -146,6 +154,18 @@ func NewFramework(baseName string, options Options, client clientset.Interface) ClientSet: client, } + f.AddAfterEach("dumpNamespaceInfo", func(f *Framework, failed bool) { + if !failed { + return + } + if !TestContext.DumpLogsOnFailure { + return + } + if !f.SkipNamespaceCreation { + DumpAllNamespaceInfo(f.ClientSet, f.Namespace.Name) + } + }) + ginkgo.BeforeEach(f.BeforeEach) ginkgo.AfterEach(f.AfterEach) @@ -315,6 +335,19 @@ func printSummaries(summaries []TestDataSummary, testBaseName string) { } } +// AddAfterEach is a way to add a function to be called after every test. The execution order is intentionally random +// to avoid growing dependencies. If you register the same name twice, it is a coding error and will panic. +func (f *Framework) AddAfterEach(name string, fn AfterEachActionFunc) { + if _, ok := f.afterEaches[name]; ok { + panic(fmt.Sprintf("%q is already registered", name)) + } + + if f.afterEaches == nil { + f.afterEaches = map[string]AfterEachActionFunc{} + } + f.afterEaches[name] = fn +} + // AfterEach deletes the namespace, after reading its events. func (f *Framework) AfterEach() { RemoveCleanupAction(f.cleanupHandle) @@ -365,12 +398,9 @@ func (f *Framework) AfterEach() { } }() - // Print events if the test failed. - if ginkgo.CurrentGinkgoTestDescription().Failed && TestContext.DumpLogsOnFailure { - // Pass both unversioned client and versioned clientset, till we have removed all uses of the unversioned client. - if !f.SkipNamespaceCreation { - DumpAllNamespaceInfo(f.ClientSet, f.Namespace.Name) - } + // run all aftereach functions in random order to ensure no dependencies grow + for _, afterEachFn := range f.afterEaches { + afterEachFn(f, ginkgo.CurrentGinkgoTestDescription().Failed) } if TestContext.GatherKubeSystemResourceUsageData != "false" && TestContext.GatherKubeSystemResourceUsageData != "none" && f.gatherer != nil { From d0210220e4887fb337afdf7be80361519f10dce3 Mon Sep 17 00:00:00 2001 From: David Eads Date: Wed, 11 Dec 2019 16:00:57 -0500 Subject: [PATCH 2/3] dump information for all namespaces related to the test --- test/e2e/framework/framework.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index 3f9e5fef8df..d9aeb4fa878 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -162,7 +162,9 @@ func NewFramework(baseName string, options Options, client clientset.Interface) return } if !f.SkipNamespaceCreation { - DumpAllNamespaceInfo(f.ClientSet, f.Namespace.Name) + for _, ns := range f.namespacesToDelete { + DumpAllNamespaceInfo(f.ClientSet, ns.Name) + } } }) From 773fbeab1e39b15092f54457018b14e56040f097 Mon Sep 17 00:00:00 2001 From: David Eads Date: Wed, 11 Dec 2019 16:05:32 -0500 Subject: [PATCH 3/3] expose the clientConfig to consumers trying to build custom clients against the kubeapiserver --- test/e2e/framework/framework.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index d9aeb4fa878..e68c1a8cdb9 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -30,6 +30,8 @@ import ( "sync" "time" + "k8s.io/apimachinery/pkg/runtime" + v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -71,6 +73,7 @@ type Framework struct { // test multiple times in parallel. UniqueName string + clientConfig *rest.Config ClientSet clientset.Interface KubemarkExternalClusterClientSet clientset.Interface @@ -192,6 +195,7 @@ func (f *Framework) BeforeEach() { if TestContext.KubeAPIContentType != "" { config.ContentType = TestContext.KubeAPIContentType } + f.clientConfig = rest.CopyConfig(config) f.ClientSet, err = clientset.NewForConfig(config) ExpectNoError(err) f.DynamicClient, err = dynamic.NewForConfig(config) @@ -387,6 +391,7 @@ func (f *Framework) AfterEach() { // Paranoia-- prevent reuse! f.Namespace = nil + f.clientConfig = nil f.ClientSet = nil f.namespacesToDelete = nil @@ -522,6 +527,15 @@ func (f *Framework) WaitForPodNoLongerRunning(podName string) error { return e2epod.WaitForPodNoLongerRunningInNamespace(f.ClientSet, podName, f.Namespace.Name) } +// ClientConfig an externally accessible method for reading the kube client config. +func (f *Framework) ClientConfig() *rest.Config { + ret := rest.CopyConfig(f.clientConfig) + // json is least common denominator + ret.ContentType = runtime.ContentTypeJSON + ret.AcceptContentTypes = runtime.ContentTypeJSON + return ret +} + // TestContainerOutput runs the given pod in the given namespace and waits // for all of the containers in the podSpec to move into the 'Success' status, and tests // the specified container log against the given expected output using a substring matcher.