From 12b7ead6ed26e8dc1cae5dac849c3ec0dac05844 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 23 Aug 2025 21:06:42 +0300 Subject: [PATCH 01/11] test: Use fully qualified image name We can pull unqualified image to docker: kicbase/echo-server:latest but the image is created with the fully qualified name: docker.io/kicbase/echo-server:latest Tagging works with the unqualified name, but when we load images to minikube the we get the fully qualified name. Mixing unqualified and qualified is confusing and make searching harder if we want to exact matching. --- test/integration/constats_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/constats_test.go b/test/integration/constats_test.go index a597411e4b..8462a475a1 100644 --- a/test/integration/constats_test.go +++ b/test/integration/constats_test.go @@ -19,5 +19,5 @@ limitations under the License. package integration const ( - echoServerImage = "kicbase/echo-server" + echoServerImage = "docker.io/kicbase/echo-server" ) From ac378f46f578f1f9fac88e47612c533839c110bf Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 23 Aug 2025 15:34:29 +0300 Subject: [PATCH 02/11] test: Improve image list checks We had similar code to check if image exists or not. Add checkImageNotExists() helper and refactor listImages() helper to make it easier to use. listImages() fails the test so the caller do no not need to check for errors. It returns list of images so the caller does not have to parse the output. When parsing the output of `minikube image ls` use scanner to split the output to lines. Previously we search images in rr.Output() (formatted message with contents of stdout and stderr) instead of rr.Stdout.String(), which could lead to returning a wrong result. --- test/integration/functional_test.go | 45 ++++++++++++++++++----------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 9888cf90ba..4f4b420886 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -34,6 +34,7 @@ import ( "path/filepath" "regexp" "runtime" + "slices" "strings" "testing" "time" @@ -409,14 +410,7 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { t.Fatalf("removing image from minikube: %v\n%s", err, rr.Output()) } - // make sure the image was removed - rr, err = listImages(ctx, t, profile) - if err != nil { - t.Fatalf("listing images: %v\n%s", err, rr.Output()) - } - if strings.Contains(rr.Output(), taggedImage) { - t.Fatalf("expected %q to be removed from minikube but still exists", taggedImage) - } + checkImageNotExists(ctx, t, profile, taggedImage) }) // docs: Make sure image loading from file works by `minikube image load` @@ -452,18 +446,35 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { } func checkImageExists(ctx context.Context, t *testing.T, profile string, image string) { - // make sure the image was correctly loaded - rr, err := listImages(ctx, t, profile) - if err != nil { - t.Fatalf("listing images: %v\n%s", err, rr.Output()) - } - if !strings.Contains(rr.Output(), image) { - t.Fatalf("expected %q to be loaded into minikube but the image is not there", image) + images := listImages(ctx, t, profile) + if !slices.Contains(images, image) { + t.Fatalf("expected %q to exist in minikube but the image is not there", image) } } -func listImages(ctx context.Context, t *testing.T, profile string) (*RunResult, error) { - return Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "ls")) +func checkImageNotExists(ctx context.Context, t *testing.T, profile string, image string) { + images := listImages(ctx, t, profile) + if slices.Contains(images, image) { + t.Fatalf("expected %q to not exist in minikube but the image is there", image) + } +} + +func listImages(ctx context.Context, t *testing.T, profile string) []string { + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "ls")) + if err != nil { + t.Fatalf("failed to list images: %v\n%s", err, rr.Output()) + } + + scanner := bufio.NewScanner(rr.Stdout) + var images []string + for scanner.Scan() { + images = append(images, scanner.Text()) + } + if err := scanner.Err(); err != nil { + t.Fatalf("failed to scan lines: %v: %q", err, rr.Stdout.String()) + } + + return images } // check functionality of minikube after evaluating docker-env From 7affa76cc5947143a67dbf7618a68c94b146cc70 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 23 Aug 2025 15:56:38 +0300 Subject: [PATCH 03/11] test: Unify test names Add load and save tests use ToFile, ToDaemon, FromFile, FromDaemon suffix. --- test/integration/functional_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 4f4b420886..f4f4244c8c 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -367,7 +367,7 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { }) // docs: Make sure image loading from Docker daemon works by `minikube image load --daemon` - t.Run("ImageLoadDaemon", func(t *testing.T) { + t.Run("ImageLoadFromDaemon", func(t *testing.T) { rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", taggedImage, "--alsologtostderr")) if err != nil { t.Fatalf("loading image into minikube from daemon: %v\n%s", err, rr.Output()) @@ -377,7 +377,7 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { }) // docs: Try to load image already loaded and make sure `minikube image load --daemon` works - t.Run("ImageReloadDaemon", func(t *testing.T) { + t.Run("ImageReloadFromDaemon", func(t *testing.T) { rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", taggedImage, "--alsologtostderr")) if err != nil { t.Fatalf("loading image into minikube from daemon: %v\n%s", err, rr.Output()) @@ -387,7 +387,7 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { }) // docs: Make sure a new updated tag works by `minikube image load --daemon` - t.Run("ImageTagAndLoadDaemon", func(t *testing.T) { + t.Run("ImageTagAndLoadFromDaemon", func(t *testing.T) { tagAndLoadImage(ctx, t, profile, taggedImage) }) @@ -424,7 +424,7 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { }) // docs: Make sure image saving to Docker daemon works by `minikube image load` - t.Run("ImageSaveDaemon", func(t *testing.T) { + t.Run("ImageSaveToDaemon", func(t *testing.T) { rr, err := Run(t, exec.CommandContext(ctx, "docker", "rmi", taggedImage)) if err != nil { t.Fatalf("failed to remove image from docker: %v\n%s", err, rr.Output()) From b2acf957d714fd787c9e09afae8604f03e93f826 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 23 Aug 2025 15:58:34 +0300 Subject: [PATCH 04/11] test: Inline unneeded helper tagAndLoadDaemon was called once from function doing nothing else. Inline it in the caller. --- test/integration/functional_test.go | 39 +++++++++++++---------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index f4f4244c8c..d49b6b9d17 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -245,27 +245,6 @@ func validateNodeLabels(ctx context.Context, t *testing.T, profile string) { } } -// tagAndLoadImage is a helper function to pull, tag, load image (decreases cyclomatic complexity for linter). -func tagAndLoadImage(ctx context.Context, t *testing.T, profile, taggedImage string) { - newPulledImage := fmt.Sprintf("%s:%s", echoServerImage, "latest") - rr, err := Run(t, exec.CommandContext(ctx, "docker", "pull", newPulledImage)) - if err != nil { - t.Fatalf("failed to setup test (pull image): %v\n%s", err, rr.Output()) - } - - rr, err = Run(t, exec.CommandContext(ctx, "docker", "tag", newPulledImage, taggedImage)) - if err != nil { - t.Fatalf("failed to setup test (tag image) : %v\n%s", err, rr.Output()) - } - - rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", taggedImage, "--alsologtostderr")) - if err != nil { - t.Fatalf("loading image into minikube from daemon: %v\n%s", err, rr.Output()) - } - - checkImageExists(ctx, t, profile, taggedImage) -} - // runImageList is a helper function to run 'image ls' command test. func runImageList(ctx context.Context, t *testing.T, profile, testName, format, expectedFormat string) { expectedResult := expectedImageFormat(expectedFormat) @@ -388,7 +367,23 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { // docs: Make sure a new updated tag works by `minikube image load --daemon` t.Run("ImageTagAndLoadFromDaemon", func(t *testing.T) { - tagAndLoadImage(ctx, t, profile, taggedImage) + newPulledImage := fmt.Sprintf("%s:%s", echoServerImage, "latest") + rr, err := Run(t, exec.CommandContext(ctx, "docker", "pull", newPulledImage)) + if err != nil { + t.Fatalf("failed to setup test (pull image): %v\n%s", err, rr.Output()) + } + + rr, err = Run(t, exec.CommandContext(ctx, "docker", "tag", newPulledImage, taggedImage)) + if err != nil { + t.Fatalf("failed to setup test (tag image) : %v\n%s", err, rr.Output()) + } + + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", taggedImage, "--alsologtostderr")) + if err != nil { + t.Fatalf("loading image into minikube from daemon: %v\n%s", err, rr.Output()) + } + + checkImageExists(ctx, t, profile, taggedImage) }) // docs: Make sure image saving works by `minikube image load --daemon` From 646794e917d826c678b9ef45c28d1a7d56462a6c Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 23 Aug 2025 16:03:42 +0300 Subject: [PATCH 05/11] test: Improve image list tests Improve the image list tests to actually parsing the json and yaml output and extract list of image names. The ensures that the expected images names exist in minikube. Previously we looked up the image names in rr.Output() is a multiple formatted message including additional text and contents of stdout and stderr. Using it for searching images can return the wrong result. --- test/integration/functional_test.go | 155 +++++++++++++++++++--------- 1 file changed, 107 insertions(+), 48 deletions(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index d49b6b9d17..2ce2e785f3 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -40,6 +40,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + "gopkg.in/yaml.v2" "k8s.io/minikube/pkg/drivers/kic/oci" "k8s.io/minikube/pkg/minikube/config" @@ -71,6 +72,11 @@ var mitm *StartSession var runCorpProxy = detect.GithubActionRunner() && runtime.GOOS == "linux" && !arm64Platform() +var expectedImageNames = []string{ + "registry.k8s.io/pause", + "registry.k8s.io/kube-apiserver", +} + // TestFunctional are functionality tests which can safely share a profile in parallel func TestFunctional(t *testing.T) { testFunctional(t, "") @@ -245,41 +251,6 @@ func validateNodeLabels(ctx context.Context, t *testing.T, profile string) { } } -// runImageList is a helper function to run 'image ls' command test. -func runImageList(ctx context.Context, t *testing.T, profile, testName, format, expectedFormat string) { - expectedResult := expectedImageFormat(expectedFormat) - - // docs: Make sure image listing works by `minikube image ls` - t.Run(testName, func(t *testing.T) { - MaybeParallel(t) - - rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "ls", "--format", format, "--alsologtostderr")) - if err != nil { - t.Fatalf("listing image with minikube: %v\n%s", err, rr.Output()) - } - if rr.Stdout.Len() > 0 { - t.Logf("(dbg) Stdout: %s:\n%s", rr.Command(), rr.Stdout) - } - if rr.Stderr.Len() > 0 { - t.Logf("(dbg) Stderr: %s:\n%s", rr.Command(), rr.Stderr) - } - - list := rr.Output() - for _, theImage := range expectedResult { - if !strings.Contains(list, theImage) { - t.Fatalf("expected %s to be listed with minikube but the image is not there", theImage) - } - } - }) -} - -func expectedImageFormat(format string) []string { - return []string{ - fmt.Sprintf(format, "registry.k8s.io/pause"), - fmt.Sprintf(format, "registry.k8s.io/kube-apiserver"), - } -} - // validateImageCommands runs tests on all the `minikube image` commands, ex. `minikube image load`, `minikube image list`, etc. func validateImageCommands(ctx context.Context, t *testing.T, profile string) { // docs(skip): Skips on `none` driver as image loading is not supported @@ -291,10 +262,50 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { t.Skip("skipping on darwin github action runners, as this test requires a running docker daemon") } - runImageList(ctx, t, profile, "ImageListShort", "short", "%s") - runImageList(ctx, t, profile, "ImageListTable", "table", "│ %s") - runImageList(ctx, t, profile, "ImageListJson", "json", "[\"%s") - runImageList(ctx, t, profile, "ImageListYaml", "yaml", "- %s") + // docs: Make sure image listing works by `minikube image ls --format short` + t.Run("ImageListShort", func(t *testing.T) { + images := listImagesShort(ctx, t, profile) + names := imageNames(images) + for _, name := range expectedImageNames { + if _, ok := names[name]; !ok { + t.Errorf("expected %q to be listed with minikube but the image is not there", name) + } + } + }) + + // docs: Make sure image listing works by `minikube image ls --format table` + t.Run("ImageListTable", func(t *testing.T) { + out := listImagesTable(ctx, t, profile) + for _, name := range expectedImageNames { + // | registry/name:tag | ... + needle := fmt.Sprintf("| %s ", name) + if strings.Contains(out, needle) { + t.Errorf("expected %q to be listed with minikube but the image is not there", name) + } + } + }) + + // docs: Make sure image listing works by `minikube image ls --format json` + t.Run("ImageListJSON", func(t *testing.T) { + images := listImagesJSON(ctx, t, profile) + names := repoTagNames(images) + for _, name := range expectedImageNames { + if _, ok := names[name]; !ok { + t.Errorf("expected %q to be listed with minikube but the image is not there", name) + } + } + }) + + // docs: Make sure image listing works by `minikube image ls --format yaml` + t.Run("ImageListYAML", func(t *testing.T) { + images := listImagesJSON(ctx, t, profile) + names := repoTagNames(images) + for _, name := range expectedImageNames { + if _, ok := names[name]; !ok { + t.Errorf("expected %q to be listed with minikube but the image is not there", name) + } + } + }) // docs: Make sure image building works by `minikube image build` t.Run("ImageBuild", func(t *testing.T) { @@ -441,25 +452,21 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { } func checkImageExists(ctx context.Context, t *testing.T, profile string, image string) { - images := listImages(ctx, t, profile) + images := listImagesShort(ctx, t, profile) if !slices.Contains(images, image) { t.Fatalf("expected %q to exist in minikube but the image is not there", image) } } func checkImageNotExists(ctx context.Context, t *testing.T, profile string, image string) { - images := listImages(ctx, t, profile) + images := listImagesShort(ctx, t, profile) if slices.Contains(images, image) { t.Fatalf("expected %q to not exist in minikube but the image is there", image) } } -func listImages(ctx context.Context, t *testing.T, profile string) []string { - rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "ls")) - if err != nil { - t.Fatalf("failed to list images: %v\n%s", err, rr.Output()) - } - +func listImagesShort(ctx context.Context, t *testing.T, profile string) []string { + rr := listImages(ctx, t, profile, "short") scanner := bufio.NewScanner(rr.Stdout) var images []string for scanner.Scan() { @@ -468,10 +475,62 @@ func listImages(ctx context.Context, t *testing.T, profile string) []string { if err := scanner.Err(); err != nil { t.Fatalf("failed to scan lines: %v: %q", err, rr.Stdout.String()) } - return images } +func listImagesTable(ctx context.Context, t *testing.T, profile string) string { + rr := listImages(ctx, t, profile, "table") + return rr.Stdout.String() +} + +func listImagesJSON(ctx context.Context, t *testing.T, profile string) []cruntime.ListImage { + rr := listImages(ctx, t, profile, "json") + var images []cruntime.ListImage + if err := json.Unmarshal(rr.Stdout.Bytes(), &images); err != nil { + t.Fatalf("failed to parse json: %v\n%q", err, rr.Stdout.String()) + } + return images +} + +func listImagesYAML(ctx context.Context, t *testing.T, profile string) []cruntime.ListImage { + rr := listImages(ctx, t, profile, "yaml") + var images []cruntime.ListImage + if err := yaml.Unmarshal(rr.Stdout.Bytes(), &images); err != nil { + t.Fatalf("failed to parse json: %v\n%q", err, rr.Stdout.String()) + } + return images +} + +func listImages(ctx context.Context, t *testing.T, profile string, format string) *RunResult { + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "ls", "--format", format)) + if err != nil { + t.Fatalf("failed to list images: %v\n%s", err, rr.Output()) + } + return rr +} + +func repoTagNames(images []cruntime.ListImage) map[string]struct{} { + names := map[string]struct{}{} + for _, image := range images { + for _, repoTag := range image.RepoTags { + // "registry/repo/name:tag" -> ["registry/repo/name", "tag"] + parts := strings.SplitN(repoTag, ":", 2) + names[parts[0]] = struct{}{} + } + } + return names +} + +func imageNames(images []string) map[string]struct{} { + names := map[string]struct{}{} + for _, image := range images { + // "registry/repo/name:tag" -> ["registry/repo/name", "tag"] + parts := strings.SplitN(image, ":", 2) + names[parts[0]] = struct{}{} + } + return names +} + // check functionality of minikube after evaluating docker-env func validateDockerEnv(ctx context.Context, t *testing.T, profile string) { // docs(skip): Skips on `none` drive since `docker-env` is not supported From 4aea9a42678ba107fb9e23c600b59cf7667b027d Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 23 Aug 2025 18:18:20 +0300 Subject: [PATCH 06/11] test: Group related image tests Group ImageSaveToDaemon with other Image*Daemon tests. Grouping related tests make it easier to work with the code. --- test/integration/functional_test.go | 42 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 2ce2e785f3..62809384fd 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -397,6 +397,27 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { checkImageExists(ctx, t, profile, taggedImage) }) + // docs: Make sure image saving to Docker daemon works by `minikube image save --daemon` + t.Run("ImageSaveToDaemon", func(t *testing.T) { + rr, err := Run(t, exec.CommandContext(ctx, "docker", "rmi", taggedImage)) + if err != nil { + t.Fatalf("failed to remove image from docker: %v\n%s", err, rr.Output()) + } + + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "save", "--daemon", taggedImage, "--alsologtostderr")) + if err != nil { + t.Fatalf("saving image from minikube to daemon: %v\n%s", err, rr.Output()) + } + imageToDelete := taggedImage + if ContainerRuntime() == "crio" { + imageToDelete = cruntime.AddLocalhostPrefix(imageToDelete) + } + rr, err = Run(t, exec.CommandContext(ctx, "docker", "image", "inspect", imageToDelete)) + if err != nil { + t.Fatalf("expected image to be loaded into Docker, but image was not found: %v\n%s", err, rr.Output()) + } + }) + // docs: Make sure image saving works by `minikube image load --daemon` t.Run("ImageSaveToFile", func(t *testing.T) { rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "save", taggedImage, imagePath, "--alsologtostderr")) @@ -428,27 +449,6 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { checkImageExists(ctx, t, profile, taggedImage) }) - - // docs: Make sure image saving to Docker daemon works by `minikube image load` - t.Run("ImageSaveToDaemon", func(t *testing.T) { - rr, err := Run(t, exec.CommandContext(ctx, "docker", "rmi", taggedImage)) - if err != nil { - t.Fatalf("failed to remove image from docker: %v\n%s", err, rr.Output()) - } - - rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "save", "--daemon", taggedImage, "--alsologtostderr")) - if err != nil { - t.Fatalf("saving image from minikube to daemon: %v\n%s", err, rr.Output()) - } - imageToDelete := taggedImage - if ContainerRuntime() == "crio" { - imageToDelete = cruntime.AddLocalhostPrefix(imageToDelete) - } - rr, err = Run(t, exec.CommandContext(ctx, "docker", "image", "inspect", imageToDelete)) - if err != nil { - t.Fatalf("expected image to be loaded into Docker, but image was not found: %v\n%s", err, rr.Output()) - } - }) } func checkImageExists(ctx context.Context, t *testing.T, profile string, image string) { From 79391d138d39fe4e2cc0b4829cedd85842b3791a Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 23 Aug 2025 18:56:21 +0300 Subject: [PATCH 07/11] test: Rewrite independent images tests Rewrite test for loading image from files, saving to file, removing and tagging images. These tests do not depend on docker or podman and can use the container runtime in minikube. Testing image commands without depending on docker or podman requires dependency on an image inside minikube. We depend on the pause image which is part of k8s. We know the image name but not the tag since we may run different versions of k8s. Add findPauseImage() helper to return the image name. Testing requires dependency on minikube commands. Extract tagImage(), removeImage(), saveImageToFile(), and loadImageFromFile() helpers. We test in the following order: 1. Test image tag using the pause image, using `image rm` to clean up (new test). 2. Test image remove using the `image tag` to create a new image. 3. Test image save to file using the pause image. 4. Test image load from file by creating new image using `image tag` saving to file using `image save`, removing using `image rm`, and loading it from file using `image load`. The modified tests do not use the global variables. Each test uses its own image name and paths are in temporary test directory so they are cleaned up automatically. This change fixes 2 failing tests and add a new test. --- test/integration/constats_test.go | 1 + test/integration/functional_test.go | 108 +++++++++++++++++++++------- 2 files changed, 83 insertions(+), 26 deletions(-) diff --git a/test/integration/constats_test.go b/test/integration/constats_test.go index 8462a475a1..d5372a434e 100644 --- a/test/integration/constats_test.go +++ b/test/integration/constats_test.go @@ -20,4 +20,5 @@ package integration const ( echoServerImage = "docker.io/kicbase/echo-server" + pauseImageName = "registry.k8s.io/pause" ) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 62809384fd..045d36d204 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -333,16 +333,9 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { }) taggedImage := fmt.Sprintf("%s:%s", echoServerImage, profile) - imageFile := "echo-server-save.tar" - var imagePath string - defer os.Remove(imageFile) t.Run("Setup", func(t *testing.T) { var err error - imagePath, err = filepath.Abs(imageFile) - if err != nil { - t.Fatalf("failed to get absolute path of file %q: %v", imageFile, err) - } pulledImage := fmt.Sprintf("%s:%s", echoServerImage, "1.0") rr, err := Run(t, exec.CommandContext(ctx, "docker", "pull", pulledImage)) @@ -418,36 +411,58 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { } }) - // docs: Make sure image saving works by `minikube image load --daemon` - t.Run("ImageSaveToFile", func(t *testing.T) { - rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "save", taggedImage, imagePath, "--alsologtostderr")) - if err != nil { - t.Fatalf("saving image from minikube to file: %v\n%s", err, rr.Output()) - } + // docs: Make sure image tagging works by `minikube image tag` + t.Run("ImageTag", func(t *testing.T) { + pauseImage := findPauseImage(ctx, t, profile) + testImage := "localhost/image-tag:test" - if _, err := os.Stat(imagePath); err != nil { - t.Fatalf("expected %q to exist after `image save`, but doesn't exist", imagePath) - } + tagImage(ctx, t, profile, pauseImage, testImage) + t.Cleanup(func() { + removeImage(ctx, t, profile, testImage) + }) + + checkImageExists(ctx, t, profile, testImage) }) // docs: Make sure image removal works by `minikube image rm` t.Run("ImageRemove", func(t *testing.T) { - rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "rm", taggedImage, "--alsologtostderr")) - if err != nil { - t.Fatalf("removing image from minikube: %v\n%s", err, rr.Output()) - } + pauseImage := findPauseImage(ctx, t, profile) + testImage := "localhost/image-remove:test" - checkImageNotExists(ctx, t, profile, taggedImage) + tagImage(ctx, t, profile, pauseImage, testImage) + removeImage(ctx, t, profile, testImage) + + checkImageNotExists(ctx, t, profile, testImage) + }) + + // docs: Make sure image saving works by `minikube image load --daemon` + t.Run("ImageSaveToFile", func(t *testing.T) { + pauseImage := findPauseImage(ctx, t, profile) + testPath := filepath.Join(t.TempDir(), "test.tar") + + saveImageToFile(ctx, t, profile, pauseImage, testPath) + + if _, err := os.Stat(testPath); err != nil { + t.Fatalf("expected %q to exist after `image save`, but doesn't exist", testPath) + } }) // docs: Make sure image loading from file works by `minikube image load` t.Run("ImageLoadFromFile", func(t *testing.T) { - rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", imagePath, "--alsologtostderr")) - if err != nil || strings.Contains(rr.Output(), "failed pushing to: functional") { - t.Fatalf("loading image into minikube from file: %v\n%s", err, rr.Output()) - } + pauseImage := findPauseImage(ctx, t, profile) + testImage := "localhost/image-load-from-file:test" + testPath := filepath.Join(t.TempDir(), "test.tar") - checkImageExists(ctx, t, profile, taggedImage) + tagImage(ctx, t, profile, pauseImage, testImage) + t.Cleanup(func() { + removeImage(ctx, t, profile, testImage) + }) + + saveImageToFile(ctx, t, profile, testImage, testPath) + removeImage(ctx, t, profile, testImage) + loadImageFromFile(ctx, t, profile, testPath) + + checkImageExists(ctx, t, profile, testImage) }) } @@ -531,6 +546,47 @@ func imageNames(images []string) map[string]struct{} { return names } +func findPauseImage(ctx context.Context, t *testing.T, profile string) string { + images := listImagesShort(ctx, t, profile) + for _, image := range images { + // "registry/repo/name:tag" -> ["registry/repo/name", "tag"] + parts := strings.SplitN(image, ":", 2) + if parts[0] == pauseImageName { + return image + } + } + t.Fatalf("failed to find pause image %q", pauseImageName) + return "" +} + +func tagImage(ctx context.Context, t *testing.T, profile string, sourceImage string, targetImage string) { + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "tag", sourceImage, targetImage, "--alsologtostderr")) + if err != nil { + t.Fatalf("failed to tag image: %v\n%s", err, rr.Output()) + } +} + +func removeImage(ctx context.Context, t *testing.T, profile string, image string) { + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "rm", image, "--alsologtostderr")) + if err != nil { + t.Fatalf("failed to remove image: %v\n%s", err, rr.Output()) + } +} + +func saveImageToFile(ctx context.Context, t *testing.T, profile string, sourceImage string, targetPath string) { + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "save", sourceImage, targetPath, "--alsologtostderr")) + if err != nil { + t.Fatalf("failed to save image to file: %v\n%s", err, rr.Output()) + } +} + +func loadImageFromFile(ctx context.Context, t *testing.T, profile string, sourcePath string) { + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", sourcePath, "--alsologtostderr")) + if err != nil || strings.Contains(rr.Output(), "failed pushing to: functional") { + t.Fatalf("failed to load image from file: %v\n%s", err, rr.Output()) + } +} + // check functionality of minikube after evaluating docker-env func validateDockerEnv(ctx context.Context, t *testing.T, profile string) { // docs(skip): Skips on `none` drive since `docker-env` is not supported From dacee036c12395efe41f909d237faa9e4750e9df Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 23 Aug 2025 19:46:29 +0300 Subject: [PATCH 08/11] test: Skip tests that require docker daemon Skip tests that require a docker daemon if one is not available on the host. This eliminate the skip on github actions on darwin, enabling image tests that were previously skipped. With this change functional tests pass on macOS and may pass also in github actions. --- PASS: TestFunctional/parallel/ImageCommands (1.62s) --- PASS: TestFunctional/parallel/ImageCommands/ImageListShort (0.07s) --- PASS: TestFunctional/parallel/ImageCommands/ImageListTable (0.07s) --- PASS: TestFunctional/parallel/ImageCommands/ImageListJSON (0.07s) --- PASS: TestFunctional/parallel/ImageCommands/ImageListYAML (0.07s) --- SKIP: TestFunctional/parallel/ImageCommands/SetupDaemon (0.00s) --- SKIP: TestFunctional/parallel/ImageCommands/ImageLoadFromDaemon (0.00s) --- SKIP: TestFunctional/parallel/ImageCommands/ImageReloadFromDaemon (0.00s) --- SKIP: TestFunctional/parallel/ImageCommands/ImageTagAndLoadFromDaemon (0.00s) --- SKIP: TestFunctional/parallel/ImageCommands/ImageSaveToDaemon (0.00s) --- PASS: TestFunctional/parallel/ImageCommands/ImageTag (0.30s) --- PASS: TestFunctional/parallel/ImageCommands/ImageRemove (0.29s) --- PASS: TestFunctional/parallel/ImageCommands/ImageSaveToFile (0.17s) --- PASS: TestFunctional/parallel/ImageCommands/ImageLoadFromFile (0.58s) --- PASS: TestFunctional/parallel/ImageCommands/ImageBuild (4.21s) --- test/integration/functional_test.go | 31 ++++++++++++++++++++++++----- test/integration/main_test.go | 10 ++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 045d36d204..4ec5b5ef12 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -257,10 +257,6 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { if NoneDriver() { t.Skip("image commands are not available on the none driver") } - // docs(skip): Skips on GitHub Actions and macOS as this test case requires a running docker daemon - if detect.GithubActionRunner() && runtime.GOOS == "darwin" { - t.Skip("skipping on darwin github action runners, as this test requires a running docker daemon") - } // docs: Make sure image listing works by `minikube image ls --format short` t.Run("ImageListShort", func(t *testing.T) { @@ -334,9 +330,13 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { taggedImage := fmt.Sprintf("%s:%s", echoServerImage, profile) - t.Run("Setup", func(t *testing.T) { + t.Run("SetupDaemon", func(t *testing.T) { var err error + if !HaveDockerDaemon() { + t.Skip("docker daemon is not available on this host") + } + pulledImage := fmt.Sprintf("%s:%s", echoServerImage, "1.0") rr, err := Run(t, exec.CommandContext(ctx, "docker", "pull", pulledImage)) if err != nil { @@ -351,6 +351,10 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { // docs: Make sure image loading from Docker daemon works by `minikube image load --daemon` t.Run("ImageLoadFromDaemon", func(t *testing.T) { + if !HaveDockerDaemon() { + t.Skip("docker daemon is not available on this host") + } + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", taggedImage, "--alsologtostderr")) if err != nil { t.Fatalf("loading image into minikube from daemon: %v\n%s", err, rr.Output()) @@ -361,6 +365,10 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { // docs: Try to load image already loaded and make sure `minikube image load --daemon` works t.Run("ImageReloadFromDaemon", func(t *testing.T) { + if !HaveDockerDaemon() { + t.Skip("docker daemon is not available on this host") + } + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", taggedImage, "--alsologtostderr")) if err != nil { t.Fatalf("loading image into minikube from daemon: %v\n%s", err, rr.Output()) @@ -371,6 +379,10 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { // docs: Make sure a new updated tag works by `minikube image load --daemon` t.Run("ImageTagAndLoadFromDaemon", func(t *testing.T) { + if !HaveDockerDaemon() { + t.Skip("docker daemon is not available on this host") + } + newPulledImage := fmt.Sprintf("%s:%s", echoServerImage, "latest") rr, err := Run(t, exec.CommandContext(ctx, "docker", "pull", newPulledImage)) if err != nil { @@ -392,6 +404,10 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { // docs: Make sure image saving to Docker daemon works by `minikube image save --daemon` t.Run("ImageSaveToDaemon", func(t *testing.T) { + if !HaveDockerDaemon() { + t.Skip("docker daemon is not available on this host") + } + rr, err := Run(t, exec.CommandContext(ctx, "docker", "rmi", taggedImage)) if err != nil { t.Fatalf("failed to remove image from docker: %v\n%s", err, rr.Output()) @@ -598,6 +614,11 @@ func validateDockerEnv(ctx context.Context, t *testing.T, profile string) { if cr := ContainerRuntime(); cr != "docker" { t.Skipf("only validate docker env with docker container runtime, currently testing %s", cr) } + + if _, err := exec.LookPath(oci.Docker); err != nil { + t.Skipf("docker command is not found on the host") + } + defer PostMortemLogs(t, profile) type ShellTest struct { diff --git a/test/integration/main_test.go b/test/integration/main_test.go index 29354089fc..e7532a1471 100644 --- a/test/integration/main_test.go +++ b/test/integration/main_test.go @@ -29,6 +29,7 @@ import ( "testing" "time" + "k8s.io/minikube/pkg/drivers/kic/oci" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/detect" ) @@ -186,6 +187,15 @@ func ContainerRuntime() string { return constants.Docker } +// HaveDockerDaemon return true if docker daemon is accessble on the host. Can +// be used to skip tests depending on docker daemon. +func HaveDockerDaemon() bool { + if _, err := oci.CachedDaemonInfo(oci.Docker); err == nil { + return true + } + return false +} + // arm64Platform returns true if running on arm64/* platform func arm64Platform() bool { return runtime.GOARCH == "arm64" From 64d64ebda316ab4b4d3c8da308ae050d3f501c73 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 23 Aug 2025 19:51:15 +0300 Subject: [PATCH 09/11] test: Rename taggedImage to daemonTestImage This share image name create by SetupDaemon and used by the Image*Daemon tests. Rename to make this more clear. --- test/integration/functional_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 4ec5b5ef12..d7abffedf5 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -328,7 +328,7 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { checkImageExists(ctx, t, profile, newImage) }) - taggedImage := fmt.Sprintf("%s:%s", echoServerImage, profile) + daemonTestImage := fmt.Sprintf("%s:%s", echoServerImage, profile) t.Run("SetupDaemon", func(t *testing.T) { var err error @@ -343,7 +343,7 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { t.Fatalf("failed to setup test (pull image): %v\n%s", err, rr.Output()) } - rr, err = Run(t, exec.CommandContext(ctx, "docker", "tag", pulledImage, taggedImage)) + rr, err = Run(t, exec.CommandContext(ctx, "docker", "tag", pulledImage, daemonTestImage)) if err != nil { t.Fatalf("failed to setup test (tag image) : %v\n%s", err, rr.Output()) } @@ -355,12 +355,12 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { t.Skip("docker daemon is not available on this host") } - rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", taggedImage, "--alsologtostderr")) + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", daemonTestImage, "--alsologtostderr")) if err != nil { t.Fatalf("loading image into minikube from daemon: %v\n%s", err, rr.Output()) } - checkImageExists(ctx, t, profile, taggedImage) + checkImageExists(ctx, t, profile, daemonTestImage) }) // docs: Try to load image already loaded and make sure `minikube image load --daemon` works @@ -369,12 +369,12 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { t.Skip("docker daemon is not available on this host") } - rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", taggedImage, "--alsologtostderr")) + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", daemonTestImage, "--alsologtostderr")) if err != nil { t.Fatalf("loading image into minikube from daemon: %v\n%s", err, rr.Output()) } - checkImageExists(ctx, t, profile, taggedImage) + checkImageExists(ctx, t, profile, daemonTestImage) }) // docs: Make sure a new updated tag works by `minikube image load --daemon` @@ -389,17 +389,17 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { t.Fatalf("failed to setup test (pull image): %v\n%s", err, rr.Output()) } - rr, err = Run(t, exec.CommandContext(ctx, "docker", "tag", newPulledImage, taggedImage)) + rr, err = Run(t, exec.CommandContext(ctx, "docker", "tag", newPulledImage, daemonTestImage)) if err != nil { t.Fatalf("failed to setup test (tag image) : %v\n%s", err, rr.Output()) } - rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", taggedImage, "--alsologtostderr")) + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", "--daemon", daemonTestImage, "--alsologtostderr")) if err != nil { t.Fatalf("loading image into minikube from daemon: %v\n%s", err, rr.Output()) } - checkImageExists(ctx, t, profile, taggedImage) + checkImageExists(ctx, t, profile, daemonTestImage) }) // docs: Make sure image saving to Docker daemon works by `minikube image save --daemon` @@ -408,16 +408,16 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { t.Skip("docker daemon is not available on this host") } - rr, err := Run(t, exec.CommandContext(ctx, "docker", "rmi", taggedImage)) + rr, err := Run(t, exec.CommandContext(ctx, "docker", "rmi", daemonTestImage)) if err != nil { t.Fatalf("failed to remove image from docker: %v\n%s", err, rr.Output()) } - rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "save", "--daemon", taggedImage, "--alsologtostderr")) + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "save", "--daemon", daemonTestImage, "--alsologtostderr")) if err != nil { t.Fatalf("saving image from minikube to daemon: %v\n%s", err, rr.Output()) } - imageToDelete := taggedImage + imageToDelete := daemonTestImage if ContainerRuntime() == "crio" { imageToDelete = cruntime.AddLocalhostPrefix(imageToDelete) } From 8d94f99b60872f0bf082dedfc4db930a87e1fc0e Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sun, 24 Aug 2025 14:03:03 +0300 Subject: [PATCH 10/11] test: Check saved image Previously we check that the file exist which is not enough since with containerd runtime `image save` we get an empty file (#21408). Now we try to read the tar file and fail if we cannot read at least one header, and we log the contents for debugging. With this change ImageSaveToFile and ImageLoadFromFile fail early with clear error. Previously ImageSaveToFile succeeded created empty file, and ImageLoadFromFile failed with much longer and harder to understand error logs. Example run with docker runtime: === RUN TestFunctional/parallel/ImageCommands/ImageSaveToFile functional_test.go:566: (dbg) Run: out/minikube -p functional-712000 image ls --format short === NAME TestFunctional/parallel/ImageCommands/ImageSaveToFile functional_test.go:623: (dbg) Run: out/minikube -p functional-712000 image save registry.k8s.io/pause:latest /var/.../test.tar --alsologtostderr === NAME TestFunctional/parallel/ImageCommands/ImageSaveToFile functional_test.go:520: tar: blobs/ (0 bytes) functional_test.go:520: tar: blobs/sha256/ (0 bytes) functional_test.go:520: tar: blobs/sha256/5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef (1024 bytes) functional_test.go:520: tar: blobs/sha256/8cb2091f603e75187e2f6226c5901d12e00b1d1f778c6471ae4578e8a1c4724a (862 bytes) functional_test.go:520: tar: blobs/sha256/ac1aaec37732e181631ff5ecd6e89fc37beafe3ea63589075c3ae2c7c7e6ef2f (401 bytes) functional_test.go:520: tar: blobs/sha256/ad6f202d9ebc6878d90ffe7606a5c219a4155fee7b6d0032266053c6500cc352 (1042 bytes) functional_test.go:520: tar: blobs/sha256/b80dff767ab7ca9d1e43e874be74d34560c8bda1a1e4717790d8cc2a573fefc7 (477 bytes) functional_test.go:520: tar: blobs/sha256/e16a89738269fec22db26ec6362823a9ec42d0163685d88ba03c4fb5d5e723f6 (241664 bytes) functional_test.go:520: tar: blobs/sha256/f4b88e6ef8afe83e03b936888755b5a7517650bab05e73c4710b9542c049bdcb (699 bytes) functional_test.go:520: tar: index.json (359 bytes) functional_test.go:520: tar: manifest.json (855 bytes) functional_test.go:520: tar: oci-layout (31 bytes) functional_test.go:520: tar: repositories (104 bytes) Example run with containerd runtime: === RUN TestFunctional/parallel/ImageCommands/ImageSaveToFile functional_test.go:566: (dbg) Run: out/minikube -p functional-109000 image ls --format short functional_test.go:623: (dbg) Run: out/minikube -p functional-109000 image save registry.k8s.io/pause:3.10 /var/.../test.tar --alsologtostderr functional_test.go:525: tar file is empty: "/var/folders/.../test.tar" Example run with cri-o runtime: === RUN TestFunctional/parallel/ImageCommands/ImageSaveToFile functional_test.go:566: (dbg) Run: out/minikube -p functional-418000 image ls --format short === NAME TestFunctional/parallel/ImageCommands/ImageSaveToFile functional_test.go:623: (dbg) Run: out/minikube -p functional-418000 image save registry.k8s.io/pause:3.10 /var/.../test.tar --alsologtostderr === NAME TestFunctional/parallel/ImageCommands/ImageSaveToFile functional_test.go:520: tar: blobs/ (0 bytes) functional_test.go:520: tar: blobs/sha256/ (0 bytes) functional_test.go:520: tar: blobs/sha256/1975aab740f75a0e29b0dc8da5e898fcd3d70a5792becd9a847b4c7515cf154f (515584 bytes) functional_test.go:520: tar: blobs/sha256/afb61768ce381961ca0beff95337601f29dc70ff3ed14e5e4b3e5699057e6aa8 (886 bytes) functional_test.go:520: tar: blobs/sha256/b2bb3889281dcb7d517ca61ef4856f4529c695c32272b454f05b0d6b831d131e (399 bytes) functional_test.go:520: tar: blobs/sha256/e1df216055fb593d78bf169d2e455ce6a233289cf40db7997f895939f2f68364 (789 bytes) functional_test.go:520: tar: index.json (355 bytes) functional_test.go:520: tar: manifest.json (469 bytes) functional_test.go:520: tar: oci-layout (31 bytes) functional_test.go:520: tar: repositories (102 bytes) --- test/integration/functional_test.go | 38 ++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index d7abffedf5..b064a52f28 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -19,6 +19,7 @@ limitations under the License. package integration import ( + "archive/tar" "bufio" "bytes" "context" @@ -457,10 +458,7 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { testPath := filepath.Join(t.TempDir(), "test.tar") saveImageToFile(ctx, t, profile, pauseImage, testPath) - - if _, err := os.Stat(testPath); err != nil { - t.Fatalf("expected %q to exist after `image save`, but doesn't exist", testPath) - } + checkSavedImage(t, testPath) }) // docs: Make sure image loading from file works by `minikube image load` @@ -475,6 +473,8 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { }) saveImageToFile(ctx, t, profile, testImage, testPath) + checkSavedImage(t, testPath) + removeImage(ctx, t, profile, testImage) loadImageFromFile(ctx, t, profile, testPath) @@ -496,6 +496,36 @@ func checkImageNotExists(ctx context.Context, t *testing.T, profile string, imag } } +// checkSavedImage validates a saved image. Validating the contents is not easy +// without depending on docker or podmamn, but we can check that we have a non +// empty tar file. We log the tar contents for debugging. +func checkSavedImage(t *testing.T, tarPath string) { + f, err := os.Open(tarPath) + if err != nil { + t.Fatalf("failed to open file %q: %v", tarPath, err) + } + defer f.Close() + + reader := tar.NewReader(f) + count := 0 + + for { + header, err := reader.Next() + if err != nil { + if err == io.EOF { + break + } + t.Fatalf("failed to read tar header: %s", err) + } + t.Logf("tar: %s (%d bytes)", header.Name, header.Size) + count++ + } + + if count == 0 { + t.Fatalf("tar file is empty: %q", tarPath) + } +} + func listImagesShort(ctx context.Context, t *testing.T, profile string) []string { rr := listImages(ctx, t, profile, "short") scanner := bufio.NewScanner(rr.Stdout) From 7bdf00c4f1245b8a9779d474d34ce5fc3d6097fa Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sun, 24 Aug 2025 17:49:27 +0300 Subject: [PATCH 11/11] test: Skip image save to file with containerd This tests cannot pass now due to #21408. This should be reverted when we fix the issue. --- test/integration/functional_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index b064a52f28..27158b9a36 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -454,6 +454,11 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { // docs: Make sure image saving works by `minikube image load --daemon` t.Run("ImageSaveToFile", func(t *testing.T) { + if ContainerRuntime() == "containerd" { + // https://github.com/kubernetes/minikube/issues/21408 + t.Skip("image save is broken with containerd runtime") + } + pauseImage := findPauseImage(ctx, t, profile) testPath := filepath.Join(t.TempDir(), "test.tar") @@ -463,6 +468,11 @@ func validateImageCommands(ctx context.Context, t *testing.T, profile string) { // docs: Make sure image loading from file works by `minikube image load` t.Run("ImageLoadFromFile", func(t *testing.T) { + if ContainerRuntime() == "containerd" { + // https://github.com/kubernetes/minikube/issues/21408 + t.Skip("image save is broken with containerd runtime") + } + pauseImage := findPauseImage(ctx, t, profile) testImage := "localhost/image-load-from-file:test" testPath := filepath.Join(t.TempDir(), "test.tar")