rye 0.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.txt ADDED
@@ -0,0 +1,26 @@
1
+ RYE, CHANGES
2
+
3
+ #### 0.3 (2009-04-05) ###############################
4
+
5
+ * ADDED: Rye::Set supports executing commands parallel
6
+ * ADDED: Rye::Rap now contains STDERR output from command
7
+ * FIXED: Rye::Box wasn't properly adding keypairs to SSH Agent
8
+ * CHANGED: Moved all SSH key stuff to Rye (used to be done per Box)
9
+ * ADDED: Supports all options provided by Net::SSH#start. This
10
+ includes support for password logins and proxies.
11
+ * ADDED: Safe mode can now be disabled (to allow file globs
12
+ and environment variable access).
13
+ * ADDED: Basic sanity test
14
+ * FIXED: Rye::Box.method_missing Symbol/String ambiguity
15
+ * ADDED: Mucho more rdocs and examples.
16
+
17
+ #### 0.2 (2009-04-04) ###############################
18
+
19
+ * FIXED: ssh-agent shutdown wasn't deleting the SSH tmp directory
20
+ * ADDED: Now with more rdocs!
21
+
22
+
23
+ #### 0.1 (2009-04-03) ###############################
24
+
25
+ Initial public release
26
+
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Delano Mandelbaum, Solutious Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,176 @@
1
+ = Rye - v0.3
2
+
3
+ Run system commands via SSH locally and remotely in a Ruby way.
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>.
6
+
7
+ See +bin/try+ for working examples.
8
+
9
+ == Installation
10
+
11
+ One of:
12
+
13
+ $ sudo gem install rye
14
+ $ sudo gem install delano-rye --source http://gems.github.com/
15
+ $ git clone git://github.com/delano/rye.git
16
+
17
+
18
+ == EXAMPLE 1 -- Basic Usage
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.add_env(: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
50
+
51
+
52
+ == EXAMPLE 2 -- Disabling Safe-Mode
53
+
54
+ rbox_safe = Rye::Box.new('localhost')
55
+ rbox_wild = Rye::Box.new('localhost', :safe => false)
56
+
57
+ # Safe-mode is enabled by default. In safe-mode, all command
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*') # =>
62
+ p rbox_safe.ls('-l | wc -l') # =>
63
+ p rbox_safe.echo('$HOME > /tmp/rye-home') # => "$HOME > /tmp/home"
64
+ p rbox_safe.cat('/tmp/rye-home') # =>
65
+ p rbox_safe.cat('/tmp/rye-home').stderr # => "No such file or directory"
66
+
67
+ # Here's the same commands with safe-mode disabled:
68
+ p rbox_wild.echo('$HOME') # => "/home/rye"
69
+ p rbox_wild['/etc'].ls('host*') # => ["hostconfig", "hosts"]
70
+ p rbox_wild.ls('-l | wc -l') # => 110
71
+ p rbox_wild.echo('$HOME > /tmp/rye-home') # =>
72
+ p rbox_wild.cat('/tmp/rye-home') # => "/home/rye"
73
+ p rbox_wild.rm('/tmp/rye-home') # =>
74
+
75
+
76
+
77
+ == EXAMPLE 3 -- Custom Commands
78
+
79
+ rbox = Rye::Box.new('localhost')
80
+ rbox.add_keys('/private/key/path') # Specify additional private keys
81
+
82
+ # There's currently no rye900 command
83
+ p rbox.commands.member?('rye9000') # => false
84
+
85
+ # But we can add our own commands to the Rye::Cmd class. They
86
+ # automatically become available to all Rye::Box objects.
87
+ module Rye::Cmd
88
+ def rye9000(*args)
89
+ add_command("ls", args)
90
+ end
91
+ def somescript(*args)
92
+ add_command("/path/to/my/script", args)
93
+ end
94
+ end
95
+
96
+ # We can now run rye9000 (with arguments)
97
+ p rbox.rye9000('-a') # => [".", "..", ".bashrc", ...]
98
+ p rbox.commands.member?('rye9000') # => true
99
+
100
+
101
+ == EXAMPLE 4 -- Accessing Multiple Machines
102
+
103
+ rset = Rye::Set.new
104
+ rbox = Rye::Box.new
105
+
106
+ rset.add_keys('/private/key/path') # For passwordless logins
107
+ rset.add_boxes(rbox, 'localhost') # Add boxes as hostnames or objects
108
+
109
+ # Calling methods on Rye::Set objects is very similar to calling them on
110
+ # Rye::Box objects. In fact, it's identical:
111
+ p rset.uptime # => [[14:19:02 up 32 days, 19:35 ...], [14:19:02 up 30 days, 01:35]]
112
+ p rset['/etc'].ls # => [['file1', 'file2', ...], ['life1', 'life2', ...]]
113
+
114
+ # Like Rye::Box, the response value is a Rye::Rap object containing the
115
+ # responses from each box. Each response is itself a Rye::Rap object.
116
+ unames = rset.uname
117
+ p unames # => [["Darwin"], ["Darwin"]]
118
+ puts unames.class # => Rye::Rap
119
+
120
+ # The Rye::Rap object also keeps a reference to the object that called the
121
+ # command. In this case, it will keep a reference to Rye::Set:
122
+ puts unames.set.class # => Rye::Set
123
+ puts unames.set == rset # => true
124
+ puts unames.size # => 2
125
+ puts unames.first # => Darwin
126
+ puts unames.first.class # => Rye::Rap
127
+ puts unames.first.box.class # => Rye::Box
128
+ puts unames.first.box == rbox # => true
129
+
130
+ # Envrionment variables can be set the same way as with Rye::Box
131
+ rset.add_env(:RYE, "Forty Creek")
132
+ p rset.env.first.select { |env| env =~ /RYE/ } # => ["RYE=Forty Creek"]
133
+
134
+
135
+ == About Safe-Mode
136
+
137
+ In safe-mode:
138
+
139
+ * You can't use file globs. This means you can't do this: <tt>rbox.ls('*.rb')</tt>.
140
+ * Commands arguments cannot contain environment variables. However, environment variables are available to the commands you run. This means you can't do this: <tt>rbox.echo('$HOME')</tt>.
141
+ * Pipes and operators don't work: <tt>|, &&, >, <, ||</tt>, etc...
142
+ * Backticks don't work either: <tt>procs=`ps aux`</tt>
143
+
144
+ Why? In safe-mode, all command arguments are escaped which turns all arguments into their literal values.
145
+
146
+ Using a Ruby interface to execute shell commands is pretty awesome, particularly to run them on several machines simultaneously. That's a lot of power and it's potentially very dangerous. That's why some stuff isn't available in Rye that you would otherwise expect to be able to do (particularly the file globs). There's probably a way to do it safely but it's not obvious yet (to me). If you have any ideas, I'd love to hear them!
147
+
148
+
149
+ == Credits
150
+
151
+ * Delano Mandelbaum (delano@solutious.com)
152
+
153
+
154
+ == Thanks
155
+
156
+ * Solutious Incorporated (http://solutious.com) for all the orange juice.
157
+ * The country of Canada for making Rye Whiskey.
158
+
159
+
160
+ == Kudos
161
+
162
+ * http://github.com/adamwiggins/rush
163
+ * http://github.com/jamis/capistrano/blob/master/lib/capistrano/shell.rb
164
+ * http://www.nofluffjuststuff.com/blog/david_bock/2008/10/ruby_s_closure_cleanup_idiom_and_net_ssh.html
165
+ * http://groups.google.com/group/ruby-talk-google/browse_thread/thread/674a6f6de15ceb49?pli=1
166
+ * http://paste.lisp.org/display/6912
167
+
168
+
169
+ == Credits
170
+
171
+ * Delano Mandelbaum (delano@solutious.com)
172
+
173
+
174
+ == License
175
+
176
+ See: LICENSE.txt
data/Rakefile ADDED
@@ -0,0 +1,83 @@
1
+ require 'rubygems'
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+ require 'hanna/rdoctask'
5
+ require 'fileutils'
6
+ include FileUtils
7
+
8
+ task :default => :package
9
+
10
+ # CONFIG =============================================================
11
+
12
+ # Change the following according to your needs
13
+ README = "README.rdoc"
14
+ CHANGES = "CHANGES.txt"
15
+ LICENSE = "LICENSE.txt"
16
+
17
+ # Files and directories to be deleted when you run "rake clean"
18
+ CLEAN.include [ 'doc', 'pkg', '*.gem', '.config' ]
19
+
20
+ # Virginia assumes your project and gemspec have the same name
21
+ name = (Dir.glob('*.gemspec') || ['rye']).first.split('.').first
22
+ load "#{name}.gemspec"
23
+ version = @spec.version
24
+
25
+ # That's it! The following defaults should allow you to get started
26
+ # on other things.
27
+
28
+
29
+ # TESTS/SPECS =========================================================
30
+
31
+
32
+
33
+ # INSTALL =============================================================
34
+
35
+ Rake::GemPackageTask.new(@spec) do |p|
36
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
37
+ end
38
+
39
+ task :release => [ :rdoc, :package ]
40
+ task :install => [ :rdoc, :package ] do
41
+ sh %{sudo gem install pkg/#{name}-#{version}.gem}
42
+ end
43
+ task :uninstall => [ :clean ] do
44
+ sh %{sudo gem uninstall #{name}}
45
+ end
46
+
47
+
48
+ # RUBYFORGE RELEASE / PUBLISH TASKS ==================================
49
+
50
+ if @spec.rubyforge_project
51
+ desc 'Publish website to rubyforge'
52
+ task 'publish:rdoc' => 'doc/index.html' do
53
+ sh "scp -rp doc/* rubyforge.org:/var/www/gforge-projects/#{name}/"
54
+ end
55
+
56
+ desc 'Public release to rubyforge'
57
+ task 'publish:gem' => [:package] do |t|
58
+ sh <<-end
59
+ rubyforge add_release -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.gem &&
60
+ rubyforge add_file -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.tgz
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+
67
+ # RUBY DOCS TASK ==================================
68
+
69
+ Rake::RDocTask.new do |t|
70
+ t.rdoc_dir = 'doc'
71
+ t.title = @spec.summary
72
+ t.options << '--line-numbers' << '-A cattr_accessor=object'
73
+ t.options << '--charset' << 'utf-8'
74
+ t.rdoc_files.include(LICENSE)
75
+ t.rdoc_files.include(README)
76
+ t.rdoc_files.include(CHANGES)
77
+ #t.rdoc_files.include('bin/*')
78
+ t.rdoc_files.include('lib/**/*.rb')
79
+ end
80
+
81
+
82
+
83
+
data/bin/try ADDED
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # Rye -- A working example
4
+ #
5
+ # If your reading this via the rdocs you won't be able to see the code
6
+ # See: http://github.com/delano/rye/blob/master/bin/try
7
+ #
8
+ # Usage: bin/try
9
+ #
10
+
11
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
12
+
13
+ require 'rubygems'
14
+ require 'stringio'
15
+ require 'yaml'
16
+ require 'rye'
17
+
18
+
19
+ puts %Q(
20
+ # ------------------------------------------------------------------
21
+ # EXAMPLE 1 -- Basic Usage
22
+ #)
23
+
24
+ rbox = Rye::Box.new('localhost')
25
+
26
+ # Commands are run as methods on the Rye::Box object
27
+ puts rbox.uptime # => 11:02 up 16:01, 3 users
28
+
29
+ # The response value for all commands is a Rye::Rap object. The rap is a
30
+ # subclass of Array so you can treat it as an Array, but it can also act
31
+ # like a String if there's only one element.
32
+ puts rbox.ls('rye.test') # => ""
33
+ puts rbox.ls('rye.test').stderr # => ls: rye.test: No such file or directory
34
+
35
+ puts rbox.touch('rye.test') # => ""
36
+ puts rbox.rm('rye.test') # => ""
37
+
38
+ # You can change directories
39
+ puts rbox.pwd # => /home/rye
40
+ puts rbox['/usr/bin'].pwd # => /usr/bin
41
+ puts rbox.pwd # => /usr/bin
42
+ puts rbox.cd('/home/rye').pwd # => /home/rye
43
+
44
+ # You can specify environment variables
45
+ rbox.add_env(:RYE, "Forty Creek")
46
+ rbox.env # => ['HOME=/home/rye', 'RYE=Forty Creek', ...]
47
+
48
+ # The commands method returns an Array of available commands:
49
+ puts rbox.commands.join(', ') # => pwd, touch, echo, wc, ...
50
+
51
+ # When you're done you can disconnect explicitly.
52
+ # (Although Rye does this automatically at exit.)
53
+ rbox.disconnect
54
+
55
+
56
+ puts %Q(
57
+ # ------------------------------------------------------------------
58
+ # EXAMPLE 2 -- Disabling Safe-Mode
59
+ #)
60
+
61
+ rbox_safe = Rye::Box.new('localhost')
62
+ rbox_wild = Rye::Box.new('localhost', :safe => false)
63
+
64
+ # Safe-mode is enabled by default. In safe-mode, all command
65
+ # arguments are thoroughly escaped. This prevents access to
66
+ # environment variables and file globs (among other things).
67
+ p rbox_safe.echo('$HOME') # => "$HOME"
68
+ p rbox_safe['/etc'].ls('host*') # =>
69
+ p rbox_safe.ls('-l | wc -l') # =>
70
+ p rbox_safe.echo('$HOME > /tmp/rye-home') # => "$HOME > /tmp/home"
71
+ p rbox_safe.cat('/tmp/rye-home') # =>
72
+ p rbox_safe.cat('/tmp/rye-home').stderr # => "No such file or directory"
73
+
74
+ # Here's the same commands with safe-mode disabled:
75
+ p rbox_wild.echo('$HOME') # => "/home/rye"
76
+ p rbox_wild['/etc'].ls('host*') # => ["hostconfig", "hosts"]
77
+ p rbox_wild.ls('-l | wc -l') # => 110
78
+ p rbox_wild.echo('$HOME > /tmp/rye-home') # =>
79
+ p rbox_wild.cat('/tmp/rye-home') # => "/home/rye"
80
+ p rbox_wild.rm('/tmp/rye-home') # =>
81
+
82
+
83
+ puts %Q(
84
+ # ------------------------------------------------------------------
85
+ # EXAMPLE 3 -- Custom Commands
86
+ #)
87
+
88
+ rbox = Rye::Box.new('localhost')
89
+ rbox.add_keys('/private/key/path') # Specify additional private keys
90
+
91
+ # There's currently no rye900 command
92
+ p rbox.commands.member?('rye9000') # => false
93
+
94
+ # But we can add our own commands to the Rye::Cmd class. They
95
+ # automatically become available to all Rye::Box objects.
96
+ module Rye::Cmd
97
+ def rye9000(*args)
98
+ add_command("ls", args)
99
+ end
100
+ def somescript(*args)
101
+ add_command("/path/to/my/script", args)
102
+ end
103
+ end
104
+
105
+ # We can now run rye9000 (with arguments)
106
+ p rbox.rye9000('-a') # => [".", "..", ".bashrc", ...]
107
+ p rbox.commands.member?('rye9000') # => true
108
+
109
+
110
+ puts %Q(
111
+ # ------------------------------------------------------------------
112
+ # EXAMPLE 4 -- Accessing Multiple Machines
113
+ #)
114
+
115
+ rset = Rye::Set.new
116
+ rbox = Rye::Box.new
117
+
118
+ rset.add_keys('/private/key/path') # For passwordless logins
119
+ rset.add_boxes(rbox, 'localhost') # Add boxes as hostnames or objects
120
+
121
+ # Calling methods on Rye::Set objects is very similar to calling them on
122
+ # Rye::Box objects. In fact, it's identical:
123
+ p rset.uptime # => [[14:19:02 up 32 days, 19:35 ...], [14:19:02 up 30 days, 01:35]]
124
+ p rset['/etc'].ls # => [['file1', 'file2', ...], ['life1', 'life2', ...]]
125
+
126
+ # Like Rye::Box, the response value is a Rye::Rap object containing the
127
+ # responses from each box. Each response is itself a Rye::Rap object.
128
+ unames = rset.uname
129
+ p unames # => [["Darwin"], ["Darwin"]]
130
+ puts unames.class # => Rye::Rap
131
+
132
+ # The Rye::Rap object also keeps a reference to the object that called the
133
+ # command. In this case, it will keep a reference to Rye::Set:
134
+ puts unames.set.class # => Rye::Set
135
+ puts unames.set == rset # => true
136
+ puts unames.size # => 2
137
+ puts unames.first # => Darwin
138
+ puts unames.first.class # => Rye::Rap
139
+ puts unames.first.box.class # => Rye::Box
140
+ puts unames.first.box == rbox # => true
141
+
142
+ # Envrionment variables can be set the same way as with Rye::Box
143
+ rset.add_env(:RYE, "Forty Creek")
144
+ p rset.env.first.select { |env| env =~ /RYE/ } # => ["RYE=Forty Creek"]
145
+
146
+
147
+
148
+