rye 0.7.2 → 0.7.3
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 +16 -0
- data/lib/rye.rb +0 -3
- data/lib/rye/box.rb +101 -68
- data/lib/rye/cmd.rb +45 -2
- data/rye.gemspec +1 -1
- metadata +2 -2
data/CHANGES.txt
CHANGED
|
@@ -7,6 +7,22 @@ TODO
|
|
|
7
7
|
* Add S3 support for Rye::Box.upload / download
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
#### 0.7.3 (2009-06-03) #############################
|
|
11
|
+
|
|
12
|
+
* ADDED: enable_safe_mode and disable_safe_mode methods
|
|
13
|
+
* ADDED: New default Rye::Cmd methods: gzip, bzip, tar and all derivatives. ./configure and make too.
|
|
14
|
+
* ADDED: Rye::Cmd#safely and Rye::Cmd#unsafely methods
|
|
15
|
+
* ADDED: Rye::Cmd#digest_md5, Rye::Cmd#digest_sha1, Rye::Cmd#digest_sha2 methods
|
|
16
|
+
* ADDED: Rye::Cmd#file_verified? method
|
|
17
|
+
* ADDED: Rye::Box#net_ssh_exec! now checks and correctly prompts for a sudo response password.
|
|
18
|
+
Note: this will be upgraded in for 0.8 to support any prompt.
|
|
19
|
+
* FIXED: Net::SSH paranoid now set to false when safe mode is disabled.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
#### 0.7.2 (2009-06-01) #############################
|
|
23
|
+
|
|
24
|
+
*A re-release of 0.7.1 to force Rubyforge to update the gem*
|
|
25
|
+
|
|
10
26
|
#### 0.7.1 (2009-06-01) #############################
|
|
11
27
|
|
|
12
28
|
* CHANGE: Removed broken grep method from Rye::Rap
|
data/lib/rye.rb
CHANGED
data/lib/rye/box.rb
CHANGED
|
@@ -36,8 +36,10 @@ module Rye
|
|
|
36
36
|
|
|
37
37
|
def host=(val); @rye_host = val; end
|
|
38
38
|
def opts=(val); @rye_opts = val; end
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
|
|
40
|
+
def enable_safe_mode; @rye_safe = true; end
|
|
41
|
+
def disable_safe_mode; @rye_safe = false; end
|
|
42
|
+
|
|
41
43
|
# The most recent value from Box.cd or Box.[]
|
|
42
44
|
def current_working_directory; @rye_current_working_directory; end
|
|
43
45
|
|
|
@@ -100,7 +102,7 @@ module Rye
|
|
|
100
102
|
@rye_info = STDOUT if @rye_info == true
|
|
101
103
|
|
|
102
104
|
@rye_opts[:logger] = Logger.new(@rye_debug) if @rye_debug # Enable Net::SSH debugging
|
|
103
|
-
@rye_opts[:paranoid] = true unless @
|
|
105
|
+
@rye_opts[:paranoid] = true unless @rye_safe == false # See Net::SSH.start
|
|
104
106
|
|
|
105
107
|
# Add the given private keys to the keychain that will be used for @rye_host
|
|
106
108
|
add_keys(@rye_opts[:keys])
|
|
@@ -169,57 +171,6 @@ module Rye
|
|
|
169
171
|
end
|
|
170
172
|
|
|
171
173
|
|
|
172
|
-
# Open an SSH session with +@rye_host+. This called automatically
|
|
173
|
-
# when you the first comamnd is run if it's not already connected.
|
|
174
|
-
# Raises a Rye::NoHost exception if +@rye_host+ is not specified.
|
|
175
|
-
# Will attempt a password login up to 3 times if the initial
|
|
176
|
-
# authentication fails.
|
|
177
|
-
# * +reconnect+ Disconnect first if already connected. The default
|
|
178
|
-
# is true. When set to false, connect will do nothing if already
|
|
179
|
-
# connected.
|
|
180
|
-
def connect(reconnect=true)
|
|
181
|
-
raise Rye::NoHost unless @rye_host
|
|
182
|
-
return if @rye_ssh && !reconnect
|
|
183
|
-
disconnect if @rye_ssh
|
|
184
|
-
debug "Opening connection to #{@rye_host} as #{@rye_opts[:user]}"
|
|
185
|
-
highline = HighLine.new # Used for password prompt
|
|
186
|
-
retried = 0
|
|
187
|
-
|
|
188
|
-
begin
|
|
189
|
-
@rye_ssh = Net::SSH.start(@rye_host, @rye_opts[:user], @rye_opts || {})
|
|
190
|
-
rescue Net::SSH::HostKeyMismatch => ex
|
|
191
|
-
STDERR.puts ex.message
|
|
192
|
-
STDERR.puts "NOTE: EC2 instances generate new SSH keys on first boot."
|
|
193
|
-
print "\a" if @rye_info # Ring the bell
|
|
194
|
-
if highline.ask("Continue? ").strip.match(/\Ay|yes|sure|ya\z/i)
|
|
195
|
-
@rye_opts[:paranoid] = false
|
|
196
|
-
retry
|
|
197
|
-
else
|
|
198
|
-
raise Net::SSH::HostKeyMismatch
|
|
199
|
-
end
|
|
200
|
-
rescue Net::SSH::AuthenticationFailed => ex
|
|
201
|
-
print "\a" if retried == 0 && @rye_info # Ring the bell once
|
|
202
|
-
retried += 1
|
|
203
|
-
if STDIN.tty? && retried <= 3
|
|
204
|
-
STDERR.puts "Passwordless login failed for #{@rye_opts[:user]}"
|
|
205
|
-
@rye_opts[:password] = highline.ask("Password: ") { |q| q.echo = '' }
|
|
206
|
-
@rye_opts[:auth_methods] ||= []
|
|
207
|
-
@rye_opts[:auth_methods] << 'password'
|
|
208
|
-
retry
|
|
209
|
-
else
|
|
210
|
-
raise Net::SSH::AuthenticationFailed
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
# We add :auth_methods (a Net::SSH joint) to force asking for a
|
|
215
|
-
# password if the initial (key-based) authentication fails. We
|
|
216
|
-
# need to delete the key from @rye_opts otherwise it lingers until
|
|
217
|
-
# the next connection (if we switch_user is called for example).
|
|
218
|
-
@rye_opts.delete :auth_methods if @rye_opts.has_key?(:auth_methods)
|
|
219
|
-
|
|
220
|
-
self
|
|
221
|
-
end
|
|
222
|
-
|
|
223
174
|
# Reconnect as another user. This is different from su=
|
|
224
175
|
# which executes subsequent commands via +su -c COMMAND USER+.
|
|
225
176
|
# * +newuser+ The username to reconnect as
|
|
@@ -509,6 +460,26 @@ module Rye
|
|
|
509
460
|
self.instance_exec *args, &block
|
|
510
461
|
end
|
|
511
462
|
|
|
463
|
+
# Like batch, except it disables safe mode before executing the block.
|
|
464
|
+
# After executing the block, safe mode is returned back to whichever
|
|
465
|
+
# state it was previously in. In other words, this method won't enable
|
|
466
|
+
# safe mode if it was already disabled.
|
|
467
|
+
def unsafely(*args, &block)
|
|
468
|
+
previous_state = @rye_safe
|
|
469
|
+
disable_safe_mode
|
|
470
|
+
self.instance_exec *args, &block
|
|
471
|
+
@rye_safe = previous_state
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# See unsafely (except in reverse)
|
|
475
|
+
def safely(*args, &block)
|
|
476
|
+
previous_state = @rye_safe
|
|
477
|
+
enable_safe_mode
|
|
478
|
+
self.instance_exec *args, &block
|
|
479
|
+
@rye_safe = previous_state
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
|
|
512
483
|
# instance_exec for Ruby 1.8 written by Mauricio Fernandez
|
|
513
484
|
# http://eigenclass.org/hiki/instance_exec
|
|
514
485
|
if RUBY_VERSION =~ /1.8/
|
|
@@ -539,6 +510,59 @@ module Rye
|
|
|
539
510
|
end
|
|
540
511
|
|
|
541
512
|
|
|
513
|
+
|
|
514
|
+
# Open an SSH session with +@rye_host+. This called automatically
|
|
515
|
+
# when you the first comamnd is run if it's not already connected.
|
|
516
|
+
# Raises a Rye::NoHost exception if +@rye_host+ is not specified.
|
|
517
|
+
# Will attempt a password login up to 3 times if the initial
|
|
518
|
+
# authentication fails.
|
|
519
|
+
# * +reconnect+ Disconnect first if already connected. The default
|
|
520
|
+
# is true. When set to false, connect will do nothing if already
|
|
521
|
+
# connected.
|
|
522
|
+
def connect(reconnect=true)
|
|
523
|
+
raise Rye::NoHost unless @rye_host
|
|
524
|
+
return if @rye_ssh && !reconnect
|
|
525
|
+
disconnect if @rye_ssh
|
|
526
|
+
debug "Opening connection to #{@rye_host} as #{@rye_opts[:user]}"
|
|
527
|
+
highline = HighLine.new # Used for password prompt
|
|
528
|
+
retried = 0
|
|
529
|
+
|
|
530
|
+
begin
|
|
531
|
+
@rye_ssh = Net::SSH.start(@rye_host, @rye_opts[:user], @rye_opts || {})
|
|
532
|
+
rescue Net::SSH::HostKeyMismatch => ex
|
|
533
|
+
STDERR.puts ex.message
|
|
534
|
+
STDERR.puts "NOTE: EC2 instances generate new SSH keys on first boot."
|
|
535
|
+
print "\a" if @rye_info # Ring the bell
|
|
536
|
+
if highline.ask("Continue? ").strip.match(/\Ay|yes|sure|ya\z/i)
|
|
537
|
+
@rye_opts[:paranoid] = false
|
|
538
|
+
retry
|
|
539
|
+
else
|
|
540
|
+
raise Net::SSH::HostKeyMismatch
|
|
541
|
+
end
|
|
542
|
+
rescue Net::SSH::AuthenticationFailed => ex
|
|
543
|
+
print "\a" if retried == 0 && @rye_info # Ring the bell once
|
|
544
|
+
retried += 1
|
|
545
|
+
if STDIN.tty? && retried <= 3
|
|
546
|
+
STDERR.puts "Passwordless login failed for #{@rye_opts[:user]}"
|
|
547
|
+
@rye_opts[:password] = highline.ask("Password: ") { |q| q.echo = '' }
|
|
548
|
+
@rye_opts[:auth_methods] ||= []
|
|
549
|
+
@rye_opts[:auth_methods] << 'password'
|
|
550
|
+
retry
|
|
551
|
+
else
|
|
552
|
+
raise Net::SSH::AuthenticationFailed
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
# We add :auth_methods (a Net::SSH joint) to force asking for a
|
|
557
|
+
# password if the initial (key-based) authentication fails. We
|
|
558
|
+
# need to delete the key from @rye_opts otherwise it lingers until
|
|
559
|
+
# the next connection (if we switch_user is called for example).
|
|
560
|
+
@rye_opts.delete :auth_methods if @rye_opts.has_key?(:auth_methods)
|
|
561
|
+
|
|
562
|
+
self
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
|
|
542
566
|
private
|
|
543
567
|
|
|
544
568
|
def debug(msg="unknown debug msg"); @rye_debug.puts msg if @rye_debug; end
|
|
@@ -582,7 +606,7 @@ module Rye
|
|
|
582
606
|
|
|
583
607
|
connect if !@rye_ssh || @rye_ssh.closed?
|
|
584
608
|
raise Rye::NotConnected, @rye_host unless @rye_ssh && !@rye_ssh.closed?
|
|
585
|
-
|
|
609
|
+
|
|
586
610
|
cmd_clean = Rye.escape(@rye_safe, cmd, args)
|
|
587
611
|
cmd_clean = prepend_env(cmd_clean)
|
|
588
612
|
|
|
@@ -660,6 +684,14 @@ module Rye
|
|
|
660
684
|
|
|
661
685
|
# Executes +command+ via SSH
|
|
662
686
|
# Returns an Array with 4 elements: [stdout, stderr, exit code, exit signal]
|
|
687
|
+
# NOTE: This method needs to be replaced to fully support interactive
|
|
688
|
+
# commands. This implementation is weird because it's getting just STDOUT and
|
|
689
|
+
# STDERR responses (check value of "type"). on_data and on_extended_data method
|
|
690
|
+
# hooks are not used. See the following threads for implementation ideas:
|
|
691
|
+
#
|
|
692
|
+
# http://www.ruby-forum.com/topic/141814
|
|
693
|
+
# http://www.ruby-forum.com/topic/169997
|
|
694
|
+
#
|
|
663
695
|
def net_ssh_exec!(command)
|
|
664
696
|
|
|
665
697
|
block ||= Proc.new do |channel, type, data|
|
|
@@ -667,7 +699,16 @@ module Rye
|
|
|
667
699
|
channel[:stderr] ||= ""
|
|
668
700
|
channel[:exit_code] ||= 0
|
|
669
701
|
channel[:stdout] << data if type == :stdout
|
|
670
|
-
|
|
702
|
+
if type == :stderr
|
|
703
|
+
# NOTE: Use sudo to test this since it prompts for a passwords.
|
|
704
|
+
# Use sudo -K to kill the user's timestamp (ask for a password every time)
|
|
705
|
+
if data =~ /Password:/
|
|
706
|
+
ret = Annoy.get_user_input("Password: ", '*')
|
|
707
|
+
channel.send_data "#{ret}\n"
|
|
708
|
+
else
|
|
709
|
+
channel[:stderr] << data
|
|
710
|
+
end
|
|
711
|
+
end
|
|
671
712
|
channel.on_request("exit-status") do |ch, data|
|
|
672
713
|
# Anything greater than 0 is an error
|
|
673
714
|
channel[:exit_code] = data.read_long
|
|
@@ -676,22 +717,14 @@ module Rye
|
|
|
676
717
|
# This should be the POSIX SIGNAL that ended the process
|
|
677
718
|
channel[:exit_signal] = data.read_long
|
|
678
719
|
end
|
|
679
|
-
# For long-running commands like top, this will print the output.
|
|
680
|
-
# It's cool, but we'd also need to enable STDIN to interact with
|
|
681
|
-
# command.
|
|
682
|
-
#channel.on_data do |ch, data|
|
|
683
|
-
# puts "got stdout: #{data}"
|
|
684
|
-
# #channel.send_data "something for stdin\n"
|
|
685
|
-
#end
|
|
686
|
-
#
|
|
687
|
-
#channel.on_extended_data do |ch, data|
|
|
688
|
-
# #puts "got stdout: #{data}"
|
|
689
|
-
# #channel.send_data "something for stdin\n"
|
|
690
|
-
#end
|
|
691
720
|
end
|
|
692
721
|
|
|
693
722
|
channel = @rye_ssh.exec(command, &block)
|
|
723
|
+
|
|
694
724
|
channel.wait # block until we get a response
|
|
725
|
+
channel.request_pty do |ch, success|
|
|
726
|
+
raise "Could not obtain pty (i.e. an interactive ssh session)" if !success
|
|
727
|
+
end
|
|
695
728
|
|
|
696
729
|
channel[:exit_code] = 0 if channel[:exit_code] == nil
|
|
697
730
|
channel[:exit_code] &&= channel[:exit_code].to_i
|
data/lib/rye/cmd.rb
CHANGED
|
@@ -43,6 +43,7 @@ module Rye;
|
|
|
43
43
|
def sed(*args); cmd('sed', args); end
|
|
44
44
|
def awk(*args); cmd('awk', args); end
|
|
45
45
|
def cat(*args); cmd('cat', args); end
|
|
46
|
+
def tar(*args); cmd('tar', args); end
|
|
46
47
|
|
|
47
48
|
#def kill(*args); cmd('kill', args); end
|
|
48
49
|
def sudo(*args); cmd('sudo', args); end
|
|
@@ -54,6 +55,8 @@ module Rye;
|
|
|
54
55
|
def echo(*args); cmd('echo', args); end
|
|
55
56
|
def test(*args); cmd('test', args); end
|
|
56
57
|
def mkfs(*args); cmd('mkfs', args); end
|
|
58
|
+
def gzip(*args); cmd('gzip', args); end
|
|
59
|
+
def make(*args); cmd('make', args); end
|
|
57
60
|
|
|
58
61
|
def mount(*args); cmd("mount", args); end
|
|
59
62
|
def sleep(*args); cmd("sleep", args); end
|
|
@@ -62,15 +65,20 @@ module Rye;
|
|
|
62
65
|
def uname(*args); cmd('uname', args); end
|
|
63
66
|
def chmod(*args); cmd('chmod', args); end
|
|
64
67
|
def chown(*args); cmd('chown', args); end
|
|
68
|
+
def unzip(*args); cmd('unzip', args); end
|
|
69
|
+
def bzip2(*args); cmd('bzip2', args); end
|
|
65
70
|
|
|
66
71
|
def umount(*args); cmd("umount", args); end
|
|
67
72
|
def uptime(*args); cmd("uptime", args); end
|
|
68
73
|
def python(*args); cmd('python', args); end
|
|
74
|
+
def gunzip(*args); cmd('gunzip', args); end
|
|
69
75
|
def useradd(*args); cmd('useradd', args); end
|
|
76
|
+
def bunzip2(*args); cmd('bunzip2', args); end
|
|
70
77
|
def getconf(*args); cmd('getconf', args); end
|
|
71
78
|
def history(*args); cmd('history', args); end
|
|
72
79
|
def printenv(*args); cmd('printenv', args); end
|
|
73
80
|
def hostname(*args); cmd('hostname', args); end
|
|
81
|
+
def configure(*args); cmd('./configure', args); end
|
|
74
82
|
|
|
75
83
|
# Transfer files to a machine via Net::SCP.
|
|
76
84
|
# * +files+ is an Array of files to upload. The last element is the
|
|
@@ -118,7 +126,7 @@ module Rye;
|
|
|
118
126
|
self.upload file_content, filepath
|
|
119
127
|
end
|
|
120
128
|
|
|
121
|
-
# Does
|
|
129
|
+
# Does +path+ from the current working directory?
|
|
122
130
|
def file_exists?(path)
|
|
123
131
|
begin
|
|
124
132
|
ret = self.ls(path)
|
|
@@ -129,7 +137,42 @@ module Rye;
|
|
|
129
137
|
# But on OSX exit code is 1. This is why we look at STDERR.
|
|
130
138
|
ret.stderr.empty?
|
|
131
139
|
end
|
|
132
|
-
|
|
140
|
+
|
|
141
|
+
# Does the calculated digest of +path+ match the known +expected_digest+?
|
|
142
|
+
# This is useful for verifying downloaded files.
|
|
143
|
+
# +digest_type+ must be one of: :md5, :sha1, :sha2
|
|
144
|
+
def file_verified?(path, expected_digest, digest_type=:md5)
|
|
145
|
+
return false unless file_exists?(path)
|
|
146
|
+
raise "Unknown disgest type: #{digest_type}" unless can?("digest_#{digest_type}")
|
|
147
|
+
digest = self.send("digest_#{digest_type}", path).first
|
|
148
|
+
info "#{digest_type} (#{path}) = #{digest}"
|
|
149
|
+
digest.to_s == expected_digest.to_s
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# * +files+ An Array of file paths
|
|
153
|
+
# Returns an Array of MD5 digests for each of the given files
|
|
154
|
+
def digest_md5(*files)
|
|
155
|
+
files.flatten.collect { |file|
|
|
156
|
+
File.exists?(file) ? Digest::MD5.hexdigest(File.read(file)) : nil
|
|
157
|
+
}
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# * +files+ An Array of file paths
|
|
161
|
+
# Returns an Array of SH1 digests for each of the given files
|
|
162
|
+
def digest_sha1(*files)
|
|
163
|
+
files.flatten.collect { |file|
|
|
164
|
+
File.exists?(file) ? Digest::SHA1.hexdigest(File.read(file)) : nil
|
|
165
|
+
}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# * +files+ An Array of file paths
|
|
169
|
+
# Returns an Array of SH2 digests for each of the given files
|
|
170
|
+
def digest_sha2(*files)
|
|
171
|
+
files.flatten.collect { |file|
|
|
172
|
+
File.exists?(file) ? Digest::SHA2.hexdigest(File.read(file)) : nil
|
|
173
|
+
}
|
|
174
|
+
end
|
|
175
|
+
|
|
133
176
|
# Returns an Array of system commands available over SSH
|
|
134
177
|
def can
|
|
135
178
|
Rye::Cmd.instance_methods
|
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.3"
|
|
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.7.
|
|
4
|
+
version: 0.7.3
|
|
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-03 00:00:00 -04:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|