From a0576349fee302c048512d334a2446bf3e0ce741 Mon Sep 17 00:00:00 2001 From: Gilles Cornu Date: Tue, 17 Nov 2015 07:55:32 +0100 Subject: [PATCH 1/9] provisioners/ansible(both): reorganize i18n texts These adaptations will make even more sense with the upcoming introduction of `ansible-galaxy` support. --- plugins/provisioners/ansible/config/base.rb | 16 +++--- plugins/provisioners/ansible/errors.rb | 10 ++-- .../provisioners/ansible/provisioner/guest.rb | 6 +-- .../provisioners/ansible/provisioner/host.rb | 2 +- templates/locales/en.yml | 54 +++++++++---------- .../provisioners/ansible/config_test.rb | 19 +++---- 6 files changed, 52 insertions(+), 55 deletions(-) diff --git a/plugins/provisioners/ansible/config/base.rb b/plugins/provisioners/ansible/config/base.rb index e0b610a27..66c765be7 100644 --- a/plugins/provisioners/ansible/config/base.rb +++ b/plugins/provisioners/ansible/config/base.rb @@ -57,26 +57,22 @@ module VagrantPlugins # Validate that a playbook path was provided if !playbook - @errors << I18n.t("vagrant.provisioners.ansible.no_playbook") + @errors << I18n.t("vagrant.provisioners.ansible.errors.no_playbook") end - # Validate the existence of the playbook if playbook - check_path_is_a_file(machine, playbook, "vagrant.provisioners.ansible.playbook_path_invalid") + check_path_is_a_file(machine, playbook, "vagrant.provisioners.ansible.errors.playbook_path_invalid") end - # Validate the existence of the inventory_path, if specified if inventory_path - check_path_exists(machine, inventory_path, "vagrant.provisioners.ansible.inventory_path_invalid") + check_path_exists(machine, inventory_path, "vagrant.provisioners.ansible.errors.inventory_path_invalid") end - # Validate the existence of the vault_password_file, if specified if vault_password_file - check_path_is_a_file(machine, vault_password_file, "vagrant.provisioners.ansible.vault_password_file_invalid") + check_path_is_a_file(machine, vault_password_file, "vagrant.provisioners.ansible.errors.vault_password_file_invalid") end - # Validate that extra_vars is either a hash, or a path to an - # existing file + # Validate that extra_vars is either a hash, or a path to an existing file if extra_vars extra_vars_is_valid = extra_vars.kind_of?(Hash) || extra_vars.kind_of?(String) if extra_vars.kind_of?(String) @@ -92,7 +88,7 @@ module VagrantPlugins if !extra_vars_is_valid @errors << I18n.t( - "vagrant.provisioners.ansible.extra_vars_invalid", + "vagrant.provisioners.ansible.errors.extra_vars_invalid", type: extra_vars.class.to_s, value: extra_vars.to_s) end diff --git a/plugins/provisioners/ansible/errors.rb b/plugins/provisioners/ansible/errors.rb index 2a6f8dbbd..aa154376e 100644 --- a/plugins/provisioners/ansible/errors.rb +++ b/plugins/provisioners/ansible/errors.rb @@ -8,15 +8,15 @@ module VagrantPlugins end class AnsiblePlaybookAppFailed < AnsibleError - error_key(:ansible_playbook_app_failed) + error_key(:ansible_command_failed) end - class AnsiblePlaybookAppNotFoundOnHost < AnsibleError - error_key(:ansible_playbook_app_not_found_on_host) + class AnsibleNotFoundOnHost < AnsibleError + error_key(:ansible_not_found_on_host) end - class AnsiblePlaybookAppNotFoundOnGuest < AnsibleError - error_key(:ansible_playbook_app_not_found_on_guest) + class AnsibleNotFoundOnGuest < AnsibleError + error_key(:ansible_not_found_on_guest) end class AnsibleVersionNotFoundOnGuest < AnsibleError diff --git a/plugins/provisioners/ansible/provisioner/guest.rb b/plugins/provisioners/ansible/provisioner/guest.rb index 285df8ac1..e2b6cb8dd 100644 --- a/plugins/provisioners/ansible/provisioner/guest.rb +++ b/plugins/provisioners/ansible/provisioner/guest.rb @@ -47,15 +47,15 @@ module VagrantPlugins if config.install && (config.version.to_s.to_sym == :latest || !@machine.guest.capability(:ansible_installed, config.version)) - @machine.ui.detail(I18n.t("vagrant.provisioners.ansible.installing")) + @machine.ui.info(I18n.t("vagrant.provisioners.ansible.installing")) @machine.guest.capability(:ansible_install) end # Check for the existence of ansible-playbook binary on the guest, @machine.communicate.execute( "ansible-playbook --help", - :error_class => Ansible::Errors::AnsibleError, - :error_key => :ansible_playbook_app_not_found_on_guest) + :error_class => Ansible::Errors::AnsibleNotFoundOnGuest, + :error_key => :ansible_not_found_on_guest) # Check if requested ansible version is available if (!config.version.empty? && diff --git a/plugins/provisioners/ansible/provisioner/host.rb b/plugins/provisioners/ansible/provisioner/host.rb index d25c88567..4b3e8881c 100644 --- a/plugins/provisioners/ansible/provisioner/host.rb +++ b/plugins/provisioners/ansible/provisioner/host.rb @@ -97,7 +97,7 @@ module VagrantPlugins end raise Ansible::Errors::AnsiblePlaybookAppFailed if result.exit_code != 0 rescue Vagrant::Errors::CommandUnavailable - raise Ansible::Errors::AnsiblePlaybookAppNotFoundOnHost + raise Ansible::Errors::AnsibleNotFoundOnHost end end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 9df64d907..886aec3c5 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -2039,12 +2039,16 @@ en: upload_path_not_set: "`upload_path` must be set for the shell provisioner." ansible: + cannot_detect: |- + Vagrant does not support detecting whether Ansible is installed + for the guest OS running in the machine. Vagrant will assume it is + installed and attempt to continue. errors: - ansible_playbook_app_failed: |- + ansible_command_failed: |- Ansible failed to complete successfully. Any error output should be visible above. Please fix these errors and try again. - ansible_playbook_app_not_found_on_guest: |- - The "ansible-playbook" program could not be found! Please verify + ansible_not_found_on_guest: |- + The Ansible software could not be found! Please verify that Ansible is correctly installed on your guest system. If you haven't installed Ansible yet, please install Ansible @@ -2052,36 +2056,32 @@ en: `install` option of this provisioner. Please check http://docs.vagrantup.com/v2/provisioning/ansible_local.html for more information. - ansible_version_not_found_on_guest: |- - The requested Ansible version (%{required_version}) was not found on the guest. - Please check the ansible installation on your guest system, - or adapt the `version` option of this provisioner in your Vagrantfile. - See http://docs.vagrantup.com/v2/provisioning/ansible_local.html - for more information. - ansible_playbook_app_not_found_on_host: |- - The "ansible-playbook" program could not be found! Please verify + ansible_not_found_on_host: |- + The Ansible software could not be found! Please verify that Ansible is correctly installed on your host system. If you haven't installed Ansible yet, please install Ansible on your host system. Vagrant can't do this for you in a safe and automated way. Please check http://docs.ansible.com for more information. - extra_vars_invalid: |- - `extra_vars` for the Ansible provisioner must be a hash or a path to an existing file. Received: %{value} (as %{type}) - inventory_path_invalid: |- - `inventory_path` for the Ansible provisioner does not exist on the %{system}: %{path} - no_playbook: |- - `playbook` must be set for the Ansible provisioner. - playbook_path_invalid: |- - `playbook` for the Ansible provisioner does not exist on the %{system}: %{path} - vault_password_file_invalid: |- - `vault_password_file` for the Ansible provisioner does not exist on the %{system}: %{path} - cannot_detect: |- - Vagrant does not support detecting whether Ansible is installed - for the guest OS running in the machine. Vagrant will assume it is - installed and attempt to continue. - installing: |- - Installing Ansible... + ansible_version_not_found_on_guest: |- + The requested Ansible version (%{required_version}) was not found on the guest. + Please check the ansible installation on your guest system, + or adapt the `version` option of this provisioner in your Vagrantfile. + See http://docs.vagrantup.com/v2/provisioning/ansible_local.html + for more information. + extra_vars_invalid: |- + `extra_vars` must be a hash or a path to an existing file. Received: %{value} (as %{type}) + inventory_path_invalid: |- + `inventory_path` does not exist on the %{system}: %{path} + no_playbook: |- + `playbook` file path must be set. + playbook_path_invalid: |- + `playbook` does not exist on the %{system}: %{path} + vault_password_file_invalid: |- + `vault_password_file` does not exist on the %{system}: %{path} + installing: "Installing Ansible..." + running_playbook: "Running ansible-playbook..." windows_not_supported_for_control_machine: |- Windows is not officially supported for the Ansible Control Machine. Please check http://docs.ansible.com/intro_installation.html#control-machine-requirements diff --git a/test/unit/plugins/provisioners/ansible/config_test.rb b/test/unit/plugins/provisioners/ansible/config_test.rb index 3fdc66ddb..6cbd3f30d 100644 --- a/test/unit/plugins/provisioners/ansible/config_test.rb +++ b/test/unit/plugins/provisioners/ansible/config_test.rb @@ -94,7 +94,7 @@ describe VagrantPlugins::Ansible::Config::Host do result = subject.validate(machine) expect(result["ansible remote provisioner"]).to eql([ - I18n.t("vagrant.provisioners.ansible.no_playbook") + I18n.t("vagrant.provisioners.ansible.errors.no_playbook") ]) end @@ -104,7 +104,7 @@ describe VagrantPlugins::Ansible::Config::Host do result = subject.validate(machine) expect(result["ansible remote provisioner"]).to eql([ - I18n.t("vagrant.provisioners.ansible.playbook_path_invalid", + I18n.t("vagrant.provisioners.ansible.errors.playbook_path_invalid", path: non_existing_file, system: "host") ]) end @@ -131,7 +131,7 @@ describe VagrantPlugins::Ansible::Config::Host do result = subject.validate(machine) expect(result["ansible remote provisioner"]).to eql([ - I18n.t("vagrant.provisioners.ansible.extra_vars_invalid", + I18n.t("vagrant.provisioners.ansible.errors.extra_vars_invalid", type: subject.extra_vars.class.to_s, value: subject.extra_vars.to_s) ]) @@ -143,7 +143,7 @@ describe VagrantPlugins::Ansible::Config::Host do result = subject.validate(machine) expect(result["ansible remote provisioner"]).to eql([ - I18n.t("vagrant.provisioners.ansible.extra_vars_invalid", + I18n.t("vagrant.provisioners.ansible.errors.extra_vars_invalid", type: subject.extra_vars.class.to_s, value: subject.extra_vars.to_s) ]) @@ -163,7 +163,7 @@ describe VagrantPlugins::Ansible::Config::Host do result = subject.validate(machine) expect(result["ansible remote provisioner"]).to eql([ - I18n.t("vagrant.provisioners.ansible.inventory_path_invalid", + I18n.t("vagrant.provisioners.ansible.errors.inventory_path_invalid", path: non_existing_file, system: "host") ]) end @@ -174,7 +174,8 @@ describe VagrantPlugins::Ansible::Config::Host do result = subject.validate(machine) expect(result["ansible remote provisioner"]).to eql([ - I18n.t("vagrant.provisioners.ansible.vault_password_file_invalid", + I18n.t("vagrant.provisioners.ansible.errors.vault_password_file_invalid", + path: non_existing_file, system: "host") path: non_existing_file, system: "host") ]) end @@ -187,14 +188,14 @@ describe VagrantPlugins::Ansible::Config::Host do result = subject.validate(machine) expect(result["ansible remote provisioner"]).to include( - I18n.t("vagrant.provisioners.ansible.playbook_path_invalid", + I18n.t("vagrant.provisioners.ansible.errors.playbook_path_invalid", path: non_existing_file, system: "host")) expect(result["ansible remote provisioner"]).to include( - I18n.t("vagrant.provisioners.ansible.extra_vars_invalid", + I18n.t("vagrant.provisioners.ansible.errors.extra_vars_invalid", type: subject.extra_vars.class.to_s, value: subject.extra_vars.to_s)) expect(result["ansible remote provisioner"]).to include( - I18n.t("vagrant.provisioners.ansible.inventory_path_invalid", + I18n.t("vagrant.provisioners.ansible.errors.inventory_path_invalid", path: non_existing_file, system: "host")) end From b9738a8c4c92306120961e89ca69a8d820d2ae37 Mon Sep 17 00:00:00 2001 From: Gilles Cornu Date: Tue, 17 Nov 2015 08:00:24 +0100 Subject: [PATCH 2/9] provisioners/ansible: add missing unit test Related to #5292 --- .../provisioners/ansible/provisioner_test.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/unit/plugins/provisioners/ansible/provisioner_test.rb b/test/unit/plugins/provisioners/ansible/provisioner_test.rb index dbcfe7d4f..fcc99cccb 100644 --- a/test/unit/plugins/provisioners/ansible/provisioner_test.rb +++ b/test/unit/plugins/provisioners/ansible/provisioner_test.rb @@ -678,7 +678,20 @@ VF # Special cases related to the Vagrant Host operating system in use # - context "with a Solaris-like host" do + context "on a Windows host" do + before do + Vagrant::Util::Platform.stub(windows?: true) + machine.ui.stub(:warn) + end + + it "warns that Windows is not officially supported for the Ansible control machine" do + expect(machine.env.ui).to receive(:warn).with { |warning| + expect(warning).to eq(I18n.t("vagrant.provisioners.ansible.windows_not_supported_for_control_machine")) + } + end + end + + context "on a Solaris-like host" do before do Vagrant::Util::Platform.stub(solaris?: true) end From c1f3d114f52654e9f285499dd548409edc0c99fe Mon Sep 17 00:00:00 2001 From: Gilles Cornu Date: Tue, 17 Nov 2015 22:06:06 +0100 Subject: [PATCH 3/9] provisioners/ansible(both): add galaxy support Close #2718 --- plugins/provisioners/ansible/config/base.rb | 13 ++++ plugins/provisioners/ansible/errors.rb | 2 +- .../provisioners/ansible/provisioner/base.rb | 20 ++++++ .../provisioners/ansible/provisioner/guest.rb | 29 +++++--- .../provisioners/ansible/provisioner/host.rb | 67 +++++++++++++------ templates/locales/en.yml | 3 + .../provisioners/ansible/config_test.rb | 13 ++++ .../provisioners/ansible/provisioner_test.rb | 29 +++++++- .../v2/provisioning/ansible_common.html.md | 24 +++++++ 9 files changed, 167 insertions(+), 33 deletions(-) diff --git a/plugins/provisioners/ansible/config/base.rb b/plugins/provisioners/ansible/config/base.rb index 66c765be7..04c703439 100644 --- a/plugins/provisioners/ansible/config/base.rb +++ b/plugins/provisioners/ansible/config/base.rb @@ -4,6 +4,9 @@ module VagrantPlugins class Base < Vagrant.plugin("2", :config) attr_accessor :extra_vars + attr_accessor :galaxy_role_file + attr_accessor :galaxy_roles_path + attr_accessor :galaxy_command attr_accessor :groups attr_accessor :inventory_path attr_accessor :limit @@ -19,6 +22,9 @@ module VagrantPlugins def initialize @extra_vars = UNSET_VALUE + @galaxy_role_file = UNSET_VALUE + @galaxy_roles_path = UNSET_VALUE + @galaxy_command = UNSET_VALUE @groups = UNSET_VALUE @inventory_path = UNSET_VALUE @limit = UNSET_VALUE @@ -35,6 +41,9 @@ module VagrantPlugins def finalize! @extra_vars = nil if @extra_vars == UNSET_VALUE + @galaxy_role_file = nil if @galaxy_role_file == UNSET_VALUE + @galaxy_roles_path = nil if @galaxy_roles_path == UNSET_VALUE + @galaxy_command = "ansible-galaxy install --role-file=%{ROLE_FILE} --roles-path=%{ROLES_PATH} --force" if @galaxy_command == UNSET_VALUE @groups = {} if @groups == UNSET_VALUE @inventory_path = nil if @inventory_path == UNSET_VALUE @limit = nil if @limit == UNSET_VALUE @@ -68,6 +77,10 @@ module VagrantPlugins check_path_exists(machine, inventory_path, "vagrant.provisioners.ansible.errors.inventory_path_invalid") end + if galaxy_role_file + check_path_is_a_file(machine, galaxy_role_file, "vagrant.provisioners.ansible.errors.galaxy_role_file_invalid") + end + if vault_password_file check_path_is_a_file(machine, vault_password_file, "vagrant.provisioners.ansible.errors.vault_password_file_invalid") end diff --git a/plugins/provisioners/ansible/errors.rb b/plugins/provisioners/ansible/errors.rb index aa154376e..06a1063cf 100644 --- a/plugins/provisioners/ansible/errors.rb +++ b/plugins/provisioners/ansible/errors.rb @@ -7,7 +7,7 @@ module VagrantPlugins error_namespace("vagrant.provisioners.ansible.errors") end - class AnsiblePlaybookAppFailed < AnsibleError + class AnsibleCommandFailed < AnsibleError error_key(:ansible_command_failed) end diff --git a/plugins/provisioners/ansible/provisioner/base.rb b/plugins/provisioners/ansible/provisioner/base.rb index 051473271..16f6d0d77 100644 --- a/plugins/provisioners/ansible/provisioner/base.rb +++ b/plugins/provisioners/ansible/provisioner/base.rb @@ -132,6 +132,26 @@ module VagrantPlugins end end + def get_galaxy_role_file(basedir) + Pathname.new(config.galaxy_role_file).expand_path(basedir) + end + + def get_galaxy_roles_path(basedir) + if config.galaxy_roles_path + Pathname.new(config.galaxy_roles_path).expand_path(basedir) + else + File.join(Pathname.new(config.playbook).expand_path(basedir).parent, 'roles') + end + end + + def ui_running_ansible_command(name, command) + @machine.ui.detail I18n.t("vagrant.provisioners.ansible.running_#{name}") + if verbosity_is_enabled? + # Show the ansible command in use + @machine.env.ui.detail(command) + end + end + def verbosity_is_enabled? config.verbose && !config.verbose.to_s.empty? end diff --git a/plugins/provisioners/ansible/provisioner/guest.rb b/plugins/provisioners/ansible/provisioner/guest.rb index e2b6cb8dd..9e4346190 100644 --- a/plugins/provisioners/ansible/provisioner/guest.rb +++ b/plugins/provisioners/ansible/provisioner/guest.rb @@ -14,8 +14,7 @@ module VagrantPlugins def provision check_and_install_ansible - prepare_common_command_arguments - prepare_common_environment_variables + execute_ansible_galaxy_on_guest if config.galaxy_role_file execute_ansible_playbook_on_guest end @@ -51,9 +50,9 @@ module VagrantPlugins @machine.guest.capability(:ansible_install) end - # Check for the existence of ansible-playbook binary on the guest, + # Check that ansible binaries are well installed on the guest, @machine.communicate.execute( - "ansible-playbook --help", + "ansible-galaxy --help && ansible-playbook --help", :error_class => Ansible::Errors::AnsibleNotFoundOnGuest, :error_key => :ansible_not_found_on_guest) @@ -65,15 +64,27 @@ module VagrantPlugins end end + def execute_ansible_galaxy_on_guest + command_values = { + :ROLE_FILE => get_galaxy_role_file(config.provisioning_path), + :ROLES_PATH => get_galaxy_roles_path(config.provisioning_path) + } + remote_command = config.galaxy_command % command_values + + ui_running_ansible_command "galaxy", remote_command + + result = execute_on_guest(remote_command) + raise Ansible::Errors::AnsibleGalaxyAppFailed if result != 0 + end + def execute_ansible_playbook_on_guest + prepare_common_command_arguments + prepare_common_environment_variables + command = (%w(ansible-playbook) << @command_arguments << config.playbook).flatten remote_command = "cd #{config.provisioning_path} && #{Helpers::stringify_ansible_playbook_command(@environment_variables, command)}" - # TODO: generic HOOK ??? - # Show the ansible command in use - if verbosity_is_enabled? - @machine.env.ui.detail(remote_command) - end + ui_running_ansible_command "playbook", remote_command result = execute_on_guest(remote_command) raise Ansible::Errors::AnsiblePlaybookAppFailed if result != 0 diff --git a/plugins/provisioners/ansible/provisioner/host.rb b/plugins/provisioners/ansible/provisioner/host.rb index 4b3e8881c..61e576c97 100644 --- a/plugins/provisioners/ansible/provisioner/host.rb +++ b/plugins/provisioners/ansible/provisioner/host.rb @@ -19,8 +19,7 @@ module VagrantPlugins @ssh_info = @machine.ssh_info warn_for_unsupported_platform - prepare_command_arguments - prepare_environment_variables + execute_ansible_galaxy_from_host if config.galaxy_role_file execute_ansible_playbook_from_host end @@ -72,33 +71,57 @@ module VagrantPlugins @environment_variables["ANSIBLE_SSH_ARGS"] = ansible_ssh_args unless ansible_ssh_args.empty? end - def execute_ansible_playbook_from_host - # Assemble the full ansible-playbook command - command = (%w(ansible-playbook) << @command_arguments << config.playbook).flatten - - # TODO: generic HOOK ??? - # Show the ansible command in use - if verbosity_is_enabled? - @machine.env.ui.detail(Helpers::stringify_ansible_playbook_command(@environment_variables, command)) + def execute_command_from_host(command) + begin + result = Vagrant::Util::Subprocess.execute(*command) do |type, data| + if type == :stdout || type == :stderr + @machine.env.ui.detail(data, new_line: false, prefix: false) + end + end + raise Ansible::Errors::AnsibleCommandFailed if result.exit_code != 0 + rescue Vagrant::Errors::CommandUnavailable + raise Ansible::Errors::AnsibleNotFoundOnHost end + end - # Write stdout and stderr data, since it's the regular Ansible output + def execute_ansible_galaxy_from_host + command_values = { + :ROLE_FILE => get_galaxy_role_file(machine.env.root_path), + :ROLES_PATH => get_galaxy_roles_path(machine.env.root_path) + } + arg_separator = '__VAGRANT_ARG_SEPARATOR__' + command_template = config.galaxy_command.gsub(' ', arg_separator) + str_command = command_template % command_values + + ui_running_ansible_command "galaxy", str_command.gsub(arg_separator, ' ') + + command = str_command.split(arg_separator) command << { - env: @environment_variables, + # Write stdout and stderr data, since it's the regular Ansible output notify: [:stdout, :stderr], workdir: @machine.env.root_path.to_s } - begin - result = Vagrant::Util::Subprocess.execute(*command) do |type, data| - if type == :stdout || type == :stderr - @machine.env.ui.info(data, new_line: false, prefix: false) - end - end - raise Ansible::Errors::AnsiblePlaybookAppFailed if result.exit_code != 0 - rescue Vagrant::Errors::CommandUnavailable - raise Ansible::Errors::AnsibleNotFoundOnHost - end + execute_command_from_host command + end + + def execute_ansible_playbook_from_host + prepare_command_arguments + prepare_environment_variables + + # Assemble the full ansible-playbook command + command = (%w(ansible-playbook) << @command_arguments << config.playbook).flatten + + ui_running_ansible_command "playbook", Helpers::stringify_ansible_playbook_command(@environment_variables, command) + + command << { + env: @environment_variables, + # Write stdout and stderr data, since it's the regular Ansible output + notify: [:stdout, :stderr], + workdir: @machine.env.root_path.to_s + } + + execute_command_from_host command end def ship_generated_inventory(inventory_content) diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 886aec3c5..6bcb24689 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -2072,6 +2072,8 @@ en: for more information. extra_vars_invalid: |- `extra_vars` must be a hash or a path to an existing file. Received: %{value} (as %{type}) + galaxy_role_file_invalid: |- + `galaxy_role_file` does not exist on the %{system}: %{path} inventory_path_invalid: |- `inventory_path` does not exist on the %{system}: %{path} no_playbook: |- @@ -2081,6 +2083,7 @@ en: vault_password_file_invalid: |- `vault_password_file` does not exist on the %{system}: %{path} installing: "Installing Ansible..." + running_galaxy: "Running ansible-galaxy..." running_playbook: "Running ansible-playbook..." windows_not_supported_for_control_machine: |- Windows is not officially supported for the Ansible Control Machine. diff --git a/test/unit/plugins/provisioners/ansible/config_test.rb b/test/unit/plugins/provisioners/ansible/config_test.rb index 6cbd3f30d..0de4ac69e 100644 --- a/test/unit/plugins/provisioners/ansible/config_test.rb +++ b/test/unit/plugins/provisioners/ansible/config_test.rb @@ -20,6 +20,9 @@ describe VagrantPlugins::Ansible::Config::Host do ask_vault_pass extra_vars force_remote_user + galaxy_command + galaxy_role_file + galaxy_roles_path groups host_key_checking inventory_path @@ -176,6 +179,16 @@ describe VagrantPlugins::Ansible::Config::Host do expect(result["ansible remote provisioner"]).to eql([ I18n.t("vagrant.provisioners.ansible.errors.vault_password_file_invalid", path: non_existing_file, system: "host") + ]) + end + + it "returns an error if galaxy_role_file is specified, but does not exist" do + subject.galaxy_role_file = non_existing_file + subject.finalize! + + result = subject.validate(machine) + expect(result["ansible remote provisioner"]).to eql([ + I18n.t("vagrant.provisioners.ansible.errors.galaxy_role_file_invalid", path: non_existing_file, system: "host") ]) end diff --git a/test/unit/plugins/provisioners/ansible/provisioner_test.rb b/test/unit/plugins/provisioners/ansible/provisioner_test.rb index fcc99cccb..94626389a 100644 --- a/test/unit/plugins/provisioners/ansible/provisioner_test.rb +++ b/test/unit/plugins/provisioners/ansible/provisioner_test.rb @@ -208,7 +208,7 @@ VF config.finalize! Vagrant::Util::Subprocess.stub(execute: Vagrant::Util::Subprocess::Result.new(1, "", "")) - expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsiblePlaybookAppFailed) + expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleCommandFailed) end end @@ -583,6 +583,33 @@ VF end end + describe "with galaxy support" do + + before do + config.galaxy_role_file = existing_file + end + + it "raises an error when ansible-galaxy command fails", 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(VagrantPlugins::Ansible::Errors::AnsibleCommandFailed) + end + + it "execute ansible-galaxy and ansible-playbook" do + # TODO: to be improved, but I'm currenty facing some issues, maybe only present in RSpec 2.14... + expect(Vagrant::Util::Subprocess).to receive(:execute).twice + end + + describe "with verbose option enabled" do + before do + config.verbose = true + end + + xit "shows the ansible-galaxy command in use" + end + end + # The Vagrant Ansible provisioner does not validate the coherency of # argument combinations, and let ansible-playbook complain. describe "with a maximum of options" do diff --git a/website/docs/source/v2/provisioning/ansible_common.html.md b/website/docs/source/v2/provisioning/ansible_common.html.md index 3b6d7fbc1..aca7c2c14 100644 --- a/website/docs/source/v2/provisioning/ansible_common.html.md +++ b/website/docs/source/v2/provisioning/ansible_common.html.md @@ -51,6 +51,30 @@ Some of these options are for advanced usage only and should not be used unless By default, this option is disabled and Vagrant generates an inventory based on the `Vagrantfile` information. +- `galaxy_command` (template string) - The command pattern used to install Galaxy roles when `galaxy_role_file` is set. + + The following placeholders can be used in this command pattern: + - `%{ROLE_FILE}` is replaced by the absolute path to the `galaxy_role_file` option + - `%{ROLES_PATH}` is + * replaced by the absolute path to the `galaxy_roles_path` option when such option is defined + * replaced by the absolute path to a `roles` subdirectory sitting in the parent directory of the configured `playbook` file otherwise. + + By default, this option is set to + + ``` + ansible-galaxy install --role-file=%{ROLE_FILE} --roles-path=%{ROLES_PATH} --force + ``` + +- `galaxy_role_file` (string) - The path to the Ansible Galaxy role file. + + By default, this option is set to `nil` and Galaxy support is then disabled. + + Note: if an absolute path is given, the `ansible_local` provisioner will assume that it corresponds to the exact location on the guest system. + +- `galaxy_roles_path` (string) - The path to the directory where Ansible Galaxy roles must be installed + + By default, this option is set to `nil`, which means that the Galaxy roles will be installed in a `roles` subdirectory located in the parent directory of the `playbook` file. + - `limit` (string or array of strings) - Set of machines or groups from the inventory file to further control which hosts [are affected](http://docs.ansible.com/glossary.html#limit-groups). The default value is set to the machine name (taken from `Vagrantfile`) to ensure that `vagrant provision` command only affect the expected machine. From 819c9b6425651e14a392dfc2f9cf460e1492d492 Mon Sep 17 00:00:00 2001 From: Gilles Cornu Date: Wed, 18 Nov 2015 09:37:27 +0100 Subject: [PATCH 4/9] provisioners/ansible_local: align console outputs Use "info" level for main tasks (installing ansible, running galaxy, running playbook). Use "detail" level for subsequent details. --- plugins/provisioners/ansible/provisioner/base.rb | 2 +- plugins/provisioners/ansible/provisioner/guest.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/provisioners/ansible/provisioner/base.rb b/plugins/provisioners/ansible/provisioner/base.rb index 16f6d0d77..0fc24ecfb 100644 --- a/plugins/provisioners/ansible/provisioner/base.rb +++ b/plugins/provisioners/ansible/provisioner/base.rb @@ -148,7 +148,7 @@ module VagrantPlugins @machine.ui.detail I18n.t("vagrant.provisioners.ansible.running_#{name}") if verbosity_is_enabled? # Show the ansible command in use - @machine.env.ui.detail(command) + @machine.env.ui.detail command end end diff --git a/plugins/provisioners/ansible/provisioner/guest.rb b/plugins/provisioners/ansible/provisioner/guest.rb index 9e4346190..2397f66df 100644 --- a/plugins/provisioners/ansible/provisioner/guest.rb +++ b/plugins/provisioners/ansible/provisioner/guest.rb @@ -46,7 +46,7 @@ module VagrantPlugins if config.install && (config.version.to_s.to_sym == :latest || !@machine.guest.capability(:ansible_installed, config.version)) - @machine.ui.info(I18n.t("vagrant.provisioners.ansible.installing")) + @machine.ui.detail I18n.t("vagrant.provisioners.ansible.installing") @machine.guest.capability(:ansible_install) end From 5659c3f2a0637fa8ad5c4fa20c4204efdafe570f Mon Sep 17 00:00:00 2001 From: Gilles Cornu Date: Thu, 19 Nov 2015 23:42:01 +0100 Subject: [PATCH 5/9] provisioners/ansible: apply @sethvargo comments Follow-up of code review of PR #6529 --- plugins/provisioners/ansible/config/base.rb | 34 ++++++++++--------- .../provisioners/ansible/provisioner/base.rb | 4 +-- .../provisioners/ansible/provisioner/guest.rb | 4 +-- .../provisioners/ansible/provisioner/host.rb | 13 +++---- templates/locales/en.yml | 14 +++++--- .../v2/provisioning/ansible_common.html.md | 14 ++++---- 6 files changed, 45 insertions(+), 38 deletions(-) diff --git a/plugins/provisioners/ansible/config/base.rb b/plugins/provisioners/ansible/config/base.rb index 04c703439..f14b15597 100644 --- a/plugins/provisioners/ansible/config/base.rb +++ b/plugins/provisioners/ansible/config/base.rb @@ -3,6 +3,8 @@ module VagrantPlugins module Config class Base < Vagrant.plugin("2", :config) + GALAXY_COMMAND_DEFAULT = "ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force" + attr_accessor :extra_vars attr_accessor :galaxy_role_file attr_accessor :galaxy_roles_path @@ -40,22 +42,22 @@ module VagrantPlugins end def finalize! - @extra_vars = nil if @extra_vars == UNSET_VALUE - @galaxy_role_file = nil if @galaxy_role_file == UNSET_VALUE - @galaxy_roles_path = nil if @galaxy_roles_path == UNSET_VALUE - @galaxy_command = "ansible-galaxy install --role-file=%{ROLE_FILE} --roles-path=%{ROLES_PATH} --force" if @galaxy_command == UNSET_VALUE - @groups = {} if @groups == UNSET_VALUE - @inventory_path = nil if @inventory_path == UNSET_VALUE - @limit = nil if @limit == UNSET_VALUE - @playbook = nil if @playbook == UNSET_VALUE - @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 + @extra_vars = nil if @extra_vars == UNSET_VALUE + @galaxy_role_file = nil if @galaxy_role_file == UNSET_VALUE + @galaxy_roles_path = nil if @galaxy_roles_path == UNSET_VALUE + @galaxy_command = GALAXY_COMMAND_DEFAULT if @galaxy_command == UNSET_VALUE + @groups = {} if @groups == UNSET_VALUE + @inventory_path = nil if @inventory_path == UNSET_VALUE + @limit = nil if @limit == UNSET_VALUE + @playbook = nil if @playbook == UNSET_VALUE + @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 end # Just like the normal configuration "validate" method except that diff --git a/plugins/provisioners/ansible/provisioner/base.rb b/plugins/provisioners/ansible/provisioner/base.rb index 0fc24ecfb..7a455b2bf 100644 --- a/plugins/provisioners/ansible/provisioner/base.rb +++ b/plugins/provisioners/ansible/provisioner/base.rb @@ -133,12 +133,12 @@ module VagrantPlugins end def get_galaxy_role_file(basedir) - Pathname.new(config.galaxy_role_file).expand_path(basedir) + File.expand_path(config.galaxy_role_file, basedir) end def get_galaxy_roles_path(basedir) if config.galaxy_roles_path - Pathname.new(config.galaxy_roles_path).expand_path(basedir) + File.expand_path(config.galaxy_roles_path, basedir) else File.join(Pathname.new(config.playbook).expand_path(basedir).parent, 'roles') end diff --git a/plugins/provisioners/ansible/provisioner/guest.rb b/plugins/provisioners/ansible/provisioner/guest.rb index 2397f66df..3d76e1fed 100644 --- a/plugins/provisioners/ansible/provisioner/guest.rb +++ b/plugins/provisioners/ansible/provisioner/guest.rb @@ -66,8 +66,8 @@ module VagrantPlugins def execute_ansible_galaxy_on_guest command_values = { - :ROLE_FILE => get_galaxy_role_file(config.provisioning_path), - :ROLES_PATH => get_galaxy_roles_path(config.provisioning_path) + :role_file => get_galaxy_role_file(config.provisioning_path), + :roles_path => get_galaxy_roles_path(config.provisioning_path) } remote_command = config.galaxy_command % command_values diff --git a/plugins/provisioners/ansible/provisioner/host.rb b/plugins/provisioners/ansible/provisioner/host.rb index 61e576c97..ccf72ca8e 100644 --- a/plugins/provisioners/ansible/provisioner/host.rb +++ b/plugins/provisioners/ansible/provisioner/host.rb @@ -25,6 +25,8 @@ module VagrantPlugins protected + VAGRANT_ARG_SEPARATOR = 'VAGRANT_ARG_SEP' + 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")) @@ -86,16 +88,15 @@ module VagrantPlugins def execute_ansible_galaxy_from_host command_values = { - :ROLE_FILE => get_galaxy_role_file(machine.env.root_path), - :ROLES_PATH => get_galaxy_roles_path(machine.env.root_path) + :role_file => get_galaxy_role_file(machine.env.root_path), + :roles_path => get_galaxy_roles_path(machine.env.root_path) } - arg_separator = '__VAGRANT_ARG_SEPARATOR__' - command_template = config.galaxy_command.gsub(' ', arg_separator) + command_template = config.galaxy_command.gsub(' ', VAGRANT_ARG_SEPARATOR) str_command = command_template % command_values - ui_running_ansible_command "galaxy", str_command.gsub(arg_separator, ' ') + ui_running_ansible_command "galaxy", str_command.gsub(VAGRANT_ARG_SEPARATOR, ' ') - command = str_command.split(arg_separator) + command = str_command.split(VAGRANT_ARG_SEPARATOR) command << { # Write stdout and stderr data, since it's the regular Ansible output notify: [:stdout, :stderr], diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 6bcb24689..aa452bff3 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -2043,6 +2043,12 @@ en: Vagrant does not support detecting whether Ansible is installed for the guest OS running in the machine. Vagrant will assume it is installed and attempt to continue. + + If you'd like this provisioner to be improved, please + take a look at the Vagrant source code linked below and try + to contribute back support. Thank you! + + https://github.com/mitchellh/vagrant errors: ansible_command_failed: |- Ansible failed to complete successfully. Any error output should be @@ -2054,7 +2060,7 @@ en: If you haven't installed Ansible yet, please install Ansible on your Vagrant basebox, or enable the automated setup with the `install` option of this provisioner. Please check - http://docs.vagrantup.com/v2/provisioning/ansible_local.html + https://docs.vagrantup.com/v2/provisioning/ansible_local.html for more information. ansible_not_found_on_host: |- The Ansible software could not be found! Please verify @@ -2063,12 +2069,12 @@ en: If you haven't installed Ansible yet, please install Ansible on your host system. Vagrant can't do this for you in a safe and automated way. - Please check http://docs.ansible.com for more information. + Please check https://docs.ansible.com for more information. ansible_version_not_found_on_guest: |- The requested Ansible version (%{required_version}) was not found on the guest. Please check the ansible installation on your guest system, or adapt the `version` option of this provisioner in your Vagrantfile. - See http://docs.vagrantup.com/v2/provisioning/ansible_local.html + See https://docs.vagrantup.com/v2/provisioning/ansible_local.html for more information. extra_vars_invalid: |- `extra_vars` must be a hash or a path to an existing file. Received: %{value} (as %{type}) @@ -2087,7 +2093,7 @@ en: running_playbook: "Running ansible-playbook..." windows_not_supported_for_control_machine: |- Windows is not officially supported for the Ansible Control Machine. - Please check http://docs.ansible.com/intro_installation.html#control-machine-requirements + Please check https://docs.ansible.com/intro_installation.html#control-machine-requirements docker: not_running: "Docker is not running on the guest VM." diff --git a/website/docs/source/v2/provisioning/ansible_common.html.md b/website/docs/source/v2/provisioning/ansible_common.html.md index aca7c2c14..cb8daf55e 100644 --- a/website/docs/source/v2/provisioning/ansible_common.html.md +++ b/website/docs/source/v2/provisioning/ansible_common.html.md @@ -53,17 +53,15 @@ Some of these options are for advanced usage only and should not be used unless - `galaxy_command` (template string) - The command pattern used to install Galaxy roles when `galaxy_role_file` is set. - The following placeholders can be used in this command pattern: - - `%{ROLE_FILE}` is replaced by the absolute path to the `galaxy_role_file` option - - `%{ROLES_PATH}` is - * replaced by the absolute path to the `galaxy_roles_path` option when such option is defined - * replaced by the absolute path to a `roles` subdirectory sitting in the parent directory of the configured `playbook` file otherwise. + The following (optional) placeholders can be used in this command pattern: + - `%{role_file}` is replaced by the absolute path to the `galaxy_role_file` option + - `%{roles_path}` is + - replaced by the absolute path to the `galaxy_roles_path` option when such option is defined, or + - replaced by the absolute path to a `roles` subdirectory sitting in the `playbook` parent directory. By default, this option is set to - ``` - ansible-galaxy install --role-file=%{ROLE_FILE} --roles-path=%{ROLES_PATH} --force - ``` + `ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force` - `galaxy_role_file` (string) - The path to the Ansible Galaxy role file. From 74eb3109cfa1eac23b4c0c216098716a394db9bb Mon Sep 17 00:00:00 2001 From: Gilles Cornu Date: Fri, 20 Nov 2015 00:07:34 +0100 Subject: [PATCH 6/9] provisioners/ansible_local: fix a lame bug I missed to rename the refactored exceptions as AnsibleCommandFailed in the guest-based parts. The lack of unit tests for these parts hurts... on my agenda, I swear! See c1f3d114f52654e9f285499dd548409edc0c99fe --- plugins/provisioners/ansible/provisioner/guest.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/provisioners/ansible/provisioner/guest.rb b/plugins/provisioners/ansible/provisioner/guest.rb index 3d76e1fed..f8777d93c 100644 --- a/plugins/provisioners/ansible/provisioner/guest.rb +++ b/plugins/provisioners/ansible/provisioner/guest.rb @@ -74,7 +74,7 @@ module VagrantPlugins ui_running_ansible_command "galaxy", remote_command result = execute_on_guest(remote_command) - raise Ansible::Errors::AnsibleGalaxyAppFailed if result != 0 + raise Ansible::Errors::AnsibleCommandFailed if result != 0 end def execute_ansible_playbook_on_guest @@ -87,7 +87,7 @@ module VagrantPlugins ui_running_ansible_command "playbook", remote_command result = execute_on_guest(remote_command) - raise Ansible::Errors::AnsiblePlaybookAppFailed if result != 0 + raise Ansible::Errors::AnsibleCommandFailed if result != 0 end def execute_on_guest(command) From c9fe02cea5dabd9136919f3fb350ca9978cab361 Mon Sep 17 00:00:00 2001 From: Gilles Cornu Date: Fri, 20 Nov 2015 00:11:44 +0100 Subject: [PATCH 7/9] provisioners/ansible_local: remove repetitions --- plugins/provisioners/ansible/provisioner/guest.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/provisioners/ansible/provisioner/guest.rb b/plugins/provisioners/ansible/provisioner/guest.rb index f8777d93c..a82bf7562 100644 --- a/plugins/provisioners/ansible/provisioner/guest.rb +++ b/plugins/provisioners/ansible/provisioner/guest.rb @@ -71,10 +71,7 @@ module VagrantPlugins } remote_command = config.galaxy_command % command_values - ui_running_ansible_command "galaxy", remote_command - - result = execute_on_guest(remote_command) - raise Ansible::Errors::AnsibleCommandFailed if result != 0 + execute_ansible_command_on_guest "galaxy", remote_command end def execute_ansible_playbook_on_guest @@ -84,9 +81,13 @@ module VagrantPlugins command = (%w(ansible-playbook) << @command_arguments << config.playbook).flatten remote_command = "cd #{config.provisioning_path} && #{Helpers::stringify_ansible_playbook_command(@environment_variables, command)}" - ui_running_ansible_command "playbook", remote_command + execute_ansible_command_on_guest "playbook", remote_command + end - result = execute_on_guest(remote_command) + def execute_ansible_command_on_guest(name, command) + ui_running_ansible_command name, command + + result = execute_on_guest(command) raise Ansible::Errors::AnsibleCommandFailed if result != 0 end From 6dbc74d814c15606ebf4c3e1b9d8a1092c8106a5 Mon Sep 17 00:00:00 2001 From: Gilles Cornu Date: Fri, 20 Nov 2015 00:13:01 +0100 Subject: [PATCH 8/9] provisioners/ansible: fix code indentation --- plugins/provisioners/ansible/provisioner/host.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/provisioners/ansible/provisioner/host.rb b/plugins/provisioners/ansible/provisioner/host.rb index ccf72ca8e..23d59f7d2 100644 --- a/plugins/provisioners/ansible/provisioner/host.rb +++ b/plugins/provisioners/ansible/provisioner/host.rb @@ -88,9 +88,9 @@ module VagrantPlugins def execute_ansible_galaxy_from_host command_values = { - :role_file => get_galaxy_role_file(machine.env.root_path), - :roles_path => get_galaxy_roles_path(machine.env.root_path) - } + :role_file => get_galaxy_role_file(machine.env.root_path), + :roles_path => get_galaxy_roles_path(machine.env.root_path) + } command_template = config.galaxy_command.gsub(' ', VAGRANT_ARG_SEPARATOR) str_command = command_template % command_values From 238403244610bec3c54b7515b68d1426992926dc Mon Sep 17 00:00:00 2001 From: Gilles Cornu Date: Fri, 20 Nov 2015 00:21:41 +0100 Subject: [PATCH 9/9] provisioners/ansible: freeze a constant value Kudos @sethvargo :) [skip ci] --- plugins/provisioners/ansible/config/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/provisioners/ansible/config/base.rb b/plugins/provisioners/ansible/config/base.rb index f14b15597..2cefe4a89 100644 --- a/plugins/provisioners/ansible/config/base.rb +++ b/plugins/provisioners/ansible/config/base.rb @@ -3,7 +3,7 @@ module VagrantPlugins module Config class Base < Vagrant.plugin("2", :config) - GALAXY_COMMAND_DEFAULT = "ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force" + GALAXY_COMMAND_DEFAULT = "ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force".freeze attr_accessor :extra_vars attr_accessor :galaxy_role_file