diff --git a/tests/e2e/common.go b/tests/e2e/common.go index 7953d71c1..cecb0f08c 100644 --- a/tests/e2e/common.go +++ b/tests/e2e/common.go @@ -70,13 +70,6 @@ type containerTestArgs struct { 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 8973f8709..c0525c216 100644 --- a/tests/e2e/crictl_test.go +++ b/tests/e2e/crictl_test.go @@ -23,13 +23,8 @@ var _ = Describe("Crictl", Ordered, ContinueOnFailure, func() { var tool *crictlInfo BeforeAll(func() { - cases := crictlTestCases() - images := getTestImages(cases) - err := pullAllImages(testCrictl, images) - Expect(err).NotTo(HaveOccurred(), "Failed to pull crictl images") - DeferCleanup(func() { - removeAllImages(testCrictl, images) + cleanupImages(crictlName) }) }) @@ -48,6 +43,9 @@ var _ = Describe("Crictl", Ordered, ContinueOnFailure, func() { skipMissingVolumes(tc) tool = newCrictlTool(tc) + By("Ensuring images are available") + Expect(ensureTestImages(tool, tc)).To(Succeed()) + By("Creating pod") pID, err := tool.createPod() Expect(err).NotTo(HaveOccurred(), "Failed to create pod: %s", pID) diff --git a/tests/e2e/ctr_test.go b/tests/e2e/ctr_test.go index 9ee2d1b49..aea846b26 100644 --- a/tests/e2e/ctr_test.go +++ b/tests/e2e/ctr_test.go @@ -16,20 +16,14 @@ package urunce2etesting import ( . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" ) var _ = Describe("Ctr", Ordered, ContinueOnFailure, func() { var tool *ctrInfo BeforeAll(func() { - cases := ctrTestCases() - images := getTestImages(cases) - err := pullAllImages(testCtr, images) - Expect(err).NotTo(HaveOccurred(), "Failed to pull ctr images") - DeferCleanup(func() { - removeAllImages(testCtr, images) + cleanupImages(ctrName) }) }) diff --git a/tests/e2e/docker_test.go b/tests/e2e/docker_test.go index 4d61ba07f..ed75c467e 100644 --- a/tests/e2e/docker_test.go +++ b/tests/e2e/docker_test.go @@ -16,20 +16,14 @@ package urunce2etesting import ( . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" ) var _ = Describe("Docker", Ordered, ContinueOnFailure, func() { var tool *dockerInfo BeforeAll(func() { - cases := dockerTestCases() - images := getTestImages(cases) - err := pullAllImages(testDocker, images) - Expect(err).NotTo(HaveOccurred(), "Failed to pull docker images") - DeferCleanup(func() { - removeAllImages(testDocker, images) + cleanupImages(dockerName) }) }) diff --git a/tests/e2e/nerdctl_test.go b/tests/e2e/nerdctl_test.go index 55431fd36..b43e603c7 100644 --- a/tests/e2e/nerdctl_test.go +++ b/tests/e2e/nerdctl_test.go @@ -16,20 +16,14 @@ package urunce2etesting import ( . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" ) var _ = Describe("Nerdctl", Ordered, ContinueOnFailure, func() { var tool *nerdctlInfo BeforeAll(func() { - cases := nerdctlTestCases() - images := getTestImages(cases) - err := pullAllImages(testNerdctl, images) - Expect(err).NotTo(HaveOccurred(), "Failed to pull nerdctl images") - DeferCleanup(func() { - removeAllImages(testNerdctl, images) + cleanupImages(nerdctlName) }) }) diff --git a/tests/e2e/suite_test.go b/tests/e2e/suite_test.go index 03d7db82e..7c74af58d 100644 --- a/tests/e2e/suite_test.go +++ b/tests/e2e/suite_test.go @@ -97,6 +97,9 @@ func captureContainerLogs(tool testTool) { // runDetachedTest runs a container in detached mode: create, start, and // verify via TestFunc. func runDetachedTest(tool testTool, tc containerTestArgs) { + By("Ensuring images are available") + Expect(ensureTestImages(tool, tc)).To(Succeed()) + By("Creating container") cID, err := tool.createContainer() Expect(err).NotTo(HaveOccurred(), "Failed to create container: %s", cID) @@ -135,6 +138,9 @@ func runDetachedTest(tool testTool, tc containerTestArgs) { // runForegroundTest runs a container in the foreground and verifies the // output contains the expected string. func runForegroundTest(tool testTool, tc containerTestArgs) { + By("Ensuring images are available") + Expect(ensureTestImages(tool, tc)).To(Succeed()) + tool.setContainerID(tc.Name) DeferCleanup(func() { diff --git a/tests/e2e/utils.go b/tests/e2e/utils.go index b83725571..0fd382497 100644 --- a/tests/e2e/utils.go +++ b/tests/e2e/utils.go @@ -22,6 +22,7 @@ import ( "path/filepath" "strconv" "strings" + "sync" "time" probing "github.com/prometheus-community/pro-bing" @@ -32,42 +33,69 @@ const ( pullRetryDelay = 2 * time.Second ) -func getTestImages(cases []containerTestArgs) []string { - unique := make(map[string]struct{}) - for _, tc := range cases { - unique[tc.Image] = struct{}{} +var ( + pulledImagesLock sync.Mutex + // pulledImages maps tool -> image -> bool. + pulledImages = make(map[string]map[string]bool) +) + +func ensureImage(tool string, image string) error { + pulledImagesLock.Lock() + if pulledImages[tool] == nil { + pulledImages[tool] = make(map[string]bool) + } + if pulledImages[tool][image] { + pulledImagesLock.Unlock() + return nil } + pulledImagesLock.Unlock() - images := make([]string, 0, len(unique)) - for img := range unique { - images = append(images, img) + log.Printf("Pulling image for %s: %s", tool, image) + if err := pullImageWithRetry(tool, image); err != nil { + return fmt.Errorf("failed to pull %s: %w", image, err) } - return images + + pulledImagesLock.Lock() + pulledImages[tool][image] = true + pulledImagesLock.Unlock() + + return nil } -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) +func ensureTestImages(tool testTool, tc containerTestArgs) error { + if tc.Image != "" { + if err := ensureImage(tool.Name(), tc.Image); err != nil { + return err + } + } + for _, side := range tc.SideContainers { + if side != "" { + if err := ensureImage(tool.Name(), side); err != nil { + return 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 cleanupImages(tool string) { + pulledImagesLock.Lock() + toolImages := pulledImages[tool] + delete(pulledImages, tool) + pulledImagesLock.Unlock() + + for img := range toolImages { + log.Printf("Removing image for %s: %s", tool, img) + if err := removeImageForTest(tool, img); err != nil { + log.Printf("Warning: failed to remove %s: %v", img, err) } } } -func pullImageWithRetry(testFunc string, image string) error { +func pullImageWithRetry(tool string, image string) error { var err error for i := 0; i < maxPullRetries; i++ { - err = pullImageForTest(testFunc, image) + err = pullImageForTest(tool, image) if err == nil { return nil } @@ -78,39 +106,31 @@ func pullImageWithRetry(testFunc string, image string) error { 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: +func pullImageForTest(tool string, image string) error { + switch tool { + case crictlName: 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) + return commonPull(tool, image) } } -func removeImageForTest(testFunc string, image string) error { - switch testFunc { - case testCrictl: +func removeImageForTest(tool string, image string) error { + switch tool { + case crictlName: 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) + return commonRmImage(tool, image) } }