sensu-plugins-kafka 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 796bfc7ffe11da8fce4d84d3819ef91b228dada6
4
- data.tar.gz: 80257076434b73e0089246be7a4ab2b5eb349db7
3
+ metadata.gz: 436d7165e1ea6b4ac7efe7ee7c4456619cb0d7bd
4
+ data.tar.gz: 1b33367a5fb5e418031ab76b3f1f571c660d4e69
5
5
  SHA512:
6
- metadata.gz: 624ce35280ff74093e0d0c917eb2db44c12d0f5e1b59011b1ec9fd79abe76938e2cb5bbcdff41c896afe3ab0eb30a3d5335087305d46fef4e854b9a2ae8c74d2
7
- data.tar.gz: 4ef0d7001c151f80a77fe68296e1419181ba7739366c49ad2f3c63f3f7b93183840ac406bf295ab8789a28ae84a2884ce526b784e5d8c1991fae665104a9f36b
6
+ metadata.gz: e0386a39236c73b7b192bd287e1cd00c7a02da9ef8af2e79f26315d49a3dc005e87ddc77f66e19bbc47fb0f96b519963001fa574e496758ef3df92a35ab8825c
7
+ data.tar.gz: b500208baf3951334ef8a055feb277cb27aa261f7f1ea0b21f4618ba86ba5ad0287abad5b462f00ad61c963c22defb08d7278ab15e2d28c78b2519b21c6e5ea6
data/CHANGELOG.md CHANGED
@@ -10,6 +10,11 @@ This CHANGELOG follows the format listed at [Keep A Changelog](http://keepachang
10
10
  ### Fixed
11
11
  ### Changed
12
12
 
13
+ ## [Unreleased]
14
+
15
+ ### Breaking Changes
16
+ - Refactoring check-consumer-lag to remove shell/jvm execution
17
+
13
18
  ## [0.7.0]
14
19
 
15
20
  ### Added
@@ -27,6 +27,17 @@
27
27
 
28
28
  require 'sensu-plugin/check/cli'
29
29
 
30
+ require 'celluloid-io'
31
+ require 'json'
32
+ require 'poseidon'
33
+ require 'zookeeper'
34
+
35
+ module Poseidon
36
+ class Connection
37
+ TCPSocket = Celluloid::IO::TCPSocket
38
+ end
39
+ end
40
+
30
41
  class ConsumerLagCheck < Sensu::Plugin::Check::CLI
31
42
  option :group,
32
43
  description: 'Consumer group',
@@ -34,25 +45,6 @@ class ConsumerLagCheck < Sensu::Plugin::Check::CLI
34
45
  long: '--group NAME',
35
46
  required: true
36
47
 
37
- option :kafka_home,
38
- description: 'Kafka home',
39
- short: '-k NAME',
40
- long: '--kafka-home NAME',
41
- default: '/opt/kafka'
42
-
43
- option :topic_excludes,
44
- description: 'Excludes consumer topics',
45
- short: '-e NAME',
46
- long: '--topic-excludes NAME',
47
- proc: proc { |a| a.split(',') }
48
-
49
- option :autolist,
50
- description: 'Auto list topics',
51
- short: '-a VALUE',
52
- long: '--auto-list VALUE',
53
- boolean: true,
54
- default: true
55
-
56
48
  option :zookeeper,
57
49
  description: 'ZooKeeper connect string',
58
50
  short: '-z NAME',
@@ -60,85 +52,85 @@ class ConsumerLagCheck < Sensu::Plugin::Check::CLI
60
52
  default: 'localhost:2181'
61
53
 
62
54
  option :warning_over,
63
- description: 'Warning if metric statistics is over specified value.',
55
+ description: 'Warning if lag is over specified value.',
64
56
  short: '-W N',
65
57
  long: '--warning-over N'
66
58
 
67
59
  option :critical_over,
68
- description: 'Critical if metric statistics is over specified value.',
60
+ description: 'Critical if lag is over specified value.',
69
61
  short: '-C N',
70
62
  long: '--critical-over N'
71
63
 
72
64
  option :warning_under,
73
- description: 'Warning if metric statistics is under specified value.',
65
+ description: 'Warning if lag is under specified value.',
74
66
  short: '-w N',
75
67
  long: '--warning-under N'
76
68
 
77
69
  option :critical_under,
78
- description: 'Critical if metric statistics is under specified value.',
70
+ description: 'Critical if lag is under specified value.',
79
71
  short: '-c N',
80
72
  long: '--critical-under N'
81
73
 
82
- # read the output of a command
83
- # @param cmd [String] the command to read the output from
84
- def read_lines(cmd)
85
- IO.popen(cmd + ' 2>&1') do |child|
86
- child.read.split("\n")
87
- end
74
+ def kafka_topics(zk, group)
75
+ zk.get_children(path: "/consumers/#{group}/owners")[:children].sort
88
76
  end
89
77
 
90
- # create a hash from the output of each line of a command
91
- # @param line [String]
92
- # @param cols
93
- def line_to_hash(line, *cols)
94
- Hash[cols.zip(line.strip.split(/\s+/, cols.size))]
78
+ def topics_partitions(zk, topic)
79
+ JSON.parse(zk.get(path: "/brokers/topics/#{topic}")[:data])['partitions'].keys.map(&:to_i).sort
95
80
  end
96
81
 
97
- # run command and return a hash from the output
98
- # @param cmd [String]
99
- def run_offset(cmd)
100
- read_lines(cmd).drop(1).map do |line|
101
- line_to_hash(line, :group, :topic, :pid, :offset, :logsize, :lag, :owner)
102
- end
82
+ def leader_broker(zk, topic, partition)
83
+ state = zk.get(path: "/brokers/topics/#{topic}/partitions/#{partition}/state")
84
+ leader = JSON.parse(state[:data])['leader']
85
+ JSON.parse(zk.get(path: "/brokers/ids/#{leader}")[:data])
103
86
  end
104
87
 
105
- # run command and return a hash from the output
106
- # @param cmd [String]
107
- def run_topics(cmd)
108
- topics = []
109
- read_lines(cmd).map do |line|
110
- if !line.include?('__consumer_offsets') && !line.include?('marked for deletion')
111
- topics.push(line)
112
- end
113
- end
114
- topics
88
+ def consumer_offset(zk, group, topic, partition)
89
+ zk.get(path: "/consumers/#{group}/offsets/#{topic}/#{partition}")[:data].to_i
90
+ end
91
+
92
+ def partition_owner(zk, group, partition)
93
+ zk.get(path: "/consumers/#{group}/offsets/#{owners}/#{partition}")[:data]
115
94
  end
116
95
 
117
96
  def run
118
- kafka_run_class = "#{config[:kafka_home]}/bin/kafka-run-class.sh"
119
- unknown "Can not find #{kafka_run_class}" unless File.exist?(kafka_run_class)
120
-
121
- topics_to_read = []
122
- if config[:autolist].to_s == 'true'
123
- cmd_topics = "#{kafka_run_class} kafka.admin.TopicCommand --zookeeper #{config[:zookeeper]} --list"
124
- topics_to_read = run_topics(cmd_topics)
125
- topics_to_read.delete_if { |x| config[:topic_excludes].include?(x) } if config[:topic_excludes]
126
- end
97
+ z = Zookeeper.new(config[:zookeeper])
98
+
99
+ group = config[:group]
100
+ topics = kafka_topics(z, group)
101
+
102
+ critical 'Could not found topics' if topics.empty?
103
+
104
+ consumers = {}
105
+ topics.each do |topic|
106
+ consumers[topic] = {}
127
107
 
128
- cmd_offset = "#{kafka_run_class} kafka.tools.ConsumerOffsetChecker --group #{config[:group]} --zookeeper #{config[:zookeeper]}"
129
- cmd_offset += " --topic #{topics_to_read.join(',')}" unless topics_to_read.empty?
108
+ topics_partitions(z, topic).each do |partition|
109
+ owner = partition_owner(z, group, partition)
130
110
 
131
- topics = run_offset(cmd_offset).group_by { |h| h[:topic] }
111
+ leader = leader_broker(z, topic, partition)
112
+ consumer = Poseidon::PartitionConsumer.new('CheckConsumerLag', leader['host'], leader['port'], topic, partition, :latest_offset)
113
+ logsize = consumer.next_offset
132
114
 
133
- critical 'Could not found topics/partitions' if topics.empty?
115
+ offset = consumer_offset(z, group, topic, partition)
116
+
117
+ lag = logsize - offset
118
+
119
+ consumers[topic][:partition] = partition
120
+ consumers[topic][:logsize] = logsize
121
+ consumers[topic][:offset] = offset
122
+ consumers[topic][:lag] = lag
123
+ consumers[topic][:owner] = owner
124
+ end
125
+ end
134
126
 
135
127
  [:offset, :logsize, :lag].each do |field|
136
- topics.map do |k, v|
128
+ consumers.map do |k, v|
137
129
  critical "Topic #{k} has partitions with #{field} < 0" unless v.select { |w| w[field].to_i < 0 }.empty?
138
130
  end
139
131
  end
140
132
 
141
- topics.map do |k, v|
133
+ consumers.map do |k, v|
142
134
  critical "Topic #{k} has partitions with no owner" unless v.select { |w| w[:owner] == 'none' }.empty?
143
135
  end
144
136
 
@@ -165,7 +157,7 @@ class ConsumerLagCheck < Sensu::Plugin::Check::CLI
165
157
  end
166
158
  when :under
167
159
  if min_lag < threshold.to_i
168
- msg = "Topics `#{min_topics}` for the group `#{config[:group]}` lag: #{min_lag} (<= #{threshold})"
160
+ msg = "Topics `#{min_topics}` for the group `#{config[:group]}` lag: #{min_lag} (<= #{threshold})"
169
161
  send severity, msg
170
162
  end
171
163
  end
@@ -5,7 +5,7 @@ module SensuPluginsKafka
5
5
  # This defines the version of the gem
6
6
  module Version
7
7
  MAJOR = 0
8
- MINOR = 7
8
+ MINOR = 8
9
9
  PATCH = 0
10
10
 
11
11
  VER_STRING = [MAJOR, MINOR, PATCH].compact.join('.')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sensu-plugins-kafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sensu-Plugins and contributors
@@ -9,8 +9,36 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain:
11
11
  - certs/sensu-plugins.pem
12
- date: 2017-02-10 00:00:00.000000000 Z
12
+ date: 2017-02-15 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: celluloid-io
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.17.3
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.17.3
28
+ - !ruby/object:Gem::Dependency
29
+ name: poseidon
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '='
33
+ - !ruby/object:Gem::Version
34
+ version: 0.0.5
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '='
40
+ - !ruby/object:Gem::Version
41
+ version: 0.0.5
14
42
  - !ruby/object:Gem::Dependency
15
43
  name: sensu-plugin
16
44
  requirement: !ruby/object:Gem::Requirement