require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/kernel_v2/config/trigger") describe Vagrant::Plugin::V2::Trigger do include_context "unit" let(:iso_env) do # We have to create a Vagrantfile so there is a root path isolated_environment.tap do |env| env.vagrantfile("") end end let(:iso_vagrant_env) { iso_env.create_vagrant_env } let(:state) { double("state", id: :running) } let(:machine) do iso_vagrant_env.machine(iso_vagrant_env.machine_names[0], :dummy).tap do |m| allow(m).to receive(:state).and_return(state) end end let(:env) { { machine: machine, ui: Vagrant::UI::Silent.new, } } let(:triggers) { VagrantPlugins::Kernel_V2::TriggerConfig.new } let(:hash_block) { {info: "hi", run: {inline: "echo 'hi'"}} } let(:hash_block_two) { {warn: "WARNING!!", run_remote: {inline: "echo 'hi'"}} } before do triggers.before(:up, hash_block) triggers.before(:destroy, hash_block) triggers.before(:halt, hash_block_two) triggers.after(:up, hash_block) triggers.after(:destroy, hash_block) triggers.finalize! end let(:subject) { described_class.new(env, triggers, machine) } context "#fire_triggers" do it "raises an error if an inproper stage is given" do expect{ subject.fire_triggers(:up, :not_real, "guest") }. to raise_error(Vagrant::Errors::TriggersNoStageGiven) end end context "#filter_triggers" do it "returns all triggers if no constraints" do before_triggers = triggers.before_triggers filtered_triggers = subject.send(:filter_triggers, before_triggers, "guest") expect(filtered_triggers).to eq(before_triggers) end it "filters a trigger if it doesn't match guest_name" do trigger_config = {info: "no", only_on: "notrealguest"} triggers.after(:up, trigger_config) triggers.finalize! after_triggers = triggers.after_triggers expect(after_triggers.size).to eq(3) subject.send(:filter_triggers, after_triggers, "ubuntu") expect(after_triggers.size).to eq(2) end it "keeps a trigger that has a restraint that matches guest name" do trigger_config = {info: "no", only_on: /guest/} triggers.after(:up, trigger_config) triggers.finalize! after_triggers = triggers.after_triggers expect(after_triggers.size).to eq(3) subject.send(:filter_triggers, after_triggers, "ubuntu-guest") expect(after_triggers.size).to eq(3) end it "keeps a trigger that has multiple restraints that matches guest name" do trigger_config = {info: "no", only_on: ["debian", /guest/]} triggers.after(:up, trigger_config) triggers.finalize! after_triggers = triggers.after_triggers expect(after_triggers.size).to eq(3) subject.send(:filter_triggers, after_triggers, "ubuntu-guest") expect(after_triggers.size).to eq(3) end end context "#fire" do it "calls the corresponding trigger methods if options set" do expect(subject).to receive(:info).twice expect(subject).to receive(:warn).once expect(subject).to receive(:run).twice expect(subject).to receive(:run_remote).once subject.send(:fire, triggers.before_triggers, "guest") end end context "#info" do let(:message) { "Printing some info" } it "prints messages at INFO" do output = "" allow(machine.ui).to receive(:info) do |data| output << data end subject.send(:info, message) expect(output).to include(message) end end context "#warn" do let(:message) { "Printing some warnings" } it "prints messages at WARN" do output = "" allow(machine.ui).to receive(:warn) do |data| output << data end subject.send(:warn, message) expect(output).to include(message) end end context "#run" do let(:trigger_run) { VagrantPlugins::Kernel_V2::TriggerConfig.new } let(:shell_block) { {info: "hi", run: {inline: "echo 'hi'", env: {"KEY"=>"VALUE"}}} } let(:path_block) { {warn: "bye", run: {path: "script.sh", env: {"KEY"=>"VALUE"}}, on_error: :continue} } let(:path_block_ps1) { {warn: "bye", run: {path: "script.ps1", env: {"KEY"=>"VALUE"}}, on_error: :continue} } let(:exit_code) { 0 } let(:options) { {:notify=>[:stdout, :stderr]} } let(:subprocess_result) do double("subprocess_result").tap do |result| allow(result).to receive(:exit_code).and_return(exit_code) allow(result).to receive(:stderr).and_return("") end end before do trigger_run.after(:up, shell_block) trigger_run.before(:destroy, path_block) trigger_run.before(:destroy, path_block_ps1) trigger_run.finalize! end it "executes an inline script with powershell if windows" do allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) allow(Vagrant::Util::PowerShell).to receive(:execute_inline). and_return(subprocess_result) trigger = trigger_run.after_triggers.first shell_config = trigger.run on_error = trigger.on_error expect(Vagrant::Util::PowerShell).to receive(:execute_inline). with("echo", "hi", options) subject.send(:run, shell_config, on_error) end it "executes an path script with powershell if windows" do allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) allow(Vagrant::Util::PowerShell).to receive(:execute). and_return(subprocess_result) allow(env).to receive(:root_path).and_return("/vagrant/home") trigger = trigger_run.before_triggers[1] shell_config = trigger.run on_error = trigger.on_error expect(Vagrant::Util::PowerShell).to receive(:execute). with("/vagrant/home/script.ps1", options) subject.send(:run, shell_config, on_error) end it "executes an inline script" do allow(Vagrant::Util::Subprocess).to receive(:execute). and_return(subprocess_result) trigger = trigger_run.after_triggers.first shell_config = trigger.run on_error = trigger.on_error expect(Vagrant::Util::Subprocess).to receive(:execute). with("echo", "hi", options) subject.send(:run, shell_config, on_error) end it "executes an path script" do allow(Vagrant::Util::Subprocess).to receive(:execute). and_return(subprocess_result) allow(env).to receive(:root_path).and_return("/vagrant/home") allow(FileUtils).to receive(:chmod).and_return(true) trigger = trigger_run.before_triggers.first shell_config = trigger.run on_error = trigger.on_error expect(Vagrant::Util::Subprocess).to receive(:execute). with("/vagrant/home/script.sh", options) subject.send(:run, shell_config, on_error) end it "continues on error" do allow(Vagrant::Util::Subprocess).to receive(:execute). and_raise("Fail!") allow(env).to receive(:root_path).and_return("/vagrant/home") allow(FileUtils).to receive(:chmod).and_return(true) trigger = trigger_run.before_triggers.first shell_config = trigger.run on_error = trigger.on_error expect(Vagrant::Util::Subprocess).to receive(:execute). with("/vagrant/home/script.sh", options) subject.send(:run, shell_config, on_error) end it "halts on error" do allow(Vagrant::Util::Subprocess).to receive(:execute). and_raise("Fail!") trigger = trigger_run.after_triggers.first shell_config = trigger.run on_error = trigger.on_error expect(Vagrant::Util::Subprocess).to receive(:execute). with("echo", "hi", options) expect { subject.send(:run, shell_config, on_error) }.to raise_error("Fail!") end end context "#run_remote" do let (:trigger_run) { VagrantPlugins::Kernel_V2::TriggerConfig.new } let (:shell_block) { {info: "hi", run_remote: {inline: "echo 'hi'", env: {"KEY"=>"VALUE"}}} } let (:path_block) { {warn: "bye", run_remote: {path: "script.sh", env: {"KEY"=>"VALUE"}}, on_error: :continue} } let(:provision) { double("provision") } before do trigger_run.after(:up, shell_block) trigger_run.before(:destroy, path_block) trigger_run.finalize! end it "raises an error and halts if guest is not running" do allow(machine.state).to receive(:id).and_return(:not_running) trigger = trigger_run.after_triggers.first shell_config = trigger.run_remote on_error = trigger.on_error expect { subject.send(:run_remote, shell_config, on_error) }. to raise_error(Vagrant::Errors::TriggersGuestNotRunning) end it "continues on if guest is not running but is configured to continue on error" do allow(machine.state).to receive(:id).and_return(:not_running) allow(env).to receive(:root_path).and_return("/vagrant/home") allow(FileUtils).to receive(:chmod).and_return(true) trigger = trigger_run.before_triggers.first shell_config = trigger.run_remote on_error = trigger.on_error subject.send(:run_remote, shell_config, on_error) end it "calls the provision function on the shell provisioner" do allow(machine.state).to receive(:id).and_return(:running) allow(provision).to receive(:provision).and_return("Provision!") allow(VagrantPlugins::Shell::Provisioner).to receive(:new). and_return(provision) trigger = trigger_run.after_triggers.first shell_config = trigger.run_remote on_error = trigger.on_error subject.send(:run_remote, shell_config, on_error) end it "continues on if provision fails" do allow(machine.state).to receive(:id).and_return(:running) allow(provision).to receive(:provision).and_raise("Nope!") allow(VagrantPlugins::Shell::Provisioner).to receive(:new). and_return(provision) trigger = trigger_run.before_triggers.first shell_config = trigger.run_remote on_error = trigger.on_error subject.send(:run_remote, shell_config, on_error) end it "fails if it encounters an error" do allow(machine.state).to receive(:id).and_return(:running) allow(provision).to receive(:provision).and_raise("Nope!") allow(VagrantPlugins::Shell::Provisioner).to receive(:new). and_return(provision) trigger = trigger_run.after_triggers.first shell_config = trigger.run_remote on_error = trigger.on_error expect { subject.send(:run_remote, shell_config, on_error) }. to raise_error("Nope!") end end end