ridley 0.10.0.rc1 → 0.10.0.rc2
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.
- data/bootstrappers/windows_omnibus.erb +1 -22
- data/lib/ridley/bootstrap_bindings/windows_template_binding.rb +0 -53
- data/lib/ridley/bootstrapper.rb +13 -10
- data/lib/ridley/bootstrapper/context.rb +2 -1
- data/lib/ridley/errors.rb +3 -0
- data/lib/ridley/host_connector.rb +21 -16
- data/lib/ridley/host_connector/ssh/worker.rb +40 -2
- data/lib/ridley/host_connector/winrm.rb +1 -0
- data/lib/ridley/host_connector/winrm/command_uploader.rb +90 -0
- data/lib/ridley/host_connector/winrm/worker.rb +67 -47
- data/lib/ridley/resources/environment_resource.rb +1 -1
- data/lib/ridley/resources/node_resource.rb +106 -19
- data/lib/ridley/version.rb +1 -1
- data/spec/unit/ridley/bootstrap_bindings/windows_template_binding_spec.rb +0 -6
- data/spec/unit/ridley/bootstrapper_spec.rb +23 -3
- data/spec/unit/ridley/host_connector/ssh/worker_spec.rb +49 -7
- data/spec/unit/ridley/host_connector/ssh_spec.rb +4 -3
- data/spec/unit/ridley/host_connector/winrm/command_uploader_spec.rb +67 -0
- data/spec/unit/ridley/host_connector/winrm/worker_spec.rb +67 -21
- data/spec/unit/ridley/host_connector/winrm_spec.rb +5 -0
- data/spec/unit/ridley/host_connector_spec.rb +39 -10
- data/spec/unit/ridley/resources/node_resource_spec.rb +171 -4
- metadata +5 -2
@@ -25,10 +25,6 @@ setlocal
|
|
25
25
|
<%= "SETX HTTP_PROXY \"#{bootstrap_proxy}\"" if bootstrap_proxy %>
|
26
26
|
mkdir <%= bootstrap_directory %>
|
27
27
|
|
28
|
-
> <%= bootstrap_directory %>\wget.vbs (
|
29
|
-
<%= windows_wget_vb %>
|
30
|
-
)
|
31
|
-
|
32
28
|
> <%= bootstrap_directory %>\wget.ps1 (
|
33
29
|
<%= windows_wget_powershell %>
|
34
30
|
)
|
@@ -87,24 +83,7 @@ set "REMOTE_SOURCE_MSI_URL=https://www.opscode.com/chef/download?p=windows&pv=%M
|
|
87
83
|
set "LOCAL_DESTINATION_MSI_PATH=<%= local_download_path %>"
|
88
84
|
set "FALLBACK_QUERY_STRING=&DownloadContext=PowerShell"
|
89
85
|
|
90
|
-
|
91
|
-
|
92
|
-
rem Work around issues found in Windows Server 2012 around job objects not respecting WSMAN memory quotas
|
93
|
-
rem that cause the MSI download process to exceed the quota even when it is increased by administrators.
|
94
|
-
rem Retry the download using a more memory-efficient mechanism that only works if PowerShell is available.
|
95
|
-
if ERRORLEVEL 1 (
|
96
|
-
echo Warning: Failed to download "%REMOTE_SOURCE_MSI_URL%" to "%LOCAL_DESTINATION_MSI_PATH%"
|
97
|
-
echo Warning: Retrying download with PowerShell if available
|
98
|
-
if EXIST "%LOCAL_DESTINATION_MSI_PATH%" del /f /q "%LOCAL_DESTINATION_MSI_PATH%"
|
99
|
-
powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive "& '<%= bootstrap_directory %>\wget.ps1' '%REMOTE_SOURCE_MSI_URL%%FALLBACK_QUERY_STRING%' '%LOCAL_DESTINATION_MSI_PATH%'"
|
100
|
-
|
101
|
-
if NOT ERRORLEVEL 1 (
|
102
|
-
echo Download succeeded
|
103
|
-
) else (
|
104
|
-
echo Failed to download "%REMOTE_SOURCE_MSI_URL%"
|
105
|
-
echo Subsequent attempt to install the downloaded MSI is likely to fail
|
106
|
-
)
|
107
|
-
)
|
86
|
+
powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive "& '<%= bootstrap_directory %>\wget.ps1' '%REMOTE_SOURCE_MSI_URL%%FALLBACK_QUERY_STRING%' '%LOCAL_DESTINATION_MSI_PATH%'"
|
108
87
|
|
109
88
|
<%= install_chef %>
|
110
89
|
|
@@ -119,59 +119,6 @@ CONFIG
|
|
119
119
|
raise Errors::EncryptedDataBagSecretNotFound, "Error bootstrapping: Encrypted data bag secret provided but not found at '#{encrypted_data_bag_secret_path}'"
|
120
120
|
end
|
121
121
|
|
122
|
-
# Implements a Visual Basic script that attempts a simple
|
123
|
-
# 'wget' to download the Chef msi
|
124
|
-
#
|
125
|
-
# @return [String]
|
126
|
-
def windows_wget_vb
|
127
|
-
win_wget = <<-WGET
|
128
|
-
url = WScript.Arguments.Named("url")
|
129
|
-
path = WScript.Arguments.Named("path")
|
130
|
-
proxy = null
|
131
|
-
Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
|
132
|
-
Set wshShell = CreateObject( "WScript.Shell" )
|
133
|
-
Set objUserVariables = wshShell.Environment("USER")
|
134
|
-
|
135
|
-
'http proxy is optional
|
136
|
-
'attempt to read from HTTP_PROXY env var first
|
137
|
-
On Error Resume Next
|
138
|
-
|
139
|
-
If NOT (objUserVariables("HTTP_PROXY") = "") Then
|
140
|
-
proxy = objUserVariables("HTTP_PROXY")
|
141
|
-
|
142
|
-
'fall back to named arg
|
143
|
-
ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then
|
144
|
-
proxy = WScript.Arguments.Named("proxy")
|
145
|
-
End If
|
146
|
-
|
147
|
-
If NOT isNull(proxy) Then
|
148
|
-
'setProxy method is only available on ServerXMLHTTP 6.0+
|
149
|
-
Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
|
150
|
-
objXMLHTTP.setProxy 2, proxy
|
151
|
-
End If
|
152
|
-
|
153
|
-
On Error Goto 0
|
154
|
-
|
155
|
-
objXMLHTTP.open "GET", url, false
|
156
|
-
objXMLHTTP.send()
|
157
|
-
If objXMLHTTP.Status = 200 Then
|
158
|
-
Set objADOStream = CreateObject("ADODB.Stream")
|
159
|
-
objADOStream.Open
|
160
|
-
objADOStream.Type = 1
|
161
|
-
objADOStream.Write objXMLHTTP.ResponseBody
|
162
|
-
objADOStream.Position = 0
|
163
|
-
Set objFSO = Createobject("Scripting.FileSystemObject")
|
164
|
-
If objFSO.Fileexists(path) Then objFSO.DeleteFile path
|
165
|
-
Set objFSO = Nothing
|
166
|
-
objADOStream.SaveToFile path
|
167
|
-
objADOStream.Close
|
168
|
-
Set objADOStream = Nothing
|
169
|
-
End if
|
170
|
-
Set objXMLHTTP = Nothing
|
171
|
-
WGET
|
172
|
-
escape_and_echo(win_wget)
|
173
|
-
end
|
174
|
-
|
175
122
|
# Implements a Powershell script that attempts a simple
|
176
123
|
# 'wget' to download the Chef msi
|
177
124
|
#
|
data/lib/ridley/bootstrapper.rb
CHANGED
@@ -4,14 +4,11 @@ module Ridley
|
|
4
4
|
autoload :Context, 'ridley/bootstrapper/context'
|
5
5
|
|
6
6
|
include Celluloid
|
7
|
-
include
|
8
|
-
|
7
|
+
include Ridley::Logging
|
8
|
+
|
9
9
|
# @return [Array<String>]
|
10
10
|
attr_reader :hosts
|
11
11
|
|
12
|
-
# @return [Array<Bootstrapper::Context>]
|
13
|
-
attr_reader :contexts
|
14
|
-
|
15
12
|
# @return [Hash]
|
16
13
|
attr_reader :options
|
17
14
|
|
@@ -47,7 +44,7 @@ module Ridley
|
|
47
44
|
# @option options [String] :template
|
48
45
|
# bootstrap template to use
|
49
46
|
def initialize(hosts, options = {})
|
50
|
-
@hosts = Array(hosts).collect(&:to_s).uniq
|
47
|
+
@hosts = Array(hosts).flatten.collect(&:to_s).uniq
|
51
48
|
@options = options.dup
|
52
49
|
@options[:ssh] ||= Hash.new
|
53
50
|
@options[:ssh] = {
|
@@ -56,16 +53,22 @@ module Ridley
|
|
56
53
|
}.merge(@options[:ssh])
|
57
54
|
|
58
55
|
@options[:sudo] = @options[:ssh][:sudo]
|
59
|
-
@contexts = @hosts.collect do |host|
|
60
|
-
Context.create(host, options)
|
61
|
-
end
|
62
56
|
end
|
63
57
|
|
58
|
+
# @raise [Errors::HostConnectionError] if a node is unreachable
|
59
|
+
#
|
60
|
+
# @return [Array<Bootstrapper::Context>]
|
61
|
+
def contexts
|
62
|
+
@contexts ||= @hosts.collect { |host| Context.create(host, options) }
|
63
|
+
end
|
64
|
+
|
65
|
+
# @raise [Errors::HostConnectionError] if a node is unreachable
|
66
|
+
#
|
64
67
|
# @return [HostConnector::ResponseSet]
|
65
68
|
def run
|
66
69
|
workers = Array.new
|
67
70
|
futures = contexts.collect do |context|
|
68
|
-
info "Running bootstrap command on #{context.host}"
|
71
|
+
log.info { "Running bootstrap command on #{context.host}" }
|
69
72
|
|
70
73
|
workers << worker = context.host_connector::Worker.new(context.host, self.options.freeze)
|
71
74
|
|
@@ -5,7 +5,6 @@ module Ridley
|
|
5
5
|
# @author Jamie Winsor <reset@riotgames.com>
|
6
6
|
class Context
|
7
7
|
class << self
|
8
|
-
|
9
8
|
# @param [String] host
|
10
9
|
# @option options [Hash] :ssh
|
11
10
|
# * :user (String) a shell user that will login to each node and perform the bootstrap command on (required)
|
@@ -38,6 +37,8 @@ module Ridley
|
|
38
37
|
# bootstrap with sudo (default: true)
|
39
38
|
# @option options [String] :template ('omnibus')
|
40
39
|
# bootstrap template to use
|
40
|
+
#
|
41
|
+
# @raise [Errors::HostConnectionError] if a node is unreachable
|
41
42
|
def create(host, options = {})
|
42
43
|
host_connector = HostConnector.best_connector_for(host, options)
|
43
44
|
template_binding = case host_connector.to_s
|
data/lib/ridley/errors.rb
CHANGED
@@ -40,6 +40,9 @@ module Ridley
|
|
40
40
|
class EncryptedDataBagSecretNotFound < BootstrapError; end
|
41
41
|
class HostConnectionError < BootstrapError; end
|
42
42
|
|
43
|
+
class RemoteCommandError < RidleyError; end
|
44
|
+
class RemoteScriptError < RemoteCommandError; end
|
45
|
+
|
43
46
|
# Exception thrown when the maximum amount of requests is exceeded.
|
44
47
|
class RedirectLimitReached < RidleyError
|
45
48
|
attr_reader :response
|
@@ -14,24 +14,32 @@ module Ridley
|
|
14
14
|
|
15
15
|
class << self
|
16
16
|
# Finds and returns the best HostConnector for a given host
|
17
|
-
#
|
17
|
+
#
|
18
18
|
# @param host [String]
|
19
19
|
# the host to attempt to connect to
|
20
20
|
# @option options [Hash] :ssh
|
21
21
|
# * :port (Fixnum) the ssh port to connect on the node the bootstrap will be performed on (22)
|
22
22
|
# @option options [Hash] :winrm
|
23
23
|
# * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
|
24
|
-
#
|
24
|
+
# @param block [Proc]
|
25
|
+
# an optional block that is yielded the best HostConnector
|
26
|
+
#
|
25
27
|
# @return [Ridley::HostConnector] a class under Ridley::HostConnector
|
26
|
-
def best_connector_for(host, options = {})
|
28
|
+
def best_connector_for(host, options = {}, &block)
|
27
29
|
ssh_port, winrm_port = parse_port_options(options)
|
28
30
|
if connector_port_open?(host, ssh_port)
|
29
|
-
Ridley::HostConnector::SSH
|
31
|
+
host_connector = Ridley::HostConnector::SSH
|
30
32
|
elsif connector_port_open?(host, winrm_port)
|
31
|
-
Ridley::HostConnector::WinRM
|
33
|
+
host_connector = Ridley::HostConnector::WinRM
|
32
34
|
else
|
33
35
|
raise Ridley::Errors::HostConnectionError, "No available connection method available on #{host}."
|
34
36
|
end
|
37
|
+
|
38
|
+
if block_given?
|
39
|
+
yield host_connector
|
40
|
+
else
|
41
|
+
host_connector
|
42
|
+
end
|
35
43
|
end
|
36
44
|
|
37
45
|
# Checks to see if the given port is open for TCP connections
|
@@ -41,19 +49,16 @@ module Ridley
|
|
41
49
|
# the host to attempt to connect to
|
42
50
|
# @param port [Fixnum]
|
43
51
|
# the port to attempt to connect on
|
44
|
-
#
|
52
|
+
#
|
45
53
|
# @return [Boolean]
|
46
54
|
def connector_port_open?(host, port)
|
47
|
-
Timeout::timeout(
|
48
|
-
|
49
|
-
|
50
|
-
socket.close
|
51
|
-
true
|
52
|
-
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
53
|
-
false
|
54
|
-
end
|
55
|
+
Timeout::timeout(3) do
|
56
|
+
socket = TCPSocket.new(host, port)
|
57
|
+
socket.close
|
55
58
|
end
|
56
|
-
|
59
|
+
|
60
|
+
true
|
61
|
+
rescue Timeout::Error, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
57
62
|
false
|
58
63
|
end
|
59
64
|
|
@@ -64,7 +69,7 @@ module Ridley
|
|
64
69
|
# * :port (Fixnum) the ssh port to connect on the node the bootstrap will be performed on (22)
|
65
70
|
# @option options [Hash] :winrm
|
66
71
|
# * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
|
67
|
-
#
|
72
|
+
#
|
68
73
|
# @return [Array]
|
69
74
|
def parse_port_options(options)
|
70
75
|
ssh_port = options[:ssh][:port] if options[:ssh]
|
@@ -12,11 +12,13 @@ module Ridley
|
|
12
12
|
attr_reader :host
|
13
13
|
# @return [Hashie::Mash]
|
14
14
|
attr_reader :options
|
15
|
+
|
16
|
+
EMBEDDED_RUBY_PATH = '/opt/chef/embedded/bin/ruby'.freeze
|
15
17
|
|
16
18
|
# @param [Hash] options
|
17
19
|
def initialize(host, options = {})
|
18
|
-
|
19
|
-
@options = options[:ssh]
|
20
|
+
options = options.deep_symbolize_keys
|
21
|
+
@options = options[:ssh] || Hash.new
|
20
22
|
@host = host
|
21
23
|
@sudo = @options[:sudo]
|
22
24
|
@user = @options[:user]
|
@@ -90,6 +92,42 @@ module Ridley
|
|
90
92
|
[ :error, response ]
|
91
93
|
end
|
92
94
|
|
95
|
+
# Executes a chef-client command on the nodes
|
96
|
+
#
|
97
|
+
# @return [#run]
|
98
|
+
def chef_client
|
99
|
+
command = "chef-client"
|
100
|
+
if sudo
|
101
|
+
command = "sudo #{command}"
|
102
|
+
end
|
103
|
+
|
104
|
+
run(command)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Executes a copy of the encrypted_data_bag_secret to the nodes
|
108
|
+
#
|
109
|
+
# @param [String] encrypted_data_bag_secret_path
|
110
|
+
# the path to the encrypted_data_bag_secret
|
111
|
+
#
|
112
|
+
# @return [#run]
|
113
|
+
def put_secret(encrypted_data_bag_secret_path)
|
114
|
+
secret = File.read(encrypted_data_bag_secret_path).chomp
|
115
|
+
command = "echo '#{secret}' > /etc/chef/encrypted_data_bag_secret; chmod 0600 /etc/chef/encrypted_data_bag_secret"
|
116
|
+
|
117
|
+
run(command)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Executes a provided Ruby script in the embedded Ruby installation
|
121
|
+
#
|
122
|
+
# @param [Array<String>] command_lines
|
123
|
+
# An Array of lines of the command to be executed
|
124
|
+
#
|
125
|
+
# @return [#run]
|
126
|
+
def ruby_script(command_lines)
|
127
|
+
command = "#{EMBEDDED_RUBY_PATH} -e \"#{command_lines.join(';')}\""
|
128
|
+
run(command)
|
129
|
+
end
|
130
|
+
|
93
131
|
private
|
94
132
|
|
95
133
|
attr_reader :runner
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Ridley
|
2
|
+
module HostConnector
|
3
|
+
class WinRM
|
4
|
+
# @author Kyle Allan <kallan@riotgames.com>
|
5
|
+
# @author Justin Campbell <justin.campbell@riotgames.com>
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# command_uploader = CommandUploader.new(long_command, winrm)
|
9
|
+
# command_uploader.upload
|
10
|
+
# command_uploader.command
|
11
|
+
#
|
12
|
+
# This class is used by WinRM Workers when the worker is told to execute a command that
|
13
|
+
# might be too long for WinRM to handle.
|
14
|
+
#
|
15
|
+
# After an instance of this class is created, the upload method will upload the long command
|
16
|
+
# to the node being worked on in chunks. Once on the node, some Powershell code will decode
|
17
|
+
# the long_command into a batch file. The command method will return a String representing the
|
18
|
+
# command the worker will use to execute the uploaded batch script.
|
19
|
+
class CommandUploader
|
20
|
+
CHUNK_LIMIT = 1024
|
21
|
+
|
22
|
+
# @return [WinRM::WinRMWebService]
|
23
|
+
attr_reader :winrm
|
24
|
+
# @return [String]
|
25
|
+
attr_reader :base64_file_name
|
26
|
+
# @return [String]
|
27
|
+
attr_reader :command_file_name
|
28
|
+
|
29
|
+
# @param [WinRM::WinRMWebService] winrm
|
30
|
+
def initialize(winrm)
|
31
|
+
@winrm = winrm
|
32
|
+
@base64_file_name = get_file_path("winrm-upload-base64-#{unique_string}")
|
33
|
+
@command_file_name = get_file_path("winrm-upload-#{unique_string}.bat")
|
34
|
+
end
|
35
|
+
|
36
|
+
# Uploads the command encoded as base64 to a file on the host
|
37
|
+
# and then uses Powershell to transform the base64 file into the
|
38
|
+
# command that was originally passed through.
|
39
|
+
#
|
40
|
+
# @param [String] command_string
|
41
|
+
def upload(command_string)
|
42
|
+
upload_command(command_string)
|
43
|
+
convert_command
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [String] the command to execute the uploaded file
|
47
|
+
def command
|
48
|
+
"cmd.exe /C #{command_file_name}"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Runs a delete command on the files generated by #base64_file_name
|
52
|
+
# and #command_file_name
|
53
|
+
def cleanup
|
54
|
+
winrm.run_cmd( "del #{base64_file_name} /F /Q" )
|
55
|
+
winrm.run_cmd( "del #{command_file_name} /F /Q" )
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def upload_command(command_string)
|
61
|
+
command_string_chars(command_string).each_slice(CHUNK_LIMIT) do |chunk|
|
62
|
+
winrm.run_cmd( "echo #{chunk.join} >> \"#{base64_file_name}\"" )
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def command_string_chars(command_string)
|
67
|
+
Base64.encode64(command_string).gsub("\n", '').chars.to_a
|
68
|
+
end
|
69
|
+
|
70
|
+
def convert_command
|
71
|
+
winrm.powershell <<-POWERSHELL
|
72
|
+
$base64_string = Get-Content \"#{base64_file_name}\"
|
73
|
+
$bytes = [System.Convert]::FromBase64String($base64_string)
|
74
|
+
$new_file = [System.IO.Path]::GetFullPath(\"#{command_file_name}\")
|
75
|
+
[System.IO.File]::WriteAllBytes($new_file,$bytes)
|
76
|
+
POWERSHELL
|
77
|
+
end
|
78
|
+
|
79
|
+
def unique_string
|
80
|
+
@unique_string ||= "#{Process.pid}-#{Time.now.to_i}"
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [String]
|
84
|
+
def get_file_path(file)
|
85
|
+
(winrm.run_cmd("echo %TEMP%\\#{file}"))[:data][0][:stdout].chomp
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -17,6 +17,14 @@ module Ridley
|
|
17
17
|
attr_reader :options
|
18
18
|
# @return [String]
|
19
19
|
attr_reader :winrm_endpoint
|
20
|
+
# @return [CommandUploader]
|
21
|
+
attr_reader :command_uploader
|
22
|
+
# @return [Array]
|
23
|
+
attr_reader :command_uploaders
|
24
|
+
|
25
|
+
finalizer :finalizer
|
26
|
+
|
27
|
+
EMBEDDED_RUBY_PATH = 'C:\opscode\chef\embedded\bin\ruby'.freeze
|
20
28
|
|
21
29
|
# @param host [String]
|
22
30
|
# the host the worker is going to work on
|
@@ -25,24 +33,38 @@ module Ridley
|
|
25
33
|
# * :password (String) the password for the user that will perform the bootstrap
|
26
34
|
# * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
|
27
35
|
def initialize(host, options = {})
|
28
|
-
|
29
|
-
@options
|
30
|
-
@host
|
31
|
-
@user
|
32
|
-
@password
|
33
|
-
@winrm_endpoint
|
36
|
+
options = options.deep_symbolize_keys
|
37
|
+
@options = options[:winrm] || Hash.new
|
38
|
+
@host = host
|
39
|
+
@user = @options[:user]
|
40
|
+
@password = @options[:password]
|
41
|
+
@winrm_endpoint = "http://#{host}:#{winrm_port}/wsman"
|
42
|
+
@command_uploaders = Array.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def finalizer
|
46
|
+
command_uploaders.map(&:cleanup)
|
34
47
|
end
|
35
48
|
|
36
49
|
def run(command)
|
37
|
-
|
50
|
+
command_uploaders << command_uploader = CommandUploader.new(winrm)
|
51
|
+
command = get_command(command, command_uploader)
|
38
52
|
|
39
53
|
response = Ridley::HostConnector::Response.new(host)
|
40
54
|
debug "Running WinRM Command: '#{command}' on: '#{host}' as: '#{user}'"
|
41
55
|
|
42
56
|
output = winrm.run_cmd(command) do |stdout, stderr|
|
43
|
-
|
44
|
-
|
57
|
+
if stdout
|
58
|
+
response.stdout += stdout
|
59
|
+
info "NODE[#{host}] #{stdout}"
|
60
|
+
end
|
61
|
+
|
62
|
+
if stderr
|
63
|
+
response.stderr += stderr unless stderr.nil?
|
64
|
+
info "NODE[#{host}] #{stdout}"
|
65
|
+
end
|
45
66
|
end
|
67
|
+
|
46
68
|
response.exit_code = output[:exitcode]
|
47
69
|
|
48
70
|
case response.exit_code
|
@@ -62,7 +84,7 @@ module Ridley
|
|
62
84
|
[ :error, response ]
|
63
85
|
end
|
64
86
|
|
65
|
-
# @return [
|
87
|
+
# @return [WinRM::WinRMWebService]
|
66
88
|
def winrm
|
67
89
|
::WinRM::WinRMWebService.new(winrm_endpoint, :plaintext, user: user, pass: password, disable_sspi: true, basic_auth_only: true)
|
68
90
|
end
|
@@ -73,53 +95,51 @@ module Ridley
|
|
73
95
|
end
|
74
96
|
|
75
97
|
# Returns the command if it does not break the WinRM command length
|
76
|
-
# limit. Otherwise, we return an execution of the command as a batch file.
|
98
|
+
# limit. Otherwise, we return an execution of the command as a batch file.
|
77
99
|
#
|
78
100
|
# @param command [String]
|
79
|
-
#
|
101
|
+
#
|
80
102
|
# @return [String]
|
81
|
-
def get_command(command)
|
82
|
-
if command.length <
|
103
|
+
def get_command(command, command_uploader)
|
104
|
+
if command.length < CommandUploader::CHUNK_LIMIT
|
83
105
|
command
|
84
106
|
else
|
85
|
-
debug "Detected a command that was longer than
|
86
|
-
|
107
|
+
debug "Detected a command that was longer than #{CommandUploader::CHUNK_LIMIT} characters, \
|
108
|
+
uploading command as a file to the host."
|
109
|
+
command_uploader.upload(command)
|
110
|
+
command_uploader.command
|
87
111
|
end
|
88
112
|
end
|
89
113
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
# @param command [String]
|
97
|
-
#
|
98
|
-
# @return [String] the command to execute the uploaded file
|
99
|
-
def upload_command_to_host(command)
|
100
|
-
base64_file = "winrm-upload-base64-#{Process.pid}-#{Time.now.to_i}"
|
101
|
-
base64_file_name = get_file_path(base64_file)
|
102
|
-
|
103
|
-
Base64.encode64(command).gsub("\n", '').chars.to_a.each_slice(8000 - base64_file_name.size) do |chunk|
|
104
|
-
out = winrm.run_cmd( "echo #{chunk.join} >> \"#{base64_file_name}\"" )
|
105
|
-
end
|
106
|
-
|
107
|
-
command_file = "winrm-upload-#{Process.pid}-#{Time.now.to_i}.bat"
|
108
|
-
command_file_name = get_file_path(command_file)
|
109
|
-
winrm.powershell <<-POWERSHELL
|
110
|
-
$base64_string = Get-Content \"#{base64_file_name}\"
|
111
|
-
$bytes = [System.Convert]::FromBase64String($base64_string)
|
112
|
-
$new_file = [System.IO.Path]::GetFullPath(\"#{command_file_name}\")
|
113
|
-
[System.IO.File]::WriteAllBytes($new_file,$bytes)
|
114
|
-
POWERSHELL
|
114
|
+
# Executes a chef-client run on the nodes
|
115
|
+
#
|
116
|
+
# @return [#run]
|
117
|
+
def chef_client
|
118
|
+
run("chef-client")
|
119
|
+
end
|
115
120
|
|
116
|
-
|
117
|
-
|
121
|
+
# Executes a copy of the encrypted_data_bag_secret to the nodes
|
122
|
+
#
|
123
|
+
# @param [String] encrypted_data_bag_secret_path
|
124
|
+
# the path to the encrypted_data_bag_secret
|
125
|
+
#
|
126
|
+
# @return [#run]
|
127
|
+
def put_secret(encrypted_data_bag_secret_path)
|
128
|
+
secret = File.read(encrypted_data_bag_secret_path).chomp
|
129
|
+
command = "echo #{secret} > C:\\chef\\encrypted_data_bag_secret"
|
130
|
+
run(command)
|
131
|
+
end
|
118
132
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
133
|
+
# Executes a provided Ruby script in the embedded Ruby installation
|
134
|
+
#
|
135
|
+
# @param [Array<String>] command_lines
|
136
|
+
# An Array of lines of the command to be executed
|
137
|
+
#
|
138
|
+
# @return [#run]
|
139
|
+
def ruby_script(command_lines)
|
140
|
+
command = "#{EMBEDDED_RUBY_PATH} -e \"#{command_lines.join(';')}\""
|
141
|
+
run(command)
|
142
|
+
end
|
123
143
|
end
|
124
144
|
end
|
125
145
|
end
|