From 2c4a40fccbcd1869c7ea457d6f251c67c30eda71 Mon Sep 17 00:00:00 2001 From: Jeff Bonhag Date: Fri, 24 Apr 2020 15:56:27 -0400 Subject: [PATCH] Fix issues with shell provisioner when WinSSH shell is set to cmd (#11547) This commit fixes a couple of issues with the shell provisioner when the WinSSH shell is set to cmd: - A check for the .bat extension returned by File.extname - Execute inline scripts with PowerShell when upload_path ends with .ps1 * Ensure script has correct extension Co-Authored-By: Sophia Castellarin --- plugins/provisioners/shell/provisioner.rb | 11 +- .../provisioners/shell/provisioner_test.rb | 119 ++++++++++++------ 2 files changed, 87 insertions(+), 43 deletions(-) diff --git a/plugins/provisioners/shell/provisioner.rb b/plugins/provisioners/shell/provisioner.rb index d148969a6..e44f3d886 100644 --- a/plugins/provisioners/shell/provisioner.rb +++ b/plugins/provisioners/shell/provisioner.rb @@ -137,13 +137,18 @@ module VagrantPlugins # Upload the script to the machine @machine.communicate.tap do |comm| env = config.env.map{|k,v| comm.generate_environment_export(k, v)}.join(';') - remote_ext = File.extname(path) + + remote_ext = File.extname(upload_path.to_s) if remote_ext.empty? - remote_ext = @machine.config.winssh.shell == "cmd" ? ".bat" : ".ps1" + remote_ext = File.extname(path.to_s) + if remote_ext.empty? + remote_ext = @machine.config.winssh.shell == "cmd" ? ".bat" : ".ps1" + end end remote_path = add_extension(upload_path, remote_ext) - if remote_ext == "bat" + + if remote_ext == ".bat" command = "#{env}\n cmd.exe /c \"#{remote_path}\" #{args}" else # Copy powershell_args from configuration diff --git a/test/unit/plugins/provisioners/shell/provisioner_test.rb b/test/unit/plugins/provisioners/shell/provisioner_test.rb index 73dc3ca70..125ed0efa 100644 --- a/test/unit/plugins/provisioners/shell/provisioner_test.rb +++ b/test/unit/plugins/provisioners/shell/provisioner_test.rb @@ -543,53 +543,92 @@ describe "Vagrant::Shell::Provisioner" do allow(machine).to receive(:ssh_info).and_return(true) } - it "ensures that files are uploaded as .bat when shell is cmd" do - allow(machine).to receive_message_chain(:config, :winssh, :shell).and_return("cmd") - allow(vsp).to receive(:with_script_file).and_yield(default_win_path) - expect(communicator).to receive(:upload).with(default_win_path, /arbitrary.bat/) - expect(communicator).to receive(:execute).with(/arbitrary.bat/, anything) - vsp.send(:provision_winssh, "") - end - - it "ensures that files are uploaded as .ps1 when shell is not cmd" do - allow(machine).to receive_message_chain(:config, :winssh, :shell).and_return("ps") - allow(vsp).to receive(:with_script_file).and_yield(default_win_path) - expect(communicator).to receive(:upload).with(default_win_path, /arbitrary.ps1/) - expect(communicator).to receive(:execute).with(/arbitrary.ps1/, anything) - vsp.send(:provision_winssh, "") - end - context "ps1 file being uploaded" do - let(:config) { - double( - :config, - :args => "doesn't matter", - :env => {}, - :upload_path => "arbitrary", - :remote? => false, - :path => "script/info.ps1", - :binary => false, - :md5 => nil, - :sha1 => 'EXPECTED_VALUE', - :sha256 => nil, - :sha384 => nil, - :sha512 => nil, - :reset => false, - :reboot => false, - :powershell_args => "", - :name => nil, - :privileged => false, - :powershell_elevated_interactive => false - ) - } + before do + allow(config).to receive(:path).and_return("script/info.ps1") + allow(vsp).to receive(:with_script_file).and_yield(config.path) + end it "ensures that files are uploaded same extension as provided path.ps1" do allow(machine).to receive_message_chain(:config, :winssh, :shell).and_return("cmd") - allow(vsp).to receive(:with_script_file).and_yield(config.path) expect(communicator).to receive(:upload).with(config.path, /arbitrary.ps1/) - expect(communicator).to receive(:execute).with(/arbitrary.ps1/, anything) + expect(communicator).to receive(:execute).with(/powershell.*arbitrary.ps1/, anything) vsp.send(:provision_winssh, "") end end + + context "bat file being uploaded" do + before do + allow(config).to receive(:path).and_return("script/info.bat") + allow(vsp).to receive(:with_script_file).and_yield(config.path) + end + + it "ensures that files are uploaded same extension as provided path.bat" do + allow(machine).to receive_message_chain(:config, :winssh, :shell).and_return("cmd") + expect(communicator).to receive(:upload).with(config.path, /arbitrary.bat/) + expect(communicator).to receive(:execute).with(/cmd.*arbitrary.bat/, anything) + vsp.send(:provision_winssh, "") + end + end + + context "with inline script" do + before do + allow(vsp).to receive(:with_script_file).and_yield("/tmp/file/contents") + end + + context "when upload path has a .ps1 extension" do + before do + allow(config).to receive(:upload_path).and_return("c:/tmp/vagrant-shell.ps1") + end + + it "executes the remote script with powershell" do + expect(communicator).to receive(:upload).with(anything, config.upload_path) + expect(communicator).to receive(:execute).with(/powershell.*\.ps1/, anything) + vsp.send(:provision_winssh, "") + end + end + + context "when upload path has a .bat extension" do + before do + allow(config).to receive(:upload_path).and_return("c:/tmp/vagrant-shell.bat") + end + + it "executes the remote script with cmd" do + expect(communicator).to receive(:upload).with(anything, config.upload_path) + expect(communicator).to receive(:execute).with(/cmd.*\.bat/, anything) + vsp.send(:provision_winssh, "") + end + end + + context "when upload path has no extension" do + before do + allow(config).to receive(:upload_path).and_return("c:/tmp/vagrant-shell") + end + + context "when winssh shell is cmd" do + before do + allow(machine).to receive_message_chain(:config, :winssh, :shell).and_return("cmd") + end + + it "adds an extension and executes the remote script with cmd" do + expect(communicator).to receive(:upload).with(anything, /\.bat$/) + expect(communicator).to receive(:execute).with(/cmd.*\.bat/, anything) + vsp.send(:provision_winssh, "") + end + end + + context "when winssh shell is powershell" do + before do + allow(machine).to receive_message_chain(:config, :winssh, :shell).and_return("powershell") + end + + it "adds an extension executes the remote script with powershell" do + expect(communicator).to receive(:upload).with(anything, /\.ps1$/) + expect(communicator).to receive(:execute).with(/powershell.*\.ps1/, anything) + vsp.send(:provision_winssh, "") + end + end + end + end end end