riemann-client 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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/ci.yml +19 -5
- data/.github/workflows/codeql-analysis.yml +72 -0
- data/.rspec +2 -0
- data/.rubocop.yml +20 -0
- data/CHANGELOG.md +28 -2
- data/Gemfile +2 -0
- data/README.markdown +19 -10
- data/Rakefile +5 -2
- data/SECURITY.md +42 -0
- data/lib/riemann/attribute.rb +2 -0
- data/lib/riemann/auto_state.rb +9 -3
- data/lib/riemann/client/ssl_socket.rb +22 -21
- data/lib/riemann/client/tcp.rb +38 -38
- data/lib/riemann/client/tcp_socket.rb +66 -51
- data/lib/riemann/client/udp.rb +8 -4
- data/lib/riemann/client.rb +97 -78
- data/lib/riemann/event.rb +45 -51
- data/lib/riemann/message.rb +2 -0
- data/lib/riemann/metric_thread.rb +59 -54
- data/lib/riemann/query.rb +4 -2
- data/lib/riemann/state.rb +8 -8
- data/lib/riemann/version.rb +3 -1
- data/lib/riemann.rb +2 -3
- data/riemann-client.gemspec +10 -5
- data/spec/client_spec.rb +66 -0
- data/spec/shared_examples.rb +531 -0
- data/spec/spec_helper.rb +38 -0
- metadata +47 -10
- data/spec/client.rb +0 -384
data/lib/riemann/event.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Riemann
|
2
4
|
class Event
|
3
5
|
require 'set'
|
4
6
|
include Beefcake::Message
|
5
7
|
|
6
8
|
optional :time, :int64, 1
|
7
|
-
optional :state, :string,
|
9
|
+
optional :state, :string, 2
|
8
10
|
optional :service, :string, 3
|
9
11
|
optional :host, :string, 4
|
10
12
|
optional :description, :string, 5
|
@@ -22,9 +24,9 @@ module Riemann
|
|
22
24
|
VIRTUAL_FIELDS = Set.new([:metric])
|
23
25
|
# Fields which are specially encoded in the Event protobuf--that is, they
|
24
26
|
# can't be used as attributes.
|
25
|
-
RESERVED_FIELDS = fields.map do |
|
27
|
+
RESERVED_FIELDS = fields.map do |_i, field|
|
26
28
|
field.name.to_sym
|
27
|
-
end.reduce(VIRTUAL_FIELDS) do |set, field|
|
29
|
+
end.reduce(VIRTUAL_FIELDS) do |set, field| # rubocop:disable Style/MultilineBlockChain
|
28
30
|
set << field
|
29
31
|
end
|
30
32
|
|
@@ -44,12 +46,10 @@ module Riemann
|
|
44
46
|
end
|
45
47
|
|
46
48
|
# Metric
|
47
|
-
init.metric_f ||= states.inject(0.0)
|
48
|
-
|
49
|
-
|
50
|
-
if init.metric_f.nan?
|
51
|
-
init.metric_f = 0.0
|
52
|
-
end
|
49
|
+
init.metric_f ||= states.inject(0.0) do |a, state|
|
50
|
+
a + (state.metric || 0)
|
51
|
+
end / states.size
|
52
|
+
init.metric_f = 0.0 if init.metric_f.nan?
|
53
53
|
|
54
54
|
# Event
|
55
55
|
init.state ||= mode states.map(&:state)
|
@@ -59,7 +59,8 @@ module Riemann
|
|
59
59
|
init.time_micros = begin
|
60
60
|
times = states.map(&:time_micros).compact
|
61
61
|
(times.inject(:+) / times.size).to_i
|
62
|
-
rescue
|
62
|
+
rescue ZeroDivisionError
|
63
|
+
nil
|
63
64
|
end
|
64
65
|
init.time_micros ||= now
|
65
66
|
|
@@ -78,12 +79,10 @@ module Riemann
|
|
78
79
|
end
|
79
80
|
|
80
81
|
# Metric
|
81
|
-
init.metric_f ||= states.inject(0.0)
|
82
|
-
|
83
|
-
}
|
84
|
-
if init.metric_f.nan?
|
85
|
-
init.metric_f = 0.0
|
82
|
+
init.metric_f ||= states.inject(0.0) do |a, state|
|
83
|
+
a + (state.metric || 0)
|
86
84
|
end
|
85
|
+
init.metric_f = 0.0 if init.metric_f.nan?
|
87
86
|
|
88
87
|
# Event
|
89
88
|
init.state ||= mode states.map(&:state)
|
@@ -93,7 +92,8 @@ module Riemann
|
|
93
92
|
init.time_micros = begin
|
94
93
|
times = states.map(&:time_micros).compact
|
95
94
|
(times.inject(:+) / times.size).to_i
|
96
|
-
rescue
|
95
|
+
rescue ZeroDivisionError
|
96
|
+
nil
|
97
97
|
end
|
98
98
|
init.time_micros ||= now
|
99
99
|
|
@@ -111,12 +111,10 @@ module Riemann
|
|
111
111
|
end
|
112
112
|
|
113
113
|
# Metric
|
114
|
-
init.metric_f ||= states.inject(0.0)
|
115
|
-
|
116
|
-
}
|
117
|
-
if init.metric.nan?
|
118
|
-
init.metric = 0.0
|
114
|
+
init.metric_f ||= states.inject(0.0) do |a, state|
|
115
|
+
a + (state.metric || 0)
|
119
116
|
end
|
117
|
+
init.metric = 0.0 if init.metric.nan?
|
120
118
|
|
121
119
|
# Event
|
122
120
|
init.state ||= states.inject(nil) do |max, state|
|
@@ -127,7 +125,8 @@ module Riemann
|
|
127
125
|
init.time_micros = begin
|
128
126
|
times = states.map(&:time_micros).compact
|
129
127
|
(times.inject(:+) / times.size).to_i
|
130
|
-
rescue
|
128
|
+
rescue ZeroDivisionError
|
129
|
+
nil
|
131
130
|
end
|
132
131
|
init.time_micros ||= now
|
133
132
|
|
@@ -135,23 +134,23 @@ module Riemann
|
|
135
134
|
end
|
136
135
|
|
137
136
|
def self.mode(array)
|
138
|
-
array.
|
137
|
+
array.each_with_object(Hash.new(0)) do |e, counts|
|
139
138
|
counts[e] += 1
|
140
|
-
|
141
|
-
|
139
|
+
end.max_by { |_e, count| count }.first # rubocop:disable Style/MultilineBlockChain
|
140
|
+
rescue StandardError
|
141
|
+
nil
|
142
142
|
end
|
143
143
|
|
144
144
|
# Partition a list of states by a field
|
145
145
|
# Returns a hash of field_value => state
|
146
146
|
def self.partition(states, field)
|
147
|
-
states.
|
147
|
+
states.each_with_object({}) do |state, p|
|
148
148
|
k = state.send field
|
149
149
|
if p.include? k
|
150
150
|
p[k] << state
|
151
151
|
else
|
152
152
|
p[k] = [state]
|
153
153
|
end
|
154
|
-
p
|
155
154
|
end
|
156
155
|
end
|
157
156
|
|
@@ -172,19 +171,15 @@ module Riemann
|
|
172
171
|
|
173
172
|
def initialize(hash = nil)
|
174
173
|
if hash
|
175
|
-
|
176
|
-
|
177
|
-
self.metric = hash[:metric]
|
178
|
-
else
|
179
|
-
super hash
|
180
|
-
end
|
174
|
+
super hash
|
175
|
+
self.metric = hash[:metric] if hash[:metric]
|
181
176
|
|
182
177
|
# Add extra attributes to the event as Attribute instances with values
|
183
178
|
# converted to String
|
184
|
-
self.attributes = hash.map do |key,
|
179
|
+
self.attributes = hash.map do |key, _value|
|
185
180
|
unless RESERVED_FIELDS.include? key.to_sym
|
186
|
-
Attribute.new(:
|
187
|
-
:
|
181
|
+
Attribute.new(key: key.to_s,
|
182
|
+
value: (hash[key] || hash[key.to_sym]).to_s)
|
188
183
|
end
|
189
184
|
end.compact
|
190
185
|
else
|
@@ -200,36 +195,35 @@ module Riemann
|
|
200
195
|
metric_f
|
201
196
|
end
|
202
197
|
|
203
|
-
def metric=(
|
204
|
-
if Integer
|
198
|
+
def metric=(value)
|
199
|
+
if value.is_a?(Integer) && (-(2**63)...2**63).include?(value)
|
205
200
|
# Long
|
206
|
-
self.metric_sint64 =
|
207
|
-
self.metric_f = m.to_f
|
201
|
+
self.metric_sint64 = value
|
208
202
|
else
|
209
|
-
self.metric_d =
|
210
|
-
self.metric_f = m.to_f
|
203
|
+
self.metric_d = value.to_f
|
211
204
|
end
|
205
|
+
self.metric_f = value.to_f
|
212
206
|
end
|
213
207
|
|
214
208
|
# Look up attributes
|
215
|
-
def [](
|
216
|
-
if RESERVED_FIELDS.include?
|
209
|
+
def [](key)
|
210
|
+
if RESERVED_FIELDS.include? key.to_sym
|
217
211
|
super
|
218
212
|
else
|
219
|
-
|
213
|
+
attributes.find { |a| a.key.to_s == key.to_s }.value
|
220
214
|
end
|
221
215
|
end
|
222
216
|
|
223
217
|
# Set attributes
|
224
|
-
def []=(
|
225
|
-
if RESERVED_FIELDS.include?
|
218
|
+
def []=(key, value)
|
219
|
+
if RESERVED_FIELDS.include? key.to_sym
|
226
220
|
super
|
227
221
|
else
|
228
|
-
|
229
|
-
if
|
230
|
-
|
222
|
+
attr = attributes.find { |a| a.key == key.to_s }
|
223
|
+
if attr
|
224
|
+
attr.value = value.to_s
|
231
225
|
else
|
232
|
-
|
226
|
+
attributes << Attribute.new(key: key.to_s, value: value.to_s)
|
233
227
|
end
|
234
228
|
end
|
235
229
|
end
|
data/lib/riemann/message.rb
CHANGED
@@ -1,68 +1,73 @@
|
|
1
|
-
|
2
|
-
# A metric thread is simple: it wraps some metric object which responds to <<,
|
3
|
-
# and every interval seconds, calls #flush which replaces the object and calls
|
4
|
-
# a user specified function.
|
5
|
-
|
6
|
-
INTERVAL = 10
|
7
|
-
|
8
|
-
attr_accessor :interval
|
9
|
-
attr_accessor :metric
|
1
|
+
# frozen_string_literal: true
|
10
2
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# loop do
|
17
|
-
# sleep rand
|
18
|
-
# m << rand
|
19
|
-
# end
|
20
|
-
def initialize(klass, *klass_args, &f)
|
21
|
-
@klass = klass
|
22
|
-
@klass_args = klass_args
|
23
|
-
@f = f
|
24
|
-
@interval = INTERVAL
|
3
|
+
module Riemann
|
4
|
+
class MetricThread
|
5
|
+
# A metric thread is simple: it wraps some metric object which responds to <<,
|
6
|
+
# and every interval seconds, calls #flush which replaces the object and calls
|
7
|
+
# a user specified function.
|
25
8
|
|
26
|
-
|
9
|
+
INTERVAL = 10
|
27
10
|
|
28
|
-
|
29
|
-
end
|
11
|
+
attr_accessor :interval, :metric
|
30
12
|
|
31
|
-
|
32
|
-
|
33
|
-
|
13
|
+
# client = Riemann::Client.new
|
14
|
+
# m = MetricThread.new Mtrc::Rate do |rate|
|
15
|
+
# client << rate
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# loop do
|
19
|
+
# sleep rand
|
20
|
+
# m << rand
|
21
|
+
# end
|
22
|
+
def initialize(klass, *klass_args, &block)
|
23
|
+
@klass = klass
|
24
|
+
@klass_args = klass_args
|
25
|
+
@block = block
|
26
|
+
@interval = INTERVAL
|
34
27
|
|
35
|
-
|
36
|
-
@klass.new *@klass_args
|
37
|
-
end
|
28
|
+
@metric = new_metric
|
38
29
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
30
|
+
start
|
31
|
+
end
|
32
|
+
|
33
|
+
def <<(value)
|
34
|
+
@metric.<<(value)
|
35
|
+
end
|
36
|
+
|
37
|
+
def new_metric
|
38
|
+
@klass.new(*@klass_args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def flush
|
42
|
+
old = @metric
|
43
|
+
@metric = new_metric
|
44
|
+
@block[old]
|
45
|
+
end
|
46
|
+
|
47
|
+
def start
|
48
|
+
raise 'already running' if @runner
|
43
49
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
rescue Exception => e
|
50
|
+
@running = true
|
51
|
+
@runner = Thread.new do
|
52
|
+
while @running
|
53
|
+
sleep @interval
|
54
|
+
begin
|
55
|
+
flush
|
56
|
+
rescue StandardError
|
57
|
+
# ignore
|
58
|
+
end
|
54
59
|
end
|
60
|
+
@runner = nil
|
55
61
|
end
|
56
|
-
@runner = nil
|
57
62
|
end
|
58
|
-
end
|
59
63
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
+
def stop
|
65
|
+
stop!
|
66
|
+
@runner.join
|
67
|
+
end
|
64
68
|
|
65
|
-
|
66
|
-
|
69
|
+
def stop!
|
70
|
+
@running = false
|
71
|
+
end
|
67
72
|
end
|
68
73
|
end
|
data/lib/riemann/query.rb
CHANGED
data/lib/riemann/state.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Riemann
|
2
4
|
class State
|
3
5
|
include Beefcake::Message
|
4
|
-
|
5
|
-
optional :time, :int64, 1
|
6
|
-
optional :state, :string,
|
6
|
+
|
7
|
+
optional :time, :int64, 1
|
8
|
+
optional :state, :string, 2
|
7
9
|
optional :service, :string, 3
|
8
10
|
optional :host, :string, 4
|
9
11
|
optional :description, :string, 5
|
@@ -12,8 +14,8 @@ module Riemann
|
|
12
14
|
optional :ttl, :float, 8
|
13
15
|
optional :metric_f, :float, 15
|
14
16
|
|
15
|
-
def initialize
|
16
|
-
super
|
17
|
+
def initialize
|
18
|
+
super
|
17
19
|
|
18
20
|
@time ||= Time.now.to_i
|
19
21
|
end
|
@@ -22,8 +24,6 @@ module Riemann
|
|
22
24
|
@metric || metric_f
|
23
25
|
end
|
24
26
|
|
25
|
-
|
26
|
-
@metric = m
|
27
|
-
end
|
27
|
+
attr_writer :metric
|
28
28
|
end
|
29
29
|
end
|
data/lib/riemann/version.rb
CHANGED
data/lib/riemann.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
3
|
+
module Riemann
|
4
4
|
require 'rubygems'
|
5
5
|
require 'beefcake'
|
6
6
|
require 'timeout'
|
@@ -10,5 +10,4 @@ module Riemann
|
|
10
10
|
require 'riemann/event'
|
11
11
|
require 'riemann/query'
|
12
12
|
require 'riemann/message'
|
13
|
-
require 'riemann/client'
|
14
13
|
end
|
data/riemann-client.gemspec
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
|
5
|
+
lib = File.expand_path('lib', __dir__)
|
3
6
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
7
|
require 'riemann/version'
|
5
8
|
|
@@ -14,15 +17,17 @@ Gem::Specification.new do |spec|
|
|
14
17
|
spec.license = 'MIT'
|
15
18
|
spec.platform = Gem::Platform::RUBY
|
16
19
|
|
17
|
-
spec.files = `git ls-files`.split(
|
20
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
18
21
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
23
|
spec.require_paths = ['lib']
|
21
24
|
|
22
|
-
spec.required_ruby_version = '>= 2.
|
25
|
+
spec.required_ruby_version = '>= 2.6.0'
|
23
26
|
|
24
27
|
spec.add_development_dependency 'bundler', '>= 1.3'
|
25
|
-
spec.add_development_dependency '
|
28
|
+
spec.add_development_dependency 'rspec'
|
29
|
+
spec.add_development_dependency 'rubocop'
|
30
|
+
spec.add_development_dependency 'rubocop-rspec'
|
26
31
|
spec.add_development_dependency 'timecop'
|
27
32
|
|
28
33
|
spec.add_dependency 'beefcake', ['>= 1.0.0 ']
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'riemann'
|
4
|
+
require 'riemann/client'
|
5
|
+
|
6
|
+
require 'spec_helper'
|
7
|
+
require 'shared_examples'
|
8
|
+
|
9
|
+
RSpec.describe 'Riemann::Client' do
|
10
|
+
let(:client) do
|
11
|
+
Riemann::Client.new(host: 'localhost', port: 5555)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:expected_rate) { 100 }
|
15
|
+
|
16
|
+
context('with TLS transport') do
|
17
|
+
let(:client) do
|
18
|
+
Riemann::Client.new(host: 'localhost', port: 5554, ssl: true,
|
19
|
+
key_file: '/etc/riemann/riemann_server.pkcs8',
|
20
|
+
cert_file: '/etc/riemann/riemann_server.crt',
|
21
|
+
ca_file: '/etc/riemann/riemann_server.crt',
|
22
|
+
ssl_verify: true)
|
23
|
+
end
|
24
|
+
let(:client_with_transport) { client.tcp }
|
25
|
+
|
26
|
+
it_behaves_like 'a riemann client'
|
27
|
+
it_behaves_like 'a riemann client that acknowledge messages'
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with TCP transport' do
|
31
|
+
let(:client_with_transport) { client.tcp }
|
32
|
+
|
33
|
+
it_behaves_like 'a riemann client'
|
34
|
+
it_behaves_like 'a riemann client that acknowledge messages'
|
35
|
+
end
|
36
|
+
|
37
|
+
context('with UDP transport') do
|
38
|
+
let(:client_with_transport) { client.udp }
|
39
|
+
let(:expected_rate) { 1000 }
|
40
|
+
|
41
|
+
it_behaves_like 'a riemann client'
|
42
|
+
it_behaves_like 'a riemann client that does not acknowledge messages'
|
43
|
+
|
44
|
+
context 'when sending a message too large for UDP transport' do
|
45
|
+
let(:large_message) do
|
46
|
+
{
|
47
|
+
data: 'X' * (Riemann::Client::UDP::MAX_SIZE + 10)
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
before do
|
52
|
+
allow(client.udp).to receive(:send_maybe_recv).and_call_original
|
53
|
+
allow(client.tcp).to receive(:send_maybe_recv).and_call_original
|
54
|
+
client << large_message
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'has tried to send the message using UDP' do
|
58
|
+
expect(client.udp).to have_received(:send_maybe_recv)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'has retried to send the message using TCP' do
|
62
|
+
expect(client.tcp).to have_received(:send_maybe_recv)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|