diff --git a/cmd/minikube/cmd/delete.go b/cmd/minikube/cmd/delete.go index 960bd4c37427..ae651a94838c 100644 --- a/cmd/minikube/cmd/delete.go +++ b/cmd/minikube/cmd/delete.go @@ -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)) } diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index 847dbc637450..500b4a4a9dea 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -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" @@ -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 ( @@ -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 @@ -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, diff --git a/cmd/minikube/cmd/stop.go b/cmd/minikube/cmd/stop.go index 8dcd5f58ee36..848e86b2d64f 100644 --- a/cmd/minikube/cmd/stop.go +++ b/cmd/minikube/cmd/stop.go @@ -17,6 +17,7 @@ limitations under the License. package cmd import ( + "os" "time" "github.com/docker/machine/libmachine/mcnerror" @@ -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 { diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index cdf1b02edc4a..992524cf15fa 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -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" @@ -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) @@ -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()}) } } @@ -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") @@ -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) } @@ -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 } @@ -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 } @@ -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: diff --git a/pkg/minikube/cluster/cluster_test.go b/pkg/minikube/cluster/cluster_test.go index bae331fd11bf..f8b21ad47c2d 100644 --- a/pkg/minikube/cluster/cluster_test.go +++ b/pkg/minikube/cluster/cluster_test.go @@ -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.") } } @@ -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 { @@ -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()) diff --git a/pkg/minikube/cluster/default_drivers.go b/pkg/minikube/cluster/default_drivers.go index 99dfe368f7f7..f4a1ba7deb1d 100644 --- a/pkg/minikube/cluster/default_drivers.go +++ b/pkg/minikube/cluster/default_drivers.go @@ -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" diff --git a/pkg/minikube/config/types.go b/pkg/minikube/config/types.go index 5716bb4c3336..d0012c278435 100644 --- a/pkg/minikube/config/types.go +++ b/pkg/minikube/config/types.go @@ -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. diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index 2929079a889c..40580079478e 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -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" diff --git a/pkg/minikube/constants/constants_darwin.go b/pkg/minikube/constants/constants_darwin.go index 707f5f9925ff..58af28b5ed24 100644 --- a/pkg/minikube/constants/constants_darwin.go +++ b/pkg/minikube/constants/constants_darwin.go @@ -27,4 +27,5 @@ var SupportedVMDrivers = [...]string{ DriverVmwareFusion, DriverHyperkit, DriverVmware, + DriverGeneric, } diff --git a/pkg/minikube/constants/constants_gendocs.go b/pkg/minikube/constants/constants_gendocs.go index 19eceaf26b81..1cb485e6da5c 100644 --- a/pkg/minikube/constants/constants_gendocs.go +++ b/pkg/minikube/constants/constants_gendocs.go @@ -29,5 +29,6 @@ var SupportedVMDrivers = [...]string{ DriverHyperkit, DriverKvm2, DriverVmware, + DriverGeneric, DriverNone, } diff --git a/pkg/minikube/constants/constants_linux.go b/pkg/minikube/constants/constants_linux.go index 0fa300617fd6..c10c6919e4a6 100644 --- a/pkg/minikube/constants/constants_linux.go +++ b/pkg/minikube/constants/constants_linux.go @@ -32,5 +32,6 @@ var SupportedVMDrivers = [...]string{ DriverVmwareFusion, DriverKvm2, DriverVmware, + DriverGeneric, DriverNone, } diff --git a/pkg/minikube/constants/constants_windows.go b/pkg/minikube/constants/constants_windows.go index fb0139964a01..07265611405b 100644 --- a/pkg/minikube/constants/constants_windows.go +++ b/pkg/minikube/constants/constants_windows.go @@ -30,4 +30,5 @@ var SupportedVMDrivers = [...]string{ DriverVmwareFusion, DriverHyperv, DriverVmware, + DriverGeneric, } diff --git a/pkg/minikube/drivers/generic/doc.go b/pkg/minikube/drivers/generic/doc.go new file mode 100644 index 000000000000..70fb524fabb2 --- /dev/null +++ b/pkg/minikube/drivers/generic/doc.go @@ -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 diff --git a/pkg/minikube/drivers/generic/driver.go b/pkg/minikube/drivers/generic/driver.go new file mode 100644 index 000000000000..0df30e3e09ab --- /dev/null +++ b/pkg/minikube/drivers/generic/driver.go @@ -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 +} diff --git a/pkg/provision/buildroot.go b/pkg/provision/buildroot.go index 3b59fb111b42..93b8481b36f4 100644 --- a/pkg/provision/buildroot.go +++ b/pkg/provision/buildroot.go @@ -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