Added WinRM command filters

These will be used to replace the guest side command_alias script that is sent with every communicator execute call. This avoids some uncessary remote calls to the guest, makes it unit testable, and allows larger PowerShell commands to be invoked.
This commit is contained in:
Shawn Neal 2014-04-23 17:50:20 -07:00
parent fdb6461af4
commit 96ab8f60c5
9 changed files with 308 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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