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 (
2020-02-26 20:44:25 +00:00
"context"
2019-12-17 23:18:31 +00:00
"fmt"
2020-01-24 22:15:48 +00:00
"net"
2019-12-17 23:18:31 +00:00
"os/exec"
"strconv"
"strings"
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-31 05:36:56 +00:00
"github.com/docker/machine/libmachine/log"
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-05 02:01:41 +00:00
"k8s.io/minikube/pkg/minikube/download"
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-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-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
t := time . Now ( )
glog . Infof ( "Starting extracting preloaded images to volume" )
// Extract preloaded images to container
2020-03-05 02:01:41 +00:00
if err := oci . ExtractTarballToVolume ( download . TarballPath ( d . NodeConfig . KubernetesVersion ) , params . Name , BaseImage ) ; err != nil {
2020-03-02 22:10:41 +00:00
glog . Infof ( "Unable to extract preloaded tarball to volume: %v" , err )
} else {
glog . Infof ( "Took %f seconds to extract preloaded images to volume" , time . Since ( t ) . Seconds ( ) )
2020-03-02 21:33:27 +00:00
}
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-01-24 22:15:48 +00:00
p , err := oci . HostPortBinding ( 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
}
// GetURL returns ip of the container running kic control-panel
func ( d * Driver ) GetURL ( ) ( string , error ) {
2020-03-12 23:01:49 +00:00
ip , err := d . GetIP ( )
if err != nil {
return "" , err
}
2020-03-12 22:31:38 +00:00
// because of libmachine problem with detecting docker port https://github.com/kubernetes/minikube/issues/7017
// we have to do this hack https://github.com/docker/machine/blob/master/libmachine/provision/utils.go#L159_L175
2020-03-12 23:01:49 +00:00
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-02-26 20:44:25 +00:00
// allow no more than 2 seconds for this. when this takes long this means deadline passed
ctx , cancel := context . WithTimeout ( context . Background ( ) , 2 * time . Second )
defer cancel ( )
2020-02-05 00:09:43 +00:00
2020-02-26 20:44:25 +00:00
cmd := exec . CommandContext ( ctx , d . NodeConfig . OCIBinary , "inspect" , "-f" , "{{.State.Status}}" , d . MachineName )
2019-12-17 23:18:31 +00:00
out , err := cmd . CombinedOutput ( )
2020-02-26 20:44:25 +00:00
if ctx . Err ( ) == context . DeadlineExceeded {
glog . Errorf ( "GetState for %s took longer than normal. Restarting your %s daemon might fix this issue." , d . MachineName , d . OCIBinary )
return state . Error , fmt . Errorf ( "inspect %s timeout" , d . MachineName )
}
2020-02-13 02:11:44 +00:00
o := strings . TrimSpace ( string ( out ) )
2019-12-17 23:18:31 +00:00
if err != nil {
2020-01-31 05:36:56 +00:00
return state . Error , errors . Wrapf ( err , "get container %s status" , d . MachineName )
2019-12-17 23:18:31 +00:00
}
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 {
2019-12-18 05:36:37 +00:00
cmd := exec . Command ( d . NodeConfig . OCIBinary , "kill" , d . MachineName )
2019-12-17 23:18:31 +00:00
if err := cmd . Run ( ) ; err != nil {
return errors . Wrapf ( err , "killing kic node %s" , d . MachineName )
}
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 {
log . Warnf ( "could not find the container %s to remove it." , d . MachineName )
2019-12-17 23:18:31 +00:00
}
2019-12-18 05:36:37 +00:00
cmd := exec . Command ( d . NodeConfig . OCIBinary , "rm" , "-f" , "-v" , d . MachineName )
2020-01-31 05:36:56 +00:00
o , err := cmd . CombinedOutput ( )
2020-02-13 02:11:44 +00:00
out := strings . TrimSpace ( string ( o ) )
2020-01-31 05:36:56 +00:00
if err != nil {
if strings . Contains ( out , "is already in progress" ) {
log . Warnf ( "Docker engine is stuck. please restart docker daemon on your computer." , d . MachineName )
}
return errors . Wrapf ( err , "removing container %s, output %s" , d . MachineName , out )
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 {
return errors . Wrap ( err , "get kic state" )
}
2019-12-19 18:01:18 +00:00
switch s {
case state . Stopped :
2019-12-17 23:18:31 +00:00
return d . Start ( )
2019-12-19 18:01:18 +00:00
case state . Running , state . Error :
2019-12-17 23:18:31 +00:00
if err = d . Stop ( ) ; err != nil {
2019-12-19 18:01:18 +00:00
return fmt . Errorf ( "restarting a kic stop phase %v" , err )
2019-12-17 23:18:31 +00:00
}
if err = d . Start ( ) ; err != nil {
2019-12-19 18:01:18 +00:00
return fmt . Errorf ( "restarting a kic start phase %v" , err )
2019-12-17 23:18:31 +00:00
}
return nil
}
2019-12-19 18:01:18 +00:00
return fmt . Errorf ( "restarted not implemented for kic state %s yet" , s )
2019-12-17 23:18:31 +00:00
}
// Start a _stopped_ kic container
// not meant to be used for Create().
func ( d * Driver ) Start ( ) error {
s , err := d . GetState ( )
if err != nil {
return errors . Wrap ( err , "get kic state" )
}
if s == state . Stopped {
2019-12-18 05:36:37 +00:00
cmd := exec . Command ( d . NodeConfig . OCIBinary , "start" , d . MachineName )
2019-12-17 23:18:31 +00:00
if err := cmd . Run ( ) ; err != nil {
return errors . Wrapf ( err , "starting a stopped kic node %s" , d . MachineName )
}
return nil
}
2019-12-20 09:17:33 +00:00
// TODO:medyagh maybe make it idempotent
2019-12-17 23:18:31 +00:00
return fmt . Errorf ( "cant start a not-stopped (%s) kic node" , s )
}
// Stop a host gracefully, including any containers that we are managing.
func ( d * Driver ) Stop ( ) error {
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
}
// RunSSHCommandFromDriver implements direct ssh control to the driver
func ( d * Driver ) RunSSHCommandFromDriver ( ) error {
return fmt . Errorf ( "driver does not support RunSSHCommandFromDriver commands" )
}