Pass driverName into Preload and PreloadExists to report no preload for BareMetal.

pull/11528/head
Andriy Dzikh 2021-05-27 14:25:26 -07:00
parent a8136ec322
commit f9f3ef9a09
16 changed files with 46 additions and 353 deletions

View File

@ -86,7 +86,8 @@ out:
if *limit > 0 && i >= *limit {
break out
}
if !download.PreloadExists(kv, cr) {
// Since none/mock are the only exceptions, it does not matter what driver we choose.
if !download.PreloadExists(kv, cr, "docker") {
toGenerate = append(toGenerate, preloadCfg{kv, cr})
i++
fmt.Printf("[%d] A preloaded tarball for k8s version %s - runtime %q does not exist.\n", i, kv, cr)

View File

@ -172,7 +172,7 @@ func (d *Driver) Create() error {
go func() {
defer waitForPreload.Done()
// If preload doesn't exist, don't bother extracting tarball to volume
if !download.PreloadExists(d.NodeConfig.KubernetesVersion, d.NodeConfig.ContainerRuntime) {
if !download.PreloadExists(d.NodeConfig.KubernetesVersion, d.NodeConfig.ContainerRuntime, d.DriverName()) {
return
}
t := time.Now()

View File

@ -866,7 +866,7 @@ func (k *Bootstrapper) UpdateCluster(cfg config.ClusterConfig) error {
return errors.Wrap(err, "runtime")
}
if err := r.Preload(cfg.KubernetesConfig); err != nil {
if err := r.Preload(cfg); err != nil {
klog.Infof("preload failed, will try to load cached images: %v", err)
}

View File

@ -464,16 +464,16 @@ func (r *Containerd) SystemLogCmd(len int) string {
}
// Preload preloads the container runtime with k8s images
func (r *Containerd) Preload(cfg config.KubernetesConfig) error {
if !download.PreloadExists(cfg.KubernetesVersion, cfg.ContainerRuntime) {
func (r *Containerd) Preload(cc config.ClusterConfig) error {
if !download.PreloadExists(cc.KubernetesConfig.KubernetesVersion, cc.KubernetesConfig.ContainerRuntime, cc.Driver) {
return nil
}
k8sVersion := cfg.KubernetesVersion
cRuntime := cfg.ContainerRuntime
k8sVersion := cc.KubernetesConfig.KubernetesVersion
cRuntime := cc.KubernetesConfig.ContainerRuntime
// If images already exist, return
images, err := images.Kubeadm(cfg.ImageRepository, k8sVersion)
images, err := images.Kubeadm(cc.KubernetesConfig.ImageRepository, k8sVersion)
if err != nil {
return errors.Wrap(err, "getting images")
}

View File

@ -316,16 +316,16 @@ func (r *CRIO) SystemLogCmd(len int) string {
}
// Preload preloads the container runtime with k8s images
func (r *CRIO) Preload(cfg config.KubernetesConfig) error {
if !download.PreloadExists(cfg.KubernetesVersion, cfg.ContainerRuntime) {
func (r *CRIO) Preload(cc config.ClusterConfig) error {
if !download.PreloadExists(cc.KubernetesConfig.KubernetesVersion, cc.KubernetesConfig.ContainerRuntime, cc.Driver) {
return nil
}
k8sVersion := cfg.KubernetesVersion
cRuntime := cfg.ContainerRuntime
k8sVersion := cc.KubernetesConfig.KubernetesVersion
cRuntime := cc.KubernetesConfig.ContainerRuntime
// If images already exist, return
images, err := images.Kubeadm(cfg.ImageRepository, k8sVersion)
images, err := images.Kubeadm(cc.KubernetesConfig.ImageRepository, k8sVersion)
if err != nil {
return errors.Wrap(err, "getting images")
}

View File

@ -125,7 +125,7 @@ type Manager interface {
// SystemLogCmd returns the command to return the system logs
SystemLogCmd(int) string
// Preload preloads the container runtime with k8s images
Preload(config.KubernetesConfig) error
Preload(config.ClusterConfig) error
// ImagesPreloaded returns true if all images have been preloaded
ImagesPreloaded([]string) bool
}

View File

@ -449,15 +449,15 @@ func (r *Docker) forceSystemd() error {
// 1. Copy over the preloaded tarball into the VM
// 2. Extract the preloaded tarball to the correct directory
// 3. Remove the tarball within the VM
func (r *Docker) Preload(cfg config.KubernetesConfig) error {
if !download.PreloadExists(cfg.KubernetesVersion, cfg.ContainerRuntime) {
func (r *Docker) Preload(cc config.ClusterConfig) error {
if !download.PreloadExists(cc.KubernetesConfig.KubernetesVersion, cc.KubernetesConfig.ContainerRuntime, cc.Driver) {
return nil
}
k8sVersion := cfg.KubernetesVersion
cRuntime := cfg.ContainerRuntime
k8sVersion := cc.KubernetesConfig.KubernetesVersion
cRuntime := cc.KubernetesConfig.ContainerRuntime
// If images already exist, return
images, err := images.Kubeadm(cfg.ImageRepository, k8sVersion)
images, err := images.Kubeadm(cc.KubernetesConfig.ImageRepository, k8sVersion)
if err != nil {
return errors.Wrap(err, "getting images")
}

View File

@ -86,14 +86,14 @@ func testPreloadDownloadPreventsMultipleDownload(t *testing.T) {
}
return nil, nil
}
checkPreloadExists = func(k8sVersion, containerRuntime string, forcePreload ...bool) bool { return true }
checkPreloadExists = func(k8sVersion, containerRuntime, driverName string, forcePreload ...bool) bool { return true }
getChecksum = func(k8sVersion, containerRuntime string) ([]byte, error) { return []byte("check"), nil }
ensureChecksumValid = func(k8sVersion, containerRuntime, path string, checksum []byte) error { return nil }
var group sync.WaitGroup
group.Add(2)
dlCall := func() {
if err := Preload(constants.DefaultKubernetesVersion, constants.DefaultContainerRuntime); err != nil {
if err := Preload(constants.DefaultKubernetesVersion, constants.DefaultContainerRuntime, "docker"); err != nil {
t.Logf("Failed to download preload: %+v (may be ok)", err)
}
group.Done()
@ -114,11 +114,11 @@ func testPreloadNotExists(t *testing.T) {
DownloadMock = mockSleepDownload(&downloadNum)
checkCache = func(file string) (fs.FileInfo, error) { return nil, fmt.Errorf("cache not found") }
checkPreloadExists = func(k8sVersion, containerRuntime string, forcePreload ...bool) bool { return false }
checkPreloadExists = func(k8sVersion, containerRuntime, driverName string, forcePreload ...bool) bool { return false }
getChecksum = func(k8sVersion, containerRuntime string) ([]byte, error) { return []byte("check"), nil }
ensureChecksumValid = func(k8sVersion, containerRuntime, path string, checksum []byte) error { return nil }
err := Preload(constants.DefaultKubernetesVersion, constants.DefaultContainerRuntime)
err := Preload(constants.DefaultKubernetesVersion, constants.DefaultContainerRuntime, "docker")
if err != nil {
t.Errorf("Expected no error when preload exists")
}
@ -133,13 +133,13 @@ func testPreloadChecksumMismatch(t *testing.T) {
DownloadMock = mockSleepDownload(&downloadNum)
checkCache = func(file string) (fs.FileInfo, error) { return nil, fmt.Errorf("cache not found") }
checkPreloadExists = func(k8sVersion, containerRuntime string, forcePreload ...bool) bool { return true }
checkPreloadExists = func(k8sVersion, containerRuntime, driverName string, forcePreload ...bool) bool { return true }
getChecksum = func(k8sVersion, containerRuntime string) ([]byte, error) { return []byte("check"), nil }
ensureChecksumValid = func(k8sVersion, containerRuntime, path string, checksum []byte) error {
return fmt.Errorf("checksum mismatch")
}
err := Preload(constants.DefaultKubernetesVersion, constants.DefaultContainerRuntime)
err := Preload(constants.DefaultKubernetesVersion, constants.DefaultContainerRuntime, "docker")
expectedErrMsg := "checksum mismatch"
if err == nil {
t.Errorf("Expected error when checksum mismatches")

View File

@ -33,6 +33,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/viper"
"k8s.io/klog/v2"
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
@ -98,7 +99,7 @@ func remoteTarballURL(k8sVersion, containerRuntime string) string {
}
// PreloadExists returns true if there is a preloaded tarball that can be used
func PreloadExists(k8sVersion, containerRuntime string, forcePreload ...bool) bool {
func PreloadExists(k8sVersion, containerRuntime, driverName string, forcePreload ...bool) bool {
// TODO (#8166): Get rid of the need for this and viper at all
force := false
if len(forcePreload) > 0 {
@ -107,7 +108,9 @@ func PreloadExists(k8sVersion, containerRuntime string, forcePreload ...bool) bo
// TODO: debug why this func is being called two times
klog.Infof("Checking if preload exists for k8s version %s and runtime %s", k8sVersion, containerRuntime)
if !viper.GetBool("preload") && !force {
// If `driverName` is BareMetal, there is no preload. Note: some uses of
// `PreloadExists` assume that the driver is irrelevant unless BareMetal.
if driver.BareMetal(driverName) || !viper.GetBool("preload") && !force {
return false
}
@ -147,7 +150,7 @@ func PreloadExists(k8sVersion, containerRuntime string, forcePreload ...bool) bo
var checkPreloadExists = PreloadExists
// Preload caches the preloaded images tarball on the host machine
func Preload(k8sVersion, containerRuntime string) error {
func Preload(k8sVersion, containerRuntime, driverName string) error {
targetPath := TarballPath(k8sVersion, containerRuntime)
targetLock := targetPath + ".lock"
@ -165,7 +168,7 @@ func Preload(k8sVersion, containerRuntime string) error {
}
// Make sure we support this k8s version
if !checkPreloadExists(k8sVersion, containerRuntime) {
if !checkPreloadExists(k8sVersion, containerRuntime, driverName) {
klog.Infof("Preloaded tarball for k8s version %s does not exist", k8sVersion)
return nil
}

View File

@ -1,164 +0,0 @@
/*
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 driver
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/blang/semver"
"github.com/juju/mutex"
"github.com/pkg/errors"
"k8s.io/klog/v2"
"k8s.io/minikube/pkg/minikube/download"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/lock"
)
// InstallOrUpdate downloads driver if it is not present, or updates it if there's a newer version
func InstallOrUpdate(name string, directory string, v semver.Version, interactive bool, autoUpdate bool) error {
if name != KVM2 && name != HyperKit {
return nil
}
executable := fmt.Sprintf("docker-machine-driver-%s", name)
// Lock before we check for existence to avoid thundering herd issues
spec := lock.PathMutexSpec(executable)
spec.Timeout = 10 * time.Minute
klog.Infof("acquiring lock: %+v", spec)
releaser, err := mutex.Acquire(spec)
if err != nil {
return errors.Wrapf(err, "unable to acquire lock for %+v", spec)
}
defer releaser.Release()
exists := driverExists(executable)
path, err := validateDriver(executable, minAcceptableDriverVersion(name, v))
if !exists || (err != nil && autoUpdate) {
klog.Warningf("%s: %v", executable, err)
path = filepath.Join(directory, executable)
if err := download.Driver(executable, path, v); err != nil {
return err
}
}
return fixDriverPermissions(name, path, interactive)
}
// fixDriverPermissions fixes the permissions on a driver
func fixDriverPermissions(name string, path string, interactive bool) error {
if name != HyperKit {
return nil
}
// Using the find command for hyperkit is far easier than cross-platform uid checks in Go.
stdout, err := exec.Command("find", path, "-uid", "0", "-perm", "4755").Output()
klog.Infof("stdout: %s", stdout)
if err == nil && strings.TrimSpace(string(stdout)) == path {
klog.Infof("%s looks good", path)
return nil
}
cmds := []*exec.Cmd{
exec.Command("sudo", "chown", "root:wheel", path),
exec.Command("sudo", "chmod", "u+s", path),
}
var example strings.Builder
for _, c := range cmds {
example.WriteString(fmt.Sprintf(" $ %s \n", strings.Join(c.Args, " ")))
}
out.Styled(style.Permissions, "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\n\n{{ .example }}\n", out.V{"driver": name, "example": example.String()})
for _, c := range cmds {
testArgs := append([]string{"-n"}, c.Args[1:]...)
test := exec.Command("sudo", testArgs...)
klog.Infof("testing: %v", test.Args)
if err := test.Run(); err != nil {
klog.Infof("%v may require a password: %v", c.Args, err)
if !interactive {
return fmt.Errorf("%v requires a password, and --interactive=false", c.Args)
}
}
klog.Infof("running: %v", c.Args)
err := c.Run()
if err != nil {
return errors.Wrapf(err, "%v", c.Args)
}
}
return nil
}
// validateDriver validates if a driver appears to be up-to-date and installed properly
func validateDriver(executable string, v semver.Version) (string, error) {
klog.Infof("Validating %s, PATH=%s", executable, os.Getenv("PATH"))
path, err := exec.LookPath(executable)
if err != nil {
return path, err
}
output, err := exec.Command(path, "version").Output()
if err != nil {
return path, err
}
ev := extractDriverVersion(string(output))
if len(ev) == 0 {
return path, fmt.Errorf("%s: unable to extract version from %q", executable, output)
}
driverVersion, err := semver.Make(ev)
if err != nil {
return path, errors.Wrap(err, "can't parse driver version")
}
klog.Infof("%s version is %s", path, driverVersion)
if driverVersion.LT(v) {
return path, fmt.Errorf("%s is version %s, want %s", executable, driverVersion, v)
}
return path, nil
}
// extractDriverVersion extracts the driver version.
// KVM and Hyperkit drivers support the 'version' command, that display the information as:
// version: vX.X.X
// commit: XXXX
// This method returns the version 'vX.X.X' or empty if the version isn't found.
func extractDriverVersion(s string) string {
versionRegex := regexp.MustCompile(`version:(.*)`)
matches := versionRegex.FindStringSubmatch(s)
if len(matches) != 2 {
return ""
}
v := strings.TrimSpace(matches[1])
return strings.TrimPrefix(v, "v")
}
func driverExists(driver string) bool {
_, err := exec.LookPath(driver)
return err == nil
}

View File

@ -1,45 +0,0 @@
/*
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 driver
import (
"testing"
)
func TestExtractDriverVersion(t *testing.T) {
v := extractDriverVersion("")
if len(v) != 0 {
t.Error("Expected empty string")
}
v = extractDriverVersion("random text")
if len(v) != 0 {
t.Error("Expected empty string")
}
expectedVersion := "1.2.3"
v = extractDriverVersion("version: v1.2.3")
if expectedVersion != v {
t.Errorf("Expected version: %s, got: %s", expectedVersion, v)
}
v = extractDriverVersion("version: 1.2.3")
if expectedVersion != v {
t.Errorf("Expected version: %s, got: %s", expectedVersion, v)
}
}

View File

@ -1,52 +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 driver
import (
"github.com/blang/semver"
"k8s.io/klog/v2"
)
// minHyperkitVersion is the minimum version of the minikube hyperkit driver compatible with the current minikube code
var minHyperkitVersion *semver.Version
const minHyperkitVersionStr = "1.11.0"
func init() {
v, err := semver.New(minHyperkitVersionStr)
if err != nil {
klog.Errorf("Failed to parse the hyperkit driver version: %v", err)
} else {
minHyperkitVersion = v
}
}
// minAcceptableDriverVersion is the minimum version of driver supported by current version of minikube
func minAcceptableDriverVersion(driver string, mkVer semver.Version) semver.Version {
switch driver {
case HyperKit:
if minHyperkitVersion != nil {
return *minHyperkitVersion
}
return mkVer
case KVM2:
return mkVer
default:
klog.Warningf("Unexpected driver: %v", driver)
return mkVer
}
}

View File

@ -1,52 +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 driver
import (
"testing"
"github.com/blang/semver"
)
func Test_minDriverVersion(t *testing.T) {
tests := []struct {
desc string
driver string
mkV string
want semver.Version
}{
{"Hyperkit", HyperKit, "1.1.1", *minHyperkitVersion},
{"Invalid", "_invalid_", "1.1.1", v("1.1.1")},
{"KVM2", KVM2, "1.1.1", v("1.1.1")},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
if got := minAcceptableDriverVersion(tt.driver, v(tt.mkV)); !got.EQ(tt.want) {
t.Errorf("Invalid min supported version, got: %v, want: %v", got, tt.want)
}
})
}
}
func v(s string) semver.Version {
r, err := semver.New(s)
if err != nil {
panic(err)
}
return *r
}

View File

@ -48,11 +48,11 @@ const (
)
// BeginCacheKubernetesImages caches images required for Kubernetes version in the background
func beginCacheKubernetesImages(g *errgroup.Group, imageRepository string, k8sVersion string, cRuntime string) {
func beginCacheKubernetesImages(g *errgroup.Group, imageRepository string, k8sVersion string, cRuntime string, driverName string) {
// TODO: remove imageRepository check once #7695 is fixed
if imageRepository == "" && download.PreloadExists(k8sVersion, cRuntime) {
if imageRepository == "" && download.PreloadExists(k8sVersion, cRuntime, driverName) {
klog.Info("Caching tarball of preloaded images")
err := download.Preload(k8sVersion, cRuntime)
err := download.Preload(k8sVersion, cRuntime, driverName)
if err == nil {
klog.Infof("Finished verifying existence of preloaded tar for %s on %s", k8sVersion, cRuntime)
return // don't cache individual images if preload is successful.
@ -70,12 +70,12 @@ func beginCacheKubernetesImages(g *errgroup.Group, imageRepository string, k8sVe
}
// handleDownloadOnly caches appropariate binaries and images
func handleDownloadOnly(cacheGroup, kicGroup *errgroup.Group, k8sVersion, containerRuntime string) {
func handleDownloadOnly(cacheGroup, kicGroup *errgroup.Group, k8sVersion, containerRuntime, driverName string) {
// If --download-only, complete the remaining downloads and exit.
if !viper.GetBool("download-only") {
return
}
if err := doCacheBinaries(k8sVersion, containerRuntime); err != nil {
if err := doCacheBinaries(k8sVersion, containerRuntime, driverName); err != nil {
exit.Error(reason.InetCacheBinaries, "Failed to cache binaries", err)
}
if _, err := CacheKubectlBinary(k8sVersion); err != nil {
@ -101,9 +101,9 @@ func CacheKubectlBinary(k8sVersion string) (string, error) {
}
// doCacheBinaries caches Kubernetes binaries in the foreground
func doCacheBinaries(k8sVersion, containerRuntime string) error {
func doCacheBinaries(k8sVersion, containerRuntime, driverName string) error {
existingBinaries := constants.KubernetesReleaseBinaries
if !download.PreloadExists(k8sVersion, containerRuntime) {
if !download.PreloadExists(k8sVersion, containerRuntime, driverName) {
existingBinaries = nil
}
return machine.CacheBinariesForBootstrapper(k8sVersion, viper.GetString(cmdcfg.Bootstrapper), existingBinaries)

View File

@ -280,7 +280,7 @@ func Provision(cc *config.ClusterConfig, n *config.Node, apiServer bool, delOnFa
}
if !driver.BareMetal(cc.Driver) {
beginCacheKubernetesImages(&cacheGroup, cc.KubernetesConfig.ImageRepository, n.KubernetesVersion, cc.KubernetesConfig.ContainerRuntime)
beginCacheKubernetesImages(&cacheGroup, cc.KubernetesConfig.ImageRepository, n.KubernetesVersion, cc.KubernetesConfig.ContainerRuntime, cc.Driver)
}
// Abstraction leakage alert: startHost requires the config to be saved, to satistfy pkg/provision/buildroot.
@ -289,7 +289,7 @@ func Provision(cc *config.ClusterConfig, n *config.Node, apiServer bool, delOnFa
return nil, false, nil, nil, errors.Wrap(err, "Failed to save config")
}
handleDownloadOnly(&cacheGroup, &kicGroup, n.KubernetesVersion, cc.KubernetesConfig.ContainerRuntime)
handleDownloadOnly(&cacheGroup, &kicGroup, n.KubernetesVersion, cc.KubernetesConfig.ContainerRuntime, cc.Driver)
waitDownloadKicBaseImage(&kicGroup)
return startMachine(cc, n, delOnFail)
@ -318,7 +318,7 @@ func configureRuntimes(runner cruntime.CommandRunner, cc config.ClusterConfig, k
// Preload is overly invasive for bare metal, and caching is not meaningful.
// KIC handles preload elsewhere.
if driver.IsVM(cc.Driver) {
if err := cr.Preload(cc.KubernetesConfig); err != nil {
if err := cr.Preload(cc); err != nil {
switch err.(type) {
case *cruntime.ErrISOFeature:
out.ErrT(style.Tip, "Existing disk is missing new features ({{.error}}). To upgrade, run 'minikube delete'", out.V{"error": err})

View File

@ -97,7 +97,9 @@ func TestDownloadOnly(t *testing.T) {
if NoneDriver() {
t.Skip("None driver does not have preload")
}
if download.PreloadExists(v, containerRuntime, true) {
// Driver does not matter here, since the only exception is none driver,
// which cannot occur here.
if download.PreloadExists(v, containerRuntime, "docker", true) {
// Just make sure the tarball path exists
if _, err := os.Stat(download.TarballPath(v, containerRuntime)); err != nil {
t.Errorf("failed to verify preloaded tarball file exists: %v", err)