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
Anders F Björklund 2022-04-03 18:14:43 +02:00
parent 927d7d5596
commit 77ed383578
11 changed files with 875 additions and 19 deletions

2
go.mod
View File

@ -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

739
pkg/drivers/qemu/qemu.go Normal file
View File

@ -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
}

View File

@ -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) {

View File

@ -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,

View File

@ -25,6 +25,7 @@ var supportedDrivers = []string{
VirtualBox,
VMwareFusion,
KVM2,
QEMU2,
QEMU,
VMware,
None,

View File

@ -32,7 +32,7 @@ var supportedDrivers = []string{
VMwareFusion,
HyperV,
VMware,
QEMU,
QEMU2,
Docker,
Podman,
SSH,

View File

@ -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
}

View File

@ -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"

View File

@ -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}
}

View File

@ -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

View File

@ -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}
}