Merge branch 'master' into GH4201-ShowRSyncOutput

This commit is contained in:
Chad Maloney 2015-01-05 14:42:08 -06:00
commit dd62d3c5f1
163 changed files with 5614 additions and 609 deletions

View File

@ -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]

8
Vagrantfile vendored
View File

@ -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

View File

@ -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
;;

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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<Symbol>]
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.
#

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -54,6 +54,11 @@ module Vagrant
# @return [Hash<Symbol, Registry>]
attr_reader :provider_capabilities
# This contains all the push implementations by name.
#
# @return [Registry<Symbol, Array<Class, Hash>>]
attr_reader :pushes
# This contains all the synced folder implementations by name.
#
# @return [Registry<Symbol, Array<Class, Integer>>]
@ -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

View File

@ -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]

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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?

View File

@ -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 ""

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -80,7 +80,7 @@ module VagrantPlugins
acc
end
vm.action(:package, opts)
vm.action(:package, **opts)
end
end
end

View File

@ -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<Symbol>] 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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|

View File

@ -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|

View File

@ -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"})

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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: "<push config: #{name}>",
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<Symbol, Array<Class, Object>>]
def __compiled_pushes
raise "Must finalize first!" if !@__finalized
@__compiled_pushes.dup
end
end
end
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 $_"
}
}

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}"

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 }

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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<String>]
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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<String>]
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

View File

@ -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

View File

@ -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

120
plugins/pushes/ftp/push.rb Normal file
View File

@ -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<String>]
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<String>]
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<String>] list
# the filepaths
# @param [Array<String>] 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<String>]
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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,9 @@
module VagrantPlugins
module NoopDeploy
class Push < Vagrant.plugin("2", :push)
def push
puts "pushed"
end
end
end
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,6 +1,3 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.vm.box = "<%= box_name %>"
<% if box_url -%>

View File

@ -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."

View File

@ -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 %>

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

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