diff --git a/pkg/minikube/machine/cache_images.go b/pkg/minikube/machine/cache_images.go index 36c2fe2ad446..ad97ddcf8d93 100644 --- a/pkg/minikube/machine/cache_images.go +++ b/pkg/minikube/machine/cache_images.go @@ -269,11 +269,8 @@ func transferAndLoadImage(cr command.Runner, k8s config.KubernetesConfig, src st return errors.Wrap(err, "runtime") } - if err := r.RemoveImage(imgName); err != nil { - errStr := strings.ToLower(err.Error()) - if !strings.Contains(errStr, "no such image") { - return errors.Wrap(err, "removing image") - } + if err := removeExistingImage(r, src, imgName); err != nil { + return err } klog.Infof("Loading image from: %s", src) @@ -309,6 +306,26 @@ func transferAndLoadImage(cr command.Runner, k8s config.KubernetesConfig, src st return nil } +func removeExistingImage(r cruntime.Manager, src string, imgName string) error { + // if loading an image from tar, skip deleting as we don't have the actual image name + // ie. imgName = "C:\this_is_a_dir\image.tar.gz" + if src == imgName { + return nil + } + + err := r.RemoveImage(imgName) + if err == nil { + return nil + } + + errStr := strings.ToLower(err.Error()) + if !strings.Contains(errStr, "no such image") { + return errors.Wrap(err, "removing image") + } + + return nil +} + // pullImages pulls images to the container run time func pullImages(cruntime cruntime.Manager, images []string) error { klog.Infof("PullImages start: %s", images) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 0516e39712e1..10bee2cbbfd7 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -152,6 +152,7 @@ func TestFunctional(t *testing.T) { {"NodeLabels", validateNodeLabels}, {"LoadImage", validateLoadImage}, {"RemoveImage", validateRemoveImage}, + {"LoadImageFromFile", validateLoadImageFromFile}, {"BuildImage", validateBuildImage}, {"ListImages", validateListImages}, {"NonActiveRuntimeDisabled", validateNotActiveRuntimeDisabled}, @@ -264,6 +265,57 @@ func validateLoadImage(ctx context.Context, t *testing.T, profile string) { } +// validateLoadImageFromFile makes sure that `minikube image load` works from a local file +func validateLoadImageFromFile(ctx context.Context, t *testing.T, profile string) { + if NoneDriver() { + t.Skip("load image not available on none driver") + } + if GithubActionRunner() && runtime.GOOS == "darwin" { + t.Skip("skipping on github actions and darwin, as this test requires a running docker daemon") + } + defer PostMortemLogs(t, profile) + // pull busybox + busyboxImage := "busybox:1.31" + rr, err := Run(t, exec.CommandContext(ctx, "docker", "pull", busyboxImage)) + if err != nil { + t.Fatalf("failed to setup test (pull image): %v\n%s", err, rr.Output()) + } + + tag := fmt.Sprintf("load-from-file-%s", profile) + taggedImage := fmt.Sprintf("docker.io/library/busybox:%s", tag) + rr, err = Run(t, exec.CommandContext(ctx, "docker", "tag", busyboxImage, taggedImage)) + if err != nil { + t.Fatalf("failed to setup test (tag image) : %v\n%s", err, rr.Output()) + } + + // save image to file + imageFile := "busybox.tar" + rr, err = Run(t, exec.CommandContext(ctx, "docker", "save", "-o", imageFile, taggedImage)) + if err != nil { + t.Fatalf("failed to save image to file: %v\n%s", err, rr.Output()) + } + defer os.Remove(imageFile) + + // try to load the new image into minikube + imagePath, err := filepath.Abs(imageFile) + if err != nil { + t.Fatalf("failed to get absolute path of file %q: %v", imageFile, err) + } + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "load", imagePath)) + if err != nil { + t.Fatalf("loading image into minikube: %v\n%s", err, rr.Output()) + } + + // 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(), tag) { + t.Fatalf("expected %s to be loaded into minikube but the image is not there", taggedImage) + } +} + // validateRemoveImage makes sures that `minikube image rm` works as expected func validateRemoveImage(ctx context.Context, t *testing.T, profile string) { if NoneDriver() {