Paul Hinze 142b1af0cc
Fix local exec pushes in server mode
The local-exec push strategy was assuming it was running from a CLI and
so it wouldn't be a big deal for it to straight up `exec` and replace
its running with the user command. That command will just do its thing
and we want the exit code for the CLI command to match anyways, right?

Sure that works for a shell, but in a GRPC server setting it's decidedly
Not Cool to suddenly swap out the running process!

As you can imagine - the effect of doing this was all sorts of broken
pipes and unexpected EOFs and a very confused @phinze.

Luckily we had a subprocess strategy sitting right there for Windows
compat, so it was just a matter of switching to that in the server
context as well. Long and winding debugging process; simple fix;
just another classic!
2022-04-25 12:26:17 -05:00

134 lines
3.9 KiB
Ruby

require_relative "../../../base"
require Vagrant.source_root.join("plugins/pushes/local-exec/push")
describe VagrantPlugins::LocalExecPush::Push do
include_context "unit"
before(:all) do
I18n.load_path << Vagrant.source_root.join("plugins/pushes/local-exec/locales/en.yml")
I18n.reload!
end
let(:env) { isolated_environment }
let(:config) do
double("config",
script: nil,
inline: nil,
args: "some args",
)
end
subject { described_class.new(env, config) }
before do
allow(env).to receive(:root_path)
.and_return(File.expand_path("..", __FILE__))
end
describe "#push" do
before do
allow(subject).to receive(:execute_inline!)
allow(subject).to receive(:execute_script!)
allow(subject).to receive(:execute!)
end
context "when inline is given" do
before { allow(config).to receive(:inline).and_return("echo") }
it "executes the inline script" do
expect(subject).to receive(:execute_inline!)
.with(config.inline, config.args)
subject.push
end
end
context "when script is given" do
before { allow(config).to receive(:script).and_return("foo.sh") }
it "executes the script" do
expect(subject).to receive(:execute_script!)
.with(config.script, config.args)
subject.push
end
end
end
describe "#execute_inline!" do
before { allow(subject).to receive(:execute_script!) }
it "writes the script to a tempfile" do
expect(Tempfile).to receive(:new).and_call_original
subject.execute_inline!("echo", config.args)
end
it "executes the script" do
expect(subject).to receive(:execute_script!)
subject.execute_inline!("echo", config.args)
end
end
describe "#execute_script!" do
before do
allow(subject).to receive(:execute!)
allow(FileUtils).to receive(:chmod)
end
it "expands the path relative to the machine root" do
expect(subject).to receive(:execute!)
.with(File.expand_path("foo.sh", env.root_path))
subject.execute_script!("./foo.sh", nil)
end
it "makes the file executable" do
expect(FileUtils).to receive(:chmod)
.with("+x", File.expand_path("foo.sh", env.root_path))
subject.execute_script!("./foo.sh", config.args)
end
it "calls execute!" do
expect(subject).to receive(:execute!)
.with(File.expand_path("foo.sh", env.root_path))
subject.execute_script!("./foo.sh", nil)
end
context "when args is given" do
it "passes string args to execute!" do
expect(subject).to receive(:execute!)
.with(File.expand_path("foo.sh", env.root_path) + " " + config.args)
subject.execute_script!("./foo.sh", config.args)
end
it "passes array args as string to execute!" do
expect(subject).to receive(:execute!)
.with(File.expand_path("foo.sh", env.root_path) + " \"one\" \"two\" \"three\"")
subject.execute_script!("./foo.sh", ["one", "two", "three"])
end
end
end
describe "#execute!" do
it "uses exec on unix" do
allow(Vagrant::Util::Platform).to receive(:windows?).and_return(false)
expect(Vagrant::Util::SafeExec).to receive(:exec)
expect { subject.execute! }.to_not raise_error
end
it "uses subprocess on windows" do
allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true)
result = double("result", exit_code: 0)
expect(Vagrant::Util::Subprocess).to receive(:execute).and_return(result)
expect { subject.execute! }.to raise_error { |e|
expect(e).to be_a(SystemExit)
}
end
it "uses subprocess when running in server mode, and does not exit" do
allow(Vagrant).to receive(:server_mode?).and_return(true)
result = double("result", exit_code: 0)
expect(Vagrant::Util::Subprocess).to receive(:execute).and_return(result)
expect { subject.execute! }.to_not raise_error
end
end
end