riemann-client 0.2.5 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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