balepc-zombie_passenger_killer 0.2.1.patched
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +7 -0
- data/Gemfile.lock +26 -0
- data/Rakefile +21 -0
- data/Readme.md +66 -0
- data/bin/zombie_passenger_killer +39 -0
- data/lib/zombie_passenger_killer/reaper.rb +91 -0
- data/lib/zombie_passenger_killer/version.rb +3 -0
- data/lib/zombie_passenger_killer.rb +4 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/zombie_passenger_killer_spec.rb +178 -0
- data/zombie_passenger_killer.gemspec +12 -0
- metadata +78 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
zombie_passenger_killer (0.2.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.3)
|
10
|
+
rake (0.9.2)
|
11
|
+
rspec (2.6.0)
|
12
|
+
rspec-core (~> 2.6.0)
|
13
|
+
rspec-expectations (~> 2.6.0)
|
14
|
+
rspec-mocks (~> 2.6.0)
|
15
|
+
rspec-core (2.6.4)
|
16
|
+
rspec-expectations (2.6.0)
|
17
|
+
diff-lcs (~> 1.1.2)
|
18
|
+
rspec-mocks (2.6.0)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
rake
|
25
|
+
rspec (~> 2)
|
26
|
+
zombie_passenger_killer!
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
task :default do
|
4
|
+
sh "rspec spec/"
|
5
|
+
end
|
6
|
+
|
7
|
+
rule /^version:bump:.*/ do |t|
|
8
|
+
sh "git status | grep 'nothing to commit'" # ensure we are not dirty
|
9
|
+
index = ['major', 'minor','patch'].index(t.name.split(':').last)
|
10
|
+
file = 'lib/zombie_passenger_killer/version.rb'
|
11
|
+
|
12
|
+
version_file = File.read(file)
|
13
|
+
old_version, *version_parts = version_file.match(/(\d+)\.(\d+)\.(\d+)/).to_a
|
14
|
+
version_parts[index] = version_parts[index].to_i + 1
|
15
|
+
version_parts[2] = 0 if index < 2 # remove patch for minor
|
16
|
+
version_parts[1] = 0 if index < 1 # remove minor for major
|
17
|
+
new_version = version_parts * '.'
|
18
|
+
File.open(file,'w'){|f| f.write(version_file.sub(old_version, new_version)) }
|
19
|
+
|
20
|
+
sh "bundle && git add #{file} Gemfile.lock && git commit -m 'bump version to #{new_version}'"
|
21
|
+
end
|
data/Readme.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
![Zombies on a train](http://dl.dropbox.com/u/2670385/Web/zombie.jpeg)
|
2
|
+
|
3
|
+
Guaranteed zombie passengers death.
|
4
|
+
|
5
|
+
- passenger process no longer listed in passenger-status ? => Death
|
6
|
+
- high CPU load over long period (Optional) ? => Death
|
7
|
+
|
8
|
+
strace of killed zombies is printed, so debugging is easier.
|
9
|
+
|
10
|
+
(god/bluepill are not suited to monitor passenger apps because of ever-changing pids)
|
11
|
+
|
12
|
+
Add passenger-status to `/etc/sudoers` or run with sudo.
|
13
|
+
|
14
|
+
Install
|
15
|
+
=======
|
16
|
+
sudo gem install zombie_passenger_killer
|
17
|
+
|
18
|
+
Usage
|
19
|
+
=====
|
20
|
+
|
21
|
+
zombie_passenger_killer [options]
|
22
|
+
|
23
|
+
Options:
|
24
|
+
-m, --max [SIZE] Max high CPU entries in history before killing (default: off)
|
25
|
+
--history [SIZE] History size (default: 5)
|
26
|
+
-c, --cpu [PERCENT] Mark as high CPU when above PERCENT (default: 70)
|
27
|
+
-g, --grace [SECONDS] Wait SECONDS before hard-killing (-9) a process (default: 5)
|
28
|
+
-i, --interval [SECONDS] Check every SECONDS (default: 10)
|
29
|
+
-p, --pattern [PATTERN] Find processes with this pattern (default: ' Rack: ')
|
30
|
+
-r, --rvm-sudo Use `rvmsudo` to see passenger-status
|
31
|
+
-h, --help Show this
|
32
|
+
-v, --version Show Version
|
33
|
+
-t, --time Show time in output
|
34
|
+
|
35
|
+
|
36
|
+
### Bluepill script
|
37
|
+
|
38
|
+
app.process("zombie_passenger_killer") do |process|
|
39
|
+
process.start_command = "zombie_passenger_killer --max 5 --history 10 --cpu 30 --interval 10"
|
40
|
+
process.stdout = process.stderr = "/var/log/autorotate/zombie_passenger_killer.log"
|
41
|
+
process.pid_file = "/var/run/zombie_passenger_killer.pid"
|
42
|
+
process.daemonize = true
|
43
|
+
end
|
44
|
+
|
45
|
+
### Monit script
|
46
|
+
|
47
|
+
check process zombie_killer
|
48
|
+
with pidfile "/var/run/zombie_passenger_killer.pid"
|
49
|
+
start program = "/bin/bash -c 'export PATH=$PATH:/usr/local/bin HOME=/home;zombie_passenger_killer --max 5 --history 10 --cpu 30 --interval 10 &>/var/log/zombie_passenger_killer.log & &>/dev/null;echo $! > /var/run/zombie_passenger_killer.pid'"
|
50
|
+
stop program = "/bin/bash -c 'PIDF=/var/run/zombie_passenger_killer.pid;/bin/kill `cat $PIDF` && rm -f $PIDF'"
|
51
|
+
group zombie_killer
|
52
|
+
|
53
|
+
### God script
|
54
|
+
|
55
|
+
# TODO
|
56
|
+
|
57
|
+
Author
|
58
|
+
======
|
59
|
+
|
60
|
+
###Contributors
|
61
|
+
- [Roman Heinrich](https://github.com/mindreframer)
|
62
|
+
- [Kevin Mullin](https://github.com/kmullin)
|
63
|
+
|
64
|
+
[Michael Grosser](http://grosser.it)<br/>
|
65
|
+
michael@grosser.it<br/>
|
66
|
+
Hereby placed under public domain, do what you want, just do not hold me accountable...
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
5
|
+
require 'zombie_passenger_killer'
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
version = ZombiePassengerKiller::VERSION
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.banner = <<BANNER
|
11
|
+
Guaranteed zombie passengers death
|
12
|
+
|
13
|
+
Usage:
|
14
|
+
zombie_passenger_killer [options]
|
15
|
+
|
16
|
+
Options:
|
17
|
+
BANNER
|
18
|
+
opts.on("-m", "--max [SIZE]", Integer, "Max high CPU entries in history before killing (default: off)") {|i| options[:max]=i }
|
19
|
+
opts.on("--history [SIZE]", Integer, "History size (default: 5)") {|i| options[:history]=i }
|
20
|
+
opts.on("-c", "--cpu [PERCENT]", Integer, "Mark as high CPU when above PERCENT (default: 70)") {|i| options[:cpu]=i }
|
21
|
+
opts.on("-g", "--grace [SECONDS]", Integer, "Wait SECONDS before hard-killing (-9) a process (default: 5)") {|i| options[:grace]=i }
|
22
|
+
opts.on("-i", "--interval [SECONDS]", Integer, "Check every SECONDS (default: 10)") {|i| options[:interval]=i }
|
23
|
+
opts.on("-p", "--pattern [PATTERN]", String, "Find processes with this pattern (default: ' Rack: ')") {|i| options[:pattern]=i }
|
24
|
+
opts.on("-n", "--no-daemon","Do not start daemon. Check once and exit") {|i| options[:no_daemon]=i }
|
25
|
+
opts.on("-r", "--rvm-sudo","Use `rvmsudo` to see passenger-status") {|i| options[:rvm_sudo]=i }
|
26
|
+
opts.on("-h", "--help","Show this") { puts opts; exit }
|
27
|
+
opts.on("-v", "--version","Show Version"){ puts version; exit }
|
28
|
+
opts.on("-t", "--timing","Show timing on output") { options[:show_times]=true }
|
29
|
+
end.parse!
|
30
|
+
|
31
|
+
$stdout.sync = true
|
32
|
+
puts "PID: #{$$} Version: #{version} Started at #{Time.now}"
|
33
|
+
|
34
|
+
$0 = File.basename(__FILE__) + '-' + version
|
35
|
+
unless options[:no_daemon].nil?
|
36
|
+
ZombiePassengerKiller::Reaper.new(options).hunt_zombies
|
37
|
+
else
|
38
|
+
ZombiePassengerKiller::Reaper.new(options).lurk
|
39
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module ZombiePassengerKiller
|
2
|
+
class Reaper
|
3
|
+
|
4
|
+
attr_accessor :out # overwriteable for tests
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
@history = {}
|
8
|
+
@history_entries = options[:history] || 5
|
9
|
+
@max_high_cpu = options[:max]
|
10
|
+
@high_cpu = options[:cpu] || 70
|
11
|
+
@grace_time = options[:grace] || 5
|
12
|
+
@pattern = options[:pattern] || ' Rack: '
|
13
|
+
@show_times = options[:show_times] || false
|
14
|
+
@interval = options[:interval] || 10
|
15
|
+
@strace_time = 5
|
16
|
+
@out = STDOUT
|
17
|
+
@rvm_sudo = options[:rvm_sudo] || false
|
18
|
+
end
|
19
|
+
|
20
|
+
def lurk
|
21
|
+
loop do
|
22
|
+
hunt_zombies
|
23
|
+
sleep @interval
|
24
|
+
end
|
25
|
+
rescue Interrupt
|
26
|
+
log "Exiting..."
|
27
|
+
raise $!
|
28
|
+
end
|
29
|
+
|
30
|
+
def store_current_cpu(processes)
|
31
|
+
keys_to_remove = @history.keys - processes.map{|x| x[:pid] }
|
32
|
+
keys_to_remove.each{|k| !@history.delete k }
|
33
|
+
|
34
|
+
processes.each do |process|
|
35
|
+
@history[process[:pid]] ||= []
|
36
|
+
@history[process[:pid]] << process[:cpu]
|
37
|
+
@history[process[:pid]] = @history[process[:pid]].last(@history_entries)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_strace(pid, time)
|
42
|
+
Process.getpgid(pid) rescue return 'No such process'
|
43
|
+
`( strace -p #{pid} 2>&1 ) & sleep #{time} ; kill $! 2>&1`
|
44
|
+
end
|
45
|
+
|
46
|
+
def hunt_zombies
|
47
|
+
active_pids_in_passenger_status = passenger_pids
|
48
|
+
active_processes_in_processlist = process_status
|
49
|
+
zombies = active_processes_in_processlist.map{|x| x[:pid] } - active_pids_in_passenger_status rescue Array.new
|
50
|
+
|
51
|
+
# kill processes with high CPU if user wants it
|
52
|
+
high_load = if @max_high_cpu
|
53
|
+
store_current_cpu active_processes_in_processlist
|
54
|
+
active_pids_in_passenger_status.select do |pid|
|
55
|
+
@history[pid].count{|x| x > @high_cpu } >= @max_high_cpu
|
56
|
+
end
|
57
|
+
else
|
58
|
+
[]
|
59
|
+
end
|
60
|
+
|
61
|
+
(high_load + zombies).each do |pid|
|
62
|
+
kill_zombie pid
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# return array of pids reported from passenger-status command, nil if passenger-status doesn't run
|
67
|
+
def passenger_pids
|
68
|
+
pids = %x(#{'rvmsudo ' if @rvm_sudo}passenger-status|grep PID).split("\n").map { |x| x.strip.match(/PID: \d*/).to_s.split[1].to_i }
|
69
|
+
pids if $?.exitstatus.zero?
|
70
|
+
end
|
71
|
+
|
72
|
+
def process_status
|
73
|
+
%x(ps -eo pid,pcpu,args|grep -v grep|grep '#{@pattern}').split("\n").map do |line|
|
74
|
+
values = line.strip.split[0..1]
|
75
|
+
{:pid => values.first.to_i, :cpu => values.last.to_f}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def kill_zombie(pid)
|
80
|
+
log "Killing passenger process #{pid}"
|
81
|
+
log get_strace(pid, @strace_time)
|
82
|
+
Process.kill('TERM', pid) rescue nil
|
83
|
+
sleep @grace_time # wait for it to die
|
84
|
+
Process.kill('KILL', pid) rescue nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def log(msg)
|
88
|
+
@out.puts "#{@show_times ? "** [#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] #$$: " : ''}#{msg}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
require File.expand_path('spec/spec_helper')
|
2
|
+
|
3
|
+
describe ZombiePassengerKiller do
|
4
|
+
let(:killer){
|
5
|
+
ZombiePassengerKiller::Reaper.new(@options || {}).tap do |k|
|
6
|
+
k.stub!(:passenger_pids).and_return([111])
|
7
|
+
k.out = StringIO.new
|
8
|
+
end
|
9
|
+
}
|
10
|
+
|
11
|
+
def output
|
12
|
+
killer.out.rewind
|
13
|
+
killer.out.read
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has a VERSION" do
|
17
|
+
ZombiePassengerKiller::VERSION.should =~ /^\d+\.\d+\.\d+$/
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#hunt_zombies" do
|
21
|
+
it "does not kill anything by default" do
|
22
|
+
killer.should_not_receive(:kill_zombie)
|
23
|
+
killer.hunt_zombies
|
24
|
+
end
|
25
|
+
|
26
|
+
it "finds the right zombies" do
|
27
|
+
killer.stub!(:passenger_pids).and_return([123])
|
28
|
+
killer.stub!(:process_status).and_return([{:pid => 124, :cpu => 0}])
|
29
|
+
killer.should_receive(:kill_zombie).with(124)
|
30
|
+
killer.hunt_zombies
|
31
|
+
end
|
32
|
+
|
33
|
+
it "kills zombies with high cpu over max" do
|
34
|
+
@options = {:max => 1}
|
35
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 100}])
|
36
|
+
killer.should_receive(:kill_zombie).with(111)
|
37
|
+
killer.hunt_zombies
|
38
|
+
end
|
39
|
+
|
40
|
+
it "does not kills zombies with high cpu under max" do
|
41
|
+
@options = {:max => 2}
|
42
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 100}])
|
43
|
+
killer.should_not_receive(:kill_zombie).with(111)
|
44
|
+
killer.hunt_zombies
|
45
|
+
end
|
46
|
+
|
47
|
+
it "ignores high cpu levels in old history" do
|
48
|
+
@options = {:max => 2, :history => 2}
|
49
|
+
killer.should_not_receive(:kill_zombie).with(111)
|
50
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 100}])
|
51
|
+
killer.hunt_zombies
|
52
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 0}])
|
53
|
+
killer.hunt_zombies
|
54
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 100}])
|
55
|
+
killer.hunt_zombies
|
56
|
+
end
|
57
|
+
|
58
|
+
it "kills on high cpu levels in recent history" do
|
59
|
+
@options = {:max => 2, :history => 2}
|
60
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 100}])
|
61
|
+
killer.hunt_zombies
|
62
|
+
killer.should_receive(:kill_zombie).with(111)
|
63
|
+
killer.hunt_zombies
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#kill_zombies" do
|
68
|
+
before do
|
69
|
+
killer.instance_eval{
|
70
|
+
@grace_time = 0.1
|
71
|
+
@strace_time = 0.1
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def pid_of(marker)
|
76
|
+
processes = `ps -ef | grep '#{marker}' | grep -v grep`
|
77
|
+
processes.strip.split("\n").last.split(/\s+/)[1].to_i
|
78
|
+
end
|
79
|
+
|
80
|
+
def start_bogus_process(options={})
|
81
|
+
marker = "TEST---#{rand(999999999999)}"
|
82
|
+
Thread.new do
|
83
|
+
`ruby -e 'at_exit{ puts "proper exit"; #{'sleep 10' if options[:hang]}}; sleep 10; puts "#{marker}"' 2>&1`
|
84
|
+
end
|
85
|
+
sleep 1 # give process time to spin up
|
86
|
+
pid_of(marker)
|
87
|
+
end
|
88
|
+
|
89
|
+
def process_alive?(pid)
|
90
|
+
Process.getpgid(pid)
|
91
|
+
rescue Errno::ESRCH
|
92
|
+
false
|
93
|
+
end
|
94
|
+
|
95
|
+
it "kills normal processes" do
|
96
|
+
pid = start_bogus_process
|
97
|
+
lambda{
|
98
|
+
killer.kill_zombie(pid)
|
99
|
+
sleep 0.1
|
100
|
+
}.should change{ process_alive?(pid) }
|
101
|
+
end
|
102
|
+
|
103
|
+
it "kills hanging processes" do
|
104
|
+
pid = start_bogus_process :hang => true
|
105
|
+
lambda{
|
106
|
+
killer.kill_zombie(pid)
|
107
|
+
sleep 0.1
|
108
|
+
}.should change{ process_alive?(pid) }
|
109
|
+
end
|
110
|
+
|
111
|
+
it "prints an strace of the process" do
|
112
|
+
pid = start_bogus_process
|
113
|
+
killer.kill_zombie(pid)
|
114
|
+
output.should include('attach:')
|
115
|
+
end
|
116
|
+
|
117
|
+
it "does not take a strace of a dead process" do
|
118
|
+
killer.kill_zombie(111)
|
119
|
+
output.should_not include('attach:')
|
120
|
+
end
|
121
|
+
|
122
|
+
it "does not fail with an unknown pid" do
|
123
|
+
killer.kill_zombie(111)
|
124
|
+
output.should include('No such process')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#log" do
|
129
|
+
it "logs simple when :show_times is not given" do
|
130
|
+
killer.log "X"
|
131
|
+
output.should == "X\n"
|
132
|
+
end
|
133
|
+
|
134
|
+
it "logs simple when :show_times is not given" do
|
135
|
+
@options = {:show_times => true}
|
136
|
+
killer.log "X"
|
137
|
+
output.should include(Time.now.year.to_s)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#lurk" do
|
142
|
+
it "sleeps after checking" do
|
143
|
+
killer.should_receive(:hunt_zombies)
|
144
|
+
killer.should_receive(:sleep).with(10).and_raise "LOOP-BREAKER"
|
145
|
+
lambda{
|
146
|
+
killer.lurk
|
147
|
+
}.should raise_error "LOOP-BREAKER"
|
148
|
+
end
|
149
|
+
|
150
|
+
it "calls sleep with the given interval" do
|
151
|
+
@options = {:interval => 5}
|
152
|
+
killer.stub(:hunt_zombies)
|
153
|
+
killer.should_receive(:sleep).with(5).and_raise "LOOP-BREAKER"
|
154
|
+
lambda{
|
155
|
+
killer.lurk
|
156
|
+
}.should raise_error "LOOP-BREAKER"
|
157
|
+
end
|
158
|
+
|
159
|
+
it "prints Exiting on Interrupt" do
|
160
|
+
killer.stub(:hunt_zombies)
|
161
|
+
killer.should_receive(:sleep).and_raise Interrupt.new
|
162
|
+
lambda{
|
163
|
+
killer.lurk
|
164
|
+
}.should raise_error Interrupt
|
165
|
+
output.should include("Exiting")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "cli" do
|
170
|
+
it "prints its version" do
|
171
|
+
`./bin/zombie_passenger_killer -v`.should =~ /^\d+\.\d+\.\d+$/m
|
172
|
+
end
|
173
|
+
|
174
|
+
it "prints help" do
|
175
|
+
`./bin/zombie_passenger_killer -h`.should include('Usage')
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
require 'zombie_passenger_killer/version'
|
3
|
+
|
4
|
+
Gem::Specification.new "balepc-zombie_passenger_killer", ZombiePassengerKiller::VERSION do |s|
|
5
|
+
s.summary = "Guaranteed zombie passengers death"
|
6
|
+
s.authors = ["Michael Grosser"]
|
7
|
+
s.email = "michael@grosser.it"
|
8
|
+
s.homepage = "http://github.com/grosser/zombie_passenger_killer"
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.executables = ["zombie_passenger_killer"]
|
11
|
+
s.license = "MIT"
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: balepc-zombie_passenger_killer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: -1513943312
|
5
|
+
prerelease: 6
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 1
|
10
|
+
- patched
|
11
|
+
version: 0.2.1.patched
|
12
|
+
platform: ruby
|
13
|
+
authors:
|
14
|
+
- Michael Grosser
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2012-03-14 00:00:00 Z
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description:
|
23
|
+
email: michael@grosser.it
|
24
|
+
executables:
|
25
|
+
- zombie_passenger_killer
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- Gemfile
|
32
|
+
- Gemfile.lock
|
33
|
+
- Rakefile
|
34
|
+
- Readme.md
|
35
|
+
- bin/zombie_passenger_killer
|
36
|
+
- lib/zombie_passenger_killer.rb
|
37
|
+
- lib/zombie_passenger_killer/reaper.rb
|
38
|
+
- lib/zombie_passenger_killer/version.rb
|
39
|
+
- spec/spec_helper.rb
|
40
|
+
- spec/zombie_passenger_killer_spec.rb
|
41
|
+
- zombie_passenger_killer.gemspec
|
42
|
+
homepage: http://github.com/grosser/zombie_passenger_killer
|
43
|
+
licenses:
|
44
|
+
- MIT
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">"
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 25
|
65
|
+
segments:
|
66
|
+
- 1
|
67
|
+
- 3
|
68
|
+
- 1
|
69
|
+
version: 1.3.1
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.8.17
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Guaranteed zombie passengers death
|
77
|
+
test_files: []
|
78
|
+
|