provisioners/ansible: Validate compatibility_mode

- Use `'auto'` instead of `nil` for the auto-detection mode
- Add strict validation and related error message
This commit is contained in:
Gilles Cornu 2017-08-26 08:47:35 +02:00
parent e2621a42fc
commit a327e34861
No known key found for this signature in database
GPG Key ID: F6BC2CF7E1FE8FFF
7 changed files with 56 additions and 24 deletions

View File

@ -46,7 +46,7 @@ module VagrantPlugins
def initialize def initialize
@become = UNSET_VALUE @become = UNSET_VALUE
@become_user = UNSET_VALUE @become_user = UNSET_VALUE
@compatibility_mode = UNSET_VALUE @compatibility_mode = Ansible::COMPATIBILITY_MODE_AUTO
@config_file = UNSET_VALUE @config_file = UNSET_VALUE
@extra_vars = UNSET_VALUE @extra_vars = UNSET_VALUE
@galaxy_role_file = UNSET_VALUE @galaxy_role_file = UNSET_VALUE
@ -95,6 +95,12 @@ module VagrantPlugins
def validate(machine) def validate(machine)
@errors = _detected_errors @errors = _detected_errors
# Validate that a compatibility mode was provided
if !compatibility_mode
@errors << I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode",
valid_modes: Ansible::COMPATIBILITY_MODES.map { |s| "'#{s}'" }.join(', '))
end
# Validate that a playbook path was provided # Validate that a playbook path was provided
if !playbook if !playbook
@errors << I18n.t("vagrant.provisioners.ansible.errors.no_playbook") @errors << I18n.t("vagrant.provisioners.ansible.errors.no_playbook")

View File

@ -1,9 +1,14 @@
module VagrantPlugins module VagrantPlugins
module Ansible module Ansible
COMPATIBILITY_MODE_AUTO = "auto".freeze
COMPATIBILITY_MODE_V1_8 = "1.8".freeze COMPATIBILITY_MODE_V1_8 = "1.8".freeze
COMPATIBILITY_MODE_V2_0 = "2.0".freeze COMPATIBILITY_MODE_V2_0 = "2.0".freeze
DEFAULT_COMPATIBILITY_MODE = COMPATIBILITY_MODE_V1_8 SAFE_COMPATIBILITY_MODE = COMPATIBILITY_MODE_V1_8
COMPATIBILITY_MODES = [COMPATIBILITY_MODE_V1_8, COMPATIBILITY_MODE_V2_0].freeze COMPATIBILITY_MODES = [
COMPATIBILITY_MODE_AUTO,
COMPATIBILITY_MODE_V1_8,
COMPATIBILITY_MODE_V2_0,
].freeze
end end
end end

View File

@ -48,11 +48,11 @@ module VagrantPlugins
end end
def set_compatibility_mode def set_compatibility_mode
unless config.compatibility_mode if config.compatibility_mode == Ansible::COMPATIBILITY_MODE_AUTO
detect_compatibility_mode(gather_ansible_version) detect_compatibility_mode(gather_ansible_version)
end end
unless Ansible::COMPATIBILITY_MODES.include?(config.compatibility_mode) unless Ansible::COMPATIBILITY_MODES.slice(1..-1).include?(config.compatibility_mode)
raise "Programming Error: compatibility_mode must correctly set at this stage!" raise "Programming Error: compatibility_mode must correctly set at this stage!"
end end
@ -60,7 +60,7 @@ module VagrantPlugins
end end
def detect_compatibility_mode(ansible_version_stdoutput) def detect_compatibility_mode(ansible_version_stdoutput)
if config.compatibility_mode if config.compatibility_mode != Ansible::COMPATIBILITY_MODE_AUTO
raise "Programming Error: detect_compatibility_mode() shouldn't have been called." raise "Programming Error: detect_compatibility_mode() shouldn't have been called."
end end
@ -86,8 +86,8 @@ module VagrantPlugins
# Nothing to do here, the fallback to default compatibility_mode is done below # Nothing to do here, the fallback to default compatibility_mode is done below
end end
unless config.compatibility_mode if config.compatibility_mode == Ansible::COMPATIBILITY_MODE_AUTO
config.compatibility_mode = Ansible::DEFAULT_COMPATIBILITY_MODE config.compatibility_mode = Ansible::SAFE_COMPATIBILITY_MODE
@machine.env.ui.warn(I18n.t("vagrant.provisioners.ansible.compatibility_mode_not_detected", @machine.env.ui.warn(I18n.t("vagrant.provisioners.ansible.compatibility_mode_not_detected",
compatibility_mode: config.compatibility_mode, compatibility_mode: config.compatibility_mode,

View File

@ -2374,6 +2374,8 @@ en:
`%{config_option}` does not exist on the %{system}: %{path} `%{config_option}` does not exist on the %{system}: %{path}
extra_vars_invalid: |- extra_vars_invalid: |-
`extra_vars` must be a hash or a path to an existing file. Received: %{value} (as %{type}) `extra_vars` must be a hash or a path to an existing file. Received: %{value} (as %{type})
no_compatibility_mode: |-
`compatibility_mode` must be correctly set (possible values: %{valid_modes}).
no_playbook: |- no_playbook: |-
`playbook` file path must be set. `playbook` file path must be set.
raw_arguments_invalid: |- raw_arguments_invalid: |-

View File

@ -5,7 +5,7 @@ shared_examples_for 'options shared by both Ansible provisioners' do
expect(subject.become).to be(false) expect(subject.become).to be(false)
expect(subject.become_user).to be_nil expect(subject.become_user).to be_nil
expect(subject.compatibility_mode).to be_nil expect(subject.compatibility_mode).to eql(VagrantPlugins::Ansible::COMPATIBILITY_MODE_AUTO)
expect(subject.config_file).to be_nil 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")
@ -46,23 +46,37 @@ shared_examples_for 'an Ansible provisioner' do | path_prefix, ansible_setup |
describe "compatibility_mode option" do describe "compatibility_mode option" do
%w(1.8 2.0).each do |minimal_version| VagrantPlugins::Ansible::COMPATIBILITY_MODES.each do |valid_mode|
it "supports compatibility mode '#{minimal_version}'" do it "supports compatibility mode '#{valid_mode}'" do
subject.compatibility_mode = minimal_version subject.compatibility_mode = valid_mode
subject.finalize! subject.finalize!
result = subject.validate(machine) result = subject.validate(machine)
expect(subject.compatibility_mode).to eql(minimal_version) expect(subject.compatibility_mode).to eql(valid_mode)
end end
end end
it "returns an error if the compatibility mode is not set" do
subject.compatibility_mode = nil
subject.finalize!
result = subject.validate(machine)
expect(result[provisioner_label]).to eql([
I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode",
valid_modes: "'auto', '1.8', '2.0'")
])
end
%w(invalid 1.9 2.3).each do |invalid_mode| %w(invalid 1.9 2.3).each do |invalid_mode|
it "silently forces the compatibility mode detection for invalid mode '#{invalid_mode}'" do it "returns an error if the compatibility mode is invalid (e.g. '#{invalid_mode}')" do
subject.compatibility_mode = invalid_mode subject.compatibility_mode = invalid_mode
subject.finalize! subject.finalize!
result = subject.validate(machine) result = subject.validate(machine)
expect(subject.compatibility_mode).to be_nil expect(result[provisioner_label]).to eql([
I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode",
valid_modes: "'auto', '1.8', '2.0'")
])
end end
end end
@ -109,6 +123,7 @@ shared_examples_for 'an Ansible provisioner' do | path_prefix, ansible_setup |
end end
it "it collects and returns all detected errors" do it "it collects and returns all detected errors" do
subject.compatibility_mode = nil
subject.playbook = nil subject.playbook = nil
subject.extra_vars = ["var1", 3, "var2", 5] subject.extra_vars = ["var1", 3, "var2", 5]
subject.raw_arguments = { arg1: 1, arg2: "foo" } subject.raw_arguments = { arg1: 1, arg2: "foo" }
@ -116,7 +131,10 @@ shared_examples_for 'an Ansible provisioner' do | path_prefix, ansible_setup |
result = subject.validate(machine) result = subject.validate(machine)
expect(result[provisioner_label].size).to eql(3) expect(result[provisioner_label].size).to eql(4)
expect(result[provisioner_label]).to include(
I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode",
valid_modes: "'auto', '1.8', '2.0'"))
expect(result[provisioner_label]).to include( expect(result[provisioner_label]).to include(
I18n.t("vagrant.provisioners.ansible.errors.no_playbook")) I18n.t("vagrant.provisioners.ansible.errors.no_playbook"))
expect(result[provisioner_label]).to include( expect(result[provisioner_label]).to include(

View File

@ -305,9 +305,9 @@ VF
"ask_become_pass" => "--ask-sudo-pass"}) "ask_become_pass" => "--ask-sudo-pass"})
end end
context "with no compatibility_mode defined" do context "with compatibility_mode 'auto'" do
before do before do
config.compatibility_mode = nil config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_AUTO
end end
valid_versions = { valid_versions = {
@ -348,16 +348,16 @@ VF
allow(subject).to receive(:gather_ansible_version).and_return(unknown_ansible_version) allow(subject).to receive(:gather_ansible_version).and_return(unknown_ansible_version)
end end
it "applies the default compatibility mode ('#{VagrantPlugins::Ansible::DEFAULT_COMPATIBILITY_MODE}')" do it "applies the safest compatibility mode ('#{VagrantPlugins::Ansible::SAFE_COMPATIBILITY_MODE}')" do
expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args|
expect(config.compatibility_mode).to eq(VagrantPlugins::Ansible::DEFAULT_COMPATIBILITY_MODE) expect(config.compatibility_mode).to eq(VagrantPlugins::Ansible::SAFE_COMPATIBILITY_MODE)
}.and_return(default_execute_result) }.and_return(default_execute_result)
end end
it "warns about not being able to detect the best compatibility mode" do it "warns about not being able to detect the best compatibility mode" do
expect(machine.env.ui).to receive(:warn).with( expect(machine.env.ui).to receive(:warn).with(
I18n.t("vagrant.provisioners.ansible.compatibility_mode_not_detected", I18n.t("vagrant.provisioners.ansible.compatibility_mode_not_detected",
compatibility_mode: VagrantPlugins::Ansible::DEFAULT_COMPATIBILITY_MODE, compatibility_mode: VagrantPlugins::Ansible::SAFE_COMPATIBILITY_MODE,
gathered_version: unknown_ansible_version) + gathered_version: unknown_ansible_version) +
"\n") "\n")
end end

View File

@ -29,10 +29,11 @@ Some of these options are for advanced usage only and should not be used unless
Possible values: Possible values:
- `"1.8"` (Ansible versions prior to 1.8 should mostly work well, but some options might not be supported) - `"auto"` _(Vagrant will automatically select the optimal compatibilty mode by checking the Ansible version currently available)_
- `"2.0"` (The generated Ansible inventory will be incompatible with Ansible 1.x) - `"1.8"` _(Ansible versions prior to 1.8 should mostly work well, but some options might not be supported)_
- `"2.0"` _(The generated Ansible inventory will be incompatible with Ansible 1.x)_
By default this option is not set, and Vagrant will try to automatically set the optimal compatibilty mode by checking the Ansible version currently available. Note that Vagrant doesn't validate this option, and any unsupported value (e.g. "2.3") will also lead Vagrant to auto-detect the compatibility mode. By default this option is set to `"auto"`. If Vagrant is not able to detect any supported Ansible version, it will falls back on the compatibility mode `"1.8"` with a warning.
<div class="alert alert-info"> <div class="alert alert-info">
<strong>Compatibility Note:</strong> <strong>Compatibility Note:</strong>