Merge pull request #11797 from soapy1/synced-folder-caps

Synced folder caps for mount options and default fstab modification behaviour
This commit is contained in:
Sophia Castellarin 2020-08-21 11:44:15 -05:00 committed by GitHub
commit 3835da3839
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 732 additions and 254 deletions

View File

@ -97,7 +97,8 @@ module Vagrant
end
end
folder_data = JSON.dump(folders)
# Remove implementation instances
folder_data = JSON.dump(folders.to_h)
machine.data_dir.join("synced_folders").open("w") do |f|
f.write(folder_data)
@ -120,7 +121,7 @@ module Vagrant
end
config_folders = config.synced_folders
folders = {}
folders = Vagrant::Plugin::V2::SyncedFolder::Collection.new
# Determine all the synced folders as well as the implementation
# they're going to use.
@ -177,8 +178,10 @@ module Vagrant
# Apply the scoped hash overrides to get the options
folders.dup.each do |impl_name, fs|
impl = plugins[impl_name].first.new._initialize(machine, impl_name)
new_fs = {}
fs.each do |id, data|
data[:plugin] = impl
id = data[:id] if data[:id]
new_fs[id] = scoped_hash_override(data, impl_name)
end
@ -186,7 +189,7 @@ module Vagrant
folders[impl_name] = new_fs
end
return folders
folders
end
# This finds the difference between two lists of synced folder
@ -235,26 +238,26 @@ module Vagrant
protected
def cached_synced_folders(machine)
JSON.parse(machine.data_dir.join("synced_folders").read).tap do |r|
# We have to do all sorts of things to make the proper things
# symbols and
r.keys.each do |k|
r[k].each do |ik, v|
v.keys.each do |vk|
v[vk.to_sym] = v[vk]
v.delete(vk)
end
end
r[k.to_sym] = r[k]
r.delete(k)
end
import = JSON.parse(machine.data_dir.join("synced_folders").read)
import.each do |type, folders|
impl = plugins[type.to_sym].first.new._initialize(machine, type.to_sym)
folders.each { |_, v| v[:plugin] = impl }
end
# Symbolize the keys we want as symbols
import.keys.dup.each do |k|
import[k].values.each do |item|
item.keys.dup.each do |ik|
item[ik.to_sym] = item.delete(ik)
end
end
import[k.to_sym] = import.delete(k)
end
Vagrant::Plugin::V2::SyncedFolder::Collection[import]
rescue Errno::ENOENT
# If the file doesn't exist, we probably just have a machine created
# by a version of Vagrant that didn't cache shared folders. Report no
# shared folders to be safe.
return {}
Vagrant::Plugin::V2::SyncedFolder::Collection.new
end
end
end

View File

@ -128,6 +128,16 @@ module Vagrant
# Save the synced folders
save_synced_folders(env[:machine], original_folders, **save_opts)
end
# Persist the mounts by adding them to fstab
if env[:machine].guest.capability?(:persist_mount_shared_folder)
if env[:machine].config.vm.allow_fstab_modification
fstab_folders = original_folders
else
fstab_folders = nil
end
env[:machine].guest.capability(:persist_mount_shared_folder, fstab_folders)
end
end
end
end

View File

@ -1,4 +1,5 @@
require_relative "util/ssh"
require_relative "action/builtin/mixin_synced_folders"
require "digest/md5"
require "thread"
@ -10,6 +11,8 @@ module Vagrant
# API for querying the state and making state changes to the machine, which
# is backed by any sort of provider (VirtualBox, VMware, etc.).
class Machine
extend Vagrant::Action::Builtin::MixinSyncedFolders
# The box that is backing this machine.
#
# @return [Box]
@ -616,6 +619,15 @@ module Vagrant
end
end
# This returns the set of shared folders that should be done for
# this machine. It returns the folders in a hash keyed by the
# implementation class for the synced folders.
#
# @return [Hash<Symbol, Hash<String, Hash>>]
def synced_folders
self.class.synced_folders(self)
end
protected
# Returns the path to the file that stores the UID.

View File

@ -64,6 +64,11 @@ module Vagrant
# @return [Registry<Symbol, Array<Class, Integer>>]
attr_reader :synced_folders
# This contains all the registered synced folder capabilities.
#
# @return [Hash<Symbol, Registry>]
attr_reader :synced_folder_capabilities
def initialize
# The action hooks hash defaults to []
@action_hooks = Hash.new { |h, k| h[k] = [] }
@ -78,6 +83,7 @@ module Vagrant
@provider_capabilities = Hash.new { |h, k| h[k] = Registry.new }
@pushes = Registry.new
@synced_folders = Registry.new
@synced_folder_capabilities = Hash.new { |h, k| h[k] = Registry.new }
end
end
end

View File

@ -257,7 +257,21 @@ module Vagrant
end
end
end
# This returns all the registered synced folder capabilities.
#
# @return [Hash]
def synced_folder_capabilities
results = Hash.new { |h, k| h[k] = Registry.new }
@registered.each do |plugin|
plugin.components.synced_folder_capabilities.each do |synced_folder, caps|
results[synced_folder].merge!(caps)
end
end
results
end
# This registers a plugin. This should _NEVER_ be called by the public
# and should only be called from within Vagrant. Vagrant will
# automatically register V2 plugins when a name is set on the

View File

@ -247,6 +247,18 @@ module Vagrant
nil
end
# Defines a capability for the given synced folder. The block should return
# a class/module that has a method with the capability name, ready
# to be executed. This means that if it is an instance method,
# the block should return an instance of the class.
#
# @param [String] synced_folder The name of the synced folder
# @param [String] cap The name of the capability
def self.synced_folder_capability(synced_folder, cap, &block)
components.synced_folder_capabilities[synced_folder.to_sym].register(cap.to_sym, &block)
nil
end
# Returns the internal data associated with this plugin. This
# should NOT be called by the general public.
#

View File

@ -3,6 +3,49 @@ module Vagrant
module V2
# This is the base class for a synced folder implementation.
class SyncedFolder
class Collection < Hash
# @return [Array<Symbol>] names of synced folder types
def types
keys
end
# Fetch the synced plugin folder of the given type
#
# @param [Symbol] t Synced folder type
# @return [Vagrant::Plugin::V2::SyncedFolder]
def type(t)
f = detect { |k, _| k.to_sym == t.to_sym }.last
raise KeyError, "Unknown synced folder type" if !f
f.values.first[:plugin]
end
# Converts to a regular Hash and removes
# plugin instances so the result is ready
# for serialization
#
# @return [Hash]
def to_h
c = lambda do |h|
h.keys.each do |k|
if h[k].is_a?(Hash)
h[k] = c.call(h[k].to_h.clone)
end
end
h
end
h = c.call(super)
h.values.each do |f|
f.values.each do |g|
g.delete(:plugin)
end
end
h
end
end
include CapabilityHost
# This is called early when the synced folder is set to determine
# if this implementation can be used for this machine. This should
# return true or false.
@ -54,6 +97,13 @@ module Vagrant
# @param [Hash] opts
def cleanup(machine, opts)
end
def _initialize(machine, synced_folder_type)
plugins = Vagrant.plugin("2").manager.synced_folders
capabilities = Vagrant.plugin("2").manager.synced_folder_capabilities
initialize_capabilities!(synced_folder_type, plugins, capabilities, machine)
self
end
end
end
end

View File

@ -6,17 +6,16 @@ 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)
mount_type = options[:plugin].capability(:mount_type)
@@logger.debug("Mounting #{name} (#{options[:hostpath]} to #{guestpath})")
builtin_mount_type = "-cit #{VB_MOUNT_TYPE}"
addon_mount_type = "-t #{VB_MOUNT_TYPE}"
builtin_mount_type = "-cit #{mount_type}"
addon_mount_type = "-t #{mount_type}"
mount_options, mount_uid, mount_gid = mount_options(machine, name, guest_path, options)
mount_options, mount_uid, mount_gid = options[:plugin].capability(:mount_options, 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

View File

@ -10,27 +10,41 @@ module VagrantPlugins
# 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
#
# 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?
def self.persist_mount_shared_folder(machine, folders)
if folders.nil?
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,
ssh_info = machine.ssh_info
export_folders = folders.map { |type, folder|
folder.map { |name, data|
guest_path = Shellwords.escape(data[:guestpath])
data[:owner] ||= ssh_info[:username]
data[:group] ||= ssh_info[:username]
if data[:plugin].capability?(:mount_type)
mount_type = data[:plugin].capability(:mount_type)
mount_options, _, _ = data[:plugin].capability(
:mount_options, name, guest_path, data)
else
next
end
mount_options = "#{mount_options},nofail"
{
name: name,
mount_point: guest_path,
mount_type: mount_type,
mount_options: mount_options,
}
}
end
}.flatten.compact
fstab_entry = Vagrant::Util::TemplateRenderer.render('guests/linux/etc_fstab', folders: export_folders)
self.remove_vagrant_managed_fstab(machine)
@ -45,4 +59,4 @@ module VagrantPlugins
end
end
end
end
end

View File

@ -47,7 +47,6 @@ module VagrantPlugins
args += rdp_info[:extra_args] if rdp_info[:extra_args]
end
# require "pry-byebug"; binding.pry
# Finally, run the client.
Vagrant::Util::Subprocess.execute(rdp_client, *args, {:detach => true})
end

View File

@ -508,7 +508,6 @@ 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
@ -726,6 +725,17 @@ module VagrantPlugins
def validate(machine, ignore_provider=nil)
errors = _detected_errors
if @allow_fstab_modification == UNSET_VALUE
machine.synced_folders.types.each do |impl_name|
inst = machine.synced_folders.type(impl_name)
if inst.capability?(:default_fstab_modification) && inst.capability(:default_fstab_modification) == false
@allow_fstab_modification = false
break
end
end
@allow_fstab_modification = true if @allow_fstab_modification == UNSET_VALUE
end
if !box && !clone && !machine.provider_options[:box_optional]
errors << I18n.t("vagrant.config.vm.box_missing")
end

View File

@ -0,0 +1,35 @@
require_relative "../../../synced_folders/unix_mount_helpers"
module VagrantPlugins
module ProviderVirtualBox
module Cap
module MountOptions
extend VagrantPlugins::SyncedFolder::UnixMountHelpers
VB_MOUNT_TYPE = "vboxsf".freeze
# Returns mount options for a virual box synced folder
#
# @param [Machine] machine
# @param [String] name of mount
# @param [String] path of mount on guest
# @param [Hash] hash of mount options
def self.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
def self.mount_type(machine)
return VB_MOUNT_TYPE
end
end
end
end
end

View File

@ -68,6 +68,16 @@ module VagrantPlugins
require_relative "cap"
Cap
end
synced_folder_capability(:virtualbox, "mount_options") do
require_relative "cap/mount_options"
Cap::MountOptions
end
synced_folder_capability(:virtualbox, "mount_type") do
require_relative "cap/mount_options"
Cap::MountOptions
end
end
autoload :Action, File.expand_path("../action", __FILE__)

View File

@ -53,20 +53,12 @@ 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

@ -30,7 +30,9 @@ module VagrantPlugins
if !machine.config.nfs.functional
return false
end
return true if machine.env.host.capability(:nfs_installed)
if machine.env.host.capability?(:nfs_installed)
return true if machine.env.host.capability(:nfs_installed)
end
return false if !raise_error
raise Vagrant::Errors::NFSNotSupported
end

View File

@ -0,0 +1,11 @@
module VagrantPlugins
module SyncedFolderSMB
module Cap
module DefaultFstabModification
def self.default_fstab_modification(machine)
return false
end
end
end
end
end

View File

@ -0,0 +1,36 @@
require_relative "../../unix_mount_helpers"
module VagrantPlugins
module SyncedFolderSMB
module Cap
module MountOptions
extend VagrantPlugins::SyncedFolder::UnixMountHelpers
def self.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]
mnt_opts = []
if machine.env.host.capability?(:smb_mount_options)
mnt_opts += machine.env.host.capability(:smb_mount_options)
else
mnt_opts << "sec=ntlmssp"
end
mnt_opts << "credentials=/etc/smb_creds_#{name}"
mnt_opts << "uid=#{mount_uid}"
mnt_opts << "gid=#{mount_gid}"
if !ENV['VAGRANT_DISABLE_SMBMFSYMLINKS']
mnt_opts << "mfsymlinks"
end
mnt_opts = merge_mount_options(mnt_opts, options[:mount_options] || [])
mount_options = mnt_opts.join(",")
return mount_options, mount_uid, mount_gid
end
end
end
end
end

View File

@ -23,6 +23,16 @@ module VagrantPlugins
SyncedFolder
end
synced_folder_capability("smb", "default_fstab_modification") do
require_relative "cap/default_fstab_modification"
Cap::DefaultFstabModification
end
synced_folder_capability("smb", "mount_options") do
require_relative "cap/mount_options"
Cap::MountOptions
end
protected
def self.init!

View File

@ -100,16 +100,19 @@ 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
def merge_mount_options(base, overrides)
base = base.join(",").split(",")
overrides = overrides.join(",").split(",")
b_kv = Hash[base.map{|item| item.split("=", 2) }]
o_kv = Hash[overrides.map{|item| item.split("=", 2) }]
merged = {}.tap do |opts|
(b_kv.keys + o_kv.keys).uniq.each do |key|
opts[key] = o_kv.fetch(key, b_kv[key])
end
end
merged.map do |key, value|
[key, value].compact.join("=")
end
end
end
end

View File

@ -29,6 +29,7 @@ describe VagrantPlugins::CommandPort::Command do
before do
allow(machine).to receive(:state).and_return(state)
allow(machine).to receive_message_chain(:synced_folders).and_return( double(:types => []) )
allow(subject).to receive(:with_target_vms) { |&block| block.call(machine) }
end

View File

@ -16,13 +16,17 @@ describe "VagrantPlugins::GuestLinux::Cap::MountVirtualBoxSharedFolder" do
let(:mount_name){ "vagrant" }
let(:mount_guest_path){ "/vagrant" }
let(:folder_options) do
{
owner: mount_owner,
group: mount_group,
hostpath: "/host/directory/path"
}
Vagrant::Plugin::V2::SyncedFolder::Collection[
{
owner: mount_owner,
group: mount_group,
hostpath: "/host/directory/path",
plugin: folder_plugin
}
]
end
let(:cap){ caps.get(:mount_virtualbox_shared_folder) }
let(:folder_plugin) { double("folder_plugin") }
before do
allow(machine).to receive(:communicate).and_return(comm)
@ -40,159 +44,36 @@ describe "VagrantPlugins::GuestLinux::Cap::MountVirtualBoxSharedFolder" do
end
it "generates the expected default mount command" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
expect(folder_plugin).to receive(:capability).with(:mount_options, mount_name, mount_guest_path, folder_options).
and_return(["uid=#{mount_uid},gid=#{mount_gid}", mount_uid, mount_gid])
expect(folder_plugin).to receive(:capability).with(:mount_type).and_return("vboxsf")
expect(comm).to receive(:sudo).with("mount -t vboxsf -o uid=#{mount_uid},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything)
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options)
end
it "automatically chown's the mounted directory on guest" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
expect(folder_plugin).to receive(:capability).with(:mount_options, mount_name, mount_guest_path, folder_options).
and_return(["uid=#{mount_uid},gid=#{mount_gid}", mount_uid, mount_gid])
expect(folder_plugin).to receive(:capability).with(:mount_type).and_return("vboxsf")
expect(comm).to receive(:sudo).with("mount -t vboxsf -o uid=#{mount_uid},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything)
expect(comm).to receive(:sudo).with("chown #{mount_uid}:#{mount_gid} #{mount_guest_path}")
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options)
end
context "with owner user ID explicitly defined" do
before do
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
end
context "with user ID provided as Integer" do
let(:mount_owner){ 2000 }
it "generates the expected mount command using mount_owner directly" do
expect(comm).to receive(:sudo).with("mount -t vboxsf -o uid=#{mount_owner},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything)
expect(comm).to receive(:sudo).with("chown #{mount_owner}:#{mount_gid} #{mount_guest_path}")
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options)
end
end
context "with user ID provided as String" do
let(:mount_owner){ "2000" }
it "generates the expected mount command using mount_owner directly" do
expect(comm).to receive(:sudo).with("mount -t vboxsf -o uid=#{mount_owner},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything)
expect(comm).to receive(:sudo).with("chown #{mount_owner}:#{mount_gid} #{mount_guest_path}")
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options)
end
end
end
context "with owner group ID explicitly defined" do
before do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
end
context "with owner group ID provided as Integer" do
let(:mount_group){ 2000 }
it "generates the expected mount command using mount_group directly" do
expect(comm).to receive(:sudo).with("mount -t vboxsf -o uid=#{mount_uid},gid=#{mount_group} #{mount_name} #{mount_guest_path}", anything)
expect(comm).to receive(:sudo).with("chown #{mount_uid}:#{mount_group} #{mount_guest_path}")
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options)
end
end
context "with owner group ID provided as String" do
let(:mount_group){ "2000" }
it "generates the expected mount command using mount_group directly" do
expect(comm).to receive(:sudo).with("mount -t vboxsf -o uid=#{mount_uid},gid=#{mount_group} #{mount_name} #{mount_guest_path}", anything)
expect(comm).to receive(:sudo).with("chown #{mount_uid}:#{mount_group} #{mount_guest_path}")
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options)
end
end
end
context "with non-existent default owner group" do
it "fetches the effective group ID of the user" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_raise(Vagrant::Errors::VirtualBoxMountFailed, {command: '', output: ''})
expect(comm).to receive(:execute).with("id -g #{mount_owner}", anything).and_yield(:stdout, "1").and_return(0)
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options)
end
end
context "with non-existent owner group" do
it "raises an error" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_raise(Vagrant::Errors::VirtualBoxMountFailed, {command: '', output: ''})
expect do
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options)
end.to raise_error Vagrant::Errors::VirtualBoxMountFailed
end
end
context "with read-only option defined" do
it "does not chown mounted guest directory" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
expect(comm).to receive(:sudo).with("mount -t vboxsf -o ro,uid=#{mount_uid},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything)
expect(comm).not_to receive(:sudo).with("chown #{mount_uid}:#{mount_gid} #{mount_guest_path}")
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["ro"]))
end
end
context "with upstart init" do
it "emits mount event" do
expect(comm).to receive(:sudo).with(/initctl emit/)
expect(folder_plugin).to receive(:capability).with(:mount_type).and_return("vboxsf")
expect(folder_plugin).to receive(:capability).with(:mount_options, mount_name, mount_guest_path, folder_options).
and_return(["uid=#{mount_uid},gid=#{mount_gid}", mount_uid, mount_gid])
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options)
end
end
context "with custom mount options" do
let(:ui){ double(:ui) }
before do
allow(ui).to receive(:warn)
allow(machine).to receive(:ui).and_return(ui)
end
context "with uid defined" do
let(:options_uid){ '1234' }
it "should only include uid defined within mount options" do
expect(comm).not_to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
expect(comm).to receive(:sudo).with("mount -t vboxsf -o uid=#{options_uid},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything)
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["uid=#{options_uid}"]))
end
end
context "with gid defined" do
let(:options_gid){ '1234' }
it "should only include gid defined within mount options" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).not_to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
expect(comm).to receive(:sudo).with("mount -t vboxsf -o uid=#{mount_uid},gid=#{options_gid} #{mount_name} #{mount_guest_path}", anything)
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["gid=#{options_gid}"]))
end
end
context "with uid and gid defined" do
let(:options_gid){ '1234' }
let(:options_uid){ '1234' }
it "should only include uid and gid defined within mount options" do
expect(comm).not_to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).not_to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{options_gid}:")
expect(comm).to receive(:sudo).with("mount -t vboxsf -o uid=#{options_uid},gid=#{options_gid} #{mount_name} #{mount_guest_path}", anything)
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["gid=#{options_gid}", "uid=#{options_uid}"]))
end
end
end
context "with guest builtin vboxsf module" do
let(:vbox_stderr){ <<-EOF
mount.vboxsf cannot be used with mainline vboxsf; instead use:
@ -201,8 +82,12 @@ mount.vboxsf cannot be used with mainline vboxsf; instead use:
EOF
}
it "should perform guest mount using builtin module" do
expect(folder_plugin).to receive(:capability).with(:mount_options, mount_name, mount_guest_path, folder_options).
and_return(["uid=#{mount_uid},gid=#{mount_gid}", mount_uid, mount_gid])
expect(folder_plugin).to receive(:capability).with(:mount_type).and_return("vboxsf")
expect(comm).to receive(:sudo).with(/mount -t vboxsf/, any_args).and_yield(:stderr, vbox_stderr).and_return(1)
expect(comm).to receive(:sudo).with(/mount -cit/, any_args)
cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options)
end
end

View File

@ -9,10 +9,34 @@ describe "VagrantPlugins::GuestLinux::Cap::PersistMountSharedFolder" do
let(:machine) { double("machine") }
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
let(:options_gid){ '1234' }
let(:options_uid){ '1234' }
let(:cap){ caps.get(:persist_mount_shared_folder) }
let(:folder_plugin){ double("folder_plugin") }
let(:ssh_info) {{
:username => "vagrant"
}}
let (:fstab_folders) {
Vagrant::Plugin::V2::SyncedFolder::Collection[
{
"test1" => {guestpath: "/test1", hostpath: "/my/host/path", disabled: false, plugin: folder_plugin,
__vagrantfile: true, owner: "vagrant", group: "vagrant", mount_options: ["uid=#{options_uid}", "gid=#{options_gid}"]},
"vagrant" => {guestpath: "/vagrant", hostpath: "/my/host/vagrant", disabled: false, __vagrantfile: true,
owner: "vagrant", group: "vagrant", mount_options: ["uid=#{options_uid}", "gid=#{options_gid}}"], plugin: folder_plugin}
}
]
}
let (:folders) { {
:virtualbox => fstab_folders
} }
before do
allow(machine).to receive(:communicate).and_return(comm)
allow(machine).to receive(:ssh_info).and_return(ssh_info)
allow(folder_plugin).to receive(:capability?).with(:mount_type).and_return(true)
allow(folder_plugin).to receive(:capability).with(:mount_options, any_args).
and_return(["uid=#{options_uid},gid=#{options_gid}", options_uid, options_gid])
allow(folder_plugin).to receive(:capability).with(:mount_type).and_return("vboxsf")
end
after do
@ -20,13 +44,6 @@ describe "VagrantPlugins::GuestLinux::Cap::PersistMountSharedFolder" do
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) }
@ -37,16 +54,29 @@ describe "VagrantPlugins::GuestLinux::Cap::PersistMountSharedFolder" do
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"
expected_entry_vagrant = "vagrant /vagrant vboxsf uid=#{options_uid},gid=#{options_gid},nofail 0 0"
expected_entry_test = "test1 /test1 vboxsf uid=#{options_uid},gid=#{options_gid},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")
cap.persist_mount_shared_folder(machine, folders)
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")
cap.persist_mount_shared_folder(machine, nil)
end
context "folders do not support mount_type capability" do
before do
allow(folder_plugin).to receive(:capability?).with(:mount_type).and_return(false)
end
it "does not inserts folders into /etc/fstab" do
expect(cap).to receive(:remove_vagrant_managed_fstab)
expect(comm).not_to receive(:sudo).with(/echo '' >> \/etc\/fstab/)
cap.persist_mount_shared_folder(machine, folders)
end
end
end
end

View File

@ -37,7 +37,7 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
allow(machine).to receive(:env).and_return(env)
allow(machine).to receive(:provider_config).and_return(nil)
allow(machine).to receive(:provider_options).and_return({})
allow(machine).to receive_message_chain(:synced_folders, :types).and_return( {} )
allow(provider).to receive(:capability?).with(:validate_disk_ext).and_return(true)
allow(provider).to receive(:capability).with(:validate_disk_ext, "vdi").and_return(true)
allow(provider).to receive(:capability?).with(:set_default_disk_ext).and_return(true)
@ -51,7 +51,7 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
assert_valid
end
it "validates disables_host_modification option" do
it "validates disables_host_modification option" do
subject.allow_hosts_modification = true
subject.finalize!
assert_valid
@ -65,6 +65,13 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
assert_invalid
end
it "does not check for fstab caps if already set" do
expect(machine).to_not receive(:synced_folder_types)
subject.allow_fstab_modification = true
subject.finalize!
assert_valid
end
describe "#base_mac" do
it "defaults properly" do
subject.finalize!
@ -729,7 +736,7 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
context "WSL host paths" do
let(:valid_path){ "/mnt/c/path" }
let(:invalid_path){ "/home/vagrant/path" }
let(:synced_folder_impl){ double("synced_folder_impl", new: double("synced_folder_inst", usable?: true)) }
let(:synced_folder_impl){ double("synced_folder_impl", new: double("synced_folder_inst", usable?: true, _initialize: true)) }
let(:fs_config){ double("fs_config", vm: double("fs_vm", allowed_synced_folder_types: nil)) }
let(:plugin){ double("plugin", manager: manager) }
let(:manager){ double("manager", synced_folders: {sf_impl: [synced_folder_impl, 1]}) }

View File

@ -29,6 +29,7 @@ describe VagrantPlugins::ProviderVirtualBox::Action::PrepareNFSSettings do
before do
env[:test] = true
allow(machine.env).to receive(:host) { host }
allow(host).to receive(:capability?).with(:nfs_installed) { true }
allow(host).to receive(:capability).with(:nfs_installed) { true }
# We don't care about smb support so return not installed
allow(host).to receive(:capability?).with(:smb_installed).and_return(false)

View File

@ -0,0 +1,179 @@
require_relative "../../../../base"
describe "VagrantPlugins::ProviderVirtualBox::Cap::MountOptions" do
let(:caps) do
VagrantPlugins::ProviderVirtualBox::Plugin
.components
.synced_folder_capabilities[:virtualbox]
end
let(:machine) { double("machine") }
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
let(:mount_owner){ "vagrant" }
let(:mount_group){ "vagrant" }
let(:mount_uid){ "1000" }
let(:mount_gid){ "1000" }
let(:mount_name){ "vagrant" }
let(:mount_guest_path){ "/vagrant" }
let(:folder_options) do
{
owner: mount_owner,
group: mount_group,
hostpath: "/host/directory/path"
}
end
let(:cap){ caps.get(:mount_options) }
before do
allow(machine).to receive(:communicate).and_return(comm)
end
after do
comm.verify_expectations!
end
describe ".mount_options" do
before do
allow(comm).to receive(:sudo).with(any_args)
allow(comm).to receive(:execute).with(any_args)
end
context "with owner user ID explicitly defined" do
before do
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
end
context "with user ID provided as Integer" do
let(:mount_owner){ 2000 }
it "generates the expected mount command using mount_owner directly" do
out_mount_options, out_mount_uid, out_mount_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options)
expect(out_mount_options).to eq("uid=#{mount_owner},gid=#{mount_gid}")
expect(out_mount_uid).to eq(mount_owner)
expect(out_mount_gid).to eq(mount_gid)
end
end
context "with user ID provided as String" do
let(:mount_owner){ "2000" }
it "generates the expected mount command using mount_owner directly" do
out_mount_options, out_mount_uid, out_mount_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options)
expect(out_mount_options).to eq("uid=#{mount_owner},gid=#{mount_gid}")
expect(out_mount_uid).to eq(mount_owner)
expect(out_mount_gid).to eq(mount_gid)
end
end
end
context "with owner group ID explicitly defined" do
before do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
end
context "with owner group ID provided as Integer" do
let(:mount_group){ 2000 }
it "generates the expected mount command using mount_group directly" do
out_mount_options, out_mount_uid, out_mount_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options)
expect(out_mount_options).to eq("uid=#{mount_uid},gid=#{mount_group}")
expect(out_mount_uid).to eq(mount_uid)
expect(out_mount_gid).to eq(mount_group)
end
end
context "with owner group ID provided as String" do
let(:mount_group){ "2000" }
it "generates the expected mount command using mount_group directly" do
out_mount_options, out_mount_uid, out_mount_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options)
expect(out_mount_options).to eq("uid=#{mount_uid},gid=#{mount_group}")
expect(out_mount_uid).to eq(mount_uid)
expect(out_mount_gid).to eq(mount_group)
end
end
end
context "with non-existent default owner group" do
it "fetches the effective group ID of the user" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_raise(Vagrant::Errors::VirtualBoxMountFailed, {command: '', output: ''})
expect(comm).to receive(:execute).with("id -g #{mount_owner}", anything).and_yield(:stdout, "1").and_return(0)
out_mount_options, out_mount_uid, out_mount_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options)
expect(out_mount_options).to eq("uid=#{mount_uid},gid=1")
end
end
context "with non-existent owner group" do
it "raises an error" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_raise(Vagrant::Errors::VirtualBoxMountFailed, {command: '', output: ''})
expect do
cap.mount_options(machine, mount_name, mount_guest_path, folder_options)
end.to raise_error Vagrant::Errors::VirtualBoxMountFailed
end
end
context "with read-only option defined" do
it "does not chown mounted guest directory" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
out_mount_options, out_mount_uid, out_mount_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["ro"]))
expect(out_mount_options).to eq("ro,uid=#{mount_uid},gid=#{mount_gid}")
expect(out_mount_uid).to eq(mount_uid)
expect(out_mount_gid).to eq(mount_gid)
end
end
context "with custom mount options" do
let(:ui){ double(:ui) }
before do
allow(ui).to receive(:warn)
allow(machine).to receive(:ui).and_return(ui)
end
context "with uid defined" do
let(:options_uid){ '1234' }
it "should only include uid defined within mount options" do
expect(comm).not_to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
out_mount_options, out_mount_uid, out_mount_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["uid=#{options_uid}"]) )
expect(out_mount_options).to eq("uid=#{options_uid},gid=#{mount_gid}")
expect(out_mount_uid).to eq(options_uid)
expect(out_mount_gid).to eq(mount_gid)
end
end
context "with gid defined" do
let(:options_gid){ '1234' }
it "should only include gid defined within mount options" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).not_to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
out_mount_options, out_mount_uid, out_mount_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["gid=#{options_gid}"]) )
expect(out_mount_options).to eq("uid=#{mount_uid},gid=#{options_gid}")
expect(out_mount_uid).to eq(mount_uid)
expect(out_mount_gid).to eq(options_gid)
end
end
context "with uid and gid defined" do
let(:options_gid){ '1234' }
let(:options_uid){ '1234' }
it "should only include uid and gid defined within mount options" do
expect(comm).not_to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).not_to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{options_gid}:")
out_mount_options, out_mount_uid, out_mount_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["uid=#{options_uid}", "gid=#{options_gid}"]) )
expect(out_mount_options).to eq("uid=#{options_uid},gid=#{options_gid}")
expect(out_mount_uid).to eq(options_uid)
expect(out_mount_gid).to eq(options_gid)
end
end
end
end
end

View File

@ -78,30 +78,6 @@ describe VagrantPlugins::ProviderVirtualBox::SyncedFolder do
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

View File

@ -0,0 +1,67 @@
require_relative "../../../../base"
require_relative "../../../../../../plugins/synced_folders/smb/cap/mount_options"
describe VagrantPlugins::SyncedFolderSMB::Cap::MountOptions do
let(:caps) do
VagrantPlugins::SyncedFolderSMB::Plugin
.components
.synced_folder_capabilities[:smb]
end
let(:cap){ caps.get(:mount_options) }
let(:machine) { double("machine") }
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
let(:mount_owner){ "vagrant" }
let(:mount_group){ "vagrant" }
let(:mount_uid){ "1000" }
let(:mount_gid){ "1000" }
let(:mount_name){ "vagrant" }
let(:mount_guest_path){ "/vagrant" }
let(:folder_options) do
{
owner: mount_owner,
group: mount_group,
hostpath: "/host/directory/path"
}
end
before do
allow(machine).to receive(:communicate).and_return(comm)
allow(machine).to receive_message_chain(:env, :host, :capability?).with(:smb_mount_options).and_return(false)
ENV['VAGRANT_DISABLE_SMBMFSYMLINKS'] = "1"
end
describe ".mount_options" do
it "generates the expected default mount command" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
out_opts, out_uid, out_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options)
expect(out_opts).to eq("sec=ntlmssp,credentials=/etc/smb_creds_vagrant,uid=1000,gid=1000")
expect(out_uid).to eq(mount_uid)
expect(out_gid).to eq(mount_gid)
end
it "includes provided mount options" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:")
folder_options[:mount_options] =["ro"]
out_opts, out_uid, out_gid = cap.mount_options(machine, mount_name, mount_guest_path, folder_options)
expect(out_opts).to eq("sec=ntlmssp,credentials=/etc/smb_creds_vagrant,uid=1000,gid=1000,ro")
expect(out_uid).to eq(mount_uid)
expect(out_gid).to eq(mount_gid)
end
context "with non-existent owner group" do
it "raises an error" do
expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid)
expect(comm).to receive(:execute).with("id -g #{mount_group}", anything).and_yield(:stdout, mount_gid)
expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_raise(Vagrant::Errors::VirtualBoxMountFailed, {command: '', output: ''})
expect do
cap.mount_options(machine, mount_name, mount_guest_path, folder_options)
end.to raise_error Vagrant::Errors::VirtualBoxMountFailed
end
end
end
end

View File

@ -10,6 +10,10 @@ shared_context "synced folder actions" do
raise "#{name}: usable" if raise_error && !usable
usable
end
define_method(:_initialize) do |machine, type|
true
end
end
end
end

View File

@ -14,6 +14,7 @@ describe Vagrant::Action::Builtin::MixinSyncedFolders do
end
let(:data_dir) { Pathname.new(Dir.mktmpdir("vagrant-test-mixin-synced-folders")) }
let(:folders_class) { Vagrant::Plugin::V2::SyncedFolder::Collection }
let(:machine) do
double("machine").tap do |machine|
@ -91,7 +92,7 @@ describe Vagrant::Action::Builtin::MixinSyncedFolders do
end
describe "synced_folders" do
let(:folders) { {} }
let(:folders) { folders_class.new }
let(:plugins) { {} }
before do
@ -118,13 +119,14 @@ describe Vagrant::Action::Builtin::MixinSyncedFolders do
result = subject.synced_folders(machine)
expect(result.length).to eq(2)
expect(result[:default]).to eq({
"another" => folders["another"].merge(__vagrantfile: true),
"foo" => folders["foo"].merge(__vagrantfile: true),
"root" => folders["root"].merge(__vagrantfile: true),
"another" => folders["another"].merge(__vagrantfile: true, plugin: true),
"foo" => folders["foo"].merge(__vagrantfile: true, plugin: true),
"root" => folders["root"].merge(__vagrantfile: true, plugin: true),
})
expect(result[:nfs]).to eq({
"nfs" => folders["nfs"].merge(__vagrantfile: true),
"nfs" => folders["nfs"].merge(__vagrantfile: true, plugin: true),
})
expect(result.types).to eq([:default, :nfs])
end
it "should return the proper set of folders of a custom config" do
@ -138,8 +140,9 @@ describe Vagrant::Action::Builtin::MixinSyncedFolders do
result = subject.synced_folders(machine, config: other)
expect(result.length).to eq(1)
expect(result[:default]).to eq({
"bar" => other_folders["bar"],
"bar" => other_folders["bar"].merge(plugin: true),
})
expect(result.types).to eq([:default])
end
it "should error if an explicit type is unusable" do
@ -157,6 +160,7 @@ describe Vagrant::Action::Builtin::MixinSyncedFolders do
result = subject.synced_folders(machine)
expect(result.length).to eq(1)
expect(result[:default].length).to eq(1)
expect(result.types).to eq([:default])
end
it "should scope hash override the settings" do
@ -168,11 +172,13 @@ describe Vagrant::Action::Builtin::MixinSyncedFolders do
result = subject.synced_folders(machine)
expect(result[:nfs]["root"][:foo]).to eql("bar")
expect(result.types).to eq([:nfs])
end
it "returns {} if cached read with no cache" do
result = subject.synced_folders(machine, cached: true)
expect(result).to eql({})
expect(result.types).to eq([])
end
it "should be able to save and retrieve cached versions" do
@ -191,11 +197,12 @@ describe Vagrant::Action::Builtin::MixinSyncedFolders do
result = subject.synced_folders(machine, cached: true)
expect(result.length).to eq(2)
expect(result[:default]).to eq({
"another" => old_folders["another"].merge(__vagrantfile: true),
"foo" => old_folders["foo"].merge(__vagrantfile: true),
"root" => old_folders["root"].merge(__vagrantfile: true),
"another" => old_folders["another"].merge(__vagrantfile: true, plugin: true),
"foo" => old_folders["foo"].merge(__vagrantfile: true, plugin: true),
"root" => old_folders["root"].merge(__vagrantfile: true, plugin: true),
})
expect(result[:nfs]).to eq({ "nfs" => old_folders["nfs"].merge(__vagrantfile: true) })
expect(result[:nfs]).to eq({ "nfs" => old_folders["nfs"].merge(__vagrantfile: true, plugin: true) })
expect(result.types).to eq([:default, :nfs])
end
it "should be able to save and retrieve cached versions" do
@ -221,12 +228,13 @@ describe Vagrant::Action::Builtin::MixinSyncedFolders do
result = subject.synced_folders(machine, cached: true)
expect(result.length).to eq(2)
expect(result[:default]).to eq({
"foo" => { type: "default" },
"bar" => { type: "default", __vagrantfile: true},
"foo" => { type: "default", plugin: true },
"bar" => { type: "default", __vagrantfile: true, plugin: true },
})
expect(result[:nfs]).to eq({
"baz" => { type: "nfs", __vagrantfile: true }
"baz" => { type: "nfs", __vagrantfile: true, plugin: true }
})
expect(result.types).to eq([:default, :nfs])
end
it "should remove items from the vagrantfile that were removed" do
@ -248,16 +256,17 @@ describe Vagrant::Action::Builtin::MixinSyncedFolders do
result = subject.synced_folders(machine, cached: true)
expect(result.length).to eq(2)
expect(result[:default]).to eq({
"bar" => { type: "default", __vagrantfile: true},
"bar" => { type: "default", __vagrantfile: true, plugin: true},
})
expect(result[:nfs]).to eq({
"baz" => { type: "nfs", __vagrantfile: true }
"baz" => { type: "nfs", __vagrantfile: true, plugin: true }
})
expect(result.types).to eq([:default, :nfs])
end
end
describe "#save_synced_folders" do
let(:folders) { {} }
let(:folders) { folders_class.new }
let(:options) { {} }
let(:output_file) { double("output_file") }

View File

@ -45,6 +45,7 @@ describe Vagrant::Action::Builtin::SyncedFolders do
allow(subject).to receive(:plugins).and_return(plugins)
allow(subject).to receive(:synced_folders).and_return(synced_folders)
allow(subject).to receive(:save_synced_folders)
allow(machine).to receive_message_chain(:guest, :capability?).with(:persist_mount_shared_folder).and_return(false)
end
after do
@ -228,5 +229,31 @@ describe Vagrant::Action::Builtin::SyncedFolders do
subject.call(env)
end
end
context "with guest capability to persist synced folders" do
it "persists folders" do
synced_folders["default"] = {
"root" => {
hostpath: "foo",
},
"other" => {
hostpath: "bar",
create: true,
}
}
allow(machine).to receive_message_chain(:guest, :capability?).with(:persist_mount_shared_folder).and_return(true)
expect(vm_config).to receive(:allow_fstab_modification).and_return(true)
expect(machine).to receive_message_chain(:guest, :capability).with(:persist_mount_shared_folder, synced_folders)
subject.call(env)
end
it "does not persists folders if configured to not do so" do
allow(machine).to receive_message_chain(:guest, :capability?).with(:persist_mount_shared_folder).and_return(true)
expect(vm_config).to receive(:allow_fstab_modification).and_return(false)
expect(machine).to receive_message_chain(:guest, :capability).with(:persist_mount_shared_folder, nil)
subject.call(env)
end
end
end
end

View File

@ -357,4 +357,22 @@ describe Vagrant::Plugin::V2::Manager do
expect(instance.synced_folders[:foo]).to eq(["bar", 10])
expect(instance.synced_folders[:bar]).to eq(["baz", 50])
end
it "should enumerate registered synced_folder_capabilities classes" do
pA = plugin do |p|
p.synced_folder_capability("foo", "foo") { "bar" }
end
pB = plugin do |p|
p.synced_folder_capability("bar", "bar") { "baz" }
end
instance.register(pA)
instance.register(pB)
expect(instance.synced_folder_capabilities.to_hash.length).to eq(2)
expect(instance.synced_folder_capabilities[:foo][:foo]).to eq("bar")
expect(instance.synced_folder_capabilities[:bar][:bar]).to eq("baz")
end
end

View File

@ -0,0 +1,46 @@
require File.expand_path("../../../../base", __FILE__)
describe Vagrant::Plugin::V2::SyncedFolder::Collection do
include_context "unit"
let(:folders) { described_class[
:nfs=>
{"/other"=>
{:type=>:nfs, :guestpath=>"/other", :hostpath=>"/other", :disabled=>false, :__vagrantfile=>true, plugin:"someclass"},
"/tests"=>
{:type=>:nfs, :guestpath=>"/tests", :hostpath=>"/tests", :disabled=>false, :__vagrantfile=>true, plugin:"someclass"}},
:virtualbox=>
{"/vagrant"=>
{:guestpath=>"/vagrant", :hostpath=>"/vagrant", :disabled=>false, :__vagrantfile=>true, plugin:"someotherclass"}}
]}
describe "#types" do
it "gets all the types of synced folders" do
expect(folders.types).to eq([:nfs, :virtualbox])
end
end
describe "#type" do
it "returns the plugin for a type" do
expect(folders.type(:nfs)).to eq("someclass")
expect(folders.type(:virtualbox)).to eq("someotherclass")
end
end
describe "to_h" do
it "removed plugin key" do
original_folders = folders
folders_h = folders.to_h
folders_h.values.each do |v|
v.values.each do |w|
expect(w).not_to include(:plugin)
end
end
original_folders.values.each do |v|
v.values.each do |w|
expect(w).to include(:plugin)
end
end
end
end
end