ztk 0.2.4 → 0.2.5

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.
@@ -32,24 +32,22 @@ module ZTK
32
32
  class << self
33
33
 
34
34
  def try(options={}, &block)
35
- !block_given? and raise RescueRetryError, "You must supply a block!"
35
+ options = Base.build_config({
36
+ :tries => 1,
37
+ :on => Exception
38
+ }.merge(options))
39
+ options.logger.debug { "options(#{options.inspect})" }
36
40
 
37
- options = { :logger => $logger, :tries => 1, :on => Exception }.merge(options)
38
-
39
- logger = options[:logger]
40
- tries = options[:tries]
41
- on = options[:on]
42
-
43
- logger and logger.debug { options.inspect }
41
+ !block_given? and Base.log_and_raise(options.logger, RescueRetryError, "You must supply a block!")
44
42
 
45
43
  begin
46
44
  return block.call
47
- rescue on => e
48
- if ((tries -= 1) > 0)
49
- logger and logger.warn { "Caught #{e.inspect}, we will give it #{tries} more tr#{tries > 1 ? 'ies' : 'y'}." }
45
+ rescue options.on => e
46
+ if ((options.tries -= 1) > 0)
47
+ options.logger.warn { "Caught #{e.inspect}, we will give it #{options.tries} more tr#{options.tries > 1 ? 'ies' : 'y'}." }
50
48
  retry
51
49
  else
52
- logger and logger.fatal { "Caught #{e.inspect}, sorry, we have to give up now." }
50
+ options.logger.fatal { "Caught #{e.inspect}, sorry, we have to give up now." }
53
51
  raise e
54
52
  end
55
53
  end
@@ -36,15 +36,22 @@ module ZTK
36
36
 
37
37
  class << self
38
38
 
39
- def spin(stdout=STDOUT)
39
+ def spin(options={}, &block)
40
+ options = Base.build_config({
41
+ :sleep => 0.1
42
+ }.merge(options))
43
+ options.logger.debug { "options(#{options.inspect})" }
44
+
45
+ !block_given? and Base.log_and_raise(options.logger, SpinnerError, "You must supply a block!")
46
+
40
47
  charset = %w( | / - \\ )
41
48
  count = 0
42
49
  spinner = Thread.new do
43
50
  while count do
44
- stdout.print(charset[(count += 1) % charset.length])
45
- stdout.print("\b")
46
- stdout.respond_to?(:flush) and stdout.flush
47
- sleep(0.1)
51
+ options.stdout.print(charset[(count += 1) % charset.length])
52
+ options.stdout.print("\b")
53
+ options.stdout.respond_to?(:flush) and options.stdout.flush
54
+ sleep(options.sleep)
48
55
  end
49
56
  end
50
57
  yield.tap do
@@ -101,37 +101,50 @@ module ZTK
101
101
  # authentication.
102
102
  # @option config [String, Array<String>] :proxy_keys A single or series of
103
103
  # identity files to use for authentication with the proxy.
104
- def initialize(config={})
104
+ def initialize(configuration={})
105
105
  super({
106
106
  :forward_agent => true,
107
107
  :compression => false,
108
- :user_known_hosts_file => '/dev/null'
109
- }.merge(config))
108
+ :user_known_hosts_file => '/dev/null',
109
+ :timeout => 60
110
+ }.merge(configuration))
111
+ config.logger.debug { "config(#{config.inspect})" }
110
112
  end
111
113
 
112
114
  def inspect
113
- user_host = "#{@config.user}@#{@config.host_name}"
114
- port = (@config.port ? ":#{@config.port}" : nil)
115
- [user_host, port].compact.join
115
+ tags = Array.new
116
+
117
+ if config.proxy_host_name
118
+ proxy_user_host = "#{config.proxy_user}@#{config.proxy_host_name}"
119
+ proxy_port = (config.proxy_port ? ":#{config.proxy_port}" : nil)
120
+ tags << [proxy_user_host, proxy_port].compact.join
121
+ tags << " >>> "
122
+ end
123
+
124
+ user_host = "#{config.user}@#{config.host_name}"
125
+ port = (config.port ? ":#{config.port}" : nil)
126
+ tags << [user_host, port].compact.join
127
+
128
+ tags.join.strip
116
129
  end
117
130
 
118
131
  # Starts an SSH session. Can also be used to get the Net::SSH object.
119
132
  #
120
133
  # Primarily used internally.
121
134
  def ssh
122
- @ssh ||= Net::SSH.start(@config.host_name, @config.user, ssh_options)
135
+ @ssh ||= Net::SSH.start(config.host_name, config.user, ssh_options)
123
136
  end
124
137
 
125
138
  # Starts an SFTP session. Can also be used to get the Net::SSH object.
126
139
  #
127
140
  # Primarily used internally.
128
141
  def sftp
129
- @sftp ||= Net::SFTP.start(@config.host_name, @config.user, ssh_options)
142
+ @sftp ||= Net::SFTP.start(config.host_name, config.user, ssh_options)
130
143
  end
131
144
 
132
145
  # Close our session gracefully.
133
146
  def close
134
- log(:debug) { "close" }
147
+ config.logger.debug { "close" }
135
148
  ssh and !ssh.closed? and ssh.close
136
149
  end
137
150
 
@@ -147,8 +160,8 @@ module ZTK
147
160
  # end
148
161
  # ssh.console
149
162
  def console
150
- log(:info) { "console(#{console_command.inspect})" }
151
- log(:debug) { "config(#{@config.inspect})" }
163
+ config.logger.debug { "config(#{config.inspect})" }
164
+ config.logger.info { "console(#{console_command.inspect})" }
152
165
 
153
166
  Kernel.exec(console_command)
154
167
  end
@@ -183,63 +196,89 @@ module ZTK
183
196
  "#{header}\n"
184
197
  end
185
198
 
186
- log(:debug) { "config(#{@config.inspect})" }
187
- log(:info) { "exec(#{command.inspect}, #{options.inspect})" }
199
+ options = OpenStruct.new({ :exit_code => 0, :silence => false }.merge(options))
188
200
 
189
- options = OpenStruct.new({ :silence => false }.merge(options))
190
- log(:debug) { "options(#{options.inspect})" }
201
+ config.logger.debug { "config(#{config.inspect})" }
202
+ config.logger.debug { "options(#{options.inspect})" }
203
+ config.logger.info { "exec(#{command.inspect}, #{options.inspect})" }
191
204
 
192
205
  output = ""
206
+ exit_code = nil
207
+ exit_signal = nil
193
208
  stdout_header = false
194
209
  stderr_header = false
195
210
 
196
- ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
197
- @ssh = Net::SSH.start(@config.host_name, @config.user, ssh_options)
198
-
199
- channel = ssh.open_channel do |chan|
200
- log(:debug) { "Channel opened." }
201
- direct_log(:debug) { log_header("OPENED") }
202
-
203
- chan.exec(command) do |ch, success|
204
- raise SSHError, "Could not execute '#{command}'." unless success
211
+ begin
212
+ Timeout.timeout(config.timeout) do
213
+ ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
214
+ @ssh = Net::SSH.start(config.host_name, config.user, ssh_options)
215
+
216
+ channel = ssh.open_channel do |chan|
217
+ config.logger.debug { "Channel opened." }
218
+
219
+ direct_log(:debug) { log_header("COMMAND") }
220
+ direct_log(:debug) { "#{command}\n" }
221
+ direct_log(:debug) { log_header("OPENED") }
222
+
223
+ chan.exec(command) do |ch, success|
224
+ success or log_and_raise(SSHError, "Could not execute '#{command}'.")
225
+
226
+ ch.on_data do |c, data|
227
+ if !stdout_header
228
+ direct_log(:debug) { log_header("STDOUT") }
229
+ stdout_header = true
230
+ stderr_header = false
231
+ end
232
+ direct_log(:debug) { data }
233
+
234
+ config.stdout.print(data) unless options.silence
235
+ output += data
236
+ end
237
+
238
+ ch.on_extended_data do |c, type, data|
239
+ if !stderr_header
240
+ direct_log(:warn) { log_header("STDERR") }
241
+ stderr_header = true
242
+ stdout_header = false
243
+ end
244
+ direct_log(:warn) { data }
245
+
246
+ config.stderr.print(data) unless options.silence
247
+ output += data
248
+ end
249
+
250
+ ch.on_request("exit-status") do |ch, data|
251
+ exit_code = data.read_long
252
+ end
253
+
254
+ ch.on_request("exit-signal") do |ch, data|
255
+ exit_signal = data.read_long
256
+ end
257
+
258
+ ch.on_open_failed do |c, code, desc|
259
+ config.logger.fatal { "Open failed! (#{code.inspect} - #{desc.inspect})" }
260
+ end
205
261
 
206
- ch.on_data do |c, data|
207
- if !stdout_header
208
- direct_log(:debug) { log_header("STDOUT") }
209
- stdout_header = true
210
- stderr_header = false
211
262
  end
212
- direct_log(:debug) { data }
213
-
214
- @config.stdout.print(data) unless options.silence
215
- output += data
216
- end
217
-
218
- ch.on_extended_data do |c, type, data|
219
- if !stderr_header
220
- direct_log(:warn) { log_header("STDERR") }
221
- stderr_header = true
222
- stdout_header = false
223
- end
224
- direct_log(:warn) { data }
225
-
226
- @config.stderr.print(data) unless options.silence
227
- output += data
228
- end
229
-
230
- ch.on_open_failed do |c, code, desc|
231
- log(:fatal) { "Open failed! (#{code.inspect} - #{desc.inspect})" }
232
263
  end
264
+ channel.wait
233
265
 
266
+ direct_log(:debug) { log_header("CLOSED") }
267
+ config.logger.debug { "Channel closed." }
234
268
  end
235
269
  end
236
- channel.wait
237
270
 
238
- direct_log(:debug) { log_header("CLOSED") }
239
- log(:debug) { "Channel closed." }
271
+ rescue Timeout::Error => e
272
+ direct_log(:debug) { log_header("TIMEOUT") }
273
+ log_and_raise(SSHError, "Session timed out after #{config.timeout} seconds!")
240
274
  end
241
275
 
242
- OpenStruct.new(:output => output, :exit => $?)
276
+ config.logger.debug { "exit_code(#{exit_code}), exit_signal(#{exit_signal})" }
277
+
278
+ if (exit_code != options.exit_code)
279
+ log_and_raise(SSHError, "exec(#{command.inspect}, #{options.inspect}) failed! [#{exit_code}, #{exit_signal}]")
280
+ end
281
+ OpenStruct.new(:output => output, :exit_code => exit_code, :exit_signal => exit_signal)
243
282
  end
244
283
 
245
284
  # Uploads a local file to a remote host.
@@ -258,23 +297,23 @@ module ZTK
258
297
  # remote = File.expand_path(File.join("/tmp", "id_rsa.pub"))
259
298
  # ssh.upload(local, remote)
260
299
  def upload(local, remote)
261
- log(:debug) { "config(#{@config.inspect})" }
262
- log(:info) { "upload(#{local.inspect}, #{remote.inspect})" }
300
+ config.logger.debug { "config(#{config.inspect})" }
301
+ config.logger.info { "upload(#{local.inspect}, #{remote.inspect})" }
263
302
 
264
303
  ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
265
- @sftp = Net::SFTP.start(@config.host_name, @config.user, ssh_options)
304
+ @sftp = Net::SFTP.start(config.host_name, config.user, ssh_options)
266
305
  sftp.upload!(local.to_s, remote.to_s) do |event, uploader, *args|
267
306
  case event
268
307
  when :open
269
- log(:debug) { "upload(#{args[0].local} -> #{args[0].remote})" }
308
+ config.logger.debug { "upload(#{args[0].local} -> #{args[0].remote})" }
270
309
  when :close
271
- log(:debug) { "close(#{args[0].remote})" }
310
+ config.logger.debug { "close(#{args[0].remote})" }
272
311
  when :mkdir
273
- log(:debug) { "mkdir(#{args[0]})" }
312
+ config.logger.debug { "mkdir(#{args[0]})" }
274
313
  when :put
275
- log(:debug) { "put(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
314
+ config.logger.debug { "put(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
276
315
  when :finish
277
- log(:debug) { "finish" }
316
+ config.logger.debug { "finish" }
278
317
  end
279
318
  end
280
319
  end
@@ -298,23 +337,23 @@ module ZTK
298
337
  # remote = File.expand_path(File.join(ENV["HOME"], ".ssh", "id_rsa.pub"))
299
338
  # ssh.download(remote, local)
300
339
  def download(remote, local)
301
- log(:debug) { "config(#{@config.inspect})" }
302
- log(:info) { "download(#{remote.inspect}, #{local.inspect})" }
340
+ config.logger.debug { "config(#{config.inspect})" }
341
+ config.logger.info { "download(#{remote.inspect}, #{local.inspect})" }
303
342
 
304
343
  ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
305
- @sftp = Net::SFTP.start(@config.host_name, @config.user, ssh_options)
344
+ @sftp = Net::SFTP.start(config.host_name, config.user, ssh_options)
306
345
  sftp.download!(remote.to_s, local.to_s) do |event, downloader, *args|
307
346
  case event
308
347
  when :open
309
- log(:debug) { "download(#{args[0].remote} -> #{args[0].local})" }
348
+ config.logger.debug { "download(#{args[0].remote} -> #{args[0].local})" }
310
349
  when :close
311
- log(:debug) { "close(#{args[0].local})" }
350
+ config.logger.debug { "close(#{args[0].local})" }
312
351
  when :mkdir
313
- log(:debug) { "mkdir(#{args[0]})" }
352
+ config.logger.debug { "mkdir(#{args[0]})" }
314
353
  when :get
315
- log(:debug) { "get(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
354
+ config.logger.debug { "get(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
316
355
  when :finish
317
- log(:debug) { "finish" }
356
+ config.logger.debug { "finish" }
318
357
  end
319
358
  end
320
359
  end
@@ -334,28 +373,19 @@ module ZTK
334
373
  command << [ "-o", "StrictHostKeyChecking=no" ]
335
374
  command << [ "-o", "KeepAlive=yes" ]
336
375
  command << [ "-o", "ServerAliveInterval=60" ]
337
- command << [ "-i", @config.keys ] if @config.keys
338
- command << [ "-p", @config.port ] if @config.port
339
- command << [ "-o", "ProxyCommand=\"#{proxy_command}\"" ] if @config.proxy_host_name
340
- command << "#{@config.user}@#{@config.host_name}"
376
+ command << [ "-i", config.keys ] if config.keys
377
+ command << [ "-p", config.port ] if config.port
378
+ command << [ "-o", "ProxyCommand=\"#{proxy_command}\"" ] if config.proxy_host_name
379
+ command << "#{config.user}@#{config.host_name}"
341
380
  command = command.flatten.compact.join(" ")
342
- log(:debug) { "console_command(#{command.inspect})" }
381
+ config.logger.debug { "console_command(#{command.inspect})" }
343
382
  command
344
383
  end
345
384
 
346
385
  # Builds our SSH proxy command.
347
386
  def proxy_command
348
- if !@config.proxy_user
349
- message = "You must specify an proxy user in order to SSH proxy."
350
- log(:fatal) { message }
351
- raise SSHError, message
352
- end
353
-
354
- if !@config.proxy_host_name
355
- message = "You must specify an proxy host_name in order to SSH proxy."
356
- log(:fatal) { message }
357
- raise SSHError, message
358
- end
387
+ !config.proxy_user and log_and_raise(SSHError, "You must specify an proxy user in order to SSH proxy.")
388
+ !config.proxy_host_name and log_and_raise(SSHError, "You must specify an proxy host_name in order to SSH proxy.")
359
389
 
360
390
  command = ["ssh"]
361
391
  command << [ "-q" ]
@@ -364,12 +394,12 @@ module ZTK
364
394
  command << [ "-o", "StrictHostKeyChecking=no" ]
365
395
  command << [ "-o", "KeepAlive=yes" ]
366
396
  command << [ "-o", "ServerAliveInterval=60" ]
367
- command << [ "-i", @config.proxy_keys ] if @config.proxy_keys
368
- command << [ "-p", @config.proxy_port ] if @config.proxy_port
369
- command << "#{@config.proxy_user}@#{@config.proxy_host_name}"
397
+ command << [ "-i", config.proxy_keys ] if config.proxy_keys
398
+ command << [ "-p", config.proxy_port ] if config.proxy_port
399
+ command << "#{config.proxy_user}@#{config.proxy_host_name}"
370
400
  command << "nc %h %p"
371
401
  command = command.flatten.compact.join(" ")
372
- log(:debug) { "proxy_command(#{command.inspect})" }
402
+ config.logger.debug { "proxy_command(#{command.inspect})" }
373
403
  command
374
404
  end
375
405
 
@@ -378,29 +408,29 @@ module ZTK
378
408
  options = {}
379
409
 
380
410
  # These are plainly documented on the Net::SSH config class.
381
- options.merge!(:encryption => @config.encryption) if @config.encryption
382
- options.merge!(:compression => @config.compression) if @config.compression
383
- options.merge!(:compression_level => @config.compression_level) if @config.compression_level
384
- options.merge!(:timeout => @config.timeout) if @config.timeout
385
- options.merge!(:forward_agent => @config.forward_agent) if @config.forward_agent
386
- options.merge!(:global_known_hosts_file => @config.global_known_hosts_file) if @config.global_known_hosts_file
387
- options.merge!(:auth_methods => @config.auth_methods) if @config.auth_methods
388
- options.merge!(:host_key => @config.host_key) if @config.host_key
389
- options.merge!(:host_key_alias => @config.host_key_alias) if @config.host_key_alias
390
- options.merge!(:host_name => @config.host_name) if @config.host_name
391
- options.merge!(:keys => @config.keys) if @config.keys
392
- options.merge!(:keys_only => @config.keys_only) if @config.keys_only
393
- options.merge!(:hmac => @config.hmac) if @config.hmac
394
- options.merge!(:port => @config.port) if @config.port
395
- options.merge!(:proxy => Net::SSH::Proxy::Command.new(proxy_command)) if @config.proxy_host_name
396
- options.merge!(:rekey_limit => @config.rekey_limit) if @config.rekey_limit
397
- options.merge!(:user => @config.user) if @config.user
398
- options.merge!(:user_known_hosts_file => @config.user_known_hosts_file) if @config.user_known_hosts_file
411
+ options.merge!(:encryption => config.encryption) if config.encryption
412
+ options.merge!(:compression => config.compression) if config.compression
413
+ options.merge!(:compression_level => config.compression_level) if config.compression_level
414
+ options.merge!(:timeout => config.timeout) if config.timeout
415
+ options.merge!(:forward_agent => config.forward_agent) if config.forward_agent
416
+ options.merge!(:global_known_hosts_file => config.global_known_hosts_file) if config.global_known_hosts_file
417
+ options.merge!(:auth_methods => config.auth_methods) if config.auth_methods
418
+ options.merge!(:host_key => config.host_key) if config.host_key
419
+ options.merge!(:host_key_alias => config.host_key_alias) if config.host_key_alias
420
+ options.merge!(:host_name => config.host_name) if config.host_name
421
+ options.merge!(:keys => config.keys) if config.keys
422
+ options.merge!(:keys_only => config.keys_only) if config.keys_only
423
+ options.merge!(:hmac => config.hmac) if config.hmac
424
+ options.merge!(:port => config.port) if config.port
425
+ options.merge!(:proxy => Net::SSH::Proxy::Command.new(proxy_command)) if config.proxy_host_name
426
+ options.merge!(:rekey_limit => config.rekey_limit) if config.rekey_limit
427
+ options.merge!(:user => config.user) if config.user
428
+ options.merge!(:user_known_hosts_file => config.user_known_hosts_file) if config.user_known_hosts_file
399
429
 
400
430
  # This is not plainly documented on the Net::SSH config class.
401
- options.merge!(:password => @config.password) if @config.password
431
+ options.merge!(:password => config.password) if config.password
402
432
 
403
- log(:debug) { "ssh_options(#{options.inspect})" }
433
+ config.logger.debug { "ssh_options(#{options.inspect})" }
404
434
  options
405
435
  end
406
436
 
@@ -90,11 +90,12 @@ module ZTK
90
90
  # @option config [Integer] :timeout (5) Set the IO select timeout.
91
91
  # @option config [Integer] :wait (60) Set the amount of time before the wait
92
92
  # method call will timeout.
93
- def initialize(config={})
93
+ def initialize(configuration={})
94
94
  super({
95
95
  :timeout => 5,
96
96
  :wait => 60
97
- }.merge(config))
97
+ }.merge(configuration))
98
+ config.logger.debug { "config(#{config.inspect})" }
98
99
  end
99
100
 
100
101
  # Check to see if socket on the host and port specified is ready. This
@@ -104,30 +105,21 @@ module ZTK
104
105
  # @return [Boolean] Returns true or false depending on weither the socket
105
106
  # is ready or not.
106
107
  def ready?
107
- if @config.host.nil?
108
- message = "You must supply a host!"
109
- log(:fatal) { message }
110
- raise TCPSocketCheckError, message
111
- end
112
-
113
- if @config.port.nil?
114
- message = "You must supply a port!"
115
- log(:fatal) { message }
116
- raise TCPSocketCheckError, message
117
- end
108
+ config.host.nil? and log_and_raise(TCPSocketCheckError, "You must supply a host!")
109
+ config.port.nil? and log_and_raise(TCPSocketCheckError, "You must supply a port!")
118
110
 
119
- socket = TCPSocket.new(@config.host, @config.port)
111
+ socket = TCPSocket.new(config.host, config.port)
120
112
 
121
- if @config.data.nil?
122
- log(:debug) { "read(#{@config.host}:#{@config.port})" }
123
- ((IO.select([socket], nil, nil, @config.timeout) && socket.gets) ? true : false)
113
+ if config.data.nil?
114
+ config.logger.debug { "read(#{config.host}:#{config.port})" }
115
+ ((IO.select([socket], nil, nil, config.timeout) && socket.gets) ? true : false)
124
116
  else
125
- log(:debug) { "write(#{@config.host}:#{@config.port}, '#{@config.data}')" }
126
- ((IO.select(nil, [socket], nil, @config.timeout) && socket.write(@config.data)) ? true : false)
117
+ config.logger.debug { "write(#{config.host}:#{config.port}, #{config.data.size} bytes)" }
118
+ ((IO.select(nil, [socket], nil, config.timeout) && socket.write(config.data)) ? true : false)
127
119
  end
128
120
 
129
121
  rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH => e
130
- log(:debug) { "#{@config.host}:#{@config.port} - #{e.message}" }
122
+ config.logger.debug { "#{config.host}:#{config.port} - #{e.message}" }
131
123
  false
132
124
  ensure
133
125
  (socket && socket.close)
@@ -140,16 +132,16 @@ module ZTK
140
132
  # @return [Boolean] Returns true or false depending on weither the socket
141
133
  # became ready or not.
142
134
  def wait
143
- log(:debug) { "waiting for socket to become available; timeout after #{@config.wait} seconds" }
144
- Timeout.timeout(@config.wait) do
135
+ config.logger.debug { "Waiting for socket to become available; timeout after #{config.wait} seconds." }
136
+ Timeout.timeout(config.wait) do
145
137
  until ready?
146
- log(:debug) { "sleeping 1 second" }
138
+ config.logger.debug { "Sleeping 1 second." }
147
139
  sleep(1)
148
140
  end
149
141
  end
150
142
  true
151
143
  rescue Timeout::Error => e
152
- log(:warn) { "socket(#{@config.host}:#{@config.port}) timeout!" }
144
+ config.logger.warn { "socket(#{config.host}:#{config.port}) timeout!" }
153
145
  false
154
146
  end
155
147