From adcbcf113b9e6340cfa0015328902217429562c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Sat, 23 Mar 2019 16:49:30 +0100 Subject: [PATCH 1/3] Refactor: move cache calls into separate functions Need to split out the caching functions, from the loading/copying. Also need to make the lists accessible from outside the functions. --- cmd/minikube/cmd/cache.go | 21 ++++++++++---- pkg/minikube/bootstrapper/kubeadm/kubeadm.go | 30 ++++++++++++++------ pkg/minikube/cluster/cluster.go | 17 ++++++++--- pkg/minikube/constants/constants.go | 5 ++++ 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/cmd/minikube/cmd/cache.go b/cmd/minikube/cmd/cache.go index b6fdb246cab2..86926c5d353f 100644 --- a/cmd/minikube/cmd/cache.go +++ b/cmd/minikube/cmd/cache.go @@ -66,20 +66,31 @@ var deleteCacheCmd = &cobra.Command{ }, } -// LoadCachedImagesInConfigFile loads the images currently in the config file (minikube start) -func LoadCachedImagesInConfigFile() error { +func imagesInConfigFile() ([]string, error) { configFile, err := config.ReadConfig() if err != nil { - return err + return nil, err } if values, ok := configFile[constants.Cache]; ok { var images []string for key := range values.(map[string]interface{}) { images = append(images, key) } - return machine.CacheAndLoadImages(images) + return images, nil + } + return []string{}, nil +} + +// LoadCachedImagesInConfigFile loads the images currently in the config file (minikube start) +func LoadCachedImagesInConfigFile() error { + images, err := imagesInConfigFile() + if err != nil { + return err + } + if len(images) == 0 { + return nil } - return nil + return machine.CacheAndLoadImages(images) } func init() { diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go index 6be7e6b2ab6b..2be01ac73196 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go @@ -466,19 +466,16 @@ func (k *Bootstrapper) UpdateCluster(cfg config.KubernetesConfig) error { } var g errgroup.Group - for _, bin := range []string{"kubelet", "kubeadm"} { + for _, bin := range constants.GetKubeadmCachedBinaries() { bin := bin g.Go(func() error { - path, err := maybeDownloadAndCache(bin, cfg.KubernetesVersion) + path, err := CacheBinary(bin, cfg.KubernetesVersion) if err != nil { return errors.Wrapf(err, "downloading %s", bin) } - f, err := assets.NewFileAsset(path, "/usr/bin", bin, "0641") + err = CopyBinary(k.c, bin, path) if err != nil { - return errors.Wrap(err, "new file asset") - } - if err := k.c.Copy(f); err != nil { - return errors.Wrapf(err, "copy") + return errors.Wrapf(err, "copying %s", bin) } return nil }) @@ -584,13 +581,17 @@ func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) (string, er return b.String(), nil } -func maybeDownloadAndCache(binary, version string) (string, error) { +// CacheBinary will cache a binary on the host +func CacheBinary(binary, version string) (string, error) { targetDir := constants.MakeMiniPath("cache", version) targetFilepath := path.Join(targetDir, binary) + url := constants.GetKubernetesReleaseURL(binary, version) + _, err := os.Stat(targetFilepath) // If it exists, do no verification and continue if err == nil { + glog.Infof("Not caching binary, using %s", url) return targetFilepath, nil } if !os.IsNotExist(err) { @@ -601,7 +602,6 @@ func maybeDownloadAndCache(binary, version string) (string, error) { return "", errors.Wrapf(err, "mkdir %s", targetDir) } - url := constants.GetKubernetesReleaseURL(binary, version) options := download.FileOptions{ Mkdirs: download.MkdirAll, } @@ -615,3 +615,15 @@ func maybeDownloadAndCache(binary, version string) (string, error) { } return targetFilepath, nil } + +// CopyBinary copies previously cached binaries into the path +func CopyBinary(cr bootstrapper.CommandRunner, binary, path string) error { + f, err := assets.NewFileAsset(path, "/usr/bin", binary, "0641") + if err != nil { + return errors.Wrap(err, "new file asset") + } + if err := cr.Copy(f); err != nil { + return errors.Wrapf(err, "copy") + } + return nil +} diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index 4ad5aa3d2192..cf500b7868db 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -58,6 +58,16 @@ func init() { ssh.SetDefaultClient(ssh.Native) } +// CacheISO downloads and caches ISO. +func CacheISO(config cfg.MachineConfig) error { + if config.VMDriver != "none" { + if err := config.Downloader.CacheMinikubeISOFromURL(config.MinikubeISO); err != nil { + return err + } + } + return nil +} + // StartHost starts a host VM. func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error) { exists, err := api.Exists(cfg.GetMachineName()) @@ -280,10 +290,9 @@ func createHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error exit.WithError("error getting driver", err) } - if config.VMDriver != "none" { - if err := config.Downloader.CacheMinikubeISOFromURL(config.MinikubeISO); err != nil { - return nil, errors.Wrap(err, "unable to cache ISO") - } + err = CacheISO(config) + if err != nil { + return nil, errors.Wrap(err, "unable to cache ISO") } driver := def.ConfigCreator(config) diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index 82ab828df2c0..e081019c0149 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -224,6 +224,11 @@ const DriverNone = "none" // FileScheme is the file scheme const FileScheme = "file" +// GetKubeadmCachedBinaries gets the binaries to cache for kubeadm +func GetKubeadmCachedBinaries() []string { + return []string{"kubelet", "kubeadm"} +} + // GetKubeadmCachedImages gets the images to cache for kubeadm for a version func GetKubeadmCachedImages(imageRepository string, kubernetesVersionStr string) (string, []string) { minikubeRepository := imageRepository From 54f68e068e92ad1ad6e7490b6475dfedd775d0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Sat, 23 Mar 2019 17:48:57 +0100 Subject: [PATCH 2/3] Move the cache functions from kubeadm to machine --- pkg/minikube/bootstrapper/kubeadm/kubeadm.go | 55 +------------- pkg/minikube/machine/cache_binaries.go | 79 ++++++++++++++++++++ 2 files changed, 81 insertions(+), 53 deletions(-) create mode 100644 pkg/minikube/machine/cache_binaries.go diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go index 2be01ac73196..7c0510320530 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go @@ -18,13 +18,10 @@ package kubeadm import ( "bytes" - "crypto" "crypto/tls" "fmt" "net" "net/http" - "os" - "path" "strings" "time" @@ -32,7 +29,6 @@ import ( "github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/state" "github.com/golang/glog" - "github.com/jimmidyson/go-download" "github.com/pkg/errors" "golang.org/x/sync/errgroup" "k8s.io/apimachinery/pkg/labels" @@ -469,11 +465,11 @@ func (k *Bootstrapper) UpdateCluster(cfg config.KubernetesConfig) error { for _, bin := range constants.GetKubeadmCachedBinaries() { bin := bin g.Go(func() error { - path, err := CacheBinary(bin, cfg.KubernetesVersion) + path, err := machine.CacheBinary(bin, cfg.KubernetesVersion) if err != nil { return errors.Wrapf(err, "downloading %s", bin) } - err = CopyBinary(k.c, bin, path) + err = machine.CopyBinary(k.c, bin, path) if err != nil { return errors.Wrapf(err, "copying %s", bin) } @@ -580,50 +576,3 @@ func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) (string, er return b.String(), nil } - -// CacheBinary will cache a binary on the host -func CacheBinary(binary, version string) (string, error) { - targetDir := constants.MakeMiniPath("cache", version) - targetFilepath := path.Join(targetDir, binary) - - url := constants.GetKubernetesReleaseURL(binary, version) - - _, err := os.Stat(targetFilepath) - // If it exists, do no verification and continue - if err == nil { - glog.Infof("Not caching binary, using %s", url) - return targetFilepath, nil - } - if !os.IsNotExist(err) { - return "", errors.Wrapf(err, "stat %s version %s at %s", binary, version, targetDir) - } - - if err = os.MkdirAll(targetDir, 0777); err != nil { - return "", errors.Wrapf(err, "mkdir %s", targetDir) - } - - options := download.FileOptions{ - Mkdirs: download.MkdirAll, - } - - options.Checksum = constants.GetKubernetesReleaseURLSHA1(binary, version) - options.ChecksumHash = crypto.SHA1 - - console.OutStyle("file-download", "Downloading %s %s", binary, version) - if err := download.ToFile(url, targetFilepath, options); err != nil { - return "", errors.Wrapf(err, "Error downloading %s %s", binary, version) - } - return targetFilepath, nil -} - -// CopyBinary copies previously cached binaries into the path -func CopyBinary(cr bootstrapper.CommandRunner, binary, path string) error { - f, err := assets.NewFileAsset(path, "/usr/bin", binary, "0641") - if err != nil { - return errors.Wrap(err, "new file asset") - } - if err := cr.Copy(f); err != nil { - return errors.Wrapf(err, "copy") - } - return nil -} diff --git a/pkg/minikube/machine/cache_binaries.go b/pkg/minikube/machine/cache_binaries.go new file mode 100644 index 000000000000..9745f37135f2 --- /dev/null +++ b/pkg/minikube/machine/cache_binaries.go @@ -0,0 +1,79 @@ +/* +Copyright 2016 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 machine + +import ( + "crypto" + "os" + "path" + + "github.com/golang/glog" + "github.com/jimmidyson/go-download" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" + "k8s.io/minikube/pkg/minikube/assets" + "k8s.io/minikube/pkg/minikube/bootstrapper" + "k8s.io/minikube/pkg/minikube/console" + "k8s.io/minikube/pkg/minikube/constants" +) + +// CacheBinary will cache a binary on the host +func CacheBinary(binary, version string) (string, error) { + targetDir := constants.MakeMiniPath("cache", version) + targetFilepath := path.Join(targetDir, binary) + + url := constants.GetKubernetesReleaseURL(binary, version) + + _, err := os.Stat(targetFilepath) + // If it exists, do no verification and continue + if err == nil { + glog.Infof("Not caching binary, using %s", url) + return targetFilepath, nil + } + if !os.IsNotExist(err) { + return "", errors.Wrapf(err, "stat %s version %s at %s", binary, version, targetDir) + } + + if err = os.MkdirAll(targetDir, 0777); err != nil { + return "", errors.Wrapf(err, "mkdir %s", targetDir) + } + + options := download.FileOptions{ + Mkdirs: download.MkdirAll, + } + + options.Checksum = constants.GetKubernetesReleaseURLSHA1(binary, version) + options.ChecksumHash = crypto.SHA1 + + console.OutStyle("file-download", "Downloading %s %s", binary, version) + if err := download.ToFile(url, targetFilepath, options); err != nil { + return "", errors.Wrapf(err, "Error downloading %s %s", binary, version) + } + return targetFilepath, nil +} + +// CopyBinary copies previously cached binaries into the path +func CopyBinary(cr bootstrapper.CommandRunner, binary, path string) error { + f, err := assets.NewFileAsset(path, "/usr/bin", binary, "0641") + if err != nil { + return errors.Wrap(err, "new file asset") + } + if err := cr.Copy(f); err != nil { + return errors.Wrapf(err, "copy") + } + return nil +} From 0ebfe462b9fabbfd1c7d660916265784a41a829e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 22 Feb 2019 22:32:52 +0100 Subject: [PATCH 3/3] Add --download-only option to start command Make it possible to download files without loading or installing, and without starting the host... Download: ISO, binaries, images. --- cmd/minikube/cmd/cache.go | 12 ++++++++++++ cmd/minikube/cmd/start.go | 23 +++++++++++++++++++++++ pkg/minikube/bootstrapper/bootstrapper.go | 10 ++++++++++ pkg/minikube/console/style.go | 1 + pkg/minikube/machine/cache_binaries.go | 17 +++++++++++++++++ 5 files changed, 63 insertions(+) diff --git a/cmd/minikube/cmd/cache.go b/cmd/minikube/cmd/cache.go index 86926c5d353f..7b403f4b904f 100644 --- a/cmd/minikube/cmd/cache.go +++ b/cmd/minikube/cmd/cache.go @@ -81,6 +81,18 @@ func imagesInConfigFile() ([]string, error) { return []string{}, nil } +// CacheImagesInConfigFile caches the images currently in the config file (minikube start) +func CacheImagesInConfigFile() error { + images, err := imagesInConfigFile() + if err != nil { + return err + } + if len(images) == 0 { + return nil + } + return machine.CacheImages(images, constants.ImageCacheDir) +} + // LoadCachedImagesInConfigFile loads the images currently in the config file (minikube start) func LoadCachedImagesInConfigFile() error { images, err := imagesInConfigFile() diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index f01fe4482761..da650f6b4e7c 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -87,6 +87,7 @@ const ( hidden = "hidden" embedCerts = "embed-certs" noVTXCheck = "no-vtx-check" + downloadOnly = "download-only" ) var ( @@ -136,6 +137,7 @@ func init() { startCmd.Flags().Bool(enableDefaultCNI, false, "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \"--network-plugin=cni\"") startCmd.Flags().String(featureGates, "", "A set of key=value pairs that describe feature gates for alpha/experimental features.") startCmd.Flags().Bool(cacheImages, true, "If true, cache docker images for the current bootstrapper and load them into the machine.") + startCmd.Flags().Bool(downloadOnly, false, "If true, only download and cache files for later use - don't install or start anything.") startCmd.Flags().Var(&extraOptions, "extra-config", `A set of key=value pairs that describe configuration that may be passed to different components. The key should be '.' separated, and the first part before the dot is the component to apply the configuration to. @@ -187,6 +189,22 @@ func runStart(cmd *cobra.Command, args []string) { if err != nil { exit.WithError("Failed to get machine client", err) } + + if viper.GetBool(downloadOnly) { + if err := cluster.CacheISO(config.MachineConfig); err != nil { + exit.WithError("Failed to cache ISO", err) + } + if err := doCacheBinaries(k8sVersion); err != nil { + exit.WithError("Failed to cache binaries", err) + } + waitCacheImages(&cacheGroup) + if err := CacheImagesInConfigFile(); err != nil { + exit.WithError("Failed to cache images", err) + } + console.OutStyle("check", "Download complete!") + return + } + host, preexisting := startHost(m, config.MachineConfig) ip := validateNetwork(host) @@ -245,6 +263,11 @@ func validateConfig() { } } +// doCacheBinaries caches Kubernetes binaries in the foreground +func doCacheBinaries(k8sVersion string) error { + return machine.CacheBinariesForBootstrapper(k8sVersion, viper.GetString(cmdcfg.Bootstrapper)) +} + // beginCacheImages caches Docker images in the background func beginCacheImages(g *errgroup.Group, k8sVersion string) { if !viper.GetBool(cacheImages) { diff --git a/pkg/minikube/bootstrapper/bootstrapper.go b/pkg/minikube/bootstrapper/bootstrapper.go index e7d9ed4bbb85..390430a99b30 100644 --- a/pkg/minikube/bootstrapper/bootstrapper.go +++ b/pkg/minikube/bootstrapper/bootstrapper.go @@ -51,6 +51,16 @@ const ( BootstrapperTypeKubeadm = "kubeadm" ) +// GetCachedBinaryList returns the list of binaries +func GetCachedBinaryList(bootstrapper string) []string { + switch bootstrapper { + case BootstrapperTypeKubeadm: + return constants.GetKubeadmCachedBinaries() + default: + return []string{} + } +} + // GetCachedImageList returns the list of images for a version func GetCachedImageList(imageRepository string, version string, bootstrapper string) []string { switch bootstrapper { diff --git a/pkg/minikube/console/style.go b/pkg/minikube/console/style.go index ef51ff9e2a3b..8a7e8ce5f986 100644 --- a/pkg/minikube/console/style.go +++ b/pkg/minikube/console/style.go @@ -69,6 +69,7 @@ var styles = map[string]style{ "documentation": {Prefix: "🗎 "}, "issues": {Prefix: "📚 "}, "issue": {Prefix: " ▪ "}, // Indented bullet + "check": {Prefix: "✔️ "}, // Specialized purpose styles "iso-download": {Prefix: "💿 ", LowPrefix: "@ "}, diff --git a/pkg/minikube/machine/cache_binaries.go b/pkg/minikube/machine/cache_binaries.go index 9745f37135f2..b21c76d85a0f 100644 --- a/pkg/minikube/machine/cache_binaries.go +++ b/pkg/minikube/machine/cache_binaries.go @@ -31,6 +31,23 @@ import ( "k8s.io/minikube/pkg/minikube/constants" ) +// CacheBinariesForBootstrapper will cache binaries for a bootstrapper +func CacheBinariesForBootstrapper(version string, clusterBootstrapper string) error { + binaries := bootstrapper.GetCachedBinaryList(clusterBootstrapper) + + var g errgroup.Group + for _, bin := range binaries { + bin := bin + g.Go(func() error { + if _, err := CacheBinary(bin, version); err != nil { + return errors.Wrapf(err, "caching image %s", bin) + } + return nil + }) + } + return g.Wait() +} + // CacheBinary will cache a binary on the host func CacheBinary(binary, version string) (string, error) { targetDir := constants.MakeMiniPath("cache", version)