vagrant-windows 1.6.0 → 1.7.0.pre.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/CHANGELOG.md +7 -1
- data/Gemfile +5 -1
- data/README.md +3 -1
- data/lib/vagrant-windows/communication/command_filters/cat.rb +27 -0
- data/lib/vagrant-windows/communication/command_filters/chmod.rb +21 -0
- data/lib/vagrant-windows/communication/command_filters/chown.rb +21 -0
- data/lib/vagrant-windows/communication/command_filters/rm.rb +28 -0
- data/lib/vagrant-windows/communication/command_filters/test.rb +37 -0
- data/lib/vagrant-windows/communication/command_filters/uname.rb +36 -0
- data/lib/vagrant-windows/communication/command_filters/which.rb +26 -0
- data/lib/vagrant-windows/communication/guestnetwork.rb +16 -19
- data/lib/vagrant-windows/communication/linux_command_filter.rb +44 -0
- data/lib/vagrant-windows/communication/winrmcommunicator.rb +21 -24
- data/lib/vagrant-windows/communication/winrmfilemanager.rb +166 -0
- data/lib/vagrant-windows/communication/winrmshell.rb +8 -38
- data/lib/vagrant-windows/config/windows.rb +5 -5
- data/lib/vagrant-windows/errors.rb +5 -1
- data/lib/vagrant-windows/guest/cap/configure_networks.rb +1 -1
- data/lib/vagrant-windows/monkey_patches/plugins/provisioners/chef/provisioner/chef_client.rb +2 -5
- data/lib/vagrant-windows/monkey_patches/plugins/provisioners/chef/provisioner/chef_solo.rb +2 -5
- data/lib/vagrant-windows/monkey_patches/plugins/provisioners/shell/provisioner.rb +13 -6
- data/lib/vagrant-windows/provisioners/chef_command_builder.rb +7 -53
- data/lib/vagrant-windows/scripts/elevated_shell.ps1.erb +95 -0
- data/lib/vagrant-windows/version.rb +1 -1
- data/locales/en.yml +7 -0
- data/spec/vagrant-windows/chef_command_builder_spec.rb +7 -53
- data/spec/vagrant-windows/guestnetwork_spec.rb +10 -5
- data/spec/vagrant-windows/linux_command_filter_spec.rb +73 -0
- data/spec/vagrant-windows/winrmcommunicator_spec.rb +55 -6
- data/spec/vagrant-windows/winrmshell_spec.rb +12 -5
- metadata +16 -8
- data/lib/vagrant-windows/scripts/cheftask.ps1.erb +0 -48
- data/lib/vagrant-windows/scripts/cheftask.xml.erb +0 -45
- data/lib/vagrant-windows/scripts/cheftaskrun.ps1.erb +0 -18
- data/lib/vagrant-windows/scripts/command_alias.ps1 +0 -36
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 668a6e4a7bc645fa0e951e3f04a33257e4eb5890
|
|
4
|
+
data.tar.gz: e080e229467c6a45f6d3ae5a180e1c8b4ed92528
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 77c523c5c5bd2a464cb4cabc5ce613f31ec4ab7f76c8765fffdc79fffd9175b90dfd59cd19cac4cdc4c3527cf5ba7392b7933e29cbcfaed50702b998c81761e2
|
|
7
|
+
data.tar.gz: d2b1f5f4198cdaa1e3b88480b95e69d8af18ce4f01d55836ee7a361e488a4168e821b7b9d336857b8165dca7163b6b0e57e0c63483115d7881dc0076cec33836
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -76,4 +76,10 @@
|
|
|
76
76
|
|
|
77
77
|
1.6.0
|
|
78
78
|
- Added chef-client provisioner support and extracted shared code from chef-solo provisioner
|
|
79
|
-
- WinRM communicator download support
|
|
79
|
+
- WinRM communicator download support
|
|
80
|
+
|
|
81
|
+
1.7.0
|
|
82
|
+
- Added recursive directory upload on communicator, similar to core Vagrant SCP functionality.
|
|
83
|
+
- Redesigned elevated command execution in Chef to make it reusable
|
|
84
|
+
- Shell provisioner now runs elevated by default unless privileged is false
|
|
85
|
+
- Removed *nix command alias PS script and created extensible linux command filter system
|
data/Gemfile
CHANGED
|
@@ -7,6 +7,10 @@ group :development do
|
|
|
7
7
|
# We depend on Vagrant for development, but we don't add it as a
|
|
8
8
|
# gem dependency because we expect to be installed within the
|
|
9
9
|
# Vagrant environment itself using `vagrant plugin`.
|
|
10
|
-
gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git", :tag => 'v1.
|
|
10
|
+
gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git", :tag => 'v1.5.2'
|
|
11
11
|
gem "mocha", :require => false
|
|
12
12
|
end
|
|
13
|
+
|
|
14
|
+
group :plugins do
|
|
15
|
+
gem "vagrant-windows", path: '.'
|
|
16
|
+
end
|
data/README.md
CHANGED
|
@@ -13,7 +13,9 @@ This [Vagrant](http://www.vagrantup.com/) plugin allows you to standup Windows g
|
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Vagrant 1.6 will natively support Windows guests, this plugin should not be used with Vagrant 1.6 or later.
|
|
17
|
+
|
|
18
|
+
- For Vagrant 1.1 -> 1.5 execute `vagrant plugin install vagrant-windows`.
|
|
17
19
|
- For Vagrant 1.0 execute `vagrant plugin install vagrant-windows --plugin-version 0.1.2`.
|
|
18
20
|
|
|
19
21
|
## Creating a Base Box
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module VagrantWindows
|
|
2
|
+
module Communication
|
|
3
|
+
module CommandFilters
|
|
4
|
+
|
|
5
|
+
# Handles the special case of determining the guest OS using cat
|
|
6
|
+
class Cat
|
|
7
|
+
|
|
8
|
+
def filter(command)
|
|
9
|
+
# cat /etc/release | grep -i OmniOS
|
|
10
|
+
# cat /etc/redhat-release
|
|
11
|
+
# cat /etc/issue | grep 'Core Linux'
|
|
12
|
+
# cat /etc/release | grep -i SmartOS
|
|
13
|
+
''
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def accept?(command)
|
|
17
|
+
# cat works in PowerShell, however we don't want to run Guest
|
|
18
|
+
# OS detection as this will fail on Windows because the lack of the
|
|
19
|
+
# grep command
|
|
20
|
+
command.start_with?('cat /etc/')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module VagrantWindows
|
|
2
|
+
module Communication
|
|
3
|
+
module CommandFilters
|
|
4
|
+
|
|
5
|
+
# Converts a *nix 'chmod' command to a PowerShell equivalent
|
|
6
|
+
class Chmod
|
|
7
|
+
|
|
8
|
+
def filter(command)
|
|
9
|
+
# Not support on Windows, the communicator will skip this command
|
|
10
|
+
''
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def accept?(command)
|
|
14
|
+
command.start_with?('chmod ')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module VagrantWindows
|
|
2
|
+
module Communication
|
|
3
|
+
module CommandFilters
|
|
4
|
+
|
|
5
|
+
# Converts a *nix 'chown' command to a PowerShell equivalent
|
|
6
|
+
class Chown
|
|
7
|
+
|
|
8
|
+
def filter(command)
|
|
9
|
+
# Not support on Windows, the communicator will skip this command
|
|
10
|
+
''
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def accept?(command)
|
|
14
|
+
command.start_with?('chown ')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module VagrantWindows
|
|
2
|
+
module Communication
|
|
3
|
+
module CommandFilters
|
|
4
|
+
|
|
5
|
+
# Converts a *nix 'rm' command to a PowerShell equivalent
|
|
6
|
+
class Rm
|
|
7
|
+
|
|
8
|
+
def filter(command)
|
|
9
|
+
# rm -Rf /some/dir
|
|
10
|
+
# rm /some/dir
|
|
11
|
+
cmd_parts = command.strip.split(/\s+/)
|
|
12
|
+
dir = cmd_parts[1]
|
|
13
|
+
if dir == '-Rf'
|
|
14
|
+
dir = cmd_parts[2]
|
|
15
|
+
return "rm '#{dir}' -recurse -force"
|
|
16
|
+
end
|
|
17
|
+
return "rm '#{dir}' -force"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def accept?(command)
|
|
21
|
+
command.start_with?('rm ')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module VagrantWindows
|
|
2
|
+
module Communication
|
|
3
|
+
module CommandFilters
|
|
4
|
+
|
|
5
|
+
# Converts a *nix 'test' command to a PowerShell equivalent
|
|
6
|
+
class Test
|
|
7
|
+
|
|
8
|
+
def filter(command)
|
|
9
|
+
# test -d /tmp/dir
|
|
10
|
+
# test -f /tmp/afile
|
|
11
|
+
# test -L /somelink
|
|
12
|
+
# test -x /tmp/some.exe
|
|
13
|
+
|
|
14
|
+
cmd_parts = command.strip.split(/\s+/)
|
|
15
|
+
if cmd_parts[1] == '-d'
|
|
16
|
+
# ensure it exists and is a directory
|
|
17
|
+
return "if ((Test-Path '#{cmd_parts[2]}') -and (get-item '#{cmd_parts[2]}').PSIsContainer) { exit 0 } exit 1"
|
|
18
|
+
elsif cmd_parts[1] == '-f' || cmd_parts[1] == '-x'
|
|
19
|
+
# ensure it exists and is a file
|
|
20
|
+
return "if ((Test-Path '#{cmd_parts[2]}') -and (!(get-item '#{cmd_parts[2]}').PSIsContainer)) { exit 0 } exit 1"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# otherwise, just check for existence
|
|
24
|
+
return "if (Test-Path '#{cmd_parts[2]}') { exit 0 } exit 1"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# if (Test-Path 'c:\windows' && (get-item 'c:\windows').PSIsContainer) { Write-Host 0 } Write-Host 1
|
|
28
|
+
|
|
29
|
+
def accept?(command)
|
|
30
|
+
command.start_with?('test ')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# cat /etc/release | grep -i OmniOS
|
|
2
|
+
# cat /etc/redhat-release
|
|
3
|
+
# cat /etc/issue | grep 'Core Linux'
|
|
4
|
+
# cat /etc/release | grep -i SmartOS
|
|
5
|
+
|
|
6
|
+
module VagrantWindows
|
|
7
|
+
module Communication
|
|
8
|
+
module CommandFilters
|
|
9
|
+
|
|
10
|
+
# Converts a *nix 'uname' command to a PowerShell equivalent
|
|
11
|
+
class Uname
|
|
12
|
+
|
|
13
|
+
def filter(command)
|
|
14
|
+
# uname -s | grep 'Darwin'
|
|
15
|
+
# uname -s | grep VMkernel
|
|
16
|
+
# uname -s | grep 'FreeBSD'
|
|
17
|
+
# uname -s | grep 'Linux'
|
|
18
|
+
# uname -s | grep NetBSD
|
|
19
|
+
# uname -s | grep 'OpenBSD'
|
|
20
|
+
# uname -sr | grep SunOS | grep -v 5.11
|
|
21
|
+
# uname -sr | grep 'SunOS 5.11'
|
|
22
|
+
|
|
23
|
+
# uname is used to detect the guest type in Vagrant, so don't bother running
|
|
24
|
+
# to speed up OS detection
|
|
25
|
+
''
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def accept?(command)
|
|
29
|
+
command.start_with?('uname ')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module VagrantWindows
|
|
2
|
+
module Communication
|
|
3
|
+
module CommandFilters
|
|
4
|
+
|
|
5
|
+
# Converts a *nix 'which' command to a PowerShell equivalent
|
|
6
|
+
class Which
|
|
7
|
+
|
|
8
|
+
def filter(command)
|
|
9
|
+
executable = command.strip.split(/\s+/)[1]
|
|
10
|
+
return <<-EOH
|
|
11
|
+
$command = [Array](Get-Command #{executable} -errorAction SilentlyContinue)
|
|
12
|
+
if ($null -eq $command) { exit 1 }
|
|
13
|
+
write-host $command[0].Definition
|
|
14
|
+
exit 0
|
|
15
|
+
EOH
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def accept?(command)
|
|
19
|
+
command.start_with?('which ')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -12,13 +12,10 @@ module VagrantWindows
|
|
|
12
12
|
PS_GET_WSMAN_VER = '((test-wsman).productversion.split(" ") | select -last 1).split("\.")[0]'
|
|
13
13
|
WQL_NET_ADAPTERS_V2 = 'SELECT * FROM Win32_NetworkAdapter WHERE MACAddress IS NOT NULL'
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def initialize(winrmshell)
|
|
19
|
-
@logger = Log4r::Logger.new("vagrant_windows::communication::winrmshell")
|
|
15
|
+
def initialize(communicator)
|
|
16
|
+
@logger = Log4r::Logger.new("vagrant_windows::communication::guestnetwork")
|
|
20
17
|
@logger.debug("initializing WinRMShell")
|
|
21
|
-
@
|
|
18
|
+
@communicator = communicator
|
|
22
19
|
end
|
|
23
20
|
|
|
24
21
|
# Returns an array of all NICs on the guest. Each array entry is a
|
|
@@ -33,13 +30,13 @@ module VagrantWindows
|
|
|
33
30
|
#
|
|
34
31
|
# @return [Boolean]
|
|
35
32
|
def is_dhcp_enabled(nic_index)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
cmd = <<-EOH
|
|
34
|
+
if (Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "Index=#{nic_index} and DHCPEnabled=True") {
|
|
35
|
+
exit 0
|
|
36
|
+
}
|
|
37
|
+
exit 1
|
|
38
|
+
EOH
|
|
39
|
+
@communicator.test(cmd)
|
|
43
40
|
end
|
|
44
41
|
|
|
45
42
|
# Configures the specified interface for DHCP
|
|
@@ -50,7 +47,7 @@ module VagrantWindows
|
|
|
50
47
|
@logger.info("Configuring NIC #{net_connection_id} for DHCP")
|
|
51
48
|
if !is_dhcp_enabled(nic_index)
|
|
52
49
|
netsh = "netsh interface ip set address \"#{net_connection_id}\" dhcp"
|
|
53
|
-
@
|
|
50
|
+
@communicator.execute(netsh)
|
|
54
51
|
end
|
|
55
52
|
end
|
|
56
53
|
|
|
@@ -64,7 +61,7 @@ module VagrantWindows
|
|
|
64
61
|
@logger.info("Configuring NIC #{net_connection_id} using static ip #{ip}")
|
|
65
62
|
#netsh interface ip set address "Local Area Connection 2" static 192.168.33.10 255.255.255.0
|
|
66
63
|
netsh = "netsh interface ip set address \"#{net_connection_id}\" static #{ip} #{netmask}"
|
|
67
|
-
@
|
|
64
|
+
@communicator.execute(netsh)
|
|
68
65
|
end
|
|
69
66
|
|
|
70
67
|
# Sets all networks on the guest to 'Work Network' mode. This is
|
|
@@ -73,7 +70,7 @@ module VagrantWindows
|
|
|
73
70
|
def set_all_networks_to_work()
|
|
74
71
|
@logger.info("Setting all networks to 'Work Network'")
|
|
75
72
|
command = VagrantWindows.load_script("set_work_network.ps1")
|
|
76
|
-
@
|
|
73
|
+
@communicator.execute(command)
|
|
77
74
|
end
|
|
78
75
|
|
|
79
76
|
|
|
@@ -86,7 +83,7 @@ module VagrantWindows
|
|
|
86
83
|
def wsman_version()
|
|
87
84
|
@logger.debug("querying WSMan version")
|
|
88
85
|
version = ''
|
|
89
|
-
@
|
|
86
|
+
@communicator.execute(PS_GET_WSMAN_VER) do |type, line|
|
|
90
87
|
version = version + "#{line}" if type == :stdout && !line.nil?
|
|
91
88
|
end
|
|
92
89
|
@logger.debug("wsman version: #{version}")
|
|
@@ -102,7 +99,7 @@ module VagrantWindows
|
|
|
102
99
|
@logger.debug("querying network adapters")
|
|
103
100
|
# Get all NICs that have a MAC address
|
|
104
101
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394216(v=vs.85).aspx
|
|
105
|
-
adapters = @
|
|
102
|
+
adapters = @communicator.execute(WQL_NET_ADAPTERS_V2, { :shell => :wql } )[:win32_network_adapter]
|
|
106
103
|
@logger.debug("#{adapters.inspect}")
|
|
107
104
|
return adapters
|
|
108
105
|
end
|
|
@@ -117,7 +114,7 @@ module VagrantWindows
|
|
|
117
114
|
def network_adapters_v3_winrm()
|
|
118
115
|
winrs_v3_get_adapters_ps1 = VagrantWindows.load_script("winrs_v3_get_adapters.ps1")
|
|
119
116
|
output = ''
|
|
120
|
-
@
|
|
117
|
+
@communicator.execute(winrs_v3_get_adapters_ps1) do |type, line|
|
|
121
118
|
output = output + "#{line}" if type == :stdout && !line.nil?
|
|
122
119
|
end
|
|
123
120
|
adapters = []
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module VagrantWindows
|
|
2
|
+
module Communication
|
|
3
|
+
|
|
4
|
+
# Handles loading all available Linix command filters and applying them
|
|
5
|
+
# to specified command
|
|
6
|
+
class LinuxCommandFilter
|
|
7
|
+
|
|
8
|
+
# Filter the given Vagrant command to ensure compatibility with Windows
|
|
9
|
+
#
|
|
10
|
+
# @param [String] The Vagrant shell command
|
|
11
|
+
# @returns [String] Windows runnable command or empty string
|
|
12
|
+
def filter(command)
|
|
13
|
+
win_friendly_cmd = command
|
|
14
|
+
command_filters.each do |cmd_filter|
|
|
15
|
+
win_friendly_cmd = cmd_filter.filter(win_friendly_cmd) if cmd_filter.accept?(win_friendly_cmd)
|
|
16
|
+
end
|
|
17
|
+
win_friendly_cmd
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# All the available Linux command filters
|
|
22
|
+
#
|
|
23
|
+
# @returns [Array] All Linux command filter instances
|
|
24
|
+
def command_filters
|
|
25
|
+
@command_filters ||= create_command_filters()
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Creates all the available Linux command filters
|
|
29
|
+
#
|
|
30
|
+
# @returns [Array] All Linux command filter instances
|
|
31
|
+
def create_command_filters
|
|
32
|
+
filters = []
|
|
33
|
+
Dir[File.join(File.dirname(__FILE__), '/command_filters/*.rb')].each do |file|
|
|
34
|
+
require file
|
|
35
|
+
clazz = File.basename(file, '.*').capitalize
|
|
36
|
+
filters << Module.const_get("VagrantWindows::Communication::CommandFilters::#{clazz}").new
|
|
37
|
+
end
|
|
38
|
+
filters
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -2,7 +2,9 @@ require 'timeout'
|
|
|
2
2
|
require 'log4r'
|
|
3
3
|
require_relative 'winrmshell_factory'
|
|
4
4
|
require_relative 'winrmshell'
|
|
5
|
+
require_relative 'winrmfilemanager'
|
|
5
6
|
require_relative 'winrmfinder'
|
|
7
|
+
require_relative 'linux_command_filter'
|
|
6
8
|
require_relative '../errors'
|
|
7
9
|
require_relative '../windows_machine'
|
|
8
10
|
|
|
@@ -18,7 +20,7 @@ module VagrantWindows
|
|
|
18
20
|
def initialize(machine)
|
|
19
21
|
@windows_machine = VagrantWindows::WindowsMachine.new(machine)
|
|
20
22
|
@winrm_shell_factory = WinRMShellFactory.new(@windows_machine, WinRMFinder.new(@windows_machine))
|
|
21
|
-
|
|
23
|
+
@linux_cmd_filter = LinuxCommandFilter.new()
|
|
22
24
|
@logger = Log4r::Logger.new("vagrant_windows::communication::winrmcommunicator")
|
|
23
25
|
@logger.debug("initializing WinRMCommunicator")
|
|
24
26
|
end
|
|
@@ -45,38 +47,42 @@ module VagrantWindows
|
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
def execute(command, opts={}, &block)
|
|
50
|
+
# If this is a *nix command with no Windows equivilant, don't run it
|
|
51
|
+
win_friendly_cmd = @linux_cmd_filter.filter(command)
|
|
52
|
+
return 0 if win_friendly_cmd.empty?
|
|
53
|
+
|
|
48
54
|
opts = {
|
|
49
55
|
:error_check => true,
|
|
50
56
|
:error_class => VagrantWindows::Errors::WinRMExecutionError,
|
|
51
57
|
:error_key => :winrm_execution_error,
|
|
52
|
-
:command =>
|
|
58
|
+
:command => win_friendly_cmd,
|
|
53
59
|
:shell => :powershell
|
|
54
60
|
}.merge(opts || {})
|
|
55
|
-
|
|
56
|
-
if opts[:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
|
|
62
|
+
win_friendly_cmd << "\r\nexit $LASTEXITCODE" if opts[:shell] == :powershell
|
|
63
|
+
output = winrmshell.send(opts[:shell], win_friendly_cmd, &block)
|
|
64
|
+
|
|
65
|
+
return output if opts[:shell] == :wql
|
|
66
|
+
exitcode = output[:exitcode]
|
|
67
|
+
raise_execution_error(opts, exitcode) if opts[:error_check] && exitcode != 0
|
|
68
|
+
exitcode
|
|
60
69
|
end
|
|
61
70
|
alias_method :sudo, :execute
|
|
62
71
|
|
|
63
72
|
def test(command, opts=nil)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return false unless (command =~ /^uname|^cat \/etc|^cat \/proc|grep 'Fedora/).nil?
|
|
73
|
+
# If this is a *nix command with no Windows equivilant, don't run it
|
|
74
|
+
win_friendly_cmd = @linux_cmd_filter.filter(command)
|
|
75
|
+
return false if win_friendly_cmd.empty?
|
|
68
76
|
|
|
69
77
|
opts = { :error_check => false }.merge(opts || {})
|
|
70
|
-
execute(
|
|
78
|
+
execute(win_friendly_cmd, opts) == 0
|
|
71
79
|
end
|
|
72
80
|
|
|
73
81
|
def upload(from, to)
|
|
74
|
-
@logger.debug("Uploading: #{from} to #{to}")
|
|
75
82
|
winrmshell.upload(from, to)
|
|
76
83
|
end
|
|
77
84
|
|
|
78
85
|
def download(from, to)
|
|
79
|
-
@logger.debug("Downloading: #{from} to #{to}")
|
|
80
86
|
winrmshell.download(from, to)
|
|
81
87
|
end
|
|
82
88
|
|
|
@@ -87,19 +93,10 @@ module VagrantWindows
|
|
|
87
93
|
def winrmshell
|
|
88
94
|
@winrmshell ||= @winrm_shell_factory.create_winrm_shell()
|
|
89
95
|
end
|
|
90
|
-
|
|
96
|
+
|
|
91
97
|
|
|
92
98
|
protected
|
|
93
99
|
|
|
94
|
-
def do_execute(command, shell, &block)
|
|
95
|
-
if shell.eql? :cmd
|
|
96
|
-
winrmshell.cmd(command, &block)[:exitcode]
|
|
97
|
-
else
|
|
98
|
-
command = VagrantWindows.load_script("command_alias.ps1") << "\r\n" << command << "\r\nexit $LASTEXITCODE"
|
|
99
|
-
winrmshell.powershell(command, &block)[:exitcode]
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
100
|
def raise_execution_error(opts, exit_code)
|
|
104
101
|
# The error classes expect the translation key to be _key, but that makes for an ugly
|
|
105
102
|
# configuration parameter, so we set it here from `error_key`
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
require 'log4r'
|
|
2
|
+
require 'winrm'
|
|
3
|
+
require_relative 'winrmshell'
|
|
4
|
+
require_relative '../errors'
|
|
5
|
+
|
|
6
|
+
module VagrantWindows
|
|
7
|
+
module Communication
|
|
8
|
+
|
|
9
|
+
# Manages the file system on the remote guest allowing for file tranfer
|
|
10
|
+
# between the guest and host.
|
|
11
|
+
class WinRMFileManager
|
|
12
|
+
|
|
13
|
+
def initialize(winrm_shell)
|
|
14
|
+
@logger = Log4r::Logger.new("vagrant_windows::communication::winrmfilemanager")
|
|
15
|
+
@winrm_shell = winrm_shell
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Uploads the given file or directory from the host to the guest (recursively).
|
|
19
|
+
#
|
|
20
|
+
# @param [String] The source file or directory path on the host
|
|
21
|
+
# @param [String] The destination file or directory path on the host
|
|
22
|
+
def upload(host_src_file_path, guest_dest_file_path)
|
|
23
|
+
@logger.debug("Upload: #{host_src_file_path} -> #{guest_dest_file_path}")
|
|
24
|
+
if File.directory?(host_src_file_path)
|
|
25
|
+
upload_directory(host_src_file_path, guest_dest_file_path)
|
|
26
|
+
else
|
|
27
|
+
upload_file(host_src_file_path, guest_dest_file_path)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Downloads the given file from the guest to the host.
|
|
32
|
+
# NOTE: This currently only supports single file download
|
|
33
|
+
#
|
|
34
|
+
# @param [String] The source file path on the guest
|
|
35
|
+
# @param [String] The destination file path on the host
|
|
36
|
+
def download(guest_src_file_path, host_dest_file_path)
|
|
37
|
+
@logger.debug("#{guest_src_file_path} -> #{host_dest_file_path}")
|
|
38
|
+
output = @winrm_shell.powershell("[System.convert]::ToBase64String([System.IO.File]::ReadAllBytes(\"#{guest_src_file_path}\"))")
|
|
39
|
+
contents = output[:data].map!{|line| line[:stdout]}.join.gsub("\\n\\r", '')
|
|
40
|
+
out = Base64.decode64(contents)
|
|
41
|
+
IO.binwrite(host_dest_file_path, out)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Recursively uploads the given directory from the host to the guest
|
|
48
|
+
#
|
|
49
|
+
# @param [String] The source file or directory path on the host
|
|
50
|
+
# @param [String] The destination file or directory path on the host
|
|
51
|
+
def upload_directory(host_src_file_path, guest_dest_file_path)
|
|
52
|
+
glob_patt = File.join(host_src_file_path, '**/*')
|
|
53
|
+
Dir.glob(glob_patt).select { |f| !File.directory?(f) }.each do |host_file_path|
|
|
54
|
+
guest_file_path = guest_file_path(host_src_file_path, guest_dest_file_path, host_file_path)
|
|
55
|
+
upload_file(host_file_path, guest_file_path)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Uploads the given file, but only if the target file doesn't exist
|
|
60
|
+
# or its MD5 checksum doens't match the host's source checksum.
|
|
61
|
+
#
|
|
62
|
+
# @param [String] The source file path on the host
|
|
63
|
+
# @param [String] The destination file path on the guest
|
|
64
|
+
def upload_file(host_src_file_path, guest_dest_file_path)
|
|
65
|
+
if should_upload_file?(host_src_file_path, guest_dest_file_path)
|
|
66
|
+
tmp_file_path = upload_to_temp_file(host_src_file_path)
|
|
67
|
+
decode_temp_file(tmp_file_path, guest_dest_file_path)
|
|
68
|
+
else
|
|
69
|
+
@logger.debug("Up to date: #{guest_dest_file_path}")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Uploads the given file to a new temp file on the guest
|
|
74
|
+
#
|
|
75
|
+
# @param [String] The source file path on the host
|
|
76
|
+
# @return [String] The temp file path on the guest
|
|
77
|
+
def upload_to_temp_file(host_src_file_path)
|
|
78
|
+
tmp_file_path = File.join(guest_temp_dir, "winrm-upload-#{rand()}")
|
|
79
|
+
@logger.debug("Uploading '#{host_src_file_path}' to temp file '#{tmp_file_path}'")
|
|
80
|
+
base64_host_file = Base64.encode64(IO.binread(host_src_file_path)).gsub("\n",'')
|
|
81
|
+
base64_host_file.chars.to_a.each_slice(8000-tmp_file_path.size) do |chunk|
|
|
82
|
+
out = @winrm_shell.cmd("echo #{chunk.join} >> \"#{tmp_file_path}\"")
|
|
83
|
+
raise_upload_error_if_failed(out, host_src_file_path, tmp_file_path)
|
|
84
|
+
end
|
|
85
|
+
tmp_file_path
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Moves and decodes the given file temp file on the guest to its
|
|
89
|
+
# permanent location
|
|
90
|
+
#
|
|
91
|
+
# @param [String] The source base64 encoded temp file path on the guest
|
|
92
|
+
# @param [String] The destination file path on the guest
|
|
93
|
+
def decode_temp_file(guest_tmp_file_path, guest_dest_file_path)
|
|
94
|
+
@logger.debug("Decoding temp file '#{guest_tmp_file_path}' to '#{guest_dest_file_path}'")
|
|
95
|
+
out = @winrm_shell.powershell <<-EOH
|
|
96
|
+
$tmp_file_path = [System.IO.Path]::GetFullPath('#{guest_tmp_file_path}')
|
|
97
|
+
$dest_file_path = [System.IO.Path]::GetFullPath('#{guest_dest_file_path}')
|
|
98
|
+
|
|
99
|
+
if (Test-Path $dest_file_path) {
|
|
100
|
+
rm $dest_file_path
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
$dest_dir = ([System.IO.Path]::GetDirectoryName($dest_file_path))
|
|
104
|
+
New-Item -ItemType directory -Force -Path $dest_dir
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
$base64_string = Get-Content $tmp_file_path
|
|
108
|
+
$bytes = [System.Convert]::FromBase64String($base64_string)
|
|
109
|
+
[System.IO.File]::WriteAllBytes($dest_file_path, $bytes)
|
|
110
|
+
EOH
|
|
111
|
+
raise_upload_error_if_failed(out, guest_tmp_file_path, guest_dest_file_path)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Checks to see if the target file on the guest is missing or out of date.
|
|
115
|
+
#
|
|
116
|
+
# @param [String] The source file path on the host
|
|
117
|
+
# @param [String] The destination file path on the guest
|
|
118
|
+
# @return [Boolean] True if the file is missing or out of date
|
|
119
|
+
def should_upload_file?(host_src_file_path, guest_dest_file_path)
|
|
120
|
+
local_md5 = Digest::MD5.file(host_src_file_path).hexdigest
|
|
121
|
+
cmd = <<-EOH
|
|
122
|
+
$dest_file_path = [System.IO.Path]::GetFullPath('#{guest_dest_file_path}')
|
|
123
|
+
|
|
124
|
+
if (Test-Path $dest_file_path) {
|
|
125
|
+
$crypto_provider = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
|
|
126
|
+
try {
|
|
127
|
+
$file = [System.IO.File]::Open($dest_file_path, [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
|
|
128
|
+
$guest_md5 = ([System.BitConverter]::ToString($crypto_provider.ComputeHash($file))).Replace("-","").ToLower()
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
$file.Dispose()
|
|
132
|
+
}
|
|
133
|
+
if ($guest_md5 -eq '#{local_md5}') {
|
|
134
|
+
exit 0
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
exit 1
|
|
138
|
+
EOH
|
|
139
|
+
@winrm_shell.powershell(cmd)[:exitcode] == 1
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Creates a guest file path equivalent from a host file path
|
|
143
|
+
#
|
|
144
|
+
# @param [String] The base host directory we're going to copy from
|
|
145
|
+
# @param [String] The base guest directory we're going to copy to
|
|
146
|
+
# @param [String] A full path to a file on the host underneath host_base_dir
|
|
147
|
+
# @return [String] The guest file path equivalent
|
|
148
|
+
def guest_file_path(host_base_dir, guest_base_dir, host_file_path)
|
|
149
|
+
rel_path = File.dirname(host_file_path[host_base_dir.length, host_file_path.length])
|
|
150
|
+
File.join(guest_base_dir, rel_path, File.basename(host_file_path))
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def guest_temp_dir
|
|
154
|
+
@guest_temp ||= (@winrm_shell.cmd('echo %TEMP%'))[:data][0][:stdout].chomp
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def raise_upload_error_if_failed(out, from, to)
|
|
158
|
+
raise Errors::WinRMFileTransferError,
|
|
159
|
+
:from => from,
|
|
160
|
+
:to => to,
|
|
161
|
+
:message => out.inspect if out[:exitcode] != 0
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
end #class
|
|
165
|
+
end
|
|
166
|
+
end
|