diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go new file mode 100644 index 0000000000000000000000000000000000000000..d8748d29255bb40de7f1d8ed1e884d1125457d86 --- /dev/null +++ b/cmd/minikube/cmd/image.go @@ -0,0 +1,55 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. + +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 cmd + +import ( + "github.com/spf13/cobra" + "github.com/spf13/viper" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/machine" + "k8s.io/minikube/pkg/minikube/reason" +) + +// imageCmd represents the image command +var imageCmd = &cobra.Command{ + Use: "image", + Short: "Load a local image into minikube", + Long: "Load a local image into minikube", +} + +// loadImageCmd represents the image load command +var loadImageCmd = &cobra.Command{ + Use: "load", + Short: "Load a local image into minikube", + Long: "Load a local image into minikube", + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + exit.Message(reason.Usage, "Please provide an image in your local daemon or a path to an image tarball to load into minikube via ") + } + // Cache and load images into docker daemon + profile := viper.GetString(config.ProfileName) + img := args[0] + if err := machine.LoadImage(profile, img); err != nil { + exit.Error(reason.InternalCacheLoad, "Failed to cache and load images", err) + } + }, +} + +func init() { + imageCmd.AddCommand(loadImageCmd) +} diff --git a/cmd/minikube/cmd/root.go b/cmd/minikube/cmd/root.go index a29886b5b20784a6fd97bbe1250441b53caabb0d..17850e985b82cb1c963e73bfe02e89e73cd7f2f8 100644 --- a/cmd/minikube/cmd/root.go +++ b/cmd/minikube/cmd/root.go @@ -201,6 +201,7 @@ func init() { dockerEnvCmd, podmanEnvCmd, cacheCmd, + imageCmd, }, }, { diff --git a/pkg/minikube/image/image.go b/pkg/minikube/image/image.go index ece5fb4e324dec2b0e7af3b23b119565b3a4bccd..f736e0b4b37d24196b770f21178bb0c02b1720df 100644 --- a/pkg/minikube/image/image.go +++ b/pkg/minikube/image/image.go @@ -123,6 +123,27 @@ func LoadFromTarball(binary, img string) error { } +// SaveToTarball saves img as a tarball at the given path +func SaveToTarball(img, path string) error { + if !ExistsImageInDaemon(img) { + return fmt.Errorf("%s does not exist in local daemon, can't save to tarball", img) + } + ref, err := name.ParseReference(img) + if err != nil { + return errors.Wrap(err, "parsing reference") + } + i, err := daemon.Image(ref) + if err != nil { + return errors.Wrap(err, "getting image") + } + f, err := os.Create(path) + if err != nil { + return errors.Wrap(err, "creating tmp path") + } + defer f.Close() + return tarball.Write(ref, i, f) +} + // Tag returns just the image with the tag // eg image:tag@sha256:digest -> image:tag if there is an associated tag // if not possible, just return the initial img diff --git a/pkg/minikube/machine/cache_images.go b/pkg/minikube/machine/cache_images.go index 3b5f2d379e53c0b01272b0fd2841930c09abe1e0..90731e7859f62aedc2e8fbaa7b5de74f402a9c25 100644 --- a/pkg/minikube/machine/cache_images.go +++ b/pkg/minikube/machine/cache_images.go @@ -18,7 +18,9 @@ package machine import ( "fmt" + "io/ioutil" "os" + "os/exec" "path" "path/filepath" "strings" @@ -61,6 +63,74 @@ func CacheImagesForBootstrapper(imageRepository string, version string, clusterB return nil } +// LoadImage loads the local image into the container runtime +func LoadImage(profile, img string) error { + cc, err := config.Load(profile) + if err != nil { + return errors.Wrap(err, "loading profile") + } + // if the image exists in the local daemon, save it as a tarball + if image.ExistsImageInDaemon(img) { + tmpFile, err := ioutil.TempFile("", "") + if err != nil { + return errors.Wrap(err, "temp file") + } + tmpFile.Close() + defer os.Remove(tmpFile.Name()) + img = tmpFile.Name() + } + dst := "/tmp/img.tar" + if err := copyAndLoadTarballIntoHost(cc, img, dst, profile); err != nil { + return errors.Wrap(err, "copying tarball into host") + } + return nil +} + +func copyAndLoadTarballIntoHost(cc *config.ClusterConfig, srcPath, dstPath, profile string) error { + c, err := config.Load(profile) + if err != nil { + return errors.Wrap(err, "loading profile config") + } + api, err := NewAPIClient() + if err != nil { + return errors.Wrap(err, "api") + } + defer api.Close() + for _, n := range c.Nodes { + m := config.MachineName(*c, n) + h, err := api.Load(m) + if err != nil { + return errors.Wrap(err, "loading api") + } + cr, err := CommandRunner(h) + if err != nil { + return errors.Wrap(err, "command runner") + } + tarballImg, err := assets.NewFileAsset(srcPath, filepath.Dir(dstPath), filepath.Base(dstPath), "0644") + if err != nil { + return errors.Wrap(err, "new file asset") + } + // copy tarball into minikube + + if err := cr.Copy(tarballImg); err != nil { + return errors.Wrap(err, "copying tarball") + } + // load image into container runtime + containerRuntime, err := cruntime.New(cruntime.Config{Type: cc.KubernetesConfig.ContainerRuntime, Runner: cr}) + if err != nil { + return errors.Wrap(err, "runtime") + } + if err := containerRuntime.LoadImage(dstPath); err != nil { + return errors.Wrap(err, "loading image into container runtime") + } + // delete destination image tarball on host + if _, err := cr.RunCmd(exec.Command("rm", dstPath)); err != nil { + return errors.Wrap(err, "removing destination tarball") + } + } + return nil +} + // LoadImages loads previously cached images into the container runtime func LoadImages(cc *config.ClusterConfig, runner command.Runner, images []string, cacheDir string) error { cr, err := cruntime.New(cruntime.Config{Type: cc.KubernetesConfig.ContainerRuntime, Runner: runner})