rush2 0.7.0

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +86 -0
  4. data/README.rdoc +125 -0
  5. data/Rakefile +57 -0
  6. data/VERSION +1 -0
  7. data/bin/rush +13 -0
  8. data/bin/rushd +7 -0
  9. data/lib/rush.rb +91 -0
  10. data/lib/rush/access.rb +121 -0
  11. data/lib/rush/array_ext.rb +19 -0
  12. data/lib/rush/box.rb +124 -0
  13. data/lib/rush/commands.rb +76 -0
  14. data/lib/rush/config.rb +147 -0
  15. data/lib/rush/dir.rb +166 -0
  16. data/lib/rush/embeddable_shell.rb +26 -0
  17. data/lib/rush/entry.rb +229 -0
  18. data/lib/rush/exceptions.rb +32 -0
  19. data/lib/rush/file.rb +94 -0
  20. data/lib/rush/find_by.rb +39 -0
  21. data/lib/rush/fixnum_ext.rb +18 -0
  22. data/lib/rush/head_tail.rb +11 -0
  23. data/lib/rush/local.rb +377 -0
  24. data/lib/rush/process.rb +59 -0
  25. data/lib/rush/process_set.rb +62 -0
  26. data/lib/rush/remote.rb +33 -0
  27. data/lib/rush/search_results.rb +71 -0
  28. data/lib/rush/shell.rb +111 -0
  29. data/lib/rush/shell/completion.rb +110 -0
  30. data/lib/rush/string_ext.rb +16 -0
  31. data/spec/access_spec.rb +134 -0
  32. data/spec/array_ext_spec.rb +15 -0
  33. data/spec/base.rb +22 -0
  34. data/spec/box_spec.rb +76 -0
  35. data/spec/commands_spec.rb +47 -0
  36. data/spec/config_spec.rb +108 -0
  37. data/spec/dir_spec.rb +163 -0
  38. data/spec/embeddable_shell_spec.rb +17 -0
  39. data/spec/entry_spec.rb +133 -0
  40. data/spec/file_spec.rb +83 -0
  41. data/spec/find_by_spec.rb +58 -0
  42. data/spec/fixnum_ext_spec.rb +19 -0
  43. data/spec/local_spec.rb +365 -0
  44. data/spec/process_set_spec.rb +50 -0
  45. data/spec/process_spec.rb +73 -0
  46. data/spec/remote_spec.rb +140 -0
  47. data/spec/rush_spec.rb +28 -0
  48. data/spec/search_results_spec.rb +44 -0
  49. data/spec/shell_spec.rb +35 -0
  50. data/spec/ssh_tunnel_spec.rb +122 -0
  51. data/spec/string_ext_spec.rb +23 -0
  52. metadata +209 -0
@@ -0,0 +1,50 @@
1
+ require_relative 'base'
2
+
3
+ describe Rush::ProcessSet do
4
+ before do
5
+ @process = double('process')
6
+ @set = Rush::ProcessSet.new([ @process ])
7
+ end
8
+
9
+ it "is Enumerable" do
10
+ @set.select { |s| s == @process }.should == [ @process ]
11
+ end
12
+
13
+ it "defines size" do
14
+ @set.size.should == 1
15
+ end
16
+
17
+ it "defines first" do
18
+ @set.first.should == @process
19
+ end
20
+
21
+ it "is equal to sets with the same contents" do
22
+ @set.should == Rush::ProcessSet.new([ @process ])
23
+ end
24
+
25
+ it "is equal to arrays with the same contents" do
26
+ @set.should == [ @process ]
27
+ end
28
+
29
+ it "kills all processes in the set" do
30
+ @process.should_receive(:kill)
31
+ @set.kill
32
+ end
33
+
34
+ it "checks the alive? state of all processes in the set" do
35
+ @process.should_receive(:alive?).and_return(true)
36
+ @set.alive?.should == [ true ]
37
+ end
38
+
39
+ it "filters the set from a conditions hash and returns the filtered set" do
40
+ @process.stub(:pid).and_return(123)
41
+ @set.filter(:pid => 123).first.should == @process
42
+ @set.filter(:pid => 456).size.should == 0
43
+ end
44
+
45
+ it "filters with regexps if provided in the conditions" do
46
+ @process.stub(:command).and_return('foobaz')
47
+ @set.filter(:command => /baz/).first.should == @process
48
+ @set.filter(:command => /blerg/).size.should == 0
49
+ end
50
+ end
@@ -0,0 +1,73 @@
1
+ require_relative 'base'
2
+
3
+ describe Rush::Process do
4
+ before do
5
+ @pid = fork do
6
+ sleep 999
7
+ end
8
+ @process = Rush::Process.all.detect { |p| p.pid == @pid }
9
+ end
10
+
11
+ after do
12
+ system "kill -9 #{@pid}"
13
+ end
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
+
22
+ it "gets the list of all processes" do
23
+ list = Rush::Process.all
24
+ list.size.should > 5
25
+ list.first.should be_kind_of(Rush::Process)
26
+ end
27
+
28
+ it "knows the pid" do
29
+ @process.pid.should == @pid
30
+ end
31
+
32
+ it "knows the uid" do
33
+ @process.uid.should == ::Process.uid
34
+ end
35
+
36
+ it "knows the executed binary" do
37
+ @process.command.should match(/(ruby|rbx)/)
38
+ end
39
+
40
+ it "knows the command line" do
41
+ @process.cmdline.should match(/process_spec.rb/)
42
+ end
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
+
61
+ it "can kill itself" do
62
+ process = Rush.bash("sleep 30", :background => true)
63
+ process.alive?.should be_true
64
+ process.kill
65
+ sleep 0.1
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
72
+ end
73
+ end
@@ -0,0 +1,140 @@
1
+ require_relative 'base'
2
+
3
+ describe Rush::Connection::Local do
4
+ before do
5
+ @sandbox_dir = "/tmp/rush_spec.#{Process.pid}"
6
+ system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}"
7
+
8
+ @con = Rush::Connection::Remote.new('spec.example.com')
9
+ end
10
+
11
+ after do
12
+ system "rm -rf #{@sandbox_dir}"
13
+ end
14
+
15
+ it "transmits write_file" do
16
+ @con.should_receive(:transmit).with(:action => 'write_file', :full_path => 'file', :payload => 'contents')
17
+ @con.write_file('file', 'contents')
18
+ end
19
+
20
+ it "transmits append_to_file" do
21
+ @con.should_receive(:transmit).with(:action => 'append_to_file', :full_path => 'file', :payload => 'contents')
22
+ @con.append_to_file('file', 'contents')
23
+ end
24
+
25
+ it "transmits file_contents" do
26
+ @con.should_receive(:transmit).with(:action => 'file_contents', :full_path => 'file').and_return('contents')
27
+ @con.file_contents('file').should == 'contents'
28
+ end
29
+
30
+ it "transmits destroy" do
31
+ @con.should_receive(:transmit).with(:action => 'destroy', :full_path => 'file')
32
+ @con.destroy('file')
33
+ end
34
+
35
+ it "transmits purge" do
36
+ @con.should_receive(:transmit).with(:action => 'purge', :full_path => 'dir')
37
+ @con.purge('dir')
38
+ end
39
+
40
+ it "transmits create_dir" do
41
+ @con.should_receive(:transmit).with(:action => 'create_dir', :full_path => 'file')
42
+ @con.create_dir('file')
43
+ end
44
+
45
+ it "transmits rename" do
46
+ @con.should_receive(:transmit).with(:action => 'rename', :path => 'path', :name => 'name', :new_name => 'new_name')
47
+ @con.rename('path', 'name', 'new_name')
48
+ end
49
+
50
+ it "transmits copy" do
51
+ @con.should_receive(:transmit).with(:action => 'copy', :src => 'src', :dst => 'dst')
52
+ @con.copy('src', 'dst')
53
+ end
54
+
55
+ it "transmits read_archive" do
56
+ @con.should_receive(:transmit).with(:action => 'read_archive', :full_path => 'full_path').and_return('archive data')
57
+ @con.read_archive('full_path').should == 'archive data'
58
+ end
59
+
60
+ it "transmits write_archive" do
61
+ @con.should_receive(:transmit).with(:action => 'write_archive', :dir => 'dir', :payload => 'archive')
62
+ @con.write_archive('archive', 'dir')
63
+ end
64
+
65
+ it "transmits index" do
66
+ @con.should_receive(:transmit).with(:action => 'index', :base_path => 'base_path', :glob => '*').and_return("1\n2\n")
67
+ @con.index('base_path', '*').should == %w(1 2)
68
+ end
69
+
70
+ it "transmits stat" do
71
+ @con.should_receive(:transmit).with(:action => 'stat', :full_path => 'full_path').and_return(YAML.dump(1 => 2))
72
+ @con.stat('full_path').should == { 1 => 2 }
73
+ end
74
+
75
+ it "transmits set_access" do
76
+ @con.should_receive(:transmit).with(:action => 'set_access', :full_path => 'full_path', :user => 'joe', :user_read => 1)
77
+ @con.set_access('full_path', :user => 'joe', :user_read => 1)
78
+ end
79
+
80
+ it "transmits size" do
81
+ @con.should_receive(:transmit).with(:action => 'size', :full_path => 'full_path').and_return("123")
82
+ @con.size('full_path').should == 123
83
+ end
84
+
85
+ it "transmits processes" do
86
+ @con.should_receive(:transmit).with(:action => 'processes').and_return(YAML.dump([ { :pid => 1 } ]))
87
+ @con.processes.should == [ { :pid => 1 } ]
88
+ end
89
+
90
+ it "transmits process_alive" do
91
+ @con.should_receive(:transmit).with(:action => 'process_alive', :pid => 123).and_return(true)
92
+ @con.process_alive(123).should == true
93
+ end
94
+
95
+ it "transmits kill_process" do
96
+ @con.should_receive(:transmit).with(:action => 'kill_process', :pid => 123, :payload => YAML.dump(:wait => 10))
97
+ @con.kill_process(123, :wait => 10)
98
+ end
99
+
100
+ it "transmits bash" do
101
+ @con.should_receive(:transmit).with(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'bg', :reset_environment => false).and_return('output')
102
+ @con.bash('cmd', 'user', 'bg', false).should == 'output'
103
+ end
104
+
105
+ it "an http result code of 401 raises NotAuthorized" do
106
+ lambda { @con.process_result("401", "") }.should raise_error(Rush::NotAuthorized)
107
+ end
108
+
109
+ it "an http result code of 400 raises the exception passed in the result body" do
110
+ @con.stub(:parse_exception).and_return(Rush::DoesNotExist, "message")
111
+ lambda { @con.process_result("400", "") }.should raise_error(Rush::DoesNotExist)
112
+ end
113
+
114
+ it "an http result code of 501 (or anything other than the other defined codes) raises FailedTransmit" do
115
+ lambda { @con.process_result("501", "") }.should raise_error(Rush::FailedTransmit)
116
+ end
117
+
118
+ it "parse_exception takes the class from the first line and the message from the second" do
119
+ @con.parse_exception("Rush::DoesNotExist\nthe message\n").should == [ Rush::DoesNotExist, "the message" ]
120
+ end
121
+
122
+ it "parse_exception rejects unrecognized exceptions" do
123
+ lambda { @con.parse_exception("NotARushException\n") }.should raise_error
124
+ end
125
+
126
+ it "passes through ensure_tunnel" do
127
+ @con.tunnel.should_receive(:ensure_tunnel)
128
+ @con.ensure_tunnel
129
+ end
130
+
131
+ it "is alive if the box is responding to commands" do
132
+ @con.should_receive(:index).and_return(:dummy)
133
+ @con.should be_alive
134
+ end
135
+
136
+ it "not alive if an attempted command throws an exception" do
137
+ @con.should_receive(:index).and_raise(RuntimeError)
138
+ @con.should_not be_alive
139
+ end
140
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'base'
2
+
3
+ describe Rush do
4
+ it "fetches a local file path" do
5
+ Rush['/etc/hosts'].full_path.should == '/etc/hosts'
6
+ end
7
+
8
+ it "fetches the dir of __FILE__" do
9
+ Rush.dir(__FILE__).name.should == 'spec'
10
+ end
11
+
12
+ it "fetches the launch dir (aka current working directory or pwd)" do
13
+ Dir.stub(:pwd).and_return('/tmp')
14
+ Rush.launch_dir.should == Rush::Box.new['/tmp/']
15
+ end
16
+
17
+ it "runs a bash command" do
18
+ Rush.bash('echo hi').should == "hi\n"
19
+ end
20
+
21
+ it "gets the list of local processes" do
22
+ Rush.processes.should be_kind_of(Rush::ProcessSet)
23
+ end
24
+
25
+ it "gets my process" do
26
+ Rush.my_process.pid.should == Process.pid
27
+ end
28
+ end
@@ -0,0 +1,44 @@
1
+ require_relative 'base'
2
+
3
+ describe Rush::SearchResults do
4
+ before do
5
+ @results = Rush::SearchResults.new(/pat/)
6
+ @file = Rush::File.new("file")
7
+ end
8
+
9
+ it "returns its list of entries" do
10
+ @results.add(@file, %w(a))
11
+ expect(@results.entries).to eq [ @file ]
12
+ end
13
+
14
+ it "only returns each entry once no matter how many line matches it has" do
15
+ @results.add(@file, %w(a b))
16
+ expect(@results.entries).to eq [ @file ]
17
+ end
18
+
19
+ it "returns its list of matched lines" do
20
+ @results.add(@file, %w(a b))
21
+ expect(@results.lines).to eq %w(a b)
22
+ end
23
+
24
+ it "returns all lines for each entry in a flattened form" do
25
+ file2 = Rush::File.new("another file")
26
+ @results.add(@file, %w(a b))
27
+ @results.add(file2, %w(c d))
28
+ expect(@results.lines).to eq %w(a b c d)
29
+ end
30
+
31
+ it "returns a hash of entries_with_lines" do
32
+ @results.add(@file, %w(a))
33
+ expect(@results.entries_with_lines).to eq({ @file => %w(a) })
34
+ end
35
+
36
+ it "mixes in Commands to operate like a dir or entry array" do
37
+ expect(@results.methods.include?(:search)).to eq(true)
38
+ end
39
+
40
+ it "mixes in Enumerable to behave like an array" do
41
+ @results.add(@file, %w(a))
42
+ expect(@results.map { |e| e }).to eq [ @file ]
43
+ end
44
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'base'
2
+ require_relative '../lib/rush/shell'
3
+
4
+ describe Rush::Shell do
5
+ before do
6
+ @shell = Rush::Shell.new
7
+ end
8
+
9
+ it 'Complete constants' do
10
+ expect(@shell.complete('Obj')).to eq(["Object", "ObjectSpace"])
11
+ expect(@shell.complete('Rush::Sh')).to eq(["Rush::Shell"])
12
+ end
13
+
14
+ it 'Complete executables' do
15
+ expect(@shell.complete('rub')).to include 'ruby'
16
+ end
17
+
18
+ it 'Complete global method names' do
19
+ eval('def wakawaka; p "hi"; end', @shell.pure_binding)
20
+ expect(@shell.complete('waka')).to eq ["wakawaka"]
21
+ end
22
+
23
+ it 'Complete method names' do
24
+ expect(@shell.complete('Rush.meth')).
25
+ to eq(["Rush.method_part", "Rush.method_defined?", "Rush.methods", "Rush.method"])
26
+ expect(@shell.complete('Rush.methods.inc')).to eq ["Rush.methods.include?"]
27
+ end
28
+
29
+ it 'Complete paths' do
30
+ expect(@shell.complete('root["bin/ba')).to eq ["root[\"bin/bash"]
31
+ expect(@shell.complete('root[\'bin/ba')).to eq ["root['bin/bash"]
32
+ expect(@shell.complete('root/"bin/ba')).to eq ["root/\"bin/bash"]
33
+ expect(@shell.complete('root/\'bin/ba')).to eq ["root/'bin/bash"]
34
+ end
35
+ end
@@ -0,0 +1,122 @@
1
+ require_relative 'base'
2
+
3
+ describe Rush::SshTunnel do
4
+ before do
5
+ @tunnel = Rush::SshTunnel.new('spec.example.com')
6
+ @tunnel.stub(:config).and_return(mock_config_start)
7
+ @tunnel.stub(:display)
8
+ end
9
+
10
+ after do
11
+ mock_config_cleanup
12
+ end
13
+
14
+ it "ensure_tunnel sets everything up for the tunnel when one does not already exist" do
15
+ @tunnel.should_receive(:push_credentials)
16
+ @tunnel.should_receive(:launch_rushd)
17
+ @tunnel.should_receive(:establish_tunnel)
18
+ @tunnel.ensure_tunnel
19
+ end
20
+
21
+ it "ensure_tunnel uses the existing port as long as the tunnel is still alive" do
22
+ @tunnel.should_receive(:tunnel_alive?).and_return(true)
23
+ @tunnel.instance_eval("@port = 2345")
24
+ @tunnel.ensure_tunnel
25
+ @tunnel.port.should == 2345
26
+ end
27
+
28
+ it "existing tunnel is used when it is specified in the tunnels file" do
29
+ @tunnel.config.tunnels_file.write "spec.example.com:4567\n"
30
+ @tunnel.should_receive(:tunnel_alive?).and_return(true)
31
+ @tunnel.should_not_receive(:setup_everything)
32
+ @tunnel.ensure_tunnel
33
+ @tunnel.port.should == 4567
34
+ end
35
+
36
+ it "tunnel host is always local" do
37
+ @tunnel.host.should == 'localhost'
38
+ end
39
+
40
+ it "picks the first port number when there are no tunnels yet" do
41
+ @tunnel.next_available_port.should == 7771
42
+ end
43
+
44
+ it "picks the next port number when there is already a tunnel" do
45
+ @tunnel.config.tunnels_file.write("#{@tunnel.host}:7771")
46
+ @tunnel.next_available_port.should == 7772
47
+ end
48
+
49
+ it "establishes a tunnel and saves it to ~/.rush/tunnels" do
50
+ @tunnel.should_receive(:make_ssh_tunnel)
51
+ @tunnel.should_receive(:port).exactly(0).times
52
+ @tunnel.establish_tunnel
53
+ @tunnel.config.tunnels_file.contents.should == "spec.example.com:7771\n"
54
+ end
55
+
56
+ it "converts instance vars to options hash for ssh_tunnel_command" do
57
+ @tunnel.instance_eval("@port = 1234")
58
+ @tunnel.tunnel_options.should == {
59
+ :local_port => 1234,
60
+ :remote_port => 7770,
61
+ :ssh_host => 'spec.example.com'
62
+ }
63
+ end
64
+
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
78
+ @tunnel.should_receive(:tunnel_options).at_least(:once).and_return(
79
+ :local_port => 123,
80
+ :remote_port => 456,
81
+ :ssh_host => 'example.com'
82
+ )
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"'
90
+ end
91
+
92
+ it "ssh_tunnel_command request that the port be set" do
93
+ @tunnel.should_receive(:tunnel_options).at_least(:once).and_return(:local_port => nil)
94
+ lambda { @tunnel.ssh_tunnel_command }.should raise_error(Rush::SshTunnel::NoPortSelectedYet)
95
+ end
96
+
97
+
98
+ it "push_credentials uses ssh to append to remote host's passwords file" do
99
+ @tunnel.should_receive(:ssh_append_to_credentials).and_return(true)
100
+ @tunnel.push_credentials
101
+ end
102
+
103
+ it "launches rushd on the remote host via ssh" do
104
+ @tunnel.should_receive(:ssh) do |cmd|
105
+ cmd.should match(/rushd/)
106
+ end
107
+ @tunnel.launch_rushd
108
+ end
109
+
110
+ it "tunnel_alive? checks whether a tunnel is still up" do
111
+ @tunnel.should_receive(:tunnel_count_command).and_return("echo 1")
112
+ @tunnel.tunnel_alive?.should be_true
113
+ end
114
+
115
+ it "tunnel_count_command greps ps to find the ssh tunnel" do
116
+ @tunnel.should_receive(:ssh_tunnel_command_without_stall).and_return('ssh command')
117
+ command = @tunnel.tunnel_count_command
118
+ command.should match(/ps/)
119
+ command.should match(/grep/)
120
+ command.should match(/ssh command/)
121
+ end
122
+ end