diff --git a/lib/vagrant/action/builtin/config_validate.rb b/lib/vagrant/action/builtin/config_validate.rb index 3c4461a10..3bcb0919f 100644 --- a/lib/vagrant/action/builtin/config_validate.rb +++ b/lib/vagrant/action/builtin/config_validate.rb @@ -12,7 +12,7 @@ module Vagrant def call(env) if !env.key?(:config_validate) || env[:config_validate] - errors = env[:machine].config.validate(env[:machine]) + errors = env[:machine].config.validate(env[:machine], env[:ignore_provider]) if errors && !errors.empty? raise Errors::ConfigInvalid, diff --git a/lib/vagrant/config/v2/root.rb b/lib/vagrant/config/v2/root.rb index b41e5c031..1329c57c7 100644 --- a/lib/vagrant/config/v2/root.rb +++ b/lib/vagrant/config/v2/root.rb @@ -60,14 +60,18 @@ module Vagrant # # @param [Environment] env # @return [Hash] - def validate(machine) + def validate(machine, ignore_provider=nil) # Go through each of the configuration keys and validate errors = {} @keys.each do |_key, instance| if instance.respond_to?(:validate) # Validate this single item, and if we have errors then # we merge them into our total errors list. - result = instance.validate(machine) + if _key == :vm + result = instance.validate(machine, ignore_provider) + else + result = instance.validate(machine) + end if result && !result.empty? errors = Util.merge_errors(errors, result) end diff --git a/plugins/commands/validate/command.rb b/plugins/commands/validate/command.rb index bccbc5755..fe90493bc 100644 --- a/plugins/commands/validate/command.rb +++ b/plugins/commands/validate/command.rb @@ -8,17 +8,32 @@ module VagrantPlugins end def execute + options = {} + opts = OptionParser.new do |o| - o.banner = "Usage: vagrant validate" + o.banner = "Usage: vagrant validate [options]" + o.separator "" + o.separator "Validates a Vagrantfile config" + o.separator "" + o.separator "Options:" + o.separator "" + + o.on("-p", "--ignore-provider", "Ignores provider config options") do |p| + options[:ignore_provider] = p + end end # Parse the options argv = parse_options(opts) return if !argv + action_env = {} + if options[:ignore_provider] + action_env[:ignore_provider] = true + end # Validate the configuration of all machines with_target_vms() do |machine| - machine.action_raw(:config_validate, Vagrant::Action::Builtin::ConfigValidate) + machine.action_raw(:config_validate, Vagrant::Action::Builtin::ConfigValidate, action_env) end @env.ui.info(I18n.t("vagrant.commands.validate.success")) diff --git a/plugins/kernel_v2/config/vm.rb b/plugins/kernel_v2/config/vm.rb index 6ed45729c..83e525f3d 100644 --- a/plugins/kernel_v2/config/vm.rb +++ b/plugins/kernel_v2/config/vm.rb @@ -582,7 +582,7 @@ module VagrantPlugins @__synced_folders end - def validate(machine) + def validate(machine, ignore_provider=nil) errors = _detected_errors if !box && !clone && !machine.provider_options[:box_optional] @@ -737,9 +737,13 @@ module VagrantPlugins # Validate only the _active_ provider if machine.provider_config - provider_errors = machine.provider_config.validate(machine) - if provider_errors - errors = Vagrant::Config::V2::Util.merge_errors(errors, provider_errors) + if !ignore_provider + provider_errors = machine.provider_config.validate(machine) + if provider_errors + errors = Vagrant::Config::V2::Util.merge_errors(errors, provider_errors) + end + else + machine.ui.warn(I18n.t("vagrant.config.vm.ignore_provider_config")) end end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index ddfbf73f0..00c17fed0 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -1845,6 +1845,8 @@ en: hostname_invalid_characters: |- The hostname set for the VM should only contain letters, numbers, hyphens or dots. It cannot start with a hyphen or dot. + ignore_provider_config: |- + Ignoring provider config for validation... name_invalid: |- The sub-VM name '%{name}' is invalid. Please don't use special characters. network_ip_ends_in_one: |- diff --git a/test/unit/plugins/commands/validate/command_test.rb b/test/unit/plugins/commands/validate/command_test.rb index 2dfd93e75..591a3bfe6 100644 --- a/test/unit/plugins/commands/validate/command_test.rb +++ b/test/unit/plugins/commands/validate/command_test.rb @@ -5,13 +5,15 @@ describe VagrantPlugins::CommandValidate::Command do include_context "unit" include_context "command plugin helpers" + let(:vagrantfile_content){ "" } let(:iso_env) do - isolated_environment + env = isolated_environment + env.vagrantfile(vagrantfile_content) + env.create_vagrant_env end - let(:env) do - iso_env.create_vagrant_env - end + let(:action_runner) { double("action_runner") } + let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } @@ -20,37 +22,49 @@ describe VagrantPlugins::CommandValidate::Command do I18n.reload! end - subject { described_class.new(argv, env) } + subject { described_class.new(argv, iso_env) } describe "#execute" do - it "validates correct Vagrantfile" do - iso_env.vagrantfile(<<-EOH) - Vagrant.configure("2") do |config| - config.vm.box = "hashicorp/precise64" + context "validating configs" do + let(:vagrantfile_content) do + <<-VF + Vagrant.configure("2") do |config| + config.vm.box = "hashicorp/precise64" + end + VF + end + it "validates correct Vagrantfile" do + expect(machine).to receive(:action_raw) do |name, action, env| + expect(name).to eq(:config_validate) + expect(action).to eq(Vagrant::Action::Builtin::ConfigValidate) + expect(env).to eq({}) end - EOH + expect(iso_env.ui).to receive(:info).with(any_args) { |message, _| + expect(message).to include("Vagrantfile validated successfully.") + } - expect(env.ui).to receive(:info).with(any_args) { |message, _| - expect(message).to include("Vagrantfile validated successfully.") - } - - expect(subject.execute).to eq(0) + expect(subject.execute).to eq(0) + end end - it "validates the configuration" do - iso_env.vagrantfile <<-EOH + context "invalid configs" do + let(:vagrantfile_content) do + <<-VF Vagrant.configure("2") do |config| config.vm.bix = "hashicorp/precise64" end - EOH - - expect { subject.execute }.to raise_error(Vagrant::Errors::ConfigInvalid) { |err| - expect(err.message).to include("The following settings shouldn't exist: bix") - } + VF + end + it "validates the configuration" do + expect { subject.execute }.to raise_error(Vagrant::Errors::ConfigInvalid) { |err| + expect(err.message).to include("The following settings shouldn't exist: bix") + } + end end - it "validates correct Vagrantfile of all vms" do - iso_env.vagrantfile <<-EOH + context "valid configs for multiple vms" do + let(:vagrantfile_content) do + <<-VF Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" @@ -62,17 +76,25 @@ describe VagrantPlugins::CommandValidate::Command do vm.vm.provider :virtualbox end end - EOH + VF + end + it "validates correct Vagrantfile of all vms" do + expect(machine).to receive(:action_raw) do |name, action, env| + expect(name).to eq(:config_validate) + expect(action).to eq(Vagrant::Action::Builtin::ConfigValidate) + expect(env).to eq({}) + end + expect(iso_env.ui).to receive(:info).with(any_args) { |message, _| + expect(message).to include("Vagrantfile validated successfully.") + } - expect(env.ui).to receive(:info).with(any_args) { |message, _| - expect(message).to include("Vagrantfile validated successfully.") - } - - expect(subject.execute).to eq(0) + expect(subject.execute).to eq(0) + end end - it "validates the configuration of all vms" do - iso_env.vagrantfile <<-EOH + context "an invalid config for some vms" do + let(:vagrantfile_content) do + <<-VF Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" @@ -84,15 +106,59 @@ describe VagrantPlugins::CommandValidate::Command do vm.vm.not_provider :virtualbox end end - EOH + VF + end + it "validates the configuration of all vms" do + expect(machine).to receive(:action_raw) do |name, action, env| + expect(name).to eq(:config_validate) + expect(action).to eq(Vagrant::Action::Builtin::ConfigValidate) + expect(env).to eq({}) + end - expect { subject.execute }.to raise_error(Vagrant::Errors::ConfigInvalid) { |err| - expect(err.message).to include("The following settings shouldn't exist: not_provider") - } + expect { subject.execute }.to raise_error(Vagrant::Errors::ConfigInvalid) { |err| + expect(err.message).to include("The following settings shouldn't exist: not_provider") + } + end end - it "throws an exception if there's no Vagrantfile" do - expect { subject.execute }.to raise_error(Vagrant::Errors::NoEnvironmentError) + context "with the ignore provider flag" do + let(:argv) { ["--ignore-provider"]} + let(:vagrantfile_content) do + <<-VF + Vagrant.configure("2") do |config| + config.vm.box = "hashicorp/precise64" + + config.vm.define "test" do |vm| + vm.vm.hostname = "test" + vm.vm.provider :virtualbox do |v| + v.not_a_real_option = true + end + end + end + VF + end + it "ignores provider specific configurations with the flag" do + expect(iso_env.ui).to receive(:info).with(any_args) { |message, _| + expect(message).to include("Vagrantfile validated successfully.") + } + + expect(machine).to receive(:action_raw) do |name, action, env| + expect(name).to eq(:config_validate) + expect(action).to eq(Vagrant::Action::Builtin::ConfigValidate) + expect(env).to eq({:ignore_provider=>true}) + end + + expect(subject.execute).to eq(0) + end + end + + context "no vagrantfile" do + let(:vagrantfile_content){ "" } + let(:env) { isolated_environment.create_vagrant_env } + subject { described_class.new(argv, env) } + it "throws an exception if there's no Vagrantfile" do + expect { subject.execute }.to raise_error(Vagrant::Errors::NoEnvironmentError) + end end end end diff --git a/test/unit/plugins/kernel_v2/config/vm_test.rb b/test/unit/plugins/kernel_v2/config/vm_test.rb index f88d1329c..59c28767c 100644 --- a/test/unit/plugins/kernel_v2/config/vm_test.rb +++ b/test/unit/plugins/kernel_v2/config/vm_test.rb @@ -368,6 +368,20 @@ describe VagrantPlugins::Kernel_V2::VMConfig do expect { subject.finalize! }. to raise_error(Vagrant::Errors::VagrantfileLoadError) end + + it "ignores providers entirely if flag is provided" do + subject.provider "virtualbox" do |vb| + vb.nope = true + end + + subject.provider "virtualbox" do |vb| + vb.not_real = "foo" + end + + subject.finalize! + errors = subject.validate(machine, true) + expect(errors).to eq({"vm"=>[]}) + end end describe "#provision" do diff --git a/test/unit/vagrant/config/v2/root_test.rb b/test/unit/vagrant/config/v2/root_test.rb index 7eb3cc852..1f9b5cf73 100644 --- a/test/unit/vagrant/config/v2/root_test.rb +++ b/test/unit/vagrant/config/v2/root_test.rb @@ -91,6 +91,24 @@ describe Vagrant::Config::V2::Root do expect(instance.validate(env)).to eq(errors) end + context "with vms and ignoring provider validations" do + let(:instance) do + map = { vm: Object, bar: Object } + described_class.new(map) + end + + it "should pass along the ignore_provider flag for ignoring validations" do + errors = { "vm" => ["errors!"] } + env = { "errors" => errors } + vm = instance.vm + def vm.validate(env, param) + env["errors"] + end + + expect(instance.validate({}, true)).to eq({}) + end + end + it "should merge errors via array concat if matching keys" do errors = { "foo" => ["errors!"] } env = { "errors" => errors }