zombie_passenger_killer 0.1.2 → 0.1.3
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/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
|