rush 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +5 -1
- data/lib/rush.rb +4 -0
- data/lib/rush/array_ext.rb +2 -0
- data/lib/rush/box.rb +19 -0
- data/lib/rush/dir.rb +13 -3
- data/lib/rush/entry.rb +8 -3
- data/lib/rush/exceptions.rb +26 -0
- data/lib/rush/file.rb +6 -2
- data/lib/rush/find_by.rb +37 -0
- data/lib/rush/local.rb +100 -15
- data/lib/rush/process.rb +19 -4
- data/lib/rush/remote.rb +47 -6
- data/lib/rush/server.rb +48 -12
- data/lib/rush/shell.rb +23 -14
- data/lib/rush/ssh_tunnel.rb +21 -12
- data/spec/box_spec.rb +25 -0
- data/spec/dir_spec.rb +5 -0
- data/spec/entry_spec.rb +2 -2
- data/spec/file_spec.rb +4 -0
- data/spec/find_by_spec.rb +55 -0
- data/spec/local_spec.rb +80 -13
- data/spec/process_spec.rb +32 -3
- data/spec/remote_spec.rb +46 -0
- data/spec/shell_spec.rb +1 -1
- data/spec/ssh_tunnel_spec.rb +22 -6
- metadata +33 -4
data/spec/local_spec.rb
CHANGED
@@ -27,6 +27,11 @@ describe Rush::Connection::Local do
|
|
27
27
|
@con.receive(:action => 'destroy', :full_path => 'file')
|
28
28
|
end
|
29
29
|
|
30
|
+
it "receive -> purge(dir)" do
|
31
|
+
@con.should_receive(:purge).with('dir')
|
32
|
+
@con.receive(:action => 'purge', :full_path => 'dir')
|
33
|
+
end
|
34
|
+
|
30
35
|
it "receive -> create_dir(path)" do
|
31
36
|
@con.should_receive(:create_dir).with('dir')
|
32
37
|
@con.receive(:action => 'create_dir', :full_path => 'dir')
|
@@ -79,7 +84,12 @@ describe Rush::Connection::Local do
|
|
79
84
|
|
80
85
|
it "receive -> kill_process" do
|
81
86
|
@con.should_receive(:kill_process).with(123).and_return(true)
|
82
|
-
@con.receive(:action => 'kill_process', :pid => 123)
|
87
|
+
@con.receive(:action => 'kill_process', :pid => '123')
|
88
|
+
end
|
89
|
+
|
90
|
+
it "receive -> bash" do
|
91
|
+
@con.should_receive(:bash).with('cmd').and_return('output')
|
92
|
+
@con.receive(:action => 'bash', :payload => 'cmd').should == 'output'
|
83
93
|
end
|
84
94
|
|
85
95
|
it "receive -> unknown action exception" do
|
@@ -99,6 +109,11 @@ describe Rush::Connection::Local do
|
|
99
109
|
@con.file_contents(fname).should == "stuff\n"
|
100
110
|
end
|
101
111
|
|
112
|
+
it "file_contents raises DoesNotExist if the file does not exist" do
|
113
|
+
fname = "#{@sandbox_dir}/does_not_exist"
|
114
|
+
lambda { @con.file_contents(fname) }.should raise_error(Rush::DoesNotExist, fname)
|
115
|
+
end
|
116
|
+
|
102
117
|
it "destroy to destroy a file or dir" do
|
103
118
|
fname = "#{@sandbox_dir}/delete_me"
|
104
119
|
system "touch #{fname}"
|
@@ -106,6 +121,20 @@ describe Rush::Connection::Local do
|
|
106
121
|
File.exists?(fname).should be_false
|
107
122
|
end
|
108
123
|
|
124
|
+
it "purge to purge a dir" do
|
125
|
+
system "cd #{@sandbox_dir}; touch {1,2}; mkdir 3; touch 3/4"
|
126
|
+
@con.purge(@sandbox_dir)
|
127
|
+
File.exists?(@sandbox_dir).should be_true
|
128
|
+
Dir.glob("#{@sandbox_dir}/*").should == []
|
129
|
+
end
|
130
|
+
|
131
|
+
it "purge kills hidden (dotfile) entries too" do
|
132
|
+
system "cd #{@sandbox_dir}; touch .killme"
|
133
|
+
@con.purge(@sandbox_dir)
|
134
|
+
File.exists?(@sandbox_dir).should be_true
|
135
|
+
`cd #{@sandbox_dir}; ls -lA | wc -l`.to_i.should == 0
|
136
|
+
end
|
137
|
+
|
109
138
|
it "create_dir creates a directory" do
|
110
139
|
fname = "#{@sandbox_dir}/a/b/c/"
|
111
140
|
@con.create_dir(fname)
|
@@ -127,6 +156,14 @@ describe Rush::Connection::Local do
|
|
127
156
|
File.exists?("#{@sandbox_dir}/subdir/a").should be_true
|
128
157
|
end
|
129
158
|
|
159
|
+
it "copy raises DoesNotExist with source path if it doesn't exist or otherwise can't be accessed" do
|
160
|
+
lambda { @con.copy('/does/not/exist', '/tmp') }.should raise_error(Rush::DoesNotExist, '/does/not/exist')
|
161
|
+
end
|
162
|
+
|
163
|
+
it "copy raises DoesNotExist with destination path if it can't access the destination" do
|
164
|
+
lambda { @con.copy('/tmp', '/does/not/exist') }.should raise_error(Rush::DoesNotExist, '/does/not')
|
165
|
+
end
|
166
|
+
|
130
167
|
it "read_archive to pull an archive of a dir into a byte stream" do
|
131
168
|
system "touch #{@sandbox_dir}/a"
|
132
169
|
@con.read_archive(@sandbox_dir).size.should > 50
|
@@ -150,11 +187,20 @@ describe Rush::Connection::Local do
|
|
150
187
|
@con.index(@sandbox_dir, '*.rb').should == [ 'a.rb' ]
|
151
188
|
end
|
152
189
|
|
190
|
+
it "index raises DoesNotExist when the base path is invalid" do
|
191
|
+
lambda { @con.index('/does/not/exist', '*') }.should raise_error(Rush::DoesNotExist, '/does/not/exist')
|
192
|
+
end
|
193
|
+
|
153
194
|
it "stat gives file stats like size and timestamps" do
|
154
195
|
@con.stat(@sandbox_dir).should have_key(:ctime)
|
155
196
|
@con.stat(@sandbox_dir).should have_key(:size)
|
156
197
|
end
|
157
198
|
|
199
|
+
it "stat raises DoesNotExist if the entry does not exist" do
|
200
|
+
fname = "#{@sandbox_dir}/does_not_exist"
|
201
|
+
lambda { @con.stat(fname) }.should raise_error(Rush::DoesNotExist, fname)
|
202
|
+
end
|
203
|
+
|
158
204
|
if !RUBY_PLATFORM.match(/darwin/) # doesn't work on OS X 'cause du switches are different
|
159
205
|
it "size gives size of a directory and all its contents recursively" do
|
160
206
|
system "mkdir -p #{@sandbox_dir}/a/b/; echo 1234 > #{@sandbox_dir}/a/b/c"
|
@@ -163,11 +209,12 @@ describe Rush::Connection::Local do
|
|
163
209
|
end
|
164
210
|
|
165
211
|
it "parses ps output on os x" do
|
166
|
-
@con.parse_ps("21712 501 1236 0 /usr/bin/vi somefile.rb").should == {
|
212
|
+
@con.parse_ps("21712 501 21711 1236 0 /usr/bin/vi somefile.rb").should == {
|
167
213
|
:pid => "21712",
|
168
214
|
:uid => "501",
|
169
|
-
:
|
170
|
-
:
|
215
|
+
:parent_pid => 21711,
|
216
|
+
:mem => 1236,
|
217
|
+
:cpu => 0,
|
171
218
|
:command => '/usr/bin/vi',
|
172
219
|
:cmdline => '/usr/bin/vi somefile.rb',
|
173
220
|
}
|
@@ -175,22 +222,42 @@ describe Rush::Connection::Local do
|
|
175
222
|
|
176
223
|
it "gets the list of processes on os x via the ps command" do
|
177
224
|
@con.should_receive(:os_x_raw_ps).and_return <<EOPS
|
178
|
-
PID UID RSS CPU COMMAND
|
179
|
-
1 0
|
180
|
-
2 501
|
225
|
+
PID UID PPID RSS CPU COMMAND
|
226
|
+
1 0 1 1111 0 cmd1 args
|
227
|
+
2 501 1 222 1 cmd2
|
181
228
|
EOPS
|
182
229
|
@con.os_x_processes.should == [
|
183
|
-
{ :pid => "1", :uid => "0", :
|
184
|
-
{ :pid => "2", :uid => "501", :
|
230
|
+
{ :pid => "1", :uid => "0", :parent_pid => 1, :mem => 1111, :cpu => 0, :command => "cmd1", :cmdline => "cmd1 args" },
|
231
|
+
{ :pid => "2", :uid => "501", :parent_pid => 1, :mem => 222, :cpu => 1, :command => "cmd2", :cmdline => "cmd2" },
|
185
232
|
]
|
186
233
|
end
|
187
234
|
|
188
|
-
it "
|
189
|
-
@con.process_alive(Process.pid).should
|
235
|
+
it "the current process should be alive" do
|
236
|
+
@con.process_alive(Process.pid).should be_true
|
237
|
+
end
|
238
|
+
|
239
|
+
it "a made-up process should not be alive" do
|
240
|
+
@con.process_alive(99999).should be_false
|
190
241
|
end
|
191
242
|
|
192
243
|
it "kills a process by pid" do
|
193
|
-
::Process.should_receive(:kill).
|
194
|
-
@con.kill_process(
|
244
|
+
::Process.should_receive(:kill).at_least(:once)
|
245
|
+
@con.kill_process(123)
|
246
|
+
end
|
247
|
+
|
248
|
+
it "executes a bash command, returning stdout when successful" do
|
249
|
+
@con.bash("echo test").should == "test\n"
|
250
|
+
end
|
251
|
+
|
252
|
+
it "executes a bash command, raising and error (with stderr as the message) when return value is nonzero" do
|
253
|
+
lambda { @con.bash("no_such_bin") }.should raise_error(Rush::BashFailed, /command not found/)
|
254
|
+
end
|
255
|
+
|
256
|
+
it "ensure_tunnel to match with remote connection" do
|
257
|
+
@con.ensure_tunnel
|
258
|
+
end
|
259
|
+
|
260
|
+
it "always returns true on alive?" do
|
261
|
+
@con.should be_alive
|
195
262
|
end
|
196
263
|
end
|
data/spec/process_spec.rb
CHANGED
@@ -12,6 +12,13 @@ describe Rush::Process do
|
|
12
12
|
system "kill -9 #{@pid}"
|
13
13
|
end
|
14
14
|
|
15
|
+
if !RUBY_PLATFORM.match(/darwin/) # OS x reports pids weird
|
16
|
+
it "knows all its child processes" do
|
17
|
+
parent = Rush::Process.all.detect { |p| p.pid == Process.pid }
|
18
|
+
parent.children.should == [ @process ]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
15
22
|
it "gets the list of all processes" do
|
16
23
|
list = Rush::Process.all
|
17
24
|
list.size.should > 5
|
@@ -34,11 +41,33 @@ describe Rush::Process do
|
|
34
41
|
@process.cmdline.should match(/process_spec.rb/)
|
35
42
|
end
|
36
43
|
|
44
|
+
it "knows the memory used" do
|
45
|
+
@process.mem.should > 0
|
46
|
+
end
|
47
|
+
|
48
|
+
it "knows the cpu used" do
|
49
|
+
@process.cpu.should >= 0
|
50
|
+
end
|
51
|
+
|
52
|
+
it "knows the parent process pid" do
|
53
|
+
@process.parent_pid.should == Process.pid
|
54
|
+
end
|
55
|
+
|
56
|
+
it "knows the parent process" do
|
57
|
+
this = Rush::Box.new.processes.select { |p| p.pid == Process.pid }.first
|
58
|
+
@process.parent.should == this
|
59
|
+
end
|
60
|
+
|
37
61
|
it "can kill itself" do
|
38
62
|
system "sleep 30 &"
|
39
|
-
|
40
|
-
|
63
|
+
process = Rush::Process.all.detect { |p| p.command == "sleep" }
|
64
|
+
process.kill
|
41
65
|
sleep 0.1
|
42
|
-
|
66
|
+
process.alive?.should be_false
|
67
|
+
end
|
68
|
+
|
69
|
+
it "if box and pid are the same, process is equal" do
|
70
|
+
other = Rush::Process.new({ :pid => @process.pid }, @process.box)
|
71
|
+
@process.should == other
|
43
72
|
end
|
44
73
|
end
|
data/spec/remote_spec.rb
CHANGED
@@ -27,6 +27,11 @@ describe Rush::Connection::Local do
|
|
27
27
|
@con.destroy('file')
|
28
28
|
end
|
29
29
|
|
30
|
+
it "transmits purge" do
|
31
|
+
@con.should_receive(:transmit).with(:action => 'purge', :full_path => 'dir')
|
32
|
+
@con.purge('dir')
|
33
|
+
end
|
34
|
+
|
30
35
|
it "transmits create_dir" do
|
31
36
|
@con.should_receive(:transmit).with(:action => 'create_dir', :full_path => 'file')
|
32
37
|
@con.create_dir('file')
|
@@ -81,4 +86,45 @@ describe Rush::Connection::Local do
|
|
81
86
|
@con.should_receive(:transmit).with(:action => 'kill_process', :pid => 123)
|
82
87
|
@con.kill_process(123)
|
83
88
|
end
|
89
|
+
|
90
|
+
it "transmits bash" do
|
91
|
+
@con.should_receive(:transmit).with(:action => 'bash', :payload => 'cmd').and_return('output')
|
92
|
+
@con.bash('cmd').should == 'output'
|
93
|
+
end
|
94
|
+
|
95
|
+
it "an http result code of 401 raises NotAuthorized" do
|
96
|
+
lambda { @con.process_result("401", "") }.should raise_error(Rush::NotAuthorized)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "an http result code of 400 raises the exception passed in the result body" do
|
100
|
+
@con.stub!(:parse_exception).and_return(Rush::DoesNotExist, "message")
|
101
|
+
lambda { @con.process_result("400", "") }.should raise_error(Rush::DoesNotExist)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "an http result code of 501 (or anything other than the other defined codes) raises FailedTransmit" do
|
105
|
+
lambda { @con.process_result("501", "") }.should raise_error(Rush::FailedTransmit)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "parse_exception takes the class from the first line and the message from the second" do
|
109
|
+
@con.parse_exception("Rush::DoesNotExist\nthe message\n").should == [ Rush::DoesNotExist, "the message" ]
|
110
|
+
end
|
111
|
+
|
112
|
+
it "parse_exception rejects unrecognized exceptions" do
|
113
|
+
lambda { @con.parse_exception("NotARushException\n") }.should raise_error
|
114
|
+
end
|
115
|
+
|
116
|
+
it "passes through ensure_tunnel" do
|
117
|
+
@con.tunnel.should_receive(:ensure_tunnel)
|
118
|
+
@con.ensure_tunnel
|
119
|
+
end
|
120
|
+
|
121
|
+
it "is alive if the box is responding to commands" do
|
122
|
+
@con.should_receive(:index).and_return(:dummy)
|
123
|
+
@con.should be_alive
|
124
|
+
end
|
125
|
+
|
126
|
+
it "not alive if an attempted command throws an exception" do
|
127
|
+
@con.should_receive(:index).and_raise(RuntimeError)
|
128
|
+
@con.should_not be_alive
|
129
|
+
end
|
84
130
|
end
|
data/spec/shell_spec.rb
CHANGED
data/spec/ssh_tunnel_spec.rb
CHANGED
@@ -58,19 +58,35 @@ describe Rush::SshTunnel do
|
|
58
58
|
@tunnel.tunnel_options.should == {
|
59
59
|
:local_port => 1234,
|
60
60
|
:remote_port => 7770,
|
61
|
-
:ssh_host => 'spec.example.com'
|
62
|
-
:stall_command => 'sleep 9000'
|
61
|
+
:ssh_host => 'spec.example.com'
|
63
62
|
}
|
64
63
|
end
|
65
64
|
|
66
|
-
it "
|
65
|
+
it "ssh_stall_command uses an infinite loop for :timeout => :infinite" do
|
66
|
+
@tunnel.ssh_stall_command(:timeout => :infinite).should match(/while .* sleep .* done/)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "ssh_stall_command sleeps for the number of seconds given as the :timeout option" do
|
70
|
+
@tunnel.ssh_stall_command(:timeout => 123).should == "sleep 123"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "ssh_stall_command uses the default timeout when no options are given" do
|
74
|
+
@tunnel.ssh_stall_command.should == "sleep 9000"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "constructs the ssh tunnel command (everything but stall) from the options hash" do
|
67
78
|
@tunnel.should_receive(:tunnel_options).at_least(:once).and_return(
|
68
79
|
:local_port => 123,
|
69
80
|
:remote_port => 456,
|
70
|
-
:ssh_host => 'example.com'
|
71
|
-
:stall_command => 'stall'
|
81
|
+
:ssh_host => 'example.com'
|
72
82
|
)
|
73
|
-
@tunnel.
|
83
|
+
@tunnel.ssh_tunnel_command_without_stall.should == "ssh -f -L 123:127.0.0.1:456 example.com"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "combines the tunnel command without stall and the stall command into the final command" do
|
87
|
+
@tunnel.should_receive(:ssh_tunnel_command_without_stall).and_return('ssh command')
|
88
|
+
@tunnel.should_receive(:ssh_stall_command).and_return('sleep 123')
|
89
|
+
@tunnel.ssh_tunnel_command.should == 'ssh command "sleep 123"'
|
74
90
|
end
|
75
91
|
|
76
92
|
it "ssh_tunnel_command request that the port be set" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rush
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.2"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Wiggins
|
@@ -9,10 +9,36 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-03-12 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mongrel
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: rspec
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: "0"
|
32
|
+
version:
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: session
|
35
|
+
version_requirement:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: "0"
|
41
|
+
version:
|
16
42
|
description: A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client.
|
17
43
|
email: adam@heroku.com
|
18
44
|
executables:
|
@@ -33,7 +59,9 @@ files:
|
|
33
59
|
- lib/rush/config.rb
|
34
60
|
- lib/rush/dir.rb
|
35
61
|
- lib/rush/entry.rb
|
62
|
+
- lib/rush/exceptions.rb
|
36
63
|
- lib/rush/file.rb
|
64
|
+
- lib/rush/find_by.rb
|
37
65
|
- lib/rush/fixnum_ext.rb
|
38
66
|
- lib/rush/head_tail.rb
|
39
67
|
- lib/rush/local.rb
|
@@ -53,6 +81,7 @@ files:
|
|
53
81
|
- spec/dir_spec.rb
|
54
82
|
- spec/entry_spec.rb
|
55
83
|
- spec/file_spec.rb
|
84
|
+
- spec/find_by_spec.rb
|
56
85
|
- spec/fixnum_ext_spec.rb
|
57
86
|
- spec/local_spec.rb
|
58
87
|
- spec/process_spec.rb
|