m/t/e2e: move testEventual to common test util pkg
testEventual, among other implementation, will be reused in metroctl
tests.
Change-Id: I24df31a72034b707e3906889e7a569c8e97669ad
Reviewed-on: https://review.monogon.dev/c/monogon/+/788
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/metropolis/test/e2e/BUILD.bazel b/metropolis/test/e2e/BUILD.bazel
index 4d04857..6cc0ab7 100644
--- a/metropolis/test/e2e/BUILD.bazel
+++ b/metropolis/test/e2e/BUILD.bazel
@@ -2,10 +2,7 @@
go_library(
name = "e2e",
- srcs = [
- "kubernetes_helpers.go",
- "utils.go",
- ],
+ srcs = ["kubernetes_helpers.go"],
importpath = "source.monogon.dev/metropolis/test/e2e",
visibility = ["//metropolis/test:__subpackages__"],
deps = [
@@ -37,6 +34,7 @@
"//metropolis/node/core/rpc",
"//metropolis/proto/api",
"//metropolis/test/launch/cluster",
+ "//metropolis/test/util",
"@io_k8s_api//core/v1:core",
"@io_k8s_apimachinery//pkg/api/resource",
"@io_k8s_apimachinery//pkg/apis/meta/v1:meta",
diff --git a/metropolis/test/e2e/main_test.go b/metropolis/test/e2e/main_test.go
index 89df286..32cf2bd 100644
--- a/metropolis/test/e2e/main_test.go
+++ b/metropolis/test/e2e/main_test.go
@@ -42,6 +42,7 @@
"source.monogon.dev/metropolis/node/core/rpc"
apb "source.monogon.dev/metropolis/proto/api"
"source.monogon.dev/metropolis/test/launch/cluster"
+ "source.monogon.dev/metropolis/test/util"
)
const (
@@ -114,7 +115,7 @@
// Deployments and StatefulSets
t.Run("RunGroup", func(t *testing.T) {
t.Run("Cluster", func(t *testing.T) {
- testEventual(t, "Retrieving cluster directory sucessful", ctx, 60*time.Second, func(ctx context.Context) error {
+ util.TestEventual(t, "Retrieving cluster directory sucessful", ctx, 60*time.Second, func(ctx context.Context) error {
res, err := mgmt.GetClusterInfo(ctx, &apb.GetClusterInfoRequest{})
if err != nil {
return fmt.Errorf("GetClusterInfo: %w", err)
@@ -143,14 +144,14 @@
}
return nil
})
- testEventual(t, "Node rejoin successful", ctx, 60*time.Second, func(ctx context.Context) error {
+ util.TestEventual(t, "Node rejoin successful", ctx, 60*time.Second, func(ctx context.Context) error {
// Ensure nodes rejoin the cluster after a reboot by reboting the 1st node.
if err := cluster.RebootNode(ctx, 1); err != nil {
return fmt.Errorf("while rebooting a node: %w", err)
}
return nil
})
- testEventual(t, "Heartbeat test successful", ctx, 60*time.Second, func(ctx context.Context) error {
+ util.TestEventual(t, "Heartbeat test successful", ctx, 60*time.Second, func(ctx context.Context) error {
// Ensure all cluster nodes are capable of sending heartbeat updates.
// This test assumes the expected count of nodes is already present in
// the cluster.
@@ -192,7 +193,7 @@
if err != nil {
t.Fatal(err)
}
- testEventual(t, "Nodes are registered and ready", ctx, largeTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Nodes are registered and ready", ctx, largeTestTimeout, func(ctx context.Context) error {
nodes, err := clientSet.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
if err != nil {
return err
@@ -211,11 +212,11 @@
}
return nil
})
- testEventual(t, "Simple deployment", ctx, largeTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Simple deployment", ctx, largeTestTimeout, func(ctx context.Context) error {
_, err := clientSet.AppsV1().Deployments("default").Create(ctx, makeTestDeploymentSpec("test-deploy-1"), metav1.CreateOptions{})
return err
})
- testEventual(t, "Simple deployment is running", ctx, largeTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Simple deployment is running", ctx, largeTestTimeout, func(ctx context.Context) error {
res, err := clientSet.CoreV1().Pods("default").List(ctx, metav1.ListOptions{LabelSelector: "name=test-deploy-1"})
if err != nil {
return err
@@ -234,14 +235,14 @@
return fmt.Errorf("pod is not ready: %v", events.Items[0].Message)
}
})
- testEventual(t, "Simple deployment with runc", ctx, largeTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Simple deployment with runc", ctx, largeTestTimeout, func(ctx context.Context) error {
deployment := makeTestDeploymentSpec("test-deploy-2")
var runcStr = "runc"
deployment.Spec.Template.Spec.RuntimeClassName = &runcStr
_, err := clientSet.AppsV1().Deployments("default").Create(ctx, deployment, metav1.CreateOptions{})
return err
})
- testEventual(t, "Simple deployment is running on runc", ctx, largeTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Simple deployment is running on runc", ctx, largeTestTimeout, func(ctx context.Context) error {
res, err := clientSet.CoreV1().Pods("default").List(ctx, metav1.ListOptions{LabelSelector: "name=test-deploy-2"})
if err != nil {
return err
@@ -265,11 +266,11 @@
return fmt.Errorf("pod is not ready: %v", errorMsg.String())
}
})
- testEventual(t, "Simple StatefulSet with PVC", ctx, largeTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Simple StatefulSet with PVC", ctx, largeTestTimeout, func(ctx context.Context) error {
_, err := clientSet.AppsV1().StatefulSets("default").Create(ctx, makeTestStatefulSet("test-statefulset-1", corev1.PersistentVolumeFilesystem), metav1.CreateOptions{})
return err
})
- testEventual(t, "Simple StatefulSet with PVC is running", ctx, largeTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Simple StatefulSet with PVC is running", ctx, largeTestTimeout, func(ctx context.Context) error {
res, err := clientSet.CoreV1().Pods("default").List(ctx, metav1.ListOptions{LabelSelector: "name=test-statefulset-1"})
if err != nil {
return err
@@ -288,11 +289,11 @@
return fmt.Errorf("pod is not ready: %v", events.Items[0].Message)
}
})
- testEventual(t, "Simple StatefulSet with Block PVC", ctx, largeTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Simple StatefulSet with Block PVC", ctx, largeTestTimeout, func(ctx context.Context) error {
_, err := clientSet.AppsV1().StatefulSets("default").Create(ctx, makeTestStatefulSet("test-statefulset-2", corev1.PersistentVolumeBlock), metav1.CreateOptions{})
return err
})
- testEventual(t, "Simple StatefulSet with Block PVC is running", ctx, largeTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Simple StatefulSet with Block PVC is running", ctx, largeTestTimeout, func(ctx context.Context) error {
res, err := clientSet.CoreV1().Pods("default").List(ctx, metav1.ListOptions{LabelSelector: "name=test-statefulset-2"})
if err != nil {
return err
@@ -311,7 +312,7 @@
return fmt.Errorf("pod is not ready: %v", events.Items[0].Message)
}
})
- testEventual(t, "Pod with preseeded image", ctx, smallTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Pod with preseeded image", ctx, smallTestTimeout, func(ctx context.Context) error {
_, err := clientSet.CoreV1().Pods("default").Create(ctx, &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "preseed-test-1",
@@ -327,7 +328,7 @@
}, metav1.CreateOptions{})
return err
})
- testEventual(t, "Pod with preseeded image is completed", ctx, largeTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Pod with preseeded image is completed", ctx, largeTestTimeout, func(ctx context.Context) error {
pod, err := clientSet.CoreV1().Pods("default").Get(ctx, "preseed-test-1", metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get pod: %w", err)
@@ -343,7 +344,7 @@
}
})
if os.Getenv("HAVE_NESTED_KVM") != "" {
- testEventual(t, "Pod for KVM/QEMU smoke test", ctx, smallTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "Pod for KVM/QEMU smoke test", ctx, smallTestTimeout, func(ctx context.Context) error {
runcRuntimeClass := "runc"
_, err := clientSet.CoreV1().Pods("default").Create(ctx, &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
@@ -366,7 +367,7 @@
}, metav1.CreateOptions{})
return err
})
- testEventual(t, "KVM/QEMU smoke test completion", ctx, smallTestTimeout, func(ctx context.Context) error {
+ util.TestEventual(t, "KVM/QEMU smoke test completion", ctx, smallTestTimeout, func(ctx context.Context) error {
pod, err := clientSet.CoreV1().Pods("default").Get(ctx, "vm-smoketest", metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get pod: %w", err)
diff --git a/metropolis/test/e2e/utils.go b/metropolis/test/e2e/utils.go
deleted file mode 100644
index dcc9eac..0000000
--- a/metropolis/test/e2e/utils.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2020 The Monogon Project Authors.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package e2e
-
-import (
- "context"
- "errors"
- "testing"
- "time"
-)
-
-// testEventual creates a new subtest looping the given function until it
-// either doesn't return an error anymore or the timeout is exceeded. The last
-// returned non-context-related error is being used as the test error.
-func testEventual(t *testing.T, name string, ctx context.Context, timeout time.Duration, f func(context.Context) error) {
- ctx, cancel := context.WithTimeout(ctx, timeout)
- t.Helper()
- t.Run(name, func(t *testing.T) {
- defer cancel()
- var lastErr = errors.New("test didn't run to completion at least once")
- t.Parallel()
- for {
- err := f(ctx)
- if err == nil {
- return
- }
- if err == ctx.Err() {
- t.Fatal(lastErr)
- }
- lastErr = err
- select {
- case <-ctx.Done():
- t.Fatal(lastErr)
- case <-time.After(1 * time.Second):
- }
- }
- })
-}
diff --git a/metropolis/test/util/BUILD.bazel b/metropolis/test/util/BUILD.bazel
new file mode 100644
index 0000000..7caf53b
--- /dev/null
+++ b/metropolis/test/util/BUILD.bazel
@@ -0,0 +1,8 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+ name = "util",
+ srcs = ["runners.go"],
+ importpath = "source.monogon.dev/metropolis/test/util",
+ visibility = ["//metropolis:__subpackages__"],
+)
diff --git a/metropolis/test/util/runners.go b/metropolis/test/util/runners.go
new file mode 100644
index 0000000..47bf59f
--- /dev/null
+++ b/metropolis/test/util/runners.go
@@ -0,0 +1,38 @@
+// This file implements test helper functions that augment the way any given
+// test is run.
+package util
+
+import (
+ "context"
+ "errors"
+ "testing"
+ "time"
+)
+
+// TestEventual creates a new subtest looping the given function until it
+// either doesn't return an error anymore or the timeout is exceeded. The last
+// returned non-context-related error is being used as the test error.
+func TestEventual(t *testing.T, name string, ctx context.Context, timeout time.Duration, f func(context.Context) error) {
+ ctx, cancel := context.WithTimeout(ctx, timeout)
+ t.Helper()
+ t.Run(name, func(t *testing.T) {
+ defer cancel()
+ var lastErr = errors.New("test didn't run to completion at least once")
+ t.Parallel()
+ for {
+ err := f(ctx)
+ if err == nil {
+ return
+ }
+ if err == ctx.Err() {
+ t.Fatal(lastErr)
+ }
+ lastErr = err
+ select {
+ case <-ctx.Done():
+ t.Fatal(lastErr)
+ case <-time.After(1 * time.Second):
+ }
+ }
+ })
+}