Skip to content

Commit

Permalink
Merge pull request #78 from legal90/custom-share-path
Browse files Browse the repository at this point in the history
Allow shared folder path customization
  • Loading branch information
legal90 committed Dec 23, 2018
2 parents 0be16e9 + 1959c37 commit 00a4aba
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 20 deletions.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -46,7 +46,8 @@ Available options:
- `--parallels-memory`: Size of memory for the host VM (in MB).
- `--parallels-cpu-count`: Number of CPUs to use to create the VM (-1 to use the number of CPUs available).
- `--parallels-video-size`: Size of video memory for host (in MB).
- `--parallels-no-share`: Disable the sharing of `/Users` directory.
- `--parallels-share-folder`: Path to the directory which should be shared with the host VM. Could be specified multiple times, once per each directory.
- `--parallels-no-share`: Disable the sharing of any directory.
- `--parallels-nested-virutalization`: Enable nested virtualization.

The `--parallels-boot2docker-url` flag takes a few different forms. By
Expand All @@ -70,6 +71,7 @@ Environment variables and default values:
| `--parallels-disk-size` | `PARALLELS_DISK_SIZE` | `20000` |
| `--parallels-memory` | `PARALLELS_MEMORY_SIZE` | `1024` |
| `--parallels-video-size` | `PARALLELS_VIDEO_SIZE` | `64` |
| `--parallels-share-folder` | - | `/Users` |
| `--parallels-no-share` | - | `false` |
| `--parallels-nested-virtualization` | - | `false` |

Expand Down
79 changes: 64 additions & 15 deletions parallels_darwin.go
Expand Up @@ -25,13 +25,13 @@ import (

const (
isoFilename = "boot2docker.iso"
shareFolderName = "Users"
shareFolderPath = "/Users"
shareFolderNamePrefix = "docker_machine_share_"
minDiskSize = 32
defaultCPU = 1
defaultMemory = 1024
defaultVideoSize = 64
defaultBoot2DockerURL = ""
defaultShareFolder = "/Users"
defaultNoShare = false
defaultDiskSize = 20000
defaultSSHPort = 22
Expand All @@ -43,6 +43,7 @@ var (
reMachineNotFound = regexp.MustCompile(`Failed to get VM config: The virtual machine could not be found..*`)
reParallelsVersion = regexp.MustCompile(`.* (\d+\.\d+\.\d+).*`)
reParallelsEdition = regexp.MustCompile(`edition="(.+)"`)
reSharedFolder = regexp.MustCompile(`\s*(.+) \(\+\) path='(.+)' mode=.+`)

errMachineExist = errors.New("machine already exists")
errMachineNotExist = errors.New("machine does not exist")
Expand All @@ -61,6 +62,7 @@ type Driver struct {
DiskSize int
Boot2DockerURL string
NoShare bool
ShareFolders []string
NestedVirtualization bool
}

Expand Down Expand Up @@ -226,10 +228,25 @@ func (d *Driver) Create() error {
}

if !d.NoShare {
if err = prlctl("set", d.MachineName,
"--shf-host-add", shareFolderName,
"--path", shareFolderPath); err != nil {
return err
for i, f := range d.ShareFolders {
// Ensure the path is absolute and is available
fAbs, err := filepath.Abs(f)
if err != nil {
return err
}
if _, err := os.Stat(fAbs); err != nil {
if os.IsNotExist(err) {
log.Infof("Host path '%s' does not exist. Skipping sharing it with the machine...", fAbs)
continue
}
return err
}

if err = prlctl("set", d.MachineName,
"--shf-host-add", fmt.Sprintf("%s%d", shareFolderNamePrefix, i),
"--path", fAbs); err != nil {
return err
}
}
}

Expand Down Expand Up @@ -471,7 +488,12 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
},
mcnflag.BoolFlag{
Name: "parallels-no-share",
Usage: "Disable the mount of your home directory",
Usage: "Disable the mount of shared folder",
},
mcnflag.StringSliceFlag{
Name: "parallels-share-folder",
Usage: "Path to the directory which should be shared with the machine. Default: /Users",
Value: []string{defaultShareFolder},
},
mcnflag.BoolFlag{
Name: "parallels-nested-virtualization",
Expand All @@ -492,6 +514,7 @@ func (d *Driver) SetConfigFromFlags(opts drivers.DriverOptions) error {
d.SSHUser = defaultSSHUser
d.SSHPort = defaultSSHPort
d.NoShare = opts.Bool("parallels-no-share")
d.ShareFolders = opts.StringSlice("parallels-share-folder")
d.NestedVirtualization = opts.Bool("parallels-nested-virtualization")

return nil
Expand Down Expand Up @@ -535,9 +558,16 @@ func (d *Driver) Start() error {
}

// Mount Share Folder
shareFoldersMap, err := d.getShareFolders()
if err != nil {
return err
}

if !d.NoShare {
if err := d.mountShareFolder(shareFolderName, shareFolderPath); err != nil {
return err
for shareName, sharePath := range shareFoldersMap {
if err := d.mountShareFolder(shareName, sharePath); err != nil {
return err
}
}
}

Expand Down Expand Up @@ -612,25 +642,44 @@ func (d *Driver) diskPath() string {
return absDiskPath
}

func (d *Driver) getShareFolders() (map[string]string, error) {
stdout, _, err := prlctlOutErr("list", "--info", d.MachineName)
if err != nil {
return nil, err
}

// Parse Shared Folder name (ID) and path
res := make(map[string]string)
for _, match := range reSharedFolder.FindAllStringSubmatch(string(stdout), -1) {
sName := match[1]
sPath := match[2]
log.Debugf("Found the configured shared folder. Name: %q, Path: %q\n", sName, sPath)
res[sName] = sPath
}
return res, nil
}

// Mounts shared folder to the specified guest path. It is assumed that host and guest paths are the same
func (d *Driver) mountShareFolder(shareName string, mountPoint string) error {
// Check the host path is available
if _, err := os.Stat(mountPoint); err != nil {
if os.IsNotExist(err) {
log.Infof("Host path '%s' does not exist. Skipping mount to VM...", mountPoint)
log.Infof("Host path %q does not exist. Skipping mount to VM...", mountPoint)
return nil
}
return err
}

// Ensure that share is available on the guest side
checkCmd := "sudo modprobe prl_fs && grep -w " + shareName + " /proc/fs/prl_fs/sf_list"
// Ensure that the share is available on the guest side
checkCmd := fmt.Sprintf("sudo modprobe prl_fs && grep -w %q /proc/fs/prl_fs/sf_list", shareName)
if _, err := drivers.RunSSHCommandFromDriver(d, checkCmd); err != nil {
log.Infof("Shared folder '%s' is unavailable. Skipping mount to VM...", shareName)
log.Infof("Shared folder %q is unavailable. Skipping mount to VM...", shareName)
return nil
}

// Mount shared folder
mountCmd := "sudo mkdir -p " + mountPoint + " && sudo mount -t prl_fs " + shareName + " " + mountPoint
// Mount the shared folder
log.Infof("Mounting shared folder %q ...", mountPoint)
mountCmd := fmt.Sprintf("sudo mkdir -p %q && sudo mount -t prl_fs %q %q", mountPoint, shareName, mountPoint)
if _, err := drivers.RunSSHCommandFromDriver(d, mountCmd); err != nil {
return fmt.Errorf("Error mounting shared folder: %s", err)
}
Expand Down
8 changes: 4 additions & 4 deletions test/integration/bats/core-commands.bats
Expand Up @@ -8,7 +8,7 @@ use_shared_machine
run machine inspect UNKNOWN
echo ${output}
[ "$status" -eq 1 ]
[[ ${lines[0]} =~ "Host does not exist: \"UNKNOWN\"" ]]
[[ ${lines[0]} =~ "Docker machine \"UNKNOWN\" does not exist" ]]
}

@test "$DRIVER: appears with ls" {
Expand All @@ -29,7 +29,7 @@ use_shared_machine
run machine create -d $DRIVER $NAME
echo ${output}
[ "$status" -eq 1 ]
[[ ${lines[0]} == "Host already exists: \"$NAME\"" ]]
[[ ${lines[0]} == "Docker machine \"$NAME\" already exists" ]]
}

@test "$DRIVER: run busybox container" {
Expand Down Expand Up @@ -68,8 +68,8 @@ use_shared_machine
echo ${output}
}

@test "$DRIVER: shared folder is mounted" {
run machine ssh $NAME -- "mount | grep prl_fs | awk '{ print $3 }'"
@test "$DRIVER: shared folder /Users is mounted by default" {
run machine ssh $NAME -- "mount -t prl_fs | awk -F ' on | type ' '{ print \$2 }'"
echo ${output}
[ "$status" -eq 0 ]
[[ ${output} == *"/Users"* ]]
Expand Down
23 changes: 23 additions & 0 deletions test/integration/bats/custom-share-folders.bats
@@ -0,0 +1,23 @@
#!/usr/bin/env bats

load ${BASE_TEST_DIR}/helpers.bash

use_disposable_machine

@test "$DRIVER: create with custom shared folders" {
run machine create -d $DRIVER --parallels-share-folder ~/Documents --parallels-share-folder /Library/Application\ Support $NAME
}

@test "$DRIVER: shared folder with a tilda (~) is mounted properly" {
run machine ssh $NAME -- "mount -t prl_fs | awk -F ' on | type ' '{ print \$2 }'"
echo ${output}
[ "$status" -eq 0 ]
[[ ${output} == *"/Users"*"/Documents"* ]]
}

@test "$DRIVER: shared folder with a space is mounted properly" {
run machine ssh $NAME -- "mount -t prl_fs | awk -F ' on | type ' '{ print \$2 }'"
echo ${output}
[ "$status" -eq 0 ]
[[ ${output} == *"/Library/Application Support"* ]]
}

0 comments on commit 00a4aba

Please sign in to comment.