Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --interactive flag to prevent stdin prompts #5397

Merged
merged 6 commits into from
Sep 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 12 additions & 25 deletions cmd/minikube/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const (
hostDNSResolver = "host-dns-resolver"
waitUntilHealthy = "wait"
force = "force"
interactive = "interactive"
waitTimeout = "wait-timeout"
nativeSSH = "native-ssh"
)
Expand Down Expand Up @@ -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).")
Expand Down Expand Up @@ -290,7 +292,16 @@ func runStart(cmd *cobra.Command, args []string) {

validateFlags(driver)
validateUser(driver)
installOrUpdateDriver(driver)

v, err := version.GetSemverVersion()
if err != nil {
out.WarningT("Error parsing minikube version: {{.error}}", out.V{"error": err})
} else {
if err := drivers.InstallOrUpdate(driver, constants.MakeMiniPath("bin"), v, 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)
Expand Down Expand Up @@ -1055,27 +1066,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})
}
}
154 changes: 84 additions & 70 deletions pkg/drivers/drivers.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
Expand All @@ -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"
)
Expand Down Expand Up @@ -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")
Expand All @@ -149,95 +149,128 @@ 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"))
func InstallOrUpdate(driver string, directory string, v semver.Version, interactive bool) error {
if driver != constants.DriverKvm2 && driver != constants.DriverHyperkit {
return nil
}

_, err := exec.LookPath(driver)
// if file driver doesn't exist, download it
executable := fmt.Sprintf("docker-machine-driver-%s", driver)
path, err := validateDriver(executable, v)
if err != nil {
glog.Infof("LookPath %s: %v", driver, err)
return download(driver, v, destination)
glog.Warningf("%s: %v", executable, err)
path = filepath.Join(directory, executable)
derr := download(executable, path, v)
if derr != nil {
return derr
}
}
return fixDriverPermissions(driver, path, interactive)
}

// fixDriverPermissions fixes the permissions on a driver
func fixDriverPermissions(driver string, path string, interactive bool) error {
// This method only supports hyperkit so far (because it's complicated)
if driver != constants.DriverHyperkit {
return nil
}

cmd := exec.Command(driver, "version")
output, err := cmd.Output()
// if driver doesnt support 'version', it is old, download it
// 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()
glog.Infof("stdout: %s", stdout)
if err == nil && strings.TrimSpace(string(stdout)) == path {
glog.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.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) {
glog.Infof("Validating %s, PATH=%s", driver, os.Getenv("PATH"))
path, err := exec.LookPath(driver)
if err != nil {
glog.Infof("%s version: %v", driver, err)
return download(driver, v, destination)
return path, err
}

ev := ExtractVMDriverVersion(string(output))
output, err := exec.Command(path, "version").Output()
if err != nil {
return path, err
}

// if the driver doesn't return any version, download it
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 {
base := fmt.Sprintf("https://github.com/kubernetes/minikube/releases/download/v%s/%s", v, driver)
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
// Give downloaded drivers a baseline decent file permission
return os.Chmod(destination, 0755)
}

// 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)

Expand All @@ -248,22 +281,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
}
8 changes: 4 additions & 4 deletions pkg/drivers/drivers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/minikube/out/style.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},

Expand Down
4 changes: 2 additions & 2 deletions test/integration/driver_install_or_update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func TestKVMDriverInstallOrUpdate(t *testing.T) {
t.Fatalf("Expected new semver. test: %v, got: %v", tc.name, err)
}

err = drivers.InstallOrUpdate("docker-machine-driver-kvm2", dir, newerVersion)
err = drivers.InstallOrUpdate("kvm2", dir, newerVersion, true)
if err != nil {
t.Fatalf("Failed to update driver to %v. test: %s, got: %v", newerVersion, tc.name, err)
}
Expand Down Expand Up @@ -147,7 +147,7 @@ func TestHyperKitDriverInstallOrUpdate(t *testing.T) {
t.Fatalf("Expected new semver. test: %v, got: %v", tc.name, err)
}

err = drivers.InstallOrUpdate("docker-machine-driver-hyperkit", dir, newerVersion)
err = drivers.InstallOrUpdate("hyperkit", dir, newerVersion, true)
if err != nil {
t.Fatalf("Failed to update driver to %v. test: %s, got: %v", newerVersion, tc.name, err)
}
Expand Down