riemann-tools 1.10.0 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +7 -6
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +18 -0
  5. data/CHANGELOG.md +23 -0
  6. data/Gemfile +22 -0
  7. data/Rakefile +1 -1
  8. data/bin/riemann-hwmon +8 -0
  9. data/bin/riemann-tls-check +8 -0
  10. data/lib/riemann/tools/apache_status.rb +2 -0
  11. data/lib/riemann/tools/bench.rb +4 -2
  12. data/lib/riemann/tools/consul_health.rb +2 -0
  13. data/lib/riemann/tools/dir_files_count.rb +2 -0
  14. data/lib/riemann/tools/dir_space.rb +2 -0
  15. data/lib/riemann/tools/diskstats.rb +6 -4
  16. data/lib/riemann/tools/fd.rb +2 -0
  17. data/lib/riemann/tools/freeswitch.rb +2 -0
  18. data/lib/riemann/tools/haproxy.rb +2 -0
  19. data/lib/riemann/tools/health.rb +79 -11
  20. data/lib/riemann/tools/http_check.rb +56 -17
  21. data/lib/riemann/tools/hwmon.rb +111 -0
  22. data/lib/riemann/tools/mdstat_parser.tab.rb +4 -2
  23. data/lib/riemann/tools/net.rb +3 -7
  24. data/lib/riemann/tools/nginx_status.rb +8 -4
  25. data/lib/riemann/tools/ntp.rb +2 -0
  26. data/lib/riemann/tools/portcheck.rb +2 -0
  27. data/lib/riemann/tools/proc.rb +2 -0
  28. data/lib/riemann/tools/tls_check.rb +604 -0
  29. data/lib/riemann/tools/utils.rb +39 -0
  30. data/lib/riemann/tools/varnish.rb +2 -0
  31. data/lib/riemann/tools/version.rb +1 -1
  32. data/lib/riemann/tools.rb +26 -9
  33. data/riemann-tools.gemspec +2 -11
  34. data/tools/riemann-aws/lib/riemann/tools/aws/billing.rb +3 -1
  35. data/tools/riemann-aws/lib/riemann/tools/aws/rds_status.rb +2 -0
  36. data/tools/riemann-aws/lib/riemann/tools/aws/s3_status.rb +1 -1
  37. data/tools/riemann-aws/lib/riemann/tools/aws/sqs_status.rb +2 -0
  38. data/tools/riemann-aws/lib/riemann/tools/aws/status.rb +11 -9
  39. data/tools/riemann-chronos/lib/riemann/tools/chronos.rb +2 -0
  40. data/tools/riemann-docker/lib/riemann/tools/docker.rb +5 -5
  41. data/tools/riemann-marathon/lib/riemann/tools/marathon.rb +2 -0
  42. data/tools/riemann-munin/lib/riemann/tools/munin.rb +2 -0
  43. data/tools/riemann-rabbitmq/lib/riemann/tools/rabbitmq.rb +7 -7
  44. data/tools/riemann-riak/lib/riemann/tools/riak.rb +4 -2
  45. metadata +10 -129
@@ -0,0 +1,604 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'resolv'
5
+
6
+ require 'riemann/tools'
7
+ require 'riemann/tools/utils'
8
+
9
+ module URI
10
+ {
11
+ 'IMAP' => 143,
12
+ 'IMAPS' => 993,
13
+ 'MYSQL' => 3306,
14
+ 'POSTGRES' => 5432,
15
+ }.each do |scheme, port|
16
+ klass = Class.new(Generic)
17
+ klass.const_set('DEFAULT_PORT', port)
18
+
19
+ if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('3.1.0')
20
+ scheme_list[scheme] = klass
21
+ else
22
+ register_scheme(scheme, klass)
23
+ end
24
+ end
25
+ end
26
+
27
+ module Riemann
28
+ module Tools
29
+ class TLSCheck
30
+ include Riemann::Tools
31
+
32
+ # Ruby OpenSSL does not expose ERR_error_string(3), and depending on the
33
+ # version of OpenSSL the available values change. Build a local list of
34
+ # mappings from include/openssl/x509_vfy.h.in and crypto/x509/x509_txt.c
35
+ # for lookups.
36
+ OPENSSL_ERROR_STRINGS = [
37
+ 'ok',
38
+ 'unspecified certificate verification error',
39
+ 'unable to get issuer certificate',
40
+ 'unable to get certificate CRL',
41
+ "unable to decrypt certificate's signature",
42
+ "unable to decrypt CRL's signature",
43
+ 'unable to decode issuer public key',
44
+ 'certificate signature failure',
45
+ 'CRL signature failure',
46
+ 'certificate is not yet valid',
47
+ 'certificate has expired',
48
+ 'CRL is not yet valid',
49
+ 'CRL has expired',
50
+ "format error in certificate's notBefore field",
51
+ "format error in certificate's notAfter field",
52
+ "format error in CRL's lastUpdate field",
53
+ "format error in CRL's nextUpdate field",
54
+ 'out of memory',
55
+ 'self-signed certificate',
56
+ 'self-signed certificate in certificate chain',
57
+ 'unable to get local issuer certificate',
58
+ 'unable to verify the first certificate',
59
+ 'certificate chain too long',
60
+ 'certificate revoked',
61
+ "issuer certificate doesn't have a public key",
62
+ 'path length constraint exceeded',
63
+ 'unsuitable certificate purpose',
64
+ 'certificate not trusted',
65
+ 'certificate rejected',
66
+ ].freeze
67
+
68
+ class TLSCheckResult
69
+ include Riemann::Tools::Utils
70
+
71
+ attr_reader :uri, :address, :tls_socket
72
+
73
+ def initialize(uri, address, tls_socket, checker)
74
+ @uri = uri
75
+ @address = address
76
+ @tls_socket = tls_socket
77
+ @checker = checker
78
+ end
79
+
80
+ def peer_cert
81
+ tls_socket.peer_cert
82
+ end
83
+
84
+ def peer_cert_chain
85
+ tls_socket.peer_cert_chain
86
+ end
87
+
88
+ def exception
89
+ tls_socket.exception if tls_socket.respond_to?(:exception)
90
+ end
91
+
92
+ def valid_identity?
93
+ OpenSSL::SSL.verify_certificate_identity(peer_cert, uri.host)
94
+ end
95
+
96
+ def acceptable_identities
97
+ res = []
98
+
99
+ peer_cert.extensions.each do |ext|
100
+ next unless ext.oid == 'subjectAltName'
101
+
102
+ ostr = OpenSSL::ASN1.decode(ext.to_der).value.last
103
+ sequence = OpenSSL::ASN1.decode(ostr.value)
104
+ res = sequence.value.map(&:value)
105
+ end
106
+
107
+ res << peer_cert.subject.to_s unless res.any?
108
+
109
+ res
110
+ end
111
+
112
+ def not_valid_yet?
113
+ utcnow < not_before
114
+ end
115
+
116
+ def not_after
117
+ peer_cert.not_after
118
+ end
119
+
120
+ def not_after_ago
121
+ not_after - utcnow
122
+ end
123
+
124
+ def not_after_ago_in_words
125
+ when_from_utcnow(not_after)
126
+ end
127
+
128
+ def not_before
129
+ peer_cert.not_before
130
+ end
131
+
132
+ def not_before_away
133
+ utcnow - not_before
134
+ end
135
+
136
+ def not_before_away_in_words
137
+ when_from_utcnow(not_before)
138
+ end
139
+
140
+ def validity_duration
141
+ not_after - not_before
142
+ end
143
+
144
+ def renewal_duration
145
+ [validity_duration * @checker.opts[:renewal_duration_ratio], @checker.opts[:renewal_duration_days] * 3600 * 24].min
146
+ end
147
+
148
+ def expired_or_expire_soon?
149
+ utcnow + (renewal_duration / 3) > not_after
150
+ end
151
+
152
+ def expire_soonish?
153
+ utcnow + (2 * renewal_duration / 3) > not_after
154
+ end
155
+
156
+ def expired?
157
+ utcnow > not_after
158
+ end
159
+
160
+ def verify_result
161
+ tls_socket.verify_result
162
+ end
163
+
164
+ def trusted?
165
+ verify_result == OpenSSL::X509::V_OK
166
+ end
167
+
168
+ def ocsp_status
169
+ @ocsp_status ||= check_ocsp_status
170
+ end
171
+
172
+ def ocsp?
173
+ !ocsp_status.empty?
174
+ end
175
+
176
+ def valid_ocsp?
177
+ ocsp_status == 'successful'
178
+ end
179
+
180
+ def check_ocsp_status
181
+ subject = peer_cert
182
+ issuer = peer_cert_chain[1]
183
+
184
+ return '' unless issuer
185
+
186
+ digest = OpenSSL::Digest.new('SHA1')
187
+ certificate_id = OpenSSL::OCSP::CertificateId.new(subject, issuer, digest)
188
+
189
+ request = OpenSSL::OCSP::Request.new
190
+ request.add_certid(certificate_id)
191
+
192
+ request.add_nonce
193
+
194
+ authority_info_access = subject.extensions.find do |extension|
195
+ extension.oid == 'authorityInfoAccess'
196
+ end
197
+
198
+ return '' unless authority_info_access
199
+
200
+ descriptions = authority_info_access.value.split("\n")
201
+ ocsp = descriptions.find do |description|
202
+ description.start_with? 'OCSP'
203
+ end
204
+
205
+ ocsp_uri = URI(ocsp[/URI:(.*)/, 1])
206
+
207
+ http_response = ::Net::HTTP.start(ocsp_uri.hostname, ocsp_uri.port) do |http|
208
+ ocsp_uri.path = '/' if ocsp_uri.path.empty?
209
+ http.post(ocsp_uri.path, request.to_der, 'content-type' => 'application/ocsp-request')
210
+ end
211
+
212
+ response = OpenSSL::OCSP::Response.new http_response.body
213
+ response_basic = response.basic
214
+
215
+ return '' unless response_basic&.verify([issuer], @checker.store)
216
+
217
+ response.status_string
218
+ end
219
+ end
220
+
221
+ class UnexpectedMessage < StandardError
222
+ def initialize(msg)
223
+ super(%(Unexpected message: "#{msg.chomp}"))
224
+ end
225
+ end
226
+
227
+ opt :uri, 'URI to check', short: :none, type: :strings
228
+ opt :checks, 'A list of checks to run.', short: :none, type: :strings, default: %w[identity not-after not-before ocsp trust]
229
+
230
+ opt :renewal_duration_days, 'Number of days before certificate expiration it is considered renewalable', short: :none, type: :integer, default: 90
231
+ opt :renewal_duration_ratio, 'Portion of the certificate lifespan it is considered renewalable', short: :none, type: :float, default: 1.0 / 3
232
+
233
+ opt :trust, 'Additionnal CA to trust', short: :none, type: :strings, default: []
234
+
235
+ opt :resolvers, 'Run this number of resolver threads', short: :none, type: :integer, default: 5
236
+ opt :workers, 'Run this number of worker threads', short: :none, type: :integer, default: 20
237
+ opt :connect_timeout, 'Timeout to espablish a connection (default to half the interval caped to 10s)', short: :none, type: :integer
238
+
239
+ def initialize
240
+ super
241
+
242
+ opts[:connect_timeout] ||= [10, opts[:interval] / 2].min
243
+
244
+ @resolve_queue = Queue.new
245
+ @work_queue = Queue.new
246
+
247
+ opts[:resolvers].times do
248
+ Thread.new do
249
+ loop do
250
+ uri = @resolve_queue.pop
251
+ host = uri.host
252
+
253
+ addresses = if host == 'localhost'
254
+ Socket.ip_address_list.select { |address| address.ipv6_loopback? || address.ipv4_loopback? }.map(&:ip_address)
255
+ else
256
+ Resolv::DNS.new.getaddresses(host)
257
+ end
258
+ if addresses.empty?
259
+ host = host[1...-1] if host[0] == '[' && host[-1] == ']'
260
+ begin
261
+ addresses << IPAddr.new(host)
262
+ rescue IPAddr::InvalidAddressError
263
+ # Ignore
264
+ end
265
+ end
266
+
267
+ @work_queue.push([uri, addresses])
268
+ end
269
+ end
270
+ end
271
+
272
+ opts[:workers].times do
273
+ Thread.new do
274
+ loop do
275
+ uri, addresses = @work_queue.pop
276
+ test_uri_addresses(uri, addresses)
277
+ end
278
+ end
279
+ end
280
+ end
281
+
282
+ def tick
283
+ report(
284
+ service: 'riemann tls-check resolvers utilization',
285
+ metric: (opts[:resolvers].to_f - @resolve_queue.num_waiting) / opts[:resolvers],
286
+ state: @resolve_queue.num_waiting.positive? ? 'ok' : 'critical',
287
+ tags: %w[riemann],
288
+ )
289
+ report(
290
+ service: 'riemann tls-check resolvers saturation',
291
+ metric: @resolve_queue.length,
292
+ state: @resolve_queue.empty? ? 'ok' : 'critical',
293
+ tags: %w[riemann],
294
+ )
295
+ report(
296
+ service: 'riemann tls-check workers utilization',
297
+ metric: (opts[:workers].to_f - @work_queue.num_waiting) / opts[:workers],
298
+ state: @work_queue.num_waiting.positive? ? 'ok' : 'critical',
299
+ tags: %w[riemann],
300
+ )
301
+ report(
302
+ service: 'riemann tls-check workers saturation',
303
+ metric: @work_queue.length,
304
+ state: @work_queue.empty? ? 'ok' : 'critical',
305
+ tags: %w[riemann],
306
+ )
307
+
308
+ opts[:uri].each do |uri|
309
+ @resolve_queue.push(URI(uri))
310
+ end
311
+ end
312
+
313
+ def test_uri_addresses(uri, addresses)
314
+ addresses.each do |address|
315
+ test_uri_address(uri, address.to_s)
316
+ end
317
+ end
318
+
319
+ def test_uri_address(uri, address)
320
+ socket = tls_socket(uri, address)
321
+ tls_check_result = TLSCheckResult.new(uri, address, socket, self)
322
+ report_availability(tls_check_result)
323
+ return unless socket.peer_cert
324
+
325
+ report_not_before(tls_check_result) if opts[:checks].include?('not-before')
326
+ report_not_after(tls_check_result) if opts[:checks].include?('not-after')
327
+ report_identity(tls_check_result) if opts[:checks].include?('identity')
328
+ report_trust(tls_check_result) if opts[:checks].include?('trust')
329
+ report_ocsp(tls_check_result) if opts[:checks].include?('ocsp')
330
+ rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, UnexpectedMessage => e
331
+ report_unavailability(uri, address, e)
332
+ end
333
+
334
+ def report_availability(tls_check_result)
335
+ if tls_check_result.exception
336
+ report(
337
+ service: "#{tls_endpoint_name(tls_check_result)} availability",
338
+ state: 'critical',
339
+ description: tls_check_result.exception.message,
340
+ )
341
+ else
342
+ issues = []
343
+
344
+ issues << 'Certificate is not valid yet' if tls_check_result.not_valid_yet?
345
+ issues << 'Certificate has expired' if tls_check_result.expired?
346
+ issues << 'Certificate identity could not be verified' unless tls_check_result.valid_identity?
347
+ issues << 'Certificate is not trusted' unless tls_check_result.trusted?
348
+ issues << 'Certificate OCSP verification failed' if tls_check_result.ocsp? && !tls_check_result.valid_ocsp?
349
+
350
+ report(
351
+ service: "#{tls_endpoint_name(tls_check_result)} availability",
352
+ state: issues.empty? ? 'ok' : 'critical',
353
+ description: issues.join("\n"),
354
+ )
355
+ end
356
+ end
357
+
358
+ def report_unavailability(uri, address, exception)
359
+ report(
360
+ service: "#{tls_endpoint_name2(uri, address)} availability",
361
+ state: 'critical',
362
+ description: exception.message,
363
+ )
364
+ end
365
+
366
+ def report_not_after(tls_check_result)
367
+ report(
368
+ service: "#{tls_endpoint_name(tls_check_result)} not after",
369
+ state: not_after_state(tls_check_result),
370
+ metric: tls_check_result.not_after_ago,
371
+ description: tls_check_result.not_after_ago_in_words,
372
+ )
373
+ end
374
+
375
+ def report_not_before(tls_check_result)
376
+ report(
377
+ service: "#{tls_endpoint_name(tls_check_result)} not before",
378
+ state: not_before_state(tls_check_result),
379
+ metric: tls_check_result.not_before_away,
380
+ description: tls_check_result.not_before_away_in_words,
381
+ )
382
+ end
383
+
384
+ def report_identity(tls_check_result)
385
+ report(
386
+ service: "#{tls_endpoint_name(tls_check_result)} identity",
387
+ state: tls_check_result.valid_identity? ? 'ok' : 'critical',
388
+ description: "Valid for:\n#{tls_check_result.acceptable_identities.join("\n")}",
389
+ )
390
+ end
391
+
392
+ def report_trust(tls_check_result)
393
+ commont_attrs = {
394
+ service: "#{tls_endpoint_name(tls_check_result)} trust",
395
+ }
396
+ extra_attrs = if tls_check_result.exception
397
+ {
398
+ state: 'critical',
399
+ description: tls_check_result.exception.message,
400
+ }
401
+ else
402
+ {
403
+ state: tls_check_result.trusted? ? 'ok' : 'critical',
404
+ description: if OPENSSL_ERROR_STRINGS[tls_check_result.verify_result]
405
+ format('%<code>d - %<msg>s', code: tls_check_result.verify_result, msg: OPENSSL_ERROR_STRINGS[tls_check_result.verify_result])
406
+ else
407
+ tls_check_result.verify_result.to_s
408
+ end,
409
+ }
410
+ end
411
+ report(commont_attrs.merge(extra_attrs))
412
+ end
413
+
414
+ def report_ocsp(tls_check_result)
415
+ return unless tls_check_result.ocsp?
416
+
417
+ report(
418
+ service: "#{tls_endpoint_name(tls_check_result)} OCSP status",
419
+ state: tls_check_result.valid_ocsp? ? 'ok' : 'critical',
420
+ description: tls_check_result.ocsp_status,
421
+ )
422
+ end
423
+
424
+ # not_before not_after
425
+ # |<----------------------------->| validity_duration
426
+ # …ccccccccoooooooooooooooooooooooooooooooooooooo… not_before_state
427
+ #
428
+ # time --->>>>
429
+ def not_before_state(tls_check_result)
430
+ tls_check_result.not_valid_yet? ? 'critical' : 'ok'
431
+ end
432
+
433
+ # not_before not_after
434
+ # |<----------------------------->| validity_duration
435
+ # |<--------->| renewal_duration
436
+ # | ⅓ | ⅓ | ⅓ |
437
+ # …oooooooooooooooooooooooooooooooowwwwcccccccccc… not_after_state
438
+ #
439
+ # time --->>>>
440
+ def not_after_state(tls_check_result)
441
+ if tls_check_result.expired_or_expire_soon?
442
+ 'critical'
443
+ elsif tls_check_result.expire_soonish?
444
+ 'warning'
445
+ else
446
+ 'ok'
447
+ end
448
+ end
449
+
450
+ def tcp_socket(host, port)
451
+ Socket.tcp(host, port, connect_timeout: opts[:connect_timeout])
452
+ end
453
+
454
+ def tls_socket(uri, address)
455
+ case uri.scheme
456
+ when 'smtp'
457
+ smtp_tls_socket(uri, address)
458
+ when 'imap'
459
+ imap_tls_socket(uri, address)
460
+ when 'ldap'
461
+ ldap_tls_socket(uri, address)
462
+ when 'mysql'
463
+ mysql_tls_socket(uri, address)
464
+ when 'postgres'
465
+ postgres_tls_socket(uri, address)
466
+ else
467
+ raw_tls_socket(uri, address)
468
+ end
469
+ end
470
+
471
+ def mysql_tls_socket(uri, address)
472
+ socket = tcp_socket(address, uri.port)
473
+ length = "#{socket.read(3)}\0".unpack1('L*')
474
+ _sequence = socket.read(1)
475
+ body = socket.read(length)
476
+ initial_handshake_packet = body.unpack('cZ*La8aScSS')
477
+
478
+ capabilities = initial_handshake_packet[5] | (initial_handshake_packet[8] << 16)
479
+
480
+ ssl_flag = 1 << 11
481
+ raise 'No TLS support' if (capabilities & ssl_flag).zero?
482
+
483
+ socket.write(['2000000185ae7f0000000001210000000000000000000000000000000000000000000000'].pack('H*'))
484
+ tls_handshake(socket, uri.host)
485
+ end
486
+
487
+ def postgres_tls_socket(uri, address)
488
+ socket = tcp_socket(address, uri.port)
489
+ socket.write(['0000000804d2162f'].pack('H*'))
490
+ raise 'Unexpected reply' unless socket.read(1) == 'S'
491
+
492
+ tls_handshake(socket, uri.host)
493
+ end
494
+
495
+ def smtp_tls_socket(uri, address)
496
+ socket = tcp_socket(address, uri.port)
497
+ read_socket_lines_until_prefix_matched(socket, '220 ', also_accept_prefixes: ['220-'])
498
+ socket.send("EHLO #{my_hostname}\r\n", 0)
499
+ read_socket_lines_until_prefix_matched(socket, '250 ', also_accept_prefixes: ['250-'])
500
+ socket.send("STARTTLS\r\n", 0)
501
+ socket.gets
502
+
503
+ tls_handshake(socket, uri.host)
504
+ end
505
+
506
+ def my_hostname
507
+ Addrinfo.tcp(Socket.gethostname, 8023).getnameinfo.first
508
+ rescue SocketError
509
+ Socket.gethostname
510
+ end
511
+
512
+ def read_socket_lines_until_prefix_matched(socket, prefix, also_accept_prefixes: [])
513
+ loop do
514
+ line = socket.gets
515
+ break if line.start_with?(prefix)
516
+ next if also_accept_prefixes.map { |accepted_prefix| line.start_with?(accepted_prefix) }.any?
517
+
518
+ raise UnexpectedMessage, line
519
+ end
520
+ end
521
+
522
+ def imap_tls_socket(uri, address)
523
+ socket = tcp_socket(address, uri.port)
524
+ read_socket_lines_until_prefix_matched(socket, '* OK')
525
+ socket.send(". CAPABILITY\r\n", 0)
526
+ read_socket_lines_until_prefix_matched(socket, '. OK', also_accept_prefixes: ['* CAPABILITY'])
527
+ socket.send(". STARTTLS\r\n", 0)
528
+ read_socket_lines_until_prefix_matched(socket, '. OK')
529
+
530
+ tls_handshake(socket, uri.host)
531
+ end
532
+
533
+ def ldap_tls_socket(uri, address)
534
+ socket = tcp_socket(address, uri.port)
535
+ socket.write(['301d02010177188016312e332e362e312e342e312e313436362e3230303337'].pack('H*'))
536
+ expected_res = ['300c02010178070a010004000400'].pack('H*')
537
+ res = socket.read(expected_res.length)
538
+
539
+ return nil unless res == expected_res
540
+
541
+ tls_handshake(socket, uri.host)
542
+ end
543
+
544
+ def raw_tls_socket(uri, address)
545
+ raise "No default port for #{uri.scheme} scheme" unless uri.port
546
+
547
+ socket = tcp_socket(address, uri.port)
548
+ tls_handshake(socket, uri.host)
549
+ end
550
+
551
+ def tls_handshake(raw_socket, hostname)
552
+ tls_socket = OpenSSL::SSL::SSLSocket.new(raw_socket, ssl_context)
553
+ tls_socket.hostname = hostname
554
+ begin
555
+ tls_socket.connect
556
+ rescue OpenSSL::SSL::SSLError => e
557
+ # This may fail for example if a client certificate is required but
558
+ # not provided. In this case, the remote certificate is available and
559
+ # we can ignore this issue. In other cases, the remote certificate is
560
+ # not available, in this case we want to stop and report the issue
561
+ # (e.g. connecting to a host with a SNI for a name not handled by
562
+ # that host).
563
+ tls_socket.define_singleton_method(:exception) do
564
+ e
565
+ end
566
+ end
567
+ tls_socket
568
+ end
569
+
570
+ def ssl_context
571
+ @ssl_context ||= begin
572
+ ctx = OpenSSL::SSL::SSLContext.new
573
+ ctx.cert_store = store
574
+ ctx.verify_hostname = false
575
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
576
+ ctx
577
+ end
578
+ end
579
+
580
+ def store
581
+ @store ||= begin
582
+ store = OpenSSL::X509::Store.new
583
+ store.set_default_paths
584
+ opts[:trust].each do |path|
585
+ if File.directory?(path)
586
+ store.add_path(path)
587
+ else
588
+ store.add_file(path)
589
+ end
590
+ end
591
+ store
592
+ end
593
+ end
594
+
595
+ def tls_endpoint_name(tls_check_result)
596
+ tls_endpoint_name2(tls_check_result.uri, tls_check_result.address)
597
+ end
598
+
599
+ def tls_endpoint_name2(uri, address)
600
+ "TLS certificate #{uri} #{endpoint_name(IPAddr.new(address), uri.port)}"
601
+ end
602
+ end
603
+ end
604
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'socket'
4
+
3
5
  module Riemann
4
6
  module Tools
5
7
  module Utils # :nodoc:
@@ -60,6 +62,43 @@ module Riemann
60
62
 
61
63
  (header + lines[0, count]).join("\n")
62
64
  end
65
+
66
+ def when_from_utcnow(date)
67
+ if date > utcnow
68
+ "in #{distance_of_time_in_words_to_utcnow(date)}"
69
+ else
70
+ "#{distance_of_time_in_words_to_utcnow(date)} ago"
71
+ end
72
+ end
73
+
74
+ # Stolen from ActionView, to avoid pulling a lot of dependencies
75
+ def distance_of_time_in_words_to_utcnow(to_time)
76
+ distance_in_seconds = (to_time - utcnow).round.abs
77
+ distance_in_minutes = distance_in_seconds / 60
78
+
79
+ case distance_in_minutes
80
+ when 0 then 'less than 1 minute'
81
+ when 1...45 then pluralize_string('%d minute', distance_in_minutes)
82
+ when 45...1440 then pluralize_string('about %d hour', (distance_in_minutes.to_f / 60.0).round)
83
+ # 24 hours up to 30 days
84
+ when 1440...43_200 then pluralize_string('%d day', (distance_in_minutes.to_f / 1440.0).round)
85
+ # 30 days up to 60 days
86
+ when 43_200...86_400 then pluralize_string('about %d month', (distance_in_minutes.to_f / 43_200.0).round)
87
+ # 60 days up to 365 days
88
+ when 86_400...525_600 then pluralize_string('%d month', (distance_in_minutes.to_f / 43_200.0).round)
89
+ else
90
+ pluralize_string('about %d year', (distance_in_minutes.to_f / 525_600.0).round)
91
+ end
92
+ end
93
+
94
+ def pluralize_string(string, number)
95
+ format(string, number) + (number == 1 ? '' : 's')
96
+ end
97
+
98
+ # The current date and time, but in UTC
99
+ def utcnow
100
+ Time.at(Time.now, in: '+00:00')
101
+ end
63
102
  end
64
103
  end
65
104
  end
@@ -12,6 +12,8 @@ module Riemann
12
12
  opt :varnish_host, 'Varnish hostname', default: `hostname`.chomp
13
13
 
14
14
  def initialize
15
+ super
16
+
15
17
  cmd = 'varnishstat -V'
16
18
  Open3.popen3(cmd) do |_stdin, _stdout, stderr, _wait_thr|
17
19
  @ver = /varnishstat \(varnish-(\d+)/.match(stderr.read)[1].to_i
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Riemann
4
4
  module Tools # :nodoc:
5
- VERSION = '1.10.0'
5
+ VERSION = '1.11.0'
6
6
  end
7
7
  end