diff --git a/test/e2e/e2e.go b/test/e2e/e2e.go index 38062834aa4..e771187313d 100644 --- a/test/e2e/e2e.go +++ b/test/e2e/e2e.go @@ -53,6 +53,7 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { return nil }, func(data []byte) { // Run on all Ginkgo nodes + framework.SetupSuitePerGinkgoNode() }) var _ = ginkgo.SynchronizedAfterSuite(func() { diff --git a/test/e2e/framework/log/logger_test.go b/test/e2e/framework/log/logger_test.go index 76439c06298..8a51ff51cfa 100644 --- a/test/e2e/framework/log/logger_test.go +++ b/test/e2e/framework/log/logger_test.go @@ -84,7 +84,7 @@ func TestFailureOutput(t *testing.T) { output: "INFO: before\nFAIL: hard-coded error\nUnexpected error:\n <*errors.errorString>: {\n s: \"an error with a long, useless description\",\n }\n an error with a long, useless description\noccurred\nINFO: after\nFAIL: true is never false either\nExpected\n : true\nto equal\n : false\n", failure: "hard-coded error\nUnexpected error:\n <*errors.errorString>: {\n s: \"an error with a long, useless description\",\n }\n an error with a long, useless description\noccurred", // TODO: should start with k8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.4() - stack: "\tutil.go:1362\nk8s.io/kubernetes/test/e2e/framework.ExpectNoError()\n\tutil.go:1356\nk8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.4()\n\tlogger_test.go:49\nk8s.io/kubernetes/vendor/github.com/onsi/ginkgo/internal/leafnodes.(*runner).runSync()\n\tlogger_test.go:65\n", + stack: "\tutil.go:1369\nk8s.io/kubernetes/test/e2e/framework.ExpectNoError()\n\tutil.go:1363\nk8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.4()\n\tlogger_test.go:49\nk8s.io/kubernetes/vendor/github.com/onsi/ginkgo/internal/leafnodes.(*runner).runSync()\n\tlogger_test.go:65\n", }, testResult{ name: "[Top Level] log fails", diff --git a/test/e2e/framework/suites.go b/test/e2e/framework/suites.go index 823d8273336..9dedeca4747 100644 --- a/test/e2e/framework/suites.go +++ b/test/e2e/framework/suites.go @@ -116,23 +116,32 @@ func SetupSuite() { e2elog.Logf("kube-apiserver version: %s", serverVersion.GitVersion) } - // Obtain the default IP family of the cluster - // Some e2e test are designed to work on IPv4 only, this global variable - // allows to adapt those tests to work on both IPv4 and IPv6 - // TODO(dual-stack): dual stack clusters should pass full e2e testing at least with the primary IP family - // the dual stack clusters can be ipv4-ipv6 or ipv6-ipv4, order matters, - // and services use the primary IP family by default - // If we´ll need to provide additional context for dual-stack, we can detect it - // because pods have two addresses (one per family) - TestContext.IPFamily = getDefaultClusterIPFamily(c) - e2elog.Logf("Cluster IP family: %s", TestContext.IPFamily) - if TestContext.NodeKiller.Enabled { nodeKiller := NewNodeKiller(TestContext.NodeKiller, c, TestContext.Provider) go nodeKiller.Run(TestContext.NodeKiller.NodeKillerStopCh) } } +// SetupSuitePerGinkgoNode is the boilerplate that can be used to setup ginkgo test suites, on the SynchronizedBeforeSuite step. +// There are certain operations we only want to run once per overall test invocation on each Ginkgo node +// such as making some global variables accessible to all parallel executions +// Because of the way Ginkgo runs tests in parallel, we must use SynchronizedBeforeSuite +// Ref: https://onsi.github.io/ginkgo/#parallel-specs +func SetupSuitePerGinkgoNode() { + // Obtain the default IP family of the cluster + // Some e2e test are designed to work on IPv4 only, this global variable + // allows to adapt those tests to work on both IPv4 and IPv6 + // TODO: dual-stack + // the dual stack clusters can be ipv4-ipv6 or ipv6-ipv4, order matters, + // and services use the primary IP family by default + c, err := LoadClientset() + if err != nil { + klog.Fatal("Error loading client: ", err) + } + TestContext.IPFamily = getDefaultClusterIPFamily(c) + e2elog.Logf("Cluster IP family: %s", TestContext.IPFamily) +} + // CleanupSuite is the boilerplate that can be used after tests on ginkgo were run, on the SynchronizedAfterSuite step. // Similar to SynchronizedBeforeSuite, we want to run some operations only once (such as collecting cluster logs). // Here, the order of functions is reversed; first, the function which runs everywhere, diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index 49b6ee13abd..daf25b8f27b 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -415,6 +415,8 @@ func runKubernetesServiceTestContainer(c clientset.Interface, ns string) { // getDefaultClusterIPFamily obtains the default IP family of the cluster // using the Cluster IP address of the kubernetes service created in the default namespace // This unequivocally identifies the default IP family because services are single family +// TODO: dual-stack may support multiple families per service +// but we can detect if a cluster is dual stack because pods have two addresses (one per family) func getDefaultClusterIPFamily(c clientset.Interface) string { // Get the ClusterIP of the kubernetes service created in the default namespace svc, err := c.CoreV1().Services(metav1.NamespaceDefault).Get("kubernetes", metav1.GetOptions{}) @@ -428,6 +430,11 @@ func getDefaultClusterIPFamily(c clientset.Interface) string { return "ipv4" } +// ClusterIsIPv6 returns true if the cluster is IPv6 +func ClusterIsIPv6() bool { + return TestContext.IPFamily == "ipv6" +} + // ProviderIs returns true if the provider is included is the providers. Otherwise false. func ProviderIs(providers ...string) bool { for _, provider := range providers {