silver_spurs 1.0.1 → 1.0.2
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.
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/.simplecov +4 -0
- data/.travis.yml +6 -0
- data/README.md +55 -8
- data/Rakefile +18 -0
- data/lib/silver_spurs/asyncifier.rb +3 -3
- data/lib/silver_spurs/client.rb +10 -8
- data/lib/silver_spurs/client/bootstrap_run.rb +1 -4
- data/lib/silver_spurs/knife_interface.rb +1 -1
- data/lib/silver_spurs/version.rb +1 -1
- data/silver_spurs.gemspec +1 -0
- data/spec/lib/silver_spurs/app_spec.rb +192 -9
- data/spec/lib/silver_spurs/asyncifier_spec.rb +509 -0
- data/spec/lib/silver_spurs/client/bootstrap_run_spec.rb +162 -0
- data/spec/lib/silver_spurs/client_spec.rb +113 -0
- data/spec/lib/silver_spurs/knife_interface_spec.rb +26 -1
- data/spec/spec_helper.rb +2 -0
- metadata +33 -3
@@ -0,0 +1,509 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
describe SilverSpurs::Asyncifier do
|
5
|
+
before :each do
|
6
|
+
SilverSpurs::Asyncifier.logger = Logger.new('/dev/null')
|
7
|
+
end
|
8
|
+
|
9
|
+
describe :timeout do
|
10
|
+
it 'defaults to 60 minutes' do
|
11
|
+
SilverSpurs::Asyncifier.timeout.should eq 60 * 60
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe :logger do
|
16
|
+
before :each do
|
17
|
+
SilverSpurs::Asyncifier.logger = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'defaults to STDERR' do
|
21
|
+
logger_dbl = double('logger')
|
22
|
+
logger_dbl.stub(:level=)
|
23
|
+
Logger.should_receive(:new).with(STDERR).and_return logger_dbl
|
24
|
+
|
25
|
+
SilverSpurs::Asyncifier.instance.logger
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe :spawn_process do
|
30
|
+
before :each do
|
31
|
+
SilverSpurs::Asyncifier.instance.stub(:create_directory_tree)
|
32
|
+
Process.stub(:spawn)
|
33
|
+
File.stub(:open)
|
34
|
+
Process.stub(:detach)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'creates the directory tree' do
|
38
|
+
SilverSpurs::Asyncifier.instance.should_receive(:create_directory_tree)
|
39
|
+
SilverSpurs::Asyncifier.spawn_process('foo', 'echo foo')
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'spawns the process' do
|
43
|
+
Process.should_receive(:spawn)
|
44
|
+
SilverSpurs::Asyncifier.spawn_process('foo', 'echo foo')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'writes the pid to the lock file' do
|
48
|
+
File.should_receive(:open)
|
49
|
+
SilverSpurs::Asyncifier.instance.should_receive(:pid_file_path)
|
50
|
+
SilverSpurs::Asyncifier.spawn_process('foo', 'echo foo')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'detaches the process' do
|
54
|
+
Process.should_receive(:detach)
|
55
|
+
SilverSpurs::Asyncifier.spawn_process('foo', 'echo foo')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe :has_lock? do
|
60
|
+
context 'when there is no pid file' do
|
61
|
+
before :each do
|
62
|
+
File.stub(:exists?).and_return false
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns false' do
|
66
|
+
SilverSpurs::Asyncifier.has_lock?('foo').should be_false
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'checks for the pid file' do
|
70
|
+
SilverSpurs::Asyncifier.instance.should_receive(:pid_file_path)
|
71
|
+
SilverSpurs::Asyncifier.has_lock?('foo')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'when there is a pid file' do
|
76
|
+
before :each do
|
77
|
+
File.stub(:exists?).and_return true
|
78
|
+
File.stub(:read).and_return '1234'
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'checks to see if the process is running' do
|
82
|
+
IO.should_receive(:popen).with('ps -o command -p 1234').and_return StringIO.new("1\n2")
|
83
|
+
SilverSpurs::Asyncifier.has_lock? '1234'
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when the process is running' do
|
87
|
+
before :each do
|
88
|
+
IO.stub(:popen).and_return StringIO.new("COMMAND\nknife whatever")
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'returns true' do
|
92
|
+
SilverSpurs::Asyncifier.has_lock?('1234').should be_true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when the process is not running' do
|
97
|
+
before :each do
|
98
|
+
IO.stub(:popen).and_return StringIO.new("COMMAND")
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'returns false' do
|
102
|
+
SilverSpurs::Asyncifier.has_lock?('1234').should be_false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
describe :success? do
|
111
|
+
before :each do
|
112
|
+
SilverSpurs::Asyncifier.instance.stub(:success_file_path).and_return 'success_file'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'checks for a success file' do
|
116
|
+
File.should_receive(:exists?).with('success_file').and_return false
|
117
|
+
|
118
|
+
SilverSpurs::Asyncifier.success?('foo')
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'when the success file does not exist' do
|
122
|
+
before :each do
|
123
|
+
File.stub(:exists?).and_return false
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'returns false' do
|
127
|
+
SilverSpurs::Asyncifier.success?('foo').should be_false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when the success file exists' do
|
132
|
+
before :each do
|
133
|
+
SilverSpurs::Asyncifier.instance.stub(:pid_file_path).and_return 'pid_file'
|
134
|
+
File.stub(:exists?).with('success_file').and_return true
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'checks to see if the pid file exists' do
|
138
|
+
File.should_receive(:exists?).twice.and_return(true, false)
|
139
|
+
SilverSpurs::Asyncifier.success? 'foo'
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'when the pid file exists' do
|
143
|
+
before :each do
|
144
|
+
File.stub(:exists?).and_return(true, true)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'checks the modified time of the success and pid file' do
|
148
|
+
File.should_receive(:mtime).twice.and_return(Time.now)
|
149
|
+
SilverSpurs::Asyncifier.success? 'foo'
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'when the success file is younger than the pid file' do
|
153
|
+
before :each do
|
154
|
+
File.stub(:mtime).and_return(Time.new(2013), Time.new(2012))
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should return true' do
|
158
|
+
SilverSpurs::Asyncifier.success?('foo').should be_true
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'when the pid file is younger than the success file' do
|
163
|
+
before :each do
|
164
|
+
File.stub(:mtime).and_return(Time.new(2012), Time.new(2013))
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should return false' do
|
168
|
+
SilverSpurs::Asyncifier.success?('foo').should be_false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
describe :get_log do
|
179
|
+
before :each do
|
180
|
+
SilverSpurs::Asyncifier.instance.stub(:log_file_path).and_return 'log_file'
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'checks to see if the log file exists' do
|
184
|
+
File.should_receive(:exists?).and_return false
|
185
|
+
SilverSpurs::Asyncifier.get_log 'foo'
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'when the log file exists' do
|
189
|
+
before :each do
|
190
|
+
File.stub(:exists?).and_return true
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'reads the log' do
|
194
|
+
File.stub(:read).and_return 'logs are cool'
|
195
|
+
SilverSpurs::Asyncifier.get_log('foo').should eq 'logs are cool'
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context 'when the log file does not exist' do
|
200
|
+
before :each do
|
201
|
+
File.stub(:exists?).and_return false
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'returns nil' do
|
205
|
+
SilverSpurs::Asyncifier.get_log('foo').should be_nil
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
describe :reap_orphaned_lock do
|
212
|
+
before :each do
|
213
|
+
SilverSpurs::Asyncifier.instance.stub(:pid_file_path).and_return 'pid_file'
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'checks to see if the process is active' do
|
217
|
+
SilverSpurs::Asyncifier.instance.should_receive(:has_lock?).and_return true
|
218
|
+
SilverSpurs::Asyncifier.reap_orphaned_lock 'foo'
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'when the process is still active' do
|
222
|
+
before :each do
|
223
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return true
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'should not try to delete the lock' do
|
227
|
+
File.should_not_receive(:delete)
|
228
|
+
SilverSpurs::Asyncifier.reap_orphaned_lock 'foo'
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'when the process is not active' do
|
233
|
+
before :each do
|
234
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return false
|
235
|
+
SilverSpurs::Asyncifier.instance.stub(:pid_file_path).and_return 'pid_file'
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'should delete the lock file' do
|
239
|
+
File.should_receive(:delete).with('pid_file')
|
240
|
+
SilverSpurs::Asyncifier.reap_orphaned_lock 'foo'
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
describe :reap_process do
|
247
|
+
before :each do
|
248
|
+
SilverSpurs::Asyncifier.instance.stub(:sleep)
|
249
|
+
SilverSpurs::Asyncifier.instance.stub(:reap_orphaned_lock)
|
250
|
+
SilverSpurs::Asyncifier.instance.stub(:pid_file_path).and_return 'pid_file'
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'checks to see if the process is active' do
|
254
|
+
SilverSpurs::Asyncifier.instance.should_receive(:has_lock?).and_return false
|
255
|
+
SilverSpurs::Asyncifier.reap_process 'foo'
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'reaps the lock file' do
|
259
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return false
|
260
|
+
SilverSpurs::Asyncifier.instance.should_receive(:reap_orphaned_lock)
|
261
|
+
SilverSpurs::Asyncifier.reap_process 'foo'
|
262
|
+
end
|
263
|
+
|
264
|
+
context 'when the process is active' do
|
265
|
+
before :each do
|
266
|
+
File.stub(:read).and_return '1234'
|
267
|
+
Process.stub(:kill)
|
268
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return true
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'kills the process' do
|
272
|
+
Process.should_receive(:kill)
|
273
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return(true, false)
|
274
|
+
SilverSpurs::Asyncifier.reap_process 'foo'
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'checks to see if the process really died' do
|
278
|
+
SilverSpurs::Asyncifier.instance.should_receive(:has_lock?).twice.and_return(true, false)
|
279
|
+
SilverSpurs::Asyncifier.reap_process 'foo'
|
280
|
+
end
|
281
|
+
|
282
|
+
context 'when the process dies when asked nicely' do
|
283
|
+
before :each do
|
284
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return(true, false)
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'just walks away' do
|
288
|
+
Process.should_receive(:kill).once
|
289
|
+
SilverSpurs::Asyncifier.reap_process 'foo'
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
context "when the process doesn't die after being asked nicely" do
|
294
|
+
before :each do
|
295
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return(true, true)
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'kills the process with fire' do
|
299
|
+
Process.should_receive(:kill).twice
|
300
|
+
SilverSpurs::Asyncifier.reap_process 'foo'
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
305
|
+
|
306
|
+
context 'if the process is inactive' do
|
307
|
+
before :each do
|
308
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return false
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'skips to reaping the lock file' do
|
312
|
+
File.should_not_receive(:read)
|
313
|
+
Process.should_not_receive(:kill)
|
314
|
+
|
315
|
+
SilverSpurs::Asyncifier.instance.should_receive(:reap_orphaned_lock)
|
316
|
+
|
317
|
+
SilverSpurs::Asyncifier.reap_process 'foo'
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|
322
|
+
|
323
|
+
describe :reap_old_process do
|
324
|
+
before :each do
|
325
|
+
SilverSpurs::Asyncifier.instance.stub(:pid_file_path).and_return 'pid_file'
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'checks if the process is active' do
|
329
|
+
SilverSpurs::Asyncifier.instance.should_receive(:has_lock?).and_return false
|
330
|
+
SilverSpurs::Asyncifier.reap_old_process 'foo'
|
331
|
+
end
|
332
|
+
|
333
|
+
context 'when the process is active' do
|
334
|
+
before :each do
|
335
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return true
|
336
|
+
end
|
337
|
+
|
338
|
+
context 'when the process is beyond the timeout window' do
|
339
|
+
before :each do
|
340
|
+
Time.stub(:now).and_return Time.new(2013)
|
341
|
+
SilverSpurs::Asyncifier.instance.stub(:timeout).and_return 100
|
342
|
+
File.stub(:mtime).and_return Time.new(2013) - 101
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'reaps the process' do
|
346
|
+
SilverSpurs::Asyncifier.instance.should_receive(:reap_process)
|
347
|
+
SilverSpurs::Asyncifier.reap_old_process 'foo'
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
context 'when the process is within the timeout window' do
|
352
|
+
before :each do
|
353
|
+
Time.stub(:now).and_return Time.new(2013)
|
354
|
+
SilverSpurs::Asyncifier.instance.stub(:timeout).and_return 100
|
355
|
+
File.stub(:mtime).and_return Time.new(2013) - 99
|
356
|
+
end
|
357
|
+
|
358
|
+
it 'does not reap the process' do
|
359
|
+
SilverSpurs::Asyncifier.instance.should_not_receive(:reap_process)
|
360
|
+
SilverSpurs::Asyncifier.reap_old_process 'foo'
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
end
|
365
|
+
|
366
|
+
context 'when the process is not active' do
|
367
|
+
before :each do
|
368
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return false
|
369
|
+
end
|
370
|
+
|
371
|
+
it 'does not attempt to reap the process' do
|
372
|
+
SilverSpurs::Asyncifier.instance.should_not_receive :reap_process
|
373
|
+
SilverSpurs::Asyncifier.reap_old_process 'foo'
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
378
|
+
|
379
|
+
describe :exists? do
|
380
|
+
context 'when the process is active' do
|
381
|
+
before :each do
|
382
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return true
|
383
|
+
end
|
384
|
+
|
385
|
+
it 'returns true' do
|
386
|
+
SilverSpurs::Asyncifier.exists?('foo').should be_true
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
390
|
+
|
391
|
+
context 'when the process is not active' do
|
392
|
+
before :each do
|
393
|
+
SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return false
|
394
|
+
SilverSpurs::Asyncifier.instance.stub(:log_file_path).and_return 'log_file'
|
395
|
+
end
|
396
|
+
|
397
|
+
context 'if the log file exists' do
|
398
|
+
before :each do
|
399
|
+
File.stub(:exists?).and_return true
|
400
|
+
end
|
401
|
+
|
402
|
+
it 'returns true' do
|
403
|
+
SilverSpurs::Asyncifier.exists?('foo').should be_true
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
context 'if the log file does not exist' do
|
408
|
+
before :each do
|
409
|
+
File.stub(:exists?).and_return false
|
410
|
+
end
|
411
|
+
|
412
|
+
it 'returns false' do
|
413
|
+
SilverSpurs::Asyncifier.exists?('foo').should be_false
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
end
|
418
|
+
|
419
|
+
end
|
420
|
+
|
421
|
+
describe :log_file_path do
|
422
|
+
it 'builds a path in the base directory' do
|
423
|
+
SilverSpurs::Asyncifier.instance.should_receive(:base_path).and_return 'base_path'
|
424
|
+
File.should_receive(:join)
|
425
|
+
SilverSpurs::Asyncifier.instance.send(:log_file_path, 'foo')
|
426
|
+
end
|
427
|
+
|
428
|
+
it 'returns a file name' do
|
429
|
+
SilverSpurs::Asyncifier.instance.send(:log_file_path, 'foo').should be_a_kind_of(String)
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
describe :success_file_path do
|
434
|
+
it 'builds a path in the base directory' do
|
435
|
+
SilverSpurs::Asyncifier.instance.should_receive(:base_path).and_return 'base_path'
|
436
|
+
File.should_receive(:join)
|
437
|
+
SilverSpurs::Asyncifier.instance.send(:success_file_path, 'foo')
|
438
|
+
end
|
439
|
+
|
440
|
+
it 'returns a file name' do
|
441
|
+
SilverSpurs::Asyncifier.instance.send(:success_file_path, 'foo').should be_a_kind_of(String)
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
describe :pid_file_path do
|
446
|
+
it 'builds a path in the base directory' do
|
447
|
+
SilverSpurs::Asyncifier.instance.should_receive(:base_path).and_return 'base_path'
|
448
|
+
File.should_receive(:join)
|
449
|
+
SilverSpurs::Asyncifier.instance.send(:pid_file_path, 'foo')
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'returns a file name' do
|
453
|
+
SilverSpurs::Asyncifier.instance.send(:pid_file_path, 'foo').should be_a_kind_of(String)
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
describe :create_directory_tree do
|
458
|
+
it 'checks to see if the base path already exists' do
|
459
|
+
Dir.should_receive(:exists?).at_least(:once).and_return true
|
460
|
+
SilverSpurs::Asyncifier.instance.send(:create_directory_tree)
|
461
|
+
end
|
462
|
+
|
463
|
+
context 'if the base path already exists' do
|
464
|
+
before :each do
|
465
|
+
Dir.stub(:exists?).and_return true
|
466
|
+
end
|
467
|
+
|
468
|
+
it 'does not create the base directory' do
|
469
|
+
Dir.should_not_receive(:mkdir)
|
470
|
+
SilverSpurs::Asyncifier.instance.send(:create_directory_tree)
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
context 'if the base path does not exist' do
|
475
|
+
before :each do
|
476
|
+
Dir.stub(:exists?).and_return(false, true)
|
477
|
+
end
|
478
|
+
|
479
|
+
it 'creates the base directory' do
|
480
|
+
Dir.should_receive(:mkdir)
|
481
|
+
SilverSpurs::Asyncifier.instance.send(:create_directory_tree)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
context 'if a directory exists' do
|
486
|
+
before :each do
|
487
|
+
Dir.stub(:exists?).and_return true
|
488
|
+
end
|
489
|
+
|
490
|
+
it 'does not create the directory' do
|
491
|
+
Dir.should_not_receive(:mkdir)
|
492
|
+
SilverSpurs::Asyncifier.instance.send(:create_directory_tree)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
context 'if a directory does not exist' do
|
497
|
+
before :each do
|
498
|
+
Dir.stub(:exists?).and_return(true, false)
|
499
|
+
end
|
500
|
+
|
501
|
+
it 'creates the directory' do
|
502
|
+
Dir.should_receive(:mkdir).exactly(3).times
|
503
|
+
SilverSpurs::Asyncifier.instance.send(:create_directory_tree)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
end
|
508
|
+
|
509
|
+
end
|