delano-rye 0.7.4 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.txt CHANGED
@@ -7,6 +7,16 @@ TODO
7
7
  * Add S3 support for Rye::Box.upload / download
8
8
 
9
9
 
10
+ #### 0.7.5 (2009-06-14) #############################
11
+
12
+ * FIXED: Rye::Set methods were not accepting or passing blocks.
13
+ * ADDED: Rye::Set#user and Rye::Set#opts methods
14
+ * ADDED: Rye::Box#nickname
15
+ * ADDED: exception hooks for Rye::Box.
16
+ * CHANGE: Rye::Set method missing now forwards to Rye::Box
17
+ methods instead of Rye::Cmd
18
+
19
+
10
20
  #### 0.7.4 (2009-06-04) #############################
11
21
 
12
22
  * FIXED: Bug in Rye::Box#interactive_ssh related to instance variable renaming from 0.7.0.
data/lib/rye/box.rb CHANGED
@@ -34,9 +34,17 @@ module Rye
34
34
  def safe; @rye_safe; end
35
35
  def user; (@rye_opts || {})[:user]; end
36
36
 
37
+ # A storage area +@rye_stash+
38
+ def stash; @rye_stash; end
39
+ def nickname; @rye_nickname || host; end
40
+
37
41
  def host=(val); @rye_host = val; end
38
42
  def opts=(val); @rye_opts = val; end
39
43
 
44
+ # Returns the storage area +@rye_stash+
45
+ def stash=(val); @rye_stash = val; end
46
+ def nickname=(val); @rye_nickname = val; end
47
+
40
48
  def enable_safe_mode; @rye_safe = true; end
41
49
  def disable_safe_mode; @rye_safe = false; end
42
50
 
@@ -45,14 +53,15 @@ module Rye
45
53
 
46
54
  # The most recent valud for umask (or 0022)
47
55
  def current_umask; @rye_current_umask; end
48
-
49
- def ssh; @rye_ssh; end
56
+
50
57
  def info; @rye_info; end
51
58
  def debug; @rye_debug; end
52
59
  def error; @rye_error; end
53
60
 
54
61
  def pre_command_hook=(val); @rye_pre_command_hook = val; end
55
62
  def post_command_hook=(val); @rye_post_command_hook = val; end
63
+ # A Hash. The keys are exception classes, the values are Procs to execute
64
+ def exception_hook=(val); @rye_exception_hook = val; end
56
65
 
57
66
  # * +host+ The hostname to connect to. The default is localhost.
58
67
  # * +opts+ a hash of optional arguments.
@@ -72,6 +81,7 @@ module Rye
72
81
  # Net::SSH.start that is not already mentioned above.
73
82
  #
74
83
  def initialize(host='localhost', opts={})
84
+ @rye_exception_hook = {}
75
85
  @rye_host = host
76
86
 
77
87
  # These opts are use by Rye::Box and also passed to Net::SSH
@@ -222,7 +232,7 @@ module Rye
222
232
  debug "ssh-add stdout: #{ret.stdout}"
223
233
  debug "ssh-add stderr: #{ret.stderr}"
224
234
  end
225
- self #MUST RETURN itself
235
+ self # MUST RETURN self
226
236
  end
227
237
  alias :add_key :add_keys
228
238
 
@@ -279,8 +289,8 @@ module Rye
279
289
  def to_s; '%s@rye_%s' % [user, @rye_host]; end
280
290
 
281
291
  def inspect
282
- %q{#<%s:%s cwd=%s umask=%s env=%s safe=%s opts=%s>} %
283
- [self.class.to_s, self.host,
292
+ %q{#<%s:%s name=%s cwd=%s umask=%s env=%s safe=%s opts=%s>} %
293
+ [self.class.to_s, self.host, self.nickname,
284
294
  @rye_current_working_directory, @rye_current_umask,
285
295
  (@rye_current_environment_variables || '').inspect,
286
296
  self.safe, self.opts.inspect]
@@ -424,7 +434,9 @@ module Rye
424
434
  # A handler for undefined commands.
425
435
  # Raises Rye::CommandNotFound exception.
426
436
  def method_missing(meth, *args, &block)
427
- raise Rye::CommandNotFound, "#{meth.to_s}"
437
+ ex = Rye::CommandNotFound.new(meth.to_s)
438
+ raise ex unless @rye_exception_hook.has_key? ex.class
439
+ @rye_exception_hook[Rye::CommandNotFound].call ex
428
440
  end
429
441
 
430
442
  # Returns the command an arguments as a String.
@@ -444,6 +456,27 @@ module Rye
444
456
  @rye_pre_command_hook
445
457
  end
446
458
 
459
+ # Supply a block to be called whenever there's an Exception. It's called
460
+ # with 1 argument: the exception class. If the exception block returns
461
+ # :retry, the command will be executed again.
462
+ #
463
+ # e.g.
464
+ # rbox.exception_hook(CommandNotFound) do |ex|
465
+ # STDERR.puts "An error occurred: #{ex.class}"
466
+ # choice = Annoy.get_user_input('(S)kip (R)etry (A)bort: ')
467
+ # if choice == 'R'
468
+ # :retry
469
+ # elsif choice == 'S'
470
+ # # do nothing
471
+ # else
472
+ # exit # !
473
+ # end
474
+ # end
475
+ def exception_hook(klass, &block)
476
+ @rye_exception_hook[klass] = block if block
477
+ @rye_exception_hook[klass]
478
+ end
479
+
447
480
  # Execute a block in the context of an instance of Rye::Box.
448
481
  #
449
482
  # rbox = Rye::Box.new
@@ -622,12 +655,11 @@ module Rye
622
655
  cmd_clean = [cwd, cmd_clean].join(' && ')
623
656
  end
624
657
 
625
-
626
658
  info "COMMAND: #{cmd_clean}"
627
659
  debug "Executing: %s" % cmd_clean
628
660
 
629
661
  if @rye_pre_command_hook.is_a?(Proc)
630
- @rye_pre_command_hook.call(cmd, args, user, host)
662
+ @rye_pre_command_hook.call(cmd, args, user, host, nickname)
631
663
  end
632
664
 
633
665
  ## NOTE: Do not raise a CommandNotFound exception in this method.
@@ -639,25 +671,30 @@ module Rye
639
671
  # this is good enough for now.
640
672
  ## raise Rye::CommandNotFound unless self.can?(cmd)
641
673
 
642
- stdout, stderr, ecode, esignal = net_ssh_exec!(cmd_clean)
643
-
644
- rap = Rye::Rap.new(self)
645
- rap.add_stdout(stdout || '')
646
- rap.add_stderr(stderr || '')
647
- rap.add_exit_code(ecode)
648
- rap.exit_signal = esignal
649
- rap.cmd = cmd
650
-
651
- if @rye_post_command_hook.is_a?(Proc)
652
- @rye_post_command_hook.call(rap)
653
- else
674
+ begin
675
+ stdout, stderr, ecode, esignal = net_ssh_exec!(cmd_clean)
676
+
677
+ rap = Rye::Rap.new(self)
678
+ rap.add_stdout(stdout || '')
679
+ rap.add_stderr(stderr || '')
680
+ rap.add_exit_code(ecode)
681
+ rap.exit_signal = esignal
682
+ rap.cmd = cmd
683
+
654
684
  # It seems a convention for various commands to return -1
655
685
  # when something only mildly concerning happens. ls even
656
686
  # returns -1 for apparently no reason sometimes. In any
657
687
  # case, the real errors are the ones greater than zero
658
688
  raise Rye::CommandError.new(rap) if ecode > 0
689
+
690
+ rescue Exception => ex
691
+ raise ex unless @rye_exception_hook.has_key? ex.class
692
+ ret = @rye_exception_hook[ex.class].call(ex)
693
+ retry if ret == :retry
659
694
  end
660
695
 
696
+ @rye_post_command_hook.call(rap) if @rye_post_command_hook.is_a?(Proc)
697
+
661
698
  rap
662
699
  end
663
700
  alias :cmd :run_command
@@ -722,7 +759,7 @@ module Rye
722
759
 
723
760
  channel.wait # block until we get a response
724
761
  channel.request_pty do |ch, success|
725
- raise "Could not obtain pty (i.e. an interactive ssh session)" if !success
762
+ raise Rye::NoPty if !success
726
763
  end
727
764
 
728
765
  channel[:exit_code] = 0 if channel[:exit_code] == nil
data/lib/rye/cmd.rb CHANGED
@@ -67,6 +67,7 @@ module Rye;
67
67
  def chown(*args); cmd('chown', args); end
68
68
  def unzip(*args); cmd('unzip', args); end
69
69
  def bzip2(*args); cmd('bzip2', args); end
70
+ def which(*args); cmd('which', args); end
70
71
 
71
72
  def umount(*args); cmd("umount", args); end
72
73
  def uptime(*args); cmd("uptime", args); end
data/lib/rye/set.rb CHANGED
@@ -42,6 +42,9 @@ module Rye
42
42
  add_keys(@opts[:keys])
43
43
  end
44
44
 
45
+ def opts; @opts; end
46
+ def user; (@opts || {})[:user]; end
47
+
45
48
  # * +boxes+ one or more boxes. Rye::Box objects will be added directly
46
49
  # to the set. Hostnames will be used to create new instances of Rye::Box
47
50
  # and those will be added to the list.
@@ -97,7 +100,12 @@ module Rye
97
100
  run_command(:cd, key)
98
101
  self
99
102
  end
100
-
103
+
104
+ # Are there any boxes in this set?
105
+ def empty?
106
+ @boxes.nil? || @boxes.empty?
107
+ end
108
+
101
109
  # Catches calls to Rye::Box commands. If +meth+ is the name of an
102
110
  # instance method defined in Rye::Cmd then we call it against all
103
111
  # the boxes in +@boxes+. Otherwise this method raises a
@@ -105,33 +113,32 @@ module Rye
105
113
  # exception if this set has no boxes defined.
106
114
  #
107
115
  # Returns a Rye::Rap object containing the responses from each Rye::Box.
108
- def method_missing(meth, *args)
116
+ def method_missing(meth, *args, &block)
109
117
  # Ruby 1.8 populates Module.instance_methods with Strings. 1.9 uses Symbols.
110
118
  meth = (Rye.sysinfo.ruby[1] == 8) ? meth.to_s : meth.to_sym
111
119
  raise Rye::NoBoxes if @boxes.empty?
112
- raise Rye::CommandNotFound, meth.to_s unless Rye::Cmd.instance_methods.member?(meth)
113
- run_command(meth, *args)
120
+ raise Rye::CommandNotFound, meth.to_s unless Rye::Box.instance_methods.member?(meth)
121
+ run_command(meth, *args, &block)
114
122
  end
115
123
 
116
124
  private
117
125
 
118
126
  # Determines whether to call the serial or parallel method, then calls it.
119
- def run_command(meth, *args)
127
+ def run_command(meth, *args, &block)
120
128
  runner = @parallel ? :run_command_parallel : :run_command_serial
121
- self.send(runner, meth, *args)
129
+ self.send(runner, meth, *args, &block)
122
130
  end
123
131
 
124
132
 
125
133
  # Run the command on all boxes in parallel
126
- def run_command_parallel(meth, *args)
127
- p @boxes
134
+ def run_command_parallel(meth, *args, &block)
128
135
  debug "P: #{meth} on #{@boxes.size} boxes (#{@boxes.collect {|b| b.host }.join(', ')})"
129
136
  threads = []
130
137
 
131
138
  raps = Rye::Rap.new(self)
132
139
  (@boxes || []).each do |box|
133
140
  threads << Thread.new do
134
- Thread.current[:rap] = box.send(meth, *args) # Store the result in the thread
141
+ Thread.current[:rap] = box.send(meth, *args, &block) # Store the result in the thread
135
142
  end
136
143
  end
137
144
 
@@ -148,11 +155,11 @@ module Rye
148
155
 
149
156
 
150
157
  # Run the command on all boxes in serial
151
- def run_command_serial(meth, *args)
158
+ def run_command_serial(meth, *args, &block)
152
159
  debug "S: #{meth} on #{@boxes.size} boxes (#{@boxes.collect {|b| b.host }.join(', ')})"
153
160
  raps = Rye::Rap.new(self)
154
161
  (@boxes || []).each do |box|
155
- raps << box.send(meth, *args)
162
+ raps << box.send(meth, *args, &block)
156
163
  end
157
164
  raps
158
165
  end
data/lib/rye.rb CHANGED
@@ -63,6 +63,9 @@ module Rye
63
63
  class NoHost < RuntimeError; end
64
64
  class NotConnected < RuntimeError; end
65
65
  class CommandNotFound < RuntimeError; end
66
+ class NoPty < RuntimeError
67
+ def message; "Could not obtain pty (i.e. an interactive ssh session)"; end
68
+ end
66
69
  class CommandError < RuntimeError
67
70
  attr_reader :rap
68
71
  # * +rap+ a Rye::Rap object
data/rye.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  @spec = Gem::Specification.new do |s|
2
2
  s.name = "rye"
3
3
  s.rubyforge_project = "rye"
4
- s.version = "0.7.4"
4
+ s.version = "0.7.5"
5
5
  s.summary = "Rye: Safely run SSH commands on a bunch of machines at the same time (from Ruby)."
6
6
  s.description = s.summary
7
7
  s.author = "Delano Mandelbaum"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delano-rye
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 0.7.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum