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 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: addons:
apt: apt:
packages: packages:
- bsdtar - bsdtar
rvm: rvm:
- 2.2.3 - 2.2.5
- 2.3.3
branches: branches:
only: 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: 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: IMPROVEMENTS:
- Add detection for DragonFly BSD [GH-7701] - Add detection for DragonFly BSD [GH-7701]

View File

@ -2,7 +2,7 @@
* Website: [https://www.vagrantup.com/](https://www.vagrantup.com/) * Website: [https://www.vagrantup.com/](https://www.vagrantup.com/)
* Source: [https://github.com/mitchellh/vagrant](https://github.com/mitchellh/vagrant) * 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) * Mailing list: [Google Groups](https://groups.google.com/group/vagrant-up)
Vagrant is a tool for building and distributing development environments. 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 ## Installing the Gem from Git
If you want the bleeding edge version of Vagrant, we try to keep master pretty stable 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: and you're welcome to give it a shot. Please review the installation page [here](https://www.vagrantup.com/docs/installation/source.html).
rake install
Ruby 2.0 is needed.
## Contributing to Vagrant ## Contributing to Vagrant
@ -66,11 +62,6 @@ like so:
bundle exec vagrant help 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 ### Acceptance Tests
Vagrant also comes with an acceptance test suite that does black-box 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 # Split arguments by "--" if its there, we'll recombine them later
argv = ARGV.dup argv = ARGV.dup
argv_extra = [] argv_extra = []
# These will be the options that are passed to initialze the Vagrant
# environment.
opts = {}
if idx = argv.index("--") if idx = argv.index("--")
argv_extra = argv.slice(idx+1, argv.length-2) argv_extra = argv.slice(idx+1, argv.length-2)
argv = argv.slice(0, idx) argv = argv.slice(0, idx)
@ -20,43 +25,26 @@ if argv.include?("-v") || argv.include?("--version")
exit 0 exit 0
end end
# This is kind of hacky, and I'd love to find a better way to do this, but # Disable plugin loading for commands where plugins are not required. This will
# if we're accessing the plugin interface, we want to NOT load plugins # also disable loading of the Vagrantfile if it available as the environment
# for this run, because they can actually interfere with the function # is not required for these commands
# of the plugin interface.
argv.each_index do |i| argv.each_index do |i|
arg = argv[i] arg = argv[i]
if !arg.start_with?("-") if !arg.start_with?("-")
if arg == "plugin" if ["plugin", "help"].include?(arg) || (arg == "box" && argv[i+1] == "list")
ENV["VAGRANT_NO_PLUGINS"] = "1" opts[:vagrantfile_name] = ""
ENV["VAGRANT_VAGRANTFILE"] = "plugin_command_#{Time.now.to_i}" ENV['VAGRANT_NO_PLUGINS'] = "1"
end end
if arg == "help" if arg == "plugin" && argv[i+1] != "list"
ENV["VAGRANT_VAGRANTFILE"] = "plugin_command_#{Time.now.to_i}" ENV['VAGRANT_DISABLE_PLUGIN_INIT'] = "1"
end
if arg == "box" && argv[i+1] == "list"
ENV["VAGRANT_VAGRANTFILE"] = "plugin_command_#{Time.now.to_i}"
end end
break break
end end
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 # Set logging level to `debug`. This is done before loading 'vagrant', as it
# sets up the logging system. # sets up the logging system.
if argv.include?("--debug") if argv.include?("--debug")
@ -64,37 +52,6 @@ if argv.include?("--debug")
ENV["VAGRANT_LOG"] = "debug" ENV["VAGRANT_LOG"] = "debug"
end 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/stderr should not buffer output
$stdout.sync = true $stdout.sync = true
$stderr.sync = true $stderr.sync = true
@ -114,10 +71,6 @@ begin
logger = Log4r::Logger.new("vagrant::bin::vagrant") logger = Log4r::Logger.new("vagrant::bin::vagrant")
logger.info("`vagrant` invoked: #{ARGV.inspect}") 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: # Disable color in a few cases:
# #
# * --no-color is anywhere in our arguments # * --no-color is anywhere in our arguments

View File

@ -1,12 +1,5 @@
require "vagrant/shared_helpers" 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 'rubygems'
require 'log4r' require 'log4r'
@ -72,11 +65,6 @@ global_logger.info("RubyGems version: #{Gem::VERSION}")
ENV.each do |k, v| ENV.each do |k, v|
global_logger.info("#{k}=#{v.inspect}") if k =~ /^VAGRANT_/ global_logger.info("#{k}=#{v.inspect}") if k =~ /^VAGRANT_/
end 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 # We need these components always so instead of an autoload we
# just require them explicitly here. # just require them explicitly here.
@ -254,6 +242,35 @@ if I18n.config.respond_to?(:enforce_available_locales=)
I18n.config.enforce_available_locales = true I18n.config.enforce_available_locales = true
end 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. # A lambda that knows how to load plugins from a single directory.
plugin_load_proc = lambda do |directory| plugin_load_proc = lambda do |directory|
# We only care about directories # We only care about directories
@ -288,8 +305,40 @@ end
if Vagrant.plugins_enabled? if Vagrant.plugins_enabled?
begin begin
global_logger.info("Loading plugins!") 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 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 raise Vagrant::Errors::PluginLoadError, message: e.to_s
end end
else
global_logger.debug("Plugin loading is currently disabled.")
end end

View File

@ -4,7 +4,9 @@ require "set"
require "tempfile" require "tempfile"
require "fileutils" require "fileutils"
require "bundler" require "rubygems/package"
require "rubygems/uninstaller"
require "rubygems/name_tuple"
require_relative "shared_helpers" require_relative "shared_helpers"
require_relative "version" require_relative "version"
@ -15,83 +17,77 @@ module Vagrant
# Bundler as a way to properly resolve all dependencies of Vagrant and # Bundler as a way to properly resolve all dependencies of Vagrant and
# all Vagrant-installed plugins. # all Vagrant-installed plugins.
class Bundler class Bundler
HASHICORP_GEMSTORE = 'https://gems.hashicorp.com'.freeze
def self.instance def self.instance
@bundler ||= self.new @bundler ||= self.new
end end
attr_reader :plugin_gem_path
def initialize def initialize
@enabled = true if ENV["VAGRANT_INSTALLER_ENV"] || @plugin_gem_path = Vagrant.user_data_path.join("gems", RUBY_VERSION).freeze
ENV["VAGRANT_FORCE_BUNDLER"] @logger = Log4r::Logger.new("vagrant::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
end end
# Initializes Bundler and the various gem paths so that we can begin # Initializes Bundler and the various gem paths so that we can begin
# loading gems. This must only be called once. # loading gems. This must only be called once.
def init!(plugins) def init!(plugins, repair=false)
# If we're not enabled, then we don't do anything. # Add HashiCorp RubyGems source
return if !@enabled Gem.sources << HASHICORP_GEMSTORE
bundle_path = Vagrant.user_data_path.join("gems") # Generate dependencies for all registered plugins
plugin_deps = plugins.map do |name, info|
# Setup the "local" Bundler configuration. We need to set BUNDLE_PATH Gem::Dependency.new(name, info['gem_version'].to_s.empty? ? '> 0' : info['gem_version'])
# 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}\"")
end end
# Setup the Bundler configuration @logger.debug("Current generated plugin dependency list: #{plugin_deps}")
@configfile = tempfile("vagrant-configfile")
@configfile.close
# Build up the Gemfile for our Bundler context. We make sure to # Load dependencies into a request set for resolution
# lock Vagrant to our current Vagrant version. In addition to that, request_set = Gem::RequestSet.new(*plugin_deps)
# we add all our plugin dependencies. # Never allow dependencies to be remotely satisfied during init
@gemfile = build_gemfile(plugins) request_set.remote = false
Util::SafeEnv.change_env do |env| repair_result = nil
# Set the environmental variables for Bundler begin
env["BUNDLE_APP_CONFIG"] = @appconfigpath # Compose set for resolution
env["BUNDLE_CONFIG"] = @configfile.path composed_set = generate_vagrant_set
env["BUNDLE_GEMFILE"] = @gemfile.path # Resolve the request set to ensure proper activation order
env["BUNDLE_RETRY"] = "3" solution = request_set.resolve(composed_set)
env["GEM_PATH"] = rescue Gem::UnsatisfiableDependencyError => failure
"#{bundle_path}#{::File::PATH_SEPARATOR}#{@gem_path}" 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 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 end
# Removes any temporary files created by init # Removes any temporary files created by init
def deinit def deinit
# If we weren't enabled, then we don't do anything. # no-op
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
end end
# Installs the list of plugins. # Installs the list of plugins.
@ -106,35 +102,17 @@ module Vagrant
# #
# @param [String] path Path to a local gem file. # @param [String] path Path to a local gem file.
# @return [Gem::Specification] # @return [Gem::Specification]
def install_local(path) def install_local(path, opts={})
# We have to do this load here because this file can be loaded plugin_source = Gem::Source::SpecificFile.new(path)
# before RubyGems is actually loaded. plugin_info = {
require "rubygems/dependency_installer" plugin_source.spec.name => {
begin "local_source" => plugin_source,
require "rubygems/format" "sources" => opts.fetch(:sources, Gem.sources.map(&:to_s))
rescue LoadError }
# rubygems 2.x }
end @logger.debug("Installing local plugin - #{plugin_info}")
internal_install(plugin_info, {})
# If we're installing from a gem file, determine the name plugin_source.spec
# 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
end end
# Update updates the given plugins, or every plugin if none is given. # 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. # empty or nil, all plugins will be updated.
def update(plugins, specific) def update(plugins, specific)
specific ||= [] specific ||= []
update = true update = {gems: specific.empty? ? true : specific}
update = { gems: specific } if !specific.empty?
internal_install(plugins, update) internal_install(plugins, update)
end end
# Clean removes any unused gems. # Clean removes any unused gems.
def clean(plugins) def clean(plugins)
gemfile = build_gemfile(plugins) @logger.debug("Cleaning Vagrant plugins of stale gems.")
lockfile = "#{gemfile.path}.lock" # Generate dependencies for all registered plugins
definition = ::Bundler::Definition.build(gemfile, lockfile, nil) plugin_deps = plugins.map do |name, info|
root = File.dirname(gemfile.path) 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 @logger.debug("Current plugin dependency list: #{plugin_deps}")
runtime = ::Bundler::Runtime.new(root, definition)
runtime.clean # 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
end end
# During the duration of the yielded block, Bundler loud output # During the duration of the yielded block, Bundler loud output
# is enabled. # is enabled.
def verbose def verbose
@monitor.synchronize do if block_given?
begin initial_state = @verbose
old_ui = ::Bundler.ui @verbose = true
require 'bundler/vendored_thor' yield
::Bundler.ui = ::Bundler::UI::Shell.new @verbose = initial_state
yield else
ensure @verbose = true
::Bundler.ui = old_ui
end
end end
end end
protected protected
# Builds a valid Gemfile for use with Bundler given the list of def internal_install(plugins, update, **extra)
# plugins. # Only allow defined Gem sources
# Gem.sources.clear
# @return [Tempfile]
def build_gemfile(plugins)
sources = plugins.values.map { |p| p["sources"] }.flatten.compact.uniq
f = tempfile("vagrant-gemfile") update = {} if !update.is_a?(Hash)
f.tap do |gemfile| skips = []
sources.each do |source| installer_set = Gem::Resolver::InstallerSet.new(:both)
next if source == ""
gemfile.puts(%Q[source "#{source}"]) # 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 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") # Create the request set for the new plugins
plugins.each do |name, plugin| request_set = Gem::RequestSet.new(*plugin_deps)
version = plugin["gem_version"]
version = nil if version == ""
opts = {} installer_set = Gem::Resolver.compose_sets(
if plugin["require"] && plugin["require"] != "" installer_set,
opts[:require] = plugin["require"] 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 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 end
gemfile.puts("end")
gemfile.close
end end
end end
# This installs a set of plugins and optionally updates those gems. # 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
# @param [Hash] plugins # enforcing strict constraints. This ensures that plugins cannot "upgrade"
# @param [Hash, Boolean] update If true, updates all plugins, otherwise # gems that are builtin to vagrant itself.
# can be a hash of options. See Bundler.definition. class BuiltinSet < Gem::Resolver::Set
# @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
def initialize def initialize
# We _can not_ `super` here because that can really mess up super
# some other configuration state. We need to just set everything @remote = false
# directly. @specs = []
end
@api_keys = {} def add_builtin_spec(spec)
@args = [] @specs.push(spec).uniq!
@backtrace = false end
@bulk_threshold = 1000
@hash = {} def find_all(req)
@update_sources = true @specs.select do |spec|
@verbose = true req.match?(spec)
end.map do |spec|
Gem::Resolver::InstalledSpecification.new(self, spec)
end
end end
end end
# This monkey patches Gem::Specification from RubyGems to add a new method, # This is a custom Gem::Resolver::Set for use with Vagrant plugins. It is
# `vagrant_reset!`. For some background, Vagrant needs to set the value # a modified Gem::Resolver::VendorSet that supports multiple versions of
# of these variables to nil to force new specs to be loaded. Previously, # a specific gem
# this was accomplished by setting Gem::Specification.specs = nil. However, class PluginSet < Gem::Resolver::VendorSet
# newer versions of Rubygems try to map across that nil using a group_by ##
# clause, breaking things. # Adds a specification to the set with the given +name+ which has been
# # unpacked into the given +directory+.
# This generally never affected Vagrant users who were using the official def add_vendor_gem(name, directory)
# Vagrant installers because we lock to an older version of Rubygems that gemspec = File.join(directory, "#{name}.gemspec")
# does not have this issue. The users of the official debian packages, spec = Gem::Specification.load(gemspec)
# however, experienced this issue because they float on Rubygems. if !spec
# raise Gem::GemNotFoundException,
# In GH-7073, a number of Debian users reported this issue, but it was not "unable to find #{gemspec} for gem #{name}"
# reproducible in the official installer for reasons described above. Commit end
# ba77d4b switched to using Gem::Specification.reset, but this actually
# broke the ability to install gems locally (GH-7493) because it resets spec.full_gem_path = File.expand_path(directory)
# the complete local cache, which is already built.
# @specs[spec.name] ||= []
# The only solution that works with both new and old versions of Rubygems @specs[spec.name] << spec
# is to provide our own function for JUST resetting all the stubs. Both @directories[spec] = directory
# @@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 spec
# 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
end end
end
if ::Bundler::UI.const_defined? :Silent ##
class BundlerUI < ::Bundler::UI::Silent # Returns an Array of VendorSpecification objects matching the
attr_reader :output # DependencyRequest +req+.
def find_all(req)
def initialize @specs.values.flatten.select do |spec|
@output = "" req.match?(spec)
end.map do |spec|
source = Gem::Source::Vendor.new(@directories[spec])
Gem::Resolver::VendorSpecification.new(self, spec, source)
end end
end
def info(message, newline = nil) ##
end # Loads a spec with the given +name+. +version+, +platform+ and +source+ are
# ignored.
def confirm(message, newline = nil) def load_spec (name, version, platform, source)
end version = Gem::Version.new(version) if !version.is_a?(Gem::Version)
@specs.fetch(name, []).detect{|s| s.name == name && s.version = version}
def warn(message, newline = nil) end
@output += message end
@output += "\n" if newline end
end end
def error(message, newline = nil) # Patch for Ruby 2.2 and Bundler to behave properly when uninstalling plugins
@output += message if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
@output += "\n" if newline if defined?(::Bundler) && !::Bundler::SpecSet.instance_methods.include?(:delete)
end class Gem::Specification
def self.remove_spec(spec)
def debug(message, newline = nil) Gem::Specification.reset
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
end end
end 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. # a priority to each in the order they exist so that we try these first.
config = {} config = {}
root_config.vm.__providers.reverse.each_with_index do |key, idx| root_config.vm.__providers.reverse.each_with_index do |key, idx|
config[key] = idx config[key] = idx + 1
end end
# Determine the max priority so that we can add the config priority # Determine the max priority so that we can add the config priority

View File

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

View File

@ -44,10 +44,9 @@ module Vagrant
local = false local = false
if name =~ /\.gem$/ if name =~ /\.gem$/
# If this is a gem file, then we install that gem locally. # 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 name = local_spec.name
opts[:version] = local_spec.version.to_s opts[:version] = local_spec.version.to_s
local = true
end end
plugins = installed_plugins plugins = installed_plugins
@ -57,33 +56,41 @@ module Vagrant
"sources" => opts[:sources], "sources" => opts[:sources],
} }
result = nil if local_spec.nil?
install_lambda = lambda do result = nil
Vagrant::Bundler.instance.install(plugins, local).each do |spec| install_lambda = lambda do
next if spec.name != name Vagrant::Bundler.instance.install(plugins, local).each do |spec|
next if result && result.version >= spec.version next if spec.name != name
result = spec next if result && result.version >= spec.version
result = spec
end
end end
end
if opts[:verbose] if opts[:verbose]
Vagrant::Bundler.instance.verbose(&install_lambda) Vagrant::Bundler.instance.verbose(&install_lambda)
else
install_lambda.call
end
else else
install_lambda.call result = local_spec
end end
# Add the plugin to the state file # Add the plugin to the state file
@user_file.add_plugin( @user_file.add_plugin(
result.name, result.name,
version: opts[:version], version: opts[:version],
require: opts[:require], require: opts[:require],
sources: opts[:sources], 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 result
rescue ::Bundler::GemNotFound rescue Gem::GemNotFoundException
raise Errors::PluginGemNotFound, name: name raise Errors::PluginGemNotFound, name: name
rescue ::Bundler::BundlerError => e rescue Gem::Exception => e
raise Errors::BundlerError, message: e.to_s raise Errors::BundlerError, message: e.to_s
end end
@ -102,14 +109,30 @@ module Vagrant
# Clean the environment, removing any old plugins # Clean the environment, removing any old plugins
Vagrant::Bundler.instance.clean(installed_plugins) Vagrant::Bundler.instance.clean(installed_plugins)
rescue ::Bundler::BundlerError => e rescue Gem::Exception => e
raise Errors::BundlerError, message: e.to_s raise Errors::BundlerError, message: e.to_s
end end
# Updates all or a specific set of plugins. # Updates all or a specific set of plugins.
def update_plugins(specific) def update_plugins(specific)
Vagrant::Bundler.instance.update(installed_plugins, specific) result = Vagrant::Bundler.instance.update(installed_plugins, specific)
rescue ::Bundler::BundlerError => e 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 raise Errors::BundlerError, message: e.to_s
end end
@ -123,8 +146,14 @@ module Vagrant
system[k] = v.merge("system" => true) system[k] = v.merge("system" => true)
end end
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 end
# This returns the list of plugins that are installed as # 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 # @param [String] name The name of the plugin
def add_plugin(name, **opts) def add_plugin(name, **opts)
@data["installed"][name] = { @data["installed"][name] = {
"ruby_version" => RUBY_VERSION, "ruby_version" => RUBY_VERSION,
"vagrant_version" => Vagrant::VERSION, "vagrant_version" => Vagrant::VERSION,
"gem_version" => opts[:version] || "", "gem_version" => opts[:version] || "",
"require" => opts[:require] || "", "require" => opts[:require] || "",
"sources" => opts[:sources] || [], "sources" => opts[:sources] || [],
"installed_gem_version" => opts[:installed_gem_version]
} }
save! 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"] ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"]
end 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. # This returns whether or not 3rd party plugins should and can be loaded.
# #
# @return [Boolean] # @return [Boolean]
def self.plugins_enabled? def self.plugins_enabled?
!ENV["VAGRANT_NO_PLUGINS"] && $vagrant_bundler_runtime !ENV["VAGRANT_NO_PLUGINS"]
end end
# Whether or not super quiet mode is enabled. This is ill-advised. # 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 :SafeExec, 'vagrant/util/safe_exec'
autoload :StackedProcRunner, 'vagrant/util/stacked_proc_runner' autoload :StackedProcRunner, 'vagrant/util/stacked_proc_runner'
autoload :TemplateRenderer, 'vagrant/util/template_renderer' autoload :TemplateRenderer, 'vagrant/util/template_renderer'
autoload :StringBlockEditor, 'vagrant/util/string_block_editor'
autoload :Subprocess, 'vagrant/util/subprocess' autoload :Subprocess, 'vagrant/util/subprocess'
end end
end end

View File

@ -1,7 +1,8 @@
require "uri" require "uri"
require "log4r" require "log4r"
require "digest/md5"
require "digest/sha1"
require "vagrant/util/busy" require "vagrant/util/busy"
require "vagrant/util/platform" require "vagrant/util/platform"
require "vagrant/util/subprocess" require "vagrant/util/subprocess"
@ -18,6 +19,12 @@ module Vagrant
# Vagrant/1.7.4 (+https://www.vagrantup.com; ruby2.1.0) # Vagrant/1.7.4 (+https://www.vagrantup.com; ruby2.1.0)
USER_AGENT = "Vagrant/#{VERSION} (+https://www.vagrantup.com; #{RUBY_ENGINE}#{RUBY_VERSION})".freeze 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 :source
attr_reader :destination attr_reader :destination
@ -52,6 +59,10 @@ module Vagrant
@ui = options[:ui] @ui = options[:ui]
@client_cert = options[:client_cert] @client_cert = options[:client_cert]
@location_trusted = options[:location_trusted] @location_trusted = options[:location_trusted]
@checksums = {
:md5 => options[:md5],
:sha1 => options[:sha1]
}
end end
# This executes the actual download, downloading the source file # This executes the actual download, downloading the source file
@ -161,6 +172,8 @@ module Vagrant
end end
end end
validate_download!(@source, @destination, @checksums)
# Everything succeeded # Everything succeeded
true true
end end
@ -178,6 +191,46 @@ module Vagrant
protected 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) def execute_curl(options, subprocess_options, &data_proc)
options = options.dup options = options.dup
options << subprocess_options options << subprocess_options

View File

@ -1,11 +1,11 @@
require "bundler"
module Vagrant module Vagrant
module Util module Util
class Env class Env
def self.with_original_env def self.with_original_env
original_env = ENV.to_hash 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) ENV.update(Vagrant.original_env)
yield yield
ensure ensure
@ -37,7 +37,9 @@ module Vagrant
# the block to execute with the cleaned environment # the block to execute with the cleaned environment
def self.with_clean_env def self.with_clean_env
with_original_env do 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_" } ENV.delete_if { |k,_| k[0,7] == "BUNDLE_" }
if ENV.has_key? "RUBYOPT" if ENV.has_key? "RUBYOPT"
ENV["RUBYOPT"] = ENV["RUBYOPT"].sub("-rbundler/setup", "") 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 # thread. In that case, `safe_exec` automatically falls back to
# forking. # forking.
class SafeExec class SafeExec
@@logger = Log4r::Logger.new("vagrant::util::safe_exec")
def self.exec(command, *args) def self.exec(command, *args)
# Create a list of things to rescue from. Since this is OS # Create a list of things to rescue from. Since this is OS
# specific, we need to do some defined? checks here to make # specific, we need to do some defined? checks here to make
@ -18,10 +21,27 @@ module Vagrant
fork_instead = false fork_instead = false
begin begin
pid = nil if fork_instead
pid = fork if fork_instead if Vagrant::Util::Platform.windows?
Kernel.exec(command, *args) if pid.nil? @@logger.debug("Using subprocess because windows platform")
Process.wait(pid) if pid 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 rescue *rescue_from
# We retried already, raise the issue and be done # We retried already, raise the issue and be done
raise if fork_instead raise if fork_instead

View File

@ -144,10 +144,11 @@ module Vagrant
# Record the start time for timeout purposes # Record the start time for timeout purposes
start_time = Time.now.to_i start_time = Time.now.to_i
open_readers = [stdout, stderr]
open_writers = notify_stdin ? [process.io.stdin] : []
@logger.debug("Selecting on IO") @logger.debug("Selecting on IO")
while true while true
writers = notify_stdin ? [process.io.stdin] : [] results = ::IO.select(open_readers, open_writers, nil, 0.1)
results = ::IO.select([stdout, stderr], writers, nil, 0.1)
results ||= [] results ||= []
readers = results[0] readers = results[0]
writers = results[1] writers = results[1]
@ -178,8 +179,14 @@ module Vagrant
break if process.exited? break if process.exited?
# Check the writers to see if they're ready, and notify any listeners # Check the writers to see if they're ready, and notify any listeners
if writers && !writers.empty? if writers && !writers.empty? && block_given?
yield :stdin, process.io.stdin if 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
end end
@ -290,7 +297,9 @@ module Vagrant
def jailbreak(env = {}) def jailbreak(env = {})
return if ENV.key?("VAGRANT_SKIP_SUBPROCESS_JAILBREAK") 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) env.merge!(Vagrant.original_env)
# Bundler does this, so I guess we should as well, since I think it # 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 Remove
end end
@subcommands.register(:prune) do
require_relative "prune"
Prune
end
@subcommands.register(:repackage) do @subcommands.register(:repackage) do
require File.expand_path("../repackage", __FILE__) require File.expand_path("../repackage", __FILE__)
Repackage Repackage

View File

@ -6,6 +6,12 @@ module VagrantPlugins
module CommandPlugin module CommandPlugin
module Action module Action
# This middleware sequence will install a plugin. # 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 def self.action_install
Vagrant::Action::Builder.new.tap do |b| Vagrant::Action::Builder.new.tap do |b|
b.use InstallGem b.use InstallGem
@ -27,6 +33,13 @@ module VagrantPlugins
end end
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. # This middleware sequence will uninstall a plugin.
def self.action_uninstall def self.action_uninstall
Vagrant::Action::Builder.new.tap do |b| Vagrant::Action::Builder.new.tap do |b|
@ -44,10 +57,12 @@ module VagrantPlugins
# The autoload farm # The autoload farm
action_root = Pathname.new(File.expand_path("../action", __FILE__)) action_root = Pathname.new(File.expand_path("../action", __FILE__))
autoload :ExpungePlugins, action_root.join("expunge_plugins")
autoload :InstallGem, action_root.join("install_gem") autoload :InstallGem, action_root.join("install_gem")
autoload :LicensePlugin, action_root.join("license_plugin") autoload :LicensePlugin, action_root.join("license_plugin")
autoload :ListPlugins, action_root.join("list_plugins") autoload :ListPlugins, action_root.join("list_plugins")
autoload :PluginExistsCheck, action_root.join("plugin_exists_check") autoload :PluginExistsCheck, action_root.join("plugin_exists_check")
autoload :RepairPlugins, action_root.join("repair_plugins")
autoload :UninstallPlugin, action_root.join("uninstall_plugin") autoload :UninstallPlugin, action_root.join("uninstall_plugin")
autoload :UpdateGems, action_root.join("update_gems") autoload :UpdateGems, action_root.join("update_gems")
end 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) def call(env)
manager = Vagrant::Plugin::Manager.instance manager = Vagrant::Plugin::Manager.instance
plugins = manager.installed_plugins plugins = manager.installed_plugins
specs = manager.installed_specs specs = Hash[
manager.installed_specs.map do |spec|
[spec.name, spec]
end
]
# Output! # Output!
if specs.empty? if specs.empty?
@ -26,9 +30,10 @@ module VagrantPlugins
return @app.call(env) return @app.call(env)
end end
specs.each do |spec| plugins.each do |plugin_name, plugin|
# Grab the plugin.
plugin = plugins[spec.name] spec = specs[plugin_name]
next if spec.nil?
system = "" system = ""
system = ", system" if plugin && plugin["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 end
manager = Vagrant::Plugin::Manager.instance manager = Vagrant::Plugin::Manager.instance
installed_specs = manager.installed_specs installed_plugins = manager.installed_plugins
new_specs = manager.update_plugins(names) new_specs = manager.update_plugins(names)
updated_plugins = manager.installed_plugins
updated = {} updated = {}
installed_specs.each do |ispec| installed_plugins.each do |name, info|
new_specs.each do |uspec| update = updated_plugins[name]
next if uspec.name != ispec.name if update && update["installed_gem_version"] != info["installed_gem_version"]
next if ispec.version >= uspec.version updated[name] = update["installed_gem_version"]
next if updated[uspec.name] && updated[uspec.name].version >= uspec.version
updated[uspec.name] = uspec
end end
end end
@ -37,9 +35,9 @@ module VagrantPlugins
env[:ui].success(I18n.t("vagrant.commands.plugin.up_to_date")) env[:ui].success(I18n.t("vagrant.commands.plugin.up_to_date"))
end end
updated.values.each do |spec| updated.each do |name, version|
env[:ui].success(I18n.t("vagrant.commands.plugin.updated", env[:ui].success(I18n.t("vagrant.commands.plugin.updated",
name: spec.name, version: spec.version.to_s)) name: name, version: version.to_s))
end end
# Continue # 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 options[:entry_point] = entry_point
end 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", o.on("--plugin-clean-sources",
"Remove all plugin sources defined so far (including defaults)") do |clean| "Remove all plugin sources defined so far (including defaults)") do |clean|
options[:plugin_sources] = [] if 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) @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new @subcommands = Vagrant::Registry.new
@subcommands.register(:expunge) do
require_relative "expunge"
Expunge
end
@subcommands.register(:install) do @subcommands.register(:install) do
require_relative "install" require_relative "install"
Install Install
@ -29,6 +34,11 @@ module VagrantPlugins
List List
end end
@subcommands.register(:repair) do
require_relative "repair"
Repair
end
@subcommands.register(:update) do @subcommands.register(:update) do
require_relative "update" require_relative "update"
Update Update

View File

@ -540,7 +540,7 @@ module VagrantPlugins
end end
# Set the terminal # 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. # Set SSH_AUTH_SOCK if we are in sudo and forwarding agent.
# This is to work around often misconfigured boxes where # 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.") @logger.warn("No SSH_AUTH_SOCK found despite forward_agent being set.")
else else
@logger.info("Setting SSH_AUTH_SOCK remotely: #{auth_socket}") @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
end end
@ -572,9 +572,9 @@ module VagrantPlugins
# without the cruft added from pty mode. # without the cruft added from pty mode.
if pty if pty
data = "stty raw -echo\n" data = "stty raw -echo\n"
data += "export PS1=\n" data += generate_environment_export("PS1", "")
data += "export PS2=\n" data += generate_environment_export("PS2", "")
data += "export PROMPT_COMMAND=\n" data += generate_environment_export("PROMPT_COMMAND", "")
data += "printf #{PTY_DELIM_START}\n" data += "printf #{PTY_DELIM_START}\n"
data += "#{command}\n" data += "#{command}\n"
data += "exitcode=$?\n" data += "exitcode=$?\n"
@ -670,6 +670,11 @@ module VagrantPlugins
source_path = Vagrant.source_root.join("keys", "vagrant") source_path = Vagrant.source_root.join("keys", "vagrant")
return File.read(path).chomp == source_path.read.chomp return File.read(path).chomp == source_path.read.chomp
end 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 end
end end

View File

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

View File

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

View File

@ -8,18 +8,16 @@ module VagrantPlugins
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false) if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0] basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, "") 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 # Set hostname
hostnamectl set-hostname '#{basename}' 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 # 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 sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts
} })
EOH EOH
end end
end end

View File

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

View File

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

View File

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

View File

@ -8,18 +8,16 @@ module VagrantPlugins
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false) if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0] basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, "") 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 # Set hostname
hostnamectl set-hostname '#{basename}' 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 # 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 sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts
} })
EOH EOH
end end
end end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -94,7 +94,7 @@ SCRIPT
# Emit an upstart event if we can # Emit an upstart event if we can
machine.communicate.sudo <<-SCRIPT 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}' /sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
fi fi
SCRIPT SCRIPT

View File

@ -89,7 +89,7 @@ module VagrantPlugins
# Emit an upstart event if we can # Emit an upstart event if we can
machine.communicate.sudo <<-EOH.gsub(/^ {12}/, "") 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} /sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guest_path}
fi fi
EOH EOH

View File

@ -2,6 +2,11 @@ module VagrantPlugins
module GuestLinux module GuestLinux
module Cap module Cap
class NetworkInterfaces 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") @@logger = Log4r::Logger.new("vagrant::guest::linux::network_interfaces")
# Get network interfaces as a list. The result will be something like: # Get network interfaces as a list. The result will be something like:
@ -18,6 +23,9 @@ module VagrantPlugins
@@logger.debug("Unsorted list: #{ifaces.inspect}") @@logger.debug("Unsorted list: #{ifaces.inspect}")
# Break out integers from strings and sort the arrays to provide # Break out integers from strings and sort the arrays to provide
# a natural sort for the interface names # 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| ifaces = ifaces.map do |iface|
iface.scan(/(.+?)(\d+)/).flatten.map do |iface_part| iface.scan(/(.+?)(\d+)/).flatten.map do |iface_part|
if iface_part.to_i.to_s == iface_part if iface_part.to_i.to_s == iface_part
@ -26,8 +34,32 @@ module VagrantPlugins
iface_part iface_part
end end
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}") @@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 ifaces
end end
end end

View File

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

View File

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

View File

@ -1,8 +1,22 @@
module VagrantPlugins module VagrantPlugins
module GuestLinux module GuestLinux
class Guest < Vagrant.plugin("2", :guest) class Guest < Vagrant.plugin("2", :guest)
# Name used for guest detection
GUEST_DETECTION_NAME = "linux".freeze
def detect?(machine) 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 end
end end

View File

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

View File

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

View File

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

View File

@ -7,10 +7,21 @@ module VagrantPlugins
extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap
def self.rsync_install(machine) def self.rsync_install(machine)
machine.communicate.sudo( install_output = {:stderr => '', :stdout => ''}
'PKG_PATH="http://ftp.openbsd.org/pub/OpenBSD/' \ command = 'PKG_PATH="http://ftp.openbsd.org/pub/OpenBSD/' \
'`uname -r`/packages/`arch -s`/" ' \ '`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 end
end end

View File

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

View File

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

View File

@ -40,16 +40,23 @@ module VagrantPlugins
# Down the interface before munging the config file. This might # Down the interface before munging the config file. This might
# fail if the interface is not actually set up yet so ignore # fail if the interface is not actually set up yet so ignore
# errors. # errors.
/sbin/ifdown '#{network[:device]}' || true /sbin/ifdown '#{network[:device]}'
# Move new config into place # Move new config into place
mv '#{remote_path}' '#{final_path}' mv -f '#{remote_path}' '#{final_path}'
# attempt to force network manager to reload configurations
# Bring the interface up nmcli c reload || true
ARPCHECK=no /sbin/ifup '#{network[:device]}'
EOH EOH
end 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")) comm.sudo(commands.join("\n"))
end end
end end

View File

@ -10,7 +10,7 @@ module VagrantPlugins
end end
# Detect various flavors we care about # 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 return :rhel_7
else else
return :rhel return :rhel

View File

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

View File

@ -1,24 +1,10 @@
require_relative '../linux/guest'
module VagrantPlugins module VagrantPlugins
module GuestUbuntu module GuestUbuntu
class Guest < Vagrant.plugin("2", :guest) class Guest < VagrantPlugins::GuestLinux::Guest
def detect?(machine) # Name used for guest detection
# This command detects if we are running on Ubuntu. /etc/os-release is GUEST_DETECTION_NAME = "ubuntu".freeze
# 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
end end
end end
end end

View File

@ -6,6 +6,8 @@ module VagrantPlugins
module HostLinux module HostLinux
module Cap module Cap
class NFS class NFS
NFS_EXPORTS_PATH = "/etc/exports".freeze
extend Vagrant::Util::Retryable extend Vagrant::Util::Retryable
def self.nfs_apply_command(env) def self.nfs_apply_command(env)
@ -36,16 +38,9 @@ module VagrantPlugins
ui.info I18n.t("vagrant.hosts.linux.nfs_export") ui.info I18n.t("vagrant.hosts.linux.nfs_export")
sleep 0.5 sleep 0.5
nfs_cleanup(id) nfs_cleanup("#{Process.uid} #{id}")
output = "#{nfs_exports_content}\n#{output}"
# Only use "sudo" if we can't write to /etc/exports directly nfs_write_exports(output)
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
if nfs_running?(nfs_check_command) if nfs_running?(nfs_check_command)
system("sudo #{nfs_apply_command}") system("sudo #{nfs_apply_command}")
@ -62,48 +57,111 @@ module VagrantPlugins
end end
def self.nfs_prune(environment, ui, valid_ids) 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 = Log4r::Logger.new("vagrant::hosts::linux")
logger.info("Pruning invalid NFS entries...") logger.info("Pruning invalid NFS entries...")
output = false
user = Process.uid user = Process.uid
File.read("/etc/exports").lines.each do |line| # Create editor instance for removing invalid IDs
if id = line[/^# VAGRANT-BEGIN:( #{user})? ([\.\/A-Za-z0-9\-_:]+?)$/, 2] editor = Vagrant::Util::StringBlockEditor.new(nfs_exports_content)
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
logger.info("Invalid ID, pruning: #{id}") # Build composite IDs with UID information and discover invalid entries
nfs_cleanup(id) composite_ids = valid_ids.map do |v_id|
end "#{user} #{v_id}"
end 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
end end
protected protected
def self.nfs_cleanup(id) def self.nfs_cleanup(remove_ids)
return if !File.exist?("/etc/exports") return if !File.exist?(NFS_EXPORTS_PATH)
user = Regexp.escape(Process.uid.to_s) editor = Vagrant::Util::StringBlockEditor.new(nfs_exports_content)
id = Regexp.escape(id.to_s) remove_ids = Array(remove_ids)
# Only use "sudo" if we can't write to /etc/exports directly # Remove all invalid ID entries
sudo_command = "" remove_ids.each do |r_id|
sudo_command = "sudo " if !File.writable?("/etc/exports") editor.delete(r_id)
end
nfs_write_exports(editor.value)
end
# Use sed to just strip out the block of code which was inserted def self.nfs_write_exports(new_exports_content)
# by Vagrant if(nfs_exports_content != new_exports_content.strip)
tmp = ENV["TMPDIR"] || ENV["TMP"] || "/tmp" begin
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") # 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 end
def self.nfs_opts_setup(folders) def self.nfs_opts_setup(folders)

View File

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

View File

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

View File

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

View File

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

View File

@ -74,7 +74,15 @@ module VagrantPlugins
end end
def import(options) 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 end
def net_set_vlan(vlan_id) 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( Param(
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true)]
[string]$vm_xml_config, [string]$vm_config_file,
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true)]
[string]$image_path, [string]$dest_path,
[string]$switchname=$null, [string]$switchname=$null,
[string]$memory=$null, [string]$memory=$null,
@ -17,7 +17,7 @@ Param(
$Dir = Split-Path $script:MyInvocation.MyCommand.Path $Dir = Split-Path $script:MyInvocation.MyCommand.Path
. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) . ([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 $generation = [int]($vmconfig.configuration.properties.subtype.'#text')+1
@ -190,7 +190,7 @@ foreach ($controller in $controllers) {
$addDriveParam = @{ $addDriveParam = @{
ControllerNumber = $rx.Match($controller.node.name).value ControllerNumber = $rx.Match($controller.node.name).value
Path = $image_path Path = $dest_path
} }
if ($drive.pool_id."#text") { if ($drive.pool_id."#text") {

View File

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

View File

@ -26,25 +26,44 @@ module VagrantPlugins
end end
def check_files_existence 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_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.config_file, :config_file) if config.config_file
check_path_is_a_file config.galaxy_role_file, :galaxy_role_file if config.galaxy_role_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.vault_password_file, :vault_password if config.vault_password_file 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 end
def ansible_playbook_command_for_shell_execution def ansible_playbook_command_for_shell_execution
shell_command = [] shell_command = get_environment_variables_for_shell_execution
@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 << "ansible-playbook" shell_command << config.playbook_command
shell_args = [] shell_args = []
@command_arguments.each do |arg| @command_arguments.each do |arg|
@ -102,6 +121,12 @@ module VagrantPlugins
# Use ANSIBLE_ROLES_PATH to tell ansible-playbook where to look for roles # Use ANSIBLE_ROLES_PATH to tell ansible-playbook where to look for roles
# (there is no equivalent command line argument in ansible-playbook) # (there is no equivalent command line argument in ansible-playbook)
@environment_variables["ANSIBLE_ROLES_PATH"] = get_galaxy_roles_path if config.galaxy_roles_path @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 end
# Auto-generate "safe" inventory file based on Vagrantfile, # 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) @machine.guest.capability(:ansible_install, config.install_mode, config.version)
end end
# Check that ansible binaries are well installed on the guest, # Check that Ansible Playbook command is available on the guest
@machine.communicate.execute( @machine.communicate.execute(
"ansible-galaxy info --help && ansible-playbook --help", "test -x \"$(command -v #{config.playbook_command})\"",
error_class: Ansible::Errors::AnsibleNotFoundOnGuest, error_class: Ansible::Errors::AnsibleNotFoundOnGuest,
error_key: :ansible_not_found_on_guest error_key: :ansible_not_found_on_guest
) )
@ -72,14 +72,9 @@ module VagrantPlugins
end end
def execute_ansible_galaxy_on_guest def execute_ansible_galaxy_on_guest
command_values = { prepare_ansible_config_environment_variable
role_file: "'#{get_galaxy_role_file}'",
roles_path: "'#{get_galaxy_roles_path}'"
}
remote_command = config.galaxy_command % command_values execute_ansible_command_on_guest "galaxy", ansible_galaxy_command_for_shell_execution
execute_ansible_command_on_guest "galaxy", remote_command
end end
def execute_ansible_playbook_on_guest def execute_ansible_playbook_on_guest
@ -156,7 +151,7 @@ module VagrantPlugins
# and error if it doesn't exist. # and error if it doesn't exist.
remote_path = Helpers::expand_path_in_unix_style(path, config.provisioning_path) 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( @machine.communicate.execute(
command, command,

View File

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

View File

@ -64,16 +64,6 @@ module VagrantPlugins
# @return [String] # @return [String]
attr_accessor :installer_download_path 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 def initialize
super super

View File

@ -5,15 +5,6 @@ module VagrantPlugins
class Config < Vagrant.plugin("2", :config) class Config < Vagrant.plugin("2", :config)
attr_reader :images 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 def initialize
@images = Set.new @images = Set.new

View File

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

View File

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

View File

@ -4,11 +4,6 @@ require "vagrant/util/deep_merge"
module VagrantPlugins module VagrantPlugins
module Salt module Salt
class Config < Vagrant.plugin("2", :config) 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 ## salty-vagrant options
attr_accessor :minion_config attr_accessor :minion_config
attr_accessor :minion_key attr_accessor :minion_key
@ -74,12 +69,6 @@ module VagrantPlugins
end end
def finalize! 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 @grains_config = nil if @grains_config == UNSET_VALUE
@run_highstate = nil if @run_highstate == UNSET_VALUE @run_highstate = nil if @run_highstate == UNSET_VALUE
@run_overstate = nil if @run_overstate == UNSET_VALUE @run_overstate = nil if @run_overstate == UNSET_VALUE
@ -102,6 +91,15 @@ module VagrantPlugins
@version = nil if @version == UNSET_VALUE @version = nil if @version == UNSET_VALUE
@run_service = nil if @run_service == UNSET_VALUE @run_service = nil if @run_service == UNSET_VALUE
@master_id = nil if @master_id == 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 end
def pillar(data) def pillar(data)
@ -111,14 +109,14 @@ module VagrantPlugins
def validate(machine) def validate(machine)
errors = _detected_errors errors = _detected_errors
if @minion_config if @minion_config && @minion_config != UNSET_VALUE
expanded = Pathname.new(@minion_config).expand_path(machine.env.root_path) expanded = Pathname.new(@minion_config).expand_path(machine.env.root_path)
if !expanded.file? if !expanded.file?
errors << I18n.t("vagrant.provisioners.salt.minion_config_nonexist", missing_config_file: expanded) errors << I18n.t("vagrant.provisioners.salt.minion_config_nonexist", missing_config_file: expanded)
end end
end end
if @master_config if @master_config && @master_config != UNSET_VALUE
expanded = Pathname.new(@master_config).expand_path(machine.env.root_path) expanded = Pathname.new(@master_config).expand_path(machine.env.root_path)
if !expanded.file? if !expanded.file?
errors << I18n.t("vagrant.provisioners.salt.master_config_nonexist", missing_config_file: expanded) errors << I18n.t("vagrant.provisioners.salt.master_config_nonexist", missing_config_file: expanded)

View File

@ -3,7 +3,20 @@ require 'json'
module VagrantPlugins module VagrantPlugins
module Salt module Salt
class Provisioner < Vagrant.plugin("2", :provisioner) 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 def provision
set_default_configs
upload_configs upload_configs
upload_keys upload_keys
run_bootstrap_script run_bootstrap_script
@ -238,19 +251,19 @@ module VagrantPlugins
bootstrap_path = get_bootstrap bootstrap_path = get_bootstrap
if @machine.config.vm.communicator == :winrm if @machine.config.vm.communicator == :winrm
if @config.version if @config.version
options += " -version %s" % @config.version options += " -version %s" % @config.version
end end
if @config.run_service if @config.run_service
@machine.env.ui.info "Salt minion will be stopped after installing." @machine.env.ui.info "Salt minion will be stopped after installing."
options += " -runservice %s" % @config.run_service options += " -runservice %s" % @config.run_service
end end
if @config.minion_id if @config.minion_id
@machine.env.ui.info "Setting minion to @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 end
if @config.master_id if @config.master_id
@machine.env.ui.info "Setting master to @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 end
bootstrap_destination = File.join(config_dir, "bootstrap_salt.ps1") bootstrap_destination = File.join(config_dir, "bootstrap_salt.ps1")
else else
@ -390,6 +403,16 @@ module VagrantPlugins
@machine.communicate.sudo(cmd, &log_output) @machine.communicate.sudo(cmd, &log_output)
end end
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 end
end end

View File

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

View File

@ -177,7 +177,12 @@ module VagrantPlugins
download_path.delete if download_path.file? download_path.delete if download_path.file?
begin 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) ext = File.extname(config.path)
script = download_path.read script = download_path.read
ensure ensure

View File

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

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

@ -734,6 +734,14 @@ en:
downloader_interrupted: |- downloader_interrupted: |-
The download was interrupted by an external signal. It did not The download was interrupted by an external signal. It did not
complete. 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: |- env_inval: |-
Vagrant received an "EINVAL" error while attempting to set some Vagrant received an "EINVAL" error while attempting to set some
environment variables. This is usually caused by the total size of your environment variables. This is usually caused by the total size of your
@ -877,6 +885,14 @@ en:
the issues below and execute "vagrant reload": the issues below and execute "vagrant reload":
%{output} %{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: |- nfs_cant_read_exports: |-
Vagrant can't read your current NFS exports! The exports file should be Vagrant can't read your current NFS exports! The exports file should be
readable by any user. This is usually caused by invalid permissions 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. by contacting a plugin author to see if they can address the conflict.
%{conflicts} %{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: |- plugin_load_error: |-
The plugins failed to load properly. The error message given is The plugins failed to load properly. The error message given is
shown below. shown below.
@ -1065,6 +1095,15 @@ en:
Guest path: %{guestpath} Guest path: %{guestpath}
Command: %{command} Command: %{command}
Error: %{stderr} 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_not_found: |-
"rsync" could not be found on your PATH. Make sure that rsync "rsync" could not be found on your PATH. Make sure that rsync
is properly installed on your system and available on the PATH. 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 the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant. `vagrantup.com` for more information on using Vagrant.
plugin: 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: |- installed_license: |-
The license for '%{name}' was successfully installed! The license for '%{name}' was successfully installed!
installing_license: |- installing_license: |-
@ -1584,6 +1641,20 @@ en:
post_install: |- post_install: |-
Post install message from the '%{name}' plugin: 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} %{message}
snapshot: snapshot:
not_supported: |- 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 describe "#call" do
it "should update all plugins if none are specified" 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(:update_plugins).with([]).once.and_return([])
expect(manager).to receive(:installed_plugins).twice.and_return({})
expect(app).to receive(:call).with(env).once expect(app).to receive(:call).with(env).once
subject.call(env) subject.call(env)
end end
it "should update specified plugins" do it "should update specified plugins" do
expect(manager).to receive(:update_plugins).with(["foo"]).once.and_return([]) 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 expect(app).to receive(:call).with(env).once
env[:plugin_name] = ["foo"] env[:plugin_name] = ["foo"]

View File

@ -5,6 +5,8 @@ require Vagrant.source_root.join("plugins/communicators/ssh/communicator")
describe VagrantPlugins::CommunicatorSSH::Communicator do describe VagrantPlugins::CommunicatorSSH::Communicator do
include_context "unit" include_context "unit"
let(:export_command_template){ 'export %ENV_KEY%="%ENV_VALUE%"' }
# SSH configuration information mock # SSH configuration information mock
let(:ssh) do let(:ssh) do
double("ssh", double("ssh",
@ -15,6 +17,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
pty: false, pty: false,
keep_alive: false, keep_alive: false,
insert_key: false, insert_key: false,
export_command_template: export_command_template,
shell: 'bash -l' shell: 'bash -l'
) )
end end
@ -509,4 +512,18 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
end end
end 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 end

View File

@ -45,7 +45,7 @@ describe VagrantPlugins::CommunicatorWinRM::Communicator do
port: '22', port: '22',
}) })
# Makes ready? return true # 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 end
it "retries ssh_info until ready" do it "retries ssh_info until ready" do
@ -57,22 +57,22 @@ describe VagrantPlugins::CommunicatorWinRM::Communicator do
describe ".ready?" do describe ".ready?" do
it "returns true if hostname command executes without error" 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 expect(subject.ready?).to be_true
end end
it "returns false if hostname command fails with a transient error" do 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 expect(subject.ready?).to be_false
end end
it "raises an error if hostname command fails with an unknown error" do 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) expect { subject.ready? }.to raise_error(Vagrant::Errors::VagrantError)
end end
it "raises timeout error when hostname command takes longer then winrm timeout" do 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 sleep 2 # winrm.timeout = 1
end end
expect { subject.ready? }.to raise_error(Timeout::Error) 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 describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
include_context "unit" include_context "unit"
let(:session) { double("winrm_session", create_executor: executor) } let(:session) { double("winrm_session") }
let(:executor) { double("command_executor") } let(:executor) { double("command_executor") }
let(:port) { config.transport == :ssl ? 5986 : 5985 } let(:port) { config.transport == :ssl ? 5986 : 5985 }
let(:config) { let(:config) {
@ -22,6 +22,8 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
end end
} }
before { allow(session).to receive(:create_executor).and_yield(executor) }
subject do subject do
described_class.new('localhost', port, config).tap do |comm| described_class.new('localhost', port, config).tap do |comm|
allow(comm).to receive(:new_session).and_return(session) 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) 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(/mkdir -p \/guest/)
expect(comm.received_commands[0]).to match(/mount -t nfs/) expect(comm.received_commands[1]).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(/1.2.3.4:\/host \/guest/)
end end
it "mounts with options" do it "mounts with options" do
@ -49,7 +48,7 @@ describe "VagrantPlugins::GuestBSD::Cap::NFS" do
} }
cap.mount_nfs_folder(machine, ip, folders) 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 end
it "escapes host and guest paths" do it "escapes host and guest paths" do
@ -61,8 +60,8 @@ describe "VagrantPlugins::GuestBSD::Cap::NFS" do
} }
cap.mount_nfs_folder(machine, ip, folders) cap.mount_nfs_folder(machine, ip, folders)
expect(comm.received_commands[0]).to match(/host\\\'s/) expect(comm.received_commands[1]).to match(/host\\\'s/)
expect(comm.received_commands[0]).to match(/guest\\\ with\\\ spaces/) expect(comm.received_commands[1]).to match(/guest\\\ with\\\ spaces/)
end end
end end
end end

View File

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

View File

@ -44,5 +44,41 @@ describe "VagrantPlugins::GuestLinux::Cap::NetworkInterfaces" do
result = cap.network_interfaces(machine) result = cap.network_interfaces(machine)
expect(result).to eq(["enp0s3", "enp0s5", "enp0s8", "enp0s10", "enp1s3"]) expect(result).to eq(["enp0s3", "enp0s5", "enp0s8", "enp0s10", "enp1s3"])
end 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
end end

View File

@ -70,15 +70,27 @@ describe "VagrantPlugins::GuestLinux::Cap::Rsync" do
end end
context "with excludes provided" do context "with excludes provided" do
let(:excludes){ ["tmp", "state/*"] } let(:excludes){ ["tmp", "state/*", "path/with a/space"] }
it "ignores files that are excluded" do it "ignores files that are excluded" do
comm.expect_command( # comm.expect_command(
"find #{guest_directory} -path #{File.join(guest_directory, excludes.first)} -prune -o " \ # "find #{guest_directory} -path #{Shellwords.escape(File.join(guest_directory, excludes.first))} -prune -o " \
"-path #{File.join(guest_directory, excludes.last)} -prune -o '!' -type l -a '(' ! -user " \ # "-path #{Shellwords.escape(File.join(guest_directory, excludes.last))} -prune -o '!' " \
"#{owner} -or ! -group #{group} ')' -exec chown #{owner}:#{group} '{}' +" # "-path -type l -a '(' ! -user " \
) # "#{owner} -or ! -group #{group} ')' -exec chown #{owner}:#{group} '{}' +"
# )
cap.rsync_post(machine, options) 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 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 it "creates and starts the networks" do
cap.configure_networks(machine, [network_1, network_2]) 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 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 end
end end

View File

@ -56,9 +56,10 @@ describe "VagrantPlugins::GuestRedHat::Cap::ConfigureNetworks" do
cap.configure_networks(machine, [network_1, network_2]) 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\/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\/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 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 assert_valid
end 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 end
describe "#communicator" do describe "#communicator" do
@ -526,6 +534,21 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
subject.finalize! subject.finalize!
assert_valid assert_valid
end 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 end
describe "#usable_port_range" do 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" } let(:existing_file) { "this/path/is/a/stub" }
it "supports a list of options" do it "supports a list of options" do
supported_options = %w( extra_vars supported_options = %w( config_file
extra_vars
galaxy_command galaxy_command
galaxy_role_file galaxy_role_file
galaxy_roles_path galaxy_roles_path
@ -27,6 +28,7 @@ describe VagrantPlugins::Ansible::Config::Guest do
inventory_path inventory_path
limit limit
playbook playbook
playbook_command
provisioning_path provisioning_path
raw_arguments raw_arguments
skip_tags skip_tags

View File

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

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