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 new driver "SSH" to bootstrap generic minkube clusters over ssh #10099

Merged
merged 32 commits into from
Jan 19, 2021
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0076335
Add minikube support for the "generic" VM driver
afbjorklund Jul 10, 2019
c076669
Don't download ISO for the generic VM driver
afbjorklund Jul 14, 2019
7c5ba62
Generic driver does not add user to docker group
afbjorklund Jul 14, 2019
2b5c5b9
Try to uninstall kubernetes on delete, for generic
afbjorklund Jul 14, 2019
6dad258
Don't try to start/stop drivers without VMs
afbjorklund Jul 15, 2019
d0260cc
Allow actually using the generic driver
afbjorklund Oct 28, 2020
d3ea174
Call DetectProvisioner for the generic driver
afbjorklund Oct 28, 2020
c3a23cd
Fix failing unit test for MachineType
afbjorklund Oct 29, 2020
d96d3be
Log the os-release also for the generic driver
afbjorklund Oct 29, 2020
a0d2c1a
Need to set up docker group in start - not in fix
afbjorklund Oct 29, 2020
a3b3b55
Add helper for checking the generic driver name
afbjorklund Nov 14, 2020
fa05fcd
Fork the generic driver to the minikube code base
afbjorklund Jan 6, 2021
a77e3de
Use CommonDrivers and drop machine GetCreateFlags
afbjorklund Jan 6, 2021
a120c54
Change meaning of Stop/Start/Restart to minikube
afbjorklund Jan 6, 2021
72a0fd6
Move the constants for flag defaults from drivers
afbjorklund Jan 6, 2021
1d6c86c
Also remove unused machine SetConfigFromFlags
afbjorklund Jan 6, 2021
1d5ee9e
OK to call Stop when implementation is changed
afbjorklund Jan 6, 2021
7498432
Merge branch 'master' into generic
afbjorklund Jan 8, 2021
3921836
Improve help text for generic driver flags
afbjorklund Jan 8, 2021
d51443b
Show remote host info and proper progress
afbjorklund Oct 29, 2020
010e5fb
Add some error checking on the parsed data fields
afbjorklund Oct 30, 2020
e4ebaea
Complete the container runtime and config change
afbjorklund Jan 8, 2021
fb6cf6b
Add placeholder for docs for the generic driver
afbjorklund Jan 8, 2021
40ec0e8
Avoid the hard-coded dependency on systemctl
afbjorklund Jan 8, 2021
e53f537
Merge branch 'master' into generic
afbjorklund Jan 9, 2021
b2121ea
Rename the generic driver to the ssh driver
afbjorklund Jan 9, 2021
1663a03
Only add docker group for the docker runtime
afbjorklund Jan 11, 2021
529d2c3
Prevent trying to use localhost as address
afbjorklund Jan 11, 2021
abb556f
Merge branch 'master' into generic
afbjorklund Jan 13, 2021
099c6b7
The MachineName function was moved to config
afbjorklund Jan 13, 2021
4611e2f
Merge branch 'master' into generic
afbjorklund Jan 16, 2021
541193c
Address review comments, rename to ssh-ip-address
afbjorklund Jan 16, 2021
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
2 changes: 1 addition & 1 deletion cmd/minikube/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ func deleteProfile(profile *config.Profile) error {
return DeletionError{Err: delErr, Errtype: MissingProfile}
}

if err == nil && driver.BareMetal(cc.Driver) {
if err == nil && (driver.BareMetal(cc.Driver) || driver.IsGeneric(cc.Driver)) {
if err := uninstallKubernetes(api, *cc, cc.Nodes[0], viper.GetString(cmdcfg.Bootstrapper)); err != nil {
deletionError, ok := err.(DeletionError)
if ok {
Expand Down
2 changes: 1 addition & 1 deletion cmd/minikube/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ func provisionWithDriver(cmd *cobra.Command, ds registry.DriverState, existing *
os.Exit(0)
}

if driver.IsVM(driverName) {
if driver.IsVM(driverName) && !driver.IsGeneric(driverName) {
url, err := download.ISO(viper.GetStringSlice(isoURL), cmd.Flags().Changed(isoURL))
if err != nil {
return node.Starter{}, errors.Wrap(err, "Failed to cache ISO")
Expand Down
16 changes: 16 additions & 0 deletions cmd/minikube/cmd/start_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ const (
network = "network"
startNamespace = "namespace"
trace = "trace"
genericIPAddress = "generic-ip-address"
genericSSHUser = "generic-ssh-user"
genericSSHKey = "generic-ssh-key"
genericSSHPort = "generic-ssh-port"
defaultSSHUser = "root"
defaultSSHPort = 22
)

var (
Expand Down Expand Up @@ -221,6 +227,12 @@ func initNetworkingFlags() {
startCmd.Flags().String(serviceCIDR, constants.DefaultServiceCIDR, "The CIDR to be used for service cluster IPs.")
startCmd.Flags().StringArrayVar(&config.DockerEnv, "docker-env", nil, "Environment variables to pass to the Docker daemon. (format: key=value)")
startCmd.Flags().StringArrayVar(&config.DockerOpt, "docker-opt", nil, "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)")

// generic
startCmd.Flags().String(genericIPAddress, "", "IP address (generic)")
afbjorklund marked this conversation as resolved.
Show resolved Hide resolved
startCmd.Flags().String(genericSSHUser, defaultSSHUser, "SSH user (generic)")
afbjorklund marked this conversation as resolved.
Show resolved Hide resolved
startCmd.Flags().String(genericSSHKey, "", "SSH key (generic)")
startCmd.Flags().Int(genericSSHPort, defaultSSHPort, "SSH port (generic)")
}

// ClusterFlagValue returns the current cluster name based on flags
Expand Down Expand Up @@ -335,6 +347,10 @@ func generateClusterConfig(cmd *cobra.Command, existing *config.ClusterConfig, k
NatNicType: viper.GetString(natNicType),
StartHostTimeout: viper.GetDuration(waitTimeout),
ExposedPorts: viper.GetStringSlice(ports),
GenericIPAddress: viper.GetString(genericIPAddress),
GenericSSHUser: viper.GetString(genericSSHUser),
GenericSSHKey: viper.GetString(genericSSHKey),
GenericSSHPort: viper.GetInt(genericSSHPort),
KubernetesConfig: config.KubernetesConfig{
KubernetesVersion: k8sVersion,
ClusterName: ClusterFlagValue(),
Expand Down
230 changes: 230 additions & 0 deletions pkg/drivers/generic/generic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*
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"
"net"
"os"
"os/exec"
"path"
"strconv"
"time"

"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/libmachine/state"
"k8s.io/klog/v2"
pkgdrivers "k8s.io/minikube/pkg/drivers"
"k8s.io/minikube/pkg/minikube/command"
"k8s.io/minikube/pkg/minikube/sysinit"
)

type Driver struct {
*drivers.BaseDriver
*pkgdrivers.CommonDriver
EnginePort int
SSHKey string
}

const (
defaultTimeout = 15 * time.Second
)

// NewDriver creates and returns a new instance of the driver
func NewDriver(hostName, storePath string) *Driver {
return &Driver{
EnginePort: engine.DefaultPort,
BaseDriver: &drivers.BaseDriver{
MachineName: hostName,
StorePath: storePath,
},
}
}

// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
return "generic"
}

func (d *Driver) GetSSHHostname() (string, error) {
return d.GetIP()
}

func (d *Driver) GetSSHUsername() string {
return d.SSHUser
}

func (d *Driver) GetSSHKeyPath() string {
return d.SSHKeyPath
}

func (d *Driver) PreCreateCheck() error {
if d.SSHKey != "" {
if _, err := os.Stat(d.SSHKey); os.IsNotExist(err) {
return fmt.Errorf("SSH key does not exist: %q", d.SSHKey)
}

// TODO: validate the key is a valid key
}

return nil
}

func (d *Driver) Create() error {
if d.SSHKey == "" {
log.Info("No SSH key specified. Assuming an existing key at the default location.")
} else {
log.Info("Importing SSH key...")

d.SSHKeyPath = d.ResolveStorePath(path.Base(d.SSHKey))
if err := copySSHKey(d.SSHKey, d.SSHKeyPath); err != nil {
return err
}

if err := copySSHKey(d.SSHKey+".pub", d.SSHKeyPath+".pub"); err != nil {
log.Infof("Couldn't copy SSH public key : %s", err)
}
}

log.Debugf("IP: %s", d.IPAddress)

return nil
}

func (d *Driver) GetURL() (string, error) {
if err := drivers.MustBeRunning(d); err != nil {
return "", err
}

ip, err := d.GetIP()
if err != nil {
return "", err
}

return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, strconv.Itoa(d.EnginePort))), nil
}

func (d *Driver) GetState() (state.State, error) {
address := net.JoinHostPort(d.IPAddress, strconv.Itoa(d.SSHPort))

_, err := net.DialTimeout("tcp", address, defaultTimeout)
if err != nil {
return state.Stopped, nil
}

return state.Running, nil
}

// Start a host
func (d *Driver) Start() error {
return nil
}

// Stop a host gracefully, including any containers that we are managing.
func (d *Driver) Stop() error {
exec := command.NewSSHRunner(d)
if err := sysinit.New(exec).Stop("kubelet"); err != nil {
klog.Warningf("couldn't stop kubelet. will continue with stop anyways: %v", err)
if err := sysinit.New(exec).ForceStop("kubelet"); err != nil {
klog.Warningf("couldn't force stop kubelet. will continue with stop anyways: %v", err)
}
}
/* TODO
afbjorklund marked this conversation as resolved.
Show resolved Hide resolved
containers, err := d.runtime.ListContainers(cruntime.ListOptions{})
if err != nil {
return errors.Wrap(err, "containers")
}
if len(containers) > 0 {
if err := d.runtime.StopContainers(containers); err != nil {
return errors.Wrap(err, "stop containers")
}
}
*/
klog.Infof("generic driver is stopped!")
return nil
}

// Restart a host
func (d *Driver) Restart() error {
exec := command.NewSSHRunner(d)
return restartKubelet(exec)
}

// Kill stops a host forcefully, including any containers that we are managing.
func (d *Driver) Kill() error {
exec := command.NewSSHRunner(d)
if err := sysinit.New(exec).ForceStop("kubelet"); err != nil {
klog.Warningf("couldn't force stop kubelet. will continue with kill anyways: %v", err)
}

/* TODO
afbjorklund marked this conversation as resolved.
Show resolved Hide resolved
// First try to gracefully stop containers
containers, err := d.runtime.ListContainers(cruntime.ListOptions{})
if err != nil {
return errors.Wrap(err, "containers")
}
if len(containers) == 0 {
return nil
}
// Try to be graceful before sending SIGKILL everywhere.
if err := d.runtime.StopContainers(containers); err != nil {
return errors.Wrap(err, "stop")
}

containers, err = d.runtime.ListContainers(cruntime.ListOptions{})
if err != nil {
return errors.Wrap(err, "containers")
}
if len(containers) == 0 {
return nil
}
if err := d.runtime.KillContainers(containers); err != nil {
return errors.Wrap(err, "kill")
}
*/
return nil

}

func (d *Driver) Remove() error {
return nil
}

func copySSHKey(src, dst string) error {
if err := mcnutils.CopyFile(src, dst); err != nil {
return fmt.Errorf("unable to copy ssh key: %s", err)
}

if err := os.Chmod(dst, 0600); err != nil {
return fmt.Errorf("unable to set permissions on the ssh key: %s", err)
}

return nil
}

// restartKubelet restarts the kubelet
func restartKubelet(cr command.Runner) error {
klog.Infof("restarting kubelet.service ...")
c := exec.Command("sudo", "systemctl", "restart", "kubelet.service")
if _, err := cr.RunCmd(c); err != nil {
return err
}
return nil
}
6 changes: 6 additions & 0 deletions pkg/minikube/cluster/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ func HostIP(host *host.Host, clusterName string) (net.IP, error) {
return oci.RoutableHostIPFromInside(oci.Docker, clusterName, host.Name)
case driver.Podman:
return oci.RoutableHostIPFromInside(oci.Podman, clusterName, host.Name)
case driver.Generic:
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 driver.KVM2:
return net.ParseIP("192.168.39.1"), nil
case driver.HyperV:
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 @@ -66,6 +66,10 @@ type ClusterConfig struct {
HostDNSResolver bool // Only used by virtualbox
HostOnlyNicType string // Only used by virtualbox
NatNicType string // 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 KubernetesConfig
Nodes []Node
Addons map[string]bool
Expand Down
11 changes: 11 additions & 0 deletions pkg/minikube/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const (
Mock = "mock"
// None driver
None = "none"
// Generic driver
Generic = "generic"
// KVM2 driver
KVM2 = "kvm2"
// VirtualBox driver
Expand Down Expand Up @@ -97,6 +99,10 @@ func MachineType(name string) string {
return "container"
}

if IsGeneric(name) {
return "bare metal machine"
}

if IsVM(name) {
return "VM"
}
Expand Down Expand Up @@ -144,6 +150,11 @@ func BareMetal(name string) bool {
return name == None || name == Mock
}

// IsGeneric checks if the driver is generic
func IsGeneric(name string) bool {
return name == Generic
}

// NeedsPortForward returns true if driver is unable provide direct IP connectivity
func NeedsPortForward(name string) bool {
if !IsKIC(name) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/minikube/driver/driver_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var supportedDrivers = []string{
HyperKit,
VMware,
Docker,
Podman,
Generic,
}

func VBoxManagePath() string {
Expand Down
1 change: 1 addition & 0 deletions pkg/minikube/driver/driver_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var supportedDrivers = []string{
None,
Docker,
Podman,
Generic,
}

// VBoxManagePath returns the path to the VBoxManage command
Expand Down
1 change: 1 addition & 0 deletions pkg/minikube/driver/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func TestMachineType(t *testing.T) {
Docker: "container",
Mock: "bare metal machine",
None: "bare metal machine",
Generic: "bare metal machine",
KVM2: "VM",
VirtualBox: "VM",
HyperKit: "VM",
Expand Down
1 change: 1 addition & 0 deletions pkg/minikube/driver/driver_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var supportedDrivers = []string{
HyperV,
VMware,
Docker,
Generic,
}

// TODO: medyagh add same check for kic docker
Expand Down
2 changes: 1 addition & 1 deletion pkg/minikube/machine/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func fastDetectProvisioner(h *host.Host) (libprovision.Provisioner, error) {
switch {
case driver.IsKIC(d):
return provision.NewUbuntuProvisioner(h.Driver), nil
case driver.BareMetal(d):
case driver.BareMetal(d), driver.IsGeneric(d):
return libprovision.DetectProvisioner(h.Driver)
default:
return provision.NewBuildrootProvisioner(h.Driver), nil
Expand Down
Loading