Merge pull request #86177 from deads2k/configure-test

make test framework easier to re-use
This commit is contained in:
Kubernetes Prow Robot 2019-12-13 07:57:08 -08:00 committed by GitHub
commit 5b77616374
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -30,6 +30,8 @@ import (
"sync" "sync"
"time" "time"
"k8s.io/apimachinery/pkg/runtime"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -71,6 +73,7 @@ type Framework struct {
// test multiple times in parallel. // test multiple times in parallel.
UniqueName string UniqueName string
clientConfig *rest.Config
ClientSet clientset.Interface ClientSet clientset.Interface
KubemarkExternalClusterClientSet clientset.Interface KubemarkExternalClusterClientSet clientset.Interface
@ -102,6 +105,11 @@ type Framework struct {
// should abort, the AfterSuite hook should run all Cleanup actions. // should abort, the AfterSuite hook should run all Cleanup actions.
cleanupHandle CleanupActionHandle 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 // configuration for framework's client
Options Options Options Options
@ -113,6 +121,9 @@ type Framework struct {
clusterAutoscalerMetricsBeforeTest e2emetrics.Collection 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. // TestDataSummary is an interface for managing test data.
type TestDataSummary interface { type TestDataSummary interface {
SummaryKind() string SummaryKind() string
@ -146,6 +157,20 @@ func NewFramework(baseName string, options Options, client clientset.Interface)
ClientSet: client, ClientSet: client,
} }
f.AddAfterEach("dumpNamespaceInfo", func(f *Framework, failed bool) {
if !failed {
return
}
if !TestContext.DumpLogsOnFailure {
return
}
if !f.SkipNamespaceCreation {
for _, ns := range f.namespacesToDelete {
DumpAllNamespaceInfo(f.ClientSet, ns.Name)
}
}
})
ginkgo.BeforeEach(f.BeforeEach) ginkgo.BeforeEach(f.BeforeEach)
ginkgo.AfterEach(f.AfterEach) ginkgo.AfterEach(f.AfterEach)
@ -170,6 +195,7 @@ func (f *Framework) BeforeEach() {
if TestContext.KubeAPIContentType != "" { if TestContext.KubeAPIContentType != "" {
config.ContentType = TestContext.KubeAPIContentType config.ContentType = TestContext.KubeAPIContentType
} }
f.clientConfig = rest.CopyConfig(config)
f.ClientSet, err = clientset.NewForConfig(config) f.ClientSet, err = clientset.NewForConfig(config)
ExpectNoError(err) ExpectNoError(err)
f.DynamicClient, err = dynamic.NewForConfig(config) f.DynamicClient, err = dynamic.NewForConfig(config)
@ -315,6 +341,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. // AfterEach deletes the namespace, after reading its events.
func (f *Framework) AfterEach() { func (f *Framework) AfterEach() {
RemoveCleanupAction(f.cleanupHandle) RemoveCleanupAction(f.cleanupHandle)
@ -352,6 +391,7 @@ func (f *Framework) AfterEach() {
// Paranoia-- prevent reuse! // Paranoia-- prevent reuse!
f.Namespace = nil f.Namespace = nil
f.clientConfig = nil
f.ClientSet = nil f.ClientSet = nil
f.namespacesToDelete = nil f.namespacesToDelete = nil
@ -365,12 +405,9 @@ func (f *Framework) AfterEach() {
} }
}() }()
// Print events if the test failed. // run all aftereach functions in random order to ensure no dependencies grow
if ginkgo.CurrentGinkgoTestDescription().Failed && TestContext.DumpLogsOnFailure { for _, afterEachFn := range f.afterEaches {
// Pass both unversioned client and versioned clientset, till we have removed all uses of the unversioned client. afterEachFn(f, ginkgo.CurrentGinkgoTestDescription().Failed)
if !f.SkipNamespaceCreation {
DumpAllNamespaceInfo(f.ClientSet, f.Namespace.Name)
}
} }
if TestContext.GatherKubeSystemResourceUsageData != "false" && TestContext.GatherKubeSystemResourceUsageData != "none" && f.gatherer != nil { if TestContext.GatherKubeSystemResourceUsageData != "false" && TestContext.GatherKubeSystemResourceUsageData != "none" && f.gatherer != nil {
@ -490,6 +527,15 @@ func (f *Framework) WaitForPodNoLongerRunning(podName string) error {
return e2epod.WaitForPodNoLongerRunningInNamespace(f.ClientSet, podName, f.Namespace.Name) 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 // 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 // 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. // the specified container log against the given expected output using a substring matcher.