Skip to content

Commit

Permalink
MacOS improvements
Browse files Browse the repository at this point in the history
* Enable support of virtfs in Podman and darwin.  At the time of this writing, it requires a special patch not yet included in upstream qemu.
* Prefer to use a specially built qemu to support virtfs.  The qemu is installed under libexec/podman.

[NO NEW TESTS NEEDED]
Signed-off-by: Brent Baude <[email protected]>
  • Loading branch information
baude authored and n1hility committed Mar 18, 2022
1 parent e3184e9 commit 44e9b53
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 12 deletions.
3 changes: 3 additions & 0 deletions pkg/machine/config.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build amd64 || arm64
// +build amd64 arm64

package machine
Expand Down Expand Up @@ -28,6 +29,8 @@ type InitOptions struct {
Username string
ReExec bool
Rootful bool
// The numberical userid of the user that called machine
UID string
}

type QemuMachineStatus = string
Expand Down
28 changes: 24 additions & 4 deletions pkg/machine/ignition.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type DynamicIgnition struct {
Name string
Key string
TimeZone string
UID int
VMName string
WritePath string
}
Expand All @@ -63,12 +64,13 @@ func NewIgnitionFile(ign DynamicIgnition) error {
ignVersion := Ignition{
Version: "3.2.0",
}

ignPassword := Passwd{
Users: []PasswdUser{
{
Name: ign.Name,
SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)},
// Set the UID of the core user inside the machine
UID: intToPtr(ign.UID),
},
{
Name: "root",
Expand Down Expand Up @@ -289,9 +291,7 @@ func getDirs(usrName string) []Directory {
}

func getFiles(usrName string) []File {
var (
files []File
)
files := make([]File, 0)

lingerExample := `[Unit]
Description=A systemd user unit demo
Expand All @@ -310,6 +310,7 @@ machine_enabled=true
delegateConf := `[Service]
Delegate=memory pids cpu io
`
subUID := `%s:100000:1000000`

// Add a fake systemd service to get the user socket rolling
files = append(files, File{
Expand Down Expand Up @@ -344,6 +345,25 @@ Delegate=memory pids cpu io
},
})

// Setup /etc/subuid and /etc/subgid
for _, sub := range []string{"/etc/subuid", "/etc/subgid"} {
files = append(files, File{
Node: Node{
Group: getNodeGrp("root"),
Path: sub,
User: getNodeUsr("root"),
Overwrite: boolToPtr(true),
},
FileEmbedded1: FileEmbedded1{
Append: nil,
Contents: Resource{
Source: encodeDataURLPtr(fmt.Sprintf(subUID, usrName)),
},
Mode: intToPtr(0744),
},
})
}

// Set delegate.conf so cpu,io subsystem is delegated to non-root users as well for cgroupv2
// by default
files = append(files, File{
Expand Down
7 changes: 6 additions & 1 deletion pkg/machine/qemu/config.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
//go:build (amd64 && !windows) || (arm64 && !windows)
// +build amd64,!windows arm64,!windows

package qemu

import "time"
import (
"time"
)

type Provider struct{}

Expand Down Expand Up @@ -35,6 +38,8 @@ type MachineVM struct {
RemoteUsername string
// Whether this machine should run in a rootful or rootless manner
Rootful bool
// UID is the numerical id of the user that called machine
UID int
}

type Mount struct {
Expand Down
49 changes: 42 additions & 7 deletions pkg/machine/qemu/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,16 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
vm.Memory = opts.Memory
vm.DiskSize = opts.DiskSize

// Look up the executable
execPath, err := exec.LookPath(QemuCommand)
// Find the qemu executable
cfg, err := config.Default()
if err != nil {
return nil, err
}
execPath, err := cfg.FindHelperBinary(QemuCommand, true)
if err != nil {
return nil, err
}

cmd := append([]string{execPath})
// Add memory
cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...)
Expand Down Expand Up @@ -245,12 +250,13 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
}
}
v.Mounts = mounts
v.UID = os.Getuid()

// Add location of bootable image
v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.ImagePath)
// This kind of stinks but no other way around this r/n
if len(opts.IgnitionPath) < 1 {
uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/user/1000/podman/podman.sock", strconv.Itoa(v.Port), v.RemoteUsername)
uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", fmt.Sprintf("/run/user/%d/podman/podman.sock", v.UID), strconv.Itoa(v.Port), v.RemoteUsername)
uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root")
identity := filepath.Join(sshDir, v.Name)

Expand Down Expand Up @@ -296,7 +302,16 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
// only if the virtualdisk size is less than
// the given disk size
if opts.DiskSize<<(10*3) > originalDiskSize {
resize := exec.Command("qemu-img", []string{"resize", v.ImagePath, strconv.Itoa(int(opts.DiskSize)) + "G"}...)
// Find the qemu executable
cfg, err := config.Default()
if err != nil {
return false, err
}
resizePath, err := cfg.FindHelperBinary("qemu-img", true)
if err != nil {
return false, err
}
resize := exec.Command(resizePath, []string{"resize", v.ImagePath, strconv.Itoa(int(opts.DiskSize)) + "G"}...)
resize.Stdout = os.Stdout
resize.Stderr = os.Stderr
if err := resize.Run(); err != nil {
Expand All @@ -319,6 +334,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
VMName: v.Name,
TimeZone: opts.TimeZone,
WritePath: v.IgnitionFilePath,
UID: v.UID,
}
err = machine.NewIgnitionFile(ign)
return err == nil, err
Expand Down Expand Up @@ -459,7 +475,17 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
for _, mount := range v.Mounts {
fmt.Printf("Mounting volume... %s:%s\n", mount.Source, mount.Target)
// create mountpoint directory if it doesn't exist
err = v.SSH(name, machine.SSHOptions{Args: []string{"-q", "--", "sudo", "mkdir", "-p", mount.Target}})
// because / is immutable, we have to monkey around with permissions
// if we dont mount in /home or /mnt
args := []string{"-q", "--"}
if !strings.HasPrefix(mount.Target, "/home") || !strings.HasPrefix(mount.Target, "/mnt") {
args = append(args, "sudo", "chattr", "-i", "/", ";")
}
args = append(args, "sudo", "mkdir", "-p", mount.Target)
if !strings.HasPrefix(mount.Target, "/home") || !strings.HasPrefix(mount.Target, "/mnt") {
args = append(args, ";", "sudo", "chattr", "+i", "/", ";")
}
err = v.SSH(name, machine.SSHOptions{Args: args})
if err != nil {
return err
}
Expand Down Expand Up @@ -790,7 +816,16 @@ func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error {
// executes qemu-image info to get the virtual disk size
// of the diskimage
func getDiskSize(path string) (uint64, error) {
diskInfo := exec.Command("qemu-img", "info", "--output", "json", path)
// Find the qemu executable
cfg, err := config.Default()
if err != nil {
return 0, err
}
qemuPathDir, err := cfg.FindHelperBinary("qemu-img", true)
if err != nil {
return 0, err
}
diskInfo := exec.Command(qemuPathDir, "info", "--output", "json", path)
stdout, err := diskInfo.StdoutPipe()
if err != nil {
return 0, err
Expand Down Expand Up @@ -952,7 +987,7 @@ func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwa
return cmd, "", noForwarding
}

destSock := "/run/user/1000/podman/podman.sock"
destSock := fmt.Sprintf("/run/user/%d/podman/podman.sock", v.UID)
forwardUser := "core"

if v.Rootful {
Expand Down
1 change: 1 addition & 0 deletions pkg/machine/qemu/options_darwin_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func getOvmfDir(imagePath, vmName string) string {
*/
func getEdk2CodeFd(name string) string {
dirs := []string{
"/opt/homebrew/opt/podman/libexec/share/qemu",
"/usr/local/share/qemu",
"/opt/homebrew/share/qemu",
}
Expand Down

0 comments on commit 44e9b53

Please sign in to comment.