delano-rye 0.7.6 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +12 -0
- data/README.rdoc +15 -7
- data/lib/rye.rb +3 -6
- data/lib/rye/box.rb +50 -21
- data/lib/rye/cmd.rb +5 -5
- data/lib/rye/set.rb +11 -7
- data/rye.gemspec +1 -1
- metadata +1 -1
data/CHANGES.txt
CHANGED
@@ -7,6 +7,18 @@ TODO
|
|
7
7
|
* Add S3 support for Rye::Box.upload / download
|
8
8
|
|
9
9
|
|
10
|
+
#### 0.8.0 (2009-06-21) #############################
|
11
|
+
|
12
|
+
* FIXED: safely and unsafely (Rye::Box) now return the block return value
|
13
|
+
* ADDED: Rye::Set#parallel and Rye::Set#parallel= methods
|
14
|
+
* ADDED: Rye::Box#quietly
|
15
|
+
* CHANGE: Updated Rye::Set#inspect and Rye::Set#to_s
|
16
|
+
* CHANGE: Exception hook now receives: ex, cmd, user, host, nickname
|
17
|
+
* CHANGE: Increased Kernel.sleep to 0.03 in Rye::Set.run_command_parallel
|
18
|
+
* CHANGE: Renamed Rye::Cmd#upload and Rye::Cmd#download to file_upload and file_download
|
19
|
+
* CHANGE: Rye::Cmd#file_exists? now executes quietly (doesn't pollute logging)
|
20
|
+
|
21
|
+
|
10
22
|
#### 0.7.6 (2009-06-19) #############################
|
11
23
|
|
12
24
|
* FIXED: Raise Rye::NoPty exception when Net::SSH returns message
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Rye - v0.
|
1
|
+
= Rye - v0.8
|
2
2
|
|
3
3
|
Safely run SSH commands on a bunch of machines at the same time (from Ruby).
|
4
4
|
|
@@ -42,7 +42,7 @@ You can specify environment variables
|
|
42
42
|
rbox.env # => ['HOME=/home/rye', 'RYE=Forty Creek', ...]
|
43
43
|
|
44
44
|
|
45
|
-
== EXAMPLE
|
45
|
+
== EXAMPLE 3a -- Accessing Multiple Machines
|
46
46
|
|
47
47
|
rset = Rye::Set.new
|
48
48
|
rbox = Rye::Box.new
|
@@ -54,25 +54,33 @@ Calling methods on Rye::Set objects is very similar to calling them on Rye::Box
|
|
54
54
|
p rset.uptime # => [[14:19:02 up 32 days, 19:35 ...], [14:19:02 up 30 days, 01:35]]
|
55
55
|
p rset['/etc'].ls # => [['file1', 'file2', ...], ['life1', 'life2', ...]]
|
56
56
|
|
57
|
-
|
58
|
-
|
57
|
+
== EXAMPLE 3b -- Accessing Multiple Machines -- In Parallel
|
58
|
+
|
59
|
+
By default, Rye:Set connects to each machine sequentially in the order they were added to the set. Commands can also be run in parallel:
|
60
|
+
|
61
|
+
rset = Rye::Set.new('set-name', :parallel => true)
|
62
|
+
OR
|
63
|
+
rset.parallel = true
|
64
|
+
|
65
|
+
|
66
|
+
== EXAMPLE 4 -- File Transfers
|
59
67
|
|
60
68
|
rbox = Rye::Box.new("localhost", :info => true)
|
61
69
|
|
62
70
|
dir_upload = "#{Rye.sysinfo.tmpdir}/rye-upload/"
|
63
71
|
dir_download = "#{Rye.sysinfo.tmpdir}/rye-download/"
|
64
72
|
|
65
|
-
rbox.
|
73
|
+
rbox.file_upload("#{RYE_HOME}/README.rdoc",
|
66
74
|
"#{RYE_HOME}/LICENSE.txt", dir_upload)
|
67
75
|
|
68
76
|
applejack = StringIO.new("Some in-memory content")
|
69
|
-
rbox.
|
77
|
+
rbox.file_upload(applejack, "#{dir_upload}/applejack.txt")
|
70
78
|
|
71
79
|
p rbox.ls(dir_upload) # => [README.rdoc, LICENSE.txt, applejack.txt]
|
72
80
|
p rbox.cat("#{dir_upload}/applejack.txt") # => "Some in-memory content"
|
73
81
|
|
74
82
|
filecontent = StringIO.new
|
75
|
-
rbox.
|
83
|
+
rbox.file_download("#{dir_upload}/applejack.txt", filecontent)
|
76
84
|
|
77
85
|
p filecontent.read
|
78
86
|
|
data/lib/rye.rb
CHANGED
@@ -1,8 +1,4 @@
|
|
1
1
|
|
2
|
-
# Temporary fix before Highline 1.5.1 is release. This fixes
|
3
|
-
# a the issue with Ruby 1.9 that causes the prompts to hang.
|
4
|
-
$:.unshift File.join(File.dirname(__FILE__), '..', 'vendor', 'highline-1.5.1', 'lib')
|
5
|
-
|
6
2
|
require 'logger'
|
7
3
|
require 'thread'
|
8
4
|
require 'base64'
|
@@ -47,7 +43,7 @@ module Rye
|
|
47
43
|
extend self
|
48
44
|
|
49
45
|
unless defined?(SYSINFO)
|
50
|
-
VERSION = 0.
|
46
|
+
VERSION = "0.8.0".freeze
|
51
47
|
SYSINFO = SysInfo.new.freeze
|
52
48
|
end
|
53
49
|
|
@@ -205,7 +201,8 @@ module Rye
|
|
205
201
|
# NOTE: shell is a bit paranoid so it escapes every argument. This means
|
206
202
|
# you can only use literal values. That means no asterisks too.
|
207
203
|
#
|
208
|
-
# Returns a Rye::Rap object
|
204
|
+
# Returns a Rye::Rap object.
|
205
|
+
#
|
209
206
|
def shell(cmd, *args)
|
210
207
|
args = args.flatten.compact
|
211
208
|
cmd = cmd.to_s if cmd.is_a?(Symbol)
|
data/lib/rye/box.rb
CHANGED
@@ -34,20 +34,24 @@ module Rye
|
|
34
34
|
def safe; @rye_safe; end
|
35
35
|
def user; (@rye_opts || {})[:user]; end
|
36
36
|
|
37
|
-
#
|
37
|
+
# Returns the current value of the stash +@rye_stash+
|
38
38
|
def stash; @rye_stash; end
|
39
|
+
def quiet; @rye_quiet; end
|
39
40
|
def nickname; @rye_nickname || host; end
|
40
|
-
|
41
|
+
|
41
42
|
def host=(val); @rye_host = val; end
|
42
43
|
def opts=(val); @rye_opts = val; end
|
43
44
|
|
44
|
-
#
|
45
|
+
# Store a value to the stash +@rye_stash+
|
45
46
|
def stash=(val); @rye_stash = val; end
|
46
47
|
def nickname=(val); @rye_nickname = val; end
|
47
48
|
|
48
49
|
def enable_safe_mode; @rye_safe = true; end
|
49
50
|
def disable_safe_mode; @rye_safe = false; end
|
50
51
|
|
52
|
+
def enable_quiet_mode; @rye_quiet = true; end
|
53
|
+
def disable_quiet_mode; @rye_quiet = false; end
|
54
|
+
|
51
55
|
# The most recent value from Box.cd or Box.[]
|
52
56
|
def current_working_directory; @rye_current_working_directory; end
|
53
57
|
|
@@ -94,6 +98,7 @@ module Rye
|
|
94
98
|
:debug => nil,
|
95
99
|
:error => STDERR,
|
96
100
|
:getenv => true,
|
101
|
+
:quiet => false
|
97
102
|
}.merge(opts)
|
98
103
|
|
99
104
|
# Close the SSH session before Ruby exits. This will do nothing
|
@@ -105,6 +110,7 @@ module Rye
|
|
105
110
|
@rye_safe, @rye_debug = @rye_opts.delete(:safe), @rye_opts.delete(:debug)
|
106
111
|
@rye_info, @rye_error = @rye_opts.delete(:info), @rye_opts.delete(:error)
|
107
112
|
@rye_getenv = {} if @rye_opts.delete(:getenv) # Enable getenv with a hash
|
113
|
+
@rye_quiet = @rye_opts.delete(:quiet)
|
108
114
|
|
109
115
|
# Just in case someone sends a true value rather than IO object
|
110
116
|
@rye_debug = STDERR if @rye_debug == true
|
@@ -279,11 +285,11 @@ module Rye
|
|
279
285
|
def to_s; '%s@rye_%s' % [user, @rye_host]; end
|
280
286
|
|
281
287
|
def inspect
|
282
|
-
%q{#<%s:%s name=%s cwd=%s umask=%s env=%s safe=%s opts=%s>} %
|
288
|
+
%q{#<%s:%s name=%s cwd=%s umask=%s env=%s safe=%s opts=%s keys=%s>} %
|
283
289
|
[self.class.to_s, self.host, self.nickname,
|
284
290
|
@rye_current_working_directory, @rye_current_umask,
|
285
291
|
(@rye_current_environment_variables || '').inspect,
|
286
|
-
self.safe, self.opts.inspect]
|
292
|
+
self.safe, self.opts.inspect, self.keys.inspect]
|
287
293
|
end
|
288
294
|
|
289
295
|
# Compares itself with the +other+ box. If the hostnames
|
@@ -372,7 +378,7 @@ module Rye
|
|
372
378
|
if self.file_exists?(akey_path)
|
373
379
|
# TODO: Make Rye::Cmd.incremental_backup
|
374
380
|
self.cp(akey_path, "#{akey_path}-previous")
|
375
|
-
authorized_keys = self.
|
381
|
+
authorized_keys = self.file_download("#{homedir}/#{akey_path}")
|
376
382
|
end
|
377
383
|
authorized_keys ||= StringIO.new
|
378
384
|
|
@@ -391,7 +397,7 @@ module Rye
|
|
391
397
|
authorized_keys.rewind
|
392
398
|
|
393
399
|
self.mkdir(:p, :m, '700', File.dirname(akey_path))
|
394
|
-
self.
|
400
|
+
self.file_upload(authorized_keys, "#{homedir}/#{akey_path}")
|
395
401
|
self.chmod('0600', akey_path)
|
396
402
|
self.chown(:R, this_user.to_s, File.dirname(akey_path))
|
397
403
|
end
|
@@ -490,18 +496,35 @@ module Rye
|
|
490
496
|
def unsafely(*args, &block)
|
491
497
|
previous_state = @rye_safe
|
492
498
|
disable_safe_mode
|
493
|
-
self.instance_exec *args, &block
|
499
|
+
ret = self.instance_exec *args, &block
|
494
500
|
@rye_safe = previous_state
|
501
|
+
ret
|
495
502
|
end
|
496
503
|
|
497
504
|
# See unsafely (except in reverse)
|
498
505
|
def safely(*args, &block)
|
499
506
|
previous_state = @rye_safe
|
500
507
|
enable_safe_mode
|
501
|
-
self.instance_exec *args, &block
|
508
|
+
ret = self.instance_exec *args, &block
|
502
509
|
@rye_safe = previous_state
|
510
|
+
ret
|
503
511
|
end
|
504
512
|
|
513
|
+
# Like batch, except it enables quiet mode before executing the block.
|
514
|
+
# After executing the block, quiet mode is returned back to whichever
|
515
|
+
# state it was previously in. In other words, this method won't enable
|
516
|
+
# quiet mode if it was already disabled.
|
517
|
+
#
|
518
|
+
# In quiet mode, the pre and post command hooks are not called. This
|
519
|
+
# is used internally when calling commands like +ls+ to check whether
|
520
|
+
# a file path exists (to prevent polluting the logs).
|
521
|
+
def quietly(*args, &block)
|
522
|
+
previous_state = @rye_quiet
|
523
|
+
enable_quiet_mode
|
524
|
+
ret = self.instance_exec *args, &block
|
525
|
+
@rye_quiet = previous_state
|
526
|
+
ret
|
527
|
+
end
|
505
528
|
|
506
529
|
# instance_exec for Ruby 1.8 written by Mauricio Fernandez
|
507
530
|
# http://eigenclass.org/hiki/instance_exec
|
@@ -646,22 +669,24 @@ module Rye
|
|
646
669
|
raise Rye::NotConnected, @rye_host unless @rye_ssh && !@rye_ssh.closed?
|
647
670
|
|
648
671
|
cmd_clean = Rye.escape(@rye_safe, cmd, args)
|
649
|
-
|
672
|
+
|
673
|
+
# This following is the command we'll actually execute. cmd_clean
|
674
|
+
# can be used for logging, otherwise the output is confusing.
|
675
|
+
cmd_internal = prepend_env(cmd_clean)
|
650
676
|
|
651
677
|
# Add the current working directory before the command if supplied.
|
652
678
|
# The command will otherwise run in the user's home directory.
|
653
679
|
if @rye_current_working_directory
|
654
680
|
cwd = Rye.escape(@rye_safe, 'cd', @rye_current_working_directory)
|
655
|
-
|
681
|
+
cmd_internal = [cwd, cmd_internal].join(' && ')
|
656
682
|
end
|
657
683
|
|
658
684
|
# ditto (same explanation as cwd)
|
659
685
|
if @rye_current_umask
|
660
686
|
cwd = Rye.escape(@rye_safe, 'umask', @rye_current_umask)
|
661
|
-
|
687
|
+
cmd_internal = [cwd, cmd_internal].join(' && ')
|
662
688
|
end
|
663
689
|
|
664
|
-
|
665
690
|
## NOTE: Do not raise a CommandNotFound exception in this method.
|
666
691
|
# We want it to be possible to define methods to a single instance
|
667
692
|
# of Rye::Box. i.e. def rbox.rm()...
|
@@ -673,20 +698,21 @@ module Rye
|
|
673
698
|
|
674
699
|
begin
|
675
700
|
info "COMMAND: #{cmd_clean}"
|
676
|
-
debug "Executing:
|
701
|
+
debug "Executing: #{cmd_internal}"
|
677
702
|
|
678
|
-
if @rye_pre_command_hook.is_a?(Proc)
|
703
|
+
if !@rye_quiet && @rye_pre_command_hook.is_a?(Proc)
|
679
704
|
@rye_pre_command_hook.call(cmd_clean, user, host, nickname)
|
680
705
|
end
|
681
706
|
|
682
|
-
stdout, stderr, ecode, esignal = net_ssh_exec!(cmd_clean)
|
683
|
-
|
684
707
|
rap = Rye::Rap.new(self)
|
708
|
+
rap.cmd = cmd_clean
|
709
|
+
|
710
|
+
stdout, stderr, ecode, esignal = net_ssh_exec!(cmd_internal)
|
711
|
+
|
685
712
|
rap.add_stdout(stdout || '')
|
686
713
|
rap.add_stderr(stderr || '')
|
687
714
|
rap.add_exit_code(ecode)
|
688
715
|
rap.exit_signal = esignal
|
689
|
-
rap.cmd = cmd
|
690
716
|
|
691
717
|
#info "stdout: #{rap.stdout}"
|
692
718
|
#info "stderr: #{rap.stderr}"
|
@@ -699,10 +725,11 @@ module Rye
|
|
699
725
|
raise Rye::CommandError.new(rap) if ecode != 0
|
700
726
|
|
701
727
|
rescue Exception => ex
|
728
|
+
return rap if @rye_quiet
|
702
729
|
choice = nil
|
703
730
|
@rye_exception_hook.each_pair do |klass,act|
|
704
731
|
next unless ex.kind_of? klass
|
705
|
-
choice =
|
732
|
+
choice = act.call(ex, cmd_clean, user, host, nickname)
|
706
733
|
break
|
707
734
|
end
|
708
735
|
if choice == :retry
|
@@ -714,7 +741,9 @@ module Rye
|
|
714
741
|
end
|
715
742
|
end
|
716
743
|
|
717
|
-
|
744
|
+
if !@rye_quiet && @rye_post_command_hook.is_a?(Proc)
|
745
|
+
@rye_post_command_hook.call(rap)
|
746
|
+
end
|
718
747
|
|
719
748
|
rap
|
720
749
|
end
|
@@ -775,7 +804,7 @@ module Rye
|
|
775
804
|
raise Rye::NoPty if data =~ /Pseudo-terminal will not/
|
776
805
|
|
777
806
|
end
|
778
|
-
|
807
|
+
|
779
808
|
end
|
780
809
|
|
781
810
|
channel = @rye_ssh.exec(command, &block)
|
data/lib/rye/cmd.rb
CHANGED
@@ -92,7 +92,7 @@ module Rye;
|
|
92
92
|
# Always return nil.
|
93
93
|
#
|
94
94
|
# NOTE: Changes to current working directory with +cd+ or +[]+ are ignored.
|
95
|
-
def
|
95
|
+
def file_upload(*files); net_scp_transfer!(:upload, *files); end
|
96
96
|
|
97
97
|
# Transfer files from a machine via Net::SCP.
|
98
98
|
# * +files+ is an Array of files to download. The last element must be the
|
@@ -105,7 +105,7 @@ module Rye;
|
|
105
105
|
# Return nil or a StringIO object, if specified as the target.
|
106
106
|
#
|
107
107
|
# NOTE: Changes to current working directory with +cd+ or +[]+ are ignored.
|
108
|
-
def
|
108
|
+
def file_download(*files); net_scp_transfer!(:download, *files); end
|
109
109
|
|
110
110
|
# Append +newcontent+ to remote +filepath+. If the file doesn't exist
|
111
111
|
# it will be created. If +backup+ is specified, +filepath+ will be
|
@@ -113,7 +113,7 @@ module Rye;
|
|
113
113
|
def file_append(filepath, newcontent, backup=false)
|
114
114
|
if self.file_exists?(filepath)
|
115
115
|
self.cp filepath, "#{filepath}-previous" if backup
|
116
|
-
file_content = self.
|
116
|
+
file_content = self.file_download filepath
|
117
117
|
end
|
118
118
|
|
119
119
|
file_content ||= StringIO.new
|
@@ -124,13 +124,13 @@ module Rye;
|
|
124
124
|
file_content.puts newcontent
|
125
125
|
end
|
126
126
|
|
127
|
-
self.
|
127
|
+
self.file_upload file_content, filepath
|
128
128
|
end
|
129
129
|
|
130
130
|
# Does +path+ from the current working directory?
|
131
131
|
def file_exists?(path)
|
132
132
|
begin
|
133
|
-
ret = self.ls(path)
|
133
|
+
ret = self.quietly { ls(path) }
|
134
134
|
rescue Rye::CommandError => ex
|
135
135
|
ret = ex.rap
|
136
136
|
end
|
data/lib/rye/set.rb
CHANGED
@@ -8,6 +8,10 @@ module Rye
|
|
8
8
|
attr_reader :boxes
|
9
9
|
attr_reader :opts
|
10
10
|
|
11
|
+
# Run commands in parallel? A Boolean value. Default: false.
|
12
|
+
attr_accessor :parallel
|
13
|
+
|
14
|
+
|
11
15
|
# * +name+ The name of the set of machines
|
12
16
|
# * +opts+ a hash of optional arguments
|
13
17
|
#
|
@@ -83,11 +87,12 @@ module Rye
|
|
83
87
|
end
|
84
88
|
|
85
89
|
def to_s
|
86
|
-
"%s
|
90
|
+
"%s:%s" % [self.class.to_s, @name]
|
87
91
|
end
|
88
92
|
|
89
93
|
def inspect
|
90
|
-
|
94
|
+
a = [self.class.to_s, @name, @parallel, @opts.inspect, @boxes.inspect]
|
95
|
+
%q{#<%s:%s parallel=%s opts=%s boxes=%s>} % a
|
91
96
|
end
|
92
97
|
|
93
98
|
# See Rye::Box.[]
|
@@ -142,12 +147,11 @@ module Rye
|
|
142
147
|
end
|
143
148
|
end
|
144
149
|
|
145
|
-
# Should it bubble up the exception for a single box?
|
146
|
-
# socket errors?
|
150
|
+
# Should it bubble up the exception for a single box? socket errors?
|
147
151
|
threads.each do |t|
|
148
|
-
sleep 0.
|
149
|
-
t.join
|
150
|
-
raps << t[:rap]
|
152
|
+
Kernel.sleep 0.03 # Give the thread some breathing room
|
153
|
+
t.join # Wait for the thread to finish
|
154
|
+
raps << t[:rap] # Grab the result
|
151
155
|
end
|
152
156
|
|
153
157
|
raps
|
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.
|
4
|
+
s.version = "0.8.0"
|
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"
|