snipr 1.0.0 → 1.1.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/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