vagrant_utm 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +1 -1
- data/docs/Gemfile.lock +2 -2
- data/docs/_config.yml +2 -2
- data/docs/boxes/creating_utm_box.md +7 -6
- data/docs/boxes/utm_box_gallery.md +10 -5
- data/docs/features/synced_folders.md +11 -11
- data/docs/index.md +7 -7
- data/docs/internals/actions.md +2 -1
- data/docs/known_issues.md +2 -2
- data/lib/vagrant_utm/action/prepare_nfs_settings.rb +79 -0
- data/lib/vagrant_utm/action/prepare_nfs_valid_ids.rb +24 -0
- data/lib/vagrant_utm/action.rb +22 -2
- data/lib/vagrant_utm/cap/mount_options.rb +55 -0
- data/lib/vagrant_utm/config.rb +26 -6
- data/lib/vagrant_utm/driver/base.rb +6 -0
- data/lib/vagrant_utm/driver/meta.rb +11 -3
- data/lib/vagrant_utm/driver/version_4_5.rb +10 -0
- data/lib/vagrant_utm/driver/version_4_6.rb +67 -0
- data/lib/vagrant_utm/plugin.rb +21 -0
- data/lib/vagrant_utm/scripts/add_folder_share.applescript +96 -0
- data/lib/vagrant_utm/scripts/add_qemu_additional_args.applescript +43 -0
- data/lib/vagrant_utm/scripts/read_shared_folders.js +47 -0
- data/lib/vagrant_utm/scripts/read_shared_folders_args.js +53 -0
- data/lib/vagrant_utm/scripts/remove_qemu_additional_args.applescript +46 -0
- data/lib/vagrant_utm/synced_folder.rb +127 -0
- data/lib/vagrant_utm/util/unix_mount_helpers.rb +124 -0
- data/lib/vagrant_utm/version.rb +1 -1
- data/notes/README.md +26 -6
- metadata +15 -5
@@ -13,6 +13,25 @@ module VagrantPlugins
|
|
13
13
|
@logger = Log4r::Logger.new("vagrant::provider::utm::version_4_6")
|
14
14
|
end
|
15
15
|
|
16
|
+
# Implement clear_shared_folders
|
17
|
+
def clear_shared_folders
|
18
|
+
# Get the list of shared folders
|
19
|
+
shared_folders = read_shared_folders
|
20
|
+
# Get the args to remove the shared folders
|
21
|
+
script_path = @script_path.join("read_shared_folders_args.js")
|
22
|
+
cmd = ["osascript", script_path.to_s, @uuid, "--ids", shared_folders.join(",")]
|
23
|
+
output = execute_shell(*cmd)
|
24
|
+
result = JSON.parse(output)
|
25
|
+
return unless result["status"]
|
26
|
+
|
27
|
+
# Flatten the list of args and build the command
|
28
|
+
sf_args = result["result"].flatten
|
29
|
+
return unless sf_args.any?
|
30
|
+
|
31
|
+
command = ["remove_qemu_additional_args.applescript", @uuid, "--args", *sf_args]
|
32
|
+
execute_osa_script(command)
|
33
|
+
end
|
34
|
+
|
16
35
|
def import(utm)
|
17
36
|
utm = Vagrant::Util::Platform.windows_path(utm)
|
18
37
|
|
@@ -36,6 +55,54 @@ module VagrantPlugins
|
|
36
55
|
command = ["export_vm.applescript", @uuid, path]
|
37
56
|
execute_osa_script(command)
|
38
57
|
end
|
58
|
+
|
59
|
+
def read_shared_folders
|
60
|
+
@logger.debug("Reading shared folders")
|
61
|
+
script_path = @script_path.join("read_shared_folders.js")
|
62
|
+
cmd = ["osascript", script_path.to_s, @uuid]
|
63
|
+
output = execute_shell(*cmd)
|
64
|
+
result = JSON.parse(output)
|
65
|
+
return unless result["status"]
|
66
|
+
|
67
|
+
# Return the list of shared folders names(id)
|
68
|
+
result["result"]
|
69
|
+
end
|
70
|
+
|
71
|
+
def share_folders(folders)
|
72
|
+
# sync folder cleanup will call clear_shared_folders
|
73
|
+
# This is just a precaution, to make sure we don't
|
74
|
+
# have duplicate shared folders
|
75
|
+
shared_folders = read_shared_folders
|
76
|
+
@logger.debug("Shared folders: #{shared_folders}")
|
77
|
+
@logger.debug("Sharing folders: #{folders}")
|
78
|
+
|
79
|
+
folders.each do |folder|
|
80
|
+
# Skip if the folder is already shared
|
81
|
+
next if shared_folders.include?(folder[:name])
|
82
|
+
|
83
|
+
args = ["--id", folder[:name],
|
84
|
+
"--dir", folder[:hostpath]]
|
85
|
+
command = ["add_folder_share.applescript", @uuid, *args]
|
86
|
+
execute_osa_script(command)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def unshare_folders(folders)
|
91
|
+
@logger.debug("Unsharing folder: #{folder[:name]}")
|
92
|
+
# Get the args to remove the shared folders
|
93
|
+
script_path = @script_path.join("read_shared_folders_args.js")
|
94
|
+
cmd = ["osascript", script_path.to_s, @uuid, "--ids", folders.join(",")]
|
95
|
+
output = execute_shell(*cmd)
|
96
|
+
result = JSON.parse(output)
|
97
|
+
return unless result["status"]
|
98
|
+
|
99
|
+
# Flatten the list of args and build the command
|
100
|
+
sf_args = result["result"].flatten
|
101
|
+
return unless sf_args.any?
|
102
|
+
|
103
|
+
command = ["remove_qemu_additional_args.applescript", @uuid, "--args", *sf_args]
|
104
|
+
execute_osa_script(command)
|
105
|
+
end
|
39
106
|
end
|
40
107
|
end
|
41
108
|
end
|
data/lib/vagrant_utm/plugin.rb
CHANGED
@@ -33,6 +33,12 @@ module VagrantPlugins
|
|
33
33
|
Config
|
34
34
|
end
|
35
35
|
|
36
|
+
# Register the synced folder implementation
|
37
|
+
synced_folder(:utm) do
|
38
|
+
require_relative "synced_folder"
|
39
|
+
SyncedFolder
|
40
|
+
end
|
41
|
+
|
36
42
|
# Register capabilities
|
37
43
|
provider_capability(:utm, :forwarded_ports) do
|
38
44
|
require_relative "cap"
|
@@ -44,6 +50,21 @@ module VagrantPlugins
|
|
44
50
|
Cap
|
45
51
|
end
|
46
52
|
|
53
|
+
synced_folder_capability(:utm, "mount_options") do
|
54
|
+
require_relative "cap/mount_options"
|
55
|
+
Cap::MountOptions
|
56
|
+
end
|
57
|
+
|
58
|
+
synced_folder_capability(:utm, "mount_type") do
|
59
|
+
require_relative "cap/mount_options"
|
60
|
+
Cap::MountOptions
|
61
|
+
end
|
62
|
+
|
63
|
+
synced_folder_capability(:utm, "mount_name") do
|
64
|
+
require_relative "cap/mount_options"
|
65
|
+
Cap::MountOptions
|
66
|
+
end
|
67
|
+
|
47
68
|
# Register the command
|
48
69
|
## Start machine as a snapshot and do not save changes to disk
|
49
70
|
command "disposable" do
|
@@ -0,0 +1,96 @@
|
|
1
|
+
---
|
2
|
+
-- add_directory_share.applescript
|
3
|
+
-- This script adds QEMU arguments for directory sharing in UTM (QEMU) for given id and directory pairs.
|
4
|
+
-- Usage: osascript add_directory_share.applescript UUID --id <ID1> --dir <DIR1> --id <ID2> --dir <DIR2> ...
|
5
|
+
-- Example: osascript add_directory_share.applescript UUID --id no1 --dir "/path/to/dir1" --id no2 --dir "/path/to/dir2"
|
6
|
+
|
7
|
+
-- Function to create QEMU arguments for directory sharing
|
8
|
+
on createQemuArgsForDir(dirId, dirPath)
|
9
|
+
|
10
|
+
-- Prepare the QEMU argument strings
|
11
|
+
set fsdevArgStr to "-fsdev local,id=" & dirId & ",path=" & dirPath & ",security_model=mapped-xattr"
|
12
|
+
set deviceArgStr to "-device virtio-9p-pci,fsdev=" & dirId & ",mount_tag=" & dirId
|
13
|
+
|
14
|
+
return {fsdevArgStr, deviceArgStr}
|
15
|
+
end createQemuArgsForDir
|
16
|
+
|
17
|
+
-- Main script
|
18
|
+
on run argv
|
19
|
+
-- VM id is assumed to be the first argument
|
20
|
+
set vmId to item 1 of argv
|
21
|
+
|
22
|
+
-- Initialize variables
|
23
|
+
set idList to {} --
|
24
|
+
set dirList to {}
|
25
|
+
set idFlag to false
|
26
|
+
set dirFlag to false
|
27
|
+
|
28
|
+
-- Parse arguments
|
29
|
+
repeat with i from 2 to (count of argv)
|
30
|
+
set currentArg to item i of argv
|
31
|
+
if currentArg is "--id" then
|
32
|
+
set idFlag to true
|
33
|
+
set dirFlag to false
|
34
|
+
else if currentArg is "--dir" then
|
35
|
+
set dirFlag to true
|
36
|
+
set idFlag to false
|
37
|
+
else if idFlag then
|
38
|
+
set end of idList to currentArg
|
39
|
+
set idFlag to false
|
40
|
+
else if dirFlag then
|
41
|
+
set end of dirList to currentArg
|
42
|
+
set dirFlag to false
|
43
|
+
end if
|
44
|
+
end repeat
|
45
|
+
|
46
|
+
-- Ensure the lists are of the same length
|
47
|
+
if (count of idList) is not (count of dirList) then
|
48
|
+
error "The number of IDs and directories must be the same."
|
49
|
+
end if
|
50
|
+
|
51
|
+
-- Initialize the list of QEMU arguments
|
52
|
+
set qemuNewArgs to {}
|
53
|
+
|
54
|
+
-- Initialize the directory list
|
55
|
+
set directoryList to {}
|
56
|
+
|
57
|
+
-- Create QEMU arguments for each directory
|
58
|
+
repeat with i from 1 to (count of dirList)
|
59
|
+
set dirPath to item i of dirList
|
60
|
+
set dirId to item i of idList
|
61
|
+
set dirURL to POSIX file dirPath
|
62
|
+
set {fsdevArgStr, deviceArgStr} to createQemuArgsForDir(dirId, dirPath)
|
63
|
+
|
64
|
+
-- add the directory file obj to the list
|
65
|
+
set end of directoryList to dirURL
|
66
|
+
-- append the arguments to the list
|
67
|
+
set end of qemuNewArgs to {fsdevArg:fsdevArgStr, deviceArg:deviceArgStr, dirURL:dirURL}
|
68
|
+
end repeat
|
69
|
+
|
70
|
+
-- Example usage in UTM
|
71
|
+
tell application "UTM"
|
72
|
+
set vm to virtual machine id vmId
|
73
|
+
set config to configuration of vm
|
74
|
+
|
75
|
+
-- Get the current QEMU additional arguments
|
76
|
+
set qemuAddArgs to qemu additional arguments of config
|
77
|
+
|
78
|
+
-- Add the new arguments to the existing ones
|
79
|
+
repeat with arg in qemuNewArgs
|
80
|
+
-- SKIP: adding file urls to qemu args file urls , since it is not necessary. UTM#6977
|
81
|
+
set end of qemuAddArgs to {argument string:fsdevArg of arg}
|
82
|
+
set end of qemuAddArgs to {argument string:deviceArg of arg}
|
83
|
+
end repeat
|
84
|
+
|
85
|
+
-- Update the configuration with the new arguments list
|
86
|
+
set qemu additional arguments of config to qemuAddArgs
|
87
|
+
update configuration of vm with config
|
88
|
+
|
89
|
+
-- Get the current directory shares in registry
|
90
|
+
set reg to registry of vm
|
91
|
+
-- Add new directory shares to the registry
|
92
|
+
set reg to reg & directoryList
|
93
|
+
-- Update registry of vm with new directory shares
|
94
|
+
update registry of vm with reg
|
95
|
+
end tell
|
96
|
+
end run
|
@@ -0,0 +1,43 @@
|
|
1
|
+
---
|
2
|
+
-- add_qemu_additional_args.applescript
|
3
|
+
-- This script adds qemu arguments to a specified UTM virtual machine.
|
4
|
+
-- Usage: osascript add_qemu_additional_args.applescript <VM_UUID> --args <arg1> <arg2> ...
|
5
|
+
-- Example: osascript add_qemu_additional_args.applescript A123 --args "-vnc 127.0.0.1:13" "-vnc..."
|
6
|
+
|
7
|
+
on run argv
|
8
|
+
set vmId to item 1 of argv # UUID of the VM
|
9
|
+
|
10
|
+
-- Initialize variables
|
11
|
+
set argsList to {}
|
12
|
+
set argsFlag to false
|
13
|
+
|
14
|
+
-- Parse the --args arguments
|
15
|
+
repeat with i from 2 to (count of argv)
|
16
|
+
set currentArg to item i of argv
|
17
|
+
if currentArg is "--args" then
|
18
|
+
set argsFlag to true
|
19
|
+
else if argsFlag then
|
20
|
+
set end of argsList to currentArg
|
21
|
+
end if
|
22
|
+
end repeat
|
23
|
+
|
24
|
+
tell application "UTM"
|
25
|
+
-- Get the VM and its configuration
|
26
|
+
set vm to virtual machine id vmId -- Id is assumed to be valid
|
27
|
+
set config to configuration of vm
|
28
|
+
|
29
|
+
-- Existing arguments
|
30
|
+
set qemuAddArgs to qemu additional arguments of config
|
31
|
+
|
32
|
+
-- Create new arguments from argsList and add them to the existing arguments
|
33
|
+
repeat with arg in argsList
|
34
|
+
set end of qemuAddArgs to {argument string:arg}
|
35
|
+
end repeat
|
36
|
+
|
37
|
+
--- set qemu args with new args list
|
38
|
+
set qemu additional arguments of config to qemuAddArgs
|
39
|
+
|
40
|
+
--- save the configuration (VM must be stopped)
|
41
|
+
update configuration of vm with config
|
42
|
+
end tell
|
43
|
+
end run
|
@@ -0,0 +1,47 @@
|
|
1
|
+
/**
|
2
|
+
* Reads the shared directory IDs from the QEMU additional arguments of a specified VM in UTM.
|
3
|
+
*
|
4
|
+
* This function uses the UTM application to read the QEMU additional arguments
|
5
|
+
* of a VM identified by the given VM ID and extracts the IDs of shared directories.
|
6
|
+
*
|
7
|
+
* @param {string} vmIdentifier - The ID of the VM.
|
8
|
+
* @returns {string} A JSON string containing the shared directory IDs or an error message.
|
9
|
+
*/
|
10
|
+
function run(argv) {
|
11
|
+
// Check if a VM ID is provided
|
12
|
+
if (argv.length === 0) {
|
13
|
+
console.log("Usage: osascript -l JavaScript read_shared_directories.js <vm_id>");
|
14
|
+
return JSON.stringify({ status: false, result: "No VM ID provided." });
|
15
|
+
}
|
16
|
+
|
17
|
+
const vmIdentifier = argv[0];
|
18
|
+
const utm = Application('UTM');
|
19
|
+
utm.includeStandardAdditions = true;
|
20
|
+
|
21
|
+
try {
|
22
|
+
// Attempt to get the VM by ID
|
23
|
+
const vm = utm.virtualMachines.byId(vmIdentifier);
|
24
|
+
// Get the config of the VM
|
25
|
+
const config = vm.configuration();
|
26
|
+
// Get the QEMU additional arguments
|
27
|
+
const qemuArgs = config.qemuAdditionalArguments;
|
28
|
+
|
29
|
+
// Extract shared directory IDs
|
30
|
+
const sharedDirIds = [];
|
31
|
+
qemuArgs.forEach(arg => {
|
32
|
+
const argStr = arg.argumentString;
|
33
|
+
if (argStr.startsWith("-fsdev")) {
|
34
|
+
const match = argStr.match(/id=([^,]+)/);
|
35
|
+
if (match) {
|
36
|
+
sharedDirIds.push(match[1]);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
});
|
40
|
+
|
41
|
+
// Return the shared directory IDs
|
42
|
+
return JSON.stringify({ status: true, result: sharedDirIds });
|
43
|
+
} catch (error) {
|
44
|
+
// Return an error message
|
45
|
+
return JSON.stringify({ status: false, result: error.message });
|
46
|
+
}
|
47
|
+
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
/**
|
2
|
+
* Reads the shared folder QEMU arguments for specified IDs from the QEMU additional arguments of a VM in UTM.
|
3
|
+
*
|
4
|
+
* This function uses the UTM application to read the QEMU additional arguments
|
5
|
+
* of a VM identified by the given VM ID and extracts the arguments for the specified IDs.
|
6
|
+
*
|
7
|
+
* @param {string} vmIdentifier - The ID of the VM.
|
8
|
+
* @param {string} ids - Comma-separated list of directory IDs.
|
9
|
+
* @returns {string} A JSON string containing the QEMU arguments for the specified IDs or an error message.
|
10
|
+
*/
|
11
|
+
function run(argv) {
|
12
|
+
// Check if a VM ID and IDs are provided
|
13
|
+
if (argv.length < 2) {
|
14
|
+
console.log("Usage: osascript -l JavaScript read_shared_folders_args.js <vm_id> --ids <dirID>,<dirID>");
|
15
|
+
return JSON.stringify({ status: false, result: "No VM ID or IDs provided." });
|
16
|
+
}
|
17
|
+
|
18
|
+
const vmIdentifier = argv[0];
|
19
|
+
const idsArgIndex = argv.indexOf("--ids");
|
20
|
+
if (idsArgIndex === -1 || idsArgIndex + 1 >= argv.length) {
|
21
|
+
return JSON.stringify({ status: false, result: "No IDs provided." });
|
22
|
+
}
|
23
|
+
const ids = argv[idsArgIndex + 1].split(",");
|
24
|
+
|
25
|
+
const utm = Application('UTM');
|
26
|
+
utm.includeStandardAdditions = true;
|
27
|
+
|
28
|
+
try {
|
29
|
+
// Attempt to get the VM by ID
|
30
|
+
const vm = utm.virtualMachines.byId(vmIdentifier);
|
31
|
+
// Get the config of the VM
|
32
|
+
const config = vm.configuration();
|
33
|
+
// Get the QEMU additional arguments
|
34
|
+
const qemuArgs = config.qemuAdditionalArguments;
|
35
|
+
|
36
|
+
// Extract QEMU arguments for the specified IDs
|
37
|
+
const sharedDirArgs = [];
|
38
|
+
qemuArgs.forEach(arg => {
|
39
|
+
const argStr = arg.argumentString;
|
40
|
+
ids.forEach(id => {
|
41
|
+
if (argStr.includes(`id=${id}`) || argStr.includes(`fsdev=${id}`)) {
|
42
|
+
sharedDirArgs.push(argStr);
|
43
|
+
}
|
44
|
+
});
|
45
|
+
});
|
46
|
+
|
47
|
+
// Return the QEMU arguments for the specified IDs
|
48
|
+
return JSON.stringify({ status: true, result: sharedDirArgs });
|
49
|
+
} catch (error) {
|
50
|
+
// Return an error message
|
51
|
+
return JSON.stringify({ status: false, result: error.message });
|
52
|
+
}
|
53
|
+
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
---
|
2
|
+
-- remove_qemu_additional_args.applescript
|
3
|
+
-- This script removes specified qemu arguments from a specified UTM virtual machine.
|
4
|
+
-- Usage: osascript remove_qemu_additional_args.applescript <VM_UUID> --args <arg1> <arg2> ...
|
5
|
+
-- Example: osascript remove_qemu_additional_args.applescript A123 --args "-vnc 127.0.0.1:13" "-vnc..."
|
6
|
+
|
7
|
+
on run argv
|
8
|
+
set vmId to item 1 of argv -- UUID of the VM
|
9
|
+
|
10
|
+
-- Initialize variables
|
11
|
+
set argsToRemove to {}
|
12
|
+
set argsFlag to false
|
13
|
+
|
14
|
+
-- Parse the --args arguments
|
15
|
+
repeat with i from 2 to (count of argv)
|
16
|
+
set currentArg to item i of argv
|
17
|
+
if currentArg is "--args" then
|
18
|
+
set argsFlag to true
|
19
|
+
else if argsFlag then
|
20
|
+
set end of argsToRemove to currentArg
|
21
|
+
end if
|
22
|
+
end repeat
|
23
|
+
|
24
|
+
tell application "UTM"
|
25
|
+
-- Get the VM and its configuration
|
26
|
+
set vm to virtual machine id vmId -- Id is assumed to be valid
|
27
|
+
set config to configuration of vm
|
28
|
+
|
29
|
+
-- Get the current QEMU additional arguments
|
30
|
+
set qemuAddArgs to qemu additional arguments of config
|
31
|
+
|
32
|
+
-- Initialize a new list for the updated arguments
|
33
|
+
set updatedArgs to {}
|
34
|
+
|
35
|
+
-- Iterate through the current arguments and add all except the ones to remove
|
36
|
+
repeat with arg in qemuAddArgs
|
37
|
+
if arg is not in argsToRemove then
|
38
|
+
set end of updatedArgs to arg
|
39
|
+
end if
|
40
|
+
end repeat
|
41
|
+
|
42
|
+
-- Update the configuration with the new arguments list
|
43
|
+
set qemu additional arguments of config to updatedArgs
|
44
|
+
update configuration of vm with config
|
45
|
+
end tell
|
46
|
+
end run
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Utm
|
5
|
+
# Default Synced folder implementation for UTM
|
6
|
+
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
|
7
|
+
def usable?(machine, _raise_errors = false) # rubocop:disable Style/OptionalBooleanParameter
|
8
|
+
# These synced folders only work if the provider is UTM
|
9
|
+
return false if machine.provider_name != :utm
|
10
|
+
|
11
|
+
# This only happens with `vagrant package --base`. Sigh.
|
12
|
+
return true unless machine.provider_config
|
13
|
+
|
14
|
+
machine.provider_config.functional_9pfs
|
15
|
+
end
|
16
|
+
|
17
|
+
# This is called before VM Boot to prepare the synced folders.
|
18
|
+
# Add required configs to the VM.
|
19
|
+
def prepare(machine, folders, _opts)
|
20
|
+
share_folders(machine, folders)
|
21
|
+
end
|
22
|
+
|
23
|
+
# This is called after VM Boot to mount the synced folders.
|
24
|
+
# Mount the shared folders inside the VM.
|
25
|
+
def enable(machine, folders, _opts) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/PerceivedComplexity
|
26
|
+
share_folders(machine, folders)
|
27
|
+
|
28
|
+
# sort guestpaths first, so we don't step on ourselves
|
29
|
+
folders = folders.sort_by do |_id, data|
|
30
|
+
if data[:guestpath]
|
31
|
+
data[:guestpath].length
|
32
|
+
else
|
33
|
+
# A long enough path to just do this at the end.
|
34
|
+
10_000
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Go through each folder and mount
|
39
|
+
machine.ui.output(I18n.t("vagrant.actions.vm.share_folders.mounting"))
|
40
|
+
# refresh fstab
|
41
|
+
fstab_folders = [] # rubocop:disable Lint/UselessAssignment
|
42
|
+
folders.each do |id, data|
|
43
|
+
if data[:guestpath]
|
44
|
+
# Guest path specified, so mount the folder to specified point
|
45
|
+
machine.ui.detail(I18n.t("vagrant.actions.vm.share_folders.mounting_entry",
|
46
|
+
guestpath: data[:guestpath],
|
47
|
+
hostpath: data[:hostpath]))
|
48
|
+
|
49
|
+
# Dup the data so we can pass it to the guest API
|
50
|
+
data = data.dup
|
51
|
+
|
52
|
+
# Calculate the owner and group
|
53
|
+
ssh_info = machine.ssh_info
|
54
|
+
data[:owner] ||= ssh_info[:username]
|
55
|
+
data[:group] ||= ssh_info[:username]
|
56
|
+
|
57
|
+
# Unmount the folder before we mount it
|
58
|
+
machine.guest.capability(
|
59
|
+
:unmount_virtualbox_shared_folder,
|
60
|
+
data[:guestpath], data
|
61
|
+
)
|
62
|
+
|
63
|
+
# Mount the actual folder
|
64
|
+
machine.guest.capability(
|
65
|
+
:mount_virtualbox_shared_folder,
|
66
|
+
os_friendly_id(id), data[:guestpath], data
|
67
|
+
)
|
68
|
+
else
|
69
|
+
# If no guest path is specified, then automounting is disabled
|
70
|
+
machine.ui.detail(I18n.t("vagrant.actions.vm.share_folders.nomount_entry",
|
71
|
+
hostpath: data[:hostpath]))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def disable(machine, folders, _opts)
|
77
|
+
if machine.guest.capability?(:unmount_virtualbox_shared_folder)
|
78
|
+
folders.each_value do |data|
|
79
|
+
machine.guest.capability(
|
80
|
+
:unmount_virtualbox_shared_folder,
|
81
|
+
data[:guestpath], data
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Remove the shared folders from the VM metadata
|
87
|
+
names = folders.map { |id, _data| os_friendly_id(id) }
|
88
|
+
driver(machine).unshare_folders(names)
|
89
|
+
end
|
90
|
+
|
91
|
+
def cleanup(machine, _opts)
|
92
|
+
driver(machine).clear_shared_folders if machine.id && machine.id != ""
|
93
|
+
end
|
94
|
+
|
95
|
+
protected
|
96
|
+
|
97
|
+
# This is here so that we can stub it for tests
|
98
|
+
def driver(machine)
|
99
|
+
machine.provider.driver
|
100
|
+
end
|
101
|
+
|
102
|
+
def os_friendly_id(id)
|
103
|
+
id.gsub(%r{[\s/\\]}, "_").sub(/^_/, "")
|
104
|
+
end
|
105
|
+
|
106
|
+
# share_folders sets up the shared folder definitions on the
|
107
|
+
# UTM VM.
|
108
|
+
#
|
109
|
+
def share_folders(machine, folders)
|
110
|
+
defs = []
|
111
|
+
|
112
|
+
folders.each do |id, data|
|
113
|
+
hostpath = data[:hostpath]
|
114
|
+
hostpath = Vagrant::Util::Platform.cygwin_windows_path(hostpath) unless data[:hostpath_exact]
|
115
|
+
|
116
|
+
defs << {
|
117
|
+
name: os_friendly_id(id),
|
118
|
+
hostpath: hostpath.to_s,
|
119
|
+
automount: !data[:automount].nil?
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
driver(machine).share_folders(defs)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copied from vagrant/plugins/synced_folder/unix_mount_helpers.rb
|
4
|
+
# Copyright (c) HashiCorp, Inc.
|
5
|
+
# SPDX-License-Identifier: BUSL-1.1
|
6
|
+
|
7
|
+
require "shellwords"
|
8
|
+
require "vagrant/util/retryable"
|
9
|
+
|
10
|
+
module VagrantPlugins
|
11
|
+
module SyncedFolder
|
12
|
+
# Contains helper methods for mounting folders on Unix-based systems.
|
13
|
+
module UnixMountHelpers # rubocop:disable Metrics/ModuleLength
|
14
|
+
def self.extended(klass)
|
15
|
+
unless klass.class_variable_defined?(:@@logger)
|
16
|
+
klass.class_variable_set(:@@logger, Log4r::Logger.new(klass.name.downcase)) # rubocop:disable Style/ClassVars
|
17
|
+
end
|
18
|
+
klass.extend Vagrant::Util::Retryable
|
19
|
+
end
|
20
|
+
|
21
|
+
def detect_owner_group_ids(machine, guest_path, mount_options, options) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
22
|
+
mount_uid = find_mount_options_id("uid", mount_options)
|
23
|
+
mount_gid = find_mount_options_id("gid", mount_options)
|
24
|
+
|
25
|
+
if mount_uid.nil?
|
26
|
+
if options[:owner].to_i.to_s == options[:owner].to_s
|
27
|
+
mount_uid = options[:owner]
|
28
|
+
class_variable_get(:@@logger).debug("Owner user ID (provided): #{mount_uid}")
|
29
|
+
else
|
30
|
+
output = { stdout: String.new, stderr: String.new } # Ensure strings are not frozen
|
31
|
+
uid_command = "id -u #{options[:owner]}"
|
32
|
+
machine.communicate.execute(uid_command,
|
33
|
+
error_class: Vagrant::Errors::VirtualBoxMountFailed,
|
34
|
+
error_key: :virtualbox_mount_failed,
|
35
|
+
command: uid_command,
|
36
|
+
output: output[:stderr]) { |type, data| output[type] << data if output[type] }
|
37
|
+
mount_uid = output[:stdout].chomp
|
38
|
+
class_variable_get(:@@logger).debug("Owner user ID (lookup): #{options[:owner]} -> #{mount_uid}")
|
39
|
+
end
|
40
|
+
else
|
41
|
+
machine.ui.warn "Detected mount owner ID within mount options. (uid: #{mount_uid} guestpath: #{guest_path})"
|
42
|
+
end
|
43
|
+
|
44
|
+
if mount_gid.nil?
|
45
|
+
if options[:group].to_i.to_s == options[:group].to_s
|
46
|
+
mount_gid = options[:group]
|
47
|
+
class_variable_get(:@@logger).debug("Owner group ID (provided): #{mount_gid}")
|
48
|
+
else
|
49
|
+
begin
|
50
|
+
{ stdout: String.new, stderr: String.new } # Ensure strings are not frozen
|
51
|
+
gid_command = "getent group #{options[:group]}"
|
52
|
+
machine.communicate.execute(gid_command,
|
53
|
+
error_class: Vagrant::Errors::VirtualBoxMountFailed,
|
54
|
+
error_key: :virtualbox_mount_failed,
|
55
|
+
command: gid_command,
|
56
|
+
output: output[:stderr]) { |type, data| output[type] << data if output[type] }
|
57
|
+
mount_gid = output[:stdout].split(":").at(2).to_s.chomp
|
58
|
+
class_variable_get(:@@logger).debug("Owner group ID (lookup): #{options[:group]} -> #{mount_gid}")
|
59
|
+
rescue Vagrant::Errors::VirtualBoxMountFailed
|
60
|
+
if options[:owner] == options[:group] # rubocop:disable Metrics/BlockNesting
|
61
|
+
class_variable_get(:@@logger).debug("Failed to locate group `#{options[:group]}`. Group name matches owner. Fetching effective group ID.") # rubocop:disable Layout/LineLength
|
62
|
+
output = { stdout: String.new }
|
63
|
+
result = machine.communicate.execute("id -g #{options[:owner]}",
|
64
|
+
error_check: false) do |type, data|
|
65
|
+
output[type] << data if output[type] # rubocop:disable Metrics/BlockNesting
|
66
|
+
end
|
67
|
+
mount_gid = output[:stdout].chomp if result.zero? # rubocop:disable Metrics/BlockNesting
|
68
|
+
class_variable_get(:@@logger).debug("Owner group ID (effective): #{mount_gid}")
|
69
|
+
end
|
70
|
+
raise unless mount_gid
|
71
|
+
end
|
72
|
+
end
|
73
|
+
else
|
74
|
+
machine.ui.warn "Detected mount group ID within mount options. (gid: #{mount_gid} guestpath: #{guest_path})"
|
75
|
+
end
|
76
|
+
{ gid: mount_gid, uid: mount_uid }
|
77
|
+
end
|
78
|
+
|
79
|
+
def find_mount_options_id(id_name, mount_options) # rubocop:disable Metrics/AbcSize
|
80
|
+
id_line = mount_options.detect { |line| line.include?("#{id_name}=") }
|
81
|
+
if id_line
|
82
|
+
match = id_line.match(/,?#{Regexp.escape(id_name)}=(?<option_id>\d+),?/)
|
83
|
+
found_id = match["option_id"]
|
84
|
+
updated_id_line = [
|
85
|
+
match.pre_match,
|
86
|
+
match.post_match
|
87
|
+
].find_all { |string| !string.empty? }.join(",")
|
88
|
+
if updated_id_line.empty?
|
89
|
+
mount_options.delete(id_line)
|
90
|
+
else
|
91
|
+
idx = mount_options.index(id_line)
|
92
|
+
mount_options.delete(idx)
|
93
|
+
mount_options.insert(idx, updated_id_line)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
found_id
|
97
|
+
end
|
98
|
+
|
99
|
+
def emit_upstart_notification(machine, guest_path)
|
100
|
+
# Emit an upstart event if we can
|
101
|
+
machine.communicate.sudo <<-NOTIFICATION.gsub(/^ {12}/, "")
|
102
|
+
if test -x /sbin/initctl && command -v /sbin/init && /sbin/init 2>/dev/null --version | grep upstart; then
|
103
|
+
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guest_path}
|
104
|
+
fi
|
105
|
+
NOTIFICATION
|
106
|
+
end
|
107
|
+
|
108
|
+
def merge_mount_options(base, overrides) # rubocop:disable Metrics/AbcSize
|
109
|
+
base = base.join(",").split(",")
|
110
|
+
overrides = overrides.join(",").split(",")
|
111
|
+
b_kv = Hash[base.map { |item| item.split("=", 2) }]
|
112
|
+
o_kv = Hash[overrides.map { |item| item.split("=", 2) }]
|
113
|
+
merged = {}.tap do |opts|
|
114
|
+
(b_kv.keys + o_kv.keys).uniq.each do |key|
|
115
|
+
opts[key] = o_kv.fetch(key, b_kv[key])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
merged.map do |key, value|
|
119
|
+
[key, value].compact.join("=")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|