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:
commit
3835da3839
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
#
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
35
plugins/providers/virtualbox/cap/mount_options.rb
Normal file
35
plugins/providers/virtualbox/cap/mount_options.rb
Normal 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
|
||||
@ -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__)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
11
plugins/synced_folders/smb/cap/default_fstab_modification.rb
Normal file
11
plugins/synced_folders/smb/cap/default_fstab_modification.rb
Normal 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
|
||||
36
plugins/synced_folders/smb/cap/mount_options.rb
Normal file
36
plugins/synced_folders/smb/cap/mount_options.rb
Normal 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
|
||||
@ -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!
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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]}) }
|
||||
|
||||
@ -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)
|
||||
|
||||
179
test/unit/plugins/providers/virtualbox/cap/mount_options_test.rb
Normal file
179
test/unit/plugins/providers/virtualbox/cap/mount_options_test.rb
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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") }
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
46
test/unit/vagrant/plugin/v2/synced_folder_test.rb
Normal file
46
test/unit/vagrant/plugin/v2/synced_folder_test.rb
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user