delano-rye 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +6 -0
- data/README.rdoc +32 -149
- data/bin/rye +7 -6
- data/lib/rye.rb +9 -1
- data/lib/rye/box.rb +1 -0
- data/rye.gemspec +1 -1
- metadata +1 -1
data/CHANGES.txt
CHANGED
@@ -4,6 +4,12 @@ TODO
|
|
4
4
|
|
5
5
|
* Fingerprints: ssh-keygen -l -f id_rsa_repos.pub
|
6
6
|
|
7
|
+
#### 0.6.1 (2009-04-29) #############################
|
8
|
+
|
9
|
+
* ADDED: Prints message to STDERR when passwordless login fails.
|
10
|
+
* ADDED: Highline 1.5.1 to vendor to fix the Ruby 1.9 issue (Highline
|
11
|
+
1.5.1 is not released as a gem yet)
|
12
|
+
* CHANGE: Cleaned examples and links in README
|
7
13
|
|
8
14
|
#### 0.6.0 (2009-04-28) #############################
|
9
15
|
|
data/README.rdoc
CHANGED
@@ -4,7 +4,6 @@ Safely run SSH commands on a bunch of machines at the same time (from Ruby).
|
|
4
4
|
|
5
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
|
-
See the examples below (which are taken from bin/try).
|
8
7
|
|
9
8
|
== Installation
|
10
9
|
|
@@ -15,145 +14,48 @@ One of:
|
|
15
14
|
$ git clone git://github.com/delano/rye.git
|
16
15
|
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
rbox = Rye::Box.new('localhost')
|
21
|
-
|
22
|
-
# Commands are run as methods on the Rye::Box object
|
23
|
-
puts rbox.uptime # => 11:02 up 16:01, 3 users
|
24
|
-
|
25
|
-
# The response value for all commands is a Rye::Rap object. The rap is a
|
26
|
-
# subclass of Array so you can treat it as an Array, but it can also act
|
27
|
-
# like a String if there's only one element.
|
28
|
-
puts rbox.ls('rye.test') # => ""
|
29
|
-
puts rbox.ls('rye.test').stderr # => ls: rye.test: No such file or directory
|
30
|
-
|
31
|
-
puts rbox.touch('rye.test') # => ""
|
32
|
-
puts rbox.rm('rye.test') # => ""
|
33
|
-
|
34
|
-
# You can change directories
|
35
|
-
puts rbox.pwd # => /home/rye
|
36
|
-
puts rbox['/usr/bin'].pwd # => /usr/bin
|
37
|
-
puts rbox.pwd # => /usr/bin
|
38
|
-
puts rbox.cd('/home/rye').pwd # => /home/rye
|
39
|
-
|
40
|
-
# You can specify environment variables
|
41
|
-
rbox.setenv(:RYE, "Forty Creek")
|
42
|
-
rbox.env # => ['HOME=/home/rye', 'RYE=Forty Creek', ...]
|
43
|
-
|
44
|
-
# The commands method returns an Array of available commands:
|
45
|
-
puts rbox.commands.join(', ') # => pwd, touch, echo, wc, ...
|
46
|
-
|
47
|
-
# When you're done you can disconnect explicitly.
|
48
|
-
# (Although Rye does this automatically at exit.)
|
49
|
-
rbox.disconnect
|
17
|
+
See bin/try for examples!
|
50
18
|
|
51
19
|
|
52
|
-
== EXAMPLE
|
20
|
+
== EXAMPLE 1 -- SSH Authorization
|
53
21
|
|
54
|
-
|
55
|
-
rbox_wild = Rye::Box.new('localhost', :safe => false)
|
22
|
+
Does it annoy you to manually authorize remote SSH accounts? Rye can help!
|
56
23
|
|
57
|
-
|
58
|
-
# arguments are thoroughly escaped. This prevents access to
|
59
|
-
# environment variables and file globs (among other things).
|
60
|
-
p rbox_safe.echo('$HOME') # => "$HOME"
|
61
|
-
p rbox_safe['/etc'].ls('host*') rescue Rye::CommandError # Doesn't exist
|
62
|
-
p rbox_safe.ls('-l | wc -l') rescue Rye::CommandError # => '|' is not a valid ls arg
|
24
|
+
Enable passwordless logins to HOST1 and HOST2:
|
63
25
|
|
64
|
-
|
65
|
-
p rbox_wild.echo('$HOME') # => "/home/rye"
|
66
|
-
p rbox_wild['/etc'].ls('host*') # => ["hostconfig", "hosts"]
|
67
|
-
p rbox_wild.ls('-l | wc -l') # => 110
|
68
|
-
p rbox_wild.echo('$HOME > /tmp/rye-home') # =>
|
69
|
-
p rbox_wild.cat('/tmp/rye-home') # => "/home/rye"
|
26
|
+
$ rye authorize HOST1 HOST2
|
70
27
|
|
28
|
+
This will copy your public SSH keys to the <tt>~/.ssh/authorized_keys</tt> and <tt>~/.ssh/authorized_keys2</tt> files on the remote machine(s).
|
71
29
|
|
72
|
-
|
73
|
-
|
74
|
-
rbox = Rye::Box.new('localhost')
|
75
|
-
rbox.add_keys('/private/key/path') # Specify additional private keys
|
30
|
+
See <tt>rye -h</tt> for more info
|
76
31
|
|
77
|
-
# There's currently no rye900 command
|
78
|
-
p rbox.commands.member?('rye9000') # => false
|
79
32
|
|
80
|
-
|
81
|
-
# automatically become available to all Rye::Box objects.
|
82
|
-
module Rye::Cmd
|
83
|
-
def rye9000(*args)
|
84
|
-
run_command("ls", args)
|
85
|
-
end
|
86
|
-
def somescript(*args)
|
87
|
-
run_command("/path/to/my/script", args)
|
88
|
-
end
|
89
|
-
end
|
33
|
+
== EXAMPLE 2 -- Basic Usage
|
90
34
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
35
|
+
rbox = Rye::Box.new('localhost')
|
36
|
+
rbox.uptime # => 11:02 up 16:01, 3 users
|
37
|
+
rbox['/usr/bin'].pwd # => /usr/bin
|
38
|
+
|
39
|
+
You can specify environment variables
|
95
40
|
|
96
|
-
|
41
|
+
rbox.setenv(:RYE, "Forty Creek")
|
42
|
+
rbox.env # => ['HOME=/home/rye', 'RYE=Forty Creek', ...]
|
43
|
+
|
44
|
+
|
45
|
+
== EXAMPLE 3 -- Accessing Multiple Machines
|
97
46
|
|
98
47
|
rset = Rye::Set.new
|
99
48
|
rbox = Rye::Box.new
|
100
49
|
|
101
|
-
rset.add_keys('/private/key/path') # For passwordless logins
|
102
50
|
rset.add_boxes(rbox, 'localhost') # Add boxes as hostnames or objects
|
103
51
|
|
104
|
-
|
105
|
-
|
52
|
+
Calling methods on Rye::Set objects is very similar to calling them on Rye::Box objects. In fact, it's identical:
|
53
|
+
|
106
54
|
p rset.uptime # => [[14:19:02 up 32 days, 19:35 ...], [14:19:02 up 30 days, 01:35]]
|
107
55
|
p rset['/etc'].ls # => [['file1', 'file2', ...], ['life1', 'life2', ...]]
|
108
56
|
|
109
|
-
# Like Rye::Box, the response value is a Rye::Rap object containing the
|
110
|
-
# responses from each box. Each response is itself a Rye::Rap object.
|
111
|
-
unames = rset.uname
|
112
|
-
p unames # => [["Darwin"], ["Darwin"]]
|
113
|
-
puts unames.class # => Rye::Rap
|
114
57
|
|
115
|
-
|
116
|
-
# command. In this case, it will keep a reference to Rye::Set:
|
117
|
-
puts unames.set.class # => Rye::Set
|
118
|
-
puts unames.set == rset # => true
|
119
|
-
puts unames.size # => 2
|
120
|
-
puts unames.first # => Darwin
|
121
|
-
puts unames.first.class # => Rye::Rap
|
122
|
-
puts unames.first.box.class # => Rye::Box
|
123
|
-
puts unames.first.box == rbox # => true
|
124
|
-
|
125
|
-
# Envrionment variables can be set the same way as with Rye::Box
|
126
|
-
rset.setenv(:RYE, "Forty Creek")
|
127
|
-
p rset.env.first.select { |env| env =~ /RYE/ } # => ["RYE=Forty Creek"]
|
128
|
-
|
129
|
-
|
130
|
-
== EXAMPLE 5 -- ERROR HANDLING
|
131
|
-
|
132
|
-
rbox = Rye::Box.new('localhost', :safe => false)
|
133
|
-
|
134
|
-
# Rye follows the standard convention of taking exception to a non-zero
|
135
|
-
# exit code by raising a Rye::CommandError. In this case, rye9000.test
|
136
|
-
# is not found by the ls command.
|
137
|
-
begin
|
138
|
-
rbox.ls('rye9000.test')
|
139
|
-
rescue Rye::CommandError => ex
|
140
|
-
puts ex.exit_code # => 1
|
141
|
-
puts ex.stderr # => ls: rye.test: No such file or directory
|
142
|
-
end
|
143
|
-
|
144
|
-
# The Rye:Rap response objects also give you the STDOUT and STDERR
|
145
|
-
# content separately. Here we redirect STDOUT to STDERR, so this
|
146
|
-
# will return nothing:
|
147
|
-
puts rbox.uname('-a 1>&2').stdout # =>
|
148
|
-
|
149
|
-
# It all went to STDERR:
|
150
|
-
puts rbox.uname('-a 1>&2').stderr # => Darwin ryehost 9.6.0 ...
|
151
|
-
|
152
|
-
# There were no actual error so the exit code should be 0.
|
153
|
-
puts rbox.uname('-a 1>&2').exit_code # => 0
|
154
|
-
|
155
|
-
|
156
|
-
== EXAMPLE 6 -- FILE TRANSFERS
|
58
|
+
== EXAMPLE 4 -- FILE TRANSFERS
|
157
59
|
|
158
60
|
rbox = Rye::Box.new("localhost", :info => true)
|
159
61
|
|
@@ -166,7 +68,7 @@ One of:
|
|
166
68
|
applejack = StringIO.new("Some in-memory content")
|
167
69
|
rbox.upload(applejack, "#{dir_upload}/applejack.txt")
|
168
70
|
|
169
|
-
p rbox.ls(dir_upload)
|
71
|
+
p rbox.ls(dir_upload) # => [README.rdoc, LICENSE.txt, applejack.txt]
|
170
72
|
p rbox.cat("#{dir_upload}/applejack.txt") # => "Some in-memory content"
|
171
73
|
|
172
74
|
filecontent = StringIO.new
|
@@ -174,32 +76,13 @@ One of:
|
|
174
76
|
|
175
77
|
p filecontent.read
|
176
78
|
|
177
|
-
== EXAMPLE 7 -- rye
|
178
|
-
|
179
|
-
Rye comes with a command-line utility called <tt>rye</tt>.
|
180
|
-
|
181
|
-
# Prints the paths to your private keys
|
182
|
-
$ rye keys
|
183
|
-
|
184
|
-
# Prints the server host keys (suitable for ~/.ssh/known_hosts)
|
185
|
-
$ rye hostkey HOST1 HOST2
|
186
|
-
|
187
|
-
# Add your public keys to authorized_keys and authorized_keys2
|
188
|
-
# on a remote machine
|
189
|
-
$ rye authorize HOST1 HOST2
|
190
|
-
More info:
|
191
|
-
|
192
|
-
$ rye -h
|
193
|
-
$ rye COMMAND -h
|
194
|
-
$ rye show-commands
|
195
|
-
|
196
79
|
|
197
80
|
== About Safe-Mode
|
198
81
|
|
199
82
|
In safe-mode:
|
200
83
|
|
201
84
|
* You can't use file globs. This means you can't do this: <tt>rbox.ls('*.rb')</tt>. <tt>~</tt> also doesn't work!
|
202
|
-
*
|
85
|
+
* You can't use environment variables as arguments. This means you can't do this: <tt>rbox.echo('$HOME')</tt>. However, environment variables are available to the commands you run.
|
203
86
|
* Pipes and operators don't work: <tt>|, &&, >, <, ||, ~</tt>, etc...
|
204
87
|
* Backticks don't work either: <tt>procs=`ps aux`</tt>
|
205
88
|
|
@@ -221,28 +104,28 @@ Rye permits only a limited number of system commands to be run. This default whi
|
|
221
104
|
* highline
|
222
105
|
* drydock
|
223
106
|
|
107
|
+
|
224
108
|
== Known Issues
|
225
109
|
|
226
110
|
This list will grow. If you find one let me know!
|
227
111
|
|
228
112
|
* Rye doesn't read the ~/.ssh/config file yet
|
229
|
-
* Highline 1.5 not working in Ruby 1.9 (password prompts hang)
|
230
113
|
* Rye uses OpenSSL's ssh-agent (if it exists). Rye starts it up as a child process and shuts it down using at_exit. If you have code in an at_exit that rely's on Rye, make sure your code runs before Rye's at_exit block is called. For example, Drydock uses at_exit too which is why in bin/rye you can see that Drydock is called explicitly so that Rye's at_exit is executed after Drydock executes a command.
|
231
114
|
|
115
|
+
|
232
116
|
== Thanks
|
233
117
|
|
234
118
|
* Solutious Incorporated (http://solutious.com) for all the orange juice.
|
235
|
-
* http://
|
236
|
-
* http://github.com/
|
237
|
-
* http://
|
238
|
-
|
239
|
-
* http://paste.lisp.org/display/6912
|
119
|
+
* Kalin Harvey (http://rely.ca)
|
120
|
+
* Rush[http://github.com/adamwiggins/rush]
|
121
|
+
* Capistrano[http://github.com/jamis/capistrano/blob/master/lib/capistrano/shell.rb]
|
122
|
+
|
240
123
|
|
241
124
|
== More Info
|
242
125
|
|
243
|
-
* http://github.com/delano/rye
|
244
|
-
* http://delano.github.com/rye
|
245
|
-
* http://www.youtube.com/watch?v=_StUVh6ENuw
|
126
|
+
* GitHub[http://github.com/delano/rye]
|
127
|
+
* Rdocs[http://delano.github.com/rye]
|
128
|
+
* Inspiration[http://www.youtube.com/watch?v=_StUVh6ENuw]
|
246
129
|
|
247
130
|
== Credits
|
248
131
|
|
data/bin/rye
CHANGED
@@ -35,7 +35,8 @@ after do
|
|
35
35
|
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
about "Add your public keys to one or more remote machines"
|
39
|
+
usage "rye authorize [-u username] host"
|
39
40
|
option :u, :user, String, "Username"
|
40
41
|
argv :hostname
|
41
42
|
command :authorize do |obj|
|
@@ -56,7 +57,7 @@ command :authorize do |obj|
|
|
56
57
|
end
|
57
58
|
command_alias :authorize, :authorise
|
58
59
|
|
59
|
-
|
60
|
+
about "Add your public keys to your current account on this machine"
|
60
61
|
command :authorize_local do |obj|
|
61
62
|
user = Rye.sysinfo.user
|
62
63
|
puts "Authorizing #{user}@localhost"
|
@@ -68,11 +69,11 @@ end
|
|
68
69
|
command_alias :local_authorize, :local_authorise
|
69
70
|
|
70
71
|
|
71
|
-
|
72
|
+
about "Generate a public key from a private key"
|
72
73
|
|
73
74
|
|
74
75
|
|
75
|
-
|
76
|
+
about "Fetch the host keys for remote machines (suitable for your ~/.ssh/known_hosts file)"
|
76
77
|
usage "rye hostkey HOSTNAME [HOSTNAME2 HOSTNAME3 ...]"
|
77
78
|
argv :hostname
|
78
79
|
command :hostkeys do |obj|
|
@@ -84,14 +85,14 @@ end
|
|
84
85
|
command_alias :hostkeys, :hostkey
|
85
86
|
|
86
87
|
|
87
|
-
|
88
|
+
about "Display your private keys"
|
88
89
|
command :keys do |obj|
|
89
90
|
Rye.keys.each do |key|
|
90
91
|
puts key.join(' ')
|
91
92
|
end
|
92
93
|
end
|
93
94
|
|
94
|
-
|
95
|
+
about "Display your public SSH keys"
|
95
96
|
usage "rye pubkeys [--pem] [KEYPATH]"
|
96
97
|
option :p, :pem, "Output in PEM format"
|
97
98
|
argv :keypath
|
data/lib/rye.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
|
2
|
-
|
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')
|
3
5
|
|
4
6
|
require 'tempfile'
|
5
7
|
require 'logger'
|
@@ -32,6 +34,12 @@ require 'sys'
|
|
32
34
|
# * See +bin/try+ for a bunch of working examples.
|
33
35
|
# * See Rye::Box#initialize for info about disabling safe-mode.
|
34
36
|
#
|
37
|
+
#--
|
38
|
+
# The following posts were really helpful when I first wrote Rye:
|
39
|
+
# http://www.nofluffjuststuff.com/blog/david_bock/2008/10/ruby_s_closure_cleanup_idiom_and_net_ssh.html
|
40
|
+
# http://groups.google.com/group/ruby-talk-google/browse_thread/thread/674a6f6de15ceb49?pli=1
|
41
|
+
# http://paste.lisp.org/display/6912
|
42
|
+
#++
|
35
43
|
module Rye
|
36
44
|
extend self
|
37
45
|
|
data/lib/rye/box.rb
CHANGED
@@ -182,6 +182,7 @@ module Rye
|
|
182
182
|
print "\a" if retried == 0 && @info # Ring the bell once
|
183
183
|
retried += 1
|
184
184
|
if STDIN.tty? && retried <= 3
|
185
|
+
STDERR.puts "Passwordless login failed for #{@opts[:user]}"
|
185
186
|
@opts[:password] = highline.ask("Password: ") { |q| q.echo = '' }
|
186
187
|
@opts[:auth_methods] ||= []
|
187
188
|
@opts[:auth_methods] << 'password'
|
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.6.
|
4
|
+
s.version = "0.6.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"
|