File: admin_tools_spec.rb

package info (click to toggle)
ruby-passenger 3.0.13debian-1%2Bdeb7u2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 15,920 kB
  • sloc: cpp: 99,104; ruby: 18,098; ansic: 9,846; sh: 8,632; python: 141; makefile: 30
file content (362 lines) | stat: -rw-r--r-- 12,865 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
require 'fileutils'
require 'phusion_passenger/utils'
require 'phusion_passenger/admin_tools'
require 'phusion_passenger/admin_tools/server_instance'

module PhusionPassenger

describe AdminTools do
	include Utils
	
	before :each do
		Dir.mkdir("#{passenger_tmpdir}/master")
	end
end

describe AdminTools::ServerInstance do
	include Utils
	
	DIR_STRUCTURE_MAJOR_VERSION = AdminTools::ServerInstance::DIR_STRUCTURE_MAJOR_VERSION
	DIR_STRUCTURE_MINOR_VERSION = AdminTools::ServerInstance::DIR_STRUCTURE_MINOR_VERSION
	GENERATION_STRUCTURE_MAJOR_VERSION = AdminTools::ServerInstance::GENERATION_STRUCTURE_MAJOR_VERSION
	GENERATION_STRUCTURE_MINOR_VERSION = AdminTools::ServerInstance::GENERATION_STRUCTURE_MINOR_VERSION
	
	before :each do
		File.chmod(0700, passenger_tmpdir)
	end
	
	after :each do
		if @process1
			Process.kill('KILL', @process1.pid)
			@process1.close
		end
		if @process2
			Process.kill('KILL', @process2.pid)
			@process2.close
		end
		if @process3
			Process.kill('KILL', @process3.pid)
			@process3.close
		end
	end
	
	def spawn_process
		IO.popen("sleep 999")
	end
	
	def create_instance_dir(pid, major = DIR_STRUCTURE_MAJOR_VERSION, minor = DIR_STRUCTURE_MINOR_VERSION)
		dir = "#{passenger_tmpdir}/passenger.#{major}.#{minor}.#{pid}"
		Dir.mkdir(dir)
		return dir
	end
	
	def create_generation(dir, number = 0, major = GENERATION_STRUCTURE_MAJOR_VERSION, minor = GENERATION_STRUCTURE_MINOR_VERSION)
		dir = "#{dir}/generation-#{number}"
		Dir.mkdir(dir)
		File.write("#{dir}/structure_version.txt", "#{major}.#{minor}")
		return dir
	end
	
	describe ".list" do
		before :each do
			AdminTools.should_receive(:tmpdir).and_return(passenger_tmpdir)
			AdminTools::ServerInstance.stub!(:current_time).
				and_return(Time.now + AdminTools::ServerInstance::STALE_TIME_THRESHOLD + 1)
		end
		
		it "returns a list of ServerInstances representing the running Phusion Passenger instances" do
			@process1 = spawn_process
			@process2 = spawn_process
			processes = [@process1, @process2].sort { |a, b| a.pid <=> b.pid }
			
			dir1 = create_instance_dir(@process1.pid)
			create_generation(dir1)
			dir2 = create_instance_dir(@process2.pid)
			create_generation(dir2)
			
			instances = AdminTools::ServerInstance.list.sort { |a, b| a.pid <=> b.pid }
			instances.should have(2).items
			instances[0].pid.should == processes[0].pid
			instances[1].pid.should == processes[1].pid
		end
		
		it "doesn't list directories that don't look like Phusion Passenger server instance directories" do
			@process1 = spawn_process
			
			dir = create_instance_dir(@process1.pid)
			create_generation(dir)
			
			Dir.mkdir("#{passenger_tmpdir}/foo.123")
			create_generation("#{passenger_tmpdir}/foo.123")
			
			instances = AdminTools::ServerInstance.list
			instances.should have(1).item
			instances[0].pid.should == @process1.pid
		end
		
		it "doesn't list server instance directories that have a different major structure version" do
			@process1 = spawn_process
			@process2 = spawn_process
			
			dir1 = create_instance_dir(@process1.pid, 0)
			create_generation(dir1)
			dir2 = create_instance_dir(@process2.pid)
			create_generation(dir2)
			
			instances = AdminTools::ServerInstance.list
			instances.should have(1).item
			instances[0].pid.should == @process2.pid
		end
		
		it "doesn't list server instance directories that have the same major structure version but a larger minor structure version" do
			@process1 = spawn_process
			@process2 = spawn_process
			
			dir1 = create_instance_dir(@process1.pid, DIR_STRUCTURE_MAJOR_VERSION, DIR_STRUCTURE_MINOR_VERSION + 1)
			create_generation(dir1)
			dir2 = create_instance_dir(@process2.pid)
			create_generation(dir2)
			
			instances = AdminTools::ServerInstance.list
			instances.should have(1).item
			instances[0].pid.should == @process2.pid
		end
		
		it "doesn't list server instance directories with no generations" do
			@process1 = spawn_process
			create_instance_dir(@process1.pid)
			AdminTools::ServerInstance.list.should be_empty
		end
		
		it "doesn't list server instance directories for which the newest generation has a different major version" do
			@process1 = spawn_process
			@process2 = spawn_process
			dir1 = create_instance_dir(@process1.pid)
			create_generation(dir1, 0, GENERATION_STRUCTURE_MAJOR_VERSION)
			create_generation(dir1, 1, GENERATION_STRUCTURE_MAJOR_VERSION + 1)
			dir2 = create_instance_dir(@process2.pid)
			create_generation(dir2)
			
			instances = AdminTools::ServerInstance.list
			instances.should have(1).item
			instances[0].pid.should == @process2.pid
		end
		
		it "doesn't list server instance directories for which the newest generation has the same major version but a larger minor version" do
			@process1 = spawn_process
			@process2 = spawn_process
			dir1 = create_instance_dir(@process1.pid)
			create_generation(dir1, 0, GENERATION_STRUCTURE_MAJOR_VERSION)
			create_generation(dir1, 1, GENERATION_STRUCTURE_MAJOR_VERSION + 1, GENERATION_STRUCTURE_MINOR_VERSION + 1)
			dir2 = create_instance_dir(@process2.pid)
			create_generation(dir2)
			
			instances = AdminTools::ServerInstance.list
			instances.should have(1).item
			instances[0].pid.should == @process2.pid
		end
		
		it "cleans up server instance directories for which its PID doesn't exist" do
			@process1 = spawn_process
			process2 = spawn_process
			process2_pid = process2.pid
			process3 = spawn_process
			process3_pid = process3.pid
			
			dir1 = create_instance_dir(@process1.pid)
			create_generation(dir1)
			dir2 = create_instance_dir(process2_pid)
			create_generation(dir2)
			dir3 = create_instance_dir(process3_pid + 1)
			create_generation(dir3)
			File.write("#{dir3}/control_process.pid", process3_pid)
			
			Process.kill('KILL', process2_pid) rescue nil
			process2.close
			Process.kill('KILL', process3_pid) rescue nil
			process3.close
			
			AdminTools::ServerInstance.should_receive(:log_cleaning_action).twice
			instances = AdminTools::ServerInstance.list
			instances.should have(1).item
			instances[0].pid.should == @process1.pid
			File.exist?(dir2).should be_false
			File.exist?(dir3).should be_false
		end
		
		it "doesn't clean up server instance directories for which the major structure version is different" do
			process1 = spawn_process
			dir1 = create_instance_dir(process1.pid, DIR_STRUCTURE_MAJOR_VERSION + 1)
			create_generation(dir1)
			Process.kill('KILL', process1.pid) rescue nil
			process1.close
			
			AdminTools::ServerInstance.should_not_receive(:log_cleaning_action)
			instances = AdminTools::ServerInstance.list
			instances.should be_empty
			File.exist?(dir1).should be_true
		end
		
		it "doesn't clean up server instance directories for which the major structure version is the same but the minor structure version is larger" do
			process1 = spawn_process
			dir1 = create_instance_dir(process1.pid, DIR_STRUCTURE_MAJOR_VERSION, DIR_STRUCTURE_MINOR_VERSION + 1)
			create_generation(dir1)
			Process.kill('KILL', process1.pid) rescue nil
			process1.close
			
			AdminTools::ServerInstance.should_not_receive(:log_cleaning_action)
			instances = AdminTools::ServerInstance.list
			instances.should be_empty
			File.exist?(dir1).should be_true
		end
		
		it "doesn't clean up server instance directories for which the latest generation has a different major version" do
			process1 = spawn_process
			dir1 = create_instance_dir(process1.pid)
			create_generation(dir1, 0, GENERATION_STRUCTURE_MAJOR_VERSION + 1)
			Process.kill('KILL', process1.pid) rescue nil
			process1.close
			
			AdminTools::ServerInstance.should_not_receive(:log_cleaning_action)
			instances = AdminTools::ServerInstance.list
			instances.should be_empty
			File.exist?(dir1).should be_true
		end
		
		it "doesn't clean up server instance directories for which the latest generation has the same major version but a larger minor version" do
			process1 = spawn_process
			dir1 = create_instance_dir(process1.pid)
			create_generation(dir1, 0, GENERATION_STRUCTURE_MAJOR_VERSION, GENERATION_STRUCTURE_MINOR_VERSION + 1)
			Process.kill('KILL', process1.pid) rescue nil
			process1.close
			
			AdminTools::ServerInstance.should_not_receive(:log_cleaning_action)
			instances = AdminTools::ServerInstance.list
			instances.should be_empty
			File.exist?(dir1).should be_true
		end
		
		it "cleans up server instance directories that contain a corrupted control_process.pid" do
			@process1 = spawn_process
			@process2 = spawn_process
			
			dir1 = create_instance_dir(@process1.pid)
			create_generation(dir1)
			dir2 = create_instance_dir(@process2.pid)
			create_generation(dir2)
			File.write("#{dir2}/control_process.pid", "")
			
			AdminTools::ServerInstance.should_receive(:log_cleaning_action)
			instances = AdminTools::ServerInstance.list
			instances.should have(1).item
			instances[0].pid.should == @process1.pid
			File.exist?(dir2).should be_false
		end
		
		it "cleans up server instance directories for which the latest generation has a corrupted structure_version.txt" do
			@process1 = spawn_process
			@process2 = spawn_process
			@process3 = spawn_process
			
			dir1 = create_instance_dir(@process1.pid)
			create_generation(dir1)
			dir2 = create_instance_dir(@process2.pid)
			generation2 = create_generation(dir2)
			File.write("#{generation2}/structure_version.txt", "")
			dir3 = create_instance_dir(@process3.pid)
			generation3 = create_generation(dir3)
			File.write("#{generation3}/structure_version.txt", "1.x")
			
			AdminTools::ServerInstance.should_receive(:log_cleaning_action).twice
			instances = AdminTools::ServerInstance.list
			instances.should have(1).item
			instances[0].pid.should == @process1.pid
			File.exist?(dir2).should be_false
			File.exist?(dir3).should be_false
		end
		
		it "cleans up server instance directories for which the latest generation doesn't have a structure_version.txt" do
			@process1 = spawn_process
			@process2 = spawn_process
			
			dir1 = create_instance_dir(@process1.pid)
			create_generation(dir1)
			dir2 = create_instance_dir(@process2.pid)
			generation2 = create_generation(dir2)
			File.unlink("#{generation2}/structure_version.txt")
			
			AdminTools::ServerInstance.should_receive(:log_cleaning_action)
			instances = AdminTools::ServerInstance.list
			instances.should have(1).item
			instances[0].pid.should == @process1.pid
			File.exist?(dir2).should be_false
		end
		
		it "only cleans up an instance directory if it was modified more than STALE_TIME_THRESHOLD seconds ago" do
			@process1 = spawn_process
			@process2 = spawn_process
			creation_time = Time.now
			list_time = Time.now + AdminTools::ServerInstance::STALE_TIME_THRESHOLD + 1
			
			# This directory was created more than STALE_TIME_THRESHOLD secs ago, but
			# modified just now.
			dir1 = create_instance_dir(@process1.pid)
			generation1 = create_generation(dir1)
			File.unlink("#{generation1}/structure_version.txt")
			File.utime(creation_time, list_time, dir1)
			
			# This directory was created and modified more than STALE_TIME_THRESHOLD
			# secs ago.
			dir2 = create_instance_dir(@process2.pid)
			generation2 = create_generation(dir2)
			File.unlink("#{generation2}/structure_version.txt")
			File.utime(creation_time, creation_time, dir2)
			
			AdminTools::ServerInstance.should_receive(:log_cleaning_action)
			AdminTools::ServerInstance.should_receive(:current_time).and_return(list_time)
			instances = AdminTools::ServerInstance.list
			instances.should have(0).items
			File.exist?(dir1).should be_true
			File.exist?(dir2).should be_false
		end
		
		it "does not clean up instance directories if clean_stale_or_corrupted is false" do
			@process1 = spawn_process
			@process2 = spawn_process
			
			dir1 = create_instance_dir(@process1.pid)
			create_generation(dir1)
			dir2 = create_instance_dir(@process2.pid)
			create_generation(dir2)
			File.write("#{dir2}/control_process.pid", "")
			
			AdminTools::ServerInstance.should_not_receive(:log_cleaning_action)
			instances = AdminTools::ServerInstance.list(:clean_stale_or_corrupted => false)
			instances.should have(1).item
			instances[0].pid.should == @process1.pid
			File.exist?(dir2).should be_true
		end
	end
	
	describe "#pid" do
		before :each do
			@process1 = spawn_process
		end
		
		it "returns the PID in the directory filename if instance.pid doesn't exist" do
			dir = create_instance_dir(@process1.pid)
			create_generation(dir)
			AdminTools::ServerInstance.new(dir).pid.should == @process1.pid
		end
		
		it "returns the PID in control_process.pid if it exists" do
			dir = create_instance_dir(@process1.pid + 1)
			create_generation(dir)
			File.write("#{dir}/control_process.pid", @process1.pid)
			AdminTools::ServerInstance.new(dir).pid.should == @process1.pid
		end
	end
end

end # module PhusionPassenger