Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
6f53c3991c
48
CHANGELOG.md
48
CHANGELOG.md
|
@ -4,12 +4,54 @@ FEATURES:
|
|||
|
||||
IMPROVEMENTS:
|
||||
|
||||
- commands/ssh-config: Properly display windows path if invoked from msys2 or cygwin [GH-8915]
|
||||
- providers/salt: Remove duplicate stdout, stderr output from salt [GH-8767]
|
||||
BUG FIXES:
|
||||
|
||||
## 2.0.0 (September 7, 2017)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
- commands/login: Add support for two-factor authentication [GH-8935]
|
||||
- commands/ssh-config: Properly display windows path if invoked from msys2 or
|
||||
cygwin [GH-8915]
|
||||
- guests/alt: Add support for ALT Linux [GH-8746]
|
||||
- guests/kali: Fix file permissions on guest plugin ruby files [GH-8950]
|
||||
- hosts/linux: Provide common systemd detection for services interaction, fix NFS
|
||||
host interactions [GH-8938]
|
||||
- providers/salt: Remove duplicate stdout, stderr output from salt [GH-8767]
|
||||
- providers/salt: Introduce salt_call_args and salt_args option for salt provisioner
|
||||
[GH-8927]
|
||||
- providers/virtualbox: Improving resilience of some VirtualBox commands [GH-8951]
|
||||
- provisioners/ansible(both): Add the compatibility_mode option, with auto-detection
|
||||
enabled by default [GH-8913, GH-6570]
|
||||
- provisioners/ansible: Add the version option to the host-based provisioner
|
||||
[GH-8913, GH-8914]
|
||||
- provisioners/ansible(both): Add the become and become_user options with deprecation
|
||||
of sudo and sudo_user options [GH-8913, GH-6570]
|
||||
- provisioners/ansible: Add the ask_become_pass option with deprecation of the
|
||||
ask_sudo_pass option [GH-8913, GH-6570]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
- guests/shell_expand_guest_path : Properly expand guest paths that include relative path alias [GH-8918]
|
||||
- guests/shell_expand_guest_path : Properly expand guest paths that include relative
|
||||
path alias [GH-8918]
|
||||
- hosts/linux: Remove duplicate export folders before writing /etc/exports [GH-8945]
|
||||
- provisioners/ansible(both): Add single quotes to the inventory host variables, only
|
||||
when necessary [GH-8597]
|
||||
- provisioners/ansible(both): Add the "all:vars" section to the inventory when defined
|
||||
in `groups` option [GH-7730]
|
||||
- provisioners/ansible_local: Extra variables are no longer truncated when a dollar ($)
|
||||
character is present [GH-7735]
|
||||
- provisioners/file: Align file provisioner functionality on all platforms [GH-8939]
|
||||
- util/ssh: Properly quote key path for IdentityFile option to allow for spaces [GH-8924]
|
||||
|
||||
BREAKING CHANGES:
|
||||
|
||||
- Both Ansible provisioners are now capable of automatically setting the compatibility_mode that
|
||||
best fits with the Ansible version in use. You may encounter some compatibility issues when
|
||||
upgrading. If you were using Ansible 2.x and referring to the _ssh-prefixed variables present
|
||||
in the generated inventory (e.g. `ansible_ssh_host`). In this case, you can fix your Vagrant
|
||||
setup by setting compatibility_mode = "1.8", or by migrating to the new variable names (e.g.
|
||||
ansible_host).
|
||||
|
||||
## 1.9.8 (August 23, 2017)
|
||||
|
||||
|
|
|
@ -468,6 +468,10 @@ module Vagrant
|
|||
error_key(:nfs_bad_exports)
|
||||
end
|
||||
|
||||
class NFSDupePerms < VagrantError
|
||||
error_key(:nfs_dupe_permissions)
|
||||
end
|
||||
|
||||
class NFSExportsFailed < VagrantError
|
||||
error_key(:nfs_exports_failed)
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ module Vagrant
|
|||
|
||||
## systemd helpers
|
||||
|
||||
# systemd is in used
|
||||
# systemd is in use
|
||||
#
|
||||
# @return [Boolean]
|
||||
def systemd?(comm)
|
||||
|
|
|
@ -448,6 +448,19 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# systemd is in use
|
||||
def systemd?
|
||||
if !defined?(@_systemd)
|
||||
if !windows?
|
||||
result = Vagrant::Util::Subprocess.execute("ps", "-o", "comm=", "1")
|
||||
@_systemd = result.stdout.chomp == "systemd"
|
||||
else
|
||||
@_systemd = false
|
||||
end
|
||||
end
|
||||
@_systemd
|
||||
end
|
||||
|
||||
# @private
|
||||
# Reset the cached values for platform. This is not considered a public
|
||||
# API and should only be used for testing.
|
||||
|
|
|
@ -139,7 +139,8 @@ module Vagrant
|
|||
# Use '-o' instead of '-i' because '-i' does not call
|
||||
# percent_expand in misc.c, but '-o' does. when passing the path,
|
||||
# replace '%' in the path with '%%' to escape the '%'
|
||||
command_options += ["-o", "IdentityFile=%s" % [path.to_s.gsub('%', '%%')]]
|
||||
path = path.to_s.gsub('%', '%%')
|
||||
command_options += ["-o", "IdentityFile=\"#{path}\""]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,8 +5,15 @@ require "vagrant/util/presence"
|
|||
module VagrantPlugins
|
||||
module LoginCommand
|
||||
class Client
|
||||
APP = "app".freeze
|
||||
|
||||
include Vagrant::Util::Presence
|
||||
|
||||
attr_accessor :username_or_email
|
||||
attr_accessor :password
|
||||
attr_reader :two_factor_default_delivery_method
|
||||
attr_reader :two_factor_delivery_methods
|
||||
|
||||
# Initializes a login client with the given Vagrant::Environment.
|
||||
#
|
||||
# @param [Vagrant::Environment] env
|
||||
|
@ -35,29 +42,67 @@ module VagrantPlugins
|
|||
RestClient.get(url, content_type: :json)
|
||||
true
|
||||
end
|
||||
rescue Errors::Unauthorized
|
||||
false
|
||||
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] username_or_email
|
||||
# @param [String] password
|
||||
# @param [String] description
|
||||
# @param [String] code
|
||||
# @return [String] token The access token, or nil if auth failed.
|
||||
def login(username_or_email, password, description: nil)
|
||||
def login(description: nil, code: nil)
|
||||
@logger.info("Logging in '#{username_or_email}'")
|
||||
|
||||
with_error_handling do
|
||||
url = "#{Vagrant.server_url}/api/v1/authenticate"
|
||||
request = {
|
||||
response = post(
|
||||
"/api/v1/authenticate", {
|
||||
user: {
|
||||
login: username_or_email,
|
||||
password: password
|
||||
},
|
||||
token: {
|
||||
description: description
|
||||
},
|
||||
two_factor: {
|
||||
code: code
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
response["token"]
|
||||
end
|
||||
|
||||
# Requests a 2FA code
|
||||
# @param [String] delivery_method
|
||||
def request_code(delivery_method)
|
||||
@env.ui.warn("Requesting 2FA code via #{delivery_method.upcase}...")
|
||||
|
||||
response = post(
|
||||
"/api/v1/two-factor/request-code", {
|
||||
user: {
|
||||
login: username_or_email,
|
||||
password: password
|
||||
},
|
||||
two_factor: {
|
||||
delivery_method: delivery_method.downcase
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
two_factor = response['two_factor']
|
||||
obfuscated_destination = two_factor['obfuscated_destination']
|
||||
|
||||
@env.ui.success("2FA code sent to #{obfuscated_destination}.")
|
||||
end
|
||||
|
||||
# Issues a post to a Vagrant Cloud path with the given payload.
|
||||
# @param [String] path
|
||||
# @param [Hash] payload
|
||||
# @return [Hash] response data
|
||||
def post(path, payload)
|
||||
with_error_handling do
|
||||
url = File.join(Vagrant.server_url, path)
|
||||
|
||||
proxy = nil
|
||||
proxy ||= ENV["HTTPS_PROXY"] || ENV["https_proxy"]
|
||||
|
@ -67,7 +112,7 @@ module VagrantPlugins
|
|||
response = RestClient::Request.execute(
|
||||
method: :post,
|
||||
url: url,
|
||||
payload: JSON.dump(request),
|
||||
payload: JSON.dump(payload),
|
||||
proxy: proxy,
|
||||
headers: {
|
||||
accept: :json,
|
||||
|
@ -76,8 +121,7 @@ module VagrantPlugins
|
|||
},
|
||||
)
|
||||
|
||||
data = JSON.load(response.to_s)
|
||||
data["token"]
|
||||
JSON.load(response.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -138,14 +182,33 @@ EOH
|
|||
yield
|
||||
rescue RestClient::Unauthorized
|
||||
@logger.debug("Unauthorized!")
|
||||
false
|
||||
raise Errors::Unauthorized
|
||||
rescue RestClient::BadRequest => e
|
||||
@logger.debug("Bad request:")
|
||||
@logger.debug(e.message)
|
||||
@logger.debug(e.backtrace.join("\n"))
|
||||
parsed_response = JSON.parse(e.response)
|
||||
errors = parsed_response["errors"].join("\n")
|
||||
raise Errors::ServerError, errors: errors
|
||||
rescue RestClient::NotAcceptable => e
|
||||
@logger.debug("Got unacceptable response:")
|
||||
@logger.debug(e.message)
|
||||
@logger.debug(e.backtrace.join("\n"))
|
||||
|
||||
parsed_response = JSON.parse(e.response)
|
||||
|
||||
if two_factor = parsed_response['two_factor']
|
||||
store_two_factor_information two_factor
|
||||
|
||||
if two_factor_default_delivery_method != APP
|
||||
request_code two_factor_default_delivery_method
|
||||
end
|
||||
|
||||
raise Errors::TwoFactorRequired
|
||||
end
|
||||
|
||||
begin
|
||||
errors = JSON.parse(e.response)["errors"].join("\n")
|
||||
errors = parsed_response["errors"].join("\n")
|
||||
raise Errors::ServerError, errors: errors
|
||||
rescue JSON::ParserError; end
|
||||
|
||||
|
@ -158,6 +221,33 @@ EOH
|
|||
def token_path
|
||||
@env.data_dir.join("vagrant_login_token")
|
||||
end
|
||||
|
||||
def store_two_factor_information(two_factor)
|
||||
@two_factor_default_delivery_method =
|
||||
two_factor['default_delivery_method']
|
||||
|
||||
@two_factor_delivery_methods =
|
||||
two_factor['delivery_methods']
|
||||
|
||||
@env.ui.warn "2FA is enabled for your account."
|
||||
if two_factor_default_delivery_method == APP
|
||||
@env.ui.info "Enter the code from your authenticator."
|
||||
else
|
||||
@env.ui.info "Default method is " \
|
||||
"'#{two_factor_default_delivery_method}'."
|
||||
end
|
||||
|
||||
other_delivery_methods =
|
||||
two_factor_delivery_methods - [APP]
|
||||
|
||||
if other_delivery_methods.any?
|
||||
other_delivery_methods_sentence = other_delivery_methods
|
||||
.map { |word| "'#{word}'" }
|
||||
.join(' or ')
|
||||
@env.ui.info "You can also type #{other_delivery_methods_sentence} " \
|
||||
"to request a new code."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,6 +17,10 @@ module VagrantPlugins
|
|||
options[:check] = c
|
||||
end
|
||||
|
||||
o.on("-d", "--description DESCRIPTION", String, "Description for the Vagrant Cloud token") do |t|
|
||||
options[:description] = t
|
||||
end
|
||||
|
||||
o.on("-k", "--logout", "Logs you out if you're logged in") do |k|
|
||||
options[:logout] = k
|
||||
end
|
||||
|
@ -24,6 +28,10 @@ module VagrantPlugins
|
|||
o.on("-t", "--token TOKEN", String, "Set the Vagrant Cloud token") do |t|
|
||||
options[:token] = t
|
||||
end
|
||||
|
||||
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
|
||||
options[:login] = t
|
||||
end
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
|
@ -31,6 +39,7 @@ module VagrantPlugins
|
|||
return if !argv
|
||||
|
||||
@client = Client.new(@env)
|
||||
@client.username_or_email = options[:login]
|
||||
|
||||
# Determine what task we're actually taking based on flags
|
||||
if options[:check]
|
||||
|
@ -50,28 +59,44 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
# Ask for the username
|
||||
login = nil
|
||||
password = nil
|
||||
description = nil
|
||||
while !login
|
||||
login = @env.ui.ask("Vagrant Cloud Username: ")
|
||||
if @client.username_or_email
|
||||
@env.ui.output("Vagrant Cloud username or email: #{@client.username_or_email}")
|
||||
end
|
||||
until @client.username_or_email
|
||||
@client.username_or_email = @env.ui.ask("Vagrant Cloud username or email: ")
|
||||
end
|
||||
|
||||
while !password
|
||||
password = @env.ui.ask("Password (will be hidden): ", echo: false)
|
||||
until @client.password
|
||||
@client.password = @env.ui.ask("Password (will be hidden): ", echo: false)
|
||||
end
|
||||
|
||||
description_default = "Vagrant login from #{Socket.gethostname}"
|
||||
while !description
|
||||
description =
|
||||
@env.ui.ask("Token description (Defaults to #{description_default.inspect}): ")
|
||||
description = options[:description]
|
||||
if description
|
||||
@env.ui.output("Token description: #{description}")
|
||||
else
|
||||
description_default = "Vagrant login from #{Socket.gethostname}"
|
||||
until description
|
||||
description =
|
||||
@env.ui.ask("Token description (Defaults to #{description_default.inspect}): ")
|
||||
end
|
||||
description = description_default if description.empty?
|
||||
end
|
||||
description = description_default if description.empty?
|
||||
|
||||
token = @client.login(login, password, description: description)
|
||||
if !token
|
||||
@env.ui.error(I18n.t("login_command.invalid_login"))
|
||||
return 1
|
||||
code = nil
|
||||
|
||||
begin
|
||||
token = @client.login(description: description, code: code)
|
||||
rescue Errors::TwoFactorRequired
|
||||
until code
|
||||
code = @env.ui.ask("2FA code: ")
|
||||
|
||||
if @client.two_factor_delivery_methods.include?(code.downcase)
|
||||
delivery_method, code = code, nil
|
||||
@client.request_code delivery_method
|
||||
end
|
||||
end
|
||||
|
||||
retry
|
||||
end
|
||||
|
||||
@client.store_token(token)
|
||||
|
|
|
@ -12,6 +12,13 @@ module VagrantPlugins
|
|||
class ServerUnreachable < Error
|
||||
error_key(:server_unreachable)
|
||||
end
|
||||
|
||||
class Unauthorized < Error
|
||||
error_key(:unauthorized)
|
||||
end
|
||||
|
||||
class TwoFactorRequired < Error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,13 +2,16 @@ en:
|
|||
login_command:
|
||||
errors:
|
||||
server_error: |-
|
||||
The Vagrant Cloud server responded with an not-OK response:
|
||||
The Vagrant Cloud server responded with a not-OK response:
|
||||
|
||||
%{errors}
|
||||
server_unreachable: |-
|
||||
The Vagrant Cloud server is not currently accepting connections. Please check
|
||||
your network connection and try again later.
|
||||
|
||||
unauthorized: |-
|
||||
Invalid username or password. Please try again.
|
||||
|
||||
check_logged_in: |-
|
||||
You are already logged in.
|
||||
check_not_logged_in: |-
|
||||
|
|
|
@ -83,9 +83,22 @@ module VagrantPlugins
|
|||
raise_winrm_exception(e, "run_wql", query)
|
||||
end
|
||||
|
||||
# @param from [Array<String>, String] a single path or folder, or an
|
||||
# array of paths and folders to upload to the guest
|
||||
# @param to [String] a path or folder on the guest to upload to
|
||||
# @return [FixNum] Total size transfered from host to guest
|
||||
def upload(from, to)
|
||||
file_manager = WinRM::FS::FileManager.new(connection)
|
||||
file_manager.upload(from, to)
|
||||
if from.is_a?(Array)
|
||||
# Preserve return FixNum of bytes transfered
|
||||
return_bytes = 0
|
||||
from.each do |file|
|
||||
return_bytes += file_manager.upload(file, to)
|
||||
end
|
||||
return return_bytes
|
||||
else
|
||||
file_manager.upload(from, to)
|
||||
end
|
||||
end
|
||||
|
||||
def download(from, to)
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
module VagrantPlugins
|
||||
module GuestALT
|
||||
module Cap
|
||||
class ChangeHostName
|
||||
def self.change_host_name(machine, name)
|
||||
comm = machine.communicate
|
||||
|
||||
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
|
||||
basename = name.split('.', 2)[0]
|
||||
comm.sudo <<-EOH.gsub(/^ {14}/, '')
|
||||
# Save current hostname saved in /etc/hosts
|
||||
CURRENT_HOSTNAME_FULL="$(hostname -f)"
|
||||
CURRENT_HOSTNAME_SHORT="$(hostname -s)"
|
||||
|
||||
# New hostname to be saved in /etc/hosts
|
||||
NEW_HOSTNAME_FULL='#{name}'
|
||||
NEW_HOSTNAME_SHORT="${NEW_HOSTNAME_FULL%%.*}"
|
||||
|
||||
# Update sysconfig
|
||||
sed -i 's/\\(HOSTNAME=\\).*/\\1#{name}/' /etc/sysconfig/network
|
||||
|
||||
# Set the hostname - use hostnamectl if available
|
||||
if command -v hostnamectl; then
|
||||
hostnamectl set-hostname --static '#{name}'
|
||||
hostnamectl set-hostname --transient '#{name}'
|
||||
else
|
||||
hostname '#{name}'
|
||||
fi
|
||||
|
||||
# Update ourselves in /etc/hosts
|
||||
if grep -w "$CURRENT_HOSTNAME_FULL" /etc/hosts; then
|
||||
sed -i -e "s/\(\s\)$CURRENT_HOSTNAME_FULL\(\s\)/\1$NEW_HOSTNAME_FULL\2/g" -e "s/\(\s\)$CURRENT_HOSTNAME_FULL$/\1$NEW_HOSTNAME_FULL/g" /etc/hosts
|
||||
fi
|
||||
if grep -w "$CURRENT_HOSTNAME_SHORT" /etc/hosts; then
|
||||
sed -i -e "s/\(\s\)$CURRENT_HOSTNAME_SHORT\(\s\)/\1$NEW_HOSTNAME_SHORT\2/g" -e "s/\(\s\)$CURRENT_HOSTNAME_SHORT$/\1$NEW_HOSTNAME_SHORT/g" /etc/hosts
|
||||
fi
|
||||
|
||||
# Restart network
|
||||
service network restart
|
||||
EOH
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,126 @@
|
|||
require "tempfile"
|
||||
|
||||
require_relative "../../../../lib/vagrant/util/template_renderer"
|
||||
|
||||
module VagrantPlugins
|
||||
module GuestALT
|
||||
module Cap
|
||||
class ConfigureNetworks
|
||||
include Vagrant::Util
|
||||
extend Vagrant::Util::GuestInspection::Linux
|
||||
|
||||
def self.configure_networks(machine, networks)
|
||||
comm = machine.communicate
|
||||
|
||||
network_scripts_dir = machine.guest.capability(:network_scripts_dir)
|
||||
|
||||
commands = {:start => [], :middle => [], :end => []}
|
||||
interfaces = machine.guest.capability(:network_interfaces)
|
||||
|
||||
# Check if NetworkManager is installed on the system
|
||||
nmcli_installed = nmcli?(comm)
|
||||
networks.each.with_index do |network, i|
|
||||
network[:device] = interfaces[network[:interface]]
|
||||
extra_opts = machine.config.vm.networks[i].last.dup
|
||||
|
||||
if nmcli_installed
|
||||
# Now check if the device is actively being managed by NetworkManager
|
||||
nm_controlled = nm_controlled?(comm, network[:device])
|
||||
end
|
||||
|
||||
if !extra_opts.key?(:nm_controlled)
|
||||
extra_opts[:nm_controlled] = !!nm_controlled
|
||||
end
|
||||
|
||||
extra_opts[:nm_controlled] = case extra_opts[:nm_controlled]
|
||||
when true
|
||||
"yes"
|
||||
when false, nil
|
||||
"no"
|
||||
else
|
||||
extra_opts[:nm_controlled].to_s
|
||||
end
|
||||
|
||||
if extra_opts[:nm_controlled] == "yes" && !nmcli_installed
|
||||
raise Vagrant::Errors::NetworkManagerNotInstalled, device: network[:device]
|
||||
end
|
||||
|
||||
# Render a new configuration
|
||||
template_options = network.merge(extra_opts)
|
||||
|
||||
# ALT expects netmasks to be in the CIDR notation, but users may
|
||||
# specify IPV4 netmasks like "255.255.255.0". This magic converts
|
||||
# the netmask to the proper value.
|
||||
if template_options[:netmask] && template_options[:netmask].to_s.include?(".")
|
||||
template_options[:netmask] = (32-Math.log2((IPAddr.new(template_options[:netmask], Socket::AF_INET).to_i^0xffffffff)+1)).to_i
|
||||
end
|
||||
|
||||
options_entry = TemplateRenderer.render("guests/alt/network_#{network[:type]}", options: template_options)
|
||||
|
||||
# Upload the new configuration
|
||||
options_remote_path = "/tmp/vagrant-network-entry-#{network[:device]}-#{Time.now.to_i}-#{i}"
|
||||
ipv4_address_remote_path = "/tmp/vagrant-network-ipv4-address-entry-#{network[:device]}-#{Time.now.to_i}-#{i}"
|
||||
ipv4_route_remote_path = "/tmp/vagrant-network-ipv4-route-entry-#{network[:device]}-#{Time.now.to_i}-#{i}"
|
||||
|
||||
Tempfile.open("vagrant-alt-configure-networks") do |f|
|
||||
f.binmode
|
||||
f.write(options_entry)
|
||||
f.fsync
|
||||
f.close
|
||||
machine.communicate.upload(f.path, options_remote_path)
|
||||
end
|
||||
|
||||
# Add the new interface and bring it back up
|
||||
iface_path = "#{network_scripts_dir}/ifaces/#{network[:device]}"
|
||||
|
||||
if network[:type].to_sym == :static
|
||||
ipv4_address_entry = TemplateRenderer.render("guests/alt/network_ipv4address", options: template_options)
|
||||
|
||||
# Upload the new ipv4address configuration
|
||||
Tempfile.open("vagrant-alt-configure-ipv4-address") do |f|
|
||||
f.binmode
|
||||
f.write(ipv4_address_entry)
|
||||
f.fsync
|
||||
f.close
|
||||
machine.communicate.upload(f.path, ipv4_address_remote_path)
|
||||
end
|
||||
|
||||
ipv4_route_entry = TemplateRenderer.render("guests/alt/network_ipv4route", options: template_options)
|
||||
|
||||
# Upload the new ipv4route configuration
|
||||
Tempfile.open("vagrant-alt-configure-ipv4-route") do |f|
|
||||
f.binmode
|
||||
f.write(ipv4_route_entry)
|
||||
f.fsync
|
||||
f.close
|
||||
machine.communicate.upload(f.path, ipv4_route_remote_path)
|
||||
end
|
||||
end
|
||||
|
||||
if nm_controlled and extra_opts[:nm_controlled] == "yes"
|
||||
commands[:start] << "nmcli d disconnect iface '#{network[:device]}'"
|
||||
else
|
||||
commands[:start] << "/sbin/ifdown '#{network[:device]}'"
|
||||
end
|
||||
commands[:middle] << "mkdir -p '#{iface_path}'"
|
||||
commands[:middle] << "mv -f '#{options_remote_path}' '#{iface_path}/options'"
|
||||
if network[:type].to_sym == :static
|
||||
commands[:middle] << "mv -f '#{ipv4_address_remote_path}' '#{iface_path}/ipv4address'"
|
||||
commands[:middle] << "mv -f '#{ipv4_route_remote_path}' '#{iface_path}/ipv4route'"
|
||||
end
|
||||
if extra_opts[:nm_controlled] == "no"
|
||||
commands[:end] << "/sbin/ifup '#{network[:device]}'"
|
||||
end
|
||||
end
|
||||
if nmcli_installed
|
||||
commands[:middle] << "((systemctl | grep NetworkManager.service) && systemctl restart NetworkManager) || " \
|
||||
"(test -f /etc/init.d/NetworkManager && /etc/init.d/NetworkManager restart)"
|
||||
end
|
||||
commands = commands[:start] + commands[:middle] + commands[:end]
|
||||
comm.sudo(commands.join("\n"))
|
||||
comm.wait_for_ready(5)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,63 @@
|
|||
module VagrantPlugins
|
||||
module GuestALT
|
||||
module Cap
|
||||
class Flavor
|
||||
def self.flavor(machine)
|
||||
comm = machine.communicate
|
||||
|
||||
# Read the version file
|
||||
if comm.test("test -f /etc/os-release")
|
||||
name = nil
|
||||
comm.sudo("grep NAME /etc/os-release") do |type, data|
|
||||
if type == :stdout
|
||||
name = data.split("=")[1].gsub!(/\A"|"\Z/, '')
|
||||
end
|
||||
end
|
||||
|
||||
if !name.nil? and name == "Sisyphus"
|
||||
return :alt
|
||||
end
|
||||
|
||||
version = nil
|
||||
comm.sudo("grep VERSION_ID /etc/os-release") do |type, data|
|
||||
if type == :stdout
|
||||
verstr = data.split("=")[1]
|
||||
if verstr == "p8"
|
||||
version = 8
|
||||
elsif verstr =~ /^[[\d]]/
|
||||
version = verstr.chomp.to_i
|
||||
subversion = verstr.chomp.split(".")[1].to_i
|
||||
if subversion > 90
|
||||
version += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if version.nil? or version == 0
|
||||
return :alt
|
||||
else
|
||||
return :"alt_#{version}"
|
||||
end
|
||||
else
|
||||
output = ""
|
||||
comm.sudo("cat /etc/altlinux-release") do |_, data|
|
||||
output = data
|
||||
end
|
||||
|
||||
# Detect various flavors we care about
|
||||
if output =~ /(ALT SP|ALT Education|ALT Workstation|ALT Workstation K|ALT Linux starter kit)\s*8(\.[1-9])?( .+)?/i
|
||||
return :alt_8
|
||||
elsif output =~ /ALT\s+8(\.[1-9])?( .+)?\s.+/i
|
||||
return :alt_8
|
||||
elsif output =~ /ALT Linux p8( .+)?/i
|
||||
return :alt_8
|
||||
else
|
||||
return :alt
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
module VagrantPlugins
|
||||
module GuestALT
|
||||
module Cap
|
||||
class NetworkScriptsDir
|
||||
def self.network_scripts_dir(machine)
|
||||
"/etc/net"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
module VagrantPlugins
|
||||
module GuestALT
|
||||
module Cap
|
||||
class RSync
|
||||
def self.rsync_install(machine)
|
||||
machine.communicate.sudo <<-EOH.gsub(/^ {12}/, '')
|
||||
apt-get install -y -qq install rsync
|
||||
EOH
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
module VagrantPlugins
|
||||
module GuestALT
|
||||
class Guest < Vagrant.plugin("2", :guest)
|
||||
def detect?(machine)
|
||||
machine.communicate.test("cat /etc/altlinux-release")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module GuestALT
|
||||
class Plugin < Vagrant.plugin("2")
|
||||
name "ALT Platform guest"
|
||||
description "ALT Platform guest support."
|
||||
|
||||
guest(:alt, :redhat) do
|
||||
require_relative "guest"
|
||||
Guest
|
||||
end
|
||||
|
||||
guest_capability(:alt, :change_host_name) do
|
||||
require_relative "cap/change_host_name"
|
||||
Cap::ChangeHostName
|
||||
end
|
||||
|
||||
guest_capability(:alt, :configure_networks) do
|
||||
require_relative "cap/configure_networks"
|
||||
Cap::ConfigureNetworks
|
||||
end
|
||||
|
||||
guest_capability(:alt, :flavor) do
|
||||
require_relative "cap/flavor"
|
||||
Cap::Flavor
|
||||
end
|
||||
|
||||
guest_capability(:alt, :network_scripts_dir) do
|
||||
require_relative "cap/network_scripts_dir"
|
||||
Cap::NetworkScriptsDir
|
||||
end
|
||||
|
||||
guest_capability(:alt, :rsync_install) do
|
||||
require_relative "cap/rsync"
|
||||
Cap::RSync
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
require "vagrant/util/subprocess"
|
||||
require "vagrant/util/which"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostALT
|
||||
module Cap
|
||||
class NFS
|
||||
def self.nfs_check_command(env)
|
||||
if systemd?
|
||||
return "systemctl status --no-pager nfs-server.service"
|
||||
else
|
||||
return "/etc/init.d/nfs status"
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_start_command(env)
|
||||
if systemd?
|
||||
return "systemctl start rpcbind nfs-server.service"
|
||||
else
|
||||
return "/etc/init.d/nfs restart"
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_installed(environment)
|
||||
if systemd?
|
||||
system("systemctl --no-pager --no-legend --plain list-unit-files --all --type=service | grep --fixed-strings --quiet nfs-server.service")
|
||||
else
|
||||
system("rpm -q nfs-server --quiet 2>&1")
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This tests to see if systemd is used on the system. This is used
|
||||
# in newer versions of ALT, and requires a change in behavior.
|
||||
def self.systemd?
|
||||
result = Vagrant::Util::Subprocess.execute("ps", "-o", "comm=", "1")
|
||||
return result.stdout.chomp == "systemd"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostALT
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
def detect?(env)
|
||||
File.exist?("/etc/altlinux-release")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostALT
|
||||
class Plugin < Vagrant.plugin("2")
|
||||
name "ALT Platform host"
|
||||
description "ALT Platform host support."
|
||||
|
||||
host("alt", "linux") do
|
||||
require_relative "host"
|
||||
Host
|
||||
end
|
||||
|
||||
host_capability("alt", "nfs_installed") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
# Linux-specific helpers we need to determine paths that can
|
||||
# be overriden.
|
||||
host_capability("alt", "nfs_check_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("alt", "nfs_start_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,30 +6,23 @@ module VagrantPlugins
|
|||
module Cap
|
||||
class NFS
|
||||
def self.nfs_check_command(env)
|
||||
if systemd?
|
||||
return "#{systemctl_path} status --no-pager nfs-server.service"
|
||||
if Vagrant::Util::Platform.systemd?
|
||||
"#{systemctl_path} status --no-pager nfs-server.service"
|
||||
else
|
||||
return "/etc/init.d/nfs status"
|
||||
"/etc/init.d/nfs status"
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_start_command(env)
|
||||
if systemd?
|
||||
return "#{systemctl_path} start rpcbind nfs-server.service"
|
||||
if Vagrant::Util::Platform.systemd?
|
||||
"#{systemctl_path} start rpcbind nfs-server.service"
|
||||
else
|
||||
return "/etc/init.d/nfs restart"
|
||||
"/etc/init.d/nfs restart"
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This tests to see if systemd is used on the system. This is used
|
||||
# in newer versions of Arch, and requires a change in behavior.
|
||||
def self.systemd?
|
||||
result = Vagrant::Util::Subprocess.execute("ps", "-o", "comm=", "1")
|
||||
return result.stdout.chomp == "systemd"
|
||||
end
|
||||
|
||||
def self.systemctl_path
|
||||
path = Vagrant::Util::Which.which("systemctl")
|
||||
return path if path
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
require "shellwords"
|
||||
require "vagrant/util"
|
||||
require "vagrant/util/shell_quote"
|
||||
require "vagrant/util/retryable"
|
||||
|
@ -15,11 +16,19 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def self.nfs_check_command(env)
|
||||
"/etc/init.d/nfs-kernel-server status"
|
||||
if Vagrant::Util::Platform.systemd?
|
||||
"systemctl status --no-pager nfs-server.service"
|
||||
else
|
||||
"/etc/init.d/nfs-kernel-server status"
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_start_command(env)
|
||||
"/etc/init.d/nfs-kernel-server start"
|
||||
if Vagrant::Util::Platform.systemd?
|
||||
"systemctl start nfs-server.service"
|
||||
else
|
||||
"/etc/init.d/nfs-kernel-server start"
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_export(env, ui, id, ips, folders)
|
||||
|
@ -29,6 +38,7 @@ module VagrantPlugins
|
|||
nfs_start_command = env.host.capability(:nfs_start_command)
|
||||
|
||||
nfs_opts_setup(folders)
|
||||
folders = folder_dupe_check(folders)
|
||||
output = Vagrant::Util::TemplateRenderer.render('nfs/exports_linux',
|
||||
uuid: id,
|
||||
ips: ips,
|
||||
|
@ -43,16 +53,20 @@ module VagrantPlugins
|
|||
nfs_write_exports(output)
|
||||
|
||||
if nfs_running?(nfs_check_command)
|
||||
system("sudo #{nfs_apply_command}")
|
||||
Vagrant::Util::Subprocess.execute("sudo", *Shellwords.split(nfs_apply_command)).exit_code == 0
|
||||
else
|
||||
system("sudo #{nfs_start_command}")
|
||||
Vagrant::Util::Subprocess.execute("sudo", *Shellwords.split(nfs_start_command)).exit_code == 0
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_installed(environment)
|
||||
retryable(tries: 10, on: TypeError) do
|
||||
# Check procfs to see if NFSd is a supported filesystem
|
||||
system("cat /proc/filesystems | grep nfsd > /dev/null 2>&1")
|
||||
if Vagrant::Util::Platform.systemd?
|
||||
Vagrant::Util::Subprocess.execute("/bin/sh", "-c",
|
||||
"systemctl --no-pager --no-legend --plain list-unit-files --all --type=service " \
|
||||
"| grep nfs-server.service").exit_code == 0
|
||||
else
|
||||
Vagrant::Util::Subprocess.execute("modinfo", "nfsd").exit_code == 0 ||
|
||||
Vagrant::Util::Subprocess.execute("grep", "nfsd", "/proc/filesystems").exit_code == 0
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -84,6 +98,37 @@ module VagrantPlugins
|
|||
|
||||
protected
|
||||
|
||||
# Takes a hash of folders and removes any duplicate exports that
|
||||
# share the same hostpath to avoid duplicate entries in /etc/exports
|
||||
# ref: GH-4666
|
||||
def self.folder_dupe_check(folders)
|
||||
return_folders = {}
|
||||
# Group by hostpath to see if there are multiple exports coming
|
||||
# from the same folder
|
||||
export_groups = folders.values.group_by { |h| h[:hostpath] }
|
||||
|
||||
# We need to check that each group key only has 1 value,
|
||||
# and if not, check each nfs option. If all nfs options are the same
|
||||
# we're good, otherwise throw an exception
|
||||
export_groups.each do |path,group|
|
||||
if group.size > 1
|
||||
# if the linux nfs options aren't all the same throw an exception
|
||||
group1_opts = group.first[:linux__nfs_options]
|
||||
|
||||
if !group.all? {|g| g[:linux__nfs_options] == group1_opts}
|
||||
raise Vagrant::Errors::NFSDupePerms, hostpath: group.first[:hostpath]
|
||||
else
|
||||
# if they're the same just pick the first one
|
||||
return_folders[path] = group.first
|
||||
end
|
||||
else
|
||||
# just return folder, there are no duplicates
|
||||
return_folders[path] = group.first
|
||||
end
|
||||
end
|
||||
return_folders
|
||||
end
|
||||
|
||||
def self.nfs_cleanup(remove_ids)
|
||||
return if !File.exist?(NFS_EXPORTS_PATH)
|
||||
|
||||
|
@ -186,7 +231,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def self.nfs_running?(check_command)
|
||||
system(check_command)
|
||||
Vagrant::Util::Subprocess.execute(*Shellwords.split(check_command)).exit_code == 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,11 +5,19 @@ module VagrantPlugins
|
|||
module Cap
|
||||
class NFS
|
||||
def self.nfs_check_command(env)
|
||||
"#{nfs_server_binary} status"
|
||||
if Vagrant::Util::Platform.systemd?
|
||||
"systemctl status --no-pager nfs-server.service"
|
||||
else
|
||||
"#{nfs_server_binary} status"
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_start_command(env)
|
||||
"#{nfs_server_binary} start"
|
||||
if Vagrant::Util::Platform.systemd?
|
||||
"systemctl start nfs-server.service"
|
||||
else
|
||||
"#{nfs_server_binary} start"
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -85,11 +85,18 @@ module VagrantPlugins
|
|||
|
||||
execute("list", "vms").split("\n").each do |line|
|
||||
if vm_name = line[/^".+?"\s+\{(.+?)\}$/, 1]
|
||||
info = execute("showvminfo", vm_name, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |line|
|
||||
if network_name = line[/^hostonlyadapter\d+="(.+?)"$/, 1]
|
||||
networks.delete(network_name)
|
||||
begin
|
||||
info = execute("showvminfo", vm_name, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |line|
|
||||
if network_name = line[/^hostonlyadapter\d+="(.+?)"$/, 1]
|
||||
networks.delete(network_name)
|
||||
end
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
|
||||
# VirtualBox could not find the vm. It may have been deleted
|
||||
# by another process after we called 'vboxmanage list vms'? Ignore this error.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -422,8 +429,15 @@ module VagrantPlugins
|
|||
# Ignore our own used ports
|
||||
next if uuid == @uuid
|
||||
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
|
||||
ports << hostport
|
||||
begin
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
|
||||
ports << hostport
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
|
||||
# VirtualBox could not find the vm. It may have been deleted
|
||||
# by another process after we called 'vboxmanage list vms'? Ignore this error.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -176,11 +176,18 @@ module VagrantPlugins
|
|||
|
||||
execute("list", "vms").split("\n").each do |line|
|
||||
if vm = line[/^".+?"\s+\{(.+?)\}$/, 1]
|
||||
info = execute("showvminfo", vm, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |line|
|
||||
if adapter = line[/^hostonlyadapter\d+="(.+?)"$/, 1]
|
||||
networks.delete(adapter)
|
||||
begin
|
||||
info = execute("showvminfo", vm, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |line|
|
||||
if adapter = line[/^hostonlyadapter\d+="(.+?)"$/, 1]
|
||||
networks.delete(adapter)
|
||||
end
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
|
||||
# VirtualBox could not find the vm. It may have been deleted
|
||||
# by another process after we called 'vboxmanage list vms'? Ignore this error.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -525,8 +532,15 @@ module VagrantPlugins
|
|||
# Ignore our own used ports
|
||||
next if uuid == @uuid
|
||||
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
|
||||
ports << hostport
|
||||
begin
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
|
||||
ports << hostport
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
|
||||
# VirtualBox could not find the vm. It may have been deleted
|
||||
# by another process after we called 'vboxmanage list vms'? Ignore this error.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -83,11 +83,18 @@ module VagrantPlugins
|
|||
|
||||
execute("list", "vms").split("\n").each do |line|
|
||||
if line =~ /^".+?"\s+\{(.+?)\}$/
|
||||
info = execute("showvminfo", $1.to_s, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |inner_line|
|
||||
if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/
|
||||
networks.delete($1.to_s)
|
||||
begin
|
||||
info = execute("showvminfo", $1.to_s, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |inner_line|
|
||||
if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/
|
||||
networks.delete($1.to_s)
|
||||
end
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
|
||||
# VirtualBox could not find the vm. It may have been deleted
|
||||
# by another process after we called 'vboxmanage list vms'? Ignore this error.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -458,8 +465,15 @@ module VagrantPlugins
|
|||
# Ignore our own used ports
|
||||
next if uuid == @uuid
|
||||
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
|
||||
ports << hostport
|
||||
begin
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
|
||||
ports << hostport
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
|
||||
# VirtualBox could not find the vm. It may have been deleted
|
||||
# by another process after we called 'vboxmanage list vms'? Ignore this error.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -180,11 +180,18 @@ module VagrantPlugins
|
|||
|
||||
execute("list", "vms", retryable: true).split("\n").each do |line|
|
||||
if line =~ /^".+?"\s+\{(.+?)\}$/
|
||||
info = execute("showvminfo", $1.to_s, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |inner_line|
|
||||
if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/
|
||||
networks.delete($1.to_s)
|
||||
begin
|
||||
info = execute("showvminfo", $1.to_s, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |inner_line|
|
||||
if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/
|
||||
networks.delete($1.to_s)
|
||||
end
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
|
||||
# VirtualBox could not find the vm. It may have been deleted
|
||||
# by another process after we called 'vboxmanage list vms'? Ignore this error.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -569,8 +576,15 @@ module VagrantPlugins
|
|||
# Ignore our own used ports
|
||||
next if uuid == @uuid
|
||||
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
|
||||
ports << hostport
|
||||
begin
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
|
||||
ports << hostport
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
|
||||
# VirtualBox could not find the vm. It may have been deleted
|
||||
# by another process after we called 'vboxmanage list vms'? Ignore this error.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,19 +17,23 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def clear_forwarded_ports
|
||||
args = []
|
||||
read_forwarded_ports(@uuid).each do |nic, name, _, _|
|
||||
args.concat(["--natpf#{nic}", "delete", name])
|
||||
end
|
||||
retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do
|
||||
args = []
|
||||
read_forwarded_ports(@uuid).each do |nic, name, _, _|
|
||||
args.concat(["--natpf#{nic}", "delete", name])
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args) if !args.empty?
|
||||
execute("modifyvm", @uuid, *args) if !args.empty?
|
||||
end
|
||||
end
|
||||
|
||||
def clear_shared_folders
|
||||
info = execute("showvminfo", @uuid, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |line|
|
||||
if line =~ /^SharedFolderNameMachineMapping\d+="(.+?)"$/
|
||||
execute("sharedfolder", "remove", @uuid, "--name", $1.to_s)
|
||||
retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do
|
||||
info = execute("showvminfo", @uuid, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |line|
|
||||
if line =~ /^SharedFolderNameMachineMapping\d+="(.+?)"$/
|
||||
execute("sharedfolder", "remove", @uuid, "--name", $1.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -41,22 +45,29 @@ module VagrantPlugins
|
|||
args += ["--snapshot", snapshot_name, "--options", "link"]
|
||||
end
|
||||
|
||||
execute("clonevm", master_id, *args)
|
||||
execute("clonevm", master_id, *args, retryable: true)
|
||||
return get_machine_id(machine_name)
|
||||
end
|
||||
|
||||
def create_dhcp_server(network, options)
|
||||
execute("dhcpserver", "add", "--ifname", network,
|
||||
"--ip", options[:dhcp_ip],
|
||||
"--netmask", options[:netmask],
|
||||
"--lowerip", options[:dhcp_lower],
|
||||
"--upperip", options[:dhcp_upper],
|
||||
"--enable")
|
||||
retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do
|
||||
begin
|
||||
execute("dhcpserver", "add", "--ifname", network,
|
||||
"--ip", options[:dhcp_ip],
|
||||
"--netmask", options[:netmask],
|
||||
"--lowerip", options[:dhcp_lower],
|
||||
"--upperip", options[:dhcp_upper],
|
||||
"--enable")
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
return if e.extra_data[:stderr] == 'VBoxManage: error: DHCP server already exists'
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_host_only_network(options)
|
||||
# Create the interface
|
||||
execute("hostonlyif", "create") =~ /^Interface '(.+?)' was successfully created$/
|
||||
execute("hostonlyif", "create", retryable: true) =~ /^Interface '(.+?)' was successfully created$/
|
||||
name = $1.to_s
|
||||
|
||||
# Get the IP so we can determine v4 vs v6
|
||||
|
@ -66,11 +77,13 @@ module VagrantPlugins
|
|||
if ip.ipv4?
|
||||
execute("hostonlyif", "ipconfig", name,
|
||||
"--ip", options[:adapter_ip],
|
||||
"--netmask", options[:netmask])
|
||||
"--netmask", options[:netmask],
|
||||
retryable: true)
|
||||
elsif ip.ipv6?
|
||||
execute("hostonlyif", "ipconfig", name,
|
||||
"--ipv6", options[:adapter_ip],
|
||||
"--netmasklengthv6", options[:netmask].to_s)
|
||||
"--netmasklengthv6", options[:netmask].to_s,
|
||||
retryable: true)
|
||||
else
|
||||
raise "BUG: Unknown IP type: #{ip.inspect}"
|
||||
end
|
||||
|
@ -85,7 +98,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def create_snapshot(machine_id, snapshot_name)
|
||||
execute("snapshot", machine_id, "take", snapshot_name)
|
||||
execute("snapshot", machine_id, "take", snapshot_name, retryable: true)
|
||||
end
|
||||
|
||||
def delete_snapshot(machine_id, snapshot_name)
|
||||
|
@ -95,7 +108,7 @@ module VagrantPlugins
|
|||
yield 0 if block_given?
|
||||
|
||||
# Snapshot and report the % progress
|
||||
execute("snapshot", machine_id, "delete", snapshot_name) do |type, data|
|
||||
execute("snapshot", machine_id, "delete", snapshot_name, retryable: true) do |type, data|
|
||||
if type == :stderr
|
||||
# Append the data so we can see the full view
|
||||
total << data.gsub("\r", "")
|
||||
|
@ -142,7 +155,7 @@ module VagrantPlugins
|
|||
total = ""
|
||||
yield 0 if block_given?
|
||||
|
||||
execute("snapshot", machine_id, "restore", snapshot_name) do |type, data|
|
||||
execute("snapshot", machine_id, "restore", snapshot_name, retryable: true) do |type, data|
|
||||
if type == :stderr
|
||||
# Append the data so we can see the full view
|
||||
total << data.gsub("\r", "")
|
||||
|
@ -165,7 +178,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def delete
|
||||
execute("unregistervm", @uuid, "--delete")
|
||||
execute("unregistervm", @uuid, "--delete", retryable: true)
|
||||
end
|
||||
|
||||
def delete_unused_host_only_networks
|
||||
|
@ -176,11 +189,18 @@ module VagrantPlugins
|
|||
|
||||
execute("list", "vms", retryable: true).split("\n").each do |line|
|
||||
if line =~ /^".+?"\s+\{(.+?)\}$/
|
||||
info = execute("showvminfo", $1.to_s, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |inner_line|
|
||||
if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/
|
||||
networks.delete($1.to_s)
|
||||
begin
|
||||
info = execute("showvminfo", $1.to_s, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |inner_line|
|
||||
if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/
|
||||
networks.delete($1.to_s)
|
||||
end
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
|
||||
# VirtualBox could not find the vm. It may have been deleted
|
||||
# by another process after we called 'vboxmanage list vms'? Ignore this error.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -192,12 +212,12 @@ module VagrantPlugins
|
|||
raw("dhcpserver", "remove", "--ifname", name)
|
||||
|
||||
# Delete the actual host only network interface.
|
||||
execute("hostonlyif", "remove", name)
|
||||
execute("hostonlyif", "remove", name, retryable: true)
|
||||
end
|
||||
end
|
||||
|
||||
def discard_saved_state
|
||||
execute("discardstate", @uuid)
|
||||
execute("discardstate", @uuid, retryable: true)
|
||||
end
|
||||
|
||||
def enable_adapters(adapters)
|
||||
|
@ -230,7 +250,7 @@ module VagrantPlugins
|
|||
end
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args)
|
||||
execute("modifyvm", @uuid, *args, retryable: true)
|
||||
end
|
||||
|
||||
def execute_command(command)
|
||||
|
@ -238,7 +258,17 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def export(path)
|
||||
execute("export", @uuid, "--output", path.to_s)
|
||||
retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do
|
||||
begin
|
||||
execute("export", @uuid, "--output", path.to_s)
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VERR_E_FILE_ERROR")
|
||||
|
||||
# If the file already exists we'll throw a custom error
|
||||
raise Vagrant::Errors::VirtualBoxFileExists,
|
||||
stderr: e.extra_data[:stderr]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def forward_ports(ports)
|
||||
|
@ -255,7 +285,7 @@ module VagrantPlugins
|
|||
pf_builder.join(",")])
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args) if !args.empty?
|
||||
execute("modifyvm", @uuid, *args, retryable: true) if !args.empty?
|
||||
end
|
||||
|
||||
def get_machine_id(machine_name)
|
||||
|
@ -266,7 +296,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def halt
|
||||
execute("controlvm", @uuid, "poweroff")
|
||||
execute("controlvm", @uuid, "poweroff", retryable: true)
|
||||
end
|
||||
|
||||
def import(ovf)
|
||||
|
@ -315,7 +345,7 @@ module VagrantPlugins
|
|||
end
|
||||
end
|
||||
|
||||
execute("import", ovf , *name_params, *disk_params) do |type, data|
|
||||
execute("import", ovf , *name_params, *disk_params, retryable: true) do |type, data|
|
||||
if type == :stdout
|
||||
# Keep track of the stdout so that we can get the VM name
|
||||
output << data
|
||||
|
@ -567,9 +597,16 @@ module VagrantPlugins
|
|||
# Ignore our own used ports
|
||||
next if uuid == @uuid
|
||||
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _, hostip|
|
||||
hostip = '*' if hostip.nil? || hostip.empty?
|
||||
used_ports[hostport].add?(hostip)
|
||||
begin
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _, hostip|
|
||||
hostip = '*' if hostip.nil? || hostip.empty?
|
||||
used_ports[hostport].add?(hostip)
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
|
||||
# VirtualBox could not find the vm. It may have been deleted
|
||||
# by another process after we called 'vboxmanage list vms'? Ignore this error.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -590,26 +627,30 @@ module VagrantPlugins
|
|||
|
||||
def reconfig_host_only(interface)
|
||||
execute("hostonlyif", "ipconfig", interface[:name],
|
||||
"--ipv6", interface[:ipv6])
|
||||
"--ipv6", interface[:ipv6], retryable: true)
|
||||
end
|
||||
|
||||
def remove_dhcp_server(network_name)
|
||||
execute("dhcpserver", "remove", "--netname", network_name)
|
||||
execute("dhcpserver", "remove", "--netname", network_name, retryable: true)
|
||||
end
|
||||
|
||||
def set_mac_address(mac)
|
||||
execute("modifyvm", @uuid, "--macaddress1", mac)
|
||||
execute("modifyvm", @uuid, "--macaddress1", mac, retryable: true)
|
||||
end
|
||||
|
||||
def set_name(name)
|
||||
execute("modifyvm", @uuid, "--name", name, retryable: true)
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VERR_ALREADY_EXISTS")
|
||||
retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do
|
||||
begin
|
||||
execute("modifyvm", @uuid, "--name", name)
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VERR_ALREADY_EXISTS")
|
||||
|
||||
# We got VERR_ALREADY_EXISTS. This means that we're renaming to
|
||||
# a VM name that already exists. Raise a custom error.
|
||||
raise Vagrant::Errors::VirtualBoxNameExists,
|
||||
stderr: e.extra_data[:stderr]
|
||||
# We got VERR_ALREADY_EXISTS. This means that we're renaming to
|
||||
# a VM name that already exists. Raise a custom error.
|
||||
raise Vagrant::Errors::VirtualBoxNameExists,
|
||||
stderr: e.extra_data[:stderr]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def share_folders(folders)
|
||||
|
@ -633,10 +674,10 @@ module VagrantPlugins
|
|||
args << "--transient" if folder.key?(:transient) && folder[:transient]
|
||||
|
||||
# Enable symlinks on the shared folder
|
||||
execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1")
|
||||
execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1", retryable: true)
|
||||
|
||||
# Add the shared folder
|
||||
execute("sharedfolder", "add", @uuid, *args)
|
||||
execute("sharedfolder", "add", @uuid, *args, retryable: true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -658,40 +699,40 @@ module VagrantPlugins
|
|||
|
||||
def start(mode)
|
||||
command = ["startvm", @uuid, "--type", mode.to_s]
|
||||
r = raw(*command)
|
||||
retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do
|
||||
r = raw(*command)
|
||||
|
||||
if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/
|
||||
# Some systems return an exit code 1 for some reason. For that
|
||||
# we depend on the output.
|
||||
return true
|
||||
if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/
|
||||
# Some systems return an exit code 1 for some reason. For that
|
||||
# we depend on the output.
|
||||
return true
|
||||
end
|
||||
|
||||
# If we reached this point then it didn't work out.
|
||||
raise Vagrant::Errors::VBoxManageError,
|
||||
command: command.inspect,
|
||||
stderr: r.stderr
|
||||
end
|
||||
|
||||
# If we reached this point then it didn't work out.
|
||||
raise Vagrant::Errors::VBoxManageError,
|
||||
command: command.inspect,
|
||||
stderr: r.stderr
|
||||
end
|
||||
|
||||
def suspend
|
||||
execute("controlvm", @uuid, "savestate")
|
||||
execute("controlvm", @uuid, "savestate", retryable: true)
|
||||
end
|
||||
|
||||
def unshare_folders(names)
|
||||
names.each do |name|
|
||||
begin
|
||||
execute(
|
||||
"sharedfolder", "remove", @uuid,
|
||||
"--name", name,
|
||||
"--transient")
|
||||
retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do
|
||||
begin
|
||||
execute(
|
||||
"sharedfolder", "remove", @uuid,
|
||||
"--name", name,
|
||||
"--transient")
|
||||
|
||||
execute(
|
||||
"setextradata", @uuid,
|
||||
"VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{name}")
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
if e.extra_data[:stderr].include?("VBOX_E_FILE_ERROR")
|
||||
# The folder doesn't exist. ignore.
|
||||
else
|
||||
raise
|
||||
execute(
|
||||
"setextradata", @uuid,
|
||||
"VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{name}")
|
||||
rescue Vagrant::Errors::VBoxManageError => e
|
||||
raise if !e.extra_data[:stderr].include?("VBOX_E_FILE_ERROR")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require_relative "../constants"
|
||||
|
||||
module VagrantPlugins
|
||||
module Ansible
|
||||
module Config
|
||||
|
@ -6,6 +8,9 @@ module VagrantPlugins
|
|||
GALAXY_COMMAND_DEFAULT = "ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force".freeze
|
||||
PLAYBOOK_COMMAND_DEFAULT = "ansible-playbook".freeze
|
||||
|
||||
attr_accessor :become
|
||||
attr_accessor :become_user
|
||||
attr_accessor :compatibility_mode
|
||||
attr_accessor :config_file
|
||||
attr_accessor :extra_vars
|
||||
attr_accessor :galaxy_role_file
|
||||
|
@ -20,13 +25,29 @@ module VagrantPlugins
|
|||
attr_accessor :raw_arguments
|
||||
attr_accessor :skip_tags
|
||||
attr_accessor :start_at_task
|
||||
attr_accessor :sudo
|
||||
attr_accessor :sudo_user
|
||||
attr_accessor :tags
|
||||
attr_accessor :vault_password_file
|
||||
attr_accessor :verbose
|
||||
attr_accessor :version
|
||||
|
||||
#
|
||||
# Deprecated options
|
||||
#
|
||||
alias :sudo :become
|
||||
def sudo=(value)
|
||||
show_deprecation_info 'sudo', 'become'
|
||||
@become = value
|
||||
end
|
||||
alias :sudo_user :become_user
|
||||
def sudo_user=(value)
|
||||
show_deprecation_info 'sudo_user', 'become_user'
|
||||
@become_user = value
|
||||
end
|
||||
|
||||
def initialize
|
||||
@become = UNSET_VALUE
|
||||
@become_user = UNSET_VALUE
|
||||
@compatibility_mode = Ansible::COMPATIBILITY_MODE_AUTO
|
||||
@config_file = UNSET_VALUE
|
||||
@extra_vars = UNSET_VALUE
|
||||
@galaxy_role_file = UNSET_VALUE
|
||||
|
@ -41,14 +62,16 @@ module VagrantPlugins
|
|||
@raw_arguments = UNSET_VALUE
|
||||
@skip_tags = UNSET_VALUE
|
||||
@start_at_task = UNSET_VALUE
|
||||
@sudo = UNSET_VALUE
|
||||
@sudo_user = UNSET_VALUE
|
||||
@tags = UNSET_VALUE
|
||||
@vault_password_file = UNSET_VALUE
|
||||
@verbose = UNSET_VALUE
|
||||
@version = UNSET_VALUE
|
||||
end
|
||||
|
||||
def finalize!
|
||||
@become = false if @become != true
|
||||
@become_user = nil if @become_user == UNSET_VALUE
|
||||
@compatibility_mode = nil unless Ansible::COMPATIBILITY_MODES.include?(@compatibility_mode)
|
||||
@config_file = nil if @config_file == UNSET_VALUE
|
||||
@extra_vars = nil if @extra_vars == UNSET_VALUE
|
||||
@galaxy_role_file = nil if @galaxy_role_file == UNSET_VALUE
|
||||
|
@ -63,11 +86,10 @@ module VagrantPlugins
|
|||
@raw_arguments = nil if @raw_arguments == UNSET_VALUE
|
||||
@skip_tags = nil if @skip_tags == UNSET_VALUE
|
||||
@start_at_task = nil if @start_at_task == UNSET_VALUE
|
||||
@sudo = false if @sudo != true
|
||||
@sudo_user = nil if @sudo_user == UNSET_VALUE
|
||||
@tags = nil if @tags == UNSET_VALUE
|
||||
@vault_password_file = nil if @vault_password_file == UNSET_VALUE
|
||||
@verbose = false if @verbose == UNSET_VALUE
|
||||
@version = "" if @version == UNSET_VALUE
|
||||
end
|
||||
|
||||
# Just like the normal configuration "validate" method except that
|
||||
|
@ -76,6 +98,12 @@ module VagrantPlugins
|
|||
def validate(machine)
|
||||
@errors = _detected_errors
|
||||
|
||||
# Validate that a compatibility mode was provided
|
||||
if !compatibility_mode
|
||||
@errors << I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode",
|
||||
valid_modes: Ansible::COMPATIBILITY_MODES.map { |s| "'#{s}'" }.join(', '))
|
||||
end
|
||||
|
||||
# Validate that a playbook path was provided
|
||||
if !playbook
|
||||
@errors << I18n.t("vagrant.provisioners.ansible.errors.no_playbook")
|
||||
|
@ -112,6 +140,14 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def show_deprecation_info(deprecated_option, new_option)
|
||||
puts "DEPRECATION: The '#{deprecated_option}' option for the Ansible provisioner is deprecated."
|
||||
puts "Please use the '#{new_option}' option instead."
|
||||
puts "The '#{deprecated_option}' option will be removed in a future release of Vagrant.\n\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,7 +11,6 @@ module VagrantPlugins
|
|||
attr_accessor :install
|
||||
attr_accessor :install_mode
|
||||
attr_accessor :pip_args
|
||||
attr_accessor :version
|
||||
|
||||
def initialize
|
||||
super
|
||||
|
@ -21,7 +20,6 @@ module VagrantPlugins
|
|||
@pip_args = UNSET_VALUE
|
||||
@provisioning_path = UNSET_VALUE
|
||||
@tmp_path = UNSET_VALUE
|
||||
@version = UNSET_VALUE
|
||||
end
|
||||
|
||||
def finalize!
|
||||
|
@ -32,7 +30,6 @@ module VagrantPlugins
|
|||
@pip_args = "" if @pip_args == UNSET_VALUE
|
||||
@provisioning_path = "/vagrant" if provisioning_path == UNSET_VALUE
|
||||
@tmp_path = "/tmp/vagrant-ansible" if tmp_path == UNSET_VALUE
|
||||
@version = "" if @version == UNSET_VALUE
|
||||
end
|
||||
|
||||
def validate(machine)
|
||||
|
|
|
@ -5,16 +5,25 @@ module VagrantPlugins
|
|||
module Config
|
||||
class Host < Base
|
||||
|
||||
attr_accessor :ask_sudo_pass
|
||||
attr_accessor :ask_become_pass
|
||||
attr_accessor :ask_vault_pass
|
||||
attr_accessor :force_remote_user
|
||||
attr_accessor :host_key_checking
|
||||
attr_accessor :raw_ssh_args
|
||||
|
||||
#
|
||||
# Deprecated options
|
||||
#
|
||||
alias :ask_sudo_pass :ask_become_pass
|
||||
def ask_sudo_pass=(value)
|
||||
show_deprecation_warning 'ask_sudo_pass', 'ask_become_pass'
|
||||
@ask_become_pass = value
|
||||
end
|
||||
|
||||
def initialize
|
||||
super
|
||||
|
||||
@ask_sudo_pass = false
|
||||
@ask_become_pass = false
|
||||
@ask_vault_pass = false
|
||||
@force_remote_user = true
|
||||
@host_key_checking = false
|
||||
|
@ -24,7 +33,7 @@ module VagrantPlugins
|
|||
def finalize!
|
||||
super
|
||||
|
||||
@ask_sudo_pass = false if @ask_sudo_pass != true
|
||||
@ask_become_pass = false if @ask_become_pass != true
|
||||
@ask_vault_pass = false if @ask_vault_pass != true
|
||||
@force_remote_user = true if @force_remote_user != false
|
||||
@host_key_checking = false if @host_key_checking != true
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
module VagrantPlugins
|
||||
module Ansible
|
||||
COMPATIBILITY_MODE_AUTO = "auto".freeze
|
||||
COMPATIBILITY_MODE_V1_8 = "1.8".freeze
|
||||
COMPATIBILITY_MODE_V2_0 = "2.0".freeze
|
||||
SAFE_COMPATIBILITY_MODE = COMPATIBILITY_MODE_V1_8
|
||||
COMPATIBILITY_MODES = [
|
||||
COMPATIBILITY_MODE_AUTO,
|
||||
COMPATIBILITY_MODE_V1_8,
|
||||
COMPATIBILITY_MODE_V2_0,
|
||||
].freeze
|
||||
end
|
||||
end
|
|
@ -11,21 +11,30 @@ module VagrantPlugins
|
|||
error_key(:ansible_command_failed)
|
||||
end
|
||||
|
||||
class AnsibleNotFoundOnHost < AnsibleError
|
||||
error_key(:ansible_not_found_on_host)
|
||||
class AnsibleCompatibilityModeConflict < AnsibleError
|
||||
error_key(:ansible_compatibility_mode_conflict)
|
||||
end
|
||||
|
||||
class AnsibleNotFoundOnGuest < AnsibleError
|
||||
error_key(:ansible_not_found_on_guest)
|
||||
end
|
||||
|
||||
class AnsibleNotFoundOnHost < AnsibleError
|
||||
error_key(:ansible_not_found_on_host)
|
||||
end
|
||||
|
||||
class AnsiblePipInstallIsNotSupported < AnsibleError
|
||||
error_key(:cannot_support_pip_install)
|
||||
end
|
||||
|
||||
class AnsibleVersionNotFoundOnGuest < AnsibleError
|
||||
error_key(:ansible_version_not_found_on_guest)
|
||||
class AnsibleProgrammingError < AnsibleError
|
||||
error_key(:ansible_programming_error)
|
||||
end
|
||||
|
||||
class AnsibleVersionMismatch < AnsibleError
|
||||
error_key(:ansible_version_mismatch)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +1,4 @@
|
|||
require_relative "../constants"
|
||||
require_relative "../errors"
|
||||
require_relative "../helpers"
|
||||
|
||||
|
@ -14,15 +15,80 @@ module VagrantPlugins
|
|||
|
||||
RANGE_PATTERN = %r{(?:\[[a-z]:[a-z]\]|\[[0-9]+?:[0-9]+?\])}.freeze
|
||||
|
||||
ANSIBLE_PARAMETER_NAMES = {
|
||||
Ansible::COMPATIBILITY_MODE_V1_8 => {
|
||||
ansible_host: "ansible_ssh_host",
|
||||
ansible_password: "ansible_ssh_pass",
|
||||
ansible_port: "ansible_ssh_port",
|
||||
ansible_user: "ansible_ssh_user",
|
||||
ask_become_pass: "ask-sudo-pass",
|
||||
become: "sudo",
|
||||
become_user: "sudo-user",
|
||||
},
|
||||
Ansible::COMPATIBILITY_MODE_V2_0 => {
|
||||
ansible_host: "ansible_host",
|
||||
ansible_password: "ansible_password",
|
||||
ansible_port: "ansible_port",
|
||||
ansible_user: "ansible_user",
|
||||
ask_become_pass: "ask-become-pass",
|
||||
become: "become",
|
||||
become_user: "become-user",
|
||||
}
|
||||
}
|
||||
|
||||
protected
|
||||
|
||||
def initialize(machine, config)
|
||||
super
|
||||
@control_machine = nil
|
||||
|
||||
@command_arguments = []
|
||||
@environment_variables = {}
|
||||
@inventory_machines = {}
|
||||
@inventory_path = nil
|
||||
|
||||
@gathered_version_stdout = nil
|
||||
@gathered_version_major = nil
|
||||
@gathered_version = nil
|
||||
end
|
||||
|
||||
def set_and_check_compatibility_mode
|
||||
begin
|
||||
set_gathered_ansible_version(gather_ansible_version)
|
||||
rescue StandardError => e
|
||||
# Nothing to do here, as the fallback on safe compatibility_mode is done below
|
||||
@logger.error("Error while gathering the ansible version: #{e.to_s}")
|
||||
end
|
||||
|
||||
if @gathered_version_major
|
||||
if config.compatibility_mode == Ansible::COMPATIBILITY_MODE_AUTO
|
||||
detect_compatibility_mode
|
||||
elsif @gathered_version_major.to_i < 2 && config.compatibility_mode == Ansible::COMPATIBILITY_MODE_V2_0
|
||||
# A better version comparator will be needed
|
||||
# when more compatibility modes come... but so far let's keep it simple!
|
||||
raise Ansible::Errors::AnsibleCompatibilityModeConflict,
|
||||
ansible_version: @gathered_version,
|
||||
system: @control_machine,
|
||||
compatibility_mode: config.compatibility_mode
|
||||
end
|
||||
end
|
||||
|
||||
if config.compatibility_mode == Ansible::COMPATIBILITY_MODE_AUTO
|
||||
config.compatibility_mode = Ansible::SAFE_COMPATIBILITY_MODE
|
||||
|
||||
@machine.env.ui.warn(I18n.t("vagrant.provisioners.ansible.compatibility_mode_not_detected",
|
||||
compatibility_mode: config.compatibility_mode,
|
||||
gathered_version: @gathered_version_stdout) +
|
||||
"\n")
|
||||
end
|
||||
|
||||
unless Ansible::COMPATIBILITY_MODES.slice(1..-1).include?(config.compatibility_mode)
|
||||
raise Ansible::Errors::AnsibleProgrammingError,
|
||||
message: "The config.compatibility_mode must be correctly set at this stage!",
|
||||
details: "config.compatibility_mode: '#{config.compatibility_mode}'"
|
||||
end
|
||||
|
||||
@lexicon = ANSIBLE_PARAMETER_NAMES[config.compatibility_mode]
|
||||
end
|
||||
|
||||
def check_files_existence
|
||||
|
@ -70,7 +136,7 @@ module VagrantPlugins
|
|||
if arg =~ /(--start-at-task|--limit)=(.+)/
|
||||
shell_args << %Q(#{$1}="#{$2}")
|
||||
elsif arg =~ /(--extra-vars)=(.+)/
|
||||
shell_args << %Q(%s="%s") % [$1, $2.gsub('\\', '\\\\\\').gsub('"', %Q(\\"))]
|
||||
shell_args << %Q(%s=%s) % [$1, $2.shellescape]
|
||||
else
|
||||
shell_args << arg
|
||||
end
|
||||
|
@ -97,8 +163,8 @@ module VagrantPlugins
|
|||
|
||||
@command_arguments << "--inventory-file=#{inventory_path}"
|
||||
@command_arguments << "--extra-vars=#{extra_vars_argument}" if config.extra_vars
|
||||
@command_arguments << "--sudo" if config.sudo
|
||||
@command_arguments << "--sudo-user=#{config.sudo_user}" if config.sudo_user
|
||||
@command_arguments << "--#{@lexicon[:become]}" if config.become
|
||||
@command_arguments << "--#{@lexicon[:become_user]}=#{config.become_user}" if config.become_user
|
||||
@command_arguments << "#{verbosity_argument}" if verbosity_is_enabled?
|
||||
@command_arguments << "--vault-password-file=#{config.vault_password_file}" if config.vault_password_file
|
||||
@command_arguments << "--tags=#{Helpers::as_list_argument(config.tags)}" if config.tags
|
||||
|
@ -148,7 +214,13 @@ module VagrantPlugins
|
|||
end
|
||||
s = nil
|
||||
if vars.is_a?(Hash)
|
||||
s = vars.each.collect{ |k, v| "#{k}=#{v}" }.join(" ")
|
||||
s = vars.each.collect {
|
||||
|k, v|
|
||||
if v.is_a?(String) && v.include?(' ') && !v.match(/^('|")[^'"]+('|")$/)
|
||||
v = %Q('#{v}')
|
||||
end
|
||||
"#{k}=#{v}"
|
||||
}.join(" ")
|
||||
elsif vars.is_a?(Array)
|
||||
s = vars.join(" ")
|
||||
elsif vars.is_a?(String)
|
||||
|
@ -228,7 +300,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
group_vars.each_pair do |gname, gmembers|
|
||||
if defined_groups.include?(gname.sub(/:vars$/, ""))
|
||||
if defined_groups.include?(gname.sub(/:vars$/, "")) || gname == "all:vars"
|
||||
inventory_groups += "\n[#{gname}]\n" + gmembers.join("\n") + "\n"
|
||||
end
|
||||
end
|
||||
|
@ -285,6 +357,44 @@ module VagrantPlugins
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def detect_compatibility_mode
|
||||
if !@gathered_version_major || config.compatibility_mode != Ansible::COMPATIBILITY_MODE_AUTO
|
||||
raise Ansible::Errors::AnsibleProgrammingError,
|
||||
message: "The detect_compatibility_mode() function shouldn't have been called!",
|
||||
details: %Q(config.compatibility_mode: '#{config.compatibility_mode}'
|
||||
gathered version major number: '#{@gathered_version_major}'
|
||||
gathered version stdout version:
|
||||
#{@gathered_version_stdout})
|
||||
end
|
||||
|
||||
if @gathered_version_major.to_i <= 1
|
||||
config.compatibility_mode = Ansible::COMPATIBILITY_MODE_V1_8
|
||||
else
|
||||
config.compatibility_mode = Ansible::COMPATIBILITY_MODE_V2_0
|
||||
end
|
||||
|
||||
@machine.env.ui.warn(I18n.t("vagrant.provisioners.ansible.compatibility_mode_warning",
|
||||
compatibility_mode: config.compatibility_mode,
|
||||
ansible_version: @gathered_version) +
|
||||
"\n")
|
||||
end
|
||||
|
||||
def set_gathered_ansible_version(stdout_output)
|
||||
@gathered_version_stdout = stdout_output
|
||||
if !@gathered_version_stdout.empty?
|
||||
first_line = @gathered_version_stdout.lines[0]
|
||||
ansible_version_pattern = first_line.match(/(^ansible\s+)(.+)$/)
|
||||
if ansible_version_pattern
|
||||
_, @gathered_version, _ = ansible_version_pattern.captures
|
||||
if @gathered_version
|
||||
@gathered_version_major = @gathered_version.match(/^(\d)\..+$/).captures[0].to_i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,12 +10,14 @@ module VagrantPlugins
|
|||
|
||||
def initialize(machine, config)
|
||||
super
|
||||
@control_machine = "guest"
|
||||
@logger = Log4r::Logger.new("vagrant::provisioners::ansible_guest")
|
||||
end
|
||||
|
||||
def provision
|
||||
check_files_existence
|
||||
check_and_install_ansible
|
||||
|
||||
execute_ansible_galaxy_on_guest if config.galaxy_role_file
|
||||
execute_ansible_playbook_on_guest
|
||||
end
|
||||
|
@ -25,14 +27,14 @@ module VagrantPlugins
|
|||
#
|
||||
# This handles verifying the Ansible installation, installing it if it was
|
||||
# requested, and so on. This method will raise exceptions if things are wrong.
|
||||
# The compatibility mode checks are also performed here in order to fetch the
|
||||
# Ansible version information only once.
|
||||
#
|
||||
# Current limitations:
|
||||
# - The installation of a specific Ansible version is not supported.
|
||||
# Such feature is difficult to systematically provide via package repositories (apt, yum, ...).
|
||||
# Installing via pip python packaging or directly from github source would be appropriate,
|
||||
# but these approaches require more dependency burden.
|
||||
# - There is no guarantee that the automated installation will replace
|
||||
# a previous Ansible installation.
|
||||
# - The installation of a specific Ansible version is only supported by
|
||||
# the "pip" install_mode.
|
||||
# - There is no absolute guarantee that the automated installation will replace
|
||||
# a previous Ansible installation (although it works fine in many cases)
|
||||
#
|
||||
def check_and_install_ansible
|
||||
@logger.info("Checking for Ansible installation...")
|
||||
|
@ -52,21 +54,39 @@ module VagrantPlugins
|
|||
@machine.guest.capability(:ansible_install, config.install_mode, config.version, config.pip_args)
|
||||
end
|
||||
|
||||
# Check that Ansible Playbook command is available on the guest
|
||||
@machine.communicate.execute(
|
||||
"test -x \"$(command -v #{config.playbook_command})\"",
|
||||
error_class: Ansible::Errors::AnsibleNotFoundOnGuest,
|
||||
error_key: :ansible_not_found_on_guest
|
||||
)
|
||||
# This step will also fetch the Ansible version data into related instance variables
|
||||
set_and_check_compatibility_mode
|
||||
|
||||
# Check if requested ansible version is available
|
||||
if (!config.version.empty? &&
|
||||
config.version.to_s.to_sym != :latest &&
|
||||
!@machine.guest.capability(:ansible_installed, config.version))
|
||||
raise Ansible::Errors::AnsibleVersionNotFoundOnGuest, required_version: config.version.to_s
|
||||
config.version != @gathered_version)
|
||||
raise Ansible::Errors::AnsibleVersionMismatch,
|
||||
system: @control_machine,
|
||||
required_version: config.version,
|
||||
current_version: @gathered_version
|
||||
end
|
||||
end
|
||||
|
||||
def gather_ansible_version
|
||||
raw_output = ""
|
||||
|
||||
result = @machine.communicate.execute(
|
||||
"ansible --version",
|
||||
error_class: Ansible::Errors::AnsibleNotFoundOnGuest,
|
||||
error_key: :ansible_not_found_on_guest) do |type, output|
|
||||
if type == :stdout && output.lines[0]
|
||||
raw_output = output.lines[0]
|
||||
end
|
||||
end
|
||||
|
||||
if result != 0
|
||||
raw_output = ""
|
||||
end
|
||||
|
||||
raw_output
|
||||
end
|
||||
|
||||
def get_provisioning_working_directory
|
||||
config.provisioning_path
|
||||
end
|
||||
|
@ -159,7 +179,7 @@ module VagrantPlugins
|
|||
error_key: :config_file_not_found,
|
||||
config_option: option_name,
|
||||
path: remote_path,
|
||||
system: "guest"
|
||||
system: @control_machine
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ module VagrantPlugins
|
|||
|
||||
def initialize(machine, config)
|
||||
super
|
||||
@control_machine = "host"
|
||||
@logger = Log4r::Logger.new("vagrant::provisioners::ansible_host")
|
||||
end
|
||||
|
||||
|
@ -18,8 +19,9 @@ module VagrantPlugins
|
|||
# At this stage, the SSH access is guaranteed to be ready
|
||||
@ssh_info = @machine.ssh_info
|
||||
|
||||
check_files_existence
|
||||
warn_for_unsupported_platform
|
||||
check_files_existence
|
||||
check_ansible_version_and_compatibility
|
||||
|
||||
execute_ansible_galaxy_from_host if config.galaxy_role_file
|
||||
execute_ansible_playbook_from_host
|
||||
|
@ -31,7 +33,24 @@ module VagrantPlugins
|
|||
|
||||
def warn_for_unsupported_platform
|
||||
if Vagrant::Util::Platform.windows?
|
||||
@machine.env.ui.warn(I18n.t("vagrant.provisioners.ansible.windows_not_supported_for_control_machine"))
|
||||
@machine.env.ui.warn(I18n.t("vagrant.provisioners.ansible.windows_not_supported_for_control_machine") + "\n")
|
||||
end
|
||||
end
|
||||
|
||||
def check_ansible_version_and_compatibility
|
||||
# This step will also fetch the Ansible version data into related instance variables
|
||||
set_and_check_compatibility_mode
|
||||
|
||||
# Skip this check when not required, nor possible
|
||||
if !@gathered_version || config.version.empty? || config.version.to_s.to_sym == :latest
|
||||
return
|
||||
end
|
||||
|
||||
if config.version != @gathered_version
|
||||
raise Ansible::Errors::AnsibleVersionMismatch,
|
||||
system: @control_machine,
|
||||
required_version: config.version,
|
||||
current_version: @gathered_version
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -49,15 +68,15 @@ module VagrantPlugins
|
|||
|
||||
if !config.force_remote_user
|
||||
# Pass the vagrant ssh username as Ansible default remote user, because
|
||||
# the ansible_ssh_user parameter won't be added to the auto-generated inventory.
|
||||
# the ansible_ssh_user/ansible_user parameter won't be added to the auto-generated inventory.
|
||||
@command_arguments << "--user=#{@ssh_info[:username]}"
|
||||
elsif config.inventory_path
|
||||
# Using an extra variable is the only way to ensure that the Ansible remote user
|
||||
# is overridden (as the ansible inventory is not under vagrant control)
|
||||
@command_arguments << "--extra-vars=ansible_ssh_user='#{@ssh_info[:username]}'"
|
||||
@command_arguments << "--extra-vars=#{@lexicon[:ansible_user]}='#{@ssh_info[:username]}'"
|
||||
end
|
||||
|
||||
@command_arguments << "--ask-sudo-pass" if config.ask_sudo_pass
|
||||
@command_arguments << "--#{@lexicon[:ask_become_pass]}" if config.ask_become_pass
|
||||
@command_arguments << "--ask-vault-pass" if config.ask_vault_pass
|
||||
|
||||
prepare_common_command_arguments
|
||||
|
@ -88,6 +107,30 @@ module VagrantPlugins
|
|||
end
|
||||
end
|
||||
|
||||
def gather_ansible_version
|
||||
raw_output = ""
|
||||
command = %w(ansible --version)
|
||||
|
||||
command << {
|
||||
notify: [:stdout, :stderr]
|
||||
}
|
||||
|
||||
begin
|
||||
result = Vagrant::Util::Subprocess.execute(*command) do |type, output|
|
||||
if type == :stdout && output.lines[0]
|
||||
raw_output = output
|
||||
end
|
||||
end
|
||||
if result.exit_code != 0
|
||||
raw_output = ""
|
||||
end
|
||||
rescue Vagrant::Errors::CommandUnavailable
|
||||
raise Ansible::Errors::AnsibleNotFoundOnHost
|
||||
end
|
||||
|
||||
raw_output
|
||||
end
|
||||
|
||||
def execute_ansible_galaxy_from_host
|
||||
prepare_ansible_config_environment_variable
|
||||
|
||||
|
@ -199,19 +242,19 @@ module VagrantPlugins
|
|||
def get_inventory_ssh_machine(machine, ssh_info)
|
||||
forced_remote_user = ""
|
||||
if config.force_remote_user
|
||||
forced_remote_user = "ansible_ssh_user='#{ssh_info[:username]}' "
|
||||
forced_remote_user = "#{@lexicon[:ansible_user]}='#{ssh_info[:username]}' "
|
||||
end
|
||||
|
||||
"#{machine.name} ansible_ssh_host=#{ssh_info[:host]} ansible_ssh_port=#{ssh_info[:port]} #{forced_remote_user}ansible_ssh_private_key_file='#{ssh_info[:private_key_path][0]}'\n"
|
||||
"#{machine.name} #{@lexicon[:ansible_host]}=#{ssh_info[:host]} #{@lexicon[:ansible_port]}=#{ssh_info[:port]} #{forced_remote_user}ansible_ssh_private_key_file='#{ssh_info[:private_key_path][0]}'\n"
|
||||
end
|
||||
|
||||
def get_inventory_winrm_machine(machine, winrm_net_info)
|
||||
forced_remote_user = ""
|
||||
if config.force_remote_user
|
||||
forced_remote_user = "ansible_ssh_user='#{machine.config.winrm.username}' "
|
||||
forced_remote_user = "#{@lexicon[:ansible_user]}='#{machine.config.winrm.username}' "
|
||||
end
|
||||
|
||||
"#{machine.name} ansible_connection=winrm ansible_ssh_host=#{winrm_net_info[:host]} ansible_ssh_port=#{winrm_net_info[:port]} #{forced_remote_user}ansible_ssh_pass='#{machine.config.winrm.password}'\n"
|
||||
"#{machine.name} ansible_connection=winrm #{@lexicon[:ansible_host]}=#{winrm_net_info[:host]} #{@lexicon[:ansible_port]}=#{winrm_net_info[:port]} #{forced_remote_user}#{@lexicon[:ansible_password]}='#{machine.config.winrm.password}'\n"
|
||||
end
|
||||
|
||||
def ansible_ssh_args
|
||||
|
@ -287,7 +330,7 @@ module VagrantPlugins
|
|||
_key: :config_file_not_found,
|
||||
config_option: option_name,
|
||||
path: expanded_path,
|
||||
system: "host"
|
||||
system: @control_machine
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -11,15 +11,28 @@ module VagrantPlugins
|
|||
if File.directory?(source)
|
||||
# We need to make sure the actual destination folder
|
||||
# also exists before uploading, otherwise
|
||||
# you will get nested folders. We also need to append
|
||||
# a './' to the source folder so we copy the contents
|
||||
# rather than the folder itself, in case a users destination
|
||||
# folder differs from its source.
|
||||
# you will get nested folders
|
||||
#
|
||||
# https://serverfault.com/questions/538368/make-scp-always-overwrite-or-create-directory
|
||||
# https://unix.stackexchange.com/questions/292641/get-scp-path-behave-like-rsync-path/292732
|
||||
command = "mkdir -p \"%s\"" % destination
|
||||
source << "/."
|
||||
if !destination.end_with?(File::SEPARATOR) &&
|
||||
!source.end_with?("#{File::SEPARATOR}.")
|
||||
# We also need to append a '/.' to the source folder so we copy
|
||||
# the contents rather than the folder itself, in case a users
|
||||
# destination folder differs from its source
|
||||
#
|
||||
# If source is set as `source/` it will lose the trailing
|
||||
# slash due to how `File.expand_path` works, so we don't need
|
||||
# a conditional for that case.
|
||||
if @machine.config.vm.communicator == :winrm
|
||||
# windows needs an array of paths because of the
|
||||
# winrm-fs function Vagrant is using to upload file/folder.
|
||||
source = Dir["#{source}#{File::SEPARATOR}*"]
|
||||
else
|
||||
source << "#{File::SEPARATOR}."
|
||||
end
|
||||
end
|
||||
else
|
||||
command = "mkdir -p \"%s\"" % File.dirname(destination)
|
||||
end
|
||||
|
|
|
@ -24,6 +24,8 @@ module VagrantPlugins
|
|||
attr_accessor :log_level
|
||||
attr_accessor :masterless
|
||||
attr_accessor :minion_id
|
||||
attr_accessor :salt_call_args
|
||||
attr_accessor :salt_args
|
||||
|
||||
## bootstrap options
|
||||
attr_accessor :temp_config_dir
|
||||
|
@ -68,6 +70,8 @@ module VagrantPlugins
|
|||
@python_version = UNSET_VALUE
|
||||
@run_service = UNSET_VALUE
|
||||
@master_id = UNSET_VALUE
|
||||
@salt_call_args = UNSET_VALUE
|
||||
@salt_args = UNSET_VALUE
|
||||
end
|
||||
|
||||
def finalize!
|
||||
|
@ -94,6 +98,8 @@ module VagrantPlugins
|
|||
@python_version = nil if @python_version == UNSET_VALUE
|
||||
@run_service = nil if @run_service == UNSET_VALUE
|
||||
@master_id = nil if @master_id == UNSET_VALUE
|
||||
@salt_call_args = nil if @salt_call_args == UNSET_VALUE
|
||||
@salt_args = nil if @salt_args == UNSET_VALUE
|
||||
|
||||
# NOTE: Optimistic defaults are set in the provisioner. UNSET_VALUEs
|
||||
# are converted there to allow proper detection of unset values.
|
||||
|
@ -149,6 +155,14 @@ module VagrantPlugins
|
|||
errors << I18n.t("vagrant.provisioners.salt.must_accept_keys")
|
||||
end
|
||||
|
||||
if @salt_call_args && !@salt_call_args.is_a?(Array)
|
||||
errors << I18n.t("vagrant.provisioners.salt.args_array")
|
||||
end
|
||||
|
||||
if @salt_args && !@salt_args.is_a?(Array)
|
||||
errors << I18n.t("vagrant.provisioners.salt.args_array")
|
||||
end
|
||||
|
||||
return {"salt provisioner" => errors}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -198,6 +198,16 @@ module VagrantPlugins
|
|||
return options
|
||||
end
|
||||
|
||||
# Append additional arguments to the salt command
|
||||
def get_salt_args
|
||||
" " + Array(@config.salt_args).join(" ")
|
||||
end
|
||||
|
||||
# Append additional arguments to the salt-call command
|
||||
def get_call_args
|
||||
" " + Array(@config.salt_call_args).join(" ")
|
||||
end
|
||||
|
||||
# Copy master and minion configs to VM
|
||||
def upload_configs
|
||||
if @config.minion_config
|
||||
|
@ -368,7 +378,8 @@ module VagrantPlugins
|
|||
unless @config.masterless?
|
||||
@machine.communicate.sudo("salt '*' saltutil.sync_all")
|
||||
end
|
||||
@machine.communicate.sudo("salt '*' state.highstate --verbose#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}", ssh_opts) do |type, data|
|
||||
options = "#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}#{get_salt_args}"
|
||||
@machine.communicate.sudo("salt '*' state.highstate --verbose#{options}", ssh_opts) do |type, data|
|
||||
if @config.verbose
|
||||
@machine.env.ui.info(data.rstrip)
|
||||
end
|
||||
|
@ -380,7 +391,8 @@ module VagrantPlugins
|
|||
@machine.communicate.execute("C:\\salt\\salt-call.bat saltutil.sync_all", opts)
|
||||
end
|
||||
# TODO: something equivalent to { error_key: :ssh_bad_exit_status_muted }?
|
||||
@machine.communicate.execute("C:\\salt\\salt-call.bat state.highstate --retcode-passthrough#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}", opts) do |type, data|
|
||||
options = "#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}#{get_call_args}"
|
||||
@machine.communicate.execute("C:\\salt\\salt-call.bat state.highstate --retcode-passthrough#{options}", opts) do |type, data|
|
||||
if @config.verbose
|
||||
@machine.env.ui.info(data.rstrip)
|
||||
end
|
||||
|
@ -389,7 +401,8 @@ module VagrantPlugins
|
|||
unless @config.masterless?
|
||||
@machine.communicate.sudo("salt-call saltutil.sync_all")
|
||||
end
|
||||
@machine.communicate.sudo("salt-call state.highstate --retcode-passthrough#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}", ssh_opts) do |type, data|
|
||||
options = "#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}#{get_call_args}"
|
||||
@machine.communicate.sudo("salt-call state.highstate --retcode-passthrough#{options}", ssh_opts) do |type, data|
|
||||
if @config.verbose
|
||||
@machine.env.ui.info(data.rstrip)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#VAGRANT-BEGIN
|
||||
# The contents below are automatically generated by Vagrant. Do not modify.
|
||||
TYPE=eth
|
||||
NM_CONTROLLED=<%= options.fetch(:nm_controlled, "no") %>
|
||||
BOOTPROTO=dhcp
|
||||
ONBOOT=yes
|
||||
#VAGRANT-END
|
|
@ -0,0 +1,3 @@
|
|||
#VAGRANT-BEGIN
|
||||
<%= options[:ip] %>/<%= options[:netmask] %>
|
||||
#VAGRANT-END
|
|
@ -0,0 +1,5 @@
|
|||
#VAGRANT-BEGIN
|
||||
<% if options[:gateway] %>
|
||||
default via <%= options[:gateway] %>
|
||||
<% end %>
|
||||
#VAGRANT-END
|
|
@ -0,0 +1,7 @@
|
|||
#VAGRANT-BEGIN
|
||||
# The contents below are automatically generated by Vagrant. Do not modify.
|
||||
TYPE=eth
|
||||
NM_CONTROLLED=<%= options.fetch(:nm_controlled, "no") %>
|
||||
BOOTPROTO=static
|
||||
ONBOOT=yes
|
||||
#VAGRANT-END
|
|
@ -912,6 +912,9 @@ en:
|
|||
command: %{command}
|
||||
stdout: %{stdout}
|
||||
stderr: %{stderr}
|
||||
nfs_dupe_permissions: |-
|
||||
You have attempted to export the same nfs host path at %{hostpath} with
|
||||
different nfs permissions. Please pick one permission and reload your guest.
|
||||
nfs_cant_read_exports: |-
|
||||
Vagrant can't read your current NFS exports! The exports file should be
|
||||
readable by any user. This is usually caused by invalid permissions
|
||||
|
@ -2338,14 +2341,19 @@ en:
|
|||
ansible_command_failed: |-
|
||||
Ansible failed to complete successfully. Any error output should be
|
||||
visible above. Please fix these errors and try again.
|
||||
ansible_compatibility_mode_conflict: |-
|
||||
The requested Ansible compatibility mode (%{compatibility_mode}) is in conflict with
|
||||
the Ansible installation on your Vagrant %{system} system (currently: %{ansible_version}).
|
||||
See https://docs.vagrantup.com/v2/provisioning/ansible_common.html#compatibility_mode
|
||||
for more information.
|
||||
ansible_not_found_on_guest: |-
|
||||
The Ansible software could not be found! Please verify
|
||||
that Ansible is correctly installed on your guest system.
|
||||
|
||||
If you haven't installed Ansible yet, please install Ansible
|
||||
on your Vagrant basebox, or enable the automated setup with the
|
||||
`install` option of this provisioner. Please check
|
||||
https://docs.vagrantup.com/v2/provisioning/ansible_local.html
|
||||
ansible_local provisioner `install` option. Please check
|
||||
https://docs.vagrantup.com/v2/provisioning/ansible_local.html#install
|
||||
for more information.
|
||||
ansible_not_found_on_host: |-
|
||||
The Ansible software could not be found! Please verify
|
||||
|
@ -2355,6 +2363,18 @@ en:
|
|||
on your host system. Vagrant can't do this for you in a safe and
|
||||
automated way.
|
||||
Please check https://docs.ansible.com for more information.
|
||||
ansible_programming_error: |-
|
||||
Ansible Provisioner Programming Error:
|
||||
|
||||
%{message}
|
||||
|
||||
Internal Details:
|
||||
|
||||
%{details}
|
||||
|
||||
Sorry, but this Vagrant error should never occur.
|
||||
Please check https://github.com/mitchellh/vagrant/issues for any
|
||||
existing bug report. If needed, please create a new issue. Thank you!
|
||||
cannot_support_pip_install: |-
|
||||
Unfortunately Vagrant does not support yet installing Ansible
|
||||
from pip for the guest OS running in the machine.
|
||||
|
@ -2364,16 +2384,18 @@ en:
|
|||
to contribute back support. Thank you!
|
||||
|
||||
https://github.com/mitchellh/vagrant
|
||||
ansible_version_not_found_on_guest: |-
|
||||
The requested Ansible version (%{required_version}) was not found on the guest.
|
||||
Please check the ansible installation on your guest system,
|
||||
or adapt the `version` option of this provisioner in your Vagrantfile.
|
||||
See https://docs.vagrantup.com/v2/provisioning/ansible_local.html
|
||||
ansible_version_mismatch: |-
|
||||
The requested Ansible version (%{required_version}) was not found on the %{system}.
|
||||
Please check the Ansible installation on your Vagrant %{system} system (currently: %{current_version}),
|
||||
or adapt the provisioner `version` option in your Vagrantfile.
|
||||
See https://docs.vagrantup.com/v2/provisioning/ansible_common.html#version
|
||||
for more information.
|
||||
config_file_not_found: |-
|
||||
`%{config_option}` does not exist on the %{system}: %{path}
|
||||
extra_vars_invalid: |-
|
||||
`extra_vars` must be a hash or a path to an existing file. Received: %{value} (as %{type})
|
||||
no_compatibility_mode: |-
|
||||
`compatibility_mode` must be a valid mode (possible values: %{valid_modes}).
|
||||
no_playbook: |-
|
||||
`playbook` file path must be set.
|
||||
raw_arguments_invalid: |-
|
||||
|
@ -2387,6 +2409,20 @@ en:
|
|||
windows_not_supported_for_control_machine: |-
|
||||
Windows is not officially supported for the Ansible Control Machine.
|
||||
Please check https://docs.ansible.com/intro_installation.html#control-machine-requirements
|
||||
compatibility_mode_not_detected: |-
|
||||
Vagrant gathered an unknown Ansible version:
|
||||
|
||||
%{gathered_version}
|
||||
and falls back on the compatibility mode '%{compatibility_mode}'.
|
||||
|
||||
Alternatively, the compatibility mode can be specified in your Vagrantfile:
|
||||
https://www.vagrantup.com/docs/provisioning/ansible_common.html#compatibility_mode
|
||||
compatibility_mode_warning: |-
|
||||
Vagrant has automatically selected the compatibility mode '%{compatibility_mode}'
|
||||
according to the Ansible version installed (%{ansible_version}).
|
||||
|
||||
Alternatively, the compatibility mode can be specified in your Vagrantfile:
|
||||
https://www.vagrantup.com/docs/provisioning/ansible_common.html#compatibility_mode
|
||||
|
||||
docker:
|
||||
wrong_provisioner: |-
|
||||
|
@ -2406,6 +2442,8 @@ en:
|
|||
You must include both public and private keys.
|
||||
must_accept_keys: |-
|
||||
You must accept keys when running highstate with master!
|
||||
args_array: |-
|
||||
You must set this value as an array.
|
||||
|
||||
pushes:
|
||||
file:
|
||||
|
|
|
@ -7,7 +7,12 @@ describe VagrantPlugins::LoginCommand::Client do
|
|||
|
||||
let(:env) { isolated_environment.create_vagrant_env }
|
||||
|
||||
subject { described_class.new(env) }
|
||||
subject(:client) { described_class.new(env) }
|
||||
|
||||
before(:all) do
|
||||
I18n.load_path << Vagrant.source_root.join("plugins/commands/login/locales/en.yml")
|
||||
I18n.reload!
|
||||
end
|
||||
|
||||
before do
|
||||
stub_env("ATLAS_TOKEN" => nil)
|
||||
|
@ -38,7 +43,7 @@ describe VagrantPlugins::LoginCommand::Client do
|
|||
expect(subject.logged_in?).to be(true)
|
||||
end
|
||||
|
||||
it "returns false if the endpoint returns a non-200" do
|
||||
it "raises an error 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)
|
||||
|
@ -55,47 +60,159 @@ describe VagrantPlugins::LoginCommand::Client do
|
|||
end
|
||||
|
||||
describe "#login" do
|
||||
it "returns the access token after successful login" do
|
||||
request = {
|
||||
"user" => {
|
||||
"login" => "foo",
|
||||
"password" => "bar",
|
||||
let(:request) {
|
||||
{
|
||||
user: {
|
||||
login: login,
|
||||
password: password,
|
||||
},
|
||||
"token" => {
|
||||
"description" => "Token description"
|
||||
token: {
|
||||
description: description,
|
||||
},
|
||||
two_factor: {
|
||||
code: nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = {
|
||||
"token" => "baz",
|
||||
}
|
||||
let(:login) { "foo" }
|
||||
let(:password) { "bar" }
|
||||
let(:description) { "Token description" }
|
||||
|
||||
headers = {
|
||||
let(:headers) {
|
||||
{
|
||||
"Accept" => "application/json",
|
||||
"Content-Type" => "application/json",
|
||||
}
|
||||
}
|
||||
let(:response) {
|
||||
{
|
||||
token: "baz"
|
||||
}
|
||||
}
|
||||
|
||||
it "returns the access token after successful login" do
|
||||
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", description: "Token description"))
|
||||
.to eq("baz")
|
||||
client.username_or_email = login
|
||||
client.password = password
|
||||
|
||||
expect(client.login(description: "Token description")).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: "")
|
||||
context "when 2fa is required" do
|
||||
let(:response) {
|
||||
{
|
||||
two_factor: {
|
||||
default_delivery_method: default_delivery_method,
|
||||
delivery_methods: delivery_methods
|
||||
}
|
||||
}
|
||||
}
|
||||
let(:default_delivery_method) { "app" }
|
||||
let(:delivery_methods) { ["app"] }
|
||||
|
||||
expect(subject.login("foo", "bar")).to be(false)
|
||||
before do
|
||||
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
|
||||
to_return(status: 406, body: JSON.dump(response))
|
||||
end
|
||||
|
||||
it "raises a two-factor required error" do
|
||||
expect {
|
||||
client.login
|
||||
}.to raise_error(VagrantPlugins::LoginCommand::Errors::TwoFactorRequired)
|
||||
end
|
||||
|
||||
context "when the default delivery method is not app" do
|
||||
let(:default_delivery_method) { "sms" }
|
||||
let(:delivery_methods) { ["app", "sms"] }
|
||||
|
||||
it "requests a code and then raises a two-factor required error" do
|
||||
expect(client)
|
||||
.to receive(:request_code)
|
||||
.with(default_delivery_method)
|
||||
|
||||
expect {
|
||||
client.login
|
||||
}.to raise_error(VagrantPlugins::LoginCommand::Errors::TwoFactorRequired)
|
||||
end
|
||||
end
|
||||
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)
|
||||
context "on bad login" do
|
||||
before do
|
||||
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
|
||||
to_return(status: 401, body: "")
|
||||
end
|
||||
|
||||
expect { subject.login("foo", "bar") }.
|
||||
to raise_error(VagrantPlugins::LoginCommand::Errors::ServerUnreachable)
|
||||
it "raises an error" do
|
||||
expect {
|
||||
client.login
|
||||
}.to raise_error(VagrantPlugins::LoginCommand::Errors::Unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context "if it can't reach the server" do
|
||||
before do
|
||||
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
|
||||
to_raise(SocketError)
|
||||
end
|
||||
|
||||
it "raises an exception" do
|
||||
expect {
|
||||
subject.login
|
||||
}.to raise_error(VagrantPlugins::LoginCommand::Errors::ServerUnreachable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#request_code" do
|
||||
let(:request) {
|
||||
{
|
||||
user: {
|
||||
login: login,
|
||||
password: password,
|
||||
},
|
||||
two_factor: {
|
||||
delivery_method: delivery_method
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let(:login) { "foo" }
|
||||
let(:password) { "bar" }
|
||||
let(:delivery_method) { "sms" }
|
||||
|
||||
let(:headers) {
|
||||
{
|
||||
"Accept" => "application/json",
|
||||
"Content-Type" => "application/json"
|
||||
}
|
||||
}
|
||||
|
||||
let(:response) {
|
||||
{
|
||||
two_factor: {
|
||||
obfuscated_destination: "SMS number ending in 1234"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it "displays that the code was sent" do
|
||||
expect(env.ui)
|
||||
.to receive(:success)
|
||||
.with("2FA code sent to SMS number ending in 1234.")
|
||||
|
||||
stub_request(:post, "#{Vagrant.server_url}/api/v1/two-factor/request-code").
|
||||
with(body: JSON.dump(request), headers: headers).
|
||||
to_return(status: 201, body: JSON.dump(response))
|
||||
|
||||
client.username_or_email = login
|
||||
client.password = password
|
||||
|
||||
client.request_code delivery_method
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -31,6 +31,35 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#upload" do
|
||||
let(:fm) { double("file_manager") }
|
||||
it "should call file_manager.upload for each passed in path" do
|
||||
from = ["/path", "/path/folder", "/path/folder/file.py"]
|
||||
to = "/destination"
|
||||
size = 80
|
||||
|
||||
allow(WinRM::FS::FileManager).to receive(:new).with(connection)
|
||||
.and_return(fm)
|
||||
allow(fm).to receive(:upload).and_return(size)
|
||||
|
||||
expect(fm).to receive(:upload).exactly(from.size).times
|
||||
expect(subject.upload(from, to)).to eq(size*from.size)
|
||||
end
|
||||
|
||||
it "should call file_manager.upload once for a single path" do
|
||||
from = "/path/folder/file.py"
|
||||
to = "/destination"
|
||||
size = 80
|
||||
|
||||
allow(WinRM::FS::FileManager).to receive(:new).with(connection)
|
||||
.and_return(fm)
|
||||
allow(fm).to receive(:upload).and_return(size)
|
||||
|
||||
expect(fm).to receive(:upload).exactly(1).times
|
||||
expect(subject.upload(from, to)).to eq(size)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".powershell" do
|
||||
it "should call winrm powershell" do
|
||||
expect(shell).to receive(:run).with("dir").and_return(output)
|
||||
|
@ -66,7 +95,7 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
|
|||
end
|
||||
end
|
||||
|
||||
describe ".cmd" do
|
||||
describe ".cmd" do
|
||||
it "should call winrm cmd" do
|
||||
expect(connection).to receive(:shell).with(:cmd, { })
|
||||
expect(shell).to receive(:run).with("dir").and_return(output)
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
require_relative "../../../../base"
|
||||
|
||||
describe "VagrantPlugins::GuestALT::Cap::ChangeHostName" do
|
||||
let(:caps) do
|
||||
VagrantPlugins::GuestALT::Plugin
|
||||
.components
|
||||
.guest_capabilities[:alt]
|
||||
end
|
||||
|
||||
let(:machine) { double("machine") }
|
||||
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
|
||||
|
||||
before do
|
||||
allow(machine).to receive(:communicate).and_return(comm)
|
||||
end
|
||||
|
||||
after do
|
||||
comm.verify_expectations!
|
||||
end
|
||||
|
||||
describe ".change_host_name" do
|
||||
let(:cap) { caps.get(:change_host_name) }
|
||||
|
||||
let(:name) { "banana-rama.example.com" }
|
||||
|
||||
it "sets the hostname" do
|
||||
comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1)
|
||||
|
||||
cap.change_host_name(machine, name)
|
||||
expect(comm.received_commands[1]).to match(/\/etc\/sysconfig\/network/)
|
||||
expect(comm.received_commands[1]).to match(/hostnamectl set-hostname --static '#{name}'/)
|
||||
expect(comm.received_commands[1]).to match(/hostnamectl set-hostname --transient '#{name}'/)
|
||||
expect(comm.received_commands[1]).to match(/service network restart/)
|
||||
end
|
||||
|
||||
it "does not change the hostname if already set" do
|
||||
comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0)
|
||||
cap.change_host_name(machine, name)
|
||||
expect(comm.received_commands.size).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,213 @@
|
|||
require_relative "../../../../base"
|
||||
|
||||
describe "VagrantPlugins::GuestALT::Cap::ConfigureNetworks" do
|
||||
let(:caps) do
|
||||
VagrantPlugins::GuestALT::Plugin
|
||||
.components
|
||||
.guest_capabilities[:alt]
|
||||
end
|
||||
|
||||
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
|
||||
let(:config) { double("config", vm: vm) }
|
||||
let(:guest) { double("guest") }
|
||||
let(:machine) { double("machine", guest: guest, config: config) }
|
||||
let(:networks){ [[{}], [{}]] }
|
||||
let(:vm){ double("vm", networks: networks) }
|
||||
|
||||
before do
|
||||
allow(machine).to receive(:communicate).and_return(comm)
|
||||
end
|
||||
|
||||
after do
|
||||
comm.verify_expectations!
|
||||
end
|
||||
|
||||
describe ".configure_networks" do
|
||||
let(:cap) { caps.get(:configure_networks) }
|
||||
|
||||
before do
|
||||
allow(guest).to receive(:capability)
|
||||
.with(:flavor)
|
||||
.and_return(:alt)
|
||||
|
||||
allow(guest).to receive(:capability)
|
||||
.with(:network_scripts_dir)
|
||||
.and_return("/etc/net")
|
||||
|
||||
allow(guest).to receive(:capability)
|
||||
.with(:network_interfaces)
|
||||
.and_return(["eth1", "eth2"])
|
||||
end
|
||||
|
||||
let(:network_1) do
|
||||
{
|
||||
interface: 0,
|
||||
type: "dhcp",
|
||||
}
|
||||
end
|
||||
|
||||
let(:network_2) do
|
||||
{
|
||||
interface: 1,
|
||||
type: "static",
|
||||
ip: "33.33.33.10",
|
||||
netmask: "255.255.0.0",
|
||||
gateway: "33.33.0.1",
|
||||
}
|
||||
end
|
||||
|
||||
context "with NetworkManager installed" do
|
||||
before do
|
||||
allow(cap).to receive(:nmcli?).and_return true
|
||||
end
|
||||
|
||||
context "with devices managed by NetworkManager" do
|
||||
before do
|
||||
allow(cap).to receive(:nm_controlled?).and_return true
|
||||
end
|
||||
|
||||
context "with nm_controlled option omitted" do
|
||||
it "downs networks via nmcli, creates ifaces and restart NetworksManager" do
|
||||
cap.configure_networks(machine, [network_1, network_2])
|
||||
expect(comm.received_commands[0]).to match(/nmcli.*disconnect/)
|
||||
expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/)
|
||||
expect(comm.received_commands[0]).to match(/NetworkManager/)
|
||||
expect(comm.received_commands[0]).to_not match(/ifdown|ifup/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with nm_controlled option set to true" do
|
||||
let(:networks){ [[{nm_controlled: true}], [{nm_controlled: true}]] }
|
||||
|
||||
it "downs networks via nmcli, creates ifaces and restart NetworksManager" do
|
||||
cap.configure_networks(machine, [network_1, network_2])
|
||||
expect(comm.received_commands[0]).to match(/nmcli.*disconnect/)
|
||||
expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/)
|
||||
expect(comm.received_commands[0]).to match(/NetworkManager/)
|
||||
expect(comm.received_commands[0]).to_not match(/(ifdown|ifup)/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with nm_controlled option set to false" do
|
||||
let(:networks){ [[{nm_controlled: false}], [{nm_controlled: false}]] }
|
||||
|
||||
it "downs networks manually, creates ifaces, starts networks manually and restart NetworksManager" do
|
||||
cap.configure_networks(machine, [network_1, network_2])
|
||||
expect(comm.received_commands[0]).to match(/ifdown/)
|
||||
expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/)
|
||||
expect(comm.received_commands[0]).to match(/ifup/)
|
||||
expect(comm.received_commands[0]).to match(/NetworkManager/)
|
||||
expect(comm.received_commands[0]).to_not match(/nmcli/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with nm_controlled option set to false on first device" do
|
||||
let(:networks){ [[{nm_controlled: false}], [{nm_controlled: true}]] }
|
||||
|
||||
it "downs networks, creates ifaces and starts the networks with one managed manually and one NetworkManager controlled" do
|
||||
cap.configure_networks(machine, [network_1, network_2])
|
||||
expect(comm.received_commands[0]).to match(/ifdown/)
|
||||
expect(comm.received_commands[0]).to match(/nmcli.*disconnect/)
|
||||
expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/)
|
||||
expect(comm.received_commands[0]).to match(/ifup/)
|
||||
expect(comm.received_commands[0]).to match(/NetworkManager/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with devices not managed by NetworkManager" do
|
||||
before do
|
||||
allow(cap).to receive(:nm_controlled?).and_return false
|
||||
end
|
||||
|
||||
context "with nm_controlled option omitted" do
|
||||
it "creates and starts the networks manually" do
|
||||
cap.configure_networks(machine, [network_1, network_2])
|
||||
expect(comm.received_commands[0]).to match(/ifdown/)
|
||||
expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/)
|
||||
expect(comm.received_commands[0]).to match(/ifup/)
|
||||
expect(comm.received_commands[0]).to match(/NetworkManager/)
|
||||
expect(comm.received_commands[0]).to_not match(/nmcli/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with nm_controlled option set to true" do
|
||||
let(:networks){ [[{nm_controlled: true}], [{nm_controlled: true}]] }
|
||||
|
||||
it "creates and starts the networks via nmcli" do
|
||||
cap.configure_networks(machine, [network_1, network_2])
|
||||
expect(comm.received_commands[0]).to match(/ifdown/)
|
||||
expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/)
|
||||
expect(comm.received_commands[0]).to match(/NetworkManager/)
|
||||
expect(comm.received_commands[0]).to_not match(/ifup/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with nm_controlled option set to false" do
|
||||
let(:networks){ [[{nm_controlled: false}], [{nm_controlled: false}]] }
|
||||
|
||||
it "creates and starts the networks via ifup " do
|
||||
cap.configure_networks(machine, [network_1, network_2])
|
||||
expect(comm.received_commands[0]).to match(/ifdown/)
|
||||
expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/)
|
||||
expect(comm.received_commands[0]).to match(/ifup/)
|
||||
expect(comm.received_commands[0]).to match(/NetworkManager/)
|
||||
expect(comm.received_commands[0]).to_not match(/nmcli/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with nm_controlled option set to false on first device" do
|
||||
let(:networks){ [[{nm_controlled: false}], [{nm_controlled: true}]] }
|
||||
|
||||
it "creates and starts the networks with one managed manually and one NetworkManager controlled" do
|
||||
cap.configure_networks(machine, [network_1, network_2])
|
||||
expect(comm.received_commands[0]).to match(/ifdown/)
|
||||
expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/)
|
||||
expect(comm.received_commands[0]).to match(/ifup/)
|
||||
expect(comm.received_commands[0]).to match(/NetworkManager/)
|
||||
expect(comm.received_commands[0]).to_not match(/nmcli.*disconnect/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "without NetworkManager installed" do
|
||||
before do
|
||||
allow(cap).to receive(:nmcli?).and_return false
|
||||
end
|
||||
|
||||
context "with nm_controlled option omitted" do
|
||||
|
||||
it "creates and starts the networks manually" do
|
||||
cap.configure_networks(machine, [network_1, network_2])
|
||||
expect(comm.received_commands[0]).to match(/ifdown/)
|
||||
expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/)
|
||||
expect(comm.received_commands[0]).to match(/ifup/)
|
||||
expect(comm.received_commands[0]).to_not match(/nmcli/)
|
||||
expect(comm.received_commands[0]).to_not match(/NetworkManager/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with nm_controlled option omitted" do
|
||||
let(:networks){ [[{nm_controlled: false}], [{nm_controlled: false}]] }
|
||||
|
||||
it "creates and starts the networks manually" do
|
||||
cap.configure_networks(machine, [network_1, network_2])
|
||||
expect(comm.received_commands[0]).to match(/ifdown/)
|
||||
expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/)
|
||||
expect(comm.received_commands[0]).to match(/ifup/)
|
||||
expect(comm.received_commands[0]).to_not match(/nmcli/)
|
||||
expect(comm.received_commands[0]).to_not match(/NetworkManager/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with nm_controlled option set" do
|
||||
let(:networks){ [[{nm_controlled: false}], [{nm_controlled: true}]] }
|
||||
|
||||
it "raises an error" do
|
||||
expect{ cap.configure_networks(machine, [network_1, network_2]) }.to raise_error(Vagrant::Errors::NetworkManagerNotInstalled)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,72 @@
|
|||
require_relative "../../../../base"
|
||||
|
||||
describe "VagrantPlugins::GuestALT::Cap::Flavor" do
|
||||
let(:caps) do
|
||||
VagrantPlugins::GuestALT::Plugin
|
||||
.components
|
||||
.guest_capabilities[:alt]
|
||||
end
|
||||
|
||||
let(:machine) { double("machine") }
|
||||
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
|
||||
|
||||
before do
|
||||
allow(machine).to receive(:communicate).and_return(comm)
|
||||
end
|
||||
|
||||
after do
|
||||
comm.verify_expectations!
|
||||
end
|
||||
|
||||
describe ".flavor" do
|
||||
let(:cap) { caps.get(:flavor) }
|
||||
|
||||
context "without /etc/os-release file" do
|
||||
{
|
||||
"ALT 8.1 Server" => :alt_8,
|
||||
"ALT Education 8.1" => :alt_8,
|
||||
"ALT Workstation 8.1" => :alt_8,
|
||||
"ALT Workstation K 8.1 (Centaurea Ruthenica)" => :alt_8,
|
||||
"ALT Linux p8 (Hypericum)" => :alt_8,
|
||||
|
||||
"ALT Sisyphus (unstable) (sisyphus)" => :alt,
|
||||
"ALT Linux Sisyphus (unstable)" => :alt,
|
||||
"ALT Linux 6.0.1 Spt (separator)" => :alt,
|
||||
"ALT Linux 7.0.5 School Master" => :alt,
|
||||
"ALT starter kit (Hypericum)" => :alt,
|
||||
|
||||
"ALT" => :alt,
|
||||
"Simply" => :alt,
|
||||
}.each do |str, expected|
|
||||
it "returns #{expected} for #{str} in /etc/altlinux-release" do
|
||||
comm.stub_command("test -f /etc/os-release", exit_code: 1)
|
||||
comm.stub_command("cat /etc/altlinux-release", stdout: str)
|
||||
expect(cap.flavor(machine)).to be(expected)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with /etc/os-release file" do
|
||||
{
|
||||
[ "NAME=\"Sisyphus\"", "VERSION_ID=20161130" ] => :alt,
|
||||
|
||||
[ "NAME=\"ALT Education\"", "VERSION_ID=8.1" ] => :alt_8,
|
||||
[ "NAME=\"ALT Server\"", "VERSION_ID=8.1" ] => :alt_8,
|
||||
[ "NAME=\"ALT SPServer\"", "VERSION_ID=8.0" ] => :alt_8,
|
||||
[ "NAME=\"starter kit\"", "VERSION_ID=p8" ] => :alt_8,
|
||||
[ "NAME=\"ALT Linux\"", "VERSION_ID=8.0.0" ] => :alt_8,
|
||||
[ "NAME=\"Simply Linux\"", "VERSION_ID=7.95.0" ] => :alt_8,
|
||||
|
||||
[ "NAME=\"ALT Linux\"", "VERSION_ID=7.0.5" ] => :alt_7,
|
||||
[ "NAME=\"School Junior\"", "VERSION_ID=7.0.5" ] => :alt_7,
|
||||
}.each do |strs, expected|
|
||||
it "returns #{expected} for #{strs[0]} and #{strs[1]} in /etc/os-release" do
|
||||
comm.stub_command("test -f /etc/os-release", exit_code: 0)
|
||||
comm.stub_command("grep NAME /etc/os-release", stdout: strs[0])
|
||||
comm.stub_command("grep VERSION_ID /etc/os-release", stdout: strs[1])
|
||||
expect(cap.flavor(machine)).to be(expected)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
require_relative "../../../../base"
|
||||
|
||||
describe "VagrantPlugins::GuestALT::Cap::NetworkScriptsDir" do
|
||||
let(:caps) do
|
||||
VagrantPlugins::GuestALT::Plugin
|
||||
.components
|
||||
.guest_capabilities[:alt]
|
||||
end
|
||||
|
||||
let(:machine) { double("machine") }
|
||||
|
||||
describe ".network_scripts_dir" do
|
||||
let(:cap) { caps.get(:network_scripts_dir) }
|
||||
|
||||
let(:name) { "banana-rama.example.com" }
|
||||
|
||||
it "is /etc/net" do
|
||||
expect(cap.network_scripts_dir(machine)).to eq("/etc/net")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
require_relative "../../../../base"
|
||||
|
||||
describe "VagrantPlugins::GuestALT::Cap:RSync" do
|
||||
let(:caps) do
|
||||
VagrantPlugins::GuestALT::Plugin
|
||||
.components
|
||||
.guest_capabilities[:alt]
|
||||
end
|
||||
|
||||
let(:machine) { double("machine") }
|
||||
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
|
||||
|
||||
before do
|
||||
allow(machine).to receive(:communicate).and_return(comm)
|
||||
end
|
||||
|
||||
after do
|
||||
comm.verify_expectations!
|
||||
end
|
||||
|
||||
describe ".rsync_install" do
|
||||
let(:cap) { caps.get(:rsync_install) }
|
||||
|
||||
it "installs rsync" do
|
||||
cap.rsync_install(machine)
|
||||
expect(comm.received_commands[0]).to match(/install rsync/)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -33,6 +33,44 @@ describe VagrantPlugins::HostLinux::Cap::NFS do
|
|||
@tmp_exports = nil
|
||||
end
|
||||
|
||||
describe ".nfs_check_command" do
|
||||
let(:cap){ caps.get(:nfs_check_command) }
|
||||
|
||||
context "without systemd" do
|
||||
before{ expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(false) }
|
||||
|
||||
it "should use init.d script" do
|
||||
expect(cap.nfs_check_command(env)).to include("init.d")
|
||||
end
|
||||
end
|
||||
context "with systemd" do
|
||||
before{ expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(true) }
|
||||
|
||||
it "should use systemctl" do
|
||||
expect(cap.nfs_check_command(env)).to include("systemctl")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".nfs_start_command" do
|
||||
let(:cap){ caps.get(:nfs_start_command) }
|
||||
|
||||
context "without systemd" do
|
||||
before{ expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(false) }
|
||||
|
||||
it "should use init.d script" do
|
||||
expect(cap.nfs_start_command(env)).to include("init.d")
|
||||
end
|
||||
end
|
||||
context "with systemd" do
|
||||
before{ expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(true) }
|
||||
|
||||
it "should use systemctl" do
|
||||
expect(cap.nfs_start_command(env)).to include("systemctl")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".nfs_export" do
|
||||
|
||||
let(:cap){ caps.get(:nfs_export) }
|
||||
|
@ -43,8 +81,9 @@ describe VagrantPlugins::HostLinux::Cap::NFS do
|
|||
allow(host).to receive(:capability).with(:nfs_check_command).and_return("/bin/true")
|
||||
allow(host).to receive(:capability).with(:nfs_start_command).and_return("/bin/true")
|
||||
allow(ui).to receive(:info)
|
||||
allow(cap).to receive(:system).with("sudo /bin/true").and_return(true)
|
||||
allow(cap).to receive(:system).with("/bin/true").and_return(true)
|
||||
allow(Vagrant::Util::Subprocess).to receive(:execute).and_call_original
|
||||
allow(Vagrant::Util::Subprocess).to receive(:execute).with("sudo", "/bin/true").and_return(double(:result, exit_code: 0))
|
||||
allow(Vagrant::Util::Subprocess).to receive(:execute).with("/bin/true").and_return(double(:result, exit_code: 0))
|
||||
end
|
||||
|
||||
it "should export new entries" do
|
||||
|
@ -79,6 +118,42 @@ EOH
|
|||
expect(exports_content).to include("/tmp")
|
||||
expect(exports_content).not_to include("/var")
|
||||
end
|
||||
|
||||
it "throws an exception with at least 2 different nfs options" do
|
||||
folders = {"/vagrant"=>
|
||||
{:hostpath=>"/home/vagrant",
|
||||
:linux__nfs_options=>["rw","all_squash"]},
|
||||
"/var/www/project"=>
|
||||
{:hostpath=>"/home/vagrant",
|
||||
:linux__nfs_options=>["rw","sync"]}}
|
||||
|
||||
expect { cap.nfs_export(env, ui, SecureRandom.uuid, ["127.0.0.1"], folders) }.
|
||||
to raise_error Vagrant::Errors::NFSDupePerms
|
||||
end
|
||||
|
||||
it "writes only 1 hostpath for multiple exports" do
|
||||
folders = {"/vagrant"=>
|
||||
{:hostpath=>"/home/vagrant",
|
||||
:linux__nfs_options=>["rw","all_squash"]},
|
||||
"/var/www/otherproject"=>
|
||||
{:hostpath=>"/newhome/otherproject",
|
||||
:linux__nfs_options=>["rw","all_squash"]},
|
||||
"/var/www/project"=>
|
||||
{:hostpath=>"/home/vagrant",
|
||||
:linux__nfs_options=>["rw","all_squash"]}}
|
||||
valid_id = SecureRandom.uuid
|
||||
content =<<-EOH
|
||||
\n# VAGRANT-BEGIN: #{Process.uid} #{valid_id}
|
||||
"/home/vagrant" 127.0.0.1(rw,all_squash,anonuid=,anongid=,fsid=)
|
||||
"/newhome/otherproject" 127.0.0.1(rw,all_squash,anonuid=,anongid=,fsid=)
|
||||
# VAGRANT-END: #{Process.uid} #{valid_id}
|
||||
EOH
|
||||
|
||||
cap.nfs_export(env, ui, valid_id, ["127.0.0.1"], folders)
|
||||
exports_content = File.read(exports_path)
|
||||
expect(exports_content).to eq(content)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe ".nfs_prune" do
|
||||
|
|
|
@ -16,7 +16,11 @@ describe VagrantPlugins::Ansible::Config::Guest do
|
|||
let(:existing_file) { "this/path/is/a/stub" }
|
||||
|
||||
it "supports a list of options" do
|
||||
supported_options = %w( config_file
|
||||
supported_options = %w(
|
||||
become
|
||||
become_user
|
||||
compatibility_mode
|
||||
config_file
|
||||
extra_vars
|
||||
galaxy_command
|
||||
galaxy_role_file
|
||||
|
@ -40,7 +44,8 @@ describe VagrantPlugins::Ansible::Config::Guest do
|
|||
tmp_path
|
||||
vault_password_file
|
||||
verbose
|
||||
version )
|
||||
version
|
||||
)
|
||||
|
||||
expect(get_provisioner_option_names(described_class)).to eql(supported_options)
|
||||
end
|
||||
|
@ -55,7 +60,6 @@ describe VagrantPlugins::Ansible::Config::Guest do
|
|||
expect(subject.install_mode).to eql(:default)
|
||||
expect(subject.provisioning_path).to eql("/vagrant")
|
||||
expect(subject.tmp_path).to eql("/tmp/vagrant-ansible")
|
||||
expect(subject.version).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -13,8 +13,13 @@ describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do
|
|||
let(:existing_file) { File.expand_path(__FILE__) }
|
||||
|
||||
it "supports a list of options" do
|
||||
supported_options = %w( ask_sudo_pass
|
||||
supported_options = %w(
|
||||
ask_become_pass
|
||||
ask_sudo_pass
|
||||
ask_vault_pass
|
||||
become
|
||||
become_user
|
||||
compatibility_mode
|
||||
config_file
|
||||
extra_vars
|
||||
force_remote_user
|
||||
|
@ -36,7 +41,9 @@ describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do
|
|||
sudo_user
|
||||
tags
|
||||
vault_password_file
|
||||
verbose )
|
||||
verbose
|
||||
version
|
||||
)
|
||||
|
||||
expect(get_provisioner_option_names(described_class)).to eql(supported_options)
|
||||
end
|
||||
|
@ -47,7 +54,8 @@ describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do
|
|||
it "assigns default values to unset host-specific options" do
|
||||
subject.finalize!
|
||||
|
||||
expect(subject.ask_sudo_pass).to be(false)
|
||||
expect(subject.ask_become_pass).to be(false)
|
||||
expect(subject.ask_sudo_pass).to be(false) # deprecated
|
||||
expect(subject.ask_vault_pass).to be(false)
|
||||
expect(subject.force_remote_user).to be(true)
|
||||
expect(subject.host_key_checking).to be(false)
|
||||
|
@ -61,7 +69,14 @@ describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do
|
|||
describe "host_key_checking option" do
|
||||
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :host_key_checking, false
|
||||
end
|
||||
describe "ask_become_pass option" do
|
||||
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :ask_become_pass, false
|
||||
end
|
||||
describe "ask_sudo_pass option" do
|
||||
before do
|
||||
# Filter the deprecation notice
|
||||
allow($stdout).to receive(:puts)
|
||||
end
|
||||
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :ask_sudo_pass, false
|
||||
end
|
||||
describe "ask_vault_pass option" do
|
||||
|
|
|
@ -3,6 +3,9 @@ shared_examples_for 'options shared by both Ansible provisioners' do
|
|||
it "assigns default values to unset common options" do
|
||||
subject.finalize!
|
||||
|
||||
expect(subject.become).to be(false)
|
||||
expect(subject.become_user).to be_nil
|
||||
expect(subject.compatibility_mode).to eql(VagrantPlugins::Ansible::COMPATIBILITY_MODE_AUTO)
|
||||
expect(subject.config_file).to be_nil
|
||||
expect(subject.extra_vars).to be_nil
|
||||
expect(subject.galaxy_command).to eql("ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force")
|
||||
|
@ -17,11 +20,12 @@ shared_examples_for 'options shared by both Ansible provisioners' do
|
|||
expect(subject.raw_arguments).to be_nil
|
||||
expect(subject.skip_tags).to be_nil
|
||||
expect(subject.start_at_task).to be_nil
|
||||
expect(subject.sudo).to be(false)
|
||||
expect(subject.sudo_user).to be_nil
|
||||
expect(subject.sudo).to be(false) # deprecated
|
||||
expect(subject.sudo_user).to be_nil # deprecated
|
||||
expect(subject.tags).to be_nil
|
||||
expect(subject.vault_password_file).to be_nil
|
||||
expect(subject.verbose).to be(false)
|
||||
expect(subject.version).to be_empty
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -41,6 +45,44 @@ shared_examples_for 'an Ansible provisioner' do | path_prefix, ansible_setup |
|
|||
])
|
||||
end
|
||||
|
||||
describe "compatibility_mode option" do
|
||||
|
||||
VagrantPlugins::Ansible::COMPATIBILITY_MODES.each do |valid_mode|
|
||||
it "supports compatibility mode '#{valid_mode}'" do
|
||||
subject.compatibility_mode = valid_mode
|
||||
subject.finalize!
|
||||
|
||||
result = subject.validate(machine)
|
||||
expect(subject.compatibility_mode).to eql(valid_mode)
|
||||
end
|
||||
end
|
||||
|
||||
it "returns an error if the compatibility mode is not set" do
|
||||
subject.compatibility_mode = nil
|
||||
subject.finalize!
|
||||
|
||||
result = subject.validate(machine)
|
||||
expect(result[provisioner_label]).to eql([
|
||||
I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode",
|
||||
valid_modes: "'auto', '1.8', '2.0'")
|
||||
])
|
||||
end
|
||||
|
||||
%w(invalid 1.9 2.3).each do |invalid_mode|
|
||||
it "returns an error if the compatibility mode is invalid (e.g. '#{invalid_mode}')" do
|
||||
subject.compatibility_mode = invalid_mode
|
||||
subject.finalize!
|
||||
|
||||
result = subject.validate(machine)
|
||||
expect(result[provisioner_label]).to eql([
|
||||
I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode",
|
||||
valid_modes: "'auto', '1.8', '2.0'")
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it "passes if the extra_vars option is a hash" do
|
||||
subject.extra_vars = { var1: 1, var2: "foo" }
|
||||
subject.finalize!
|
||||
|
@ -82,6 +124,7 @@ shared_examples_for 'an Ansible provisioner' do | path_prefix, ansible_setup |
|
|||
end
|
||||
|
||||
it "it collects and returns all detected errors" do
|
||||
subject.compatibility_mode = nil
|
||||
subject.playbook = nil
|
||||
subject.extra_vars = ["var1", 3, "var2", 5]
|
||||
subject.raw_arguments = { arg1: 1, arg2: "foo" }
|
||||
|
@ -89,7 +132,10 @@ shared_examples_for 'an Ansible provisioner' do | path_prefix, ansible_setup |
|
|||
|
||||
result = subject.validate(machine)
|
||||
|
||||
expect(result[provisioner_label].size).to eql(3)
|
||||
expect(result[provisioner_label].size).to eql(4)
|
||||
expect(result[provisioner_label]).to include(
|
||||
I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode",
|
||||
valid_modes: "'auto', '1.8', '2.0'"))
|
||||
expect(result[provisioner_label]).to include(
|
||||
I18n.t("vagrant.provisioners.ansible.errors.no_playbook"))
|
||||
expect(result[provisioner_label]).to include(
|
||||
|
@ -102,7 +148,15 @@ shared_examples_for 'an Ansible provisioner' do | path_prefix, ansible_setup |
|
|||
value: subject.raw_arguments.to_s))
|
||||
end
|
||||
|
||||
describe "become option" do
|
||||
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :become, false
|
||||
end
|
||||
|
||||
describe "sudo option" do
|
||||
before do
|
||||
# Filter the deprecation notice
|
||||
allow($stdout).to receive(:puts)
|
||||
end
|
||||
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :sudo, false
|
||||
end
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@ VF
|
|||
|
||||
stubbed_ui = Vagrant::UI::Colored.new
|
||||
allow(stubbed_ui).to receive(:detail).and_return("")
|
||||
allow(stubbed_ui).to receive(:warn).and_return("")
|
||||
|
||||
allow(machine.env).to receive(:ui).and_return(stubbed_ui)
|
||||
|
||||
config.playbook = 'playbook.yml'
|
||||
|
@ -69,6 +71,15 @@ VF
|
|||
# Class methods for code reuse across examples
|
||||
#
|
||||
|
||||
def self.it_should_check_ansible_version()
|
||||
it "execute 'ansible --version' before executing 'ansible-playbook'" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).
|
||||
once.with('ansible', '--version', { :notify => [:stdout, :stderr] })
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).
|
||||
once.with('ansible-playbook', any_args)
|
||||
end
|
||||
end
|
||||
|
||||
def self.it_should_set_arguments_and_environment_variables(
|
||||
expected_args_count = 5,
|
||||
expected_vars_count = 4,
|
||||
|
@ -76,9 +87,7 @@ VF
|
|||
expected_transport_mode = "ssh")
|
||||
|
||||
it "sets implicit arguments in a specific order" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
|
||||
expect(args[0]).to eq("ansible-playbook")
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args[1]).to eq("--connection=ssh")
|
||||
expect(args[2]).to eq("--timeout=30")
|
||||
|
||||
|
@ -90,7 +99,7 @@ VF
|
|||
end
|
||||
|
||||
it "sets --limit argument" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
all_limits = args.select { |x| x =~ /^(--limit=|-l)/ }
|
||||
if config.raw_arguments
|
||||
raw_limits = config.raw_arguments.select { |x| x =~ /^(--limit=|-l)/ }
|
||||
|
@ -108,7 +117,7 @@ VF
|
|||
end
|
||||
|
||||
it "exports environment variables" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
|
||||
if expected_host_key_checking
|
||||
|
@ -116,6 +125,7 @@ VF
|
|||
else
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o UserKnownHostsFile=/dev/null")
|
||||
end
|
||||
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentitiesOnly=yes")
|
||||
expect(cmd_opts[:env]['ANSIBLE_FORCE_COLOR']).to eql("true")
|
||||
expect(cmd_opts[:env]).to_not include("ANSIBLE_NOCOLOR")
|
||||
|
@ -126,14 +136,14 @@ VF
|
|||
|
||||
# "roughly" verify that only expected args/vars have been defined by the provisioner
|
||||
it "sets the expected number of arguments and environment variables" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(args.length-2).to eq(expected_args_count)
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args.length - 2).to eq(expected_args_count)
|
||||
expect(args.last[:env].length).to eq(expected_vars_count)
|
||||
}.and_return(default_execute_result)
|
||||
end
|
||||
|
||||
it "enables '#{expected_transport_mode}' as default transport mode" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*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)
|
||||
|
@ -144,7 +154,7 @@ VF
|
|||
|
||||
def self.it_should_set_optional_arguments(arg_map)
|
||||
it "sets optional arguments" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
arg_map.each_pair do |vagrant_option, ansible_argument|
|
||||
index = args.index(ansible_argument)
|
||||
if config.send(vagrant_option)
|
||||
|
@ -159,7 +169,7 @@ VF
|
|||
|
||||
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(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ControlMaster=auto")
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ControlPersist=60s")
|
||||
|
@ -167,23 +177,24 @@ VF
|
|||
end
|
||||
end
|
||||
|
||||
def self.it_should_create_and_use_generated_inventory(with_ssh_user = true)
|
||||
def self.it_should_create_and_use_generated_inventory(with_user = true)
|
||||
it "generates an inventory with all active machines" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(config.inventory_path).to be_nil
|
||||
expect(File.exists?(generated_inventory_file)).to be(true)
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
if with_ssh_user
|
||||
expect(inventory_content).to include("#{machine.name} ansible_ssh_host=#{machine.ssh_info[:host]} ansible_ssh_port=#{machine.ssh_info[:port]} ansible_ssh_user='#{machine.ssh_info[:username]}' ansible_ssh_private_key_file='#{machine.ssh_info[:private_key_path][0]}'\n")
|
||||
_ssh = config.compatibility_mode == VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0 ? "" : "_ssh"
|
||||
if with_user
|
||||
expect(inventory_content).to include("#{machine.name} ansible#{_ssh}_host=#{machine.ssh_info[:host]} ansible#{_ssh}_port=#{machine.ssh_info[:port]} ansible#{_ssh}_user='#{machine.ssh_info[:username]}' ansible_ssh_private_key_file='#{machine.ssh_info[:private_key_path][0]}'\n")
|
||||
else
|
||||
expect(inventory_content).to include("#{machine.name} ansible_ssh_host=#{machine.ssh_info[:host]} ansible_ssh_port=#{machine.ssh_info[:port]} ansible_ssh_private_key_file='#{machine.ssh_info[:private_key_path][0]}'\n")
|
||||
expect(inventory_content).to include("#{machine.name} ansible#{_ssh}_host=#{machine.ssh_info[:host]} ansible#{_ssh}_port=#{machine.ssh_info[:port]} ansible_ssh_private_key_file='#{machine.ssh_info[:private_key_path][0]}'\n")
|
||||
end
|
||||
expect(inventory_content).to include("# MISSING: '#{iso_env.machine_names[1]}' machine was probably removed without using Vagrant. This machine should be recreated.\n")
|
||||
}.and_return(default_execute_result)
|
||||
end
|
||||
|
||||
it "sets as ansible inventory the directory containing the auto-generated inventory file" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
inventory_index = args.rindex("--inventory-file=#{generated_inventory_dir}")
|
||||
expect(inventory_index).to be > 0
|
||||
expect(find_last_argument_after(inventory_index, args, /--inventory-file=\w+/)).to be(false)
|
||||
|
@ -260,11 +271,12 @@ VF
|
|||
end
|
||||
|
||||
describe "with default options" do
|
||||
it_should_check_ansible_version
|
||||
it_should_set_arguments_and_environment_variables
|
||||
it_should_create_and_use_generated_inventory
|
||||
|
||||
it "does not add any group section to the generated inventory" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) {
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) {
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
expect(inventory_content).to_not match(/^\s*\[^\\+\]\s*$/)
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -275,14 +287,152 @@ VF
|
|||
end
|
||||
end
|
||||
|
||||
describe "deprecated 'sudo' options are aliases for equivalent 'become' options" do
|
||||
before do
|
||||
# Filter the deprecation notices
|
||||
allow($stdout).to receive(:puts)
|
||||
|
||||
config.sudo = true
|
||||
config.sudo_user = 'deployer'
|
||||
config.ask_sudo_pass = true
|
||||
end
|
||||
|
||||
it_should_set_optional_arguments({"sudo" => "--sudo",
|
||||
"sudo_user" => "--sudo-user=deployer",
|
||||
"ask_sudo_pass" => "--ask-sudo-pass",
|
||||
"become" => "--sudo",
|
||||
"become_user" => "--sudo-user=deployer",
|
||||
"ask_become_pass" => "--ask-sudo-pass"})
|
||||
end
|
||||
|
||||
context "with compatibility_mode 'auto'" do
|
||||
before do
|
||||
config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_AUTO
|
||||
end
|
||||
|
||||
valid_versions = {
|
||||
"0.6": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8,
|
||||
"1.9.4": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8,
|
||||
"2.5.0.0-rc1": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0,
|
||||
"2.x.y.z": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0,
|
||||
"4.3.2.1": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0,
|
||||
}
|
||||
valid_versions.each_pair do |ansible_version, mode|
|
||||
describe "and ansible version #{ansible_version}" do
|
||||
before do
|
||||
allow(subject).to receive(:gather_ansible_version).and_return("ansible #{ansible_version}\n...\n")
|
||||
end
|
||||
|
||||
it "detects the compatibility mode #{mode}" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(config.compatibility_mode).to eq(mode)
|
||||
}.and_return(default_execute_result)
|
||||
end
|
||||
|
||||
it "warns about compatibility mode auto-detection being used" do
|
||||
expect(machine.env.ui).to receive(:warn).with(
|
||||
I18n.t("vagrant.provisioners.ansible.compatibility_mode_warning",
|
||||
compatibility_mode: mode, ansible_version: ansible_version) +
|
||||
"\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
invalid_versions = [
|
||||
"ansible devel",
|
||||
"anything 1.2",
|
||||
"2.9.2.1",
|
||||
]
|
||||
invalid_versions.each do |unknown_ansible_version|
|
||||
describe "and `ansible --version` returning '#{unknown_ansible_version}'" do
|
||||
before do
|
||||
allow(subject).to receive(:gather_ansible_version).and_return(unknown_ansible_version)
|
||||
end
|
||||
|
||||
it "applies the safest compatibility mode ('#{VagrantPlugins::Ansible::SAFE_COMPATIBILITY_MODE}')" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(config.compatibility_mode).to eq(VagrantPlugins::Ansible::SAFE_COMPATIBILITY_MODE)
|
||||
}.and_return(default_execute_result)
|
||||
end
|
||||
|
||||
it "warns about not being able to detect the best compatibility mode" do
|
||||
expect(machine.env.ui).to receive(:warn).with(
|
||||
I18n.t("vagrant.provisioners.ansible.compatibility_mode_not_detected",
|
||||
compatibility_mode: VagrantPlugins::Ansible::SAFE_COMPATIBILITY_MODE,
|
||||
gathered_version: unknown_ansible_version) +
|
||||
"\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "with compatibility_mode '#{VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8}'" do
|
||||
before do
|
||||
config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8
|
||||
end
|
||||
|
||||
it_should_check_ansible_version
|
||||
it_should_create_and_use_generated_inventory
|
||||
|
||||
it "doesn't warn about compatibility mode auto-detection" do
|
||||
expect(machine.env.ui).to_not receive(:warn)
|
||||
end
|
||||
end
|
||||
|
||||
context "with compatibility_mode '#{VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0}'" do
|
||||
before do
|
||||
config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0
|
||||
allow(subject).to receive(:gather_ansible_version).and_return("ansible 2.3.0.0\n...\n")
|
||||
end
|
||||
|
||||
it_should_create_and_use_generated_inventory
|
||||
|
||||
it "doesn't warn about compatibility mode auto-detection" do
|
||||
expect(machine.env.ui).to_not receive(:warn)
|
||||
end
|
||||
|
||||
describe "and an incompatible ansible version" do
|
||||
before do
|
||||
allow(subject).to receive(:gather_ansible_version).and_return("ansible 1.9.3\n...\n")
|
||||
end
|
||||
|
||||
it "raises a compatibility conflict error", skip_before: false, skip_after: true do
|
||||
expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleCompatibilityModeConflict)
|
||||
end
|
||||
end
|
||||
|
||||
describe "deprecated 'sudo' options are aliases for equivalent 'become' options" do
|
||||
before do
|
||||
# Filter the deprecation notices
|
||||
allow($stdout).to receive(:puts)
|
||||
|
||||
config.sudo = true
|
||||
config.sudo_user = 'deployer'
|
||||
config.ask_sudo_pass = true
|
||||
end
|
||||
|
||||
it_should_set_optional_arguments({"sudo" => "--become",
|
||||
"sudo_user" => "--become-user=deployer",
|
||||
"ask_sudo_pass" => "--ask-become-pass",
|
||||
"become" => "--become",
|
||||
"become_user" => "--become-user=deployer",
|
||||
"ask_become_pass" => "--ask-become-pass"})
|
||||
end
|
||||
end
|
||||
|
||||
describe "with playbook_command option" do
|
||||
before do
|
||||
config.playbook_command = "custom-ansible-playbook"
|
||||
|
||||
# set the compatibility mode to ensure that only ansible-playbook is excuted
|
||||
config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8
|
||||
end
|
||||
|
||||
it "uses custom playbook_command to run playbooks" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute)
|
||||
.with("custom-ansible-playbook", any_args)
|
||||
.and_return(default_execute_result)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -291,11 +441,15 @@ VF
|
|||
|
||||
it "adds host variables (given in Hash format) to the generated inventory" do
|
||||
config.host_vars = {
|
||||
machine1: {"http_port" => 80, "comments" => "'some text with spaces'"}
|
||||
machine1: {
|
||||
"http_port" => 80,
|
||||
"comments" => "'some text with spaces and quotes'",
|
||||
"description" => "text with spaces but no quotes",
|
||||
}
|
||||
}
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) {
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) {
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 comments='some text with spaces'$")
|
||||
expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 comments='some text with spaces and quotes' description='text with spaces but no quotes'")
|
||||
}.and_return(default_execute_result)
|
||||
end
|
||||
|
||||
|
@ -303,7 +457,8 @@ VF
|
|||
config.host_vars = {
|
||||
machine1: ["http_port=80", "maxRequestsPerChild=808"]
|
||||
}
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) {
|
||||
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) {
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 maxRequestsPerChild=808")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -313,7 +468,8 @@ VF
|
|||
config.host_vars = {
|
||||
:machine1 => "http_port=80 maxRequestsPerChild=808"
|
||||
}
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) {
|
||||
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) {
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 maxRequestsPerChild=808")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -323,7 +479,8 @@ VF
|
|||
config.host_vars = {
|
||||
"machine1" => "http_port=80 maxRequestsPerChild=808"
|
||||
}
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) {
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 maxRequestsPerChild=808")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -345,7 +502,7 @@ VF
|
|||
"bar:children" => ["group1", "group2", "group3", "group5"],
|
||||
}
|
||||
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
|
||||
# Accept String instead of Array for group member list
|
||||
|
@ -383,7 +540,7 @@ VF
|
|||
"group3:vars" => "stringvar1=stringvalue1 stringvar2=stringvalue2",
|
||||
}
|
||||
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
|
||||
# Hash syntax
|
||||
|
@ -396,6 +553,19 @@ VF
|
|||
expect(inventory_content).to include("[group3:vars]\nstringvar1=stringvalue1\nstringvar2=stringvalue2\n")
|
||||
}.and_return(default_execute_result)
|
||||
end
|
||||
|
||||
it "adds 'all:vars' section to the generated inventory" do
|
||||
config.groups = {
|
||||
"all:vars" => { "var1" => "value1", "var2" => "value2" }
|
||||
}
|
||||
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
|
||||
expect(inventory_content).to include("[all:vars]\nvar1=value1\nvar2=value2\n")
|
||||
|
||||
}.and_return(default_execute_result)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with host_key_checking option enabled" do
|
||||
|
@ -408,18 +578,18 @@ VF
|
|||
|
||||
describe "with boolean (flag) options disabled" do
|
||||
before do
|
||||
config.sudo = false
|
||||
config.ask_sudo_pass = false
|
||||
config.become = false
|
||||
config.ask_become_pass = false
|
||||
config.ask_vault_pass = false
|
||||
|
||||
config.sudo_user = 'root'
|
||||
config.become_user = 'root'
|
||||
end
|
||||
|
||||
it_should_set_arguments_and_environment_variables 6
|
||||
it_should_set_optional_arguments({ "sudo_user" => "--sudo-user=root" })
|
||||
it_should_set_optional_arguments({ "become_user" => "--sudo-user=root" })
|
||||
|
||||
it "it does not set boolean flag when corresponding option is set to false" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args.index("--sudo")).to be_nil
|
||||
expect(args.index("--ask-sudo-pass")).to be_nil
|
||||
expect(args.index("--ask-vault-pass")).to be_nil
|
||||
|
@ -429,7 +599,7 @@ VF
|
|||
|
||||
describe "with raw_arguments option" do
|
||||
before do
|
||||
config.sudo = false
|
||||
config.become = false
|
||||
config.force_remote_user = false
|
||||
config.skip_tags = %w(foo bar)
|
||||
config.limit = "all"
|
||||
|
@ -448,7 +618,7 @@ VF
|
|||
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(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
config.raw_arguments.each do |raw_arg|
|
||||
expect(args).to include(raw_arg)
|
||||
end
|
||||
|
@ -456,7 +626,7 @@ VF
|
|||
end
|
||||
|
||||
it "sets raw arguments after arguments related to supported options" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
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")
|
||||
|
@ -465,7 +635,7 @@ VF
|
|||
end
|
||||
|
||||
it "sets boolean flag (e.g. --sudo) defined in raw_arguments, even if corresponding option is set to false" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args).to include('--sudo')
|
||||
}.and_return(default_execute_result)
|
||||
end
|
||||
|
@ -490,7 +660,7 @@ VF
|
|||
it_should_set_arguments_and_environment_variables 6
|
||||
|
||||
it "uses a --user argument to set a default remote user" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args).not_to include("--extra-vars=ansible_ssh_user='#{machine.ssh_info[:username]}'")
|
||||
expect(args).to include("--user=#{machine.ssh_info[:username]}")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -521,8 +691,7 @@ VF
|
|||
it_should_set_arguments_and_environment_variables
|
||||
|
||||
it "generates an inventory with winrm connection settings" do
|
||||
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(config.inventory_path).to be_nil
|
||||
expect(File.exists?(generated_inventory_file)).to be(true)
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
|
@ -537,7 +706,7 @@ VF
|
|||
end
|
||||
|
||||
it "doesn't set the ansible remote user in inventory and use '--user' argument with the vagrant ssh username" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
|
||||
expect(inventory_content).to include("machine1 ansible_connection=winrm ansible_ssh_host=127.0.0.1 ansible_ssh_port=55986 ansible_ssh_pass='winword'\n")
|
||||
|
@ -555,7 +724,7 @@ VF
|
|||
it_should_set_arguments_and_environment_variables 6
|
||||
|
||||
it "does not generate the inventory and uses given inventory path instead" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args).to include("--inventory-file=#{existing_file}")
|
||||
expect(args).not_to include("--inventory-file=#{generated_inventory_file}")
|
||||
expect(File.exists?(generated_inventory_file)).to be(false)
|
||||
|
@ -563,7 +732,7 @@ VF
|
|||
end
|
||||
|
||||
it "uses an --extra-vars argument to force ansible_ssh_user parameter" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args).not_to include("--user=#{machine.ssh_info[:username]}")
|
||||
expect(args).to include("--extra-vars=ansible_ssh_user='#{machine.ssh_info[:username]}'")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -575,7 +744,7 @@ VF
|
|||
end
|
||||
|
||||
it "uses a --user argument to set a default remote user" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args).not_to include("--extra-vars=ansible_ssh_user='#{machine.ssh_info[:username]}'")
|
||||
expect(args).to include("--user=#{machine.ssh_info[:username]}")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -589,7 +758,7 @@ VF
|
|||
end
|
||||
|
||||
it "sets ANSIBLE_CONFIG environment variable" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]).to include("ANSIBLE_CONFIG")
|
||||
expect(cmd_opts[:env]['ANSIBLE_CONFIG']).to eql(existing_file)
|
||||
|
@ -605,7 +774,7 @@ VF
|
|||
it_should_set_arguments_and_environment_variables 6
|
||||
|
||||
it "should ask the vault password" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args).to include("--ask-vault-pass")
|
||||
}.and_return(default_execute_result)
|
||||
end
|
||||
|
@ -619,7 +788,7 @@ VF
|
|||
it_should_set_arguments_and_environment_variables 6
|
||||
|
||||
it "uses the given vault password file" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args).to include("--vault-password-file=#{existing_file}")
|
||||
}.and_return(default_execute_result)
|
||||
end
|
||||
|
@ -634,7 +803,7 @@ VF
|
|||
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(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
raw_opt_index = cmd_opts[:env]['ANSIBLE_SSH_ARGS'].index("-o ControlMaster=no")
|
||||
default_opt_index = cmd_opts[:env]['ANSIBLE_SSH_ARGS'].index("-o ControlMaster=auto")
|
||||
|
@ -648,7 +817,7 @@ VF
|
|||
end
|
||||
|
||||
it "sets '-o ForwardAgent=yes' via ANSIBLE_SSH_ARGS with higher priority than raw_ssh_args values" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
forwardAgentYes = cmd_opts[:env]['ANSIBLE_SSH_ARGS'].index("-o ForwardAgent=yes")
|
||||
forwardAgentNo = cmd_opts[:env]['ANSIBLE_SSH_ARGS'].index("-o ForwardAgent=no")
|
||||
|
@ -668,7 +837,7 @@ VF
|
|||
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(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentityFile=/an/other/identity")
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentityFile=/yet/an/other/key")
|
||||
|
@ -682,7 +851,7 @@ VF
|
|||
end
|
||||
|
||||
it "replaces `%` with `%%`" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentityFile=/foo%%bar/key")
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentityFile=/bar%%%%buz/key")
|
||||
|
@ -699,7 +868,7 @@ VF
|
|||
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(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ForwardAgent=yes")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -712,7 +881,7 @@ VF
|
|||
end
|
||||
|
||||
it "sets '-o ProxyCommand' via ANSIBLE_SSH_ARGS" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ProxyCommand='ssh -W %h:%p -q user@remote_libvirt_host'")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -782,10 +951,12 @@ VF
|
|||
describe "without colorized output" do
|
||||
before do
|
||||
allow(machine.env).to receive(:ui).and_return(Vagrant::UI::Basic.new)
|
||||
|
||||
allow(machine.env.ui).to receive(:warn).and_return("") # hide the breaking change warning
|
||||
end
|
||||
|
||||
it "disables ansible-playbook colored output" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]).to_not include("ANSIBLE_FORCE_COLOR")
|
||||
expect(cmd_opts[:env]['ANSIBLE_NOCOLOR']).to eql("true")
|
||||
|
@ -793,6 +964,54 @@ VF
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
context "with version option set" do
|
||||
before do
|
||||
config.version = "2.3.4.5"
|
||||
end
|
||||
|
||||
describe "and the installed ansible version is correct" do
|
||||
before do
|
||||
allow(subject).to receive(:gather_ansible_version).and_return("ansible #{config.version}\n...\n")
|
||||
end
|
||||
|
||||
it "executes ansible-playbook command" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args).and_return(default_execute_result)
|
||||
end
|
||||
end
|
||||
|
||||
describe "and there is an ansible version mismatch" do
|
||||
before do
|
||||
allow(subject).to receive(:gather_ansible_version).and_return("ansible 1.9.6\n...\n")
|
||||
end
|
||||
|
||||
it "raises an error about the ansible version mismatch", skip_before: false, skip_after: true do
|
||||
expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleVersionMismatch)
|
||||
end
|
||||
end
|
||||
|
||||
describe "and the installed ansible version cannot be detected" do
|
||||
before do
|
||||
allow(subject).to receive(:gather_ansible_version).and_return("")
|
||||
end
|
||||
|
||||
it "skips the ansible version check and executes ansible-playbook command" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args).and_return(default_execute_result)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with special value: 'latest'" do
|
||||
before do
|
||||
config.version = :latest
|
||||
allow(subject).to receive(:gather_ansible_version).and_return("ansible 2.2.0.1\n...\n")
|
||||
end
|
||||
|
||||
it "skips the ansible version check and executes ansible-playbook command" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args).and_return(default_execute_result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with galaxy support" do
|
||||
|
||||
before do
|
||||
|
@ -809,7 +1028,11 @@ VF
|
|||
expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleCommandFailed)
|
||||
end
|
||||
|
||||
it "execute ansible-galaxy, and then ansible-playbook" do
|
||||
it "execute three commands: ansible --version, ansible-galaxy, and ansible-playbook" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute)
|
||||
.once
|
||||
.with('ansible', '--version', { :notify => [:stdout, :stderr] })
|
||||
.and_return(default_execute_result)
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute)
|
||||
.once
|
||||
.with('ansible-galaxy', any_args)
|
||||
|
@ -843,7 +1066,7 @@ VF
|
|||
end
|
||||
|
||||
it "sets ANSIBLE_ROLES_PATH with corresponding absolute path" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]).to include("ANSIBLE_ROLES_PATH")
|
||||
expect(cmd_opts[:env]['ANSIBLE_ROLES_PATH']).to eql(File.join(machine.env.root_path, "my-roles"))
|
||||
|
@ -854,10 +1077,10 @@ VF
|
|||
context "with extra_vars option defined" do
|
||||
describe "with a hash value" do
|
||||
before do
|
||||
config.extra_vars = { var1: %Q(string with 'apostrophes', \\, " and =), var2: { x: 42 } }
|
||||
config.extra_vars = { var1: %Q(string with 'apo$trophe$', \\, " and =), var2: { x: 42 } }
|
||||
end
|
||||
|
||||
it_should_set_optional_arguments({ "extra_vars" => "--extra-vars={\"var1\":\"string with 'apostrophes', \\\\, \\\" and =\",\"var2\":{\"x\":42}}" })
|
||||
it_should_set_optional_arguments({ "extra_vars" => "--extra-vars={\"var1\":\"string with 'apo$trophe$', \\\\, \\\" and =\",\"var2\":{\"x\":42}}" })
|
||||
end
|
||||
|
||||
describe "with a string value referring to file path (with the '@' prefix)" do
|
||||
|
@ -879,11 +1102,11 @@ VF
|
|||
|
||||
# command line arguments
|
||||
config.galaxy_roles_path = "/up/to the stars"
|
||||
config.extra_vars = { var1: %Q(string with 'apostrophes', \\, " and =), var2: { x: 42 } }
|
||||
config.sudo = true
|
||||
config.sudo_user = 'deployer'
|
||||
config.extra_vars = { var1: %Q(string with 'apo$trophe$', \\, " and =), var2: { x: 42 } }
|
||||
config.become = true
|
||||
config.become_user = 'deployer'
|
||||
config.verbose = "vvv"
|
||||
config.ask_sudo_pass = true
|
||||
config.ask_become_pass = true
|
||||
config.ask_vault_pass = true
|
||||
config.vault_password_file = existing_file
|
||||
config.tags = %w(db www)
|
||||
|
@ -900,11 +1123,11 @@ VF
|
|||
|
||||
it_should_set_arguments_and_environment_variables 21, 6, true
|
||||
it_should_explicitly_enable_ansible_ssh_control_persist_defaults
|
||||
it_should_set_optional_arguments({ "extra_vars" => "--extra-vars={\"var1\":\"string with 'apostrophes', \\\\, \\\" and =\",\"var2\":{\"x\":42}}",
|
||||
"sudo" => "--sudo",
|
||||
"sudo_user" => "--sudo-user=deployer",
|
||||
it_should_set_optional_arguments({ "extra_vars" => "--extra-vars={\"var1\":\"string with 'apo$trophe$', \\\\, \\\" and =\",\"var2\":{\"x\":42}}",
|
||||
"become" => "--sudo",
|
||||
"become_user" => "--sudo-user=deployer",
|
||||
"verbose" => "-vvv",
|
||||
"ask_sudo_pass" => "--ask-sudo-pass",
|
||||
"ask_become_pass" => "--ask-sudo-pass",
|
||||
"ask_vault_pass" => "--ask-vault-pass",
|
||||
"vault_password_file" => "--vault-password-file=#{File.expand_path(__FILE__)}",
|
||||
"tags" => "--tags=db,www",
|
||||
|
@ -914,7 +1137,7 @@ VF
|
|||
})
|
||||
|
||||
it "also includes given raw arguments" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(args).to include("--why-not")
|
||||
expect(args).to include("--su-user=foot")
|
||||
expect(args).to include("--ask-su-pass")
|
||||
|
@ -925,7 +1148,7 @@ VF
|
|||
|
||||
it "shows the ansible-playbook command, with additional quotes when required" do
|
||||
expect(machine.env.ui).to receive(:detail)
|
||||
.with(%Q(PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_ROLES_PATH='/up/to the stars' ANSIBLE_CONFIG='#{existing_file}' ANSIBLE_HOST_KEY_CHECKING=true ANSIBLE_SSH_ARGS='-o IdentitiesOnly=yes -o IdentityFile=/my/key1 -o IdentityFile=/my/key2 -o ForwardAgent=yes -o ControlMaster=no -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --ask-sudo-pass --ask-vault-pass --limit="machine*:&vagrant:!that_one" --inventory-file=#{generated_inventory_dir} --extra-vars="{\\"var1\\":\\"string with 'apostrophes', \\\\\\\\, \\\\\\" and =\\",\\"var2\\":{\\"x\\":42}}" --sudo --sudo-user=deployer -vvv --vault-password-file=#{existing_file} --tags=db,www --skip-tags=foo,bar --start-at-task="joe's awesome task" --why-not --su-user=foot --ask-su-pass --limit=all --private-key=./myself.key --extra-vars='{\"var3\":\"foo\"}' playbook.yml))
|
||||
.with(%Q(PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_ROLES_PATH='/up/to the stars' ANSIBLE_CONFIG='#{existing_file}' ANSIBLE_HOST_KEY_CHECKING=true ANSIBLE_SSH_ARGS='-o IdentitiesOnly=yes -o IdentityFile=/my/key1 -o IdentityFile=/my/key2 -o ForwardAgent=yes -o ControlMaster=no -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --ask-sudo-pass --ask-vault-pass --limit="machine*:&vagrant:!that_one" --inventory-file=#{generated_inventory_dir} --extra-vars=\\{\\"var1\\":\\"string\\ with\\ \\'apo\\$trophe\\$\\',\\ \\\\\\\\,\\ \\\\\\"\\ and\\ \\=\\",\\"var2\\":\\{\\"x\\":42\\}\\} --sudo --sudo-user=deployer -vvv --vault-password-file=#{existing_file} --tags=db,www --skip-tags=foo,bar --start-at-task="joe's awesome task" --why-not --su-user=foot --ask-su-pass --limit=all --private-key=./myself.key --extra-vars='{\"var3\":\"foo\"}' playbook.yml))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -954,7 +1177,7 @@ VF
|
|||
end
|
||||
|
||||
it "uses an SSH ProxyCommand to reach the VM" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ProxyCommand='ssh boot9docker@127.0.0.1 -p 2299 -i /path/to/docker/host/key -o Compression=yes -o ConnectTimeout=5 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no exec nc %h %p 2>/dev/null'")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -969,11 +1192,14 @@ VF
|
|||
before do
|
||||
allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true)
|
||||
allow(machine.ui).to receive(:warn)
|
||||
|
||||
# Set the compatibility mode to only get the Windows warning
|
||||
config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8
|
||||
end
|
||||
|
||||
it "warns that Windows is not officially supported for the Ansible control machine" do
|
||||
expect(machine.env.ui).to receive(:warn)
|
||||
.with(I18n.t("vagrant.provisioners.ansible.windows_not_supported_for_control_machine"))
|
||||
.with(I18n.t("vagrant.provisioners.ansible.windows_not_supported_for_control_machine") + "\n")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -983,7 +1209,7 @@ VF
|
|||
end
|
||||
|
||||
it "does not set IdentitiesOnly=yes in ANSIBLE_SSH_ARGS" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to_not include("-o IdentitiesOnly=yes")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -993,7 +1219,7 @@ VF
|
|||
it "does not set ANSIBLE_SSH_ARGS environment variable" do
|
||||
config.host_key_checking = true
|
||||
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]).to_not include('ANSIBLE_SSH_ARGS')
|
||||
}.and_return(Vagrant::Util::Subprocess::Result.new(0, "", ""))
|
||||
|
@ -1006,7 +1232,7 @@ VF
|
|||
it 'does not set IdentitiesOnly=yes in ANSIBLE_SSH_ARGS' do
|
||||
ssh_info[:keys_only] = false
|
||||
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args|
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
cmd_opts = args.last
|
||||
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to_not include("-o IdentitiesOnly=yes")
|
||||
}.and_return(default_execute_result)
|
||||
|
|
|
@ -89,5 +89,39 @@ describe VagrantPlugins::FileUpload::Provisioner do
|
|||
|
||||
subject.provision
|
||||
end
|
||||
|
||||
it "appends a '/.' if the destination doesnt end with a file separator" do
|
||||
allow(config).to receive(:source).and_return("/source")
|
||||
allow(config).to receive(:destination).and_return("/foo/bar")
|
||||
allow(File).to receive(:directory?).with("/source").and_return(true)
|
||||
|
||||
expect(guest).to receive(:capability?).
|
||||
with(:shell_expand_guest_path).and_return(true)
|
||||
expect(guest).to receive(:capability).
|
||||
with(:shell_expand_guest_path, "/foo/bar").and_return("/foo/bar")
|
||||
|
||||
expect(communicator).to receive(:upload).with("/source/.", "/foo/bar")
|
||||
|
||||
subject.provision
|
||||
end
|
||||
|
||||
it "sends an array of files and folders if winrm and destination doesn't end with file separator" do
|
||||
files = ["/source/file.py", "/source/folder"]
|
||||
allow(Dir).to receive(:[]).and_return(files)
|
||||
allow(config).to receive(:source).and_return("/source")
|
||||
allow(config).to receive(:destination).and_return("/foo/bar")
|
||||
allow(File).to receive(:directory?).with("/source").and_return(true)
|
||||
allow(machine.config.vm).to receive(:communicator).and_return(:winrm)
|
||||
|
||||
expect(guest).to receive(:capability?).
|
||||
with(:shell_expand_guest_path).and_return(true)
|
||||
expect(guest).to receive(:capability).
|
||||
with(:shell_expand_guest_path, "/foo/bar").and_return("/foo/bar")
|
||||
|
||||
expect(communicator).to receive(:upload)
|
||||
.with(files, "/foo/bar")
|
||||
|
||||
subject.provision
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -78,5 +78,41 @@ describe VagrantPlugins::Salt::Config do
|
|||
expect(result[error_key]).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "salt_call_args" do
|
||||
it "fails if salt_call_args is not an array" do
|
||||
subject.salt_call_args = "--flags"
|
||||
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.salt_call_args = ["--flags"]
|
||||
subject.finalize!
|
||||
|
||||
result = subject.validate(machine)
|
||||
expect(result[error_key]).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "salt_args" do
|
||||
it "fails if not an array" do
|
||||
subject.salt_args = "--flags"
|
||||
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.salt_args = ["--flags"]
|
||||
subject.finalize!
|
||||
|
||||
result = subject.validate(machine)
|
||||
expect(result[error_key]).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,4 +32,89 @@ describe VagrantPlugins::Salt::Provisioner do
|
|||
describe "#provision" do
|
||||
|
||||
end
|
||||
|
||||
describe "#call_highstate" do
|
||||
context "master" do
|
||||
it "passes along extra cli flags" do
|
||||
allow(config).to receive(:run_highstate).and_return(true)
|
||||
allow(config).to receive(:verbose).and_return(true)
|
||||
allow(config).to receive(:masterless?).and_return(false)
|
||||
allow(config).to receive(:masterless).and_return(false)
|
||||
allow(config).to receive(:minion_id).and_return(nil)
|
||||
allow(config).to receive(:log_level).and_return(nil)
|
||||
allow(config).to receive(:colorize).and_return(false)
|
||||
allow(config).to receive(:pillar_data).and_return([])
|
||||
allow(config).to receive(:install_master).and_return(true)
|
||||
|
||||
allow(config).to receive(:salt_args).and_return(["--async"])
|
||||
allow(machine.communicate).to receive(:sudo)
|
||||
allow(machine.config.vm).to receive(:communicator).and_return(:notwinrm)
|
||||
|
||||
expect(machine.communicate).to receive(:sudo).with("salt '*' state.highstate --verbose --log-level=debug --no-color --async", {:error_key=>:ssh_bad_exit_status_muted})
|
||||
subject.call_highstate()
|
||||
end
|
||||
|
||||
it "has no additional cli flags if not included" do
|
||||
allow(config).to receive(:run_highstate).and_return(true)
|
||||
allow(config).to receive(:verbose).and_return(true)
|
||||
allow(config).to receive(:masterless?).and_return(false)
|
||||
allow(config).to receive(:masterless).and_return(false)
|
||||
allow(config).to receive(:minion_id).and_return(nil)
|
||||
allow(config).to receive(:log_level).and_return(nil)
|
||||
allow(config).to receive(:colorize).and_return(false)
|
||||
allow(config).to receive(:pillar_data).and_return([])
|
||||
allow(config).to receive(:install_master).and_return(true)
|
||||
|
||||
allow(config).to receive(:salt_args).and_return(nil)
|
||||
allow(machine.communicate).to receive(:sudo)
|
||||
allow(machine.config.vm).to receive(:communicator).and_return(:notwinrm)
|
||||
|
||||
expect(machine.communicate).to receive(:sudo).with("salt '*' state.highstate --verbose --log-level=debug --no-color ", {:error_key=>:ssh_bad_exit_status_muted})
|
||||
subject.call_highstate()
|
||||
end
|
||||
end
|
||||
|
||||
context "with masterless" do
|
||||
it "passes along extra cli flags" do
|
||||
allow(config).to receive(:run_highstate).and_return(true)
|
||||
allow(config).to receive(:verbose).and_return(true)
|
||||
allow(config).to receive(:masterless?).and_return(true)
|
||||
allow(config).to receive(:masterless).and_return(true)
|
||||
allow(config).to receive(:minion_id).and_return(nil)
|
||||
allow(config).to receive(:log_level).and_return(nil)
|
||||
allow(config).to receive(:colorize).and_return(false)
|
||||
allow(config).to receive(:pillar_data).and_return([])
|
||||
|
||||
allow(config).to receive(:salt_args).and_return(["--async"])
|
||||
allow(config).to receive(:salt_call_args).and_return(["--output-dif"])
|
||||
allow(machine.communicate).to receive(:sudo)
|
||||
allow(machine.config.vm).to receive(:communicator).and_return(:notwinrm)
|
||||
allow(config).to receive(:install_master).and_return(false)
|
||||
|
||||
expect(machine.communicate).to receive(:sudo).with("salt-call state.highstate --retcode-passthrough --local --log-level=debug --no-color --output-dif", {:error_key=>:ssh_bad_exit_status_muted})
|
||||
subject.call_highstate()
|
||||
end
|
||||
|
||||
it "has no additional cli flags if not included" do
|
||||
allow(config).to receive(:run_highstate).and_return(true)
|
||||
allow(config).to receive(:verbose).and_return(true)
|
||||
allow(config).to receive(:masterless?).and_return(true)
|
||||
allow(config).to receive(:masterless).and_return(true)
|
||||
allow(config).to receive(:minion_id).and_return(nil)
|
||||
allow(config).to receive(:log_level).and_return(nil)
|
||||
allow(config).to receive(:colorize).and_return(false)
|
||||
allow(config).to receive(:pillar_data).and_return([])
|
||||
|
||||
allow(config).to receive(:salt_call_args).and_return(nil)
|
||||
allow(config).to receive(:salt_args).and_return(nil)
|
||||
allow(machine.communicate).to receive(:sudo)
|
||||
allow(machine.config.vm).to receive(:communicator).and_return(:notwinrm)
|
||||
allow(config).to receive(:install_master).and_return(false)
|
||||
|
||||
expect(machine.communicate).to receive(:sudo).with("salt-call state.highstate --retcode-passthrough --local --log-level=debug --no-color ", {:error_key=>:ssh_bad_exit_status_muted})
|
||||
subject.call_highstate()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -133,4 +133,27 @@ describe Vagrant::Util::Platform do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".systemd?" do
|
||||
before{ allow(subject).to receive(:windows?).and_return(false) }
|
||||
after{ subject.reset! }
|
||||
|
||||
context "on windows" do
|
||||
before{ expect(subject).to receive(:windows?).and_return(true) }
|
||||
|
||||
it "should return false" do
|
||||
expect(subject.systemd?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
it "should return true if systemd is in use" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).and_return(double(:result, stdout: "systemd"))
|
||||
expect(subject.systemd?).to be_truthy
|
||||
end
|
||||
|
||||
it "should return false if systemd is not in use" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).and_return(double(:result, stdout: "other"))
|
||||
expect(subject.systemd?).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -67,7 +67,7 @@ describe Vagrant::Util::SSH do
|
|||
|
||||
expect(described_class.exec(ssh_info)).to eq(nil)
|
||||
expect(Vagrant::Util::SafeExec).to have_received(:exec)
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL","-o", "Compression=yes", "-o", "DSAAuthentication=yes", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=#{ssh_info[:private_key_path][0]}")
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL","-o", "Compression=yes", "-o", "DSAAuthentication=yes", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"")
|
||||
end
|
||||
|
||||
context "when disabling compression or dsa_authentication flags" do
|
||||
|
@ -85,7 +85,7 @@ describe Vagrant::Util::SSH do
|
|||
|
||||
expect(described_class.exec(ssh_info)).to eq(nil)
|
||||
expect(Vagrant::Util::SafeExec).to have_received(:exec)
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=#{ssh_info[:private_key_path][0]}")
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,7 +103,7 @@ describe Vagrant::Util::SSH do
|
|||
|
||||
expect(described_class.exec(ssh_info)).to eq(nil)
|
||||
expect(Vagrant::Util::SafeExec).to have_received(:exec)
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "IdentityFile=#{ssh_info[:private_key_path][0]}")
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -140,7 +140,7 @@ describe Vagrant::Util::SSH do
|
|||
|
||||
expect(described_class.exec(ssh_info)).to eq(nil)
|
||||
expect(Vagrant::Util::SafeExec).to have_received(:exec)
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=#{ssh_info[:private_key_path][0]}","-o", "ForwardX11=yes", "-o", "ForwardX11Trusted=yes")
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"","-o", "ForwardX11=yes", "-o", "ForwardX11Trusted=yes")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -158,7 +158,7 @@ describe Vagrant::Util::SSH do
|
|||
|
||||
expect(described_class.exec(ssh_info)).to eq(nil)
|
||||
expect(Vagrant::Util::SafeExec).to have_received(:exec)
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=#{ssh_info[:private_key_path][0]}","-o", "ForwardAgent=yes")
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"","-o", "ForwardAgent=yes")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -176,7 +176,7 @@ describe Vagrant::Util::SSH do
|
|||
|
||||
expect(described_class.exec(ssh_info)).to eq(nil)
|
||||
expect(Vagrant::Util::SafeExec).to have_received(:exec)
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=#{ssh_info[:private_key_path][0]}", "-L", "8008:localhost:80")
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"", "-L", "8008:localhost:80")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -194,7 +194,7 @@ describe Vagrant::Util::SSH do
|
|||
|
||||
expect(described_class.exec(ssh_info)).to eq(nil)
|
||||
expect(Vagrant::Util::SafeExec).to have_received(:exec)
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=#{ssh_info[:private_key_path][0]}", "-6")
|
||||
.with("ssh", "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"", "-6")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.9.9.dev
|
||||
2.0.1.dev
|
||||
|
|
|
@ -2,7 +2,7 @@ set :base_url, "https://www.vagrantup.com/"
|
|||
|
||||
activate :hashicorp do |h|
|
||||
h.name = "vagrant"
|
||||
h.version = "1.9.8"
|
||||
h.version = "2.0.0"
|
||||
h.github_slug = "mitchellh/vagrant"
|
||||
h.website_root = "website"
|
||||
end
|
||||
|
|
|
@ -14,10 +14,8 @@ description: |-
|
|||
The Vagrant Ansible provisioner allows you to provision the guest using [Ansible](http://ansible.com) playbooks by executing **`ansible-playbook` from the Vagrant host**.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Warning:</strong> If you are not familiar with Ansible and Vagrant already,
|
||||
I recommend starting with the <a href="/docs/provisioning/shell.html">shell
|
||||
provisioner</a>. However, if you are comfortable with Vagrant already, Vagrant
|
||||
is a great way to learn Ansible.
|
||||
<strong>Warning:</strong>
|
||||
If you are not familiar with Ansible and Vagrant already, I recommend starting with the <a href="/docs/provisioning/shell.html">shell provisioner</a>. However, if you are comfortable with Vagrant already, Vagrant is a great way to learn Ansible.
|
||||
</div>
|
||||
|
||||
## Setup Requirements
|
||||
|
@ -53,10 +51,17 @@ end
|
|||
|
||||
This section lists the _specific_ options for the Ansible (remote) provisioner. In addition to the options listed below, this provisioner supports the [**common options** for both Ansible provisioners](/docs/provisioning/ansible_common.html).
|
||||
|
||||
- `ask_sudo_pass` (boolean) - require Ansible to [prompt for a sudo password](https://docs.ansible.com/intro_getting_started.html#remote-connection-information).
|
||||
- `ask_become_pass` (boolean) - require Ansible to [prompt for a password](https://docs.ansible.com/intro_getting_started.html#remote-connection-information) when switching to another user with the [become/sudo mechanism](http://docs.ansible.com/ansible/become.html).
|
||||
|
||||
The default value is `false`.
|
||||
|
||||
- `ask_sudo_pass` (boolean) - Backwards compatible alias for the [ask_become_pass](#ask_become_pass) option.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Deprecation:</strong>
|
||||
The `ask_sudo_pass` option is deprecated and will be removed in a future release. Please use the [**`ask_become_pass`**](#ask_become_pass) option instead.
|
||||
</div>
|
||||
|
||||
- `ask_vault_pass` (boolean) - require Ansible to [prompt for a vault password](https://docs.ansible.com/playbooks_vault.html#vault).
|
||||
|
||||
The default value is `false`.
|
||||
|
@ -67,7 +72,10 @@ This section lists the _specific_ options for the Ansible (remote) provisioner.
|
|||
|
||||
The default value is `true`.
|
||||
|
||||
**Note:** This option was introduced in Vagrant 1.8.0. Previous Vagrant versions behave like if this option was set to `false`.
|
||||
<div class="alert alert-info">
|
||||
<strong>Compatibility Note:</strong>
|
||||
This option was introduced in Vagrant 1.8.0. Previous Vagrant versions behave like if this option was set to `false`.
|
||||
</div>
|
||||
|
||||
- `host_key_checking` (boolean) - require Ansible to [enable SSH host key checking](https://docs.ansible.com/intro_getting_started.html#host-key-checking).
|
||||
|
||||
|
@ -115,9 +123,10 @@ N = 3
|
|||
end
|
||||
```
|
||||
|
||||
**Caveats:**
|
||||
|
||||
If you apply this parallel provisioning pattern with a static Ansible inventory, you will have to organize the things so that [all the relevant private keys are provided to the `ansible-playbook` command](https://github.com/mitchellh/vagrant/pull/5765#issuecomment-120247738). The same kind of considerations applies if you are using multiple private keys for a same machine (see [`config.ssh.private_key_path` SSH setting](/docs/vagrantfile/ssh_settings.html)).
|
||||
<div class="alert alert-info">
|
||||
<strong>Tip:</strong>
|
||||
If you apply this parallel provisioning pattern with a static Ansible inventory, you will have to organize the things so that [all the relevant private keys are provided to the `ansible-playbook` command](https://github.com/mitchellh/vagrant/pull/5765#issuecomment-120247738). The same kind of considerations applies if you are using multiple private keys for a same machine (see [`config.ssh.private_key_path` SSH setting](/docs/vagrantfile/ssh_settings.html)).
|
||||
</div>
|
||||
|
||||
### Force Paramiko Connection Mode
|
||||
|
||||
|
|
|
@ -17,6 +17,36 @@ These options get passed to the `ansible-playbook` command that ships with Ansib
|
|||
|
||||
Some of these options are for advanced usage only and should not be used unless you understand their purpose.
|
||||
|
||||
- `become` (boolean) - Perform all the Ansible playbook tasks [as another user](http://docs.ansible.com/ansible/become.html), different from the user used to log into the guest system.
|
||||
|
||||
The default value is `false`.
|
||||
|
||||
- `become_user` (string) - Set the default username to be used by the Ansible `become` [privilege escalation](http://docs.ansible.com/ansible/become.html) mechanism.
|
||||
|
||||
By default this option is not set, and the Ansible default value (`root`) will be used.
|
||||
|
||||
- `compatibility_mode` (string) - Set the **minimal** version of Ansible to be supported. Vagrant will only use parameters that are compatible with the given version.
|
||||
|
||||
Possible values:
|
||||
|
||||
- `"auto"` _(Vagrant will automatically select the optimal compatibilty mode by checking the Ansible version currently available)_
|
||||
- `"1.8"` _(Ansible versions prior to 1.8 should mostly work well, but some options might not be supported)_
|
||||
- `"2.0"` _(The generated Ansible inventory will be incompatible with Ansible 1.x)_
|
||||
|
||||
By default this option is set to `"auto"`. If Vagrant is not able to detect any supported Ansible version, it will fall back on the compatibility mode `"1.8"` with a warning.
|
||||
|
||||
Vagrant will error if the specified compatibility mode is incompatible with the current Ansible version.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Attention:</strong>
|
||||
Vagrant doesn't perform any validation between the `compatibility_mode` value and the value of the [`version`](#version) option.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>Compatibility Note:</strong>
|
||||
This option was introduced in Vagrant 2.0. The behavior of previous Vagrant versions can be simulated by setting the `compatibility_mode` to `"1.8"`.
|
||||
</div>
|
||||
|
||||
- `config_file` (string) - The path to an [Ansible Configuration file](https://docs.ansible.com/intro_configuration.html).
|
||||
|
||||
By default, this option is not set, and Ansible will [search for a possible configuration file in some default locations](/docs/provisioning/ansible_intro.html#ANSIBLE_CONFIG).
|
||||
|
@ -93,7 +123,7 @@ Some of these options are for advanced usage only and should not be used unless
|
|||
ansible.host_vars = {
|
||||
"host1" => {"http_port" => 80,
|
||||
"maxRequestsPerChild" => 808},
|
||||
"comments" => "'text with spaces'",
|
||||
"comments" => "text with spaces",
|
||||
"host2" => {"http_port" => 303,
|
||||
"maxRequestsPerChild" => 909}
|
||||
}
|
||||
|
@ -123,17 +153,28 @@ Some of these options are for advanced usage only and should not be used unless
|
|||
- `['--check', '-M', '/my/modules']`
|
||||
- `["--connection=paramiko", "--forks=10"]`
|
||||
|
||||
**Caveat:** The `ansible` provisioner does not support whitespace characters in `raw_arguments` elements. Therefore **don't write** something like `["-c paramiko"]`, which will result with an invalid `" parmiko"` parameter value.
|
||||
<div class="alert alert-warn">
|
||||
<strong>Attention:</strong>
|
||||
The `ansible` provisioner does not support whitespace characters in `raw_arguments` elements. Therefore **don't write** something like `["-c paramiko"]`, which will result with an invalid `" parmiko"` parameter value.
|
||||
</div>
|
||||
|
||||
- `skip_tags` (string or array of strings) - Only plays, roles and tasks that [*do not match* these values will be executed](https://docs.ansible.com/playbooks_tags.html).
|
||||
|
||||
- `start_at_task` (string) - The task name where the [playbook execution will start](https://docs.ansible.com/playbooks_startnstep.html#start-at-task).
|
||||
|
||||
- `sudo` (boolean) - Cause Ansible to perform all the playbook tasks [using sudo](https://docs.ansible.com/glossary.html#sudo).
|
||||
- `sudo` (boolean) - Backwards compatible alias for the [`become`](#become) option.
|
||||
|
||||
The default value is `false`.
|
||||
<div class="alert alert-warning">
|
||||
<strong>Deprecation:</strong>
|
||||
The `sudo` option is deprecated and will be removed in a future release. Please use the [**`become`**](#become) option instead.
|
||||
</div>
|
||||
|
||||
- `sudo_user` (string) - set the default username who should be used by the sudo command.
|
||||
- `sudo_user` (string) - Backwards compatible alias for the [`become_user`](#become_user) option.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Deprecation:</strong>
|
||||
The `sudo_user` option is deprecated and will be removed in a future release. Please use the [**`become_user`**](#become_user) option instead.
|
||||
</div>
|
||||
|
||||
- `tags` (string or array of strings) - Only plays, roles and tasks [tagged with these values will be executed](https://docs.ansible.com/playbooks_tags.html) .
|
||||
|
||||
|
@ -146,3 +187,16 @@ Some of these options are for advanced usage only and should not be used unless
|
|||
Examples: `true` (equivalent to `v`), `-vvv` (equivalent to `vvv`), `vvvv`.
|
||||
|
||||
Note that when the `verbose` option is enabled, the `ansible-playbook` command used by Vagrant will be displayed.
|
||||
|
||||
- `version` (string) - The expected Ansible version.
|
||||
|
||||
This option is disabled by default.
|
||||
|
||||
When an Ansible version is defined (e.g. `"2.1.6.0"`), the Ansible provisioner will be executed only if Ansible is installed at the requested version.
|
||||
|
||||
When this option is set to `"latest"`, no version check is applied.
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>Tip:</strong>
|
||||
With the `ansible_local` provisioner, it is currently possible to use this option to specify which version of Ansible must be automatically installed, but <strong>only</strong> in combination with the [**`install_mode`**](/docs/provisioning/ansible_local.html#install_mode) set to <strong>`:pip`</strong>.
|
||||
</div>
|
||||
|
|
|
@ -14,10 +14,8 @@ description: |-
|
|||
The Vagrant Ansible Local provisioner allows you to provision the guest using [Ansible](http://ansible.com) playbooks by executing **`ansible-playbook` directly on the guest machine**.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Warning:</strong> If you are not familiar with Ansible and Vagrant already,
|
||||
I recommend starting with the <a href="/docs/provisioning/shell.html">shell
|
||||
provisioner</a>. However, if you are comfortable with Vagrant already, Vagrant
|
||||
is a great way to learn Ansible.
|
||||
<strong>Warning:</strong>
|
||||
If you are not familiar with Ansible and Vagrant already, I recommend starting with the <a href="/docs/provisioning/shell.html">shell provisioner</a>. However, if you are comfortable with Vagrant already, Vagrant is a great way to learn Ansible.
|
||||
</div>
|
||||
|
||||
## Setup Requirements
|
||||
|
@ -64,10 +62,13 @@ This section lists the _specific_ options for the Ansible Local provisioner. In
|
|||
Vagrant will try to install (or upgrade) Ansible when one of these conditions are met:
|
||||
|
||||
- Ansible is not installed (or cannot be found).
|
||||
- The `version` option is set to `"latest"`.
|
||||
- The current Ansible version does not correspond to the `version` option.
|
||||
- The [`version`](/docs/provisioning/ansible_common.html#version) option is set to `"latest"`.
|
||||
- The current Ansible version does not correspond to the [`version`](/docs/provisioning/ansible_common.html#version) option.
|
||||
|
||||
**Attention:** There is no guarantee that this automated installation will replace a custom Ansible setup, that might be already present on the Vagrant box.
|
||||
<div class="alert alert-warning">
|
||||
<strong>Attention:</strong>
|
||||
There is no guarantee that this automated installation will replace a custom Ansible setup, that might be already present on the Vagrant box.
|
||||
</div>
|
||||
|
||||
- `install_mode` (`:default`, `:pip`, or `:pip_args_only`) - Select the way to automatically install Ansible on the guest system.
|
||||
|
||||
|
@ -75,7 +76,7 @@ This section lists the _specific_ options for the Ansible Local provisioner. In
|
|||
- On Ubuntu-like systems, the latest Ansible release is installed from the `ppa:ansible/ansible` repository.
|
||||
- On RedHat-like systems, the latest Ansible release is installed from the [EPEL](http://fedoraproject.org/wiki/EPEL) repository.
|
||||
|
||||
- `:pip`: Ansible is installed from [PyPI](https://pypi.python.org/pypi) with [pip](https://pip.pypa.io) package installer. With this mode, Vagrant will systematically try to [install the latest pip version](https://pip.pypa.io/en/stable/installing/#installing-with-get-pip-py). With the `:pip` mode you can optionally install a specific Ansible release by setting the [`version`](#version) option.
|
||||
- `:pip`: Ansible is installed from [PyPI](https://pypi.python.org/pypi) with [pip](https://pip.pypa.io) package installer. With this mode, Vagrant will systematically try to [install the latest pip version](https://pip.pypa.io/en/stable/installing/#installing-with-get-pip-py). With the `:pip` mode you can optionally install a specific Ansible release by setting the [`version`](/docs/provisioning/ansible_common.html#version) option.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -140,16 +141,6 @@ This section lists the _specific_ options for the Ansible Local provisioner. In
|
|||
|
||||
The default value is `/tmp/vagrant-ansible`
|
||||
|
||||
- `version` (string) - The expected Ansible version.
|
||||
|
||||
This option is disabled by default.
|
||||
|
||||
When an Ansible version is defined (e.g. `"1.8.2"`), the Ansible local provisioner will be executed only if Ansible is installed at the requested version.
|
||||
|
||||
When this option is set to `"latest"`, no version check is applied.
|
||||
|
||||
**Warning:** It is currently possible to use this option to specify which version of Ansible must be automatically installed, but only in combination with the `install_mode` set to `:pip`.
|
||||
|
||||
## Tips and Tricks
|
||||
|
||||
### Ansible Parallel Execution from a Guest
|
||||
|
|
|
@ -25,6 +25,42 @@ new VM.
|
|||
config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig"
|
||||
end
|
||||
|
||||
If you want to upload a folder to your guest system, it can be accomplished by
|
||||
using a file provisioner seen below. When copied, the resulting folder on the guest will
|
||||
replace `folder` as `newfolder` and place its on the guest machine. Note that if
|
||||
you'd like the same folder name on your guest machine, make sure that the destination
|
||||
path has the same name as the folder on your host.
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
# ... other configuration
|
||||
|
||||
config.vm.provision "file", source: "~/path/to/host/folder", destination: "$HOME/remote/newfolder"
|
||||
end
|
||||
|
||||
Prior to copying `~/path/to/host/folder` to the guest machine:
|
||||
|
||||
folder
|
||||
├── script.sh
|
||||
├── otherfolder
|
||||
│ └── hello.sh
|
||||
├── goodbye.sh
|
||||
├── hello.sh
|
||||
└── woot.sh
|
||||
|
||||
1 directory, 5 files
|
||||
|
||||
After to copying `~/path/to/host/folder` into `$HOME/remote/newfolder` to the guest machine:
|
||||
|
||||
newfolder
|
||||
├── script.sh
|
||||
├── otherfolder
|
||||
│ └── hello.sh
|
||||
├── goodbye.sh
|
||||
├── hello.sh
|
||||
└── woot.sh
|
||||
|
||||
1 directory, 5 files
|
||||
|
||||
Note that, unlike with synced folders, files or directories that are uploaded
|
||||
will not be kept in sync. Continuing with the example above, if you make
|
||||
further changes to your local ~/.gitconfig, they will not be immediately
|
||||
|
@ -49,3 +85,36 @@ The file provisioner takes only two options, both of which are required:
|
|||
the source will be uploaded to. The file/folder is uploaded as the SSH user
|
||||
over SCP, so this location must be writable to that user. The SSH user can be
|
||||
determined by running `vagrant ssh-config`, and defaults to "vagrant".
|
||||
|
||||
## Caveats
|
||||
|
||||
While the file provisioner does support trailing slashes or "globing", this can
|
||||
lead to some confusing results due to the underlying tool used to copy files and
|
||||
folders between the host and guests. For example, if you have a source and
|
||||
destination with a trailing slash defined below:
|
||||
|
||||
config.vm.provision "file", source: "~/pathfolder", destination: "/remote/newlocation/"
|
||||
|
||||
You are telling vagrant to upload `~/pathfolder` under the remote dir `/remote/newlocation`,
|
||||
which will look like:
|
||||
|
||||
newlocation
|
||||
├── pathfolder
|
||||
│ └── file.sh
|
||||
|
||||
1 directory, 2 files
|
||||
|
||||
This behavior can also be achieved by defining your file provisioner below:
|
||||
|
||||
config.vm.provision "file", source: "~/pathfolder", destination: "/remote/newlocation/pathfolder"
|
||||
|
||||
Another example is using globing on the host machine to grab all files within a
|
||||
folder, but not the top level folder itself:
|
||||
|
||||
config.vm.provision "file", source: "~/otherfolder/.", destination: "/remote/otherlocation"
|
||||
|
||||
The file provisioner is defined to include all files under `~/otherfolder`
|
||||
to the new location `/remote/otherlocation`. This idea can be achieved by simply
|
||||
having your destination folder differ from the source folder:
|
||||
|
||||
config.vm.provision "file", source: "/otherfolder", destination: "/remote/otherlocation"
|
||||
|
|
|
@ -96,6 +96,8 @@ public key
|
|||
|
||||
* `masterless` (boolean) - Calls state.highstate in local mode. Uses `minion_id` and `pillar_data` when provided.
|
||||
|
||||
* `salt_call_args` (array) - An array of additional command line flag arguments to be passed to the `salt-call` command when provisioning with masterless.
|
||||
|
||||
## Master Options
|
||||
These only make sense when `install_master` is `true`. Not supported on Windows guest machines.
|
||||
|
||||
|
@ -109,6 +111,8 @@ These only make sense when `install_master` is `true`. Not supported on Windows
|
|||
* `seed_master` (dictionary) - Upload keys to master, thereby
|
||||
pre-seeding it before use. Example: `{minion_name:/path/to/key.pub}`
|
||||
|
||||
* `salt_args` (array) - An array of additional command line flag arguments to be passed to the `salt` command when provisioning with masterless.
|
||||
|
||||
## Execute States
|
||||
|
||||
Either of the following may be used to actually execute states
|
||||
|
|
Loading…
Reference in New Issue