diff --git a/cmd/run.go b/cmd/run.go
index fbc83740..b23ff6b2 100644
--- a/cmd/run.go
+++ b/cmd/run.go
@@ -28,6 +28,7 @@ type osVmConfig struct {
RemoveVm bool // Kill the running VM when it exits
RemoveDiskImage bool // After exit of the VM, remove the disk image
Quiet bool
+ BindMounts []string
}
var (
@@ -58,6 +59,7 @@ func init() {
runCmd.Flags().BoolVar(&vmConfig.Quiet, "quiet", false, "Suppress output from bootc disk creation and VM boot console")
runCmd.Flags().StringVar(&diskImageConfigInstance.RootSizeMax, "root-size-max", "", "Maximum size of root filesystem in bytes; optionally accepts M, G, T suffixes")
runCmd.Flags().StringVar(&diskImageConfigInstance.DiskSize, "disk-size", "", "Allocate a disk image of this size in bytes; optionally accepts M, G, T suffixes")
+ runCmd.Flags().StringArrayVar(&vmConfig.BindMounts, "bind", nil, "Create a virtiofs mount between host and guest, separated by a `:`")
}
func doRun(flags *cobra.Command, args []string) error {
@@ -141,6 +143,7 @@ func doRun(flags *cobra.Command, args []string) error {
CloudInitDir: vmConfig.CloudInitDir,
NoCredentials: vmConfig.NoCredentials,
CloudInitData: flags.Flags().Changed("cloudinit"),
+ BindMounts: vmConfig.BindMounts,
RemoveVm: vmConfig.RemoveVm,
Background: vmConfig.Background,
SSHPort: sshPort,
diff --git a/pkg/vm/domain-template.xml b/pkg/vm/domain-template.xml
index 33388cee..bf431044 100644
--- a/pkg/vm/domain-template.xml
+++ b/pkg/vm/domain-template.xml
@@ -17,6 +17,8 @@
hvm
+
+
@@ -33,6 +35,7 @@
{{.CloudInitCDRom}}
+ {{.Filesystems}}
diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go
index b9529681..1a32f0c3 100644
--- a/pkg/vm/vm.go
+++ b/pkg/vm/vm.go
@@ -62,6 +62,7 @@ type RunVMParameters struct {
Cmd []string
RemoveVm bool
Background bool
+ BindMounts []string
}
type BootcVM interface {
@@ -97,6 +98,7 @@ type BootcVMCommon struct {
hasCloudInit bool
cloudInitDir string
cloudInitArgs string
+ BindMounts []string
bootcDisk bootc.BootcDisk
cacheDirLock utils.CacheLock
}
diff --git a/pkg/vm/vm_darwin.go b/pkg/vm/vm_darwin.go
index b381333c..9d777b82 100644
--- a/pkg/vm/vm_darwin.go
+++ b/pkg/vm/vm_darwin.go
@@ -25,6 +25,10 @@ func NewVM(params NewVMParameters) (vm *BootcVMMac, err error) {
return nil, fmt.Errorf("image ID is required")
}
+ if len(params.BindMounts) > 0 {
+ return fmt.Errorf("bind mounts are currently not supported on this platform")
+ }
+
longId, cacheDir, err := GetVMCachePath(params.ImageID, params.User)
if err != nil {
return nil, fmt.Errorf("unable to get VM cache path: %w", err)
diff --git a/pkg/vm/vm_linux.go b/pkg/vm/vm_linux.go
index 5e8f10c0..5f888937 100644
--- a/pkg/vm/vm_linux.go
+++ b/pkg/vm/vm_linux.go
@@ -7,6 +7,7 @@ import (
"fmt"
"path/filepath"
"strconv"
+ "strings"
"text/template"
"time"
@@ -120,6 +121,7 @@ func (v *BootcVMLinux) Run(params RunVMParameters) (err error) {
v.cmd = params.Cmd
v.hasCloudInit = params.CloudInitData
v.cloudInitDir = params.CloudInitDir
+ v.BindMounts = params.BindMounts
v.vmUsername = params.VMUser
v.sshIdentity = params.SSHIdentity
@@ -192,6 +194,7 @@ func (v *BootcVMLinux) parseDomainTemplate() (domainXML string, err error) {
Name string
CloudInitCDRom string
CloudInitSMBios string
+ Filesystems string
}
templateParams := TemplateParams{
@@ -230,6 +233,31 @@ func (v *BootcVMLinux) parseDomainTemplate() (domainXML string, err error) {
`, v.cloudInitArgs)
}
+ bindMounts := ""
+ for _, mnt := range v.BindMounts {
+ parts := strings.SplitN(mnt, ":", 2)
+ if len(parts) < 2 {
+ return "", fmt.Errorf("invalid bind mount: %q", mnt)
+ }
+ src := parts[0]
+ absSrc, err := filepath.Abs(src)
+ if err != nil {
+ return "", fmt.Errorf("failed to resolve bind mount source %q", src)
+ }
+ dst := parts[1]
+ // Note we're not properly quoting for XML here, just relying
+ // on the Go quoting. Fixing that would involve pulling in a real
+ // XML library.
+ bindMounts += fmt.Sprintf(`
+
+
+
+
+
+ `, absSrc, dst)
+ }
+ templateParams.Filesystems = bindMounts
+
err = tmpl.Execute(&domainXMLBuf, templateParams)
if err != nil {
return "", fmt.Errorf("unable to execute domain template: %w", err)