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 +10 -0
- data/lib/rye/box.rb +58 -21
- data/lib/rye/cmd.rb +1 -0
- data/lib/rye/set.rb +18 -11
- data/lib/rye.rb +3 -0
- data/rye.gemspec +1 -1
- metadata +1 -1
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
|
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
|
-
|
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
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
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
|
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::
|
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
|
+
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"
|