snipr 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/snipe CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
  require 'ostruct'
3
3
  require 'optparse'
4
4
 
@@ -59,6 +59,11 @@ parser = OptionParser.new do |config|
59
59
  config.on("-d", "--dry-run", desc) do
60
60
  options.no_signals = true
61
61
  end
62
+
63
+ desc = "Use the pkill utility to send the signal (targetted process only)"
64
+ config.on("--pkill", desc) do
65
+ options.pkill = true
66
+ end
62
67
  end.parse!
63
68
 
64
69
  # TODO remove me
@@ -88,6 +93,7 @@ signaller = Snipr::ProcessSignaller.new do |signaller|
88
93
  end
89
94
  signaller.signal options.signal
90
95
  signaller.target_parent options.target_parent
96
+ signaller.pkill if options.pkill
91
97
  signaller.dry_run if options.no_signals
92
98
 
93
99
  if options.bytes
@@ -132,14 +132,51 @@ module Snipr
132
132
  @dry_run = true
133
133
  end
134
134
 
135
+ ##
136
+ # Use pkill to ensure that the process we are attempting to signal
137
+ # matches what we know about it. This only works for the
138
+ # targetted process, not the parent.
139
+ def pkill
140
+ @pkill = which("pkill")
141
+ raise "pkill not found in path or is not executable!" unless @pkill
142
+ end
143
+
144
+ # Determine if a command exists in our PATH
145
+ def which(cmd)
146
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
147
+ location = File.join(path, cmd)
148
+ return location if File.executable?(location)
149
+ end
150
+ return nil
151
+ end
152
+
153
+ ##
154
+ # Ensure that the PID we are attempting to kill still matches what we expect
155
+ # This is not used when pkill option is set
156
+ def cmd_matches?(process)
157
+ process.command == Snipr.exec_cmd("ps -p #{process.pid} -o 'command='").first.strip
158
+ end
159
+
160
+ def ppid_matches?(process)
161
+ Integer(process.ppid) == Integer(Snipr.exec_cmd("ps -p #{process.pid} -o 'ppid='").first.strip)
162
+ end
163
+
164
+ def process_matches?(process)
165
+ cmd_matches?(process) && ppid_matches?(process)
166
+ end
167
+
135
168
  private
136
169
  def signal_process(process)
137
170
  @before_signal.call(@signal, process)
138
171
  unless @dry_run
139
172
  if @target_parent
140
- Process.kill(@signal, process.ppid)
173
+ Process.kill(@signal, process.ppid) if process_matches?(process)
141
174
  else
142
- Process.kill(@signal, process.pid)
175
+ if @pkill
176
+ system("#{@pkill} --signal #{@signal} -P #{process.ppid} -f \"^#{Regexp.escape(process.command)}\"")
177
+ else
178
+ Process.kill(@signal, process.pid) if process_matches?(process)
179
+ end
143
180
  end
144
181
  end
145
182
  @after_signal.call(@signal, process)
@@ -1,3 +1,3 @@
1
1
  module Snipr
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -6,6 +6,7 @@ module Snipr
6
6
  let(:signal) {Signal.list["USR1"]}
7
7
  let(:ps_output) {File.read("spec/ps_output.txt").split("\n")}
8
8
  let(:checkins) {OpenStruct.new}
9
+ let(:pkill) { "/bin/pkill" }
9
10
  subject do
10
11
  ProcessSignaller.new do |signaller|
11
12
  signaller.include /resque/i
@@ -64,13 +65,29 @@ module Snipr
64
65
 
65
66
  context "when process is found" do
66
67
  before do
67
- expect(Snipr).to receive(:exec_cmd).and_return(ps_output).at_least(:once)
68
+ allow(Snipr).to receive(:exec_cmd).and_return(:default)
69
+ expect(Snipr).to receive(:exec_cmd).with("ps h -eo pid,ppid,%mem,%cpu,etime,command").and_return(ps_output).at_least(:once)
68
70
  subject.cpu_greater_than(90)
69
71
  end
70
72
 
71
73
  context "targetting the process itself" do
72
74
  it "should send the appropriate signal to the process and call callbacks" do
73
75
  expect(Process).to receive(:kill).with(signal, 6337)
76
+ expect(Snipr).to receive(:exec_cmd).with(/ps -p \d+ -o/).and_return(
77
+ "resque-1.24.1: Processing foo since 1410189132 [FooJob]",
78
+ "4347 "
79
+ ).twice
80
+ subject.send_signals
81
+ expect(checkins.before_signal).to eq("#{signal} > 6337")
82
+ expect(checkins.after_signal).to eq("#{signal} > 6337")
83
+ end
84
+
85
+ it "should shell out to pkill when --pkill option is set" do
86
+ expect(subject).to receive(:which).and_return(pkill)
87
+ subject.pkill
88
+ expect(subject).to receive(:system).with(
89
+ "#{pkill} --signal #{signal} -P 4347 -f \"^#{Regexp.escape('resque-1.24.1: Processing foo since 1410189132 [FooJob]')}\""
90
+ )
74
91
  subject.send_signals
75
92
  expect(checkins.before_signal).to eq("#{signal} > 6337")
76
93
  expect(checkins.after_signal).to eq("#{signal} > 6337")
@@ -80,6 +97,10 @@ module Snipr
80
97
  context "targetting the parent process" do
81
98
  it "should send the appropriate signal to the parent process and call callbacks" do
82
99
  subject.target_parent true
100
+ expect(Snipr).to receive(:exec_cmd).with(/ps -p \d+ -o/).and_return(
101
+ "resque-1.24.1: Processing foo since 1410189132 [FooJob]",
102
+ "4347 "
103
+ ).twice
83
104
  expect(Process).to receive(:kill).with(signal, 4347)
84
105
  subject.send_signals
85
106
  expect(checkins.before_signal).to eq("#{signal} > 6337")
@@ -89,6 +110,7 @@ module Snipr
89
110
 
90
111
  context "when encountering an error signalling a process" do
91
112
  it "should call the on_error callback" do
113
+ expect(subject).to receive(:process_matches?).and_return(true)
92
114
  expect(Process).to receive(:kill).with(signal, 6337).and_raise('Ouch!')
93
115
  subject.send_signals
94
116
  expect(checkins.on_error).to eq("Ouch! #{signal} > 6337")
@@ -105,6 +127,13 @@ module Snipr
105
127
  end
106
128
  end
107
129
  end
130
+
131
+ context "when --pkill is set but no pkill is found in the path" do
132
+ it "raise an exception" do
133
+ expect(subject).to receive(:which).and_return(nil)
134
+ expect{ subject.pkill }.to raise_error
135
+ end
136
+ end
108
137
  end
109
138
  end
110
139
  end
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snipr
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 0
10
+ version: 1.1.0
5
11
  platform: ruby
6
12
  authors:
7
13
  - Lance Woodson
@@ -9,49 +15,69 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2014-11-25 00:00:00 Z
18
+ date: 2017-10-27 00:00:00 -05:00
19
+ default_executable:
13
20
  dependencies:
14
21
  - !ruby/object:Gem::Dependency
15
- name: bundler
16
- prerelease: false
17
22
  requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
18
24
  requirements:
19
25
  - - ~>
20
26
  - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 1
30
+ - 6
21
31
  version: "1.6"
22
- type: :development
23
32
  version_requirements: *id001
24
- - !ruby/object:Gem::Dependency
25
- name: rake
26
33
  prerelease: false
34
+ type: :development
35
+ name: bundler
36
+ - !ruby/object:Gem::Dependency
27
37
  requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
28
39
  requirements:
29
40
  - - ~>
30
41
  - !ruby/object:Gem::Version
42
+ hash: 35
43
+ segments:
44
+ - 10
45
+ - 0
31
46
  version: "10.0"
32
- type: :development
33
47
  version_requirements: *id002
34
- - !ruby/object:Gem::Dependency
35
- name: rspec
36
48
  prerelease: false
49
+ type: :development
50
+ name: rake
51
+ - !ruby/object:Gem::Dependency
37
52
  requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
38
54
  requirements:
39
55
  - - ~>
40
56
  - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 3
60
+ - 1
61
+ - 0
41
62
  version: 3.1.0
42
- type: :development
43
63
  version_requirements: *id003
44
- - !ruby/object:Gem::Dependency
45
- name: pry
46
64
  prerelease: false
65
+ type: :development
66
+ name: rspec
67
+ - !ruby/object:Gem::Dependency
47
68
  requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
48
70
  requirements:
49
- - &id005
50
- - ">="
71
+ - - ">="
51
72
  - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
52
76
  version: "0"
53
- type: :development
54
77
  version_requirements: *id004
78
+ prerelease: false
79
+ type: :development
80
+ name: pry
55
81
  description: |
56
82
  Ruby classes and executables for targetting and sending signals to
57
83
  *nix processes that match/don't match command name patterns, memory
@@ -84,28 +110,39 @@ files:
84
110
  - spec/process_signaller_spec.rb
85
111
  - spec/ps_output.txt
86
112
  - spec/spec_helper.rb
113
+ has_rdoc: true
87
114
  homepage: ""
88
115
  licenses:
89
116
  - MIT
90
- metadata: {}
91
-
92
117
  post_install_message:
93
118
  rdoc_options: []
94
119
 
95
120
  require_paths:
96
121
  - lib
97
122
  required_ruby_version: !ruby/object:Gem::Requirement
123
+ none: false
98
124
  requirements:
99
- - *id005
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ hash: 3
128
+ segments:
129
+ - 0
130
+ version: "0"
100
131
  required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
101
133
  requirements:
102
- - *id005
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ hash: 3
137
+ segments:
138
+ - 0
139
+ version: "0"
103
140
  requirements: []
104
141
 
105
142
  rubyforge_project:
106
- rubygems_version: 2.0.14
143
+ rubygems_version: 1.6.2
107
144
  signing_key:
108
- specification_version: 4
145
+ specification_version: 3
109
146
  summary: Take aim and fire at runaway processes using ruby
110
147
  test_files:
111
148
  - spec/process_locator_spec.rb
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- data.tar.gz: 0432d5218407fc9b0a154390e2ac486f87e6df94
4
- metadata.gz: d415fed3259b55fbfaa03a951951f79ed5620fde
5
- SHA512:
6
- data.tar.gz: bc24aca5874832df60f75758f0958e96628af0bcedbb2ab7284dff51cadd46cc2af3c31e125f4808b7695cc25ad497e8be9291e71818fc6ddd3162ccda56f3e8
7
- metadata.gz: cc023afbc9374259e1f1923b1c720e8ad6e1729fe9809c3adffb36855a849a76488f99f3e0b97812cfe25ef4a26057a1e03ce927089bd8c309dd2ca76b29bb4b