provisioners/ansible: Move `version` to common options

Before this change, only the ansible_local provisioner supported this
option (for ansible version requirement, and pip installation). Now, the
ansible host-based provisioner can also require a exact ansible version.

Resolve #8914

Note: this has been added as part of #6570 resolution, since the
introduction of the `compatibility_mode` auto-detection made both
provisioners made capable to detect ansible version.

Pending: optimize the code to avoid duplicated executions of "ansible
--version" command.
This commit is contained in:
Gilles Cornu 2017-08-29 05:32:38 +02:00
parent 15e74e264d
commit 8c0df3d046
No known key found for this signature in database
GPG Key ID: F6BC2CF7E1FE8FFF
12 changed files with 91 additions and 32 deletions

View File

@ -28,6 +28,7 @@ module VagrantPlugins
attr_accessor :tags attr_accessor :tags
attr_accessor :vault_password_file attr_accessor :vault_password_file
attr_accessor :verbose attr_accessor :verbose
attr_accessor :version
# #
# Deprecated options # Deprecated options
@ -64,6 +65,7 @@ module VagrantPlugins
@tags = UNSET_VALUE @tags = UNSET_VALUE
@vault_password_file = UNSET_VALUE @vault_password_file = UNSET_VALUE
@verbose = UNSET_VALUE @verbose = UNSET_VALUE
@version = UNSET_VALUE
end end
def finalize! def finalize!
@ -87,6 +89,7 @@ module VagrantPlugins
@tags = nil if @tags == UNSET_VALUE @tags = nil if @tags == UNSET_VALUE
@vault_password_file = nil if @vault_password_file == UNSET_VALUE @vault_password_file = nil if @vault_password_file == UNSET_VALUE
@verbose = false if @verbose == UNSET_VALUE @verbose = false if @verbose == UNSET_VALUE
@version = "" if @version == UNSET_VALUE
end end
# Just like the normal configuration "validate" method except that # Just like the normal configuration "validate" method except that

View File

@ -11,7 +11,6 @@ module VagrantPlugins
attr_accessor :install attr_accessor :install
attr_accessor :install_mode attr_accessor :install_mode
attr_accessor :pip_args attr_accessor :pip_args
attr_accessor :version
def initialize def initialize
super super
@ -21,7 +20,6 @@ module VagrantPlugins
@pip_args = UNSET_VALUE @pip_args = UNSET_VALUE
@provisioning_path = UNSET_VALUE @provisioning_path = UNSET_VALUE
@tmp_path = UNSET_VALUE @tmp_path = UNSET_VALUE
@version = UNSET_VALUE
end end
def finalize! def finalize!
@ -32,7 +30,6 @@ module VagrantPlugins
@pip_args = "" if @pip_args == UNSET_VALUE @pip_args = "" if @pip_args == UNSET_VALUE
@provisioning_path = "/vagrant" if provisioning_path == UNSET_VALUE @provisioning_path = "/vagrant" if provisioning_path == UNSET_VALUE
@tmp_path = "/tmp/vagrant-ansible" if tmp_path == UNSET_VALUE @tmp_path = "/tmp/vagrant-ansible" if tmp_path == UNSET_VALUE
@version = "" if @version == UNSET_VALUE
end end
def validate(machine) def validate(machine)

View File

@ -23,8 +23,8 @@ module VagrantPlugins
error_key(:cannot_support_pip_install) error_key(:cannot_support_pip_install)
end end
class AnsibleVersionNotFoundOnGuest < AnsibleError class AnsibleVersionMismatch < AnsibleError
error_key(:ansible_version_not_found_on_guest) error_key(:ansible_version_mismatch)
end end
end end
end end

View File

@ -65,7 +65,7 @@ module VagrantPlugins
if (!config.version.empty? && if (!config.version.empty? &&
config.version.to_s.to_sym != :latest && config.version.to_s.to_sym != :latest &&
!@machine.guest.capability(:ansible_installed, config.version)) !@machine.guest.capability(:ansible_installed, config.version))
raise Ansible::Errors::AnsibleVersionNotFoundOnGuest, required_version: config.version.to_s raise Ansible::Errors::AnsibleVersionMismatch, system: "guest", required_version: config.version.to_s
end end
end end

View File

@ -19,6 +19,7 @@ module VagrantPlugins
@ssh_info = @machine.ssh_info @ssh_info = @machine.ssh_info
warn_for_unsupported_platform warn_for_unsupported_platform
check_required_ansible_version unless config.version.empty?
check_files_existence check_files_existence
set_compatibility_mode set_compatibility_mode
@ -36,6 +37,19 @@ module VagrantPlugins
end end
end end
def check_required_ansible_version
if config.version.to_s.to_sym == :latest
@logger.debug("The :latest version requirement is not supported (yet) by the host-based provisioner")
return
end
@logger.info("Checking for Ansible version on Vagrant host...")
found_version = gather_ansible_version
if (!found_version || "ansible #{config.version}\n" != found_version.lines[0])
raise Ansible::Errors::AnsibleVersionMismatch, system: "host", required_version: config.version.to_s
end
end
def prepare_command_arguments def prepare_command_arguments
# Connect with native OpenSSH client # Connect with native OpenSSH client
# Other modes (e.g. paramiko) are not officially supported, # Other modes (e.g. paramiko) are not officially supported,

View File

@ -2364,11 +2364,11 @@ en:
to contribute back support. Thank you! to contribute back support. Thank you!
https://github.com/mitchellh/vagrant https://github.com/mitchellh/vagrant
ansible_version_not_found_on_guest: |- ansible_version_mismatch: |-
The requested Ansible version (%{required_version}) was not found on the guest. The requested Ansible version (%{required_version}) was not found on the %{system}.
Please check the ansible installation on your guest system, Please check the Ansible installation on your Vagrant %{system} system,
or adapt the `version` option of this provisioner in your Vagrantfile. or adapt the `version` option of this provisioner in your Vagrantfile.
See https://docs.vagrantup.com/v2/provisioning/ansible_local.html See https://docs.vagrantup.com/v2/provisioning/ansible_common.html#version
for more information. for more information.
config_file_not_found: |- config_file_not_found: |-
`%{config_option}` does not exist on the %{system}: %{path} `%{config_option}` does not exist on the %{system}: %{path}

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( become supported_options = %w(
become
become_user become_user
compatibility_mode compatibility_mode
config_file config_file
@ -43,7 +44,8 @@ describe VagrantPlugins::Ansible::Config::Guest do
tmp_path tmp_path
vault_password_file vault_password_file
verbose verbose
version ) version
)
expect(get_provisioner_option_names(described_class)).to eql(supported_options) expect(get_provisioner_option_names(described_class)).to eql(supported_options)
end end
@ -58,7 +60,6 @@ describe VagrantPlugins::Ansible::Config::Guest do
expect(subject.install_mode).to eql(:default) expect(subject.install_mode).to eql(:default)
expect(subject.provisioning_path).to eql("/vagrant") expect(subject.provisioning_path).to eql("/vagrant")
expect(subject.tmp_path).to eql("/tmp/vagrant-ansible") expect(subject.tmp_path).to eql("/tmp/vagrant-ansible")
expect(subject.version).to be_empty
end end
end end

View File

@ -13,7 +13,8 @@ describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do
let(:existing_file) { File.expand_path(__FILE__) } let(:existing_file) { File.expand_path(__FILE__) }
it "supports a list of options" do it "supports a list of options" do
supported_options = %w( ask_become_pass supported_options = %w(
ask_become_pass
ask_sudo_pass ask_sudo_pass
ask_vault_pass ask_vault_pass
become become
@ -40,7 +41,9 @@ describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do
sudo_user sudo_user
tags tags
vault_password_file vault_password_file
verbose ) verbose
version
)
expect(get_provisioner_option_names(described_class)).to eql(supported_options) expect(get_provisioner_option_names(described_class)).to eql(supported_options)
end end

View File

@ -25,6 +25,7 @@ shared_examples_for 'options shared by both Ansible provisioners' do
expect(subject.tags).to be_nil expect(subject.tags).to be_nil
expect(subject.vault_password_file).to be_nil expect(subject.vault_password_file).to be_nil
expect(subject.verbose).to be(false) expect(subject.verbose).to be(false)
expect(subject.version).to be_empty
end end
end end

View File

@ -947,6 +947,46 @@ VF
end end
end end
context "with version option set" do
before do
config.version = "2.3.4.5"
end
describe "and the installed ansible version is correct" do
before do
allow(subject).to receive(:gather_ansible_version).and_return("ansible #{config.version}\n...\n")
end
it "executes ansible-playbook command" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args).and_return(default_execute_result)
end
end
describe "and there is an ansible version mismatch" do
before do
allow(subject).to receive(:gather_ansible_version).and_return("ansible 1.9.6\n...\n")
end
it "raises an error about the ansible version mismatch", skip_before: true, skip_after: true do
config.finalize!
expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleVersionMismatch)
end
end
describe "and the installed ansible version cannot be detected" do
before do
allow(subject).to receive(:gather_ansible_version).and_return(nil)
end
it "raises an error about the ansible version mismatch", skip_before: true, skip_after: true do
config.finalize!
expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleVersionMismatch)
end
end
end
# TODO: add more tests, now that we know how to deal with multiple Subprocess stub executions
describe "with galaxy support" do describe "with galaxy support" do
before do before do

View File

@ -42,7 +42,7 @@ Some of these options are for advanced usage only and should not be used unless
<div class="alert alert-warning"> <div class="alert alert-warning">
<strong>Attention:</strong> <strong>Attention:</strong>
Vagrant doesn't perform any validation between the `compatibility_mode` value and the value of the ansible_local [`version`](/docs/provisioning/ansible_local.html#version) option. Vagrant doesn't perform any validation between the `compatibility_mode` value and the value of the [`version`](#version) option.
</div> </div>
- `config_file` (string) - The path to an [Ansible Configuration file](https://docs.ansible.com/intro_configuration.html). - `config_file` (string) - The path to an [Ansible Configuration file](https://docs.ansible.com/intro_configuration.html).
@ -185,3 +185,16 @@ Some of these options are for advanced usage only and should not be used unless
Examples: `true` (equivalent to `v`), `-vvv` (equivalent to `vvv`), `vvvv`. Examples: `true` (equivalent to `v`), `-vvv` (equivalent to `vvv`), `vvvv`.
Note that when the `verbose` option is enabled, the `ansible-playbook` command used by Vagrant will be displayed. Note that when the `verbose` option is enabled, the `ansible-playbook` command used by Vagrant will be displayed.
- `version` (string) - The expected Ansible version.
This option is disabled by default.
When an Ansible version is defined (e.g. `"2.1.6.0"`), the Ansible provisioner will be executed only if Ansible is installed at the requested version.
When this option is set to `"latest"`, no version check is applied.
<div class="alert alert-info">
<strong>Tip:</strong>
With the `ansible_local` provisioner, it is currently possible to use this option to specify which version of Ansible must be automatically installed, but <strong>only</strong> in combination with the [**`install_mode`**](ansible_local.html#install_mode) set to <strong>`:pip`</strong>.
</div>

View File

@ -62,8 +62,8 @@ This section lists the _specific_ options for the Ansible Local provisioner. In
Vagrant will try to install (or upgrade) Ansible when one of these conditions are met: Vagrant will try to install (or upgrade) Ansible when one of these conditions are met:
- Ansible is not installed (or cannot be found). - Ansible is not installed (or cannot be found).
- The `version` option is set to `"latest"`. - The [`version`](/docs/provisioning/ansible_common.html#version) option is set to `"latest"`.
- The current Ansible version does not correspond to the `version` option. - The current Ansible version does not correspond to the [`version`](/docs/provisioning/ansible_common.html#version) option.
<div class="alert alert-warning"> <div class="alert alert-warning">
<strong>Attention:</strong> <strong>Attention:</strong>
@ -76,7 +76,7 @@ This section lists the _specific_ options for the Ansible Local provisioner. In
- On Ubuntu-like systems, the latest Ansible release is installed from the `ppa:ansible/ansible` repository. - On Ubuntu-like systems, the latest Ansible release is installed from the `ppa:ansible/ansible` repository.
- On RedHat-like systems, the latest Ansible release is installed from the [EPEL](http://fedoraproject.org/wiki/EPEL) repository. - On RedHat-like systems, the latest Ansible release is installed from the [EPEL](http://fedoraproject.org/wiki/EPEL) repository.
- `:pip`: Ansible is installed from [PyPI](https://pypi.python.org/pypi) with [pip](https://pip.pypa.io) package installer. With this mode, Vagrant will systematically try to [install the latest pip version](https://pip.pypa.io/en/stable/installing/#installing-with-get-pip-py). With the `:pip` mode you can optionally install a specific Ansible release by setting the [`version`](#version) option. - `:pip`: Ansible is installed from [PyPI](https://pypi.python.org/pypi) with [pip](https://pip.pypa.io) package installer. With this mode, Vagrant will systematically try to [install the latest pip version](https://pip.pypa.io/en/stable/installing/#installing-with-get-pip-py). With the `:pip` mode you can optionally install a specific Ansible release by setting the [`version`](/docs/provisioning/ansible_common.html#version) option.
Example: Example:
@ -141,19 +141,6 @@ This section lists the _specific_ options for the Ansible Local provisioner. In
The default value is `/tmp/vagrant-ansible` The default value is `/tmp/vagrant-ansible`
- `version` (string) - The expected Ansible version.
This option is disabled by default.
When an Ansible version is defined (e.g. `"1.8.2"`), the Ansible local provisioner will be executed only if Ansible is installed at the requested version.
When this option is set to `"latest"`, no version check is applied.
<div class="alert alert-info">
<strong>Tip:</strong>
It is currently possible to use this option to specify which version of Ansible must be automatically installed, but <strong>only</strong> in combination with the `install_mode` set to <strong>`:pip`</strong>.
</div>
## Tips and Tricks ## Tips and Tricks
### Ansible Parallel Execution from a Guest ### Ansible Parallel Execution from a Guest