necktie 0.1.1 → 0.2.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 (57) hide show
  1. data/bin/necktie +1 -1
  2. data/lib/necktie/rush.rb +11 -0
  3. data/lib/necktie/services.rb +31 -11
  4. data/lib/necktie.rb +10 -6
  5. data/necktie.gemspec +2 -2
  6. data/rush/README.rdoc +87 -0
  7. data/rush/Rakefile +60 -0
  8. data/rush/VERSION +1 -0
  9. data/rush/bin/rush +13 -0
  10. data/rush/bin/rushd +7 -0
  11. data/rush/lib/rush/access.rb +130 -0
  12. data/rush/lib/rush/array_ext.rb +19 -0
  13. data/rush/lib/rush/box.rb +112 -0
  14. data/rush/lib/rush/commands.rb +55 -0
  15. data/rush/lib/rush/config.rb +154 -0
  16. data/rush/lib/rush/dir.rb +160 -0
  17. data/rush/lib/rush/embeddable_shell.rb +26 -0
  18. data/rush/lib/rush/entry.rb +185 -0
  19. data/rush/lib/rush/exceptions.rb +31 -0
  20. data/rush/lib/rush/file.rb +85 -0
  21. data/rush/lib/rush/find_by.rb +39 -0
  22. data/rush/lib/rush/fixnum_ext.rb +18 -0
  23. data/rush/lib/rush/head_tail.rb +11 -0
  24. data/rush/lib/rush/local.rb +402 -0
  25. data/rush/lib/rush/process.rb +59 -0
  26. data/rush/lib/rush/process_set.rb +62 -0
  27. data/rush/lib/rush/remote.rb +156 -0
  28. data/rush/lib/rush/search_results.rb +58 -0
  29. data/rush/lib/rush/server.rb +117 -0
  30. data/rush/lib/rush/shell.rb +187 -0
  31. data/rush/lib/rush/ssh_tunnel.rb +122 -0
  32. data/rush/lib/rush/string_ext.rb +3 -0
  33. data/rush/lib/rush.rb +87 -0
  34. data/rush/rush.gemspec +113 -0
  35. data/rush/spec/access_spec.rb +134 -0
  36. data/rush/spec/array_ext_spec.rb +15 -0
  37. data/rush/spec/base.rb +24 -0
  38. data/rush/spec/box_spec.rb +64 -0
  39. data/rush/spec/commands_spec.rb +47 -0
  40. data/rush/spec/config_spec.rb +108 -0
  41. data/rush/spec/dir_spec.rb +164 -0
  42. data/rush/spec/embeddable_shell_spec.rb +17 -0
  43. data/rush/spec/entry_spec.rb +133 -0
  44. data/rush/spec/file_spec.rb +83 -0
  45. data/rush/spec/find_by_spec.rb +58 -0
  46. data/rush/spec/fixnum_ext_spec.rb +19 -0
  47. data/rush/spec/local_spec.rb +352 -0
  48. data/rush/spec/process_set_spec.rb +50 -0
  49. data/rush/spec/process_spec.rb +73 -0
  50. data/rush/spec/remote_spec.rb +140 -0
  51. data/rush/spec/rush_spec.rb +28 -0
  52. data/rush/spec/search_results_spec.rb +44 -0
  53. data/rush/spec/shell_spec.rb +23 -0
  54. data/rush/spec/ssh_tunnel_spec.rb +122 -0
  55. data/rush/spec/string_ext_spec.rb +23 -0
  56. metadata +53 -3
  57. data/lib/necktie/files.rb +0 -14
@@ -0,0 +1,133 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe Rush::Entry do
4
+ before do
5
+ @sandbox_dir = "/tmp/rush_spec.#{Process.pid}/"
6
+ system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}"
7
+
8
+ @filename = "#{@sandbox_dir}/test_file"
9
+ system "touch #{@filename}"
10
+
11
+ @entry = Rush::Entry.new(@filename)
12
+ end
13
+
14
+ after do
15
+ system "rm -rf #{@sandbox_dir}"
16
+ end
17
+
18
+ it "knows its name" do
19
+ @entry.name.should == File.basename(@filename)
20
+ end
21
+
22
+ it "knows its parent dir" do
23
+ @entry.parent.should be_kind_of(Rush::Dir)
24
+ @entry.parent.name.should == File.basename(@sandbox_dir)
25
+ @entry.parent.full_path.should == @sandbox_dir
26
+ end
27
+
28
+ it "cleans its pathname" do
29
+ Rush::Entry.new('/a//b//c').full_path.should == '/a/b/c'
30
+ Rush::Entry.new('/1/2/../3').full_path.should == '/1/3'
31
+ end
32
+
33
+ it "knows its changed_at time" do
34
+ @entry.changed_at.should == File.stat(@filename).ctime
35
+ end
36
+
37
+ it "knows its last_modified time" do
38
+ @entry.last_modified.should == File.stat(@filename).mtime
39
+ end
40
+
41
+ it "knows its last_accessed time" do
42
+ @entry.last_accessed.should == File.stat(@filename).atime
43
+ end
44
+
45
+ it "considers itself equal to other instances with the same full path" do
46
+ Rush::Entry.new('/not/the/same').should_not == @entry
47
+ Rush::Entry.new(@entry.full_path).should == @entry
48
+ end
49
+
50
+ it "can rename itself" do
51
+ new_file = "test2"
52
+
53
+ @entry.rename(new_file)
54
+
55
+ File.exists?(@filename).should be_false
56
+ File.exists?("#{@sandbox_dir}/#{new_file}").should be_true
57
+ end
58
+
59
+ it "rename returns the renamed file" do
60
+ @entry.rename('file2').should == @entry.parent['file2']
61
+ end
62
+
63
+ it "can't rename itself if another file already exists with that name" do
64
+ new_file = "test3"
65
+ system "touch #{@sandbox_dir}/#{new_file}"
66
+
67
+ lambda { @entry.rename(new_file) }.should raise_error(Rush::NameAlreadyExists, /#{new_file}/)
68
+ end
69
+
70
+ it "can't rename itself to something with a slash in it" do
71
+ lambda { @entry.rename('has/slash') }.should raise_error(Rush::NameCannotContainSlash, /slash/)
72
+ end
73
+
74
+ it "can duplicate itself within the directory" do
75
+ @entry.duplicate('newfile').should == Rush::File.new("#{@sandbox_dir}/newfile")
76
+ end
77
+
78
+ it "can move itself to another dir" do
79
+ newdir = "#{@sandbox_dir}/newdir"
80
+ system "mkdir -p #{newdir}"
81
+
82
+ dst = Rush::Dir.new(newdir)
83
+ @entry.move_to(dst)
84
+
85
+ File.exists?(@filename).should be_false
86
+ File.exists?("#{newdir}/#{@entry.name}").should be_true
87
+ end
88
+
89
+ it "can copy itself to another directory" do
90
+ newdir = "#{@sandbox_dir}/newdir"
91
+ system "mkdir -p #{newdir}"
92
+
93
+ dst = Rush::Dir.new(newdir)
94
+ @copied_dir = @entry.copy_to(dst)
95
+
96
+ File.exists?(@filename).should be_true
97
+ File.exists?("#{newdir}/#{@entry.name}").should be_true
98
+
99
+ @copied_dir.full_path.should == "#{@sandbox_dir}newdir/#{@entry.name}"
100
+ end
101
+
102
+ it "considers dotfiles to be hidden" do
103
+ Rush::Entry.new("#{@sandbox_dir}/show").should_not be_hidden
104
+ Rush::Entry.new("#{@sandbox_dir}/.dont_show").should be_hidden
105
+ end
106
+
107
+ it "is considered equal to entries with the same full path and on the same box" do
108
+ same = Rush::Entry.new(@entry.full_path, @entry.box)
109
+ @entry.should == same
110
+ end
111
+
112
+ it "is considered not equal to entries with the same full path on a different box" do
113
+ same = Rush::Entry.new(@entry.full_path, Rush::Box.new('dummy'))
114
+ @entry.should_not == same
115
+ end
116
+
117
+ it "can mimic another entry" do
118
+ copy = Rush::Entry.new('abc', :dummy)
119
+ copy.mimic(@entry)
120
+ copy.path.should == @entry.path
121
+ end
122
+
123
+ it "can update the read access permission" do
124
+ system "chmod 666 #{@filename}"
125
+ @entry.access = { :user_can => :read }
126
+ `ls -l #{@filename}`.should match(/^-r--------/)
127
+ end
128
+
129
+ it "reads the file permissions in the access hash" do
130
+ system "chmod 640 #{@filename}"
131
+ @entry.access.should == { :user_can_read => true, :user_can_write => true, :group_can_read => true }
132
+ end
133
+ end
@@ -0,0 +1,83 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe Rush::File do
4
+ before do
5
+ @sandbox_dir = "/tmp/rush_spec.#{Process.pid}"
6
+ system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}"
7
+
8
+ @filename = "#{@sandbox_dir}/test_file"
9
+ @contents = "1234"
10
+ system "echo #{@contents} > #{@filename}"
11
+ @contents += "\n"
12
+
13
+ @file = Rush::File.new(@filename)
14
+ end
15
+
16
+ after do
17
+ system "rm -rf #{@sandbox_dir}"
18
+ end
19
+
20
+ it "is a child of Rush::Entry" do
21
+ @file.should be_kind_of(Rush::Entry)
22
+ end
23
+
24
+ it "is not a dir" do
25
+ @file.should_not be_dir
26
+ end
27
+
28
+ it "can create itself as a blank file, and return itself" do
29
+ create_me = Rush::File.new("#{@sandbox_dir}/create_me")
30
+ create_me.create.should == create_me
31
+ File.exists?("#{@sandbox_dir}/create_me").should == true
32
+ end
33
+
34
+ it "knows its size in bytes" do
35
+ @file.size.should == @contents.length
36
+ end
37
+
38
+ it "can read its contents" do
39
+ @file.contents.should == @contents
40
+ end
41
+
42
+ it "read is an alias for contents" do
43
+ @file.read.should == @contents
44
+ end
45
+
46
+ it "can write new contents" do
47
+ @file.write('write test')
48
+ @file.contents.should == 'write test'
49
+ end
50
+
51
+ it "can count the number of lines it contains" do
52
+ @file.write("1\n2\n3\n")
53
+ @file.line_count.should == 3
54
+ end
55
+
56
+ it "searches its contents for matching lines" do
57
+ @file.write("a\n1\nb\n2\n")
58
+ @file.search(/\d/).should == %w(1 2)
59
+ end
60
+
61
+ it "search returns nil if no lines match" do
62
+ @file.write("a\nb\nc\n")
63
+ @file.search(/\d/).should be_nil
64
+ end
65
+
66
+ it "find-in-file replace" do
67
+ @file.replace_contents!(/\d/, 'x')
68
+ @file.contents.should == "xxxx\n"
69
+ end
70
+
71
+ it "can destroy itself" do
72
+ @file.destroy
73
+ ::File.exists?(@filename).should be_false
74
+ end
75
+
76
+ it "can fetch contents or blank if doesn't exist" do
77
+ Rush::File.new('/does/not/exist').contents_or_blank.should == ""
78
+ end
79
+
80
+ it "can fetch lines, or empty if doesn't exist" do
81
+ Rush::File.new('/does/not/exist').lines_or_empty.should == []
82
+ end
83
+ end
@@ -0,0 +1,58 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe Rush::FindBy do
4
+ before do
5
+ class Foo
6
+ attr_accessor :bar
7
+
8
+ def initialize(bar)
9
+ @bar = bar
10
+ end end
11
+
12
+ @one = Foo.new('one')
13
+ @two = Foo.new('two')
14
+ @three = Foo.new('three')
15
+
16
+ @list = [ @one, @two, @three ]
17
+ end
18
+
19
+ it "compare_or_match exact match success" do
20
+ @list.compare_or_match('1', '1').should == true
21
+ end
22
+
23
+ it "compare_or_match exact match failure" do
24
+ @list.compare_or_match('1', '2').should == false
25
+ end
26
+
27
+ it "compare_or_match regexp match success" do
28
+ @list.compare_or_match('123', /2/).should == true
29
+ end
30
+
31
+ it "compare_or_match regexp match failure" do
32
+ @list.compare_or_match('123', /x/).should == false
33
+ end
34
+
35
+ it "find_by_ extact match" do
36
+ @list.find_by_bar('two').should == @two
37
+ end
38
+
39
+ it "find_by_ regexp match" do
40
+ @list.find_by_bar(/.hree/).should == @three
41
+ end
42
+
43
+ it "find_all_by_ exact match" do
44
+ @list.find_all_by_bar('one').should == [ @one ]
45
+ end
46
+
47
+ it "find_all_by_ regexp match" do
48
+ @list.find_all_by_bar(/^...$/).should == [ @one, @two ]
49
+ end
50
+
51
+ it "find_by_ with field not recognized by objects raises no errors" do
52
+ @list.find_by_nothing('x')
53
+ end
54
+
55
+ it "raises NoMethodError for things other than find_by" do
56
+ lambda { @list.does_not_exist }.should raise_error(NoMethodError)
57
+ end
58
+ end
@@ -0,0 +1,19 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe Fixnum do
4
+ before do
5
+ @num = 2
6
+ end
7
+
8
+ it "counts kb" do
9
+ @num.kb.should == 2*1024
10
+ end
11
+
12
+ it "counts mb" do
13
+ @num.mb.should == 2*1024*1024
14
+ end
15
+
16
+ it "counts gb" do
17
+ @num.gb.should == 2*1024*1024*1024
18
+ end
19
+ end
@@ -0,0 +1,352 @@
1
+ require File.dirname(__FILE__) + '/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::Local.new
9
+ end
10
+
11
+ after do
12
+ system "rm -rf #{@sandbox_dir}"
13
+ end
14
+
15
+ it "receive -> write_file(file, contents)" do
16
+ @con.should_receive(:write_file).with('file', 'contents')
17
+ @con.receive(:action => 'write_file', :full_path => 'file', :payload => 'contents')
18
+ end
19
+
20
+ it "receive -> append_to_file(file, contents)" do
21
+ @con.should_receive(:append_to_file).with('file', 'contents')
22
+ @con.receive(:action => 'append_to_file', :full_path => 'file', :payload => 'contents')
23
+ end
24
+
25
+ it "receive -> file_contents(file)" do
26
+ @con.should_receive(:file_contents).with('file').and_return('the contents')
27
+ @con.receive(:action => 'file_contents', :full_path => 'file').should == 'the contents'
28
+ end
29
+
30
+ it "receive -> destroy(file or dir)" do
31
+ @con.should_receive(:destroy).with('file')
32
+ @con.receive(:action => 'destroy', :full_path => 'file')
33
+ end
34
+
35
+ it "receive -> purge(dir)" do
36
+ @con.should_receive(:purge).with('dir')
37
+ @con.receive(:action => 'purge', :full_path => 'dir')
38
+ end
39
+
40
+ it "receive -> create_dir(path)" do
41
+ @con.should_receive(:create_dir).with('dir')
42
+ @con.receive(:action => 'create_dir', :full_path => 'dir')
43
+ end
44
+
45
+ it "receive -> rename(path, name, new_name)" do
46
+ @con.should_receive(:rename).with('path', 'name', 'new_name')
47
+ @con.receive(:action => 'rename', :path => 'path', :name => 'name', :new_name => 'new_name')
48
+ end
49
+
50
+ it "receive -> copy(src, dst)" do
51
+ @con.should_receive(:copy).with('src', 'dst')
52
+ @con.receive(:action => 'copy', :src => 'src', :dst => 'dst')
53
+ end
54
+
55
+ it "receive -> read_archive(full_path)" do
56
+ @con.should_receive(:read_archive).with('full_path').and_return('archive data')
57
+ @con.receive(:action => 'read_archive', :full_path => 'full_path').should == 'archive data'
58
+ end
59
+
60
+ it "receive -> write_archive(archive, dir)" do
61
+ @con.should_receive(:write_archive).with('archive', 'dir')
62
+ @con.receive(:action => 'write_archive', :dir => 'dir', :payload => 'archive')
63
+ end
64
+
65
+ it "receive -> index(base_path, glob)" do
66
+ @con.should_receive(:index).with('base_path', '*').and_return(%w(1 2))
67
+ @con.receive(:action => 'index', :base_path => 'base_path', :glob => '*').should == "1\n2\n"
68
+ end
69
+
70
+ it "receive -> stat(full_path)" do
71
+ @con.should_receive(:stat).with('full_path').and_return(1 => 2)
72
+ @con.receive(:action => 'stat', :full_path => 'full_path').should == YAML.dump(1 => 2)
73
+ end
74
+
75
+ it "receive -> set_access(full_path, user, group, permissions)" do
76
+ access = mock("access")
77
+ Rush::Access.should_receive(:from_hash).with(:action => 'set_access', :full_path => 'full_path', :user => 'joe').and_return(access)
78
+
79
+ @con.should_receive(:set_access).with('full_path', access)
80
+ @con.receive(:action => 'set_access', :full_path => 'full_path', :user => 'joe')
81
+ end
82
+
83
+ it "receive -> size(full_path)" do
84
+ @con.should_receive(:size).with('full_path').and_return("1024")
85
+ @con.receive(:action => 'size', :full_path => 'full_path').should == "1024"
86
+ end
87
+
88
+ it "receive -> processes" do
89
+ @con.should_receive(:processes).with().and_return([ { :pid => 1 } ])
90
+ @con.receive(:action => 'processes').should == YAML.dump([ { :pid => 1 } ])
91
+ end
92
+
93
+ it "receive -> process_alive" do
94
+ @con.should_receive(:process_alive).with(123).and_return(true)
95
+ @con.receive(:action => 'process_alive', :pid => 123).should == '1'
96
+ end
97
+
98
+ it "receive -> kill_process" do
99
+ @con.should_receive(:kill_process).with(123, :wait => 10).and_return(true)
100
+ @con.receive(:action => 'kill_process', :pid => '123', :payload => YAML.dump(:wait => 10))
101
+ end
102
+
103
+ it "receive -> bash (foreground)" do
104
+ @con.should_receive(:bash).with('cmd', 'user', false).and_return('output')
105
+ @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'false').should == 'output'
106
+ end
107
+
108
+ it "receive -> bash (background)" do
109
+ @con.should_receive(:bash).with('cmd', 'user', true).and_return('output')
110
+ @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'true').should == 'output'
111
+ end
112
+
113
+ it "receive -> unknown action exception" do
114
+ lambda { @con.receive(:action => 'does_not_exist') }.should raise_error(Rush::Connection::Local::UnknownAction)
115
+ end
116
+
117
+ it "write_file writes contents to a file" do
118
+ fname = "#{@sandbox_dir}/a_file"
119
+ data = "some data"
120
+ @con.write_file(fname, data)
121
+ File.read(fname).should == data
122
+ end
123
+
124
+ it "append_to_file appends contents to a file" do
125
+ fname = "#{@sandbox_dir}/a_file"
126
+ system "echo line1 > #{fname}"
127
+ @con.append_to_file(fname, 'line2')
128
+ File.read(fname).should == "line1\nline2"
129
+ end
130
+
131
+ it "file_contents reads a file's contents" do
132
+ fname = "#{@sandbox_dir}/a_file"
133
+ system "echo stuff > #{fname}"
134
+ @con.file_contents(fname).should == "stuff\n"
135
+ end
136
+
137
+ it "file_contents raises DoesNotExist if the file does not exist" do
138
+ fname = "#{@sandbox_dir}/does_not_exist"
139
+ lambda { @con.file_contents(fname) }.should raise_error(Rush::DoesNotExist, fname)
140
+ end
141
+
142
+ it "destroy to destroy a file or dir" do
143
+ fname = "#{@sandbox_dir}/delete_me"
144
+ system "touch #{fname}"
145
+ @con.destroy(fname)
146
+ File.exists?(fname).should be_false
147
+ end
148
+
149
+ it "purge to purge a dir" do
150
+ system "cd #{@sandbox_dir}; touch {1,2}; mkdir 3; touch 3/4"
151
+ @con.purge(@sandbox_dir)
152
+ File.exists?(@sandbox_dir).should be_true
153
+ Dir.glob("#{@sandbox_dir}/*").should == []
154
+ end
155
+
156
+ it "purge kills hidden (dotfile) entries too" do
157
+ system "cd #{@sandbox_dir}; touch .killme"
158
+ @con.purge(@sandbox_dir)
159
+ File.exists?(@sandbox_dir).should be_true
160
+ `cd #{@sandbox_dir}; ls -lA | grep -v total | wc -l`.to_i.should == 0
161
+ end
162
+
163
+ it "create_dir creates a directory" do
164
+ fname = "#{@sandbox_dir}/a/b/c/"
165
+ @con.create_dir(fname)
166
+ File.directory?(fname).should be_true
167
+ end
168
+
169
+ it "rename to rename entries within a dir" do
170
+ system "touch #{@sandbox_dir}/a"
171
+ @con.rename(@sandbox_dir, 'a', 'b')
172
+ File.exists?("#{@sandbox_dir}/a").should be_false
173
+ File.exists?("#{@sandbox_dir}/b").should be_true
174
+ end
175
+
176
+ it "copy to copy an entry to another dir on the same box" do
177
+ system "mkdir #{@sandbox_dir}/subdir"
178
+ system "touch #{@sandbox_dir}/a"
179
+ @con.copy("#{@sandbox_dir}/a", "#{@sandbox_dir}/subdir")
180
+ File.exists?("#{@sandbox_dir}/a").should be_true
181
+ File.exists?("#{@sandbox_dir}/subdir/a").should be_true
182
+ end
183
+
184
+ it "copy raises DoesNotExist with source path if it doesn't exist or otherwise can't be accessed" do
185
+ lambda { @con.copy('/does/not/exist', '/tmp') }.should raise_error(Rush::DoesNotExist, '/does/not/exist')
186
+ end
187
+
188
+ it "copy raises DoesNotExist with destination path if it can't access the destination" do
189
+ lambda { @con.copy('/tmp', '/does/not/exist') }.should raise_error(Rush::DoesNotExist, '/does/not')
190
+ end
191
+
192
+ it "read_archive to pull an archive of a dir into a byte stream" do
193
+ system "touch #{@sandbox_dir}/a"
194
+ @con.read_archive(@sandbox_dir).size.should > 50
195
+ end
196
+
197
+ it "read_archive works for paths with spaces" do
198
+ system "mkdir -p #{@sandbox_dir}/with\\ space; touch #{@sandbox_dir}/with\\ space/a"
199
+ @con.read_archive("#{@sandbox_dir}/with space").size.should > 50
200
+ end
201
+
202
+ it "write_archive to turn a byte stream into a dir" do
203
+ system "cd #{@sandbox_dir}; mkdir -p a; touch a/b; tar cf xfer.tar a; mkdir dst"
204
+ archive = File.read("#{@sandbox_dir}/xfer.tar")
205
+ @con.write_archive(archive, "#{@sandbox_dir}/dst")
206
+ File.directory?("#{@sandbox_dir}/dst/a").should be_true
207
+ File.exists?("#{@sandbox_dir}/dst/a/b").should be_true
208
+ end
209
+
210
+ it "write_archive works for paths with spaces" do
211
+ system "cd #{@sandbox_dir}; mkdir -p a; touch a/b; tar cf xfer.tar a; mkdir with\\ space"
212
+ archive = File.read("#{@sandbox_dir}/xfer.tar")
213
+ @con.write_archive(archive, "#{@sandbox_dir}/with space")
214
+ File.directory?("#{@sandbox_dir}/with space/a").should be_true
215
+ File.exists?("#{@sandbox_dir}/with space/a/b").should be_true
216
+ end
217
+
218
+ it "index fetches list of all files and dirs in a dir when pattern is empty" do
219
+ system "cd #{@sandbox_dir}; mkdir dir; touch file"
220
+ @con.index(@sandbox_dir, '').should == [ 'dir/', 'file' ]
221
+ end
222
+
223
+ it "index fetches only files with a certain extension with a flat pattern, *.rb" do
224
+ system "cd #{@sandbox_dir}; touch a.rb; touch b.txt"
225
+ @con.index(@sandbox_dir, '*.rb').should == [ 'a.rb' ]
226
+ end
227
+
228
+ it "index raises DoesNotExist when the base path is invalid" do
229
+ lambda { @con.index('/does/not/exist', '*') }.should raise_error(Rush::DoesNotExist, '/does/not/exist')
230
+ end
231
+
232
+ it "stat gives file stats like size and timestamps" do
233
+ @con.stat(@sandbox_dir).should have_key(:ctime)
234
+ @con.stat(@sandbox_dir).should have_key(:size)
235
+ end
236
+
237
+ it "stat fetches the octal permissions" do
238
+ @con.stat(@sandbox_dir)[:mode].should be_kind_of(Fixnum)
239
+ end
240
+
241
+ it "stat raises DoesNotExist if the entry does not exist" do
242
+ fname = "#{@sandbox_dir}/does_not_exist"
243
+ lambda { @con.stat(fname) }.should raise_error(Rush::DoesNotExist, fname)
244
+ end
245
+
246
+ it "set_access invokes the access object" do
247
+ access = mock("access")
248
+ access.should_receive(:apply).with('/some/path')
249
+ @con.set_access('/some/path', access)
250
+ end
251
+
252
+ if !RUBY_PLATFORM.match(/darwin/) # doesn't work on OS X 'cause du switches are different
253
+ it "size gives size of a directory and all its contents recursively" do
254
+ system "mkdir -p #{@sandbox_dir}/a/b/; echo 1234 > #{@sandbox_dir}/a/b/c"
255
+ @con.size(@sandbox_dir).should == (4096*3 + 5)
256
+ end
257
+ end
258
+
259
+ it "parses ps output on os x" do
260
+ @con.parse_ps("21712 501 21711 1236 0 /usr/bin/vi somefile.rb").should == {
261
+ :pid => "21712",
262
+ :uid => "501",
263
+ :parent_pid => 21711,
264
+ :mem => 1236,
265
+ :cpu => 0,
266
+ :command => '/usr/bin/vi',
267
+ :cmdline => '/usr/bin/vi somefile.rb',
268
+ }
269
+ end
270
+
271
+ it "gets the list of processes on os x via the ps command" do
272
+ @con.should_receive(:os_x_raw_ps).and_return <<EOPS
273
+ PID UID PPID RSS CPU COMMAND
274
+ 1 0 1 1111 0 cmd1 args
275
+ 2 501 1 222 1 cmd2
276
+ EOPS
277
+ @con.os_x_processes.should == [
278
+ { :pid => "1", :uid => "0", :parent_pid => 1, :mem => 1111, :cpu => 0, :command => "cmd1", :cmdline => "cmd1 args" },
279
+ { :pid => "2", :uid => "501", :parent_pid => 1, :mem => 222, :cpu => 1, :command => "cmd2", :cmdline => "cmd2" },
280
+ ]
281
+ end
282
+
283
+ it "the current process should be alive" do
284
+ @con.process_alive(Process.pid).should be_true
285
+ end
286
+
287
+ it "a made-up process should not be alive" do
288
+ @con.process_alive(99999).should be_false
289
+ end
290
+
291
+ it "kills a process by pid sending a TERM" do
292
+ @con.stub!(:process_alive).and_return(false)
293
+ ::Process.should_receive(:kill).with('TERM', 123).once
294
+ @con.kill_process(123)
295
+ end
296
+
297
+ it "kills a process by pid sending a KILL signal if TERM doesn't work" do
298
+ @con.stub!(:process_alive).and_return(true)
299
+ ::Process.should_receive(:kill).with('TERM', 123).at_least(:twice)
300
+ ::Process.should_receive(:kill).with('KILL', 123)
301
+ @con.kill_process(123)
302
+ end
303
+
304
+ it "kills a process by pid without sending TERM if :wait is zero" do
305
+ ::Process.should_not_receive(:kill).with('TERM', 123)
306
+ ::Process.should_receive(:kill).with('KILL', 123)
307
+ @con.kill_process(123, :wait => 0)
308
+ end
309
+
310
+ it "does not raise an error if the process is already dead" do
311
+ ::Process.should_receive(:kill).and_raise(Errno::ESRCH)
312
+ lambda { @con.kill_process(123) }.should_not raise_error
313
+ end
314
+
315
+ it "executes a bash command, returning stdout when successful" do
316
+ @con.bash("echo test").should == "test\n"
317
+ end
318
+
319
+ it "executes a bash command, raising and error (with stderr as the message) when return value is nonzero" do
320
+ lambda { @con.bash("no_such_bin") }.should raise_error(Rush::BashFailed, /command not found/)
321
+ end
322
+
323
+ it "executes a bash command as another user using sudo" do
324
+ @con.bash("echo test2", ENV['USER']).should == "test2\n"
325
+ end
326
+
327
+ it "executes a bash command in the background, returning the pid" do
328
+ @con.bash("true", nil, true).should > 0
329
+ end
330
+
331
+ it "ensure_tunnel to match with remote connection" do
332
+ @con.ensure_tunnel
333
+ end
334
+
335
+ it "always returns true on alive?" do
336
+ @con.should be_alive
337
+ end
338
+
339
+ it "resolves a unix uid to a user" do
340
+ @con.resolve_unix_uid_to_user(0).should == "root"
341
+ @con.resolve_unix_uid_to_user('0').should == "root"
342
+ end
343
+
344
+ it "returns nil if the unix uid does not exist" do
345
+ @con.resolve_unix_uid_to_user(9999).should be_nil
346
+ end
347
+
348
+ it "iterates through a process list and resolves the unix uid for each" do
349
+ list = [ { :uid => 0, :command => 'pureftpd' }, { :uid => 9999, :command => 'defunk' } ]
350
+ @con.resolve_unix_uids(list).should == [ { :uid => 0, :user => 'root', :command => 'pureftpd' }, { :uid => 9999, :command => 'defunk', :user => nil } ]
351
+ end
352
+ end
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe Rush::ProcessSet do
4
+ before do
5
+ @process = mock('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