diff --git a/tests/e2e/common.go b/tests/e2e/common.go index b4dafbdf..e232c636 100644 --- a/tests/e2e/common.go +++ b/tests/e2e/common.go @@ -45,6 +45,38 @@ type testTool interface { inspectPAndGet(string) (string, error) } +type testMethod func(tool testTool) error + +type containerVolume struct { + Source string + Dest string +} + +type containerTestArgs struct { + Name string + Image string + Devmapper bool + Seccomp bool + UID int + GID int + Groups []int64 + Memory string + Cli string + Volumes []containerVolume + StaticNet bool + SideContainers []string + Skippable bool + TestFunc testMethod + ExpectOut string +} + +const ( + testCtr = "TestCtr" + testCrictl = "TestCrictl" + testDocker = "TestDocker" + testNerdctl = "TestNerdctl" +) + var errToolDoesNotSupport = errors.New("Operation not support") func commonNewContainerCmd(a containerTestArgs) string { diff --git a/tests/e2e/crictl_test.go b/tests/e2e/crictl_test.go index e6e02c9e..3b2e875a 100644 --- a/tests/e2e/crictl_test.go +++ b/tests/e2e/crictl_test.go @@ -22,8 +22,6 @@ import ( . "github.com/onsi/gomega" ) -const testCrictl = "TestCrictl" - var _ = Describe("Crictl", Ordered, ContinueOnFailure, func() { var tool *crictlInfo diff --git a/tests/e2e/ctr_test.go b/tests/e2e/ctr_test.go index 4548ea27..0ccb8c5c 100644 --- a/tests/e2e/ctr_test.go +++ b/tests/e2e/ctr_test.go @@ -22,8 +22,6 @@ import ( . "github.com/onsi/gomega" ) -const testCtr = "TestCtr" - var _ = Describe("Ctr", Ordered, ContinueOnFailure, func() { var tool *ctrInfo diff --git a/tests/e2e/docker_test.go b/tests/e2e/docker_test.go index 2e4f30fa..e95d1030 100644 --- a/tests/e2e/docker_test.go +++ b/tests/e2e/docker_test.go @@ -22,8 +22,6 @@ import ( . "github.com/onsi/gomega" ) -const testDocker = "TestDocker" - var _ = Describe("Docker", Ordered, ContinueOnFailure, func() { var tool *dockerInfo diff --git a/tests/e2e/nerdctl_test.go b/tests/e2e/nerdctl_test.go index 4871ce21..f4fcb7a8 100644 --- a/tests/e2e/nerdctl_test.go +++ b/tests/e2e/nerdctl_test.go @@ -22,8 +22,6 @@ import ( . "github.com/onsi/gomega" ) -const testNerdctl = "TestNerdctl" - var _ = Describe("Nerdctl", Ordered, ContinueOnFailure, func() { var tool *nerdctlInfo diff --git a/tests/e2e/setup_test.go b/tests/e2e/setup_test.go deleted file mode 100644 index f48f87b6..00000000 --- a/tests/e2e/setup_test.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2023-2026, Nubificus LTD -// -// 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 urunce2etesting - -import ( - "flag" - "fmt" - "log" - "os" - "strings" - "testing" - "time" -) - -const ( - maxPullRetries = 5 - pullRetryDelay = 2 * time.Second -) - -func TestMain(m *testing.M) { - flag.Parse() - - testFunc, subtestName := parseTestPattern() - cases := filterTestCases(testFunc, subtestName) - images := getTestImages(cases) - - if err := pullAllImages(testFunc, images); err != nil { - log.Fatalf("Setup failed: %v", err) - } - - code := m.Run() - - removeAllImages(testFunc, images) - - os.Exit(code) -} - -func parseTestPattern() (string, string) { - runFlag := flag.Lookup("test.run") - if runFlag == nil { - return "", "" - } - pattern := runFlag.Value.String() - if pattern == "" { - return "", "" - } - parts := strings.SplitN(pattern, "/", 2) - if len(parts) > 1 { - return parts[0], parts[1] - } - return parts[0], "" -} - -func getTestCases(_ string) []containerTestArgs { - // TODO: Remove along with the rest of the legacy test infrastructure - // in the follow-up cleanup PR. - return []containerTestArgs{} -} - -func filterTestCases(testFunc, subtestName string) []containerTestArgs { - cases := getTestCases(testFunc) - if subtestName == "" { - return cases - } - var filtered []containerTestArgs - for _, tc := range cases { - if strings.Contains(tc.Name, subtestName) { - filtered = append(filtered, tc) - } - } - return filtered -} - -func getTestImages(cases []containerTestArgs) []string { - unique := make(map[string]struct{}) - for _, tc := range cases { - unique[tc.Image] = struct{}{} - } - - images := make([]string, 0, len(unique)) - for img := range unique { - images = append(images, img) - } - return images -} - -func pullAllImages(testFunc string, images []string) error { - for _, image := range images { - log.Printf("Pulling image: %s", image) - if err := pullImageWithRetry(testFunc, image); err != nil { - return fmt.Errorf("failed to pull %s: %w", image, err) - } - } - return nil -} - -func removeAllImages(testFunc string, images []string) { - for _, image := range images { - log.Printf("Removing image: %s", image) - if err := removeImageForTest(testFunc, image); err != nil { - log.Printf("Warning: failed to remove %s: %v", image, err) - } - } -} - -func pullImageWithRetry(testFunc string, image string) error { - var err error - for i := 0; i < maxPullRetries; i++ { - err = pullImageForTest(testFunc, image) - if err == nil { - return nil - } - - fmt.Printf("Attempt %d/%d failed to pull %s: %v. Retrying in %v...\n", i+1, maxPullRetries, image, err, pullRetryDelay) - time.Sleep(pullRetryDelay) - } - return fmt.Errorf("failed to pull %s after %d attempts: %w", image, maxPullRetries, err) -} - -func pullImageForTest(testFunc string, image string) error { - switch testFunc { - case testCrictl: - cmd := crictlName + " pull " + image - output, err := commonCmdExec(cmd) - if err != nil { - return fmt.Errorf("%s -- %v", output, err) - } - return nil - case testNerdctl: - return commonPull(nerdctlName, image) - case testDocker: - return commonPull(dockerName, image) - default: - return commonPull(ctrName, image) - } -} - -func removeImageForTest(testFunc string, image string) error { - switch testFunc { - case testCrictl: - cmd := crictlName + " rmi " + image - output, err := commonCmdExec(cmd) - if err != nil { - return fmt.Errorf("%s -- %v", output, err) - } - return nil - case testNerdctl: - return commonRmImage(nerdctlName, image) - case testDocker: - return commonRmImage(dockerName, image) - default: - return commonRmImage(ctrName, image) - } -} diff --git a/tests/e2e/test_functions.go b/tests/e2e/test_functions.go index 9b5f088b..9e205c49 100644 --- a/tests/e2e/test_functions.go +++ b/tests/e2e/test_functions.go @@ -34,6 +34,34 @@ import ( var matchTest testMethod +func testVerifyRm(tool testTool) error { + containerID := tool.getContainerID() + exists, err := tool.searchContainer(containerID) + if exists || err != nil { + return fmt.Errorf("Container %s is not removed: %v", containerID, err) + } + err = verifyNoStaleFiles(containerID) + if err != nil { + return fmt.Errorf("Failed to remove all stale files: %v", err) + } + + return nil +} + +func testCleanup(tool testTool) error { + err := tool.stopContainer() + if err != nil { + return fmt.Errorf("Failed to stop container: %v", err) + } + + err = tool.rmContainer() + if err != nil { + return fmt.Errorf("Failed to remove container: %v", err) + } + + return testVerifyRm(tool) +} + func blockMountTest(tool testTool) error { args := tool.getTestArgs() output, err := tool.logContainer() diff --git a/tests/e2e/tests_skeleton.go b/tests/e2e/tests_skeleton.go deleted file mode 100644 index ca28abf2..00000000 --- a/tests/e2e/tests_skeleton.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2023-2026, Nubificus LTD -// -// 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 urunce2etesting - -import ( - "fmt" - "os" - "strings" - "testing" - "time" -) - -type testMethod func(tool testTool) error - -type containerVolume struct { - Source string - Dest string -} - -type containerTestArgs struct { - Name string - Image string - Devmapper bool - Seccomp bool - UID int - GID int - Groups []int64 - Memory string - Cli string - Volumes []containerVolume - StaticNet bool - SideContainers []string - Skippable bool - TestFunc testMethod - ExpectOut string -} - -// TODO: Remove runTest along with the rest of the legacy test -// infrastructure in the follow-up cleanup PR. -// -//nolint:unused -func runTest(tool testTool, t *testing.T) { - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("Could not get CWD: %v", err) - } - testDir := t.TempDir() - err = os.Chdir(testDir) - if err != nil { - t.Fatalf("Could not change directory to %s: %v", testDir, err) - } - t.Cleanup(func() { - err = os.Chdir(cwd) - if err != nil { - t.Errorf("Could not switch back to %s: %v", cwd, err) - } - - }) - cntrArgs := tool.getTestArgs() - for _, vol := range cntrArgs.Volumes { - _, err := os.Stat(vol.Source) - if err != nil { - t.Skipf("Could not find %s", vol.Source) - } - } - if cntrArgs.TestFunc == nil { - if tool.Name() == "crictl" { - // TODO: Add support for matchTest in crictl - t.Fatalf("Crictl does not support matchTest") - } - output, err := tool.runContainer(false) - if err != nil { - t.Errorf("Failed to run unikernel container: %s -- %v", output, err) - } - tool.setContainerID(cntrArgs.Name) - if !strings.Contains(string(output), cntrArgs.ExpectOut) { - t.Errorf("Expected: %s, Got: %s", cntrArgs.ExpectOut, output) - } - err = testCleanup(tool) - if err != nil { - t.Errorf("Cleaning up: %v", err) - } - return - } - podID, err := tool.createPod() - if err != nil && err != errToolDoesNotSupport { - t.Fatalf("Failed to create Pod: %s - %v", podID, err) - } - tool.setPodID(podID) - t.Cleanup(func() { - err = tool.stopPod() - if err != nil && err != errToolDoesNotSupport { - t.Errorf("Failed to stop pod: %s - %v", podID, err) - } - - err = tool.rmPod() - if err != nil && err != errToolDoesNotSupport { - t.Errorf("Failed to remove pod: %s - %v", podID, err) - } - }) - cID, err := tool.createContainer() - if err != nil { - t.Fatalf("Failed to create container: %s - %v", cID, err) - } - tool.setContainerID(cID) - t.Cleanup(func() { - err = tool.rmContainer() - if err != nil { - t.Errorf("Failed to remove container: %s - %v", cntrArgs.Image, err) - } - err = testVerifyRm(tool) - if err != nil { - t.Errorf("Failed to verify container removal: %s - %v", cntrArgs.Image, err) - } - - }) - output, err := tool.startContainer(true) - if err != nil { - t.Fatalf("Failed to start unikernel container: %s - %v", output, err) - } - t.Cleanup(func() { - err = tool.stopContainer() - if err != nil { - t.Errorf("Failed to stop container: %s - %v", cntrArgs.Image, err) - } - - }) - // Give some time till the unikernel is up and running. - // Maybe we need to revisit this in the future. - // MirageOS in Qemu(nested) needs more time to be responsive - // in ping requests - time.Sleep(3 * time.Second) - err = cntrArgs.TestFunc(tool) - if err != nil { - t.Fatalf("Failed test: %v", err) - } -} - -func testVerifyRm(tool testTool) error { - containerID := tool.getContainerID() - exists, err := tool.searchContainer(containerID) - if exists || err != nil { - return fmt.Errorf("Container %s is not removed: %v", containerID, err) - } - err = verifyNoStaleFiles(containerID) - if err != nil { - return fmt.Errorf("Failed to remove all stale files: %v", err) - } - - return nil -} - -func testCleanup(tool testTool) error { - err := tool.stopContainer() - if err != nil { - return fmt.Errorf("Failed to stop container: %v", err) - } - - err = tool.rmContainer() - if err != nil { - return fmt.Errorf("Failed to remove container: %v", err) - } - - return testVerifyRm(tool) -} diff --git a/tests/e2e/utils.go b/tests/e2e/utils.go index 4f7a0c11..b8372557 100644 --- a/tests/e2e/utils.go +++ b/tests/e2e/utils.go @@ -17,6 +17,7 @@ package urunce2etesting import ( "bufio" "fmt" + "log" "os" "path/filepath" "strconv" @@ -26,6 +27,93 @@ import ( probing "github.com/prometheus-community/pro-bing" ) +const ( + maxPullRetries = 5 + pullRetryDelay = 2 * time.Second +) + +func getTestImages(cases []containerTestArgs) []string { + unique := make(map[string]struct{}) + for _, tc := range cases { + unique[tc.Image] = struct{}{} + } + + images := make([]string, 0, len(unique)) + for img := range unique { + images = append(images, img) + } + return images +} + +func pullAllImages(testFunc string, images []string) error { + for _, image := range images { + log.Printf("Pulling image: %s", image) + if err := pullImageWithRetry(testFunc, image); err != nil { + return fmt.Errorf("failed to pull %s: %w", image, err) + } + } + return nil +} + +func removeAllImages(testFunc string, images []string) { + for _, image := range images { + log.Printf("Removing image: %s", image) + if err := removeImageForTest(testFunc, image); err != nil { + log.Printf("Warning: failed to remove %s: %v", image, err) + } + } +} + +func pullImageWithRetry(testFunc string, image string) error { + var err error + for i := 0; i < maxPullRetries; i++ { + err = pullImageForTest(testFunc, image) + if err == nil { + return nil + } + + fmt.Printf("Attempt %d/%d failed to pull %s: %v. Retrying in %v...\n", i+1, maxPullRetries, image, err, pullRetryDelay) + time.Sleep(pullRetryDelay) + } + return fmt.Errorf("failed to pull %s after %d attempts: %w", image, maxPullRetries, err) +} + +func pullImageForTest(testFunc string, image string) error { + switch testFunc { + case testCrictl: + cmd := crictlName + " pull " + image + output, err := commonCmdExec(cmd) + if err != nil { + return fmt.Errorf("%s -- %v", output, err) + } + return nil + case testNerdctl: + return commonPull(nerdctlName, image) + case testDocker: + return commonPull(dockerName, image) + default: + return commonPull(ctrName, image) + } +} + +func removeImageForTest(testFunc string, image string) error { + switch testFunc { + case testCrictl: + cmd := crictlName + " rmi " + image + output, err := commonCmdExec(cmd) + if err != nil { + return fmt.Errorf("%s -- %v", output, err) + } + return nil + case testNerdctl: + return commonRmImage(nerdctlName, image) + case testDocker: + return commonRmImage(dockerName, image) + default: + return commonRmImage(ctrName, image) + } +} + func pingUnikernel(ipAddress string) error { pinger, err := probing.NewPinger(ipAddress) if err != nil {