Merge pull request #7918 from mitchellh/gildegoma/fix-7195

Add `config_file` option to both Ansible provisioners
This commit is contained in:
Gilles Cornu 2016-11-15 08:29:30 +01:00 committed by GitHub
commit 8caed8ea16
10 changed files with 108 additions and 56 deletions

View File

@ -6,6 +6,7 @@ module VagrantPlugins
GALAXY_COMMAND_DEFAULT = "ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force".freeze GALAXY_COMMAND_DEFAULT = "ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force".freeze
PLAYBOOK_COMMAND_DEFAULT = "ansible-playbook".freeze PLAYBOOK_COMMAND_DEFAULT = "ansible-playbook".freeze
attr_accessor :config_file
attr_accessor :extra_vars attr_accessor :extra_vars
attr_accessor :galaxy_role_file attr_accessor :galaxy_role_file
attr_accessor :galaxy_roles_path attr_accessor :galaxy_roles_path
@ -26,6 +27,7 @@ module VagrantPlugins
attr_accessor :verbose attr_accessor :verbose
def initialize def initialize
@config_file = UNSET_VALUE
@extra_vars = UNSET_VALUE @extra_vars = UNSET_VALUE
@galaxy_role_file = UNSET_VALUE @galaxy_role_file = UNSET_VALUE
@galaxy_roles_path = UNSET_VALUE @galaxy_roles_path = UNSET_VALUE
@ -47,6 +49,7 @@ module VagrantPlugins
end end
def finalize! def finalize!
@config_file = nil if @config_file == UNSET_VALUE
@extra_vars = nil if @extra_vars == UNSET_VALUE @extra_vars = nil if @extra_vars == UNSET_VALUE
@galaxy_role_file = nil if @galaxy_role_file == UNSET_VALUE @galaxy_role_file = nil if @galaxy_role_file == UNSET_VALUE
@galaxy_roles_path = nil if @galaxy_roles_path == UNSET_VALUE @galaxy_roles_path = nil if @galaxy_roles_path == UNSET_VALUE

View File

@ -29,20 +29,39 @@ module VagrantPlugins
check_path_is_a_file(config.playbook, :playbook) check_path_is_a_file(config.playbook, :playbook)
check_path_exists(config.inventory_path, :inventory_path) if config.inventory_path check_path_exists(config.inventory_path, :inventory_path) if config.inventory_path
check_path_is_a_file(config.config_file, :config_file) if config.config_file
check_path_is_a_file(config.extra_vars[1..-1], :extra_vars) if has_an_extra_vars_file_argument check_path_is_a_file(config.extra_vars[1..-1], :extra_vars) if has_an_extra_vars_file_argument
check_path_is_a_file(config.galaxy_role_file, :galaxy_role_file) if config.galaxy_role_file check_path_is_a_file(config.galaxy_role_file, :galaxy_role_file) if config.galaxy_role_file
check_path_is_a_file(config.vault_password_file, :vault_password_file) if config.vault_password_file check_path_is_a_file(config.vault_password_file, :vault_password_file) if config.vault_password_file
end end
def ansible_playbook_command_for_shell_execution def get_environment_variables_for_shell_execution
shell_command = [] shell_env_vars = []
@environment_variables.each_pair do |k, v| @environment_variables.each_pair do |k, v|
if k =~ /ANSIBLE_SSH_ARGS|ANSIBLE_ROLES_PATH/ if k =~ /ANSIBLE_SSH_ARGS|ANSIBLE_ROLES_PATH|ANSIBLE_CONFIG/
shell_command << "#{k}='#{v}'" shell_env_vars << "#{k}='#{v}'"
else else
shell_command << "#{k}=#{v}" shell_env_vars << "#{k}=#{v}"
end end
end end
shell_env_vars
end
def ansible_galaxy_command_for_shell_execution
command_values = {
role_file: "'#{get_galaxy_role_file}'",
roles_path: "'#{get_galaxy_roles_path}'"
}
shell_command = get_environment_variables_for_shell_execution
shell_command << config.galaxy_command % command_values
shell_command.flatten.join(' ')
end
def ansible_playbook_command_for_shell_execution
shell_command = get_environment_variables_for_shell_execution
shell_command << config.playbook_command shell_command << config.playbook_command
@ -102,6 +121,12 @@ module VagrantPlugins
# Use ANSIBLE_ROLES_PATH to tell ansible-playbook where to look for roles # Use ANSIBLE_ROLES_PATH to tell ansible-playbook where to look for roles
# (there is no equivalent command line argument in ansible-playbook) # (there is no equivalent command line argument in ansible-playbook)
@environment_variables["ANSIBLE_ROLES_PATH"] = get_galaxy_roles_path if config.galaxy_roles_path @environment_variables["ANSIBLE_ROLES_PATH"] = get_galaxy_roles_path if config.galaxy_roles_path
prepare_ansible_config_environment_variable
end
def prepare_ansible_config_environment_variable
@environment_variables["ANSIBLE_CONFIG"] = config.config_file if config.config_file
end end
# Auto-generate "safe" inventory file based on Vagrantfile, # Auto-generate "safe" inventory file based on Vagrantfile,

View File

@ -72,14 +72,9 @@ module VagrantPlugins
end end
def execute_ansible_galaxy_on_guest def execute_ansible_galaxy_on_guest
command_values = { prepare_ansible_config_environment_variable
role_file: "'#{get_galaxy_role_file}'",
roles_path: "'#{get_galaxy_roles_path}'"
}
remote_command = config.galaxy_command % command_values execute_ansible_command_on_guest "galaxy", ansible_galaxy_command_for_shell_execution
execute_ansible_command_on_guest "galaxy", remote_command
end end
def execute_ansible_playbook_on_guest def execute_ansible_playbook_on_guest

View File

@ -20,6 +20,7 @@ module VagrantPlugins
check_files_existence check_files_existence
warn_for_unsupported_platform warn_for_unsupported_platform
execute_ansible_galaxy_from_host if config.galaxy_role_file execute_ansible_galaxy_from_host if config.galaxy_role_file
execute_ansible_playbook_from_host execute_ansible_playbook_from_host
end end
@ -88,6 +89,8 @@ module VagrantPlugins
end end
def execute_ansible_galaxy_from_host def execute_ansible_galaxy_from_host
prepare_ansible_config_environment_variable
command_values = { command_values = {
role_file: get_galaxy_role_file, role_file: get_galaxy_role_file,
roles_path: get_galaxy_roles_path roles_path: get_galaxy_roles_path
@ -97,20 +100,20 @@ module VagrantPlugins
command = str_command.split(VAGRANT_ARG_SEPARATOR) command = str_command.split(VAGRANT_ARG_SEPARATOR)
command << { command << {
env: @environment_variables,
# Write stdout and stderr data, since it's the regular Ansible output # Write stdout and stderr data, since it's the regular Ansible output
notify: [:stdout, :stderr], notify: [:stdout, :stderr],
workdir: @machine.env.root_path.to_s workdir: @machine.env.root_path.to_s
} }
# FIXME: role_file and roles_path arguments should be quoted in the console output ui_running_ansible_command "galaxy", ansible_galaxy_command_for_shell_execution
ui_running_ansible_command "galaxy", str_command.gsub(VAGRANT_ARG_SEPARATOR, ' ')
execute_command_from_host command execute_command_from_host command
end end
def execute_ansible_playbook_from_host def execute_ansible_playbook_from_host
prepare_command_arguments
prepare_environment_variables prepare_environment_variables
prepare_command_arguments
# Assemble the full ansible-playbook command # Assemble the full ansible-playbook command
command = [config.playbook_command] << @command_arguments command = [config.playbook_command] << @command_arguments
@ -234,6 +237,7 @@ module VagrantPlugins
proxy_cmd += " exec nc %h %p 2>/dev/null" proxy_cmd += " exec nc %h %p 2>/dev/null"
ssh_options << "-o ProxyCommand='#{ proxy_cmd }'" ssh_options << "-o ProxyCommand='#{ proxy_cmd }'"
# TODO ssh_options << "-o ProxyCommand=\"#{ proxy_cmd }\""
end end
# Use an SSH ProxyCommand when corresponding Vagrant setting is defined # Use an SSH ProxyCommand when corresponding Vagrant setting is defined

View File

@ -16,7 +16,8 @@ describe VagrantPlugins::Ansible::Config::Guest do
let(:existing_file) { "this/path/is/a/stub" } let(:existing_file) { "this/path/is/a/stub" }
it "supports a list of options" do it "supports a list of options" do
supported_options = %w( extra_vars supported_options = %w( config_file
extra_vars
galaxy_command galaxy_command
galaxy_role_file galaxy_role_file
galaxy_roles_path galaxy_roles_path

View File

@ -15,6 +15,7 @@ describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do
it "supports a list of options" do it "supports a list of options" do
supported_options = %w( ask_sudo_pass supported_options = %w( ask_sudo_pass
ask_vault_pass ask_vault_pass
config_file
extra_vars extra_vars
force_remote_user force_remote_user
galaxy_command galaxy_command

View File

@ -3,6 +3,7 @@ shared_examples_for 'options shared by both Ansible provisioners' do
it "assigns default values to unset common options" do it "assigns default values to unset common options" do
subject.finalize! subject.finalize!
expect(subject.config_file).to be_nil
expect(subject.extra_vars).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") expect(subject.galaxy_command).to eql("ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force")
expect(subject.galaxy_role_file).to be_nil expect(subject.galaxy_role_file).to be_nil

View File

@ -59,8 +59,6 @@ VF
stubbed_ui.stub(detail: "") stubbed_ui.stub(detail: "")
machine.env.stub(ui: stubbed_ui) machine.env.stub(ui: stubbed_ui)
subject.stub(:check_path)
config.playbook = 'playbook.yml' config.playbook = 'playbook.yml'
end end
@ -195,7 +193,9 @@ VF
before do before do
unless example.metadata[:skip_before] unless example.metadata[:skip_before]
config.finalize! config.finalize!
Vagrant::Util::Subprocess.stub(execute: Vagrant::Util::Subprocess::Result.new(0, "", "")) Vagrant::Util::Subprocess.stub(execute: Vagrant::Util::Subprocess::Result.new(0, "", ""))
subject.stub(:check_path)
end end
end end
@ -207,44 +207,46 @@ VF
describe 'checking existence of Ansible configuration files' do describe 'checking existence of Ansible configuration files' do
describe 'when the playbook file does not exist' do STUBBED_INVALID_PATH = "/test/239nfmd/invalid_path".freeze
it "raises an error", skip_before: true, skip_after: true do
subject.stub(:check_path).and_raise(VagrantPlugins::Ansible::Errors::AnsibleError, it 'raises an error when the `playbook` file does not exist', skip_before: true, skip_after: true do
_key: :config_file_not_found, subject.stub(:check_path).and_raise(VagrantPlugins::Ansible::Errors::AnsibleError,
config_option: "playbook", _key: :config_file_not_found,
path: "/home/wip/test/invalid_path.yml", config_option: "playbook",
system: "host") path: STUBBED_INVALID_PATH,
system: "host")
config.playbook = "/home/wip/test/invalid_path.yml" config.playbook = STUBBED_INVALID_PATH
config.finalize! config.finalize!
expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleError, expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleError,
"`playbook` does not exist on the host: /home/wip/test/invalid_path.yml") "`playbook` does not exist on the host: #{STUBBED_INVALID_PATH}")
end
%w(config_file extra_vars inventory_path galaxy_role_file vault_password_file).each do |option_name|
it "raises an error when the '#{option_name}' does not exist", skip_before: true, skip_after: true do
Vagrant::Util::Subprocess.stub(execute: Vagrant::Util::Subprocess::Result.new(0, "", ""))
config.playbook = existing_file
config.send(option_name + '=', STUBBED_INVALID_PATH)
if option_name == 'extra_vars'
# little trick to auto-append the '@' prefix, which is a duty of the config validator...
config.validate(machine)
end
config.finalize!
expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleError,
"`#{option_name}` does not exist on the host: #{STUBBED_INVALID_PATH}")
end end
end end
describe 'when the inventory path does not exist' do
it "raises an error"
end
describe 'when the extra_vars file does not exist' do
it "raises an error"
end
describe 'when the galaxy_role_file does not exist' do
it "raises an error"
end
describe 'when the vault_password_file does not exist' do
it "raises an error"
end
end end
describe 'when ansible-playbook fails' do describe 'when ansible-playbook fails' do
it "raises an error", skip_before: true, skip_after: true do it "raises an error", skip_before: true, skip_after: true do
config.finalize! config.finalize!
subject.stub(:check_path)
Vagrant::Util::Subprocess.stub(execute: Vagrant::Util::Subprocess::Result.new(1, "", "")) Vagrant::Util::Subprocess.stub(execute: Vagrant::Util::Subprocess::Result.new(1, "", ""))
expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleCommandFailed) expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleCommandFailed)
@ -582,6 +584,20 @@ VF
end end
end end
context "with config_file option defined" do
before do
config.config_file = existing_file
end
it "sets ANSIBLE_CONFIG environment variable" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
cmd_opts = args.last
expect(cmd_opts[:env]).to include("ANSIBLE_CONFIG")
expect(cmd_opts[:env]['ANSIBLE_CONFIG']).to eql(existing_file)
}
end
end
describe "with ask_vault_pass option" do describe "with ask_vault_pass option" do
before do before do
config.ask_vault_pass = true config.ask_vault_pass = true
@ -777,6 +793,8 @@ VF
it "raises an error when ansible-galaxy command fails", skip_before: true, skip_after: true do it "raises an error when ansible-galaxy command fails", skip_before: true, skip_after: true do
config.finalize! config.finalize!
subject.stub(:check_path)
Vagrant::Util::Subprocess.stub(execute: Vagrant::Util::Subprocess::Result.new(1, "", "")) Vagrant::Util::Subprocess.stub(execute: Vagrant::Util::Subprocess::Result.new(1, "", ""))
expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleCommandFailed) expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleCommandFailed)
@ -852,11 +870,12 @@ VF
config.raw_arguments = ["--why-not", "--su-user=foot", "--ask-su-pass", "--limit=all", "--private-key=./myself.key", "--extra-vars='{\"var3\":\"foo\"}'"] config.raw_arguments = ["--why-not", "--su-user=foot", "--ask-su-pass", "--limit=all", "--private-key=./myself.key", "--extra-vars='{\"var3\":\"foo\"}'"]
# environment variables # environment variables
config.config_file = existing_file
config.host_key_checking = true config.host_key_checking = true
config.raw_ssh_args = ['-o ControlMaster=no'] config.raw_ssh_args = ['-o ControlMaster=no']
end end
it_should_set_arguments_and_environment_variables 21, 5, true it_should_set_arguments_and_environment_variables 21, 6, true
it_should_explicitly_enable_ansible_ssh_control_persist_defaults 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}}", it_should_set_optional_arguments({ "extra_vars" => "--extra-vars={\"var1\":\"string with 'apostrophes', \\\\, \\\" and =\",\"var2\":{\"x\":42}}",
"sudo" => "--sudo", "sudo" => "--sudo",
@ -883,7 +902,7 @@ VF
it "shows the ansible-playbook command, with additional quotes when required" do it "shows the ansible-playbook command, with additional quotes when required" do
expect(machine.env.ui).to receive(:detail).with { |full_command| expect(machine.env.ui).to receive(:detail).with { |full_command|
expect(full_command).to eq(%Q(PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_ROLES_PATH='/up/to the stars' ANSIBLE_HOST_KEY_CHECKING=true ANSIBLE_SSH_ARGS='-o IdentitiesOnly=yes -i '/my/key1' -i '/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=#{File.expand_path(__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)) expect(full_command).to eq(%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 -i '/my/key1' -i '/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))
} }
end end
end end

View File

@ -17,6 +17,10 @@ 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. Some of these options are for advanced usage only and should not be used unless you understand their purpose.
- `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).
- `extra_vars` (string or hash) - Pass additional variables (with highest priority) to the playbook. - `extra_vars` (string or hash) - Pass additional variables (with highest priority) to the playbook.
This parameter can be a path to a JSON or YAML file, or a hash. This parameter can be a path to a JSON or YAML file, or a hash.

View File

@ -246,12 +246,11 @@ Certain settings in Ansible are (only) adjustable via a [configuration file](htt
When shipping an Ansible configuration file it is good to know that: When shipping an Ansible configuration file it is good to know that:
- it is possible to reference an Ansible configuration file via `ANSIBLE_CONFIG` environment variable, if you want to be flexible about the location of this file. - as of Ansible 1.5, the lookup order is the following:
- as of Ansible 1.5, the lookup order is the following: - any path set as `ANSIBLE_CONFIG` environment variable
- `ansible.cfg` in the runtime working directory
- `ANSIBLE_CONFIG` an environment variable - `.ansible.cfg` in the user home directory
- `ansible.cfg` in the runtime working directory - `/etc/ansible/ansible.cfg`
- `.ansible.cfg` in the user home directory - Ansible commands don't look for a configuration file relative to the playbook file location (e.g. in the same directory)
- `/etc/ansible/ansible.cfg` - an `ansible.cfg` file located in the same directory as your `Vagrantfile` will be used by default.
- it is also possible to reference any other location with the [config_file](/docs/provisioning/ansible_common.html#config_file) provisioner option. In this case, Vagrant will set the `ANSIBLE_CONFIG` environment variable accordingly.
- `ansible-playbook` doesn't look for a configuration file relative to the playbook file location (e.g. in the same directory)