Allow loading local images as well as from cache

Previously only allowed loading from daemon or remote
via the cache, and not directly from local tarball.
pull/10807/head
Anders F Björklund 2021-03-13 14:22:50 +01:00
parent ff0e25ada8
commit 0d99d18803
4 changed files with 102 additions and 17 deletions

View File

@ -17,6 +17,9 @@ limitations under the License.
package cmd package cmd
import ( import (
"os"
"strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/config"
@ -32,11 +35,17 @@ var imageCmd = &cobra.Command{
Long: "Load a local image into minikube", Long: "Load a local image into minikube",
} }
var (
imgDaemon bool
imgRemote bool
)
// loadImageCmd represents the image load command // loadImageCmd represents the image load command
var loadImageCmd = &cobra.Command{ var loadImageCmd = &cobra.Command{
Use: "load", Use: "load",
Short: "Load a local image into minikube", Short: "Load a image into minikube",
Long: "Load a local image into minikube", Long: "Load a image into minikube",
Example: "minikube image load image\nminikube image load image.tar",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 { if len(args) == 0 {
exit.Message(reason.Usage, "Please provide an image in your local daemon to load into minikube via <minikube image load IMAGE_NAME>") exit.Message(reason.Usage, "Please provide an image in your local daemon to load into minikube via <minikube image load IMAGE_NAME>")
@ -47,12 +56,41 @@ var loadImageCmd = &cobra.Command{
exit.Error(reason.Usage, "loading profile", err) exit.Error(reason.Usage, "loading profile", err)
} }
img := args[0] img := args[0]
if err := machine.CacheAndLoadImages([]string{img}, []*config.Profile{profile}); err != nil {
exit.Error(reason.GuestImageLoad, "Failed to load image", err) var local bool
if imgRemote || imgDaemon {
local = false
} else if strings.HasPrefix(img, "/") || strings.HasPrefix(img, ".") {
local = true
imgDaemon = false
imgRemote = false
} else if _, err := os.Stat(img); err == nil {
local = true
imgDaemon = false
imgRemote = false
} else {
imgDaemon = true
imgRemote = true
}
// Currently "image.retrieveImage" always tries to load both from daemon and from remote
// There is no way to skip daemon.Image or remote.Image, for the vague "ref" string given.
if imgDaemon || imgRemote {
if err := machine.CacheAndLoadImages([]string{img}, []*config.Profile{profile}); err != nil {
exit.Error(reason.GuestImageLoad, "Failed to load image", err)
}
// Load images from local files, without doing any caching or checks in container runtime
// This is similar to tarball.Image but it is done by the container runtime in the cluster.
} else if local {
if err := machine.DoLoadImages([]string{img}, []*config.Profile{profile}, ""); err != nil {
exit.Error(reason.GuestImageLoad, "Failed to load image", err)
}
} }
}, },
} }
func init() { func init() {
imageCmd.AddCommand(loadImageCmd) imageCmd.AddCommand(loadImageCmd)
loadImageCmd.Flags().BoolVar(&imgDaemon, "daemon", false, "Cache image from docker daemon")
loadImageCmd.Flags().BoolVar(&imgRemote, "remote", false, "Cache image from remote registry")
} }

View File

@ -859,7 +859,7 @@ func (k *Bootstrapper) UpdateCluster(cfg config.ClusterConfig) error {
} }
if cfg.KubernetesConfig.ShouldLoadCachedImages { if cfg.KubernetesConfig.ShouldLoadCachedImages {
if err := machine.LoadImages(&cfg, k.c, images, constants.ImageCacheDir); err != nil { if err := machine.LoadCachedImages(&cfg, k.c, images, constants.ImageCacheDir); err != nil {
out.FailureT("Unable to load cached images: {{.error}}", out.V{"error": err}) out.FailureT("Unable to load cached images: {{.error}}", out.V{"error": err})
} }
} }

View File

@ -61,8 +61,8 @@ func CacheImagesForBootstrapper(imageRepository string, version string, clusterB
return nil return nil
} }
// LoadImages loads previously cached images into the container runtime // LoadCachedImages loads previously cached images into the container runtime
func LoadImages(cc *config.ClusterConfig, runner command.Runner, images []string, cacheDir string) error { func LoadCachedImages(cc *config.ClusterConfig, runner command.Runner, images []string, cacheDir string) error {
cr, err := cruntime.New(cruntime.Config{Type: cc.KubernetesConfig.ContainerRuntime, Runner: runner}) cr, err := cruntime.New(cruntime.Config{Type: cc.KubernetesConfig.ContainerRuntime, Runner: runner})
if err != nil { if err != nil {
return errors.Wrap(err, "runtime") return errors.Wrap(err, "runtime")
@ -73,6 +73,7 @@ func LoadImages(cc *config.ClusterConfig, runner command.Runner, images []string
klog.Infof("Images are preloaded, skipping loading") klog.Infof("Images are preloaded, skipping loading")
return nil return nil
} }
klog.Infof("LoadImages start: %s", images) klog.Infof("LoadImages start: %s", images)
start := time.Now() start := time.Now()
@ -102,7 +103,7 @@ func LoadImages(cc *config.ClusterConfig, runner command.Runner, images []string
return nil return nil
} }
klog.Infof("%q needs transfer: %v", image, err) klog.Infof("%q needs transfer: %v", image, err)
return transferAndLoadImage(runner, cc.KubernetesConfig, image, cacheDir) return transferAndLoadCachedImage(runner, cc.KubernetesConfig, image, cacheDir)
}) })
} }
if err := g.Wait(); err != nil { if err := g.Wait(); err != nil {
@ -157,6 +158,22 @@ func needsTransfer(imgClient *client.Client, imgName string, cr cruntime.Manager
return nil return nil
} }
// LoadLocalImages loads images into the container runtime
func LoadLocalImages(cc *config.ClusterConfig, runner command.Runner, images []string) error {
var g errgroup.Group
for _, image := range images {
image := image
g.Go(func() error {
return transferAndLoadImage(runner, cc.KubernetesConfig, image)
})
}
if err := g.Wait(); err != nil {
return errors.Wrap(err, "loading images")
}
klog.Infoln("Successfully loaded all images")
return nil
}
// CacheAndLoadImages caches and loads images to all profiles // CacheAndLoadImages caches and loads images to all profiles
func CacheAndLoadImages(images []string, profiles []*config.Profile) error { func CacheAndLoadImages(images []string, profiles []*config.Profile) error {
if len(images) == 0 { if len(images) == 0 {
@ -168,6 +185,11 @@ func CacheAndLoadImages(images []string, profiles []*config.Profile) error {
return errors.Wrap(err, "save to dir") return errors.Wrap(err, "save to dir")
} }
return DoLoadImages(images, profiles, constants.ImageCacheDir)
}
// DoLoadImages loads images to all profiles
func DoLoadImages(images []string, profiles []*config.Profile, cacheDir string) error {
api, err := NewAPIClient() api, err := NewAPIClient()
if err != nil { if err != nil {
return errors.Wrap(err, "api") return errors.Wrap(err, "api")
@ -209,7 +231,13 @@ func CacheAndLoadImages(images []string, profiles []*config.Profile) error {
if err != nil { if err != nil {
return err return err
} }
err = LoadImages(c, cr, images, constants.ImageCacheDir) if cacheDir != "" {
// loading image names, from cache
err = LoadCachedImages(c, cr, images, cacheDir)
} else {
// loading image files
err = LoadLocalImages(c, cr, images)
}
if err != nil { if err != nil {
failed = append(failed, m) failed = append(failed, m)
klog.Warningf("Failed to load cached images for profile %s. make sure the profile is running. %v", pName, err) klog.Warningf("Failed to load cached images for profile %s. make sure the profile is running. %v", pName, err)
@ -226,15 +254,20 @@ func CacheAndLoadImages(images []string, profiles []*config.Profile) error {
return nil return nil
} }
// transferAndLoadImage transfers and loads a single image from the cache // transferAndLoadCachedImage transfers and loads a single image from the cache
func transferAndLoadImage(cr command.Runner, k8s config.KubernetesConfig, imgName string, cacheDir string) error { func transferAndLoadCachedImage(cr command.Runner, k8s config.KubernetesConfig, imgName string, cacheDir string) error {
src := filepath.Join(cacheDir, imgName)
src = localpath.SanitizeCacheDir(src)
return transferAndLoadImage(cr, k8s, src)
}
// transferAndLoadImage transfers and loads a single image
func transferAndLoadImage(cr command.Runner, k8s config.KubernetesConfig, src string) error {
r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime, Runner: cr}) r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime, Runner: cr})
if err != nil { if err != nil {
return errors.Wrap(err, "runtime") return errors.Wrap(err, "runtime")
} }
src := filepath.Join(cacheDir, imgName) klog.Infof("Loading image from: %s", src)
src = localpath.SanitizeCacheDir(src)
klog.Infof("Loading image from cache: %s", src)
filename := filepath.Base(src) filename := filepath.Base(src)
if _, err := os.Stat(src); err != nil { if _, err := os.Stat(src); err != nil {
return err return err

View File

@ -72,16 +72,30 @@ minikube image help [command] [flags]
## minikube image load ## minikube image load
Load a local image into minikube Load a image into minikube
### Synopsis ### Synopsis
Load a local image into minikube Load a image into minikube
```shell ```shell
minikube image load [flags] minikube image load [flags]
``` ```
### Examples
```
minikube image load image
minikube image load image.tar
```
### Options
```
--daemon Cache image from docker daemon
--remote Cache image from remote registry
```
### Options inherited from parent commands ### Options inherited from parent commands
``` ```