diff --git a/plugins/communicators/winrm/command_filter.rb b/plugins/communicators/winrm/command_filter.rb new file mode 100644 index 000000000..dcae3d1fc --- /dev/null +++ b/plugins/communicators/winrm/command_filter.rb @@ -0,0 +1,47 @@ +module VagrantPlugins + module CommunicatorWinRM + + # Handles loading and applying all available WinRM command filters + class CommandFilter + + @@cmd_filters = [ + "cat", + "chmod", + "chown", + "rm", + "test", + "uname", + "which" + ] + + # Filter the given Vagrant command to ensure compatibility with Windows + # + # @param [String] The Vagrant shell command + # @returns [String] Windows runnable command or empty string + def filter(command) + command_filters.each { |c| command = c.filter(command) if c.accept?(command) } + command + end + + # All the available Linux command filters + # + # @returns [Array] All Linux command filter instances + def command_filters + @command_filters ||= create_command_filters() + end + + private + + def create_command_filters + [].tap do |filters| + @@cmd_filters.each do |cmd| + require_relative "command_filters/#{cmd}" + class_name = "VagrantPlugins::CommunicatorWinRM::CommandFilters::#{cmd.capitalize}" + filters << Module.const_get(class_name).new + end + end + end + + end + end +end diff --git a/plugins/communicators/winrm/command_filters/cat.rb b/plugins/communicators/winrm/command_filters/cat.rb new file mode 100644 index 000000000..5d9cf8e01 --- /dev/null +++ b/plugins/communicators/winrm/command_filters/cat.rb @@ -0,0 +1,27 @@ +module VagrantPlugins + module CommunicatorWinRM + module CommandFilters + + # Handles the special case of determining the guest OS using cat + class Cat + + def filter(command) + # cat /etc/release | grep -i OmniOS + # cat /etc/redhat-release + # cat /etc/issue | grep 'Core Linux' + # cat /etc/release | grep -i SmartOS + '' + end + + def accept?(command) + # cat works in PowerShell, however we don't want to run Guest + # OS detection as this will fail on Windows because the lack of the + # grep command + command.start_with?('cat /etc/') + end + + end + + end + end +end diff --git a/plugins/communicators/winrm/command_filters/chmod.rb b/plugins/communicators/winrm/command_filters/chmod.rb new file mode 100644 index 000000000..a336f5dd4 --- /dev/null +++ b/plugins/communicators/winrm/command_filters/chmod.rb @@ -0,0 +1,21 @@ +module VagrantPlugins + module CommunicatorWinRM + module CommandFilters + + # Converts a *nix 'chmod' command to a PowerShell equivalent + class Chmod + + def filter(command) + # Not support on Windows, the communicator will skip this command + '' + end + + def accept?(command) + command.start_with?('chmod ') + end + + end + + end + end +end diff --git a/plugins/communicators/winrm/command_filters/chown.rb b/plugins/communicators/winrm/command_filters/chown.rb new file mode 100644 index 000000000..127731ff6 --- /dev/null +++ b/plugins/communicators/winrm/command_filters/chown.rb @@ -0,0 +1,21 @@ +module VagrantPlugins + module CommunicatorWinRM + module CommandFilters + + # Converts a *nix 'chown' command to a PowerShell equivalent + class Chown + + def filter(command) + # Not support on Windows, the communicator will skip this command + '' + end + + def accept?(command) + command.start_with?('chown ') + end + + end + + end + end +end diff --git a/plugins/communicators/winrm/command_filters/rm.rb b/plugins/communicators/winrm/command_filters/rm.rb new file mode 100644 index 000000000..a0078c83d --- /dev/null +++ b/plugins/communicators/winrm/command_filters/rm.rb @@ -0,0 +1,28 @@ +module VagrantPlugins + module CommunicatorWinRM + module CommandFilters + + # Converts a *nix 'rm' command to a PowerShell equivalent + class Rm + + def filter(command) + # rm -Rf /some/dir + # rm /some/dir + cmd_parts = command.strip.split(/\s+/) + dir = cmd_parts[1] + if dir == '-Rf' + dir = cmd_parts[2] + return "rm '#{dir}' -recurse -force" + end + return "rm '#{dir}' -force" + end + + def accept?(command) + command.start_with?('rm ') + end + + end + + end + end +end diff --git a/plugins/communicators/winrm/command_filters/test.rb b/plugins/communicators/winrm/command_filters/test.rb new file mode 100644 index 000000000..d837ece0c --- /dev/null +++ b/plugins/communicators/winrm/command_filters/test.rb @@ -0,0 +1,37 @@ +module VagrantPlugins + module CommunicatorWinRM + module CommandFilters + + # Converts a *nix 'test' command to a PowerShell equivalent + class Test + + def filter(command) + # test -d /tmp/dir + # test -f /tmp/afile + # test -L /somelink + # test -x /tmp/some.exe + + cmd_parts = command.strip.split(/\s+/) + if cmd_parts[1] == '-d' + # ensure it exists and is a directory + return "if ((Test-Path '#{cmd_parts[2]}') -and (get-item '#{cmd_parts[2]}').PSIsContainer) { exit 0 } exit 1" + elsif cmd_parts[1] == '-f' || cmd_parts[1] == '-x' + # ensure it exists and is a file + return "if ((Test-Path '#{cmd_parts[2]}') -and (!(get-item '#{cmd_parts[2]}').PSIsContainer)) { exit 0 } exit 1" + end + + # otherwise, just check for existence + return "if (Test-Path '#{cmd_parts[2]}') { exit 0 } exit 1" + end + + # if (Test-Path 'c:\windows' && (get-item 'c:\windows').PSIsContainer) { Write-Host 0 } Write-Host 1 + + def accept?(command) + command.start_with?('test ') + end + + end + + end + end +end diff --git a/plugins/communicators/winrm/command_filters/uname.rb b/plugins/communicators/winrm/command_filters/uname.rb new file mode 100644 index 000000000..4a8b0836d --- /dev/null +++ b/plugins/communicators/winrm/command_filters/uname.rb @@ -0,0 +1,31 @@ +module VagrantPlugins + module CommunicatorWinRM + module CommandFilters + + # Converts a *nix 'uname' command to a PowerShell equivalent + class Uname + + def filter(command) + # uname -s | grep 'Darwin' + # uname -s | grep VMkernel + # uname -s | grep 'FreeBSD' + # uname -s | grep 'Linux' + # uname -s | grep NetBSD + # uname -s | grep 'OpenBSD' + # uname -sr | grep SunOS | grep -v 5.11 + # uname -sr | grep 'SunOS 5.11' + + # uname is used to detect the guest type in Vagrant, so don't bother running + # to speed up OS detection + '' + end + + def accept?(command) + command.start_with?('uname ') + end + + end + + end + end +end diff --git a/plugins/communicators/winrm/command_filters/which.rb b/plugins/communicators/winrm/command_filters/which.rb new file mode 100644 index 000000000..cf046ca19 --- /dev/null +++ b/plugins/communicators/winrm/command_filters/which.rb @@ -0,0 +1,26 @@ +module VagrantPlugins + module CommunicatorWinRM + module CommandFilters + + # Converts a *nix 'which' command to a PowerShell equivalent + class Which + + def filter(command) + executable = command.strip.split(/\s+/)[1] + return <<-EOH + $command = [Array](Get-Command #{executable} -errorAction SilentlyContinue) + if ($null -eq $command) { exit 1 } + write-host $command[0].Definition + exit 0 + EOH + end + + def accept?(command) + command.start_with?('which ') + end + + end + + end + end +end diff --git a/test/unit/plugins/communicators/winrm/command_filter_test.rb b/test/unit/plugins/communicators/winrm/command_filter_test.rb new file mode 100644 index 000000000..71010154d --- /dev/null +++ b/test/unit/plugins/communicators/winrm/command_filter_test.rb @@ -0,0 +1,70 @@ +require File.expand_path("../../../../base", __FILE__) + +require Vagrant.source_root.join("plugins/communicators/winrm/command_filter") + +describe VagrantPlugins::CommunicatorWinRM::CommandFilter, :unit => true do + + describe '.command_filters' do + it 'initializes all command filters in command filters directory' do + expect(subject.command_filters()).not_to be_empty + end + end + + describe '.filter' do + it 'filters out uname commands' do + expect(subject.filter('uname -s stuff')).to eq('') + end + + it 'filters out which commands' do + expect(subject.filter('which ruby')).to include( + '[Array](Get-Command ruby -errorAction SilentlyContinue)') + end + + it 'filters out test -d commands' do + expect(subject.filter('test -d /tmp/dir')).to eq( + "if ((Test-Path '/tmp/dir') -and (get-item '/tmp/dir').PSIsContainer) { exit 0 } exit 1") + end + + it 'filters out test -f commands' do + expect(subject.filter('test -f /tmp/file.txt')).to eq( + "if ((Test-Path '/tmp/file.txt') -and (!(get-item '/tmp/file.txt').PSIsContainer)) { exit 0 } exit 1") + end + + it 'filters out test -x commands' do + expect(subject.filter('test -x /tmp/file.txt')).to eq( + "if ((Test-Path '/tmp/file.txt') -and (!(get-item '/tmp/file.txt').PSIsContainer)) { exit 0 } exit 1") + end + + it 'filters out other test commands' do + expect(subject.filter('test -L /tmp/file.txt')).to eq( + "if (Test-Path '/tmp/file.txt') { exit 0 } exit 1") + end + + it 'filters out rm -Rf commands' do + expect(subject.filter('rm -Rf /some/dir')).to eq( + "rm '/some/dir' -recurse -force") + end + + it 'filters out rm commands' do + expect(subject.filter('rm /some/dir')).to eq( + "rm '/some/dir' -force") + end + + it 'filters out chown commands' do + expect(subject.filter("chown -R root '/tmp/dir'")).to eq('') + end + + it 'filters out chmod commands' do + expect(subject.filter("chmod 0600 ~/.ssh/authorized_keys")).to eq('') + end + + it 'filters out certain cat commands' do + expect(subject.filter("cat /etc/release | grep -i OmniOS")).to eq('') + end + + it 'should not filter out other cat commands' do + expect(subject.filter("cat /tmp/somefile")).to eq('cat /tmp/somefile') + end + end + +end