zombie_passenger_killer 0.1.3 → 0.2.0

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/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source :rubygems
2
+ gemspec
2
3
 
3
- group :dev do # not development <-> would add unneeded development dependencies in gemspec
4
+ group :development do
4
5
  gem 'rake'
5
6
  gem 'rspec', '~>2'
6
- gem 'jeweler'
7
7
  end
data/Gemfile.lock CHANGED
@@ -1,12 +1,12 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ zombie_passenger_killer (0.2.0)
5
+
1
6
  GEM
2
7
  remote: http://rubygems.org/
3
8
  specs:
4
9
  diff-lcs (1.1.3)
5
- git (1.2.5)
6
- jeweler (1.6.4)
7
- bundler (~> 1.0)
8
- git (>= 1.2.5)
9
- rake
10
10
  rake (0.9.2)
11
11
  rspec (2.6.0)
12
12
  rspec-core (~> 2.6.0)
@@ -21,6 +21,6 @@ PLATFORMS
21
21
  ruby
22
22
 
23
23
  DEPENDENCIES
24
- jeweler
25
24
  rake
26
25
  rspec (~> 2)
26
+ zombie_passenger_killer!
data/Rakefile CHANGED
@@ -1,18 +1,21 @@
1
+ require 'bundler/gem_tasks'
2
+
1
3
  task :default do
2
4
  sh "rspec spec/"
3
5
  end
4
6
 
5
- begin
6
- require 'jeweler'
7
- Jeweler::Tasks.new do |gem|
8
- gem.name = 'zombie_passenger_killer'
9
- gem.summary = "Guaranteed zombie passengers death"
10
- gem.email = "michael@grosser.it"
11
- gem.homepage = "http://github.com/grosser/#{gem.name}"
12
- gem.authors = ["Michael Grosser"]
13
- end
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)) }
14
19
 
15
- Jeweler::GemcutterTasks.new
16
- rescue LoadError
17
- puts "Jeweler, or one of its dependencies, is not available. Install it with: gem install jeweler"
20
+ sh "bundle && git add #{file} Gemfile.lock && git commit -m 'bump version to #{new_version}'"
18
21
  end
data/Readme.md CHANGED
@@ -43,14 +43,14 @@ Usage
43
43
 
44
44
  ### God script
45
45
 
46
- TODO
47
-
46
+ # TODO
48
47
 
49
48
  Author
50
49
  ======
51
50
 
52
51
  ###Contributors
53
- - [mindreframer](https://github.com/mindreframer)
52
+ - [Roman Heinrich](https://github.com/mindreframer)
53
+ - [Kevin Mullin](https://github.com/kmullin)
54
54
 
55
55
  [Michael Grosser](http://grosser.it)<br/>
56
56
  michael@grosser.it<br/>
@@ -1,14 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
2
  require 'optparse'
4
3
 
5
4
  $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
6
5
  require 'zombie_passenger_killer'
7
6
 
8
- unless system("which timeout > /dev/null")
9
- warn "Please install timeout commandline tool e.g. via apt-get install timeout / apt-get install coreutils"
10
- end
11
-
12
7
  options = {}
13
8
  OptionParser.new do |opts|
14
9
  opts.banner = <<BANNER
@@ -32,8 +27,9 @@ end.parse!
32
27
  $stdout.sync = true
33
28
  puts "Started at #{Time.now}"
34
29
 
35
- killer = ZombiePassengerKiller.new(options)
30
+ killer = ZombiePassengerKiller::Reaper.new(options)
36
31
 
32
+ $0 = File.basename(__FILE__)
37
33
  loop do
38
34
  killer.hunt_zombies
39
35
  sleep options[:interval] || 10
@@ -0,0 +1,75 @@
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
+ @strace_time = 5
14
+ @out = STDOUT
15
+ end
16
+
17
+ def store_current_cpu(processes)
18
+ keys_to_remove = @history.keys - processes.map{|x| x[:pid] }
19
+ keys_to_remove.each{|k| !@history.delete k }
20
+
21
+ processes.each do |process|
22
+ @history[process[:pid]] ||= []
23
+ @history[process[:pid]] << process[:cpu]
24
+ @history[process[:pid]] = @history[process[:pid]].last(@history_entries)
25
+ end
26
+ end
27
+
28
+ def get_strace(pid, time)
29
+ Process.getpgid(pid) rescue return 'No such process'
30
+ `( strace -p #{pid} 2>&1 ) & sleep #{time} ; kill $! 2>&1`
31
+ end
32
+
33
+ def hunt_zombies
34
+ active_pids_in_passenger_status = passenger_pids
35
+ active_processes_in_processlist = process_status
36
+ zombies = active_processes_in_processlist.map{|x| x[:pid] } - active_pids_in_passenger_status rescue Array.new
37
+
38
+ # kill processes with high CPU if user wants it
39
+ high_load = if @max_high_cpu
40
+ store_current_cpu active_processes_in_processlist
41
+ active_pids_in_passenger_status.select do |pid|
42
+ @history[pid].count{|x| x > @high_cpu } >= @max_high_cpu
43
+ end
44
+ else
45
+ []
46
+ end
47
+
48
+ (high_load + zombies).each do |pid|
49
+ kill_zombie pid
50
+ end
51
+ end
52
+
53
+ # return array of pids reported from passenger-status command, nil if passenger-status doesn't run
54
+ def passenger_pids
55
+ pids = %x(passenger-status|grep PID).split("\n").map { |x| x.strip.match(/PID: \d*/).to_s.split[1].to_i }
56
+ pids if $?.exitstatus.zero?
57
+ end
58
+
59
+ def process_status
60
+ %x(ps -eo pid,pcpu,args|grep -v grep|grep '#{@pattern}').split("\n").map do |line|
61
+ values = line.strip.split[0..1]
62
+ {:pid => values.first.to_i, :cpu => values.last.to_f}
63
+ end
64
+ end
65
+
66
+ def kill_zombie(pid)
67
+ @out.puts "Killing passenger process #{pid}"
68
+ @out.puts get_strace(pid, @strace_time)
69
+ Process.kill('TERM', pid) rescue nil
70
+ sleep @grace_time # wait for it to die
71
+ Process.kill('KILL', pid) rescue nil
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,3 @@
1
+ module ZombiePassengerKiller
2
+ Version = VERSION = '0.2.0'
3
+ end
@@ -1,71 +1,4 @@
1
- class ZombiePassengerKiller
2
- VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
3
2
 
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
- @strace_time = 5
14
- @out = STDOUT
15
- end
16
-
17
- def store_current_cpu(processes)
18
- keys_to_remove = @history.keys - processes.map{|x| x[:pid] }
19
- keys_to_remove.each{|k| !@history.delete k }
20
-
21
- processes.each do |process|
22
- @history[process[:pid]] ||= []
23
- @history[process[:pid]] << process[:cpu]
24
- @history[process[:pid]] = @history[process[:pid]].last(@history_entries)
25
- end
26
- end
27
-
28
- def get_strace(pid, time)
29
- Process.getpgid(pid) rescue return 'No such process'
30
- `( strace -p #{pid} 2>&1 ) & sleep #{time} ; kill $! 2>&1`
31
- end
32
-
33
- def hunt_zombies
34
- active_pids_in_passenger_status = passenger_pids
35
- active_processes_in_processlist = process_status
36
- zombies = active_processes_in_processlist.map{|x| x[:pid] } - active_pids_in_passenger_status
37
-
38
- # kill processes with high CPU if user wants it
39
- high_load = if @max_high_cpu
40
- store_current_cpu active_processes_in_processlist
41
- active_pids_in_passenger_status.select do |pid|
42
- @history[pid].count{|x| x > @high_cpu } >= @max_high_cpu
43
- end
44
- else
45
- []
46
- end
47
-
48
- (high_load + zombies).each do |pid|
49
- kill_zombie pid
50
- end
51
- end
52
-
53
- def passenger_pids
54
- %x(passenger-status|grep PID).split("\n").map{|x| x.strip.match(/PID: \d*/).to_s.split[1]}.map(&:to_i)
55
- end
56
-
57
- def process_status
58
- %x(ps -eo pid,pcpu,args|grep -v grep|grep '#{@pattern}').split("\n").map do |line|
59
- values = line.strip.split[0..1]
60
- {:pid => values.first.to_i, :cpu => values.last.to_f}
61
- end
62
- end
63
-
64
- def kill_zombie(pid)
65
- @out.puts "Killing passenger process #{pid}"
66
- @out.puts get_strace(pid, @strace_time)
67
- Process.kill('TERM', pid) rescue nil
68
- sleep @grace_time
69
- Process.kill('KILL', pid) rescue nil
70
- end
71
- end
3
+ require 'zombie_passenger_killer/version'
4
+ require 'zombie_passenger_killer/reaper'
@@ -2,7 +2,7 @@ require File.expand_path('spec/spec_helper')
2
2
 
3
3
  describe ZombiePassengerKiller do
4
4
  let(:killer){
5
- k = ZombiePassengerKiller.new(@options || {})
5
+ k = ZombiePassengerKiller::Reaper.new(@options || {})
6
6
  k.stub!(:passenger_pids).and_return([111])
7
7
  k
8
8
  }
@@ -1,41 +1,12 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
1
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
+ require 'zombie_passenger_killer/version'
5
3
 
6
- Gem::Specification.new do |s|
7
- s.name = "zombie_passenger_killer"
8
- s.version = "0.1.3"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
+ Gem::Specification.new "zombie_passenger_killer", ZombiePassengerKiller::VERSION do |s|
5
+ s.summary = "Guaranteed zombie passengers death"
11
6
  s.authors = ["Michael Grosser"]
12
- s.date = "2012-01-05"
13
7
  s.email = "michael@grosser.it"
14
- s.executables = ["zombie_passenger_killer"]
15
- s.files = [
16
- "Gemfile",
17
- "Gemfile.lock",
18
- "Rakefile",
19
- "Readme.md",
20
- "VERSION",
21
- "bin/zombie_passenger_killer",
22
- "lib/zombie_passenger_killer.rb",
23
- "spec/spec_helper.rb",
24
- "spec/zombie_passenger_killer_spec.rb",
25
- "zombie_passenger_killer.gemspec"
26
- ]
27
8
  s.homepage = "http://github.com/grosser/zombie_passenger_killer"
28
- s.require_paths = ["lib"]
29
- s.rubygems_version = "1.8.10"
30
- s.summary = "Guaranteed zombie passengers death"
31
-
32
- if s.respond_to? :specification_version then
33
- s.specification_version = 3
34
-
35
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
36
- else
37
- end
38
- else
39
- end
9
+ s.files = `git ls-files`.split("\n")
10
+ s.executables = ["zombie_passenger_killer"]
11
+ s.license = "MIT"
40
12
  end
41
-
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: 29
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 3
10
- version: 0.1.3
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Michael Grosser
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-05 00:00:00 Z
18
+ date: 2012-02-09 00:00:00 Z
19
19
  dependencies: []
20
20
 
21
21
  description:
@@ -31,15 +31,16 @@ files:
31
31
  - Gemfile.lock
32
32
  - Rakefile
33
33
  - Readme.md
34
- - VERSION
35
34
  - bin/zombie_passenger_killer
36
35
  - lib/zombie_passenger_killer.rb
36
+ - lib/zombie_passenger_killer/reaper.rb
37
+ - lib/zombie_passenger_killer/version.rb
37
38
  - spec/spec_helper.rb
38
39
  - spec/zombie_passenger_killer_spec.rb
39
40
  - zombie_passenger_killer.gemspec
40
41
  homepage: http://github.com/grosser/zombie_passenger_killer
41
- licenses: []
42
-
42
+ licenses:
43
+ - MIT
43
44
  post_install_message:
44
45
  rdoc_options: []
45
46
 
@@ -66,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
67
  requirements: []
67
68
 
68
69
  rubyforge_project:
69
- rubygems_version: 1.8.10
70
+ rubygems_version: 1.8.15
70
71
  signing_key:
71
72
  specification_version: 3
72
73
  summary: Guaranteed zombie passengers death
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.1.3