From a1579990370f5d1650f492b4661973ccbb94f4c4 Mon Sep 17 00:00:00 2001 From: Henry Chen Date: Fri, 3 Oct 2025 20:41:35 +0800 Subject: [PATCH] Lower priority for NeedsSudo drivers when no sudo available --- pkg/minikube/driver/driver.go | 24 +++++++-- pkg/minikube/registry/drvs/docker/docker.go | 2 + .../registry/drvs/hyperkit/hyperkit.go | 2 + pkg/minikube/registry/drvs/hyperv/hyperv.go | 2 + pkg/minikube/registry/drvs/krunkit/krunkit.go | 2 + pkg/minikube/registry/drvs/kvm2/kvm2.go | 2 + pkg/minikube/registry/drvs/none/none.go | 2 + .../registry/drvs/parallels/parallels.go | 2 + pkg/minikube/registry/drvs/podman/podman.go | 2 + pkg/minikube/registry/drvs/qemu2/qemu2.go | 2 + pkg/minikube/registry/drvs/ssh/ssh.go | 2 + pkg/minikube/registry/drvs/vfkit/vfkit.go | 2 + .../registry/drvs/virtualbox/virtualbox.go | 2 + pkg/minikube/registry/drvs/vmware/vmware.go | 2 + pkg/minikube/registry/global.go | 4 +- pkg/minikube/registry/global_test.go | 20 +++++--- pkg/minikube/registry/registry.go | 3 ++ pkg/minikube/registry/registry_test.go | 51 +++++++++++++++++++ 18 files changed, 115 insertions(+), 13 deletions(-) diff --git a/pkg/minikube/driver/driver.go b/pkg/minikube/driver/driver.go index 476b85c0c597..124b5a1d24d5 100644 --- a/pkg/minikube/driver/driver.go +++ b/pkg/minikube/driver/driver.go @@ -19,6 +19,7 @@ package driver import ( "fmt" "os" + "os/exec" "runtime" "sort" "strconv" @@ -79,6 +80,7 @@ const ( var ( // systemdResolvConf is path to systemd's DNS configuration. https://github.com/kubernetes/minikube/issues/3511 systemdResolvConf = "/run/systemd/resolve/resolv.conf" + HasSudo = realHasSudo ) // SupportedDrivers returns a list of supported drivers @@ -319,6 +321,14 @@ func FlagDefaults(name string) FlagHints { func Choices(vm bool) []registry.DriverState { options := registry.Available(vm) + if !HasSudo() { + for i := range options { + if options[i].NeedsSudo { + options[i].Priority = registry.Discouraged + } + } + } + // Descending priority for predictability and appearance sort.Slice(options, func(i, j int) bool { return options[i].Priority > options[j].Priority @@ -392,10 +402,11 @@ func Status(name string) registry.DriverState { select { case s := <-stateChannel: return registry.DriverState{ - Name: d.Name, - Default: d.Default, - Priority: d.Priority, - State: s, + Name: d.Name, + Default: d.Default, + Priority: d.Priority, + State: s, + NeedsSudo: d.NeedsSudo, } case <-timeoutChannel: klog.Infof("time out when checking for status of %s driver", name) @@ -438,3 +449,8 @@ func IndexFromMachineName(machineName string) int { } return 1 // master node } + +func realHasSudo() bool { + err := exec.Command("sudo", "-n", "true").Run() + return err == nil +} diff --git a/pkg/minikube/registry/drvs/docker/docker.go b/pkg/minikube/registry/drvs/docker/docker.go index 89f5da5617fa..695bcb8b2262 100644 --- a/pkg/minikube/registry/drvs/docker/docker.go +++ b/pkg/minikube/registry/drvs/docker/docker.go @@ -53,6 +53,8 @@ func init() { Status: status, Default: true, Priority: registry.HighlyPreferred, + // Docker driver does not require sudo + NeedsSudo: false, }); err != nil { panic(fmt.Sprintf("register failed: %v", err)) } diff --git a/pkg/minikube/registry/drvs/hyperkit/hyperkit.go b/pkg/minikube/registry/drvs/hyperkit/hyperkit.go index 1461bea7034a..43c2fc6fc35b 100644 --- a/pkg/minikube/registry/drvs/hyperkit/hyperkit.go +++ b/pkg/minikube/registry/drvs/hyperkit/hyperkit.go @@ -54,6 +54,8 @@ func init() { Status: status, Default: true, Priority: registry.Deprecated, + // Hyperkit driver requires sudo + NeedsSudo: true, }); err != nil { panic(fmt.Sprintf("register: %v", err)) } diff --git a/pkg/minikube/registry/drvs/hyperv/hyperv.go b/pkg/minikube/registry/drvs/hyperv/hyperv.go index 7a3836c0f865..192e342b32ac 100644 --- a/pkg/minikube/registry/drvs/hyperv/hyperv.go +++ b/pkg/minikube/registry/drvs/hyperv/hyperv.go @@ -49,6 +49,8 @@ func init() { Status: status, Default: true, Priority: registry.Preferred, + // Hyper-V driver requires sudo + NeedsSudo: true, }); err != nil { panic(fmt.Sprintf("register: %v", err)) } diff --git a/pkg/minikube/registry/drvs/krunkit/krunkit.go b/pkg/minikube/registry/drvs/krunkit/krunkit.go index 2faf138dba5f..83b89bf8587a 100644 --- a/pkg/minikube/registry/drvs/krunkit/krunkit.go +++ b/pkg/minikube/registry/drvs/krunkit/krunkit.go @@ -50,6 +50,8 @@ func init() { Status: status, Default: true, Priority: registry.Experimental, + // Krunkit driver requires sudo + NeedsSudo: true, }); err != nil { panic(fmt.Sprintf("register failed: %v", err)) } diff --git a/pkg/minikube/registry/drvs/kvm2/kvm2.go b/pkg/minikube/registry/drvs/kvm2/kvm2.go index 492d5319c602..6cad5da2366b 100644 --- a/pkg/minikube/registry/drvs/kvm2/kvm2.go +++ b/pkg/minikube/registry/drvs/kvm2/kvm2.go @@ -50,6 +50,8 @@ func init() { Status: status, Default: true, Priority: registry.Preferred, + // KVM2 driver does not require sudo + NeedsSudo: false, }); err != nil { panic(fmt.Sprintf("register failed: %v", err)) } diff --git a/pkg/minikube/registry/drvs/none/none.go b/pkg/minikube/registry/drvs/none/none.go index 22a183281fb8..a86155adfc2e 100644 --- a/pkg/minikube/registry/drvs/none/none.go +++ b/pkg/minikube/registry/drvs/none/none.go @@ -40,6 +40,8 @@ func init() { Status: status, Default: false, // no isolation Priority: registry.Discouraged, + // None driver requires sudo + NeedsSudo: true, }); err != nil { panic(fmt.Sprintf("register failed: %v", err)) } diff --git a/pkg/minikube/registry/drvs/parallels/parallels.go b/pkg/minikube/registry/drvs/parallels/parallels.go index d03143d7c00f..4d86b6492600 100644 --- a/pkg/minikube/registry/drvs/parallels/parallels.go +++ b/pkg/minikube/registry/drvs/parallels/parallels.go @@ -39,6 +39,8 @@ func init() { Default: true, Priority: registry.Default, Init: func() drivers.Driver { return parallels.NewDriver("", "") }, + // Parallels driver requires sudo + NeedsSudo: true, }) if err != nil { panic(fmt.Sprintf("unable to register: %v", err)) diff --git a/pkg/minikube/registry/drvs/podman/podman.go b/pkg/minikube/registry/drvs/podman/podman.go index 17fd054bbadd..eb5180c15f7c 100644 --- a/pkg/minikube/registry/drvs/podman/podman.go +++ b/pkg/minikube/registry/drvs/podman/podman.go @@ -61,6 +61,8 @@ func init() { Status: status, Default: true, Priority: priority, + // Podman driver may require sudo (on linux) + NeedsSudo: true, }); err != nil { panic(fmt.Sprintf("register failed: %v", err)) } diff --git a/pkg/minikube/registry/drvs/qemu2/qemu2.go b/pkg/minikube/registry/drvs/qemu2/qemu2.go index 4b848694abcc..0ec7af0f9ecf 100644 --- a/pkg/minikube/registry/drvs/qemu2/qemu2.go +++ b/pkg/minikube/registry/drvs/qemu2/qemu2.go @@ -53,6 +53,8 @@ func init() { Status: status, Default: true, Priority: priority, + // QEMU2 driver does not require sudo + NeedsSudo: false, }); err != nil { panic(fmt.Sprintf("register failed: %v", err)) } diff --git a/pkg/minikube/registry/drvs/ssh/ssh.go b/pkg/minikube/registry/drvs/ssh/ssh.go index 8308cf90053a..a1ba224bef7a 100644 --- a/pkg/minikube/registry/drvs/ssh/ssh.go +++ b/pkg/minikube/registry/drvs/ssh/ssh.go @@ -41,6 +41,8 @@ func init() { Default: false, // requires external VM Priority: registry.Discouraged, Init: func() drivers.Driver { return ssh.NewDriver(ssh.Config{}) }, + // SSH driver does not require sudo + NeedsSudo: false, }) if err != nil { panic(fmt.Sprintf("unable to register: %v", err)) diff --git a/pkg/minikube/registry/drvs/vfkit/vfkit.go b/pkg/minikube/registry/drvs/vfkit/vfkit.go index 970a47246e86..b02c5942a947 100644 --- a/pkg/minikube/registry/drvs/vfkit/vfkit.go +++ b/pkg/minikube/registry/drvs/vfkit/vfkit.go @@ -54,6 +54,8 @@ func init() { Status: status, Default: true, Priority: priority, + // VFKit driver requires sudo + NeedsSudo: true, }); err != nil { panic(fmt.Sprintf("register failed: %v", err)) } diff --git a/pkg/minikube/registry/drvs/virtualbox/virtualbox.go b/pkg/minikube/registry/drvs/virtualbox/virtualbox.go index edff7849d71d..70f628cebf3a 100644 --- a/pkg/minikube/registry/drvs/virtualbox/virtualbox.go +++ b/pkg/minikube/registry/drvs/virtualbox/virtualbox.go @@ -49,6 +49,8 @@ func init() { Default: true, Priority: registry.Fallback, Init: func() drivers.Driver { return virtualbox.NewDriver("", "") }, + // VirtualBox driver requires sudo + NeedsSudo: true, }) if err != nil { panic(fmt.Sprintf("unable to register: %v", err)) diff --git a/pkg/minikube/registry/drvs/vmware/vmware.go b/pkg/minikube/registry/drvs/vmware/vmware.go index efc17a78733b..5c575d55c4b1 100644 --- a/pkg/minikube/registry/drvs/vmware/vmware.go +++ b/pkg/minikube/registry/drvs/vmware/vmware.go @@ -37,6 +37,8 @@ func init() { Priority: registry.Deprecated, Init: func() drivers.Driver { return vmware.NewDriver("", "") }, Status: status, + // VMware driver requires sudo + NeedsSudo: true, }) if err != nil { panic(fmt.Sprintf("unable to register: %v", err)) diff --git a/pkg/minikube/registry/global.go b/pkg/minikube/registry/global.go index 77b8f35f7928..55f40bc92c29 100644 --- a/pkg/minikube/registry/global.go +++ b/pkg/minikube/registry/global.go @@ -81,6 +81,8 @@ type DriverState struct { Rejection string // Suggestion is how the user could improve health Suggestion string + // New field: whether this driver requires sudo permissions + NeedsSudo bool } func (d DriverState) String() string { @@ -137,7 +139,7 @@ func Available(vm bool) []DriverState { if !s.Healthy { priority = Unhealthy } - sts = append(sts, DriverState{Name: d.Name, Default: d.Default, Preference: preference, Priority: priority, State: s}) + sts = append(sts, DriverState{Name: d.Name, Default: d.Default, Preference: preference, Priority: priority, State: s, NeedsSudo: d.NeedsSudo}) } // Descending priority for predictability diff --git a/pkg/minikube/registry/global_test.go b/pkg/minikube/registry/global_test.go index 88d8268c32dd..874cc3c68f1b 100644 --- a/pkg/minikube/registry/global_test.go +++ b/pkg/minikube/registry/global_test.go @@ -72,20 +72,22 @@ func TestGlobalAvailable(t *testing.T) { } bar := DriverDef{ - Name: "healthy-bar", - Default: true, - Priority: Default, - Status: func() State { return State{Healthy: true} }, + Name: "healthy-bar", + Default: true, + Priority: Default, + NeedsSudo: false, + Status: func() State { return State{Healthy: true} }, } if err := Register(bar); err != nil { t.Errorf("register returned error: %v", err) } foo := DriverDef{ - Name: "unhealthy-foo", - Default: true, - Priority: Default, - Status: func() State { return State{Healthy: false} }, + Name: "unhealthy-foo", + Default: true, + Priority: Default, + NeedsSudo: false, + Status: func() State { return State{Healthy: false} }, } if err := Register(foo); err != nil { t.Errorf("register returned error: %v", err) @@ -97,6 +99,7 @@ func TestGlobalAvailable(t *testing.T) { Default: true, Preference: Default, Priority: Default, + NeedsSudo: false, State: State{Healthy: true}, }, { @@ -104,6 +107,7 @@ func TestGlobalAvailable(t *testing.T) { Default: true, Preference: Default, Priority: Unhealthy, + NeedsSudo: false, State: State{Healthy: false}, }, } diff --git a/pkg/minikube/registry/registry.go b/pkg/minikube/registry/registry.go index ef1d961315f4..8d192ab4b8fa 100644 --- a/pkg/minikube/registry/registry.go +++ b/pkg/minikube/registry/registry.go @@ -110,6 +110,9 @@ type DriverDef struct { // Priority returns the prioritization for selecting a driver by default. Priority Priority + + // New field: whether this driver requires sudo permissions + NeedsSudo bool } // Empty returns true if the driver is nil diff --git a/pkg/minikube/registry/registry_test.go b/pkg/minikube/registry/registry_test.go index c04959abe70a..4b9adc5cce68 100644 --- a/pkg/minikube/registry/registry_test.go +++ b/pkg/minikube/registry/registry_test.go @@ -17,6 +17,7 @@ limitations under the License. package registry import ( + "os/exec" "testing" "github.com/google/go-cmp/cmp" @@ -91,3 +92,53 @@ func TestDriverAlias(t *testing.T) { t.Errorf("driver.Empty = false, expected true") } } + +// NeedsSudo try to execute driver related commands to determine if sudo is required +func NeedsSudo(driverName string) bool { + var cmd *exec.Cmd + + switch driverName { + case "docker": + cmd = exec.Command("docker", "info") + case "podman": + cmd = exec.Command("podman", "info") + case "kvm2": + cmd = exec.Command("virsh", "list", "--all") + case "qemu2": + cmd = exec.Command("qemu-system-x86_64", "--version") + case "virtualbox": + cmd = exec.Command("VBoxManage", "list", "vms") + case "hyperkit": + cmd = exec.Command("hyperkit", "-v") + case "hyperv": + cmd = exec.Command("powershell", "-Command", "Get-VM") // Windows Hyper-V + case "vmware": + cmd = exec.Command("vmrun", "list") + case "parallels": + cmd = exec.Command("prlctl", "list") + case "vfkit": + cmd = exec.Command("vfctl", "list") // macOS vfkit CLI + case "ssh": + cmd = exec.Command("ssh", "-V") + case "krunkit": + cmd = exec.Command("krun", "--version") // krunkit CLI + case "none": + return true // none driver almost always requires root + default: + return false // By default, no sudo is required + } + + // Execute the command and check if it succeeds + if err := cmd.Run(); err != nil { + // If it fails, it may require sudo + return true + } + return false +} + +func TestNeedsSudo(t *testing.T) { + drivers := []string{"docker", "podman", "kvm2", "none", "qemu2", "virtualbox", "hyperkit", "vmware", "parallels", "ssh", "krunkit", "hyperv", "vfkit"} + for _, d := range drivers { + t.Logf("%s NeedsSudo = %v", d, NeedsSudo(d)) + } +}