Add --interactive flag support

pull/5397/head
Thomas Stromberg 2019-09-18 13:18:15 -07:00
parent 97ebe91c68
commit c0874e2a7e
4 changed files with 110 additions and 101 deletions

View File

@ -105,6 +105,7 @@ const (
hostDNSResolver = "host-dns-resolver"
waitUntilHealthy = "wait"
force = "force"
interactive = "interactive"
waitTimeout = "wait-timeout"
nativeSSH = "native-ssh"
)
@ -140,6 +141,7 @@ func initMinikubeFlags() {
viper.AutomaticEnv()
startCmd.Flags().Bool(force, false, "Force minikube to perform possibly dangerous operations")
startCmd.Flags().Bool(interactive, true, "Allow user prompts for more information")
startCmd.Flags().Int(cpus, constants.DefaultCPUS, "Number of CPUs allocated to the minikube VM.")
startCmd.Flags().String(memory, constants.DefaultMemorySize, "Amount of RAM allocated to the minikube VM (format: <number>[<unit>], where unit = b, k, m or g).")
@ -290,7 +292,10 @@ func runStart(cmd *cobra.Command, args []string) {
validateFlags(driver)
validateUser(driver)
installOrUpdateDriver(driver)
if err := drivers.InstallOrUpdate(driver, viper.GetBool(interactive)); err != nil {
glog.Errorf("error: %v", err)
out.WarningT("Unable to update {{.driver}} driver: {{.error}}", out.V{"driver": driver, "error": err})
}
k8sVersion, isUpgrade := getKubernetesVersion(oldConfig)
config, err := generateCfgFromFlags(cmd, k8sVersion, driver)
@ -1055,27 +1060,3 @@ func configureMounts() {
func saveConfig(clusterCfg *cfg.Config) error {
return cfg.CreateProfile(viper.GetString(cfg.MachineProfile), clusterCfg)
}
func installOrUpdateDriver(driver string) {
var driverExecutable string
switch driver {
case constants.DriverKvm2:
driverExecutable = fmt.Sprintf("docker-machine-driver-%s", constants.DriverKvm2)
case constants.DriverHyperkit:
driverExecutable = fmt.Sprintf("docker-machine-driver-%s", constants.DriverHyperkit)
default: // driver doesn't install or update
return
}
minikubeVersion, err := version.GetSemverVersion()
if err != nil {
out.WarningT("Error parsing minikube version: {{.error}}", out.V{"error": err})
return
}
targetDir := constants.MakeMiniPath("bin")
err = drivers.InstallOrUpdate(driverExecutable, targetDir, minikubeVersion)
if err != nil {
out.WarningT("Error downloading driver: {{.error}}", out.V{"error": err})
}
}

View File

@ -22,7 +22,6 @@ import (
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
@ -38,6 +37,7 @@ import (
"github.com/pkg/errors"
"k8s.io/minikube/pkg/version"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/util"
)
@ -123,14 +123,14 @@ func MakeDiskImage(d *drivers.BaseDriver, boot2dockerURL string, diskSize int) e
return errors.Wrapf(err, "createRawDiskImage(%s)", diskPath)
}
machPath := d.ResolveStorePath(".")
if err := fixPermissions(machPath); err != nil {
if err := fixMachinePermissions(machPath); err != nil {
return errors.Wrapf(err, "fixing permissions on %s", machPath)
}
}
return nil
}
func fixPermissions(path string) error {
func fixMachinePermissions(path string) error {
glog.Infof("Fixing permissions on %s ...", path)
if err := os.Chown(path, syscall.Getuid(), syscall.Getegid()); err != nil {
return errors.Wrap(err, "chown dir")
@ -149,44 +149,110 @@ func fixPermissions(path string) error {
}
// InstallOrUpdate downloads driver if it is not present, or updates it if there's a newer version
func InstallOrUpdate(driver, destination string, v semver.Version) error {
glog.Infof("InstallOrUpdate(%s): dest=%s, version=%s, PATH=%s", driver, destination, v, os.Getenv("PATH"))
_, err := exec.LookPath(driver)
// if file driver doesn't exist, download it
if err != nil {
glog.Infof("LookPath %s: %v", driver, err)
return download(driver, v, destination)
func InstallOrUpdate(driver string, interactive bool) error {
if driver != constants.DriverKvm2 && driver != constants.DriverHyperkit {
return nil
}
cmd := exec.Command(driver, "version")
output, err := cmd.Output()
// if driver doesnt support 'version', it is old, download it
v, err := version.GetSemverVersion()
if err != nil {
glog.Infof("%s version: %v", driver, err)
return download(driver, v, destination)
out.WarningT("Error parsing minikube version: {{.error}}", out.V{"error": err})
return err
}
ev := ExtractVMDriverVersion(string(output))
executable := fmt.Sprintf("docker-machine-driver-%s", driver)
path, err := validateDriver(executable, v)
if err != nil {
glog.Warningf("%s: %v", driver, executable)
path = filepath.Join(constants.MakeMiniPath("bin"), executable)
derr := download(executable, path, v)
if derr != nil {
return derr
}
}
return fixDriverPermissions(driver, path, interactive)
}
// if the driver doesn't return any version, download it
// fixDriverPermissions fixes the permissions on a driver
func fixDriverPermissions(driver string, path string, interactive bool) error {
// Everything is easy but hyperkit
if driver != constants.DriverHyperkit {
return os.Chmod(path, 0755)
}
// Hyperkit land
info, err := os.Stat(path)
if err != nil {
return err
}
cmds := []*exec.Cmd{}
owner := info.Sys().(*syscall.Stat_t).Uid
m := info.Mode()
glog.Infof("%s owner: %d - permissions: %s", path, owner, m)
if owner != 0 {
cmds = append(cmds, exec.Command("sudo", "chown", "root:wheel", path))
}
if m&os.ModeSetuid == 0 {
cmds = append(cmds, exec.Command("sudo", "chmod", "u+s", path))
}
// No work to be done!
if len(cmds) == 0 {
return nil
}
var example strings.Builder
for _, c := range cmds {
example.WriteString(fmt.Sprintf(" $ %s \n", strings.Join(c.Args, " ")))
}
out.T(out.Permissions, "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\n\n{{ .example }}\n", out.V{"driver": driver, "example": example.String()})
for _, c := range cmds {
testArgs := append([]string{"-n"}, c.Args[1:]...)
test := exec.Command("sudo", testArgs...)
glog.Infof("testing: %v", test.Args)
if err := test.Run(); err != nil {
glog.Infof("%v may require a password: %v", c.Args, err)
if !interactive {
return fmt.Errorf("%v requires a password, and --interactive=false", c.Args)
}
}
glog.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(driver string, v semver.Version) (string, error) {
path, err := exec.LookPath(driver)
if err != nil {
return path, err
}
output, err := exec.Command(path, "version").Output()
if err != nil {
return path, err
}
ev := extractVMDriverVersion(string(output))
if len(ev) == 0 {
glog.Infof("%s: unable to extract version from %q", driver, output)
return download(driver, v, destination)
return path, fmt.Errorf("%s: unable to extract version from %q", driver, output)
}
vmDriverVersion, err := semver.Make(ev)
if err != nil {
return errors.Wrap(err, "can't parse driver version")
return path, errors.Wrap(err, "can't parse driver version")
}
// if the current driver version is older, download newer
if vmDriverVersion.LT(v) {
glog.Infof("%s is version %s, want %s", driver, vmDriverVersion, v)
return download(driver, v, destination)
return path, fmt.Errorf("%s is version %s, want %s", driver, vmDriverVersion, v)
}
return nil
return path, nil
}
func driverWithChecksumURL(driver string, v semver.Version) string {
@ -194,50 +260,31 @@ func driverWithChecksumURL(driver string, v semver.Version) string {
return fmt.Sprintf("%s?checksum=file:%s.sha256", base, base)
}
func download(driver string, v semver.Version, destination string) error {
// supports kvm2 and hyperkit
if driver != "docker-machine-driver-kvm2" && driver != "docker-machine-driver-hyperkit" {
return nil
}
// download an arbitrary driver
func download(driver string, destination string, v semver.Version) error {
out.T(out.FileDownload, "Downloading driver {{.driver}}:", out.V{"driver": driver})
targetFilepath := path.Join(destination, driver)
os.Remove(targetFilepath)
os.Remove(destination)
url := driverWithChecksumURL(driver, v)
client := &getter.Client{
Src: url,
Dst: targetFilepath,
Dst: destination,
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)
}
err := os.Chmod(targetFilepath, 0755)
if err != nil {
return errors.Wrap(err, "chmod error")
}
if driver == "docker-machine-driver-hyperkit" {
err := setHyperKitPermissions(targetFilepath)
if err != nil {
return errors.Wrap(err, "setting hyperkit permission")
}
}
return nil
}
// ExtractVMDriverVersion extracts the driver version.
// extractVMDriverVersion 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 ExtractVMDriverVersion(s string) string {
func extractVMDriverVersion(s string) string {
versionRegex := regexp.MustCompile(`version:(.*)`)
matches := versionRegex.FindStringSubmatch(s)
@ -248,22 +295,3 @@ func ExtractVMDriverVersion(s string) string {
v := strings.TrimSpace(matches[1])
return strings.TrimPrefix(v, version.VersionPrefix)
}
func setHyperKitPermissions(driverPath string) error {
msg := fmt.Sprintf("A new hyperkit driver was installed. It needs elevated permissions to run. The following commands will be executed:\n\n $ sudo chown root:wheel %s\n $ sudo chmod u+s %s\n", driverPath, driverPath)
out.T(out.Permissions, msg, out.V{})
cmd := exec.Command("sudo", "chown", "root:wheel", driverPath)
err := cmd.Run()
if err != nil {
return errors.Wrap(err, "chown root:wheel")
}
cmd = exec.Command("sudo", "chmod", "u+s", driverPath)
err = cmd.Run()
if err != nil {
return errors.Wrap(err, "chmod u+s")
}
return nil
}

View File

@ -50,24 +50,24 @@ func Test_createDiskImage(t *testing.T) {
}
func TestExtractVMDriverVersion(t *testing.T) {
v := ExtractVMDriverVersion("")
v := extractVMDriverVersion("")
if len(v) != 0 {
t.Error("Expected empty string")
}
v = ExtractVMDriverVersion("random text")
v = extractVMDriverVersion("random text")
if len(v) != 0 {
t.Error("Expected empty string")
}
expectedVersion := "1.2.3"
v = ExtractVMDriverVersion("version: v1.2.3")
v = extractVMDriverVersion("version: v1.2.3")
if expectedVersion != v {
t.Errorf("Expected version: %s, got: %s", expectedVersion, v)
}
v = ExtractVMDriverVersion("version: 1.2.3")
v = extractVMDriverVersion("version: 1.2.3")
if expectedVersion != v {
t.Errorf("Expected version: %s, got: %s", expectedVersion, v)
}

View File

@ -78,7 +78,7 @@ var styles = map[StyleEnum]style{
Documentation: {Prefix: "📘 "},
Issues: {Prefix: "⁉️ "},
Issue: {Prefix: " ▪ ", LowPrefix: lowIndent}, // Indented bullet
Check: {Prefix: "✔️ "},
Check: {Prefix: " "},
Celebration: {Prefix: "🎉 "},
Workaround: {Prefix: "👉 ", LowPrefix: lowIndent},