Merge tag 'tags/v1.9.1' into patch-guest-esxi

This commit is contained in:
Tobias 2017-02-28 06:30:34 -08:00
commit e3e7484e16
147 changed files with 3051 additions and 1129 deletions

View File

@ -4,18 +4,14 @@ sudo: false
cache: bundler
before_install:
- gem uninstall bundler -aIxq --force
- gem uninstall -Ixq --force -i /home/travis/.rvm/gems/ruby-2.2.3@global bundler
- gem install bundler -v '1.12.5'
addons:
apt:
packages:
- bsdtar
rvm:
- 2.2.3
- 2.2.5
- 2.3.3
branches:
only:

View File

@ -1,7 +1,81 @@
## Next Version (Unreleased)
## 1.9.1 (December 7, 2016)
IMPROVEMENTS:
- core: Disable Vagrantfile loading when running plugin commands [GH-8066]
- guests/redhat: Detect and restart NetworkManager service if in use [GH-8052, GH-7994]
BUG FIXES:
- core: Detect load failures within install solution sets and retry [GH-8068]
- core: Prevent interactive shell on plugin uninstall [GH-8086, GH-8087]
- core: Remove bundler usage from Util::Env [GH-8090, GH-8094]
- guests/linux: Prevent stderr output on init version check for synced folders [GH-8051]
## 1.9.0 (November 28, 2016)
FEATURES:
- commands/box: Add `prune` subcommand for removing outdated boxes [GH-7978]
- core: Remove Bundler integration for handling internal plugins [GH-7793, GH-8000, GH-8011, GH-8031]
- providers/hyperv: Add support for Hyper-V binary configuration format
[GH-7854, GH-7706, GH-6102]
- provisioners/shell: Support MD5/SHA1 checksum validation of remote scripts [GH-7985, GH-6323]
IMPROVEMENTS:
- commands/plugin: Retain name sorted output when listing plugins [GH-8028]
- communicator/ssh: Support custom environment variable export template
[GH-7976, GH-6747]
- provisioners/ansible(both): Add `config_file` option to point the location of an
`ansible.cfg` file via ANSIBLE_CONFIG environment variable [GH-7195, GH-7918]
- synced_folders: Support custom naming and disable auto-mount [GH-7980, GH-6836]
BUG FIXES:
- guests/linux: Do not match interfaces with special characters when sorting [GH-7989, GH-7988]
- provisioner/salt: Fix Hash construction for constant [GH-7986, GH-7981]
## 1.8.7 (November 4, 2016)
IMPROVEMENTS:
- guests/linux: Place ethernet devices at start of network devices list [GH-7848]
- guests/linux: Provide more consistent guest detection [GH-7887, GH-7827]
- guests/openbsd: Validate guest rsync installation success [GH-7929, GH-7898]
- guests/redhat: Include Virtuozzo Linux 7 within flavor identification [GH-7818]
- guests/windows: Allow vagrant to start Windows Nano without provisioning [GH-7831]
- provisioners/ansible_local: Change the Ansible binary detection mechanism [GH-7536]
- provisioners/ansible(both): Add the `playbook_command` option [GH-7881]
- provisioners/puppet: Support custom environment variables [GH-7931, GH-7252, GH-2270]
- util/safe_exec: Use subprocess for safe_exec on Windows [GH-7802]
- util/subprocess: Allow closing STDIN [GH-7778]
BUG FIXES:
- communicators/winrm: Prevent connection leakage [GH-7712]
- core: Prevent duplicate provider priorities [GH-7756]
- core: Allow Numeric type for box version [GH-7874, GH-6960]
- core: Provide friendly error when user environment is too large [GH-7889, GH-7857]
- guests: Remove `set -e` usage for better shell compatibility [GH-7921, GH-7739]
- guests/linux: Fix incorrectly configured private network [GH-7844, GH-7848]
- guests/linux: Properly order network interfaces
[GH-7866, GH-7876, GH-7858, GH-7876]
- guests/linux: Only emit upstart event if initctl is available [GH-7813]
- guests/netbsd: Fix rsync installation [GH-7922, GH-7901]
- guests/photon: Fix networking setup [GH-7808, GH-7873]
- guests/redhat: Properly configure network and restart service [GH-7751]
- guests/redhat: Prevent NetworkManager from managing devices on initial start [GH-7926]
- hosts/linux: Fix race condition in writing /etc/exports file for NFS configuration
[GH-7947, GH-7938] - Thanks to Aron Griffis (@agriffis) for identifying this issue
- plugins/rsync: Escape exclude paths [GH-7928, GH-7910]
- providers/docker: Remove --interactive flag when pty is true [GH-7688]
- provisioners/ansible_local: Use enquoted path for file/directory existence checks
- provisioners/salt: Synchronize configuration defaults with documentation [GH-7907, GH-6624]
- pushes/atlas: Fix atlas push on Windows platform [GH-6938, GH-7802]
## 1.8.6 (September 27, 2016)
IMPROVEMENTS:
- Add detection for DragonFly BSD [GH-7701]

View File

@ -2,7 +2,7 @@
* Website: [https://www.vagrantup.com/](https://www.vagrantup.com/)
* Source: [https://github.com/mitchellh/vagrant](https://github.com/mitchellh/vagrant)
* IRC: `#vagrant` on Freenode
* [![Gitter chat](https://badges.gitter.im/mitchellh/vagrant.png)](https://gitter.im/mitchellh/vagrant)
* Mailing list: [Google Groups](https://groups.google.com/group/vagrant-up)
Vagrant is a tool for building and distributing development environments.
@ -45,11 +45,7 @@ To learn how to build a fully functional development environment, follow the
## Installing the Gem from Git
If you want the bleeding edge version of Vagrant, we try to keep master pretty stable
and you're welcome to give it a shot. The following is an example showing how to do this:
rake install
Ruby 2.0 is needed.
and you're welcome to give it a shot. Please review the installation page [here](https://www.vagrantup.com/docs/installation/source.html).
## Contributing to Vagrant
@ -66,11 +62,6 @@ like so:
bundle exec vagrant help
**NOTE:** By default running Vagrant via `bundle` will disable plugins.
This is necessary because Vagrant creates its own private Bundler context
(it does not respect your Gemfile), because it uses Bundler to manage plugin
dependencies.
### Acceptance Tests
Vagrant also comes with an acceptance test suite that does black-box

View File

@ -8,6 +8,11 @@ Signal.trap("INT") { abort }
# Split arguments by "--" if its there, we'll recombine them later
argv = ARGV.dup
argv_extra = []
# These will be the options that are passed to initialze the Vagrant
# environment.
opts = {}
if idx = argv.index("--")
argv_extra = argv.slice(idx+1, argv.length-2)
argv = argv.slice(0, idx)
@ -20,43 +25,26 @@ if argv.include?("-v") || argv.include?("--version")
exit 0
end
# This is kind of hacky, and I'd love to find a better way to do this, but
# if we're accessing the plugin interface, we want to NOT load plugins
# for this run, because they can actually interfere with the function
# of the plugin interface.
# Disable plugin loading for commands where plugins are not required. This will
# also disable loading of the Vagrantfile if it available as the environment
# is not required for these commands
argv.each_index do |i|
arg = argv[i]
if !arg.start_with?("-")
if arg == "plugin"
ENV["VAGRANT_NO_PLUGINS"] = "1"
ENV["VAGRANT_VAGRANTFILE"] = "plugin_command_#{Time.now.to_i}"
if ["plugin", "help"].include?(arg) || (arg == "box" && argv[i+1] == "list")
opts[:vagrantfile_name] = ""
ENV['VAGRANT_NO_PLUGINS'] = "1"
end
if arg == "help"
ENV["VAGRANT_VAGRANTFILE"] = "plugin_command_#{Time.now.to_i}"
end
if arg == "box" && argv[i+1] == "list"
ENV["VAGRANT_VAGRANTFILE"] = "plugin_command_#{Time.now.to_i}"
if arg == "plugin" && argv[i+1] != "list"
ENV['VAGRANT_DISABLE_PLUGIN_INIT'] = "1"
end
break
end
end
# First, make sure that we're executing using the proper Bundler context
# with our plugins. If we're not, then load that and reload Vagrant.
if !ENV["VAGRANT_INTERNAL_BUNDLERIZED"]
require "rbconfig"
ruby_path = File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["ruby_install_name"])
Kernel.exec(
ruby_path,
File.expand_path("../../lib/vagrant/pre-rubygems.rb", __FILE__),
*ARGV)
raise "Fatal error: this line should never be reached"
end
# Set logging level to `debug`. This is done before loading 'vagrant', as it
# sets up the logging system.
if argv.include?("--debug")
@ -64,37 +52,6 @@ if argv.include?("--debug")
ENV["VAGRANT_LOG"] = "debug"
end
# Setup our dependencies by initializing Bundler. If we're using plugins,
# then also initialize the paths to the plugins.
require "bundler"
begin
$vagrant_bundler_runtime = Bundler.setup(:default, :plugins)
rescue Bundler::GemNotFound
$stderr.puts "Bundler, the underlying system used to manage Vagrant plugins,"
$stderr.puts "is reporting that a plugin or its dependency can't be found."
$stderr.puts "This is usually caused by manual tampering with the 'plugins.json'"
$stderr.puts "file in the Vagrant home directory. To fix this error, please"
$stderr.puts "remove that file and reinstall all your plugins using `vagrant"
$stderr.puts "plugin install`."
rescue Bundler::VersionConflict => e
$stderr.puts "Vagrant experienced a version conflict with some installed plugins!"
$stderr.puts "This usually happens if you recently upgraded Vagrant. As part of the"
$stderr.puts "upgrade process, some existing plugins are no longer compatible with"
$stderr.puts "this version of Vagrant. The recommended way to fix this is to remove"
$stderr.puts "your existing plugins and reinstall them one-by-one. To remove all"
$stderr.puts "plugins:"
$stderr.puts ""
$stderr.puts " rm -r ~/.vagrant.d/plugins.json ~/.vagrant.d/gems"
$stderr.puts ""
$stderr.puts "Note if you have an alternate VAGRANT_HOME environmental variable"
$stderr.puts "set, the folders above will be in that directory rather than your"
$stderr.puts "user's home directory."
$stderr.puts ""
$stderr.puts "The error message is shown below:\n\n"
$stderr.puts e.message
exit 1
end
# Stdout/stderr should not buffer output
$stdout.sync = true
$stderr.sync = true
@ -114,10 +71,6 @@ begin
logger = Log4r::Logger.new("vagrant::bin::vagrant")
logger.info("`vagrant` invoked: #{ARGV.inspect}")
# These will be the options that are passed to initialze the Vagrant
# environment.
opts = {}
# Disable color in a few cases:
#
# * --no-color is anywhere in our arguments

View File

@ -1,12 +1,5 @@
require "vagrant/shared_helpers"
if Vagrant.plugins_enabled? && !defined?(Bundler)
puts "It appears that Vagrant was not properly loaded. Specifically,"
puts "the bundler context Vagrant requires was not setup. Please execute"
puts "vagrant using only the `vagrant` executable."
abort
end
require 'rubygems'
require 'log4r'
@ -72,11 +65,6 @@ global_logger.info("RubyGems version: #{Gem::VERSION}")
ENV.each do |k, v|
global_logger.info("#{k}=#{v.inspect}") if k =~ /^VAGRANT_/
end
global_logger.info("Plugins:")
Bundler.definition.specs_for([:plugins]).each do |spec|
global_logger.info(" - #{spec.name} = #{spec.version}")
end
# We need these components always so instead of an autoload we
# just require them explicitly here.
@ -254,6 +242,35 @@ if I18n.config.respond_to?(:enforce_available_locales=)
I18n.config.enforce_available_locales = true
end
# Setup the plugin manager and load any defined plugins
require_relative "vagrant/plugin/manager"
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
global_logger.info("Plugins:")
plugins.each do |plugin_name, plugin_info|
installed_version = plugin_info["installed_gem_version"]
version_constraint = plugin_info["gem_version"]
installed_version = 'undefined' if installed_version.to_s.empty?
version_constraint = '> 0' if version_constraint.to_s.empty?
global_logger.info(
" - #{plugin_name} = [installed: " \
"#{installed_version} constraint: " \
"#{version_constraint}]"
)
end
if Vagrant.plugins_init?
begin
Vagrant::Bundler.instance.init!(plugins)
rescue Exception => e
global_logger.error("Plugin initialization error - #{e.class}: #{e}")
e.backtrace.each do |backtrace_line|
global_logger.debug(backtrace_line)
end
raise Vagrant::Errors::PluginInitError, message: e.to_s
end
end
# A lambda that knows how to load plugins from a single directory.
plugin_load_proc = lambda do |directory|
# We only care about directories
@ -288,8 +305,40 @@ end
if Vagrant.plugins_enabled?
begin
global_logger.info("Loading plugins!")
$vagrant_bundler_runtime.require(:plugins)
plugins.each do |plugin_name, plugin_info|
if plugin_info["require"].to_s.empty?
begin
global_logger.debug("Loading plugin `#{plugin_name}` with default require: `#{plugin_name}`")
require plugin_name
rescue LoadError, Gem::LoadError => load_error
if plugin_name.include?("-")
begin
plugin_slash = plugin_name.gsub("-", "/")
global_logger.debug("Failed to load plugin `#{plugin_name}` with default require.")
global_logger.debug("Loading plugin `#{plugin_name}` with slash require: `#{plugin_slash}`")
require plugin_slash
rescue LoadError, Gem::LoadError
raise load_error
end
end
end
else
global_logger.debug("Loading plugin `#{plugin_name}` with custom require: `#{plugin_info["require"]}`")
require plugin_info["require"]
end
global_logger.debug("Successfully loaded plugin `#{plugin_name}`.")
end
if defined?(::Bundler)
global_logger.debug("Bundler detected in use. Loading `:plugins` group.")
::Bundler.require(:plugins)
end
rescue Exception => e
global_logger.error("Plugin loading error: #{e.class} - #{e}")
e.backtrace.each do |backtrace_line|
global_logger.debug(backtrace_line)
end
raise Vagrant::Errors::PluginLoadError, message: e.to_s
end
else
global_logger.debug("Plugin loading is currently disabled.")
end

View File

@ -4,7 +4,9 @@ require "set"
require "tempfile"
require "fileutils"
require "bundler"
require "rubygems/package"
require "rubygems/uninstaller"
require "rubygems/name_tuple"
require_relative "shared_helpers"
require_relative "version"
@ -15,83 +17,77 @@ module Vagrant
# Bundler as a way to properly resolve all dependencies of Vagrant and
# all Vagrant-installed plugins.
class Bundler
HASHICORP_GEMSTORE = 'https://gems.hashicorp.com'.freeze
def self.instance
@bundler ||= self.new
end
attr_reader :plugin_gem_path
def initialize
@enabled = true if ENV["VAGRANT_INSTALLER_ENV"] ||
ENV["VAGRANT_FORCE_BUNDLER"]
@enabled = !::Bundler::SharedHelpers.in_bundle? if !@enabled
@monitor = Monitor.new
@gem_home = ENV["GEM_HOME"]
@gem_path = ENV["GEM_PATH"]
# Set the Bundler UI to be a silent UI. We have to add the
# `silence` method to it because Bundler UI doesn't have it.
::Bundler.ui =
if ::Bundler::UI.const_defined? :Silent
# bundler >= 1.6.0, we use our custom UI
BundlerUI.new
else
# bundler < 1.6.0
::Bundler::UI.new
end
if !::Bundler.ui.respond_to?(:silence)
ui = ::Bundler.ui
def ui.silence(*args)
yield
end
end
@plugin_gem_path = Vagrant.user_data_path.join("gems", RUBY_VERSION).freeze
@logger = Log4r::Logger.new("vagrant::bundler")
end
# Initializes Bundler and the various gem paths so that we can begin
# loading gems. This must only be called once.
def init!(plugins)
# If we're not enabled, then we don't do anything.
return if !@enabled
def init!(plugins, repair=false)
# Add HashiCorp RubyGems source
Gem.sources << HASHICORP_GEMSTORE
bundle_path = Vagrant.user_data_path.join("gems")
# Setup the "local" Bundler configuration. We need to set BUNDLE_PATH
# because the existence of this actually suppresses `sudo`.
@appconfigpath = Dir.mktmpdir("vagrant-bundle-app-config")
File.open(File.join(@appconfigpath, "config"), "w+") do |f|
f.write("BUNDLE_PATH: \"#{bundle_path}\"")
# Generate dependencies for all registered plugins
plugin_deps = plugins.map do |name, info|
Gem::Dependency.new(name, info['gem_version'].to_s.empty? ? '> 0' : info['gem_version'])
end
# Setup the Bundler configuration
@configfile = tempfile("vagrant-configfile")
@configfile.close
@logger.debug("Current generated plugin dependency list: #{plugin_deps}")
# Build up the Gemfile for our Bundler context. We make sure to
# lock Vagrant to our current Vagrant version. In addition to that,
# we add all our plugin dependencies.
@gemfile = build_gemfile(plugins)
# Load dependencies into a request set for resolution
request_set = Gem::RequestSet.new(*plugin_deps)
# Never allow dependencies to be remotely satisfied during init
request_set.remote = false
Util::SafeEnv.change_env do |env|
# Set the environmental variables for Bundler
env["BUNDLE_APP_CONFIG"] = @appconfigpath
env["BUNDLE_CONFIG"] = @configfile.path
env["BUNDLE_GEMFILE"] = @gemfile.path
env["BUNDLE_RETRY"] = "3"
env["GEM_PATH"] =
"#{bundle_path}#{::File::PATH_SEPARATOR}#{@gem_path}"
repair_result = nil
begin
# Compose set for resolution
composed_set = generate_vagrant_set
# Resolve the request set to ensure proper activation order
solution = request_set.resolve(composed_set)
rescue Gem::UnsatisfiableDependencyError => failure
if repair
raise failure if @init_retried
@logger.debug("Resolution failed but attempting to repair. Failure: #{failure}")
install(plugins)
@init_retried = true
retry
else
raise
end
end
Gem.clear_paths
# Activate the gems
activate_solution(solution)
full_vagrant_spec_list = Gem::Specification.find_all{true} +
solution.map(&:full_spec)
if(defined?(::Bundler))
@logger.debug("Updating Bundler with full specification list")
::Bundler.rubygems.replace_entrypoints(full_vagrant_spec_list)
end
Gem.post_reset do
Gem::Specification.all = full_vagrant_spec_list
end
Gem::Specification.reset
end
# Removes any temporary files created by init
def deinit
# If we weren't enabled, then we don't do anything.
return if !@enabled
FileUtils.rm_rf(ENV["BUNDLE_APP_CONFIG"]) rescue nil
FileUtils.rm_f(ENV["BUNDLE_CONFIG"]) rescue nil
FileUtils.rm_f(ENV["BUNDLE_GEMFILE"]) rescue nil
FileUtils.rm_f(ENV["BUNDLE_GEMFILE"]+".lock") rescue nil
# no-op
end
# Installs the list of plugins.
@ -106,35 +102,17 @@ module Vagrant
#
# @param [String] path Path to a local gem file.
# @return [Gem::Specification]
def install_local(path)
# We have to do this load here because this file can be loaded
# before RubyGems is actually loaded.
require "rubygems/dependency_installer"
begin
require "rubygems/format"
rescue LoadError
# rubygems 2.x
end
# If we're installing from a gem file, determine the name
# based on the spec in the file.
pkg = if defined?(Gem::Format)
# RubyGems 1.x
Gem::Format.from_file_by_path(path)
else
# RubyGems 2.x
Gem::Package.new(path)
end
# Install the gem manually. If the gem exists locally, then
# Bundler shouldn't attempt to get it remotely.
with_isolated_gem do
installer = Gem::DependencyInstaller.new(
document: [], prerelease: false)
installer.install(path, "= #{pkg.spec.version}")
end
pkg.spec
def install_local(path, opts={})
plugin_source = Gem::Source::SpecificFile.new(path)
plugin_info = {
plugin_source.spec.name => {
"local_source" => plugin_source,
"sources" => opts.fetch(:sources, Gem.sources.map(&:to_s))
}
}
@logger.debug("Installing local plugin - #{plugin_info}")
internal_install(plugin_info, {})
plugin_source.spec
end
# Update updates the given plugins, or every plugin if none is given.
@ -144,277 +122,319 @@ module Vagrant
# empty or nil, all plugins will be updated.
def update(plugins, specific)
specific ||= []
update = true
update = { gems: specific } if !specific.empty?
update = {gems: specific.empty? ? true : specific}
internal_install(plugins, update)
end
# Clean removes any unused gems.
def clean(plugins)
gemfile = build_gemfile(plugins)
lockfile = "#{gemfile.path}.lock"
definition = ::Bundler::Definition.build(gemfile, lockfile, nil)
root = File.dirname(gemfile.path)
@logger.debug("Cleaning Vagrant plugins of stale gems.")
# Generate dependencies for all registered plugins
plugin_deps = plugins.map do |name, info|
gem_version = info['installed_gem_version']
gem_version = info['gem_version'] if gem_version.to_s.empty?
gem_version = "> 0" if gem_version.to_s.empty?
Gem::Dependency.new(name, gem_version)
end
with_isolated_gem do
runtime = ::Bundler::Runtime.new(root, definition)
runtime.clean
@logger.debug("Current plugin dependency list: #{plugin_deps}")
# Load dependencies into a request set for resolution
request_set = Gem::RequestSet.new(*plugin_deps)
# Never allow dependencies to be remotely satisfied during cleaning
request_set.remote = false
# Sets that we can resolve our dependencies from. Note that we only
# resolve from the current set as all required deps are activated during
# init.
current_set = generate_vagrant_set
# Collect all plugin specifications
plugin_specs = Dir.glob(plugin_gem_path.join('specifications/*.gemspec').to_s).map do |spec_path|
Gem::Specification.load(spec_path)
end
@logger.debug("Generating current plugin state solution set.")
# Resolve the request set to ensure proper activation order
solution = request_set.resolve(current_set)
solution_specs = solution.map(&:full_spec)
solution_full_names = solution_specs.map(&:full_name)
# Find all specs installed to plugins directory that are not
# found within the solution set
plugin_specs.delete_if do |spec|
solution_full_names.include?(spec.full_name)
end
@logger.debug("Specifications to be removed - #{plugin_specs.map(&:full_name)}")
# Now delete all unused specs
plugin_specs.each do |spec|
@logger.debug("Uninstalling gem - #{spec.full_name}")
Gem::Uninstaller.new(spec.name,
version: spec.version,
install_dir: plugin_gem_path,
all: true,
executables: true,
force: true,
ignore: true,
).uninstall_gem(spec)
end
solution.find_all do |spec|
plugins.keys.include?(spec.name)
end
end
# During the duration of the yielded block, Bundler loud output
# is enabled.
def verbose
@monitor.synchronize do
begin
old_ui = ::Bundler.ui
require 'bundler/vendored_thor'
::Bundler.ui = ::Bundler::UI::Shell.new
yield
ensure
::Bundler.ui = old_ui
end
if block_given?
initial_state = @verbose
@verbose = true
yield
@verbose = initial_state
else
@verbose = true
end
end
protected
# Builds a valid Gemfile for use with Bundler given the list of
# plugins.
#
# @return [Tempfile]
def build_gemfile(plugins)
sources = plugins.values.map { |p| p["sources"] }.flatten.compact.uniq
def internal_install(plugins, update, **extra)
# Only allow defined Gem sources
Gem.sources.clear
f = tempfile("vagrant-gemfile")
f.tap do |gemfile|
sources.each do |source|
next if source == ""
gemfile.puts(%Q[source "#{source}"])
update = {} if !update.is_a?(Hash)
skips = []
installer_set = Gem::Resolver::InstallerSet.new(:both)
# Generate all required plugin deps
plugin_deps = plugins.map do |name, info|
if update[:gems] == true || (update[:gems].respond_to?(:include?) && update[:gems].include?(name))
gem_version = '> 0'
skips << name
else
gem_version = info['gem_version'].to_s.empty? ? '> 0' : info['gem_version']
end
if plugin_source = info.delete("local_source")
installer_set.add_local(plugin_source.spec.name, plugin_source.spec, plugin_source)
end
Array(info["sources"]).each do |source|
if !Gem.sources.include?(source)
@logger.debug("Adding RubyGems source for plugin install: #{source}")
Gem.sources << source
end
end
Gem::Dependency.new(name, gem_version)
end
gemfile.puts(%Q[gem "vagrant", "= #{VERSION}"])
@logger.debug("Dependency list for installation: #{plugin_deps}")
gemfile.puts("group :plugins do")
plugins.each do |name, plugin|
version = plugin["gem_version"]
version = nil if version == ""
# Create the request set for the new plugins
request_set = Gem::RequestSet.new(*plugin_deps)
opts = {}
if plugin["require"] && plugin["require"] != ""
opts[:require] = plugin["require"]
installer_set = Gem::Resolver.compose_sets(
installer_set,
generate_builtin_set,
generate_plugin_set(skips)
)
@logger.debug("Generating solution set for installation.")
# Generate the required solution set for new plugins
solution = request_set.resolve(installer_set)
activate_solution(solution)
@logger.debug("Installing required gems.")
# Install all remote gems into plugin path. Set the installer to ignore dependencies
# as we know the dependencies are satisfied and it will attempt to validate a gem's
# dependencies are satisified by gems in the install directory (which will likely not
# be true)
result = request_set.install_into(plugin_gem_path.to_s, true, ignore_dependencies: true)
result = result.map(&:full_spec)
result
end
# Generate the composite resolver set totally all of vagrant (builtin + plugin set)
def generate_vagrant_set
Gem::Resolver.compose_sets(generate_builtin_set, generate_plugin_set)
end
# @return [Array<[Gem::Specification, String]>] spec and directory pairs
def vagrant_internal_specs
list = {}
directories = [Gem::Specification.default_specifications_dir]
Gem::Specification.find_all{true}.each do |spec|
list[spec.full_name] = spec
end
if(!defined?(::Bundler))
directories += Gem::Specification.dirs.find_all do |path|
!path.start_with?(Gem.user_dir)
end
end
Gem::Specification.each_spec(directories) do |spec|
if !list[spec.full_name]
list[spec.full_name] = spec
end
end
list.values
end
# Generate the builtin resolver set
def generate_builtin_set
builtin_set = BuiltinSet.new
@logger.debug("Generating new builtin set instance.")
vagrant_internal_specs.each do |spec|
builtin_set.add_builtin_spec(spec)
end
builtin_set
end
# Generate the plugin resolver set. Optionally provide specification names (short or
# full) that should be ignored
def generate_plugin_set(skip=[])
plugin_set = PluginSet.new
@logger.debug("Generating new plugin set instance. Skip gems - #{skip}")
Dir.glob(plugin_gem_path.join('specifications/*.gemspec').to_s).each do |spec_path|
spec = Gem::Specification.load(spec_path)
desired_spec_path = File.join(spec.gem_dir, "#{spec.name}.gemspec")
# Vendor set requires the spec to be within the gem directory. Some gems will package their
# spec file, and that's not what we want to load.
if !File.exist?(desired_spec_path) || !FileUtils.cmp(spec.spec_file, desired_spec_path)
File.write(desired_spec_path, spec.to_ruby)
end
next if skip.include?(spec.name) || skip.include?(spec.full_name)
plugin_set.add_vendor_gem(spec.name, spec.gem_dir)
end
plugin_set
end
# Activate a given solution
def activate_solution(solution)
retried = false
begin
@logger.debug("Activating solution set: #{solution.map(&:full_name)}")
solution.each do |activation_request|
unless activation_request.full_spec.activated?
@logger.debug("Activating gem #{activation_request.full_spec.full_name}")
activation_request.full_spec.activate
if(defined?(::Bundler))
@logger.debug("Marking gem #{activation_request.full_spec.full_name} loaded within Bundler.")
::Bundler.rubygems.mark_loaded activation_request.full_spec
end
end
end
rescue Gem::LoadError => e
# Depending on the version of Ruby, the ordering of the solution set
# will be either 0..n (molinillo) or n..0 (pre-molinillo). Instead of
# attempting to determine what's in use, or if it has some how changed
# again, just reverse order on failure and attempt again.
if retried
@logger.error("Failed to load solution set - #{e.class}: #{e}")
matcher = e.message.match(/Could not find '(?<gem_name>[^']+)'/)
if matcher && !matcher["gem_name"].empty?
desired_activation_request = solution.detect do |request|
request.name == matcher["gem_name"]
end
if desired_activation_request && !desired_activation_request.full_spec.activated?
activation_request = desired_activation_request
@logger.warn("Found misordered activation request for #{desired_activation_request.full_name}. Moving to solution HEAD.")
solution.delete(desired_activation_request)
solution.unshift(desired_activation_request)
retry
end
end
gemfile.puts(%Q[gem "#{name}", #{version.inspect}, #{opts.inspect}])
raise
else
@logger.debug("Failed to load solution set. Retrying with reverse order.")
retried = true
solution.reverse!
retry
end
gemfile.puts("end")
gemfile.close
end
end
# This installs a set of plugins and optionally updates those gems.
#
# @param [Hash] plugins
# @param [Hash, Boolean] update If true, updates all plugins, otherwise
# can be a hash of options. See Bundler.definition.
# @return [Array<Gem::Specification>]
def internal_install(plugins, update, **extra)
gemfile = build_gemfile(plugins)
lockfile = "#{gemfile.path}.lock"
definition = ::Bundler::Definition.build(gemfile, lockfile, update)
root = File.dirname(gemfile.path)
opts = {}
opts["local"] = true if extra[:local]
with_isolated_gem do
::Bundler::Installer.install(root, definition, opts)
end
# TODO(mitchellh): clean gems here... for some reason when I put
# it in on install, we get a GemNotFound exception. Gotta investigate.
definition.specs
rescue ::Bundler::VersionConflict => e
raise Errors::PluginInstallVersionConflict,
conflicts: e.to_s.gsub("Bundler", "Vagrant")
rescue ::Bundler::BundlerError => e
if !::Bundler.ui.is_a?(BundlerUI)
raise
end
# Add the warn/error level output from Bundler if we have any
message = "#{e.message}"
if ::Bundler.ui.output != ""
message += "\n\n#{::Bundler.ui.output}"
end
raise ::Bundler::BundlerError, message
end
def with_isolated_gem
raise Errors::BundlerDisabled if !@enabled
tmp_gemfile = tempfile("vagrant-gemfile")
tmp_gemfile.close
# Remove bundler settings so that Bundler isn't loaded when building
# native extensions because it causes all sorts of problems.
old_rubyopt = ENV["RUBYOPT"]
old_gemfile = ENV["BUNDLE_GEMFILE"]
ENV["BUNDLE_GEMFILE"] = tmp_gemfile.path
ENV["RUBYOPT"] = (ENV["RUBYOPT"] || "").gsub(/-rbundler\/setup\s*/, "")
# Set the GEM_HOME so gems are installed only to our local gem dir
ENV["GEM_HOME"] = Vagrant.user_data_path.join("gems").to_s
# Clear paths so that it reads the new GEM_HOME setting
Gem.paths = ENV
# Reset the all specs override that Bundler does
old_all = Gem::Specification._all
# WARNING: Seriously don't touch this without reading the comment attached
# to the monkey-patch at the bottom of this file.
Gem::Specification.vagrant_reset!
# /etc/gemrc and so on.
old_config = nil
begin
old_config = Gem.configuration
rescue Psych::SyntaxError
# Just ignore this. This means that the ".gemrc" file has
# an invalid syntax and can't be loaded. We don't care, because
# when we set Gem.configuration to nil later, it'll force a reload
# if it is needed.
end
Gem.configuration = NilGemConfig.new
# Use a silent UI so that we have no output
Gem::DefaultUserInteraction.use_ui(Gem::SilentUI.new) do
return yield
end
ensure
tmp_gemfile.unlink rescue nil
ENV["BUNDLE_GEMFILE"] = old_gemfile
ENV["GEM_HOME"] = @gem_home
ENV["RUBYOPT"] = old_rubyopt
Gem.configuration = old_config
Gem.paths = ENV
Gem::Specification.all = old_all
end
# This method returns a proper "tempfile" on disk. Ruby's Tempfile class
# would work really great for this, except GC can come along and remove
# the file before we are done with it. This is because we "close" the file,
# but we might be shelling out to a subprocess.
#
# @return [File]
def tempfile(name)
path = Dir::Tmpname.create(name) {}
return File.open(path, "w+")
end
# This is pretty hacky but it is a custom implementation of
# Gem::ConfigFile so that we don't load any gemrc files.
class NilGemConfig < Gem::ConfigFile
# This is a custom Gem::Resolver::Set for use with vagrant "system" gems. It
# allows the installed set of gems to be used for providing a solution while
# enforcing strict constraints. This ensures that plugins cannot "upgrade"
# gems that are builtin to vagrant itself.
class BuiltinSet < Gem::Resolver::Set
def initialize
# We _can not_ `super` here because that can really mess up
# some other configuration state. We need to just set everything
# directly.
super
@remote = false
@specs = []
end
@api_keys = {}
@args = []
@backtrace = false
@bulk_threshold = 1000
@hash = {}
@update_sources = true
@verbose = true
def add_builtin_spec(spec)
@specs.push(spec).uniq!
end
def find_all(req)
@specs.select do |spec|
req.match?(spec)
end.map do |spec|
Gem::Resolver::InstalledSpecification.new(self, spec)
end
end
end
# This monkey patches Gem::Specification from RubyGems to add a new method,
# `vagrant_reset!`. For some background, Vagrant needs to set the value
# of these variables to nil to force new specs to be loaded. Previously,
# this was accomplished by setting Gem::Specification.specs = nil. However,
# newer versions of Rubygems try to map across that nil using a group_by
# clause, breaking things.
#
# This generally never affected Vagrant users who were using the official
# Vagrant installers because we lock to an older version of Rubygems that
# does not have this issue. The users of the official debian packages,
# however, experienced this issue because they float on Rubygems.
#
# In GH-7073, a number of Debian users reported this issue, but it was not
# reproducible in the official installer for reasons described above. Commit
# ba77d4b switched to using Gem::Specification.reset, but this actually
# broke the ability to install gems locally (GH-7493) because it resets
# the complete local cache, which is already built.
#
# The only solution that works with both new and old versions of Rubygems
# is to provide our own function for JUST resetting all the stubs. Both
# @@all and @@stubs must be set to a falsey value, so some of the
# originally-suggested solutions of using an empty array do not work. Only
# setting these values to nil (without clearing the cache), allows Vagrant
# to install and manage plugins.
class Gem::Specification < Gem::BasicSpecification
def self.vagrant_reset!
@@all = @@stubs = nil
# This is a custom Gem::Resolver::Set for use with Vagrant plugins. It is
# a modified Gem::Resolver::VendorSet that supports multiple versions of
# a specific gem
class PluginSet < Gem::Resolver::VendorSet
##
# Adds a specification to the set with the given +name+ which has been
# unpacked into the given +directory+.
def add_vendor_gem(name, directory)
gemspec = File.join(directory, "#{name}.gemspec")
spec = Gem::Specification.load(gemspec)
if !spec
raise Gem::GemNotFoundException,
"unable to find #{gemspec} for gem #{name}"
end
spec.full_gem_path = File.expand_path(directory)
@specs[spec.name] ||= []
@specs[spec.name] << spec
@directories[spec] = directory
spec
end
end
if ::Bundler::UI.const_defined? :Silent
class BundlerUI < ::Bundler::UI::Silent
attr_reader :output
def initialize
@output = ""
##
# Returns an Array of VendorSpecification objects matching the
# DependencyRequest +req+.
def find_all(req)
@specs.values.flatten.select do |spec|
req.match?(spec)
end.map do |spec|
source = Gem::Source::Vendor.new(@directories[spec])
Gem::Resolver::VendorSpecification.new(self, spec, source)
end
end
def info(message, newline = nil)
end
def confirm(message, newline = nil)
end
def warn(message, newline = nil)
@output += message
@output += "\n" if newline
end
def error(message, newline = nil)
@output += message
@output += "\n" if newline
end
def debug(message, newline = nil)
end
def debug?
false
end
def quiet?
false
end
def ask(message)
end
def level=(name)
end
def level(name = nil)
"info"
end
def trace(message, newline = nil)
end
def silence
yield
end
##
# Loads a spec with the given +name+. +version+, +platform+ and +source+ are
# ignored.
def load_spec (name, version, platform, source)
version = Gem::Version.new(version) if !version.is_a?(Gem::Version)
@specs.fetch(name, []).detect{|s| s.name == name && s.version = version}
end
end
end
end
# Patch for Ruby 2.2 and Bundler to behave properly when uninstalling plugins
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
if defined?(::Bundler) && !::Bundler::SpecSet.instance_methods.include?(:delete)
class Gem::Specification
def self.remove_spec(spec)
Gem::Specification.reset
end
end
end

View File

@ -338,7 +338,7 @@ module Vagrant
# a priority to each in the order they exist so that we try these first.
config = {}
root_config.vm.__providers.reverse.each_with_index do |key, idx|
config[key] = idx
config[key] = idx + 1
end
# Determine the max priority so that we can add the config priority

View File

@ -340,6 +340,10 @@ module Vagrant
error_key(:downloader_interrupted)
end
class DownloaderChecksumError < VagrantError
error_key(:downloader_checksum_error)
end
class EnvInval < VagrantError
error_key(:env_inval)
end
@ -456,6 +460,10 @@ module Vagrant
error_key(:nfs_bad_exports)
end
class NFSExportsFailed < VagrantError
error_key(:nfs_exports_failed)
end
class NFSCantReadExports < VagrantError
error_key(:nfs_cant_read_exports)
end
@ -580,6 +588,10 @@ module Vagrant
error_key(:plugin_uninstall_system)
end
class PluginInitError < VagrantError
error_key(:plugin_init_error)
end
class PushesNotDefined < VagrantError
error_key(:pushes_not_defined)
end
@ -608,6 +620,10 @@ module Vagrant
error_key(:rsync_not_installed_in_guest)
end
class RSyncGuestInstallError < VagrantError
error_key(:rsync_guest_install_error)
end
class SCPPermissionDenied < VagrantError
error_key(:scp_permission_denied)
end

View File

@ -44,10 +44,9 @@ module Vagrant
local = false
if name =~ /\.gem$/
# If this is a gem file, then we install that gem locally.
local_spec = Vagrant::Bundler.instance.install_local(name)
local_spec = Vagrant::Bundler.instance.install_local(name, opts)
name = local_spec.name
opts[:version] = local_spec.version.to_s
local = true
end
plugins = installed_plugins
@ -57,33 +56,41 @@ module Vagrant
"sources" => opts[:sources],
}
result = nil
install_lambda = lambda do
Vagrant::Bundler.instance.install(plugins, local).each do |spec|
next if spec.name != name
next if result && result.version >= spec.version
result = spec
if local_spec.nil?
result = nil
install_lambda = lambda do
Vagrant::Bundler.instance.install(plugins, local).each do |spec|
next if spec.name != name
next if result && result.version >= spec.version
result = spec
end
end
end
if opts[:verbose]
Vagrant::Bundler.instance.verbose(&install_lambda)
if opts[:verbose]
Vagrant::Bundler.instance.verbose(&install_lambda)
else
install_lambda.call
end
else
install_lambda.call
result = local_spec
end
# Add the plugin to the state file
@user_file.add_plugin(
result.name,
version: opts[:version],
require: opts[:require],
sources: opts[:sources],
installed_gem_version: result.version.to_s
)
# After install clean plugin gems to remove any cruft. This is useful
# for removing outdated dependencies or other versions of an installed
# plugin if the plugin is upgraded/downgraded
Vagrant::Bundler.instance.clean(installed_plugins)
result
rescue ::Bundler::GemNotFound
rescue Gem::GemNotFoundException
raise Errors::PluginGemNotFound, name: name
rescue ::Bundler::BundlerError => e
rescue Gem::Exception => e
raise Errors::BundlerError, message: e.to_s
end
@ -102,14 +109,30 @@ module Vagrant
# Clean the environment, removing any old plugins
Vagrant::Bundler.instance.clean(installed_plugins)
rescue ::Bundler::BundlerError => e
rescue Gem::Exception => e
raise Errors::BundlerError, message: e.to_s
end
# Updates all or a specific set of plugins.
def update_plugins(specific)
Vagrant::Bundler.instance.update(installed_plugins, specific)
rescue ::Bundler::BundlerError => e
result = Vagrant::Bundler.instance.update(installed_plugins, specific)
installed_plugins.each do |name, info|
matching_spec = result.detect{|s| s.name == name}
info = Hash[
info.map do |key, value|
[key.to_sym, value]
end
]
if matching_spec
@user_file.add_plugin(name, **info.merge(
version: "> 0",
installed_gem_version: matching_spec.version.to_s
))
end
end
Vagrant::Bundler.instance.clean(installed_plugins)
result
rescue Gem::Exception => e
raise Errors::BundlerError, message: e.to_s
end
@ -123,8 +146,14 @@ module Vagrant
system[k] = v.merge("system" => true)
end
end
plugin_list = system.merge(@user_file.installed_plugins)
system.merge(@user_file.installed_plugins)
# Sort plugins by name
Hash[
plugin_list.map{|plugin_name, plugin_info|
[plugin_name, plugin_info]
}.sort_by(&:first)
]
end
# This returns the list of plugins that are installed as

View File

@ -31,11 +31,12 @@ module Vagrant
# @param [String] name The name of the plugin
def add_plugin(name, **opts)
@data["installed"][name] = {
"ruby_version" => RUBY_VERSION,
"vagrant_version" => Vagrant::VERSION,
"gem_version" => opts[:version] || "",
"require" => opts[:require] || "",
"sources" => opts[:sources] || [],
"ruby_version" => RUBY_VERSION,
"vagrant_version" => Vagrant::VERSION,
"gem_version" => opts[:version] || "",
"require" => opts[:require] || "",
"sources" => opts[:sources] || [],
"installed_gem_version" => opts[:installed_gem_version]
}
save!

View File

@ -1,34 +0,0 @@
# This file is to be loaded _before_ any RubyGems are loaded. This file
# initializes the Bundler context so that Vagrant and its associated plugins
# can load properly, and then execs out into Vagrant again.
require_relative "shared_helpers"
if defined?(Bundler)
require "bundler/shared_helpers"
if Bundler::SharedHelpers.in_bundle? && !Vagrant.very_quiet?
puts "Vagrant appears to be running in a Bundler environment. Your "
puts "existing Gemfile will be used. Vagrant will not auto-load any plugins"
puts "installed with `vagrant plugin`. Vagrant will autoload any plugins in"
puts "the 'plugins' group in your Gemfile. You can force Vagrant to take over"
puts "with VAGRANT_FORCE_BUNDLER."
puts
end
end
require_relative "bundler"
require_relative "plugin/manager"
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
Vagrant::Bundler.instance.init!(plugins)
ENV["VAGRANT_INTERNAL_BUNDLERIZED"] = "1"
# If the VAGRANT_EXECUTABLE env is set, then we use that to point to a
# Ruby file to directly execute. Otherwise, we just depend on PATH lookup.
# This minor optimization can save hundreds of milliseconds on Windows.
if ENV["VAGRANT_EXECUTABLE"]
Kernel.exec("ruby", ENV["VAGRANT_EXECUTABLE"], *ARGV)
else
Kernel.exec("vagrant", *ARGV)
end

View File

@ -38,11 +38,18 @@ module Vagrant
ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"]
end
# Should the plugin system be initialized
#
# @return [Boolean]
def self.plugins_init?
!ENV['VAGRANT_DISABLE_PLUGIN_INIT']
end
# This returns whether or not 3rd party plugins should and can be loaded.
#
# @return [Boolean]
def self.plugins_enabled?
!ENV["VAGRANT_NO_PLUGINS"] && $vagrant_bundler_runtime
!ENV["VAGRANT_NO_PLUGINS"]
end
# Whether or not super quiet mode is enabled. This is ill-advised.

View File

@ -9,6 +9,7 @@ module Vagrant
autoload :SafeExec, 'vagrant/util/safe_exec'
autoload :StackedProcRunner, 'vagrant/util/stacked_proc_runner'
autoload :TemplateRenderer, 'vagrant/util/template_renderer'
autoload :StringBlockEditor, 'vagrant/util/string_block_editor'
autoload :Subprocess, 'vagrant/util/subprocess'
end
end

View File

@ -1,7 +1,8 @@
require "uri"
require "log4r"
require "digest/md5"
require "digest/sha1"
require "vagrant/util/busy"
require "vagrant/util/platform"
require "vagrant/util/subprocess"
@ -18,6 +19,12 @@ module Vagrant
# Vagrant/1.7.4 (+https://www.vagrantup.com; ruby2.1.0)
USER_AGENT = "Vagrant/#{VERSION} (+https://www.vagrantup.com; #{RUBY_ENGINE}#{RUBY_VERSION})".freeze
# Supported file checksum
CHECKSUM_MAP = {
:md5 => Digest::MD5,
:sha1 => Digest::SHA1
}.freeze
attr_reader :source
attr_reader :destination
@ -52,6 +59,10 @@ module Vagrant
@ui = options[:ui]
@client_cert = options[:client_cert]
@location_trusted = options[:location_trusted]
@checksums = {
:md5 => options[:md5],
:sha1 => options[:sha1]
}
end
# This executes the actual download, downloading the source file
@ -161,6 +172,8 @@ module Vagrant
end
end
validate_download!(@source, @destination, @checksums)
# Everything succeeded
true
end
@ -178,6 +191,46 @@ module Vagrant
protected
# Apply any checksum validations based on provided
# options content
#
# @param source [String] Source of file
# @param path [String, Pathname] local file path
# @param checksums [Hash] User provided options
# @option checksums [String] :md5 Compare MD5 checksum
# @option checksums [String] :sha1 Compare SHA1 checksum
# @return [Boolean]
def validate_download!(source, path, checksums)
CHECKSUM_MAP.each do |type, klass|
if checksums[type]
result = checksum_file(klass, path)
@logger.debug("Validating checksum (#{type}) for #{source}. " \
"expected: #{checksums[type]} actual: #{result}")
if checksums[type] != result
raise Errors::DownloaderChecksumError.new(
source: source,
path: path,
type: type,
expected_checksum: checksums[type],
actual_checksum: result
)
end
end
end
true
end
# Generate checksum on given file
#
# @param digest_class [Class] Digest class to use for generating checksum
# @param path [String, Pathname] Path to file
# @return [String] hexdigest result
def checksum_file(digest_class, path)
digester = digest_class.new
digester.file(path)
digester.hexdigest
end
def execute_curl(options, subprocess_options, &data_proc)
options = options.dup
options << subprocess_options

View File

@ -1,11 +1,11 @@
require "bundler"
module Vagrant
module Util
class Env
def self.with_original_env
original_env = ENV.to_hash
ENV.replace(::Bundler::ORIGINAL_ENV) if defined?(::Bundler::ORIGINAL_ENV)
if defined?(::Bundler) && defined?(::Bundler::ORIGINAL_ENV)
ENV.replace(::Bundler::ORIGINAL_ENV)
end
ENV.update(Vagrant.original_env)
yield
ensure
@ -37,7 +37,9 @@ module Vagrant
# the block to execute with the cleaned environment
def self.with_clean_env
with_original_env do
ENV["MANPATH"] = ENV["BUNDLE_ORIG_MANPATH"]
if ENV["BUNDLE_ORIG_MANPATH"]
ENV["MANPATH"] = ENV["BUNDLE_ORIG_MANPATH"]
end
ENV.delete_if { |k,_| k[0,7] == "BUNDLE_" }
if ENV.has_key? "RUBYOPT"
ENV["RUBYOPT"] = ENV["RUBYOPT"].sub("-rbundler/setup", "")

View File

@ -7,6 +7,9 @@ module Vagrant
# thread. In that case, `safe_exec` automatically falls back to
# forking.
class SafeExec
@@logger = Log4r::Logger.new("vagrant::util::safe_exec")
def self.exec(command, *args)
# Create a list of things to rescue from. Since this is OS
# specific, we need to do some defined? checks here to make
@ -18,10 +21,27 @@ module Vagrant
fork_instead = false
begin
pid = nil
pid = fork if fork_instead
Kernel.exec(command, *args) if pid.nil?
Process.wait(pid) if pid
if fork_instead
if Vagrant::Util::Platform.windows?
@@logger.debug("Using subprocess because windows platform")
args = args.dup << {notify: [:stdout, :stderr]}
result = Vagrant::Util::Subprocess.execute(command, *args) do |type, data|
case type
when :stdout
@@logger.info(data, new_line: false)
when :stderr
@@logger.info(data, new_line: false)
end
end
Kernel.exit(result.exit_code)
else
pid = fork
Kernel.exec(command, *args)
Process.wait(pid)
end
else
Kernel.exec(command, *args)
end
rescue *rescue_from
# We retried already, raise the issue and be done
raise if fork_instead

View File

@ -144,10 +144,11 @@ module Vagrant
# Record the start time for timeout purposes
start_time = Time.now.to_i
open_readers = [stdout, stderr]
open_writers = notify_stdin ? [process.io.stdin] : []
@logger.debug("Selecting on IO")
while true
writers = notify_stdin ? [process.io.stdin] : []
results = ::IO.select([stdout, stderr], writers, nil, 0.1)
results = ::IO.select(open_readers, open_writers, nil, 0.1)
results ||= []
readers = results[0]
writers = results[1]
@ -178,8 +179,14 @@ module Vagrant
break if process.exited?
# Check the writers to see if they're ready, and notify any listeners
if writers && !writers.empty?
yield :stdin, process.io.stdin if block_given?
if writers && !writers.empty? && block_given?
yield :stdin, process.io.stdin
# if the callback closed stdin, we should remove it, because
# IO.select() will throw if called with a closed io.
if process.io.stdin.closed?
open_writers = []
end
end
end
@ -290,7 +297,9 @@ module Vagrant
def jailbreak(env = {})
return if ENV.key?("VAGRANT_SKIP_SUBPROCESS_JAILBREAK")
env.replace(::Bundler::ORIGINAL_ENV) if defined?(::Bundler::ORIGINAL_ENV)
if defined?(::Bundler) && defined?(::Bundler::ORIGINAL_ENV)
env.replace(::Bundler::ORIGINAL_ENV)
end
env.merge!(Vagrant.original_env)
# Bundler does this, so I guess we should as well, since I think it

View File

@ -0,0 +1,128 @@
require 'optparse'
module VagrantPlugins
module CommandBox
module Command
class Prune < Vagrant.plugin("2", :command)
def execute
options = {}
options[:force] = false
options[:dry_run] = false
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant box prune [options]"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-p PROVIDER", "--provider PROVIDER", String, "The specific provider type for the boxes to destroy.") do |p|
options[:provider] = p
end
o.on("-n", "--dry-run", "Only print the boxes that would be removed.") do |f|
options[:dry_run] = f
end
o.on("--name NAME", String, "The specific box name to check for outdated versions.") do |name|
options[:name] = name
end
o.on("-f", "--force", "Destroy without confirmation even when box is in use.") do |f|
options[:force] = f
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
boxes = @env.boxes.all.sort
if boxes.empty?
return @env.ui.warn(I18n.t("vagrant.commands.box.no_installed_boxes"), prefix: false)
end
delete_oldest_boxes(boxes, options[:provider], options[:force], options[:name], options[:dry_run])
# Success, exit status 0
0
end
private
def delete_oldest_boxes(boxes, only_provider, skip_confirm, only_name, dry_run)
# Find the longest box name
longest_box = boxes.max_by { |x| x[0].length }
longest_box_length = longest_box[0].length
# Hash map to keep track of newest versions
newest_boxes = Hash.new
# First find the newest version for every installed box
boxes.each do |name, version, provider|
next if only_provider and only_provider != provider.to_s
next if only_name and only_name != name
# Nested to make sure it works for boxes with different providers
if newest_boxes.has_key?(name)
if newest_boxes[name].has_key?(provider)
saved = Gem::Version.new(newest_boxes[name][provider])
current = Gem::Version.new(version)
if current > saved
newest_boxes[name][provider] = version
end
else
newest_boxes[name][provider] = version
end
else
newest_boxes[name] = Hash.new
newest_boxes[name][provider] = version
end
end
@env.ui.info("The following boxes will be kept...");
newest_boxes.each do |name, providers|
providers.each do |provider, version|
@env.ui.info("#{name.ljust(longest_box_length)} (#{provider}, #{version})")
@env.ui.machine("box-name", name)
@env.ui.machine("box-provider", provider)
@env.ui.machine("box-version", version)
end
end
@env.ui.info("", prefix: false)
@env.ui.info("Checking for older boxes...");
# Track if we removed anything so the user can be informed
removed_any_box = false
boxes.each do |name, version, provider|
next if !newest_boxes.has_key?(name) or !newest_boxes[name].has_key?(provider)
current = Gem::Version.new(version)
saved = Gem::Version.new(newest_boxes[name][provider])
if current < saved
removed_any_box = true
# Use the remove box action
if dry_run
@env.ui.info("Would remove #{name} #{provider} #{version}")
else
@env.action_runner.run(Vagrant::Action.action_box_remove, {
box_name: name,
box_provider: provider,
box_version: version,
force_confirm_box_remove: skip_confirm,
box_remove_all_versions: false,
})
end
end
end
if !removed_any_box
@env.ui.info("No old versions of boxes to remove...");
end
end
end
end
end
end

View File

@ -34,6 +34,11 @@ module VagrantPlugins
Remove
end
@subcommands.register(:prune) do
require_relative "prune"
Prune
end
@subcommands.register(:repackage) do
require File.expand_path("../repackage", __FILE__)
Repackage

View File

@ -6,6 +6,12 @@ module VagrantPlugins
module CommandPlugin
module Action
# This middleware sequence will install a plugin.
def self.action_expunge
Vagrant::Action::Builder.new.tap do |b|
b.use ExpungePlugins
end
end
def self.action_install
Vagrant::Action::Builder.new.tap do |b|
b.use InstallGem
@ -27,6 +33,13 @@ module VagrantPlugins
end
end
# This middleware sequence will repair installed plugins.
def self.action_repair
Vagrant::Action::Builder.new.tap do |b|
b.use RepairPlugins
end
end
# This middleware sequence will uninstall a plugin.
def self.action_uninstall
Vagrant::Action::Builder.new.tap do |b|
@ -44,10 +57,12 @@ module VagrantPlugins
# The autoload farm
action_root = Pathname.new(File.expand_path("../action", __FILE__))
autoload :ExpungePlugins, action_root.join("expunge_plugins")
autoload :InstallGem, action_root.join("install_gem")
autoload :LicensePlugin, action_root.join("license_plugin")
autoload :ListPlugins, action_root.join("list_plugins")
autoload :PluginExistsCheck, action_root.join("plugin_exists_check")
autoload :RepairPlugins, action_root.join("repair_plugins")
autoload :UninstallPlugin, action_root.join("uninstall_plugin")
autoload :UpdateGems, action_root.join("update_gems")
end

View File

@ -0,0 +1,51 @@
require "vagrant/plugin/manager"
module VagrantPlugins
module CommandPlugin
module Action
# This middleware removes user installed plugins by
# removing:
# * ~/.vagrant.d/plugins.json
# * ~/.vagrant.d/gems
# Usage should be restricted to when a repair is
# unsuccessful and the only reasonable option remaining
# is to re-install all plugins
class ExpungePlugins
def initialize(app, env)
@app = app
end
def call(env)
if !env[:force]
result = env[:ui].ask(
I18n.t("vagrant.commands.plugin.expunge_confirm") +
" [Y/N]:"
)
if result.to_s.downcase.strip != 'y'
abort_action = true
end
end
if !abort_action
plugins_json = File.join(env[:home_path], "plugins.json")
plugins_gems = env[:gems_path]
if File.exist?(plugins_json)
FileUtils.rm(plugins_json)
end
if File.directory?(plugins_gems)
FileUtils.rm_rf(plugins_gems)
end
env[:ui].info(I18n.t("vagrant.commands.plugin.expunge_complete"))
@app.call(env)
else
env[:ui].info(I18n.t("vagrant.commands.plugin.expunge_aborted"))
end
end
end
end
end
end

View File

@ -18,7 +18,11 @@ module VagrantPlugins
def call(env)
manager = Vagrant::Plugin::Manager.instance
plugins = manager.installed_plugins
specs = manager.installed_specs
specs = Hash[
manager.installed_specs.map do |spec|
[spec.name, spec]
end
]
# Output!
if specs.empty?
@ -26,9 +30,10 @@ module VagrantPlugins
return @app.call(env)
end
specs.each do |spec|
# Grab the plugin.
plugin = plugins[spec.name]
plugins.each do |plugin_name, plugin|
spec = specs[plugin_name]
next if spec.nil?
system = ""
system = ", system" if plugin && plugin["system"]

View File

@ -0,0 +1,39 @@
require "vagrant/plugin/manager"
module VagrantPlugins
module CommandPlugin
module Action
# This middleware attempts to repair installed plugins.
#
# In general, if plugins are failing to properly load the
# core issue will likely be one of two issues:
# 1. manual modifications within ~/.vagrant.d/
# 2. vagrant upgrade
# Running an install on configured plugin set will most
# likely fix these issues, which is all this action does.
class RepairPlugins
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::plugins::plugincommand::repair")
end
def call(env)
env[:ui].info(I18n.t("vagrant.commands.plugin.repairing"))
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
begin
Vagrant::Bundler.instance.init!(plugins, :repair)
env[:ui].info(I18n.t("vagrant.commands.plugin.repair_complete"))
rescue Exception => e
@logger.error("Failed to repair user installed plugins: #{e.class} - #{e}")
e.backtrace.each do |backtrace_line|
@logger.debug(backtrace_line)
end
env[:ui].error(I18n.t("vagrant.commands.plugin.repair_failed", message: e.message))
end
# Continue
@app.call(env)
end
end
end
end
end

View File

@ -19,17 +19,15 @@ module VagrantPlugins
end
manager = Vagrant::Plugin::Manager.instance
installed_specs = manager.installed_specs
installed_plugins = manager.installed_plugins
new_specs = manager.update_plugins(names)
updated_plugins = manager.installed_plugins
updated = {}
installed_specs.each do |ispec|
new_specs.each do |uspec|
next if uspec.name != ispec.name
next if ispec.version >= uspec.version
next if updated[uspec.name] && updated[uspec.name].version >= uspec.version
updated[uspec.name] = uspec
installed_plugins.each do |name, info|
update = updated_plugins[name]
if update && update["installed_gem_version"] != info["installed_gem_version"]
updated[name] = update["installed_gem_version"]
end
end
@ -37,9 +35,9 @@ module VagrantPlugins
env[:ui].success(I18n.t("vagrant.commands.plugin.up_to_date"))
end
updated.values.each do |spec|
updated.each do |name, version|
env[:ui].success(I18n.t("vagrant.commands.plugin.updated",
name: spec.name, version: spec.version.to_s))
name: name, version: version.to_s))
end
# Continue

View File

@ -0,0 +1,67 @@
require 'optparse'
require_relative "base"
module VagrantPlugins
module CommandPlugin
module Command
class Expunge < Base
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant plugin expunge [-h]"
o.on("--force", "Do not prompt for confirmation") do |force|
options[:force] = force
end
o.on("--reinstall", "Reinstall current plugins after expunge") do |reinstall|
options[:reinstall] = reinstall
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length > 0
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
if !options[:reinstall] && !options[:force] && !plugins.empty?
result = @env.ui.ask(
I18n.t("vagrant.commands.plugin.expunge_request_reinstall") +
" [Y/N]:"
)
options[:reinstall] = result.to_s.downcase.strip == "y"
end
# Remove all installed user plugins
action(Action.action_expunge, options)
if options[:reinstall]
@env.ui.info(I18n.t("vagrant.commands.plugin.expunge_reinstall"))
plugins.each do |plugin_name, plugin_info|
next if plugin_info["system"] # system plugins do not require re-install
# Rebuild information hash to use symbols
plugin_info = Hash[
plugin_info.map do |key, value|
["plugin_#{key}".to_sym, value]
end
]
action(
Action.action_install,
plugin_info.merge(
plugin_name: plugin_name
)
)
end
end
# Success, exit status 0
0
end
end
end
end
end

View File

@ -13,15 +13,6 @@ module VagrantPlugins
options[:entry_point] = entry_point
end
# @deprecated
o.on("--plugin-prerelease",
"Allow prerelease versions of this plugin.") do |plugin_prerelease|
puts "--plugin-prelease is deprecated and will be removed in the next"
puts "version of Vagrant. It has no effect now. Use the '--plugin-version'"
puts "flag to get a specific pre-release version."
puts
end
o.on("--plugin-clean-sources",
"Remove all plugin sources defined so far (including defaults)") do |clean|
options[:plugin_sources] = [] if clean

View File

@ -0,0 +1,28 @@
require 'optparse'
require_relative "base"
module VagrantPlugins
module CommandPlugin
module Command
class Repair < Base
def execute
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant plugin repair [-h]"
end
# Parse the options
argv = parse_options(opts)
return if !argv
raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length > 0
# Attempt to repair installed plugins
action(Action.action_repair)
# Success, exit status 0
0
end
end
end
end
end

View File

@ -14,6 +14,11 @@ module VagrantPlugins
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommands.register(:expunge) do
require_relative "expunge"
Expunge
end
@subcommands.register(:install) do
require_relative "install"
Install
@ -29,6 +34,11 @@ module VagrantPlugins
List
end
@subcommands.register(:repair) do
require_relative "repair"
Repair
end
@subcommands.register(:update) do
require_relative "update"
Update

View File

@ -540,7 +540,7 @@ module VagrantPlugins
end
# Set the terminal
ch2.send_data "export TERM=vt100\n"
ch2.send_data generate_environment_export("TERM", "vt100")
# Set SSH_AUTH_SOCK if we are in sudo and forwarding agent.
# This is to work around often misconfigured boxes where
@ -563,7 +563,7 @@ module VagrantPlugins
@logger.warn("No SSH_AUTH_SOCK found despite forward_agent being set.")
else
@logger.info("Setting SSH_AUTH_SOCK remotely: #{auth_socket}")
ch2.send_data "export SSH_AUTH_SOCK=#{auth_socket}\n"
ch2.send_data generate_environment_export("SSH_AUTH_SOCK", auth_socket)
end
end
@ -572,9 +572,9 @@ module VagrantPlugins
# without the cruft added from pty mode.
if pty
data = "stty raw -echo\n"
data += "export PS1=\n"
data += "export PS2=\n"
data += "export PROMPT_COMMAND=\n"
data += generate_environment_export("PS1", "")
data += generate_environment_export("PS2", "")
data += generate_environment_export("PROMPT_COMMAND", "")
data += "printf #{PTY_DELIM_START}\n"
data += "#{command}\n"
data += "exitcode=$?\n"
@ -670,6 +670,11 @@ module VagrantPlugins
source_path = Vagrant.source_root.join("keys", "vagrant")
return File.read(path).chomp == source_path.read.chomp
end
def generate_environment_export(env_key, env_value)
template = @machine.config.ssh.export_command_template
template.sub("%ENV_KEY%", env_key).sub("%ENV_VALUE%", env_value) + "\n"
end
end
end
end

View File

@ -104,7 +104,7 @@ module VagrantPlugins
@logger.info("Checking whether WinRM is ready...")
result = Timeout.timeout(@machine.config.winrm.timeout) do
shell(true).powershell("hostname")
shell(true).cmd("hostname")
end
@logger.info("WinRM is ready!")

View File

@ -56,11 +56,15 @@ module VagrantPlugins
def powershell(command, &block)
# Ensure an exit code
command += "\r\nif ($?) { exit 0 } else { if($LASTEXITCODE) { exit $LASTEXITCODE } else { exit 1 } }"
execute_with_rescue(executor.method("run_powershell_script"), command, &block)
session.create_executor do |executor|
execute_with_rescue(executor.method("run_powershell_script"), command, &block)
end
end
def cmd(command, &block)
execute_with_rescue(executor.method("run_cmd"), command, &block)
session.create_executor do |executor|
execute_with_rescue(executor.method("run_cmd"), command, &block)
end
end
def wql(query, &block)
@ -172,10 +176,6 @@ module VagrantPlugins
@session ||= new_session
end
def executor
@executor ||= session.create_executor
end
def endpoint
case @config.transport.to_sym
when :ssl

View File

@ -8,18 +8,16 @@ module VagrantPlugins
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, "")
set -e
# Remove comments and blank lines from /etc/hosts
sed -i'' -e 's/#.*$//' -e '/^$/d' /etc/hosts
# Set hostname
hostnamectl set-hostname '#{basename}'
# Remove comments and blank lines from /etc/hosts
sed -i'' -e 's/#.*$//' -e '/^$/d' /etc/hosts
# Prepend ourselves to /etc/hosts
grep -w '#{name}' /etc/hosts || {
test $? -eq 0 && (grep -w '#{name}' /etc/hosts || {
sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts
}
})
EOH
end
end

View File

@ -12,10 +12,9 @@ module VagrantPlugins
def self.configure_networks(machine, networks)
comm = machine.communicate
commands = []
commands = ["set -e"]
interfaces = machine.guest.capability(:network_interfaces)
networks.each.with_index do |network, i|
network[:device] = interfaces[network[:interface]]
@ -42,15 +41,15 @@ module VagrantPlugins
commands << <<-EOH.gsub(/^ {14}/, '')
# Configure #{network[:device]}
mv '#{remote_path}' '/etc/netctl/#{network[:device]}'
ip link set '#{network[:device]}' down
netctl restart '#{network[:device]}'
mv '#{remote_path}' '/etc/netctl/#{network[:device]}' &&
ip link set '#{network[:device]}' down &&
netctl restart '#{network[:device]}' &&
netctl enable '#{network[:device]}'
EOH
end
# Run all the network modification commands in one communicator call.
comm.sudo(commands.join("\n"))
comm.sudo(commands.join(" && \n"))
end
end
end

View File

@ -15,8 +15,7 @@ module VagrantPlugins
# https://bbs.archlinux.org/viewtopic.php?id=193410
#
comm.sudo <<-EOH.gsub(/^ {12}/, "")
set -e
systemctl enable rpcbind
systemctl enable rpcbind &&
systemctl start rpcbind
EOH
end
@ -24,8 +23,7 @@ module VagrantPlugins
def self.nfs_client_install(machine)
comm = machine.communicate
comm.sudo <<-EOH.gsub(/^ {12}/, "")
set -e
pacman --noconfirm -Syy
pacman --noconfirm -Syy &&
pacman --noconfirm -S nfs-utils ntp
EOH
end

View File

@ -5,9 +5,9 @@ module VagrantPlugins
def self.rsync_install(machine)
comm = machine.communicate
comm.sudo <<-EOH.gsub(/^ {12}/, '')
set -e
pacman -Sy --noconfirm
pacman -S --noconfirm rsync
exit $?
EOH
end
end

View File

@ -8,18 +8,16 @@ module VagrantPlugins
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, "")
set -e
# Remove comments and blank lines from /etc/hosts
sed -i'' -e 's/#.*$//' -e '/^$/d' /etc/hosts
# Set hostname
hostnamectl set-hostname '#{basename}'
# Remove comments and blank lines from /etc/hosts
sed -i'' -e 's/#.*$//' -e '/^$/d' /etc/hosts
# Prepend ourselves to /etc/hosts
grep -w '#{name}' /etc/hosts || {
test $? -eq 0 && (grep -w '#{name}' /etc/hosts || {
sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts
}
})
EOH
end
end

View File

@ -11,10 +11,8 @@ module VagrantPlugins
def self.mount_nfs_folder(machine, ip, folders)
comm = machine.communicate
# Mount each folder separately so we can retry.
folders.each do |name, opts|
# Mount each folder separately so we can retry.
commands = ["set -e"]
# Shellescape the paths in case they do not have special characters.
guest_path = Shellwords.escape(opts[:guestpath])
host_path = Shellwords.escape(opts[:hostpath])
@ -29,14 +27,14 @@ module VagrantPlugins
mount_opts = mount_opts.join(",")
# Make the directory on the guest.
commands << "mkdir -p #{guest_path}"
machine.communicate.sudo("mkdir -p #{guest_path}")
# Perform the mount operation.
commands << "/sbin/mount -t nfs -o '#{mount_opts}' #{ip}:#{host_path} #{guest_path}"
command = "/sbin/mount -t nfs -o '#{mount_opts}' #{ip}:#{host_path} #{guest_path}"
# Run the command, raising a specific error.
retryable(on: Vagrant::Errors::NFSMountFailed, tries: 3, sleep: 5) do
machine.communicate.sudo(commands.join("\n"),
machine.communicate.sudo(command,
error_class: Vagrant::Errors::NFSMountFailed,
shell: "sh",
)

View File

@ -22,14 +22,13 @@ module VagrantPlugins
# Use execute (not sudo) because we want to execute this as the SSH
# user (which is "vagrant" by default).
comm.execute <<-EOH.gsub(/^ {12}/, "")
set -e
mkdir -p ~/.ssh
chmod 0700 ~/.ssh
cat '#{remote_path}' >> ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys
chmod 0700 ~/.ssh &&
cat '#{remote_path}' >> ~/.ssh/authorized_keys &&
chmod 0600 ~/.ssh/authorized_keys
result=$?
rm -f '#{remote_path}'
exit $result
EOH
end
@ -49,15 +48,16 @@ module VagrantPlugins
# Use execute (not sudo) because we want to execute this as the SSH
# user (which is "vagrant" by default).
comm.execute <<-EOH.sub(/^ {12}/, "")
set -e
result=0
if test -f ~/.ssh/authorized_keys; then
grep -v -x -f '#{remote_path}' ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.tmp
mv ~/.ssh/authorized_keys.tmp ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys
grep -v -x -f '#{remote_path}' ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.tmp &&
mv ~/.ssh/authorized_keys.tmp ~/.ssh/authorized_keys &&
chmod 0600 ~/.ssh/authorized_keys
result=$?
fi
rm -f '#{remote_path}'
exit $result
EOH
end
end

View File

@ -8,16 +8,17 @@ module VagrantPlugins
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, '')
set -e
# LocalHostName should not contain dots - it is used by Bonjour and
# visible through file sharing services.
comm.sudo <<-EOH.gsub(/^ */, '')
# Set hostname
scutil --set ComputerName '#{name}'
scutil --set HostName '#{name}'
# LocalHostName should not contain dots - it is used by Bonjour and
# visible through file sharing services.
scutil --set LocalHostName '#{basename}'
scutil --set ComputerName '#{name}' &&
scutil --set HostName '#{name}' &&
scutil --set LocalHostName '#{basename}'
result=$?
if [ $result -ne 0 ]; then
exit $result
fi
hostname '#{name}'
@ -27,8 +28,8 @@ module VagrantPlugins
# Prepend ourselves to /etc/hosts - sed on bsd is sad
grep -w '#{name}' /etc/hosts || {
echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts
mv /tmp/tmp-hosts /etc/hosts
echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts &&
mv /tmp/tmp-hosts /etc/hosts
}
EOH
end

View File

@ -8,9 +8,6 @@ module VagrantPlugins
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, '')
# Ensure exit on command error
set -e
# Set the hostname
echo '#{basename}' > /etc/hostname
hostname -F /etc/hostname

View File

@ -11,7 +11,7 @@ module VagrantPlugins
def self.configure_networks(machine, networks)
comm = machine.communicate
commands = ["set -e"]
commands = []
entries = []
interfaces = machine.guest.capability(:network_interfaces)

View File

@ -5,9 +5,9 @@ module VagrantPlugins
def self.nfs_client_install(machine)
comm = machine.communicate
comm.sudo <<-EOH.gsub(/^ {12}/, '')
set -e
apt-get -yqq update
apt-get -yqq install nfs-common portmap
exit $?
EOH
end
end

View File

@ -5,7 +5,6 @@ module VagrantPlugins
def self.rsync_install(machine)
comm = machine.communicate
comm.sudo <<-EOH.gsub(/^ {14}/, '')
set -e
apt-get -yqq update
apt-get -yqq install rsync
EOH

View File

@ -1,11 +1,10 @@
require "vagrant"
require_relative '../linux/guest'
module VagrantPlugins
module GuestDebian
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
machine.communicate.test("cat /etc/issue | grep 'Debian'")
end
class Guest < VagrantPlugins::GuestLinux::Guest
# Name used for guest detection
GUEST_DETECTION_NAME = "debian".freeze
end
end
end

View File

@ -8,8 +8,6 @@ module VagrantPlugins
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, "")
set -e
# Set the hostname
hostname '#{basename}'
echo "hostname=#{basename}" > /etc/conf.d/hostname
@ -20,8 +18,8 @@ module VagrantPlugins
# Prepend ourselves to /etc/hosts
grep -w '#{name}' /etc/hosts || {
echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts
mv /tmp/tmp-hosts /etc/hosts
echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts &&
mv /tmp/tmp-hosts /etc/hosts
}
EOH
end

View File

@ -94,7 +94,7 @@ SCRIPT
# Emit an upstart event if we can
machine.communicate.sudo <<-SCRIPT
if command -v /sbin/init && /sbin/init --version | grep upstart; then
if command -v /sbin/init && /sbin/init 2>/dev/null --version | grep upstart; then
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
fi
SCRIPT

View File

@ -89,7 +89,7 @@ module VagrantPlugins
# Emit an upstart event if we can
machine.communicate.sudo <<-EOH.gsub(/^ {12}/, "")
if command -v /sbin/init && /sbin/init --version | grep upstart; then
if command -v /sbin/init && /sbin/init 2>/dev/null --version | grep upstart; then
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guest_path}
fi
EOH

View File

@ -2,6 +2,11 @@ module VagrantPlugins
module GuestLinux
module Cap
class NetworkInterfaces
# Valid ethernet device prefix values.
# eth - classic prefix
# en - predictable interface names prefix
POSSIBLE_ETHERNET_PREFIXES = ["eth".freeze, "en".freeze].freeze
@@logger = Log4r::Logger.new("vagrant::guest::linux::network_interfaces")
# Get network interfaces as a list. The result will be something like:
@ -18,6 +23,9 @@ module VagrantPlugins
@@logger.debug("Unsorted list: #{ifaces.inspect}")
# Break out integers from strings and sort the arrays to provide
# a natural sort for the interface names
# NOTE: Devices named with a hex value suffix will _not_ be sorted
# as expected. This is generally seen with veth* devices, and proper ordering
# is currently not required
ifaces = ifaces.map do |iface|
iface.scan(/(.+?)(\d+)/).flatten.map do |iface_part|
if iface_part.to_i.to_s == iface_part
@ -26,8 +34,32 @@ module VagrantPlugins
iface_part
end
end
end.sort.map(&:join)
end
ifaces = ifaces.sort do |lhs, rhs|
result = 0
slice_length = [rhs.size, lhs.size].min
slice_length.times do |idx|
if(lhs[idx].is_a?(rhs[idx].class))
result = lhs[idx] <=> rhs[idx]
elsif(lhs[idx].is_a?(String))
result = 1
else
result = -1
end
break if result != 0
end
result
end.map(&:join)
@@logger.debug("Sorted list: #{ifaces.inspect}")
# Extract ethernet devices and place at start of list
resorted_ifaces = []
resorted_ifaces += ifaces.find_all do |iface|
POSSIBLE_ETHERNET_PREFIXES.any?{|prefix| iface.start_with?(prefix)} &&
iface.match(/^[a-zA-Z0-9]+$/)
end
resorted_ifaces += ifaces - resorted_ifaces
ifaces = resorted_ifaces
@@logger.debug("Ethernet preferred sorted list: #{ifaces.inspect}")
ifaces
end
end

View File

@ -13,10 +13,8 @@ module VagrantPlugins
def self.mount_nfs_folder(machine, ip, folders)
comm = machine.communicate
# Mount each folder separately so we can retry.
folders.each do |name, opts|
# Mount each folder separately so we can retry.
commands = ["set -e"]
# Shellescape the paths in case they do not have special characters.
guest_path = Shellwords.escape(opts[:guestpath])
host_path = Shellwords.escape(opts[:hostpath])
@ -30,22 +28,24 @@ module VagrantPlugins
end
mount_opts = mount_opts.join(",")
# Make the directory on the guest.
commands << "mkdir -p #{guest_path}"
machine.communicate.sudo("mkdir -p #{guest_path}")
# Perform the mount operation.
commands << "mount -o #{mount_opts} #{ip}:#{host_path} #{guest_path}"
# Emit a mount event
commands << <<-EOH.gsub(/^ {14}/, '')
if command -v /sbin/init && /sbin/init --version | grep upstart; then
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guest_path}
# Perform the mount operation and emit mount event if applicable
command = <<-EOH.gsub(/^ */, '')
mount -o #{mount_opts} #{ip}:#{host_path} #{guest_path}
result=$?
if test $result -eq 0; then
if test -x /sbin/initctl && command -v /sbin/init && /sbin/init 2>/dev/null --version | grep upstart; then
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guest_path}
fi
else
exit $result
fi
EOH
# Run the command, raising a specific error.
retryable(on: Vagrant::Errors::NFSMountFailed, tries: 3, sleep: 5) do
machine.communicate.sudo(commands.join("\n"),
machine.communicate.sudo(command,
error_class: Vagrant::Errors::NFSMountFailed,
)
end

View File

@ -21,15 +21,13 @@ module VagrantPlugins
# Use execute (not sudo) because we want to execute this as the SSH
# user (which is "vagrant" by default).
comm.execute <<-EOH.gsub(/^ {12}/, "")
set -e
comm.execute <<-EOH.gsub(/^ */, "")
mkdir -p ~/.ssh
chmod 0700 ~/.ssh
cat '#{remote_path}' >> ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys
cat '#{remote_path}' >> ~/.ssh/authorized_keys && chmod 0600 ~/.ssh/authorized_keys
result=$?
rm -f '#{remote_path}'
exit $result
EOH
end
@ -48,16 +46,14 @@ module VagrantPlugins
# Use execute (not sudo) because we want to execute this as the SSH
# user (which is "vagrant" by default).
comm.execute <<-EOH.sub(/^ {12}/, "")
set -e
comm.execute <<-EOH.sub(/^ */, "")
if test -f ~/.ssh/authorized_keys; then
grep -v -x -f '#{remote_path}' ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.tmp
mv ~/.ssh/authorized_keys.tmp ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys
mv ~/.ssh/authorized_keys.tmp ~/.ssh/authorized_keys && chmod 0600 ~/.ssh/authorized_keys
result=$?
fi
rm -f '#{remote_path}'
exit $result
EOH
end
end

View File

@ -1,8 +1,22 @@
module VagrantPlugins
module GuestLinux
class Guest < Vagrant.plugin("2", :guest)
# Name used for guest detection
GUEST_DETECTION_NAME = "linux".freeze
def detect?(machine)
machine.communicate.test("uname -s | grep 'Linux'")
machine.communicate.test <<-EOH.gsub(/^ */, '')
if test -r /etc/os-release; then
source /etc/os-release && test x#{self.class.const_get(:GUEST_DETECTION_NAME)} = x$ID && exit
fi
if test -x /usr/bin/lsb_release; then
/usr/bin/lsb_release -i 2>/dev/null | grep -qi #{self.class.const_get(:GUEST_DETECTION_NAME)} && exit
fi
if test -r /etc/issue; then
cat /etc/issue | grep -qi #{self.class.const_get(:GUEST_DETECTION_NAME)} && exit
fi
exit 1
EOH
end
end
end

View File

@ -1,9 +1,10 @@
require_relative '../linux/guest'
module VagrantPlugins
module GuestMint
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
machine.communicate.test("cat /etc/issue | grep 'Linux Mint'")
end
class Guest < VagrantPlugins::GuestLinux::Guest
# Name used for guest detection
GUEST_DETECTION_NAME = "Linux Mint".freeze
end
end
end

View File

@ -5,9 +5,8 @@ module VagrantPlugins
def self.change_host_name(machine, name)
if !machine.communicate.test("hostname -s | grep '^#{name}$'")
machine.communicate.sudo(<<CMDS, {shell: "sh"})
set -e
sed -e 's/^hostname=.*$/hostname=#{name}/' /etc/rc.conf > /tmp/rc.conf.vagrant_changehostname_#{name}
mv /tmp/rc.conf.vagrant_changehostname_#{name} /etc/rc.conf
sed -e 's/^hostname=.*$/hostname=#{name}/' /etc/rc.conf > /tmp/rc.conf.vagrant_changehostname_#{name} &&
mv /tmp/rc.conf.vagrant_changehostname_#{name} /etc/rc.conf &&
hostname #{name}
CMDS
end

View File

@ -8,9 +8,11 @@ module VagrantPlugins
def self.rsync_install(machine)
machine.communicate.sudo(
'PKG_PATH="http://ftp.NetBSD.org/pub/pkgsrc/packages/NetBSD/' \
'`uname -m`/`uname -r | cut -d. -f1-2`/All" ' \
'pkg_add rsync')
'PATH=$PATH:/usr/sbin '\
'PKG_PATH="http://ftp.NetBSD.org/pub/pkgsrc/packages/NetBSD/' \
'`uname -m`/`uname -r | cut -d. -f1-2`/All" ' \
'pkg_add rsync'
)
end
end
end

View File

@ -7,10 +7,21 @@ module VagrantPlugins
extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap
def self.rsync_install(machine)
machine.communicate.sudo(
'PKG_PATH="http://ftp.openbsd.org/pub/OpenBSD/' \
install_output = {:stderr => '', :stdout => ''}
command = 'PKG_PATH="http://ftp.openbsd.org/pub/OpenBSD/' \
'`uname -r`/packages/`arch -s`/" ' \
'pkg_add -I rsync--')
'pkg_add -I rsync--'
machine.communicate.sudo(command) do |type, data|
install_output[type] << data if install_output.key?(type)
end
# pkg_add returns 0 even if package was not found, so
# validate package is actually installed
machine.communicate.sudo('pkg_info -cA | grep inst:rsync-[[:digit:]]',
error_class: Vagrant::Errors::RSyncNotInstalledInGuest,
command: command,
stderr: install_output[:stderr],
stdout: install_output[:stdout]
)
end
end
end

View File

@ -18,7 +18,7 @@ module VagrantPlugins
device = interfaces[network[:interface]]
command = "ifconfig #{device}"
command << " #{network[:ip]}" if network[:ip]
command << " netmast #{network[:netmask]}" if network[:netmask]
command << " netmask #{network[:netmask]}" if network[:netmask]
commands << command
end

View File

@ -31,8 +31,12 @@ module VagrantPlugins
sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts
}
# Restart network
service network restart
# Restart network (through NetworkManager if running)
if service NetworkManager status 2>&1 | grep -q running; then
service NetworkManager restart
else
service network restart
fi
EOH
end
end

View File

@ -40,16 +40,23 @@ module VagrantPlugins
# Down the interface before munging the config file. This might
# fail if the interface is not actually set up yet so ignore
# errors.
/sbin/ifdown '#{network[:device]}' || true
/sbin/ifdown '#{network[:device]}'
# Move new config into place
mv '#{remote_path}' '#{final_path}'
# Bring the interface up
ARPCHECK=no /sbin/ifup '#{network[:device]}'
mv -f '#{remote_path}' '#{final_path}'
# attempt to force network manager to reload configurations
nmcli c reload || true
EOH
end
commands << <<-EOH.gsub(/^ {12}/, '')
# Restart network (through NetworkManager if running)
if service NetworkManager status 2>&1 | grep -q running; then
service NetworkManager restart
else
service network restart
fi
EOH
comm.sudo(commands.join("\n"))
end
end

View File

@ -10,7 +10,7 @@ module VagrantPlugins
end
# Detect various flavors we care about
if output =~ /(CentOS|Red Hat Enterprise|Scientific|Cloud)\s*Linux( .+)? release 7/i
if output =~ /(CentOS|Red Hat Enterprise|Scientific|Cloud|Virtuozzo)\s*Linux( .+)? release 7/i
return :rhel_7
else
return :rhel

View File

@ -1,11 +1,10 @@
require "vagrant"
require_relative '../linux/guest'
module VagrantPlugins
module GuestTinyCore
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
machine.communicate.test("cat /etc/issue | grep 'Core Linux'")
end
class Guest < VagrantPlugins::GuestLinux::Guest
# Name used for guest detection
GUEST_DETECTION_NAME = "Core Linux".freeze
end
end
end

View File

@ -1,24 +1,10 @@
require_relative '../linux/guest'
module VagrantPlugins
module GuestUbuntu
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
# This command detects if we are running on Ubuntu. /etc/os-release is
# available on modern Ubuntu versions, but does not exist on 14.04 and
# previous versions, so we fall back to lsb_release.
#
# GH-7524
# GH-7625
#
machine.communicate.test <<-EOH.gsub(/^ {10}/, "")
if test -r /etc/os-release; then
source /etc/os-release && test xubuntu = x$ID
elif test -x /usr/bin/lsb_release; then
/usr/bin/lsb_release -i 2>/dev/null | grep -q Ubuntu
else
exit 1
fi
EOH
end
class Guest < VagrantPlugins::GuestLinux::Guest
# Name used for guest detection
GUEST_DETECTION_NAME = "ubuntu".freeze
end
end
end

View File

@ -6,6 +6,8 @@ module VagrantPlugins
module HostLinux
module Cap
class NFS
NFS_EXPORTS_PATH = "/etc/exports".freeze
extend Vagrant::Util::Retryable
def self.nfs_apply_command(env)
@ -36,16 +38,9 @@ module VagrantPlugins
ui.info I18n.t("vagrant.hosts.linux.nfs_export")
sleep 0.5
nfs_cleanup(id)
# Only use "sudo" if we can't write to /etc/exports directly
sudo_command = ""
sudo_command = "sudo " if !File.writable?("/etc/exports")
output.split("\n").each do |line|
line = Vagrant::Util::ShellQuote.escape(line, "'")
system(%Q[echo '#{line}' | #{sudo_command}tee -a /etc/exports >/dev/null])
end
nfs_cleanup("#{Process.uid} #{id}")
output = "#{nfs_exports_content}\n#{output}"
nfs_write_exports(output)
if nfs_running?(nfs_check_command)
system("sudo #{nfs_apply_command}")
@ -62,48 +57,111 @@ module VagrantPlugins
end
def self.nfs_prune(environment, ui, valid_ids)
return if !File.exist?("/etc/exports")
return if !File.exist?(NFS_EXPORTS_PATH)
logger = Log4r::Logger.new("vagrant::hosts::linux")
logger.info("Pruning invalid NFS entries...")
output = false
user = Process.uid
File.read("/etc/exports").lines.each do |line|
if id = line[/^# VAGRANT-BEGIN:( #{user})? ([\.\/A-Za-z0-9\-_:]+?)$/, 2]
if valid_ids.include?(id)
logger.debug("Valid ID: #{id}")
else
if !output
# We want to warn the user but we only want to output once
ui.info I18n.t("vagrant.hosts.linux.nfs_prune")
output = true
end
# Create editor instance for removing invalid IDs
editor = Vagrant::Util::StringBlockEditor.new(nfs_exports_content)
logger.info("Invalid ID, pruning: #{id}")
nfs_cleanup(id)
end
end
# Build composite IDs with UID information and discover invalid entries
composite_ids = valid_ids.map do |v_id|
"#{user} #{v_id}"
end
remove_ids = editor.keys - composite_ids
logger.debug("Known valid NFS export IDs: #{valid_ids}")
logger.debug("Composite valid NFS export IDs with user: #{composite_ids}")
logger.debug("NFS export IDs to be removed: #{remove_ids}")
if !remove_ids.empty?
ui.info I18n.t("vagrant.hosts.linux.nfs_prune")
nfs_cleanup(remove_ids)
end
end
protected
def self.nfs_cleanup(id)
return if !File.exist?("/etc/exports")
def self.nfs_cleanup(remove_ids)
return if !File.exist?(NFS_EXPORTS_PATH)
user = Regexp.escape(Process.uid.to_s)
id = Regexp.escape(id.to_s)
editor = Vagrant::Util::StringBlockEditor.new(nfs_exports_content)
remove_ids = Array(remove_ids)
# Only use "sudo" if we can't write to /etc/exports directly
sudo_command = ""
sudo_command = "sudo " if !File.writable?("/etc/exports")
# Remove all invalid ID entries
remove_ids.each do |r_id|
editor.delete(r_id)
end
nfs_write_exports(editor.value)
end
# Use sed to just strip out the block of code which was inserted
# by Vagrant
tmp = ENV["TMPDIR"] || ENV["TMP"] || "/tmp"
system("cp /etc/exports '#{tmp}' && #{sudo_command}sed -r -e '\\\x01^# VAGRANT-BEGIN:( #{user})? #{id}\x01,\\\x01^# VAGRANT-END:( #{user})? #{id}\x01 d' -ibak '#{tmp}/exports' ; #{sudo_command}cp '#{tmp}/exports' /etc/exports")
def self.nfs_write_exports(new_exports_content)
if(nfs_exports_content != new_exports_content.strip)
begin
# Write contents out to temporary file
new_exports_file = Tempfile.create('vagrant')
new_exports_file.puts(new_exports_content)
new_exports_file.close
new_exports_path = new_exports_file.path
# Only use "sudo" if we can't write to /etc/exports directly
sudo_command = ""
sudo_command = "sudo " if !File.writable?(NFS_EXPORTS_PATH)
# Ensure new file mode and uid/gid match existing file to replace
existing_stat = File.stat(NFS_EXPORTS_PATH)
new_stat = File.stat(new_exports_path)
if existing_stat.mode != new_stat.mode
File.chmod(existing_stat.mode, new_exports_path)
end
if existing_stat.uid != new_stat.uid || existing_stat.gid != new_stat.gid
chown_cmd = "#{sudo_command}chown #{existing_stat.uid}:#{existing_stat.gid} #{new_exports_path}"
result = Vagrant::Util::Subprocess.execute(*Shellwords.split(chown_cmd))
if result.exit_code != 0
raise Vagrant::Errors::NFSExportsFailed,
command: chown_cmd,
stderr: result.stderr,
stdout: result.stdout
end
end
# Always force move the file to prevent overwrite prompting
mv_cmd = "#{sudo_command}mv -f #{new_exports_path} #{NFS_EXPORTS_PATH}"
result = Vagrant::Util::Subprocess.execute(*Shellwords.split(mv_cmd))
if result.exit_code != 0
raise Vagrant::Errors::NFSExportsFailed,
command: mv_cmd,
stderr: result.stderr,
stdout: result.stdout
end
ensure
if File.exist?(new_exports_path)
File.unlink(new_exports_path)
end
end
end
end
def self.nfs_exports_content
if(File.exist?(NFS_EXPORTS_PATH))
if(File.readable?(NFS_EXPORTS_PATH))
File.read(NFS_EXPORTS_PATH)
else
cmd = "sudo cat #{NFS_EXPORTS_PATH}"
result = Vagrant::Util::Subprocess.execute(*Shellwords.split(cmd))
if result.exit_code != 0
raise Vagrant::Errors::NFSExportsFailed,
command: cmd,
stderr: result.stderr,
stdout: result.stdout
else
result.stdout
end
end
else
""
end
end
def self.nfs_opts_setup(folders)

View File

@ -15,24 +15,25 @@ module VagrantPlugins
attr_accessor :ssh_command
attr_accessor :pty
attr_accessor :sudo_command
attr_accessor :export_command_template
attr_reader :default
def initialize
super
@forward_agent = UNSET_VALUE
@forward_x11 = UNSET_VALUE
@forward_env = UNSET_VALUE
@guest_port = UNSET_VALUE
@keep_alive = UNSET_VALUE
@proxy_command = UNSET_VALUE
@ssh_command = UNSET_VALUE
@pty = UNSET_VALUE
@shell = UNSET_VALUE
@sudo_command = UNSET_VALUE
@default = SSHConnectConfig.new
@forward_agent = UNSET_VALUE
@forward_x11 = UNSET_VALUE
@forward_env = UNSET_VALUE
@guest_port = UNSET_VALUE
@keep_alive = UNSET_VALUE
@proxy_command = UNSET_VALUE
@ssh_command = UNSET_VALUE
@pty = UNSET_VALUE
@shell = UNSET_VALUE
@sudo_command = UNSET_VALUE
@export_command_template = UNSET_VALUE
@default = SSHConnectConfig.new
end
def merge(other)
@ -55,6 +56,10 @@ module VagrantPlugins
@pty = false if @pty == UNSET_VALUE
@shell = "bash -l" if @shell == UNSET_VALUE
if @export_command_template == UNSET_VALUE
@export_command_template = 'export %ENV_KEY%="%ENV_VALUE%"'
end
if @sudo_command == UNSET_VALUE
@sudo_command = "sudo -E -H %c"
end

View File

@ -207,7 +207,19 @@ module VagrantPlugins
hostpath = hostpath.to_s.gsub("\\", "/")
end
if guestpath.is_a?(Hash)
options = guestpath
guestpath = nil
end
options ||= {}
if options.has_key?(:name)
synced_folder_name = options.delete(:name)
else
synced_folder_name = guestpath
end
options[:guestpath] = guestpath.to_s.gsub(/\/$/, '')
options[:hostpath] = hostpath
options[:disabled] = false if !options.key?(:disabled)
@ -217,7 +229,7 @@ module VagrantPlugins
# Make sure the type is a symbol
options[:type] = options[:type].to_sym if options[:type]
@__synced_folders[options[:guestpath]] = options
@__synced_folders[synced_folder_name] = options
end
# Define a way to access the machine via a network. This exposes a
@ -581,7 +593,7 @@ module VagrantPlugins
@hostname && @hostname !~ /^[a-z0-9][-.a-z0-9]*$/i
if @box_version
@box_version.split(",").each do |v|
@box_version.to_s.split(",").each do |v|
begin
Gem::Requirement.new(v.strip)
rescue Gem::Requirement::BadRequirementError
@ -626,7 +638,7 @@ module VagrantPlugins
# If the shared folder is disabled then don't worry about validating it
next if options[:disabled]
guestpath = Pathname.new(options[:guestpath])
guestpath = Pathname.new(options[:guestpath]) if options[:guestpath]
hostpath = Pathname.new(options[:hostpath]).expand_path(machine.env.root_path)
if guestpath.to_s != ""

View File

@ -48,7 +48,7 @@ module VagrantPlugins
run_cmd += volumes.map { |v| ['-v', v.to_s] }
run_cmd += %W(--privileged) if params[:privileged]
run_cmd += %W(-h #{params[:hostname]}) if params[:hostname]
run_cmd << "-i" << "-t" if params[:pty]
run_cmd << "-t" if params[:pty]
run_cmd << "--rm=true" if params[:rm]
run_cmd += params[:extra_args] if params[:extra_args]
run_cmd += [image, cmd]

View File

@ -7,7 +7,7 @@ module VagrantPlugins
module Action
class Import
def initialize(app, env)
@app = app
@app = app
@logger = Log4r::Logger.new("vagrant::hyperv::import")
end
@ -35,13 +35,28 @@ module VagrantPlugins
end
config_path = nil
config_type = nil
vm_dir.each_child do |f|
if f.extname.downcase == ".xml"
if f.extname.downcase == '.xml'
config_path = f
config_type = 'xml'
break
end
end
# Only check for .vmcx if there is no XML found to not
# risk breaking older vagrant boxes that added an XML
# file manually
if config_type == nil
vm_dir.each_child do |f|
if f.extname.downcase == '.vmcx'
config_path = f
config_type = 'vmcx'
break
end
end
end
image_path = nil
image_ext = nil
image_filename = nil
@ -49,7 +64,7 @@ module VagrantPlugins
if %w{.vhd .vhdx}.include?(f.extname.downcase)
image_path = f
image_ext = f.extname.downcase
image_filename = File.basename(f,image_ext)
image_filename = File.basename(f, image_ext)
break
end
end
@ -100,19 +115,27 @@ module VagrantPlugins
env[:ui].detail("Cloning virtual hard drive...")
source_path = image_path.to_s
dest_path = env[:machine].data_dir.join("#{image_filename}#{image_ext}").to_s
if differencing_disk
env[:machine].provider.driver.execute("clone_vhd.ps1", {Source: source_path, Destination: dest_path})
else
FileUtils.cp(source_path, dest_path)
dest_path = env[:machine].data_dir.join("Virtual Hard Disks").join("#{image_filename}#{image_ext}").to_s
# Still hard copy the disk of old XML configurations
if config_type == 'xml'
if differencing_disk
env[:machine].provider.driver.execute("clone_vhd.ps1", {Source: source_path, Destination: dest_path})
else
FileUtils.mkdir_p(env[:machine].data_dir.join("Virtual Hard Disks"))
FileUtils.cp(source_path, dest_path)
end
end
image_path = dest_path
# We have to normalize the paths to be Windows paths since
# we're executing PowerShell.
options = {
vm_xml_config: config_path.to_s.gsub("/", "\\"),
image_path: image_path.to_s.gsub("/", "\\")
vm_config_file: config_path.to_s.gsub("/", "\\"),
vm_config_type: config_type,
source_path: source_path.to_s,
dest_path: dest_path,
data_path: env[:machine].data_dir.to_s.gsub("/", "\\")
}
options[:switchname] = switch if switch
options[:memory] = memory if memory
@ -121,6 +144,7 @@ module VagrantPlugins
options[:vmname] = vmname if vmname
options[:auto_start_action] = auto_start_action if auto_start_action
options[:auto_stop_action] = auto_stop_action if auto_stop_action
options[:differencing_disk] = differencing_disk if differencing_disk
env[:ui].detail("Creating and registering the VM...")
server = env[:machine].provider.driver.import(options)

View File

@ -74,7 +74,15 @@ module VagrantPlugins
end
def import(options)
execute('import_vm.ps1', options)
config_type = options.delete(:vm_config_type)
if config_type === "vmcx"
execute('import_vm_vmcx.ps1', options)
else
options.delete(:data_path)
options.delete(:source_path)
options.delete(:differencing_disk)
execute('import_vm_xml.ps1', options)
end
end
def net_set_vlan(vlan_id)

View File

@ -0,0 +1,157 @@
Param(
[Parameter(Mandatory=$true)]
[string]$vm_config_file,
[Parameter(Mandatory=$true)]
[string]$source_path,
[Parameter(Mandatory=$true)]
[string]$dest_path,
[Parameter(Mandatory=$true)]
[string]$data_path,
[string]$switchname=$null,
[string]$memory=$null,
[string]$maxmemory=$null,
[string]$cpus=$null,
[string]$vmname=$null,
[string]$auto_start_action=$null,
[string]$auto_stop_action=$null,
[string]$differencing_disk=$null
)
# Include the following modules
$Dir = Split-Path $script:MyInvocation.MyCommand.Path
. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
$VmProperties = @{
Path = $vm_config_file
SnapshotFilePath = Join-Path $data_path 'Snapshots'
VhdDestinationPath = Join-Path $data_path 'Virtual Hard Disks'
VirtualMachinePath = $data_path
}
$vmConfig = (Compare-VM -Copy -GenerateNewID @VmProperties)
$generation = $vmConfig.VM.Generation
if (!$vmname) {
# Get the name of the vm
$vm_name = $vmconfig.VM.VMName
} else {
$vm_name = $vmname
}
if (!$cpus) {
# Get the processorcount of the VM
$processors = (Get-VMProcessor -VM $vmConfig.VM).Count
}else {
$processors = $cpus
}
function GetUniqueName($name) {
Get-VM | ForEach-Object -Process {
if ($name -eq $_.Name) {
$name = $name + "_1"
}
}
return $name
}
do {
$name = $vm_name
$vm_name = GetUniqueName $name
} while ($vm_name -ne $name)
if (!$memory) {
$configMemory = Get-VMMemory -VM $vmConfig.VM
$dynamicmemory = $configMemory.DynamicMemoryEnabled
$MemoryMaximumBytes = ($configMemory.Maximum)
$MemoryStartupBytes = ($configMemory.Startup)
$MemoryMinimumBytes = ($configMemory.Minimum)
} else {
if (!$maxmemory){
$dynamicmemory = $False
$MemoryMaximumBytes = ($memory -as [int]) * 1MB
$MemoryStartupBytes = ($memory -as [int]) * 1MB
$MemoryMinimumBytes = ($memory -as [int]) * 1MB
} else {
$dynamicmemory = $True
$MemoryMaximumBytes = ($maxmemory -as [int]) * 1MB
$MemoryStartupBytes = ($memory -as [int]) * 1MB
$MemoryMinimumBytes = ($memory -as [int]) * 1MB
}
}
if (!$switchname) {
$switchname = (Get-VMNetworkAdapter -VM $vmConfig.VM).SwitchName
}
$vmNetworkAdapter = Get-VMNetworkAdapter -VM $vmConfig.VM
Connect-VMNetworkAdapter -VMNetworkAdapter $vmNetworkAdapter -SwitchName $switchname
Set-VM -VM $vmConfig.VM -NewVMName $vm_name
Set-VM -VM $vmConfig.VM -ErrorAction "Stop"
Set-VM -VM $vmConfig.VM -ProcessorCount $processors
if ($dynamicmemory) {
Set-VM -VM $vmConfig.VM -DynamicMemory
Set-VM -VM $vmConfig.VM -MemoryMinimumBytes $MemoryMinimumBytes -MemoryMaximumBytes $MemoryMaximumBytes -MemoryStartupBytes $MemoryStartupBytes
} else {
Set-VM -VM $vmConfig.VM -StaticMemory
Set-VM -VM $vmConfig.VM -MemoryStartupBytes $MemoryStartupBytes
}
if ($notes) {
Set-VM -VM $vmConfig.VM -Notes $notes
}
if ($auto_start_action) {
Set-VM -VM $vmConfig.VM -AutomaticStartAction $auto_start_action
}
if ($auto_stop_action) {
Set-VM -VM $vmConfig.VM -AutomaticStartAction $auto_stop_action
}
# Only set EFI secure boot for Gen 2 machines, not gen 1
if ($generation -ne 1) {
Set-VMFirmware -VM $vmConfig.VM -EnableSecureBoot (Get-VMFirmware -VM $vmConfig.VM).SecureBoot
}
$report = Compare-VM -CompatibilityReport $vmConfig
# Stop if there are incompatibilities
if($report.Incompatibilities.Length -gt 0){
Write-Error-Message $(ConvertTo-Json $($report.Incompatibilities | Select -ExpandProperty Message))
exit 0
}
if($differencing_disk){
# Get all controller on the VM, first scsi, then IDE if it is a Gen 1 device
$controllers = Get-VMScsiController -VM $vmConfig.VM
if($generation -eq 1){
$controllers = @($controllers) + @(Get-VMIdeController -VM $vmConfig.VM)
}
foreach($controller in $controllers){
foreach($drive in $controller.Drives){
if([System.IO.Path]::GetFileName($drive.Path) -eq [System.IO.Path]::GetFileName($source_path)){
# Remove the old disk and replace it with a differencing version
$path = $drive.Path
Remove-VMHardDiskDrive $drive
New-VHD -Path $dest_path -ParentPath $source_path -ErrorAction Stop
Add-VMHardDiskDrive -VM $vmConfig.VM -Path $dest_path
}
}
}
}
Import-VM -CompatibilityReport $vmConfig
$vm_id = (Get-VM $vm_name).id.guid
$resultHash = @{
name = $vm_name
id = $vm_id
}
$result = ConvertTo-Json $resultHash
Write-Output-Message $result

View File

@ -1,8 +1,8 @@
Param(
[Parameter(Mandatory=$true)]
[string]$vm_xml_config,
[string]$vm_config_file,
[Parameter(Mandatory=$true)]
[string]$image_path,
[string]$dest_path,
[string]$switchname=$null,
[string]$memory=$null,
@ -17,7 +17,7 @@ Param(
$Dir = Split-Path $script:MyInvocation.MyCommand.Path
. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
[xml]$vmconfig = Get-Content -Path $vm_xml_config
[xml]$vmconfig = Get-Content -Path $vm_config_file
$generation = [int]($vmconfig.configuration.properties.subtype.'#text')+1
@ -190,7 +190,7 @@ foreach ($controller in $controllers) {
$addDriveParam = @{
ControllerNumber = $rx.Match($controller.node.name).value
Path = $image_path
Path = $dest_path
}
if ($drive.pool_id."#text") {

View File

@ -4,16 +4,19 @@ module VagrantPlugins
class Base < Vagrant.plugin("2", :config)
GALAXY_COMMAND_DEFAULT = "ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force".freeze
PLAYBOOK_COMMAND_DEFAULT = "ansible-playbook".freeze
attr_accessor :config_file
attr_accessor :extra_vars
attr_accessor :galaxy_role_file
attr_accessor :galaxy_roles_path
attr_accessor :galaxy_command
attr_accessor :host_vars
attr_accessor :groups
attr_accessor :host_vars
attr_accessor :inventory_path
attr_accessor :limit
attr_accessor :playbook
attr_accessor :playbook_command
attr_accessor :raw_arguments
attr_accessor :skip_tags
attr_accessor :start_at_task
@ -24,15 +27,17 @@ module VagrantPlugins
attr_accessor :verbose
def initialize
@config_file = UNSET_VALUE
@extra_vars = UNSET_VALUE
@galaxy_role_file = UNSET_VALUE
@galaxy_roles_path = UNSET_VALUE
@galaxy_command = UNSET_VALUE
@host_vars = UNSET_VALUE
@groups = UNSET_VALUE
@host_vars = UNSET_VALUE
@inventory_path = UNSET_VALUE
@limit = UNSET_VALUE
@playbook = UNSET_VALUE
@playbook_command = UNSET_VALUE
@raw_arguments = UNSET_VALUE
@skip_tags = UNSET_VALUE
@start_at_task = UNSET_VALUE
@ -44,23 +49,25 @@ module VagrantPlugins
end
def finalize!
@extra_vars = nil if @extra_vars == UNSET_VALUE
@galaxy_role_file = nil if @galaxy_role_file == UNSET_VALUE
@galaxy_roles_path = nil if @galaxy_roles_path == UNSET_VALUE
@galaxy_command = GALAXY_COMMAND_DEFAULT if @galaxy_command == UNSET_VALUE
@host_vars = {} if @host_vars == UNSET_VALUE
@groups = {} if @groups == UNSET_VALUE
@inventory_path = nil if @inventory_path == UNSET_VALUE
@limit = nil if @limit == UNSET_VALUE
@playbook = nil if @playbook == UNSET_VALUE
@raw_arguments = nil if @raw_arguments == UNSET_VALUE
@skip_tags = nil if @skip_tags == UNSET_VALUE
@start_at_task = nil if @start_at_task == UNSET_VALUE
@sudo = false if @sudo != true
@sudo_user = nil if @sudo_user == UNSET_VALUE
@tags = nil if @tags == UNSET_VALUE
@vault_password_file = nil if @vault_password_file == UNSET_VALUE
@verbose = false if @verbose == UNSET_VALUE
@config_file = nil if @config_file == UNSET_VALUE
@extra_vars = nil if @extra_vars == UNSET_VALUE
@galaxy_role_file = nil if @galaxy_role_file == UNSET_VALUE
@galaxy_roles_path = nil if @galaxy_roles_path == UNSET_VALUE
@galaxy_command = GALAXY_COMMAND_DEFAULT if @galaxy_command == UNSET_VALUE
@groups = {} if @groups == UNSET_VALUE
@host_vars = {} if @host_vars == UNSET_VALUE
@inventory_path = nil if @inventory_path == UNSET_VALUE
@limit = nil if @limit == UNSET_VALUE
@playbook = nil if @playbook == UNSET_VALUE
@playbook_command = PLAYBOOK_COMMAND_DEFAULT if @playbook_command == UNSET_VALUE
@raw_arguments = nil if @raw_arguments == UNSET_VALUE
@skip_tags = nil if @skip_tags == UNSET_VALUE
@start_at_task = nil if @start_at_task == UNSET_VALUE
@sudo = false if @sudo != true
@sudo_user = nil if @sudo_user == UNSET_VALUE
@tags = nil if @tags == UNSET_VALUE
@vault_password_file = nil if @vault_password_file == UNSET_VALUE
@verbose = false if @verbose == UNSET_VALUE
end
# Just like the normal configuration "validate" method except that

View File

@ -26,25 +26,44 @@ module VagrantPlugins
end
def check_files_existence
check_path_is_a_file config.playbook, :playbook
check_path_is_a_file(config.playbook, :playbook)
check_path_exists config.inventory_path, :inventory_path if config.inventory_path
check_path_is_a_file config.extra_vars[1..-1], :extra_vars if has_an_extra_vars_file_argument
check_path_is_a_file config.galaxy_role_file, :galaxy_role_file if config.galaxy_role_file
check_path_is_a_file config.vault_password_file, :vault_password if config.vault_password_file
check_path_exists(config.inventory_path, :inventory_path) if config.inventory_path
check_path_is_a_file(config.config_file, :config_file) if config.config_file
check_path_is_a_file(config.extra_vars[1..-1], :extra_vars) if has_an_extra_vars_file_argument
check_path_is_a_file(config.galaxy_role_file, :galaxy_role_file) if config.galaxy_role_file
check_path_is_a_file(config.vault_password_file, :vault_password_file) if config.vault_password_file
end
def get_environment_variables_for_shell_execution
shell_env_vars = []
@environment_variables.each_pair do |k, v|
if k =~ /ANSIBLE_SSH_ARGS|ANSIBLE_ROLES_PATH|ANSIBLE_CONFIG/
shell_env_vars << "#{k}='#{v}'"
else
shell_env_vars << "#{k}=#{v}"
end
end
shell_env_vars
end
def ansible_galaxy_command_for_shell_execution
command_values = {
role_file: "'#{get_galaxy_role_file}'",
roles_path: "'#{get_galaxy_roles_path}'"
}
shell_command = get_environment_variables_for_shell_execution
shell_command << config.galaxy_command % command_values
shell_command.flatten.join(' ')
end
def ansible_playbook_command_for_shell_execution
shell_command = []
@environment_variables.each_pair do |k, v|
if k =~ /ANSIBLE_SSH_ARGS|ANSIBLE_ROLES_PATH/
shell_command << "#{k}='#{v}'"
else
shell_command << "#{k}=#{v}"
end
end
shell_command = get_environment_variables_for_shell_execution
shell_command << "ansible-playbook"
shell_command << config.playbook_command
shell_args = []
@command_arguments.each do |arg|
@ -102,6 +121,12 @@ module VagrantPlugins
# Use ANSIBLE_ROLES_PATH to tell ansible-playbook where to look for roles
# (there is no equivalent command line argument in ansible-playbook)
@environment_variables["ANSIBLE_ROLES_PATH"] = get_galaxy_roles_path if config.galaxy_roles_path
prepare_ansible_config_environment_variable
end
def prepare_ansible_config_environment_variable
@environment_variables["ANSIBLE_CONFIG"] = config.config_file if config.config_file
end
# Auto-generate "safe" inventory file based on Vagrantfile,

View File

@ -52,9 +52,9 @@ module VagrantPlugins
@machine.guest.capability(:ansible_install, config.install_mode, config.version)
end
# Check that ansible binaries are well installed on the guest,
# Check that Ansible Playbook command is available on the guest
@machine.communicate.execute(
"ansible-galaxy info --help && ansible-playbook --help",
"test -x \"$(command -v #{config.playbook_command})\"",
error_class: Ansible::Errors::AnsibleNotFoundOnGuest,
error_key: :ansible_not_found_on_guest
)
@ -72,14 +72,9 @@ module VagrantPlugins
end
def execute_ansible_galaxy_on_guest
command_values = {
role_file: "'#{get_galaxy_role_file}'",
roles_path: "'#{get_galaxy_roles_path}'"
}
prepare_ansible_config_environment_variable
remote_command = config.galaxy_command % command_values
execute_ansible_command_on_guest "galaxy", remote_command
execute_ansible_command_on_guest "galaxy", ansible_galaxy_command_for_shell_execution
end
def execute_ansible_playbook_on_guest
@ -156,7 +151,7 @@ module VagrantPlugins
# and error if it doesn't exist.
remote_path = Helpers::expand_path_in_unix_style(path, config.provisioning_path)
command = "test #{test_args} #{remote_path}"
command = "test #{test_args} '#{remote_path}'"
@machine.communicate.execute(
command,

View File

@ -20,6 +20,7 @@ module VagrantPlugins
check_files_existence
warn_for_unsupported_platform
execute_ansible_galaxy_from_host if config.galaxy_role_file
execute_ansible_playbook_from_host
end
@ -88,6 +89,8 @@ module VagrantPlugins
end
def execute_ansible_galaxy_from_host
prepare_ansible_config_environment_variable
command_values = {
role_file: get_galaxy_role_file,
roles_path: get_galaxy_roles_path
@ -97,23 +100,23 @@ module VagrantPlugins
command = str_command.split(VAGRANT_ARG_SEPARATOR)
command << {
env: @environment_variables,
# Write stdout and stderr data, since it's the regular Ansible output
notify: [:stdout, :stderr],
workdir: @machine.env.root_path.to_s
}
# FIXME: role_file and roles_path arguments should be quoted in the console output
ui_running_ansible_command "galaxy", str_command.gsub(VAGRANT_ARG_SEPARATOR, ' ')
ui_running_ansible_command "galaxy", ansible_galaxy_command_for_shell_execution
execute_command_from_host command
end
def execute_ansible_playbook_from_host
prepare_command_arguments
prepare_environment_variables
prepare_command_arguments
# Assemble the full ansible-playbook command
command = %w(ansible-playbook) << @command_arguments
command = [config.playbook_command] << @command_arguments
# Add the raw arguments at the end, to give them the highest precedence
command << config.raw_arguments if config.raw_arguments
@ -234,6 +237,7 @@ module VagrantPlugins
proxy_cmd += " exec nc %h %p 2>/dev/null"
ssh_options << "-o ProxyCommand='#{ proxy_cmd }'"
# TODO ssh_options << "-o ProxyCommand=\"#{ proxy_cmd }\""
end
# Use an SSH ProxyCommand when corresponding Vagrant setting is defined

View File

@ -64,16 +64,6 @@ module VagrantPlugins
# @return [String]
attr_accessor :installer_download_path
# @deprecated
def prerelease=(value)
STDOUT.puts <<-EOH
[DEPRECATED] The configuration `chef.prerelease' has been deprecated. Please use
`chef.channel' instead. The default value for channel is "stable", which
includes the latest published versions of the Chef Client. You can choose to use
prerelease versions by setting the channel to "current".
EOH
end
def initialize
super

View File

@ -5,15 +5,6 @@ module VagrantPlugins
class Config < Vagrant.plugin("2", :config)
attr_reader :images
def version=(value)
STDOUT.puts <<-EOH
[DEPRECATED] The configuration `docker.version' has been deprecated. Docker no
longer allows you to specify the version of Docker you want installed and will
automatically choose the best version for your guest. Please remove this option
from your Vagrantfile.
EOH
end
def initialize
@images = Set.new

View File

@ -13,6 +13,7 @@ module VagrantPlugins
attr_accessor :manifests_path
attr_accessor :environment
attr_accessor :environment_path
attr_accessor :environment_variables
attr_accessor :module_path
attr_accessor :options
attr_accessor :synced_folder_type
@ -23,18 +24,19 @@ module VagrantPlugins
def initialize
super
@binary_path = UNSET_VALUE
@hiera_config_path = UNSET_VALUE
@manifest_file = UNSET_VALUE
@manifests_path = UNSET_VALUE
@environment = UNSET_VALUE
@environment_path = UNSET_VALUE
@module_path = UNSET_VALUE
@options = []
@facter = {}
@synced_folder_type = UNSET_VALUE
@temp_dir = UNSET_VALUE
@working_directory = UNSET_VALUE
@binary_path = UNSET_VALUE
@hiera_config_path = UNSET_VALUE
@manifest_file = UNSET_VALUE
@manifests_path = UNSET_VALUE
@environment = UNSET_VALUE
@environment_path = UNSET_VALUE
@environment_variables = UNSET_VALUE
@module_path = UNSET_VALUE
@options = []
@facter = {}
@synced_folder_type = UNSET_VALUE
@temp_dir = UNSET_VALUE
@working_directory = UNSET_VALUE
end
def nfs=(value)
@ -87,6 +89,10 @@ module VagrantPlugins
end
end
if @environment_variables == UNSET_VALUE
@environment_variables = {}
end
@binary_path = nil if @binary_path == UNSET_VALUE
@module_path = nil if @module_path == UNSET_VALUE
@synced_folder_type = nil if @synced_folder_type == UNSET_VALUE

View File

@ -207,7 +207,7 @@ module VagrantPlugins
options = options.join(" ")
# Build up the custom facts if we have any
facter = ""
facter = nil
if !config.facter.empty?
facts = []
config.facter.each do |key, value|
@ -219,7 +219,7 @@ module VagrantPlugins
facts.map! { |v| "$env:#{v};" }
end
facter = "#{facts.join(" ")} "
facter = facts.join(" ")
end
puppet_bin = "puppet"
@ -227,7 +227,28 @@ module VagrantPlugins
puppet_bin = File.join(@config.binary_path, puppet_bin)
end
command = "#{facter} #{puppet_bin} apply #{options}"
env_vars = nil
if !config.environment_variables.nil? && !config.environment_variables.empty?
env_vars = config.environment_variables.map do |env_key, env_value|
"#{env_key}=\"#{env_value}\""
end
if windows?
env_vars.map! do |env_var_string|
"$env:#{env_var_string}"
end
end
env_vars = env_vars.join(" ")
end
command = [
env_vars,
facter,
puppet_bin,
"apply",
options
].compact.map(&:to_s).join(" ")
if config.working_directory
if windows?
command = "cd #{config.working_directory}; if ($?) \{ #{command} \}"

View File

@ -4,11 +4,6 @@ require "vagrant/util/deep_merge"
module VagrantPlugins
module Salt
class Config < Vagrant.plugin("2", :config)
## @deprecated
def config_dir=(value)
puts "salt config_dir is deprecated and will be removed in Vagrant 1.9"
end
## salty-vagrant options
attr_accessor :minion_config
attr_accessor :minion_key
@ -74,12 +69,6 @@ module VagrantPlugins
end
def finalize!
@minion_config = nil if @minion_config == UNSET_VALUE
@minion_key = nil if @minion_key == UNSET_VALUE
@minion_pub = nil if @minion_pub == UNSET_VALUE
@master_config = nil if @master_config == UNSET_VALUE
@master_key = nil if @master_key == UNSET_VALUE
@master_pub = nil if @master_pub == UNSET_VALUE
@grains_config = nil if @grains_config == UNSET_VALUE
@run_highstate = nil if @run_highstate == UNSET_VALUE
@run_overstate = nil if @run_overstate == UNSET_VALUE
@ -102,6 +91,15 @@ module VagrantPlugins
@version = nil if @version == UNSET_VALUE
@run_service = nil if @run_service == UNSET_VALUE
@master_id = nil if @master_id == UNSET_VALUE
# NOTE: Optimistic defaults are set in the provisioner. UNSET_VALUEs
# are converted there to allow proper detection of unset values.
# @minion_config = nil if @minion_config == UNSET_VALUE
# @minion_key = nil if @minion_key == UNSET_VALUE
# @minion_pub = nil if @minion_pub == UNSET_VALUE
# @master_config = nil if @master_config == UNSET_VALUE
# @master_key = nil if @master_key == UNSET_VALUE
# @master_pub = nil if @master_pub == UNSET_VALUE
end
def pillar(data)
@ -111,14 +109,14 @@ module VagrantPlugins
def validate(machine)
errors = _detected_errors
if @minion_config
if @minion_config && @minion_config != UNSET_VALUE
expanded = Pathname.new(@minion_config).expand_path(machine.env.root_path)
if !expanded.file?
errors << I18n.t("vagrant.provisioners.salt.minion_config_nonexist", missing_config_file: expanded)
end
end
if @master_config
if @master_config && @master_config != UNSET_VALUE
expanded = Pathname.new(@master_config).expand_path(machine.env.root_path)
if !expanded.file?
errors << I18n.t("vagrant.provisioners.salt.master_config_nonexist", missing_config_file: expanded)

View File

@ -3,7 +3,20 @@ require 'json'
module VagrantPlugins
module Salt
class Provisioner < Vagrant.plugin("2", :provisioner)
# Default path values to set within configuration only
# if configuration value is unset and local path exists
OPTIMISTIC_PATH_DEFAULTS = Hash[*[
"minion_config", "salt/minion",
"minion_key", "salt/key/minion.key",
"minion_pub", "salt/key/minion.pub",
"master_config", "salt/master",
"master_key", "salt/key/master.key",
"master_pub", "salt/key/master.pub"
].map(&:freeze)].freeze
def provision
set_default_configs
upload_configs
upload_keys
run_bootstrap_script
@ -238,19 +251,19 @@ module VagrantPlugins
bootstrap_path = get_bootstrap
if @machine.config.vm.communicator == :winrm
if @config.version
options += " -version %s" % @config.version
options += " -version %s" % @config.version
end
if @config.run_service
@machine.env.ui.info "Salt minion will be stopped after installing."
options += " -runservice %s" % @config.run_service
options += " -runservice %s" % @config.run_service
end
if @config.minion_id
@machine.env.ui.info "Setting minion to @config.minion_id."
options += " -minion %s" % @config.minion_id
options += " -minion %s" % @config.minion_id
end
if @config.master_id
@machine.env.ui.info "Setting master to @config.master_id."
options += " -master %s" % @config.master_id
options += " -master %s" % @config.master_id
end
bootstrap_destination = File.join(config_dir, "bootstrap_salt.ps1")
else
@ -390,6 +403,16 @@ module VagrantPlugins
@machine.communicate.sudo(cmd, &log_output)
end
end
# Sets optimistic default values into config
def set_default_configs
OPTIMISTIC_PATH_DEFAULTS.each do |config_key, config_default|
if config.send(config_key) == Config::UNSET_VALUE
config_value = File.exist?(expanded_path(config_default)) ? config_default : nil
config.send("#{config_key}=", config_value)
end
end
end
end
end
end

View File

@ -5,6 +5,8 @@ module VagrantPlugins
class Config < Vagrant.plugin("2", :config)
attr_accessor :inline
attr_accessor :path
attr_accessor :md5
attr_accessor :sha1
attr_accessor :env
attr_accessor :upload_path
attr_accessor :args
@ -19,6 +21,8 @@ module VagrantPlugins
@args = UNSET_VALUE
@inline = UNSET_VALUE
@path = UNSET_VALUE
@md5 = UNSET_VALUE
@sha1 = UNSET_VALUE
@env = UNSET_VALUE
@upload_path = UNSET_VALUE
@privileged = UNSET_VALUE
@ -33,6 +37,8 @@ module VagrantPlugins
@args = nil if @args == UNSET_VALUE
@inline = nil if @inline == UNSET_VALUE
@path = nil if @path == UNSET_VALUE
@md5 = nil if @md5 == UNSET_VALUE
@sha1 = nil if @sha1 == UNSET_VALUE
@env = {} if @env == UNSET_VALUE
@upload_path = "/tmp/vagrant-shell" if @upload_path == UNSET_VALUE
@privileged = true if @privileged == UNSET_VALUE

View File

@ -177,7 +177,12 @@ module VagrantPlugins
download_path.delete if download_path.file?
begin
Vagrant::Util::Downloader.new(config.path, download_path).download!
Vagrant::Util::Downloader.new(
config.path,
download_path,
md5: config.md5,
sha1: config.sha1
).download!
ext = File.extname(config.path)
script = download_path.read
ensure

View File

@ -32,7 +32,7 @@ module VagrantPlugins
exclude_base = Pathname.new(opts[:guestpath])
exclusions = Array(opts[:exclude]).map do |ex_path|
ex_path = ex_path.slice(1, ex_path.size) if ex_path.start_with?(File::SEPARATOR)
"-path #{exclude_base.join(ex_path)} -prune"
"-path #{Shellwords.escape(exclude_base.join(ex_path))} -prune"
end.join(" -o ") + " -o "
end
"find #{guest_path} #{exclusions}" \

71
templates/locales/en.yml Executable file → Normal file
View File

@ -734,6 +734,14 @@ en:
downloader_interrupted: |-
The download was interrupted by an external signal. It did not
complete.
downloader_checksum_error: |-
The calculated checksum of the requested file does not match the expected
checksum!
File source: %{source}
Checsum type: %{type}
Expected checksum: %{expected_checksum}
Calculated checksum: %{actual_checksum}
env_inval: |-
Vagrant received an "EINVAL" error while attempting to set some
environment variables. This is usually caused by the total size of your
@ -877,6 +885,14 @@ en:
the issues below and execute "vagrant reload":
%{output}
nfs_exports_failed: |-
Vagrant failed to install an updated NFS exports file. This may be
due to overly restrictive permissions on your NFS exports file. Please
validate them and try again.
command: %{command}
stdout: %{stdout}
stderr: %{stderr}
nfs_cant_read_exports: |-
Vagrant can't read your current NFS exports! The exports file should be
readable by any user. This is usually caused by invalid permissions
@ -966,6 +982,20 @@ en:
by contacting a plugin author to see if they can address the conflict.
%{conflicts}
plugin_init_error: |-
The plugins failed to initialize correctly. This may be due to manual
modifications made within the Vagrant home directory. Vagrant can
attempt to automatically correct this issue by running:
vagrant plugin repair
If Vagrant was recently updated, this error may be due to incompatible
versions of dependencies. To fix this problem please remove and re-install
all plugins. Vagrant can attempt to do this automatically by running:
vagrant plugin expunge --reinstall
Error message given during initialization: %{message}
plugin_load_error: |-
The plugins failed to load properly. The error message given is
shown below.
@ -1065,6 +1095,15 @@ en:
Guest path: %{guestpath}
Command: %{command}
Error: %{stderr}
rsync_guest_install_error: |-
Installation of rsync into the guest has failed! The stdout
and stderr are shown below. Please read the error output, resolve
it and try again. If the problem persists, please install rsync
manually within the guest.
Command: %{command}
Stdout: %{stdout}
Stderr: %{stderr}
rsync_not_found: |-
"rsync" could not be found on your PATH. Make sure that rsync
is properly installed on your system and available on the PATH.
@ -1559,6 +1598,24 @@ en:
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
plugin:
expunge_confirm: |-
This command permanently deletes all currently installed user plugins. It
should only be used when a repair command is unable to properly fix the
system.
Continue?
expunge_request_reinstall: |-
Would you like Vagrant to attempt to reinstall current plugins?
expunge_complete: |-
All user installed plugins have been removed from this Vagrant environment!
expunge_reinstall: |-
Vagrant will now attempt to reinstall user plugins that were removed.
expunge_aborted: |-
Vagrant expunge has been declined. Skipping removal of plugins.
installed_license: |-
The license for '%{name}' was successfully installed!
installing_license: |-
@ -1584,6 +1641,20 @@ en:
post_install: |-
Post install message from the '%{name}' plugin:
%{message}
repairing: |-
Repairing currently installed plugins. This may take a few minutes...
repair_complete: |-
Installed plugins successfully repaired!
repair_failed: |-
Failed to automatically repair installed Vagrant plugins. To fix this
problem remove all user installed plugins and reinstall. Vagrant can
do this for you automatically by running the following command:
vagrant plugin expunge --reinstall
Failure message received during repair:
%{message}
snapshot:
not_supported: |-

View File

@ -0,0 +1,194 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/box/command/prune")
describe VagrantPlugins::CommandBox::Command::Prune do
include_context "unit"
include_context "command plugin helpers"
let(:entry_klass) { Vagrant::MachineIndex::Entry }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
isolated_environment.tap do |env|
env.vagrantfile("")
end
end
let(:iso_vagrant_env) { iso_env.create_vagrant_env }
let(:argv) { [] }
# Seems this way of providing a box version triggers box in use.
def new_entry(name, box_name, box_provider, version)
entry_klass.new.tap do |e|
e.name = name
e.vagrantfile_path = "/bar"
e.extra_data["box"] = {
"name" => box_name,
"provider" => box_provider,
"version" => version,
}
end
end
subject { described_class.new(argv, iso_vagrant_env) }
describe "execute" do
context "with no args" do
it "removes the old version and keeps the current one" do
# Let's put some things in the index
iso_env.box3("foobox", "1.0", :virtualbox);
iso_env.box3("foobox", "1.1", :virtualbox);
iso_env.box3("barbox", "1.0", :vmware);
iso_env.box3("barbox", "1.1", :vmware);
iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1))
output = ""
allow(iso_vagrant_env.ui).to receive(:info) do |data|
output << data
end
expect(iso_vagrant_env.boxes.all.count).to eq(4)
expect(subject.execute).to eq(0)
expect(iso_vagrant_env.boxes.all.count).to eq(2)
expect(output).to include("barbox (vmware, 1.1)")
expect(output).to include("Removing box 'barbox' (v1.0) with provider 'vmware'...")
expect(output).to include("foobox (virtualbox, 1.1)")
expect(output).to include("Removing box 'foobox' (v1.0) with provider 'virtualbox'...")
end
it "removes nothing" do
# Let's put some things in the index
iso_env.box3("foobox", "1.0", :virtualbox);
iso_env.box3("barbox", "1.0", :vmware);
iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1))
output = ""
allow(iso_vagrant_env.ui).to receive(:info) do |data|
output << data
end
expect(iso_vagrant_env.boxes.all.count).to eq(2)
expect(subject.execute).to eq(0)
expect(iso_vagrant_env.boxes.all.count).to eq(2)
expect(output).to include("No old versions of boxes to remove...")
end
end
context "with --provider" do
let(:argv) { ["--provider", "virtualbox"] }
it "removes the old versions of the specified provider" do
# Let's put some things in the index
iso_env.box3("foobox", "1.0", :virtualbox);
iso_env.box3("foobox", "1.1", :virtualbox);
iso_env.box3("barbox", "1.0", :vmware);
iso_env.box3("barbox", "1.1", :vmware);
iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1))
output = ""
allow(iso_vagrant_env.ui).to receive(:info) do |data|
output << "\n" + data
end
expect(iso_vagrant_env.boxes.all.count).to eq(4)
expect(subject.execute).to eq(0)
expect(iso_vagrant_env.boxes.all.count).to eq(3)
expect(output).to include("foobox (virtualbox, 1.1)")
expect(output).to include("Removing box 'foobox' (v1.0) with provider 'virtualbox'...")
end
end
context "with --dry-run" do
let(:argv) { ["--dry-run"] }
it "removes the old versions of the specified provider" do
# Let's put some things in the index
iso_env.box3("foobox", "1.0", :virtualbox);
iso_env.box3("foobox", "1.1", :virtualbox);
iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1))
output = ""
allow(iso_vagrant_env.ui).to receive(:info) do |data|
output << "\n" + data
end
expect(iso_vagrant_env.boxes.all.count).to eq(2)
expect(subject.execute).to eq(0)
expect(iso_vagrant_env.boxes.all.count).to eq(2)
expect(output).to include("foobox (virtualbox, 1.1)")
expect(output).to include("Would remove foobox virtualbox 1.0")
end
end
context "with --name" do
let(:argv) { ["--name", "barbox"] }
it "removes the old versions of the specified provider" do
# Let's put some things in the index
iso_env.box3("foobox", "1.0", :virtualbox);
iso_env.box3("foobox", "1.1", :virtualbox);
iso_env.box3("barbox", "1.0", :vmware);
iso_env.box3("barbox", "1.1", :vmware);
iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1))
output = ""
allow(iso_vagrant_env.ui).to receive(:info) do |data|
output << "\n" + data
end
expect(iso_vagrant_env.boxes.all.count).to eq(4)
expect(subject.execute).to eq(0)
expect(iso_vagrant_env.boxes.all.count).to eq(3)
expect(output).to include("barbox (vmware, 1.1)")
expect(output).to include("Removing box 'barbox' (v1.0) with provider 'vmware'...")
end
end
context "with --name and --provider" do
let(:argv) { ["--name", "foobox", "--provider", "virtualbox"] }
it "removed the old versions of that name and provider only" do
# Let's put some things in the index
iso_env.box3("foobox", "1.0", :virtualbox);
iso_env.box3("foobox", "1.1", :virtualbox);
iso_env.box3("foobox", "1.0", :vmware);
iso_env.box3("foobox", "1.1", :vmware);
iso_env.box3("barbox", "1.0", :vmware);
iso_env.box3("barbox", "1.1", :vmware);
iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1))
output = ""
allow(iso_vagrant_env.ui).to receive(:info) do |data|
output << "\n" + data
end
expect(iso_vagrant_env.boxes.all.count).to eq(6)
expect(subject.execute).to eq(0)
expect(iso_vagrant_env.boxes.all.count).to eq(5)
expect(output).to include("Removing box 'foobox' (v1.0) with provider 'virtualbox'...")
end
end
end
end

View File

@ -0,0 +1,64 @@
require File.expand_path("../../../../../base", __FILE__)
describe VagrantPlugins::CommandPlugin::Action::ExpungePlugins do
let(:app) { lambda { |env| } }
let(:home_path){ '/fake/file/path/.vagrant.d' }
let(:gems_path){ "#{home_path}/gems" }
let(:force){ true }
let(:env) {{
ui: Vagrant::UI::Silent.new,
home_path: home_path,
gems_path: gems_path,
force: force
}}
let(:manager) { double("manager") }
let(:expect_to_receive) do
lambda do
allow(File).to receive(:exist?).with(File.join(home_path, 'plugins.json')).and_return(true)
allow(File).to receive(:directory?).with(gems_path).and_return(true)
expect(FileUtils).to receive(:rm).with(File.join(home_path, 'plugins.json'))
expect(FileUtils).to receive(:rm_rf).with(gems_path)
expect(app).to receive(:call).with(env).once
end
end
subject { described_class.new(app, env) }
before do
Vagrant::Plugin::Manager.stub(instance: manager)
end
describe "#call" do
before do
instance_exec(&expect_to_receive)
end
it "should delete all plugins" do
subject.call(env)
end
describe "when force is false" do
let(:force){ false }
it "should prompt user before deleting all plugins" do
expect(env[:ui]).to receive(:ask).and_return("Y\n")
subject.call(env)
end
describe "when user declines prompt" do
let(:expect_to_receive) do
lambda do
expect(app).not_to receive(:call)
end
end
it "should not delete all plugins" do
expect(env[:ui]).to receive(:ask).and_return("N\n")
subject.call(env)
end
end
end
end
end

View File

@ -18,12 +18,14 @@ describe VagrantPlugins::CommandPlugin::Action::UpdateGems do
describe "#call" do
it "should update all plugins if none are specified" do
expect(manager).to receive(:update_plugins).with([]).once.and_return([])
expect(manager).to receive(:installed_plugins).twice.and_return({})
expect(app).to receive(:call).with(env).once
subject.call(env)
end
it "should update specified plugins" do
expect(manager).to receive(:update_plugins).with(["foo"]).once.and_return([])
expect(manager).to receive(:installed_plugins).twice.and_return({})
expect(app).to receive(:call).with(env).once
env[:plugin_name] = ["foo"]

View File

@ -5,6 +5,8 @@ require Vagrant.source_root.join("plugins/communicators/ssh/communicator")
describe VagrantPlugins::CommunicatorSSH::Communicator do
include_context "unit"
let(:export_command_template){ 'export %ENV_KEY%="%ENV_VALUE%"' }
# SSH configuration information mock
let(:ssh) do
double("ssh",
@ -15,6 +17,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
pty: false,
keep_alive: false,
insert_key: false,
export_command_template: export_command_template,
shell: 'bash -l'
)
end
@ -509,4 +512,18 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
end
end
end
describe ".generate_environment_export" do
it "should generate bourne shell compatible export" do
communicator.send(:generate_environment_export, "TEST", "value").should eq("export TEST=\"value\"\n")
end
context "with custom template defined" do
let(:export_command_template){ "setenv %ENV_KEY% %ENV_VALUE%" }
it "should generate custom export based on template" do
communicator.send(:generate_environment_export, "TEST", "value").should eq("setenv TEST value\n")
end
end
end
end

View File

@ -45,7 +45,7 @@ describe VagrantPlugins::CommunicatorWinRM::Communicator do
port: '22',
})
# Makes ready? return true
allow(shell).to receive(:powershell).with("hostname").and_return({ exitcode: 0 })
allow(shell).to receive(:cmd).with("hostname").and_return({ exitcode: 0 })
end
it "retries ssh_info until ready" do
@ -57,22 +57,22 @@ describe VagrantPlugins::CommunicatorWinRM::Communicator do
describe ".ready?" do
it "returns true if hostname command executes without error" do
expect(shell).to receive(:powershell).with("hostname").and_return({ exitcode: 0 })
expect(shell).to receive(:cmd).with("hostname").and_return({ exitcode: 0 })
expect(subject.ready?).to be_true
end
it "returns false if hostname command fails with a transient error" do
expect(shell).to receive(:powershell).with("hostname").and_raise(VagrantPlugins::CommunicatorWinRM::Errors::TransientError)
expect(shell).to receive(:cmd).with("hostname").and_raise(VagrantPlugins::CommunicatorWinRM::Errors::TransientError)
expect(subject.ready?).to be_false
end
it "raises an error if hostname command fails with an unknown error" do
expect(shell).to receive(:powershell).with("hostname").and_raise(Vagrant::Errors::VagrantError)
expect(shell).to receive(:cmd).with("hostname").and_raise(Vagrant::Errors::VagrantError)
expect { subject.ready? }.to raise_error(Vagrant::Errors::VagrantError)
end
it "raises timeout error when hostname command takes longer then winrm timeout" do
expect(shell).to receive(:powershell).with("hostname") do
expect(shell).to receive(:cmd).with("hostname") do
sleep 2 # winrm.timeout = 1
end
expect { subject.ready? }.to raise_error(Timeout::Error)

View File

@ -6,7 +6,7 @@ require Vagrant.source_root.join("plugins/communicators/winrm/config")
describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
include_context "unit"
let(:session) { double("winrm_session", create_executor: executor) }
let(:session) { double("winrm_session") }
let(:executor) { double("command_executor") }
let(:port) { config.transport == :ssl ? 5986 : 5985 }
let(:config) {
@ -22,6 +22,8 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
end
}
before { allow(session).to receive(:create_executor).and_yield(executor) }
subject do
described_class.new('localhost', port, config).tap do |comm|
allow(comm).to receive(:new_session).and_return(session)

View File

@ -31,10 +31,9 @@ describe "VagrantPlugins::GuestBSD::Cap::NFS" do
}
cap.mount_nfs_folder(machine, ip, folders)
expect(comm.received_commands[0]).to match(/set -e/)
expect(comm.received_commands[0]).to match(/mkdir -p \/guest/)
expect(comm.received_commands[0]).to match(/mount -t nfs/)
expect(comm.received_commands[0]).to match(/1.2.3.4:\/host \/guest/)
expect(comm.received_commands[1]).to match(/mount -t nfs/)
expect(comm.received_commands[1]).to match(/1.2.3.4:\/host \/guest/)
end
it "mounts with options" do
@ -49,7 +48,7 @@ describe "VagrantPlugins::GuestBSD::Cap::NFS" do
}
cap.mount_nfs_folder(machine, ip, folders)
expect(comm.received_commands[0]).to match(/mount -t nfs -o 'nfsv2,mntudp,banana'/)
expect(comm.received_commands[1]).to match(/mount -t nfs -o 'nfsv2,mntudp,banana'/)
end
it "escapes host and guest paths" do
@ -61,8 +60,8 @@ describe "VagrantPlugins::GuestBSD::Cap::NFS" do
}
cap.mount_nfs_folder(machine, ip, folders)
expect(comm.received_commands[0]).to match(/host\\\'s/)
expect(comm.received_commands[0]).to match(/guest\\\ with\\\ spaces/)
expect(comm.received_commands[1]).to match(/host\\\'s/)
expect(comm.received_commands[1]).to match(/guest\\\ with\\\ spaces/)
end
end
end

View File

@ -43,7 +43,7 @@ describe "VagrantPlugins::GuestLinux::Cap::MountNFS" do
cap.mount_nfs_folder(machine, ip, folders)
expect(comm.received_commands[0]).to match(/mkdir -p #{guestpath}/)
expect(comm.received_commands[0]).to match(/1.2.3.4:#{hostpath} #{guestpath}/)
expect(comm.received_commands[1]).to match(/1.2.3.4:#{hostpath} #{guestpath}/)
end
it "mounts with options" do
@ -58,7 +58,7 @@ describe "VagrantPlugins::GuestLinux::Cap::MountNFS" do
}
cap.mount_nfs_folder(machine, ip, folders)
expect(comm.received_commands[0]).to match(/mount -o vers=2,udp/)
expect(comm.received_commands[1]).to match(/mount -o vers=2,udp/)
end
it "emits an event" do
@ -71,7 +71,7 @@ describe "VagrantPlugins::GuestLinux::Cap::MountNFS" do
}
cap.mount_nfs_folder(machine, ip, folders)
expect(comm.received_commands[0]).to include(
expect(comm.received_commands[1]).to include(
"/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guestpath}")
end
@ -84,8 +84,8 @@ describe "VagrantPlugins::GuestLinux::Cap::MountNFS" do
}
cap.mount_nfs_folder(machine, ip, folders)
expect(comm.received_commands[0]).to match(/host\\\'s/)
expect(comm.received_commands[0]).to match(/guest\\\ with\\\ spaces/)
expect(comm.received_commands[1]).to match(/host\\\'s/)
expect(comm.received_commands[1]).to match(/guest\\\ with\\\ spaces/)
end
end
end

View File

@ -44,5 +44,41 @@ describe "VagrantPlugins::GuestLinux::Cap::NetworkInterfaces" do
result = cap.network_interfaces(machine)
expect(result).to eq(["enp0s3", "enp0s5", "enp0s8", "enp0s10", "enp1s3"])
end
it "sorts ethernet devices discovered with classic naming first in list" do
expect(comm).to receive(:sudo).and_yield(:stdout, "eth1\neth2\ndocker0\nbridge0\neth0")
result = cap.network_interfaces(machine)
expect(result).to eq(["eth0", "eth1", "eth2", "bridge0", "docker0"])
end
it "sorts ethernet devices discovered with predictable network interfaces naming first in list" do
expect(comm).to receive(:sudo).and_yield(:stdout, "enp0s8\ndocker0\nenp0s3\nbridge0\nenp0s5")
result = cap.network_interfaces(machine)
expect(result).to eq(["enp0s3", "enp0s5", "enp0s8", "bridge0", "docker0"])
end
it "sorts ethernet devices discovered with predictable network interfaces naming first in list with less" do
expect(comm).to receive(:sudo).and_yield(:stdout, "enp0s3\nenp0s8\ndocker0")
result = cap.network_interfaces(machine)
expect(result).to eq(["enp0s3", "enp0s8", "docker0"])
end
it "does not include ethernet devices aliases within prefix device listing" do
expect(comm).to receive(:sudo).and_yield(:stdout, "eth1\neth2\ndocker0\nbridge0\neth0\ndocker1\neth0:0")
result = cap.network_interfaces(machine)
expect(result).to eq(["eth0", "eth1", "eth2", "bridge0", "docker0", "docker1", "eth0:0"])
end
it "does not include ethernet devices aliases within prefix device listing with dot separators" do
expect(comm).to receive(:sudo).and_yield(:stdout, "eth1\neth2\ndocker0\nbridge0\neth0\ndocker1\neth0.1@eth0")
result = cap.network_interfaces(machine)
expect(result).to eq(["eth0", "eth1", "eth2", "bridge0", "docker0", "docker1", "eth0.1@eth0"])
end
it "properly sorts non-consistent device name formats" do
expect(comm).to receive(:sudo).and_yield(:stdout, "eth0\neth1\ndocker0\nveth437f7f9\nveth06b3e44\nveth8bb7081")
result = cap.network_interfaces(machine)
expect(result).to eq(["eth0", "eth1", "docker0", "veth8bb7081", "veth437f7f9", "veth06b3e44"])
end
end
end

View File

@ -70,15 +70,27 @@ describe "VagrantPlugins::GuestLinux::Cap::Rsync" do
end
context "with excludes provided" do
let(:excludes){ ["tmp", "state/*"] }
let(:excludes){ ["tmp", "state/*", "path/with a/space"] }
it "ignores files that are excluded" do
comm.expect_command(
"find #{guest_directory} -path #{File.join(guest_directory, excludes.first)} -prune -o " \
"-path #{File.join(guest_directory, excludes.last)} -prune -o '!' -type l -a '(' ! -user " \
"#{owner} -or ! -group #{group} ')' -exec chown #{owner}:#{group} '{}' +"
)
# comm.expect_command(
# "find #{guest_directory} -path #{Shellwords.escape(File.join(guest_directory, excludes.first))} -prune -o " \
# "-path #{Shellwords.escape(File.join(guest_directory, excludes.last))} -prune -o '!' " \
# "-path -type l -a '(' ! -user " \
# "#{owner} -or ! -group #{group} ')' -exec chown #{owner}:#{group} '{}' +"
# )
cap.rsync_post(machine, options)
excludes.each do |ex_path|
expect(comm.received_commands.first).to include("-path #{Shellwords.escape(File.join(guest_directory, ex_path))} -prune")
end
end
it "properly escapes excluded directories" do
cap.rsync_post(machine, options)
exclude_with_space = excludes.detect{|ex| ex.include?(' ')}
escaped_exclude_with_space = Shellwords.escape(exclude_with_space)
expect(comm.received_commands.first).not_to include(exclude_with_space)
expect(comm.received_commands.first).to include(escaped_exclude_with_space)
end
end
end

View File

@ -0,0 +1,59 @@
require_relative "../../../../base"
describe "VagrantPlugins::GuestOpenBSD::Cap::RSync" do
let(:caps) do
VagrantPlugins::GuestOpenBSD::Plugin
.components
.guest_capabilities[:openbsd]
end
let(:machine) { double("machine") }
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
before do
allow(machine).to receive(:communicate).and_return(comm)
end
after do
comm.verify_expectations!
end
describe ".rsync_install" do
let(:cap) { caps.get(:rsync_install) }
describe "successful installation" do
it "installs rsync" do
cap.rsync_install(machine)
expect(comm.received_commands[0]).to match(/pkg_add -I rsync/)
expect(comm.received_commands[1]).to match(/pkg_info/)
end
end
describe "failure installation" do
before do
expect(comm).to receive(:execute).and_raise(Vagrant::Errors::RSyncNotInstalledInGuest, {command: '', output: ''})
end
it "raises custom exception" do
expect{ cap.rsync_install(machine) }.to raise_error(Vagrant::Errors::RSyncNotInstalledInGuest)
end
end
end
describe ".rsync_installed" do
let(:cap) { caps.get(:rsync_installed) }
it "checks if rsync is installed" do
comm.expect_command("which rsync")
cap.rsync_installed(machine)
end
end
describe ".rsync_command" do
let(:cap) { caps.get(:rsync_command) }
it "defaults to 'sudo rsync'" do
expect(cap.rsync_command(machine)).to eq("sudo rsync")
end
end
end

View File

@ -45,7 +45,7 @@ describe "VagrantPlugins::GuestPhoton::Cap:ConfigureNetworks" do
it "creates and starts the networks" do
cap.configure_networks(machine, [network_1, network_2])
expect(comm.received_commands[1]).to match(/ifconfig eth1/)
expect(comm.received_commands[1]).to match(/ifconfig eth2 33.33.33.10 netmast 255.255.0.0/)
expect(comm.received_commands[1]).to match(/ifconfig eth2 33.33.33.10 netmask 255.255.0.0/)
end
end
end

View File

@ -56,9 +56,10 @@ describe "VagrantPlugins::GuestRedHat::Cap::ConfigureNetworks" do
cap.configure_networks(machine, [network_1, network_2])
expect(comm.received_commands[0]).to match(/\/sbin\/ifdown 'eth1'/)
expect(comm.received_commands[0]).to match(/\/sbin\/ifup 'eth1'/)
expect(comm.received_commands[0]).to match(/ifcfg-eth1/)
expect(comm.received_commands[0]).to match(/\/sbin\/ifdown 'eth2'/)
expect(comm.received_commands[0]).to match(/\/sbin\/ifup 'eth2'/)
expect(comm.received_commands[0]).to match(/ifcfg-eth2/)
expect(comm.received_commands[0]).to match(/nmcli c reload/)
end
end
end

View File

@ -0,0 +1,156 @@
require_relative "../../../../base"
require_relative "../../../../../../plugins/hosts/linux/cap/nfs"
require_relative "../../../../../../lib/vagrant/util"
describe VagrantPlugins::HostLinux::Cap::NFS do
include_context "unit"
let(:caps) do
VagrantPlugins::HostLinux::Plugin
.components
.host_capabilities[:linux]
end
let(:tmp_exports_path) do
@tmp_exports ||= temporary_file
end
let(:exports_path){ VagrantPlugins::HostLinux::Cap::NFS::NFS_EXPORTS_PATH }
let(:env){ double(:env) }
let(:ui){ double(:ui) }
let(:host){ double(:host) }
before do
@original_exports_path = VagrantPlugins::HostLinux::Cap::NFS::NFS_EXPORTS_PATH
VagrantPlugins::HostLinux::Cap::NFS.send(:remove_const, :NFS_EXPORTS_PATH)
VagrantPlugins::HostLinux::Cap::NFS.const_set(:NFS_EXPORTS_PATH, tmp_exports_path.to_s)
end
after do
VagrantPlugins::HostLinux::Cap::NFS.send(:remove_const, :NFS_EXPORTS_PATH)
VagrantPlugins::HostLinux::Cap::NFS.const_set(:NFS_EXPORTS_PATH, @original_exports_path)
File.unlink(tmp_exports_path.to_s) if File.exist?(tmp_exports_path.to_s)
@tmp_exports = nil
end
describe ".nfs_export" do
let(:cap){ caps.get(:nfs_export) }
before do
allow(env).to receive(:host).and_return(host)
allow(host).to receive(:capability).with(:nfs_apply_command).and_return("/bin/true")
allow(host).to receive(:capability).with(:nfs_check_command).and_return("/bin/true")
allow(host).to receive(:capability).with(:nfs_start_command).and_return("/bin/true")
allow(ui).to receive(:info)
allow(cap).to receive(:system).with("sudo /bin/true").and_return(true)
allow(cap).to receive(:system).with("/bin/true").and_return(true)
end
it "should export new entries" do
cap.nfs_export(env, ui, SecureRandom.uuid, ["127.0.0.1"], "tmp" => {:hostpath => "/tmp"})
exports_content = File.read(exports_path)
expect(exports_content).to match(/\/tmp.*127\.0\.0\.1/)
end
it "should not remove existing entries" do
File.write(exports_path, "/custom/directory hostname1(rw,sync,no_subtree_check)")
cap.nfs_export(env, ui, SecureRandom.uuid, ["127.0.0.1"], "tmp" => {:hostpath => "/tmp"})
exports_content = File.read(exports_path)
expect(exports_content).to match(/\/tmp.*127\.0\.0\.1/)
expect(exports_content).to match(/\/custom\/directory.*hostname1/)
end
it "should remove entries no longer valid" do
valid_id = SecureRandom.uuid
other_id = SecureRandom.uuid
content =<<-EOH
# VAGRANT-BEGIN: #{Process.uid} #{other_id}
"/tmp" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=)
# VAGRANT-END: #{Process.uid} #{other_id}
# VAGRANT-BEGIN: #{Process.uid} #{valid_id}
"/var" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=)
# VAGRANT-END: #{Process.uid} #{valid_id}
EOH
File.write(exports_path, content)
cap.nfs_export(env, ui, valid_id, ["127.0.0.1"], "home" => {:hostpath => "/home"})
exports_content = File.read(exports_path)
expect(exports_content).to include("/home")
expect(exports_content).to include("/tmp")
expect(exports_content).not_to include("/var")
end
end
describe ".nfs_prune" do
let(:cap){ caps.get(:nfs_prune) }
before do
allow(ui).to receive(:info)
end
it "should remove entries no longer valid" do
invalid_id = SecureRandom.uuid
valid_id = SecureRandom.uuid
content =<<-EOH
# VAGRANT-BEGIN: #{Process.uid} #{invalid_id}
"/tmp" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=)
# VAGRANT-END: #{Process.uid} #{invalid_id}
# VAGRANT-BEGIN: #{Process.uid} #{valid_id}
"/var" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=)
# VAGRANT-END: #{Process.uid} #{valid_id}
EOH
File.write(exports_path, content)
cap.nfs_prune(env, ui, [valid_id])
exports_content = File.read(exports_path)
expect(exports_content).to include(valid_id)
expect(exports_content).not_to include(invalid_id)
expect(exports_content).to include("/var")
expect(exports_content).not_to include("/tmp")
end
end
describe ".nfs_write_exports" do
before do
File.write(tmp_exports_path, "original content")
end
it "should write updated contents to file" do
described_class.nfs_write_exports("new content")
exports_content = File.read(exports_path)
expect(exports_content).to include("new content")
expect(exports_content).not_to include("original content")
end
it "should only update contents if different" do
original_stat = File.stat(exports_path)
described_class.nfs_write_exports("original content")
updated_stat = File.stat(exports_path)
expect(original_stat).to eq(updated_stat)
end
it "should retain existing file permissions" do
File.chmod(0600, exports_path)
original_stat = File.stat(exports_path)
described_class.nfs_write_exports("original content")
updated_stat = File.stat(exports_path)
expect(original_stat.mode).to eq(updated_stat.mode)
end
it "should raise exception when failing to move new exports file" do
expect(Vagrant::Util::Subprocess).to receive(:execute).and_return(
Vagrant::Util::Subprocess::Result.new(1, "Failed to move file", "")
)
expect{ described_class.nfs_write_exports("new content") }.to raise_error(Vagrant::Errors::NFSExportsFailed)
end
it "should retain existing file owner and group IDs" do
pending("investigate using a simulated FS to test")
end
it "should raise custom exception when chown fails" do
pending("investigate using a simulated FS to test")
end
end
end

View File

@ -133,6 +133,14 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
assert_valid
end
["1", 1, "1.0", 1.0].each do |valid|
it "is valid: #{valid}" do
subject.box_version = valid
subject.finalize!
assert_valid
end
end
end
describe "#communicator" do
@ -526,6 +534,21 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
subject.finalize!
assert_valid
end
it "allows providing custom name via options" do
subject.synced_folder(".", "/vagrant", name: "my-vagrant-folder")
sf = subject.synced_folders
expect(sf).to have_key("my-vagrant-folder")
expect(sf["my-vagrant-folder"][:guestpath]).to eq("/vagrant")
expect(sf["my-vagrant-folder"][:hostpath]).to eq(".")
end
it "allows providing custom name without guest path" do
subject.synced_folder(".", name: "my-vagrant-folder")
sf = subject.synced_folders
expect(sf).to have_key("my-vagrant-folder")
expect(sf["my-vagrant-folder"][:hostpath]).to eq(".")
end
end
describe "#usable_port_range" do

View File

@ -16,7 +16,8 @@ describe VagrantPlugins::Ansible::Config::Guest do
let(:existing_file) { "this/path/is/a/stub" }
it "supports a list of options" do
supported_options = %w( extra_vars
supported_options = %w( config_file
extra_vars
galaxy_command
galaxy_role_file
galaxy_roles_path
@ -27,6 +28,7 @@ describe VagrantPlugins::Ansible::Config::Guest do
inventory_path
limit
playbook
playbook_command
provisioning_path
raw_arguments
skip_tags

View File

@ -15,6 +15,7 @@ describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do
it "supports a list of options" do
supported_options = %w( ask_sudo_pass
ask_vault_pass
config_file
extra_vars
force_remote_user
galaxy_command
@ -26,6 +27,7 @@ describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do
inventory_path
limit
playbook
playbook_command
raw_arguments
raw_ssh_args
skip_tags
@ -63,7 +65,7 @@ describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :ask_sudo_pass, false
end
describe "ask_vault_pass option" do
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :ask_sudo_pass, false
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :ask_vault_pass, false
end
describe "#validate" do

Some files were not shown because too many files have changed in this diff Show More