diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53565b085..05889b02e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,58 @@
-## 1.7.0 (unreleased)
+## 1.7.2 (unreleased)
FEATURES:
+ - provisioners/salt: add support for grains [GH-4895]
+
+IMPROVEMENTS:
+
+ - commands/reload,up: `--provision-with` implies `--provision` [GH-5085]
+
+BUG FIXES:
+
+ - core: private boxes still referencing vagrantcloud.com will have
+ their vagrant login access token properly appended
+ - core: push plugin configuration is properly validated
+ - core: restore box packaging functionality
+ - commands/push: push lookups are by user-defined name, not push
+ strategy name [GH-4975]
+ - commands/push: validate the configuration
+ - guests/arch: fix network configuration due to poor line breaks. [GH-4964]
+ - guests/solaris: Merge configurations properly so configs can be set
+ in default Vagrantfiles. [GH-5092]
+ - providers/docker: Symlinks in shared folders work. [GH-5093]
+ - providers/hyperv: VM start errors turn into proper Vagrant errors. [GH-5101]
+ - provisioners/chef: remove Chef version check from solo.rb generation and
+ make `roles_path` populate correctly
+ - pushes/ftp: expand file paths relative to the Vagrantfile
+ - pushes/ftp: improved debugging output
+ - pushes/ftp: create parent directories if they do not exist on the remote
+ server
+
+## 1.7.1 (December 11, 2014)
+
+IMPROVEMENTS:
+
+ - provisioners/ansible: Use Docker proxy if needed. [GH-4906]
+
+BUG FIXES:
+
+ - providers/docker: Add support of SSH agent forwarding. [GH-4905]
+
+## 1.7.0 (December 9, 2014)
+
+BREAKING CHANGES:
+
+ - provisioners/ansible: `raw_arguments` has now highest priority
+ - provisioners/ansible: only the `ssh` connection transport is supported
+ (`paramiko` can be enabled with `raw_arguments` at your own risks)
+
+FEATURES:
+
+ - **Vagrant Push**: Vagrant can now deploy! `vagrant push` is a single
+ command to deploy your application. Deploy to Heroku, FTP, or
+ HashiCorp's commercial product Atlas. New push strategies can be
+ added with plugins.
- **Named provisioners**: Provisioners can now be named. This name is used
for output as well as `--provision-with` for better control.
- Default provider logic improved: Providers in `config.vm.provider` blocks
@@ -9,6 +60,7 @@ FEATURES:
providers are chosen before later ones. [GH-3812]
- If the default insecure keypair is used, Vagrant will automatically replace
it with a randomly generated keypair on first `vagrant up`. [GH-2608]
+ - Vagrant Login is now part of Vagrant core
- Chef Zero provisioner: Use Chef 11's "local" mode to run recipes against an
in-memory Chef Server
- Chef Apply provisioner: Specify inline Chef recipes and recipe snippets
@@ -88,6 +140,10 @@ BUG FIXES:
IP address and don't allow it. [GH-4671]
- providers/virtualbox: Show more descriptive error if VirtualBox is
reporting an empty version. [GH-4657]
+ - provisioners/ansible: Force `ssh` (OpenSSH) connection by default [GH-3396]
+ - provisioners/ansible: Don't use or modify `~/.ssh/known_hosts` file by default,
+ similarly to native vagrant commands [GH-3900]
+ - provisioners/ansible: Use intermediate Docker host when needed. [GH-4071]
- provisioners/docker: Get GPG key over SSL. [GH-4597]
- provisioners/docker: Search for docker binary in multiple places. [GH-4580]
- provisioners/salt: Highstate works properly with a master. [GH-4471]
diff --git a/Vagrantfile b/Vagrantfile
index 075157ed9..d21f2468d 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -13,6 +13,14 @@ Vagrant.configure("2") do |config|
end
config.vm.provision "shell", inline: $shell
+
+ config.push.define "www", strategy: "local-exec" do |push|
+ push.script = "scripts/website_push_www.sh"
+ end
+
+ config.push.define "docs", strategy: "local-exec" do |push|
+ push.script = "scripts/website_push_docs.sh"
+ end
end
$shell = <<-CONTENTS
diff --git a/contrib/bash/completion.sh b/contrib/bash/completion.sh
index 636795744..e21dc8dcf 100644
--- a/contrib/bash/completion.sh
+++ b/contrib/bash/completion.sh
@@ -65,7 +65,7 @@ _vagrant() {
then
case "$prev" in
"init")
- local box_list=$(find $HOME/.vagrant.d/boxes -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
+ local box_list=$(find "${VAGRANT_HOME:-${HOME}/.vagrant.d}/boxes" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
COMPREPLY=($(compgen -W "${box_list}" -- ${cur}))
return 0
;;
@@ -111,7 +111,7 @@ _vagrant() {
then
case "$prev" in
"remove"|"repackage")
- local box_list=$(find $HOME/.vagrant.d/boxes -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
+ local box_list=$(find "${VAGRANT_HOME:-${HOME}/.vagrant.d}/boxes" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
COMPREPLY=($(compgen -W "${box_list}" -- ${cur}))
return 0
;;
diff --git a/contrib/sudoers/linux-fedora b/contrib/sudoers/linux-fedora
new file mode 100644
index 000000000..f2d64b66d
--- /dev/null
+++ b/contrib/sudoers/linux-fedora
@@ -0,0 +1,6 @@
+Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
+Cmnd_Alias VAGRANT_NFSD_CHECK = /usr/bin/systemctl status nfs-server.service
+Cmnd_Alias VAGRANT_NFSD_START = /usr/bin/systemctl start nfs-server.service
+Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar
+Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /etc/exports
+%vagrant ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE
diff --git a/contrib/sudoers/linux b/contrib/sudoers/linux-ubuntu
similarity index 100%
rename from contrib/sudoers/linux
rename to contrib/sudoers/linux-ubuntu
diff --git a/lib/vagrant.rb b/lib/vagrant.rb
index d3ab6f37c..4b5dc84ba 100644
--- a/lib/vagrant.rb
+++ b/lib/vagrant.rb
@@ -123,6 +123,7 @@ module Vagrant
c.register([:"2", :host]) { Plugin::V2::Host }
c.register([:"2", :provider]) { Plugin::V2::Provider }
c.register([:"2", :provisioner]) { Plugin::V2::Provisioner }
+ c.register([:"2", :push]) { Plugin::V2::Push }
c.register([:"2", :synced_folder]) { Plugin::V2::SyncedFolder }
end
diff --git a/lib/vagrant/action/builtin/box_add.rb b/lib/vagrant/action/builtin/box_add.rb
index 554613b94..13688bae5 100644
--- a/lib/vagrant/action/builtin/box_add.rb
+++ b/lib/vagrant/action/builtin/box_add.rb
@@ -147,7 +147,7 @@ module Vagrant
# element is an authenticated URL.
# @param [Hash] env
# @param [Bool] expanded True if the metadata URL was expanded with
- # a Vagrant Cloud server URL.
+ # a Atlas server URL.
def add_from_metadata(url, env, expanded)
original_url = env[:box_url]
provider = env[:box_provider]
diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb
index 4f087a103..acda0c178 100644
--- a/lib/vagrant/environment.rb
+++ b/lib/vagrant/environment.rb
@@ -539,6 +539,41 @@ module Vagrant
end
end
+ # This executes the push with the given name, raising any exceptions that
+ # occur.
+ #
+ # Precondition: the push is not nil and exists.
+ def push(name)
+ @logger.info("Getting push: #{name}")
+
+ name = name.to_sym
+
+ pushes = self.vagrantfile.config.push.__compiled_pushes
+ if !pushes.key?(name)
+ raise Vagrant::Errors::PushStrategyNotDefined,
+ name: name,
+ pushes: pushes.keys
+ end
+
+ strategy, config = pushes[name]
+ push_registry = Vagrant.plugin("2").manager.pushes
+ klass, _ = push_registry.get(strategy)
+ if klass.nil?
+ raise Vagrant::Errors::PushStrategyNotLoaded,
+ name: strategy,
+ pushes: push_registry.keys
+ end
+
+ klass.new(self, config).push
+ end
+
+ # The list of pushes defined in this Vagrantfile.
+ #
+ # @return [Array]
+ def pushes
+ self.vagrantfile.config.push.__compiled_pushes.keys
+ end
+
# This returns a machine with the proper provider for this environment.
# The machine named by `name` must be in this environment.
#
diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb
index 623073a97..de002ffed 100644
--- a/lib/vagrant/errors.rb
+++ b/lib/vagrant/errors.rb
@@ -556,6 +556,22 @@ module Vagrant
error_key(:plugin_uninstall_system)
end
+ class PushesNotDefined < VagrantError
+ error_key(:pushes_not_defined)
+ end
+
+ class PushStrategyNotDefined < VagrantError
+ error_key(:push_strategy_not_defined)
+ end
+
+ class PushStrategyNotLoaded < VagrantError
+ error_key(:push_strategy_not_loaded)
+ end
+
+ class PushStrategyNotProvided < VagrantError
+ error_key(:push_strategy_not_provided)
+ end
+
class RSyncError < VagrantError
error_key(:rsync_error)
end
diff --git a/lib/vagrant/machine.rb b/lib/vagrant/machine.rb
index 07489e977..636bd0b56 100644
--- a/lib/vagrant/machine.rb
+++ b/lib/vagrant/machine.rb
@@ -150,9 +150,11 @@ module Vagrant
# @param [Hash] extra_env This data will be passed into the action runner
# as extra data set on the environment hash for the middleware
# runner.
- def action(name, **opts)
+ def action(name, opts=nil)
@logger.info("Calling action: #{name} on provider #{@provider}")
+ opts ||= {}
+
# Determine whether we lock or not
lock = true
lock = opts.delete(:lock) if opts.has_key?(:lock)
diff --git a/lib/vagrant/plugin/v2.rb b/lib/vagrant/plugin/v2.rb
index 1539667bd..953f73ff6 100644
--- a/lib/vagrant/plugin/v2.rb
+++ b/lib/vagrant/plugin/v2.rb
@@ -16,6 +16,7 @@ module Vagrant
autoload :Manager, "vagrant/plugin/v2/manager"
autoload :Plugin, "vagrant/plugin/v2/plugin"
autoload :Provider, "vagrant/plugin/v2/provider"
+ autoload :Push, "vagrant/plugin/v2/push"
autoload :Provisioner, "vagrant/plugin/v2/provisioner"
autoload :SyncedFolder, "vagrant/plugin/v2/synced_folder"
end
diff --git a/lib/vagrant/plugin/v2/components.rb b/lib/vagrant/plugin/v2/components.rb
index 7bae6c29a..d7c64d370 100644
--- a/lib/vagrant/plugin/v2/components.rb
+++ b/lib/vagrant/plugin/v2/components.rb
@@ -54,6 +54,11 @@ module Vagrant
# @return [Hash]
attr_reader :provider_capabilities
+ # This contains all the push implementations by name.
+ #
+ # @return [Registry>]
+ attr_reader :pushes
+
# This contains all the synced folder implementations by name.
#
# @return [Registry>]
@@ -71,6 +76,7 @@ module Vagrant
@host_capabilities = Hash.new { |h, k| h[k] = Registry.new }
@providers = Registry.new
@provider_capabilities = Hash.new { |h, k| h[k] = Registry.new }
+ @pushes = Registry.new
@synced_folders = Registry.new
end
end
diff --git a/lib/vagrant/plugin/v2/manager.rb b/lib/vagrant/plugin/v2/manager.rb
index 62a23f82a..ce76d1fc1 100644
--- a/lib/vagrant/plugin/v2/manager.rb
+++ b/lib/vagrant/plugin/v2/manager.rb
@@ -172,6 +172,28 @@ module Vagrant
end
end
+ # This returns all registered pushes.
+ #
+ # @return [Registry]
+ def pushes
+ Registry.new.tap do |result|
+ @registered.each do |plugin|
+ result.merge!(plugin.components.pushes)
+ end
+ end
+ end
+
+ # This returns all the config classes for the various pushes.
+ #
+ # @return [Registry]
+ def push_configs
+ Registry.new.tap do |result|
+ @registered.each do |plugin|
+ result.merge!(plugin.components.configs[:push])
+ end
+ end
+ end
+
# This returns all synced folder implementations.
#
# @return [Registry]
diff --git a/lib/vagrant/plugin/v2/plugin.rb b/lib/vagrant/plugin/v2/plugin.rb
index 9a7f6177d..a39b3cf10 100644
--- a/lib/vagrant/plugin/v2/plugin.rb
+++ b/lib/vagrant/plugin/v2/plugin.rb
@@ -221,6 +221,18 @@ module Vagrant
data[:provisioners]
end
+ # Registers additional pushes to be available.
+ #
+ # @param [String] name Name of the push.
+ # @param [Hash] options List of options for the push.
+ def self.push(name, options=nil, &block)
+ components.pushes.register(name.to_sym) do
+ [block.call, options]
+ end
+
+ nil
+ end
+
# Registers additional synced folder implementations.
#
# @param [String] name Name of the implementation.
diff --git a/lib/vagrant/plugin/v2/push.rb b/lib/vagrant/plugin/v2/push.rb
new file mode 100644
index 000000000..f8bc15d53
--- /dev/null
+++ b/lib/vagrant/plugin/v2/push.rb
@@ -0,0 +1,27 @@
+module Vagrant
+ module Plugin
+ module V2
+ class Push
+ attr_reader :env
+ attr_reader :config
+
+ # Initializes the pusher with the given environment the push
+ # configuration.
+ #
+ # @param [Environment] env
+ # @param [Object] config Push configuration
+ def initialize(env, config)
+ @env = env
+ @config = config
+ end
+
+ # This is the method called when the actual pushing should be
+ # done.
+ #
+ # No return value is expected.
+ def push
+ end
+ end
+ end
+ end
+end
diff --git a/lib/vagrant/registry.rb b/lib/vagrant/registry.rb
index 5095f2097..f3c86edec 100644
--- a/lib/vagrant/registry.rb
+++ b/lib/vagrant/registry.rb
@@ -34,7 +34,7 @@ module Vagrant
def has_key?(key)
@items.has_key?(key)
end
-
+
# Returns an array populated with the keys of this object.
#
# @return [Array]
@@ -49,6 +49,21 @@ module Vagrant
end
end
+ # Return the number of elements in this registry.
+ #
+ # @return [Fixnum]
+ def length
+ @items.keys.length
+ end
+ alias_method :size, :length
+
+ # Checks if this registry has any items.
+ #
+ # @return [Boolean]
+ def empty?
+ @items.keys.empty?
+ end
+
# Merge one registry with another and return a completely new
# registry. Note that the result cache is completely busted, so
# any gets on the new registry will result in a cache miss.
diff --git a/lib/vagrant/shared_helpers.rb b/lib/vagrant/shared_helpers.rb
index c2c52ae10..b195c36df 100644
--- a/lib/vagrant/shared_helpers.rb
+++ b/lib/vagrant/shared_helpers.rb
@@ -5,12 +5,12 @@ require "thread"
module Vagrant
@@global_lock = Mutex.new
- # This is the default endpoint of the Vagrant Cloud in
+ # This is the default endpoint of the Atlas in
# use. API calls will be made to this for various functions
# of Vagrant that may require remote access.
#
# @return [String]
- DEFAULT_SERVER_URL = "https://vagrantcloud.com"
+ DEFAULT_SERVER_URL = "https://atlas.hashicorp.com"
# This holds a global lock for the duration of the block. This should
# be invoked around anything that is modifying process state (such as
diff --git a/lib/vagrant/util/subprocess.rb b/lib/vagrant/util/subprocess.rb
index dbb28ea97..278d68d01 100644
--- a/lib/vagrant/util/subprocess.rb
+++ b/lib/vagrant/util/subprocess.rb
@@ -25,7 +25,7 @@ module Vagrant
def initialize(*command)
@options = command.last.is_a?(Hash) ? command.pop : {}
@command = command.dup
- @command.each { |s| s.encode!(Encoding.default_external) }
+ @command.each { |s| s.encode(Encoding.default_external) }
@command[0] = Which.which(@command[0]) if !File.file?(@command[0])
if !@command[0]
raise Errors::CommandUnavailableWindows, file: command[0] if Platform.windows?
diff --git a/plugins/commands/box/command/add.rb b/plugins/commands/box/command/add.rb
index 0356ee298..d82d441a8 100644
--- a/plugins/commands/box/command/add.rb
+++ b/plugins/commands/box/command/add.rb
@@ -47,7 +47,7 @@ module VagrantPlugins
end
o.separator ""
- o.separator "The box descriptor can be the name of a box on Vagrant Cloud,"
+ o.separator "The box descriptor can be the name of a box on HashiCorp's Atlas,"
o.separator "or a URL, or a local .box file, or a local .json file containing"
o.separator "the catalog metadata."
o.separator ""
diff --git a/plugins/commands/login/client.rb b/plugins/commands/login/client.rb
new file mode 100644
index 000000000..64a8dd1c5
--- /dev/null
+++ b/plugins/commands/login/client.rb
@@ -0,0 +1,100 @@
+require "rest_client"
+
+module VagrantPlugins
+ module LoginCommand
+ class Client
+ # Initializes a login client with the given Vagrant::Environment.
+ #
+ # @param [Vagrant::Environment] env
+ def initialize(env)
+ @env = env
+ end
+
+ # Removes the token, effectively logging the user out.
+ def clear_token
+ token_path.delete if token_path.file?
+ end
+
+ # Checks if the user is logged in by verifying their authentication
+ # token.
+ #
+ # @return [Boolean]
+ def logged_in?
+ token = self.token
+ return false if !token
+
+ with_error_handling do
+ url = "#{Vagrant.server_url}/api/v1/authenticate" +
+ "?access_token=#{token}"
+ RestClient.get(url, content_type: :json)
+ true
+ end
+ end
+
+ # Login logs a user in and returns the token for that user. The token
+ # is _not_ stored unless {#store_token} is called.
+ #
+ # @param [String] user
+ # @param [String] pass
+ # @return [String] token The access token, or nil if auth failed.
+ def login(user, pass)
+ with_error_handling do
+ url = "#{Vagrant.server_url}/api/v1/authenticate"
+ request = { "user" => { "login" => user, "password" => pass } }
+ response = RestClient.post(
+ url, JSON.dump(request), content_type: :json)
+ data = JSON.load(response.to_s)
+ data["token"]
+ end
+ end
+
+ # Stores the given token locally, removing any previous tokens.
+ #
+ # @param [String] token
+ def store_token(token)
+ token_path.open("w") do |f|
+ f.write(token)
+ end
+ nil
+ end
+
+ # Reads the access token if there is one. This will first read the
+ # `ATLAS_TOKEN` environment variable and then fallback to the stored
+ # access token on disk.
+ #
+ # @return [String]
+ def token
+ if ENV["ATLAS_TOKEN"] && !ENV["ATLAS_TOKEN"].empty?
+ return ENV["ATLAS_TOKEN"]
+ end
+
+ if token_path.exist?
+ return token_path.read.strip
+ end
+
+ nil
+ end
+
+ protected
+
+ def with_error_handling(&block)
+ yield
+ rescue RestClient::Unauthorized
+ false
+ rescue RestClient::NotAcceptable => e
+ begin
+ errors = JSON.parse(e.response)["errors"].join("\n")
+ raise Errors::ServerError, errors: errors
+ rescue JSON::ParserError; end
+
+ raise "An unexpected error occurred: #{e.inspect}"
+ rescue SocketError
+ raise Errors::ServerUnreachable, url: Vagrant.server_url.to_s
+ end
+
+ def token_path
+ @env.data_dir.join("vagrant_login_token")
+ end
+ end
+ end
+end
diff --git a/plugins/commands/login/command.rb b/plugins/commands/login/command.rb
new file mode 100644
index 000000000..e16b1afaf
--- /dev/null
+++ b/plugins/commands/login/command.rb
@@ -0,0 +1,83 @@
+module VagrantPlugins
+ module LoginCommand
+ class Command < Vagrant.plugin("2", "command")
+ def self.synopsis
+ "log in to HashiCorp's Atlas"
+ end
+
+ def execute
+ options = {}
+
+ opts = OptionParser.new do |o|
+ o.banner = "Usage: vagrant login"
+ o.separator ""
+ o.on("-c", "--check", "Only checks if you're logged in") do |c|
+ options[:check] = c
+ end
+
+ o.on("-k", "--logout", "Logs you out if you're logged in") do |k|
+ options[:logout] = k
+ end
+ end
+
+ # Parse the options
+ argv = parse_options(opts)
+ return if !argv
+
+ @client = Client.new(@env)
+
+ # Determine what task we're actually taking based on flags
+ if options[:check]
+ return execute_check
+ elsif options[:logout]
+ return execute_logout
+ end
+
+ # Let the user know what is going on.
+ @env.ui.output(I18n.t("login_command.command_header") + "\n")
+
+ # If it is a private cloud installation, show that
+ if Vagrant.server_url != Vagrant::DEFAULT_SERVER_URL
+ @env.ui.output("Atlas URL: #{Vagrant.server_url}")
+ end
+
+ # Ask for the username
+ login = nil
+ password = nil
+ while !login
+ login = @env.ui.ask("Atlas Username: ")
+ end
+
+ while !password
+ password = @env.ui.ask("Password (will be hidden): ", echo: false)
+ end
+
+ token = @client.login(login, password)
+ if !token
+ @env.ui.error(I18n.t("login_command.invalid_login"))
+ return 1
+ end
+
+ @client.store_token(token)
+ @env.ui.success(I18n.t("login_command.logged_in"))
+ 0
+ end
+
+ def execute_check
+ if @client.logged_in?
+ @env.ui.success(I18n.t("login_command.check_logged_in"))
+ return 0
+ else
+ @env.ui.error(I18n.t("login_command.check_not_logged_in"))
+ return 1
+ end
+ end
+
+ def execute_logout
+ @client.clear_token
+ @env.ui.success(I18n.t("login_command.logged_out"))
+ return 0
+ end
+ end
+ end
+end
diff --git a/plugins/commands/login/errors.rb b/plugins/commands/login/errors.rb
new file mode 100644
index 000000000..614c37cf6
--- /dev/null
+++ b/plugins/commands/login/errors.rb
@@ -0,0 +1,17 @@
+module VagrantPlugins
+ module LoginCommand
+ module Errors
+ class Error < Vagrant::Errors::VagrantError
+ error_namespace("login_command.errors")
+ end
+
+ class ServerError < Error
+ error_key(:server_error)
+ end
+
+ class ServerUnreachable < Error
+ error_key(:server_unreachable)
+ end
+ end
+ end
+end
diff --git a/plugins/commands/login/locales/en.yml b/plugins/commands/login/locales/en.yml
new file mode 100644
index 000000000..51020df1d
--- /dev/null
+++ b/plugins/commands/login/locales/en.yml
@@ -0,0 +1,30 @@
+en:
+ login_command:
+ errors:
+ server_error: |-
+ The Atlas server responded with an not-OK response:
+
+ %{errors}
+ server_unreachable: |-
+ The Atlas server is not currently accepting connections. Please check
+ your network connection and try again later.
+
+ check_logged_in: |-
+ You are already logged in.
+ check_not_logged_in: |-
+ You are not currently logged in. Please run `vagrant login` and provide
+ your login information to authenticate.
+ command_header: |-
+ In a moment we will ask for your username and password to HashiCorp's
+ Atlas. After authenticating, we will store an access token locally on
+ disk. Your login details will be transmitted over a secure connection, and
+ are never stored on disk locally.
+
+ If you do not have an Atlas account, sign up at
+ https://atlas.hashicorp.com.
+ invalid_login: |-
+ Invalid username or password. Please try again.
+ logged_in: |-
+ You are now logged in.
+ logged_out: |-
+ You are logged out.
diff --git a/plugins/commands/login/middleware/add_authentication.rb b/plugins/commands/login/middleware/add_authentication.rb
new file mode 100644
index 000000000..faaa3cfe7
--- /dev/null
+++ b/plugins/commands/login/middleware/add_authentication.rb
@@ -0,0 +1,45 @@
+require "uri"
+
+require_relative "../client"
+
+module VagrantPlugins
+ module LoginCommand
+ class AddAuthentication
+ def initialize(app, env)
+ @app = app
+ end
+
+ def call(env)
+ client = Client.new(env[:env])
+ token = client.token
+
+ if token && Vagrant.server_url
+ server_uri = URI.parse(Vagrant.server_url)
+
+ env[:box_urls].map! do |url|
+ u = URI.parse(url)
+ replace = u.host == server_uri.host
+ if !replace
+ # We need this in here for the transition we made from
+ # Vagrant Cloud to Atlas. This preserves access tokens
+ # appending to both without leaking access tokens to
+ # unsavory URLs.
+ replace = u.host == "vagrantcloud.com" &&
+ server_uri.host == "atlas.hashicorp.com"
+ end
+
+ if replace
+ u.query ||= ""
+ u.query += "&" if u.query != ""
+ u.query += "access_token=#{token}"
+ end
+
+ u.to_s
+ end
+ end
+
+ @app.call(env)
+ end
+ end
+ end
+end
diff --git a/plugins/commands/login/plugin.rb b/plugins/commands/login/plugin.rb
new file mode 100644
index 000000000..efb84a556
--- /dev/null
+++ b/plugins/commands/login/plugin.rb
@@ -0,0 +1,35 @@
+require "vagrant"
+
+module VagrantPlugins
+ module LoginCommand
+ autoload :Client, File.expand_path("../client", __FILE__)
+ autoload :Errors, File.expand_path("../errors", __FILE__)
+
+ class Plugin < Vagrant.plugin("2")
+ name "vagrant-login"
+ description <<-DESC
+ Provides the login command and internal API access to Atlas.
+ DESC
+
+ command(:login) do
+ require_relative "command"
+ init!
+ Command
+ end
+
+ action_hook(:cloud_authenticated_boxes, :authenticate_box_url) do |hook|
+ require_relative "middleware/add_authentication"
+ hook.prepend(AddAuthentication)
+ end
+
+ protected
+
+ def self.init!
+ return if defined?(@_init)
+ I18n.load_path << File.expand_path("../locales/en.yml", __FILE__)
+ I18n.reload!
+ @_init = true
+ end
+ end
+ end
+end
diff --git a/plugins/commands/package/command.rb b/plugins/commands/package/command.rb
index 3bb2cf563..e54335475 100644
--- a/plugins/commands/package/command.rb
+++ b/plugins/commands/package/command.rb
@@ -80,7 +80,7 @@ module VagrantPlugins
acc
end
- vm.action(:package, opts)
+ vm.action(:package, **opts)
end
end
end
diff --git a/plugins/commands/push/command.rb b/plugins/commands/push/command.rb
new file mode 100644
index 000000000..902a0c946
--- /dev/null
+++ b/plugins/commands/push/command.rb
@@ -0,0 +1,75 @@
+require 'optparse'
+
+module VagrantPlugins
+ module CommandPush
+ class Command < Vagrant.plugin("2", :command)
+ def self.synopsis
+ "deploys code in this environment to a configured destination"
+ end
+
+ # @todo support multiple strategies if requested by the community
+ def execute
+ opts = OptionParser.new do |o|
+ o.banner = "Usage: vagrant push [strategy] [options]"
+ end
+
+ # Parse the options
+ argv = parse_options(opts)
+ return if !argv
+
+ name = validate_pushes!(@env.pushes, argv[0])
+
+ # Validate the configuration
+ @env.machine(@env.machine_names.first, @env.default_provider).action_raw(
+ :config_validate,
+ Vagrant::Action::Builtin::ConfigValidate)
+
+ @logger.debug("'push' environment with strategy: `#{name}'")
+ @env.push(name)
+
+ 0
+ end
+
+ # Validate that the given list of names corresponds to valid pushes.
+ #
+ # @raise Vagrant::Errors::PushesNotDefined
+ # if there are no pushes defined
+ # @raise Vagrant::Errors::PushStrategyNotProvided
+ # if there are multiple push strategies defined and none were specified
+ # @raise Vagrant::Errors::PushStrategyNotDefined
+ # if the given push name do not correspond to a push strategy
+ #
+ # @param [Array] pushes
+ # the list of pushes defined by the environment
+ # @param [String] name
+ # the name provided by the user on the command line
+ #
+ # @return [Symbol]
+ # the compiled list of pushes
+ #
+ def validate_pushes!(pushes, name = nil)
+ if pushes.nil? || pushes.empty?
+ raise Vagrant::Errors::PushesNotDefined
+ end
+
+ if name.nil?
+ if pushes.length == 1
+ return pushes.first.to_sym
+ else
+ raise Vagrant::Errors::PushStrategyNotProvided,
+ pushes: pushes.map(&:to_s)
+ end
+ end
+
+ name = name.to_sym
+ if !pushes.include?(name)
+ raise Vagrant::Errors::PushStrategyNotDefined,
+ name: name.to_s,
+ pushes: pushes.map(&:to_s)
+ end
+
+ return name
+ end
+ end
+ end
+end
diff --git a/plugins/commands/push/plugin.rb b/plugins/commands/push/plugin.rb
new file mode 100644
index 000000000..ecd24dd7a
--- /dev/null
+++ b/plugins/commands/push/plugin.rb
@@ -0,0 +1,17 @@
+require "vagrant"
+
+module VagrantPlugins
+ module CommandPush
+ class Plugin < Vagrant.plugin("2")
+ name "push command"
+ description <<-DESC
+ The `push` command deploys code in this environment.
+ DESC
+
+ command("push") do
+ require File.expand_path("../command", __FILE__)
+ Command
+ end
+ end
+ end
+end
diff --git a/plugins/commands/up/start_mixins.rb b/plugins/commands/up/start_mixins.rb
index e0aeb6de2..548d889ca 100644
--- a/plugins/commands/up/start_mixins.rb
+++ b/plugins/commands/up/start_mixins.rb
@@ -19,6 +19,8 @@ module VagrantPlugins
parser.on("--provision-with x,y,z", Array,
"Enable only certain provisioners, by type.") do |list|
options[:provision_types] = list.map { |type| type.to_sym }
+ options[:provision_enabled] = true
+ options[:provision_ignore_sentinel] = true
end
end
diff --git a/plugins/communicators/winrm/communicator.rb b/plugins/communicators/winrm/communicator.rb
index 7609f16b3..e815b4514 100644
--- a/plugins/communicators/winrm/communicator.rb
+++ b/plugins/communicators/winrm/communicator.rb
@@ -79,12 +79,21 @@ module VagrantPlugins
alias_method :sudo, :execute
def test(command, opts=nil)
- # If this is a *nix command with no Windows equivilant, assume failure
+ # If this is a *nix command (which we know about) with no Windows
+ # equivilant, assume failure
command = @cmd_filter.filter(command)
return false if command.empty?
- opts = { error_check: false }.merge(opts || {})
- execute(command, opts) == 0
+ opts = {
+ command: command,
+ elevated: false,
+ error_check: false,
+ }.merge(opts || {})
+
+ # If we're passed a *nix command which PS can't parse we get exit code
+ # 0, but output in stderr. We need to check both exit code and stderr.
+ output = shell.send(:powershell, command)
+ return output[:exitcode] == 0 && flatten_stderr(output).length == 0
end
def upload(from, to)
@@ -156,8 +165,8 @@ module VagrantPlugins
def raise_execution_error(output, opts)
# WinRM can return multiple stderr and stdout entries
error_opts = opts.merge(
- stdout: output[:data].collect { |e| e[:stdout] }.join,
- stderr: output[:data].collect { |e| e[:stderr] }.join
+ stdout: flatten_stdout(output),
+ stderr: flatten_stderr(output)
)
# Use a different error message key if the caller gave us one,
@@ -167,6 +176,20 @@ module VagrantPlugins
# Raise the error, use the type the caller gave us or the comm default
raise opts[:error_class], error_opts
end
+
+
+ # TODO: Replace with WinRM Output class when WinRM 1.3 is released
+ def flatten_stderr(output)
+ output[:data].map do | line |
+ line[:stderr]
+ end.compact.join
+ end
+
+ def flatten_stdout(output)
+ output[:data].map do | line |
+ line[:flatten_stdout]
+ end.compact.join
+ end
end #WinRM class
end
end
diff --git a/plugins/guests/arch/cap/configure_networks.rb b/plugins/guests/arch/cap/configure_networks.rb
index 3d69fee7c..fd06c24ea 100644
--- a/plugins/guests/arch/cap/configure_networks.rb
+++ b/plugins/guests/arch/cap/configure_networks.rb
@@ -11,20 +11,14 @@ module VagrantPlugins
def self.configure_networks(machine, networks)
interfaces = Array.new
- machine.communicate.sudo("ip -o -0 addr | grep -v LOOPBACK | awk
- '{print $2}' | sed 's/://'") do |_, result|
+ machine.communicate.sudo("ip -o -0 addr | grep -v LOOPBACK | awk '{print $2}' | sed 's/://'") do |_, result|
interfaces = result.split("\n")
end
networks.each do |network|
- # We use :device in the template instead of
- # eth#{network[:interface]} in order to support Predictable
- # Network Interfaces
network[:device] = interfaces[network[:interface]]
- entry =
- TemplateRenderer.render("guests/arch/network_#{network[:type]}",
- options: network)
+ entry = TemplateRenderer.render("guests/arch/network_#{network[:type]}", options: network)
temp = Tempfile.new("vagrant")
temp.binmode
@@ -32,10 +26,8 @@ module VagrantPlugins
temp.close
machine.communicate.upload(temp.path, "/tmp/vagrant_network")
- machine.communicate.sudo("mv /tmp/vagrant_network
- /etc/netctl/#{network[:device]}")
- machine.communicate.sudo("ip link set #{network[:device]} down &&
- netctl start #{network[:device]}")
+ machine.communicate.sudo("mv /tmp/vagrant_network /etc/netctl/#{network[:device]}")
+ machine.communicate.sudo("ip link set #{network[:device]} down && netctl start #{network[:device]}")
end
end
end
diff --git a/plugins/guests/debian/cap/configure_networks.rb b/plugins/guests/debian/cap/configure_networks.rb
index 49da980bf..3cb896b15 100644
--- a/plugins/guests/debian/cap/configure_networks.rb
+++ b/plugins/guests/debian/cap/configure_networks.rb
@@ -13,9 +13,8 @@ module VagrantPlugins
machine.communicate.tap do |comm|
# First, remove any previous network modifications
# from the interface file.
- comm.sudo("sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces")
- comm.sudo("su -c 'cat /tmp/vagrant-network-interfaces > /etc/network/interfaces'")
- comm.sudo("rm -f /tmp/vagrant-network-interfaces")
+ comm.sudo("sed -e '/^#VAGRANT-BEGIN/,$ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces.pre")
+ comm.sudo("sed -ne '/^#VAGRANT-END/,$ p' /etc/network/interfaces | tail -n +2 > /tmp/vagrant-network-interfaces.post")
# Accumulate the configurations to add to the interfaces file as
# well as what interfaces we're actually configuring since we use that
@@ -47,8 +46,8 @@ module VagrantPlugins
comm.sudo("/sbin/ip addr flush dev eth#{interface} 2> /dev/null")
end
- comm.sudo("cat /tmp/vagrant-network-entry >> /etc/network/interfaces")
- comm.sudo("rm -f /tmp/vagrant-network-entry")
+ comm.sudo('cat /tmp/vagrant-network-interfaces.pre /tmp/vagrant-network-entry /tmp/vagrant-network-interfaces.post > /etc/network/interfaces')
+ comm.sudo('rm -f /tmp/vagrant-network-interfaces.pre /tmp/vagrant-network-entry /tmp/vagrant-network-interfaces.post')
# Bring back up each network interface, reconfigured
interfaces.each do |interface|
diff --git a/plugins/guests/fedora/cap/configure_networks.rb b/plugins/guests/fedora/cap/configure_networks.rb
index 549a99dff..2f67099cd 100644
--- a/plugins/guests/fedora/cap/configure_networks.rb
+++ b/plugins/guests/fedora/cap/configure_networks.rb
@@ -26,7 +26,7 @@ module VagrantPlugins
end
interface_names = networks.map do |network|
- "eth#{network[:interface]}"
+ "#{interface_names[network[:interface]]}"
end
else
machine.communicate.sudo("/usr/sbin/biosdevname -d | grep Kernel | cut -f2 -d: | sed -e 's/ //;'") do |_, result|
diff --git a/plugins/guests/freebsd/cap/mount_nfs_folder.rb b/plugins/guests/freebsd/cap/mount_nfs_folder.rb
index a008ec3f4..58d8f785c 100644
--- a/plugins/guests/freebsd/cap/mount_nfs_folder.rb
+++ b/plugins/guests/freebsd/cap/mount_nfs_folder.rb
@@ -8,6 +8,8 @@ module VagrantPlugins
nfs_version_mount_option="-o nfsv#{opts[:nfs_version]}"
end
+ machine.communicate.sudo("mkdir -p #{opts[:guestpath]}", {shell: "sh"})
+
machine.communicate.sudo(
"mount -t nfs #{nfs_version_mount_option} " +
"'#{ip}:#{opts[:hostpath]}' '#{opts[:guestpath]}'", {shell: "sh"})
diff --git a/plugins/guests/solaris/cap/rsync.rb b/plugins/guests/solaris/cap/rsync.rb
index d060a8020..0d0220f40 100644
--- a/plugins/guests/solaris/cap/rsync.rb
+++ b/plugins/guests/solaris/cap/rsync.rb
@@ -17,10 +17,10 @@ module VagrantPlugins
end
def self.rsync_post(machine, opts)
- su_cmd = machine.config.solaris.su_cmd
+ suexec_cmd = machine.config.solaris.suexec_cmd
machine.communicate.execute(
- "#{su_cmd} find '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " +
- "xargs -0 -r chown #{opts[:owner]}:#{opts[:group]}")
+ "#{suexec_cmd} find '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " +
+ "xargs -0 chown #{opts[:owner]}:#{opts[:group]}")
end
end
end
diff --git a/plugins/guests/solaris/config.rb b/plugins/guests/solaris/config.rb
index e0a6a486b..5983b6837 100644
--- a/plugins/guests/solaris/config.rb
+++ b/plugins/guests/solaris/config.rb
@@ -3,24 +3,28 @@ module VagrantPlugins
class Config < Vagrant.plugin("2", :config)
attr_accessor :halt_timeout
attr_accessor :halt_check_interval
- # This sets the command to use to execute items as a superuser. sudo is default
+
attr_accessor :suexec_cmd
attr_accessor :device
def initialize
@halt_timeout = UNSET_VALUE
@halt_check_interval = UNSET_VALUE
- @suexec_cmd = 'sudo'
- @device = "e1000g"
+ @suexec_cmd = UNSET_VALUE
+ @device = UNSET_VALUE
end
def finalize!
if @halt_timeout != UNSET_VALUE
puts "solaris.halt_timeout is deprecated and will be removed in Vagrant 1.7"
end
+
if @halt_check_interval != UNSET_VALUE
puts "solaris.halt_check_interval is deprecated and will be removed in Vagrant 1.7"
end
+
+ @suexec_cmd = "sudo" if @suexec_cmd == UNSET_VALUE
+ @device = "e1000g" if @device == UNSET_VALUE
end
end
end
diff --git a/plugins/guests/solaris11/cap/rsync.rb b/plugins/guests/solaris11/cap/rsync.rb
index 039602172..95df046c9 100644
--- a/plugins/guests/solaris11/cap/rsync.rb
+++ b/plugins/guests/solaris11/cap/rsync.rb
@@ -17,10 +17,10 @@ module VagrantPlugins
end
def self.rsync_post(machine, opts)
- su_cmd = machine.config.solaris11.su_cmd
+ suexec_cmd = machine.config.solaris11.suexec_cmd
machine.communicate.execute(
- "#{su_cmd} '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " +
- "xargs -0 -r chown #{opts[:owner]}:#{opts[:group]}")
+ "#{suexec_cmd} '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " +
+ "xargs -0 chown #{opts[:owner]}:#{opts[:group]}")
end
end
end
diff --git a/plugins/hosts/bsd/cap/nfs.rb b/plugins/hosts/bsd/cap/nfs.rb
index b154ad19c..9fd1f7ef8 100644
--- a/plugins/hosts/bsd/cap/nfs.rb
+++ b/plugins/hosts/bsd/cap/nfs.rb
@@ -137,7 +137,7 @@ module VagrantPlugins
user = Process.uid
File.read("/etc/exports").lines.each do |line|
- if id = line[/^# VAGRANT-BEGIN:( #{user})? ([\.\/A-Za-z0-9-_]+?)$/, 2]
+ if id = line[/^# VAGRANT-BEGIN:( #{user})? ([\.\/A-Za-z0-9\-_]+?)$/, 2]
if valid_ids.include?(id)
logger.debug("Valid ID: #{id}")
else
diff --git a/plugins/kernel_v2/config/push.rb b/plugins/kernel_v2/config/push.rb
new file mode 100644
index 000000000..e1cb95734
--- /dev/null
+++ b/plugins/kernel_v2/config/push.rb
@@ -0,0 +1,127 @@
+require "vagrant"
+
+module VagrantPlugins
+ module Kernel_V2
+ class PushConfig < Vagrant.plugin("2", :config)
+ VALID_OPTIONS = [:strategy].freeze
+
+ attr_accessor :name
+
+ def initialize
+ @logger = Log4r::Logger.new("vagrant::config::push")
+
+ # Internal state
+ @__defined_pushes = {}
+ @__compiled_pushes = {}
+ @__finalized = false
+ end
+
+ def finalize!
+ @logger.debug("finalizing")
+
+ # Compile all the provider configurations
+ @__defined_pushes.each do |name, tuples|
+ # Capture the strategy so we can use it later. This will be used in
+ # the block iteration for merging/overwriting
+ strategy = name
+ strategy = tuples[0][0] if tuples[0]
+
+ # Find the configuration class for this push
+ config_class = Vagrant.plugin("2").manager.push_configs[strategy]
+ config_class ||= Vagrant::Config::V2::DummyConfig
+
+ # Load it up
+ config = config_class.new
+
+ begin
+ tuples.each do |s, b|
+ # Update the strategy if it has changed, reseting the current
+ # config object.
+ if s != strategy
+ @logger.warn("duplicate strategy defined, overwriting config")
+ strategy = s
+ config = config_class.new
+ end
+
+ # If we don't have any blocks, then ignore it
+ next if b.nil?
+
+ new_config = config_class.new
+ b.call(new_config, Vagrant::Config::V2::DummyConfig.new)
+ config = config.merge(new_config)
+ end
+ rescue Exception => e
+ raise Vagrant::Errors::VagrantfileLoadError,
+ path: "",
+ message: e.message
+ end
+
+ config.finalize!
+
+ # Store it for retrieval later
+ @__compiled_pushes[name] = [strategy, config]
+ end
+
+ @__finalized = true
+ end
+
+ # Define a new push in the Vagrantfile with the given name.
+ #
+ # @example
+ # vm.push.define "ftp"
+ #
+ # @example
+ # vm.push.define "ftp" do |s|
+ # s.host = "..."
+ # end
+ #
+ # @example
+ # vm.push.define "production", strategy: "docker" do |s|
+ # # ...
+ # end
+ #
+ # @param [#to_sym] name The name of the this strategy. By default, this
+ # is also the name of the strategy, but the `:strategy` key can be given
+ # to customize this behavior
+ # @param [Hash] options The list of options
+ #
+ def define(name, **options, &block)
+ name = name.to_sym
+ strategy = options[:strategy] || name
+
+ @__defined_pushes[name] ||= []
+ @__defined_pushes[name] << [strategy.to_sym, block]
+ end
+
+ # The String representation of this Push.
+ #
+ # @return [String]
+ def to_s
+ "Push"
+ end
+
+ # Custom merge method
+ def merge(other)
+ super.tap do |result|
+ other_pushes = other.instance_variable_get(:@__defined_pushes)
+ new_pushes = @__defined_pushes.dup
+
+ other_pushes.each do |key, tuples|
+ new_pushes[key] ||= []
+ new_pushes[key] += tuples
+ end
+
+ result.instance_variable_set(:@__defined_pushes, new_pushes)
+ end
+ end
+
+ # This returns the list of compiled pushes as a hash by name.
+ #
+ # @return [Hash>]
+ def __compiled_pushes
+ raise "Must finalize first!" if !@__finalized
+ @__compiled_pushes.dup
+ end
+ end
+ end
+end
diff --git a/plugins/kernel_v2/plugin.rb b/plugins/kernel_v2/plugin.rb
index 0904481df..27737854f 100644
--- a/plugins/kernel_v2/plugin.rb
+++ b/plugins/kernel_v2/plugin.rb
@@ -25,6 +25,11 @@ module VagrantPlugins
PackageConfig
end
+ config("push") do
+ require File.expand_path("../config/push", __FILE__)
+ PushConfig
+ end
+
config("vagrant") do
require File.expand_path("../config/vagrant", __FILE__)
VagrantConfig
diff --git a/plugins/providers/docker/action/prepare_ssh.rb b/plugins/providers/docker/action/prepare_ssh.rb
index 1a7298a66..450b139c6 100644
--- a/plugins/providers/docker/action/prepare_ssh.rb
+++ b/plugins/providers/docker/action/prepare_ssh.rb
@@ -19,14 +19,19 @@ module VagrantPlugins
# Modify the SSH options for when we `vagrant ssh`...
ssh_opts = env[:ssh_opts] || {}
- # Build the command we'll execute within the host machine
+ # Build the command we'll execute within the Docker host machine:
ssh_command = env[:machine].communicate.container_ssh_command
if !Array(ssh_opts[:extra_args]).empty?
ssh_command << " #{Array(ssh_opts[:extra_args]).join(" ")}"
end
+ # Modify the SSH options for the original command:
# Append "-t" to force a TTY allocation
ssh_opts[:extra_args] = ["-t"]
+ # Enable Agent forwarding when requested for the target VM
+ if env[:machine].ssh_info[:forward_agent]
+ ssh_opts[:extra_args] << "-o ForwardAgent=yes"
+ end
ssh_opts[:extra_args] << ssh_command
# Set the opts
diff --git a/plugins/providers/docker/communicator.rb b/plugins/providers/docker/communicator.rb
index 657789575..fb6c9fe9e 100644
--- a/plugins/providers/docker/communicator.rb
+++ b/plugins/providers/docker/communicator.rb
@@ -137,18 +137,21 @@ module VagrantPlugins
info[:port] ||= 22
# Make sure our private keys are synced over to the host VM
- key_args = sync_private_keys(info).map do |path|
+ ssh_args = sync_private_keys(info).map do |path|
"-i #{path}"
- end.join(" ")
+ end
+
+ # Use ad-hoc SSH options for the hop on the docker proxy
+ if info[:forward_agent]
+ ssh_args << "-o ForwardAgent=yes"
+ end
+ ssh_args.concat(["-o Compression=yes",
+ "-o ConnectTimeout=5",
+ "-o StrictHostKeyChecking=no",
+ "-o UserKnownHostsFile=/dev/null"])
# Build the SSH command
- "ssh #{key_args} " +
- "-o Compression=yes " +
- "-o ConnectTimeout=5 " +
- "-o StrictHostKeyChecking=no " +
- "-o UserKnownHostsFile=/dev/null " +
- "-p#{info[:port]} " +
- "#{info[:username]}@#{info[:host]}"
+ "ssh #{info[:username]}@#{info[:host]} -p#{info[:port]} #{ssh_args.join(" ")}"
end
protected
diff --git a/plugins/providers/hyperv/scripts/start_vm.ps1 b/plugins/providers/hyperv/scripts/start_vm.ps1
index 937ce75ed..54025c9a4 100644
--- a/plugins/providers/hyperv/scripts/start_vm.ps1
+++ b/plugins/providers/hyperv/scripts/start_vm.ps1
@@ -10,7 +10,7 @@ forEach ($module in $modules) { . $module }
try {
$vm = Get-VM -Id $VmId -ErrorAction "stop"
- Start-VM $vm
+ Start-VM $vm -ErrorAction "stop"
$state = $vm.state
$status = $vm.status
$name = $vm.name
@@ -24,4 +24,4 @@ try {
}
catch {
Write-Error-Message "Failed to start a VM $_"
-}
+}
\ No newline at end of file
diff --git a/plugins/providers/virtualbox/config.rb b/plugins/providers/virtualbox/config.rb
index aed1c692c..5bb1e754b 100644
--- a/plugins/providers/virtualbox/config.rb
+++ b/plugins/providers/virtualbox/config.rb
@@ -143,7 +143,7 @@ module VagrantPlugins
def validate(machine)
errors = _detected_errors
- valid_events = ["pre-import", "pre-boot", "post-boot"]
+ valid_events = ["pre-import", "pre-boot", "post-boot", "post-comm"]
@customizations.each do |event, _|
if !valid_events.include?(event)
errors << I18n.t(
diff --git a/plugins/providers/virtualbox/driver/version_4_1.rb b/plugins/providers/virtualbox/driver/version_4_1.rb
index b3fd44978..d8ffe3a0e 100644
--- a/plugins/providers/virtualbox/driver/version_4_1.rb
+++ b/plugins/providers/virtualbox/driver/version_4_1.rb
@@ -452,11 +452,11 @@ module VagrantPlugins
folder[:hostpath]]
args << "--transient" if folder.has_key?(:transient) && folder[:transient]
- # Add the shared folder
- execute("sharedfolder", "add", @uuid, *args)
-
# Enable symlinks on the shared folder
execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1")
+
+ # Add the shared folder
+ execute("sharedfolder", "add", @uuid, *args)
end
end
diff --git a/plugins/providers/virtualbox/driver/version_4_2.rb b/plugins/providers/virtualbox/driver/version_4_2.rb
index 765855e77..9299874a5 100644
--- a/plugins/providers/virtualbox/driver/version_4_2.rb
+++ b/plugins/providers/virtualbox/driver/version_4_2.rb
@@ -483,11 +483,11 @@ module VagrantPlugins
folder[:hostpath]]
args << "--transient" if folder.has_key?(:transient) && folder[:transient]
- # Add the shared folder
- execute("sharedfolder", "add", @uuid, *args)
-
# Enable symlinks on the shared folder
execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1")
+
+ # Add the shared folder
+ execute("sharedfolder", "add", @uuid, *args)
end
end
diff --git a/plugins/providers/virtualbox/driver/version_4_3.rb b/plugins/providers/virtualbox/driver/version_4_3.rb
index ad3cc4cd9..552a49b26 100644
--- a/plugins/providers/virtualbox/driver/version_4_3.rb
+++ b/plugins/providers/virtualbox/driver/version_4_3.rb
@@ -500,11 +500,11 @@ module VagrantPlugins
folder[:hostpath]]
args << "--transient" if folder.has_key?(:transient) && folder[:transient]
- # Add the shared folder
- execute("sharedfolder", "add", @uuid, *args)
-
# Enable symlinks on the shared folder
execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1")
+
+ # Add the shared folder
+ execute("sharedfolder", "add", @uuid, *args)
end
end
diff --git a/plugins/provisioners/ansible/provisioner.rb b/plugins/provisioners/ansible/provisioner.rb
index e43c81a96..eea544abe 100644
--- a/plugins/provisioners/ansible/provisioner.rb
+++ b/plugins/provisioners/ansible/provisioner.rb
@@ -12,31 +12,24 @@ module VagrantPlugins
@ssh_info = @machine.ssh_info
#
- # 1) Default Settings (lowest precedence)
+ # Ansible provisioner options
#
# Connect with Vagrant SSH identity
options = %W[--private-key=#{@ssh_info[:private_key_path][0]} --user=#{@ssh_info[:username]}]
- # Multiple SSH keys and/or SSH forwarding can be passed via
- # ANSIBLE_SSH_ARGS environment variable, which requires 'ssh' mode.
- # Note that multiple keys and ssh-forwarding settings are not supported
- # by deprecated 'paramiko' mode.
- options << "--connection=ssh" unless ansible_ssh_args.empty?
+ # Connect with native OpenSSH client
+ # Other modes (e.g. paramiko) are not officially supported,
+ # but can be enabled via raw_arguments option.
+ options << "--connection=ssh"
- # By default we limit by the current machine.
- # This can be overridden by the limit config option.
- options << "--limit=#{@machine.name}" unless config.limit
-
- #
- # 2) Configuration Joker
- #
-
- options.concat(self.as_array(config.raw_arguments)) if config.raw_arguments
-
- #
- # 3) Append Provisioner options (highest precedence):
- #
+ # By default we limit by the current machine, but
+ # this can be overridden by the `limit` option.
+ if config.limit
+ options << "--limit=#{as_list_argument(config.limit)}"
+ else
+ options << "--limit=#{@machine.name}"
+ end
options << "--inventory-file=#{self.setup_inventory_file}"
options << "--extra-vars=#{self.get_extra_vars_argument}" if config.extra_vars
@@ -48,22 +41,29 @@ module VagrantPlugins
options << "--vault-password-file=#{config.vault_password_file}" if config.vault_password_file
options << "--tags=#{as_list_argument(config.tags)}" if config.tags
options << "--skip-tags=#{as_list_argument(config.skip_tags)}" if config.skip_tags
- options << "--limit=#{as_list_argument(config.limit)}" if config.limit
options << "--start-at-task=#{config.start_at_task}" if config.start_at_task
+ # Finally, add the raw configuration options, which has the highest precedence
+ # and can therefore potentially override any other options of this provisioner.
+ options.concat(self.as_array(config.raw_arguments)) if config.raw_arguments
+
+ #
# Assemble the full ansible-playbook command
+ #
+
command = (%w(ansible-playbook) << options << config.playbook).flatten
- # Some Ansible options must be passed as environment variables
env = {
- "ANSIBLE_FORCE_COLOR" => "true",
- "ANSIBLE_HOST_KEY_CHECKING" => "#{config.host_key_checking}",
-
# Ensure Ansible output isn't buffered so that we receive output
# on a task-by-task basis.
- "PYTHONUNBUFFERED" => 1
+ "PYTHONUNBUFFERED" => 1,
+
+ # Some Ansible options must be passed as environment variables,
+ # as there is no equivalent command line arguments
+ "ANSIBLE_FORCE_COLOR" => "true",
+ "ANSIBLE_HOST_KEY_CHECKING" => "#{config.host_key_checking}",
}
- # Support Multiple SSH keys and SSH forwarding:
+ # ANSIBLE_SSH_ARGS is required for Multiple SSH keys, SSH forwarding and custom SSH settings
env["ANSIBLE_SSH_ARGS"] = ansible_ssh_args unless ansible_ssh_args.empty?
show_ansible_playbook_command(env, command) if (config.verbose || @logger.debug?)
@@ -183,9 +183,32 @@ module VagrantPlugins
@ansible_ssh_args ||= get_ansible_ssh_args
end
+ # Use ANSIBLE_SSH_ARGS to pass some OpenSSH options that are not wrapped by
+ # an ad-hoc Ansible option. Last update corresponds to Ansible 1.8
def get_ansible_ssh_args
ssh_options = []
+ # Use an SSH ProxyCommand when using the Docker provider with the intermediate host
+ if @machine.provider_name == :docker && machine.provider.host_vm?
+ docker_host_ssh_info = machine.provider.host_vm.ssh_info
+
+ proxy_cmd = "ssh #{docker_host_ssh_info[:username]}@#{docker_host_ssh_info[:host]}" +
+ " -p #{docker_host_ssh_info[:port]} -i #{docker_host_ssh_info[:private_key_path][0]}"
+
+ # Use same options than plugins/providers/docker/communicator.rb
+ # Note: this could be improved (DRY'ed) by sharing these settings.
+ proxy_cmd += " -o Compression=yes -o ConnectTimeout=5 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
+
+ proxy_cmd += " -o ForwardAgent=yes" if @ssh_info[:forward_agent]
+
+ proxy_cmd += " exec nc %h %p 2>/dev/null"
+
+ ssh_options << "-o ProxyCommand='#{ proxy_cmd }'"
+ end
+
+ # Don't access user's known_hosts file, except when host_key_checking is enabled.
+ ssh_options << "-o UserKnownHostsFile=/dev/null" unless config.host_key_checking
+
# Multiple Private Keys
@ssh_info[:private_key_path].drop(1).each do |key|
ssh_options << "-o IdentityFile=#{key}"
diff --git a/plugins/provisioners/chef/cap/debian/chef_install.rb b/plugins/provisioners/chef/cap/debian/chef_install.rb
index 5bb5f6acb..3d9302d80 100644
--- a/plugins/provisioners/chef/cap/debian/chef_install.rb
+++ b/plugins/provisioners/chef/cap/debian/chef_install.rb
@@ -5,11 +5,11 @@ module VagrantPlugins
module Cap
module Debian
module ChefInstall
- def self.chef_install(machine, version, prerelease)
+ def self.chef_install(machine, version, prerelease, download_path)
machine.communicate.sudo("apt-get update -y -qq")
machine.communicate.sudo("apt-get install -y -qq curl")
- command = Omnibus.build_command(version, prerelease)
+ command = Omnibus.build_command(version, prerelease, download_path)
machine.communicate.sudo(command)
end
end
diff --git a/plugins/provisioners/chef/cap/redhat/chef_install.rb b/plugins/provisioners/chef/cap/redhat/chef_install.rb
index 49db9bf9c..c6aff7bad 100644
--- a/plugins/provisioners/chef/cap/redhat/chef_install.rb
+++ b/plugins/provisioners/chef/cap/redhat/chef_install.rb
@@ -5,10 +5,10 @@ module VagrantPlugins
module Cap
module Redhat
module ChefInstall
- def self.chef_install(machine, version, prerelease)
+ def self.chef_install(machine, version, prerelease, download_path)
machine.communicate.sudo("yum install -y -q curl")
- command = Omnibus.build_command(version, prerelease)
+ command = Omnibus.build_command(version, prerelease, download_path)
machine.communicate.sudo(command)
end
end
diff --git a/plugins/provisioners/chef/config/base.rb b/plugins/provisioners/chef/config/base.rb
index 37124ad3f..4adb0d148 100644
--- a/plugins/provisioners/chef/config/base.rb
+++ b/plugins/provisioners/chef/config/base.rb
@@ -48,6 +48,14 @@ module VagrantPlugins
# @return [String]
attr_accessor :version
+ # The path where the Chef installer will be downloaded to. Only valid if
+ # install is true or "force". It defaults to nil, which means that the
+ # omnibus installer will choose the destination and you have no control
+ # over it.
+ #
+ # @return [String]
+ attr_accessor :installer_download_path
+
def initialize
super
@@ -57,6 +65,7 @@ module VagrantPlugins
@log_level = UNSET_VALUE
@prerelease = UNSET_VALUE
@version = UNSET_VALUE
+ @installer_download_path = UNSET_VALUE
end
def finalize!
@@ -66,6 +75,7 @@ module VagrantPlugins
@log_level = :info if @log_level == UNSET_VALUE
@prerelease = false if @prerelease == UNSET_VALUE
@version = :latest if @version == UNSET_VALUE
+ @installer_download_path = nil if @installer_download_path == UNSET_VALUE
# Make sure the install is a symbol if it's not a boolean
if @install.respond_to?(:to_sym)
diff --git a/plugins/provisioners/chef/installer.rb b/plugins/provisioners/chef/installer.rb
index e8b08f10d..16467111c 100644
--- a/plugins/provisioners/chef/installer.rb
+++ b/plugins/provisioners/chef/installer.rb
@@ -6,6 +6,7 @@ module VagrantPlugins
@version = options.fetch(:version, :latest)
@prerelease = options.fetch(:prerelease, :latest)
@force = options.fetch(:force, false)
+ @download_path = options.fetch(:download_path, nil)
end
# This handles verifying the Chef installation, installing it if it was
@@ -27,7 +28,7 @@ module VagrantPlugins
@machine.ui.detail(I18n.t("vagrant.chef_installing",
version: @version.to_s))
- @machine.guest.capability(:chef_install, @version, @prerelease)
+ @machine.guest.capability(:chef_install, @version, @prerelease, @download_path)
if !@machine.guest.capability(:chef_installed, @version)
raise Provisioner::Base::ChefError, :install_failed
diff --git a/plugins/provisioners/chef/omnibus.rb b/plugins/provisioners/chef/omnibus.rb
index db62b4fdc..5c2eef2fb 100644
--- a/plugins/provisioners/chef/omnibus.rb
+++ b/plugins/provisioners/chef/omnibus.rb
@@ -1,14 +1,14 @@
module VagrantPlugins
module Chef
module Omnibus
- OMNITRUCK = "https://www.getchef.com/chef/install.sh".freeze
+ OMNITRUCK = "https://www.chef.io/chef/install.sh".freeze
# Read more about the Omnibus installer here:
# https://docs.getchef.com/install_omnibus.html
- def build_command(version, prerelease = false)
+ def build_command(version, prerelease = false, download_path = nil)
command = "curl -sL #{OMNITRUCK} | sudo bash"
- if prerelease || version != :latest
+ if prerelease || version != :latest || download_path != nil
command << " -s --"
end
@@ -20,6 +20,10 @@ module VagrantPlugins
command << " -v \"#{version}\""
end
+ if download_path
+ command << " -d \"#{download_path}\""
+ end
+
command
end
module_function :build_command
diff --git a/plugins/provisioners/chef/plugin.rb b/plugins/provisioners/chef/plugin.rb
index b2fb5cef5..793eb397b 100644
--- a/plugins/provisioners/chef/plugin.rb
+++ b/plugins/provisioners/chef/plugin.rb
@@ -10,7 +10,7 @@ module VagrantPlugins
name "chef"
description <<-DESC
Provides support for provisioning your virtual machines with
- Chef via `chef-solo`, `chef-client`, or `chef-apply`.
+ Chef via `chef-solo`, `chef-client`, `chef-zero` or `chef-apply`.
DESC
config(:chef_apply, :provisioner) do
diff --git a/plugins/provisioners/chef/provisioner/base.rb b/plugins/provisioners/chef/provisioner/base.rb
index 81f8c6e48..f623bb498 100644
--- a/plugins/provisioners/chef/provisioner/base.rb
+++ b/plugins/provisioners/chef/provisioner/base.rb
@@ -29,6 +29,7 @@ module VagrantPlugins
force: config.install == :force,
version: config.version,
prerelease: config.prerelease,
+ download_path: config.installer_download_path
)
installer.ensure_installed
end
diff --git a/plugins/provisioners/chef/provisioner/chef_solo.rb b/plugins/provisioners/chef/provisioner/chef_solo.rb
index d05794f18..af9314bd9 100644
--- a/plugins/provisioners/chef/provisioner/chef_solo.rb
+++ b/plugins/provisioners/chef/provisioner/chef_solo.rb
@@ -34,7 +34,7 @@ module VagrantPlugins
share_folders(root_config, "cse", @environments_folders)
end
- def provision
+ def provision(mode = :solo)
install_chef
# Verify that the proper shared folders exist.
check = []
@@ -51,7 +51,7 @@ module VagrantPlugins
upload_encrypted_data_bag_secret
setup_json
setup_solo_config
- run_chef_solo
+ run_chef(mode)
delete_encrypted_data_bag_secret
end
@@ -130,7 +130,7 @@ module VagrantPlugins
}
end
- def run_chef_solo
+ def run_chef(mode)
if @config.run_list && @config.run_list.empty?
@machine.ui.warn(I18n.t("vagrant.chef_run_list_empty"))
end
@@ -143,9 +143,9 @@ module VagrantPlugins
@config.attempts.times do |attempt|
if attempt == 0
- @machine.ui.info I18n.t("vagrant.provisioners.chef.running_solo")
+ @machine.ui.info I18n.t("vagrant.provisioners.chef.running_#{mode}")
else
- @machine.ui.info I18n.t("vagrant.provisioners.chef.running_solo_again")
+ @machine.ui.info I18n.t("vagrant.provisioners.chef.running_#{mode}_again")
end
opts = { error_check: false, elevated: true }
diff --git a/plugins/provisioners/chef/provisioner/chef_zero.rb b/plugins/provisioners/chef/provisioner/chef_zero.rb
index 8ccae6130..f634bc9a7 100644
--- a/plugins/provisioners/chef/provisioner/chef_zero.rb
+++ b/plugins/provisioners/chef/provisioner/chef_zero.rb
@@ -22,6 +22,10 @@ module VagrantPlugins
share_folders(root_config, "csn", @node_folders)
end
+ def provision
+ super(:zero)
+ end
+
def solo_config
super.merge(
local_mode: true,
diff --git a/plugins/provisioners/salt/config.rb b/plugins/provisioners/salt/config.rb
index 0a99d3fc8..4d97d942c 100644
--- a/plugins/provisioners/salt/config.rb
+++ b/plugins/provisioners/salt/config.rb
@@ -12,6 +12,7 @@ module VagrantPlugins
attr_accessor :master_config
attr_accessor :master_key
attr_accessor :master_pub
+ attr_accessor :grains_config
attr_accessor :run_highstate
attr_accessor :run_overstate
attr_accessor :always_install
@@ -38,6 +39,7 @@ module VagrantPlugins
@master_config = UNSET_VALUE
@master_key = UNSET_VALUE
@master_pub = UNSET_VALUE
+ @grains_config = UNSET_VALUE
@run_highstate = UNSET_VALUE
@run_overstate = UNSET_VALUE
@always_install = UNSET_VALUE
@@ -63,6 +65,7 @@ module VagrantPlugins
@master_config = nil if @master_config == UNSET_VALUE
@master_key = nil if @master_key == UNSET_VALUE
@master_pub = nil if @master_pub == UNSET_VALUE
+ @grains_config = nil if @grains_config == UNSET_VALUE
@run_highstate = nil if @run_highstate == UNSET_VALUE
@run_overstate = nil if @run_overstate == UNSET_VALUE
@always_install = nil if @always_install == UNSET_VALUE
@@ -115,6 +118,13 @@ module VagrantPlugins
end
end
+ if @grains_config
+ expanded = Pathname.new(@grains_config).expand_path(machine.env.root_path)
+ if !expanded.file?
+ errors << I18n.t("vagrant.provisioners.salt.grains_config_nonexist")
+ end
+ end
+
if @install_master && !@no_minion && !@seed_master && @run_highstate
errors << I18n.t("vagrant.provisioners.salt.must_accept_keys")
end
diff --git a/plugins/provisioners/salt/provisioner.rb b/plugins/provisioners/salt/provisioner.rb
index c5db597a0..87af7cc7d 100644
--- a/plugins/provisioners/salt/provisioner.rb
+++ b/plugins/provisioners/salt/provisioner.rb
@@ -75,7 +75,7 @@ module VagrantPlugins
end
def need_configure
- @config.minion_config or @config.minion_key or @config.master_config or @config.master_key
+ @config.minion_config or @config.minion_key or @config.master_config or @config.master_key or @config.grains_config
end
def need_install
@@ -181,6 +181,11 @@ module VagrantPlugins
@machine.env.ui.info "Copying salt master config to vm."
@machine.communicate.upload(expanded_path(@config.master_config).to_s, temp_config_dir + "/master")
end
+
+ if @config.grains_config
+ @machine.env.ui.info "Copying salt grains config to vm."
+ @machine.communicate.upload(expanded_path(@config.grains_config).to_s, temp_config_dir + "/grains")
+ end
end
# Copy master and minion keys to VM
@@ -306,7 +311,7 @@ module VagrantPlugins
@machine.communicate.sudo("salt '*' saltutil.sync_all")
@machine.communicate.sudo("salt '*' state.highstate --verbose#{get_loglevel}#{get_colorize}#{get_pillar}") do |type, data|
if @config.verbose
- @machine.env.ui.info(data)
+ @machine.env.ui.info(data.rstrip)
end
end
else
@@ -315,14 +320,14 @@ module VagrantPlugins
@machine.communicate.execute("C:\\salt\\salt-call.exe saltutil.sync_all", opts)
@machine.communicate.execute("C:\\salt\\salt-call.exe state.highstate --retcode-passthrough #{get_loglevel}#{get_colorize}#{get_pillar}", opts) do |type, data|
if @config.verbose
- @machine.env.ui.info(data)
+ @machine.env.ui.info(data.rstrip)
end
end
else
@machine.communicate.sudo("salt-call saltutil.sync_all")
@machine.communicate.sudo("salt-call state.highstate --retcode-passthrough #{get_loglevel}#{get_colorize}#{get_pillar}") do |type, data|
if @config.verbose
- @machine.env.ui.info(data)
+ @machine.env.ui.info(data.rstrip)
end
end
end
diff --git a/plugins/pushes/atlas/config.rb b/plugins/pushes/atlas/config.rb
new file mode 100644
index 000000000..55543fae6
--- /dev/null
+++ b/plugins/pushes/atlas/config.rb
@@ -0,0 +1,147 @@
+module VagrantPlugins
+ module AtlasPush
+ class Config < Vagrant.plugin("2", :config)
+ # The address of the Atlas server to upload to. By default this will
+ # be the public Atlas server.
+ #
+ # @return [String]
+ attr_accessor :address
+
+ # The Atlas token to use. If the user has run `vagrant login`, this will
+ # use that token. If the environment variable `ATLAS_TOKEN` is set, the
+ # uploader will use this value. By default, this is nil.
+ #
+ # @return [String, nil]
+ attr_accessor :token
+
+ # The name of the application to push to. This will be created (with
+ # user confirmation) if it doesn't already exist.
+ #
+ # @return [String]
+ attr_accessor :app
+
+ # The base directory with file contents to upload. By default this
+ # is the same directory as the Vagrantfile, but you can specify this
+ # if you have a `src` folder or `bin` folder or some other folder
+ # you want to upload.
+ #
+ # @return [String]
+ attr_accessor :dir
+
+ # Lists of files to include/exclude in what is uploaded. Exclude is
+ # always the last run filter, so if a file is matched in both include
+ # and exclude, it will be excluded.
+ #
+ # The value of the array elements should be a simple file glob relative
+ # to the directory being packaged.
+ #
+ # @return [Array]
+ attr_accessor :includes
+ attr_accessor :excludes
+
+ # If set to true, Vagrant will automatically use VCS data to determine
+ # the files to upload. As a caveat: uncommitted changes will not be
+ # deployed.
+ #
+ # @return [Boolean]
+ attr_accessor :vcs
+
+ # The path to the uploader binary to shell out to. This usually
+ # is only set for debugging/development. If not set, the uploader
+ # will be looked for within the Vagrant installer dir followed by
+ # the PATH.
+ #
+ # @return [String]
+ attr_accessor :uploader_path
+
+ def initialize
+ @address = UNSET_VALUE
+ @token = UNSET_VALUE
+ @app = UNSET_VALUE
+ @dir = UNSET_VALUE
+ @vcs = UNSET_VALUE
+ @includes = []
+ @excludes = []
+ @uploader_path = UNSET_VALUE
+ end
+
+ def merge(other)
+ super.tap do |result|
+ result.includes = self.includes.dup.concat(other.includes).uniq
+ result.excludes = self.excludes.dup.concat(other.excludes).uniq
+ end
+ end
+
+ def finalize!
+ @address = nil if @address == UNSET_VALUE
+ @token = nil if @token == UNSET_VALUE
+ @app = nil if @app == UNSET_VALUE
+ @dir = "." if @dir == UNSET_VALUE
+ @uploader_path = nil if @uploader_path == UNSET_VALUE
+ @vcs = true if @vcs == UNSET_VALUE
+ end
+
+ def validate(machine)
+ errors = _detected_errors
+
+ if missing?(@token)
+ token = token_from_vagrant_login(machine.env)
+ if missing?(token)
+ errors << I18n.t("atlas_push.errors.missing_token")
+ else
+ @token = token
+ end
+ end
+
+ if missing?(@app)
+ errors << I18n.t("atlas_push.errors.missing_attribute",
+ attribute: "app",
+ )
+ end
+
+ if missing?(@dir)
+ errors << I18n.t("atlas_push.errors.missing_attribute",
+ attribute: "dir",
+ )
+ end
+
+ { "Atlas push" => errors }
+ end
+
+ # Add the filepath to the list of includes
+ # @param [String] filepath
+ def include(filepath)
+ @includes << filepath
+ end
+ alias_method :include=, :include
+
+ # Add the filepath to the list of excludes
+ # @param [String] filepath
+ def exclude(filepath)
+ @excludes << filepath
+ end
+ alias_method :exclude=, :exclude
+
+ private
+
+ # Determine if the given string is "missing" (blank)
+ # @return [true, false]
+ def missing?(obj)
+ obj.to_s.strip.empty?
+ end
+
+ # Attempt to load the token from disk using the vagrant-login plugin. If
+ # the constant is not defined, that means the user is operating in some
+ # bespoke and unsupported Ruby environment.
+ #
+ # @param [Vagrant::Environment] env
+ #
+ # @return [String, nil]
+ # the token, or nil if it does not exist
+ def token_from_vagrant_login(env)
+ client = VagrantPlugins::LoginCommand::Client.new(env)
+ client.token
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/atlas/errors.rb b/plugins/pushes/atlas/errors.rb
new file mode 100644
index 000000000..7ba1d712a
--- /dev/null
+++ b/plugins/pushes/atlas/errors.rb
@@ -0,0 +1,13 @@
+module VagrantPlugins
+ module AtlasPush
+ module Errors
+ class Error < Vagrant::Errors::VagrantError
+ error_namespace("atlas_push.errors")
+ end
+
+ class UploaderNotFound < Error
+ error_key(:uploader_not_found)
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/atlas/locales/en.yml b/plugins/pushes/atlas/locales/en.yml
new file mode 100644
index 000000000..3377f62dd
--- /dev/null
+++ b/plugins/pushes/atlas/locales/en.yml
@@ -0,0 +1,22 @@
+en:
+ atlas_push:
+ errors:
+ missing_attribute: |-
+ Missing required attribute '%{attribute}'. The Vagrant Atlas Push plugin
+ requires you set this attribute. Please set this attribute in your
+ Vagrantfile, for example:
+
+ config.push.define "atlas" do |push|
+ push.%{attribute} = "..."
+ end
+ missing_token: |-
+ Missing required configuration parameter 'token'. This is required for
+ Vagrant to securely communicate with your Atlas account.
+
+ To generate an access token, run 'vagrant login'.
+ uploader_not_found: |-
+ Vagrant was unable to find the Atlas uploader CLI. If your Vagrantfile
+ specifies the path explicitly with "uploader_path", then make sure that
+ path is valid. Otherwise, make sure that you have a valid install of
+ Vagrant. If you installed Vagrant outside of the official installers,
+ the "atlas-upload" binary must exist on your PATH.
diff --git a/plugins/pushes/atlas/plugin.rb b/plugins/pushes/atlas/plugin.rb
new file mode 100644
index 000000000..2eee2517d
--- /dev/null
+++ b/plugins/pushes/atlas/plugin.rb
@@ -0,0 +1,35 @@
+require "vagrant"
+
+module VagrantPlugins
+ module AtlasPush
+ autoload :Errors, File.expand_path("../errors", __FILE__)
+
+ class Plugin < Vagrant.plugin("2")
+ name "atlas"
+ description <<-DESC
+ Deploy using HashiCorp's Atlas service.
+ DESC
+
+ config(:atlas, :push) do
+ require_relative "config"
+ init!
+ Config
+ end
+
+ push(:atlas) do
+ require_relative "push"
+ init!
+ Push
+ end
+
+ protected
+
+ def self.init!
+ return if defined?(@_init)
+ I18n.load_path << File.expand_path("../locales/en.yml", __FILE__)
+ I18n.reload!
+ @_init = true
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/atlas/push.rb b/plugins/pushes/atlas/push.rb
new file mode 100644
index 000000000..24a6d28b0
--- /dev/null
+++ b/plugins/pushes/atlas/push.rb
@@ -0,0 +1,57 @@
+require "vagrant/util/safe_exec"
+require "vagrant/util/subprocess"
+require "vagrant/util/which"
+
+module VagrantPlugins
+ module AtlasPush
+ class Push < Vagrant.plugin("2", :push)
+ UPLOADER_BIN = "atlas-upload".freeze
+
+ def push
+ uploader = self.uploader_path
+
+ # If we didn't find the uploader binary it is a critical error
+ raise Errors::UploaderNotFound if !uploader
+
+ # We found it. Build up the command and the args.
+ execute(uploader)
+ return 0
+ end
+
+ # Executes the uploader with the proper flags based on the configuration.
+ # This function shouldn't return since it will exec, but might return
+ # if we're on a system that doesn't support exec, so handle that properly.
+ def execute(uploader)
+ cmd = []
+ cmd << "-vcs" if config.vcs
+ cmd += config.includes.map { |v| ["-include", v] }
+ cmd += config.excludes.map { |v| ["-exclude", v] }
+ cmd += ["-address", config.address] if config.address
+ cmd += ["-token", config.token] if config.token
+ cmd << config.app
+ cmd << File.expand_path(config.dir, env.root_path)
+ Vagrant::Util::SafeExec.exec(uploader, *cmd.flatten)
+ end
+
+ # This returns the path to the uploader binary, or nil if it can't
+ # be found.
+ #
+ # @return [String]
+ def uploader_path
+ # Determine the uploader path
+ uploader = config.uploader_path
+ if uploader
+ return uploader
+ end
+
+ if Vagrant.in_installer?
+ path = File.join(
+ Vagrant.installer_embedded_dir, "bin", UPLOADER_BIN)
+ return path if File.file?(path)
+ end
+
+ return Vagrant::Util::Which.which(UPLOADER_BIN)
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/ftp/adapter.rb b/plugins/pushes/ftp/adapter.rb
new file mode 100644
index 000000000..8a9414376
--- /dev/null
+++ b/plugins/pushes/ftp/adapter.rb
@@ -0,0 +1,118 @@
+require "pathname"
+
+module VagrantPlugins
+ module FTPPush
+ class Adapter
+ attr_reader :host
+ attr_reader :port
+ attr_reader :username
+ attr_reader :password
+ attr_reader :options
+ attr_reader :server
+
+ def initialize(host, username, password, options = {})
+ @host, @port = parse_host(host)
+ @username = username
+ @password = password
+ @options = options
+ @server = nil
+ end
+
+ # Parse the host into it's url and port parts.
+ # @return [Array]
+ def parse_host(host)
+ if host.include?(":")
+ split = host.split(":", 2)
+ [split[0], split[1].to_i]
+ else
+ [host, default_port]
+ end
+ end
+
+ def default_port
+ raise NotImplementedError
+ end
+
+ def connect(&block)
+ raise NotImplementedError
+ end
+
+ def upload(local, remote)
+ raise NotImplementedError
+ end
+ end
+
+ #
+ # The FTP Adapter
+ #
+ class FTPAdapter < Adapter
+ def initialize(*)
+ require "net/ftp"
+ super
+ end
+
+ def default_port
+ 21
+ end
+
+ def connect(&block)
+ @server = Net::FTP.new
+ @server.passive = options.fetch(:passive, true)
+ @server.connect(host, port)
+ @server.login(username, password)
+
+ begin
+ yield self
+ ensure
+ @server.close
+ end
+ end
+
+ def upload(local, remote)
+ parent = File.dirname(remote)
+ fullpath = Pathname.new(File.expand_path(parent, pwd))
+
+ # Create the parent directories if they does not exist (naive mkdir -p)
+ fullpath.descend do |path|
+ if @server.list(path.to_s).empty?
+ @server.mkdir(path.to_s)
+ end
+ end
+
+ # Upload the file
+ @server.putbinaryfile(local, remote)
+ end
+
+ private
+
+ def pwd
+ @pwd ||= @server.pwd
+ end
+ end
+
+ #
+ # The SFTP Adapter
+ #
+ class SFTPAdapter < Adapter
+ def initialize(*)
+ require "net/sftp"
+ super
+ end
+
+ def default_port
+ 22
+ end
+
+ def connect(&block)
+ Net::SFTP.start(@host, @username, password: @password, port: @port) do |server|
+ @server = server
+ yield self
+ end
+ end
+
+ def upload(local, remote)
+ @server.upload!(local, remote, mkdir: true)
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/ftp/config.rb b/plugins/pushes/ftp/config.rb
new file mode 100644
index 000000000..e493259db
--- /dev/null
+++ b/plugins/pushes/ftp/config.rb
@@ -0,0 +1,130 @@
+module VagrantPlugins
+ module FTPPush
+ class Config < Vagrant.plugin("2", :config)
+ # The (S)FTP host to use.
+ # @return [String]
+ attr_accessor :host
+
+ # The username to use for authentication with the (S)FTP server.
+ # @return [String]
+ attr_accessor :username
+
+ # The password to use for authentication with the (S)FTP server.
+ # @return [String]
+ attr_accessor :password
+
+ # Use passive FTP (default is true).
+ # @return [true, false]
+ attr_accessor :passive
+
+ # Use secure (SFTP) (default is false).
+ # @return [true, false]
+ attr_accessor :secure
+
+ # The root destination on the target system to sync the files (default is
+ # /).
+ # @return [String]
+ attr_accessor :destination
+
+ # Lists of files to include/exclude in what is uploaded. Exclude is
+ # always the last run filter, so if a file is matched in both include
+ # and exclude, it will be excluded.
+ #
+ # The value of the array elements should be a simple file glob relative
+ # to the directory being packaged.
+ # @return [Array]
+ attr_accessor :includes
+ attr_accessor :excludes
+
+ # The base directory with file contents to upload. By default this
+ # is the same directory as the Vagrantfile, but you can specify this
+ # if you have a `src` folder or `bin` folder or some other folder
+ # you want to upload.
+ # @return [String]
+ attr_accessor :dir
+
+ def initialize
+ @host = UNSET_VALUE
+ @username = UNSET_VALUE
+ @password = UNSET_VALUE
+ @passive = UNSET_VALUE
+ @secure = UNSET_VALUE
+ @destination = UNSET_VALUE
+
+ @includes = []
+ @excludes = []
+
+ @dir = UNSET_VALUE
+ end
+
+ def merge(other)
+ super.tap do |result|
+ result.includes = self.includes.dup.concat(other.includes).uniq
+ result.excludes = self.excludes.dup.concat(other.excludes).uniq
+ end
+ end
+
+ def finalize!
+ @host = nil if @host == UNSET_VALUE
+ @username = nil if @username == UNSET_VALUE
+ @password = nil if @password == UNSET_VALUE
+ @passive = true if @passive == UNSET_VALUE
+ @secure = false if @secure == UNSET_VALUE
+ @destination = "/" if @destination == UNSET_VALUE
+ @dir = "." if @dir == UNSET_VALUE
+ end
+
+ def validate(machine)
+ errors = _detected_errors
+
+ if missing?(@host)
+ errors << I18n.t("ftp_push.errors.missing_attribute",
+ attribute: "host",
+ )
+ end
+
+ if missing?(@username)
+ errors << I18n.t("ftp_push.errors.missing_attribute",
+ attribute: "username",
+ )
+ end
+
+ if missing?(@destination)
+ errors << I18n.t("ftp_push.errors.missing_attribute",
+ attribute: "destination",
+ )
+ end
+
+ if missing?(@dir)
+ errors << I18n.t("ftp_push.errors.missing_attribute",
+ attribute: "dir",
+ )
+ end
+
+ { "FTP push" => errors }
+ end
+
+ # Add the filepath to the list of includes
+ # @param [String] filepath
+ def include(filepath)
+ @includes << filepath
+ end
+ alias_method :include=, :include
+
+ # Add the filepath to the list of excludes
+ # @param [String] filepath
+ def exclude(filepath)
+ @excludes << filepath
+ end
+ alias_method :exclude=, :exclude
+
+ private
+
+ # Determine if the given string is "missing" (blank)
+ # @return [true, false]
+ def missing?(obj)
+ obj.to_s.strip.empty?
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/ftp/locales/en.yml b/plugins/pushes/ftp/locales/en.yml
new file mode 100644
index 000000000..0bbbe51f1
--- /dev/null
+++ b/plugins/pushes/ftp/locales/en.yml
@@ -0,0 +1,11 @@
+en:
+ ftp_push:
+ errors:
+ missing_attribute: |-
+ Missing required attribute '%{attribute}'. The Vagrant FTP Push plugin
+ requires you set this attribute. Please set this attribute in your
+ Vagrantfile, for example:
+
+ config.push.define "ftp" do |push|
+ push.%{attribute} = "..."
+ end
diff --git a/plugins/pushes/ftp/plugin.rb b/plugins/pushes/ftp/plugin.rb
new file mode 100644
index 000000000..ef333807b
--- /dev/null
+++ b/plugins/pushes/ftp/plugin.rb
@@ -0,0 +1,33 @@
+require "vagrant"
+
+module VagrantPlugins
+ module FTPPush
+ class Plugin < Vagrant.plugin("2")
+ name "ftp"
+ description <<-DESC
+ Deploy to a remote FTP or SFTP server.
+ DESC
+
+ config(:ftp, :push) do
+ require File.expand_path("../config", __FILE__)
+ init!
+ Config
+ end
+
+ push(:ftp) do
+ require File.expand_path("../push", __FILE__)
+ init!
+ Push
+ end
+
+ protected
+
+ def self.init!
+ return if defined?(@_init)
+ I18n.load_path << File.expand_path("../locales/en.yml", __FILE__)
+ I18n.reload!
+ @_init = true
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/ftp/push.rb b/plugins/pushes/ftp/push.rb
new file mode 100644
index 000000000..7a69c4f02
--- /dev/null
+++ b/plugins/pushes/ftp/push.rb
@@ -0,0 +1,120 @@
+require "net/ftp"
+require "pathname"
+
+require_relative "adapter"
+
+module VagrantPlugins
+ module FTPPush
+ class Push < Vagrant.plugin("2", :push)
+ IGNORED_FILES = %w(. ..).freeze
+ DEFAULT_EXCLUDES = %w(.git .hg .svn .vagrant).freeze
+
+ def initialize(*)
+ super
+ @logger = Log4r::Logger.new("vagrant::pushes::ftp")
+ end
+
+ def push
+ # Grab files early so if there's an exception or issue, we don't have to
+ # wait and close the (S)FTP connection as well
+ files = Hash[*all_files.flat_map do |file|
+ relative_path = relative_path_for(file, config.dir)
+ destination = File.expand_path(File.join(config.destination, relative_path))
+ file = File.expand_path(file, env.root_path)
+ [file, destination]
+ end]
+
+ ftp = "#{config.username}@#{config.host}:#{config.destination}"
+ env.ui.info "Uploading #{env.root_path} to #{ftp}"
+
+ connect do |ftp|
+ files.each do |local, remote|
+ @logger.info "Uploading #{local} => #{remote}"
+ ftp.upload(local, remote)
+ end
+ end
+ end
+
+ # Helper method for creating the FTP or SFTP connection.
+ # @yield [Adapter]
+ def connect(&block)
+ klass = config.secure ? SFTPAdapter : FTPAdapter
+ ftp = klass.new(config.host, config.username, config.password,
+ passive: config.passive)
+ ftp.connect(&block)
+ end
+
+ # The list of all files that should be pushed by this push. This method
+ # only returns **files**, not folders or symlinks!
+ # @return [Array]
+ def all_files
+ files = glob("#{config.dir}/**/*") + includes_files
+ filter_excludes!(files, config.excludes)
+ files.reject! { |f| !File.file?(f) }
+ files
+ end
+
+ # The list of files to include in addition to those specified in `dir`.
+ # @return [Array]
+ def includes_files
+ includes = config.includes.flat_map do |i|
+ path = absolute_path_for(i, config.dir)
+ [path, "#{path}/**/*"]
+ end
+
+ glob("{#{includes.join(",")}}")
+ end
+
+ # Filter the excludes out of the given list. This method modifies the
+ # given list in memory!
+ #
+ # @param [Array] list
+ # the filepaths
+ # @param [Array] excludes
+ # the exclude patterns or files
+ def filter_excludes!(list, excludes)
+ excludes = Array(excludes)
+ excludes = excludes + DEFAULT_EXCLUDES
+ excludes = excludes.flat_map { |e| [e, "#{e}/*"] }
+
+ list.reject! do |file|
+ basename = relative_path_for(file, config.dir)
+
+ # Handle the special case where the file is outside of the working
+ # directory...
+ if basename.start_with?("../")
+ basename = file
+ end
+
+ excludes.any? { |e| File.fnmatch?(e, basename, File::FNM_DOTMATCH) }
+ end
+ end
+
+ # Get the list of files that match the given pattern.
+ # @return [Array]
+ def glob(pattern)
+ Dir.glob(pattern, File::FNM_DOTMATCH).sort.reject do |file|
+ IGNORED_FILES.include?(File.basename(file))
+ end
+ end
+
+ # The absolute path to the given `path` and `parent`, unless the given
+ # path is absolute.
+ # @return [String]
+ def absolute_path_for(path, parent)
+ path = Pathname.new(path)
+ return path if path.absolute?
+ File.expand_path(path, parent)
+ end
+
+ # The relative path from the given `parent`. If files exist on another
+ # device, this will probably blow up.
+ # @return [String]
+ def relative_path_for(path, parent)
+ Pathname.new(path).relative_path_from(Pathname.new(parent)).to_s
+ rescue ArgumentError
+ return path
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/heroku/config.rb b/plugins/pushes/heroku/config.rb
new file mode 100644
index 000000000..927c1a7b9
--- /dev/null
+++ b/plugins/pushes/heroku/config.rb
@@ -0,0 +1,74 @@
+module VagrantPlugins
+ module HerokuPush
+ class Config < Vagrant.plugin("2", :config)
+ # The name of the Heroku application to push to.
+ # @return [String]
+ attr_accessor :app
+
+ # The base directory with file contents to upload. By default this
+ # is the same directory as the Vagrantfile, but you can specify this
+ # if you have a `src` folder or `bin` folder or some other folder
+ # you want to upload. This directory must be a git repository.
+ # @return [String]
+ attr_accessor :dir
+
+ # The path to the git binary to shell out to. This usually is only set for
+ # debugging/development. If not set, the git bin will be searched for
+ # in the PATH.
+ # @return [String]
+ attr_accessor :git_bin
+
+ # The Git remote to push to (default: "heroku").
+ # @return [String]
+ attr_accessor :remote
+
+ def initialize
+ @app = UNSET_VALUE
+ @dir = UNSET_VALUE
+
+ @git_bin = UNSET_VALUE
+ @remote = UNSET_VALUE
+ end
+
+ def finalize!
+ @app = nil if @app == UNSET_VALUE
+ @dir = "." if @dir == UNSET_VALUE
+
+ @git_bin = "git" if @git_bin == UNSET_VALUE
+ @remote = "heroku" if @remote == UNSET_VALUE
+ end
+
+ def validate(machine)
+ errors = _detected_errors
+
+ if missing?(@dir)
+ errors << I18n.t("heroku_push.errors.missing_attribute",
+ attribute: "dir",
+ )
+ end
+
+ if missing?(@git_bin)
+ errors << I18n.t("heroku_push.errors.missing_attribute",
+ attribute: "git_bin",
+ )
+ end
+
+ if missing?(@remote)
+ errors << I18n.t("heroku_push.errors.missing_attribute",
+ attribute: "remote",
+ )
+ end
+
+ { "Heroku push" => errors }
+ end
+
+ private
+
+ # Determine if the given string is "missing" (blank)
+ # @return [true, false]
+ def missing?(obj)
+ obj.to_s.strip.empty?
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/heroku/errors.rb b/plugins/pushes/heroku/errors.rb
new file mode 100644
index 000000000..c92a7b76c
--- /dev/null
+++ b/plugins/pushes/heroku/errors.rb
@@ -0,0 +1,21 @@
+module VagrantPlugins
+ module HerokuPush
+ module Errors
+ class Error < Vagrant::Errors::VagrantError
+ error_namespace("heroku_push.errors")
+ end
+
+ class CommandFailed < Error
+ error_key(:command_failed)
+ end
+
+ class GitNotFound < Error
+ error_key(:git_not_found)
+ end
+
+ class NotAGitRepo < Error
+ error_key(:not_a_git_repo)
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/heroku/locales/en.yml b/plugins/pushes/heroku/locales/en.yml
new file mode 100644
index 000000000..e6f6bf441
--- /dev/null
+++ b/plugins/pushes/heroku/locales/en.yml
@@ -0,0 +1,30 @@
+en:
+ heroku_push:
+ errors:
+ command_failed: |-
+ The following command exited with a non-zero exit status:
+
+ %{cmd}
+
+ stdout: %{stdout}
+ stderr: %{stderr}
+ git_not_found: |-
+ The Git binary '%{bin}' could not be found. Please ensure you
+ have downloaded and installed the latest version of Git:
+
+ http://git-scm.com/downloads
+ missing_attribute: |-
+ Missing required attribute '%{attribute}'. The Vagrant Heroku Push
+ plugin requires you set this attribute. Please set this attribute in
+ your Vagrantfile, for example:
+
+ config.push.define "heroku" do |push|
+ push.%{attribute} = "..."
+ end
+ not_a_git_repo: |-
+ The following path is not a valid Git repository:
+
+ %{path}
+
+ Please ensure you are working in the correct directory. In order to use
+ the Vagrant Heroku Push plugin, you must have a git repository.
diff --git a/plugins/pushes/heroku/plugin.rb b/plugins/pushes/heroku/plugin.rb
new file mode 100644
index 000000000..40865ee5d
--- /dev/null
+++ b/plugins/pushes/heroku/plugin.rb
@@ -0,0 +1,33 @@
+require "vagrant"
+
+module VagrantPlugins
+ module HerokuPush
+ class Plugin < Vagrant.plugin("2")
+ name "heroku"
+ description <<-DESC
+ Deploy to a Heroku
+ DESC
+
+ config(:heroku, :push) do
+ require File.expand_path("../config", __FILE__)
+ init!
+ Config
+ end
+
+ push(:heroku) do
+ require File.expand_path("../push", __FILE__)
+ init!
+ Push
+ end
+
+ protected
+
+ def self.init!
+ return if defined?(@_init)
+ I18n.load_path << File.expand_path("../locales/en.yml", __FILE__)
+ I18n.reload!
+ @_init = true
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/heroku/push.rb b/plugins/pushes/heroku/push.rb
new file mode 100644
index 000000000..b894eba81
--- /dev/null
+++ b/plugins/pushes/heroku/push.rb
@@ -0,0 +1,136 @@
+require "vagrant/util/subprocess"
+require "vagrant/util/which"
+
+require_relative "errors"
+
+module VagrantPlugins
+ module HerokuPush
+ class Push < Vagrant.plugin("2", :push)
+ def push
+ # Expand any paths relative to the root
+ dir = File.expand_path(config.dir, env.root_path)
+
+ # Verify git is installed
+ verify_git_bin!(config.git_bin)
+
+ # Verify we are operating in a git repo
+ verify_git_repo!(dir)
+
+ # Get the current branch
+ branch = git_branch(dir)
+
+ # Get the name of the app
+ app = config.app || interpret_app(dir)
+
+ # Check if we need to add the git remote
+ if !has_git_remote?(config.remote, dir)
+ add_heroku_git_remote(config.remote, app, dir)
+ end
+
+ # Push to Heroku
+ git_push_heroku(config.remote, branch, dir)
+ end
+
+ # Verify that git is installed.
+ # @raise [Errors::GitNotFound]
+ def verify_git_bin!(path)
+ if Vagrant::Util::Which.which(path).nil?
+ raise Errors::GitNotFound, bin: path
+ end
+ end
+
+ # Verify that the given path is a git directory.
+ # @raise [Errors::NotAGitRepo]
+ # @param [String]
+ def verify_git_repo!(path)
+ if !File.directory?(git_dir(path))
+ raise Errors::NotAGitRepo, path: path
+ end
+ end
+
+ # Interpret the name of the Heroku application from the given path.
+ # @param [String] path
+ # @return [String]
+ def interpret_app(path)
+ File.basename(path)
+ end
+
+ # The git directory for the given path.
+ # @param [String] path
+ # @return [String]
+ def git_dir(path)
+ "#{path}/.git"
+ end
+
+ # The name of the current git branch.
+ # @param [String] path
+ # @return [String]
+ def git_branch(path)
+ result = execute!("git",
+ "--git-dir", git_dir(path),
+ "--work-tree", path,
+ "branch",
+ )
+
+ # Returns something like "* master"
+ result.stdout.sub("*", "").strip
+ end
+
+ # Push to the Heroku remote.
+ # @param [String] remote
+ # @param [String] branch
+ def git_push_heroku(remote, branch, path)
+ execute!("git",
+ "--git-dir", git_dir(path),
+ "--work-tree", path,
+ "push", remote, "#{branch}:master",
+ )
+ end
+
+ # Check if the git remote has the given remote.
+ # @param [String] remote
+ # @return [true, false]
+ def has_git_remote?(remote, path)
+ result = execute!("git",
+ "--git-dir", git_dir(path),
+ "--work-tree", path,
+ "remote",
+ )
+ remotes = result.stdout.split(/\r?\n/).map(&:strip)
+ remotes.include?(remote.to_s)
+ end
+
+ # Add the Heroku to the current repository.
+ # @param [String] remote
+ # @param [String] app
+ def add_heroku_git_remote(remote, app, path)
+ execute!("git",
+ "--git-dir", git_dir(path),
+ "--work-tree", path,
+ "remote", "add", remote, heroku_git_url(app),
+ )
+ end
+
+ # The URL for this project on Heroku.
+ # @return [String]
+ def heroku_git_url(app)
+ "git@heroku.com:#{app}.git"
+ end
+
+ # Execute the command, raising an exception if it fails.
+ # @return [Vagrant::Util::Subprocess::Result]
+ def execute!(*cmd)
+ result = Vagrant::Util::Subprocess.execute(*cmd)
+
+ if result.exit_code != 0
+ raise Errors::CommandFailed,
+ cmd: cmd.join(" "),
+ stdout: result.stdout,
+ stderr: result.stderr
+ end
+
+ result
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/local-exec/config.rb b/plugins/pushes/local-exec/config.rb
new file mode 100644
index 000000000..747ff8925
--- /dev/null
+++ b/plugins/pushes/local-exec/config.rb
@@ -0,0 +1,48 @@
+module VagrantPlugins
+ module LocalExecPush
+ class Config < Vagrant.plugin("2", :config)
+ # The path (relative to the machine root) to a local script that will be
+ # executed.
+ # @return [String]
+ attr_accessor :script
+
+ # The command (as a string) to execute.
+ # @return [String]
+ attr_accessor :inline
+
+ def initialize
+ @script = UNSET_VALUE
+ @inline = UNSET_VALUE
+ end
+
+ def finalize!
+ @script = nil if @script == UNSET_VALUE
+ @inline = nil if @inline == UNSET_VALUE
+ end
+
+ def validate(machine)
+ errors = _detected_errors
+
+ if missing?(@script) && missing?(@inline)
+ errors << I18n.t("local_exec_push.errors.missing_attribute",
+ attribute: "script",
+ )
+ end
+
+ if !missing?(@script) && !missing?(@inline)
+ errors << I18n.t("local_exec_push.errors.cannot_specify_script_and_inline")
+ end
+
+ { "Local Exec push" => errors }
+ end
+
+ private
+
+ # Determine if the given string is "missing" (blank)
+ # @return [true, false]
+ def missing?(obj)
+ obj.to_s.strip.empty?
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/local-exec/errors.rb b/plugins/pushes/local-exec/errors.rb
new file mode 100644
index 000000000..5e5b71cca
--- /dev/null
+++ b/plugins/pushes/local-exec/errors.rb
@@ -0,0 +1,13 @@
+module VagrantPlugins
+ module LocalExecPush
+ module Errors
+ class Error < Vagrant::Errors::VagrantError
+ error_namespace("local_exec_push.errors")
+ end
+
+ class CommandFailed < Error
+ error_key(:command_failed)
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/local-exec/locales/en.yml b/plugins/pushes/local-exec/locales/en.yml
new file mode 100644
index 000000000..08808a879
--- /dev/null
+++ b/plugins/pushes/local-exec/locales/en.yml
@@ -0,0 +1,22 @@
+en:
+ local_exec_push:
+ errors:
+ cannot_specify_script_and_inline: |-
+ You have specified both the 'script' and 'inline' attributes for the
+ Vagrant Local Exec Push plugin. You may only specify one of these
+ attributes.
+ command_failed: |-
+ The following command exited with a non-zero exit status:
+
+ %{cmd}
+
+ stdout: %{stdout}
+ stderr: %{stderr}
+ missing_attribute: |-
+ Missing required attribute '%{attribute}'. The Vagrant Local Exec Push
+ plugin requires you set this attribute. Please set this attribute in
+ your Vagrantfile, for example:
+
+ config.push.define "local-exec" do |push|
+ push.%{attribute} = "..."
+ end
diff --git a/plugins/pushes/local-exec/plugin.rb b/plugins/pushes/local-exec/plugin.rb
new file mode 100644
index 000000000..e58fe4bed
--- /dev/null
+++ b/plugins/pushes/local-exec/plugin.rb
@@ -0,0 +1,33 @@
+require "vagrant"
+
+module VagrantPlugins
+ module LocalExecPush
+ class Plugin < Vagrant.plugin("2")
+ name "local-exec"
+ description <<-DESC
+ Run a local command or script to push
+ DESC
+
+ config(:"local-exec", :push) do
+ require File.expand_path("../config", __FILE__)
+ init!
+ Config
+ end
+
+ push(:"local-exec") do
+ require File.expand_path("../push", __FILE__)
+ init!
+ Push
+ end
+
+ protected
+
+ def self.init!
+ return if defined?(@_init)
+ I18n.load_path << File.expand_path("../locales/en.yml", __FILE__)
+ I18n.reload!
+ @_init = true
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/local-exec/push.rb b/plugins/pushes/local-exec/push.rb
new file mode 100644
index 000000000..bad308f3b
--- /dev/null
+++ b/plugins/pushes/local-exec/push.rb
@@ -0,0 +1,45 @@
+require "fileutils"
+require "tempfile"
+require "vagrant/util/safe_exec"
+
+require_relative "errors"
+
+module VagrantPlugins
+ module LocalExecPush
+ class Push < Vagrant.plugin("2", :push)
+ def push
+ if config.inline
+ execute_inline!(config.inline)
+ else
+ execute_script!(config.script)
+ end
+ end
+
+ # Execute the inline script by writing it to a tempfile and executing.
+ def execute_inline!(inline)
+ script = Tempfile.new(["vagrant-local-exec-script", ".sh"])
+ script.write(inline)
+ script.rewind
+
+ execute_script!(script.path)
+ ensure
+ if script
+ script.close
+ script.unlink
+ end
+ end
+
+ # Execute the script, expanding the path relative to the current env root.
+ def execute_script!(path)
+ path = File.expand_path(path, env.root_path)
+ FileUtils.chmod("+x", path)
+ execute!(path)
+ end
+
+ # Execute the script, raising an exception if it fails.
+ def execute!(*cmd)
+ Vagrant::Util::SafeExec.exec(cmd[0], *cmd[1..-1])
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/noop/config.rb b/plugins/pushes/noop/config.rb
new file mode 100644
index 000000000..9b23fb450
--- /dev/null
+++ b/plugins/pushes/noop/config.rb
@@ -0,0 +1,16 @@
+module VagrantPlugins
+ module NoopDeploy
+ class Config < Vagrant.plugin("2", :config)
+ def initialize
+ end
+
+ def finalize!
+ end
+
+ def validate(machine)
+ errors = _detected_errors
+ { "Noop push" => errors }
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/noop/plugin.rb b/plugins/pushes/noop/plugin.rb
new file mode 100644
index 000000000..d3dc6994d
--- /dev/null
+++ b/plugins/pushes/noop/plugin.rb
@@ -0,0 +1,22 @@
+require "vagrant"
+
+module VagrantPlugins
+ module NoopDeploy
+ class Plugin < Vagrant.plugin("2")
+ name "noop"
+ description <<-DESC
+ Literally do nothing
+ DESC
+
+ config(:noop, :push) do
+ require File.expand_path("../config", __FILE__)
+ Config
+ end
+
+ push(:noop) do
+ require File.expand_path("../push", __FILE__)
+ Push
+ end
+ end
+ end
+end
diff --git a/plugins/pushes/noop/push.rb b/plugins/pushes/noop/push.rb
new file mode 100644
index 000000000..537ea1c7d
--- /dev/null
+++ b/plugins/pushes/noop/push.rb
@@ -0,0 +1,9 @@
+module VagrantPlugins
+ module NoopDeploy
+ class Push < Vagrant.plugin("2", :push)
+ def push
+ puts "pushed"
+ end
+ end
+ end
+end
diff --git a/scripts/website_push_docs.sh b/scripts/website_push_docs.sh
index ec2d8a09f..fd3937fc4 100755
--- a/scripts/website_push_docs.sh
+++ b/scripts/website_push_docs.sh
@@ -8,5 +8,10 @@ DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
# Change into that directory
cd $DIR
+# Add the git remote if it doesn't exist
+git remote | grep heroku-docs || {
+ git remote add heroku-docs git@heroku.com:vagrantup-docs.git
+}
+
# Push the subtree (force)
git push heroku-docs `git subtree split --prefix website/docs master`:master --force
diff --git a/scripts/website_push_www.sh b/scripts/website_push_www.sh
index 9e8bff265..1d5f180f9 100755
--- a/scripts/website_push_www.sh
+++ b/scripts/website_push_www.sh
@@ -8,5 +8,10 @@ DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
# Change into that directory
cd $DIR
+# Add the git remote if it doesn't exist
+git remote | grep heroku-www || {
+ git remote add heroku-www git@heroku.com:vagrantup-www.git
+}
+
# Push the subtree (force)
git push heroku-www `git subtree split --prefix website/www master`:master --force
diff --git a/templates/commands/init/Vagrantfile.erb b/templates/commands/init/Vagrantfile.erb
index 710e61664..4cae3c990 100644
--- a/templates/commands/init/Vagrantfile.erb
+++ b/templates/commands/init/Vagrantfile.erb
@@ -8,9 +8,10 @@
Vagrant.configure(2) do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
- # vagrantup.com
+ # https://docs.vagrantup.com.
- # Every Vagrant virtual environment requires a box to build off of.
+ # Every Vagrant development environment requires a box. You can search for
+ # boxes at https://atlas.hashicorp.com/search.
config.vm.box = "<%= box_name %>"
<% if box_url -%>
@@ -38,10 +39,6 @@ Vagrant.configure(2) do |config|
# your network.
# config.vm.network "public_network"
- # If true, then any SSH connections made will enable agent forwarding.
- # Default value: false
- # config.ssh.forward_agent = true
-
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
@@ -53,134 +50,28 @@ Vagrant.configure(2) do |config|
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
- # # Don't boot with headless mode
+ # # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
- # # Use VBoxManage to customize the VM. For example to change memory:
- # vb.customize ["modifyvm", :id, "--memory", "1024"]
+ # # Customize the amount of memory on the VM:
+ # vb.memory = "1024"
# end
#
- # View the documentation for the provider you're using for more
+ # View the documentation for the provider you are using for more
# information on available options.
- # Enable provisioning with CFEngine. CFEngine Community packages are
- # automatically installed. For example, configure the host as a
- # policy server and optionally a policy file to run:
- #
- # config.vm.provision "cfengine" do |cf|
- # cf.am_policy_hub = true
- # # cf.run_file = "motd.cf"
- # end
- #
- # You can also configure and bootstrap a client to an existing
- # policy server:
- #
- # config.vm.provision "cfengine" do |cf|
- # cf.policy_server_address = "10.0.2.15"
+ # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
+ # such as FTP and Heroku are also available. See the documentation at
+ # https://docs.vagrantup.com/v2/push/atlas.html for more information.
+ # config.push.define "atlas" do |push|
+ # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
# end
- # Enable provisioning with Puppet stand alone. Puppet manifests
- # are contained in a directory path relative to this Vagrantfile.
- # You will need to create the manifests directory and a manifest in
- # the file default.pp in the manifests_path directory.
- #
- # config.vm.provision "puppet" do |puppet|
- # puppet.manifests_path = "manifests"
- # puppet.manifest_file = "default.pp"
- # end
-
- # Enable provisioning with Chef Solo, specifying a cookbooks path, roles
- # path, and data_bags path (all relative to this Vagrantfile), and adding
- # some recipes and/or roles.
- #
- # config.vm.provision "chef_solo" do |chef|
- # chef.cookbooks_path = "~/chef/cookbooks"
- # chef.roles_path = "~/chef/roles"
- # chef.data_bags_path = "~/chef/data_bags"
- #
- # chef.add_recipe "mysql"
- # chef.add_role "web"
- #
- # chef.json = { mysql_password: "foo" }
- # end
- #
- # Chef Solo will automatically install the latest version of Chef for you.
- # This can be configured in the provisioner block:
- #
- # config.vm.provision "chef_solo" do |chef|
- # chef.version = "11.16.4"
- # end
- #
- # Alternative you can disable the installation of Chef entirely:
- #
- # config.vm.provision "chef_solo" do |chef|
- # chef.install = false
- # end
-
- # Enable provisioning with Chef Zero. The Chef Zero provisioner accepts the
- # exact same parameter as the Chef Solo provisioner:
- #
- # config.vm.provision "chef_zero" do |chef|
- # chef.cookbooks_path = "~/chef/cookbooks"
- # chef.roles_path = "~/chef/roles"
- # chef.data_bags_path = "~/chef/data_bags"
- #
- # chef.add_recipe "mysql"
- # chef.add_role "web"
- #
- # # You may also specify custom JSON attributes:
- # chef.json = { mysql_password: "foo" }
- # end
-
- # Enable provisioning with Chef Server, specifying the chef server URL,
- # and the path to the validation key (relative to this Vagrantfile).
- #
- # The Hosted Chef platform uses HTTPS. Substitute your organization for
- # ORGNAME in the URL and validation key.
- #
- # If you have your own Chef Server, use the appropriate URL, which may be
- # HTTP instead of HTTPS depending on your configuration. Also change the
- # validation key to validation.pem.
- #
- # config.vm.provision "chef_client" do |chef|
- # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
- # chef.validation_key_path = "ORGNAME-validator.pem"
- # end
- #
- # If you're using the Hosted Chef platform, your validator client is
- # ORGNAME-validator, replacing ORGNAME with your organization name.
- #
- # If you have your own Chef Server, the default validation client name is
- # chef-validator, unless you changed the configuration.
- #
- # chef.validation_client_name = "ORGNAME-validator"
- #
- # Chef Client will automatically install the latest version of Chef for you.
- # This can be configured in the provisioner block:
- #
- # config.vm.provision "chef_client" do |chef|
- # chef.version = "11.16.4"
- # end
- #
- # Alternative you can disable the installation of Chef entirely:
- #
- # config.vm.provision "chef_client" do |chef|
- # chef.install = false
- # end
-
- # Enable provisioning with Chef Apply, specifying an inline recipe to execute
- # on the target system.
- #
- # config.vm.provision "chef_apply" do |chef|
- # chef.recipe = <<-RECIPE
- # package "curl"
- # RECIPE
- # end
- #
- # Chef Apply will automatically install the latest version of Chef for you.
- # This can be configured in the provisioner block:
- #
- # config.vm.provision "chef_apply" do |chef|
- # chef.version = "11.16.4"
- # end
+ # Enable provisioning with a shell script. Additional provisioners such as
+ # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
+ # documentation for more information about their specific syntax and use.
+ # config.vm.provision "shell", inline: <<-SHELL
+ # sudo apt-get update
+ # sudo apt-get install -y apache2
+ # SHELL
end
diff --git a/templates/commands/init/Vagrantfile.min.erb b/templates/commands/init/Vagrantfile.min.erb
index 6120ce0e4..ac70f5e7c 100644
--- a/templates/commands/init/Vagrantfile.min.erb
+++ b/templates/commands/init/Vagrantfile.min.erb
@@ -1,6 +1,3 @@
-# -*- mode: ruby -*-
-# vi: set ft=ruby :
-
Vagrant.configure(2) do |config|
config.vm.box = "<%= box_name %>"
<% if box_url -%>
diff --git a/templates/locales/en.yml b/templates/locales/en.yml
index e761ac961..c4a7d84e3 100644
--- a/templates/locales/en.yml
+++ b/templates/locales/en.yml
@@ -244,7 +244,7 @@ en:
Press ctrl-c now to exit if you want to remove some boxes or free
up some disk space.
- Press any other key to continue.
+ Press the Enter or Return key to continue.
version_current: |-
Installed Version: %{version}
version_latest: |-
@@ -370,7 +370,7 @@ en:
provider. Double-check your requested provider to verify you didn't
simply misspell it.
- If you're adding a box from Vagrant Cloud, make sure the box is
+ If you're adding a box from HashiCorp's Atlas, make sure the box is
released.
Name: %{name}
@@ -389,7 +389,7 @@ en:
box_add_short_not_found: |-
The box '%{name}' could not be found or
could not be accessed in the remote catalog. If this is a private
- box on Vagrant Cloud, please verify you're logged in via
+ box on HashiCorp's Atlas, please verify you're logged in via
`vagrant login`. Also, please double-check the name. The expanded
URL and error message are shown below:
@@ -551,16 +551,14 @@ en:
%{versions}
box_server_not_set: |-
- A URL to a Vagrant Cloud server is not set, so boxes cannot
- be added with a shorthand ("mitchellh/precise64") format.
- You may also be seeing this error if you meant to type in
- a path to a box file which doesn't exist locally on your
- system.
+ A URL to an Atlas server is not set, so boxes cannot be added with a
+ shorthand ("mitchellh/precise64") format. You may also be seeing this
+ error if you meant to type in a path to a box file which doesn't exist
+ locally on your system.
- To set a URL to a Vagrant Cloud server, set the
- `VAGRANT_SERVER_URL` environmental variable. Or, if you
- meant to use a file path, make sure the path to the file
- is valid.
+ To set a URL to an Atlas server, set the `VAGRANT_SERVER_URL`
+ environmental variable. Or, if you meant to use a file path, make sure
+ the path to the file is valid.
box_update_multi_provider: |-
You requested to update the box '%{name}'. This box has
multiple providers. You must explicitly select a single
@@ -947,6 +945,29 @@ en:
You can however, install a plugin with the same name to replace
these plugins. User-installed plugins take priority over
system-installed plugins.
+ pushes_not_defined: |-
+ The Vagrantfile does not define any 'push' strategies. In order to use
+ `vagrant push`, you must define at least one push strategy:
+
+ config.push.define "ftp" do |push|
+ # ... push-specific options
+ end
+ push_strategy_not_defined: |-
+ The push strategy '%{name}' is not defined in the Vagrantfile. Defined
+ strategy names are:
+
+ %{pushes}
+ push_strategy_not_loaded: |-
+ There are no push strategies named '%{name}'. Please make sure you
+ spelled it correctly. If you are using an external push strategy, you
+ may need to install a plugin. Loaded push strategies are:
+
+ %{pushes}
+ push_strategy_not_provided: |-
+ The Vagrantfile defines more than one 'push' strategy. Please specify a
+ strategy. Defined strategy names are:
+
+ %{pushes}
package_include_symlink: |-
A file or directory you're attempting to include with your packaged
box has symlinks in it. Vagrant cannot include symlinks in the
@@ -1789,6 +1810,8 @@ en:
running_apply: "Running chef-apply..."
running_solo: "Running chef-solo..."
running_solo_again: "Running chef-solo again (failed to converge)..."
+ running_zero: "Running chef-zero..."
+ running_zero_again: "Running chef-zero again (failed to converge)..."
missing_shared_folders: |-
Shared folders that Chef requires are missing on the virtual machine.
This is usually due to configuration changing after already booting the
@@ -1889,7 +1912,13 @@ en:
The specified minion_config file could not be found.
master_config_nonexist: |-
The specified master_config file could not be found.
+ grains_config_nonexist: |-
+ The specified grains_config file could not be found.
missing_key: |-
You must include both public and private keys.
must_accept_keys: |-
You must accept keys when running highstate with master!
+
+ pushes:
+ file:
+ no_destination: "File destination must be specified."
diff --git a/templates/provisioners/chef_solo/solo.erb b/templates/provisioners/chef_solo/solo.erb
index b7d08a65b..25d3346b7 100644
--- a/templates/provisioners/chef_solo/solo.erb
+++ b/templates/provisioners/chef_solo/solo.erb
@@ -5,11 +5,7 @@ file_cache_path "<%= file_cache_path %>"
file_backup_path "<%= file_backup_path %>"
cookbook_path <%= cookbooks_path.inspect %>
<% if roles_path %>
-if Chef::VERSION.to_f < 11.8
- role_path <%= roles_path.first.inspect %>
-else
- role_path <%= roles_path.inspect %>
-end
+role_path <%= roles_path.size == 1 ? roles_path.first.inspect : roles_path.inspect %>
<% end %>
log_level <%= log_level.inspect %>
verbose_logging <%= verbose_logging.inspect %>
diff --git a/test/unit/base.rb b/test/unit/base.rb
index c7b11b958..e3c68c099 100644
--- a/test/unit/base.rb
+++ b/test/unit/base.rb
@@ -4,6 +4,7 @@ require "rubygems"
# Gems
require "checkpoint"
require "rspec/autorun"
+require "webmock/rspec"
# Require Vagrant itself so we can reference the proper
# classes to test.
diff --git a/test/unit/plugins/commands/login/client_test.rb b/test/unit/plugins/commands/login/client_test.rb
new file mode 100644
index 000000000..f57670eb0
--- /dev/null
+++ b/test/unit/plugins/commands/login/client_test.rb
@@ -0,0 +1,130 @@
+require File.expand_path("../../../../base", __FILE__)
+
+require Vagrant.source_root.join("plugins/commands/login/command")
+
+describe VagrantPlugins::LoginCommand::Client do
+ include_context "unit"
+
+ let(:env) { isolated_environment.create_vagrant_env }
+
+ subject { described_class.new(env) }
+
+ before do
+ stub_env("ATLAS_TOKEN" => nil)
+ subject.clear_token
+ end
+
+ describe "#logged_in?" do
+ let(:url) { "#{Vagrant.server_url}/api/v1/authenticate?access_token=#{token}" }
+ let(:headers) { { "Content-Type" => "application/json" } }
+
+ before { allow(subject).to receive(:token).and_return(token) }
+
+ context "when there is no token" do
+ let(:token) { nil }
+
+ it "returns false" do
+ expect(subject.logged_in?).to be(false)
+ end
+ end
+
+ context "when there is a token" do
+ let(:token) { "ABCD1234" }
+
+ it "returns true if the endpoint returns a 200" do
+ stub_request(:get, url)
+ .with(headers: headers)
+ .to_return(body: JSON.pretty_generate("token" => token))
+ expect(subject.logged_in?).to be(true)
+ end
+
+ it "returns false if the endpoint returns a non-200" do
+ stub_request(:get, url)
+ .with(headers: headers)
+ .to_return(body: JSON.pretty_generate("bad" => true), status: 401)
+ expect(subject.logged_in?).to be(false)
+ end
+
+ it "raises an exception if the server cannot be found" do
+ stub_request(:get, url)
+ .to_raise(SocketError)
+ expect { subject.logged_in? }
+ .to raise_error(VagrantPlugins::LoginCommand::Errors::ServerUnreachable)
+ end
+ end
+ end
+
+ describe "#login" do
+ it "returns the access token after successful login" do
+ request = {
+ "user" => {
+ "login" => "foo",
+ "password" => "bar",
+ },
+ }
+
+ response = {
+ "token" => "baz",
+ }
+
+ headers = { "Content-Type" => "application/json" }
+
+ stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
+ with(body: JSON.dump(request), headers: headers).
+ to_return(status: 200, body: JSON.dump(response))
+
+ expect(subject.login("foo", "bar")).to eq("baz")
+ end
+
+ it "returns nil on bad login" do
+ stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
+ to_return(status: 401, body: "")
+
+ expect(subject.login("foo", "bar")).to be(false)
+ end
+
+ it "raises an exception if it can't reach the sever" do
+ stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
+ to_raise(SocketError)
+
+ expect { subject.login("foo", "bar") }.
+ to raise_error(VagrantPlugins::LoginCommand::Errors::ServerUnreachable)
+ end
+ end
+
+ describe "#token" do
+ it "reads ATLAS_TOKEN" do
+ stub_env("ATLAS_TOKEN" => "ABCD1234")
+ expect(subject.token).to eq("ABCD1234")
+ end
+
+ it "reads the stored file" do
+ subject.store_token("EFGH5678")
+ expect(subject.token).to eq("EFGH5678")
+ end
+
+ it "prefers the environment variable" do
+ stub_env("ATLAS_TOKEN" => "ABCD1234")
+ subject.store_token("EFGH5678")
+ expect(subject.token).to eq("ABCD1234")
+ end
+
+ it "returns nil if there's no token set" do
+ expect(subject.token).to be(nil)
+ end
+ end
+
+ describe "#store_token, #clear_token" do
+ it "stores the token and can re-access it" do
+ subject.store_token("foo")
+ expect(subject.token).to eq("foo")
+ expect(described_class.new(env).token).to eq("foo")
+ end
+
+ it "deletes the token" do
+ subject.store_token("foo")
+ subject.clear_token
+ expect(subject.token).to be_nil
+ end
+ end
+end
diff --git a/test/unit/plugins/commands/login/middleware/add_authentication_test.rb b/test/unit/plugins/commands/login/middleware/add_authentication_test.rb
new file mode 100644
index 000000000..99c99a8af
--- /dev/null
+++ b/test/unit/plugins/commands/login/middleware/add_authentication_test.rb
@@ -0,0 +1,88 @@
+require File.expand_path("../../../../../base", __FILE__)
+
+require Vagrant.source_root.join("plugins/commands/login/middleware/add_authentication")
+
+describe VagrantPlugins::LoginCommand::AddAuthentication do
+ include_context "unit"
+
+ let(:app) { lambda { |env| } }
+ let(:env) { {
+ env: iso_env,
+ } }
+
+ let(:iso_env) { isolated_environment.create_vagrant_env }
+ let(:server_url) { "http://foo.com" }
+
+ subject { described_class.new(app, env) }
+
+ before do
+ allow(Vagrant).to receive(:server_url).and_return(server_url)
+ stub_env("ATLAS_TOKEN" => nil)
+ end
+
+ describe "#call" do
+ it "does nothing if we have no server set" do
+ allow(Vagrant).to receive(:server_url).and_return(nil)
+ VagrantPlugins::LoginCommand::Client.new(iso_env).store_token("foo")
+
+ original = ["foo", "#{server_url}/bar"]
+ env[:box_urls] = original.dup
+
+ subject.call(env)
+
+ expect(env[:box_urls]).to eq(original)
+ end
+
+ it "does nothing if we aren't logged in" do
+ original = ["foo", "#{server_url}/bar"]
+ env[:box_urls] = original.dup
+
+ subject.call(env)
+
+ expect(env[:box_urls]).to eq(original)
+ end
+
+ it "appends the access token to the URL of server URLs" do
+ token = "foobarbaz"
+ VagrantPlugins::LoginCommand::Client.new(iso_env).store_token(token)
+
+ original = [
+ "http://google.com/box.box",
+ "#{server_url}/foo.box",
+ "#{server_url}/bar.box?arg=true",
+ ]
+
+ expected = original.dup
+ expected[1] = "#{original[1]}?access_token=#{token}"
+ expected[2] = "#{original[2]}&access_token=#{token}"
+
+ env[:box_urls] = original.dup
+ subject.call(env)
+
+ expect(env[:box_urls]).to eq(expected)
+ end
+
+ it "appends the access token to vagrantcloud.com URLs if Atlas" do
+ server_url = "https://atlas.hashicorp.com"
+ allow(Vagrant).to receive(:server_url).and_return(server_url)
+
+ token = "foobarbaz"
+ VagrantPlugins::LoginCommand::Client.new(iso_env).store_token(token)
+
+ original = [
+ "http://google.com/box.box",
+ "http://vagrantcloud.com/foo.box",
+ "http://vagrantcloud.com/bar.box?arg=true",
+ ]
+
+ expected = original.dup
+ expected[1] = "#{original[1]}?access_token=#{token}"
+ expected[2] = "#{original[2]}&access_token=#{token}"
+
+ env[:box_urls] = original.dup
+ subject.call(env)
+
+ expect(env[:box_urls]).to eq(expected)
+ end
+ end
+end
diff --git a/test/unit/plugins/commands/push/command_test.rb b/test/unit/plugins/commands/push/command_test.rb
new file mode 100644
index 000000000..0f649c6c4
--- /dev/null
+++ b/test/unit/plugins/commands/push/command_test.rb
@@ -0,0 +1,132 @@
+require File.expand_path("../../../../base", __FILE__)
+
+require Vagrant.source_root.join("plugins/commands/push/command")
+
+describe VagrantPlugins::CommandPush::Command do
+ include_context "unit"
+ include_context "command plugin helpers"
+
+ let(:iso_env) { isolated_environment }
+ let(:env) do
+ iso_env.vagrantfile(<<-VF)
+Vagrant.configure("2") do |config|
+ config.vm.box = "nope"
+end
+VF
+ iso_env.create_vagrant_env
+ end
+
+ let(:argv) { [] }
+ let(:pushes) { {} }
+
+ subject { described_class.new(argv, env) }
+
+ before do
+ Vagrant.plugin("2").manager.stub(pushes: pushes)
+ end
+
+ describe "#execute" do
+ before do
+ allow(subject).to receive(:validate_pushes!)
+ .and_return(:noop)
+ allow(env).to receive(:pushes)
+ allow(env).to receive(:push)
+ end
+
+ it "validates the pushes" do
+ expect(subject).to receive(:validate_pushes!).once
+ subject.execute
+ end
+
+ it "validates the configuration" do
+ iso_env.vagrantfile("")
+
+ subject = described_class.new(argv, iso_env.create_vagrant_env)
+ allow(subject).to receive(:validate_pushes!)
+ .and_return(:noop)
+
+ expect { subject.execute }.to raise_error(
+ Vagrant::Errors::ConfigInvalid)
+ end
+
+ it "delegates to Environment#push" do
+ expect(env).to receive(:push).once
+ subject.execute
+ end
+ end
+
+ describe "#validate_pushes!" do
+ context "when there are no pushes defined" do
+ let(:pushes) { [] }
+
+ context "when a strategy is given" do
+ it "raises an exception" do
+ expect { subject.validate_pushes!(pushes, :noop) }
+ .to raise_error(Vagrant::Errors::PushesNotDefined)
+ end
+ end
+
+ context "when no strategy is given" do
+ it "raises an exception" do
+ expect { subject.validate_pushes!(pushes) }
+ .to raise_error(Vagrant::Errors::PushesNotDefined)
+ end
+ end
+ end
+
+ context "when there is one push defined" do
+ let(:noop) { double("noop") }
+ let(:pushes) { [:noop] }
+
+ context "when a strategy is given" do
+ context "when that strategy is not defined" do
+ it "raises an exception" do
+ expect { subject.validate_pushes!(pushes, :bacon) }
+ .to raise_error(Vagrant::Errors::PushStrategyNotDefined)
+ end
+ end
+
+ context "when that strategy is defined" do
+ it "returns that push" do
+ expect(subject.validate_pushes!(pushes, :noop)).to eq(:noop)
+ end
+ end
+ end
+
+ context "when no strategy is given" do
+ it "returns the strategy" do
+ expect(subject.validate_pushes!(pushes)).to eq(:noop)
+ end
+ end
+ end
+
+ context "when there are multiple pushes defined" do
+ let(:noop) { double("noop") }
+ let(:ftp) { double("ftp") }
+ let(:pushes) { [:noop, :ftp] }
+
+ context "when a strategy is given" do
+ context "when that strategy is not defined" do
+ it "raises an exception" do
+ expect { subject.validate_pushes!(pushes, :bacon) }
+ .to raise_error(Vagrant::Errors::PushStrategyNotDefined)
+ end
+ end
+
+ context "when that strategy is defined" do
+ it "returns the strategy" do
+ expect(subject.validate_pushes!(pushes, :noop)).to eq(:noop)
+ expect(subject.validate_pushes!(pushes, :ftp)).to eq(:ftp)
+ end
+ end
+ end
+
+ context "when no strategy is given" do
+ it "raises an exception" do
+ expect { subject.validate_pushes!(pushes) }
+ .to raise_error(Vagrant::Errors::PushStrategyNotProvided)
+ end
+ end
+ end
+ end
+end
diff --git a/test/unit/plugins/communicators/winrm/communicator_test.rb b/test/unit/plugins/communicators/winrm/communicator_test.rb
index 1433f4317..095e2753a 100644
--- a/test/unit/plugins/communicators/winrm/communicator_test.rb
+++ b/test/unit/plugins/communicators/winrm/communicator_test.rb
@@ -75,15 +75,23 @@ describe VagrantPlugins::CommunicatorWinRM::Communicator do
describe ".test" do
it "returns true when exit code is zero" do
- expect(shell).to receive(:powershell).with(kind_of(String)).and_return({ exitcode: 0 })
+ output = { exitcode: 0, data:[{ stderr: '' }] }
+ expect(shell).to receive(:powershell).with(kind_of(String)).and_return(output)
expect(subject.test("test -d c:/windows")).to be_true
end
it "returns false when exit code is non-zero" do
- expect(shell).to receive(:powershell).with(kind_of(String)).and_return({ exitcode: 1 })
+ output = { exitcode: 1, data:[{ stderr: '' }] }
+ expect(shell).to receive(:powershell).with(kind_of(String)).and_return(output)
expect(subject.test("test -d /tmp/foobar")).to be_false
end
+ it "returns false when stderr contains output" do
+ output = { exitcode: 0, data:[{ stderr: 'this is an error' }] }
+ expect(shell).to receive(:powershell).with(kind_of(String)).and_return(output)
+ expect(subject.test("[-x stuff] && foo")).to be_false
+ end
+
it "returns false when command is testing for linux OS" do
expect(subject.test("uname -s | grep Debian")).to be_false
end
diff --git a/test/unit/plugins/kernel_v2/config/push_test.rb b/test/unit/plugins/kernel_v2/config/push_test.rb
new file mode 100644
index 000000000..e1c67996f
--- /dev/null
+++ b/test/unit/plugins/kernel_v2/config/push_test.rb
@@ -0,0 +1,352 @@
+require File.expand_path("../../../../base", __FILE__)
+
+require Vagrant.source_root.join("plugins/kernel_v2/config/push")
+
+describe VagrantPlugins::Kernel_V2::PushConfig do
+ include_context "unit"
+
+ subject { described_class.new }
+
+ describe "#define" do
+ let(:pushes) { subject.instance_variable_get(:@__defined_pushes) }
+
+ it "pushes the strategy and block onto the defined pushes array" do
+ subject.define("foo") { "bar" }
+ subject.define("foo") { "zip" }
+ subject.define("foo") { "zap" }
+
+ expect(pushes.size).to eq(1)
+ expect(pushes[:foo].size).to eq(3)
+ expect(pushes[:foo][0]).to be_a(Array)
+ expect(pushes[:foo][0][0]).to eq(:foo)
+ expect(pushes[:foo][0][1]).to be_a(Proc)
+ end
+
+ context "when no strategy is given" do
+ it "defaults to the name" do
+ subject.define("foo") { "bar" }
+
+ expect(pushes.size).to eq(1)
+ expect(pushes[:foo].size).to eq(1)
+ expect(pushes[:foo][0]).to be_a(Array)
+ expect(pushes[:foo][0][0]).to eq(:foo)
+ expect(pushes[:foo][0][1]).to be_a(Proc)
+ end
+ end
+
+ context "when a strategy is given" do
+ it "uses the strategy" do
+ subject.define("foo", strategy: "bacon") { "bar" }
+
+ expect(pushes.size).to eq(1)
+ expect(pushes[:foo].size).to eq(1)
+ expect(pushes[:foo][0]).to be_a(Array)
+ expect(pushes[:foo][0][0]).to eq(:bacon)
+ expect(pushes[:foo][0][1]).to be_a(Proc)
+ end
+ end
+ end
+
+ describe "#merge" do
+ it "appends defined pushes" do
+ a = described_class.new.tap do |i|
+ i.define("foo") { "bar" }
+ i.define("bar") { "bar" }
+ end
+ b = described_class.new.tap do |i|
+ i.define("foo") { "zip" }
+ end
+
+ result = a.merge(b)
+ pushes = result.instance_variable_get(:@__defined_pushes)
+
+ expect(pushes[:foo]).to be_a(Array)
+ expect(pushes[:foo].size).to eq(2)
+
+ expect(pushes[:bar]).to be_a(Array)
+ expect(pushes[:bar].size).to eq(1)
+ end
+ end
+
+ describe "#__compiled_pushes" do
+ it "raises an exception if not finalized" do
+ subject.instance_variable_set(:@__finalized, false)
+ expect { subject.__compiled_pushes }.to raise_error
+ end
+
+ it "returns a copy of the compiled pushes" do
+ pushes = { foo: "bar" }
+ subject.instance_variable_set(:@__finalized, true)
+ subject.instance_variable_set(:@__compiled_pushes, pushes)
+
+ expect(subject.__compiled_pushes).to_not be(pushes)
+ expect(subject.__compiled_pushes).to eq(pushes)
+ end
+ end
+
+ describe "#finalize!" do
+ let(:pushes) { a.merge(b).tap { |r| r.finalize! }.__compiled_pushes }
+ let(:key) { pushes[:foo][0] }
+ let(:config) { pushes[:foo][1] }
+ let(:unset) { Vagrant.plugin("2", :config).const_get(:UNSET_VALUE) }
+ let(:dummy_klass) { Vagrant::Config::V2::DummyConfig }
+
+ before do
+ register_plugin("2") do |plugin|
+ plugin.name "foo"
+
+ plugin.push(:foo) do
+ Class.new(Vagrant.plugin("2", :push))
+ end
+
+ plugin.config(:foo, :push) do
+ Class.new(Vagrant.plugin("2", :config)) do
+ attr_accessor :bar
+ attr_accessor :zip
+
+ def initialize
+ @bar = self.class.const_get(:UNSET_VALUE)
+ @zip = self.class.const_get(:UNSET_VALUE)
+ end
+ end
+ end
+ end
+ end
+
+ it "compiles the proper configuration with a single strategy" do
+ instance = described_class.new.tap do |i|
+ i.define "foo"
+ end
+
+ instance.finalize!
+
+ pushes = instance.__compiled_pushes
+ strategy, config = pushes[:foo]
+ expect(strategy).to eq(:foo)
+ expect(config.bar).to be(unset)
+ end
+
+ it "compiles the proper configuration with a single strategy and block" do
+ instance = described_class.new.tap do |i|
+ i.define "foo" do |b|
+ b.bar = 42
+ end
+ end
+
+ instance.finalize!
+
+ pushes = instance.__compiled_pushes
+ strategy, config = pushes[:foo]
+ expect(strategy).to eq(:foo)
+ expect(config.bar).to eq(42)
+ end
+
+ it "compiles the proper config with a name and explicit strategy" do
+ instance = described_class.new.tap do |i|
+ i.define "bar", strategy: "foo"
+ end
+
+ instance.finalize!
+
+ pushes = instance.__compiled_pushes
+ strategy, config = pushes[:bar]
+ expect(strategy).to eq(:foo)
+ expect(config.bar).to be(unset)
+ end
+
+ it "compiles the proper config with a name and explicit strategy with block" do
+ instance = described_class.new.tap do |i|
+ i.define "bar", strategy: "foo" do |b|
+ b.bar = 42
+ end
+ end
+
+ instance.finalize!
+
+ pushes = instance.__compiled_pushes
+ strategy, config = pushes[:bar]
+ expect(strategy).to eq(:foo)
+ expect(config.bar).to eq(42)
+ end
+
+ context "with the same name but different strategy" do
+ context "with no block" do
+ let(:a) do
+ described_class.new.tap do |i|
+ i.define("foo", strategy: "bar")
+ end
+ end
+
+ let(:b) do
+ described_class.new.tap do |i|
+ i.define("foo", strategy: "zip")
+ end
+ end
+
+ it "chooses the last config" do
+ expect(key).to eq(:zip)
+ expect(config).to be_kind_of(dummy_klass)
+ end
+ end
+
+ context "with a block" do
+ let(:a) do
+ described_class.new.tap do |i|
+ i.define("foo", strategy: "bar") do |p|
+ p.bar = "a"
+ end
+ end
+ end
+
+ let(:b) do
+ described_class.new.tap do |i|
+ i.define("foo", strategy: "zip") do |p|
+ p.zip = "b"
+ end
+ end
+ end
+
+ it "chooses the last config" do
+ expect(key).to eq(:zip)
+ expect(config).to be_kind_of(dummy_klass)
+ end
+ end
+
+ context "with a block, then no block" do
+ let(:a) do
+ described_class.new.tap do |i|
+ i.define("foo", strategy: "bar") do |p|
+ p.bar, p.zip = "a", "a"
+ end
+ end
+ end
+
+ let(:b) do
+ described_class.new.tap do |i|
+ i.define("foo", strategy: "zip")
+ end
+ end
+
+ it "chooses the last config" do
+ expect(key).to eq(:zip)
+ expect(config).to be_kind_of(dummy_klass)
+ end
+ end
+
+ context "with no block, then a block" do
+ let(:a) do
+ described_class.new.tap do |i|
+ i.define("foo", strategy: "bar")
+ end
+ end
+
+ let(:b) do
+ described_class.new.tap do |i|
+ i.define("foo", strategy: "zip") do |p|
+ p.bar, p.zip = "b", "b"
+ end
+ end
+ end
+
+ it "chooses the last config" do
+ expect(key).to eq(:zip)
+ expect(config).to be_kind_of(dummy_klass)
+ end
+ end
+ end
+
+ context "with the same name twice" do
+ context "with no block" do
+ let(:a) do
+ described_class.new.tap do |i|
+ i.define("foo")
+ end
+ end
+
+ let(:b) do
+ described_class.new.tap do |i|
+ i.define("foo")
+ end
+ end
+
+ it "merges the configs" do
+ expect(key).to eq(:foo)
+ expect(config.bar).to be(unset)
+ expect(config.zip).to be(unset)
+ end
+ end
+
+ context "with a block" do
+ let(:a) do
+ described_class.new.tap do |i|
+ i.define("foo") do |p|
+ p.bar = "a"
+ end
+ end
+ end
+
+ let(:b) do
+ described_class.new.tap do |i|
+ i.define("foo") do |p|
+ p.zip = "b"
+ end
+ end
+ end
+
+ it "merges the configs" do
+ expect(key).to eq(:foo)
+ expect(config.bar).to eq("a")
+ expect(config.zip).to eq("b")
+ end
+ end
+
+ context "with a block, then no block" do
+ let(:a) do
+ described_class.new.tap do |i|
+ i.define("foo") do |p|
+ p.bar = "a"
+ end
+ end
+ end
+
+ let(:b) do
+ described_class.new.tap do |i|
+ i.define("foo")
+ end
+ end
+
+ it "merges the configs" do
+ expect(key).to eq(:foo)
+ expect(config.bar).to eq("a")
+ expect(config.zip).to be(unset)
+ end
+ end
+
+ context "with no block, then a block" do
+ let(:a) do
+ described_class.new.tap do |i|
+ i.define("foo", strategy: "bar")
+ end
+ end
+
+ let(:b) do
+ described_class.new.tap do |i|
+ i.define("foo", strategy: "zip") do |p|
+ p.zip = "b"
+ end
+ end
+ end
+
+ it "merges the configs" do
+ expect(key).to eq(:zip)
+ expect(config).to be_kind_of(dummy_klass)
+ end
+ end
+ end
+
+ it "sets @__finalized to true" do
+ subject.finalize!
+ expect(subject.instance_variable_get(:@__finalized)).to be(true)
+ end
+ end
+end
diff --git a/test/unit/plugins/provisioners/ansible/provisioner_test.rb b/test/unit/plugins/provisioners/ansible/provisioner_test.rb
index f93f1f53b..756f95769 100644
--- a/test/unit/plugins/provisioners/ansible/provisioner_test.rb
+++ b/test/unit/plugins/provisioners/ansible/provisioner_test.rb
@@ -62,13 +62,16 @@ VF
# Class methods for code reuse across examples
#
- def self.it_should_set_arguments_and_environment_variables(expected_args_count = 5, expected_vars_count = 3, expected_host_key_checking = false)
+ def self.it_should_set_arguments_and_environment_variables(
+ expected_args_count = 6, expected_vars_count = 4, expected_host_key_checking = false, expected_transport_mode = "ssh")
+
it "sets implicit arguments in a specific order" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
expect(args[0]).to eq("ansible-playbook")
expect(args[1]).to eq("--private-key=#{machine.ssh_info[:private_key_path][0]}")
expect(args[2]).to eq("--user=#{machine.ssh_info[:username]}")
+ expect(args[3]).to eq("--connection=ssh")
inventory_count = args.count { |x| x =~ /^--inventory-file=.+$/ }
expect(inventory_count).to be > 0
@@ -79,18 +82,18 @@ VF
it "sets --limit argument" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
- raw_limits = []
+ all_limits = args.select { |x| x =~ /^(--limit=|-l)/ }
if config.raw_arguments
raw_limits = config.raw_arguments.select { |x| x =~ /^(--limit=|-l)/ }
- end
- all_limits = args.select { |x| x =~ /^(--limit=|-l)/ }
- expect(all_limits.length - raw_limits.length).to eq(1)
-
- if config.limit
- limit = config.limit.kind_of?(Array) ? config.limit.join(',') : config.limit
- expect(all_limits.last).to eq("--limit=#{limit}")
+ expect(all_limits.length - raw_limits.length).to eq(1)
+ expect(all_limits.last).to eq(raw_limits.last)
else
- expect(all_limits.first).to eq("--limit=#{machine.name}")
+ if config.limit
+ limit = config.limit.kind_of?(Array) ? config.limit.join(',') : config.limit
+ expect(all_limits.last).to eq("--limit=#{limit}")
+ else
+ expect(all_limits.first).to eq("--limit=#{machine.name}")
+ end
end
}
end
@@ -98,6 +101,12 @@ VF
it "exports environment variables" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
cmd_opts = args.last
+
+ if expected_host_key_checking
+ expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to be_nil unless config.raw_arguments
+ else
+ expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o UserKnownHostsFile=/dev/null")
+ end
expect(cmd_opts[:env]['ANSIBLE_FORCE_COLOR']).to eql("true")
expect(cmd_opts[:env]['ANSIBLE_HOST_KEY_CHECKING']).to eql(expected_host_key_checking.to_s)
expect(cmd_opts[:env]['PYTHONUNBUFFERED']).to eql(1)
@@ -111,6 +120,15 @@ VF
expect(args.last[:env].length).to eq(expected_vars_count)
}
end
+
+ it "enables '#{expected_transport_mode}' transport mode" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
+ index = args.rindex("--connection=#{expected_transport_mode}")
+ expect(index).to be > 0
+ expect(find_last_argument_after(index, args, /--connection=\w+/)).to be_false
+ }
+ end
+
end
def self.it_should_set_optional_arguments(arg_map)
@@ -128,35 +146,7 @@ VF
end
end
- def self.it_should_use_smart_transport_mode
- it "does not export ANSIBLE_SSH_ARGS" do
- expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
- cmd_opts = args.last
- expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to be_nil
- }
- end
-
- it "does not force any transport mode" do
- expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
- total = args.count { |x| x =~ /^--connection=\w+$/ }
- expect(total).to eql(0)
- }
- end
- end
-
- def self.it_should_use_transport_mode(transport_mode)
- it "enables '#{transport_mode}' transport mode" do
- expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
- index = args.rindex("--connection=#{transport_mode}")
- expect(index).to be > 0
- expect(find_last_argument_after(index, args, /--connection=\w+/)).to be_false
- }
- end
- end
-
- def self.it_should_force_ssh_transport_mode
- it_should_use_transport_mode('ssh')
-
+ def self.it_should_explicitly_enable_ansible_ssh_control_persist_defaults
it "configures ControlPersist (like Ansible defaults) via ANSIBLE_SSH_ARGS" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
cmd_opts = args.last
@@ -212,7 +202,6 @@ VF
describe "with default options" do
it_should_set_arguments_and_environment_variables
- it_should_use_smart_transport_mode
it_should_create_and_use_generated_inventory
it "does not add any group section to the generated inventory" do
@@ -281,8 +270,7 @@ VF
config.host_key_checking = true
end
- it_should_set_arguments_and_environment_variables 5, 3, true
- it_should_use_smart_transport_mode
+ it_should_set_arguments_and_environment_variables 6, 3, true
end
describe "with boolean (flag) options disabled" do
@@ -294,7 +282,7 @@ VF
config.sudo_user = 'root'
end
- it_should_set_arguments_and_environment_variables 6
+ it_should_set_arguments_and_environment_variables 7
it_should_set_optional_arguments({ "sudo_user" => "--sudo-user=root" })
it "it does not set boolean flag when corresponding option is set to false" do
@@ -310,6 +298,7 @@ VF
before do
config.sudo = false
config.skip_tags = %w(foo bar)
+ config.limit = "all"
config.raw_arguments = ["--connection=paramiko",
"--skip-tags=ignored",
"--module-path=/other/modules",
@@ -318,12 +307,11 @@ VF
"--limit=foo",
"--limit=bar",
"--inventory-file=/forget/it/my/friend",
+ "--user=lion",
"--new-arg=yeah"]
end
- it_should_set_arguments_and_environment_variables 15
- it_should_create_and_use_generated_inventory
- it_should_use_transport_mode('paramiko')
+ it_should_set_arguments_and_environment_variables 17, 4, false, "paramiko"
it "sets all raw arguments" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
@@ -333,9 +321,12 @@ VF
}
end
- it "sets raw arguments before arguments related to supported options" do
+ it "sets raw arguments after arguments related to supported options" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
- expect(args.index("--skip-tags=foo,bar")).to be > args.index("--skip-tags=ignored")
+ expect(args.index("--user=lion")).to be > args.index("--user=testuser")
+ expect(args.index("--inventory-file=/forget/it/my/friend")).to be > args.index("--inventory-file=#{generated_inventory_dir}")
+ expect(args.index("--limit=bar")).to be > args.index("--limit=all")
+ expect(args.index("--skip-tags=ignored")).to be > args.index("--skip-tags=foo,bar")
}
end
@@ -361,7 +352,6 @@ VF
end
it_should_set_arguments_and_environment_variables
- it_should_use_smart_transport_mode
it "does not generate the inventory and uses given inventory path instead" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
@@ -377,7 +367,7 @@ VF
config.ask_vault_pass = true
end
- it_should_set_arguments_and_environment_variables 6
+ it_should_set_arguments_and_environment_variables 7
it "should ask the vault password" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
@@ -391,7 +381,7 @@ VF
config.vault_password_file = existing_file
end
- it_should_set_arguments_and_environment_variables 6
+ it_should_set_arguments_and_environment_variables 7
it "uses the given vault password file" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
@@ -406,7 +396,7 @@ VF
end
it_should_set_arguments_and_environment_variables 6, 4
- it_should_force_ssh_transport_mode
+ it_should_explicitly_enable_ansible_ssh_control_persist_defaults
it "passes custom SSH options via ANSIBLE_SSH_ARGS with the highest priority" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
@@ -440,7 +430,7 @@ VF
end
it_should_set_arguments_and_environment_variables 6, 4
- it_should_force_ssh_transport_mode
+ it_should_explicitly_enable_ansible_ssh_control_persist_defaults
it "passes additional Identity Files via ANSIBLE_SSH_ARGS" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
@@ -457,7 +447,7 @@ VF
end
it_should_set_arguments_and_environment_variables 6, 4
- it_should_force_ssh_transport_mode
+ it_should_explicitly_enable_ansible_ssh_control_persist_defaults
it "enables SSH-Forwarding via ANSIBLE_SSH_ARGS" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
@@ -475,7 +465,7 @@ VF
it "shows the ansible-playbook command" do
expect(machine.env.ui).to receive(:detail).with { |full_command|
- expect(full_command).to eq("ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false PYTHONUNBUFFERED=1 ansible-playbook --private-key=/path/to/my/key --user=testuser --limit='machine1' --inventory-file=#{generated_inventory_dir} playbook.yml")
+ expect(full_command).to eq("PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --private-key=/path/to/my/key --user=testuser --connection=ssh --limit='machine1' --inventory-file=#{generated_inventory_dir} playbook.yml")
}
end
end
@@ -485,12 +475,12 @@ VF
config.verbose = 'v'
end
- it_should_set_arguments_and_environment_variables 6
+ it_should_set_arguments_and_environment_variables 7
it_should_set_optional_arguments({ "verbose" => "-v" })
it "shows the ansible-playbook command" do
expect(machine.env.ui).to receive(:detail).with { |full_command|
- expect(full_command).to eq("ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false PYTHONUNBUFFERED=1 ansible-playbook --private-key=/path/to/my/key --user=testuser --limit='machine1' --inventory-file=#{generated_inventory_dir} -v playbook.yml")
+ expect(full_command).to eq("PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --private-key=/path/to/my/key --user=testuser --connection=ssh --limit='machine1' --inventory-file=#{generated_inventory_dir} -v playbook.yml")
}
end
end
@@ -524,7 +514,7 @@ VF
end
it_should_set_arguments_and_environment_variables 20, 4, true
- it_should_force_ssh_transport_mode
+ it_should_explicitly_enable_ansible_ssh_control_persist_defaults
it_should_set_optional_arguments({ "extra_vars" => "--extra-vars=@#{File.expand_path(__FILE__)}",
"sudo" => "--sudo",
"sudo_user" => "--sudo-user=deployer",
@@ -537,7 +527,7 @@ VF
"limit" => "--limit=machine*:&vagrant:!that_one",
"start_at_task" => "--start-at-task=an awesome task",
})
-
+
it "also includes given raw arguments" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
expect(args).to include("--su-user=foot")
@@ -548,7 +538,7 @@ VF
it "shows the ansible-playbook command, with additional quotes when required" do
expect(machine.env.ui).to receive(:detail).with { |full_command|
- expect(full_command).to eq("ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=true PYTHONUNBUFFERED=1 ANSIBLE_SSH_ARGS='-o IdentityFile=/my/key2 -o ForwardAgent=yes -o ControlMaster=no -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --private-key=/my/key1 --user=testuser --connection=ssh --why-not --su-user=foot --ask-su-pass --limit='all' --inventory-file=#{generated_inventory_dir} --extra-vars=@#{File.expand_path(__FILE__)} --sudo --sudo-user=deployer -vvv --ask-sudo-pass --ask-vault-pass --vault-password-file=#{File.expand_path(__FILE__)} --tags=db,www --skip-tags=foo,bar --limit='machine*:&vagrant:!that_one' --start-at-task='an awesome task' playbook.yml")
+ expect(full_command).to eq("PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=true ANSIBLE_SSH_ARGS='-o IdentityFile=/my/key2 -o ForwardAgent=yes -o ControlMaster=no -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --private-key=/my/key1 --user=testuser --connection=ssh --limit='machine*:&vagrant:!that_one' --inventory-file=#{generated_inventory_dir} --extra-vars=@#{File.expand_path(__FILE__)} --sudo --sudo-user=deployer -vvv --ask-sudo-pass --ask-vault-pass --vault-password-file=#{File.expand_path(__FILE__)} --tags=db,www --skip-tags=foo,bar --start-at-task='an awesome task' --why-not --su-user=foot --ask-su-pass --limit='all' playbook.yml")
}
end
end
diff --git a/test/unit/plugins/provisioners/chef/config/base_test.rb b/test/unit/plugins/provisioners/chef/config/base_test.rb
index 2355f28cd..4b018f11b 100644
--- a/test/unit/plugins/provisioners/chef/config/base_test.rb
+++ b/test/unit/plugins/provisioners/chef/config/base_test.rb
@@ -68,4 +68,11 @@ describe VagrantPlugins::Chef::Config::Base do
expect(subject.version).to eq(:latest)
end
end
+
+ describe "#installer_download_path" do
+ it "defaults to nil" do
+ subject.finalize!
+ expect(subject.installer_download_path).to be(nil)
+ end
+ end
end
diff --git a/test/unit/plugins/provisioners/chef/omnibus_test.rb b/test/unit/plugins/provisioners/chef/omnibus_test.rb
index 9c42df1cc..3242be290 100644
--- a/test/unit/plugins/provisioners/chef/omnibus_test.rb
+++ b/test/unit/plugins/provisioners/chef/omnibus_test.rb
@@ -7,8 +7,9 @@ describe VagrantPlugins::Chef::Omnibus, :focus do
let(:version) { :latest }
let(:prerelease) { false }
+ let(:download_path) { nil }
- let(:build_command) { described_class.build_command(version, prerelease) }
+ let(:build_command) { described_class.build_command(version, prerelease, download_path) }
context "when prerelease is given" do
let(:prerelease) { true }
@@ -18,6 +19,14 @@ describe VagrantPlugins::Chef::Omnibus, :focus do
end
end
+ context "when download_path is given" do
+ let(:download_path) { '/tmp/path/to/omnibuses' }
+
+ it "returns the correct command" do
+ expect(build_command).to eq("#{prefix} | sudo bash -s -- -d \"/tmp/path/to/omnibuses\"")
+ end
+ end
+
context "when version is :latest" do
let(:version) { :latest }
@@ -34,12 +43,13 @@ describe VagrantPlugins::Chef::Omnibus, :focus do
end
end
- context "when prerelease and version are given" do
+ context "when prerelease and version and download_path are given" do
let(:version) { "1.2.3" }
let(:prerelease) { true }
+ let(:download_path) { "/some/path" }
it "returns the correct command" do
- expect(build_command).to eq("#{prefix} | sudo bash -s -- -p -v \"1.2.3\"")
+ expect(build_command).to eq("#{prefix} | sudo bash -s -- -p -v \"1.2.3\" -d \"/some/path\"")
end
end
end
diff --git a/test/unit/plugins/provisioners/salt/config_test.rb b/test/unit/plugins/provisioners/salt/config_test.rb
index c640a7497..24bf24796 100644
--- a/test/unit/plugins/provisioners/salt/config_test.rb
+++ b/test/unit/plugins/provisioners/salt/config_test.rb
@@ -60,5 +60,23 @@ describe VagrantPlugins::Salt::Config do
expect(result[error_key]).to be_empty
end
end
+
+ context "grains_config" do
+ it "fails if grains_config is set and missing" do
+ subject.grains_config = "/nope/still/not/here"
+ subject.finalize!
+
+ result = subject.validate(machine)
+ expect(result[error_key]).to_not be_empty
+ end
+
+ it "is valid if is set and not missing" do
+ subject.grains_config = File.expand_path(__FILE__)
+ subject.finalize!
+
+ result = subject.validate(machine)
+ expect(result[error_key]).to be_empty
+ end
+ end
end
end
diff --git a/test/unit/plugins/pushes/atlas/config_test.rb b/test/unit/plugins/pushes/atlas/config_test.rb
new file mode 100644
index 000000000..580a39ab0
--- /dev/null
+++ b/test/unit/plugins/pushes/atlas/config_test.rb
@@ -0,0 +1,190 @@
+require_relative "../../../base"
+
+require Vagrant.source_root.join("plugins/pushes/atlas/config")
+
+describe VagrantPlugins::AtlasPush::Config do
+ include_context "unit"
+
+ before(:all) do
+ I18n.load_path << Vagrant.source_root.join("plugins/pushes/atlas/locales/en.yml")
+ I18n.reload!
+ end
+
+ let(:machine) { double("machine") }
+
+ before do
+ subject.token = "foo"
+ end
+
+ describe "#address" do
+ it "defaults to nil" do
+ subject.finalize!
+ expect(subject.address).to be(nil)
+ end
+ end
+
+ describe "#app" do
+ it "defaults to nil" do
+ subject.finalize!
+ expect(subject.app).to be(nil)
+ end
+ end
+
+ describe "#dir" do
+ it "defaults to ." do
+ subject.finalize!
+ expect(subject.dir).to eq(".")
+ end
+ end
+
+ describe "#vcs" do
+ it "defaults to true" do
+ subject.finalize!
+ expect(subject.vcs).to be(true)
+ end
+ end
+
+ describe "#uploader_path" do
+ it "defaults to nil" do
+ subject.finalize!
+ expect(subject.uploader_path).to be(nil)
+ end
+ end
+
+ describe "#validate" do
+ before do
+ allow(machine).to receive(:env)
+ .and_return(double("env",
+ root_path: "",
+ data_dir: Pathname.new(""),
+ ))
+
+ subject.app = "sethvargo/bacon"
+ subject.dir = "."
+ subject.vcs = true
+ subject.uploader_path = "uploader"
+ end
+
+ let(:result) { subject.validate(machine) }
+ let(:errors) { result["Atlas push"] }
+
+ context "when the token is missing" do
+ context "when a vagrant-login token exists" do
+ before do
+ allow(subject).to receive(:token_from_vagrant_login)
+ .and_return("token_from_vagrant_login")
+ end
+
+ it "uses the token from vagrant-login" do
+ subject.token = ""
+ subject.finalize!
+ expect(errors).to be_empty
+ expect(subject.token).to eq("token_from_vagrant_login")
+ end
+ end
+
+ context "when a token is given in the Vagrantfile" do
+ before do
+ allow(subject).to receive(:token_from_vagrant_login)
+ .and_return("token_from_vagrant_login")
+ end
+
+ it "uses the token in the Vagrantfile" do
+ subject.token = "token_from_vagrantfile"
+ subject.finalize!
+ expect(errors).to be_empty
+ expect(subject.token).to eq("token_from_vagrantfile")
+ end
+ end
+
+ context "when no token is given" do
+ before do
+ allow(subject).to receive(:token_from_vagrant_login)
+ .and_return(nil)
+ end
+
+ it "returns an error" do
+ subject.token = ""
+ subject.finalize!
+ expect(errors).to include(I18n.t("atlas_push.errors.missing_token"))
+ end
+ end
+ end
+
+ context "when the app is missing" do
+ it "returns an error" do
+ subject.app = ""
+ subject.finalize!
+ expect(errors).to include(I18n.t("atlas_push.errors.missing_attribute",
+ attribute: "app",
+ ))
+ end
+ end
+
+ context "when the dir is missing" do
+ it "returns an error" do
+ subject.dir = ""
+ subject.finalize!
+ expect(errors).to include(I18n.t("atlas_push.errors.missing_attribute",
+ attribute: "dir",
+ ))
+ end
+ end
+
+ context "when the vcs is missing" do
+ it "does not return an error" do
+ subject.vcs = ""
+ subject.finalize!
+ expect(errors).to be_empty
+ end
+ end
+
+ context "when the uploader_path is missing" do
+ it "returns an error" do
+ subject.uploader_path = ""
+ subject.finalize!
+ expect(errors).to be_empty
+ end
+ end
+ end
+
+ describe "#merge" do
+ context "when includes are given" do
+ let(:one) { described_class.new }
+ let(:two) { described_class.new }
+
+ it "merges the result" do
+ one.includes = %w(a b c)
+ two.includes = %w(c d e)
+ result = one.merge(two)
+ expect(result.includes).to eq(%w(a b c d e))
+ end
+ end
+
+ context "when excludes are given" do
+ let(:one) { described_class.new }
+ let(:two) { described_class.new }
+
+ it "merges the result" do
+ one.excludes = %w(a b c)
+ two.excludes = %w(c d e)
+ result = one.merge(two)
+ expect(result.excludes).to eq(%w(a b c d e))
+ end
+ end
+ end
+
+ describe "#include" do
+ it "adds the item to the list" do
+ subject.include("me")
+ expect(subject.includes).to include("me")
+ end
+ end
+
+ describe "#exclude" do
+ it "adds the item to the list" do
+ subject.exclude("not me")
+ expect(subject.excludes).to include("not me")
+ end
+ end
+end
diff --git a/test/unit/plugins/pushes/atlas/push_test.rb b/test/unit/plugins/pushes/atlas/push_test.rb
new file mode 100644
index 000000000..e7ffdebd5
--- /dev/null
+++ b/test/unit/plugins/pushes/atlas/push_test.rb
@@ -0,0 +1,153 @@
+require_relative "../../../base"
+
+require Vagrant.source_root.join("plugins/pushes/atlas/config")
+require Vagrant.source_root.join("plugins/pushes/atlas/push")
+
+describe VagrantPlugins::AtlasPush::Push do
+ include_context "unit"
+
+ let(:bin) { VagrantPlugins::AtlasPush::Push::UPLOADER_BIN }
+
+ let(:env) do
+ double("env",
+ root_path: File.expand_path("..", __FILE__)
+ )
+ end
+
+ let(:config) do
+ VagrantPlugins::AtlasPush::Config.new.tap do |c|
+ c.finalize!
+ end
+ end
+
+ subject { described_class.new(env, config) }
+
+ before do
+ # Stub this right away to avoid real execs
+ allow(Vagrant::Util::SafeExec).to receive(:exec)
+ end
+
+ describe "#push" do
+ it "pushes with the uploader" do
+ allow(subject).to receive(:uploader_path).and_return("foo")
+
+ expect(subject).to receive(:execute).with("foo")
+
+ subject.push
+ end
+
+ it "raises an exception if the uploader couldn't be found" do
+ expect(subject).to receive(:uploader_path).and_return(nil)
+
+ expect { subject.push }.to raise_error(
+ VagrantPlugins::AtlasPush::Errors::UploaderNotFound)
+ end
+ end
+
+ describe "#execute" do
+ let(:app) { "foo/bar" }
+
+ before do
+ config.app = app
+ end
+
+ it "sends the basic flags" do
+ expect(Vagrant::Util::SafeExec).to receive(:exec).
+ with("foo", "-vcs", app, env.root_path.to_s)
+
+ subject.execute("foo")
+ end
+
+ it "doesn't send VCS if disabled" do
+ expect(Vagrant::Util::SafeExec).to receive(:exec).
+ with("foo", app, env.root_path.to_s)
+
+ config.vcs = false
+ subject.execute("foo")
+ end
+
+ it "sends includes" do
+ expect(Vagrant::Util::SafeExec).to receive(:exec).
+ with("foo", "-vcs", "-include", "foo", "-include",
+ "bar", app, env.root_path.to_s)
+
+ config.includes = ["foo", "bar"]
+ subject.execute("foo")
+ end
+
+ it "sends excludes" do
+ expect(Vagrant::Util::SafeExec).to receive(:exec).
+ with("foo", "-vcs", "-exclude", "foo", "-exclude",
+ "bar", app, env.root_path.to_s)
+
+ config.excludes = ["foo", "bar"]
+ subject.execute("foo")
+ end
+
+ it "sends custom server address" do
+ expect(Vagrant::Util::SafeExec).to receive(:exec).
+ with("foo", "-vcs", "-address", "foo", app, env.root_path.to_s)
+
+ config.address = "foo"
+ subject.execute("foo")
+ end
+
+ it "sends custom token" do
+ expect(Vagrant::Util::SafeExec).to receive(:exec).
+ with("foo", "-vcs", "-token", "atlas_token", app, env.root_path.to_s)
+
+ config.token = "atlas_token"
+ subject.execute("foo")
+ end
+ end
+
+ describe "#uploader_path" do
+ it "should return the configured path if set" do
+ config.uploader_path = "foo"
+ expect(subject.uploader_path).to eq("foo")
+ end
+
+ it "should look up the uploader via PATH if not set" do
+ allow(Vagrant).to receive(:in_installer?).and_return(false)
+
+ expect(Vagrant::Util::Which).to receive(:which).
+ with(described_class.const_get(:UPLOADER_BIN)).
+ and_return("bar")
+
+ expect(subject.uploader_path).to eq("bar")
+ end
+
+ it "should look up the uploader in the embedded dir if installer" do
+ dir = temporary_dir
+
+ allow(Vagrant).to receive(:in_installer?).and_return(true)
+ allow(Vagrant).to receive(:installer_embedded_dir).and_return(dir.to_s)
+
+ bin_path = dir.join("bin", bin)
+ bin_path.dirname.mkpath
+ bin_path.open("w+") { |f| f.write("hi") }
+
+ expect(subject.uploader_path).to eq(bin_path.to_s)
+ end
+
+ it "should look up the uploader in the PATH if not in the installer" do
+ dir = temporary_dir
+
+ allow(Vagrant).to receive(:in_installer?).and_return(true)
+ allow(Vagrant).to receive(:installer_embedded_dir).and_return(dir.to_s)
+
+ expect(Vagrant::Util::Which).to receive(:which).
+ with(described_class.const_get(:UPLOADER_BIN)).
+ and_return("bar")
+
+ expect(subject.uploader_path).to eq("bar")
+ end
+
+ it "should return nil if its not found anywhere" do
+ allow(Vagrant).to receive(:in_installer?).and_return(false)
+ allow(Vagrant::Util::Which).to receive(:which).and_return(nil)
+
+ expect(subject.uploader_path).to be_nil
+ end
+ end
+end
diff --git a/test/unit/plugins/pushes/ftp/adapter_test.rb b/test/unit/plugins/pushes/ftp/adapter_test.rb
new file mode 100644
index 000000000..e929078cd
--- /dev/null
+++ b/test/unit/plugins/pushes/ftp/adapter_test.rb
@@ -0,0 +1,111 @@
+require_relative "../../../base"
+require "fake_ftp"
+
+require Vagrant.source_root.join("plugins/pushes/ftp/adapter")
+
+describe VagrantPlugins::FTPPush::Adapter do
+ include_context "unit"
+
+ subject do
+ described_class.new("127.0.0.1:2345", "sethvargo", "bacon",
+ foo: "bar",
+ )
+ end
+
+ describe "#initialize" do
+ it "sets the instance variables" do
+ expect(subject.host).to eq("127.0.0.1")
+ expect(subject.port).to eq(2345)
+ expect(subject.username).to eq("sethvargo")
+ expect(subject.password).to eq("bacon")
+ expect(subject.options).to eq(foo: "bar")
+ expect(subject.server).to be(nil)
+ end
+ end
+
+ describe "#parse_host" do
+ it "has a default value" do
+ allow(subject).to receive(:default_port)
+ .and_return(5555)
+
+ result = subject.parse_host("127.0.0.1")
+ expect(result[0]).to eq("127.0.0.1")
+ expect(result[1]).to eq(5555)
+ end
+ end
+end
+
+describe VagrantPlugins::FTPPush::FTPAdapter do
+ include_context "unit"
+
+ before(:all) do
+ @server = FakeFtp::Server.new(21212, 21213)
+ @server.start
+ end
+
+ after(:all) { @server.stop }
+
+ let(:server) { @server }
+
+ before { server.reset }
+
+ subject do
+ described_class.new("127.0.0.1:#{server.port}", "sethvargo", "bacon")
+ end
+
+ describe "#default_port" do
+ it "is 21" do
+ expect(subject.default_port).to eq(21)
+ end
+ end
+
+ describe "#upload" do
+ before do
+ @dir = Dir.mktmpdir
+ FileUtils.touch("#{@dir}/file")
+ end
+
+ after do
+ FileUtils.rm_rf(@dir)
+ end
+
+ it "uploads the file" do
+ subject.connect do |ftp|
+ ftp.upload("#{@dir}/file", "/file")
+ end
+
+ expect(server.files).to include("file")
+ end
+
+ it "uploads in passive mode" do
+ subject.options[:passive] = true
+ subject.connect do |ftp|
+ ftp.upload("#{@dir}/file", "/file")
+ end
+
+ expect(server.file("file")).to be_passive
+ end
+ end
+end
+
+describe VagrantPlugins::FTPPush::SFTPAdapter do
+ include_context "unit"
+
+ subject do
+ described_class.new("127.0.0.1:2345", "sethvargo", "bacon",
+ foo: "bar",
+ )
+ end
+
+ describe "#default_port" do
+ it "is 22" do
+ expect(subject.default_port).to eq(22)
+ end
+ end
+
+ describe "#upload" do
+ it "uploads the file" do
+ pending "a way to mock an SFTP server"
+ end
+ end
+end
diff --git a/test/unit/plugins/pushes/ftp/config_test.rb b/test/unit/plugins/pushes/ftp/config_test.rb
new file mode 100644
index 000000000..f66eeb791
--- /dev/null
+++ b/test/unit/plugins/pushes/ftp/config_test.rb
@@ -0,0 +1,171 @@
+require_relative "../../../base"
+
+require Vagrant.source_root.join("plugins/pushes/ftp/config")
+
+describe VagrantPlugins::FTPPush::Config do
+ include_context "unit"
+
+ before(:all) do
+ I18n.load_path << Vagrant.source_root.join("plugins/pushes/ftp/locales/en.yml")
+ I18n.reload!
+ end
+
+ subject { described_class.new }
+
+ let(:machine) { double("machine") }
+
+ describe "#host" do
+ it "defaults to nil" do
+ subject.finalize!
+ expect(subject.host).to be(nil)
+ end
+ end
+
+ describe "#username" do
+ it "defaults to nil" do
+ subject.finalize!
+ expect(subject.username).to be(nil)
+ end
+ end
+
+ describe "#password" do
+ it "defaults to nil" do
+ subject.finalize!
+ expect(subject.password).to be(nil)
+ end
+ end
+
+ describe "#passive" do
+ it "defaults to true" do
+ subject.finalize!
+ expect(subject.passive).to be(true)
+ end
+ end
+
+ describe "#secure" do
+ it "defaults to false" do
+ subject.finalize!
+ expect(subject.secure).to be(false)
+ end
+ end
+
+ describe "#destination" do
+ it "defaults to /" do
+ subject.finalize!
+ expect(subject.destination).to eq("/")
+ end
+ end
+
+ describe "#dir" do
+ it "defaults to ." do
+ subject.finalize!
+ expect(subject.dir).to eq(".")
+ end
+ end
+
+ describe "#merge" do
+ context "when includes are given" do
+ let(:one) { described_class.new }
+ let(:two) { described_class.new }
+
+ it "merges the result" do
+ one.includes = %w(a b c)
+ two.includes = %w(c d e)
+ result = one.merge(two)
+ expect(result.includes).to eq(%w(a b c d e))
+ end
+ end
+
+ context "when excludes are given" do
+ let(:one) { described_class.new }
+ let(:two) { described_class.new }
+
+ it "merges the result" do
+ one.excludes = %w(a b c)
+ two.excludes = %w(c d e)
+ result = one.merge(two)
+ expect(result.excludes).to eq(%w(a b c d e))
+ end
+ end
+ end
+
+ describe "#validate" do
+ before do
+ allow(machine).to receive(:env)
+ .and_return(double("env",
+ root_path: "",
+ ))
+
+ subject.host = "ftp.example.com"
+ subject.username = "sethvargo"
+ subject.password = "bacon"
+ subject.destination = "/"
+ subject.dir = "."
+ end
+
+ let(:result) { subject.validate(machine) }
+ let(:errors) { result["FTP push"] }
+
+ context "when the host is missing" do
+ it "returns an error" do
+ subject.host = ""
+ subject.finalize!
+ expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute",
+ attribute: "host",
+ ))
+ end
+ end
+
+ context "when the username is missing" do
+ it "returns an error" do
+ subject.username = ""
+ subject.finalize!
+ expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute",
+ attribute: "username",
+ ))
+ end
+ end
+
+ context "when the password is missing" do
+ it "does not return an error" do
+ subject.password = ""
+ subject.finalize!
+ expect(errors).to be_empty
+ end
+ end
+
+ context "when the destination is missing" do
+ it "returns an error" do
+ subject.destination = ""
+ subject.finalize!
+ expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute",
+ attribute: "destination",
+ ))
+ end
+ end
+
+ context "when the dir is missing" do
+ it "returns an error" do
+ subject.dir = ""
+ subject.finalize!
+ expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute",
+ attribute: "dir",
+ ))
+ end
+ end
+ end
+
+ describe "#include" do
+ it "adds the item to the list" do
+ subject.include("me")
+ expect(subject.includes).to include("me")
+ end
+ end
+
+ describe "#exclude" do
+ it "adds the item to the list" do
+ subject.exclude("not me")
+ expect(subject.excludes).to include("not me")
+ end
+ end
+end
diff --git a/test/unit/plugins/pushes/ftp/push_test.rb b/test/unit/plugins/pushes/ftp/push_test.rb
new file mode 100644
index 000000000..1f6773e24
--- /dev/null
+++ b/test/unit/plugins/pushes/ftp/push_test.rb
@@ -0,0 +1,297 @@
+require_relative "../../../base"
+require "fake_ftp"
+
+require Vagrant.source_root.join("plugins/pushes/ftp/push")
+
+describe VagrantPlugins::FTPPush::Push do
+ include_context "unit"
+
+ let(:env) { isolated_environment }
+ let(:config) do
+ double("config",
+ host: "127.0.0.1:51234",
+ username: "sethvargo",
+ password: "bacon",
+ passive: false,
+ secure: false,
+ destination: "/var/www/site",
+ )
+ end
+ let(:ui) do
+ double("ui",
+ info: nil,
+ )
+ end
+
+ subject { described_class.new(env, config) }
+
+ before do
+ allow(env).to receive(:root_path)
+ .and_return(File.expand_path("..", __FILE__))
+ allow(env).to receive(:ui)
+ .and_return(ui)
+ end
+
+ describe "#push" do
+ before(:all) do
+ @server = FakeFtp::Server.new(51234, 21213)
+ @server.start
+
+ @dir = Dir.mktmpdir
+
+ FileUtils.touch("#{@dir}/.hidden.rb")
+ FileUtils.touch("#{@dir}/application.rb")
+ FileUtils.touch("#{@dir}/config.rb")
+ FileUtils.touch("#{@dir}/Gemfile")
+ FileUtils.touch("#{@dir}/data.txt")
+ FileUtils.mkdir("#{@dir}/empty_folder")
+ end
+
+ after(:all) do
+ FileUtils.rm_rf(@dir)
+ @server.stop
+ end
+
+ let(:server) { @server }
+
+ before do
+ allow(config).to receive(:dir)
+ .and_return(@dir)
+
+ allow(config).to receive(:includes)
+ .and_return([])
+
+ allow(config).to receive(:excludes)
+ .and_return(%w(*.rb))
+ end
+
+
+ it "pushes the files to the server" do
+ subject.push
+ expect(server.files).to eq(%w(Gemfile data.txt))
+ end
+ end
+
+ describe "#connect" do
+ before do
+ allow_any_instance_of(VagrantPlugins::FTPPush::FTPAdapter)
+ .to receive(:connect)
+ .and_yield(:ftp)
+ allow_any_instance_of(VagrantPlugins::FTPPush::SFTPAdapter)
+ .to receive(:connect)
+ .and_yield(:sftp)
+ end
+
+ context "when secure is requested" do
+ before do
+ allow(config).to receive(:secure)
+ .and_return(true)
+ end
+
+ it "yields a new SFTPAdapter" do
+ expect { |b| subject.connect(&b) }.to yield_with_args(:sftp)
+ end
+ end
+
+ context "when secure is not requested" do
+ before do
+ allow(config).to receive(:secure)
+ .and_return(false)
+ end
+
+ it "yields a new FTPAdapter" do
+ expect { |b| subject.connect(&b) }.to yield_with_args(:ftp)
+ end
+ end
+ end
+
+ describe "#all_files" do
+ before(:all) do
+ @dir = Dir.mktmpdir
+
+ FileUtils.touch("#{@dir}/.hidden.rb")
+ FileUtils.touch("#{@dir}/application.rb")
+ FileUtils.touch("#{@dir}/config.rb")
+ FileUtils.touch("#{@dir}/Gemfile")
+ FileUtils.mkdir("#{@dir}/empty_folder")
+ FileUtils.mkdir("#{@dir}/folder")
+ FileUtils.mkdir("#{@dir}/folder/.git")
+ FileUtils.touch("#{@dir}/folder/.git/config")
+ FileUtils.touch("#{@dir}/folder/server.rb")
+ end
+
+ after(:all) do
+ FileUtils.rm_rf(@dir)
+ end
+
+ let(:files) do
+ subject.all_files.map do |file|
+ file.sub("#{@dir}/", "")
+ end
+ end
+
+ before do
+ allow(config).to receive(:dir)
+ .and_return(@dir)
+
+ allow(config).to receive(:includes)
+ .and_return(%w(not_a_file.rb still_not_a_file.rb))
+
+ allow(config).to receive(:excludes)
+ .and_return(%w(*.rb))
+ end
+
+ it "returns the list of real files + includes, without excludes" do
+ expect(files).to eq(%w(
+ Gemfile
+ folder/.git/config
+ ))
+ end
+ end
+
+ describe "includes_files" do
+ before(:all) do
+ @dir = Dir.mktmpdir
+
+ FileUtils.touch("#{@dir}/.hidden.rb")
+ FileUtils.touch("#{@dir}/application.rb")
+ FileUtils.touch("#{@dir}/config.rb")
+ FileUtils.touch("#{@dir}/Gemfile")
+ FileUtils.mkdir("#{@dir}/folder")
+ FileUtils.mkdir("#{@dir}/folder/.git")
+ FileUtils.touch("#{@dir}/folder/.git/config")
+ FileUtils.touch("#{@dir}/folder/server.rb")
+ end
+
+ after(:all) do
+ FileUtils.rm_rf(@dir)
+ end
+
+ let(:files) do
+ subject.includes_files.map do |file|
+ file.sub("#{@dir}/", "")
+ end
+ end
+
+ before do
+ allow(config).to receive(:dir)
+ .and_return(@dir)
+ end
+
+ def set_includes(value)
+ allow(config).to receive(:includes)
+ .and_return(value)
+ end
+
+ it "includes the file" do
+ set_includes(["Gemfile"])
+ expect(files).to eq(%w(
+ Gemfile
+ ))
+ end
+
+ it "includes the files that are subdirectories" do
+ set_includes(["folder"])
+ expect(files).to eq(%w(
+ folder
+ folder/.git
+ folder/.git/config
+ folder/server.rb
+ ))
+ end
+
+ it "includes files that match a pattern" do
+ set_includes(["*.rb"])
+ expect(files).to eq(%w(
+ .hidden.rb
+ application.rb
+ config.rb
+ ))
+ end
+ end
+
+ describe "#filter_excludes" do
+ let(:dir) { "/root/dir" }
+
+ let(:list) do
+ %W(
+ #{dir}/.hidden.rb
+ #{dir}/application.rb
+ #{dir}/config.rb
+ #{dir}/Gemfile
+ #{dir}/folder
+ #{dir}/folder/.git
+ #{dir}/folder/.git/config
+ #{dir}/folder/server.rb
+
+ /path/outside/you.rb
+ /path/outside/me.rb
+ /path/outside/folder/bacon.rb
+ )
+ end
+
+ before do
+ allow(config).to receive(:dir)
+ .and_return(dir)
+ end
+
+ it "excludes files" do
+ subject.filter_excludes!(list, %w(*.rb))
+
+ expect(list).to eq(%W(
+ #{dir}/Gemfile
+ #{dir}/folder
+ #{dir}/folder/.git
+ #{dir}/folder/.git/config
+ ))
+ end
+
+ it "excludes files in a directory" do
+ subject.filter_excludes!(list, %w(folder))
+
+ expect(list).to eq(%W(
+ #{dir}/.hidden.rb
+ #{dir}/application.rb
+ #{dir}/config.rb
+ #{dir}/Gemfile
+
+ /path/outside/you.rb
+ /path/outside/me.rb
+ /path/outside/folder/bacon.rb
+ ))
+ end
+
+ it "excludes specific files in a directory" do
+ subject.filter_excludes!(list, %w(/path/outside/folder/*.rb))
+
+ expect(list).to eq(%W(
+ #{dir}/.hidden.rb
+ #{dir}/application.rb
+ #{dir}/config.rb
+ #{dir}/Gemfile
+ #{dir}/folder
+ #{dir}/folder/.git
+ #{dir}/folder/.git/config
+ #{dir}/folder/server.rb
+
+ /path/outside/you.rb
+ /path/outside/me.rb
+ ))
+ end
+
+ it "excludes files outside the #dir" do
+ subject.filter_excludes!(list, %w(/path/outside))
+
+ expect(list).to eq(%W(
+ #{dir}/.hidden.rb
+ #{dir}/application.rb
+ #{dir}/config.rb
+ #{dir}/Gemfile
+ #{dir}/folder
+ #{dir}/folder/.git
+ #{dir}/folder/.git/config
+ #{dir}/folder/server.rb
+ ))
+ end
+ end
+end
diff --git a/test/unit/plugins/pushes/heroku/config_test.rb b/test/unit/plugins/pushes/heroku/config_test.rb
new file mode 100644
index 000000000..e451ad152
--- /dev/null
+++ b/test/unit/plugins/pushes/heroku/config_test.rb
@@ -0,0 +1,99 @@
+require_relative "../../../base"
+
+require Vagrant.source_root.join("plugins/pushes/heroku/config")
+
+describe VagrantPlugins::HerokuPush::Config do
+ include_context "unit"
+
+ before(:all) do
+ I18n.load_path << Vagrant.source_root.join("plugins/pushes/heroku/locales/en.yml")
+ I18n.reload!
+ end
+
+ subject { described_class.new }
+
+ let(:machine) { double("machine") }
+
+ describe "#app" do
+ it "defaults to nil" do
+ subject.finalize!
+ expect(subject.app).to be(nil)
+ end
+ end
+
+ describe "#dir" do
+ it "defaults to ." do
+ subject.finalize!
+ expect(subject.dir).to eq(".")
+ end
+ end
+
+ describe "#git_bin" do
+ it "defaults to git" do
+ subject.finalize!
+ expect(subject.git_bin).to eq("git")
+ end
+ end
+
+ describe "#remote" do
+ it "defaults to git" do
+ subject.finalize!
+ expect(subject.remote).to eq("heroku")
+ end
+ end
+
+ describe "#validate" do
+ before do
+ allow(machine).to receive(:env)
+ .and_return(double("env",
+ root_path: "",
+ ))
+
+ subject.app = "bacon"
+ subject.dir = "."
+ subject.git_bin = "git"
+ subject.remote = "heroku"
+ end
+
+ let(:result) { subject.validate(machine) }
+ let(:errors) { result["Heroku push"] }
+
+ context "when the app is missing" do
+ it "does not return an error" do
+ subject.app = ""
+ subject.finalize!
+ expect(errors).to be_empty
+ end
+ end
+
+ context "when the git_bin is missing" do
+ it "returns an error" do
+ subject.git_bin = ""
+ subject.finalize!
+ expect(errors).to include(I18n.t("heroku_push.errors.missing_attribute",
+ attribute: "git_bin",
+ ))
+ end
+ end
+
+ context "when the remote is missing" do
+ it "returns an error" do
+ subject.remote = ""
+ subject.finalize!
+ expect(errors).to include(I18n.t("heroku_push.errors.missing_attribute",
+ attribute: "remote",
+ ))
+ end
+ end
+
+ context "when the dir is missing" do
+ it "returns an error" do
+ subject.dir = ""
+ subject.finalize!
+ expect(errors).to include(I18n.t("heroku_push.errors.missing_attribute",
+ attribute: "dir",
+ ))
+ end
+ end
+ end
+end
diff --git a/test/unit/plugins/pushes/heroku/push_test.rb b/test/unit/plugins/pushes/heroku/push_test.rb
new file mode 100644
index 000000000..c0337e41f
--- /dev/null
+++ b/test/unit/plugins/pushes/heroku/push_test.rb
@@ -0,0 +1,324 @@
+require_relative "../../../base"
+
+require Vagrant.source_root.join("plugins/pushes/heroku/push")
+
+describe VagrantPlugins::HerokuPush::Push do
+ include_context "unit"
+
+ before(:all) do
+ I18n.load_path << Vagrant.source_root.join("plugins/pushes/heroku/locales/en.yml")
+ I18n.reload!
+ end
+
+ let(:env) { isolated_environment }
+ let(:config) do
+ double("config",
+ app: "bacon",
+ dir: "lib",
+ git_bin: "git",
+ remote: "heroku",
+ )
+ end
+
+ subject { described_class.new(env, config) }
+
+ describe "#push" do
+ let(:branch) { "master" }
+
+ let(:root_path) { "/handy/dandy" }
+ let(:dir) { "#{root_path}/#{config.dir}" }
+
+ before do
+ allow(subject).to receive(:git_branch)
+ .and_return(branch)
+ allow(subject).to receive(:verify_git_bin!)
+ allow(subject).to receive(:verify_git_repo!)
+ allow(subject).to receive(:has_git_remote?)
+ allow(subject).to receive(:add_heroku_git_remote)
+ allow(subject).to receive(:git_push_heroku)
+ allow(subject).to receive(:execute!)
+
+ allow(env).to receive(:root_path)
+ .and_return(root_path)
+ end
+
+ it "verifies the git bin is present" do
+ expect(subject).to receive(:verify_git_bin!)
+ .with(config.git_bin)
+ subject.push
+ end
+
+ it "verifies the directory is a git repo" do
+ expect(subject).to receive(:verify_git_repo!)
+ .with(dir)
+ subject.push
+ end
+
+ context "when the heroku remote exists" do
+ before do
+ allow(subject).to receive(:has_git_remote?)
+ .and_return(true)
+ end
+
+ it "does not add the heroku remote" do
+ expect(subject).to_not receive(:add_heroku_git_remote)
+ subject.push
+ end
+ end
+
+ context "when the heroku remote does not exist" do
+ before do
+ allow(subject).to receive(:has_git_remote?)
+ .and_return(false)
+ end
+
+ it "adds the heroku remote" do
+ expect(subject).to receive(:add_heroku_git_remote)
+ .with(config.remote, config.app, dir)
+ subject.push
+ end
+ end
+
+ it "pushes to heroku" do
+ expect(subject).to receive(:git_push_heroku)
+ .with(config.remote, branch, dir)
+ subject.push
+ end
+ end
+
+ describe "#verify_git_bin!" do
+ context "when git does not exist" do
+ before do
+ allow(Vagrant::Util::Which).to receive(:which)
+ .with("git")
+ .and_return(nil)
+ end
+
+ it "raises an exception" do
+ expect {
+ subject.verify_git_bin!("git")
+ } .to raise_error(VagrantPlugins::HerokuPush::Errors::GitNotFound) { |error|
+ expect(error.message).to eq(I18n.t("heroku_push.errors.git_not_found",
+ bin: "git",
+ ))
+ }
+ end
+ end
+
+ context "when git exists" do
+ before do
+ allow(Vagrant::Util::Which).to receive(:which)
+ .with("git")
+ .and_return("git")
+ end
+
+ it "does not raise an exception" do
+ expect { subject.verify_git_bin!("git") }.to_not raise_error
+ end
+ end
+ end
+
+ describe "#verify_git_repo!" do
+ context "when the path is a git repo" do
+ before do
+ allow(File).to receive(:directory?)
+ .with("/repo/path/.git")
+ .and_return(false)
+ end
+
+ it "raises an exception" do
+ expect {
+ subject.verify_git_repo!("/repo/path")
+ } .to raise_error(VagrantPlugins::HerokuPush::Errors::NotAGitRepo) { |error|
+ expect(error.message).to eq(I18n.t("heroku_push.errors.not_a_git_repo",
+ path: "/repo/path",
+ ))
+ }
+ end
+ end
+
+ context "when the path is not a git repo" do
+ before do
+ allow(File).to receive(:directory?)
+ .with("/repo/path/.git")
+ .and_return(true)
+ end
+
+ it "does not raise an exception" do
+ expect { subject.verify_git_repo!("/repo/path") }.to_not raise_error
+ end
+ end
+ end
+
+ describe "#git_push_heroku" do
+ let(:dir) { "." }
+
+ before { allow(subject).to receive(:execute!) }
+
+ it "executes the proper command" do
+ expect(subject).to receive(:execute!)
+ .with("git",
+ "--git-dir", "#{dir}/.git",
+ "--work-tree", dir,
+ "push", "bacon", "hamlet:master",
+ )
+ subject.git_push_heroku("bacon", "hamlet", dir)
+ end
+ end
+
+ describe "#has_git_remote?" do
+ let(:dir) { "." }
+
+ let(:process) do
+ double("process",
+ stdout: "origin\r\nbacon\nhello"
+ )
+ end
+
+ before do
+ allow(subject).to receive(:execute!)
+ .and_return(process)
+ end
+
+ it "executes the proper command" do
+ expect(subject).to receive(:execute!)
+ .with("git",
+ "--git-dir", "#{dir}/.git",
+ "--work-tree", dir,
+ "remote",
+ )
+ subject.has_git_remote?("bacon", dir)
+ end
+
+ it "returns true when the remote exists" do
+ expect(subject.has_git_remote?("origin", dir)).to be(true)
+ expect(subject.has_git_remote?("bacon", dir)).to be(true)
+ expect(subject.has_git_remote?("hello", dir)).to be(true)
+ end
+
+ it "returns false when the remote does not exist" do
+ expect(subject.has_git_remote?("nope", dir)).to be(false)
+ end
+ end
+
+ describe "#add_heroku_git_remote" do
+ let(:dir) { "." }
+
+ before do
+ allow(subject).to receive(:execute!)
+ allow(subject).to receive(:heroku_git_url)
+ .with("app")
+ .and_return("HEROKU_URL")
+ end
+
+ it "executes the proper command" do
+ expect(subject).to receive(:execute!)
+ .with("git",
+ "--git-dir", "#{dir}/.git",
+ "--work-tree", dir,
+ "remote", "add", "bacon", "HEROKU_URL",
+ )
+ subject.add_heroku_git_remote("bacon", "app", dir)
+ end
+ end
+
+ describe "#interpret_app" do
+ it "returns the basename of the directory" do
+ expect(subject.interpret_app("/foo/bar/blitz")).to eq("blitz")
+ end
+ end
+
+ describe "#heroku_git_url" do
+ it "returns the proper string" do
+ expect(subject.heroku_git_url("bacon"))
+ .to eq("git@heroku.com:bacon.git")
+ end
+ end
+
+ describe "#git_dir" do
+ it "returns the .git directory for the path" do
+ expect(subject.git_dir("/path")).to eq("/path/.git")
+ end
+ end
+
+ describe "#git_branch" do
+ let(:stdout) { "" }
+ let(:process) { double("process", stdout: stdout) }
+
+ before do
+ allow(subject).to receive(:execute!)
+ .and_return(process)
+ end
+
+ let(:branch) { subject.git_branch("/path") }
+
+ context "when the branch is prefixed with a star" do
+ let(:stdout) { "*bacon" }
+
+ it "returns the correct name" do
+ expect(branch).to eq("bacon")
+ end
+ end
+
+ context "when the branch is prefixed with a star space" do
+ let(:stdout) { "* bacon" }
+
+ it "returns the correct name" do
+ expect(branch).to eq("bacon")
+ end
+ end
+
+ context "when the branch is not prefixed" do
+ let(:stdout) { "bacon" }
+
+ it "returns the correct name" do
+ expect(branch).to eq("bacon")
+ end
+ end
+ end
+
+ describe "#execute!" do
+ let(:exit_code) { 0 }
+ let(:stdout) { "This is the output" }
+ let(:stderr) { "This is the errput" }
+
+ let(:process) do
+ double("process",
+ exit_code: exit_code,
+ stdout: stdout,
+ stderr: stderr,
+ )
+ end
+
+ before do
+ allow(Vagrant::Util::Subprocess).to receive(:execute)
+ .and_return(process)
+ end
+
+ it "creates a subprocess" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute)
+ expect { subject.execute! }.to_not raise_error
+ end
+
+ it "returns the resulting process" do
+ expect(subject.execute!).to be(process)
+ end
+
+ context "when the exit code is non-zero" do
+ let(:exit_code) { 1 }
+
+ it "raises an exception" do
+ klass = VagrantPlugins::HerokuPush::Errors::CommandFailed
+ cmd = ["foo", "bar"]
+
+ expect { subject.execute!(*cmd) }.to raise_error(klass) { |error|
+ expect(error.message).to eq(I18n.t("heroku_push.errors.command_failed",
+ cmd: cmd.join(" "),
+ stdout: stdout,
+ stderr: stderr,
+ ))
+ }
+ end
+ end
+ end
+end
diff --git a/test/unit/plugins/pushes/local-exec/config_test.rb b/test/unit/plugins/pushes/local-exec/config_test.rb
new file mode 100644
index 000000000..045872d2f
--- /dev/null
+++ b/test/unit/plugins/pushes/local-exec/config_test.rb
@@ -0,0 +1,85 @@
+require_relative "../../../base"
+
+require Vagrant.source_root.join("plugins/pushes/local-exec/config")
+
+describe VagrantPlugins::LocalExecPush::Config do
+ include_context "unit"
+
+ before(:all) do
+ I18n.load_path << Vagrant.source_root.join("plugins/pushes/local-exec/locales/en.yml")
+ I18n.reload!
+ end
+
+ let(:machine) { double("machine") }
+
+ describe "#script" do
+ it "defaults to nil" do
+ subject.finalize!
+ expect(subject.script).to be(nil)
+ end
+ end
+
+ describe "#inline" do
+ it "defaults to nil" do
+ subject.finalize!
+ expect(subject.inline).to be(nil)
+ end
+ end
+
+ describe "#validate" do
+ before do
+ allow(machine).to receive(:env)
+ .and_return(double("env",
+ root_path: "",
+ ))
+ subject.finalize!
+ end
+
+ let(:result) { subject.validate(machine) }
+ let(:errors) { result["Local Exec push"] }
+
+ context "when script is present" do
+ before { subject.script = "foo.sh" }
+
+ context "when inline is present" do
+ before { subject.inline = "echo" }
+
+ it "returns an error" do
+ expect(errors).to include(
+ I18n.t("local_exec_push.errors.cannot_specify_script_and_inline")
+ )
+ end
+ end
+
+ context "when inline is not present" do
+ before { subject.inline = "" }
+
+ it "does not return an error" do
+ expect(errors).to be_empty
+ end
+ end
+ end
+
+ context "when script is not present" do
+ before { subject.script = "" }
+
+ context "when inline is present" do
+ before { subject.inline = "echo" }
+
+ it "does not return an error" do
+ expect(errors).to be_empty
+ end
+ end
+
+ context "when inline is not present" do
+ before { subject.inline = "" }
+
+ it "returns an error" do
+ expect(errors).to include(I18n.t("local_exec_push.errors.missing_attribute",
+ attribute: "script",
+ ))
+ end
+ end
+ end
+ end
+end
diff --git a/test/unit/plugins/pushes/local-exec/push_test.rb b/test/unit/plugins/pushes/local-exec/push_test.rb
new file mode 100644
index 000000000..1efdfa3b1
--- /dev/null
+++ b/test/unit/plugins/pushes/local-exec/push_test.rb
@@ -0,0 +1,101 @@
+require_relative "../../../base"
+
+require Vagrant.source_root.join("plugins/pushes/local-exec/push")
+
+describe VagrantPlugins::LocalExecPush::Push do
+ include_context "unit"
+
+ before(:all) do
+ I18n.load_path << Vagrant.source_root.join("plugins/pushes/local-exec/locales/en.yml")
+ I18n.reload!
+ end
+
+ let(:env) { isolated_environment }
+ let(:config) do
+ double("config",
+ script: nil,
+ inline: nil,
+ )
+ end
+
+ subject { described_class.new(env, config) }
+
+ before do
+ allow(env).to receive(:root_path)
+ .and_return(File.expand_path("..", __FILE__))
+ end
+
+ describe "#push" do
+ before do
+ allow(subject).to receive(:execute_inline!)
+ allow(subject).to receive(:execute_script!)
+ allow(subject).to receive(:execute!)
+ end
+
+ context "when inline is given" do
+ before { allow(config).to receive(:inline).and_return("echo") }
+
+ it "executes the inline script" do
+ expect(subject).to receive(:execute_inline!)
+ .with(config.inline)
+ subject.push
+ end
+ end
+
+ context "when script is given" do
+ before { allow(config).to receive(:script).and_return("foo.sh") }
+
+ it "executes the script" do
+ expect(subject).to receive(:execute_script!)
+ .with(config.script)
+ subject.push
+ end
+ end
+ end
+
+ describe "#execute_inline!" do
+ before { allow(subject).to receive(:execute_script!) }
+
+ it "writes the script to a tempfile" do
+ expect(Tempfile).to receive(:new).and_call_original
+ subject.execute_inline!("echo")
+ end
+
+ it "executes the script" do
+ expect(subject).to receive(:execute_script!)
+ subject.execute_inline!("echo")
+ end
+ end
+
+ describe "#execute_script!" do
+ before do
+ allow(subject).to receive(:execute!)
+ allow(FileUtils).to receive(:chmod)
+ end
+
+ it "expands the path relative to the machine root" do
+ expect(subject).to receive(:execute!)
+ .with(File.expand_path("foo.sh", env.root_path))
+ subject.execute_script!("./foo.sh")
+ end
+
+ it "makes the file executable" do
+ expect(FileUtils).to receive(:chmod)
+ .with("+x", File.expand_path("foo.sh", env.root_path))
+ subject.execute_script!("./foo.sh")
+ end
+
+ it "calls execute!" do
+ expect(subject).to receive(:execute!)
+ .with(File.expand_path("foo.sh", env.root_path))
+ subject.execute_script!("./foo.sh")
+ end
+ end
+
+ describe "#execute!" do
+ it "safe execs" do
+ expect(Vagrant::Util::SafeExec).to receive(:exec)
+ expect { subject.execute! }.to_not raise_error
+ end
+ end
+end
diff --git a/test/unit/plugins/pushes/noop/config_test.rb b/test/unit/plugins/pushes/noop/config_test.rb
new file mode 100644
index 000000000..05be6c3f8
--- /dev/null
+++ b/test/unit/plugins/pushes/noop/config_test.rb
@@ -0,0 +1,14 @@
+require_relative "../../../base"
+
+require Vagrant.source_root.join("plugins/pushes/noop/config")
+
+describe VagrantPlugins::NoopDeploy::Config do
+ include_context "unit"
+
+ subject { described_class.new }
+
+ let(:machine) { double("machine") }
+
+ describe "#validate" do
+ end
+end
diff --git a/test/unit/support/shared/base_context.rb b/test/unit/support/shared/base_context.rb
index 35c26788a..0f823948d 100644
--- a/test/unit/support/shared/base_context.rb
+++ b/test/unit/support/shared/base_context.rb
@@ -83,6 +83,18 @@ shared_context "unit" do
return Pathname.new(d)
end
+ # Stub the given environment in ENV, without actually touching ENV. Keys and
+ # values are converted to strings because that's how the real ENV works.
+ def stub_env(hash)
+ allow(ENV).to receive(:[]).and_call_original
+
+ hash.each do |key, value|
+ allow(ENV).to receive(:[])
+ .with(key.to_s)
+ .and_return(value.to_s)
+ end
+ end
+
# This helper provides temporary environmental variable changes.
def with_temp_env(environment)
# Build up the new environment, preserving the old values so we
diff --git a/test/unit/vagrant/environment_test.rb b/test/unit/vagrant/environment_test.rb
index 2bb52e939..02effd7db 100644
--- a/test/unit/vagrant/environment_test.rb
+++ b/test/unit/vagrant/environment_test.rb
@@ -968,6 +968,76 @@ VF
end
end
+ describe "#pushes" do
+ it "returns the pushes from the Vagrantfile config" do
+ environment = isolated_environment do |env|
+ env.vagrantfile(<<-VF.gsub(/^ {10}/, ''))
+ Vagrant.configure("2") do |config|
+ config.push.define "noop"
+ end
+ VF
+ end
+
+ env = environment.create_vagrant_env
+ expect(env.pushes).to eq([:noop])
+ end
+ end
+
+ describe "#push" do
+ let(:push_class) do
+ Class.new(Vagrant.plugin("2", :push)) do
+ def self.pushed?
+ !!class_variable_get(:@@pushed)
+ end
+
+ def push
+ !!self.class.class_variable_set(:@@pushed, true)
+ end
+ end
+ end
+
+ it "raises an exception when the push does not exist" do
+ expect { instance.push("lolwatbacon") }
+ .to raise_error(Vagrant::Errors::PushStrategyNotDefined)
+ end
+
+ it "raises an exception if the strategy does not exist" do
+ environment = isolated_environment do |env|
+ env.vagrantfile(<<-VF.gsub(/^ {10}/, ''))
+ Vagrant.configure("2") do |config|
+ config.push.define "lolwatbacon"
+ end
+ VF
+ end
+
+ env = environment.create_vagrant_env
+ expect { env.push("lolwatbacon") }
+ .to raise_error(Vagrant::Errors::PushStrategyNotLoaded)
+ end
+
+ it "executes the push action" do
+ register_plugin("2") do |plugin|
+ plugin.name "foo"
+
+ plugin.push(:foo) do
+ push_class
+ end
+ end
+
+ environment = isolated_environment do |env|
+ env.vagrantfile(<<-VF.gsub(/^ {10}/, ''))
+ Vagrant.configure("2") do |config|
+ config.push.define "foo"
+ end
+ VF
+ end
+
+ env = environment.create_vagrant_env
+ env.push("foo")
+ expect(push_class.pushed?).to be_true
+ end
+ end
+
describe "#hook" do
it "should call the action runner with the proper hook" do
hook_name = :foo
diff --git a/test/unit/vagrant/machine_test.rb b/test/unit/vagrant/machine_test.rb
index 128df3d6f..5da1be84f 100644
--- a/test/unit/vagrant/machine_test.rb
+++ b/test/unit/vagrant/machine_test.rb
@@ -263,6 +263,17 @@ describe Vagrant::Machine do
expect(foo).to eq(:bar)
end
+ it "should pass any extra options to the environment as strings" do
+ action_name = :up
+ foo = nil
+ callable = lambda { |env| foo = env["foo"] }
+
+ allow(provider).to receive(:action).with(action_name).and_return(callable)
+ instance.action(:up, "foo" => :bar)
+
+ expect(foo).to eq(:bar)
+ end
+
it "should return the environment as a result" do
action_name = :up
callable = lambda { |env| env[:result] = "FOO" }
diff --git a/test/unit/vagrant/plugin/v2/manager_test.rb b/test/unit/vagrant/plugin/v2/manager_test.rb
index 45ebd5a0c..3ddbf00e9 100644
--- a/test/unit/vagrant/plugin/v2/manager_test.rb
+++ b/test/unit/vagrant/plugin/v2/manager_test.rb
@@ -189,6 +189,42 @@ describe Vagrant::Plugin::V2::Manager do
expect(instance.provider_configs[:bar]).to eq("bar")
end
+ it "should enumerate registered push classes" do
+ pA = plugin do |p|
+ p.push("foo") { "bar" }
+ end
+
+ pB = plugin do |p|
+ p.push("bar", foo: "bar") { "baz" }
+ end
+
+ instance.register(pA)
+ instance.register(pB)
+
+ expect(instance.pushes.to_hash.length).to eq(2)
+ expect(instance.pushes[:foo]).to eq(["bar", nil])
+ expect(instance.pushes[:bar]).to eq(["baz", { foo: "bar" }])
+ end
+
+ it "provides the collection of registered push configs" do
+ pA = plugin do |p|
+ p.config("foo", :push) { "foo" }
+ end
+
+ pB = plugin do |p|
+ p.config("bar", :push) { "bar" }
+ p.config("baz") { "baz" }
+ end
+
+ instance.register(pA)
+ instance.register(pB)
+
+ expect(instance.push_configs.to_hash.length).to eq(2)
+ expect(instance.push_configs[:foo]).to eq("foo")
+ expect(instance.push_configs[:bar]).to eq("bar")
+ end
+
+
it "should enumerate all registered synced folder implementations" do
pA = plugin do |p|
p.synced_folder("foo") { "bar" }
diff --git a/test/unit/vagrant/plugin/v2/plugin_test.rb b/test/unit/vagrant/plugin/v2/plugin_test.rb
index 944d38f9f..76ad40993 100644
--- a/test/unit/vagrant/plugin/v2/plugin_test.rb
+++ b/test/unit/vagrant/plugin/v2/plugin_test.rb
@@ -322,6 +322,42 @@ describe Vagrant::Plugin::V2::Plugin do
end
end
+ describe "pushes" do
+ it "should register implementations" do
+ plugin = Class.new(described_class) do
+ push("foo") { "bar" }
+ end
+
+ expect(plugin.components.pushes[:foo]).to eq(["bar", nil])
+ end
+
+ it "should be able to specify priorities" do
+ plugin = Class.new(described_class) do
+ push("foo", bar: 1) { "bar" }
+ end
+
+ expect(plugin.components.pushes[:foo]).to eq(["bar", bar: 1])
+ end
+
+ it "should lazily register implementations" do
+ # Below would raise an error if the value of the config class was
+ # evaluated immediately. By asserting that this does not raise an
+ # error, we verify that the value is actually lazily loaded
+ plugin = nil
+ expect {
+ plugin = Class.new(described_class) do
+ push("foo") { raise StandardError, "FAIL!" }
+ end
+ }.to_not raise_error
+
+ # Now verify when we actually get the configuration key that
+ # a proper error is raised.
+ expect {
+ plugin.components.pushes[:foo]
+ }.to raise_error(StandardError)
+ end
+ end
+
describe "synced folders" do
it "should register implementations" do
plugin = Class.new(described_class) do
diff --git a/test/unit/vagrant/registry_test.rb b/test/unit/vagrant/registry_test.rb
index d12f46ffe..f177a6a1c 100644
--- a/test/unit/vagrant/registry_test.rb
+++ b/test/unit/vagrant/registry_test.rb
@@ -90,6 +90,39 @@ describe Vagrant::Registry do
expect(result["bar"]).to eq("barvalue")
end
+ describe "#length" do
+ it "should return 0 when the registry is empty" do
+ expect(instance.length).to eq(0)
+ end
+
+ it "should return the number of items in the registry" do
+ instance.register("foo") { }
+ instance.register("bar") { }
+
+ expect(instance.length).to eq(2)
+ end
+ end
+
+ describe "#size" do
+ it "should be an alias to #length" do
+ size = described_class.instance_method(:size)
+ length = described_class.instance_method(:length)
+
+ expect(size).to eq(length)
+ end
+ end
+
+ describe "#empty" do
+ it "should return true when the registry is empty" do
+ expect(instance.empty?).to be(true)
+ end
+
+ it "should return false when there is at least one element" do
+ instance.register("foo") { }
+ expect(instance.empty?).to be(false)
+ end
+ end
+
describe "merging" do
it "should merge in another registry" do
one = described_class.new
diff --git a/vagrant.gemspec b/vagrant.gemspec
index 1981493ff..d2b939852 100644
--- a/vagrant.gemspec
+++ b/vagrant.gemspec
@@ -19,12 +19,14 @@ Gem::Specification.new do |s|
s.add_dependency "childprocess", "~> 0.5.0"
s.add_dependency "erubis", "~> 2.7.0"
s.add_dependency "i18n", "~> 0.6.0"
- s.add_dependency "listen", "~> 2.7.11"
+ s.add_dependency "listen", "~> 2.8.0"
s.add_dependency "hashicorp-checkpoint", "~> 0.1.1"
s.add_dependency "log4r", "~> 1.1.9", "< 1.1.11"
s.add_dependency "net-ssh", ">= 2.6.6", "< 2.10.0"
+ s.add_dependency "net-sftp", "~> 2.1"
s.add_dependency "net-scp", "~> 1.1.0"
s.add_dependency "rb-kqueue", "~> 0.2.0"
+ s.add_dependency "rest-client", ">= 1.6.0", "< 2.0"
s.add_dependency "wdm", "~> 0.1.0"
s.add_dependency "winrm", "~> 1.1.3"
@@ -33,6 +35,8 @@ Gem::Specification.new do |s|
s.add_development_dependency "rake"
s.add_development_dependency "rspec", "~> 2.14.0"
+ s.add_development_dependency "webmock", "~> 1.20"
+ s.add_development_dependency "fake_ftp", "~> 0.1"
# The following block of code determines the files that should be included
# in the gem. It does this by reading all the files in the directory where
diff --git a/version.txt b/version.txt
index c3da48456..648dc5700 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-1.7.0.dev
+1.7.2.dev
diff --git a/website/docs/source/layouts/layout.erb b/website/docs/source/layouts/layout.erb
index 688b7507d..310d7b0a8 100644
--- a/website/docs/source/layouts/layout.erb
+++ b/website/docs/source/layouts/layout.erb
@@ -173,6 +173,7 @@
>Ansible
>CFEngine
>Chef Solo
+ >Chef Zero
>Chef Client
>Chef Apply
>Docker
@@ -288,6 +289,17 @@
<% end %>
+ >Push
+
+ <% if sidebar_section == "push" %>
+
+ <% end %>
+
>Other
<% if sidebar_section == "other" %>
diff --git a/website/docs/source/stylesheets/_base.less b/website/docs/source/stylesheets/_base.less
index d78333d04..68790ad40 100644
--- a/website/docs/source/stylesheets/_base.less
+++ b/website/docs/source/stylesheets/_base.less
@@ -1,11 +1,11 @@
html {
- font-size: 100%;
- -webkit-text-size-adjust: 100%;
- -ms-text-size-adjust: 100%;
- text-rendering: optimizeLegibility;
- -webkit-tap-highlight-color: transparent;
-}
+ font-size: 100%;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ text-rendering: optimizeLegibility;
+ -webkit-tap-highlight-color: transparent;
+}
body {
font-family: @sans-serif-stack;
font-size: @base-font-size;
@@ -14,17 +14,18 @@ body {
background-color: @white;
letter-spacing: 2px;
.museo-sans-regular;
-}
+}
.wrapper {
-margin-top: 80px;
-}
+ margin-top: 80px;
+}
.container {
-z-index: 999; //keep content on top
-position: relative;
-}
+ z-index: 999;
+ //keep content on top
+ position: relative;
+}
h1,
h2,
h3,
@@ -36,272 +37,298 @@ h6 {
color: inherit;
text-rendering: optimizelegibility;
.museo-sans-bold;
+
}
+h1 {
+ @font-size: 70px;
+ font-size: @font-size;
+ line-height: 80px;
+ letter-spacing: 3px;
- h1 {
- @font-size: 70px;
- font-size: @font-size;
- line-height: 80px;
- letter-spacing: 3px;
+ span {
+ font-size: @headline-span-size;
+ display: block;
- span {
- font-size: @headline-span-size;
- display: block;
- }
+ }
+ &.all-caps {
+ text-transform: uppercase;
+ text-align: center;
+ font-size: 40px;
+ }
- &.all-caps {
- text-transform: uppercase;
- text-align: center;
- font-size: 40px;
- }
- }
-
- h2 {
- @font-size: 30px;
- font-size: @font-size;
- line-height: 35px;
- }
-
- h3 {
- @font-size: 30px;
- font-size: @font-size;
- line-height: @font-size;
- }
-
- h4 {
- @font-size: 24px;
- font-size: @font-size;
- line-height: @font-size;
- }
-
- h5 {
- @font-size: 20px;
- font-size: @font-size;
- line-height: @font-size;
- }
-
- h6 {
- @font-size: 12px;
- font-size: @font-size;
- line-height: @font-size;
- }
-
-p, td {
- letter-spacing: normal;
- line-height: 32px;
-
- a {
- color: @docs-blue;
- text-decoration: none;
- border-bottom: 1px solid @docs-blue;
-
- &:hover {
- text-decoration: none;
- color: darken(@blue, 10%);
- border-bottom: 1px solid darken(@blue, 10%);
- }
- }
}
+h2 {
+ @font-size: 30px;
+ font-size: @font-size;
+ line-height: 35px;
+}
+h3 {
+ @font-size: 30px;
+ font-size: @font-size;
+ line-height: @font-size;
+
+}
+h4 {
+ @font-size: 24px;
+ font-size: @font-size;
+ line-height: @font-size;
+
+}
+h5 {
+ @font-size: 20px;
+ font-size: @font-size;
+ line-height: @font-size;
+
+}
+h6 {
+ @font-size: 12px;
+ font-size: @font-size;
+ line-height: @font-size;
+
+}
+p,
+td {
+ letter-spacing: normal;
+ line-height: 32px;
+
+ a {
+ color: @docs-blue;
+ text-decoration: none;
+ border-bottom: 1px solid @docs-blue;
+
+ &:hover {
+ text-decoration: none;
+ color: darken(@blue, 10%);
+ border-bottom: 1px solid darken(@blue, 10%);
+ }
+ }
+
+}
a {
- color: inherit;
- text-decoration: none;
+ color: inherit;
+ text-decoration: none;
+ &:hover {
+ text-decoration: none;
+ color: @purple;
+ .animate-text-color;
- &:hover {
- text-decoration: none;
- color: @purple;
- .animate-text-color;
- }
+ }
+ &:active {
+ color: @blue;
- &:active {
- color: @blue;
- }
+ }
+ &:visited {
- &:visited {
+ }
- }
}
-
ul {
-}
+}
li {
-line-height: @base-line-height;
-}
+ line-height: @base-line-height;
+}
blockquote {
- border: none;
+ border: none;
margin: 60px;
- p { // blockquote p
- font-size: @base-font-size * 2;
- line-height: @base-line-height * 2;
- font-style: italic;
+ p {
+ // blockquote p
+ font-size: @base-font-size * 2;
+ line-height: @base-line-height * 2;
+ font-style: italic;
}
-}
+}
strong {
-.museo-sans-bold;
-}
+ .museo-sans-bold;
+}
em {
-.museo-sans-regular-italic;
-}
+ .museo-sans-regular-italic;
+}
br {
-display:block;
-line-height: (@baseline * 2);
-}
+ display: block;
+ line-height: (@baseline * 2);
+}
pre,
code {
-font-family: @mono-stack;
-}
+ font-family: @mono-stack;
+}
code {
-font-size: inherit;
-}
+ font-size: inherit;
+}
pre {
-border: none;
-font-size: @base-font-size;
-background: @black;
-color: @white;
-padding: 20px;
-line-height: @base-line-height;
-margin-top: 20px;
-margin-bottom: 20px;
+ border: none;
+ font-size: @base-font-size;
+ background: @black;
+ color: @white;
+ padding: 20px;
+ line-height: @base-line-height;
+ margin-top: 20px;
+ margin-bottom: 20px;
+
+ span {
+ color: @code-highlight-text;
+ }
- span {
- color: @code-highlight-text;
- }
}
-
hr {
-}
+}
.vr {
-width: 2px;
-height: 100%;
-}
+ width: 2px;
+ height: 100%;
+}
form {
-}
+}
input {
-letter-spacing: 3px;
+ letter-spacing: 3px;
- &:focus {
- outline: none;
- }
+ &:focus {
+ outline: none;
+ }
}
+::-webkit-input-placeholder {
- ::-webkit-input-placeholder {
- overflow: visible;
- padding-top: 3px;
- color: @light-gray-text;
- }
-
- input:-moz-placeholder {
- overflow: visible;
- padding-top: 3px;
- color: @light-gray-text;
- }
+ overflow: visible;
+ padding-top: 3px;
+ color: @light-gray-text;
+}
+input:-moz-placeholder {
+ overflow: visible;
+ padding-top: 3px;
+ color: @light-gray-text;
+}
/* type and styles */
.meta,
.legal,
.date {
-color: @medium-gray-text;
-line-height: @base-line-height;
-.museo-sans-regular;
+ color: @medium-gray-text;
+ line-height: @base-line-height;
+ .museo-sans-regular;
+
}
+.date {
+ text-transform: uppercase;
- .date {
- text-transform: uppercase;
- }
-
+}
.button {
-color: @white;
-text-align: center;
-background-color: @primary-button-color;
-display: block;
-padding: 15px 0;
-margin-top: 20px !important;
-text-transform: uppercase;
-font-size: 25px;
-letter-spacing: 5px;
-.museo-sans-light;
-.rounded;
-.hover;
+ color: @white;
+ text-align: center;
+ background-color: @primary-button-color;
+ display: block;
+ padding: 15px 0;
+ margin-top: 20px !important;
+ text-transform: uppercase;
+ font-size: 25px;
+ letter-spacing: 5px;
+ .museo-sans-light;
+ .rounded;
+ .hover;
- &.inline-button {
- background-color: @vagrant-blue;
- padding: 5px 20px;
- color: @white !important;
- font-size: 15px;
- letter-spacing: 1px;
- .rounded;
+ &.inline-button {
+ background-color: @vagrant-blue;
+ padding: 5px 20px;
+ color: @white !important;
+ font-size: 15px;
+ letter-spacing: 1px;
+ .rounded;
+
+ a,
+ a:hover {
+ color: @white;
+ }
+ &.next-button,
+ &.prev-button {
+ max-width: 33%;
+ white-space: nowrap;
+ }
+ &.next-button {
+ float: right;
+ &:before {
+ content: "Next:";
+ display: inline-block;
+ padding-right: 3px;
+ }
+ &:after {
+ content: "\00BB";
+ display: inline-block;
+ padding-left: 3px;
+ }
+
+ }
+ &.prev-button {
+ float: left;
+ &:before {
+ content: "\00AB\0020Previous: ";
+ display: inline-block;
+ padding-right: 3px;
+ }
+ }
- a,
- a:hover {
- color: @white;
}
-}
+ &.white-button {
+ background: fade(@white, 20%);
-&.white-button {
- background: fade(@white, 20%);
+ &:hover {
+ background: fade(@white, 30%);
+ }
+ }
+ &.secondary-button {
+ background: @light-gray;
+
+ &:hover {
+ background: @purple;
+ }
+
+ }
+ &.with-carat span {
+ margin-right: -10px;
+ //recenter text if there's a carat after text
+
+ }
+ span {
+ // button text styles can go here
+
+ }
&:hover {
- background: fade(@white, 30%);
+ background-color: @purple;
+ .animate-background-color;
+
}
-}
+ &:active {
-&.secondary-button {
- background: @light-gray;
- &:hover {
- background: @purple;
}
-}
-
-&.with-carat span {
- margin-right: -10px; //recenter text if there's a carat after text
+ &.disabled {
+ background-color: @light-gray-background;
+ }
}
-
-span {
- // button text styles can go here
-}
-
-&:hover {
- background-color: @purple;
- .animate-background-color;
-}
-
-&:active {
-
-}
-
-&.disabled {
- background-color: @light-gray-background;
-}
-
-}
-
a.read-more {
color: @blue;
&:hover {
color: darken(@blue, 10%);
}
-}
+}
// misc. styles
.loading {
text-align: center;
@@ -310,8 +337,8 @@ a.read-more {
letter-spacing: 5px;
color: @medium-gray-text;
padding: 30px 0 20px;
-}
-.pinned {
- position:fixed;
+}
+.pinned {
+ position: fixed;
}
diff --git a/website/docs/source/v2/boxes.html.md b/website/docs/source/v2/boxes.html.md
index e3c1661de..e123b6c2d 100644
--- a/website/docs/source/v2/boxes.html.md
+++ b/website/docs/source/v2/boxes.html.md
@@ -14,7 +14,7 @@ boxes. You can read the documentation on the [vagrant box](/v2/cli/box.html)
command for more information.
The easiest way to use a box is to add a box from the
-[publicly available catalog of Vagrant boxes](https://vagrantcloud.com).
+[publicly available catalog of Vagrant boxes](https://atlas.hashicorp.com/boxes/search).
You can also add and share your own customized boxes on this website.
Boxes also support versioning so that members of your team using Vagrant
@@ -27,7 +27,7 @@ sub-pages in the navigation to the left.
## Discovering Boxes
The easiest way to find boxes is to look on the
-[public Vagrant box catalog](https://vagrantcloud.com)
+[public Vagrant box catalog](https://atlas.hashicorp.com/boxes/search)
for a box matching your use case. The catalog contains most major operating
systems as bases, as well as specialized boxes to get you up and running
quickly with LAMP stacks, Ruby, Python, etc.
diff --git a/website/docs/source/v2/boxes/base.html.md b/website/docs/source/v2/boxes/base.html.md
index e771187a0..61007ce92 100644
--- a/website/docs/source/v2/boxes/base.html.md
+++ b/website/docs/source/v2/boxes/base.html.md
@@ -239,7 +239,7 @@ provider-specific guides are linked to towards the top of this page.
You can distribute the box file however you'd like. However, if you want
to support versioning, putting multiple providers at a single URL, pushing
updates, analytics, and more, we recommend you add the box to
-[Vagrant Cloud](https://vagrantcloud.com).
+[HashiCorp's Atlas](https://atlas.hashicorp.com).
You can upload both public and private boxes to this service.
diff --git a/website/docs/source/v2/boxes/format.html.md b/website/docs/source/v2/boxes/format.html.md
index 0eb584026..3fa50479b 100644
--- a/website/docs/source/v2/boxes/format.html.md
+++ b/website/docs/source/v2/boxes/format.html.md
@@ -23,7 +23,7 @@ Today, there are two different components:
box file and so on.
* Box Catalog Metadata - This is a JSON document (typically exchanged
- during interactions with [Vagrant Cloud](https://vagrantcloud.com))
+ during interactions with [HashiCorp's Atlas](https://atlas.hashicorp.com))
that specifies the name of the box, a description, available
versions, available providers, and URLs to the actual box files
(next component) for each provider and version. If this catalog
@@ -78,8 +78,8 @@ providers from a single file, and more.
You don't need to manually make the metadata. If you
-have an account with
Vagrant Cloud, you
-can create boxes there, and Vagrant Cloud automatically creates
+have an account with
HashiCorp's Atlas, you
+can create boxes there, and HashiCorp's Atlas automatically creates
the metadata for you. The format is still documented here.
diff --git a/website/docs/source/v2/boxes/versioning.html.md b/website/docs/source/v2/boxes/versioning.html.md
index 61af2345a..dc4cf3e28 100644
--- a/website/docs/source/v2/boxes/versioning.html.md
+++ b/website/docs/source/v2/boxes/versioning.html.md
@@ -24,10 +24,10 @@ to update your own custom boxes with versions. That is covered in
`vagrant box list` only shows _installed_ versions of boxes. If you want
to see all available versions of a box, you'll have to find the box
-on [Vagrant Cloud](https://vagrantcloud.com). An easy way to find a box
-is to use the url `https://vagrantcloud.com/USER/BOX`. For example, for
+on [HashiCorp's Atlas](https://atlas.hashicorp.com). An easy way to find a box
+is to use the url `https://atlas.hashicorp.com/USER/BOX`. For example, for
the `hashicorp/precise64` box, you can find information about it at
-`https://vagrantcloud.com/hashicorp/precise64`.
+`https://atlas.hashicorp.com/hashicorp/precise64`.
You can check if the box you're using is outdated with `vagrant box outdated`.
This can check if the box in your current Vagrant environment is outdated
diff --git a/website/docs/source/v2/cli/box.html.md b/website/docs/source/v2/cli/box.html.md
index 0a5cff9fb..910f07d98 100644
--- a/website/docs/source/v2/cli/box.html.md
+++ b/website/docs/source/v2/cli/box.html.md
@@ -26,10 +26,10 @@ This adds a box with the given address to Vagrant. The address can be
one of three things:
* A shorthand name from the
-[public catalog of available Vagrant images](https://vagrantcloud.com),
+[public catalog of available Vagrant images](https://atlas.hashicorp.com/boxes/search),
such as "hashicorp/precise64".
-* File path or HTTP URL to a box in a [catalog](https://vagrantcloud.com).
+* File path or HTTP URL to a box in a [catalog](https://atlas.hashicorp.com/boxes/search).
For HTTP, basic authentication is supported and `http_proxy` environmental
variables are respected. HTTPS is also supported.
@@ -93,8 +93,8 @@ you're not using a catalog).
to be specified.
-Checksums for versioned boxes or boxes from Vagrant Cloud:
-For boxes from Vagrant Cloud, the checksums are embedded in the metadata
+Checksums for versioned boxes or boxes from HashiCorp's Atlas:
+For boxes from HashiCorp's Atlas, the checksums are embedded in the metadata
of the box. The metadata itself is served over TLS and its format is validated.
diff --git a/website/docs/source/v2/cli/login.html.md b/website/docs/source/v2/cli/login.html.md
index 5e2e56065..c8f6ee7d9 100644
--- a/website/docs/source/v2/cli/login.html.md
+++ b/website/docs/source/v2/cli/login.html.md
@@ -7,8 +7,8 @@ sidebar_current: "cli-login"
**Command: `vagrant login`**
-The login command is used to authenticate with a
-[Vagrant Cloud](https://vagrantcloud.com) server. Logging is only
+The login command is used to authenticate with the
+[HashiCorp's Atlas](https://atlas.hashicorp.com) server. Logging is only
necessary if you're accessing protected boxes or using
[Vagrant Share](/v2/share/index.html).
diff --git a/website/docs/source/v2/docker/basics.html.md b/website/docs/source/v2/docker/basics.html.md
index 6f2f41c48..9f4dd97a6 100644
--- a/website/docs/source/v2/docker/basics.html.md
+++ b/website/docs/source/v2/docker/basics.html.md
@@ -65,6 +65,8 @@ and networking options into Docker volumes and forwarded ports.
You don't have to use the Docker-specific configurations to do this.
This helps keep your Vagrantfile similar to how it has always looked.
+Private and public networks are not currently supported.
+
## Host VM
On systems that can't run Linux containers natively, such as Mac OS X
diff --git a/website/docs/source/v2/docker/configuration.html.md b/website/docs/source/v2/docker/configuration.html.md
index a8dfed3c7..73bde2df3 100644
--- a/website/docs/source/v2/docker/configuration.html.md
+++ b/website/docs/source/v2/docker/configuration.html.md
@@ -36,7 +36,8 @@ General settings:
but not to the host machine. Useful for links.
* `link` (method, string argument) - Link this container to another
- by name. Example: `docker.link("db:db")`. Note, if you're linking to
+ by name. The argument should be in the format of `(name:alias)`.
+ Example: `docker.link("db:db")`. Note, if you're linking to
another container in the same Vagrantfile, make sure you call
`vagrant up` with the `--no-parallel` flag.
diff --git a/website/docs/source/v2/getting-started/boxes.html.md b/website/docs/source/v2/getting-started/boxes.html.md
index b16aa893e..9d165d2ad 100644
--- a/website/docs/source/v2/getting-started/boxes.html.md
+++ b/website/docs/source/v2/getting-started/boxes.html.md
@@ -27,8 +27,8 @@ $ vagrant box add hashicorp/precise32
```
This will download the box named "hashicorp/precise32" from
-[Vagrant Cloud](https://vagrantcloud.com), a place where you can find
-and host boxes. While it is easiest to download boxes from Vagrant Cloud
+[HashiCorp's Atlas box catalog](https://atlas.hashicorp.com/boxes/search), a place where you can find
+and host boxes. While it is easiest to download boxes from HashiCorp's Atlas
you can also add boxes from a local file, custom URL, etc.
Added boxes can be re-used by multiple projects. Each project uses a box
@@ -64,11 +64,14 @@ For the remainder of this getting started guide, we'll only use the
this getting started guide, the first question you'll probably have is
"where do I find more boxes?"
-The best place to find more boxes is [Vagrant Cloud](https://vagrantcloud.com).
-Vagrant Cloud has a public directory of freely available boxes that
-run various platforms and technologies. Vagrant Cloud also has a great search
+The best place to find more boxes is [HashiCorp's Atlas box catalog](https://atlas.hashicorp.com/boxes/search).
+HashiCorp's Atlas has a public directory of freely available boxes that
+run various platforms and technologies. HashiCorp's Atlas also has a great search
feature to allow you to find the box you care about.
-In addition to finding free boxes, Vagrant Cloud lets you host your own
+In addition to finding free boxes, HashiCorp's Atlas lets you host your own
boxes, as well as private boxes if you intend on creating boxes for your
own organization.
+
+Project Setup
+Up And SSH
diff --git a/website/docs/source/v2/getting-started/index.html.md b/website/docs/source/v2/getting-started/index.html.md
index 687294274..26cbae2cb 100644
--- a/website/docs/source/v2/getting-started/index.html.md
+++ b/website/docs/source/v2/getting-started/index.html.md
@@ -17,7 +17,7 @@ since it is free, available on every major platform, and built-in to
Vagrant. After reading the guide though, don't forget that Vagrant
can work with [many other providers](/v2/getting-started/providers.html).
-Before diving into your first project, please [install Vagrant](/v2/installation/index.html).
+Before diving into your first project, please [install the latest version of Vagrant](/v2/installation/index.html).
And because we'll be using [VirtualBox](http://www.virtualbox.org) as our
provider for the getting started guide, please install that as well.
@@ -54,3 +54,5 @@ comfort of your own machine.
The rest of this guide will walk you through setting up a more
complete project, covering more features of Vagrant.
+
+Project Setup
diff --git a/website/docs/source/v2/getting-started/networking.html.md b/website/docs/source/v2/getting-started/networking.html.md
index 150b4cab1..69ac4c3fc 100644
--- a/website/docs/source/v2/getting-started/networking.html.md
+++ b/website/docs/source/v2/getting-started/networking.html.md
@@ -43,3 +43,6 @@ Vagrant also has other forms of networking, allowing you to assign
a static IP address to the guest machine, or to bridge the guest
machine onto an existing network. If you're interested in other options,
read the [networking](/v2/networking/index.html) page.
+
+Provisioning
+Share
diff --git a/website/docs/source/v2/getting-started/project_setup.html.md b/website/docs/source/v2/getting-started/project_setup.html.md
index e36fac01c..1a2029bd1 100644
--- a/website/docs/source/v2/getting-started/project_setup.html.md
+++ b/website/docs/source/v2/getting-started/project_setup.html.md
@@ -36,3 +36,6 @@ set up Vagrant for an existing project.
The Vagrantfile is meant to be committed to version control with
your project, if you use version control. This way, every person working
with that project can benefit from Vagrant without any upfront work.
+
+Getting Started
+Boxes
diff --git a/website/docs/source/v2/getting-started/providers.html.md b/website/docs/source/v2/getting-started/providers.html.md
index abda2deec..9dfd31d71 100644
--- a/website/docs/source/v2/getting-started/providers.html.md
+++ b/website/docs/source/v2/getting-started/providers.html.md
@@ -35,3 +35,5 @@ flags necessary.
For more information on providers, read the full documentation on
[providers](/v2/providers/index.html).
+
+Rebuild
diff --git a/website/docs/source/v2/getting-started/provisioning.html.md b/website/docs/source/v2/getting-started/provisioning.html.md
index ad2b24150..ceb79e82d 100644
--- a/website/docs/source/v2/getting-started/provisioning.html.md
+++ b/website/docs/source/v2/getting-started/provisioning.html.md
@@ -75,3 +75,6 @@ directory, which is the default synced folder setup by Vagrant.
You can play around some more by creating some more files and viewing
them from the terminal, but in the next step we'll cover networking
options so that you can use your own browser to access the guest machine.
+
+Synced Folders
+Networking
diff --git a/website/docs/source/v2/getting-started/rebuild.html.md b/website/docs/source/v2/getting-started/rebuild.html.md
index aa5a3bc40..1555ca6b5 100644
--- a/website/docs/source/v2/getting-started/rebuild.html.md
+++ b/website/docs/source/v2/getting-started/rebuild.html.md
@@ -15,3 +15,6 @@ $ vagrant up
That's it! Since the Vagrant environment is already all configured via
the Vagrantfile, you or any of your coworkers simply have to run a
`vagrant up` at any time and Vagrant will recreate your work environment.
+
+Teardown
+Providers
diff --git a/website/docs/source/v2/getting-started/share.html.md b/website/docs/source/v2/getting-started/share.html.md
index 96c6ffe6a..46cde9c70 100644
--- a/website/docs/source/v2/getting-started/share.html.md
+++ b/website/docs/source/v2/getting-started/share.html.md
@@ -15,10 +15,10 @@ Vagrant Share lets you share your Vagrant environment to anyone around the
world. It will give you a URL that will route directly to your Vagrant
environment from any device in the world that is connected to the internet.
-## Login to Vagrant Cloud
+## Login to HashiCorp's Atlas
Before being able to share your Vagrant environment, you'll need an account on
-[Vagrant Cloud](https://vagrantcloud.com). Don't worry, it's free.
+[HashiCorp's Atlas](https://atlas.hashicorp.com). Don't worry, it's free.
Once you have an account, log in using `vagrant login`:
@@ -54,3 +54,6 @@ the URL again to verify that your environment is no longer being shared.
Vagrant Share is much more powerful than simply HTTP sharing. For more
details, see the [complete Vagrant Share documentation](/v2/share/index.html).
+
+Networking
+Teardown
diff --git a/website/docs/source/v2/getting-started/synced_folders.html.md b/website/docs/source/v2/getting-started/synced_folders.html.md
index 11612e84c..80c42c876 100644
--- a/website/docs/source/v2/getting-started/synced_folders.html.md
+++ b/website/docs/source/v2/getting-started/synced_folders.html.md
@@ -40,3 +40,6 @@ the folders in sync.
With [synced folders](/v2/synced-folders/index.html), you can continue
to use your own editor on your host machine and have the files sync
into the guest machine.
+
+Up And SSH
+Provisioning
diff --git a/website/docs/source/v2/getting-started/teardown.html.md b/website/docs/source/v2/getting-started/teardown.html.md
index 764de1969..04ce15f9f 100644
--- a/website/docs/source/v2/getting-started/teardown.html.md
+++ b/website/docs/source/v2/getting-started/teardown.html.md
@@ -23,19 +23,22 @@ work. The downside is that the virtual machine still eats up your disk space,
and requires even more disk space to store all the state of the virtual
machine RAM on disk.
-**Halting** the virtual machine by calling `vagrant halt` will gracefully
-shut down the guest operating system and power down the guest machine.
-You can use `vagrant up` when you're ready to boot it again. The benefit of
-this method is that it will cleanly shut down your machine, preserving the
+**Halting** the virtual machine by calling `vagrant halt` will gracefully
+shut down the guest operating system and power down the guest machine.
+You can use `vagrant up` when you're ready to boot it again. The benefit of
+this method is that it will cleanly shut down your machine, preserving the
contents of disk, and allowing it to be cleanly started again. The downside is
that it'll take some extra time to start from a cold boot, and the guest machine
still consumes disk space.
-**Destroying** the virtual machine by calling `vagrant destroy` will remove
-all traces of the guest machine from your system. It'll stop the guest machine,
-power it down, and remove all of the guest hard disks. Again, when you're ready to
+**Destroying** the virtual machine by calling `vagrant destroy` will remove
+all traces of the guest machine from your system. It'll stop the guest machine,
+power it down, and remove all of the guest hard disks. Again, when you're ready to
work again, just issue a `vagrant up`. The benefit of this is that _no cruft_
is left on your machine. The disk space and RAM consumed by the guest machine
is reclaimed and your host machine is left clean. The downside is that
`vagrant up` to get working again will take some extra time since it
has to reimport the machine and reprovision it.
+
+Share
+Rebuild
diff --git a/website/docs/source/v2/getting-started/up.html.md b/website/docs/source/v2/getting-started/up.html.md
index 3a9686afa..f1e59a6cf 100644
--- a/website/docs/source/v2/getting-started/up.html.md
+++ b/website/docs/source/v2/getting-started/up.html.md
@@ -33,3 +33,6 @@ virtual machine. Cool.
When you're done fiddling around with the machine, run `vagrant destroy`
back on your host machine, and Vagrant will remove all traces of the
virtual machine.
+
+Boxes
+Synced Folders
diff --git a/website/docs/source/v2/hyperv/usage.html.md b/website/docs/source/v2/hyperv/usage.html.md
index 7f3d9ccbe..5751fa511 100644
--- a/website/docs/source/v2/hyperv/usage.html.md
+++ b/website/docs/source/v2/hyperv/usage.html.md
@@ -17,5 +17,5 @@ admin rights. Vagrant will show you an error if it doesn't have the proper
permissions.
Boxes for Hyper-V can be easily found on
-[Vagrant Cloud](https://vagrantcloud.com). To get started, you might
+[HashiCorp's Atlas](https://atlas.hashicorp.com/boxes/search). To get started, you might
want to try the `hashicorp/precise64` box.
diff --git a/website/docs/source/v2/plugins/hosts.html.md b/website/docs/source/v2/plugins/hosts.html.md
index aaf1d6bdf..aed652f84 100644
--- a/website/docs/source/v2/plugins/hosts.html.md
+++ b/website/docs/source/v2/plugins/hosts.html.md
@@ -47,7 +47,7 @@ Implementations of hosts subclass `Vagrant.plugin("2", "host")`. Within
this implementation, only the `detect?` method needs to be implemented.
The `detect?` method is called by Vagrant very early on in its initialization
-process to determine if the OS that Vagrant is running on is this hsot.
+process to determine if the OS that Vagrant is running on is this host.
If you detect that it is your operating system, return `true` from `detect?`.
Otherwise, return `false`.
diff --git a/website/docs/source/v2/provisioning/ansible.html.md b/website/docs/source/v2/provisioning/ansible.html.md
index a39315bba..9301e0fb8 100644
--- a/website/docs/source/v2/provisioning/ansible.html.md
+++ b/website/docs/source/v2/provisioning/ansible.html.md
@@ -8,7 +8,7 @@ sidebar_current: "provisioning-ansible"
**Provisioner name: `"ansible"`**
The ansible provisioner allows you to provision the guest using
-[Ansible](http://ansible.com) playbooks.
+[Ansible](http://ansible.com) playbooks by executing `ansible-playbook` from the Vagrant host.
Ansible playbooks are [YAML](http://en.wikipedia.org/wiki/YAML) documents that
comprise the set of steps to be orchestrated on one or more machines. This documentation
@@ -25,6 +25,11 @@ a single page of documentation.
+## Setup Requirements
+
+* [Install Ansible](http://docs.ansible.com/intro_installation.html#installing-the-control-machine) on your Vagrant host.
+* Your Vagrant host should ideally provide a recent version of OpenSSH that [supports ControlPersist](http://docs.ansible.com/faq.html#how-do-i-get-ansible-to-reuse-connections-enable-kerberized-ssh-or-have-ansible-pay-attention-to-my-local-ssh-config-file)
+
## Inventory File
When using Ansible, it needs to know on which machines a given playbook should run. It does
@@ -191,11 +196,9 @@ by the sudo command.
* `ansible.tags` can be set to a string or an array of tags. Only plays, roles and tasks tagged with these values will be executed.
* `ansible.skip_tags` can be set to a string or an array of tags. Only plays, roles and tasks that *do not match* these values will be executed.
* `ansible.start_at_task` can be set to a string corresponding to the task name where the playbook provision will start.
-* `ansible.raw_arguments` can be set to an array of strings corresponding to a list of `ansible-playbook` arguments (e.g. `['--check', '-M /my/modules']`). It is an *unsafe wildcard* that can be used to apply Ansible options that are not (yet) supported by this Vagrant provisioner. Following precedence rules apply:
- * Any supported options (described above) will override conflicting `raw_arguments` value (e.g. `--tags` or `--start-at-task`)
- * Vagrant default user authentication can be overridden via `raw_arguments` (with custom values for `--user` and `--private-key`)
+* `ansible.raw_arguments` can be set to an array of strings corresponding to a list of `ansible-playbook` arguments (e.g. `['--check', '-M /my/modules']`). It is an *unsafe wildcard* that can be used to apply Ansible options that are not (yet) supported by this Vagrant provisioner. As of Vagrant 1.7, `raw_arguments` has the highest priority and its values can potentially override or break other Vagrant settings.
* `ansible.raw_ssh_args` can be set to an array of strings corresponding to a list of OpenSSH client parameters (e.g. `['-o ControlMaster=no']`). It is an *unsafe wildcard* that can be used to pass additional SSH settings to Ansible via `ANSIBLE_SSH_ARGS` environment variable.
-* `ansible.host_key_checking` can be set to `true` which will enable host key checking. As Vagrant 1.5, the default value is `false`, to avoid connection problems when creating new virtual machines.
+* `ansible.host_key_checking` can be set to `true` which will enable host key checking. As of Vagrant 1.5, the default value is `false` and as of Vagrant 1.7 the user kownn host file (e.g. `~/.ssh/known_hosts`) is no longer read nor modified. In other words: by default, the Ansible provisioner behaves the same as Vagrant native commands (e.g `vagrant ssh`).
## Tips and Tricks
diff --git a/website/docs/source/v2/provisioning/basic_usage.html.md b/website/docs/source/v2/provisioning/basic_usage.html.md
index f61cae325..d53439a05 100644
--- a/website/docs/source/v2/provisioning/basic_usage.html.md
+++ b/website/docs/source/v2/provisioning/basic_usage.html.md
@@ -45,7 +45,7 @@ it can greatly improve readability. Additionally, some provisioners, like
the Chef provisioner, have special methods that can be called within that
block to ease configuration that can't be done with the key/value approach.
-Provisioners can also be named. These names are used cosmetically for output
+Provisioners can also be named (since 1.7.0). These names are used cosmetically for output
as well as overriding provisioner settings (covered further below). An example
of naming provisioners is shown below:
diff --git a/website/docs/source/v2/provisioning/chef_apply.html.md b/website/docs/source/v2/provisioning/chef_apply.html.md
index 6aae0927a..268894cb2 100644
--- a/website/docs/source/v2/provisioning/chef_apply.html.md
+++ b/website/docs/source/v2/provisioning/chef_apply.html.md
@@ -29,9 +29,6 @@ This section lists the complete set of available options for the Chef Apply
provisioner. More detailed examples of how to use the provisioner are
available below this section.
-Due to the unqiue nature of Chef Apply, the Chef Apply provisioner does not
-inherit the [common options for other Chef provisioners](/v2/provisioning/chef_common.html).
-
* `recipe` (string) - The raw recipe contents to execute using Chef Apply on
the guest.
@@ -44,6 +41,9 @@ inherit the [common options for other Chef provisioners](/v2/provisioning/chef_c
`/tmp/vagrant-chef-apply-#` where `#` is a unique counter generated by
Vagrant to prevent collisions.
+In addition to all the options listed above, the Chef Apply provisioner supports
+the [common options for all Chef provisioners](/v2/provisioning/chef_common.html).
+
## Specifying a Recipe
The easiest way to get started with the Chef Apply provisioner is to just
diff --git a/website/docs/source/v2/provisioning/chef_client.html.md b/website/docs/source/v2/provisioning/chef_client.html.md
index a22ddcc47..d56359050 100644
--- a/website/docs/source/v2/provisioning/chef_client.html.md
+++ b/website/docs/source/v2/provisioning/chef_client.html.md
@@ -82,12 +82,15 @@ end
There are a few more configuration options available. These generally don't
need to be modified but are available if your Chef Server requires customization
-of these variables:
+of these variables.
* `client_key_path`
* `node_name`
* `validation_client_name`
+In addition to all the options listed above, the Chef Client provisioner supports
+the [common options for all Chef provisioners](/v2/provisioning/chef_common.html).
+
## Cleanup
When you provision your Vagrant virtual machine with Chef Server, it creates a
diff --git a/website/docs/source/v2/provisioning/chef_common.html.md b/website/docs/source/v2/provisioning/chef_common.html.md
index ed776ad0c..d63a0d680 100644
--- a/website/docs/source/v2/provisioning/chef_common.html.md
+++ b/website/docs/source/v2/provisioning/chef_common.html.md
@@ -5,12 +5,55 @@ sidebar_current: "provisioning-chefcommon"
# Shared Chef Options
-This page documents the list of available options that are available in the
-[Chef Solo](/v2/provisioning/chef_solo.html),
-[Chef Zero](/v2/provisioning/chef_zero.html)
-and
-[Chef Client](/v2/provisioning/chef_client.html)
-provisioners.
+## All Chef Provisioners
+
+The following options are available to all Chef provisioners. Many of these
+options are for advanced users only and should not be used unless you understand
+their purpose.
+
+- `binary_path` (string) - The path to Chef's `bin/` directory on the guest
+ machine.
+
+- `binary_env` (string) - Arbitrary environment variables to set before running
+ the Chef provisioner command. This should be of the format `KEY=value` as a
+ string.
+
+- `install` (boolean, string) - Install Chef on the system if it does not exist.
+ The default value is "true", which will use the official Omnibus installer
+ from Chef. This is a trinary attribute (it can have three values):
+
+ - `true` (boolean) - install Chef
+ - `false` (boolean) - do not install Chef
+ - `"force"` (string) - install Chef, even if it is already installed at the
+ proper version on the guest
+
+- `installer_download_path` (string) - The path where the Chef installer will be
+ downloaded to. This option is only honored if the `install` attribute is
+ `true` or `"force"`. The default value is to use the path provided by Chef's
+ Omnibus installer, which varies between releases.
+
+- `log_level` (string) - The Chef log level. See the Chef docs for acceptable
+ values.
+
+- `prerelease` (boolean) - Install a prerelease version of Chef. The default
+ value is false.
+
+- `version` (string) - The version of Chef to install on the guest. If Chef is
+ already installed on the system, the installed version is compared with the
+ requested version. If they match, no action is taken. If they do not match,
+ the value specified in this attribute will be installed in favor of the
+ existing version (a message will be displayed).
+
+ You can also specify "latest" (default), which will install the latest
+ version of Chef on the system. In this case, Chef will use whatever
+ version is on the system. To force the newest version of Chef to be
+ installed on every provision, set the {#install} option to "force".
+
+
+## Runner Chef Provisioners
+
+The following options are available to any of the Chef "runner" provisioners
+which include [Chef Solo](/v2/provisioning/chef_solo.html), [Chef Zero](/v2/provisioning/chef_zero.html), and [Chef Client](/v2/provisioning/chef_client.html).
* `arguments` (string) - A list of additional arguments to pass on the
command-line to Chef. Since these are passed in a shell-like environment,
@@ -21,9 +64,6 @@ provisioners.
This defaults to 1. This can be increased to a higher number if your Chef
runs take multiple runs to reach convergence.
-* `binary_path` (string) - The path to the directory of the Chef executable
- binaries. By default, Vagrant looks for the proper Chef binary on the PATH.
-
* `custom_config_path` (string) - A path to a custom Chef configuration local
on your machine that will be used as the Chef configuration. This Chef
configuration will be loaded _after_ the Chef configuration that Vagrant
diff --git a/website/docs/source/v2/provisioning/chef_solo.html.md b/website/docs/source/v2/provisioning/chef_solo.html.md
index d916f4fe2..129e17e17 100644
--- a/website/docs/source/v2/provisioning/chef_solo.html.md
+++ b/website/docs/source/v2/provisioning/chef_solo.html.md
@@ -32,10 +32,6 @@ This section lists the complete set of available options for the Chef Solo
provisioner. More detailed examples of how to use the provisioner are
available below this section.
-Note that only the Chef-solo specific options are shown below. There is
-also a large set of [common options](/v2/provisioning/chef_common.html)
-that are available with all Chef provisioners.
-
* `cookbooks_path` (string or array) - A list of paths to where cookbooks
are stored. By default this is "cookbooks", expecting a cookbooks folder
relative to the Vagrantfile location.
@@ -62,6 +58,9 @@ that are available with all Chef provisioners.
this will use the default synced folder type. For example, you can set this
to "nfs" to use NFS synced folders.
+In addition to all the options listed above, the Chef Solo provisioner supports
+the [common options for all Chef provisioners](/v2/provisioning/chef_common.html).
+
## Specifying a Run List
The easiest way to get started with the Chef Solo provisioner is to just
diff --git a/website/docs/source/v2/provisioning/chef_zero.html.md b/website/docs/source/v2/provisioning/chef_zero.html.md
index f716d3ff9..ec50dac0a 100644
--- a/website/docs/source/v2/provisioning/chef_zero.html.md
+++ b/website/docs/source/v2/provisioning/chef_zero.html.md
@@ -31,13 +31,12 @@ This section lists the complete set of available options for the Chef Zero
provisioner. More detailed examples of how to use the provisioner are
available below this section.
-Note that only the Chef Zero specific options are shown below, but all [Chef
-Solo options](/v2/provisioning/chef_solo.html), including the [common Chef
-provisioner options](/v2/provisioning/chef_common.html), are also inherited.
-
* `nodes_path` (string) - A path where the Chef nodes are stored. Be default,
no node path is set.
+In addition to all the options listed above, the Chef Zero provisioner supports
+the [common options for all Chef provisioners](/v2/provisioning/chef_common.html).
+
## Usage
The Chef Zero provisioner is configured basically the same way as the Chef Solo
diff --git a/website/docs/source/v2/provisioning/salt.html.md b/website/docs/source/v2/provisioning/salt.html.md
index f66ac7c60..ddc81fa90 100644
--- a/website/docs/source/v2/provisioning/salt.html.md
+++ b/website/docs/source/v2/provisioning/salt.html.md
@@ -56,7 +56,7 @@ on this machine. Not supported on Windows.
`false`. Not supported on Windows.
* `install_type` (stable | git | daily | testing) - Whether to install from a
-distribution's stable package manager, git tree-ish, daily ppa, or testing repository.
+distribution's stable package manager, git tree-ish, daily ppa, or testing repository.
Not supported on Windows.
* `install_args` (develop) - When performing a git install,
@@ -77,6 +77,7 @@ a custom salt minion config file.
* `minion_pub` (salt/key/minion.pub) - Path to your minion
public key
+* `grains_config` (string) - Path to a custom salt grains file.
## Master Options
These only make sense when `install_master` is `true`.
diff --git a/website/docs/source/v2/push/atlas.html.md b/website/docs/source/v2/push/atlas.html.md
new file mode 100644
index 000000000..e5679bbf2
--- /dev/null
+++ b/website/docs/source/v2/push/atlas.html.md
@@ -0,0 +1,67 @@
+---
+page_title: "Vagrant Push - Atlas Strategy"
+sidebar_current: "push-atlas"
+description: |-
+ Atlas is HashiCorp's commercial offering to bring your Vagrant development
+ environments to production. The Vagrant Push Atlas strategy pushes your
+ application's code to HashiCorp's Atlas service.
+---
+
+# Vagrant Push
+
+## Atlas Strategy
+
+[Atlas][] is HashiCorp's commercial offering to bring your Vagrant development
+environments to production. You can read more about HashiCorp's Atlas and all
+its features on [the Atlas homepage][Atlas]. The Vagrant Push Atlas strategy
+pushes your application's code to HashiCorp's Atlas service.
+
+The Vagrant Push Atlas strategy supports the following configuration options:
+
+- `app` - The name of the application in [HashiCorp's Atlas][Atlas]. If the
+ application does not exist, it will be created with user confirmation.
+
+- `exclude` - Add a file or file pattern to exclude from the upload, relative to
+ the `dir`. This value may be specified multiple times and is additive.
+ `exclude` take precedence over `include` values.
+
+- `include` - Add a file or file pattern to include in the upload, relative to
+ the `dir`. This value may be specified multiple times and is additive.
+
+- `dir` - The base directory containing the files to upload. By default this is
+ the same directory as the Vagrantfile, but you can specify this if you have
+ a `src` folder or `bin` folder or some other folder you want to upload.
+
+- `vcs` - If set to true, Vagrant will automatically use VCS data to determine
+ the files to upload. Uncommitted changes will not be deployed.
+
+Additionally, the following options are exposed for power users of the Vagrant
+Atlas push strategy. Most users will not require these options:
+
+- `address` - The address of the Atlas server to upload to. By default this will
+ be the public Atlas server.
+
+- `token` - The Atlas token to use. If the user has run `vagrant login`, this
+ will the token generated by that command. If the environment variable
+ `ATLAS_TOKEN` is set, the uploader will use this value. By default, this is
+ nil.
+
+
+### Usage
+
+The Vagrant Push Atlas strategy is defined in the `Vagrantfile` using the
+`atlas` key:
+
+```ruby
+config.push.define "atlas" do |push|
+ push.app = "username/application"
+end
+```
+
+And then push the application to Atlas:
+
+```shell
+$ vagrant push
+```
+
+[Atlas]: https://atlas.hashicorp.com/ "HashiCorp's Atlas Service"
diff --git a/website/docs/source/v2/push/ftp.html.md b/website/docs/source/v2/push/ftp.html.md
new file mode 100644
index 000000000..54a2e1427
--- /dev/null
+++ b/website/docs/source/v2/push/ftp.html.md
@@ -0,0 +1,62 @@
+---
+page_title: "Vagrant Push - FTP & SFTP Strategy"
+sidebar_current: "push-ftp"
+description: |-
+
+---
+
+# Vagrant Push
+
+## FTP & SFTP Strategy
+
+Vagrant Push FTP and SFTP strategy pushes the code in your Vagrant development
+environment to a remote FTP or SFTP server.
+
+The Vagrant Push FTP And SFTP strategy supports the following configuration
+options:
+
+- `host` - The address of the remote (S)FTP server. If the (S)FTP server is
+ running on a non-standard port, you can specify the port after the address
+ (`host:port`).
+
+- `username` - The username to use for authentication with the (S)FTP server.
+
+- `password` - The password to use for authentication with the (S)FTP server.
+
+- `passive` - Use passive FTP (default is true).
+
+- `secure` - Use secure (SFTP) (default is false).
+
+- `destination` - The root destination on the target system to sync the files
+ (default is `/`).
+
+- `exclude` - Add a file or file pattern to exclude from the upload, relative to
+ the `dir`. This value may be specified multiple times and is additive.
+ `exclude` take precedence over `include` values.
+
+- `include` - Add a file or file pattern to include in the upload, relative to
+ the `dir`. This value may be specified multiple times and is additive.
+
+- `dir` - The base directory containing the files to upload. By default this is
+ the same directory as the Vagrantfile, but you can specify this if you have
+ a `src` folder or `bin` folder or some other folder you want to upload.
+
+
+### Usage
+
+The Vagrant Push FTP and SFTP strategy is defined in the `Vagrantfile` using the
+`ftp` key:
+
+```ruby
+config.push.define "ftp" do |push|
+ push.host = "ftp.company.com"
+ push.username = "username"
+ push.password = "password"
+end
+```
+
+And then push the application to the FTP or SFTP server:
+
+```shell
+$ vagrant push
+```
diff --git a/website/docs/source/v2/push/heroku.html.md b/website/docs/source/v2/push/heroku.html.md
new file mode 100644
index 000000000..90aa20e82
--- /dev/null
+++ b/website/docs/source/v2/push/heroku.html.md
@@ -0,0 +1,63 @@
+---
+page_title: "Vagrant Push - Heroku Strategy"
+sidebar_current: "push-heroku"
+description: |-
+ The Vagrant Push Heroku strategy pushes your application's code to Heroku.
+ Only files which are committed to the Git repository are pushed to Heroku.
+---
+
+# Vagrant Push
+
+## Heroku Strategy
+
+[Heroku][] is a public PAAS provider that makes it easy to deploy an
+application. The Vagrant Push Heroku strategy pushes your application's code to
+Heroku.
+
+
+
+ Warning: The Vagrant Push Heroku strategy requires you
+ have configured your Heroku credentials and created the Heroku application.
+ This documentation will not cover these prerequisites, but you can read more
+ about them in the Heroku documentation.
+
+
+
+Only files which are committed to the Git repository will be pushed to Heroku.
+Additionally, the current working branch is always pushed to the Heroku, even if
+it is not the "master" branch.
+
+The Vagrant Push Heroku strategy supports the following configuration options:
+
+- `app` - The name of the Heroku application. If the Heroku application does not
+ exist, an exception will be raised. If this value is not specified, the
+ basename of the directory containing the `Vagrantfile` is assumed to be the
+ name of the Heroku application. Since this value can change between users, it
+ is highly recommended that you add the `app` setting to your `Vagrantfile`.
+
+- `dir` - The base directory containing the Git repository to upload to Heroku.
+ By default this is the same directory as the Vagrantfile, but you can specify
+ this if you have a nested Git directory.
+
+- `remote` - The name of the Git remote where Heroku is configured. The default
+ value is "heroku".
+
+
+### Usage
+
+The Vagrant Push Heroku strategy is defined in the `Vagrantfile` using the
+`heroku` key:
+
+```ruby
+config.push.define "heroku" do |push|
+ push.app = "my_application"
+end
+```
+
+And then push the application to Heroku:
+
+```shell
+$ vagrant push
+```
+
+[Heroku]: https://heroku.com/ "Heroku"
diff --git a/website/docs/source/v2/push/index.html.md b/website/docs/source/v2/push/index.html.md
new file mode 100644
index 000000000..62f696ea7
--- /dev/null
+++ b/website/docs/source/v2/push/index.html.md
@@ -0,0 +1,59 @@
+---
+page_title: "Vagrant Push"
+sidebar_current: "push"
+description: |-
+ Vagrant Push is a revolutionary
+---
+
+# Vagrant Push
+
+As of version 1.7, Vagrant is capable of deploying or "pushing" application code
+in the same directory as your Vagrantfile to a remote such as an FTP server or
+[HashiCorp's Atlas][Atlas].
+
+Pushes are defined in an application's `Vagrantfile` and are invoked using the
+`vagrant push` subcommand. Much like other components of Vagrant, each Vagrant
+Push plugin has its own configuration options. Please consult the documentation
+for your Vagrant Push plugin for more information. Here is an example Vagrant
+Push configuration section in a `Vagrantfile`:
+
+```ruby
+config.push.define "ftp" do |push|
+ push.host = "ftp.company.com"
+ push.username = "..."
+ # ...
+end
+```
+
+When the application is ready to be deployed to the FTP server, just run a
+single command:
+
+```shell
+$ vagrant push
+```
+
+Much like [Vagrant Providers][], Vagrant Push also supports multiple backend
+declarations. Consider the common scenario of a staging and QA environment:
+
+```ruby
+config.push.define "staging", strategy: "ftp" do |push|
+ # ...
+end
+
+config.push.define "qa", strategy: "ftp" do |push|
+ # ...
+end
+```
+
+In this scenario, the user must pass the name of the Vagrant Push to the
+subcommand:
+
+```shell
+$ vagrant push staging
+```
+
+Vagrant Push is the easiest way to deploy your application. You can read more
+in the documentation links on the sidebar.
+
+[Atlas]: https://atlas.hashicorp.com/ "HashiCorp's Atlas Service"
+[Vagrant Providers]: /v2/providers/index.html "Vagrant Providers"
diff --git a/website/docs/source/v2/push/local-exec.html.md b/website/docs/source/v2/push/local-exec.html.md
new file mode 100644
index 000000000..536a5eab9
--- /dev/null
+++ b/website/docs/source/v2/push/local-exec.html.md
@@ -0,0 +1,60 @@
+---
+page_title: "Vagrant Push - Local Exec Strategy"
+sidebar_current: "push-local-exec"
+description: |-
+ The Vagrant Push Local Exec strategy pushes your application's code using a
+ user-defined script.
+---
+
+# Vagrant Push
+
+## Local Exec Strategy
+
+The Vagrant Push Local Exec strategy allows the user to invoke an arbitrary
+shell command or script as part of a push.
+
+
+
+ Warning: The Vagrant Push Local Exec strategy does not
+ perform any validation on the correctness of the shell script.
+
+
+
+The Vagrant Push Local Exec strategy supports the following configuration
+options:
+
+- `script` - The path to a script on disk (relative to the `Vagrantfile`) to
+ execute. Vagrant will attempt to convert this script to an executable, but an
+ exception will be raised if that fails.
+- `inline` - The inline script to execute (as a string).
+
+Please note - only one of the `script` and `inline` options may be specified in
+a single push definition.
+
+### Usage
+
+The Vagrant Push Local Exec strategy is defined in the `Vagrantfile` using the
+`local-exec` key:
+
+```ruby
+config.push.define "local-exec" do |push|
+ push.inline = <<-SCRIPT
+ scp . /var/www/website
+ SCRIPT
+end
+```
+
+For more complicated scripts, you may store them in a separate file and read
+them from the `Vagrantfile` like so:
+
+```ruby
+config.push.define "local-exec" do |push|
+ push.script = "my-script.sh"
+end
+```
+
+And then invoke the push with Vagrant:
+
+```shell
+$ vagrant push
+```
diff --git a/website/docs/source/v2/share/connect.html.md b/website/docs/source/v2/share/connect.html.md
index 37a3bf8e9..c7074d675 100644
--- a/website/docs/source/v2/share/connect.html.md
+++ b/website/docs/source/v2/share/connect.html.md
@@ -29,7 +29,7 @@ machines. `vagrant connect` creates a tiny virtual machine that takes up
only around 20 MB in RAM, using VirtualBox or VMware (more provider support
is coming soon).
-Any traffic sent to this tiny virtual machine is then proxies through to
+Any traffic sent to this tiny virtual machine is then proxied through to
the shared Vagrant environment as if it were directed at it.
## Beware: Vagrant Insecure Key
diff --git a/website/docs/source/v2/share/index.html.md b/website/docs/source/v2/share/index.html.md
index 078fd5e59..20fde2781 100644
--- a/website/docs/source/v2/share/index.html.md
+++ b/website/docs/source/v2/share/index.html.md
@@ -34,4 +34,4 @@ to the left. We also have a section where we go into detail about the
security implications of this feature.
Vagrant Share requires an account with
-[Vagrant Cloud](https://vagrantcloud.com) to be used.
+[HashiCorp's Atlas](https://atlas.hashicorp.com) to be used.
diff --git a/website/docs/source/v2/synced-folders/nfs.html.md b/website/docs/source/v2/synced-folders/nfs.html.md
index 1e0959581..dbe565b48 100644
--- a/website/docs/source/v2/synced-folders/nfs.html.md
+++ b/website/docs/source/v2/synced-folders/nfs.html.md
@@ -90,7 +90,7 @@ Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /usr/bin/sed -E -e /*/ d -ibak /etc/exports
%admin ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD, VAGRANT_EXPORTS_REMOVE
```
-For Linux, sudoers should look like this:
+For Ubuntu Linux , sudoers should look like this:
```
Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
@@ -101,3 +101,14 @@ Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /etc/exports
%sudo ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE
```
+For Fedora Linux, sudoers might look like this (given your user
+belongs to the vagrant group):
+
+```
+Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
+Cmnd_Alias VAGRANT_NFSD_CHECK = /usr/bin/systemctl status nfs-server.service
+Cmnd_Alias VAGRANT_NFSD_START = /usr/bin/systemctl start nfs-server.service
+Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar
+Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /etc/exports
+%vagrant ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE
+```
diff --git a/website/docs/source/v2/synced-folders/rsync.html.md b/website/docs/source/v2/synced-folders/rsync.html.md
index 3e97be86d..8dc3e5833 100644
--- a/website/docs/source/v2/synced-folders/rsync.html.md
+++ b/website/docs/source/v2/synced-folders/rsync.html.md
@@ -44,7 +44,7 @@ the destination folder.
The rsync synced folder type accepts the following options:
* `rsync__args` (array of strings) - A list of arguments to supply
- to `rsync`. By default this is `["--verbose", "--archive", "--delete", "-z"]`.
+ to `rsync`. By default this is `["--verbose", "--archive", "--delete", "-z", "--copy-links"]`.
* `rsync__auto` (boolean) - If false, then `rsync-auto` will not
watch and automatically sync this folder. By default, this is true.
diff --git a/website/docs/source/v2/vagrantfile/index.html.md b/website/docs/source/v2/vagrantfile/index.html.md
index 66de9b7a1..a83b98620 100644
--- a/website/docs/source/v2/vagrantfile/index.html.md
+++ b/website/docs/source/v2/vagrantfile/index.html.md
@@ -9,7 +9,7 @@ The primary function of the Vagrantfile is to describe the type
of machine required for a project, and how to configure and
provision these machines. Vagrantfiles are called Vagrantfiles because
the actual literal filename for the file is `Vagrantfile` (casing doesn't
-matter).
+matter unless your file system is running in a strict case sensitive mode).
Vagrant is meant to run with one Vagrantfile per project, and the Vagrantfile
is supposed to be committed to version control. This allows other developers
diff --git a/website/docs/source/v2/vagrantfile/machine_settings.html.md b/website/docs/source/v2/vagrantfile/machine_settings.html.md
index a6f69c194..26565200e 100644
--- a/website/docs/source/v2/vagrantfile/machine_settings.html.md
+++ b/website/docs/source/v2/vagrantfile/machine_settings.html.md
@@ -20,7 +20,7 @@ for the machine to boot and be accessible. By default this is 300 seconds.
`config.vm.box` - This configures what [box](/v2/boxes.html) the
machine will be brought up against. The value here should be the name
of an installed box or a shorthand name of a box in
-[Vagrant Cloud](https://vagrantcloud.com).
+[HashiCorp's Atlas](https://atlas.hashicorp.com).
@@ -28,7 +28,7 @@ of an installed box or a shorthand name of a box in
the configured box on every `vagrant up`. If an update is found, Vagrant
will tell the user. By default this is true. Updates will only be checked
for boxes that properly support updates (boxes from
-[Vagrant Cloud](https://vagrantcloud.com)
+[HashiCorp's Atlas](https://atlas.hashicorp.com)
or some other versioned box).
@@ -64,7 +64,7 @@ bundle.
`config.vm.box_download_ca_path` - Path to a directory containing
CA certificates for downloading a box directly. By default, Vagrant will
-use the Mozilla CA cert bundle..
+use the Mozilla CA cert bundle.
`config.vm.box_download_insecure` - If true, then SSL certificates
@@ -74,7 +74,7 @@ URL, then SSL certs will be verified.
`config.vm.box_url` - The URL that the configured box can be found at.
-If `config.vm.box` is a shorthand to a box in [Vagrant Cloud](https://vagrantcloud.com)
+If `config.vm.box` is a shorthand to a box in [HashiCorp's Atlas](https://atlas.hashicorp.com)
then this value doesn't need to be specified. Otherwise, it should
point to the proper place where the box can be found if it isn't
installed.
diff --git a/website/docs/source/v2/vagrantfile/winrm_settings.html.md b/website/docs/source/v2/vagrantfile/winrm_settings.html.md
index 462ccf56e..add316a40 100644
--- a/website/docs/source/v2/vagrantfile/winrm_settings.html.md
+++ b/website/docs/source/v2/vagrantfile/winrm_settings.html.md
@@ -44,3 +44,17 @@ example, if this is set to 5985 (the default), and Vagrant detects a forwarded
port to port 5985 on the guest from port 4567 on the host, Vagrant will attempt
to use port 4567 to talk to the guest if there is no other option.
+
+
+Warning: In order for Vagrant to communicate with a Windows
+guest, you must allow unencrypted WinRM connections on the guest machine
+itself. Some public boxes already have this configured, but if you are
+attempting to `vagrant up` a Windows box and the command hangs at
+`Waiting for WinRM to become available...`, then you will need to run the
+commands below on the guest machine itself, at the box setup stage,
+after provisioning, or through a start up script.
+
+
+Set-Item WSMan:\localhost\Service\AllowUnencrypted -Value True
+Set-Item WSMan:\localhost\Service\Auth\Basic -Value True
+
\ No newline at end of file
diff --git a/website/www/Vagrantfile b/website/www/Vagrantfile
index 7ce24fe70..2d8d8c9b4 100644
--- a/website/www/Vagrantfile
+++ b/website/www/Vagrantfile
@@ -4,13 +4,11 @@
$script = <