ztk 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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