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-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"
|
2020-02-21 20:01:03 +00:00
|
|
|
"github.com/spf13/viper"
|
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"
|
2020-02-21 20:01:03 +00:00
|
|
|
"k8s.io/minikube/pkg/minikube/config"
|
2019-12-22 05:20:32 +00:00
|
|
|
"k8s.io/minikube/pkg/minikube/constants"
|
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-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,
|
2019-12-22 05:20:32 +00:00
|
|
|
ContainerPort: constants.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-02-20 22:43:24 +00:00
|
|
|
t := time.Now()
|
|
|
|
glog.Infof("Starting creating preloaded images volume")
|
2020-02-21 20:53:24 +00:00
|
|
|
volumeName, err := oci.CreatePreloadedImagesVolume(d.NodeConfig.KubernetesVersion, d.NodeConfig.ContainerRuntime, BaseImage, viper.GetString(config.MachineProfile))
|
2020-02-20 07:02:36 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.Infof("Unable to create preloaded images volume: %v", err)
|
|
|
|
}
|
2020-02-20 22:59:43 +00:00
|
|
|
glog.Infof("Finished creating preloaded images volume in %f seconds", time.Since(t).Seconds())
|
2020-02-20 07:02:36 +00:00
|
|
|
params.PreloadedVolume = volumeName
|
|
|
|
err = oci.CreateContainerNode(params)
|
2020-01-24 03:20:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "create kic node")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := d.prepareSSH(); err != nil {
|
|
|
|
return errors.Wrap(err, "prepare kic ssh")
|
|
|
|
}
|
|
|
|
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 {
|
2019-12-18 05:36:37 +00:00
|
|
|
if d.NodeConfig.OCIBinary == "podman" {
|
2019-12-17 23:18:31 +00:00
|
|
|
return "podman"
|
|
|
|
}
|
|
|
|
return "docker"
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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-01-24 22:15:48 +00:00
|
|
|
p, err := oci.HostPortBinding(d.NodeConfig.OCIBinary, d.MachineName, d.NodeConfig.APIServerPort)
|
|
|
|
url := fmt.Sprintf("https://%s", net.JoinHostPort("127.0.0.1", fmt.Sprint(p)))
|
|
|
|
if err != nil {
|
|
|
|
return url, errors.Wrap(err, "api host port binding")
|
|
|
|
}
|
|
|
|
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-05 00:09:43 +00:00
|
|
|
if err := oci.PointToHostDockerDaemon(); err != nil {
|
|
|
|
return state.Error, errors.Wrap(err, "point host docker-daemon")
|
|
|
|
}
|
|
|
|
|
2019-12-18 05:36:37 +00:00
|
|
|
cmd := exec.Command(d.NodeConfig.OCIBinary, "inspect", "-f", "{{.State.Status}}", d.MachineName)
|
2019-12-17 23:18:31 +00:00
|
|
|
out, err := cmd.CombinedOutput()
|
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.Paused:
|
2019-12-17 23:18:31 +00:00
|
|
|
return d.Unpause()
|
2019-12-19 18:01:18 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
// Unpause a kic container
|
|
|
|
func (d *Driver) Unpause() error {
|
2019-12-30 07:54:48 +00:00
|
|
|
cmd := exec.Command(d.NodeConfig.OCIBinary, "unpause", d.MachineName)
|
2019-12-17 23:18:31 +00:00
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
return errors.Wrapf(err, "unpausing %s", d.MachineName)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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")
|
|
|
|
}
|