sensu-plugins-kafka 0.7.0 → 0.8.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.
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