zombie_passenger_killer 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme.md +3 -1
- data/VERSION +1 -1
- data/lib/zombie_passenger_killer.rb +10 -5
- data/spec/zombie_passenger_killer_spec.rb +113 -42
- data/zombie_passenger_killer.gemspec +7 -8
- metadata +5 -7
data/Readme.md
CHANGED
@@ -34,11 +34,13 @@ Usage
|
|
34
34
|
### Bluepill script
|
35
35
|
|
36
36
|
app.process("zombie_passenger_killer") do |process|
|
37
|
-
process.start_command = "zombie_passenger_killer --max 5 --history 10 --cpu 30 --interval 10
|
37
|
+
process.start_command = "zombie_passenger_killer --max 5 --history 10 --cpu 30 --interval 10"
|
38
|
+
process.stdout = process.stderr = "/var/log/autorotate/zombie_passenger_killer.log"
|
38
39
|
process.pid_file = "/var/run/zombie_passenger_killer.pid"
|
39
40
|
process.daemonize = true
|
40
41
|
end
|
41
42
|
|
43
|
+
|
42
44
|
### God script
|
43
45
|
|
44
46
|
TODO
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.3
|
@@ -1,6 +1,8 @@
|
|
1
1
|
class ZombiePassengerKiller
|
2
2
|
VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
|
3
3
|
|
4
|
+
attr_accessor :out # overwriteable for tests
|
5
|
+
|
4
6
|
def initialize(options)
|
5
7
|
@history = {}
|
6
8
|
@history_entries = options[:history] || 5
|
@@ -8,6 +10,8 @@ class ZombiePassengerKiller
|
|
8
10
|
@high_cpu = options[:cpu] || 70
|
9
11
|
@grace_time = options[:grace] || 5
|
10
12
|
@pattern = options[:pattern] || ' Rack: '
|
13
|
+
@strace_time = 5
|
14
|
+
@out = STDOUT
|
11
15
|
end
|
12
16
|
|
13
17
|
def store_current_cpu(processes)
|
@@ -22,7 +26,8 @@ class ZombiePassengerKiller
|
|
22
26
|
end
|
23
27
|
|
24
28
|
def get_strace(pid, time)
|
25
|
-
|
29
|
+
Process.getpgid(pid) rescue return 'No such process'
|
30
|
+
`( strace -p #{pid} 2>&1 ) & sleep #{time} ; kill $! 2>&1`
|
26
31
|
end
|
27
32
|
|
28
33
|
def hunt_zombies
|
@@ -57,10 +62,10 @@ class ZombiePassengerKiller
|
|
57
62
|
end
|
58
63
|
|
59
64
|
def kill_zombie(pid)
|
60
|
-
puts "Killing passenger process #{pid}"
|
61
|
-
puts get_strace(pid,
|
62
|
-
|
65
|
+
@out.puts "Killing passenger process #{pid}"
|
66
|
+
@out.puts get_strace(pid, @strace_time)
|
67
|
+
Process.kill('TERM', pid) rescue nil
|
63
68
|
sleep @grace_time
|
64
|
-
|
69
|
+
Process.kill('KILL', pid) rescue nil
|
65
70
|
end
|
66
71
|
end
|
@@ -11,56 +11,127 @@ describe ZombiePassengerKiller do
|
|
11
11
|
ZombiePassengerKiller::VERSION.should =~ /^\d+\.\d+\.\d+$/
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
describe "#hunt_zombies" do
|
15
|
+
it "does not kill anything by default" do
|
16
|
+
killer.should_not_receive(:kill_zombie)
|
17
|
+
killer.hunt_zombies
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
it "finds the right zombies" do
|
21
|
+
killer.stub!(:passenger_pids).and_return([123])
|
22
|
+
killer.stub!(:process_status).and_return([{:pid => 124, :cpu => 0}])
|
23
|
+
killer.should_receive(:kill_zombie).with(124)
|
24
|
+
killer.hunt_zombies
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
it "kills zombies with high cpu over max" do
|
28
|
+
@options = {:max => 1}
|
29
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 100}])
|
30
|
+
killer.should_receive(:kill_zombie).with(111)
|
31
|
+
killer.hunt_zombies
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
it "does not kills zombies with high cpu under max" do
|
35
|
+
@options = {:max => 2}
|
36
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 100}])
|
37
|
+
killer.should_not_receive(:kill_zombie).with(111)
|
38
|
+
killer.hunt_zombies
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
41
|
+
it "ignores high cpu levels in old history" do
|
42
|
+
@options = {:max => 2, :history => 2}
|
43
|
+
killer.should_not_receive(:kill_zombie).with(111)
|
44
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 100}])
|
45
|
+
killer.hunt_zombies
|
46
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 0}])
|
47
|
+
killer.hunt_zombies
|
48
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 100}])
|
49
|
+
killer.hunt_zombies
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
it "kills on high cpu levels in recent history" do
|
53
|
+
@options = {:max => 2, :history => 2}
|
54
|
+
killer.stub!(:process_status).and_return([{:pid => 111, :cpu => 100}])
|
55
|
+
killer.hunt_zombies
|
56
|
+
killer.should_receive(:kill_zombie).with(111)
|
57
|
+
killer.hunt_zombies
|
58
|
+
end
|
57
59
|
end
|
58
60
|
|
59
|
-
|
60
|
-
|
61
|
+
describe "#kill_zombies" do
|
62
|
+
before do
|
63
|
+
killer.out = StringIO.new
|
64
|
+
killer.instance_eval{
|
65
|
+
@grace_time = 0.1
|
66
|
+
@strace_time = 0.1
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def pid_of(marker)
|
71
|
+
processes = `ps -ef | grep '#{marker}' | grep -v grep`
|
72
|
+
processes.strip.split("\n").last.split(/\s+/)[1].to_i
|
73
|
+
end
|
74
|
+
|
75
|
+
def start_bogus_process(options={})
|
76
|
+
marker = "TEST---#{rand(999999999999)}"
|
77
|
+
Thread.new do
|
78
|
+
`ruby -e 'at_exit{ puts "proper exit"; #{'sleep 10' if options[:hang]}}; sleep 10; puts "#{marker}"' 2>&1`
|
79
|
+
end
|
80
|
+
sleep 1 # give process time to spin up
|
81
|
+
pid_of(marker)
|
82
|
+
end
|
83
|
+
|
84
|
+
def process_alive?(pid)
|
85
|
+
Process.getpgid(pid)
|
86
|
+
rescue Errno::ESRCH
|
87
|
+
false
|
88
|
+
end
|
89
|
+
|
90
|
+
def output
|
91
|
+
killer.out.rewind
|
92
|
+
killer.out.read
|
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
|
61
126
|
end
|
62
127
|
|
63
|
-
|
64
|
-
|
128
|
+
describe "cli" do
|
129
|
+
it "prints its version" do
|
130
|
+
`./bin/zombie_passenger_killer -v`.should =~ /^\d+\.\d+\.\d+$/m
|
131
|
+
end
|
132
|
+
|
133
|
+
it "prints help" do
|
134
|
+
`./bin/zombie_passenger_killer -h`.should include('Usage')
|
135
|
+
end
|
65
136
|
end
|
66
137
|
end
|
@@ -4,14 +4,13 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.1.
|
7
|
+
s.name = "zombie_passenger_killer"
|
8
|
+
s.version = "0.1.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Michael Grosser"]
|
12
|
-
s.date =
|
13
|
-
s.
|
14
|
-
s.email = %q{michael@grosser.it}
|
12
|
+
s.date = "2012-01-05"
|
13
|
+
s.email = "michael@grosser.it"
|
15
14
|
s.executables = ["zombie_passenger_killer"]
|
16
15
|
s.files = [
|
17
16
|
"Gemfile",
|
@@ -25,10 +24,10 @@ Gem::Specification.new do |s|
|
|
25
24
|
"spec/zombie_passenger_killer_spec.rb",
|
26
25
|
"zombie_passenger_killer.gemspec"
|
27
26
|
]
|
28
|
-
s.homepage =
|
27
|
+
s.homepage = "http://github.com/grosser/zombie_passenger_killer"
|
29
28
|
s.require_paths = ["lib"]
|
30
|
-
s.rubygems_version =
|
31
|
-
s.summary =
|
29
|
+
s.rubygems_version = "1.8.10"
|
30
|
+
s.summary = "Guaranteed zombie passengers death"
|
32
31
|
|
33
32
|
if s.respond_to? :specification_version then
|
34
33
|
s.specification_version = 3
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zombie_passenger_killer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 29
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 3
|
10
|
+
version: 0.1.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Michael Grosser
|
@@ -15,8 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
19
|
-
default_executable: zombie_passenger_killer
|
18
|
+
date: 2012-01-05 00:00:00 Z
|
20
19
|
dependencies: []
|
21
20
|
|
22
21
|
description:
|
@@ -38,7 +37,6 @@ files:
|
|
38
37
|
- spec/spec_helper.rb
|
39
38
|
- spec/zombie_passenger_killer_spec.rb
|
40
39
|
- zombie_passenger_killer.gemspec
|
41
|
-
has_rdoc: true
|
42
40
|
homepage: http://github.com/grosser/zombie_passenger_killer
|
43
41
|
licenses: []
|
44
42
|
|
@@ -68,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
66
|
requirements: []
|
69
67
|
|
70
68
|
rubyforge_project:
|
71
|
-
rubygems_version: 1.
|
69
|
+
rubygems_version: 1.8.10
|
72
70
|
signing_key:
|
73
71
|
specification_version: 3
|
74
72
|
summary: Guaranteed zombie passengers death
|