Merge pull request #11570 from soapy1/mount-shared-folders

Automatically mount virtual box shared folder when machine reboots
This commit is contained in:
Sophia Castellarin 2020-06-12 14:11:21 -05:00 committed by GitHub
commit 4704606056
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 219 additions and 18 deletions

View File

@ -6,22 +6,17 @@ module VagrantPlugins
class MountVirtualBoxSharedFolder
extend SyncedFolder::UnixMountHelpers
VB_MOUNT_TYPE = "vboxsf".freeze
def self.mount_virtualbox_shared_folder(machine, name, guestpath, options)
guest_path = Shellwords.escape(guestpath)
@@logger.debug("Mounting #{name} (#{options[:hostpath]} to #{guestpath})")
builtin_mount_type = "-cit vboxsf"
addon_mount_type = "-t vboxsf"
builtin_mount_type = "-cit #{VB_MOUNT_TYPE}"
addon_mount_type = "-t #{VB_MOUNT_TYPE}"
mount_options = options.fetch(:mount_options, [])
detected_ids = detect_owner_group_ids(machine, guest_path, mount_options, options)
mount_uid = detected_ids[:uid]
mount_gid = detected_ids[:gid]
mount_options << "uid=#{mount_uid}"
mount_options << "gid=#{mount_gid}"
mount_options = mount_options.join(',')
mount_options, mount_uid, mount_gid = mount_options(machine, name, guest_path, options)
mount_command = "mount #{addon_mount_type} -o #{mount_options} #{name} #{guest_path}"
# Create the guest path if it doesn't exist
@ -61,7 +56,6 @@ module VagrantPlugins
emit_upstart_notification(machine, guest_path)
end
def self.unmount_virtualbox_shared_folder(machine, guestpath, options)
guest_path = Shellwords.escape(guestpath)

View File

@ -0,0 +1,48 @@
require "vagrant/util"
require_relative "../../../synced_folders/unix_mount_helpers"
module VagrantPlugins
module GuestLinux
module Cap
class PersistMountSharedFolder
extend SyncedFolder::UnixMountHelpers
# Inserts fstab entry for a set of synced folders. Will fully replace
# the currently managed group of Vagrant managed entries. Note, passing
# empty list of folders will just remove entries
#
# @param [Machine] machine The machine to run the action on
# @param [Map<String, Map>] A map of folders to add to fstab
# @param [String] mount type, ex. vboxfs, cifs, etc
def self.persist_mount_shared_folder(machine, fstab_folders, mount_type)
if fstab_folders.empty?
self.remove_vagrant_managed_fstab(machine)
return
end
export_folders = fstab_folders.map do |name, data|
guest_path = Shellwords.escape(data[:guestpath])
mount_options, mount_uid, mount_gid = mount_options(machine, name, guest_path, data)
mount_options = "#{mount_options},nofail"
{
name: name,
mount_point: guest_path,
mount_type: mount_type,
mount_options: mount_options,
}
end
fstab_entry = Vagrant::Util::TemplateRenderer.render('guests/linux/etc_fstab', folders: export_folders)
self.remove_vagrant_managed_fstab(machine)
machine.communicate.sudo("echo '#{fstab_entry}' >> /etc/fstab")
end
private
def self.remove_vagrant_managed_fstab(machine)
machine.communicate.sudo("sed -i '/\#VAGRANT-BEGIN/,/\#VAGRANT-END/d' /etc/fstab")
end
end
end
end
end

View File

@ -61,6 +61,11 @@ module VagrantPlugins
Cap::MountVirtualBoxSharedFolder
end
guest_capability(:linux, :persist_mount_shared_folder) do
require_relative "cap/persist_mount_shared_folder"
Cap::PersistMountSharedFolder
end
guest_capability(:linux, :network_interfaces) do
require_relative "cap/network_interfaces"
Cap::NetworkInterfaces

View File

@ -23,6 +23,7 @@ module VagrantPlugins
DEFAULT_VM_NAME = :default
attr_accessor :allowed_synced_folder_types
attr_accessor :allow_fstab_modification
attr_accessor :base_mac
attr_accessor :base_address
attr_accessor :boot_timeout
@ -58,6 +59,7 @@ module VagrantPlugins
@logger = Log4r::Logger.new("vagrant::config::vm")
@allowed_synced_folder_types = UNSET_VALUE
@allow_fstab_modification = UNSET_VALUE
@base_mac = UNSET_VALUE
@base_address = UNSET_VALUE
@boot_timeout = UNSET_VALUE
@ -502,6 +504,7 @@ module VagrantPlugins
def finalize!
# Defaults
@allow_fstab_modification = true if @allow_fstab_modification == UNSET_VALUE
@allowed_synced_folder_types = nil if @allowed_synced_folder_types == UNSET_VALUE
@base_mac = nil if @base_mac == UNSET_VALUE
@base_address = nil if @base_address == UNSET_VALUE
@ -988,6 +991,12 @@ module VagrantPlugins
end
end
if ![TrueClass, FalseClass].include?(@allow_fstab_modification.class)
errors["vm"] << I18n.t("vagrant.config.vm.config_type",
option: "allow_fstab_modification", given: @allow_fstab_modification.class, required: "Boolean"
)
end
errors
end

View File

@ -33,6 +33,7 @@ module VagrantPlugins
# Go through each folder and mount
machine.ui.output(I18n.t("vagrant.actions.vm.share_folders.mounting"))
fstab_folders = []
folders.each do |id, data|
if data[:guestpath]
# Guest path specified, so mount the folder to specified point
@ -52,12 +53,20 @@ module VagrantPlugins
machine.guest.capability(
:mount_virtualbox_shared_folder,
os_friendly_id(id), data[:guestpath], data)
fstab_folders << [os_friendly_id(id), data]
else
# If no guest path is specified, then automounting is disabled
machine.ui.detail(I18n.t("vagrant.actions.vm.share_folders.nomount_entry",
hostpath: data[:hostpath]))
end
end
if machine.guest.capability?(:persist_mount_shared_folder)
# If Vagrant has been configured to not allow fstab modification, then
# execute the guest capability with an empty list in order to ensure
# there is no Vagrant managed fstab entries.
fstab_folders = [] if !machine.config.vm.allow_fstab_modification
machine.guest.capability(:persist_mount_shared_folder, fstab_folders, "vboxsf")
end
end
def disable(machine, folders, _opts)

View File

@ -100,6 +100,17 @@ module VagrantPlugins
EOH
end
def mount_options(machine, name, guest_path, options)
mount_options = options.fetch(:mount_options, [])
detected_ids = detect_owner_group_ids(machine, guest_path, mount_options, options)
mount_uid = detected_ids[:uid]
mount_gid = detected_ids[:gid]
mount_options << "uid=#{mount_uid}"
mount_options << "gid=#{mount_gid}"
mount_options = mount_options.join(',')
return mount_options, mount_uid, mount_gid
end
end
end
end

View File

@ -0,0 +1,6 @@
#VAGRANT-BEGIN
# The contents below are automatically generated by Vagrant. Do not modify.
<% folders.each do |opts| %>
<%= opts[:name] %> <%= opts[:mount_point] %> <%= opts[:mount_type] %> <%= opts[:mount_options] || 'default' %> <%= opts[:dump] || 0 %> <%= opts[:fsck] || 0 %>
<%end%>
#VAGRANT-END

View File

@ -1947,6 +1947,7 @@ en:
box_download_options_not_converted: |-
Something went wrong converting VM config `box_download_options`. Value for provided key '%{missing_key}' is invalid type. Should be String or Bool
clone_and_box: "Only one of clone or box can be specified."
config_type: "Found '%{option}' specified as type '%{given}', should be '%{required}'"
hostname_invalid_characters: |-
The hostname set for the VM '%{name}' should only contain letters, numbers,
hyphens or dots. It cannot start with a hyphen or dot.

View File

@ -0,0 +1,52 @@
require_relative "../../../../base"
describe "VagrantPlugins::GuestLinux::Cap::PersistMountSharedFolder" do
let(:caps) do
VagrantPlugins::GuestLinux::Plugin
.components
.guest_capabilities[:linux]
end
let(:machine) { double("machine") }
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
let(:cap){ caps.get(:persist_mount_shared_folder) }
before do
allow(machine).to receive(:communicate).and_return(comm)
end
after do
comm.verify_expectations!
end
describe ".persist_mount_shared_folder" do
let(:options_gid){ '1234' }
let(:options_uid){ '1234' }
let (:fstab_folders) { [
["test1", {:guestpath=>"/test1", :hostpath=>"/my/host/path", :disabled=>false, :__vagrantfile=>true, :owner=>"vagrant", :group=>"vagrant", :mount_options=>["uid=1234", "gid=1234"] }],
["vagrant", {:guestpath=>"/vagrant", :hostpath=>"/my/host/vagrant", :disabled=>false, :__vagrantfile=>true, :owner=>"vagrant", :group=>"vagrant", :mount_options=>["uid=1234", "gid=1234"] }]
]}
let(:ui){ double(:ui) }
before do
allow(comm).to receive(:sudo).with(any_args)
allow(ui).to receive(:warn)
allow(machine).to receive(:ui).and_return(ui)
end
it "inserts folders into /etc/fstab" do
expected_entry_vagrant = "vagrant /vagrant vboxsf uid=1234,gid=1234,nofail 0 0"
expected_entry_test = "test1 /test1 vboxsf uid=1234,gid=1234,nofail 0 0"
expect(cap).to receive(:remove_vagrant_managed_fstab)
expect(comm).to receive(:sudo).with(/#{expected_entry_test}\n#{expected_entry_vagrant}/)
cap.persist_mount_shared_folder(machine, fstab_folders, "vboxsf")
end
it "does not insert an empty set of folders" do
expect(cap).to receive(:remove_vagrant_managed_fstab)
cap.persist_mount_shared_folder(machine, [], "type")
end
end
end

View File

@ -6,13 +6,35 @@ require Vagrant.source_root.join("plugins/providers/virtualbox/synced_folder")
describe VagrantPlugins::ProviderVirtualBox::SyncedFolder do
include_context "unit"
let(:vm_config) do
double("vm_config").tap do |vm_config|
allow(vm_config).to receive(:allow_fstab_modification).and_return(true)
end
end
let(:machine_config) do
double("machine_config").tap do |top_config|
allow(top_config).to receive(:vm).and_return(vm_config)
end
end
let(:machine) do
double("machine").tap do |m|
allow(m).to receive(:provider_config).and_return(VagrantPlugins::ProviderVirtualBox::Config.new)
allow(m).to receive(:provider_name).and_return(:virtualbox)
allow(m).to receive(:config).and_return(machine_config)
end
end
let(:folders) { {"/folder"=>
{:SharedFoldersEnableSymlinksCreate=>true,
:guestpath=>"/folder",
:hostpath=>"/Users/brian/vagrant-folder",
:automount=>false,
:disabled=>false,
:__vagrantfile=>true}} }
subject { described_class.new }
before do
@ -36,16 +58,55 @@ describe VagrantPlugins::ProviderVirtualBox::SyncedFolder do
end
end
describe "#enable" do
let(:ui){ double(:ui) }
let(:guest) { double("guest") }
let(:no_guestpath_folder) { {"/no_guestpath_folder"=>
{:SharedFoldersEnableSymlinksCreate=>false,
:guestpath=>nil,
:hostpath=>"/Users/brian/vagrant-folder",
:automount=>false,
:disabled=>true,
:__vagrantfile=>true}} }
before do
allow(subject).to receive(:share_folders).and_return(true)
allow(ui).to receive(:detail).with(any_args)
allow(ui).to receive(:output).with(any_args)
allow(machine).to receive(:ui).and_return(ui)
allow(machine).to receive(:ssh_info).and_return({:username => "test"})
allow(machine).to receive(:guest).and_return(guest)
end
it "should mount and persist all folders with a guest path" do
expect(guest).to receive(:capability).with(:mount_virtualbox_shared_folder, "folder", any_args)
expect(guest).not_to receive(:capability).with(:mount_virtualbox_shared_folder, "no_guestpath_folder", any_args)
expect(guest).to receive(:capability?).with(:persist_mount_shared_folder).and_return(true)
expect(guest).to receive(:capability).with(:persist_mount_shared_folder, any_args)
test_folders = folders.merge(no_guestpath_folder)
subject.enable(machine, test_folders, nil)
end
context "fstab modification disabled" do
before do
allow(vm_config).to receive(:allow_fstab_modification).and_return(false)
end
it "should not persist folders" do
expect(guest).to receive(:capability).with(:mount_virtualbox_shared_folder, "folder", any_args)
expect(guest).not_to receive(:capability).with(:mount_virtualbox_shared_folder, "no_guestpath_folder", any_args)
expect(guest).to receive(:capability?).with(:persist_mount_shared_folder).and_return(true)
expect(guest).to receive(:capability).with(:persist_mount_shared_folder, [], "vboxsf")
test_folders = folders.merge(no_guestpath_folder)
subject.enable(machine, test_folders, nil)
end
end
end
describe "#prepare" do
let(:driver) { double("driver") }
let(:provider) { double("driver", driver: driver) }
let(:folders) { {"/folder"=>
{:SharedFoldersEnableSymlinksCreate=>true,
:guestpath=>"/folder",
:hostpath=>"/Users/brian/vagrant-folder",
:automount=>false,
:disabled=>false,
:__vagrantfile=>true}} }
let(:folders_disabled) { {"/folder"=>
{:SharedFoldersEnableSymlinksCreate=>false,

View File

@ -16,6 +16,11 @@ machine that Vagrant manages.
## Available Settings
- `config.vm.allow_fstab_modification` (boolean) - If true, will add fstab
entries for synced folders. If false, no modifications to fstab will be made
by Vagrant. Note, this may mean that folders will not be automatically mounted
on machine reboot. Defaults to true.
- `config.vm.base_mac` (string) - The MAC address to be assigned to the default
NAT interface on the guest. _Support for this option is provider dependent._