ridley-connectors 2.2.0 → 2.3.0
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/lib/ridley-connectors/host_commander.rb +5 -90
- data/lib/ridley-connectors/host_connector.rb +87 -0
- data/lib/ridley-connectors/host_connector/ssh.rb +63 -2
- data/lib/ridley-connectors/host_connector/winrm.rb +17 -0
- data/lib/ridley-connectors/version.rb +1 -1
- data/ridley-connectors.gemspec +2 -1
- data/spec/unit/ridley-connectors/host_commander_spec.rb +36 -125
- data/spec/unit/ridley-connectors/host_connector/ssh_spec.rb +56 -0
- data/spec/unit/ridley-connectors/host_connector/winrm_spec.rb +16 -0
- data/spec/unit/ridley-connectors/host_connector_spec.rb +78 -0
- metadata +18 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0963bedc809b1573895731a6ef403acd8bb22d36
|
|
4
|
+
data.tar.gz: 585e09bb5fe887c8b234c93e447353c5c632eca4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85cd162eede9fb6ef393460caf4a71e77a00149242876906c10ea19c1d9d6665347d67856bc71f463d20fd7d59a267adca978c3e80fc3f5c960ffb49c6be50f2
|
|
7
|
+
data.tar.gz: bd05027c733e9b157de24014fcff761191a880e59f1a78366c23f625d7c9d401d9b12baa7aef021d758f20434c3374883c936dcd649ae36956601f8a104d5dd0
|
|
@@ -23,24 +23,11 @@ module Ridley
|
|
|
23
23
|
include Celluloid
|
|
24
24
|
include Ridley::Logging
|
|
25
25
|
|
|
26
|
-
PORT_CHECK_TIMEOUT = 3
|
|
27
|
-
RETRY_COUNT = 3
|
|
28
|
-
|
|
29
26
|
DEFAULT_WINDOWS_CONNECTOR = "winrm"
|
|
30
27
|
DEFAULT_LINUX_CONNECTOR = "ssh"
|
|
31
28
|
|
|
32
29
|
VALID_CONNECTORS = [ DEFAULT_WINDOWS_CONNECTOR, DEFAULT_LINUX_CONNECTOR ]
|
|
33
30
|
|
|
34
|
-
CONNECTOR_PORT_ERRORS = [
|
|
35
|
-
Errno::ETIMEDOUT, Timeout::Error, SocketError,
|
|
36
|
-
Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::EADDRNOTAVAIL,
|
|
37
|
-
Resolv::ResolvError
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
if Buff::RubyEngine.jruby?
|
|
41
|
-
CONNECTOR_PORT_ERRORS << Java::JavaNet::ConnectException
|
|
42
|
-
end
|
|
43
|
-
|
|
44
31
|
finalizer :finalize_callback
|
|
45
32
|
|
|
46
33
|
def initialize(connector_pool_size=nil)
|
|
@@ -228,6 +215,7 @@ module Ridley
|
|
|
228
215
|
# @option options [Hash] :ssh
|
|
229
216
|
# * :port (Fixnum) the ssh port to connect on the node the bootstrap will be performed on (22)
|
|
230
217
|
# * :timeout (Float) [5.0] timeout value for testing SSH connection
|
|
218
|
+
# * :gateway (String) user@host:port
|
|
231
219
|
# @option options [Hash] :winrm
|
|
232
220
|
# * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
|
|
233
221
|
# @option options [String] :connector
|
|
@@ -237,12 +225,6 @@ module Ridley
|
|
|
237
225
|
#
|
|
238
226
|
# @return [HostConnector::SSH, HostConnector::WinRM, NilClass]
|
|
239
227
|
def connector_for(host, options = {})
|
|
240
|
-
options[:ssh] ||= Hash.new
|
|
241
|
-
options[:winrm] ||= Hash.new
|
|
242
|
-
options[:ssh][:port] ||= HostConnector::SSH::DEFAULT_PORT
|
|
243
|
-
options[:winrm][:port] ||= HostConnector::WinRM::DEFAULT_PORT
|
|
244
|
-
options[:retries] ||= RETRY_COUNT
|
|
245
|
-
|
|
246
228
|
connector = options[:connector]
|
|
247
229
|
|
|
248
230
|
if !VALID_CONNECTORS.include?(connector)
|
|
@@ -250,10 +232,12 @@ module Ridley
|
|
|
250
232
|
connector = nil
|
|
251
233
|
end
|
|
252
234
|
|
|
253
|
-
if (connector == DEFAULT_WINDOWS_CONNECTOR || connector.nil?) &&
|
|
235
|
+
if (connector == DEFAULT_WINDOWS_CONNECTOR || connector.nil?) &&
|
|
236
|
+
winrm.connector_port_open?(host, options)
|
|
254
237
|
options.delete(:ssh)
|
|
255
238
|
winrm
|
|
256
|
-
elsif (connector == DEFAULT_LINUX_CONNECTOR || connector.nil?) &&
|
|
239
|
+
elsif (connector == DEFAULT_LINUX_CONNECTOR || connector.nil?) &&
|
|
240
|
+
ssh.connector_port_open?(host, options)
|
|
257
241
|
options.delete(:winrm)
|
|
258
242
|
ssh
|
|
259
243
|
else
|
|
@@ -286,75 +270,6 @@ module Ridley
|
|
|
286
270
|
end
|
|
287
271
|
end
|
|
288
272
|
|
|
289
|
-
# Checks to see if the given port is open for TCP connections
|
|
290
|
-
# on the given host.
|
|
291
|
-
#
|
|
292
|
-
# @param [String] host
|
|
293
|
-
# the host to attempt to connect to
|
|
294
|
-
# @param [Fixnum] port
|
|
295
|
-
# the port to attempt to connect on
|
|
296
|
-
# @param [Float] timeout ({PORT_CHECK_TIMEOUT})
|
|
297
|
-
# the number of seconds to wait
|
|
298
|
-
# @param [Int] retries ({RETRY_COUNT})
|
|
299
|
-
# the number of times to retry the connection before counting it unavailable
|
|
300
|
-
#
|
|
301
|
-
# @return [Boolean]
|
|
302
|
-
def connector_port_open?(host, port, timeout = PORT_CHECK_TIMEOUT, retries = RETRY_COUNT)
|
|
303
|
-
@retry_count = retries
|
|
304
|
-
begin
|
|
305
|
-
defer {
|
|
306
|
-
connectable?(host, port, timeout || PORT_CHECK_TIMEOUT)
|
|
307
|
-
}
|
|
308
|
-
rescue *CONNECTOR_PORT_ERRORS => ex
|
|
309
|
-
@retry_count -= 1
|
|
310
|
-
if @retry_count > 0
|
|
311
|
-
log.info { "Retrying connector_port_open? on '#{host}' #{port} due to: #{ex.class}" }
|
|
312
|
-
retry
|
|
313
|
-
end
|
|
314
|
-
false
|
|
315
|
-
end
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
# Check if a port on a host is able to be connected, failing if the timeout transpires.
|
|
319
|
-
#
|
|
320
|
-
# @param [String] host
|
|
321
|
-
# the host to attempt to connect to
|
|
322
|
-
# @param [Fixnum] port
|
|
323
|
-
# the port to attempt to connect on
|
|
324
|
-
# @param [Fixnum] timeout ({PORT_CHECK_TIMEOUT})
|
|
325
|
-
#
|
|
326
|
-
# @return [Boolean]
|
|
327
|
-
def connectable?(host, port, timeout = PORT_CHECK_TIMEOUT)
|
|
328
|
-
addr = Socket.getaddrinfo(host, nil)
|
|
329
|
-
sockaddr = Socket.pack_sockaddr_in(port, addr[0][3])
|
|
330
|
-
socket = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
|
|
331
|
-
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
332
|
-
|
|
333
|
-
success = false
|
|
334
|
-
begin
|
|
335
|
-
socket.connect_nonblock(sockaddr)
|
|
336
|
-
success = true
|
|
337
|
-
rescue ::IO::WaitWritable
|
|
338
|
-
if ::IO.select(nil, [socket], nil, timeout || PORT_CHECK_TIMEOUT)
|
|
339
|
-
begin
|
|
340
|
-
socket.connect_nonblock(sockaddr)
|
|
341
|
-
success = true
|
|
342
|
-
rescue Errno::EISCONN
|
|
343
|
-
success = true
|
|
344
|
-
rescue
|
|
345
|
-
begin
|
|
346
|
-
socket.close
|
|
347
|
-
rescue Errno::EBADF
|
|
348
|
-
# socket is not open
|
|
349
|
-
end
|
|
350
|
-
end
|
|
351
|
-
else
|
|
352
|
-
socket.close
|
|
353
|
-
end
|
|
354
|
-
end
|
|
355
|
-
success
|
|
356
|
-
end
|
|
357
|
-
|
|
358
273
|
def finalize_callback
|
|
359
274
|
@connector_supervisor.async.terminate if @connector_supervisor && @connector_supervisor.alive?
|
|
360
275
|
end
|
|
@@ -4,6 +4,20 @@ module Ridley
|
|
|
4
4
|
include Celluloid
|
|
5
5
|
include Ridley::Logging
|
|
6
6
|
|
|
7
|
+
PORT_CHECK_TIMEOUT = 3
|
|
8
|
+
RETRY_COUNT = 3
|
|
9
|
+
CONNECTOR_PORT_ERRORS = [Errno::ETIMEDOUT,
|
|
10
|
+
Timeout::Error,
|
|
11
|
+
SocketError,
|
|
12
|
+
Errno::ECONNREFUSED,
|
|
13
|
+
Errno::EHOSTUNREACH,
|
|
14
|
+
Errno::EADDRNOTAVAIL,
|
|
15
|
+
Resolv::ResolvError]
|
|
16
|
+
|
|
17
|
+
if Buff::RubyEngine.jruby?
|
|
18
|
+
CONNECTOR_PORT_ERRORS << Java::JavaNet::ConnectException
|
|
19
|
+
end
|
|
20
|
+
|
|
7
21
|
# Execute a shell command on a node
|
|
8
22
|
#
|
|
9
23
|
# @param [String] host
|
|
@@ -74,6 +88,79 @@ module Ridley
|
|
|
74
88
|
def uninstall_chef(host, options = {})
|
|
75
89
|
raise RuntimeError, "abstract function: must be implemented on includer"
|
|
76
90
|
end
|
|
91
|
+
|
|
92
|
+
def connector_port_open?
|
|
93
|
+
raise RuntimeError, "abstract function: must be implemented on includer"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Checks to see if the given port is open for TCP connections
|
|
97
|
+
# on the given host.
|
|
98
|
+
#
|
|
99
|
+
# @param [String] host
|
|
100
|
+
# the host to attempt to connect to
|
|
101
|
+
# @param [Fixnum] port
|
|
102
|
+
# the port to attempt to connect on
|
|
103
|
+
# @param [Float] timeout ({PORT_CHECK_TIMEOUT})
|
|
104
|
+
# the number of seconds to wait
|
|
105
|
+
# @param [Int] retries ({RETRY_COUNT})
|
|
106
|
+
# the number of times to retry the connection before counting it unavailable
|
|
107
|
+
#
|
|
108
|
+
# @return [Boolean]
|
|
109
|
+
def port_open?(host, port, timeout = PORT_CHECK_TIMEOUT, retries = RETRY_COUNT)
|
|
110
|
+
@retry_count = retries
|
|
111
|
+
begin
|
|
112
|
+
defer {
|
|
113
|
+
connectable?(host, port, timeout || PORT_CHECK_TIMEOUT)
|
|
114
|
+
}
|
|
115
|
+
rescue *CONNECTOR_PORT_ERRORS => ex
|
|
116
|
+
@retry_count -= 1
|
|
117
|
+
if @retry_count > 0
|
|
118
|
+
log.info { "Retrying connector_port_open? on '#{host}' #{port} due to: #{ex.class}" }
|
|
119
|
+
retry
|
|
120
|
+
end
|
|
121
|
+
false
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Check if a port on a host is able to be connected, failing if the timeout transpires.
|
|
126
|
+
#
|
|
127
|
+
# @param [String] host
|
|
128
|
+
# the host to attempt to connect to
|
|
129
|
+
# @param [Fixnum] port
|
|
130
|
+
# the port to attempt to connect on
|
|
131
|
+
# @param [Fixnum] timeout ({PORT_CHECK_TIMEOUT})
|
|
132
|
+
#
|
|
133
|
+
# @return [Boolean]
|
|
134
|
+
def connectable?(host, port, timeout = PORT_CHECK_TIMEOUT)
|
|
135
|
+
addr = Socket.getaddrinfo(host, nil)
|
|
136
|
+
sockaddr = Socket.pack_sockaddr_in(port, addr[0][3])
|
|
137
|
+
socket = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
|
|
138
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
139
|
+
|
|
140
|
+
success = false
|
|
141
|
+
begin
|
|
142
|
+
socket.connect_nonblock(sockaddr)
|
|
143
|
+
success = true
|
|
144
|
+
rescue ::IO::WaitWritable
|
|
145
|
+
if ::IO.select(nil, [socket], nil, timeout || PORT_CHECK_TIMEOUT)
|
|
146
|
+
begin
|
|
147
|
+
socket.connect_nonblock(sockaddr)
|
|
148
|
+
success = true
|
|
149
|
+
rescue Errno::EISCONN
|
|
150
|
+
success = true
|
|
151
|
+
rescue
|
|
152
|
+
begin
|
|
153
|
+
socket.close
|
|
154
|
+
rescue Errno::EBADF
|
|
155
|
+
# socket is not open
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
else
|
|
159
|
+
socket.close
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
success
|
|
163
|
+
end
|
|
77
164
|
end
|
|
78
165
|
|
|
79
166
|
require_relative 'host_connector/response'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'net/ssh'
|
|
2
|
+
require 'net/ssh/gateway'
|
|
2
3
|
|
|
3
4
|
module Ridley
|
|
4
5
|
module HostConnector
|
|
@@ -6,7 +7,8 @@ module Ridley
|
|
|
6
7
|
DEFAULT_PORT = 22
|
|
7
8
|
EMBEDDED_RUBY_PATH = '/opt/chef/embedded/bin/ruby'.freeze
|
|
8
9
|
|
|
9
|
-
# Execute a shell command on a node
|
|
10
|
+
# Execute a shell command on a node using ssh. If the gateway option is present then
|
|
11
|
+
# execute the command through the gateway.
|
|
10
12
|
#
|
|
11
13
|
# @param [String] host
|
|
12
14
|
# the host to perform the action on
|
|
@@ -18,6 +20,7 @@ module Ridley
|
|
|
18
20
|
# * :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password
|
|
19
21
|
# * :timeout (Float) timeout value for SSH bootstrap (5.0)
|
|
20
22
|
# * :sudo (Boolean) run as sudo
|
|
23
|
+
# * :gateway (String) user@host:port
|
|
21
24
|
#
|
|
22
25
|
# @return [HostConnector::Response]
|
|
23
26
|
def run(host, command, options = {})
|
|
@@ -31,7 +34,7 @@ module Ridley
|
|
|
31
34
|
log.info "Running SSH command: '#{command}' on: '#{host}' as: '#{options[:ssh][:user]}'"
|
|
32
35
|
|
|
33
36
|
defer {
|
|
34
|
-
|
|
37
|
+
ssh(host, options) do |ssh|
|
|
35
38
|
ssh.open_channel do |channel|
|
|
36
39
|
if options[:sudo]
|
|
37
40
|
channel.request_pty do |channel, success|
|
|
@@ -225,8 +228,66 @@ module Ridley
|
|
|
225
228
|
run(host, CommandContext::UnixUpdateOmnibus.command(options), options)
|
|
226
229
|
end
|
|
227
230
|
|
|
231
|
+
# Checks to see if the given port is open for TCP connections
|
|
232
|
+
# on the given host. If a gateway is provided in the ssh
|
|
233
|
+
# options, then return true if we can connect to the gateway host.
|
|
234
|
+
# If no gateway config is found then just verify we can connect to
|
|
235
|
+
# the destination host.
|
|
236
|
+
#
|
|
237
|
+
# @param [String] host
|
|
238
|
+
# the host to attempt to connect to
|
|
239
|
+
# @option options [Hash] :ssh
|
|
240
|
+
# * :gateway (String) user@host:port
|
|
241
|
+
# * :timeout (Float) timeout value for SSH
|
|
242
|
+
# * :port (Fixnum) the SSH port
|
|
243
|
+
# @return [Boolean]
|
|
244
|
+
def connector_port_open?(host, options = {})
|
|
245
|
+
options[:ssh] ||= Hash.new
|
|
246
|
+
options[:ssh][:port] ||= HostConnector::SSH::DEFAULT_PORT
|
|
247
|
+
|
|
248
|
+
if options[:ssh][:gateway]
|
|
249
|
+
gw_host, gw_port, _ = gateway(options)
|
|
250
|
+
log.info("Connecting to host '#{gw_host}' via SSH gateway over port '#{gw_port}'")
|
|
251
|
+
port_open?(gw_host, gw_port, options[:ssh][:timeout])
|
|
252
|
+
else
|
|
253
|
+
port_open?(host, options[:ssh][:port], options[:ssh][:timeout])
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
228
257
|
private
|
|
229
258
|
|
|
259
|
+
def gateway(options)
|
|
260
|
+
options[:ssh] ||= Hash.new
|
|
261
|
+
|
|
262
|
+
if options[:ssh][:gateway]
|
|
263
|
+
gw_host, gw_user = options[:ssh][:gateway].split("@").reverse
|
|
264
|
+
gw_host, gw_port = gw_host.split(":")
|
|
265
|
+
gw_port ||= HostConnector::SSH::DEFAULT_PORT
|
|
266
|
+
[gw_host, gw_port, gw_user]
|
|
267
|
+
else
|
|
268
|
+
[nil, nil, nil]
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Open an SSH connection either directly or through a gateway.
|
|
273
|
+
def ssh(host, options, &block)
|
|
274
|
+
if options[:ssh][:gateway]
|
|
275
|
+
gw_host, gw_port, gw_user = gateway(options)
|
|
276
|
+
gateway = Net::SSH::Gateway.new(gw_host, gw_user, {:port => gw_port})
|
|
277
|
+
begin
|
|
278
|
+
gateway.ssh(host, options[:ssh][:user], options[:ssh].slice(*Net::SSH::VALID_OPTIONS)) do |ssh|
|
|
279
|
+
yield ssh
|
|
280
|
+
end
|
|
281
|
+
ensure
|
|
282
|
+
gateway.shutdown!
|
|
283
|
+
end
|
|
284
|
+
else
|
|
285
|
+
Net::SSH.start(host, options[:ssh][:user], options[:ssh].slice(*Net::SSH::VALID_OPTIONS)) do |ssh|
|
|
286
|
+
yield ssh
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
230
291
|
def channel_exec(channel, command, host, response)
|
|
231
292
|
channel.exec(command) do |ch, success|
|
|
232
293
|
unless success
|
|
@@ -218,6 +218,23 @@ module Ridley
|
|
|
218
218
|
run(host, CommandContext::WindowsUpdateOmnibus.command(options), options)
|
|
219
219
|
end
|
|
220
220
|
|
|
221
|
+
# Checks to see if the given port is open for TCP connections
|
|
222
|
+
# on the given host.
|
|
223
|
+
#
|
|
224
|
+
# @param [String] host
|
|
225
|
+
# the host to attempt to connect to
|
|
226
|
+
# @option options [Hash] :winrm
|
|
227
|
+
# * port (Fixnum) the winrm port to connect to
|
|
228
|
+
# @option options [Fixnum] :retries
|
|
229
|
+
#
|
|
230
|
+
# @return [Boolean]
|
|
231
|
+
def connector_port_open?(host, options = {})
|
|
232
|
+
options[:winrm] ||= Hash.new
|
|
233
|
+
options[:winrm][:port] ||= HostConnector::WinRM::DEFAULT_PORT
|
|
234
|
+
|
|
235
|
+
port_open?(host, options[:winrm][:port], options[:retries])
|
|
236
|
+
end
|
|
237
|
+
|
|
221
238
|
private
|
|
222
239
|
|
|
223
240
|
# @param [String] host
|
data/ridley-connectors.gemspec
CHANGED
|
@@ -21,7 +21,8 @@ Gem::Specification.new do |s|
|
|
|
21
21
|
s.add_dependency 'celluloid-io', '~> 0.16.0.pre'
|
|
22
22
|
s.add_dependency 'erubis'
|
|
23
23
|
s.add_dependency 'net-ssh'
|
|
24
|
-
s.add_dependency '
|
|
24
|
+
s.add_dependency 'net-ssh-gateway'
|
|
25
|
+
s.add_dependency 'ridley', '~> 4.0'
|
|
25
26
|
s.add_dependency 'winrm', '~> 1.1.0'
|
|
26
27
|
|
|
27
28
|
s.add_development_dependency 'buff-ruby_engine', '~> 0.1'
|
|
@@ -12,8 +12,8 @@ describe Ridley::HostCommander do
|
|
|
12
12
|
|
|
13
13
|
context "when communicating to a unix node" do
|
|
14
14
|
before do
|
|
15
|
-
subject.
|
|
16
|
-
subject.
|
|
15
|
+
subject.send(:winrm).stub(:port_open?).and_return(false)
|
|
16
|
+
subject.send(:ssh).stub(:port_open?).and_return(true)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
it "sends a #run message to the ssh host connector" do
|
|
@@ -24,8 +24,8 @@ describe Ridley::HostCommander do
|
|
|
24
24
|
|
|
25
25
|
context "when communicating to a windows node" do
|
|
26
26
|
before do
|
|
27
|
-
subject.
|
|
28
|
-
subject.
|
|
27
|
+
subject.send(:winrm).stub(:port_open?).and_return(true)
|
|
28
|
+
subject.send(:ssh).stub(:port_open?).and_return(false)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
it "sends a #run message to the ssh host connector" do
|
|
@@ -43,8 +43,8 @@ describe Ridley::HostCommander do
|
|
|
43
43
|
|
|
44
44
|
context "when communicating to a unix node" do
|
|
45
45
|
before do
|
|
46
|
-
subject.
|
|
47
|
-
subject.
|
|
46
|
+
subject.send(:winrm).stub(:port_open?).and_return(false)
|
|
47
|
+
subject.send(:ssh).stub(:port_open?).and_return(true)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
it "sends a #bootstrap message to the ssh host connector" do
|
|
@@ -56,8 +56,8 @@ describe Ridley::HostCommander do
|
|
|
56
56
|
|
|
57
57
|
context "when communicating to a windows node" do
|
|
58
58
|
before do
|
|
59
|
-
subject.
|
|
60
|
-
subject.
|
|
59
|
+
subject.send(:winrm).stub(:port_open?).and_return(true)
|
|
60
|
+
subject.send(:ssh).stub(:port_open?).and_return(false)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
it "sends a #bootstrap message to the winrm host connector" do
|
|
@@ -75,8 +75,8 @@ describe Ridley::HostCommander do
|
|
|
75
75
|
|
|
76
76
|
context "when communicating to a unix node" do
|
|
77
77
|
before do
|
|
78
|
-
subject.
|
|
79
|
-
subject.
|
|
78
|
+
subject.send(:winrm).stub(:port_open?).and_return(false)
|
|
79
|
+
subject.send(:ssh).stub(:port_open?).and_return(true)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
it "sends a #chef_client message to the ssh host connector" do
|
|
@@ -88,8 +88,8 @@ describe Ridley::HostCommander do
|
|
|
88
88
|
|
|
89
89
|
context "when communicating to a windows node" do
|
|
90
90
|
before do
|
|
91
|
-
subject.
|
|
92
|
-
subject.
|
|
91
|
+
subject.send(:winrm).stub(:port_open?).and_return(true)
|
|
92
|
+
subject.send(:ssh).stub(:port_open?).and_return(false)
|
|
93
93
|
end
|
|
94
94
|
|
|
95
95
|
it "sends a #chef_client message to the ssh host connector" do
|
|
@@ -108,8 +108,8 @@ describe Ridley::HostCommander do
|
|
|
108
108
|
|
|
109
109
|
context "when communicating to a unix node" do
|
|
110
110
|
before do
|
|
111
|
-
subject.
|
|
112
|
-
subject.
|
|
111
|
+
subject.send(:winrm).stub(:port_open?).and_return(false)
|
|
112
|
+
subject.send(:ssh).stub(:port_open?).and_return(true)
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
it "sends a #put_secret message to the ssh host connector" do
|
|
@@ -121,8 +121,8 @@ describe Ridley::HostCommander do
|
|
|
121
121
|
|
|
122
122
|
context "when communicating to a windows node" do
|
|
123
123
|
before do
|
|
124
|
-
subject.
|
|
125
|
-
subject.
|
|
124
|
+
subject.send(:winrm).stub(:port_open?).and_return(true)
|
|
125
|
+
subject.send(:ssh).stub(:port_open?).and_return(false)
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
it "sends a #put_secret message to the ssh host connector" do
|
|
@@ -141,8 +141,8 @@ describe Ridley::HostCommander do
|
|
|
141
141
|
|
|
142
142
|
context "when communicating to a unix node" do
|
|
143
143
|
before do
|
|
144
|
-
subject.
|
|
145
|
-
subject.
|
|
144
|
+
subject.send(:winrm).stub(:port_open?).and_return(false)
|
|
145
|
+
subject.send(:ssh).stub(:port_open?).and_return(true)
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
it "sends a #ruby_script message to the ssh host connector" do
|
|
@@ -154,8 +154,8 @@ describe Ridley::HostCommander do
|
|
|
154
154
|
|
|
155
155
|
context "when communicating to a windows node" do
|
|
156
156
|
before do
|
|
157
|
-
subject.
|
|
158
|
-
subject.
|
|
157
|
+
subject.send(:winrm).stub(:port_open?).and_return(true)
|
|
158
|
+
subject.send(:ssh).stub(:port_open?).and_return(false)
|
|
159
159
|
end
|
|
160
160
|
|
|
161
161
|
it "sends a #ruby_script message to the ssh host connector" do
|
|
@@ -171,12 +171,12 @@ describe Ridley::HostCommander do
|
|
|
171
171
|
{ ssh: { port: 22, timeout: 3 }, winrm: { port: 5985, timeout: 3 }, retries: 3 }
|
|
172
172
|
end
|
|
173
173
|
|
|
174
|
-
context "when
|
|
174
|
+
context "when port_open? experiences an error" do
|
|
175
175
|
let(:socket) { double(close: true) }
|
|
176
176
|
|
|
177
177
|
it "executes retry logic" do
|
|
178
178
|
@times_called = 0
|
|
179
|
-
subject.should_receive(:connectable?).twice.and_return do
|
|
179
|
+
subject.send(:winrm).should_receive(:connectable?).twice.and_return do
|
|
180
180
|
@times_called += 1
|
|
181
181
|
if @times_called == 1
|
|
182
182
|
raise Errno::ETIMEDOUT
|
|
@@ -190,22 +190,14 @@ describe Ridley::HostCommander do
|
|
|
190
190
|
end
|
|
191
191
|
|
|
192
192
|
it "should return winrm if winrm is open" do
|
|
193
|
-
subject.
|
|
194
|
-
subject.
|
|
195
|
-
subject.connector_for(host)
|
|
193
|
+
subject.send(:winrm).stub(:port_open?).and_return(true)
|
|
194
|
+
expect(subject.connector_for(host).class).to eq(Ridley::HostConnector::WinRM)
|
|
196
195
|
end
|
|
197
196
|
|
|
198
197
|
it "should return ssh if winrm is closed" do
|
|
199
|
-
subject.
|
|
200
|
-
subject.
|
|
201
|
-
subject.
|
|
202
|
-
subject.connector_for(host)
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
it "should still set the default ports if an explicit nil is passed in" do
|
|
206
|
-
subject.stub(:connector_port_open?).with(host, Ridley::HostConnector::WinRM::DEFAULT_PORT, anything, anything).and_return(true)
|
|
207
|
-
subject.should_receive(:winrm)
|
|
208
|
-
subject.connector_for(host, winrm: nil, ssh: nil)
|
|
198
|
+
subject.send(:winrm).stub(:port_open?).and_return(false)
|
|
199
|
+
subject.send(:ssh).stub(:port_open?).and_return(true)
|
|
200
|
+
expect(subject.connector_for(host).class).to eq(Ridley::HostConnector::SSH)
|
|
209
201
|
end
|
|
210
202
|
|
|
211
203
|
context "when a connector of winrm is given" do
|
|
@@ -213,31 +205,28 @@ describe Ridley::HostCommander do
|
|
|
213
205
|
let(:winrm) { double }
|
|
214
206
|
|
|
215
207
|
it "should return winrm if winrm is open" do
|
|
216
|
-
subject.
|
|
217
|
-
subject.
|
|
218
|
-
expect(subject.connector_for(host, connector_options)).to eql(winrm)
|
|
208
|
+
subject.send(:winrm).stub(:port_open?).and_return(true)
|
|
209
|
+
expect(subject.connector_for(host, options).class).to eql(Ridley::HostConnector::WinRM)
|
|
219
210
|
end
|
|
220
211
|
|
|
221
212
|
it "should return nil if winrm is closed" do
|
|
222
|
-
subject.stub(:
|
|
213
|
+
subject.stub(:port_open?).and_return(false)
|
|
223
214
|
expect(subject.connector_for(host, connector_options)).to be_nil
|
|
224
215
|
end
|
|
225
216
|
end
|
|
226
217
|
|
|
227
218
|
context "when a connector of ssh is given" do
|
|
228
219
|
let(:connector_options) { options.merge(connector: "ssh") }
|
|
229
|
-
let(:ssh) { double }
|
|
230
220
|
|
|
231
221
|
it "should return ssh if ssh is open" do
|
|
232
|
-
subject.
|
|
233
|
-
subject.
|
|
234
|
-
subject.
|
|
235
|
-
expect(subject.connector_for(host, connector_options)).to eql(ssh)
|
|
222
|
+
subject.send(:ssh).stub(:port_open?).and_return(true)
|
|
223
|
+
subject.send(:winrm).should_not_receive(:port_open?)
|
|
224
|
+
expect(subject.connector_for(host, connector_options).class).to eql(Ridley::HostConnector::SSH)
|
|
236
225
|
end
|
|
237
226
|
|
|
238
227
|
it "should return nil if ssh is closed" do
|
|
239
|
-
subject.
|
|
240
|
-
subject.
|
|
228
|
+
subject.send(:ssh).stub(:port_open?).and_return(false)
|
|
229
|
+
subject.send(:winrm).should_not_receive(:port_open?)
|
|
241
230
|
expect(subject.connector_for(host, connector_options)).to be_nil
|
|
242
231
|
end
|
|
243
232
|
end
|
|
@@ -246,87 +235,9 @@ describe Ridley::HostCommander do
|
|
|
246
235
|
let(:connector_options) { options.merge(connector: "foo") }
|
|
247
236
|
|
|
248
237
|
it "should try both connectors" do
|
|
249
|
-
subject.should_receive(:
|
|
238
|
+
[:winrm, :ssh].each { |c| subject.send(c).should_receive(:port_open?) }
|
|
250
239
|
subject.connector_for(host, connector_options)
|
|
251
240
|
end
|
|
252
241
|
end
|
|
253
242
|
end
|
|
254
|
-
|
|
255
|
-
describe "#connectable?" do
|
|
256
|
-
let(:port) { 1234 }
|
|
257
|
-
|
|
258
|
-
before do
|
|
259
|
-
Socket
|
|
260
|
-
.stub(:getaddrinfo)
|
|
261
|
-
.with(host, nil)
|
|
262
|
-
.and_return [["AF_INET", 0, "33.33.33.10", "33.33.33.10", 2, 2, 17],
|
|
263
|
-
["AF_INET", 0, "33.33.33.10", "33.33.33.10", 2, 1, 6]]
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
context "when the target is accessible" do
|
|
267
|
-
before do
|
|
268
|
-
calls = 0
|
|
269
|
-
Socket.any_instance.stub(:connect_nonblock).and_return do
|
|
270
|
-
calls += 1
|
|
271
|
-
if calls == 1
|
|
272
|
-
raise WaitWritableError.new
|
|
273
|
-
end
|
|
274
|
-
raise Errno::EISCONN.new
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
it "should return true when a connection is initiated" do
|
|
279
|
-
::IO.stub(:select).and_return ["an array!"]
|
|
280
|
-
|
|
281
|
-
expect(subject.send(:connectable?, host, port)).to be_true
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
it "should return true when a connection is initiated and an explicit nil is passed as the timeout" do
|
|
285
|
-
::IO.stub(:select).with(anything, anything, anything, Ridley::HostCommander::PORT_CHECK_TIMEOUT).and_return ["an array!"]
|
|
286
|
-
|
|
287
|
-
expect(subject.send(:connectable?, host, port, nil)).to be_true
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
it "should return false when select times out" do
|
|
291
|
-
::IO.stub(:select).and_return nil
|
|
292
|
-
|
|
293
|
-
expect(subject.send(:connectable?, host, port)).to be_false
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
it "should return true when the connection does not have to wait" do
|
|
297
|
-
Socket.any_instance.stub(:connect_nonblock).and_return 0
|
|
298
|
-
|
|
299
|
-
expect(subject.send(:connectable?, host, port)).to be_true
|
|
300
|
-
end
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
Ridley::HostCommander::CONNECTOR_PORT_ERRORS.each do |error|
|
|
304
|
-
context "when the target causes #{error}" do
|
|
305
|
-
before do
|
|
306
|
-
calls = 0
|
|
307
|
-
Socket.any_instance.stub(:connect_nonblock).and_return do
|
|
308
|
-
calls += 1
|
|
309
|
-
if calls == 1
|
|
310
|
-
raise WaitWritableError.new
|
|
311
|
-
end
|
|
312
|
-
raise error.new
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
::IO.stub(:select).and_return []
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
context "should return false" do
|
|
319
|
-
it "" do
|
|
320
|
-
expect(subject.send(:connectable?, host, port)).to be_false
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
it "when the socket close throws EBAFD" do
|
|
324
|
-
Socket.any_instance.stub(:close).and_return { raise Errno::EBADF.new }
|
|
325
|
-
|
|
326
|
-
expect(subject.send(:connectable?, host, port)).to be_false
|
|
327
|
-
end
|
|
328
|
-
end
|
|
329
|
-
end
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
243
|
end
|
|
@@ -16,6 +16,34 @@ describe Ridley::HostConnector::SSH do
|
|
|
16
16
|
}
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
describe "#run" do
|
|
20
|
+
let(:ssh_user) { 'ssh_user' }
|
|
21
|
+
|
|
22
|
+
context "when a gateway is given" do
|
|
23
|
+
let(:gw_host) { "bar.com" }
|
|
24
|
+
let(:gw_user) { "foo" }
|
|
25
|
+
let(:gw_port) { "1234" }
|
|
26
|
+
let(:gateway) { double(Net::SSH::Gateway) }
|
|
27
|
+
|
|
28
|
+
before do
|
|
29
|
+
Net::SSH::Gateway.stub(:new).with(gw_host, gw_user, {:port => gw_port}).and_return(gateway)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should connect to the gateway with Net::SSH::Gateway and then to the destination host via the gateway" do
|
|
33
|
+
gateway.should_receive(:ssh).with(host, ssh_user, anything).ordered
|
|
34
|
+
gateway.should_receive(:shutdown!).ordered
|
|
35
|
+
subject.run(host, "some_command", ssh: { user: ssh_user, gateway: 'foo@bar.com:1234' })
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context "when a gateway is not given" do
|
|
40
|
+
it "should use Net::SSH to connect to the destination host" do
|
|
41
|
+
Net::SSH.should_receive(:start).with(host, ssh_user, {:paranoid=>false, :port=>"1234", :user=>"ssh_user"})
|
|
42
|
+
subject.run(host, "some_command", ssh: { user: ssh_user, port: "1234" })
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
19
47
|
describe "#bootstrap" do
|
|
20
48
|
let(:bootstrap_context) { Ridley::BootstrapContext::Unix.new(options) }
|
|
21
49
|
|
|
@@ -94,4 +122,32 @@ describe Ridley::HostConnector::SSH do
|
|
|
94
122
|
connector.update_omnibus(host, options)
|
|
95
123
|
end
|
|
96
124
|
end
|
|
125
|
+
|
|
126
|
+
describe "#connector_port_open?" do
|
|
127
|
+
context "when there are no ssh options specified" do
|
|
128
|
+
it "should try to connect to the host on the defaul ssh port" do
|
|
129
|
+
subject.should_receive(:port_open?).with(host, Ridley::HostConnector::SSH::DEFAULT_PORT, nil)
|
|
130
|
+
subject.connector_port_open?(host, {})
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
context "when the port is specified" do
|
|
135
|
+
it "should try to connecto to the host on the given port" do
|
|
136
|
+
subject.should_receive(:port_open?).with(host, 1234, nil)
|
|
137
|
+
subject.connector_port_open?(host, ssh: {port: 1234} )
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
context "when the gateway is given" do
|
|
142
|
+
it "should try to connect to the gateway host" do
|
|
143
|
+
subject.should_receive(:port_open?).with("bar.com", "1234", nil)
|
|
144
|
+
subject.connector_port_open?(host, ssh: {gateway: 'foo@bar.com:1234'} )
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it "should use the timeout from the ssh settings" do
|
|
148
|
+
subject.should_receive(:port_open?).with("bar.com", "1234", 12)
|
|
149
|
+
subject.connector_port_open?(host, ssh: {timeout: 12, gateway: 'foo@bar.com:1234'} )
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
97
153
|
end
|
|
@@ -180,4 +180,20 @@ describe Ridley::HostConnector::WinRM do
|
|
|
180
180
|
ruby_script
|
|
181
181
|
end
|
|
182
182
|
end
|
|
183
|
+
|
|
184
|
+
describe "#connector_port_open?" do
|
|
185
|
+
context "when no winrm options are specified" do
|
|
186
|
+
it "should connect using the default winrm port" do
|
|
187
|
+
subject.should_receive(:port_open?).with(host, Ridley::HostConnector::WinRM::DEFAULT_PORT, nil)
|
|
188
|
+
subject.connector_port_open?(host, {})
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
context "when winrm options are given" do
|
|
193
|
+
it "should use the winrm port" do
|
|
194
|
+
subject.should_receive(:port_open?).with(host, 1234, nil)
|
|
195
|
+
subject.connector_port_open?(host, winrm: {port: 1234})
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
183
199
|
end
|
|
@@ -47,4 +47,82 @@ describe Ridley::HostConnector::Base do
|
|
|
47
47
|
expect { subject.uninstall_chef(host, options) }.to raise_error(RuntimeError)
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
|
+
|
|
51
|
+
describe "#connectable?" do
|
|
52
|
+
let(:port) { 1234 }
|
|
53
|
+
|
|
54
|
+
before do
|
|
55
|
+
Socket
|
|
56
|
+
.stub(:getaddrinfo)
|
|
57
|
+
.with(host, nil)
|
|
58
|
+
.and_return [["AF_INET", 0, "33.33.33.10", "33.33.33.10", 2, 2, 17],
|
|
59
|
+
["AF_INET", 0, "33.33.33.10", "33.33.33.10", 2, 1, 6]]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "when the target is accessible" do
|
|
63
|
+
before do
|
|
64
|
+
calls = 0
|
|
65
|
+
Socket.any_instance.stub(:connect_nonblock).and_return do
|
|
66
|
+
calls += 1
|
|
67
|
+
if calls == 1
|
|
68
|
+
raise WaitWritableError.new
|
|
69
|
+
end
|
|
70
|
+
raise Errno::EISCONN.new
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "should return true when a connection is initiated" do
|
|
75
|
+
::IO.stub(:select).and_return ["an array!"]
|
|
76
|
+
|
|
77
|
+
expect(subject.send(:connectable?, host, port)).to be_true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "should return true when a connection is initiated and an explicit nil is passed as the timeout" do
|
|
81
|
+
::IO.stub(:select).with(anything, anything, anything, Ridley::HostConnector::Base::PORT_CHECK_TIMEOUT).and_return ["an array!"]
|
|
82
|
+
|
|
83
|
+
expect(subject.send(:connectable?, host, port, nil)).to be_true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should return false when select times out" do
|
|
87
|
+
::IO.stub(:select).and_return nil
|
|
88
|
+
|
|
89
|
+
expect(subject.send(:connectable?, host, port)).to be_false
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should return true when the connection does not have to wait" do
|
|
93
|
+
Socket.any_instance.stub(:connect_nonblock).and_return 0
|
|
94
|
+
|
|
95
|
+
expect(subject.send(:connectable?, host, port)).to be_true
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
Ridley::HostConnector::Base::CONNECTOR_PORT_ERRORS.each do |error|
|
|
100
|
+
context "when the target causes #{error}" do
|
|
101
|
+
before do
|
|
102
|
+
calls = 0
|
|
103
|
+
Socket.any_instance.stub(:connect_nonblock).and_return do
|
|
104
|
+
calls += 1
|
|
105
|
+
if calls == 1
|
|
106
|
+
raise WaitWritableError.new
|
|
107
|
+
end
|
|
108
|
+
raise error.new
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
::IO.stub(:select).and_return []
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
context "should return false" do
|
|
115
|
+
it "" do
|
|
116
|
+
expect(subject.send(:connectable?, host, port)).to be_false
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "when the socket close throws EBAFD" do
|
|
120
|
+
Socket.any_instance.stub(:close).and_return { raise Errno::EBADF.new }
|
|
121
|
+
|
|
122
|
+
expect(subject.send(:connectable?, host, port)).to be_false
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
50
128
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ridley-connectors
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jamie Winsor
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2014-
|
|
12
|
+
date: 2014-06-03 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: celluloid
|
|
@@ -67,20 +67,34 @@ dependencies:
|
|
|
67
67
|
- - ">="
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
69
|
version: '0'
|
|
70
|
+
- !ruby/object:Gem::Dependency
|
|
71
|
+
name: net-ssh-gateway
|
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - ">="
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0'
|
|
77
|
+
type: :runtime
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0'
|
|
70
84
|
- !ruby/object:Gem::Dependency
|
|
71
85
|
name: ridley
|
|
72
86
|
requirement: !ruby/object:Gem::Requirement
|
|
73
87
|
requirements:
|
|
74
88
|
- - "~>"
|
|
75
89
|
- !ruby/object:Gem::Version
|
|
76
|
-
version: '
|
|
90
|
+
version: '4.0'
|
|
77
91
|
type: :runtime
|
|
78
92
|
prerelease: false
|
|
79
93
|
version_requirements: !ruby/object:Gem::Requirement
|
|
80
94
|
requirements:
|
|
81
95
|
- - "~>"
|
|
82
96
|
- !ruby/object:Gem::Version
|
|
83
|
-
version: '
|
|
97
|
+
version: '4.0'
|
|
84
98
|
- !ruby/object:Gem::Dependency
|
|
85
99
|
name: winrm
|
|
86
100
|
requirement: !ruby/object:Gem::Requirement
|