provisioners/ansible(both): Add compatibility mode
With this change, it is now possible to get rid of many deprecation messages successively introduced in Ansible 1.9, and 2.0. More interesting, the generated inventory will contain the recommended variable names (e.g. `ansible_host` instead of `ansible_ssh_host`) when the compatibility mode is set to '2.0'. Details: - Add `compatibility_mode` option to control the Ansible parameters format to be used. The value corresponds to the minimal version supported. For the moment, possible values are '1.8' (corresponding to Vagrant's former behaviour) or '2.0'. Note that a dynamic inventory generated in compatibility mode '2.0' is not supported by Ansible 1.x. On the other hand, Ansible 2.x so far supports inventory format generated by the compatibility mode '1.8'. - Add compatibility mode auto-detection, based on the available Ansible version. This is the default behaviour in order to bring a maximum of user friendliness. The drawback of this approach is to let potential compatibility breaking risks, for `ansible` provisioner setups that already integrate Ansible 2.x **AND** rely on the existence of the generated `_ssh` variable names. Thanks to the vagrant warnings (and its release notes), I argue that it is worth to offer auto-detection by default, which offers a sweet transition to most users. - Add `become`, `become_user` and `ask_become_pass` options and their backwards compatible aliases. The legacy options are now deprecated. Note that we intentionally didn't provide a '1.9' compatibility mode, as it would add extra-complexity for practically no added-value. To my knowledge, the Ansible 2.x series haven't introduced yet any major changes or deprecations that would motivate to introduce a higher version compatibility mode (to be confirmed/verified). Resolve GH-6570 Still Pending: - Optimization: Reduce the number of `ansible` command executions. Currently two exec calls will be performed when the compatibility mode auto-detection is enabled (i.e. by default). We could make the provisioner a little bit smarter to only execute `ansible` only once in any situation (by combining "presence" and "version" checks). - User-friendliness: Add better validator on `compatibility_mode` option, and shows a warning or an error instead of the silent fallback on the auto-detection modus. - Test coverage: All the added behaviours are not fully covered yet.
This commit is contained in:
parent
e09848ca59
commit
8834afbd8e
|
@ -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,28 @@ 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
|
||||
|
||||
#
|
||||
# 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 = UNSET_VALUE
|
||||
@config_file = UNSET_VALUE
|
||||
@extra_vars = UNSET_VALUE
|
||||
@galaxy_role_file = UNSET_VALUE
|
||||
|
@ -41,14 +61,15 @@ 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
|
||||
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,8 +84,6 @@ 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
|
||||
|
@ -112,6 +131,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
|
||||
|
|
|
@ -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,9 @@
|
|||
|
||||
module VagrantPlugins
|
||||
module Ansible
|
||||
COMPATIBILITY_MODE_V1_8 = "1.8".freeze
|
||||
COMPATIBILITY_MODE_V2_0 = "2.0".freeze
|
||||
DEFAULT_COMPATIBILITY_MODE = COMPATIBILITY_MODE_V1_8
|
||||
COMPATIBILITY_MODES = [COMPATIBILITY_MODE_V1_8, COMPATIBILITY_MODE_V2_0].freeze
|
||||
end
|
||||
end
|
|
@ -1,3 +1,4 @@
|
|||
require_relative "../constants"
|
||||
require_relative "../errors"
|
||||
require_relative "../helpers"
|
||||
|
||||
|
@ -14,6 +15,27 @@ 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)
|
||||
|
@ -25,6 +47,55 @@ module VagrantPlugins
|
|||
@inventory_path = nil
|
||||
end
|
||||
|
||||
def set_compatibility_mode
|
||||
unless config.compatibility_mode
|
||||
detect_compatibility_mode(gather_ansible_version)
|
||||
end
|
||||
|
||||
unless Ansible::COMPATIBILITY_MODES.include?(config.compatibility_mode)
|
||||
raise "Programming Error: compatibility_mode must correctly set at this stage!"
|
||||
end
|
||||
|
||||
@lexicon = ANSIBLE_PARAMETER_NAMES[config.compatibility_mode]
|
||||
end
|
||||
|
||||
def detect_compatibility_mode(ansible_version_stdoutput)
|
||||
if config.compatibility_mode
|
||||
raise "Programming Error: detect_compatibility_mode() shouldn't have been called."
|
||||
end
|
||||
|
||||
begin
|
||||
first_line = ansible_version_stdoutput.lines[0]
|
||||
full_version = first_line.match(/ansible (\d)(\.\d+){1,}/)
|
||||
|
||||
if full_version
|
||||
major_version, _ = full_version.captures
|
||||
|
||||
if major_version.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: full_version) +
|
||||
"\n")
|
||||
end
|
||||
rescue
|
||||
# Nothing to do here, the fallback to default compatibility_mode is done below
|
||||
end
|
||||
|
||||
unless config.compatibility_mode
|
||||
config.compatibility_mode = Ansible::DEFAULT_COMPATIBILITY_MODE
|
||||
|
||||
@machine.env.ui.warn(I18n.t("vagrant.provisioners.ansible.compatibility_mode_not_detected",
|
||||
compatibility_mode: config.compatibility_mode,
|
||||
gathered_version: ansible_version_stdoutput) +
|
||||
"\n")
|
||||
end
|
||||
end
|
||||
|
||||
def check_files_existence
|
||||
check_path_is_a_file(config.playbook, :playbook)
|
||||
|
||||
|
@ -97,8 +168,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
|
||||
|
|
|
@ -14,8 +14,10 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def provision
|
||||
check_files_existence
|
||||
check_and_install_ansible
|
||||
check_files_existence
|
||||
set_compatibility_mode
|
||||
|
||||
execute_ansible_galaxy_on_guest if config.galaxy_role_file
|
||||
execute_ansible_playbook_on_guest
|
||||
end
|
||||
|
@ -67,6 +69,19 @@ module VagrantPlugins
|
|||
end
|
||||
end
|
||||
|
||||
def gather_ansible_version
|
||||
raw_output = nil
|
||||
result = @machine.communicate.execute("ansible --version", error_check: false) do |type, output|
|
||||
if type == :stdout && output.lines[0]
|
||||
raw_output = output.lines[0]
|
||||
end
|
||||
end
|
||||
if result != 0
|
||||
raw_output = nil
|
||||
end
|
||||
raw_output
|
||||
end
|
||||
|
||||
def get_provisioning_working_directory
|
||||
config.provisioning_path
|
||||
end
|
||||
|
|
|
@ -18,8 +18,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
|
||||
set_compatibility_mode
|
||||
|
||||
execute_ansible_galaxy_from_host if config.galaxy_role_file
|
||||
execute_ansible_playbook_from_host
|
||||
|
@ -31,7 +32,7 @@ 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
|
||||
|
||||
|
@ -49,15 +50,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 +89,30 @@ module VagrantPlugins
|
|||
end
|
||||
end
|
||||
|
||||
def gather_ansible_version
|
||||
raw_output = nil
|
||||
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 = nil
|
||||
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 +224,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
|
||||
|
|
|
@ -2387,6 +2387,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: |-
|
||||
|
|
|
@ -16,7 +16,10 @@ 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
|
||||
|
|
|
@ -13,8 +13,12 @@ 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
|
||||
|
@ -47,7 +51,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 +66,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 be_nil
|
||||
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,8 +20,8 @@ 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)
|
||||
|
@ -41,6 +44,30 @@ shared_examples_for 'an Ansible provisioner' do | path_prefix, ansible_setup |
|
|||
])
|
||||
end
|
||||
|
||||
describe "compatibility_mode option" do
|
||||
|
||||
%w(1.8 2.0).each do |minimal_version|
|
||||
it "supports compatibility mode '#{minimal_version}'" do
|
||||
subject.compatibility_mode = minimal_version
|
||||
subject.finalize!
|
||||
|
||||
result = subject.validate(machine)
|
||||
expect(subject.compatibility_mode).to eql(minimal_version)
|
||||
end
|
||||
end
|
||||
|
||||
%w(invalid 1.9 2.3).each do |invalid_mode|
|
||||
it "silently forces the compatibility mode detection for invalid mode '#{invalid_mode}'" do
|
||||
subject.compatibility_mode = invalid_mode
|
||||
subject.finalize!
|
||||
|
||||
result = subject.validate(machine)
|
||||
expect(subject.compatibility_mode).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it "passes if the extra_vars option is a hash" do
|
||||
subject.extra_vars = { var1: 1, var2: "foo" }
|
||||
subject.finalize!
|
||||
|
@ -102,7 +129,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,139 @@ 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 no compatibility_mode defined" do
|
||||
before do
|
||||
config.compatibility_mode = nil
|
||||
end
|
||||
|
||||
valid_versions = {
|
||||
"0.6": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8,
|
||||
"1.9.4": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8,
|
||||
"2.2.1.0": 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}")
|
||||
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 #{ansible_version}") +
|
||||
"\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
invalid_versions = [
|
||||
"ansible devel",
|
||||
"ansible 2.x.y.z",
|
||||
"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 default compatibility mode ('#{VagrantPlugins::Ansible::DEFAULT_COMPATIBILITY_MODE}')" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
|
||||
expect(config.compatibility_mode).to eq(VagrantPlugins::Ansible::DEFAULT_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::DEFAULT_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_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
|
||||
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 "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_V2_0
|
||||
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
|
||||
|
||||
|
@ -293,7 +430,7 @@ VF
|
|||
config.host_vars = {
|
||||
machine1: {"http_port" => 80, "comments" => "'some text with spaces'"}
|
||||
}
|
||||
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'$")
|
||||
}.and_return(default_execute_result)
|
||||
|
@ -303,7 +440,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 +451,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 +462,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 +485,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 +523,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
|
||||
|
@ -421,18 +561,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
|
||||
|
@ -442,7 +582,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"
|
||||
|
@ -461,7 +601,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
|
||||
|
@ -469,7 +609,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")
|
||||
|
@ -478,7 +618,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
|
||||
|
@ -503,7 +643,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)
|
||||
|
@ -534,8 +674,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)
|
||||
|
@ -550,7 +689,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")
|
||||
|
@ -568,7 +707,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)
|
||||
|
@ -576,7 +715,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)
|
||||
|
@ -588,7 +727,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)
|
||||
|
@ -602,7 +741,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)
|
||||
|
@ -618,7 +757,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
|
||||
|
@ -632,7 +771,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
|
||||
|
@ -647,7 +786,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")
|
||||
|
@ -661,7 +800,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")
|
||||
|
@ -681,7 +820,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")
|
||||
|
@ -695,7 +834,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")
|
||||
|
@ -712,7 +851,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)
|
||||
|
@ -725,7 +864,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)
|
||||
|
@ -795,10 +934,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")
|
||||
|
@ -822,7 +963,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)
|
||||
|
@ -856,7 +1001,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"))
|
||||
|
@ -893,10 +1038,10 @@ VF
|
|||
# command line arguments
|
||||
config.galaxy_roles_path = "/up/to the stars"
|
||||
config.extra_vars = { var1: %Q(string with 'apo$trophe$', \\, " and =), var2: { x: 42 } }
|
||||
config.sudo = true
|
||||
config.sudo_user = 'deployer'
|
||||
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)
|
||||
|
@ -914,10 +1059,10 @@ 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 'apo$trophe$', \\\\, \\\" and =\",\"var2\":{\"x\":42}}",
|
||||
"sudo" => "--sudo",
|
||||
"sudo_user" => "--sudo-user=deployer",
|
||||
"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",
|
||||
|
@ -927,7 +1072,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")
|
||||
|
@ -967,7 +1112,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)
|
||||
|
@ -982,11 +1127,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_V2_0
|
||||
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
|
||||
|
||||
|
@ -996,7 +1144,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)
|
||||
|
@ -1006,7 +1154,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, "", ""))
|
||||
|
@ -1019,7 +1167,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)
|
||||
|
|
|
@ -53,10 +53,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`.
|
||||
|
|
|
@ -17,6 +17,33 @@ 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) - Cause Ansible to perform all the playbook tasks [as another user](http://docs.ansible.com/ansible/become.html), different from the one 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 defined, and the Ansible default value (`root`) will be used.
|
||||
|
||||
- `compatibility_mode` (string) - Set the **minimal** version of Ansible to be supported. Vagrant will use some parameters that are only compatible since the given version.
|
||||
|
||||
Possible values:
|
||||
|
||||
- `"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 not set, and Vagrant will try to automatically set the optimal compatibilty mode by checking the Ansible version currently available. Note that Vagrant doesn't validate this option, and any unsupported value (e.g. "2.3") will also lead Vagrant to auto-detect the compatibility mode.
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>Compatibility Note:</strong>
|
||||
This option was introduced in Vagrant 2.0. Previous Vagrant versions behave like if this option was set to `"1.8"`.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Attention:</strong>
|
||||
Vagrant doesn't perform any validation between the `compatibility_mode` value and the value of the ansible_local [`version`](/docs/provisioning/ansible_local.html#version) option.
|
||||
</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).
|
||||
|
@ -129,11 +156,19 @@ Some of these options are for advanced usage only and should not be used unless
|
|||
|
||||
- `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) .
|
||||
|
||||
|
|
Loading…
Reference in New Issue