ridley 0.10.0.rc1 → 0.10.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|