2019-12-17 23:18:31 +00:00
/ *
Copyright 2019 The Kubernetes Authors All rights reserved .
2019-12-18 00:38:56 +00:00
2019-12-17 23:18:31 +00:00
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
2019-12-18 00:38:56 +00:00
2019-12-17 23:18:31 +00:00
http : //www.apache.org/licenses/LICENSE-2.0
2019-12-18 00:38:56 +00:00
2019-12-17 23:18:31 +00:00
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 kic
import (
"fmt"
2020-01-24 22:15:48 +00:00
"net"
2019-12-17 23:18:31 +00:00
"os/exec"
"strconv"
"strings"
2020-04-07 21:19:20 +00:00
"sync"
2020-02-20 22:43:24 +00:00
"time"
2019-12-17 23:18:31 +00:00
"github.com/docker/machine/libmachine/drivers"
2020-01-24 02:43:26 +00:00
"github.com/docker/machine/libmachine/ssh"
2019-12-17 23:18:31 +00:00
"github.com/docker/machine/libmachine/state"
2020-01-24 02:43:26 +00:00
"github.com/golang/glog"
2020-01-24 03:20:44 +00:00
"github.com/pkg/errors"
2019-12-17 23:18:31 +00:00
pkgdrivers "k8s.io/minikube/pkg/drivers"
2019-12-18 00:04:03 +00:00
"k8s.io/minikube/pkg/drivers/kic/oci"
2020-01-24 03:20:44 +00:00
"k8s.io/minikube/pkg/minikube/assets"
2019-12-17 23:18:31 +00:00
"k8s.io/minikube/pkg/minikube/command"
2019-12-22 05:20:32 +00:00
"k8s.io/minikube/pkg/minikube/constants"
2020-03-23 23:38:09 +00:00
"k8s.io/minikube/pkg/minikube/cruntime"
2020-03-05 02:01:41 +00:00
"k8s.io/minikube/pkg/minikube/download"
2020-04-09 01:20:30 +00:00
"k8s.io/minikube/pkg/minikube/sysinit"
2020-04-16 23:10:44 +00:00
"k8s.io/minikube/pkg/util/retry"
2019-12-17 23:18:31 +00:00
)
2020-02-13 02:11:44 +00:00
// Driver represents a kic driver https://minikube.sigs.k8s.io/docs/reference/drivers/docker
2019-12-17 23:18:31 +00:00
type Driver struct {
* drivers . BaseDriver
* pkgdrivers . CommonDriver
2019-12-18 05:36:37 +00:00
URL string
exec command . Runner
NodeConfig Config
OCIBinary string // docker,podman
2019-12-17 23:18:31 +00:00
}
// NewDriver returns a fully configured Kic driver
func NewDriver ( c Config ) * Driver {
d := & Driver {
BaseDriver : & drivers . BaseDriver {
MachineName : c . MachineName ,
StorePath : c . StorePath ,
} ,
2019-12-18 05:36:37 +00:00
exec : command . NewKICRunner ( c . MachineName , c . OCIBinary ) ,
NodeConfig : c ,
2019-12-21 22:31:39 +00:00
OCIBinary : c . OCIBinary ,
2019-12-17 23:18:31 +00:00
}
return d
}
// Create a host using the driver's config
func ( d * Driver ) Create ( ) error {
2020-01-31 05:01:27 +00:00
params := oci . CreateParams {
2020-01-24 22:15:48 +00:00
Name : d . NodeConfig . MachineName ,
Image : d . NodeConfig . ImageDigest ,
2020-02-14 09:01:53 +00:00
ClusterLabel : oci . ProfileLabelKey + "=" + d . MachineName ,
2020-03-02 23:21:37 +00:00
NodeLabel : oci . NodeLabelKey + "=" + d . NodeConfig . MachineName ,
2020-01-24 22:15:48 +00:00
CPUs : strconv . Itoa ( d . NodeConfig . CPU ) ,
Memory : strconv . Itoa ( d . NodeConfig . Memory ) + "mb" ,
Envs : d . NodeConfig . Envs ,
ExtraArgs : [ ] string { "--expose" , fmt . Sprintf ( "%d" , d . NodeConfig . APIServerPort ) } ,
OCIBinary : d . NodeConfig . OCIBinary ,
APIServerPort : d . NodeConfig . APIServerPort ,
2019-12-17 23:18:31 +00:00
}
2019-12-18 05:36:37 +00:00
// control plane specific options
params . PortMappings = append ( params . PortMappings , oci . PortMapping {
2020-02-13 02:11:44 +00:00
ListenAddress : oci . DefaultBindIPV4 ,
2020-03-11 01:33:59 +00:00
ContainerPort : int32 ( params . APIServerPort ) ,
2020-01-24 01:45:50 +00:00
} ,
oci . PortMapping {
2020-02-13 02:11:44 +00:00
ListenAddress : oci . DefaultBindIPV4 ,
2020-01-24 01:45:50 +00:00
ContainerPort : constants . SSHPort ,
} ,
2020-01-30 22:30:04 +00:00
oci . PortMapping {
2020-02-13 02:11:44 +00:00
ListenAddress : oci . DefaultBindIPV4 ,
2020-01-30 22:30:04 +00:00
ContainerPort : constants . DockerDaemonPort ,
} ,
2020-04-10 13:20:12 +00:00
oci . PortMapping {
ListenAddress : oci . DefaultBindIPV4 ,
2020-04-10 23:50:57 +00:00
ContainerPort : constants . RegistryAddonPort ,
2020-04-10 13:20:12 +00:00
} ,
2020-01-24 01:45:50 +00:00
)
2020-03-06 00:42:56 +00:00
2020-03-06 06:31:44 +00:00
exists , err := oci . ContainerExists ( d . OCIBinary , params . Name )
2020-03-06 00:42:56 +00:00
if err != nil {
2020-03-06 06:38:47 +00:00
glog . Warningf ( "failed to check if container already exists: %v" , err )
2020-03-06 00:42:56 +00:00
}
2020-03-06 06:31:44 +00:00
if exists {
// if container was created by minikube it is safe to delete and recreate it.
if oci . IsCreatedByMinikube ( d . OCIBinary , params . Name ) {
glog . Info ( "Found already existing abandoned minikube container, will try to delete." )
2020-03-06 06:38:47 +00:00
if err := oci . DeleteContainer ( d . OCIBinary , params . Name ) ; err != nil {
2020-03-06 18:25:48 +00:00
glog . Errorf ( "Failed to delete a conflicting minikube container %s. You might need to restart your %s daemon and delete it manually and try again: %v" , params . Name , params . OCIBinary , err )
2020-03-06 00:42:56 +00:00
}
2020-03-06 06:31:44 +00:00
} else {
2020-03-06 06:34:10 +00:00
// The conflicting container name was not created by minikube
// user has a container that conflicts with minikube profile name, will not delete users container.
2020-03-06 18:25:48 +00:00
return errors . Wrapf ( err , "user has a conflicting container name %q with minikube container. Needs to be deleted by user's consent." , params . Name )
2020-03-06 00:42:56 +00:00
}
}
2020-04-08 20:44:04 +00:00
if err := oci . PrepareContainerNode ( params ) ; err != nil {
2020-04-07 21:19:20 +00:00
return errors . Wrap ( err , "setting up container node" )
}
var waitForPreload sync . WaitGroup
waitForPreload . Add ( 1 )
go func ( ) {
defer waitForPreload . Done ( )
// If preload doesn't exist, don't bother extracting tarball to volume
if ! download . PreloadExists ( d . NodeConfig . KubernetesVersion , d . NodeConfig . ContainerRuntime ) {
return
}
t := time . Now ( )
2020-04-20 02:59:50 +00:00
glog . Infof ( "Starting extracting preloaded images to volume ...." )
2020-04-07 21:19:20 +00:00
// Extract preloaded images to container
if err := oci . ExtractTarballToVolume ( download . TarballPath ( d . NodeConfig . KubernetesVersion , d . NodeConfig . ContainerRuntime ) , params . Name , BaseImage ) ; err != nil {
glog . Infof ( "Unable to extract preloaded tarball to volume: %v" , err )
} else {
2020-04-07 22:00:28 +00:00
glog . Infof ( "duration metric: took %f seconds to extract preloaded images to volume" , time . Since ( t ) . Seconds ( ) )
2020-04-07 21:19:20 +00:00
}
} ( )
2020-03-02 21:33:27 +00:00
if err := oci . CreateContainerNode ( params ) ; err != nil {
2020-01-24 03:20:44 +00:00
return errors . Wrap ( err , "create kic node" )
}
if err := d . prepareSSH ( ) ; err != nil {
return errors . Wrap ( err , "prepare kic ssh" )
}
2020-03-02 21:33:27 +00:00
2020-04-07 21:19:20 +00:00
waitForPreload . Wait ( )
2020-01-24 03:20:44 +00:00
return nil
}
// prepareSSH will generate keys and copy to the container so minikube ssh works
func ( d * Driver ) prepareSSH ( ) error {
2020-01-24 02:43:26 +00:00
keyPath := d . GetSSHKeyPath ( )
glog . Infof ( "Creating ssh key for kic: %s..." , keyPath )
if err := ssh . GenerateSSHKey ( keyPath ) ; err != nil {
return errors . Wrap ( err , "generate ssh key" )
}
2020-01-24 03:20:44 +00:00
cmder := command . NewKICRunner ( d . NodeConfig . MachineName , d . NodeConfig . OCIBinary )
f , err := assets . NewFileAsset ( d . GetSSHKeyPath ( ) + ".pub" , "/home/docker/.ssh/" , "authorized_keys" , "0644" )
2019-12-17 23:18:31 +00:00
if err != nil {
2020-01-24 03:20:44 +00:00
return errors . Wrap ( err , "create pubkey assetfile " )
}
if err := cmder . Copy ( f ) ; err != nil {
return errors . Wrap ( err , "copying pub key" )
2019-12-17 23:18:31 +00:00
}
2020-01-24 03:20:44 +00:00
if rr , err := cmder . RunCmd ( exec . Command ( "chown" , "docker:docker" , "/home/docker/.ssh/authorized_keys" ) ) ; err != nil {
return errors . Wrapf ( err , "apply authorized_keys file ownership, output %s" , rr . Output ( ) )
}
2019-12-17 23:18:31 +00:00
return nil
}
// DriverName returns the name of the driver
func ( d * Driver ) DriverName ( ) string {
2020-03-04 18:37:10 +00:00
if d . NodeConfig . OCIBinary == oci . Podman {
return oci . Podman
2019-12-17 23:18:31 +00:00
}
2020-03-04 18:37:10 +00:00
return oci . Docker
2019-12-17 23:18:31 +00:00
}
// GetIP returns an IP or hostname that this host is available at
func ( d * Driver ) GetIP ( ) ( string , error ) {
2020-01-31 00:20:55 +00:00
ip , _ , err := oci . ContainerIPs ( d . OCIBinary , d . MachineName )
2019-12-17 23:18:31 +00:00
return ip , err
}
2020-01-31 00:20:55 +00:00
// GetExternalIP returns an IP which is accissble from outside
func ( d * Driver ) GetExternalIP ( ) ( string , error ) {
2020-02-13 02:11:44 +00:00
return oci . DefaultBindIPV4 , nil
2020-01-31 00:20:55 +00:00
}
2019-12-17 23:18:31 +00:00
// GetSSHHostname returns hostname for use with ssh
func ( d * Driver ) GetSSHHostname ( ) ( string , error ) {
2020-02-13 02:11:44 +00:00
return oci . DefaultBindIPV4 , nil
2019-12-17 23:18:31 +00:00
}
// GetSSHPort returns port for use with ssh
func ( d * Driver ) GetSSHPort ( ) ( int , error ) {
2020-03-14 15:58:12 +00:00
p , err := oci . ForwardedPort ( d . OCIBinary , d . MachineName , constants . SSHPort )
2020-01-24 07:51:41 +00:00
if err != nil {
2020-01-24 22:15:48 +00:00
return p , errors . Wrap ( err , "get ssh host-port" )
2020-01-24 07:51:41 +00:00
}
2020-01-24 22:15:48 +00:00
return p , nil
}
2020-01-24 22:42:28 +00:00
// GetSSHUsername returns the ssh username
func ( d * Driver ) GetSSHUsername ( ) string {
return "docker"
}
2020-01-24 22:15:48 +00:00
// GetSSHKeyPath returns the ssh key path
func ( d * Driver ) GetSSHKeyPath ( ) string {
if d . SSHKeyPath == "" {
d . SSHKeyPath = d . ResolveStorePath ( "id_rsa" )
}
return d . SSHKeyPath
2019-12-17 23:18:31 +00:00
}
2020-03-13 17:37:29 +00:00
// GetURL returns a Docker URL inside this host
// e.g. tcp://1.2.3.4:2376
// more info https://github.com/docker/machine/blob/b170508bf44c3405e079e26d5fdffe35a64c6972/libmachine/provision/utils.go#L159_L175
2019-12-17 23:18:31 +00:00
func ( d * Driver ) GetURL ( ) ( string , error ) {
2020-03-12 23:01:49 +00:00
ip , err := d . GetIP ( )
if err != nil {
return "" , err
}
url := fmt . Sprintf ( "tcp://%s" , net . JoinHostPort ( ip , "2376" ) )
2020-01-24 22:15:48 +00:00
return url , nil
2019-12-17 23:18:31 +00:00
}
// GetState returns the state that the host is in (running, stopped, etc)
func ( d * Driver ) GetState ( ) ( state . State , error ) {
2020-03-26 21:03:22 +00:00
out , err := oci . WarnIfSlow ( d . NodeConfig . OCIBinary , "inspect" , "-f" , "{{.State.Status}}" , d . MachineName )
2019-12-17 23:18:31 +00:00
if err != nil {
2020-03-26 21:03:22 +00:00
return state . Error , err
2019-12-17 23:18:31 +00:00
}
2020-03-26 21:03:22 +00:00
o := strings . TrimSpace ( string ( out ) )
2020-01-08 08:21:23 +00:00
switch o {
case "running" :
2019-12-17 23:18:31 +00:00
return state . Running , nil
2020-01-08 08:21:23 +00:00
case "exited" :
2019-12-17 23:18:31 +00:00
return state . Stopped , nil
2020-01-08 08:21:23 +00:00
case "paused" :
2019-12-17 23:18:31 +00:00
return state . Paused , nil
2020-01-08 08:21:23 +00:00
case "restarting" :
2019-12-17 23:18:31 +00:00
return state . Starting , nil
2020-01-08 08:21:23 +00:00
case "dead" :
2019-12-17 23:18:31 +00:00
return state . Error , nil
2020-01-08 08:21:23 +00:00
default :
return state . None , fmt . Errorf ( "unknown state" )
2019-12-17 23:18:31 +00:00
}
}
// Kill stops a host forcefully, including any containers that we are managing.
func ( d * Driver ) Kill ( ) error {
2020-03-24 03:39:21 +00:00
// on init this doesn't get filled when called from cmd
d . exec = command . NewKICRunner ( d . MachineName , d . OCIBinary )
2020-04-09 01:20:30 +00:00
if err := sysinit . New ( d . exec ) . ForceStop ( "kubelet" ) ; err != nil {
2020-03-23 21:07:47 +00:00
glog . Warningf ( "couldn't force stop kubelet. will continue with kill anyways: %v" , err )
}
2020-04-16 23:10:44 +00:00
if err := oci . ShutDown ( d . OCIBinary , d . MachineName ) ; err != nil {
2020-04-16 23:53:04 +00:00
glog . Warningf ( "couldn't shutdown the container, will continue with kill anyways: %v" , err )
2020-04-16 23:10:44 +00:00
}
cr := command . NewExecRunner ( ) // using exec runner for interacting with dameon.
if _ , err := cr . RunCmd ( exec . Command ( d . NodeConfig . OCIBinary , "kill" , d . MachineName ) ) ; err != nil {
return errors . Wrapf ( err , "killing %q" , d . MachineName )
2019-12-17 23:18:31 +00:00
}
return nil
}
// Remove will delete the Kic Node Container
func ( d * Driver ) Remove ( ) error {
2020-01-31 05:36:56 +00:00
if _ , err := oci . ContainerID ( d . OCIBinary , d . MachineName ) ; err != nil {
2020-04-15 04:31:12 +00:00
glog . Infof ( "could not find the container %s to remove it. will try anyways" , d . MachineName )
2019-12-17 23:18:31 +00:00
}
2020-04-15 03:51:18 +00:00
if err := oci . DeleteContainer ( d . NodeConfig . OCIBinary , d . MachineName ) ; err != nil {
if strings . Contains ( err . Error ( ) , "is already in progress" ) {
2020-04-15 17:17:23 +00:00
return errors . Wrap ( err , "stuck delete" )
2020-01-31 05:36:56 +00:00
}
2020-04-15 03:51:18 +00:00
if strings . Contains ( err . Error ( ) , "No such container:" ) {
2020-04-15 17:17:23 +00:00
return nil // nothing was found to delete.
2020-04-15 03:51:18 +00:00
}
2019-12-17 23:18:31 +00:00
}
2020-04-15 05:20:28 +00:00
2020-04-15 17:17:23 +00:00
// check there be no container left after delete
2020-04-15 05:20:28 +00:00
if id , err := oci . ContainerID ( d . OCIBinary , d . MachineName ) ; err == nil && id != "" {
return fmt . Errorf ( "expected no container ID be found for %q after delete. but got %q" , d . MachineName , id )
}
2019-12-17 23:18:31 +00:00
return nil
}
// Restart a host
func ( d * Driver ) Restart ( ) error {
s , err := d . GetState ( )
if err != nil {
2020-04-16 23:10:44 +00:00
glog . Warningf ( "get state during restart: %v" , err )
2019-12-17 23:18:31 +00:00
}
2020-04-16 23:10:44 +00:00
if s == state . Stopped { // don't stop if already stopped
2019-12-17 23:18:31 +00:00
return d . Start ( )
}
2020-04-16 23:10:44 +00:00
if err = d . Stop ( ) ; err != nil {
return fmt . Errorf ( "stop during restart %v" , err )
}
if err = d . Start ( ) ; err != nil {
return fmt . Errorf ( "start during restart %v" , err )
}
return nil
2019-12-17 23:18:31 +00:00
}
2020-04-16 23:10:44 +00:00
// Start an already created kic container
2019-12-17 23:18:31 +00:00
func ( d * Driver ) Start ( ) error {
2020-04-16 23:20:04 +00:00
cr := command . NewExecRunner ( ) // using exec runner for interacting with docker/podman daemon
2020-04-16 23:10:44 +00:00
if _ , err := cr . RunCmd ( exec . Command ( d . NodeConfig . OCIBinary , "start" , d . MachineName ) ) ; err != nil {
2020-04-16 23:20:04 +00:00
return errors . Wrap ( err , "start" )
2019-12-17 23:18:31 +00:00
}
2020-04-16 23:10:44 +00:00
checkRunning := func ( ) error {
s , err := oci . ContainerStatus ( d . NodeConfig . OCIBinary , d . MachineName )
if err != nil {
return err
}
if s != state . Running {
return fmt . Errorf ( "expected container state be running but got %q" , s )
2019-12-17 23:18:31 +00:00
}
2020-04-16 23:10:44 +00:00
glog . Infof ( "container %q state is running." , d . MachineName )
2019-12-17 23:18:31 +00:00
return nil
}
2020-04-16 23:10:44 +00:00
if err := retry . Expo ( checkRunning , 500 * time . Microsecond , time . Second * 30 ) ; err != nil {
return err
}
return nil
2019-12-17 23:18:31 +00:00
}
// Stop a host gracefully, including any containers that we are managing.
func ( d * Driver ) Stop ( ) error {
2020-03-24 03:39:21 +00:00
// on init this doesn't get filled when called from cmd
d . exec = command . NewKICRunner ( d . MachineName , d . OCIBinary )
2020-03-23 20:34:36 +00:00
// docker does not send right SIG for systemd to know to stop the systemd.
2020-03-23 20:41:15 +00:00
// to avoid bind address be taken on an upgrade. more info https://github.com/kubernetes/minikube/issues/7171
2020-04-09 01:20:30 +00:00
if err := sysinit . New ( d . exec ) . Stop ( "kubelet" ) ; err != nil {
2020-03-23 20:41:15 +00:00
glog . Warningf ( "couldn't stop kubelet. will continue with stop anyways: %v" , err )
2020-04-09 01:20:30 +00:00
if err := sysinit . New ( d . exec ) . ForceStop ( "kubelet" ) ; err != nil {
2020-03-23 23:38:09 +00:00
glog . Warningf ( "couldn't force stop kubelet. will continue with stop anyways: %v" , err )
}
}
runtime , err := cruntime . New ( cruntime . Config { Type : d . NodeConfig . ContainerRuntime , Runner : d . exec } )
if err != nil { // won't return error because:
// even though we can't stop the cotainers inside, we still wanna stop the minikube container itself
glog . Errorf ( "unable to get container runtime: %v" , err )
} else {
containers , err := runtime . ListContainers ( cruntime . ListOptions { Namespaces : constants . DefaultNamespaces } )
if err != nil {
2020-03-24 00:43:21 +00:00
glog . Infof ( "unable list containers : %v" , err )
2020-03-23 23:38:09 +00:00
}
if len ( containers ) > 0 {
if err := runtime . StopContainers ( containers ) ; err != nil {
2020-04-10 03:01:11 +00:00
glog . Infof ( "unable to stop containers : %v" , err )
}
if err := runtime . KillContainers ( containers ) ; err != nil {
glog . Errorf ( "unable to kill containers : %v" , err )
2020-03-23 23:38:09 +00:00
}
}
glog . Infof ( "successfully stopped kubernetes!" )
2020-03-23 20:34:36 +00:00
}
2020-04-10 04:50:09 +00:00
if err := killAPIServerProc ( d . exec ) ; err != nil {
2020-04-10 03:53:15 +00:00
glog . Warningf ( "couldn't stop kube-apiserver proc: %v" , err )
}
2019-12-18 05:36:37 +00:00
cmd := exec . Command ( d . NodeConfig . OCIBinary , "stop" , d . MachineName )
2019-12-17 23:18:31 +00:00
if err := cmd . Run ( ) ; err != nil {
return errors . Wrapf ( err , "stopping %s" , d . MachineName )
}
return nil
}
2020-04-15 05:20:28 +00:00
// RunSSHCommandFromDriver implements direct ssh control to the driver
func ( d * Driver ) RunSSHCommandFromDriver ( ) error {
return fmt . Errorf ( "driver does not support RunSSHCommandFromDriver commands" )
}
2020-04-10 04:50:09 +00:00
// killAPIServerProc will kill an api server proc if it exists
// to ensure this never happens https://github.com/kubernetes/minikube/issues/7521
func killAPIServerProc ( runner command . Runner ) error {
// first check if it exists
rr , err := runner . RunCmd ( exec . Command ( "pgrep" , "kube-apiserver" ) )
if err == nil { // this means we might have a running kube-apiserver
pid , err := strconv . Atoi ( rr . Stdout . String ( ) )
if err == nil { // this means we have a valid pid
2020-04-10 04:56:00 +00:00
glog . Warningf ( "Found a kube-apiserver running with pid %d, will try to kill the proc" , pid )
2020-04-10 04:50:09 +00:00
if _ , err = runner . RunCmd ( exec . Command ( "pkill" , "-9" , string ( pid ) ) ) ; err != nil {
return errors . Wrap ( err , "kill" )
}
}
}
return nil
}