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

WIP: Add minikube support for the "generic" VM driver #4734

Closed
wants to merge 11 commits into from
2 changes: 1 addition & 1 deletion cmd/minikube/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func runDelete(cmd *cobra.Command, args []string) {
}

// In the case of "none", we want to uninstall Kubernetes as there is no VM to delete
if err == nil && cc.MachineConfig.VMDriver == constants.DriverNone {
if err == nil && (cc.MachineConfig.VMDriver == constants.DriverNone || cc.MachineConfig.VMDriver == constants.DriverGeneric) {
uninstallKubernetes(api, cc.KubernetesConfig, viper.GetString(cmdcfg.Bootstrapper))
}

Expand Down
15 changes: 15 additions & 0 deletions cmd/minikube/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (

"github.com/blang/semver"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/host"
"github.com/golang/glog"
"github.com/google/go-containerregistry/pkg/authn"
Expand Down Expand Up @@ -102,6 +103,10 @@ const (
dnsProxy = "dns-proxy"
hostDNSResolver = "host-dns-resolver"
waitUntilHealthy = "wait"
genericIPAddress = "generic-ip-address"
genericSSHUser = "generic-ssh-user"
genericSSHKey = "generic-ssh-key"
genericSSHPort = "generic-ssh-port"
)

var (
Expand Down Expand Up @@ -186,6 +191,12 @@ func initDriverFlags() {

// hyperv
startCmd.Flags().String(hypervVirtualSwitch, "", "The hyperv virtual switch name. Defaults to first found. (only supported with HyperV driver)")

// generic
startCmd.Flags().String(genericIPAddress, "", "IP address (generic)")
startCmd.Flags().String(genericSSHUser, drivers.DefaultSSHUser, "SSH user (generic)")
startCmd.Flags().String(genericSSHKey, "", "SSH key (generic)")
startCmd.Flags().Int(genericSSHPort, drivers.DefaultSSHPort, "SSH port (generic)")
}

// initNetworkingFlags inits the commandline flags for connectivity related flags for start
Expand Down Expand Up @@ -588,6 +599,10 @@ func generateConfig(cmd *cobra.Command, k8sVersion string) (cfg.Config, error) {
NoVTXCheck: viper.GetBool(noVTXCheck),
DNSProxy: viper.GetBool(dnsProxy),
HostDNSResolver: viper.GetBool(hostDNSResolver),
GenericIPAddress: viper.GetString(genericIPAddress),
GenericSSHUser: viper.GetString(genericSSHUser),
GenericSSHKey: viper.GetString(genericSSHKey),
GenericSSHPort: viper.GetInt(genericSSHPort),
},
KubernetesConfig: cfg.KubernetesConfig{
KubernetesVersion: k8sVersion,
Expand Down
40 changes: 24 additions & 16 deletions cmd/minikube/cmd/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package cmd

import (
"os"
"time"

"github.com/docker/machine/libmachine/mcnerror"
Expand Down Expand Up @@ -51,24 +52,31 @@ func runStop(cmd *cobra.Command, args []string) {
}
defer api.Close()

nonexistent := false
cc, err := pkg_config.Load()
if err != nil && !os.IsNotExist(err) {
out.ErrLn("Error loading profile config: %v", err)
}

if err == nil && (cc.MachineConfig.VMDriver != constants.DriverNone && cc.MachineConfig.VMDriver != constants.DriverGeneric) {
nonexistent := false

stop := func() (err error) {
err = cluster.StopHost(api)
switch err := errors.Cause(err).(type) {
case mcnerror.ErrHostDoesNotExist:
out.T(out.Meh, `"{{.profile_name}}" VM does not exist, nothing to stop`, out.V{"profile_name": profile})
nonexistent = true
return nil
default:
return err
stop := func() (err error) {
err = cluster.StopHost(api, cc.MachineConfig)
switch err := errors.Cause(err).(type) {
case mcnerror.ErrHostDoesNotExist:
out.T(out.Meh, `"{{.profile_name}}" VM does not exist, nothing to stop`, out.V{"profile_name": profile})
nonexistent = true
return nil
default:
return err
}
}
if err := pkgutil.RetryAfter(5, stop, 2*time.Second); err != nil {
exit.WithError("Unable to stop VM", err)
}
if !nonexistent {
out.T(out.Stopped, `"{{.profile_name}}" stopped.`, out.V{"profile_name": profile})
}
}
if err := pkgutil.RetryAfter(5, stop, 2*time.Second); err != nil {
exit.WithError("Unable to stop VM", err)
}
if !nonexistent {
out.T(out.Stopped, `"{{.profile_name}}" stopped.`, out.V{"profile_name": profile})
}

if err := cmdUtil.KillMountProcess(); err != nil {
Expand Down
56 changes: 40 additions & 16 deletions pkg/minikube/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"time"

"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/mcnerror"
Expand Down Expand Up @@ -75,7 +76,7 @@ func init() {

// CacheISO downloads and caches ISO.
func CacheISO(config cfg.MachineConfig) error {
if localDriver(config.VMDriver) {
if localDriver(config.VMDriver) || config.VMDriver == constants.DriverGeneric {
return nil
}
return config.Downloader.CacheMinikubeISOFromURL(config.MinikubeISO)
Expand Down Expand Up @@ -117,15 +118,23 @@ func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error)
return nil, errors.Wrap(err, "Error getting state for host")
}

if s == state.Running {
out.T(out.Running, `Re-using the currently running {{.driver_name}} VM for "{{.profile_name}}" ...`, out.V{"driver_name": h.Driver.DriverName(), "profile_name": cfg.GetMachineName()})
} else {
out.T(out.Restarting, `Restarting existing {{.driver_name}} VM for "{{.profile_name}}" ...`, out.V{"driver_name": h.Driver.DriverName(), "profile_name": cfg.GetMachineName()})
if err := h.Driver.Start(); err != nil {
return nil, errors.Wrap(err, "start")
if !localDriver(config.VMDriver) && config.VMDriver != constants.DriverGeneric {
if s == state.Running {
out.T(out.Running, `Re-using the currently running {{.driver_name}} VM for "{{.profile_name}}" ...`, out.V{"driver_name": h.Driver.DriverName(), "profile_name": cfg.GetMachineName()})
} else {
out.T(out.Restarting, `Restarting existing {{.driver_name}} VM for "{{.profile_name}}" ...`, out.V{"driver_name": h.Driver.DriverName(), "profile_name": cfg.GetMachineName()})
if err := h.Driver.Start(); err != nil {
return nil, errors.Wrap(err, "start")
}
if err := api.Save(h); err != nil {
return nil, errors.Wrap(err, "save")
}
}
if err := api.Save(h); err != nil {
return nil, errors.Wrap(err, "save")
} else {
if s == state.Running {
out.T(out.Running, `Re-using the {{.driver_name}} VM for "{{.profile_name}}" ...`, out.V{"driver_name": h.Driver.DriverName(), "profile_name": cfg.GetMachineName()})
} else {
exit.WithCodeT(exit.Unavailable, `Unable to reach {{.driver_name}} VM for "{{.profile_name}}" ...`, out.V{"driver_name": h.Driver.DriverName(), "profile_name": cfg.GetMachineName()})
}
}

Expand Down Expand Up @@ -160,6 +169,7 @@ func configureHost(h *host.Host, e *engine.Options) error {
if err != nil {
return errors.Wrap(err, "detecting provisioner")
}
glog.Infof("Provisioning with %s...", provisioner.String())
glog.Infof("Provisioning: %+v", *h.HostOptions)
if err := provisioner.Provision(*h.HostOptions.SwarmOptions, *h.HostOptions.AuthOptions, *h.HostOptions.EngineOptions); err != nil {
return errors.Wrap(err, "provision")
Expand All @@ -171,6 +181,11 @@ func configureHost(h *host.Host, e *engine.Options) error {
if err := h.ConfigureAuth(); err != nil {
return &util.RetriableError{Err: errors.Wrap(err, "Error configuring auth on host")}
}
if h.Driver.DriverName() == constants.DriverGeneric {
if _, err := h.RunSSHCommand(fmt.Sprintf("sudo usermod -aG docker %s", h.Driver.GetSSHUsername())); err != nil {
return errors.Wrap(err, "usermod")
}
}
return ensureSyncedGuestClock(h)
}

Expand Down Expand Up @@ -244,18 +259,20 @@ func trySSHPowerOff(h *host.Host) {
}

// StopHost stops the host VM, saving state to disk.
func StopHost(api libmachine.API) error {
func StopHost(api libmachine.API, config cfg.MachineConfig) error {
host, err := api.Load(cfg.GetMachineName())
if err != nil {
return errors.Wrapf(err, "load")
}
out.T(out.Stopping, `Stopping "{{.profile_name}}" in {{.driver_name}} ...`, out.V{"profile_name": cfg.GetMachineName(), "driver_name": host.DriverName})
if err := host.Stop(); err != nil {
alreadyInStateError, ok := err.(mcnerror.ErrHostAlreadyInState)
if ok && alreadyInStateError.State == state.Stopped {
return nil
if !localDriver(config.VMDriver) && config.VMDriver != constants.DriverGeneric {
out.T(out.Stopping, `Stopping "{{.profile_name}}" in {{.driver_name}} ...`, out.V{"profile_name": cfg.GetMachineName(), "driver_name": host.DriverName})
if err := host.Stop(); err != nil {
alreadyInStateError, ok := err.(mcnerror.ErrHostAlreadyInState)
if ok && alreadyInStateError.State == state.Stopped {
return nil
}
return &util.RetriableError{Err: errors.Wrapf(err, "Stop: %s", cfg.GetMachineName())}
}
return &util.RetriableError{Err: errors.Wrapf(err, "Stop: %s", cfg.GetMachineName())}
}
return nil
}
Expand Down Expand Up @@ -327,6 +344,7 @@ func engineOptions(config cfg.MachineConfig) *engine.Options {
InsecureRegistry: append([]string{pkgutil.DefaultServiceCIDR}, config.InsecureRegistry...),
RegistryMirror: config.RegistryMirror,
ArbitraryFlags: config.DockerOpt,
InstallURL: drivers.DefaultEngineInstallURL,
}
return &o
}
Expand Down Expand Up @@ -441,6 +459,12 @@ func GetHostDockerEnv(api libmachine.API) (map[string]string, error) {
// GetVMHostIP gets the ip address to be used for mapping host -> VM and VM -> host
func GetVMHostIP(host *host.Host) (net.IP, error) {
switch host.DriverName {
case constants.DriverGeneric:
ip, err := host.Driver.GetIP()
if err != nil {
return []byte{}, errors.Wrap(err, "Error getting VM/Host IP address")
}
return net.ParseIP(ip), nil
case constants.DriverKvm2:
return net.ParseIP("192.168.39.1"), nil
case constants.DriverHyperv:
Expand Down
6 changes: 3 additions & 3 deletions pkg/minikube/cluster/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func TestStartHostConfig(t *testing.T) {
func TestStopHostError(t *testing.T) {
RegisterMockDriver(t)
api := tests.NewMockAPI(t)
if err := StopHost(api); err == nil {
if err := StopHost(api, defaultMachineConfig); err == nil {
t.Fatal("An error should be thrown when stopping non-existing machine.")
}
}
Expand All @@ -256,7 +256,7 @@ func TestStopHost(t *testing.T) {
t.Errorf("createHost failed: %v", err)
}

if err := StopHost(api); err != nil {
if err := StopHost(api, defaultMachineConfig); err != nil {
t.Fatal("An error should be thrown when stopping non-existing machine.")
}
if s, _ := h.Driver.GetState(); s != state.Stopped {
Expand Down Expand Up @@ -327,7 +327,7 @@ func TestGetHostStatus(t *testing.T) {

checkState(state.Running.String())

if err := StopHost(api); err != nil {
if err := StopHost(api, defaultMachineConfig); err != nil {
t.Errorf("StopHost failed: %v", err)
}
checkState(state.Stopped.String())
Expand Down
1 change: 1 addition & 0 deletions pkg/minikube/cluster/default_drivers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cluster

import (
// Import all the default drivers
_ "k8s.io/minikube/pkg/minikube/drivers/generic"
_ "k8s.io/minikube/pkg/minikube/drivers/hyperkit"
_ "k8s.io/minikube/pkg/minikube/drivers/hyperv"
_ "k8s.io/minikube/pkg/minikube/drivers/kvm2"
Expand Down
4 changes: 4 additions & 0 deletions pkg/minikube/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ type MachineConfig struct {
NoVTXCheck bool // Only used by virtualbox
DNSProxy bool // Only used by virtualbox
HostDNSResolver bool // Only used by virtualbox
GenericIPAddress string // Only used by generic
GenericSSHUser string // Only used by generic
GenericSSHKey string // Only used by generic
GenericSSHPort int // Only used by generic
}

// KubernetesConfig contains the parameters used to configure the VM Kubernetes.
Expand Down
3 changes: 3 additions & 0 deletions pkg/minikube/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ const DriverMock = "mock-driver"
// DriverNone is the none driver.
const DriverNone = "none"

// DriverGeneric is the generic driver.
const DriverGeneric = "generic"

// DriverKvm2 is the kvm2 driver option name for in linux
const DriverKvm2 = "kvm2"

Expand Down
1 change: 1 addition & 0 deletions pkg/minikube/constants/constants_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ var SupportedVMDrivers = [...]string{
DriverVmwareFusion,
DriverHyperkit,
DriverVmware,
DriverGeneric,
}
1 change: 1 addition & 0 deletions pkg/minikube/constants/constants_gendocs.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ var SupportedVMDrivers = [...]string{
DriverHyperkit,
DriverKvm2,
DriverVmware,
DriverGeneric,
DriverNone,
}
1 change: 1 addition & 0 deletions pkg/minikube/constants/constants_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ var SupportedVMDrivers = [...]string{
DriverVmwareFusion,
DriverKvm2,
DriverVmware,
DriverGeneric,
DriverNone,
}
1 change: 1 addition & 0 deletions pkg/minikube/constants/constants_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ var SupportedVMDrivers = [...]string{
DriverVmwareFusion,
DriverHyperv,
DriverVmware,
DriverGeneric,
}
17 changes: 17 additions & 0 deletions pkg/minikube/drivers/generic/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
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 generic
58 changes: 58 additions & 0 deletions pkg/minikube/drivers/generic/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
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 generic

import (
"fmt"

"github.com/docker/machine/drivers/generic"
"github.com/docker/machine/libmachine/drivers"
cfg "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/registry"
)

func init() {
err := registry.Register(registry.DriverDef{
Name: constants.DriverGeneric,
Builtin: true,
ConfigCreator: createGenericHost,
DriverCreator: func() drivers.Driver {
return generic.NewDriver("", "")
},
})
if err != nil {
panic(fmt.Sprintf("unable to register: %v", err))
}
}

func createGenericHost(config cfg.MachineConfig) interface{} {
d := generic.NewDriver(cfg.GetMachineName(), constants.GetMinipath())

if config.GenericIPAddress == "" {
exit.UsageT(`Please provide an IP address. the vm-driver "{{.driver_name}}" requires it.`, out.V{"driver_name": constants.DriverGeneric})
}

d.(*generic.Driver).IPAddress = config.GenericIPAddress
d.(*generic.Driver).SSHUser = config.GenericSSHUser
d.(*generic.Driver).SSHKey = config.GenericSSHKey
d.(*generic.Driver).SSHPort = config.GenericSSHPort

return d
}
5 changes: 5 additions & 0 deletions pkg/provision/buildroot.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ func (p *BuildrootProvisioner) String() string {
return "buildroot"
}

// CompatibleWithHost checks if provisioner is compatible with host
func (p *BuildrootProvisioner) CompatibleWithHost() bool {
return p.OsReleaseInfo.ID == "buildroot"
}

// escapeSystemdDirectives escapes special characters in the input variables used to create the
// systemd unit file, which would otherwise be interpreted as systemd directives. An example
// are template specifiers (e.g. '%i') which are predefined variables that get evaluated dynamically
Expand Down