From d506aa18b40dcbaf5bde4df9bfe84ba9a2ff3e1d Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Wed, 19 Feb 2020 21:53:44 -0800 Subject: [PATCH 01/28] Download preloaded images tarball with --download-only flag --- pkg/drivers/kic/cache.go | 41 +++++++++++++++++++++++++++++++++++++ pkg/drivers/kic/types.go | 3 +++ pkg/minikube/image/image.go | 30 +++++++++++++++++++++++++++ pkg/minikube/node/cache.go | 30 ++++++++++++++++++++++++++- pkg/minikube/node/start.go | 2 +- 5 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 pkg/drivers/kic/cache.go diff --git a/pkg/drivers/kic/cache.go b/pkg/drivers/kic/cache.go new file mode 100644 index 0000000000..d299f8e19b --- /dev/null +++ b/pkg/drivers/kic/cache.go @@ -0,0 +1,41 @@ +/* +Copyright 2019 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 kic + +import ( + "fmt" + "os" + "path" + + "github.com/golang/glog" + "github.com/jimmidyson/go-download" + "k8s.io/minikube/pkg/minikube/localpath" +) + +func CachePreloadedTarball(k8sVersion string) error { + targetDir := localpath.MakeMiniPath("cache", "preloaded-tarball") + targetFilepath := path.Join(targetDir, fmt.Sprintf("%s-k8s-%s.tar", Version, k8sVersion)) + + if _, err := os.Stat(targetFilepath); err == nil { + glog.Infof("Found %s in cache, skipping downloading", targetFilepath) + return nil + } + + url := fmt.Sprintf("https://storage.googleapis.com/minikube-docker-volume-tarballs/%s-k8s-%s.tar", Version, k8sVersion) + glog.Infof("Downloading %s to %s", url, targetFilepath) + return download.ToFile(url, targetFilepath, download.FileOptions{Mkdirs: download.MkdirAll}) +} diff --git a/pkg/drivers/kic/types.go b/pkg/drivers/kic/types.go index f3082952a5..63f2240a5a 100644 --- a/pkg/drivers/kic/types.go +++ b/pkg/drivers/kic/types.go @@ -24,6 +24,9 @@ const ( // DefaultPodCIDR is The CIDR to be used for pods inside the node. DefaultPodCIDR = "10.244.0.0/16" + // Version is the current version of kic + Version = "v0.0.5" + // BaseImage is the base image is used to spin up kic containers. it uses same base-image as kind. BaseImage = "gcr.io/k8s-minikube/kicbase:v0.0.5@sha256:3ddd8461dfb5c3e452ccc44d87750b87a574ec23fc425da67dccc1f0c57d428a" // OverlayImage is the cni plugin used for overlay image, created by kind. diff --git a/pkg/minikube/image/image.go b/pkg/minikube/image/image.go index 5a61f840e4..712ca5d618 100644 --- a/pkg/minikube/image/image.go +++ b/pkg/minikube/image/image.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "time" "github.com/docker/docker/client" @@ -30,6 +31,8 @@ import ( v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/daemon" "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/pkg/errors" + "k8s.io/minikube/pkg/drivers/kic/oci" "k8s.io/minikube/pkg/minikube/constants" ) @@ -69,6 +72,33 @@ func DigestByGoLib(imgName string) string { return cf.Hex } +// WriteImageToDaemon write img to the local docker daemon +func WriteImageToDaemon(img string) error { + glog.Infof("Writing %s to local daemon", img) + if err := oci.PointToHostDockerDaemon(); err != nil { + return errors.Wrap(err, "point host docker-daemon") + } + // Check if image exists locally + ref, err := name.ParseReference(img) + if err != nil { + return errors.Wrap(err, "parsing reference") + } + if _, err := daemon.Image(ref); err == nil { + glog.Infof("Found %s in local docker daemon, skipping pull", img) + return nil + } + i, err := remote.Image(ref) + if err != nil { + return errors.Wrap(err, "getting remote image") + } + tag, err := name.NewTag(strings.Split(img, "@")[0]) + if err != nil { + return errors.Wrap(err, "getting tag") + } + _, err = daemon.Write(tag, i) + return err +} + func retrieveImage(ref name.Reference) (v1.Image, error) { glog.Infof("retrieving image: %+v", ref) img, err := daemon.Image(ref) diff --git a/pkg/minikube/node/cache.go b/pkg/minikube/node/cache.go index b1d5ad8d12..16ee65cb81 100644 --- a/pkg/minikube/node/cache.go +++ b/pkg/minikube/node/cache.go @@ -24,8 +24,10 @@ import ( "github.com/spf13/viper" "golang.org/x/sync/errgroup" cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config" + "k8s.io/minikube/pkg/drivers/kic" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/image" "k8s.io/minikube/pkg/minikube/localpath" @@ -44,7 +46,7 @@ func beginCacheRequiredImages(g *errgroup.Group, imageRepository string, k8sVers }) } -func handleDownloadOnly(cacheGroup *errgroup.Group, k8sVersion string) { +func handleDownloadOnly(cacheGroup *errgroup.Group, k8sVersion, driverName string) { // If --download-only, complete the remaining downloads and exit. if !viper.GetBool("download-only") { return @@ -55,7 +57,13 @@ func handleDownloadOnly(cacheGroup *errgroup.Group, k8sVersion string) { if _, err := CacheKubectlBinary(k8sVersion); err != nil { exit.WithError("Failed to cache kubectl", err) } + var kicArtifactsGroup errgroup.Group + if driver.IsKIC(driverName) { // for kic we need to find what port docker/podman chose for us + // Download kic base image and preloaded images tarball + beginDownloadKicArtifacts(&kicArtifactsGroup, k8sVersion) + } waitCacheRequiredImages(cacheGroup) + waitDownloadKicArtifacts(&kicArtifactsGroup) if err := saveImagesToTarFromConfig(); err != nil { exit.WithError("Failed to cache images to tar", err) } @@ -79,6 +87,26 @@ func doCacheBinaries(k8sVersion string) error { return machine.CacheBinariesForBootstrapper(k8sVersion, viper.GetString(cmdcfg.Bootstrapper)) } +func beginDownloadKicArtifacts(g *errgroup.Group, k8sVersion string) { + glog.Info("Beginning downloading kic artifacts") + g.Go(func() error { + glog.Infof("Downloading %s to local daemon", kic.BaseImage) + return image.WriteImageToDaemon(kic.BaseImage) + }) + + g.Go(func() error { + glog.Info("Caching tarball of preloaded images") + return kic.CachePreloadedTarball(k8sVersion) + }) +} + +func waitDownloadKicArtifacts(g *errgroup.Group) { + if err := g.Wait(); err != nil { + glog.Errorln("Error downloading kic artifacts: ", err) + } + glog.Info("Succesfully downloaded all kic artifacts") +} + // waitCacheRequiredImages blocks until the required images are all cached. func waitCacheRequiredImages(g *errgroup.Group) { if !viper.GetBool(cacheImages) { diff --git a/pkg/minikube/node/start.go b/pkg/minikube/node/start.go index 07d14d4525..c8c05b3418 100644 --- a/pkg/minikube/node/start.go +++ b/pkg/minikube/node/start.go @@ -47,7 +47,7 @@ func Start(mc config.MachineConfig, n config.Node, primary bool, existingAddons k8sVersion := mc.KubernetesConfig.KubernetesVersion driverName := mc.Driver // exits here in case of --download-only option. - handleDownloadOnly(&cacheGroup, k8sVersion) + handleDownloadOnly(&cacheGroup, k8sVersion, driverName) mRunner, preExists, machineAPI, host := startMachine(&mc, &n) defer machineAPI.Close() // configure the runtime (docker, containerd, crio) From bb11bc666b6091333ba69a0c44ba7afb083d66cd Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Wed, 19 Feb 2020 23:02:36 -0800 Subject: [PATCH 02/28] Create volume of preloaded images and mount it in --- pkg/drivers/kic/cache.go | 1 + pkg/drivers/kic/kic.go | 7 ++- pkg/drivers/kic/oci/oci.go | 14 ++++-- pkg/drivers/kic/oci/types.go | 25 +++++----- pkg/drivers/kic/oci/volumes.go | 54 +++++++++++++++++++++ pkg/drivers/kic/types.go | 21 ++++---- pkg/minikube/machine/cache_images.go | 5 ++ pkg/minikube/registry/drvs/docker/docker.go | 15 +++--- 8 files changed, 108 insertions(+), 34 deletions(-) diff --git a/pkg/drivers/kic/cache.go b/pkg/drivers/kic/cache.go index d299f8e19b..fa94071157 100644 --- a/pkg/drivers/kic/cache.go +++ b/pkg/drivers/kic/cache.go @@ -26,6 +26,7 @@ import ( "k8s.io/minikube/pkg/minikube/localpath" ) +// CachePreloadedTarball caches the preloaded images tarball on the host machine func CachePreloadedTarball(k8sVersion string) error { targetDir := localpath.MakeMiniPath("cache", "preloaded-tarball") targetFilepath := path.Join(targetDir, fmt.Sprintf("%s-k8s-%s.tar", Version, k8sVersion)) diff --git a/pkg/drivers/kic/kic.go b/pkg/drivers/kic/kic.go index 0ecb6a2ade..26ef158dcf 100644 --- a/pkg/drivers/kic/kic.go +++ b/pkg/drivers/kic/kic.go @@ -88,7 +88,12 @@ func (d *Driver) Create() error { ContainerPort: constants.DockerDaemonPort, }, ) - err := oci.CreateContainerNode(params) + volumeName, err := oci.CreatePreloadedImagesVolume(Version, d.NodeConfig.KubernetesVersion) + if err != nil { + glog.Infof("Unable to create preloaded images volume: %v", err) + } + params.PreloadedVolume = volumeName + err = oci.CreateContainerNode(params) if err != nil { return errors.Wrap(err, "create kic node") } diff --git a/pkg/drivers/kic/oci/oci.go b/pkg/drivers/kic/oci/oci.go index 6380fd34b5..81158982fe 100644 --- a/pkg/drivers/kic/oci/oci.go +++ b/pkg/drivers/kic/oci/oci.go @@ -74,11 +74,17 @@ func CreateContainerNode(p CreateParams) error { runArgs = append(runArgs, "--volume", fmt.Sprintf("%s:/var:exec", hostVarVolPath)) } if p.OCIBinary == Docker { - if err := createDockerVolume(p.Name); err != nil { - return errors.Wrapf(err, "creating volume for %s container", p.Name) + volumeName := p.PreloadedVolume + if volumeName == "" { + fmt.Println("volume name was empty!!") + os.Exit(1) + if err := createDockerVolume(p.Name); err != nil { + return errors.Wrapf(err, "creating volume for %s container", p.Name) + } + glog.Infof("Successfully created a docker volume %s", p.Name) + volumeName = p.Name } - glog.Infof("Successfully created a docker volume %s", p.Name) - runArgs = append(runArgs, "--volume", fmt.Sprintf("%s:/var", p.Name)) + runArgs = append(runArgs, "--volume", fmt.Sprintf("%s:/var", volumeName)) // setting resource limit in privileged mode is only supported by docker // podman error: "Error: invalid configuration, cannot set resources with rootless containers not using cgroups v2 unified mode" runArgs = append(runArgs, fmt.Sprintf("--cpus=%s", p.CPUs), fmt.Sprintf("--memory=%s", p.Memory)) diff --git a/pkg/drivers/kic/oci/types.go b/pkg/drivers/kic/oci/types.go index ffc71f57c0..deed6d6e07 100644 --- a/pkg/drivers/kic/oci/types.go +++ b/pkg/drivers/kic/oci/types.go @@ -31,18 +31,19 @@ const ( // CreateParams are parameters needed to create a container type CreateParams struct { - Name string // used for container name and hostname - Image string // container image to use to create the node. - ClusterLabel string // label the containers we create using minikube so we can clean up - Role string // currently only role supported is control-plane - Mounts []Mount // volume mounts - APIServerPort int // kubernetes api server port - PortMappings []PortMapping // ports to map to container from host - CPUs string // number of cpu cores assign to container - Memory string // memory (mbs) to assign to the container - Envs map[string]string // environment variables to pass to the container - ExtraArgs []string // a list of any extra option to pass to oci binary during creation time, for example --expose 8080... - OCIBinary string // docker or podman + Name string // used for container name and hostname + Image string // container image to use to create the node. + ClusterLabel string // label the containers we create using minikube so we can clean up + Role string // currently only role supported is control-plane + Mounts []Mount // volume mounts + APIServerPort int // kubernetes api server port + PortMappings []PortMapping // ports to map to container from host + CPUs string // number of cpu cores assign to container + Memory string // memory (mbs) to assign to the container + Envs map[string]string // environment variables to pass to the container + ExtraArgs []string // a list of any extra option to pass to oci binary during creation time, for example --expose 8080... + OCIBinary string // docker or podman + PreloadedVolume string // volume of preloaded images } // createOpt is an option for Create diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index e23b35c394..c3efff7615 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -17,10 +17,14 @@ limitations under the License. package oci import ( + "fmt" "os/exec" + "path" + "strings" "github.com/golang/glog" "github.com/pkg/errors" + "k8s.io/minikube/pkg/minikube/localpath" ) // DeleteAllVolumesByLabel deletes all volumes that have a specific label @@ -40,6 +44,56 @@ func DeleteAllVolumesByLabel(ociBin string, label string) error { return nil } +func CreatePreloadedImagesVolume(kicVersion, k8sVersion string) (string, error) { + if err := PointToHostDockerDaemon(); err != nil { + return "", errors.Wrap(err, "point host docker-daemon") + } + volumeName := fmt.Sprintf("%s-k8s-%s", kicVersion, k8sVersion) + if dockerVolumeExists(volumeName) { + return volumeName, nil + } + if err := createDockerVolume(volumeName); err != nil { + return "", errors.Wrap(err, "creating docker volume") + } + targetDir := localpath.MakeMiniPath("cache", "preloaded-tarball") + tarballPath := path.Join(targetDir, fmt.Sprintf("%s-k8s-%s.tar", kicVersion, k8sVersion)) + + if err := extractTarballToVolume(tarballPath, volumeName); err != nil { + return "", errors.Wrap(err, "extracting tarball to volume") + } + return volumeName, nil +} + +func dockerVolumeExists(name string) bool { + if err := PointToHostDockerDaemon(); err != nil { + return false + } + cmd := exec.Command(Docker, "volume", "ls", "-q") + out, err := cmd.CombinedOutput() + if err != nil { + return false + } + names := strings.Split(string(out), "\n") + for _, n := range names { + if n == name { + return true + } + } + return false +} + +func extractTarballToVolume(tarballPath, volumeName string) error { + if err := PointToHostDockerDaemon(); err != nil { + return errors.Wrap(err, "point host docker-daemon") + } + cmd := exec.Command(Docker, "-v", fmt.Sprintf("%s:/preloaded.tar:ro", tarballPath), "-v", fmt.Sprintf("%s:/extractDir", volumeName), "busybox", "tar", "xvf", "/preloaded.tar", "-C", "/extractDir") + fmt.Println(cmd.Args) + if out, err := cmd.CombinedOutput(); err != nil { + return errors.Wrapf(err, "output %s", string(out)) + } + return nil +} + // createDockerVolume creates a docker volume to be attached to the container with correct labels and prefixes based on profile name // Caution ! if volume already exists does NOT return an error and will not apply the minikube labels on it. // TODO: this should be fixed as a part of https://github.com/kubernetes/minikube/issues/6530 diff --git a/pkg/drivers/kic/types.go b/pkg/drivers/kic/types.go index 63f2240a5a..1c2811a1a7 100644 --- a/pkg/drivers/kic/types.go +++ b/pkg/drivers/kic/types.go @@ -36,14 +36,15 @@ const ( // Config is configuration for the kic driver used by registry type Config struct { - MachineName string // maps to the container name being created - CPU int // Number of CPU cores assigned to the container - Memory int // max memory in MB - StorePath string // libmachine store path - OCIBinary string // oci tool to use (docker, podman,...) - ImageDigest string // image name with sha to use for the node - Mounts []oci.Mount // mounts - APIServerPort int // kubernetes api server port inside the container - PortMappings []oci.PortMapping // container port mappings - Envs map[string]string // key,value of environment variables passed to the node + MachineName string // maps to the container name being created + CPU int // Number of CPU cores assigned to the container + Memory int // max memory in MB + StorePath string // libmachine store path + OCIBinary string // oci tool to use (docker, podman,...) + ImageDigest string // image name with sha to use for the node + Mounts []oci.Mount // mounts + APIServerPort int // kubernetes api server port inside the container + PortMappings []oci.PortMapping // container port mappings + Envs map[string]string // key,value of environment variables passed to the node + KubernetesVersion string // kubernetes version to install } diff --git a/pkg/minikube/machine/cache_images.go b/pkg/minikube/machine/cache_images.go index 29d61b244f..3becfd3c83 100644 --- a/pkg/minikube/machine/cache_images.go +++ b/pkg/minikube/machine/cache_images.go @@ -62,6 +62,11 @@ func CacheImagesForBootstrapper(imageRepository string, version string, clusterB // LoadImages loads previously cached images into the container runtime func LoadImages(cc *config.MachineConfig, runner command.Runner, images []string, cacheDir string) error { + glog.Info("skipping load images") + if true { + return nil + } + glog.Infof("LoadImages start: %s", images) start := time.Now() diff --git a/pkg/minikube/registry/drvs/docker/docker.go b/pkg/minikube/registry/drvs/docker/docker.go index 38cc95acb7..69e4ab3c99 100644 --- a/pkg/minikube/registry/drvs/docker/docker.go +++ b/pkg/minikube/registry/drvs/docker/docker.go @@ -45,13 +45,14 @@ func init() { func configure(mc config.MachineConfig) (interface{}, error) { return kic.NewDriver(kic.Config{ - MachineName: mc.Name, - StorePath: localpath.MiniPath(), - ImageDigest: kic.BaseImage, - CPU: mc.CPUs, - Memory: mc.Memory, - OCIBinary: oci.Docker, - APIServerPort: mc.Nodes[0].Port, + MachineName: mc.Name, + StorePath: localpath.MiniPath(), + ImageDigest: kic.BaseImage, + CPU: mc.CPUs, + Memory: mc.Memory, + OCIBinary: oci.Docker, + APIServerPort: mc.Nodes[0].Port, + KubernetesVersion: mc.KubernetesConfig.KubernetesVersion, }), nil } From 335637f4a34ab7cdd0d66bb674a8e703f4bbe182 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Thu, 20 Feb 2020 09:42:04 -0800 Subject: [PATCH 03/28] Name tarball by k8s version name --- pkg/drivers/kic/cache.go | 2 +- pkg/drivers/kic/kic.go | 2 +- pkg/drivers/kic/oci/volumes.go | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/drivers/kic/cache.go b/pkg/drivers/kic/cache.go index fa94071157..e8421b2c27 100644 --- a/pkg/drivers/kic/cache.go +++ b/pkg/drivers/kic/cache.go @@ -29,7 +29,7 @@ import ( // CachePreloadedTarball caches the preloaded images tarball on the host machine func CachePreloadedTarball(k8sVersion string) error { targetDir := localpath.MakeMiniPath("cache", "preloaded-tarball") - targetFilepath := path.Join(targetDir, fmt.Sprintf("%s-k8s-%s.tar", Version, k8sVersion)) + targetFilepath := path.Join(targetDir, fmt.Sprintf("%s.tar", k8sVersion)) if _, err := os.Stat(targetFilepath); err == nil { glog.Infof("Found %s in cache, skipping downloading", targetFilepath) diff --git a/pkg/drivers/kic/kic.go b/pkg/drivers/kic/kic.go index 26ef158dcf..8e2bee1660 100644 --- a/pkg/drivers/kic/kic.go +++ b/pkg/drivers/kic/kic.go @@ -88,7 +88,7 @@ func (d *Driver) Create() error { ContainerPort: constants.DockerDaemonPort, }, ) - volumeName, err := oci.CreatePreloadedImagesVolume(Version, d.NodeConfig.KubernetesVersion) + volumeName, err := oci.CreatePreloadedImagesVolume(d.NodeConfig.KubernetesVersion) if err != nil { glog.Infof("Unable to create preloaded images volume: %v", err) } diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index 0158df7bb9..8a13fef528 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -22,11 +22,11 @@ import ( "fmt" "os/exec" "path" + "path/filepath" "strings" "github.com/golang/glog" "github.com/pkg/errors" - "k8s.io/minikube/pkg/minikube/localpath" ) // DeleteAllVolumesByLabel deletes all volumes that have a specific label @@ -90,19 +90,20 @@ func allVolumesByLabel(ociBin string, label string) ([]string, error) { return vols, err } -func CreatePreloadedImagesVolume(kicVersion, k8sVersion string) (string, error) { +// CreatePreloadedImagesVolume creates a volume with preloaded images +func CreatePreloadedImagesVolume(k8sVersion string) (string, error) { if err := PointToHostDockerDaemon(); err != nil { return "", errors.Wrap(err, "point host docker-daemon") } - volumeName := fmt.Sprintf("%s-k8s-%s", kicVersion, k8sVersion) + volumeName := fmt.Sprintf("k8s-%s", k8sVersion) if dockerVolumeExists(volumeName) { return volumeName, nil } if err := createDockerVolume(volumeName); err != nil { return "", errors.Wrap(err, "creating docker volume") } - targetDir := localpath.MakeMiniPath("cache", "preloaded-tarball") - tarballPath := path.Join(targetDir, fmt.Sprintf("%s-k8s-%s.tar", kicVersion, k8sVersion)) + targetDir := filepath.Join("cache", "preloaded-tarball") + tarballPath := path.Join(targetDir, fmt.Sprintf("%s.tar", k8sVersion)) if err := extractTarballToVolume(tarballPath, volumeName); err != nil { return "", errors.Wrap(err, "extracting tarball to volume") From 055e4bb6189748b1a301e5fdcb9b3ef8a7d9a927 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Thu, 20 Feb 2020 09:57:19 -0800 Subject: [PATCH 04/28] Add run to docker command --- pkg/drivers/kic/kic.go | 1 + pkg/drivers/kic/oci/volumes.go | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/drivers/kic/kic.go b/pkg/drivers/kic/kic.go index 8e2bee1660..5ae5f85b02 100644 --- a/pkg/drivers/kic/kic.go +++ b/pkg/drivers/kic/kic.go @@ -93,6 +93,7 @@ func (d *Driver) Create() error { glog.Infof("Unable to create preloaded images volume: %v", err) } params.PreloadedVolume = volumeName + fmt.Println("Setting params.preloadedvolume = ", volumeName) err = oci.CreateContainerNode(params) if err != nil { return errors.Wrap(err, "create kic node") diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index 8a13fef528..30151b8807 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -22,11 +22,11 @@ import ( "fmt" "os/exec" "path" - "path/filepath" "strings" "github.com/golang/glog" "github.com/pkg/errors" + "k8s.io/minikube/pkg/minikube/localpath" ) // DeleteAllVolumesByLabel deletes all volumes that have a specific label @@ -102,7 +102,7 @@ func CreatePreloadedImagesVolume(k8sVersion string) (string, error) { if err := createDockerVolume(volumeName); err != nil { return "", errors.Wrap(err, "creating docker volume") } - targetDir := filepath.Join("cache", "preloaded-tarball") + targetDir := localpath.MakeMiniPath("cache", "preloaded-tarball") tarballPath := path.Join(targetDir, fmt.Sprintf("%s.tar", k8sVersion)) if err := extractTarballToVolume(tarballPath, volumeName); err != nil { @@ -133,7 +133,7 @@ func extractTarballToVolume(tarballPath, volumeName string) error { if err := PointToHostDockerDaemon(); err != nil { return errors.Wrap(err, "point host docker-daemon") } - cmd := exec.Command(Docker, "-v", fmt.Sprintf("%s:/preloaded.tar:ro", tarballPath), "-v", fmt.Sprintf("%s:/extractDir", volumeName), "busybox", "tar", "xvf", "/preloaded.tar", "-C", "/extractDir") + cmd := exec.Command(Docker, "run", "-v", fmt.Sprintf("%s:/preloaded.tar:ro", tarballPath), "-v", fmt.Sprintf("%s:/extractDir", volumeName), "busybox", "tar", "xvf", "/preloaded.tar", "-C", "/extractDir") fmt.Println(cmd.Args) if out, err := cmd.CombinedOutput(); err != nil { return errors.Wrapf(err, "output %s", string(out)) From 0e88697f1621d59adba0c8afd50a96ca206d7863 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Thu, 20 Feb 2020 10:06:15 -0800 Subject: [PATCH 05/28] Delete container once it has finished executing --- pkg/drivers/kic/oci/volumes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index 30151b8807..576f9b763e 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -133,7 +133,7 @@ func extractTarballToVolume(tarballPath, volumeName string) error { if err := PointToHostDockerDaemon(); err != nil { return errors.Wrap(err, "point host docker-daemon") } - cmd := exec.Command(Docker, "run", "-v", fmt.Sprintf("%s:/preloaded.tar:ro", tarballPath), "-v", fmt.Sprintf("%s:/extractDir", volumeName), "busybox", "tar", "xvf", "/preloaded.tar", "-C", "/extractDir") + cmd := exec.Command(Docker, "run", "--rm", "-v", fmt.Sprintf("%s:/preloaded.tar:ro", tarballPath), "-v", fmt.Sprintf("%s:/extractDir", volumeName), "busybox", "tar", "xvf", "/preloaded.tar", "-C", "/extractDir") fmt.Println(cmd.Args) if out, err := cmd.CombinedOutput(); err != nil { return errors.Wrapf(err, "output %s", string(out)) From 97ea1b0b858e42bae59ca1db980ac8a81f48a6be Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Thu, 20 Feb 2020 11:55:47 -0800 Subject: [PATCH 06/28] Remove preloaded base image and add makefile rule to upload prelaoded tar to gcs --- Makefile | 9 +++---- hack/images/kicbase.Dockerfile | 10 -------- hack/preload-images/preload_images.go | 35 +++++++++------------------ 3 files changed, 16 insertions(+), 38 deletions(-) diff --git a/Makefile b/Makefile index 9ecabac82e..3b170cbd24 100755 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ VERSION_BUILD ?= 3 RAW_VERSION=$(VERSION_MAJOR).$(VERSION_MINOR).${VERSION_BUILD} VERSION ?= v$(RAW_VERSION) -KUBERNETES_VERSION ?= $(shell egrep "^var DefaultKubernetesVersion" pkg/minikube/constants/constants.go | cut -d \" -f2) +KUBERNETES_VERSION ?= $(shell egrep "DefaultKubernetesVersion =" pkg/minikube/constants/constants.go | cut -d \" -f2) # Default to .0 for higher cache hit rates, as build increments typically don't require new ISO versions ISO_VERSION ?= v$(VERSION_MAJOR).$(VERSION_MINOR).3 @@ -512,10 +512,9 @@ kic-base-image: ## builds the base image used for kic. docker build -f ./hack/images/kicbase.Dockerfile -t $(REGISTRY)/kicbase:v$(KIC_VERSION)-snapshot --build-arg COMMIT_SHA=${VERSION}-$(COMMIT) --target base . -.PHONY: kic-preloaded-base-image -kic-preloaded-base-image: generate-preloaded-images-tar ## builds the base image used for kic. - docker rmi -f $(REGISTRY)/kicbase:v$(KIC_VERSION)-k8s-${KUBERNETES_VERSION} || true - docker build -f ./hack/images/kicbase.Dockerfile -t $(REGISTRY)/kicbase:v$(KIC_VERSION)-k8s-${KUBERNETES_VERSION} --build-arg COMMIT_SHA=${VERSION}-$(COMMIT) --build-arg KUBERNETES_VERSION=${KUBERNETES_VERSION} . +.PHONY: upload-preloaded-images-tar +upload-preloaded-images-tar: generate-preloaded-images-tar # Upload the preloaded images tar to the GCS bucket. Specify a specific kubernetes version to build via `KUBERNETES_VERSION=vx.y.z make upload-preloaded-images-tar`. + gsutil cp out/preloaded-images-k8s-${KUBERNETES_VERSION}.tar gs://minikube-docker-volume-tarballs .PHONY: generate-preloaded-images-tar generate-preloaded-images-tar: out/minikube diff --git a/hack/images/kicbase.Dockerfile b/hack/images/kicbase.Dockerfile index 58fe4bb55b..940b6602d4 100644 --- a/hack/images/kicbase.Dockerfile +++ b/hack/images/kicbase.Dockerfile @@ -43,13 +43,3 @@ RUN rm -rf \ /usr/share/man/* \ /usr/share/local/* \ RUN echo "kic! Build: ${COMMIT_SHA} Time :$(date)" > "/kic.txt" - - -FROM busybox -ARG KUBERNETES_VERSION -COPY out/preloaded-images-k8s-$KUBERNETES_VERSION.tar /preloaded-images.tar -RUN tar xvf /preloaded-images.tar -C / - -FROM base -COPY --from=1 /var/lib/docker /var/lib/docker -COPY --from=1 /var/lib/minikube/binaries /var/lib/minikube/binaries diff --git a/hack/preload-images/preload_images.go b/hack/preload-images/preload_images.go index 82fdba5b41..0acbf76a3a 100644 --- a/hack/preload-images/preload_images.go +++ b/hack/preload-images/preload_images.go @@ -22,7 +22,6 @@ import ( "os" "os/exec" "path/filepath" - "strings" "github.com/pkg/errors" ) @@ -66,32 +65,28 @@ func executePreloadImages() error { } func startMinikube() error { - cmd := exec.Command(minikubePath, "start", "-p", profile, "--memory", "4000", "--kubernetes-version", kubernetesVersion, "--wait=false") + cmd := exec.Command(minikubePath, "start", "-p", profile, "--memory", "4000", "--kubernetes-version", kubernetesVersion, "--wait=false", "--vm-driver=docker") cmd.Stdout = os.Stdout return cmd.Run() } func createImageTarball() error { - cmd := exec.Command(minikubePath, "ssh", "-p", profile, "--", "sudo", "tar", "cvf", tarballFilename, "/var/lib/docker", "/var/lib/minikube/binaries") + cmd := exec.Command(minikubePath, "ssh", "-p", profile, "--", "cd", "/var", "&&", "sudo", "tar", "cvf", tarballFilename, "./lib/docker", "./lib/minikube/binaries") cmd.Stdout = os.Stdout - return cmd.Run() + if err := cmd.Run(); err != nil { + return errors.Wrap(err, "creating image tarball") + } + return nil } func copyTarballToHost() error { - sshKey, err := runCmd([]string{minikubePath, "ssh-key", "-p", profile}) - if err != nil { - return errors.Wrap(err, "getting ssh-key") - } - - ip, err := runCmd([]string{minikubePath, "ip", "-p", profile}) - if err != nil { - return errors.Wrap(err, "getting ip") - } - dest := filepath.Join("out/", tarballFilename) - args := []string{"scp", "-o", "StrictHostKeyChecking=no", "-i", sshKey, fmt.Sprintf("docker@%s:/home/docker/%s", ip, tarballFilename), dest} - _, err = runCmd(args) - return err + cmd := exec.Command("docker", "cp", fmt.Sprintf("%s:/var/%s", profile, tarballFilename), dest) + cmd.Stdout = os.Stdout + if err := cmd.Run(); err != nil { + return errors.Wrap(err, "copying tarball to host") + } + return nil } func deleteMinikube() error { @@ -99,9 +94,3 @@ func deleteMinikube() error { cmd.Stdout = os.Stdout return cmd.Run() } - -func runCmd(command []string) (string, error) { - cmd := exec.Command(command[0], command[1:]...) - output, err := cmd.Output() - return strings.Trim(string(output), "\n "), err -} From 19fa296625334ad4945e8d272b6cc590d0fc1bdf Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Thu, 20 Feb 2020 14:07:03 -0800 Subject: [PATCH 07/28] Compress with lz4 --- Makefile | 2 +- hack/images/kicbase.Dockerfile | 1 + hack/preload-images/preload_images.go | 4 ++-- pkg/drivers/kic/types.go | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 3b170cbd24..0c8d3fd412 100755 --- a/Makefile +++ b/Makefile @@ -514,7 +514,7 @@ kic-base-image: ## builds the base image used for kic. .PHONY: upload-preloaded-images-tar upload-preloaded-images-tar: generate-preloaded-images-tar # Upload the preloaded images tar to the GCS bucket. Specify a specific kubernetes version to build via `KUBERNETES_VERSION=vx.y.z make upload-preloaded-images-tar`. - gsutil cp out/preloaded-images-k8s-${KUBERNETES_VERSION}.tar gs://minikube-docker-volume-tarballs + gsutil cp out/preloaded-images-k8s-${KUBERNETES_VERSION}.tar.lz4 gs://minikube-docker-volume-tarballs .PHONY: generate-preloaded-images-tar generate-preloaded-images-tar: out/minikube diff --git a/hack/images/kicbase.Dockerfile b/hack/images/kicbase.Dockerfile index 940b6602d4..6a1fce7ecd 100644 --- a/hack/images/kicbase.Dockerfile +++ b/hack/images/kicbase.Dockerfile @@ -9,6 +9,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ dnsutils \ openssh-server \ docker.io \ + lz4 \ && apt-get clean -y # disable containerd by default RUN systemctl disable containerd diff --git a/hack/preload-images/preload_images.go b/hack/preload-images/preload_images.go index 0acbf76a3a..46a033c11d 100644 --- a/hack/preload-images/preload_images.go +++ b/hack/preload-images/preload_images.go @@ -39,7 +39,7 @@ var ( func init() { flag.StringVar(&kubernetesVersion, "kubernetes-version", "", "desired kubernetes version, for example `v1.17.2`") flag.Parse() - tarballFilename = fmt.Sprintf("preloaded-images-k8s-%s.tar", kubernetesVersion) + tarballFilename = fmt.Sprintf("preloaded-images-k8s-%s.tar.lz4", kubernetesVersion) } func main() { @@ -71,7 +71,7 @@ func startMinikube() error { } func createImageTarball() error { - cmd := exec.Command(minikubePath, "ssh", "-p", profile, "--", "cd", "/var", "&&", "sudo", "tar", "cvf", tarballFilename, "./lib/docker", "./lib/minikube/binaries") + cmd := exec.Command(minikubePath, "ssh", "-p", profile, "--", "cd", "/var", "&&", "sudo", "tar", "-I", "lz4", "-cvf", tarballFilename, "./lib/docker", "./lib/minikube/binaries") cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { return errors.Wrap(err, "creating image tarball") diff --git a/pkg/drivers/kic/types.go b/pkg/drivers/kic/types.go index 1c2811a1a7..060cf4f42a 100644 --- a/pkg/drivers/kic/types.go +++ b/pkg/drivers/kic/types.go @@ -28,7 +28,7 @@ const ( Version = "v0.0.5" // BaseImage is the base image is used to spin up kic containers. it uses same base-image as kind. - BaseImage = "gcr.io/k8s-minikube/kicbase:v0.0.5@sha256:3ddd8461dfb5c3e452ccc44d87750b87a574ec23fc425da67dccc1f0c57d428a" + BaseImage = "gcr.io/k8s-minikube/kicbase:v0.0.5-snapshot" // OverlayImage is the cni plugin used for overlay image, created by kind. // CNI plugin image used for kic drivers created by kind. OverlayImage = "kindest/kindnetd:0.5.3" From b0f685c0722f04a78ec50d4b0fab088c8c4893a6 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Thu, 20 Feb 2020 14:43:24 -0800 Subject: [PATCH 08/28] Move all preloading code into preload package --- pkg/drivers/kic/kic.go | 6 +++++- pkg/drivers/kic/oci/volumes.go | 14 ++++++-------- .../kic/{cache.go => preload/preload.go} | 18 ++++++++++++------ pkg/minikube/node/cache.go | 13 +++++++------ 4 files changed, 30 insertions(+), 21 deletions(-) rename pkg/drivers/kic/{cache.go => preload/preload.go} (67%) diff --git a/pkg/drivers/kic/kic.go b/pkg/drivers/kic/kic.go index 5ae5f85b02..ca57ec6567 100644 --- a/pkg/drivers/kic/kic.go +++ b/pkg/drivers/kic/kic.go @@ -22,6 +22,7 @@ import ( "os/exec" "strconv" "strings" + "time" "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/log" @@ -88,10 +89,13 @@ func (d *Driver) Create() error { ContainerPort: constants.DockerDaemonPort, }, ) - volumeName, err := oci.CreatePreloadedImagesVolume(d.NodeConfig.KubernetesVersion) + t := time.Now() + glog.Infof("Starting creating preloaded images volume") + volumeName, err := oci.CreatePreloadedImagesVolume(d.NodeConfig.KubernetesVersion, BaseImage) if err != nil { glog.Infof("Unable to create preloaded images volume: %v", err) } + glog.Infof("Finished creating preloaded images volume in %d seconds", time.Since(t).Seconds()) params.PreloadedVolume = volumeName fmt.Println("Setting params.preloadedvolume = ", volumeName) err = oci.CreateContainerNode(params) diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index 576f9b763e..96ef3143db 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -21,12 +21,11 @@ import ( "bytes" "fmt" "os/exec" - "path" "strings" "github.com/golang/glog" "github.com/pkg/errors" - "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/drivers/kic/preload" ) // DeleteAllVolumesByLabel deletes all volumes that have a specific label @@ -91,7 +90,7 @@ func allVolumesByLabel(ociBin string, label string) ([]string, error) { } // CreatePreloadedImagesVolume creates a volume with preloaded images -func CreatePreloadedImagesVolume(k8sVersion string) (string, error) { +func CreatePreloadedImagesVolume(k8sVersion, baseImage string) (string, error) { if err := PointToHostDockerDaemon(); err != nil { return "", errors.Wrap(err, "point host docker-daemon") } @@ -102,10 +101,9 @@ func CreatePreloadedImagesVolume(k8sVersion string) (string, error) { if err := createDockerVolume(volumeName); err != nil { return "", errors.Wrap(err, "creating docker volume") } - targetDir := localpath.MakeMiniPath("cache", "preloaded-tarball") - tarballPath := path.Join(targetDir, fmt.Sprintf("%s.tar", k8sVersion)) + tarballPath := preload.TarballPath(k8sVersion) - if err := extractTarballToVolume(tarballPath, volumeName); err != nil { + if err := extractTarballToVolume(tarballPath, volumeName, baseImage); err != nil { return "", errors.Wrap(err, "extracting tarball to volume") } return volumeName, nil @@ -129,11 +127,11 @@ func dockerVolumeExists(name string) bool { return false } -func extractTarballToVolume(tarballPath, volumeName string) error { +func extractTarballToVolume(tarballPath, volumeName, imageName string) error { if err := PointToHostDockerDaemon(); err != nil { return errors.Wrap(err, "point host docker-daemon") } - cmd := exec.Command(Docker, "run", "--rm", "-v", fmt.Sprintf("%s:/preloaded.tar:ro", tarballPath), "-v", fmt.Sprintf("%s:/extractDir", volumeName), "busybox", "tar", "xvf", "/preloaded.tar", "-C", "/extractDir") + cmd := exec.Command(Docker, "run", "--rm", "--entrypoint", "/bin/bash", "-v", fmt.Sprintf("%s:/preloaded.tar:ro", tarballPath), "-v", fmt.Sprintf("%s:/extractDir", volumeName), imageName, "tar", "-I", "lz4", "-xvf", "/preloaded.tar", "-C", "/extractDir") fmt.Println(cmd.Args) if out, err := cmd.CombinedOutput(); err != nil { return errors.Wrapf(err, "output %s", string(out)) diff --git a/pkg/drivers/kic/cache.go b/pkg/drivers/kic/preload/preload.go similarity index 67% rename from pkg/drivers/kic/cache.go rename to pkg/drivers/kic/preload/preload.go index e8421b2c27..1a88762059 100644 --- a/pkg/drivers/kic/cache.go +++ b/pkg/drivers/kic/preload/preload.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Kubernetes Authors All rights reserved. +Copyright 2020 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. @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kic +package preload import ( "fmt" @@ -26,17 +26,23 @@ import ( "k8s.io/minikube/pkg/minikube/localpath" ) -// CachePreloadedTarball caches the preloaded images tarball on the host machine -func CachePreloadedTarball(k8sVersion string) error { +// TarballPath returns the path to the preloaded tarball +func TarballPath(k8sVersion string) string { targetDir := localpath.MakeMiniPath("cache", "preloaded-tarball") - targetFilepath := path.Join(targetDir, fmt.Sprintf("%s.tar", k8sVersion)) + targetFilepath := path.Join(targetDir, fmt.Sprintf("preloaded-images-k8s-%s.tar.lz4", k8sVersion)) + return targetFilepath +} + +// CacheTarball caches the preloaded images tarball on the host machine +func CacheTarball(k8sVersion string) error { + targetFilepath := TarballPath(k8sVersion) if _, err := os.Stat(targetFilepath); err == nil { glog.Infof("Found %s in cache, skipping downloading", targetFilepath) return nil } - url := fmt.Sprintf("https://storage.googleapis.com/minikube-docker-volume-tarballs/%s-k8s-%s.tar", Version, k8sVersion) + url := fmt.Sprintf("https://storage.googleapis.com/minikube-docker-volume-tarballs/preloaded-images-k8s-%s.tar", k8sVersion) glog.Infof("Downloading %s to %s", url, targetFilepath) return download.ToFile(url, targetFilepath, download.FileOptions{Mkdirs: download.MkdirAll}) } diff --git a/pkg/minikube/node/cache.go b/pkg/minikube/node/cache.go index 16ee65cb81..7919f77093 100644 --- a/pkg/minikube/node/cache.go +++ b/pkg/minikube/node/cache.go @@ -25,6 +25,7 @@ import ( "golang.org/x/sync/errgroup" cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config" "k8s.io/minikube/pkg/drivers/kic" + "k8s.io/minikube/pkg/drivers/kic/preload" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/driver" @@ -51,17 +52,17 @@ func handleDownloadOnly(cacheGroup *errgroup.Group, k8sVersion, driverName strin if !viper.GetBool("download-only") { return } + var kicArtifactsGroup errgroup.Group + if driver.IsKIC(driverName) { // for kic we need to find what port docker/podman chose for us + // Download kic base image and preloaded images tarball + beginDownloadKicArtifacts(&kicArtifactsGroup, k8sVersion) + } if err := doCacheBinaries(k8sVersion); err != nil { exit.WithError("Failed to cache binaries", err) } if _, err := CacheKubectlBinary(k8sVersion); err != nil { exit.WithError("Failed to cache kubectl", err) } - var kicArtifactsGroup errgroup.Group - if driver.IsKIC(driverName) { // for kic we need to find what port docker/podman chose for us - // Download kic base image and preloaded images tarball - beginDownloadKicArtifacts(&kicArtifactsGroup, k8sVersion) - } waitCacheRequiredImages(cacheGroup) waitDownloadKicArtifacts(&kicArtifactsGroup) if err := saveImagesToTarFromConfig(); err != nil { @@ -96,7 +97,7 @@ func beginDownloadKicArtifacts(g *errgroup.Group, k8sVersion string) { g.Go(func() error { glog.Info("Caching tarball of preloaded images") - return kic.CachePreloadedTarball(k8sVersion) + return preload.CacheTarball(k8sVersion) }) } From 5ac8520ab8c1fcde163d3d874f95836f001dd70f Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Thu, 20 Feb 2020 14:59:43 -0800 Subject: [PATCH 09/28] skip transferring binaries and transferring imagse if using preloaded volume --- pkg/drivers/kic/kic.go | 3 +-- pkg/drivers/kic/oci/volumes.go | 2 +- pkg/drivers/kic/preload/preload.go | 7 +++++++ pkg/minikube/bootstrapper/kubeadm/kubeadm.go | 9 +++++++-- pkg/minikube/node/start.go | 12 +++++++++--- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/pkg/drivers/kic/kic.go b/pkg/drivers/kic/kic.go index ca57ec6567..3e0ef46dfc 100644 --- a/pkg/drivers/kic/kic.go +++ b/pkg/drivers/kic/kic.go @@ -95,9 +95,8 @@ func (d *Driver) Create() error { if err != nil { glog.Infof("Unable to create preloaded images volume: %v", err) } - glog.Infof("Finished creating preloaded images volume in %d seconds", time.Since(t).Seconds()) + glog.Infof("Finished creating preloaded images volume in %f seconds", time.Since(t).Seconds()) params.PreloadedVolume = volumeName - fmt.Println("Setting params.preloadedvolume = ", volumeName) err = oci.CreateContainerNode(params) if err != nil { return errors.Wrap(err, "create kic node") diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index 96ef3143db..dd8fd31707 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -131,7 +131,7 @@ func extractTarballToVolume(tarballPath, volumeName, imageName string) error { if err := PointToHostDockerDaemon(); err != nil { return errors.Wrap(err, "point host docker-daemon") } - cmd := exec.Command(Docker, "run", "--rm", "--entrypoint", "/bin/bash", "-v", fmt.Sprintf("%s:/preloaded.tar:ro", tarballPath), "-v", fmt.Sprintf("%s:/extractDir", volumeName), imageName, "tar", "-I", "lz4", "-xvf", "/preloaded.tar", "-C", "/extractDir") + cmd := exec.Command(Docker, "run", "--rm", "--entrypoint", "/usr/bin/tar", "-v", fmt.Sprintf("%s:/preloaded.tar:ro", tarballPath), "-v", fmt.Sprintf("%s:/extractDir", volumeName), imageName, "-I", "lz4", "-xvf", "/preloaded.tar", "-C", "/extractDir") fmt.Println(cmd.Args) if out, err := cmd.CombinedOutput(); err != nil { return errors.Wrapf(err, "output %s", string(out)) diff --git a/pkg/drivers/kic/preload/preload.go b/pkg/drivers/kic/preload/preload.go index 1a88762059..f95a7f092c 100644 --- a/pkg/drivers/kic/preload/preload.go +++ b/pkg/drivers/kic/preload/preload.go @@ -46,3 +46,10 @@ func CacheTarball(k8sVersion string) error { glog.Infof("Downloading %s to %s", url, targetFilepath) return download.ToFile(url, targetFilepath, download.FileOptions{Mkdirs: download.MkdirAll}) } + +// UsingPreloadedVolume returns true if the preloaded tarball exists +func UsingPreloadedVolume(k8sVersion string) bool { + path := TarballPath(k8sVersion) + _, err := os.Stat(path) + return err == nil +} diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go index 9eb428d8fa..ac2922b539 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go @@ -40,6 +40,7 @@ import ( kconst "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/minikube/pkg/drivers/kic" "k8s.io/minikube/pkg/drivers/kic/oci" + "k8s.io/minikube/pkg/drivers/kic/preload" "k8s.io/minikube/pkg/kapi" "k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/minikube/bootstrapper/bsutil" @@ -436,8 +437,12 @@ func (k *Bootstrapper) UpdateCluster(cfg config.MachineConfig) error { glog.Warningf("unable to stop kubelet: %s command: %q output: %q", err, rr.Command(), rr.Output()) } - if err := bsutil.TransferBinaries(cfg.KubernetesConfig, k.c); err != nil { - return errors.Wrap(err, "downloading binaries") + // Skip transfer if using preloaded kic volume + skipTransfer := driver.IsKIC(cfg.Driver) && preload.UsingPreloadedVolume(cfg.KubernetesConfig.KubernetesVersion) + if !skipTransfer { + if err := bsutil.TransferBinaries(cfg.KubernetesConfig, k.c); err != nil { + return errors.Wrap(err, "downloading binaries") + } } var cniFile []byte diff --git a/pkg/minikube/node/start.go b/pkg/minikube/node/start.go index a7ada50c41..8d2983a9ca 100644 --- a/pkg/minikube/node/start.go +++ b/pkg/minikube/node/start.go @@ -22,6 +22,7 @@ import ( "github.com/spf13/viper" "golang.org/x/sync/errgroup" "k8s.io/minikube/pkg/addons" + "k8s.io/minikube/pkg/drivers/kic/preload" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" @@ -34,9 +35,16 @@ import ( // Start spins up a guest and starts the kubernetes node. func Start(mc config.MachineConfig, n config.Node, primary bool, existingAddons map[string]bool) (*kubeconfig.Settings, error) { + k8sVersion := mc.KubernetesConfig.KubernetesVersion + driverName := mc.Driver + // Now that the ISO is downloaded, pull images in the background while the VM boots. var cacheGroup errgroup.Group - beginCacheRequiredImages(&cacheGroup, mc.KubernetesConfig.ImageRepository, n.KubernetesVersion) + if !preload.UsingPreloadedVolume(k8sVersion) && driver.IsKIC(driverName) { + beginCacheRequiredImages(&cacheGroup, mc.KubernetesConfig.ImageRepository, k8sVersion) + } else { + mc.KubernetesConfig.ShouldLoadCachedImages = false + } // Abstraction leakage alert: startHost requires the config to be saved, to satistfy pkg/provision/buildroot. // Hence, saveConfig must be called before startHost, and again afterwards when we know the IP. @@ -44,8 +52,6 @@ func Start(mc config.MachineConfig, n config.Node, primary bool, existingAddons exit.WithError("Failed to save config", err) } - k8sVersion := mc.KubernetesConfig.KubernetesVersion - driverName := mc.Driver // exits here in case of --download-only option. handleDownloadOnly(&cacheGroup, k8sVersion, driverName) mRunner, preExists, machineAPI, host := startMachine(&mc, &n) From 55dd40efc5c3bc882b223ebbc27dc6a9d51eaaa6 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Thu, 20 Feb 2020 15:05:07 -0800 Subject: [PATCH 10/28] Debugging --- pkg/drivers/kic/oci/oci.go | 2 -- pkg/drivers/kic/types.go | 5 ++--- pkg/minikube/machine/cache_images.go | 5 ----- pkg/minikube/node/cache.go | 2 +- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/pkg/drivers/kic/oci/oci.go b/pkg/drivers/kic/oci/oci.go index c28b6a748e..9c83b7a4ca 100644 --- a/pkg/drivers/kic/oci/oci.go +++ b/pkg/drivers/kic/oci/oci.go @@ -101,8 +101,6 @@ func CreateContainerNode(p CreateParams) error { if p.OCIBinary == Docker { volumeName := p.PreloadedVolume if volumeName == "" { - fmt.Println("volume name was empty!!") - os.Exit(1) if err := createDockerVolume(p.Name); err != nil { return errors.Wrapf(err, "creating volume for %s container", p.Name) } diff --git a/pkg/drivers/kic/types.go b/pkg/drivers/kic/types.go index 060cf4f42a..c6b65f79a1 100644 --- a/pkg/drivers/kic/types.go +++ b/pkg/drivers/kic/types.go @@ -28,9 +28,8 @@ const ( Version = "v0.0.5" // BaseImage is the base image is used to spin up kic containers. it uses same base-image as kind. - BaseImage = "gcr.io/k8s-minikube/kicbase:v0.0.5-snapshot" - // OverlayImage is the cni plugin used for overlay image, created by kind. - // CNI plugin image used for kic drivers created by kind. + BaseImage = "gcr.io/k8s-minikube/kicbase:v0.0.5@sha256:3ddd8461dfb5c3e452ccc44d87750b87a574ec23fc425da67dccc1f0c57d428a" // CNI plugin image used for kic drivers created by kind. + OverlayImage = "kindest/kindnetd:0.5.3" ) diff --git a/pkg/minikube/machine/cache_images.go b/pkg/minikube/machine/cache_images.go index 3becfd3c83..29d61b244f 100644 --- a/pkg/minikube/machine/cache_images.go +++ b/pkg/minikube/machine/cache_images.go @@ -62,11 +62,6 @@ func CacheImagesForBootstrapper(imageRepository string, version string, clusterB // LoadImages loads previously cached images into the container runtime func LoadImages(cc *config.MachineConfig, runner command.Runner, images []string, cacheDir string) error { - glog.Info("skipping load images") - if true { - return nil - } - glog.Infof("LoadImages start: %s", images) start := time.Now() diff --git a/pkg/minikube/node/cache.go b/pkg/minikube/node/cache.go index 7919f77093..633c02e406 100644 --- a/pkg/minikube/node/cache.go +++ b/pkg/minikube/node/cache.go @@ -105,7 +105,7 @@ func waitDownloadKicArtifacts(g *errgroup.Group) { if err := g.Wait(); err != nil { glog.Errorln("Error downloading kic artifacts: ", err) } - glog.Info("Succesfully downloaded all kic artifacts") + glog.Info("Successfully downloaded all kic artifacts") } // waitCacheRequiredImages blocks until the required images are all cached. From c99bf76ee46b75302220b3115e9ab53c480f0dea Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Fri, 21 Feb 2020 12:01:03 -0800 Subject: [PATCH 11/28] Add checksum verification --- Makefile | 3 +- go.mod | 1 + pkg/drivers/kic/kic.go | 4 +- pkg/drivers/kic/oci/volumes.go | 16 +- pkg/drivers/kic/preload/preload.go | 55 ------- pkg/minikube/bootstrapper/kubeadm/kubeadm.go | 2 +- pkg/minikube/constants/constants.go | 3 + pkg/minikube/node/cache.go | 12 +- pkg/minikube/node/start.go | 16 +- pkg/minikube/preload/preload.go | 157 +++++++++++++++++++ 10 files changed, 189 insertions(+), 80 deletions(-) delete mode 100644 pkg/drivers/kic/preload/preload.go create mode 100644 pkg/minikube/preload/preload.go diff --git a/Makefile b/Makefile index 60d5347ffb..850ad0204d 100755 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ VERSION ?= v$(RAW_VERSION) KUBERNETES_VERSION ?= $(shell egrep "DefaultKubernetesVersion =" pkg/minikube/constants/constants.go | cut -d \" -f2) KIC_VERSION ?= $(shell egrep "Version =" pkg/drivers/kic/types.go | cut -d \" -f2) +PRELOADED_VOLUMES_GCS_BUCKET ?= $(shell egrep "PreloadedVolumeTarballsBucket =" pkg/minikube/constants/constants.go | cut -d \" -f2) # Default to .0 for higher cache hit rates, as build increments typically don't require new ISO versions ISO_VERSION ?= v$(VERSION_MAJOR).$(VERSION_MINOR).3 @@ -513,7 +514,7 @@ kic-base-image: ## builds the base image used for kic. .PHONY: upload-preloaded-images-tar upload-preloaded-images-tar: generate-preloaded-images-tar # Upload the preloaded images tar to the GCS bucket. Specify a specific kubernetes version to build via `KUBERNETES_VERSION=vx.y.z make upload-preloaded-images-tar`. - gsutil cp out/preloaded-images-k8s-${KUBERNETES_VERSION}.tar.lz4 gs://minikube-docker-volume-tarballs + gsutil cp out/preloaded-images-k8s-${KUBERNETES_VERSION}.tar.lz4 gs://${PRELOADED_VOLUMES_GCS_BUCKET} .PHONY: generate-preloaded-images-tar generate-preloaded-images-tar: out/minikube diff --git a/go.mod b/go.mod index e4798d896c..5442fdf4dc 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module k8s.io/minikube go 1.13 require ( + cloud.google.com/go v0.45.1 github.com/Parallels/docker-machine-parallels v1.3.0 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/blang/semver v3.5.0+incompatible diff --git a/pkg/drivers/kic/kic.go b/pkg/drivers/kic/kic.go index 3e0ef46dfc..c9b8c246ec 100644 --- a/pkg/drivers/kic/kic.go +++ b/pkg/drivers/kic/kic.go @@ -30,10 +30,12 @@ import ( "github.com/docker/machine/libmachine/state" "github.com/golang/glog" "github.com/pkg/errors" + "github.com/spf13/viper" pkgdrivers "k8s.io/minikube/pkg/drivers" "k8s.io/minikube/pkg/drivers/kic/oci" "k8s.io/minikube/pkg/minikube/assets" "k8s.io/minikube/pkg/minikube/command" + "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" ) @@ -91,7 +93,7 @@ func (d *Driver) Create() error { ) t := time.Now() glog.Infof("Starting creating preloaded images volume") - volumeName, err := oci.CreatePreloadedImagesVolume(d.NodeConfig.KubernetesVersion, BaseImage) + volumeName, err := oci.CreatePreloadedImagesVolume(d.NodeConfig.KubernetesVersion, BaseImage, viper.GetString(config.MachineProfile)) if err != nil { glog.Infof("Unable to create preloaded images volume: %v", err) } diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index dd8fd31707..36742c59d7 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -25,7 +25,7 @@ import ( "github.com/golang/glog" "github.com/pkg/errors" - "k8s.io/minikube/pkg/drivers/kic/preload" + "k8s.io/minikube/pkg/minikube/preload" ) // DeleteAllVolumesByLabel deletes all volumes that have a specific label @@ -90,18 +90,20 @@ func allVolumesByLabel(ociBin string, label string) ([]string, error) { } // CreatePreloadedImagesVolume creates a volume with preloaded images -func CreatePreloadedImagesVolume(k8sVersion, baseImage string) (string, error) { +// k8sVersion is used to name the volume and baseImage is the image that is run +// to extract the preloaded images to the volume +func CreatePreloadedImagesVolume(k8sVersion, baseImage, profile string) (string, error) { if err := PointToHostDockerDaemon(); err != nil { return "", errors.Wrap(err, "point host docker-daemon") } - volumeName := fmt.Sprintf("k8s-%s", k8sVersion) + volumeName := fmt.Sprintf("k8s-%s-%s", k8sVersion, profile) if dockerVolumeExists(volumeName) { return volumeName, nil } if err := createDockerVolume(volumeName); err != nil { return "", errors.Wrap(err, "creating docker volume") } - tarballPath := preload.TarballPath(k8sVersion) + tarballPath := preload.TarballFilepath(k8sVersion) if err := extractTarballToVolume(tarballPath, volumeName, baseImage); err != nil { return "", errors.Wrap(err, "extracting tarball to volume") @@ -109,6 +111,7 @@ func CreatePreloadedImagesVolume(k8sVersion, baseImage string) (string, error) { return volumeName, nil } +// dockerVolumeExists returns true if a docker volume with the passed in name exists func dockerVolumeExists(name string) bool { if err := PointToHostDockerDaemon(); err != nil { return false @@ -120,19 +123,20 @@ func dockerVolumeExists(name string) bool { } names := strings.Split(string(out), "\n") for _, n := range names { - if n == name { + if strings.TrimSpace(n) == name { return true } } return false } +// extractTarballToVolume runs a docker image imageName which extracts the tarball at tarballPath +// to the volume named volumeName func extractTarballToVolume(tarballPath, volumeName, imageName string) error { if err := PointToHostDockerDaemon(); err != nil { return errors.Wrap(err, "point host docker-daemon") } cmd := exec.Command(Docker, "run", "--rm", "--entrypoint", "/usr/bin/tar", "-v", fmt.Sprintf("%s:/preloaded.tar:ro", tarballPath), "-v", fmt.Sprintf("%s:/extractDir", volumeName), imageName, "-I", "lz4", "-xvf", "/preloaded.tar", "-C", "/extractDir") - fmt.Println(cmd.Args) if out, err := cmd.CombinedOutput(); err != nil { return errors.Wrapf(err, "output %s", string(out)) } diff --git a/pkg/drivers/kic/preload/preload.go b/pkg/drivers/kic/preload/preload.go deleted file mode 100644 index f95a7f092c..0000000000 --- a/pkg/drivers/kic/preload/preload.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2020 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 preload - -import ( - "fmt" - "os" - "path" - - "github.com/golang/glog" - "github.com/jimmidyson/go-download" - "k8s.io/minikube/pkg/minikube/localpath" -) - -// TarballPath returns the path to the preloaded tarball -func TarballPath(k8sVersion string) string { - targetDir := localpath.MakeMiniPath("cache", "preloaded-tarball") - targetFilepath := path.Join(targetDir, fmt.Sprintf("preloaded-images-k8s-%s.tar.lz4", k8sVersion)) - return targetFilepath -} - -// CacheTarball caches the preloaded images tarball on the host machine -func CacheTarball(k8sVersion string) error { - targetFilepath := TarballPath(k8sVersion) - - if _, err := os.Stat(targetFilepath); err == nil { - glog.Infof("Found %s in cache, skipping downloading", targetFilepath) - return nil - } - - url := fmt.Sprintf("https://storage.googleapis.com/minikube-docker-volume-tarballs/preloaded-images-k8s-%s.tar", k8sVersion) - glog.Infof("Downloading %s to %s", url, targetFilepath) - return download.ToFile(url, targetFilepath, download.FileOptions{Mkdirs: download.MkdirAll}) -} - -// UsingPreloadedVolume returns true if the preloaded tarball exists -func UsingPreloadedVolume(k8sVersion string) bool { - path := TarballPath(k8sVersion) - _, err := os.Stat(path) - return err == nil -} diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go index ac2922b539..091ed4a720 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go @@ -40,7 +40,6 @@ import ( kconst "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/minikube/pkg/drivers/kic" "k8s.io/minikube/pkg/drivers/kic/oci" - "k8s.io/minikube/pkg/drivers/kic/preload" "k8s.io/minikube/pkg/kapi" "k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/minikube/bootstrapper/bsutil" @@ -53,6 +52,7 @@ import ( "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/machine" "k8s.io/minikube/pkg/minikube/out" + "k8s.io/minikube/pkg/minikube/preload" "k8s.io/minikube/pkg/minikube/vmpath" "k8s.io/minikube/pkg/util/retry" ) diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index dbf74c4747..33a4892ebb 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -63,6 +63,9 @@ const ( MinikubeActiveDockerdEnv = "MINIKUBE_ACTIVE_DOCKERD" // PodmanVarlinkBridgeEnv is used for podman settings PodmanVarlinkBridgeEnv = "PODMAN_VARLINK_BRIDGE" + + // PreloadedVolumeTarballsBucket is the name of the GCS bucket where preloaded volume tarballs exist + PreloadedVolumeTarballsBucket = "minikube-preloaded-volume-tarballs" ) var ( diff --git a/pkg/minikube/node/cache.go b/pkg/minikube/node/cache.go index 633c02e406..73d08cb9fa 100644 --- a/pkg/minikube/node/cache.go +++ b/pkg/minikube/node/cache.go @@ -25,15 +25,14 @@ import ( "golang.org/x/sync/errgroup" cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config" "k8s.io/minikube/pkg/drivers/kic" - "k8s.io/minikube/pkg/drivers/kic/preload" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" - "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/image" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/machine" "k8s.io/minikube/pkg/minikube/out" + "k8s.io/minikube/pkg/minikube/preload" ) // beginCacheRequiredImages caches images required for kubernetes version in the background @@ -47,16 +46,11 @@ func beginCacheRequiredImages(g *errgroup.Group, imageRepository string, k8sVers }) } -func handleDownloadOnly(cacheGroup *errgroup.Group, k8sVersion, driverName string) { +func handleDownloadOnly(cacheGroup, kicGroup *errgroup.Group, k8sVersion, driverName string) { // If --download-only, complete the remaining downloads and exit. if !viper.GetBool("download-only") { return } - var kicArtifactsGroup errgroup.Group - if driver.IsKIC(driverName) { // for kic we need to find what port docker/podman chose for us - // Download kic base image and preloaded images tarball - beginDownloadKicArtifacts(&kicArtifactsGroup, k8sVersion) - } if err := doCacheBinaries(k8sVersion); err != nil { exit.WithError("Failed to cache binaries", err) } @@ -64,7 +58,7 @@ func handleDownloadOnly(cacheGroup *errgroup.Group, k8sVersion, driverName strin exit.WithError("Failed to cache kubectl", err) } waitCacheRequiredImages(cacheGroup) - waitDownloadKicArtifacts(&kicArtifactsGroup) + waitDownloadKicArtifacts(kicGroup) if err := saveImagesToTarFromConfig(); err != nil { exit.WithError("Failed to cache images to tar", err) } diff --git a/pkg/minikube/node/start.go b/pkg/minikube/node/start.go index 8d2983a9ca..ed8cdc136f 100644 --- a/pkg/minikube/node/start.go +++ b/pkg/minikube/node/start.go @@ -22,7 +22,6 @@ import ( "github.com/spf13/viper" "golang.org/x/sync/errgroup" "k8s.io/minikube/pkg/addons" - "k8s.io/minikube/pkg/drivers/kic/preload" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" @@ -38,13 +37,15 @@ func Start(mc config.MachineConfig, n config.Node, primary bool, existingAddons k8sVersion := mc.KubernetesConfig.KubernetesVersion driverName := mc.Driver - // Now that the ISO is downloaded, pull images in the background while the VM boots. - var cacheGroup errgroup.Group - if !preload.UsingPreloadedVolume(k8sVersion) && driver.IsKIC(driverName) { - beginCacheRequiredImages(&cacheGroup, mc.KubernetesConfig.ImageRepository, k8sVersion) - } else { + // See if we can create a volume of preloaded images + // If not, pull images in the background while the VM boots. + var kicGroup errgroup.Group + if driver.IsKIC(driverName) { + beginDownloadKicArtifacts(&kicGroup, k8sVersion) mc.KubernetesConfig.ShouldLoadCachedImages = false } + var cacheGroup errgroup.Group + beginCacheRequiredImages(&cacheGroup, mc.KubernetesConfig.ImageRepository, k8sVersion) // Abstraction leakage alert: startHost requires the config to be saved, to satistfy pkg/provision/buildroot. // Hence, saveConfig must be called before startHost, and again afterwards when we know the IP. @@ -53,13 +54,14 @@ func Start(mc config.MachineConfig, n config.Node, primary bool, existingAddons } // exits here in case of --download-only option. - handleDownloadOnly(&cacheGroup, k8sVersion, driverName) + handleDownloadOnly(&cacheGroup, &kicGroup, k8sVersion, driverName) mRunner, preExists, machineAPI, host := startMachine(&mc, &n) defer machineAPI.Close() // configure the runtime (docker, containerd, crio) cr := configureRuntimes(mRunner, driverName, mc.KubernetesConfig) showVersionInfo(k8sVersion, cr) waitCacheRequiredImages(&cacheGroup) + waitDownloadKicArtifacts(&kicGroup) //TODO(sharifelgamal): Part out the cluster-wide operations, perhaps using the "primary" param diff --git a/pkg/minikube/preload/preload.go b/pkg/minikube/preload/preload.go new file mode 100644 index 0000000000..cf64230ec2 --- /dev/null +++ b/pkg/minikube/preload/preload.go @@ -0,0 +1,157 @@ +/* +Copyright 2020 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 preload + +import ( + "context" + "crypto/md5" + "fmt" + "io/ioutil" + "net/http" + "os" + "path" + + "cloud.google.com/go/storage" + "github.com/golang/glog" + "github.com/hashicorp/go-getter" + "github.com/pkg/errors" + "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/out" + "k8s.io/minikube/pkg/util" +) + +// returns name of the tarball +func tarballName(k8sVersion string) string { + return fmt.Sprintf("preloaded-images-k8s-%s.tar.lz4", k8sVersion) +} + +// returns the name of the checksum file +func checksumName(k8sVersion string) string { + return fmt.Sprintf("preloaded-images-k8s-%s.tar.lz4.checksum", k8sVersion) +} + +// returns target dir for all cached items related to preloading +func targetDir() string { + return localpath.MakeMiniPath("cache", "preloaded-tarball") +} + +// returns path to checksum file +func checksumFilepath(k8sVersion string) string { + return path.Join(targetDir(), checksumName(k8sVersion)) +} + +// TarballFilepath returns the path to the preloaded tarball +func TarballFilepath(k8sVersion string) string { + return path.Join(targetDir(), tarballName(k8sVersion)) +} + +// remoteTarballURL returns the URL for the remote tarball in GCS +func remoteTarballURL(k8sVersion string) string { + return fmt.Sprintf("https://storage.googleapis.com/%s/%s", constants.PreloadedVolumeTarballsBucket, tarballName(k8sVersion)) +} + +// CacheTarball caches the preloaded images tarball on the host machine +func CacheTarball(k8sVersion string) error { + targetFilepath := TarballFilepath(k8sVersion) + + if _, err := os.Stat(targetFilepath); err == nil { + if err := verifyChecksum(k8sVersion); err == nil { + glog.Infof("Found %s in cache, skipping downloading", targetFilepath) + return nil + } + } + + url := remoteTarballURL(k8sVersion) + + // Make sure we support this k8s version + if _, err := http.Get(url); err != nil { + glog.Infof("Unable to get response from %s, skipping downloading: %v", err) + return nil + } + + out.T(out.FileDownload, "Downloading preloaded images tarball for k8s {{.version}}:", out.V{"version": k8sVersion}) + os.Remove(targetFilepath) + client := &getter.Client{ + Src: url, + Dst: targetFilepath, + Mode: getter.ClientModeFile, + Options: []getter.ClientOption{getter.WithProgress(util.DefaultProgressBar)}, + } + + glog.Infof("Downloading: %+v", client) + if err := client.Get(); err != nil { + return errors.Wrapf(err, "download failed: %s", url) + } + // Give downloaded drivers a baseline decent file permission + if err := os.Chmod(targetFilepath, 0755); err != nil { + return err + } + // Save checksum file locally + if err := saveChecksumFile(k8sVersion); err != nil { + return errors.Wrap(err, "saving checksum file") + } + return verifyChecksum(k8sVersion) +} + +func saveChecksumFile(k8sVersion string) error { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + return err + } + attrs, err := client.Bucket(constants.PreloadedVolumeTarballsBucket).Object(tarballName(k8sVersion)).Attrs(ctx) + if err != nil { + return err + } + checksum := attrs.MD5 + return ioutil.WriteFile(checksumFilepath(k8sVersion), checksum, 0644) +} + +func cachedTarballExists(k8sVersion string) bool { + _, err := os.Stat(TarballFilepath(k8sVersion)) + if err != nil { + return false + } + return false +} + +// verifyChecksum returns true if the checksum of the local binary matches +// the checksum of the remote binary +func verifyChecksum(k8sVersion string) error { + // get md5 checksum of tarball path + contents, err := ioutil.ReadFile(TarballFilepath(k8sVersion)) + if err != nil { + return errors.Wrap(err, "reading tarball") + } + checksum := md5.Sum(contents) + + remoteChecksum, err := ioutil.ReadFile(checksumFilepath(k8sVersion)) + if err != nil { + return errors.Wrap(err, "reading checksum file") + } + + // create a slice of checksum, which is [16]byte + if string(remoteChecksum) != string(checksum[:]) { + return fmt.Errorf("checksum of %s does not match remote checksum", TarballFilepath(k8sVersion)) + } + return nil +} + +func UsingPreloadedVolume(k8sVersion string) bool { + return true +} From cc1a6f11f07a15ccea8db07b566155fbf1da1694 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Fri, 21 Feb 2020 12:13:34 -0800 Subject: [PATCH 12/28] Delete volume if it isn't extracted properly so future runs don't try to use it --- pkg/drivers/kic/oci/volumes.go | 16 ++++++++++++++++ pkg/minikube/preload/preload.go | 1 - 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index 36742c59d7..54a1d3da83 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -106,6 +106,10 @@ func CreatePreloadedImagesVolume(k8sVersion, baseImage, profile string) (string, tarballPath := preload.TarballFilepath(k8sVersion) if err := extractTarballToVolume(tarballPath, volumeName, baseImage); err != nil { + // If the extraction didn't work, delete the corrupt docker volume + if err := deleteDockerVolume(volumeName); err != nil { + glog.Warningf("Corrupt docker volume %s was not deleted successfully. You may need to delete it manually via `docker volume rm %s` for minikube to continue to work.", volumeName, volumeName) + } return "", errors.Wrap(err, "extracting tarball to volume") } return volumeName, nil @@ -156,3 +160,15 @@ func createDockerVolume(name string) error { } return nil } + +// deleteDockerVolume deletes a docker volume with the given name +func deleteDockerVolume(name string) error { + if err := PointToHostDockerDaemon(); err != nil { + return errors.Wrap(err, "point host docker-daemon") + } + cmd := exec.Command(Docker, "volume", "rm", name) + if out, err := cmd.CombinedOutput(); err != nil { + return errors.Wrapf(err, "output %s", string(out)) + } + return nil +} diff --git a/pkg/minikube/preload/preload.go b/pkg/minikube/preload/preload.go index cf64230ec2..885273667b 100644 --- a/pkg/minikube/preload/preload.go +++ b/pkg/minikube/preload/preload.go @@ -85,7 +85,6 @@ func CacheTarball(k8sVersion string) error { } out.T(out.FileDownload, "Downloading preloaded images tarball for k8s {{.version}}:", out.V{"version": k8sVersion}) - os.Remove(targetFilepath) client := &getter.Client{ Src: url, Dst: targetFilepath, From 328466f1e16f007261c4ea489bed9738f8e29c3e Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Fri, 21 Feb 2020 12:41:33 -0800 Subject: [PATCH 13/28] Check if preloaded volume is attached before skipping binary transfer --- pkg/drivers/kic/oci/volumes.go | 30 +++++++++++++++++++- pkg/minikube/bootstrapper/kubeadm/kubeadm.go | 3 +- pkg/minikube/preload/preload.go | 12 -------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index 54a1d3da83..342fe12b33 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -96,7 +96,7 @@ func CreatePreloadedImagesVolume(k8sVersion, baseImage, profile string) (string, if err := PointToHostDockerDaemon(); err != nil { return "", errors.Wrap(err, "point host docker-daemon") } - volumeName := fmt.Sprintf("k8s-%s-%s", k8sVersion, profile) + volumeName := preloadedVolumeName(k8sVersion, profile) if dockerVolumeExists(volumeName) { return volumeName, nil } @@ -172,3 +172,31 @@ func deleteDockerVolume(name string) error { } return nil } + +func preloadedVolumeName(k8sVersion, profile string) string { + return fmt.Sprintf("k8s-%s-%s", k8sVersion, profile) +} + +// PreloadedVolumeAttached returns true if the preloaded volume is attached +// to the running profile +func PreloadedVolumeAttached(k8sVersion, profile string) bool { + glog.Infof("Checking if preloaded volume is attached to %s", profile) + if err := PointToHostDockerDaemon(); err != nil { + glog.Infof("error pointing host to docker daemon: %v", err) + return false + } + volumeName := preloadedVolumeName(k8sVersion, profile) + cmd := exec.Command(Docker, "inspect", "-f", "{{range .Mounts}} {{.Name}} {{end}}", profile) + out, err := cmd.CombinedOutput() + if err != nil { + glog.Infof("error inspecting mounted volumes: %v", err) + return false + } + vols := strings.Split(string(out), " ") + for _, v := range vols { + if strings.TrimSpace(v) == volumeName { + return true + } + } + return false +} diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go index 091ed4a720..7fb3e76c0a 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go @@ -52,7 +52,6 @@ import ( "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/machine" "k8s.io/minikube/pkg/minikube/out" - "k8s.io/minikube/pkg/minikube/preload" "k8s.io/minikube/pkg/minikube/vmpath" "k8s.io/minikube/pkg/util/retry" ) @@ -438,7 +437,7 @@ func (k *Bootstrapper) UpdateCluster(cfg config.MachineConfig) error { } // Skip transfer if using preloaded kic volume - skipTransfer := driver.IsKIC(cfg.Driver) && preload.UsingPreloadedVolume(cfg.KubernetesConfig.KubernetesVersion) + skipTransfer := oci.PreloadedVolumeAttached(cfg.KubernetesConfig.KubernetesVersion, viper.GetString(config.MachineProfile)) if !skipTransfer { if err := bsutil.TransferBinaries(cfg.KubernetesConfig, k.c); err != nil { return errors.Wrap(err, "downloading binaries") diff --git a/pkg/minikube/preload/preload.go b/pkg/minikube/preload/preload.go index 885273667b..85181be1c4 100644 --- a/pkg/minikube/preload/preload.go +++ b/pkg/minikube/preload/preload.go @@ -121,14 +121,6 @@ func saveChecksumFile(k8sVersion string) error { return ioutil.WriteFile(checksumFilepath(k8sVersion), checksum, 0644) } -func cachedTarballExists(k8sVersion string) bool { - _, err := os.Stat(TarballFilepath(k8sVersion)) - if err != nil { - return false - } - return false -} - // verifyChecksum returns true if the checksum of the local binary matches // the checksum of the remote binary func verifyChecksum(k8sVersion string) error { @@ -150,7 +142,3 @@ func verifyChecksum(k8sVersion string) error { } return nil } - -func UsingPreloadedVolume(k8sVersion string) bool { - return true -} From 97bf1287037d8c0544b1719ec415cb642123d108 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Fri, 21 Feb 2020 12:53:24 -0800 Subject: [PATCH 14/28] only use preloaded volumes for docker runtime --- pkg/drivers/kic/kic.go | 2 +- pkg/drivers/kic/oci/volumes.go | 5 ++++- pkg/drivers/kic/types.go | 1 + pkg/minikube/node/cache.go | 4 ++-- pkg/minikube/node/start.go | 2 +- pkg/minikube/preload/preload.go | 5 ++++- pkg/minikube/registry/drvs/docker/docker.go | 1 + 7 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pkg/drivers/kic/kic.go b/pkg/drivers/kic/kic.go index c9b8c246ec..4849a3c7a6 100644 --- a/pkg/drivers/kic/kic.go +++ b/pkg/drivers/kic/kic.go @@ -93,7 +93,7 @@ func (d *Driver) Create() error { ) t := time.Now() glog.Infof("Starting creating preloaded images volume") - volumeName, err := oci.CreatePreloadedImagesVolume(d.NodeConfig.KubernetesVersion, BaseImage, viper.GetString(config.MachineProfile)) + volumeName, err := oci.CreatePreloadedImagesVolume(d.NodeConfig.KubernetesVersion, d.NodeConfig.ContainerRuntime, BaseImage, viper.GetString(config.MachineProfile)) if err != nil { glog.Infof("Unable to create preloaded images volume: %v", err) } diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index 342fe12b33..40a8c77ee2 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -92,7 +92,10 @@ func allVolumesByLabel(ociBin string, label string) ([]string, error) { // CreatePreloadedImagesVolume creates a volume with preloaded images // k8sVersion is used to name the volume and baseImage is the image that is run // to extract the preloaded images to the volume -func CreatePreloadedImagesVolume(k8sVersion, baseImage, profile string) (string, error) { +func CreatePreloadedImagesVolume(k8sVersion, cRuntime, baseImage, profile string) (string, error) { + if cRuntime != "docker" { + return "", nil + } if err := PointToHostDockerDaemon(); err != nil { return "", errors.Wrap(err, "point host docker-daemon") } diff --git a/pkg/drivers/kic/types.go b/pkg/drivers/kic/types.go index 0fd591aef3..d1e2edea6c 100644 --- a/pkg/drivers/kic/types.go +++ b/pkg/drivers/kic/types.go @@ -56,4 +56,5 @@ type Config struct { PortMappings []oci.PortMapping // container port mappings Envs map[string]string // key,value of environment variables passed to the node KubernetesVersion string // kubernetes version to install + ContainerRuntime string // container runtime kic is running } diff --git a/pkg/minikube/node/cache.go b/pkg/minikube/node/cache.go index 73d08cb9fa..da64030f62 100644 --- a/pkg/minikube/node/cache.go +++ b/pkg/minikube/node/cache.go @@ -82,7 +82,7 @@ func doCacheBinaries(k8sVersion string) error { return machine.CacheBinariesForBootstrapper(k8sVersion, viper.GetString(cmdcfg.Bootstrapper)) } -func beginDownloadKicArtifacts(g *errgroup.Group, k8sVersion string) { +func beginDownloadKicArtifacts(g *errgroup.Group, k8sVersion, cRuntime string) { glog.Info("Beginning downloading kic artifacts") g.Go(func() error { glog.Infof("Downloading %s to local daemon", kic.BaseImage) @@ -91,7 +91,7 @@ func beginDownloadKicArtifacts(g *errgroup.Group, k8sVersion string) { g.Go(func() error { glog.Info("Caching tarball of preloaded images") - return preload.CacheTarball(k8sVersion) + return preload.CacheTarball(k8sVersion, cRuntime) }) } diff --git a/pkg/minikube/node/start.go b/pkg/minikube/node/start.go index ed8cdc136f..cb6564088f 100644 --- a/pkg/minikube/node/start.go +++ b/pkg/minikube/node/start.go @@ -41,7 +41,7 @@ func Start(mc config.MachineConfig, n config.Node, primary bool, existingAddons // If not, pull images in the background while the VM boots. var kicGroup errgroup.Group if driver.IsKIC(driverName) { - beginDownloadKicArtifacts(&kicGroup, k8sVersion) + beginDownloadKicArtifacts(&kicGroup, k8sVersion, mc.KubernetesConfig.ContainerRuntime) mc.KubernetesConfig.ShouldLoadCachedImages = false } var cacheGroup errgroup.Group diff --git a/pkg/minikube/preload/preload.go b/pkg/minikube/preload/preload.go index 85181be1c4..6cb2b40580 100644 --- a/pkg/minikube/preload/preload.go +++ b/pkg/minikube/preload/preload.go @@ -66,7 +66,10 @@ func remoteTarballURL(k8sVersion string) string { } // CacheTarball caches the preloaded images tarball on the host machine -func CacheTarball(k8sVersion string) error { +func CacheTarball(k8sVersion, containerRuntime string) error { + if containerRuntime != "docker" { + return nil + } targetFilepath := TarballFilepath(k8sVersion) if _, err := os.Stat(targetFilepath); err == nil { diff --git a/pkg/minikube/registry/drvs/docker/docker.go b/pkg/minikube/registry/drvs/docker/docker.go index 69e4ab3c99..183ae783e8 100644 --- a/pkg/minikube/registry/drvs/docker/docker.go +++ b/pkg/minikube/registry/drvs/docker/docker.go @@ -53,6 +53,7 @@ func configure(mc config.MachineConfig) (interface{}, error) { OCIBinary: oci.Docker, APIServerPort: mc.Nodes[0].Port, KubernetesVersion: mc.KubernetesConfig.KubernetesVersion, + ContainerRuntime: mc.KubernetesConfig.ContainerRuntime, }), nil } From dee885267b9177f4ee2b888a64b343ea176a8a9a Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Fri, 21 Feb 2020 15:59:36 -0800 Subject: [PATCH 15/28] Don't require authentication to get checksum --- go.mod | 1 + pkg/minikube/preload/preload.go | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 5442fdf4dc..6e74463f72 100644 --- a/go.mod +++ b/go.mod @@ -70,6 +70,7 @@ require ( golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/sys v0.0.0-20191010194322-b09406accb47 golang.org/x/text v0.3.2 + google.golang.org/api v0.9.0 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect k8s.io/api v0.17.2 k8s.io/apimachinery v0.17.2 diff --git a/pkg/minikube/preload/preload.go b/pkg/minikube/preload/preload.go index 6cb2b40580..9c12a8ea6a 100644 --- a/pkg/minikube/preload/preload.go +++ b/pkg/minikube/preload/preload.go @@ -26,6 +26,8 @@ import ( "path" "cloud.google.com/go/storage" + "google.golang.org/api/option" + "github.com/golang/glog" "github.com/hashicorp/go-getter" "github.com/pkg/errors" @@ -87,7 +89,7 @@ func CacheTarball(k8sVersion, containerRuntime string) error { return nil } - out.T(out.FileDownload, "Downloading preloaded images tarball for k8s {{.version}}:", out.V{"version": k8sVersion}) + out.T(out.FileDownload, "Downloading preloaded images tarball for k8s {{.version}} ...", out.V{"version": k8sVersion}) client := &getter.Client{ Src: url, Dst: targetFilepath, @@ -112,13 +114,13 @@ func CacheTarball(k8sVersion, containerRuntime string) error { func saveChecksumFile(k8sVersion string) error { ctx := context.Background() - client, err := storage.NewClient(ctx) + client, err := storage.NewClient(ctx, option.WithoutAuthentication()) if err != nil { - return err + return errors.Wrap(err, "getting storage client") } attrs, err := client.Bucket(constants.PreloadedVolumeTarballsBucket).Object(tarballName(k8sVersion)).Attrs(ctx) if err != nil { - return err + return errors.Wrap(err, "getting storage object") } checksum := attrs.MD5 return ioutil.WriteFile(checksumFilepath(k8sVersion), checksum, 0644) @@ -141,7 +143,7 @@ func verifyChecksum(k8sVersion string) error { // create a slice of checksum, which is [16]byte if string(remoteChecksum) != string(checksum[:]) { - return fmt.Errorf("checksum of %s does not match remote checksum", TarballFilepath(k8sVersion)) + return fmt.Errorf("checksum of %s does not match remote checksum (%s != %s)", TarballFilepath(k8sVersion), string(remoteChecksum), string(checksum[:])) } return nil } From 8409fc38166c45b7fac442a79f3ee7ca21a0577e Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Fri, 21 Feb 2020 16:19:00 -0800 Subject: [PATCH 16/28] wait to finish downloading kic artifacts before starting machine --- pkg/minikube/node/start.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/minikube/node/start.go b/pkg/minikube/node/start.go index cb6564088f..f1d26acce9 100644 --- a/pkg/minikube/node/start.go +++ b/pkg/minikube/node/start.go @@ -55,13 +55,14 @@ func Start(mc config.MachineConfig, n config.Node, primary bool, existingAddons // exits here in case of --download-only option. handleDownloadOnly(&cacheGroup, &kicGroup, k8sVersion, driverName) + waitDownloadKicArtifacts(&kicGroup) + mRunner, preExists, machineAPI, host := startMachine(&mc, &n) defer machineAPI.Close() // configure the runtime (docker, containerd, crio) cr := configureRuntimes(mRunner, driverName, mc.KubernetesConfig) showVersionInfo(k8sVersion, cr) waitCacheRequiredImages(&cacheGroup) - waitDownloadKicArtifacts(&kicGroup) //TODO(sharifelgamal): Part out the cluster-wide operations, perhaps using the "primary" param From 697359b4f3602630ac90c0c25335e33b4ff2a752 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 13:37:22 -0800 Subject: [PATCH 17/28] Fixed lint --- hack/preload-images/preload_images.go | 6 --- pkg/drivers/kic/oci/oci.go | 65 ++++++++++++++++++++------- pkg/minikube/node/cache.go | 2 +- pkg/minikube/node/start.go | 2 +- pkg/minikube/preload/preload.go | 2 +- 5 files changed, 53 insertions(+), 24 deletions(-) diff --git a/hack/preload-images/preload_images.go b/hack/preload-images/preload_images.go index 293594fa80..2c1c979bf6 100644 --- a/hack/preload-images/preload_images.go +++ b/hack/preload-images/preload_images.go @@ -147,12 +147,6 @@ func executePreloadImages() error { return copyTarballToHost() } -func startMinikube() error { - cmd := exec.Command(minikubePath, "start", "-p", profile, "--memory", "4000", "--kubernetes-version", kubernetesVersion, "--vm-driver=docker") - cmd.Stdout = os.Stdout - return cmd.Run() -} - func createImageTarball() error { cmd := exec.Command("docker", "exec", profile, "sudo", "tar", "-I", "lz4", "-C", "/var/lib/docker", "-cvf", tarballFilename, "./") cmd.Stdout = os.Stdout diff --git a/pkg/drivers/kic/oci/oci.go b/pkg/drivers/kic/oci/oci.go index 95ce68392b..2d510aa0e3 100644 --- a/pkg/drivers/kic/oci/oci.go +++ b/pkg/drivers/kic/oci/oci.go @@ -17,9 +17,11 @@ limitations under the License. package oci import ( + "context" "os" "path/filepath" "strconv" + "time" "bufio" "bytes" @@ -34,13 +36,13 @@ import ( "strings" ) -// DeleteAllContainersByLabel deletes all containers that have a specific label +// DeleteContainersByLabel deletes all containers that have a specific label // if there no containers found with the given label, it will return nil -func DeleteAllContainersByLabel(ociBin string, label string) []error { +func DeleteContainersByLabel(ociBin string, label string) []error { var deleteErrs []error if ociBin == Docker { if err := PointToHostDockerDaemon(); err != nil { - return []error{errors.Wrap(err, "point host docker-daemon")} + return []error{errors.Wrap(err, "point host docker daemon")} } } cs, err := listContainersByLabel(ociBin, label) @@ -51,10 +53,19 @@ func DeleteAllContainersByLabel(ociBin string, label string) []error { return nil } for _, c := range cs { + _, err := ContainerStatus(ociBin, c) + // only try to delete if docker/podman inspect returns + // if it doesn't it means docker daemon is stuck and needs restart + if err != nil { + deleteErrs = append(deleteErrs, errors.Wrapf(err, "delete container %s: %s daemon is stuck. please try again!", c, ociBin)) + glog.Errorf("%s daemon seems to be stuck. Please try restarting your %s.", ociBin, ociBin) + continue + } cmd := exec.Command(ociBin, "rm", "-f", "-v", c) if out, err := cmd.CombinedOutput(); err != nil { deleteErrs = append(deleteErrs, errors.Wrapf(err, "delete container %s: output %s", c, out)) } + } return deleteErrs } @@ -62,7 +73,7 @@ func DeleteAllContainersByLabel(ociBin string, label string) []error { // CreateContainerNode creates a new container node func CreateContainerNode(p CreateParams) error { if err := PointToHostDockerDaemon(); err != nil { - return errors.Wrap(err, "point host docker-daemon") + return errors.Wrap(err, "point host docker daemon") } runArgs := []string{ @@ -103,7 +114,7 @@ func CreateContainerNode(p CreateParams) error { return errors.Wrapf(err, "creating volume for %s container", p.Name) } glog.Infof("Successfully created a docker volume %s", p.Name) - runArgs = append(runArgs, "--volume", fmt.Sprintf("%s:%s", p.Name, "/var")) + runArgs = append(runArgs, "--volume", fmt.Sprintf("%s:/var", p.Name)) // setting resource limit in privileged mode is only supported by docker // podman error: "Error: invalid configuration, cannot set resources with rootless containers not using cgroups v2 unified mode" runArgs = append(runArgs, fmt.Sprintf("--cpus=%s", p.CPUs), fmt.Sprintf("--memory=%s", p.Memory)) @@ -141,7 +152,7 @@ func CreateContainerNode(p CreateParams) error { // CreateContainer creates a container with "docker/podman run" func createContainer(ociBinary string, image string, opts ...createOpt) ([]string, error) { if err := PointToHostDockerDaemon(); err != nil { - return nil, errors.Wrap(err, "point host docker-daemon") + return nil, errors.Wrap(err, "point host docker daemon") } o := &createOpts{} @@ -185,7 +196,7 @@ func createContainer(ociBinary string, image string, opts ...createOpt) ([]strin // Copy copies a local asset into the container func Copy(ociBinary string, ociID string, targetDir string, fName string) error { if err := PointToHostDockerDaemon(); err != nil { - return errors.Wrap(err, "point host docker-daemon") + return errors.Wrap(err, "point host docker daemon") } if _, err := os.Stat(fName); os.IsNotExist(err) { return errors.Wrapf(err, "error source %s does not exist", fName) @@ -206,7 +217,7 @@ func Copy(ociBinary string, ociID string, targetDir string, fName string) error // only supports TCP ports func HostPortBinding(ociBinary string, ociID string, contPort int) (int, error) { if err := PointToHostDockerDaemon(); err != nil { - return 0, errors.Wrap(err, "point host docker-daemon") + return 0, errors.Wrap(err, "point host docker daemon") } var out []byte var err error @@ -261,7 +272,7 @@ func podmanConttainerIP(name string) (string, string, error) { // dockerContainerIP returns ipv4, ipv6 of container or error func dockerContainerIP(name string) (string, string, error) { if err := PointToHostDockerDaemon(); err != nil { - return "", "", errors.Wrap(err, "point host docker-daemon") + return "", "", errors.Wrap(err, "point host docker daemon") } // retrieve the IP address of the node using docker inspect lines, err := inspect(Docker, name, "{{range .NetworkSettings.Networks}}{{.IPAddress}},{{.GlobalIPv6Address}}{{end}}") @@ -281,7 +292,7 @@ func dockerContainerIP(name string) (string, string, error) { // ContainerID returns id of a container name func ContainerID(ociBinary string, nameOrID string) (string, error) { if err := PointToHostDockerDaemon(); err != nil { - return "", errors.Wrap(err, "point host docker-daemon") + return "", errors.Wrap(err, "point host docker daemon") } cmd := exec.Command(ociBinary, "inspect", "-f", "{{.Id}}", nameOrID) id, err := cmd.CombinedOutput() @@ -299,7 +310,7 @@ func ListOwnedContainers(ociBinary string) ([]string, error) { // inspect return low-level information on containers func inspect(ociBinary string, containerNameOrID, format string) ([]string, error) { if err := PointToHostDockerDaemon(); err != nil { - return nil, errors.Wrap(err, "point host docker-daemon") + return nil, errors.Wrap(err, "point host docker daemon") } cmd := exec.Command(ociBinary, "inspect", "-f", format, @@ -365,7 +376,7 @@ func generateMountBindings(mounts ...Mount) []string { // isUsernsRemapEnabled checks if userns-remap is enabled in docker func isUsernsRemapEnabled(ociBinary string) (bool, error) { if err := PointToHostDockerDaemon(); err != nil { - return false, errors.Wrap(err, "point host docker-daemon") + return false, errors.Wrap(err, "point host docker daemon") } cmd := exec.Command(ociBinary, "info", "--format", "'{{json .SecurityOptions}}'") var buff bytes.Buffer @@ -427,9 +438,12 @@ func withPortMappings(portMappings []PortMapping) createOpt { // listContainersByLabel returns all the container names with a specified label func listContainersByLabel(ociBinary string, label string) ([]string, error) { if err := PointToHostDockerDaemon(); err != nil { - return nil, errors.Wrap(err, "point host docker-daemon") + return nil, errors.Wrap(err, "point host docker daemon") } - cmd := exec.Command(ociBinary, "ps", "-a", "--filter", fmt.Sprintf("label=%s", label), "--format", "{{.Names}}") + // allow no more than 5 seconds for docker ps + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + cmd := exec.CommandContext(ctx, ociBinary, "ps", "-a", "--filter", fmt.Sprintf("label=%s", label), "--format", "{{.Names}}") stdout, err := cmd.Output() s := bufio.NewScanner(bytes.NewReader(stdout)) var names []string @@ -439,7 +453,6 @@ func listContainersByLabel(ociBinary string, label string) ([]string, error) { names = append(names, n) } } - return names, err } @@ -461,3 +474,25 @@ func PointToHostDockerDaemon() error { } return nil } + +// ContainerStatus returns status of a container running,exited,... +func ContainerStatus(ociBin string, name string) (string, error) { + if ociBin == Docker { + if err := PointToHostDockerDaemon(); err != nil { + return "", errors.Wrap(err, "point host docker daemon") + } + } + // allow no more than 2 seconds for this. when this takes long this means deadline passed + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + cmd := exec.CommandContext(ctx, ociBin, "inspect", name, "--format={{.State.Status}}") + out, err := cmd.CombinedOutput() + if ctx.Err() == context.DeadlineExceeded { + glog.Warningf("%s inspect %s took longer than normal. Restarting your %s daemon might fix this issue.", ociBin, name, ociBin) + return strings.TrimSpace(string(out)), fmt.Errorf("inspect %s timeout", name) + } + if err != nil { + return string(out), errors.Wrapf(err, "inspecting container: output %s", out) + } + return strings.TrimSpace(string(out)), nil +} diff --git a/pkg/minikube/node/cache.go b/pkg/minikube/node/cache.go index 3d997c8778..fce265bf77 100644 --- a/pkg/minikube/node/cache.go +++ b/pkg/minikube/node/cache.go @@ -46,7 +46,7 @@ func beginCacheRequiredImages(g *errgroup.Group, imageRepository string, k8sVers }) } -func handleDownloadOnly(cacheGroup, kicGroup *errgroup.Group, k8sVersion, driverName string) { +func handleDownloadOnly(cacheGroup, kicGroup *errgroup.Group, k8sVersion string) { // If --download-only, complete the remaining downloads and exit. if !viper.GetBool("download-only") { return diff --git a/pkg/minikube/node/start.go b/pkg/minikube/node/start.go index 91bf0c57c8..24f6f185f5 100644 --- a/pkg/minikube/node/start.go +++ b/pkg/minikube/node/start.go @@ -54,7 +54,7 @@ func Start(mc config.ClusterConfig, n config.Node, primary bool, existingAddons } // exits here in case of --download-only option. - handleDownloadOnly(&cacheGroup, &kicGroup, k8sVersion, driverName) + handleDownloadOnly(&cacheGroup, &kicGroup, k8sVersion) waitDownloadKicArtifacts(&kicGroup) mRunner, preExists, machineAPI, host := startMachine(&mc, &n) diff --git a/pkg/minikube/preload/preload.go b/pkg/minikube/preload/preload.go index 9c12a8ea6a..bcc1c50a1d 100644 --- a/pkg/minikube/preload/preload.go +++ b/pkg/minikube/preload/preload.go @@ -85,7 +85,7 @@ func CacheTarball(k8sVersion, containerRuntime string) error { // Make sure we support this k8s version if _, err := http.Get(url); err != nil { - glog.Infof("Unable to get response from %s, skipping downloading: %v", err) + glog.Infof("Unable to get response from %s, skipping downloading: %v", url, err) return nil } From 474561d082293179ee23666232bdcc95809f9cbd Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 13:38:35 -0800 Subject: [PATCH 18/28] remove unused functions --- pkg/drivers/kic/oci/volumes.go | 61 ---------------------------------- 1 file changed, 61 deletions(-) diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index adb75f7b05..85fc80434f 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -27,7 +27,6 @@ import ( "github.com/golang/glog" "github.com/pkg/errors" - "k8s.io/minikube/pkg/minikube/preload" ) // DeleteAllVolumesByLabel deletes all volumes that have a specific label @@ -104,54 +103,6 @@ func allVolumesByLabel(ociBin string, label string) ([]string, error) { return vols, err } -// CreatePreloadedImagesVolume creates a volume with preloaded images -// k8sVersion is used to name the volume and baseImage is the image that is run -// to extract the preloaded images to the volume -func CreatePreloadedImagesVolume(k8sVersion, cRuntime, baseImage, profile string) (string, error) { - if cRuntime != "docker" { - return "", nil - } - if err := PointToHostDockerDaemon(); err != nil { - return "", errors.Wrap(err, "point host docker-daemon") - } - volumeName := preloadedVolumeName(k8sVersion, profile) - if dockerVolumeExists(volumeName) { - return volumeName, nil - } - if err := createDockerVolume(volumeName); err != nil { - return "", errors.Wrap(err, "creating docker volume") - } - tarballPath := preload.TarballFilepath(k8sVersion) - - if err := ExtractTarballToVolume(tarballPath, volumeName, baseImage); err != nil { - // If the extraction didn't work, delete the corrupt docker volume - if err := deleteDockerVolume(volumeName); err != nil { - glog.Warningf("Corrupt docker volume %s was not deleted successfully. You may need to delete it manually via `docker volume rm %s` for minikube to continue to work.", volumeName, volumeName) - } - return "", errors.Wrap(err, "extracting tarball to volume") - } - return volumeName, nil -} - -// dockerVolumeExists returns true if a docker volume with the passed in name exists -func dockerVolumeExists(name string) bool { - if err := PointToHostDockerDaemon(); err != nil { - return false - } - cmd := exec.Command(Docker, "volume", "ls", "-q") - out, err := cmd.CombinedOutput() - if err != nil { - return false - } - names := strings.Split(string(out), "\n") - for _, n := range names { - if strings.TrimSpace(n) == name { - return true - } - } - return false -} - // ExtractTarballToVolume runs a docker image imageName which extracts the tarball at tarballPath // to the volume named volumeName func ExtractTarballToVolume(tarballPath, volumeName, imageName string) error { @@ -179,18 +130,6 @@ func createDockerVolume(name string) error { return nil } -// deleteDockerVolume deletes a docker volume with the given name -func deleteDockerVolume(name string) error { - if err := PointToHostDockerDaemon(); err != nil { - return errors.Wrap(err, "point host docker-daemon") - } - cmd := exec.Command(Docker, "volume", "rm", name) - if out, err := cmd.CombinedOutput(); err != nil { - return errors.Wrapf(err, "output %s", string(out)) - } - return nil -} - func preloadedVolumeName(k8sVersion, profile string) string { return fmt.Sprintf("k8s-%s-%s", k8sVersion, profile) } From 095b4b4596cd7808b991f7757c12c09d42aa5053 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 13:51:44 -0800 Subject: [PATCH 19/28] small fixes --- Makefile | 6 ++++++ pkg/minikube/bootstrapper/kubeadm/kubeadm.go | 8 ++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 374844ab34..413a52843d 100755 --- a/Makefile +++ b/Makefile @@ -365,8 +365,14 @@ out/linters/golangci-lint-$(GOLINT_VERSION): # this one is meant for local use .PHONY: lint +ifeq ($(MINIKUBE_BUILD_IN_DOCKER),y) +lint: pkg/minikube/assets/assets.go pkg/minikube/translate/translations.go + docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:$(GOLINT_VERSION) \ + golangci-lint run ${GOLINT_OPTIONS} --skip-dirs "cmd/drivers/kvm|cmd/drivers/hyperkit|pkg/drivers/kvm|pkg/drivers/hyperkit" ./... +else lint: pkg/minikube/assets/assets.go pkg/minikube/translate/translations.go out/linters/golangci-lint-$(GOLINT_VERSION) ## Run lint ./out/linters/golangci-lint-$(GOLINT_VERSION) run ${GOLINT_OPTIONS} ./... +endif # lint-ci is slower version of lint and is meant to be used in ci (travis) to avoid out of memory leaks. .PHONY: lint-ci diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go index 0f229901cf..59573563b3 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go @@ -427,12 +427,8 @@ func (k *Bootstrapper) UpdateCluster(cfg config.ClusterConfig) error { glog.Warningf("unable to stop kubelet: %s command: %q output: %q", err, rr.Command(), rr.Output()) } - // Skip transfer if using preloaded kic volume - skipTransfer := oci.PreloadedVolumeAttached(cfg.KubernetesConfig.KubernetesVersion, viper.GetString(config.MachineProfile)) - if !skipTransfer { - if err := bsutil.TransferBinaries(cfg.KubernetesConfig, k.c); err != nil { - return errors.Wrap(err, "downloading binaries") - } + if err := bsutil.TransferBinaries(cfg.KubernetesConfig, k.c); err != nil { + return errors.Wrap(err, "downloading binaries") } var cniFile []byte From 742b3e44d6be079bebd9fef68765d60acd4df365 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 13:53:23 -0800 Subject: [PATCH 20/28] remove transfer binaries --- hack/preload-images/preload_images.go | 37 --------------------------- 1 file changed, 37 deletions(-) diff --git a/hack/preload-images/preload_images.go b/hack/preload-images/preload_images.go index 2c1c979bf6..54beb2da7c 100644 --- a/hack/preload-images/preload_images.go +++ b/hack/preload-images/preload_images.go @@ -21,20 +21,14 @@ import ( "fmt" "os" "os/exec" - "path" "path/filepath" - "runtime" "github.com/pkg/errors" - "golang.org/x/sync/errgroup" "k8s.io/minikube/pkg/drivers/kic" "k8s.io/minikube/pkg/drivers/kic/oci" "k8s.io/minikube/pkg/minikube/bootstrapper/images" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/localpath" - "k8s.io/minikube/pkg/minikube/machine" - "k8s.io/minikube/pkg/minikube/vmpath" ) const ( @@ -103,37 +97,6 @@ func executePreloadImages() error { return errors.Wrapf(err, "downloading %s", img) } } - // Transfer in binaries - var g errgroup.Group - dir := filepath.Join(vmpath.GuestPersistentDir, "binaries") - mkdirCmd := exec.Command("docker", "exec", profile, "mkdir", "-p", dir) - if err := mkdirCmd.Run(); err != nil { - return err - } - - for _, name := range constants.KubernetesReleaseBinaries { - name := name - g.Go(func() error { - src, err := machine.CacheBinary(name, kubernetesVersion, "linux", runtime.GOARCH) - if err != nil { - return errors.Wrapf(err, "downloading %s", name) - } - - dst := path.Join(dir, name) - copyCmd := exec.Command("docker", "cp", src, fmt.Sprintf("%s:%s", profile, dst)) - copyCmd.Stdout = os.Stdout - copyCmd.Stderr = os.Stderr - fmt.Println(copyCmd.Args) - if err := copyCmd.Run(); err != nil { - return errors.Wrapf(err, "copybinary %s -> %s", src, dst) - } - return nil - }) - } - - if err := g.Wait(); err != nil { - return errors.Wrap(err, "pulling binaries") - } // Delete /var/lib/docker/network if err := deleteDirInMinikube("/var/lib/docker/network"); err != nil { From d6e94c1ee4a7ad5bc15d859b0a5f6a23afeb73ff Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 13:54:35 -0800 Subject: [PATCH 21/28] remove unused functions --- pkg/drivers/kic/oci/volumes.go | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/pkg/drivers/kic/oci/volumes.go b/pkg/drivers/kic/oci/volumes.go index 85fc80434f..ecba94cf15 100644 --- a/pkg/drivers/kic/oci/volumes.go +++ b/pkg/drivers/kic/oci/volumes.go @@ -129,31 +129,3 @@ func createDockerVolume(name string) error { } return nil } - -func preloadedVolumeName(k8sVersion, profile string) string { - return fmt.Sprintf("k8s-%s-%s", k8sVersion, profile) -} - -// PreloadedVolumeAttached returns true if the preloaded volume is attached -// to the running profile -func PreloadedVolumeAttached(k8sVersion, profile string) bool { - glog.Infof("Checking if preloaded volume is attached to %s", profile) - if err := PointToHostDockerDaemon(); err != nil { - glog.Infof("error pointing host to docker daemon: %v", err) - return false - } - volumeName := preloadedVolumeName(k8sVersion, profile) - cmd := exec.Command(Docker, "inspect", "-f", "{{range .Mounts}} {{.Name}} {{end}}", profile) - out, err := cmd.CombinedOutput() - if err != nil { - glog.Infof("error inspecting mounted volumes: %v", err) - return false - } - vols := strings.Split(string(out), " ") - for _, v := range vols { - if strings.TrimSpace(v) == volumeName { - return true - } - } - return false -} From 481010d8dc6519b441bddb94079f2f2b0870cf44 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 14:10:41 -0800 Subject: [PATCH 22/28] added overlay2 to preloaded images tarball name --- Makefile | 4 ++-- hack/preload-images/preload_images.go | 27 ++++++++++++++++++++++++--- pkg/drivers/kic/kic.go | 5 +++-- pkg/minikube/preload/preload.go | 4 ++-- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 413a52843d..fe6301b0b9 100755 --- a/Makefile +++ b/Makefile @@ -524,8 +524,8 @@ kic-base-image: ## builds the base image used for kic. .PHONY: upload-preloaded-images-tar upload-preloaded-images-tar: generate-preloaded-images-tar # Upload the preloaded images tar to the GCS bucket. Specify a specific kubernetes version to build via `KUBERNETES_VERSION=vx.y.z make upload-preloaded-images-tar`. - gsutil cp out/preloaded-images-k8s-${KUBERNETES_VERSION}.tar.lz4 gs://${PRELOADED_VOLUMES_GCS_BUCKET} - gsutil acl ch -u AllUsers:R gs://${PRELOADED_VOLUMES_GCS_BUCKET}/preloaded-images-k8s-${KUBERNETES_VERSION}.tar.lz4 + gsutil cp out/preloaded-images-k8s-${KUBERNETES_VERSION}-overlay2.tar.lz4 gs://${PRELOADED_VOLUMES_GCS_BUCKET} + gsutil acl ch -u AllUsers:R gs://${PRELOADED_VOLUMES_GCS_BUCKET}/preloaded-images-k8s-${KUBERNETES_VERSION}-overlay2.tar.lz4 .PHONY: generate-preloaded-images-tar generate-preloaded-images-tar: diff --git a/hack/preload-images/preload_images.go b/hack/preload-images/preload_images.go index 54beb2da7c..916a4cd224 100644 --- a/hack/preload-images/preload_images.go +++ b/hack/preload-images/preload_images.go @@ -22,6 +22,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" "github.com/pkg/errors" "k8s.io/minikube/pkg/drivers/kic" @@ -37,17 +38,24 @@ const ( ) var ( - kubernetesVersion = "" - tarballFilename = "" + kubernetesVersion = "" + tarballFilename = "" + dockerStorageDriver = "" ) func init() { flag.StringVar(&kubernetesVersion, "kubernetes-version", "", "desired kubernetes version, for example `v1.17.2`") + flag.StringVar(&dockerStorageDriver, "docker-storage-driver", "overlay2", "docker storage driver backend") + flag.Parse() - tarballFilename = fmt.Sprintf("preloaded-images-k8s-%s.tar.lz4", kubernetesVersion) + tarballFilename = fmt.Sprintf("preloaded-images-k8s-%s-%s.tar.lz4", kubernetesVersion, dockerStorageDriver) } func main() { + if err := verifyDockerStorage(); err != nil { + fmt.Println(err) + os.Exit(1) + } if err := executePreloadImages(); err != nil { fmt.Println(err) os.Exit(1) @@ -143,3 +151,16 @@ func deleteMinikube() error { cmd.Stdout = os.Stdout return cmd.Run() } + +func verifyDockerStorage() error { + cmd := exec.Command("docker", "info", "-f", "{{.Info.Driver}}") + output, err := cmd.Output() + if err != nil { + return err + } + driver := strings.Trim(string(output), " \n") + if driver != dockerStorageDriver { + return fmt.Errorf("docker storage driver %s does not match requested %s", driver, dockerStorageDriver) + } + return nil +} diff --git a/pkg/drivers/kic/kic.go b/pkg/drivers/kic/kic.go index 46b348fb86..9195845711 100644 --- a/pkg/drivers/kic/kic.go +++ b/pkg/drivers/kic/kic.go @@ -103,9 +103,10 @@ func (d *Driver) Create() error { glog.Infof("Starting extracting preloaded images to volume") // Extract preloaded images to container if err := oci.ExtractTarballToVolume(preload.TarballFilepath(d.NodeConfig.KubernetesVersion), params.Name, BaseImage); err != nil { - return errors.Wrap(err, "extracting tarball to volume") + glog.Infof("Unable to extract preloaded tarball to volume: %v", err) + } else { + glog.Infof("Took %f seconds to extract preloaded images to volume", time.Since(t).Seconds()) } - glog.Infof("Took %f seconds to extract preloaded images to volume", time.Since(t).Seconds()) return nil } diff --git a/pkg/minikube/preload/preload.go b/pkg/minikube/preload/preload.go index bcc1c50a1d..a8a379998b 100644 --- a/pkg/minikube/preload/preload.go +++ b/pkg/minikube/preload/preload.go @@ -39,12 +39,12 @@ import ( // returns name of the tarball func tarballName(k8sVersion string) string { - return fmt.Sprintf("preloaded-images-k8s-%s.tar.lz4", k8sVersion) + return fmt.Sprintf("preloaded-images-k8s-%s-overlay2.tar.lz4", k8sVersion) } // returns the name of the checksum file func checksumName(k8sVersion string) string { - return fmt.Sprintf("preloaded-images-k8s-%s.tar.lz4.checksum", k8sVersion) + return fmt.Sprintf("preloaded-images-k8s-%s-overlay2.tar.lz4.checksum", k8sVersion) } // returns target dir for all cached items related to preloading From 4996b14a28bdea2454182e706a4ea8ad8d446ce2 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 15:10:40 -0800 Subject: [PATCH 23/28] check for preloaded images before loading again --- hack/preload-images/preload_images.go | 2 +- pkg/minikube/machine/cache_images.go | 30 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/hack/preload-images/preload_images.go b/hack/preload-images/preload_images.go index 916a4cd224..cb726a26cd 100644 --- a/hack/preload-images/preload_images.go +++ b/hack/preload-images/preload_images.go @@ -119,7 +119,7 @@ func executePreloadImages() error { } func createImageTarball() error { - cmd := exec.Command("docker", "exec", profile, "sudo", "tar", "-I", "lz4", "-C", "/var/lib/docker", "-cvf", tarballFilename, "./") + cmd := exec.Command("docker", "exec", profile, "sudo", "tar", "-I", "lz4", "-C", "/var", "-cvf", tarballFilename, "./lib/docker") cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { return errors.Wrap(err, "creating image tarball") diff --git a/pkg/minikube/machine/cache_images.go b/pkg/minikube/machine/cache_images.go index 301c3b02fd..b35ab79692 100644 --- a/pkg/minikube/machine/cache_images.go +++ b/pkg/minikube/machine/cache_images.go @@ -19,8 +19,10 @@ package machine import ( "fmt" "os" + "os/exec" "path" "path/filepath" + "strings" "sync" "time" @@ -62,6 +64,12 @@ func CacheImagesForBootstrapper(imageRepository string, version string, clusterB // LoadImages loads previously cached images into the container runtime func LoadImages(cc *config.ClusterConfig, runner command.Runner, images []string, cacheDir string) error { + // Skip loading images if images already exist + if imagesPreloaded(runner, images) { + glog.Infof("Images are preloaded, skipping loading") + return nil + } + glog.Infof("LoadImages start: %s", images) start := time.Now() @@ -99,6 +107,28 @@ func LoadImages(cc *config.ClusterConfig, runner command.Runner, images []string return nil } +func imagesPreloaded(runner command.Runner, images []string) bool { + rr, err := runner.RunCmd(exec.Command("docker", "images", "--format", "{{.Repository}}:{{.Tag}}")) + if err != nil { + return false + } + preloadedImages := map[string]struct{}{} + for _, i := range strings.Split(rr.Stdout.String(), "\n") { + preloadedImages[i] = struct{}{} + } + + glog.Infof("Got preloaded images: %s", rr.Output()) + + // Make sure images == imgs + for _, i := range images { + if _, ok := preloadedImages[i]; !ok { + glog.Infof("%s wasn't preloaded", i) + return false + } + } + return true +} + // needsTransfer returns an error if an image needs to be retransfered func needsTransfer(imgClient *client.Client, imgName string, cr cruntime.Manager) error { imgDgst := "" // for instance sha256:7c92a2c6bbcb6b6beff92d0a940779769c2477b807c202954c537e2e0deb9bed From 05a46f652600a16eb66451d13cef5934d7a6fbb9 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 16:17:35 -0800 Subject: [PATCH 24/28] remove unnecessary PreloadedVolume --- pkg/drivers/kic/oci/types.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/pkg/drivers/kic/oci/types.go b/pkg/drivers/kic/oci/types.go index 22faa85715..d2b6eca033 100644 --- a/pkg/drivers/kic/oci/types.go +++ b/pkg/drivers/kic/oci/types.go @@ -33,19 +33,18 @@ const ( // CreateParams are parameters needed to create a container type CreateParams struct { - Name string // used for container name and hostname - Image string // container image to use to create the node. - ClusterLabel string // label the containers we create using minikube so we can clean up - Role string // currently only role supported is control-plane - Mounts []Mount // volume mounts - APIServerPort int // kubernetes api server port - PortMappings []PortMapping // ports to map to container from host - CPUs string // number of cpu cores assign to container - Memory string // memory (mbs) to assign to the container - Envs map[string]string // environment variables to pass to the container - ExtraArgs []string // a list of any extra option to pass to oci binary during creation time, for example --expose 8080... - OCIBinary string // docker or podman - PreloadedVolume string // volume of preloaded images + Name string // used for container name and hostname + Image string // container image to use to create the node. + ClusterLabel string // label the containers we create using minikube so we can clean up + Role string // currently only role supported is control-plane + Mounts []Mount // volume mounts + APIServerPort int // kubernetes api server port + PortMappings []PortMapping // ports to map to container from host + CPUs string // number of cpu cores assign to container + Memory string // memory (mbs) to assign to the container + Envs map[string]string // environment variables to pass to the container + ExtraArgs []string // a list of any extra option to pass to oci binary during creation time, for example --expose 8080... + OCIBinary string // docker or podman } // createOpt is an option for Create From 26de14676d0f99221900bf7eb811058f52fee68b Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 16:54:05 -0800 Subject: [PATCH 25/28] Add integration test for docker download only --- pkg/minikube/preload/preload.go | 8 ++-- test/integration/aaa_download_only_test.go | 56 ++++++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/pkg/minikube/preload/preload.go b/pkg/minikube/preload/preload.go index a8a379998b..871c2087e7 100644 --- a/pkg/minikube/preload/preload.go +++ b/pkg/minikube/preload/preload.go @@ -52,8 +52,8 @@ func targetDir() string { return localpath.MakeMiniPath("cache", "preloaded-tarball") } -// returns path to checksum file -func checksumFilepath(k8sVersion string) string { +// ChecksumFilepath returns path to checksum file +func ChecksumFilepath(k8sVersion string) string { return path.Join(targetDir(), checksumName(k8sVersion)) } @@ -123,7 +123,7 @@ func saveChecksumFile(k8sVersion string) error { return errors.Wrap(err, "getting storage object") } checksum := attrs.MD5 - return ioutil.WriteFile(checksumFilepath(k8sVersion), checksum, 0644) + return ioutil.WriteFile(ChecksumFilepath(k8sVersion), checksum, 0644) } // verifyChecksum returns true if the checksum of the local binary matches @@ -136,7 +136,7 @@ func verifyChecksum(k8sVersion string) error { } checksum := md5.Sum(contents) - remoteChecksum, err := ioutil.ReadFile(checksumFilepath(k8sVersion)) + remoteChecksum, err := ioutil.ReadFile(ChecksumFilepath(k8sVersion)) if err != nil { return errors.Wrap(err, "reading checksum file") } diff --git a/test/integration/aaa_download_only_test.go b/test/integration/aaa_download_only_test.go index e0bf1fba32..7e2f5777c8 100644 --- a/test/integration/aaa_download_only_test.go +++ b/test/integration/aaa_download_only_test.go @@ -20,19 +20,26 @@ package integration import ( "context" + "crypto/md5" "encoding/json" "fmt" + "io/ioutil" "os" "os/exec" "path/filepath" "runtime" "strings" "testing" + "time" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/daemon" + "k8s.io/minikube/pkg/drivers/kic" "k8s.io/minikube/pkg/minikube/bootstrapper/images" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/preload" ) func TestDownloadOnly(t *testing.T) { @@ -160,3 +167,52 @@ func TestDownloadOnly(t *testing.T) { }) } +func TestDownloadOnlyDocker(t *testing.T) { + if !runningDockerDriver(StartArgs()) { + t.Skip("this test only runs with the docker driver") + } + + profile := UniqueProfileName("download-docker") + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) + defer Cleanup(t, profile, cancel) + + args := []string{"start", "--download-only", "-p", profile, "--force", "--alsologtostderr", "--vm-driver=docker"} + rr, err := Run(t, exec.CommandContext(ctx, Target(), args...)) + if err != nil { + t.Errorf("%s failed: %v:\n%s", args, err, rr.Output()) + } + + // Make sure the preloaded image tarball exists + tarball := preload.TarballFilepath(constants.DefaultKubernetesVersion) + contents, err := ioutil.ReadFile(tarball) + if err != nil { + t.Errorf("reading tarball: %v", err) + } + // Make sure it has the correct checksum + checksum := md5.Sum(contents) + remoteChecksum, err := ioutil.ReadFile(preload.ChecksumFilepath(constants.DefaultKubernetesVersion)) + if err != nil { + t.Errorf("reading checksum file: %v", err) + } + if string(remoteChecksum) != string(checksum[:]) { + t.Errorf("checksum of %s does not match remote checksum (%s != %s)", tarball, string(remoteChecksum), string(checksum[:])) + } + + // Make sure this image exists in the docker daemon + ref, err := name.ParseReference(kic.BaseImage) + if err != nil { + t.Errorf("parsing reference failed: %v", err) + } + if _, err := daemon.Image(ref); err != nil { + t.Errorf("expected image does not exist in local daemon: %v", err) + } +} + +func runningDockerDriver(startArgs []string) bool { + for _, s := range startArgs { + if s == "--vm-driver=docker" { + return true + } + } + return false +} From 59615132a8de571bbac585ca830093d27d258d48 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 17:04:50 -0800 Subject: [PATCH 26/28] Include container runtime and tarball version in preloaded tarball name --- Makefile | 7 ++++--- hack/preload-images/preload_images.go | 12 ++++++++---- pkg/minikube/preload/constants.go | 22 ++++++++++++++++++++++ pkg/minikube/preload/preload.go | 4 ++-- 4 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 pkg/minikube/preload/constants.go diff --git a/Makefile b/Makefile index fe6301b0b9..3933f3ac34 100755 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ VERSION ?= v$(RAW_VERSION) KUBERNETES_VERSION ?= $(shell egrep "DefaultKubernetesVersion =" pkg/minikube/constants/constants.go | cut -d \" -f2) KIC_VERSION ?= $(shell egrep "Version =" pkg/drivers/kic/types.go | cut -d \" -f2) +PRELOADED_TARBALL_VERSION ?= $(shell egrep "Version =" pkg/minikube/preload/constants.go | cut -d \" -f2) PRELOADED_VOLUMES_GCS_BUCKET ?= $(shell egrep "PreloadedVolumeTarballsBucket =" pkg/minikube/constants/constants.go | cut -d \" -f2) # Default to .0 for higher cache hit rates, as build increments typically don't require new ISO versions @@ -524,12 +525,12 @@ kic-base-image: ## builds the base image used for kic. .PHONY: upload-preloaded-images-tar upload-preloaded-images-tar: generate-preloaded-images-tar # Upload the preloaded images tar to the GCS bucket. Specify a specific kubernetes version to build via `KUBERNETES_VERSION=vx.y.z make upload-preloaded-images-tar`. - gsutil cp out/preloaded-images-k8s-${KUBERNETES_VERSION}-overlay2.tar.lz4 gs://${PRELOADED_VOLUMES_GCS_BUCKET} - gsutil acl ch -u AllUsers:R gs://${PRELOADED_VOLUMES_GCS_BUCKET}/preloaded-images-k8s-${KUBERNETES_VERSION}-overlay2.tar.lz4 + gsutil cp out/preloaded-images-k8s-${PRELOADED_TARBALL_VERSION}-${KUBERNETES_VERSION}-docker-overlay2.tar.lz4 gs://${PRELOADED_VOLUMES_GCS_BUCKET} + gsutil acl ch -u AllUsers:R gs://${PRELOADED_VOLUMES_GCS_BUCKET}/preloaded-images-k8s-${PRELOADED_TARBALL_VERSION}-${KUBERNETES_VERSION}-docker-overlay2.tar.lz4 .PHONY: generate-preloaded-images-tar generate-preloaded-images-tar: - go run ./hack/preload-images/preload_images.go -kubernetes-version ${KUBERNETES_VERSION} + go run ./hack/preload-images/preload_images.go -kubernetes-version ${KUBERNETES_VERSION} -preloaded-tarball-version ${PRELOADED_TARBALL_VERSION} .PHONY: push-storage-provisioner-image diff --git a/hack/preload-images/preload_images.go b/hack/preload-images/preload_images.go index cb726a26cd..a0444f2e26 100644 --- a/hack/preload-images/preload_images.go +++ b/hack/preload-images/preload_images.go @@ -38,17 +38,21 @@ const ( ) var ( - kubernetesVersion = "" - tarballFilename = "" - dockerStorageDriver = "" + kubernetesVersion = "" + tarballFilename = "" + dockerStorageDriver = "" + preloadedTarballVersion = "" + containerRuntime = "" ) func init() { flag.StringVar(&kubernetesVersion, "kubernetes-version", "", "desired kubernetes version, for example `v1.17.2`") flag.StringVar(&dockerStorageDriver, "docker-storage-driver", "overlay2", "docker storage driver backend") + flag.StringVar(&preloadedTarballVersion, "preloaded-tarball-version", "", "preloaded tarball version") + flag.StringVar(&containerRuntime, "container-runtime", "docker", "container runtime") flag.Parse() - tarballFilename = fmt.Sprintf("preloaded-images-k8s-%s-%s.tar.lz4", kubernetesVersion, dockerStorageDriver) + tarballFilename = fmt.Sprintf("preloaded-images-k8s-%s-%s-%s-%s.tar.lz4", preloadedTarballVersion, kubernetesVersion, containerRuntime, dockerStorageDriver) } func main() { diff --git a/pkg/minikube/preload/constants.go b/pkg/minikube/preload/constants.go new file mode 100644 index 0000000000..ca09325e37 --- /dev/null +++ b/pkg/minikube/preload/constants.go @@ -0,0 +1,22 @@ +/* +Copyright 2020 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 preload + +const ( + // Version is the current version of the preloaded tarball + Version = "v1" +) diff --git a/pkg/minikube/preload/preload.go b/pkg/minikube/preload/preload.go index 871c2087e7..703f5bd39e 100644 --- a/pkg/minikube/preload/preload.go +++ b/pkg/minikube/preload/preload.go @@ -39,12 +39,12 @@ import ( // returns name of the tarball func tarballName(k8sVersion string) string { - return fmt.Sprintf("preloaded-images-k8s-%s-overlay2.tar.lz4", k8sVersion) + return fmt.Sprintf("preloaded-images-k8s-%s-%s-docker-overlay2.tar.lz4", Version, k8sVersion) } // returns the name of the checksum file func checksumName(k8sVersion string) string { - return fmt.Sprintf("preloaded-images-k8s-%s-overlay2.tar.lz4.checksum", k8sVersion) + return fmt.Sprintf("%s.checksum", tarballName(k8sVersion)) } // returns target dir for all cached items related to preloading From 3d291fd69e4498887a3966bd9bd8683ea2fb1cac Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 17:20:31 -0800 Subject: [PATCH 27/28] Only add necessary directories to tarball --- hack/preload-images/preload_images.go | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/hack/preload-images/preload_images.go b/hack/preload-images/preload_images.go index a0444f2e26..92d4d86f04 100644 --- a/hack/preload-images/preload_images.go +++ b/hack/preload-images/preload_images.go @@ -110,11 +110,6 @@ func executePreloadImages() error { } } - // Delete /var/lib/docker/network - if err := deleteDirInMinikube("/var/lib/docker/network"); err != nil { - return errors.Wrap(err, "deleting dir") - } - // Create image tarball if err := createImageTarball(); err != nil { return err @@ -123,7 +118,13 @@ func executePreloadImages() error { } func createImageTarball() error { - cmd := exec.Command("docker", "exec", profile, "sudo", "tar", "-I", "lz4", "-C", "/var", "-cvf", tarballFilename, "./lib/docker") + dirs := []string{ + fmt.Sprintf("./lib/docker/%s", dockerStorageDriver), + "./lib/docker/image", + } + args := []string{"exec", profile, "sudo", "tar", "-I", "lz4", "-C", "/var", "-cvf", tarballFilename} + args = append(args, dirs...) + cmd := exec.Command("docker", args...) cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { return errors.Wrap(err, "creating image tarball") @@ -131,15 +132,6 @@ func createImageTarball() error { return nil } -func deleteDirInMinikube(dir string) error { - cmd := exec.Command("docker", "exec", profile, "sudo", "rm", "-rf", dir) - cmd.Stdout = os.Stdout - if err := cmd.Run(); err != nil { - return errors.Wrapf(err, "deleting %s", dir) - } - return nil -} - func copyTarballToHost() error { dest := filepath.Join("out/", tarballFilename) cmd := exec.Command("docker", "cp", fmt.Sprintf("%s:/%s", profile, tarballFilename), dest) From 5d1628113a97e4fae56ec308645845c1db0a2139 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 2 Mar 2020 17:30:26 -0800 Subject: [PATCH 28/28] use docker command line to check if image exists, since it is much faster than go-containerregistry --- pkg/minikube/image/image.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/minikube/image/image.go b/pkg/minikube/image/image.go index c393e95264..a14ad85f71 100644 --- a/pkg/minikube/image/image.go +++ b/pkg/minikube/image/image.go @@ -20,6 +20,7 @@ import ( "context" "io/ioutil" "os" + "os/exec" "path/filepath" "runtime" "strings" @@ -86,14 +87,18 @@ func WriteImageToDaemon(img string) error { return errors.Wrap(err, "point host docker-daemon") } // Check if image exists locally + cmd := exec.Command("docker", "images", "--format", "{{.Repository}}:{{.Tag}}@{{.Digest}}") + if output, err := cmd.Output(); err == nil { + if strings.Contains(string(output), img) { + glog.Infof("Found %s in local docker daemon, skipping pull", img) + return nil + } + } + // Else, pull it ref, err := name.ParseReference(img) if err != nil { return errors.Wrap(err, "parsing reference") } - if _, err := daemon.Image(ref); err == nil { - glog.Infof("Found %s in local docker daemon, skipping pull", img) - return nil - } i, err := remote.Image(ref) if err != nil { return errors.Wrap(err, "getting remote image")