MacOS improvements
* 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 <bbaude@redhat.com>
This commit is contained in:
		
							parent
							
								
									e1f00b4512
								
							
						
					
					
						commit
						cdb6deb148
					
				|  | @ -1,3 +1,4 @@ | ||||||
|  | //go:build amd64 || arm64
 | ||||||
| // +build amd64 arm64
 | // +build amd64 arm64
 | ||||||
| 
 | 
 | ||||||
| package machine | package machine | ||||||
|  | @ -28,6 +29,8 @@ type InitOptions struct { | ||||||
| 	Username     string | 	Username     string | ||||||
| 	ReExec       bool | 	ReExec       bool | ||||||
| 	Rootful      bool | 	Rootful      bool | ||||||
|  | 	// The numberical userid of the user that called machine
 | ||||||
|  | 	UID string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type QemuMachineStatus = string | type QemuMachineStatus = string | ||||||
|  |  | ||||||
|  | @ -51,6 +51,7 @@ type DynamicIgnition struct { | ||||||
| 	Name      string | 	Name      string | ||||||
| 	Key       string | 	Key       string | ||||||
| 	TimeZone  string | 	TimeZone  string | ||||||
|  | 	UID       int | ||||||
| 	VMName    string | 	VMName    string | ||||||
| 	WritePath string | 	WritePath string | ||||||
| } | } | ||||||
|  | @ -63,12 +64,13 @@ func NewIgnitionFile(ign DynamicIgnition) error { | ||||||
| 	ignVersion := Ignition{ | 	ignVersion := Ignition{ | ||||||
| 		Version: "3.2.0", | 		Version: "3.2.0", | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	ignPassword := Passwd{ | 	ignPassword := Passwd{ | ||||||
| 		Users: []PasswdUser{ | 		Users: []PasswdUser{ | ||||||
| 			{ | 			{ | ||||||
| 				Name:              ign.Name, | 				Name:              ign.Name, | ||||||
| 				SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)}, | 				SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)}, | ||||||
|  | 				// Set the UID of the core user inside the machine
 | ||||||
|  | 				UID: intToPtr(ign.UID), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
| 				Name:              "root", | 				Name:              "root", | ||||||
|  | @ -289,9 +291,7 @@ func getDirs(usrName string) []Directory { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func getFiles(usrName string) []File { | func getFiles(usrName string) []File { | ||||||
| 	var ( | 	files := make([]File, 0) | ||||||
| 		files []File |  | ||||||
| 	) |  | ||||||
| 
 | 
 | ||||||
| 	lingerExample := `[Unit] | 	lingerExample := `[Unit] | ||||||
| Description=A systemd user unit demo | Description=A systemd user unit demo | ||||||
|  | @ -310,6 +310,7 @@ machine_enabled=true | ||||||
| 	delegateConf := `[Service] | 	delegateConf := `[Service] | ||||||
| Delegate=memory pids cpu io | Delegate=memory pids cpu io | ||||||
| ` | ` | ||||||
|  | 	subUID := `%s:100000:1000000` | ||||||
| 
 | 
 | ||||||
| 	// Add a fake systemd service to get the user socket rolling
 | 	// Add a fake systemd service to get the user socket rolling
 | ||||||
| 	files = append(files, File{ | 	files = append(files, File{ | ||||||
|  | @ -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
 | 	// Set delegate.conf so cpu,io subsystem is delegated to non-root users as well for cgroupv2
 | ||||||
| 	// by default
 | 	// by default
 | ||||||
| 	files = append(files, File{ | 	files = append(files, File{ | ||||||
|  |  | ||||||
|  | @ -1,8 +1,11 @@ | ||||||
|  | //go:build (amd64 && !windows) || (arm64 && !windows)
 | ||||||
| // +build amd64,!windows arm64,!windows
 | // +build amd64,!windows arm64,!windows
 | ||||||
| 
 | 
 | ||||||
| package qemu | package qemu | ||||||
| 
 | 
 | ||||||
| import "time" | import ( | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| type Provider struct{} | type Provider struct{} | ||||||
| 
 | 
 | ||||||
|  | @ -35,6 +38,8 @@ type MachineVM struct { | ||||||
| 	RemoteUsername string | 	RemoteUsername string | ||||||
| 	// Whether this machine should run in a rootful or rootless manner
 | 	// Whether this machine should run in a rootful or rootless manner
 | ||||||
| 	Rootful bool | 	Rootful bool | ||||||
|  | 	// UID is the numerical id of the user that called machine
 | ||||||
|  | 	UID int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Mount struct { | type Mount struct { | ||||||
|  |  | ||||||
|  | @ -88,11 +88,16 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { | ||||||
| 	vm.Memory = opts.Memory | 	vm.Memory = opts.Memory | ||||||
| 	vm.DiskSize = opts.DiskSize | 	vm.DiskSize = opts.DiskSize | ||||||
| 
 | 
 | ||||||
| 	// Look up the executable
 | 	// Find the qemu executable
 | ||||||
| 	execPath, err := exec.LookPath(QemuCommand) | 	cfg, err := config.Default() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | 	execPath, err := cfg.FindHelperBinary(QemuCommand, true) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	cmd := append([]string{execPath}) | 	cmd := append([]string{execPath}) | ||||||
| 	// Add memory
 | 	// Add memory
 | ||||||
| 	cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...) | 	cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...) | ||||||
|  | @ -245,12 +250,13 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	v.Mounts = mounts | 	v.Mounts = mounts | ||||||
|  | 	v.UID = os.Getuid() | ||||||
| 
 | 
 | ||||||
| 	// Add location of bootable image
 | 	// Add location of bootable image
 | ||||||
| 	v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.ImagePath) | 	v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.ImagePath) | ||||||
| 	// This kind of stinks but no other way around this r/n
 | 	// This kind of stinks but no other way around this r/n
 | ||||||
| 	if len(opts.IgnitionPath) < 1 { | 	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") | 		uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root") | ||||||
| 		identity := filepath.Join(sshDir, v.Name) | 		identity := filepath.Join(sshDir, v.Name) | ||||||
| 
 | 
 | ||||||
|  | @ -296,7 +302,16 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { | ||||||
| 	// only if the virtualdisk size is less than
 | 	// only if the virtualdisk size is less than
 | ||||||
| 	// the given disk size
 | 	// the given disk size
 | ||||||
| 	if opts.DiskSize<<(10*3) > originalDiskSize { | 	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.Stdout = os.Stdout | ||||||
| 		resize.Stderr = os.Stderr | 		resize.Stderr = os.Stderr | ||||||
| 		if err := resize.Run(); err != nil { | 		if err := resize.Run(); err != nil { | ||||||
|  | @ -319,6 +334,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { | ||||||
| 		VMName:    v.Name, | 		VMName:    v.Name, | ||||||
| 		TimeZone:  opts.TimeZone, | 		TimeZone:  opts.TimeZone, | ||||||
| 		WritePath: v.IgnitionFilePath, | 		WritePath: v.IgnitionFilePath, | ||||||
|  | 		UID:       v.UID, | ||||||
| 	} | 	} | ||||||
| 	err = machine.NewIgnitionFile(ign) | 	err = machine.NewIgnitionFile(ign) | ||||||
| 	return err == nil, err | 	return err == nil, err | ||||||
|  | @ -459,7 +475,17 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { | ||||||
| 	for _, mount := range v.Mounts { | 	for _, mount := range v.Mounts { | ||||||
| 		fmt.Printf("Mounting volume... %s:%s\n", mount.Source, mount.Target) | 		fmt.Printf("Mounting volume... %s:%s\n", mount.Source, mount.Target) | ||||||
| 		// create mountpoint directory if it doesn't exist
 | 		// 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 { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  | @ -795,7 +821,16 @@ func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error { | ||||||
| // executes qemu-image info to get the virtual disk size
 | // executes qemu-image info to get the virtual disk size
 | ||||||
| // of the diskimage
 | // of the diskimage
 | ||||||
| func getDiskSize(path string) (uint64, error) { | 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() | 	stdout, err := diskInfo.StdoutPipe() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, err | 		return 0, err | ||||||
|  | @ -957,7 +992,7 @@ func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwa | ||||||
| 		return cmd, "", noForwarding | 		return cmd, "", noForwarding | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	destSock := "/run/user/1000/podman/podman.sock" | 	destSock := fmt.Sprintf("/run/user/%d/podman/podman.sock", v.UID) | ||||||
| 	forwardUser := "core" | 	forwardUser := "core" | ||||||
| 
 | 
 | ||||||
| 	if v.Rootful { | 	if v.Rootful { | ||||||
|  |  | ||||||
|  | @ -45,6 +45,7 @@ func getOvmfDir(imagePath, vmName string) string { | ||||||
|  */ |  */ | ||||||
| func getEdk2CodeFd(name string) string { | func getEdk2CodeFd(name string) string { | ||||||
| 	dirs := []string{ | 	dirs := []string{ | ||||||
|  | 		"/opt/homebrew/opt/podman/libexec/share/qemu", | ||||||
| 		"/usr/local/share/qemu", | 		"/usr/local/share/qemu", | ||||||
| 		"/opt/homebrew/share/qemu", | 		"/opt/homebrew/share/qemu", | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue