Merge pull request #1355 from evrardjp/improve_testing

Simplify testing
This commit is contained in:
Jean-Philippe Evrard
2026-06-29 18:57:05 +02:00
committed by GitHub
4 changed files with 185 additions and 280 deletions
+1
View File
@@ -1,4 +1,5 @@
[tools]
actionlint = "latest"
cosign = "3.0.6"
go = "1.26.4"
golangci-lint = "2.12.2"
+28 -15
View File
@@ -38,7 +38,7 @@ jobs:
# It is only used to make us aware of any potential security failure that
# should trigger a bump of the image in build/.
pr-vuln-scan:
name: Build image and scan it against known vulnerabilities
name: Trivy scan # Builds image and scan it against known vulnerabilities
runs-on: ubuntu-latest
steps:
- name: Harden Runner
@@ -68,6 +68,29 @@ jobs:
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'
e2e-test-matrix:
name: Generate e2e test matrix
runs-on: ubuntu-latest
outputs:
testnames: ${{ steps.matrix.outputs.testnames }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: jdx/mise-action@dba19683ed58901619b14f395a24841710cb4925 # v4.1.0
with:
version: 2026.6.1
- name: Generate matrix
id: matrix
run: |
testnames=$(go test -list '^Test' ./tests/kind | jq -R -s -c 'split("\n") | map(select(startswith("Test")))')
echo "testnames=${testnames}" >> "${GITHUB_OUTPUT}"
# This ensures the latest code works with the manifests built from tree.
# It is useful for two things:
# - Test manifests changes (obviously), ensuring they don't break existing clusters
@@ -76,23 +99,13 @@ jobs:
# - Test some features are working as expected.
# Helm charts are _trailing_ releases, while manifests are done during development.
e2e-manifests:
name: End-to-End test with kured with code and manifests from HEAD
name: e2e #End-to-End test with kured with code and manifests from HEAD
runs-on: ubuntu-latest
needs: e2e-test-matrix
strategy:
fail-fast: false
matrix:
testname:
- "TestE2EWithCommand/current"
- "TestE2EWithCommand/previous"
- "TestE2EWithSignal/current"
- "TestE2EWithSignal/previous"
- "TestE2EConcurrentWithCommand/current"
- "TestE2EConcurrentWithCommand/previous"
- "TestE2EConcurrentWithSignal/current"
- "TestE2EConcurrentWithSignal/previous"
- "TestCordonningIsKept/concurrency1"
- "TestCordonningIsKept/concurrency2"
- "TestE2EBlocker/podblocker"
testname: ${{ fromJSON(needs.e2e-test-matrix.outputs.testnames) }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
@@ -109,4 +122,4 @@ jobs:
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: Run specific e2e tests
run: make e2e-test ARGS="-run ^${{ matrix.testname }}"
run: make e2e-test ARGS="-run ^${{ matrix.testname }}$"
+10 -2
View File
@@ -1,5 +1,5 @@
.DEFAULT: all
.PHONY: all clean install-tools dev-image release dev-manifest e2e-test minikube-publish test lint lint-docs
.PHONY: all clean install-tools dev-image release dev-manifest e2e-test minikube-publish test lint lint-go lint-sh lint-ghactions lint-docs
REGISTRY ?= ghcr.io
IMAGE_NAME ?= kubereboot/kured
@@ -52,9 +52,17 @@ test: lint
@echo "Running short go tests"
go test -test.short -json ./... > test.json
lint:
lint: lint-sh lint-ghactions lint-go
lint-sh:
@echo "Running shellcheck"
find . -name '*.sh' | xargs -n1 shellcheck
lint-ghactions:
@echo "Running actionlint"
actionlint -verbose
lint-go:
@echo "Running golangci-lint..."
golangci-lint run --config $(GOLANGCI_CONFIG) ./...
+146 -263
View File
@@ -134,292 +134,175 @@ func (k *KindTest) Destroy() error {
return nil
}
func TestE2EWithCommand(t *testing.T) {
const (
currentK8sClusterConfig = "../../.github/kind-cluster-current.yaml"
previousK8sClusterConfig = "../../.github/kind-cluster-previous.yaml"
rebootSentinelsScript = "testfiles/create-reboot-sentinels.sh"
coordinatedRebootScript = "testfiles/follow-coordinated-reboot.sh"
)
type e2eScenario struct {
clusterNamePrefix string
clusterConfigFile string
manifest string
setupScript string
testScript string
failureMessage string
}
func runE2E(t *testing.T, scenario e2eScenario) {
t.Helper()
t.Parallel()
if testing.Short() {
t.Skip("skipping test in short mode.")
}
var kindClusterConfigs = []string{
"previous",
"current",
randomInt := strconv.Itoa(rand.Intn(100))
kindClusterName := fmt.Sprintf("%s-%s", scenario.clusterNamePrefix, randomInt)
kindContext := fmt.Sprintf("kind-%v", kindClusterName)
k := NewKindTester(kindClusterName, scenario.clusterConfigFile, t, LocalImage(kuredDevImage), Deploy("../../kured-rbac.yaml"), Deploy(scenario.manifest))
defer k.FlushLog()
err := k.Create()
if err != nil {
t.Fatalf("Error creating cluster %v", err)
}
// Iterate over each Kubernetes version
for _, version := range kindClusterConfigs {
version := version
// Define a subtest for each combination
t.Run(version, func(t *testing.T) {
t.Parallel() // Allow tests to run in parallel
defer func(k *KindTest) {
err := k.Destroy()
if err != nil {
t.Fatalf("Error destroying cluster %v", err)
}
}(k)
randomInt := strconv.Itoa(rand.Intn(100))
kindClusterName := fmt.Sprintf("kured-e2e-command-%v-%v", version, randomInt)
kindClusterConfigFile := fmt.Sprintf("../../.github/kind-cluster-%v.yaml", version)
kindContext := fmt.Sprintf("kind-%v", kindClusterName)
k.Write([]byte("Now running e2e tests"))
k := NewKindTester(kindClusterName, kindClusterConfigFile, t, LocalImage(kuredDevImage), Deploy("../../kured-rbac.yaml"), Deploy("testfiles/kured-ds.yaml"))
defer k.FlushLog()
if scenario.setupScript != "" {
if err := k.RunCmd("bash", scenario.setupScript, kindContext); err != nil {
t.Fatalf("failed to run setup script %s: %v", scenario.setupScript, err)
}
}
err := k.Create()
if err != nil {
t.Fatalf("Error creating cluster %v", err)
}
defer func(k *KindTest) {
err := k.Destroy()
if err != nil {
t.Fatalf("Error destroying cluster %v", err)
}
}(k)
k.Write([]byte("Now running e2e tests"))
if err := k.RunCmd("bash", "testfiles/create-reboot-sentinels.sh", kindContext); err != nil {
t.Fatalf("failed to create sentinels: %v", err)
}
if err := k.RunCmd("bash", "testfiles/follow-coordinated-reboot.sh", kindContext); err != nil {
t.Fatalf("failed to follow reboot: %v", err)
}
})
if err := k.RunCmd("bash", scenario.testScript, kindContext); err != nil {
t.Fatalf("%s: %v", scenario.failureMessage, err)
}
}
func TestE2EWithSignal(t *testing.T) {
t.Parallel()
if testing.Short() {
t.Skip("skipping test in short mode.")
}
var kindClusterConfigs = []string{
"previous",
"current",
}
// Iterate over each Kubernetes version
for _, version := range kindClusterConfigs {
version := version
// Define a subtest for each combination
t.Run(version, func(t *testing.T) {
t.Parallel() // Allow tests to run in parallel
randomInt := strconv.Itoa(rand.Intn(100))
kindClusterName := fmt.Sprintf("kured-e2e-signal-%v-%v", version, randomInt)
kindClusterConfigFile := fmt.Sprintf("../../.github/kind-cluster-%v.yaml", version)
kindContext := fmt.Sprintf("kind-%v", kindClusterName)
k := NewKindTester(kindClusterName, kindClusterConfigFile, t, LocalImage(kuredDevImage), Deploy("../../kured-rbac.yaml"), Deploy("testfiles/kured-ds-signal.yaml"))
defer k.FlushLog()
err := k.Create()
if err != nil {
t.Fatalf("Error creating cluster %v", err)
}
defer func(k *KindTest) {
err := k.Destroy()
if err != nil {
t.Fatalf("Error destroying cluster %v", err)
}
}(k)
k.Write([]byte("Now running e2e tests"))
if err := k.RunCmd("bash", "testfiles/create-reboot-sentinels.sh", kindContext); err != nil {
t.Fatalf("failed to create sentinels: %v", err)
}
if err := k.RunCmd("bash", "testfiles/follow-coordinated-reboot.sh", kindContext); err != nil {
t.Fatalf("failed to follow reboot: %v", err)
}
})
}
func TestRebootCommandCurrentk8sRelease(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-command-current",
clusterConfigFile: currentK8sClusterConfig,
manifest: "testfiles/kured-ds.yaml",
setupScript: rebootSentinelsScript,
testScript: coordinatedRebootScript,
failureMessage: "failed to follow reboot",
})
}
func TestE2EConcurrentWithCommand(t *testing.T) {
t.Parallel()
if testing.Short() {
t.Skip("skipping test in short mode.")
}
var kindClusterConfigs = []string{
"previous",
"current",
}
// Iterate over each Kubernetes version
for _, version := range kindClusterConfigs {
version := version
// Define a subtest for each combination
t.Run(version, func(t *testing.T) {
t.Parallel() // Allow tests to run in parallel
randomInt := strconv.Itoa(rand.Intn(100))
kindClusterName := fmt.Sprintf("kured-e2e-concurrentcommand-%v-%v", version, randomInt)
kindClusterConfigFile := fmt.Sprintf("../../.github/kind-cluster-%v.yaml", version)
kindContext := fmt.Sprintf("kind-%v", kindClusterName)
k := NewKindTester(kindClusterName, kindClusterConfigFile, t, LocalImage(kuredDevImage), Deploy("../../kured-rbac.yaml"), Deploy("testfiles/kured-ds-concurrent-command.yaml"))
defer k.FlushLog()
err := k.Create()
if err != nil {
t.Fatalf("Error creating cluster %v", err)
}
defer func(k *KindTest) {
err := k.Destroy()
if err != nil {
t.Fatalf("Error destroying cluster %v", err)
}
}(k)
k.Write([]byte("Now running e2e tests"))
if err := k.RunCmd("bash", "testfiles/create-reboot-sentinels.sh", kindContext); err != nil {
t.Fatalf("failed to create sentinels: %v", err)
}
if err := k.RunCmd("bash", "testfiles/follow-coordinated-reboot.sh", kindContext); err != nil {
t.Fatalf("failed to follow reboot: %v", err)
}
})
}
func TestRebootCommandPreviousk8sRelease(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-command-previous",
clusterConfigFile: previousK8sClusterConfig,
manifest: "testfiles/kured-ds.yaml",
setupScript: rebootSentinelsScript,
testScript: coordinatedRebootScript,
failureMessage: "failed to follow reboot",
})
}
func TestE2EConcurrentWithSignal(t *testing.T) {
t.Parallel()
if testing.Short() {
t.Skip("skipping test in short mode.")
}
var kindClusterConfigs = []string{
"previous",
"current",
}
// Iterate over each Kubernetes version
for _, version := range kindClusterConfigs {
version := version
// Define a subtest for each combination
t.Run(version, func(t *testing.T) {
t.Parallel() // Allow tests to run in parallel
randomInt := strconv.Itoa(rand.Intn(100))
kindClusterName := fmt.Sprintf("kured-e2e-concurrentsignal-%v-%v", version, randomInt)
kindClusterConfigFile := fmt.Sprintf("../../.github/kind-cluster-%v.yaml", version)
kindContext := fmt.Sprintf("kind-%v", kindClusterName)
k := NewKindTester(kindClusterName, kindClusterConfigFile, t, LocalImage(kuredDevImage), Deploy("../../kured-rbac.yaml"), Deploy("testfiles/kured-ds-concurrent-signal.yaml"))
defer k.FlushLog()
err := k.Create()
if err != nil {
t.Fatalf("Error creating cluster %v", err)
}
defer func(k *KindTest) {
err := k.Destroy()
if err != nil {
t.Fatalf("Error destroying cluster %v", err)
}
}(k)
k.Write([]byte("Now running e2e tests"))
if err := k.RunCmd("bash", "testfiles/create-reboot-sentinels.sh", kindContext); err != nil {
t.Fatalf("failed to create sentinels: %v", err)
}
if err := k.RunCmd("bash", "testfiles/follow-coordinated-reboot.sh", kindContext); err != nil {
t.Fatalf("failed to follow reboot: %v", err)
}
})
}
func TestRebootSignalCurrentk8sRelease(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-signal-current",
clusterConfigFile: currentK8sClusterConfig,
manifest: "testfiles/kured-ds-signal.yaml",
setupScript: rebootSentinelsScript,
testScript: coordinatedRebootScript,
failureMessage: "failed to follow reboot",
})
}
func TestCordonningIsKept(t *testing.T) {
t.Parallel()
if testing.Short() {
t.Skip("skipping test in short mode.")
}
var kindClusterConfigs = []string{
"concurrency1",
"concurrency2",
}
// Iterate over each test variant
for _, variant := range kindClusterConfigs {
variant := variant
// Define a subtest for each combination
t.Run(variant, func(t *testing.T) {
t.Parallel() // Allow tests to run in parallel
randomInt := strconv.Itoa(rand.Intn(100))
kindClusterName := fmt.Sprintf("kured-e2e-cordon-%v-%v", variant, randomInt)
kindClusterConfigFile := "../../.github/kind-cluster-current.yaml"
kindContext := fmt.Sprintf("kind-%v", kindClusterName)
var manifest string
if variant == "concurrency1" {
manifest = "testfiles/kured-ds-signal.yaml"
} else {
manifest = "testfiles/kured-ds-concurrent-signal.yaml"
}
k := NewKindTester(kindClusterName, kindClusterConfigFile, t, LocalImage(kuredDevImage), Deploy("../../kured-rbac.yaml"), Deploy(manifest))
defer k.FlushLog()
err := k.Create()
if err != nil {
t.Fatalf("Error creating cluster %v", err)
}
defer func(k *KindTest) {
err := k.Destroy()
if err != nil {
t.Fatalf("Error destroying cluster %v", err)
}
}(k)
k.Write([]byte("Now running e2e tests"))
if err := k.RunCmd("bash", "testfiles/node-stays-as-cordonned.sh", kindContext); err != nil {
t.Fatalf("node did not reboot in time: %v", err)
}
})
}
func TestRebootSignalPreviousk8sRelease(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-signal-previous",
clusterConfigFile: previousK8sClusterConfig,
manifest: "testfiles/kured-ds-signal.yaml",
setupScript: rebootSentinelsScript,
testScript: coordinatedRebootScript,
failureMessage: "failed to follow reboot",
})
}
func TestE2EBlocker(t *testing.T) {
t.Parallel()
if testing.Short() {
t.Skip("skipping test in short mode.")
}
var kindClusterConfigs = []string{
"podblocker",
}
// Iterate over each variant of the test
for _, variant := range kindClusterConfigs {
variant := variant
// Define a subtest for each combination
t.Run(variant, func(t *testing.T) {
t.Parallel() // Allow tests to run in parallel
func TestConcurrentRebootCommandCurrentk8sRelease(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-concurrentcommand-current",
clusterConfigFile: currentK8sClusterConfig,
manifest: "testfiles/kured-ds-concurrent-command.yaml",
setupScript: rebootSentinelsScript,
testScript: coordinatedRebootScript,
failureMessage: "failed to follow reboot",
})
}
randomInt := strconv.Itoa(rand.Intn(100))
kindClusterName := fmt.Sprintf("kured-e2e-cordon-%v-%v", variant, randomInt)
kindClusterConfigFile := "../../.github/kind-cluster-current.yaml"
kindContext := fmt.Sprintf("kind-%v", kindClusterName)
func TestConcurrentRebootCommandPreviousk8sRelease(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-concurrentcommand-previous",
clusterConfigFile: previousK8sClusterConfig,
manifest: "testfiles/kured-ds-concurrent-command.yaml",
setupScript: rebootSentinelsScript,
testScript: coordinatedRebootScript,
failureMessage: "failed to follow reboot",
})
}
k := NewKindTester(kindClusterName, kindClusterConfigFile, t, LocalImage(kuredDevImage), Deploy("../../kured-rbac.yaml"), Deploy(fmt.Sprintf("testfiles/kured-ds-%v.yaml", variant)))
defer k.FlushLog()
func TestConcurrentRebootSignalCurrentk8sRelease(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-concurrentsignal-current",
clusterConfigFile: currentK8sClusterConfig,
manifest: "testfiles/kured-ds-concurrent-signal.yaml",
setupScript: rebootSentinelsScript,
testScript: coordinatedRebootScript,
failureMessage: "failed to follow reboot",
})
}
err := k.Create()
if err != nil {
t.Fatalf("Error creating cluster %v", err)
}
defer func(k *KindTest) {
err := k.Destroy()
if err != nil {
t.Fatalf("Error destroying cluster %v", err)
}
}(k)
func TestConcurrentRebootSignalPreviousk8sRelease(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-concurrentsignal-previous",
clusterConfigFile: previousK8sClusterConfig,
manifest: "testfiles/kured-ds-concurrent-signal.yaml",
setupScript: rebootSentinelsScript,
testScript: coordinatedRebootScript,
failureMessage: "failed to follow reboot",
})
}
k.Write([]byte("Now running e2e tests"))
func TestCordonningIsKeptWithoutConcurrency(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-cordon-without-concurrency",
clusterConfigFile: currentK8sClusterConfig,
manifest: "testfiles/kured-ds-signal.yaml",
testScript: "testfiles/node-stays-as-cordonned.sh",
failureMessage: "node did not reboot in time",
})
}
if err := k.RunCmd("bash", fmt.Sprintf("testfiles/%v.sh", variant), kindContext); err != nil {
t.Fatalf("node blocker test did not succeed: %v", err)
}
})
}
func TestCordonningIsKeptWithConcurrency(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-cordon-with-concurrency",
clusterConfigFile: currentK8sClusterConfig,
manifest: "testfiles/kured-ds-concurrent-signal.yaml",
testScript: "testfiles/node-stays-as-cordonned.sh",
failureMessage: "node did not reboot in time",
})
}
func TestRebootBlockedPodblocker(t *testing.T) {
runE2E(t, e2eScenario{
clusterNamePrefix: "kured-e2e-cordon-podblocker",
clusterConfigFile: currentK8sClusterConfig,
manifest: "testfiles/kured-ds-podblocker.yaml",
testScript: "testfiles/podblocker.sh",
failureMessage: "node blocker test did not succeed",
})
}