rye 0.7.6 → 0.8.0
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.
- data/CHANGES.txt +12 -0
- data/README.rdoc +15 -7
- data/lib/rye/box.rb +50 -21
- data/lib/rye/cmd.rb +5 -5
- data/lib/rye/set.rb +11 -7
- data/lib/rye.rb +3 -6
- data/rye.gemspec +1 -1
- metadata +2 -2
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/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/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/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"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rye
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-06-
|
12
|
+
date: 2009-06-21 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|