Merge pull request #3287 from gildegoma/ansible-unit-tests
provisioners/ansible: add first unit tests
This commit is contained in:
commit
efefe06fd3
|
@ -33,7 +33,7 @@ module VagrantPlugins
|
||||||
@skip_tags = UNSET_VALUE
|
@skip_tags = UNSET_VALUE
|
||||||
@start_at_task = UNSET_VALUE
|
@start_at_task = UNSET_VALUE
|
||||||
@groups = UNSET_VALUE
|
@groups = UNSET_VALUE
|
||||||
@host_key_checking = "false"
|
@host_key_checking = UNSET_VALUE
|
||||||
@raw_arguments = UNSET_VALUE
|
@raw_arguments = UNSET_VALUE
|
||||||
@raw_ssh_args = UNSET_VALUE
|
@raw_ssh_args = UNSET_VALUE
|
||||||
end
|
end
|
||||||
|
@ -42,16 +42,16 @@ module VagrantPlugins
|
||||||
@playbook = nil if @playbook == UNSET_VALUE
|
@playbook = nil if @playbook == UNSET_VALUE
|
||||||
@extra_vars = nil if @extra_vars == UNSET_VALUE
|
@extra_vars = nil if @extra_vars == UNSET_VALUE
|
||||||
@inventory_path = nil if @inventory_path == UNSET_VALUE
|
@inventory_path = nil if @inventory_path == UNSET_VALUE
|
||||||
@ask_sudo_pass = nil if @ask_sudo_pass == UNSET_VALUE
|
@ask_sudo_pass = false unless @ask_sudo_pass == true
|
||||||
@limit = nil if @limit == UNSET_VALUE
|
@limit = nil if @limit == UNSET_VALUE
|
||||||
@sudo = nil if @sudo == UNSET_VALUE
|
@sudo = false unless @sudo == true
|
||||||
@sudo_user = nil if @sudo_user == UNSET_VALUE
|
@sudo_user = nil if @sudo_user == UNSET_VALUE
|
||||||
@verbose = nil if @verbose == UNSET_VALUE
|
@verbose = nil if @verbose == UNSET_VALUE
|
||||||
@tags = nil if @tags == UNSET_VALUE
|
@tags = nil if @tags == UNSET_VALUE
|
||||||
@skip_tags = nil if @skip_tags == UNSET_VALUE
|
@skip_tags = nil if @skip_tags == UNSET_VALUE
|
||||||
@start_at_task = nil if @start_at_task == UNSET_VALUE
|
@start_at_task = nil if @start_at_task == UNSET_VALUE
|
||||||
@groups = {} if @groups == UNSET_VALUE
|
@groups = {} if @groups == UNSET_VALUE
|
||||||
@host_key_checking = nil if @host_key_checking == UNSET_VALUE
|
@host_key_checking = false unless @host_key_checking == true
|
||||||
@raw_arguments = nil if @raw_arguments == UNSET_VALUE
|
@raw_arguments = nil if @raw_arguments == UNSET_VALUE
|
||||||
@raw_ssh_args = nil if @raw_ssh_args == UNSET_VALUE
|
@raw_ssh_args = nil if @raw_ssh_args == UNSET_VALUE
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
require_relative "../../../base"
|
||||||
|
require_relative "../support/shared/config"
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/provisioners/ansible/config")
|
||||||
|
|
||||||
|
describe VagrantPlugins::Ansible::Config do
|
||||||
|
include_context "unit"
|
||||||
|
|
||||||
|
subject { described_class.new }
|
||||||
|
|
||||||
|
let(:machine) { double("machine", env: Vagrant::Environment.new) }
|
||||||
|
let(:existing_file) { File.expand_path(__FILE__) }
|
||||||
|
let(:non_existing_file) { "/this/does/not/exist" }
|
||||||
|
|
||||||
|
it "supports a list of options" do
|
||||||
|
config_options = subject.public_methods(false).find_all { |i| i.to_s.end_with?('=') }
|
||||||
|
config_options.map! { |i| i.to_s.sub('=', '') }
|
||||||
|
supported_options = %w( ask_sudo_pass
|
||||||
|
extra_vars
|
||||||
|
groups
|
||||||
|
host_key_checking
|
||||||
|
inventory_path
|
||||||
|
limit
|
||||||
|
playbook
|
||||||
|
raw_arguments
|
||||||
|
raw_ssh_args
|
||||||
|
skip_tags
|
||||||
|
start_at_task
|
||||||
|
sudo
|
||||||
|
sudo_user
|
||||||
|
tags
|
||||||
|
verbose )
|
||||||
|
|
||||||
|
expect(config_options.sort).to eql(supported_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "assigns default values to unset options" do
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
expect(subject.playbook).to be_nil
|
||||||
|
expect(subject.extra_vars).to be_nil
|
||||||
|
expect(subject.ask_sudo_pass).to be_false
|
||||||
|
expect(subject.limit).to be_nil
|
||||||
|
expect(subject.sudo).to be_false
|
||||||
|
expect(subject.sudo_user).to be_nil
|
||||||
|
expect(subject.verbose).to be_nil
|
||||||
|
expect(subject.tags).to be_nil
|
||||||
|
expect(subject.skip_tags).to be_nil
|
||||||
|
expect(subject.start_at_task).to be_nil
|
||||||
|
expect(subject.groups).to eq({})
|
||||||
|
expect(subject.host_key_checking).to be_false
|
||||||
|
expect(subject.raw_arguments).to be_nil
|
||||||
|
expect(subject.raw_ssh_args).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "host_key_checking option" do
|
||||||
|
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :host_key_checking, false
|
||||||
|
end
|
||||||
|
describe "ask_sudo_pass option" do
|
||||||
|
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :ask_sudo_pass, false
|
||||||
|
end
|
||||||
|
describe "sudo option" do
|
||||||
|
it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :sudo, false
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#validate" do
|
||||||
|
before do
|
||||||
|
subject.playbook = existing_file
|
||||||
|
end
|
||||||
|
|
||||||
|
it "passes if the playbook option refers to an existing file" do
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
expect(result["ansible provisioner"]).to eql([])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error if the playbook option is undefined" do
|
||||||
|
subject.playbook = nil
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
expect(result["ansible provisioner"]).to eql([
|
||||||
|
I18n.t("vagrant.provisioners.ansible.no_playbook")
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error if the playbook file does not exist" do
|
||||||
|
subject.playbook = non_existing_file
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
expect(result["ansible provisioner"]).to eql([
|
||||||
|
I18n.t("vagrant.provisioners.ansible.playbook_path_invalid",
|
||||||
|
:path => non_existing_file)
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "passes if the extra_vars option refers to an existing file" do
|
||||||
|
subject.extra_vars = existing_file
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
expect(result["ansible provisioner"]).to eql([])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "passes if the extra_vars option is a hash" do
|
||||||
|
subject.extra_vars = { var1: 1, var2: "foo" }
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
expect(result["ansible provisioner"]).to eql([])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error if the extra_vars option refers to a file that does not exist" do
|
||||||
|
subject.extra_vars = non_existing_file
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
expect(result["ansible provisioner"]).to eql([
|
||||||
|
I18n.t("vagrant.provisioners.ansible.extra_vars_invalid",
|
||||||
|
:type => subject.extra_vars.class.to_s,
|
||||||
|
:value => subject.extra_vars.to_s)
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error if the extra_vars option is of wrong data type" do
|
||||||
|
subject.extra_vars = ["var1", 3, "var2", 5]
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
expect(result["ansible provisioner"]).to eql([
|
||||||
|
I18n.t("vagrant.provisioners.ansible.extra_vars_invalid",
|
||||||
|
:type => subject.extra_vars.class.to_s,
|
||||||
|
:value => subject.extra_vars.to_s)
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "passes if inventory_path refers to an existing location" do
|
||||||
|
subject.inventory_path = existing_file
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
expect(result["ansible provisioner"]).to eql([])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error if inventory_path is specified, but does not exist" do
|
||||||
|
subject.inventory_path = non_existing_file
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
expect(result["ansible provisioner"]).to eql([
|
||||||
|
I18n.t("vagrant.provisioners.ansible.inventory_path_invalid",
|
||||||
|
:path => non_existing_file)
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "it collects and returns all detected errors" do
|
||||||
|
subject.playbook = non_existing_file
|
||||||
|
subject.inventory_path = non_existing_file
|
||||||
|
subject.extra_vars = non_existing_file
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
expect(result["ansible provisioner"]).to include(
|
||||||
|
I18n.t("vagrant.provisioners.ansible.playbook_path_invalid",
|
||||||
|
:path => non_existing_file))
|
||||||
|
expect(result["ansible provisioner"]).to include(
|
||||||
|
I18n.t("vagrant.provisioners.ansible.extra_vars_invalid",
|
||||||
|
:type => subject.extra_vars.class.to_s,
|
||||||
|
:value => subject.extra_vars.to_s))
|
||||||
|
expect(result["ansible provisioner"]).to include(
|
||||||
|
I18n.t("vagrant.provisioners.ansible.inventory_path_invalid",
|
||||||
|
:path => non_existing_file))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,448 @@
|
||||||
|
require_relative "../../../base"
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/provisioners/ansible/config")
|
||||||
|
require Vagrant.source_root.join("plugins/provisioners/ansible/provisioner")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Helper Functions
|
||||||
|
#
|
||||||
|
|
||||||
|
def find_last_argument_after(ref_index, ansible_playbook_args, arg_pattern)
|
||||||
|
subset = ansible_playbook_args[(ref_index + 1)..(ansible_playbook_args.length-2)].reverse
|
||||||
|
subset.each do |i|
|
||||||
|
return true if i =~ arg_pattern
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
describe VagrantPlugins::Ansible::Provisioner do
|
||||||
|
include_context "unit"
|
||||||
|
|
||||||
|
subject { described_class.new(machine, config) }
|
||||||
|
|
||||||
|
let(:iso_env) do
|
||||||
|
# We have to create a Vagrantfile so there is a Vagrant Environment to provide:
|
||||||
|
# - a location for the generated inventory
|
||||||
|
# - multi-machines configuration
|
||||||
|
|
||||||
|
env = isolated_environment
|
||||||
|
env.vagrantfile(<<-VF)
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
config.vm.box = "base"
|
||||||
|
config.vm.define :machine1
|
||||||
|
config.vm.define :machine2
|
||||||
|
end
|
||||||
|
VF
|
||||||
|
env.create_vagrant_env
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) }
|
||||||
|
let(:config) { VagrantPlugins::Ansible::Config.new }
|
||||||
|
let(:ssh_info) {{
|
||||||
|
private_key_path: ['/path/to/my/key'],
|
||||||
|
username: 'testuser',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 2223
|
||||||
|
}}
|
||||||
|
|
||||||
|
let(:existing_file) { File.expand_path(__FILE__) }
|
||||||
|
let(:generated_inventory_dir) { File.join(machine.env.local_data_path, %w(provisioners ansible inventory)) }
|
||||||
|
let(:generated_inventory_file) { File.join(generated_inventory_dir, 'vagrant_ansible_inventory') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
machine.stub(ssh_info: ssh_info)
|
||||||
|
machine.env.stub(active_machines: [[iso_env.machine_names[0], :dummy], [iso_env.machine_names[1], :dummy]])
|
||||||
|
|
||||||
|
config.playbook = 'playbook.yml'
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Class methods for code reuse across examples
|
||||||
|
#
|
||||||
|
|
||||||
|
def self.it_should_set_arguments_and_environment_variables(expected_args_count = 5, expected_vars_count = 3, expected_host_key_checking = false)
|
||||||
|
it "sets implicit arguments in a specific order" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
|
||||||
|
expect(args[0]).to eq("ansible-playbook")
|
||||||
|
expect(args[1]).to eq("--private-key=#{machine.ssh_info[:private_key_path][0]}")
|
||||||
|
expect(args[2]).to eq("--user=#{machine.ssh_info[:username]}")
|
||||||
|
|
||||||
|
limit_index = args.index("--limit=#{machine.name}")
|
||||||
|
expect(limit_index).to be > 2
|
||||||
|
expect(limit_index).to be < 5
|
||||||
|
if (limit_index == 4)
|
||||||
|
expect(args[3]).to match("--connection=ssh")
|
||||||
|
end
|
||||||
|
|
||||||
|
inventory_count = args.count { |x| x =~ /--inventory-file=.+/ }
|
||||||
|
expect(inventory_count).to be > 0
|
||||||
|
|
||||||
|
expect(args[args.length-2]).to eq("playbook.yml")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "exports environment variables" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
cmd_opts = args.last
|
||||||
|
expect(cmd_opts[:env]['ANSIBLE_FORCE_COLOR']).to eql("true")
|
||||||
|
expect(cmd_opts[:env]['ANSIBLE_HOST_KEY_CHECKING']).to eql(expected_host_key_checking.to_s)
|
||||||
|
expect(cmd_opts[:env]['PYTHONUNBUFFERED']).to eql(1)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# "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 { |*args|
|
||||||
|
expect(args.length-2).to eq(expected_args_count)
|
||||||
|
expect(args.last[:env].length).to eq(expected_vars_count)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.it_should_set_optional_arguments(arg_map)
|
||||||
|
it "sets optional arguments" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
arg_map.each_pair do |vagrant_option, ansible_argument|
|
||||||
|
index = args.index(ansible_argument)
|
||||||
|
if config.send(vagrant_option)
|
||||||
|
expect(index).to be > 0
|
||||||
|
else
|
||||||
|
expect(index).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.it_should_use_smart_transport_mode
|
||||||
|
it "does not export ANSIBLE_SSH_ARGS" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
cmd_opts = args.last
|
||||||
|
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to be_nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not force any transport mode" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
total = args.count { |x| x =~ /--connection=\w+/ }
|
||||||
|
expect(total).to eql(0)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.it_should_use_transport_mode(transport_mode)
|
||||||
|
it "it enables '#{transport_mode}' transport mode" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
index = args.rindex("--connection=#{transport_mode}")
|
||||||
|
expect(index).to be > 0
|
||||||
|
expect(find_last_argument_after(index, args, /--connection=\w+/)).to be_false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.it_should_force_ssh_transport_mode
|
||||||
|
it_should_use_transport_mode('ssh')
|
||||||
|
|
||||||
|
it "configures ControlPersist (like Ansible defaults) via ANSIBLE_SSH_ARGS" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
cmd_opts = args.last
|
||||||
|
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ControlMaster=auto")
|
||||||
|
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ControlPersist=60s")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.it_should_create_and_use_generated_inventory
|
||||||
|
it "generates an inventory with all active machines" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
expect(config.inventory_path).to be_nil
|
||||||
|
expect(File.exists?(generated_inventory_file)).to be_true
|
||||||
|
inventory_content = File.read(generated_inventory_file)
|
||||||
|
expect(inventory_content).to include("#{machine.name} ansible_ssh_host=#{machine.ssh_info[:host]} ansible_ssh_port=#{machine.ssh_info[:port]}\n")
|
||||||
|
expect(inventory_content).to include("# MISSING: '#{iso_env.machine_names[1]}' machine was probably removed without using Vagrant. This machine should be recreated.\n")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets as ansible inventory the directory containing the auto-generated inventory file" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*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
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#provision" do
|
||||||
|
|
||||||
|
before do
|
||||||
|
unless example.metadata[:skip_before]
|
||||||
|
config.finalize!
|
||||||
|
Vagrant::Util::Subprocess.stub(execute: Vagrant::Util::Subprocess::Result.new(0, "", ""))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
unless example.metadata[:skip_after]
|
||||||
|
subject.provision
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when ansible-playbook fails' do
|
||||||
|
it "raises an error", skip_before: true, skip_after: true do
|
||||||
|
config.finalize!
|
||||||
|
Vagrant::Util::Subprocess.stub(execute: Vagrant::Util::Subprocess::Result.new(1, "", ""))
|
||||||
|
|
||||||
|
expect {subject.provision}.to raise_error(Vagrant::Errors::AnsibleFailed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with default options" do
|
||||||
|
it_should_set_arguments_and_environment_variables
|
||||||
|
it_should_use_smart_transport_mode
|
||||||
|
it_should_create_and_use_generated_inventory
|
||||||
|
|
||||||
|
it "does not add any group section to the generated inventory" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
inventory_content = File.read(generated_inventory_file)
|
||||||
|
expect(inventory_content).to_not match(/^\s*\[^\\+\]\s*$/)
|
||||||
|
|
||||||
|
# Note:
|
||||||
|
# The expectation below is a workaround to a possible misuse (or bug) in RSpec/Ruby stack.
|
||||||
|
# If 'args' variable is not required by in this block, the "Vagrant::Util::Subprocess).to receive"
|
||||||
|
# surprisingly expects to receive "no args".
|
||||||
|
# This problem can be "solved" by using args the "unnecessary" (but harmless) expectation below:
|
||||||
|
expect(args.length).to be > 0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with groups option" do
|
||||||
|
it_should_create_and_use_generated_inventory
|
||||||
|
|
||||||
|
it "adds group sections to the generated inventory" do
|
||||||
|
config.groups = {
|
||||||
|
"group1" => "#{machine.name}",
|
||||||
|
"group1:children" => 'bar',
|
||||||
|
"group2" => ["iso_env.machine_names[1]"],
|
||||||
|
"group3" => ["unknown", "#{machine.name}"],
|
||||||
|
"bar" => ["#{machine.name}", "group3"],
|
||||||
|
"bar:children" => ["group1", "group2", "group3", "group4"],
|
||||||
|
"bar:vars" => ["myvar=foo"],
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
inventory_content = File.read(generated_inventory_file)
|
||||||
|
|
||||||
|
# Group variables are intentionally not supported in generated inventory
|
||||||
|
expect(inventory_content).not_to match(/^\[.*:vars\]$/)
|
||||||
|
|
||||||
|
# Accept String instead of Array for group that contains a single item
|
||||||
|
expect(inventory_content).to include("[group1]\n#{machine.name}\n")
|
||||||
|
expect(inventory_content).to include("[group1:children]\nbar\n")
|
||||||
|
|
||||||
|
# Skip "lost" machines
|
||||||
|
expect(inventory_content).to include("[group2]\n\n")
|
||||||
|
|
||||||
|
# Skip "unknown" machines
|
||||||
|
expect(inventory_content).to include("[group3]\n#{machine.name}\n")
|
||||||
|
|
||||||
|
# Don't mix group names and host names
|
||||||
|
expect(inventory_content).to include("[bar]\n#{machine.name}\n")
|
||||||
|
|
||||||
|
# A group of groups only includes declared groups
|
||||||
|
expect(inventory_content).not_to match(/^group4$/)
|
||||||
|
expect(inventory_content).to include("[bar:children]\ngroup1\ngroup2\ngroup3\n")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with host_key_checking option enabled" do
|
||||||
|
before do
|
||||||
|
config.host_key_checking = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_set_arguments_and_environment_variables 5, 3, true
|
||||||
|
it_should_use_smart_transport_mode
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with boolean (flag) options disabled" do
|
||||||
|
before do
|
||||||
|
config.sudo = false
|
||||||
|
config.ask_sudo_pass = false
|
||||||
|
|
||||||
|
config.sudo_user = 'root'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_set_arguments_and_environment_variables 6
|
||||||
|
it_should_set_optional_arguments({ "sudo_user" => "--sudo-user=root" })
|
||||||
|
|
||||||
|
it "it does not set boolean flag when corresponding option is set to false" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
expect(args.index("--sudo")).to be_nil
|
||||||
|
expect(args.index("--ask-sudo-pass")).to be_nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with raw_arguments option" do
|
||||||
|
before do
|
||||||
|
config.sudo = false
|
||||||
|
config.skip_tags = %w(foo bar)
|
||||||
|
config.raw_arguments = ["--connection=paramiko",
|
||||||
|
"--skip-tags=ignored",
|
||||||
|
"--module-path=/other/modules",
|
||||||
|
"--sudo",
|
||||||
|
"--inventory-file=/forget/it/my/friend",
|
||||||
|
"--new-arg=yeah"]
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_set_arguments_and_environment_variables 12
|
||||||
|
it_should_create_and_use_generated_inventory
|
||||||
|
it_should_use_transport_mode('paramiko')
|
||||||
|
|
||||||
|
it "sets all raw arguments" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
config.raw_arguments.each do |raw_arg|
|
||||||
|
expect(args).to include(raw_arg)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets raw arguments before arguments related to supported options" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
expect(args.index("--skip-tags=foo,bar")).to be > args.index("--skip-tags=ignored")
|
||||||
|
}
|
||||||
|
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 { |*args|
|
||||||
|
expect(args).to include('--sudo')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with limit option" do
|
||||||
|
before do
|
||||||
|
config.limit = %w(foo !bar)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_set_arguments_and_environment_variables 6
|
||||||
|
it_should_set_optional_arguments({ "limit" => "--limit=foo,!bar" })
|
||||||
|
|
||||||
|
it "sets custom limit argument after implicit default limit" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
expect(args.index("--limit=foo,!bar")).to be > args.index("--limit=#{machine.name}")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with inventory_path option" do
|
||||||
|
before do
|
||||||
|
config.inventory_path = existing_file
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_set_arguments_and_environment_variables
|
||||||
|
it_should_use_smart_transport_mode
|
||||||
|
|
||||||
|
it "does not generate the inventory and uses given inventory path instead" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with raw_ssh_args" do
|
||||||
|
before do
|
||||||
|
config.raw_ssh_args = ['-o ControlMaster=no']
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_set_arguments_and_environment_variables 6, 4
|
||||||
|
it_should_force_ssh_transport_mode
|
||||||
|
|
||||||
|
it "passes custom SSH options via ANSIBLE_SSH_ARGS with the highest priority" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*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")
|
||||||
|
expect(raw_opt_index).not_to be_nil
|
||||||
|
expect(default_opt_index).not_to be_nil
|
||||||
|
expect(raw_opt_index).to be < default_opt_index
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with multiple SSH identities" do
|
||||||
|
before do
|
||||||
|
ssh_info[:private_key_path] = ['/path/to/my/key', '/an/other/identity', '/yet/an/other/key']
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_set_arguments_and_environment_variables 6, 4
|
||||||
|
it_should_force_ssh_transport_mode
|
||||||
|
|
||||||
|
it "passes additional Identity Files via ANSIBLE_SSH_ARGS" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
cmd_opts = args.last
|
||||||
|
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentityFile=/an/other/identity")
|
||||||
|
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentityFile=/yet/an/other/key")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with ssh forwarding enabled" do
|
||||||
|
before do
|
||||||
|
ssh_info[:forward_agent] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_set_arguments_and_environment_variables 6, 4
|
||||||
|
it_should_force_ssh_transport_mode
|
||||||
|
|
||||||
|
it "enables SSH-Forwarding via ANSIBLE_SSH_ARGS" do
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||||
|
cmd_opts = args.last
|
||||||
|
expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ForwardAgent=yes")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Note:
|
||||||
|
# The Vagrant Ansible provisioner does not validate the coherency of arguments combination,
|
||||||
|
# and let ansible-playbook complaign.
|
||||||
|
describe "with a maximum of options" do
|
||||||
|
before do
|
||||||
|
# command line arguments
|
||||||
|
config.extra_vars = "@#{existing_file}"
|
||||||
|
config.sudo = true
|
||||||
|
config.sudo_user = 'deployer'
|
||||||
|
config.verbose = "vvv"
|
||||||
|
config.ask_sudo_pass = true
|
||||||
|
config.tags = %w(db www)
|
||||||
|
config.skip_tags = %w(foo bar)
|
||||||
|
config.limit = 'machine*:&vagrant:!that_one'
|
||||||
|
config.start_at_task = 'an awesome task'
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
config.host_key_checking = true
|
||||||
|
config.raw_ssh_args = ['-o ControlMaster=no']
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_set_arguments_and_environment_variables 15, 4, true
|
||||||
|
it_should_force_ssh_transport_mode
|
||||||
|
it_should_set_optional_arguments({ "extra_vars" => "--extra-vars=@#{File.expand_path(__FILE__)}",
|
||||||
|
"sudo" => "--sudo",
|
||||||
|
"sudo_user" => "--sudo-user=deployer",
|
||||||
|
"verbose" => "-vvv",
|
||||||
|
"ask_sudo_pass" => "--ask-sudo-pass",
|
||||||
|
"tags" => "--tags=db,www",
|
||||||
|
"skip_tags" => "--skip-tags=foo,bar",
|
||||||
|
"limit" => "--limit=machine*:&vagrant:!that_one",
|
||||||
|
"start_at_task" => '--start-at-task=an awesome task',
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
shared_examples_for 'any VagrantConfigProvisioner strict boolean attribute' do |attr_name, attr_default_value|
|
||||||
|
|
||||||
|
[true, false].each do |bool|
|
||||||
|
it "returns the assigned boolean value (#{bool})" do
|
||||||
|
subject.send("#{attr_name}=", bool)
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
expect(subject.send(attr_name)).to eql(bool)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the default value (#{attr_default_value}) if undefined" do
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
expect(subject.send(attr_name)).to eql(attr_default_value)
|
||||||
|
end
|
||||||
|
|
||||||
|
[nil, 'true', 'false', 1, 0, 'this is not a boolean'].each do |nobool|
|
||||||
|
it "returns the default value when assigned value is invalid (#{nobool.class}: #{nobool})" do
|
||||||
|
subject.send("#{attr_name}=", nobool)
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
expect(subject.send(attr_name)).to eql(attr_default_value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue