rye 0.4.3 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +8 -0
- data/README.rdoc +2 -2
- data/bin/rye +12 -1
- data/bin/try +2 -1
- data/lib/rye/box.rb +86 -26
- data/lib/rye/cmd.rb +7 -2
- data/lib/rye/set.rb +10 -3
- data/lib/rye.rb +4 -4
- data/rye.gemspec +1 -1
- metadata +5 -3
data/CHANGES.txt
CHANGED
@@ -4,6 +4,14 @@ TODO
|
|
4
4
|
|
5
5
|
* Fingerprints: ssh-keygen -l -f id_rsa_repos.pub
|
6
6
|
|
7
|
+
#### 0.5.0 (2009-04-18) #############################
|
8
|
+
|
9
|
+
* ADDED: Rye::Box.switch_user
|
10
|
+
* ADDED: Several new commands to Rye::Cmd
|
11
|
+
* ADDED: Rye::Box.authorize_keys_local and "rye authorize-local
|
12
|
+
* FIXED: Bug in connect which prevented key-based logins for reconnections
|
13
|
+
* FIXED: Method errors in JRuby
|
14
|
+
* FIXED: Bug in Rye::Set.add_boxes pushing nils into the list of boxes
|
7
15
|
|
8
16
|
#### 0.4.3 (2009-04-14) #############################
|
9
17
|
|
data/README.rdoc
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
= Rye - v0.
|
1
|
+
= Rye - v0.5
|
2
2
|
|
3
3
|
Safely run SSH commands on a bunch of machines at the same time (from Ruby).
|
4
4
|
|
5
|
-
Rye is similar to Rush[http://rush.heroku.com] but everything happens over SSH (no HTTP daemon) and the default settings are less powerful (for safety). For example, file globs are disabled so unless otherwise specified, you can't do this: <tt>rbox.rm('-rf', '/etc/**/*')</tt>.
|
5
|
+
Rye is similar to Rush[http://rush.heroku.com] but everything happens over SSH (no HTTP daemon) and the default settings are less powerful (for safety). For example, file globs and the "rm" command are disabled so unless otherwise specified, you can't do this: <tt>rbox.rm('-rf', '/etc/**/*')</tt>.
|
6
6
|
|
7
7
|
See the examples below (which are taken from bin/try).
|
8
8
|
|
data/bin/rye
CHANGED
@@ -43,8 +43,8 @@ command :authorize do |obj|
|
|
43
43
|
|
44
44
|
obj.argv.each do |hostname|
|
45
45
|
|
46
|
-
rbox = Rye::Box.new(hostname, opts).connect
|
47
46
|
puts "Authorizing #{rbox.opts[:user]}@#{hostname}"
|
47
|
+
rbox = Rye::Box.new(hostname, opts).connect
|
48
48
|
puts "Added public keys for: ", rbox.authorize_keys
|
49
49
|
puts "Now try: " << "ssh #{rbox.opts[:user]}@#{hostname}"
|
50
50
|
|
@@ -53,6 +53,17 @@ command :authorize do |obj|
|
|
53
53
|
end
|
54
54
|
command_alias :authorize, :authorise
|
55
55
|
|
56
|
+
desc "Add your public keys to your current account on this machine"
|
57
|
+
command :authorize_local do |obj|
|
58
|
+
user = Rye.sysinfo.user
|
59
|
+
puts "Authorizing #{user}@localhost"
|
60
|
+
rbox = Rye::Box.new('localhost').connect
|
61
|
+
puts "Added public keys for: ", rbox.authorize_keys_local
|
62
|
+
puts "Now try: " << "ssh #{user}@localhost"
|
63
|
+
|
64
|
+
end
|
65
|
+
command_alias :local_authorize, :local_authorise
|
66
|
+
|
56
67
|
|
57
68
|
desc "Generate a public key from a private key"
|
58
69
|
|
data/bin/try
CHANGED
@@ -107,9 +107,10 @@ puts %Q(
|
|
107
107
|
|
108
108
|
rset = Rye::Set.new('default', :parallel => true)
|
109
109
|
rbox = Rye::Box.new
|
110
|
+
p rbox
|
110
111
|
|
111
112
|
rset.add_keys('/private/key/path') # For passwordless logins
|
112
|
-
rset.add_boxes(rbox, '
|
113
|
+
rset.add_boxes(rbox, '127.0.0.1') # Add boxes as hostnames or objects
|
113
114
|
|
114
115
|
# Calling methods on Rye::Set objects is very similar to calling them on
|
115
116
|
# Rye::Box objects. In fact, it's identical:
|
data/lib/rye/box.rb
CHANGED
@@ -123,8 +123,12 @@ module Rye
|
|
123
123
|
@current_working_directory = key
|
124
124
|
self
|
125
125
|
end
|
126
|
-
alias :cd :'[]'
|
127
|
-
|
126
|
+
# alias :cd :'[]' # fix for jruby
|
127
|
+
def cd(key=nil);
|
128
|
+
@current_working_directory = key
|
129
|
+
self
|
130
|
+
end
|
131
|
+
|
128
132
|
# Open an SSH session with +@host+. This called automatically
|
129
133
|
# when you the first comamnd is run if it's not already connected.
|
130
134
|
# Raises a Rye::NoHost exception if +@host+ is not specified.
|
@@ -133,9 +137,10 @@ module Rye
|
|
133
137
|
def connect
|
134
138
|
raise Rye::NoHost unless @host
|
135
139
|
disconnect if @ssh
|
136
|
-
debug "Opening connection to #{@host}"
|
140
|
+
debug "Opening connection to #{@host} as #{@opts[:user]}"
|
137
141
|
highline = HighLine.new # Used for password prompt
|
138
142
|
retried = 0
|
143
|
+
|
139
144
|
begin
|
140
145
|
@ssh = Net::SSH.start(@host, @opts[:user], @opts || {})
|
141
146
|
rescue Net::SSH::AuthenticationFailed => ex
|
@@ -143,7 +148,7 @@ module Rye
|
|
143
148
|
if STDIN.tty? && retried <= 3
|
144
149
|
@opts[:password] = highline.ask("Password: ") { |q| q.echo = '' }
|
145
150
|
@opts[:auth_methods] ||= []
|
146
|
-
@opts[:auth_methods]
|
151
|
+
@opts[:auth_methods] << 'password'
|
147
152
|
retry
|
148
153
|
else
|
149
154
|
STDERR.puts "Authentication failed."
|
@@ -151,10 +156,38 @@ module Rye
|
|
151
156
|
end
|
152
157
|
end
|
153
158
|
|
159
|
+
# We add :auth_methods (a Net::SSH joint) to force asking for a
|
160
|
+
# password if the initial (key-based) authentication fails. We
|
161
|
+
# need to delete the key from @opts otherwise it lingers until
|
162
|
+
# the next connection (if we switch_user is called for example).
|
163
|
+
@opts.delete :auth_methods if @opts.has_key?(:auth_methods)
|
164
|
+
|
154
165
|
@ssh.is_a?(Net::SSH::Connection::Session) && !@ssh.closed?
|
155
166
|
self
|
156
167
|
end
|
157
168
|
|
169
|
+
# Close the SSH session with +@host+. This is called
|
170
|
+
# automatically at exit if the connection is open.
|
171
|
+
def disconnect
|
172
|
+
return unless @ssh && !@ssh.closed?
|
173
|
+
@ssh.loop(0.1) { @ssh.busy? }
|
174
|
+
debug "Closing connection to #{@ssh.host}"
|
175
|
+
@ssh.close
|
176
|
+
end
|
177
|
+
|
178
|
+
# Reconnect as another user
|
179
|
+
# * +newuser+ The username to reconnect as
|
180
|
+
#
|
181
|
+
# NOTE: if there is an open connection, it's disconnected
|
182
|
+
# and a new one is opened for the given user.
|
183
|
+
def switch_user(newuser)
|
184
|
+
return if newuser.to_s == self.user.to_s
|
185
|
+
@opts ||= {}
|
186
|
+
@opts[:user] = newuser
|
187
|
+
disconnect
|
188
|
+
connect
|
189
|
+
end
|
190
|
+
|
158
191
|
# Open an interactive SSH session. This only works if STDIN.tty?
|
159
192
|
# returns true. Otherwise it returns the SSH command that would
|
160
193
|
# have been run. This requires the SSH command-line executable (ssh).
|
@@ -169,15 +202,6 @@ module Rye
|
|
169
202
|
system(cmd)
|
170
203
|
end
|
171
204
|
|
172
|
-
# Close the SSH session with +@host+. This is called
|
173
|
-
# automatically at exit if the connection is open.
|
174
|
-
def disconnect
|
175
|
-
return unless @ssh && !@ssh.closed?
|
176
|
-
@ssh.loop(0.1) { @ssh.busy? }
|
177
|
-
debug "Closing connection to #{@ssh.host}"
|
178
|
-
@ssh.close
|
179
|
-
end
|
180
|
-
|
181
205
|
# Add one or more private keys to the SSH Agent.
|
182
206
|
# * +additional_keys+ is a list of file paths to private keys
|
183
207
|
# Returns the instance of Box
|
@@ -190,7 +214,7 @@ module Rye
|
|
190
214
|
debug "ssh-add stdout: #{ret.stdout}"
|
191
215
|
debug "ssh-add stderr: #{ret.stderr}"
|
192
216
|
end
|
193
|
-
self
|
217
|
+
self #MUST RETURN itself
|
194
218
|
end
|
195
219
|
alias :add_key :add_keys
|
196
220
|
|
@@ -203,6 +227,10 @@ module Rye
|
|
203
227
|
end
|
204
228
|
alias :add_environment_variable :add_env
|
205
229
|
|
230
|
+
def user
|
231
|
+
(@opts || {})[:user]
|
232
|
+
end
|
233
|
+
|
206
234
|
# See Rye.keys
|
207
235
|
def keys
|
208
236
|
Rye.keys
|
@@ -236,7 +264,9 @@ module Rye
|
|
236
264
|
# this box into ~/.ssh/authorized_keys and ~/.ssh/authorized_keys2.
|
237
265
|
# Returns an Array of the private keys files used to generate the public keys.
|
238
266
|
#
|
239
|
-
# NOTE: authorize_keys disables safe-mode for this box while it runs
|
267
|
+
# NOTE: authorize_keys disables safe-mode for this box while it runs
|
268
|
+
# which will hit you funky style if your using a single instance
|
269
|
+
# of Rye::Box in a multithreaded situation.
|
240
270
|
#
|
241
271
|
def authorize_keys
|
242
272
|
added_keys = []
|
@@ -255,11 +285,32 @@ module Rye
|
|
255
285
|
added_keys
|
256
286
|
end
|
257
287
|
|
288
|
+
# Authorize the current user to login to the local machine via
|
289
|
+
# SSH without a password. This is the same functionality as
|
290
|
+
# authorize_keys except run with local shell commands.
|
291
|
+
def authorize_keys_local
|
292
|
+
added_keys = []
|
293
|
+
Rye.keys.each do |key|
|
294
|
+
path = key[2]
|
295
|
+
debug "# Public key for #{path}"
|
296
|
+
k = Rye::Key.from_file(path).public_key.to_ssh2
|
297
|
+
Rye.shell(:mkdir, :p, :m, '700', '$HOME/.ssh') # Silently create dir if it doesn't exist
|
298
|
+
Rye.shell(:echo, "'#{k}' >> $HOME/.ssh/authorized_keys")
|
299
|
+
Rye.shell(:echo, "'#{k}' >> $HOME/.ssh/authorized_keys2")
|
300
|
+
Rye.shell(:chmod, '-R', '0600', '$HOME/.ssh/authorized_keys*')
|
301
|
+
added_keys << path
|
302
|
+
end
|
303
|
+
added_keys
|
304
|
+
end
|
305
|
+
|
258
306
|
# A handler for undefined commands.
|
259
307
|
# Raises Rye::CommandNotFound exception.
|
260
308
|
def method_missing(meth, *args, &block)
|
261
309
|
raise Rye::CommandNotFound, "#{meth.to_s} (args: #{args.join(' ')})"
|
262
310
|
end
|
311
|
+
def preview_command(*args)
|
312
|
+
prep_args(*args).join(' ')
|
313
|
+
end
|
263
314
|
|
264
315
|
private
|
265
316
|
|
@@ -300,18 +351,9 @@ module Rye
|
|
300
351
|
def run_command(*args)
|
301
352
|
debug "run_command with keys: #{Rye.keys.inspect}"
|
302
353
|
|
303
|
-
|
304
|
-
args = args.flatten.compact
|
305
|
-
args = args.first.split(/\s+/) if args.size == 1
|
306
|
-
cmd = args.shift
|
307
|
-
|
308
|
-
# Symbols to switches. :l -> -l, :help -> --help
|
309
|
-
args.collect! do |a|
|
310
|
-
a = "-#{a}" if a.is_a?(Symbol) && a.to_s.size == 1
|
311
|
-
a = "--#{a}" if a.is_a?(Symbol)
|
312
|
-
a
|
313
|
-
end
|
354
|
+
cmd, args = prep_args(*args)
|
314
355
|
|
356
|
+
connect if !@ssh || @ssh.closed?
|
315
357
|
raise Rye::NotConnected, @host unless @ssh && !@ssh.closed?
|
316
358
|
|
317
359
|
cmd_clean = Rye.escape(@safe, cmd, args)
|
@@ -336,6 +378,24 @@ module Rye
|
|
336
378
|
end
|
337
379
|
alias :cmd :run_command
|
338
380
|
|
381
|
+
|
382
|
+
|
383
|
+
# Takes a list of arguments appropriate for run_command or
|
384
|
+
# preview_command and returns: [cmd, args]
|
385
|
+
def prep_args(*args)
|
386
|
+
args = args.flatten.compact
|
387
|
+
args = args.first.to_s.split(/\s+/) if args.size == 1
|
388
|
+
cmd = args.shift
|
389
|
+
|
390
|
+
# Symbols to switches. :l -> -l, :help -> --help
|
391
|
+
args.collect! do |a|
|
392
|
+
a = "-#{a}" if a.is_a?(Symbol) && a.to_s.size == 1
|
393
|
+
a = "--#{a}" if a.is_a?(Symbol)
|
394
|
+
a
|
395
|
+
end
|
396
|
+
[cmd, args]
|
397
|
+
end
|
398
|
+
|
339
399
|
# Executes +command+ via SSH
|
340
400
|
# Returns an Array with 4 elements: [stdout, stderr, exit code, exit signal]
|
341
401
|
def net_ssh_exec!(command)
|
data/lib/rye/cmd.rb
CHANGED
@@ -25,6 +25,8 @@ module Rye;
|
|
25
25
|
#def rm(*args); cmd('rm', args); end
|
26
26
|
def ps(*args); cmd('ps', args); end
|
27
27
|
def sh(*args); cmd('sh', args); end
|
28
|
+
def df(*args); cmd('df', args); end
|
29
|
+
def du(*args); cmd('du', args); end
|
28
30
|
|
29
31
|
def env(*args); cmd "env"; end
|
30
32
|
def pwd(*args); cmd "pwd"; end
|
@@ -44,16 +46,19 @@ module Rye;
|
|
44
46
|
def bash(*args); cmd('bash', args); end
|
45
47
|
def echo(*args); cmd('echo', args); end
|
46
48
|
def test(*args); cmd('test', args); end
|
49
|
+
def mkfs(*args); cmd('mkfs', args); end
|
47
50
|
|
48
|
-
def mount(*args); cmd("mount"); end
|
51
|
+
def mount(*args); cmd("mount", args); end
|
49
52
|
def sleep(seconds=1); cmd("sleep", seconds); end
|
50
53
|
def mkdir(*args); cmd('mkdir', args); end
|
51
54
|
def touch(*args); cmd('touch', args); end
|
52
55
|
def uname(*args); cmd('uname', args); end
|
53
56
|
def chmod(*args); cmd('chmod', args); end
|
54
57
|
|
55
|
-
def
|
58
|
+
def umount(*args); cmd("umount", args); end
|
59
|
+
def uptime(*args); cmd("uptime", args); end
|
56
60
|
def python(*args); cmd('python', args); end
|
61
|
+
def history(*args); cmd('history', args); end
|
57
62
|
def printenv(*args); cmd('printenv', args); end
|
58
63
|
def hostname(*args); cmd('hostname', args); end
|
59
64
|
|
data/lib/rye/set.rb
CHANGED
@@ -48,7 +48,9 @@ module Rye
|
|
48
48
|
def add_box(*boxes)
|
49
49
|
boxes = boxes.flatten.compact
|
50
50
|
@boxes += boxes.collect do |box|
|
51
|
-
box.is_a?(
|
51
|
+
b = box.is_a?(String) ? Rye::Box.new(box, @opts) : box
|
52
|
+
b.add_keys(@keys)
|
53
|
+
b
|
52
54
|
end
|
53
55
|
self
|
54
56
|
end
|
@@ -90,8 +92,12 @@ module Rye
|
|
90
92
|
run_command(:cd, key)
|
91
93
|
self
|
92
94
|
end
|
93
|
-
alias :cd :'[]'
|
94
|
-
|
95
|
+
# alias :cd :'[]' # fix for jruby
|
96
|
+
def cd(key=nil)
|
97
|
+
run_command(:cd, key)
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
95
101
|
# Catches calls to Rye::Box commands. If +meth+ is the name of an
|
96
102
|
# instance method defined in Rye::Cmd then we call it against all
|
97
103
|
# the boxes in +@boxes+. Otherwise this method raises a
|
@@ -118,6 +124,7 @@ module Rye
|
|
118
124
|
|
119
125
|
# Run the command on all boxes in parallel
|
120
126
|
def run_command_parallel(meth, *args)
|
127
|
+
p @boxes
|
121
128
|
debug "P: #{meth} on #{@boxes.size} boxes (#{@boxes.collect {|b| b.host }.join(', ')})"
|
122
129
|
threads = []
|
123
130
|
|
data/lib/rye.rb
CHANGED
@@ -18,9 +18,9 @@ require 'sys'
|
|
18
18
|
#
|
19
19
|
# Rye is similar to Rush[http://rush.heroku.com] but everything
|
20
20
|
# happens over SSH (no HTTP daemon) and the default settings are
|
21
|
-
# less dangerous (for safety). For example, file globs
|
22
|
-
# disabled so unless otherwise specified, you
|
23
|
-
# <tt>rbox.rm('/etc/**/*')</tt>.
|
21
|
+
# less dangerous (for safety). For example, file globs and the
|
22
|
+
# "rm" command are disabled so unless otherwise specified, you
|
23
|
+
# can't do this: <tt>rbox.rm('/etc/**/*')</tt>.
|
24
24
|
#
|
25
25
|
# However, you can do this:
|
26
26
|
#
|
@@ -35,7 +35,7 @@ module Rye
|
|
35
35
|
extend self
|
36
36
|
|
37
37
|
unless defined?(SYSINFO)
|
38
|
-
VERSION = 0.
|
38
|
+
VERSION = 0.5.freeze
|
39
39
|
SYSINFO = SystemInfo.new.freeze
|
40
40
|
end
|
41
41
|
|
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.5.1"
|
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.5.1
|
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-04-
|
12
|
+
date: 2009-04-19 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -87,6 +87,8 @@ files:
|
|
87
87
|
- tst/50_rye_test.rb
|
88
88
|
has_rdoc: true
|
89
89
|
homepage: http://solutious.com/
|
90
|
+
licenses: []
|
91
|
+
|
90
92
|
post_install_message:
|
91
93
|
rdoc_options:
|
92
94
|
- --line-numbers
|
@@ -111,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
113
|
requirements: []
|
112
114
|
|
113
115
|
rubyforge_project: rye
|
114
|
-
rubygems_version: 1.3.
|
116
|
+
rubygems_version: 1.3.2
|
115
117
|
signing_key:
|
116
118
|
specification_version: 2
|
117
119
|
summary: "Rye: Safely run SSH commands on a bunch of machines at the same time (from Ruby)."
|