Merge branch 'pluggable-synced-folder'
This enables synced folder implementations to be completely standalone plugins and simplifies the way synced folders are enabled for providers by providing a standard `SyncedFolder` middleware that does everything. This is all much better tested than previously. The built-in synced folder types are now: * "virtualbox" - VirtualBox shared folders * "nfs" - NFS shared folders These can now easily be extended
This commit is contained in:
commit
14faccfd79
|
@ -101,6 +101,7 @@ module Vagrant
|
||||||
c.register([:"2", :host]) { Plugin::V2::Host }
|
c.register([:"2", :host]) { Plugin::V2::Host }
|
||||||
c.register([:"2", :provider]) { Plugin::V2::Provider }
|
c.register([:"2", :provider]) { Plugin::V2::Provider }
|
||||||
c.register([:"2", :provisioner]) { Plugin::V2::Provisioner }
|
c.register([:"2", :provisioner]) { Plugin::V2::Provisioner }
|
||||||
|
c.register([:"2", :synced_folder]) { Plugin::V2::SyncedFolder }
|
||||||
end
|
end
|
||||||
|
|
||||||
# This returns a true/false showing whether we're running from the
|
# This returns a true/false showing whether we're running from the
|
||||||
|
|
|
@ -18,12 +18,12 @@ module Vagrant
|
||||||
autoload :HandleBoxUrl, "vagrant/action/builtin/handle_box_url"
|
autoload :HandleBoxUrl, "vagrant/action/builtin/handle_box_url"
|
||||||
autoload :HandleForwardedPortCollisions, "vagrant/action/builtin/handle_forwarded_port_collisions"
|
autoload :HandleForwardedPortCollisions, "vagrant/action/builtin/handle_forwarded_port_collisions"
|
||||||
autoload :Lock, "vagrant/action/builtin/lock"
|
autoload :Lock, "vagrant/action/builtin/lock"
|
||||||
autoload :NFS, "vagrant/action/builtin/nfs"
|
|
||||||
autoload :Provision, "vagrant/action/builtin/provision"
|
autoload :Provision, "vagrant/action/builtin/provision"
|
||||||
autoload :ProvisionerCleanup, "vagrant/action/builtin/provisioner_cleanup"
|
autoload :ProvisionerCleanup, "vagrant/action/builtin/provisioner_cleanup"
|
||||||
autoload :SetHostname, "vagrant/action/builtin/set_hostname"
|
autoload :SetHostname, "vagrant/action/builtin/set_hostname"
|
||||||
autoload :SSHExec, "vagrant/action/builtin/ssh_exec"
|
autoload :SSHExec, "vagrant/action/builtin/ssh_exec"
|
||||||
autoload :SSHRun, "vagrant/action/builtin/ssh_run"
|
autoload :SSHRun, "vagrant/action/builtin/ssh_run"
|
||||||
|
autoload :SyncedFolders, "vagrant/action/builtin/synced_folders"
|
||||||
autoload :WaitForCommunicator, "vagrant/action/builtin/wait_for_communicator"
|
autoload :WaitForCommunicator, "vagrant/action/builtin/wait_for_communicator"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
require 'fileutils'
|
|
||||||
require 'pathname'
|
|
||||||
require 'zlib'
|
|
||||||
|
|
||||||
require "log4r"
|
|
||||||
|
|
||||||
require 'vagrant/util/platform'
|
|
||||||
|
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module Builtin
|
|
||||||
# This built-in middleware exports and mounts NFS shared folders.
|
|
||||||
#
|
|
||||||
# To use this middleware, two configuration parameters must be given
|
|
||||||
# via the environment hash:
|
|
||||||
#
|
|
||||||
# - `:nfs_host_ip` - The IP of where to mount the NFS folder from.
|
|
||||||
# - `:nfs_machine_ip` - The IP of the machine where the NFS folder
|
|
||||||
# will be mounted.
|
|
||||||
#
|
|
||||||
class NFS
|
|
||||||
def initialize(app, env)
|
|
||||||
@app = app
|
|
||||||
@logger = Log4r::Logger.new("vagrant::action::builtin::nfs")
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
# We forward things along first. We do everything on the tail
|
|
||||||
# end of the middleware call.
|
|
||||||
@app.call(env)
|
|
||||||
|
|
||||||
# Used by prepare_permission, so we need to save it
|
|
||||||
@env = env
|
|
||||||
|
|
||||||
folders = {}
|
|
||||||
env[:machine].config.vm.synced_folders.each do |id, opts|
|
|
||||||
# If this synced folder doesn't enable NFS, ignore it.
|
|
||||||
next if !opts[:nfs]
|
|
||||||
|
|
||||||
# Expand the host path, create it if we have to and
|
|
||||||
# store away the folder.
|
|
||||||
hostpath = Pathname.new(opts[:hostpath]).
|
|
||||||
expand_path(env[:root_path])
|
|
||||||
|
|
||||||
if !hostpath.directory? && opts[:create]
|
|
||||||
# Host path doesn't exist, so let's create it.
|
|
||||||
@logger.debug("Host path doesn't exist, creating: #{hostpath}")
|
|
||||||
|
|
||||||
begin
|
|
||||||
FileUtils.mkpath(hostpath)
|
|
||||||
rescue Errno::EACCES
|
|
||||||
raise Vagrant::Errors::SharedFolderCreateFailed,
|
|
||||||
:path => hostpath.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Determine the real path by expanding symlinks and getting
|
|
||||||
# proper casing. We have to do this after creating it
|
|
||||||
# if it doesn't exist.
|
|
||||||
hostpath = hostpath.realpath
|
|
||||||
hostpath = Util::Platform.fs_real_path(hostpath)
|
|
||||||
|
|
||||||
# Set the hostpath back on the options and save it
|
|
||||||
opts[:hostpath] = hostpath.to_s
|
|
||||||
folders[id] = opts
|
|
||||||
end
|
|
||||||
|
|
||||||
if !folders.empty?
|
|
||||||
raise Errors::NFSNoHostIP if !env[:nfs_host_ip]
|
|
||||||
raise Errors::NFSNoGuestIP if !env[:nfs_machine_ip]
|
|
||||||
|
|
||||||
machine_ip = env[:nfs_machine_ip]
|
|
||||||
machine_ip = [machine_ip] if !machine_ip.is_a?(Array)
|
|
||||||
|
|
||||||
# Prepare the folder, this means setting up various options
|
|
||||||
# and such on the folder itself.
|
|
||||||
folders.each { |id, opts| prepare_folder(opts) }
|
|
||||||
|
|
||||||
# Export the folders
|
|
||||||
env[:ui].info I18n.t("vagrant.actions.vm.nfs.exporting")
|
|
||||||
env[:host].nfs_export(env[:machine].id, machine_ip, folders)
|
|
||||||
|
|
||||||
# Mount
|
|
||||||
env[:ui].info I18n.t("vagrant.actions.vm.nfs.mounting")
|
|
||||||
|
|
||||||
# Only mount folders that have a guest path specified.
|
|
||||||
mount_folders = {}
|
|
||||||
folders.each do |id, opts|
|
|
||||||
mount_folders[id] = opts.dup if opts[:guestpath]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Mount them!
|
|
||||||
env[:machine].guest.capability(
|
|
||||||
:mount_nfs_folder, env[:nfs_host_ip], mount_folders)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def prepare_folder(opts)
|
|
||||||
opts[:map_uid] = prepare_permission(:uid, opts)
|
|
||||||
opts[:map_gid] = prepare_permission(:gid, opts)
|
|
||||||
opts[:nfs_version] ||= 3
|
|
||||||
|
|
||||||
# We use a CRC32 to generate a 32-bit checksum so that the
|
|
||||||
# fsid is compatible with both old and new kernels.
|
|
||||||
opts[:uuid] = Zlib.crc32(opts[:hostpath]).to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# Prepares the UID/GID settings for a single folder.
|
|
||||||
def prepare_permission(perm, opts)
|
|
||||||
key = "map_#{perm}".to_sym
|
|
||||||
return nil if opts.has_key?(key) && opts[key].nil?
|
|
||||||
|
|
||||||
# The options on the hash get priority, then the default
|
|
||||||
# values
|
|
||||||
value = opts.has_key?(key) ? opts[key] : @env[:machine].config.nfs.send(key)
|
|
||||||
return value if value != :auto
|
|
||||||
|
|
||||||
# Get UID/GID from folder if we've made it this far
|
|
||||||
# (value == :auto)
|
|
||||||
stat = File.stat(opts[:hostpath])
|
|
||||||
return stat.send(perm)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
require "log4r"
|
||||||
|
|
||||||
|
require 'vagrant/util/platform'
|
||||||
|
|
||||||
|
module Vagrant
|
||||||
|
module Action
|
||||||
|
module Builtin
|
||||||
|
# This middleware will setup the synced folders for the machine using
|
||||||
|
# the appropriate synced folder plugin.
|
||||||
|
class SyncedFolders
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
@logger = Log4r::Logger.new("vagrant::action::builtin::synced_folders")
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
folders = synced_folders(env[:machine])
|
||||||
|
|
||||||
|
# Log all the folders that we have enabled and with what
|
||||||
|
# implementation...
|
||||||
|
folders.each do |impl, fs|
|
||||||
|
@logger.info("Synced Folder Implementation: #{impl}")
|
||||||
|
fs.each do |id, data|
|
||||||
|
@logger.info(" - #{id}: #{data[:hostpath]} => #{data[:guestpath]}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Go through each folder and make sure to create it if
|
||||||
|
# it does not exist on host
|
||||||
|
folders.each do |impl, fs|
|
||||||
|
fs.each do |id, data|
|
||||||
|
data[:hostpath] = File.expand_path(data[:hostpath], env[:root_path])
|
||||||
|
|
||||||
|
# Create the hostpath if it doesn't exist and we've been told to
|
||||||
|
if !File.directory?(data[:hostpath]) && data[:create]
|
||||||
|
@logger.info("Creating shared folder host directory: #{data[:hostpath]}")
|
||||||
|
begin
|
||||||
|
Pathname.new(data[:hostpath]).mkpath
|
||||||
|
rescue Errno::EACCES
|
||||||
|
raise Vagrant::Errors::SharedFolderCreateFailed,
|
||||||
|
path: data[:hostpath]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if File.directory?(data[:hostpath])
|
||||||
|
data[:hostpath] = File.realpath(data[:hostpath])
|
||||||
|
data[:hostpath] = Util::Platform.fs_real_path(data[:hostpath]).to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Go through each folder and prepare the folders
|
||||||
|
folders.each do |impl_name, fs|
|
||||||
|
@logger.info("Invoking synced folder prepare for: #{impl_name}")
|
||||||
|
plugins[impl_name.to_sym][0].new.prepare(env[:machine], fs, impl_opts(impl_name, env))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Continue, we need the VM to be booted.
|
||||||
|
@app.call(env)
|
||||||
|
|
||||||
|
# Once booted, setup the folder contents
|
||||||
|
folders.each do |impl_name, fs|
|
||||||
|
@logger.info("Invoking synced folder enable: #{impl_name}")
|
||||||
|
plugins[impl_name.to_sym][0].new.enable(env[:machine], fs, impl_opts(impl_name, env))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This goes over all the registered synced folder types and returns
|
||||||
|
# the highest priority implementation that is usable for this machine.
|
||||||
|
def default_synced_folder_type(machine, plugins)
|
||||||
|
ordered = []
|
||||||
|
|
||||||
|
# First turn the plugins into an array
|
||||||
|
plugins.each do |key, data|
|
||||||
|
impl = data[0]
|
||||||
|
priority = data[1]
|
||||||
|
|
||||||
|
ordered << [priority, key, impl]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Order the plugins by priority
|
||||||
|
ordered = ordered.sort { |a, b| b[0] <=> a[0] }
|
||||||
|
|
||||||
|
# Find the proper implementation
|
||||||
|
ordered.each do |_, key, impl|
|
||||||
|
return key if impl.new.usable?(machine)
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# This finds the options in the env that are set for a given
|
||||||
|
# synced folder type.
|
||||||
|
def impl_opts(name, env)
|
||||||
|
{}.tap do |result|
|
||||||
|
env.each do |k, v|
|
||||||
|
if k.to_s.start_with?("#{name}_")
|
||||||
|
k = k.dup if !k.is_a?(Symbol)
|
||||||
|
v = v.dup if !v.is_a?(Symbol)
|
||||||
|
result[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This returns the available synced folder implementations. This
|
||||||
|
# is a separate method so that it can be easily stubbed by tests.
|
||||||
|
def plugins
|
||||||
|
@plugins ||= Vagrant.plugin("2").manager.synced_folders
|
||||||
|
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.
|
||||||
|
def synced_folders(machine)
|
||||||
|
folders = {}
|
||||||
|
|
||||||
|
# Determine all the synced folders as well as the implementation
|
||||||
|
# they're going to use.
|
||||||
|
machine.config.vm.synced_folders.each do |id, data|
|
||||||
|
# Ignore disabled synced folders
|
||||||
|
next if data[:disabled]
|
||||||
|
|
||||||
|
impl = ""
|
||||||
|
impl = data[:type].to_sym if data[:type]
|
||||||
|
|
||||||
|
if impl != ""
|
||||||
|
impl_class = plugins[impl]
|
||||||
|
if !impl_class
|
||||||
|
# This should never happen because configuration validation
|
||||||
|
# should catch this case. But we put this here as an assert
|
||||||
|
raise "Internal error. Report this as a bug. Invalid: #{data[:type]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if !impl_class[0].new.usable?(machine)
|
||||||
|
# Verify that explicitly defined shared folder types are
|
||||||
|
# actually usable.
|
||||||
|
raise Errors::SyncedFolderUnusable, type: data[:type].to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Keep track of this shared folder by the implementation.
|
||||||
|
folders[impl] ||= {}
|
||||||
|
folders[impl][id] = data.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
# If we have folders with the "default" key, then determine the
|
||||||
|
# most appropriate implementation for this.
|
||||||
|
if folders.has_key?("") && !folders[""].empty?
|
||||||
|
default_impl = default_synced_folder_type(machine, plugins)
|
||||||
|
if !default_impl
|
||||||
|
types = plugins.to_hash.keys.map { |t| t.to_s }.sort.join(", ")
|
||||||
|
raise Errors::NoDefaultSyncedFolderImpl, types: types
|
||||||
|
end
|
||||||
|
|
||||||
|
folders[default_impl] = folders[""]
|
||||||
|
folders.delete("")
|
||||||
|
end
|
||||||
|
|
||||||
|
return folders
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -364,6 +364,10 @@ module Vagrant
|
||||||
error_key(:nfs_no_hostonly_network)
|
error_key(:nfs_no_hostonly_network)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class NoDefaultSyncedFolderImpl < VagrantError
|
||||||
|
error_key(:no_default_synced_folder_impl)
|
||||||
|
end
|
||||||
|
|
||||||
class NoEnvironmentError < VagrantError
|
class NoEnvironmentError < VagrantError
|
||||||
error_key(:no_env)
|
error_key(:no_env)
|
||||||
end
|
end
|
||||||
|
@ -508,6 +512,10 @@ module Vagrant
|
||||||
error_key(:ssh_unavailable_windows)
|
error_key(:ssh_unavailable_windows)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class SyncedFolderUnusable < VagrantError
|
||||||
|
error_key(:synced_folder_unusable)
|
||||||
|
end
|
||||||
|
|
||||||
class UIExpectsTTY < VagrantError
|
class UIExpectsTTY < VagrantError
|
||||||
error_key(:ui_expects_tty)
|
error_key(:ui_expects_tty)
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,7 @@ module Vagrant
|
||||||
autoload :Plugin, "vagrant/plugin/v2/plugin"
|
autoload :Plugin, "vagrant/plugin/v2/plugin"
|
||||||
autoload :Provider, "vagrant/plugin/v2/provider"
|
autoload :Provider, "vagrant/plugin/v2/provider"
|
||||||
autoload :Provisioner, "vagrant/plugin/v2/provisioner"
|
autoload :Provisioner, "vagrant/plugin/v2/provisioner"
|
||||||
|
autoload :SyncedFolder, "vagrant/plugin/v2/synced_folder"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,6 +32,11 @@ module Vagrant
|
||||||
# @return [Hash<Symbol, Registry>]
|
# @return [Hash<Symbol, Registry>]
|
||||||
attr_reader :providers
|
attr_reader :providers
|
||||||
|
|
||||||
|
# This contains all the synced folder implementations by name.
|
||||||
|
#
|
||||||
|
# @return [Registry<Symbol, Array<Class, Integer>>]
|
||||||
|
attr_reader :synced_folders
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
# The action hooks hash defaults to []
|
# The action hooks hash defaults to []
|
||||||
@action_hooks = Hash.new { |h, k| h[k] = [] }
|
@action_hooks = Hash.new { |h, k| h[k] = [] }
|
||||||
|
@ -40,6 +45,7 @@ module Vagrant
|
||||||
@guests = Registry.new
|
@guests = Registry.new
|
||||||
@guest_capabilities = Hash.new { |h, k| h[k] = Registry.new }
|
@guest_capabilities = Hash.new { |h, k| h[k] = Registry.new }
|
||||||
@providers = Registry.new
|
@providers = Registry.new
|
||||||
|
@synced_folders = Registry.new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -142,6 +142,17 @@ module Vagrant
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This returns all synced folder implementations.
|
||||||
|
#
|
||||||
|
# @return [Registry]
|
||||||
|
def synced_folders
|
||||||
|
Registry.new.tap do |result|
|
||||||
|
@registered.each do |plugin|
|
||||||
|
result.merge!(plugin.components.synced_folders)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# This registers a plugin. This should _NEVER_ be called by the public
|
# This registers a plugin. This should _NEVER_ be called by the public
|
||||||
# and should only be called from within Vagrant. Vagrant will
|
# and should only be called from within Vagrant. Vagrant will
|
||||||
# automatically register V2 plugins when a name is set on the
|
# automatically register V2 plugins when a name is set on the
|
||||||
|
|
|
@ -194,6 +194,19 @@ module Vagrant
|
||||||
data[:provisioners]
|
data[:provisioners]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Registers additional synced folder implementations.
|
||||||
|
#
|
||||||
|
# @param [String] name Name of the implementation.
|
||||||
|
# @param [Integer] priority The priority of the implementation,
|
||||||
|
# higher (big) numbers are tried before lower (small) numbers.
|
||||||
|
def self.synced_folder(name, priority=10, &block)
|
||||||
|
components.synced_folders.register(name.to_sym) do
|
||||||
|
[block.call, priority]
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the internal data associated with this plugin. This
|
# Returns the internal data associated with this plugin. This
|
||||||
# should NOT be called by the general public.
|
# should NOT be called by the general public.
|
||||||
#
|
#
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
module Vagrant
|
||||||
|
module Plugin
|
||||||
|
module V2
|
||||||
|
# This is the base class for a synced folder implementation.
|
||||||
|
class SyncedFolder
|
||||||
|
def usable?(machine)
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare(machine, folders, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable(machine, folders, opts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,14 +0,0 @@
|
||||||
require "vagrant"
|
|
||||||
|
|
||||||
module VagrantPlugins
|
|
||||||
module Kernel_V2
|
|
||||||
class NFSConfig < Vagrant.plugin("2", :config)
|
|
||||||
attr_accessor :map_uid
|
|
||||||
attr_accessor :map_gid
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
"NFS"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -294,9 +294,16 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
|
|
||||||
@__synced_folders.each do |id, options|
|
@__synced_folders.each do |id, options|
|
||||||
|
if options[:nfs]
|
||||||
|
options[:type] = :nfs
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make sure the type is a symbol
|
||||||
|
options[:type] = options[:type].to_sym if options[:type]
|
||||||
|
|
||||||
# Ignore NFS on Windows
|
# Ignore NFS on Windows
|
||||||
if options[:nfs] && Vagrant::Util::Platform.windows?
|
if options[:type] == :nfs && Vagrant::Util::Platform.windows?
|
||||||
options[:nfs] = false
|
options.delete(:type)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -381,7 +388,7 @@ module VagrantPlugins
|
||||||
:path => options[:hostpath])
|
:path => options[:hostpath])
|
||||||
end
|
end
|
||||||
|
|
||||||
if options[:nfs]
|
if options[:type] == :nfs
|
||||||
has_nfs = true
|
has_nfs = true
|
||||||
|
|
||||||
if options[:owner] || options[:group]
|
if options[:owner] || options[:group]
|
||||||
|
|
|
@ -20,11 +20,6 @@ module VagrantPlugins
|
||||||
SSHConfig
|
SSHConfig
|
||||||
end
|
end
|
||||||
|
|
||||||
config("nfs") do
|
|
||||||
require File.expand_path("../config/nfs", __FILE__)
|
|
||||||
NFSConfig
|
|
||||||
end
|
|
||||||
|
|
||||||
config("package") do
|
config("package") do
|
||||||
require File.expand_path("../config/package", __FILE__)
|
require File.expand_path("../config/package", __FILE__)
|
||||||
PackageConfig
|
PackageConfig
|
||||||
|
|
|
@ -40,7 +40,6 @@ module VagrantPlugins
|
||||||
autoload :SaneDefaults, File.expand_path("../action/sane_defaults", __FILE__)
|
autoload :SaneDefaults, File.expand_path("../action/sane_defaults", __FILE__)
|
||||||
autoload :SetName, File.expand_path("../action/set_name", __FILE__)
|
autoload :SetName, File.expand_path("../action/set_name", __FILE__)
|
||||||
autoload :SetupPackageFiles, File.expand_path("../action/setup_package_files", __FILE__)
|
autoload :SetupPackageFiles, File.expand_path("../action/setup_package_files", __FILE__)
|
||||||
autoload :ShareFolders, File.expand_path("../action/share_folders", __FILE__)
|
|
||||||
autoload :Suspend, File.expand_path("../action/suspend", __FILE__)
|
autoload :Suspend, File.expand_path("../action/suspend", __FILE__)
|
||||||
|
|
||||||
# Include the built-in modules so that we can use them as top-level
|
# Include the built-in modules so that we can use them as top-level
|
||||||
|
@ -60,10 +59,9 @@ module VagrantPlugins
|
||||||
b.use PrepareForwardedPortCollisionParams
|
b.use PrepareForwardedPortCollisionParams
|
||||||
b.use HandleForwardedPortCollisions
|
b.use HandleForwardedPortCollisions
|
||||||
b.use PruneNFSExports
|
b.use PruneNFSExports
|
||||||
b.use NFS
|
|
||||||
b.use PrepareNFSSettings
|
|
||||||
b.use ClearSharedFolders
|
b.use ClearSharedFolders
|
||||||
b.use ShareFolders
|
b.use SyncedFolders
|
||||||
|
b.use PrepareNFSSettings
|
||||||
b.use ClearNetworkInterfaces
|
b.use ClearNetworkInterfaces
|
||||||
b.use Network
|
b.use Network
|
||||||
b.use ForwardPorts
|
b.use ForwardPorts
|
||||||
|
|
|
@ -12,7 +12,7 @@ module VagrantPlugins
|
||||||
|
|
||||||
using_nfs = false
|
using_nfs = false
|
||||||
env[:machine].config.vm.synced_folders.each do |id, opts|
|
env[:machine].config.vm.synced_folders.each do |id, opts|
|
||||||
if opts[:nfs]
|
if opts[:type] == :nfs
|
||||||
using_nfs = true
|
using_nfs = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
require "pathname"
|
|
||||||
|
|
||||||
require "log4r"
|
|
||||||
|
|
||||||
require "vagrant/util/platform"
|
|
||||||
require "vagrant/util/scoped_hash_override"
|
|
||||||
|
|
||||||
module VagrantPlugins
|
|
||||||
module ProviderVirtualBox
|
|
||||||
module Action
|
|
||||||
class ShareFolders
|
|
||||||
include Vagrant::Util::ScopedHashOverride
|
|
||||||
|
|
||||||
def initialize(app, env)
|
|
||||||
@logger = Log4r::Logger.new("vagrant::action::vm::share_folders")
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
@env = env
|
|
||||||
|
|
||||||
prepare_folders
|
|
||||||
create_metadata
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
|
|
||||||
mount_shared_folders
|
|
||||||
end
|
|
||||||
|
|
||||||
# This method returns an actual list of VirtualBox shared
|
|
||||||
# folders to create and their proper path.
|
|
||||||
def shared_folders
|
|
||||||
{}.tap do |result|
|
|
||||||
@env[:machine].config.vm.synced_folders.each do |id, data|
|
|
||||||
data = scoped_hash_override(data, :virtualbox)
|
|
||||||
|
|
||||||
# Ignore NFS shared folders
|
|
||||||
next if data[:nfs]
|
|
||||||
|
|
||||||
# Ignore disabled shared folders
|
|
||||||
next if data[:disabled]
|
|
||||||
|
|
||||||
# This to prevent overwriting the actual shared folders data
|
|
||||||
result[id] = data.dup
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Prepares the shared folders by verifying they exist and creating them
|
|
||||||
# if they don't.
|
|
||||||
def prepare_folders
|
|
||||||
shared_folders.each do |id, options|
|
|
||||||
hostpath = Pathname.new(options[:hostpath]).expand_path(@env[:root_path])
|
|
||||||
|
|
||||||
if !hostpath.directory? && options[:create]
|
|
||||||
# Host path doesn't exist, so let's create it.
|
|
||||||
@logger.debug("Host path doesn't exist, creating: #{hostpath}")
|
|
||||||
|
|
||||||
begin
|
|
||||||
hostpath.mkpath
|
|
||||||
rescue Errno::EACCES
|
|
||||||
raise Vagrant::Errors::SharedFolderCreateFailed,
|
|
||||||
:path => hostpath.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_metadata
|
|
||||||
@env[:ui].info I18n.t("vagrant.actions.vm.share_folders.creating")
|
|
||||||
|
|
||||||
folders = []
|
|
||||||
shared_folders.each do |id, data|
|
|
||||||
hostpath = File.expand_path(data[:hostpath], @env[:root_path])
|
|
||||||
hostpath = Vagrant::Util::Platform.cygwin_windows_path(hostpath)
|
|
||||||
|
|
||||||
folders << {
|
|
||||||
:name => id,
|
|
||||||
:hostpath => hostpath,
|
|
||||||
:transient => data[:transient]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
@env[:machine].provider.driver.share_folders(folders)
|
|
||||||
end
|
|
||||||
|
|
||||||
def mount_shared_folders
|
|
||||||
@env[:ui].info I18n.t("vagrant.actions.vm.share_folders.mounting")
|
|
||||||
|
|
||||||
# short guestpaths first, so we don't step on ourselves
|
|
||||||
folders = shared_folders.sort_by do |id, data|
|
|
||||||
if data[:guestpath]
|
|
||||||
data[:guestpath].length
|
|
||||||
else
|
|
||||||
# A long enough path to just do this at the end.
|
|
||||||
10000
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Go through each folder and mount
|
|
||||||
folders.each do |id, data|
|
|
||||||
if data[:guestpath]
|
|
||||||
# Guest path specified, so mount the folder to specified point
|
|
||||||
@env[:ui].info(I18n.t("vagrant.actions.vm.share_folders.mounting_entry",
|
|
||||||
:guest_path => data[:guestpath]))
|
|
||||||
|
|
||||||
# Dup the data so we can pass it to the guest API
|
|
||||||
data = data.dup
|
|
||||||
|
|
||||||
# Calculate the owner and group
|
|
||||||
ssh_info = @env[:machine].ssh_info
|
|
||||||
data[:owner] ||= ssh_info[:username]
|
|
||||||
data[:group] ||= ssh_info[:username]
|
|
||||||
|
|
||||||
# Mount the actual folder
|
|
||||||
@env[:machine].guest.capability(
|
|
||||||
:mount_virtualbox_shared_folder, id, data[:guestpath], data)
|
|
||||||
else
|
|
||||||
# If no guest path is specified, then automounting is disabled
|
|
||||||
@env[:ui].info(I18n.t("vagrant.actions.vm.share_folders.nomount_entry",
|
|
||||||
:host_path => data[:hostpath]))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -18,6 +18,11 @@ module VagrantPlugins
|
||||||
require File.expand_path("../config", __FILE__)
|
require File.expand_path("../config", __FILE__)
|
||||||
Config
|
Config
|
||||||
end
|
end
|
||||||
|
|
||||||
|
synced_folder(:virtualbox) do
|
||||||
|
require File.expand_path("../synced_folder", __FILE__)
|
||||||
|
SyncedFolder
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
autoload :Action, File.expand_path("../action", __FILE__)
|
autoload :Action, File.expand_path("../action", __FILE__)
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
require "vagrant/util/platform"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module ProviderVirtualBox
|
||||||
|
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
|
||||||
|
def usable?(machine)
|
||||||
|
# These synced folders only work if the provider if VirtualBox
|
||||||
|
machine.provider_name == :virtualbox
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare(machine, folders, _opts)
|
||||||
|
defs = []
|
||||||
|
folders.each do |id, data|
|
||||||
|
hostpath = Vagrant::Util::Platform.cygwin_windows_path(data[:hostpath])
|
||||||
|
|
||||||
|
defs << {
|
||||||
|
name: id,
|
||||||
|
hostpath: hostpath.to_s,
|
||||||
|
transient: data[:transient],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
driver(machine).share_folders(defs)
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable(machine, folders, _opts)
|
||||||
|
# short guestpaths first, so we don't step on ourselves
|
||||||
|
folders = folders.sort_by do |id, data|
|
||||||
|
if data[:guestpath]
|
||||||
|
data[:guestpath].length
|
||||||
|
else
|
||||||
|
# A long enough path to just do this at the end.
|
||||||
|
10000
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Go through each folder and mount
|
||||||
|
machine.ui.info(I18n.t("vagrant.actions.vm.share_folders.mounting"))
|
||||||
|
folders.each do |id, data|
|
||||||
|
if data[:guestpath]
|
||||||
|
# Guest path specified, so mount the folder to specified point
|
||||||
|
machine.ui.info(I18n.t("vagrant.actions.vm.share_folders.mounting_entry",
|
||||||
|
:guest_path => data[:guestpath]))
|
||||||
|
|
||||||
|
# Dup the data so we can pass it to the guest API
|
||||||
|
data = data.dup
|
||||||
|
|
||||||
|
# Calculate the owner and group
|
||||||
|
ssh_info = machine.ssh_info
|
||||||
|
data[:owner] ||= ssh_info[:username]
|
||||||
|
data[:group] ||= ssh_info[:username]
|
||||||
|
|
||||||
|
# Mount the actual folder
|
||||||
|
machine.guest.capability(
|
||||||
|
:mount_virtualbox_shared_folder, id, data[:guestpath], data)
|
||||||
|
else
|
||||||
|
# If no guest path is specified, then automounting is disabled
|
||||||
|
machine.ui.info(I18n.t("vagrant.actions.vm.share_folders.nomount_entry",
|
||||||
|
:host_path => data[:hostpath]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
# This is here so that we can stub it for tests
|
||||||
|
def driver(machine)
|
||||||
|
machine.provider.driver
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,26 @@
|
||||||
|
require "vagrant"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module SyncedFolderNFS
|
||||||
|
class Config < Vagrant.plugin("2", :config)
|
||||||
|
attr_accessor :map_uid
|
||||||
|
attr_accessor :map_gid
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
super
|
||||||
|
|
||||||
|
@map_uid = UNSET_VALUE
|
||||||
|
@map_gid = UNSET_VALUE
|
||||||
|
end
|
||||||
|
|
||||||
|
def finalize!
|
||||||
|
@map_uid = nil if @map_uid == UNSET_VALUE
|
||||||
|
@map_gid = nil if @map_gid == UNSET_VALUE
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"NFS"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
require "vagrant"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module SyncedFolderNFS
|
||||||
|
class Plugin < Vagrant.plugin("2")
|
||||||
|
name "NFS synced folders"
|
||||||
|
description <<-EOF
|
||||||
|
The NFS synced folders plugin enables you to use NFS as a synced folder
|
||||||
|
implementation.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
config("nfs") do
|
||||||
|
require File.expand_path("../config", __FILE__)
|
||||||
|
Config
|
||||||
|
end
|
||||||
|
|
||||||
|
synced_folder("nfs", 5) do
|
||||||
|
require File.expand_path("../synced_folder", __FILE__)
|
||||||
|
SyncedFolder
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,83 @@
|
||||||
|
require 'fileutils'
|
||||||
|
require 'zlib'
|
||||||
|
|
||||||
|
require "vagrant/util/platform"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module SyncedFolderNFS
|
||||||
|
# This synced folder requires that two keys be set on the environment
|
||||||
|
# within the middleware sequence:
|
||||||
|
#
|
||||||
|
# - `:nfs_host_ip` - The IP of where to mount the NFS folder from.
|
||||||
|
# - `:nfs_machine_ip` - The IP of the machine where the NFS folder
|
||||||
|
# will be mounted.
|
||||||
|
#
|
||||||
|
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
|
||||||
|
def usable?(machine)
|
||||||
|
# NFS is always available
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare(machine, folders, opts)
|
||||||
|
# Nothing is necessary to do before VM boot.
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable(machine, folders, nfsopts)
|
||||||
|
raise Vagrant::Errors::NFSNoHostIP if !nfsopts[:nfs_host_ip]
|
||||||
|
raise Vagrant::Errors::NFSNoGuestIP if !nfsopts[:nfs_machine_ip]
|
||||||
|
|
||||||
|
machine_ip = nfsopts[:nfs_machine_ip]
|
||||||
|
machine_ip = [machine_ip] if !machine_ip.is_a?(Array)
|
||||||
|
|
||||||
|
# Prepare the folder, this means setting up various options
|
||||||
|
# and such on the folder itself.
|
||||||
|
folders.each { |id, opts| prepare_folder(machine, opts) }
|
||||||
|
|
||||||
|
# Export the folders
|
||||||
|
machine.ui.info I18n.t("vagrant.actions.vm.nfs.exporting")
|
||||||
|
machine.env.host.nfs_export(machine.id, machine_ip, folders)
|
||||||
|
|
||||||
|
# Mount
|
||||||
|
machine.ui.info I18n.t("vagrant.actions.vm.nfs.mounting")
|
||||||
|
|
||||||
|
# Only mount folders that have a guest path specified.
|
||||||
|
mount_folders = {}
|
||||||
|
folders.each do |id, opts|
|
||||||
|
mount_folders[id] = opts.dup if opts[:guestpath]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Mount them!
|
||||||
|
machine.guest.capability(
|
||||||
|
:mount_nfs_folder, nfsopts[:nfs_host_ip], mount_folders)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def prepare_folder(machine, opts)
|
||||||
|
opts[:map_uid] = prepare_permission(machine, :uid, opts)
|
||||||
|
opts[:map_gid] = prepare_permission(machine, :gid, opts)
|
||||||
|
opts[:nfs_version] ||= 3
|
||||||
|
|
||||||
|
# We use a CRC32 to generate a 32-bit checksum so that the
|
||||||
|
# fsid is compatible with both old and new kernels.
|
||||||
|
opts[:uuid] = Zlib.crc32(opts[:hostpath]).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# Prepares the UID/GID settings for a single folder.
|
||||||
|
def prepare_permission(machine, perm, opts)
|
||||||
|
key = "map_#{perm}".to_sym
|
||||||
|
return nil if opts.has_key?(key) && opts[key].nil?
|
||||||
|
|
||||||
|
# The options on the hash get priority, then the default
|
||||||
|
# values
|
||||||
|
value = opts.has_key?(key) ? opts[key] : machine.config.nfs.send(key)
|
||||||
|
return value if value != :auto
|
||||||
|
|
||||||
|
# Get UID/GID from folder if we've made it this far
|
||||||
|
# (value == :auto)
|
||||||
|
stat = File.stat(opts[:hostpath])
|
||||||
|
return stat.send(perm)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -387,6 +387,14 @@ en:
|
||||||
NFS requires a host-only network with a static IP to be created.
|
NFS requires a host-only network with a static IP to be created.
|
||||||
Please add a host-only network with a static IP to the machine
|
Please add a host-only network with a static IP to the machine
|
||||||
for NFS to work.
|
for NFS to work.
|
||||||
|
no_default_synced_folder_impl: |-
|
||||||
|
No synced folder implementation is available for your synced folders!
|
||||||
|
Please consult the documentation to learn why this may be the case.
|
||||||
|
You may force a synced folder implementation by specifying a "type:"
|
||||||
|
option for the synced folders. Available synced folder implementations
|
||||||
|
are listed below.
|
||||||
|
|
||||||
|
%{types}
|
||||||
no_env: |-
|
no_env: |-
|
||||||
A Vagrant environment is required to run this command. Run `vagrant init`
|
A Vagrant environment is required to run this command. Run `vagrant init`
|
||||||
to set one up in this directory, or change to a directory with a
|
to set one up in this directory, or change to a directory with a
|
||||||
|
@ -572,6 +580,10 @@ en:
|
||||||
Port: %{port}
|
Port: %{port}
|
||||||
Username: %{username}
|
Username: %{username}
|
||||||
Private key: %{key_path}
|
Private key: %{key_path}
|
||||||
|
synced_folder_unusable: |
|
||||||
|
The synced folder type '%{type}' is reporting as unusable for
|
||||||
|
your current setup. Please verify you have all the proper
|
||||||
|
prerequisites for using this shared folder type and try again.
|
||||||
test_key: "test value"
|
test_key: "test value"
|
||||||
ui_expects_tty: |-
|
ui_expects_tty: |-
|
||||||
Vagrant is attempting to interface with the UI in a way that requires
|
Vagrant is attempting to interface with the UI in a way that requires
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
require "vagrant"
|
||||||
|
require Vagrant.source_root.join("test/unit/base")
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/providers/virtualbox/synced_folder")
|
||||||
|
|
||||||
|
# TODO(mitchellh): tag with v2
|
||||||
|
describe VagrantPlugins::ProviderVirtualBox::SyncedFolder do
|
||||||
|
let(:machine) do
|
||||||
|
double("machine").tap do |m|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { described_class.new }
|
||||||
|
|
||||||
|
describe "usable" do
|
||||||
|
it "should be with virtualbox provider" do
|
||||||
|
machine.stub(provider_name: :virtualbox)
|
||||||
|
subject.should be_usable(machine)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not be with another provider" do
|
||||||
|
machine.stub(provider_name: :vmware_fusion)
|
||||||
|
subject.should_not be_usable(machine)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "prepare" do
|
||||||
|
let(:driver) { double("driver") }
|
||||||
|
|
||||||
|
before do
|
||||||
|
machine.stub(driver: driver)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should share the folders" do
|
||||||
|
pending
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,180 @@
|
||||||
|
require "pathname"
|
||||||
|
require "tmpdir"
|
||||||
|
|
||||||
|
require File.expand_path("../../../../base", __FILE__)
|
||||||
|
|
||||||
|
describe Vagrant::Action::Builtin::SyncedFolders do
|
||||||
|
let(:app) { lambda { |env| } }
|
||||||
|
let(:env) { { :machine => machine, :ui => ui } }
|
||||||
|
let(:machine) do
|
||||||
|
double("machine").tap do |machine|
|
||||||
|
machine.stub(:config).and_return(machine_config)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:machine_config) do
|
||||||
|
double("machine_config").tap do |top_config|
|
||||||
|
top_config.stub(:vm => vm_config)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:vm_config) { double("machine_vm_config") }
|
||||||
|
|
||||||
|
let(:ui) do
|
||||||
|
double("ui").tap do |result|
|
||||||
|
result.stub(:info)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { described_class.new(app, env) }
|
||||||
|
|
||||||
|
# This creates a synced folder implementation.
|
||||||
|
def impl(usable, name)
|
||||||
|
Class.new(Vagrant.plugin("2", :synced_folder)) do
|
||||||
|
define_method(:name) do
|
||||||
|
name
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method(:usable?) do |machine|
|
||||||
|
usable
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "call" do
|
||||||
|
let(:synced_folders) { {} }
|
||||||
|
let(:plugins) { {} }
|
||||||
|
|
||||||
|
before do
|
||||||
|
plugins[:default] = [impl(true, "default"), 10]
|
||||||
|
plugins[:nfs] = [impl(true, "nfs"), 5]
|
||||||
|
|
||||||
|
env[:root_path] = Pathname.new(Dir.mktmpdir)
|
||||||
|
subject.stub(:plugins => plugins)
|
||||||
|
subject.stub(:synced_folders => synced_folders)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should create on the host if specified" do
|
||||||
|
synced_folders["default"] = {
|
||||||
|
"root" => {
|
||||||
|
hostpath: "foo",
|
||||||
|
},
|
||||||
|
|
||||||
|
"other" => {
|
||||||
|
hostpath: "bar",
|
||||||
|
create: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subject.call(env)
|
||||||
|
|
||||||
|
env[:root_path].join("foo").should_not be_directory
|
||||||
|
env[:root_path].join("bar").should be_directory
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should invoke prepare then enable" do
|
||||||
|
order = []
|
||||||
|
tracker = Class.new(impl(true, "good")) do
|
||||||
|
define_method(:prepare) do |machine, folders, opts|
|
||||||
|
order << :prepare
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method(:enable) do |machine, folders, opts|
|
||||||
|
order << :enable
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
plugins[:tracker] = [tracker, 15]
|
||||||
|
|
||||||
|
synced_folders["tracker"] = {
|
||||||
|
"root" => {
|
||||||
|
hostpath: "foo",
|
||||||
|
},
|
||||||
|
|
||||||
|
"other" => {
|
||||||
|
hostpath: "bar",
|
||||||
|
create: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subject.call(env)
|
||||||
|
|
||||||
|
order.should == [:prepare, :enable]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "default_synced_folder_type" do
|
||||||
|
it "returns the usable implementation" do
|
||||||
|
plugins = {
|
||||||
|
"bad" => [impl(false, "bad"), 0],
|
||||||
|
"nope" => [impl(true, "nope"), 1],
|
||||||
|
"good" => [impl(true, "good"), 5],
|
||||||
|
}
|
||||||
|
|
||||||
|
result = subject.default_synced_folder_type(machine, plugins)
|
||||||
|
result.should == "good"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "impl_opts" do
|
||||||
|
it "should return only relevant keys" do
|
||||||
|
env = {
|
||||||
|
:foo_bar => "baz",
|
||||||
|
:bar_bar => "nope",
|
||||||
|
:foo_baz => "bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
result = subject.impl_opts("foo", env)
|
||||||
|
result.length.should == 2
|
||||||
|
result[:foo_bar].should == "baz"
|
||||||
|
result[:foo_baz].should == "bar"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "synced_folders" do
|
||||||
|
let(:folders) { {} }
|
||||||
|
let(:plugins) { {} }
|
||||||
|
|
||||||
|
before do
|
||||||
|
plugins[:default] = [impl(true, "default"), 10]
|
||||||
|
plugins[:nfs] = [impl(true, "nfs"), 5]
|
||||||
|
|
||||||
|
subject.stub(:plugins => plugins)
|
||||||
|
vm_config.stub(:synced_folders => folders)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise exception if bad type is given" do
|
||||||
|
folders["root"] = { type: "bad" }
|
||||||
|
|
||||||
|
expect { subject.synced_folders(machine) }.
|
||||||
|
to raise_error(StandardError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return the proper set of folders" do
|
||||||
|
folders["root"] = {}
|
||||||
|
folders["nfs"] = { type: "nfs" }
|
||||||
|
|
||||||
|
result = subject.synced_folders(machine)
|
||||||
|
result.length.should == 2
|
||||||
|
result[:default].should == { "root" => folders["root"] }
|
||||||
|
result[:nfs].should == { "nfs" => folders["nfs"] }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should error if an explicit type is unusable" do
|
||||||
|
plugins[:unusable] = [impl(false, "bad"), 15]
|
||||||
|
folders["root"] = { type: "unusable" }
|
||||||
|
|
||||||
|
expect { subject.synced_folders(machine) }.
|
||||||
|
to raise_error
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should ignore disabled folders" do
|
||||||
|
folders["root"] = {}
|
||||||
|
folders["foo"] = { disabled: true }
|
||||||
|
|
||||||
|
result = subject.synced_folders(machine)
|
||||||
|
result.length.should == 1
|
||||||
|
result[:default].length.should == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,15 +3,19 @@ require File.expand_path("../../../../base", __FILE__)
|
||||||
require "vagrant/registry"
|
require "vagrant/registry"
|
||||||
|
|
||||||
describe Vagrant::Plugin::V2::Components do
|
describe Vagrant::Plugin::V2::Components do
|
||||||
let(:instance) { described_class.new }
|
subject { described_class.new }
|
||||||
|
|
||||||
|
it "should have synced folders" do
|
||||||
|
subject.synced_folders.should be_kind_of(Vagrant::Registry)
|
||||||
|
end
|
||||||
|
|
||||||
describe "configs" do
|
describe "configs" do
|
||||||
it "should have configs" do
|
it "should have configs" do
|
||||||
instance.configs.should be_kind_of(Hash)
|
subject.configs.should be_kind_of(Hash)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should default the values to registries" do
|
it "should default the values to registries" do
|
||||||
instance.configs[:i_probably_dont_exist].should be_kind_of(Vagrant::Registry)
|
subject.configs[:i_probably_dont_exist].should be_kind_of(Vagrant::Registry)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -171,4 +171,21 @@ describe Vagrant::Plugin::V2::Manager do
|
||||||
instance.provider_configs[:foo].should == "foo"
|
instance.provider_configs[:foo].should == "foo"
|
||||||
instance.provider_configs[:bar].should == "bar"
|
instance.provider_configs[:bar].should == "bar"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should enumerate all registered synced folder implementations" do
|
||||||
|
pA = plugin do |p|
|
||||||
|
p.synced_folder("foo") { "bar" }
|
||||||
|
end
|
||||||
|
|
||||||
|
pB = plugin do |p|
|
||||||
|
p.synced_folder("bar", 50) { "baz" }
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.register(pA)
|
||||||
|
instance.register(pB)
|
||||||
|
|
||||||
|
instance.synced_folders.to_hash.length.should == 2
|
||||||
|
instance.synced_folders[:foo].should == ["bar", 10]
|
||||||
|
instance.synced_folders[:bar].should == ["baz", 50]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -278,6 +278,42 @@ describe Vagrant::Plugin::V2::Plugin do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "synced folders" do
|
||||||
|
it "should register implementations" do
|
||||||
|
plugin = Class.new(described_class) do
|
||||||
|
synced_folder("foo") { "bar" }
|
||||||
|
end
|
||||||
|
|
||||||
|
plugin.components.synced_folders[:foo].should == ["bar", 10]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be able to specify priorities" do
|
||||||
|
plugin = Class.new(described_class) do
|
||||||
|
synced_folder("foo", 50) { "bar" }
|
||||||
|
end
|
||||||
|
|
||||||
|
plugin.components.synced_folders[:foo].should == ["bar", 50]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should lazily register implementations" do
|
||||||
|
# Below would raise an error if the value of the config class was
|
||||||
|
# evaluated immediately. By asserting that this does not raise an
|
||||||
|
# error, we verify that the value is actually lazily loaded
|
||||||
|
plugin = nil
|
||||||
|
expect {
|
||||||
|
plugin = Class.new(described_class) do
|
||||||
|
synced_folder("foo") { raise StandardError, "FAIL!" }
|
||||||
|
end
|
||||||
|
}.to_not raise_error
|
||||||
|
|
||||||
|
# Now verify when we actually get the configuration key that
|
||||||
|
# a proper error is raised.
|
||||||
|
expect {
|
||||||
|
plugin.components.synced_folders[:foo]
|
||||||
|
}.to raise_error(StandardError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "plugin registration" do
|
describe "plugin registration" do
|
||||||
let(:manager) { described_class.manager }
|
let(:manager) { described_class.manager }
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,23 @@ describe Vagrant do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "v2" do
|
||||||
|
it "returns the proper class for version 2" do
|
||||||
|
described_class.plugin("2").should == Vagrant::Plugin::V2::Plugin
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the proper components for version 2" do
|
||||||
|
described_class.plugin("2", :command).should == Vagrant::Plugin::V2::Command
|
||||||
|
described_class.plugin("2", :communicator).should == Vagrant::Plugin::V2::Communicator
|
||||||
|
described_class.plugin("2", :config).should == Vagrant::Plugin::V2::Config
|
||||||
|
described_class.plugin("2", :guest).should == Vagrant::Plugin::V2::Guest
|
||||||
|
described_class.plugin("2", :host).should == Vagrant::Plugin::V2::Host
|
||||||
|
described_class.plugin("2", :provider).should == Vagrant::Plugin::V2::Provider
|
||||||
|
described_class.plugin("2", :provisioner).should == Vagrant::Plugin::V2::Provisioner
|
||||||
|
described_class.plugin("2", :synced_folder).should == Vagrant::Plugin::V2::SyncedFolder
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "raises an exception if an unsupported version is given" do
|
it "raises an exception if an unsupported version is given" do
|
||||||
expect { described_class.plugin("88") }.
|
expect { described_class.plugin("88") }.
|
||||||
to raise_error(ArgumentError)
|
to raise_error(ArgumentError)
|
||||||
|
|
Loading…
Reference in New Issue