Fork the machine qemu driver into minikube qemu2
This is an internal driver, with the new driver config rather than the machine flags of the external driver.pull/13639/head
parent
927d7d5596
commit
77ed383578
2
go.mod
2
go.mod
|
@ -47,6 +47,7 @@ require (
|
|||
github.com/juju/version v0.0.0-20180108022336-b64dbd566305 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/cpuid v1.2.0
|
||||
github.com/machine-drivers/docker-machine-driver-qemu v0.1.1-0.20220331133007-0324171328f7
|
||||
github.com/machine-drivers/docker-machine-driver-vmware v0.1.5
|
||||
github.com/mattbaird/jsonpatch v0.0.0-20200820163806-098863c1fc24
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
|
@ -155,7 +156,6 @@ require (
|
|||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.13.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/machine-drivers/docker-machine-driver-qemu v0.1.1-0.20220331133007-0324171328f7
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
|
|
|
@ -0,0 +1,739 @@
|
|||
/*
|
||||
Copyright 2018 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 qemu
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/docker/machine/libmachine/drivers"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
"github.com/docker/machine/libmachine/mcnutils"
|
||||
"github.com/docker/machine/libmachine/ssh"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
|
||||
pkgdrivers "k8s.io/minikube/pkg/drivers"
|
||||
)
|
||||
|
||||
const (
|
||||
isoFilename = "boot2docker.iso"
|
||||
privateNetworkName = "docker-machines"
|
||||
|
||||
defaultSSHUser = "docker"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
*drivers.BaseDriver
|
||||
*pkgdrivers.CommonDriver
|
||||
EnginePort int
|
||||
FirstQuery bool
|
||||
|
||||
Memory int
|
||||
DiskSize int
|
||||
CPU int
|
||||
Program string
|
||||
Display bool
|
||||
DisplayType string
|
||||
Nographic bool
|
||||
VirtioDrives bool
|
||||
Network string
|
||||
PrivateNetwork string
|
||||
Boot2DockerURL string
|
||||
NetworkInterface string
|
||||
NetworkAddress string
|
||||
NetworkBridge string
|
||||
CaCertPath string
|
||||
PrivateKeyPath string
|
||||
DiskPath string
|
||||
CacheMode string
|
||||
IOMode string
|
||||
connectionString string
|
||||
// conn *libvirt.Connect
|
||||
// VM *libvirt.Domain
|
||||
vmLoaded bool
|
||||
UserDataFile string
|
||||
CloudConfigRoot string
|
||||
LocalPorts string
|
||||
}
|
||||
|
||||
func (d *Driver) GetMachineName() string {
|
||||
return d.MachineName
|
||||
}
|
||||
|
||||
func (d *Driver) GetSSHHostname() (string, error) {
|
||||
return "localhost", nil
|
||||
//return d.GetIP()
|
||||
}
|
||||
|
||||
func (d *Driver) GetSSHKeyPath() string {
|
||||
return d.ResolveStorePath("id_rsa")
|
||||
}
|
||||
|
||||
func (d *Driver) GetSSHPort() (int, error) {
|
||||
if d.SSHPort == 0 {
|
||||
d.SSHPort = 22
|
||||
}
|
||||
|
||||
return d.SSHPort, nil
|
||||
}
|
||||
|
||||
func (d *Driver) GetSSHUsername() string {
|
||||
if d.SSHUser == "" {
|
||||
d.SSHUser = "docker"
|
||||
}
|
||||
|
||||
return d.SSHUser
|
||||
}
|
||||
|
||||
func (d *Driver) DriverName() string {
|
||||
return "qemu2"
|
||||
}
|
||||
|
||||
func (d *Driver) GetURL() (string, error) {
|
||||
log.Debugf("GetURL called")
|
||||
if _, err := os.Stat(d.pidfilePath()); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
ip, err := d.GetIP()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get IP: %s", err)
|
||||
return "", err
|
||||
}
|
||||
if ip == "" {
|
||||
return "", nil
|
||||
}
|
||||
port := d.GetPort()
|
||||
return fmt.Sprintf("tcp://%s:%d", ip, port), nil
|
||||
}
|
||||
|
||||
func NewDriver(hostName, storePath string) drivers.Driver {
|
||||
return &Driver{
|
||||
PrivateNetwork: privateNetworkName,
|
||||
BaseDriver: &drivers.BaseDriver{
|
||||
SSHUser: defaultSSHUser,
|
||||
MachineName: hostName,
|
||||
StorePath: storePath,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Driver) GetIP() (string, error) {
|
||||
if d.Network == "user" {
|
||||
return "127.0.0.1", nil
|
||||
}
|
||||
return d.NetworkAddress, nil
|
||||
}
|
||||
|
||||
func (d *Driver) GetPort() int {
|
||||
var port = d.EnginePort
|
||||
if d.FirstQuery {
|
||||
d.FirstQuery = false
|
||||
port = 2376
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
func checkPid(pid int) error {
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return process.Signal(syscall.Signal(0))
|
||||
}
|
||||
|
||||
func (d *Driver) GetState() (state.State, error) {
|
||||
|
||||
if _, err := os.Stat(d.pidfilePath()); err != nil {
|
||||
return state.Stopped, nil
|
||||
}
|
||||
p, err := ioutil.ReadFile(d.pidfilePath())
|
||||
if err != nil {
|
||||
return state.Error, err
|
||||
}
|
||||
pid, err := strconv.Atoi(strings.TrimSpace(string(p)))
|
||||
if err != nil {
|
||||
return state.Error, err
|
||||
}
|
||||
if err := checkPid(pid); err != nil {
|
||||
// No pid, remove pidfile
|
||||
os.Remove(d.pidfilePath())
|
||||
return state.Stopped, nil
|
||||
}
|
||||
ret, err := d.RunQMPCommand("query-status")
|
||||
if err != nil {
|
||||
return state.Error, err
|
||||
}
|
||||
// RunState is one of:
|
||||
// 'debug', 'inmigrate', 'internal-error', 'io-error', 'paused',
|
||||
// 'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm',
|
||||
// 'running', 'save-vm', 'shutdown', 'suspended', 'watchdog',
|
||||
// 'guest-panicked'
|
||||
switch ret["status"] {
|
||||
case "running":
|
||||
return state.Running, nil
|
||||
case "paused":
|
||||
return state.Paused, nil
|
||||
case "shutdown":
|
||||
return state.Stopped, nil
|
||||
}
|
||||
return state.None, nil
|
||||
}
|
||||
|
||||
func (d *Driver) PreCreateCheck() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) Create() error {
|
||||
var err error
|
||||
if d.Network == "user" {
|
||||
minPort, maxPort, err := parsePortRange(d.LocalPorts)
|
||||
log.Debugf("port range: %d -> %d", minPort, maxPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.SSHPort, err = getAvailableTCPPortFromRange(minPort, maxPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
d.EnginePort, err = getAvailableTCPPortFromRange(minPort, maxPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.EnginePort == d.SSHPort {
|
||||
// can't have both on same port
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
b2dutils := mcnutils.NewB2dUtils(d.StorePath)
|
||||
if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Creating SSH key...")
|
||||
if err := ssh.GenerateSSHKey(d.sshKeyPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Creating Disk image...")
|
||||
if err := d.generateDiskImage(d.DiskSize); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.UserDataFile != "" {
|
||||
log.Infof("Creating Userdata Disk...")
|
||||
if d.CloudConfigRoot, err = d.generateUserdataDisk(d.UserDataFile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Starting QEMU VM...")
|
||||
return d.Start()
|
||||
}
|
||||
|
||||
func parsePortRange(rawPortRange string) (int, int, error) {
|
||||
if rawPortRange == "" {
|
||||
return 0, 65535, nil
|
||||
}
|
||||
|
||||
portRange := strings.Split(rawPortRange, "-")
|
||||
|
||||
minPort, err := strconv.Atoi(portRange[0])
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("Invalid port range")
|
||||
}
|
||||
maxPort, err := strconv.Atoi(portRange[1])
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("Invalid port range")
|
||||
}
|
||||
|
||||
if maxPort < minPort {
|
||||
return 0, 0, fmt.Errorf("Invalid port range")
|
||||
}
|
||||
|
||||
if maxPort-minPort < 2 {
|
||||
return 0, 0, fmt.Errorf("Port range must be minimum 2 ports")
|
||||
}
|
||||
|
||||
return minPort, maxPort, nil
|
||||
}
|
||||
|
||||
func getRandomPortNumberInRange(min int, max int) int {
|
||||
return rand.Intn(max-min) + min
|
||||
}
|
||||
|
||||
func getAvailableTCPPortFromRange(minPort int, maxPort int) (int, error) {
|
||||
port := 0
|
||||
for i := 0; i <= 10; i++ {
|
||||
var ln net.Listener
|
||||
var err error
|
||||
if minPort == 0 && maxPort == 65535 {
|
||||
ln, err = net.Listen("tcp4", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
port = getRandomPortNumberInRange(minPort, maxPort)
|
||||
log.Debugf("testing port: %d", port)
|
||||
ln, err = net.Listen("tcp4", fmt.Sprintf("127.0.0.1:%d", port))
|
||||
if err != nil {
|
||||
log.Debugf("port already in use: %d", port)
|
||||
continue
|
||||
}
|
||||
}
|
||||
defer ln.Close()
|
||||
addr := ln.Addr().String()
|
||||
addrParts := strings.SplitN(addr, ":", 2)
|
||||
p, err := strconv.Atoi(addrParts[1])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if p != 0 {
|
||||
port = p
|
||||
return port, nil
|
||||
}
|
||||
time.Sleep(1)
|
||||
}
|
||||
return 0, fmt.Errorf("unable to allocate tcp port")
|
||||
}
|
||||
|
||||
func (d *Driver) Start() error {
|
||||
// fmt.Printf("Init qemu %s\n", i.VM)
|
||||
machineDir := filepath.Join(d.StorePath, "machines", d.GetMachineName())
|
||||
|
||||
var startCmd []string
|
||||
|
||||
if d.Display {
|
||||
if d.DisplayType != "" {
|
||||
startCmd = append(startCmd,
|
||||
"-display", d.DisplayType,
|
||||
)
|
||||
} else {
|
||||
// Use the default graphic output
|
||||
}
|
||||
} else {
|
||||
if d.Nographic {
|
||||
startCmd = append(startCmd,
|
||||
"-nographic",
|
||||
)
|
||||
} else {
|
||||
startCmd = append(startCmd,
|
||||
"-display", "none",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
startCmd = append(startCmd,
|
||||
"-m", fmt.Sprintf("%d", d.Memory),
|
||||
"-smp", fmt.Sprintf("%d", d.CPU),
|
||||
"-boot", "d")
|
||||
var isoPath = filepath.Join(machineDir, isoFilename)
|
||||
if d.VirtioDrives {
|
||||
startCmd = append(startCmd,
|
||||
"-drive", fmt.Sprintf("file=%s,index=2,media=cdrom,if=virtio", isoPath))
|
||||
} else {
|
||||
startCmd = append(startCmd,
|
||||
"-cdrom", isoPath)
|
||||
}
|
||||
startCmd = append(startCmd,
|
||||
"-qmp", fmt.Sprintf("unix:%s,server,nowait", d.monitorPath()),
|
||||
"-pidfile", d.pidfilePath(),
|
||||
)
|
||||
|
||||
if d.Network == "user" {
|
||||
startCmd = append(startCmd,
|
||||
"-nic", fmt.Sprintf("user,model=virtio,hostfwd=tcp::%d-:22,hostfwd=tcp::%d-:2376,hostname=%s", d.SSHPort, d.EnginePort, d.GetMachineName()),
|
||||
)
|
||||
} else if d.Network == "tap" {
|
||||
startCmd = append(startCmd,
|
||||
"-nic", fmt.Sprintf("tap,model=virtio,ifname=%s,script=no,downscript=no", d.NetworkInterface),
|
||||
)
|
||||
} else if d.Network == "bridge" {
|
||||
startCmd = append(startCmd,
|
||||
"-nic", fmt.Sprintf("bridge,model=virtio,br=%s", d.NetworkBridge),
|
||||
)
|
||||
} else {
|
||||
log.Errorf("Unknown network: %s", d.Network)
|
||||
}
|
||||
|
||||
startCmd = append(startCmd, "-daemonize")
|
||||
|
||||
// other options
|
||||
// "-enable-kvm" if its available
|
||||
if _, err := os.Stat("/dev/kvm"); err == nil {
|
||||
startCmd = append(startCmd, "-enable-kvm")
|
||||
}
|
||||
|
||||
if d.CloudConfigRoot != "" {
|
||||
startCmd = append(startCmd,
|
||||
"-fsdev",
|
||||
fmt.Sprintf("local,security_model=passthrough,readonly,id=fsdev0,path=%s", d.CloudConfigRoot))
|
||||
startCmd = append(startCmd, "-device", "virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=config-2")
|
||||
}
|
||||
|
||||
if d.VirtioDrives {
|
||||
startCmd = append(startCmd,
|
||||
"-drive", fmt.Sprintf("file=%s,index=0,media=disk,if=virtio", d.diskPath()))
|
||||
} else {
|
||||
// last argument is always the name of the disk image
|
||||
startCmd = append(startCmd, d.diskPath())
|
||||
}
|
||||
|
||||
if stdout, stderr, err := cmdOutErr(d.Program, startCmd...); err != nil {
|
||||
fmt.Printf("OUTPUT: %s\n", stdout)
|
||||
fmt.Printf("ERROR: %s\n", stderr)
|
||||
return err
|
||||
//if err := cmdStart(d.Program, startCmd...); err != nil {
|
||||
// return err
|
||||
}
|
||||
log.Infof("Waiting for VM to start (ssh -p %d docker@localhost)...", d.SSHPort)
|
||||
|
||||
//return ssh.WaitForTCP(fmt.Sprintf("localhost:%d", d.SSHPort))
|
||||
return WaitForTCPWithDelay(fmt.Sprintf("localhost:%d", d.SSHPort), time.Second)
|
||||
}
|
||||
|
||||
func cmdOutErr(cmdStr string, args ...string) (string, string, error) {
|
||||
cmd := exec.Command(cmdStr, args...)
|
||||
log.Debugf("executing: %v %v", cmdStr, strings.Join(args, " "))
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
stderrStr := stderr.String()
|
||||
log.Debugf("STDOUT: %v", stdout.String())
|
||||
log.Debugf("STDERR: %v", stderrStr)
|
||||
if err != nil {
|
||||
if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound {
|
||||
err = fmt.Errorf("mystery error: %s", ee)
|
||||
}
|
||||
} else {
|
||||
// also catch error messages in stderr, even if the return code
|
||||
// looks OK
|
||||
if strings.Contains(stderrStr, "error:") {
|
||||
err = fmt.Errorf("%v %v failed: %v", cmdStr, strings.Join(args, " "), stderrStr)
|
||||
}
|
||||
}
|
||||
return stdout.String(), stderrStr, err
|
||||
}
|
||||
|
||||
func cmdStart(cmdStr string, args ...string) error {
|
||||
cmd := exec.Command(cmdStr, args...)
|
||||
log.Debugf("executing: %v %v", cmdStr, strings.Join(args, " "))
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
func (d *Driver) Stop() error {
|
||||
// _, err := d.RunQMPCommand("stop")
|
||||
_, err := d.RunQMPCommand("system_powerdown")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) Remove() error {
|
||||
s, err := d.GetState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s == state.Running {
|
||||
if err := d.Kill(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if s != state.Stopped {
|
||||
_, err = d.RunQMPCommand("quit")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) Restart() error {
|
||||
s, err := d.GetState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s == state.Running {
|
||||
if err := d.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return d.Start()
|
||||
}
|
||||
|
||||
func (d *Driver) Kill() error {
|
||||
// _, err := d.RunQMPCommand("quit")
|
||||
_, err := d.RunQMPCommand("system_powerdown")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) StartDocker() error {
|
||||
return fmt.Errorf("hosts without a driver cannot start docker")
|
||||
}
|
||||
|
||||
func (d *Driver) StopDocker() error {
|
||||
return fmt.Errorf("hosts without a driver cannot stop docker")
|
||||
}
|
||||
|
||||
func (d *Driver) GetDockerConfigDir() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *Driver) Upgrade() error {
|
||||
return fmt.Errorf("hosts without a driver cannot be upgraded")
|
||||
}
|
||||
|
||||
//func (d *Driver) GetSSHCommand(args ...string) (*exec.Cmd, error) {
|
||||
// return ssh.GetSSHCommand("localhost", d.SSHPort, "docker", d.sshKeyPath(), args...), nil
|
||||
//}
|
||||
|
||||
func (d *Driver) sshKeyPath() string {
|
||||
machineDir := filepath.Join(d.StorePath, "machines", d.GetMachineName())
|
||||
return filepath.Join(machineDir, "id_rsa")
|
||||
}
|
||||
|
||||
func (d *Driver) publicSSHKeyPath() string {
|
||||
return d.sshKeyPath() + ".pub"
|
||||
}
|
||||
|
||||
func (d *Driver) diskPath() string {
|
||||
machineDir := filepath.Join(d.StorePath, "machines", d.GetMachineName())
|
||||
return filepath.Join(machineDir, "disk.qcow2")
|
||||
}
|
||||
|
||||
func (d *Driver) monitorPath() string {
|
||||
machineDir := filepath.Join(d.StorePath, "machines", d.GetMachineName())
|
||||
return filepath.Join(machineDir, "monitor")
|
||||
}
|
||||
|
||||
func (d *Driver) pidfilePath() string {
|
||||
machineDir := filepath.Join(d.StorePath, "machines", d.GetMachineName())
|
||||
return filepath.Join(machineDir, "qemu.pid")
|
||||
}
|
||||
|
||||
// Make a boot2docker VM disk image.
|
||||
func (d *Driver) generateDiskImage(size int) error {
|
||||
log.Debugf("Creating %d MB hard disk image...", size)
|
||||
|
||||
magicString := "boot2docker, please format-me"
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
|
||||
// magicString first so the automount script knows to format the disk
|
||||
file := &tar.Header{Name: magicString, Size: int64(len(magicString))}
|
||||
if err := tw.WriteHeader(file); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tw.Write([]byte(magicString)); err != nil {
|
||||
return err
|
||||
}
|
||||
// .ssh/key.pub => authorized_keys
|
||||
file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700}
|
||||
if err := tw.WriteHeader(file); err != nil {
|
||||
return err
|
||||
}
|
||||
pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644}
|
||||
if err := tw.WriteHeader(file); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tw.Write([]byte(pubKey)); err != nil {
|
||||
return err
|
||||
}
|
||||
file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644}
|
||||
if err := tw.WriteHeader(file); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tw.Write([]byte(pubKey)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tw.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
rawFile := fmt.Sprintf("%s.raw", d.diskPath())
|
||||
if err := ioutil.WriteFile(rawFile, buf.Bytes(), 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
if stdout, stderr, err := cmdOutErr("qemu-img", "convert", "-f", "raw", "-O", "qcow2", rawFile, d.diskPath()); err != nil {
|
||||
fmt.Printf("OUTPUT: %s\n", stdout)
|
||||
fmt.Printf("ERROR: %s\n", stderr)
|
||||
return err
|
||||
}
|
||||
if stdout, stderr, err := cmdOutErr("qemu-img", "resize", d.diskPath(), fmt.Sprintf("+%dM", size)); err != nil {
|
||||
fmt.Printf("OUTPUT: %s\n", stdout)
|
||||
fmt.Printf("ERROR: %s\n", stderr)
|
||||
return err
|
||||
}
|
||||
log.Debugf("DONE writing to %s and %s", rawFile, d.diskPath())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) generateUserdataDisk(userdataFile string) (string, error) {
|
||||
// Start with virtio, add ISO & FAT format later
|
||||
// Start with local file, add wget/fetct URL? (or if URL, use datasource..)
|
||||
userdata, err := ioutil.ReadFile(userdataFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
machineDir := filepath.Join(d.StorePath, "machines", d.GetMachineName())
|
||||
ccRoot := filepath.Join(machineDir, "cloud-config")
|
||||
os.MkdirAll(ccRoot, 0755)
|
||||
|
||||
userDataDir := filepath.Join(ccRoot, "openstack/latest")
|
||||
os.MkdirAll(userDataDir, 0755)
|
||||
|
||||
writeFile := filepath.Join(userDataDir, "user_data")
|
||||
if err := ioutil.WriteFile(writeFile, userdata, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ccRoot, nil
|
||||
|
||||
}
|
||||
|
||||
func (d *Driver) RunQMPCommand(command string) (map[string]interface{}, error) {
|
||||
|
||||
// connect to monitor
|
||||
conn, err := net.Dial("unix", d.monitorPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// initial QMP response
|
||||
var buf [1024]byte
|
||||
nr, err := conn.Read(buf[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type qmpInitialResponse struct {
|
||||
QMP struct {
|
||||
Version struct {
|
||||
QEMU struct {
|
||||
Micro int `json:"micro"`
|
||||
Minor int `json:"minor"`
|
||||
Major int `json:"major"`
|
||||
} `json:"qemu"`
|
||||
Package string `json:"package"`
|
||||
} `json:"version"`
|
||||
Capabilities []string `json:"capabilities"`
|
||||
} `jason:"QMP"`
|
||||
}
|
||||
|
||||
var initialResponse qmpInitialResponse
|
||||
json.Unmarshal(buf[:nr], &initialResponse)
|
||||
|
||||
// run 'qmp_capabilities' to switch to command mode
|
||||
// { "execute": "qmp_capabilities" }
|
||||
type qmpCommand struct {
|
||||
Command string `json:"execute"`
|
||||
}
|
||||
jsonCommand, err := json.Marshal(qmpCommand{Command: "qmp_capabilities"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = conn.Write(jsonCommand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nr, err = conn.Read(buf[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type qmpResponse struct {
|
||||
Return map[string]interface{} `json:"return"`
|
||||
}
|
||||
var response qmpResponse
|
||||
err = json.Unmarshal(buf[:nr], &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// expecting empty response
|
||||
if len(response.Return) != 0 {
|
||||
return nil, fmt.Errorf("qmp_capabilities failed: %v", response.Return)
|
||||
}
|
||||
|
||||
// { "execute": command }
|
||||
jsonCommand, err = json.Marshal(qmpCommand{Command: command})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = conn.Write(jsonCommand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nr, err = conn.Read(buf[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(buf[:nr], &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strings.HasPrefix(command, "query-") {
|
||||
return response.Return, nil
|
||||
}
|
||||
// non-query commands should return an empty response
|
||||
if len(response.Return) != 0 {
|
||||
return nil, fmt.Errorf("%s failed: %v", command, response.Return)
|
||||
}
|
||||
return response.Return, nil
|
||||
}
|
||||
|
||||
func WaitForTCPWithDelay(addr string, duration time.Duration) error {
|
||||
for {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer conn.Close()
|
||||
if _, err = conn.Read(make([]byte, 1)); err != nil {
|
||||
time.Sleep(duration)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -46,6 +46,8 @@ const (
|
|||
SSH = "ssh"
|
||||
// KVM2 driver
|
||||
KVM2 = "kvm2"
|
||||
// QEMU2 driver
|
||||
QEMU2 = "qemu2"
|
||||
// QEMU driver
|
||||
QEMU = "qemu"
|
||||
// VirtualBox driver
|
||||
|
@ -158,6 +160,11 @@ func IsKVM(name string) bool {
|
|||
return name == KVM2 || name == AliasKVM
|
||||
}
|
||||
|
||||
// IsQEMU checks if the driver is a QEMU[2]
|
||||
func IsQEMU(name string) bool {
|
||||
return name == QEMU2 || name == QEMU
|
||||
}
|
||||
|
||||
// IsVM checks if the driver is a VM
|
||||
func IsVM(name string) bool {
|
||||
if IsKIC(name) || BareMetal(name) {
|
||||
|
@ -183,7 +190,7 @@ func AllowsPreload(driverName string) bool {
|
|||
|
||||
// NeedsPortForward returns true if driver is unable provide direct IP connectivity
|
||||
func NeedsPortForward(name string) bool {
|
||||
if name == QEMU {
|
||||
if IsQEMU(name) {
|
||||
return true
|
||||
}
|
||||
if !IsKIC(name) {
|
||||
|
|
|
@ -27,7 +27,7 @@ var supportedDrivers = func() []string {
|
|||
if runtime.GOARCH == "arm64" {
|
||||
// on darwin/arm64 only docker and ssh are supported yet
|
||||
return []string{
|
||||
QEMU,
|
||||
QEMU2,
|
||||
Docker,
|
||||
Podman,
|
||||
SSH,
|
||||
|
@ -51,7 +51,7 @@ var supportedDrivers = func() []string {
|
|||
VMwareFusion,
|
||||
HyperKit,
|
||||
VMware,
|
||||
QEMU,
|
||||
QEMU2,
|
||||
Docker,
|
||||
Podman,
|
||||
SSH,
|
||||
|
|
|
@ -25,6 +25,7 @@ var supportedDrivers = []string{
|
|||
VirtualBox,
|
||||
VMwareFusion,
|
||||
KVM2,
|
||||
QEMU2,
|
||||
QEMU,
|
||||
VMware,
|
||||
None,
|
||||
|
|
|
@ -32,7 +32,7 @@ var supportedDrivers = []string{
|
|||
VMwareFusion,
|
||||
HyperV,
|
||||
VMware,
|
||||
QEMU,
|
||||
QEMU2,
|
||||
Docker,
|
||||
Podman,
|
||||
SSH,
|
||||
|
|
|
@ -632,7 +632,7 @@ func validateNetwork(h *host.Host, r command.Runner, imageRepository string) (st
|
|||
}
|
||||
}
|
||||
|
||||
if !driver.BareMetal(h.Driver.DriverName()) && !driver.IsKIC(h.Driver.DriverName()) {
|
||||
if !driver.BareMetal(h.Driver.DriverName()) && !driver.IsKIC(h.Driver.DriverName()) && !driver.IsQEMU(h.Driver.DriverName()) {
|
||||
if err := trySSH(h, ip); err != nil {
|
||||
return ip, err
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
_ "k8s.io/minikube/pkg/minikube/registry/drvs/parallels"
|
||||
_ "k8s.io/minikube/pkg/minikube/registry/drvs/podman"
|
||||
_ "k8s.io/minikube/pkg/minikube/registry/drvs/qemu"
|
||||
_ "k8s.io/minikube/pkg/minikube/registry/drvs/qemu2"
|
||||
_ "k8s.io/minikube/pkg/minikube/registry/drvs/ssh"
|
||||
_ "k8s.io/minikube/pkg/minikube/registry/drvs/virtualbox"
|
||||
_ "k8s.io/minikube/pkg/minikube/registry/drvs/vmware"
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/machine/libmachine/drivers"
|
||||
drvqemu "github.com/machine-drivers/docker-machine-driver-qemu"
|
||||
|
@ -67,18 +66,7 @@ func configure(cc config.ClusterConfig, n config.Node) (interface{}, error) {
|
|||
}
|
||||
|
||||
func status() registry.State {
|
||||
var qemuSystem string
|
||||
arch := runtime.GOARCH
|
||||
switch arch {
|
||||
case "amd64":
|
||||
qemuSystem = "qemu-system-x86_64"
|
||||
case "arm64":
|
||||
qemuSystem = "qemu-system-aarch64"
|
||||
default:
|
||||
return registry.State{Error: fmt.Errorf("unknown arch: %s", arch), Doc: docURL}
|
||||
}
|
||||
|
||||
_, err := exec.LookPath(qemuSystem)
|
||||
_, err := exec.LookPath("qemu-system-x86_64")
|
||||
if err != nil {
|
||||
return registry.State{Error: err, Fix: "Install qemu-system", Doc: docURL}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
Copyright 2018 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 qemu2
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
Copyright 2018 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 qemu2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/machine/libmachine/drivers"
|
||||
"k8s.io/minikube/pkg/drivers/qemu"
|
||||
|
||||
"k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/download"
|
||||
"k8s.io/minikube/pkg/minikube/driver"
|
||||
"k8s.io/minikube/pkg/minikube/localpath"
|
||||
"k8s.io/minikube/pkg/minikube/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
docURL = "https://minikube.sigs.k8s.io/docs/reference/drivers/qemu2/"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := registry.Register(registry.DriverDef{
|
||||
Name: driver.QEMU2,
|
||||
Init: func() drivers.Driver { return qemu.NewDriver("", "") },
|
||||
Config: configure,
|
||||
Status: status,
|
||||
Default: true,
|
||||
Priority: registry.Experimental,
|
||||
}); err != nil {
|
||||
panic(fmt.Sprintf("register failed: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
func qemuSystemProgram() (string, error) {
|
||||
arch := runtime.GOARCH
|
||||
switch arch {
|
||||
case "amd64":
|
||||
return "qemu-system-x86_64", nil
|
||||
case "arm64":
|
||||
return "qemu-system-aarch64", nil
|
||||
default:
|
||||
return "", fmt.Errorf("unknown arch: %s", arch)
|
||||
}
|
||||
}
|
||||
|
||||
func configure(cc config.ClusterConfig, n config.Node) (interface{}, error) {
|
||||
name := config.MachineName(cc, n)
|
||||
qemuSystem, err := qemuSystemProgram()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return qemu.Driver{
|
||||
BaseDriver: &drivers.BaseDriver{
|
||||
MachineName: name,
|
||||
StorePath: localpath.MiniPath(),
|
||||
SSHUser: "docker",
|
||||
},
|
||||
Boot2DockerURL: download.LocalISOResource(cc.MinikubeISO),
|
||||
DiskSize: cc.DiskSize,
|
||||
Memory: cc.Memory,
|
||||
CPU: cc.CPUs,
|
||||
EnginePort: 2376,
|
||||
FirstQuery: true,
|
||||
DiskPath: filepath.Join(localpath.MiniPath(), "machines", name, fmt.Sprintf("%s.img", name)),
|
||||
Program: qemuSystem,
|
||||
VirtioDrives: false,
|
||||
Network: "user",
|
||||
CacheMode: "default",
|
||||
IOMode: "threads",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func status() registry.State {
|
||||
qemuSystem, err := qemuSystemProgram()
|
||||
if err != nil {
|
||||
return registry.State{Error: err, Doc: docURL}
|
||||
}
|
||||
|
||||
_, err = exec.LookPath(qemuSystem)
|
||||
if err != nil {
|
||||
return registry.State{Error: err, Fix: "Install qemu-system", Doc: docURL}
|
||||
}
|
||||
|
||||
return registry.State{Installed: true, Healthy: true, Running: true}
|
||||
}
|
Loading…
Reference in New Issue