riemann-client 0.2.5 → 1.0.1

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/spec/client.rb ADDED
@@ -0,0 +1,384 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # How to run the bacon tests:
5
+ # 1. Start Riemann using the config from riemann.config
6
+ # 2. $ bundle exec bacon spec/client.rb
7
+
8
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'riemann'))
9
+ require 'riemann/client'
10
+ require 'bacon'
11
+ require 'set'
12
+ require 'timecop'
13
+
14
+ Bacon.summary_on_exit
15
+
16
+ include Riemann # rubocop:disable Style/MixinUsage
17
+
18
+ INACTIVITY_TIME = 5
19
+
20
+ def wait_for(&block)
21
+ tries = 0
22
+ while tries < 30
23
+ tries += 1
24
+ begin
25
+ res = block.call
26
+ return res if res
27
+ rescue NoMethodError
28
+ # If a query returns no result (#query retruns nil or #[] returns []),
29
+ # calling #first on it will raise a NoMethodError. We can ignore it for
30
+ # these tests.
31
+ end
32
+ sleep(0.1)
33
+ end
34
+
35
+ raise 'wait_for condition never realized'
36
+ end
37
+
38
+ def roundtrip_metric(metric)
39
+ @client_with_transport << {
40
+ service: 'metric-test',
41
+ metric: metric
42
+ }
43
+
44
+ wait_for { @client["service = \"metric-test\" and metric = #{metric}"].first }
45
+ .metric.should.equal metric
46
+ end
47
+
48
+ def truthy
49
+ ->(obj) { !(obj.nil? || obj == false) }
50
+ end
51
+
52
+ def falsey
53
+ ->(obj) { obj.nil? || obj == false }
54
+ end
55
+
56
+ shared 'a riemann client' do
57
+ should 'yield itself to given block' do
58
+ client = nil
59
+ Client.new(host: 'localhost', port: 5555) do |c|
60
+ client = c
61
+ end
62
+ client.should.be.is_a?(Client)
63
+ client.should.not.be.connected
64
+ end
65
+
66
+ should 'close sockets if given a block that raises' do
67
+ client = nil
68
+ begin
69
+ Client.new(host: 'localhost', port: 5555) do |c|
70
+ client = c
71
+ raise 'The Boom'
72
+ end
73
+ rescue StandardError
74
+ # swallow the exception
75
+ end
76
+ client.should.be.is_a?(Client)
77
+ client.should.not.be.connected
78
+ end
79
+
80
+ should 'be connected after sending' do
81
+ @client_with_transport.connected?.should.be falsey
82
+ @client.connected?.should.be falsey
83
+ @client_with_transport << { state: 'ok', service: 'connected check' }
84
+ @client_with_transport.connected?.should.be truthy
85
+ # NOTE: only single transport connected at this point, @client.connected? is still false until all transports used
86
+ end
87
+
88
+ should 'send longs' do
89
+ roundtrip_metric(0)
90
+ roundtrip_metric(-3)
91
+ roundtrip_metric(5)
92
+ roundtrip_metric(-(2**63))
93
+ roundtrip_metric(2**63 - 1)
94
+ end
95
+
96
+ should 'send doubles' do
97
+ roundtrip_metric 0.0
98
+ roundtrip_metric 12.0
99
+ roundtrip_metric 1.2300000190734863
100
+ end
101
+
102
+ should 'send custom attributes' do
103
+ event = Event.new(
104
+ service: 'custom',
105
+ state: 'ok',
106
+ cats: 'meow',
107
+ env: 'prod'
108
+ )
109
+ event[:sneak] = 'attack'
110
+ @client_with_transport << event
111
+ event2 = wait_for { @client['service = "custom"'].first }
112
+ event2.service.should.equal 'custom'
113
+ event2.state.should.equal 'ok'
114
+ event2[:cats].should.equal 'meow'
115
+ event2[:env].should.equal 'prod'
116
+ event2[:sneak].should.equal 'attack'
117
+ end
118
+
119
+ should 'send a state with a time' do
120
+ Timecop.freeze do
121
+ t = (Time.now - 10).to_i
122
+ @client_with_transport << {
123
+ state: 'ok',
124
+ service: 'test',
125
+ time: t
126
+ }
127
+ wait_for { @client.query('service = "test"').events.first.time == t }
128
+ e = @client.query('service = "test"').events.first
129
+ e.time.should.equal t
130
+ e.time_micros.should.equal t * 1_000_000
131
+ end
132
+ end
133
+
134
+ should 'send a state with a time_micros' do
135
+ Timecop.freeze do
136
+ t = ((Time.now - 10).to_f * 1_000_000).to_i
137
+ @client_with_transport << {
138
+ state: 'ok',
139
+ service: 'test',
140
+ time_micros: t
141
+ }
142
+ wait_for { @client.query('service = "test"').events.first.time_micros == t }
143
+ e = @client.query('service = "test"').events.first
144
+ e.time.should.equal (Time.now - 10).to_i
145
+ e.time_micros.should.equal t
146
+ end
147
+ end
148
+
149
+ should 'send a state without time nor time_micros' do
150
+ time_before = (Time.now.to_f * 1_000_000).to_i
151
+ @client_with_transport << {
152
+ state: 'ok',
153
+ service: 'timeless test'
154
+ }
155
+ wait_for { @client.query('service = "timeless test"').events.first.time_micros >= time_before }
156
+ e = @client.query('service = "timeless test"').events.first
157
+ time_after = (Time.now.to_f * 1_000_000).to_i
158
+
159
+ [time_before, e.time_micros, time_after].sort.should.equal([time_before, e.time_micros, time_after])
160
+ end
161
+
162
+ should 'query states' do
163
+ @client_with_transport << { state: 'critical', service: '1' }
164
+ @client_with_transport << { state: 'warning', service: '2' }
165
+ @client_with_transport << { state: 'critical', service: '3' }
166
+ wait_for { @client.query('service = "3"').events.first }
167
+ @client.query.events
168
+ .map(&:service).to_set.should.superset %w[1 2 3].to_set
169
+ @client.query('state = "critical" and (service = "1" or service = "2" or service = "3")').events
170
+ .map(&:service).to_set.should.equal %w[1 3].to_set
171
+ end
172
+
173
+ it '[]' do
174
+ # @client['state = "critical"'].should == []
175
+ @client_with_transport << { state: 'critical' }
176
+ wait_for { @client['state = "critical"'].first }.state.should.equal 'critical'
177
+ end
178
+
179
+ should 'query quickly' do
180
+ t1 = Time.now
181
+ total = 1000
182
+ total.times do |_i|
183
+ @client.query('state = "critical"')
184
+ end
185
+ t2 = Time.now
186
+
187
+ rate = total / (t2 - t1)
188
+ puts "\n #{format('%.2f', rate)} queries/sec (#{format('%.2f', (1000 / rate))}ms per query)"
189
+ rate.should > 100
190
+ end
191
+
192
+ should 'be threadsafe' do
193
+ concurrency = 10
194
+ per_thread = 200
195
+ total = concurrency * per_thread
196
+
197
+ t1 = Time.now
198
+ (0...concurrency).map do |_i|
199
+ Thread.new do
200
+ per_thread.times do
201
+ @client_with_transport.<<({
202
+ state: 'ok',
203
+ service: 'test',
204
+ description: 'desc',
205
+ metric_f: 1.0
206
+ })
207
+ end
208
+ end
209
+ end.each(&:join)
210
+ t2 = Time.now
211
+
212
+ rate = total / (t2 - t1)
213
+ puts "\n #{format('%.2f', rate)} inserts/sec (#{format('%.2f', (1000 / rate))}ms per insert)"
214
+ rate.should > @expected_rate
215
+ end
216
+ end
217
+
218
+ describe 'Riemann::Client (TLS transport)' do
219
+ before do
220
+ @client = Client.new(host: 'localhost', port: 5554, ssl: true,
221
+ key_file: '/etc/riemann/riemann_server.pkcs8',
222
+ cert_file: '/etc/riemann/riemann_server.crt',
223
+ ca_file: '/etc/riemann/riemann_server.crt',
224
+ ssl_verify: true)
225
+ @client_with_transport = @client.tcp
226
+ @expected_rate = 100
227
+ end
228
+ behaves_like 'a riemann client'
229
+
230
+ should 'send a state' do
231
+ res = @client_with_transport << {
232
+ state: 'ok',
233
+ service: 'test',
234
+ description: 'desc',
235
+ metric_f: 1.0
236
+ }
237
+
238
+ res.ok.should.be truthy
239
+ wait_for { @client['service = "test"'].first }.state.should.equal 'ok'
240
+ end
241
+
242
+ should 'survive inactivity' do
243
+ @client_with_transport.<<({
244
+ state: 'warning',
245
+ service: 'survive TCP inactivity'
246
+ })
247
+ wait_for { @client['service = "survive TCP inactivity"'].first.state == 'warning' }
248
+
249
+ sleep INACTIVITY_TIME
250
+
251
+ @client_with_transport.<<({
252
+ state: 'ok',
253
+ service: 'survive TCP inactivity'
254
+ }).ok.should.be truthy
255
+ wait_for { @client['service = "survive TCP inactivity"'].first.state == 'ok' }
256
+ end
257
+
258
+ should 'survive local close' do
259
+ @client_with_transport.<<({
260
+ state: 'warning',
261
+ service: 'survive TCP local close'
262
+ }).ok.should.be truthy
263
+ wait_for { @client['service = "survive TCP local close"'].first.state == 'warning' }
264
+
265
+ @client.close
266
+
267
+ @client_with_transport.<<({
268
+ state: 'ok',
269
+ service: 'survive TCP local close'
270
+ }).ok.should.be truthy
271
+ wait_for { @client['service = "survive TCP local close"'].first.state == 'ok' }
272
+ end
273
+ end
274
+
275
+ describe 'Riemann::Client (TCP transport)' do
276
+ before do
277
+ @client = Client.new(host: 'localhost', port: 5555)
278
+ @client_with_transport = @client.tcp
279
+ @expected_rate = 100
280
+ end
281
+ behaves_like 'a riemann client'
282
+
283
+ should 'send a state' do
284
+ res = @client_with_transport << {
285
+ state: 'ok',
286
+ service: 'test',
287
+ description: 'desc',
288
+ metric_f: 1.0
289
+ }
290
+
291
+ res.ok.should.be truthy
292
+ wait_for { @client['service = "test"'].first }.state.should.equal 'ok'
293
+ end
294
+
295
+ should 'survive inactivity' do
296
+ @client_with_transport.<<({
297
+ state: 'warning',
298
+ service: 'survive TCP inactivity'
299
+ })
300
+ wait_for { @client['service = "survive TCP inactivity"'].first.state == 'warning' }
301
+
302
+ sleep INACTIVITY_TIME
303
+
304
+ @client_with_transport.<<({
305
+ state: 'ok',
306
+ service: 'survive TCP inactivity'
307
+ }).ok.should.be truthy
308
+ wait_for { @client['service = "survive TCP inactivity"'].first.state == 'ok' }
309
+ end
310
+
311
+ should 'survive local close' do
312
+ @client_with_transport.<<({
313
+ state: 'warning',
314
+ service: 'survive TCP local close'
315
+ }).ok.should.be truthy
316
+ wait_for { @client['service = "survive TCP local close"'].first.state == 'warning' }
317
+
318
+ @client.close
319
+
320
+ @client_with_transport.<<({
321
+ state: 'ok',
322
+ service: 'survive TCP local close'
323
+ }).ok.should.be truthy
324
+ wait_for { @client['service = "survive TCP local close"'].first.state == 'ok' }
325
+ end
326
+ end
327
+
328
+ describe 'Riemann::Client (UDP transport)' do
329
+ before do
330
+ @client = Client.new(host: 'localhost', port: 5555)
331
+ @client_with_transport = @client.udp
332
+ @expected_rate = 1000
333
+ end
334
+ behaves_like 'a riemann client'
335
+
336
+ should 'send a state' do
337
+ res = @client_with_transport << {
338
+ state: 'ok',
339
+ service: 'test',
340
+ description: 'desc',
341
+ metric_f: 1.0
342
+ }
343
+
344
+ res.should.be.nil
345
+ wait_for { @client['service = "test"'].first }.state.should.equal 'ok'
346
+ end
347
+
348
+ should 'survive inactivity' do
349
+ @client_with_transport.<<({
350
+ state: 'warning',
351
+ service: 'survive UDP inactivity'
352
+ }).should.be.nil
353
+ wait_for { @client['service = "survive UDP inactivity"'].first.state == 'warning' }
354
+
355
+ sleep INACTIVITY_TIME
356
+
357
+ @client_with_transport.<<({
358
+ state: 'ok',
359
+ service: 'survive UDP inactivity'
360
+ }).should.be.nil
361
+ wait_for { @client['service = "survive UDP inactivity"'].first.state == 'ok' }
362
+ end
363
+
364
+ should 'survive local close' do
365
+ @client_with_transport.<<({
366
+ state: 'warning',
367
+ service: 'survive UDP local close'
368
+ }).should.be.nil
369
+ wait_for { @client['service = "survive UDP local close"'].first.state == 'warning' }
370
+
371
+ @client.close
372
+
373
+ @client_with_transport.<<({
374
+ state: 'ok',
375
+ service: 'survive UDP local close'
376
+ }).should.be.nil
377
+ wait_for { @client['service = "survive UDP local close"'].first.state == 'ok' }
378
+ end
379
+
380
+ should 'raise Riemann::Client::Unsupported exception on query' do
381
+ should.raise(Riemann::Client::Unsupported) { @client_with_transport['service = "test"'] }
382
+ should.raise(Riemann::Client::Unsupported) { @client_with_transport.query('service = "test"') }
383
+ end
384
+ end
@@ -0,0 +1,30 @@
1
+ ; -*- mode: clojure; -*-
2
+ ; vim: filetype=clojure
3
+
4
+ (logging/init {:file "/var/log/riemann/riemann.log"})
5
+
6
+ ; Listen on the local interface over TCP (5555), UDP (5555), websockets
7
+ ; (5556) and TLS (5554)
8
+ (let [host "127.0.0.1"]
9
+ (tcp-server {:host host})
10
+ (udp-server {:host host})
11
+ (ws-server {:host host})
12
+ (tcp-server {:host host :port 5554 :tls? true :key "/etc/riemann/riemann_server.pkcs8" :cert "/etc/riemann/riemann_server.crt" :ca-cert "/etc/riemann/riemann_server.crt"}))
13
+
14
+ ; Expire old events from the index every 5 seconds.
15
+ (periodically-expire 5)
16
+
17
+ (let [index (index)]
18
+ ; Inbound events will be passed to these streams:
19
+ (streams
20
+ (default :ttl 60
21
+ ; Index all events immediately.
22
+ ;index
23
+
24
+ ; Index all events after a delay.
25
+ (batch 1000 1/10
26
+ (sflatten index))
27
+
28
+ ; Log expired events.
29
+ (expired
30
+ (fn [event] (info "expired" event))))))
metadata CHANGED
@@ -1,74 +1,118 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riemann-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
5
- prerelease:
4
+ version: 1.0.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Kyle Kingsbury
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2015-02-05 00:00:00.000000000 Z
11
+ date: 2022-06-25 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: beefcake
14
+ name: bacon
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
- version: 0.3.5
22
- type: :runtime
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
23
35
  prerelease: false
24
36
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
37
  requirements:
27
- - - ! '>='
38
+ - - ">="
28
39
  - !ruby/object:Gem::Version
29
- version: 0.3.5
40
+ version: '1.3'
30
41
  - !ruby/object:Gem::Dependency
31
- name: trollop
42
+ name: rubocop
32
43
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
44
  requirements:
35
- - - ! '>='
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
36
53
  - !ruby/object:Gem::Version
37
- version: 1.16.2
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: timecop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: beefcake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.0.0
38
76
  type: :runtime
39
77
  prerelease: false
40
78
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
79
  requirements:
43
- - - ! '>='
80
+ - - ">="
44
81
  - !ruby/object:Gem::Version
45
- version: 1.16.2
82
+ version: 1.0.0
46
83
  - !ruby/object:Gem::Dependency
47
84
  name: mtrc
48
85
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
86
  requirements:
51
- - - ! '>='
87
+ - - ">="
52
88
  - !ruby/object:Gem::Version
53
89
  version: 0.0.4
54
90
  type: :runtime
55
91
  prerelease: false
56
92
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
93
  requirements:
59
- - - ! '>='
94
+ - - ">="
60
95
  - !ruby/object:Gem::Version
61
96
  version: 0.0.4
62
- description:
97
+ description: Client for the distributed event system Riemann.
63
98
  email: aphyr@aphyr.com
64
99
  executables: []
65
100
  extensions: []
66
101
  extra_rdoc_files: []
67
102
  files:
103
+ - ".github/workflows/ci.yml"
104
+ - ".gitignore"
105
+ - ".rubocop.yml"
106
+ - CHANGELOG.md
107
+ - Gemfile
108
+ - LICENSE
109
+ - README.markdown
110
+ - Rakefile
68
111
  - lib/riemann.rb
69
112
  - lib/riemann/attribute.rb
70
113
  - lib/riemann/auto_state.rb
71
114
  - lib/riemann/client.rb
115
+ - lib/riemann/client/ssl_socket.rb
72
116
  - lib/riemann/client/tcp.rb
73
117
  - lib/riemann/client/tcp_socket.rb
74
118
  - lib/riemann/client/udp.rb
@@ -78,30 +122,32 @@ files:
78
122
  - lib/riemann/query.rb
79
123
  - lib/riemann/state.rb
80
124
  - lib/riemann/version.rb
81
- - LICENSE
82
- - README.markdown
125
+ - riemann-client.gemspec
126
+ - spec/client.rb
127
+ - spec/riemann.config
83
128
  homepage: https://github.com/aphyr/riemann-ruby-client
84
- licenses: []
85
- post_install_message:
129
+ licenses:
130
+ - MIT
131
+ metadata: {}
132
+ post_install_message:
86
133
  rdoc_options: []
87
134
  require_paths:
88
135
  - lib
89
136
  required_ruby_version: !ruby/object:Gem::Requirement
90
- none: false
91
137
  requirements:
92
- - - ! '>='
138
+ - - ">="
93
139
  - !ruby/object:Gem::Version
94
- version: 1.8.7
140
+ version: 2.6.0
95
141
  required_rubygems_version: !ruby/object:Gem::Requirement
96
- none: false
97
142
  requirements:
98
- - - ! '>='
143
+ - - ">="
99
144
  - !ruby/object:Gem::Version
100
145
  version: '0'
101
146
  requirements: []
102
- rubyforge_project: riemann-client
103
- rubygems_version: 1.8.25
104
- signing_key:
105
- specification_version: 3
147
+ rubygems_version: 3.3.15
148
+ signing_key:
149
+ specification_version: 4
106
150
  summary: Client for the distributed event system Riemann.
107
- test_files: []
151
+ test_files:
152
+ - spec/client.rb
153
+ - spec/riemann.config