riemann-client-1.2.1/0000755000004100000410000000000014523635061014455 5ustar www-datawww-datariemann-client-1.2.1/.rspec0000644000004100000410000000004314523635061015567 0ustar www-datawww-data--require spec_helper --format doc riemann-client-1.2.1/spec/0000755000004100000410000000000014523635061015407 5ustar www-datawww-datariemann-client-1.2.1/spec/riemann.config0000644000004100000410000000161114523635061020226 0ustar www-datawww-data; -*- mode: clojure; -*- ; vim: filetype=clojure (logging/init {:file "/var/log/riemann/riemann.log"}) ; Listen on the local interface over TCP (5555), UDP (5555), websockets ; (5556) and TLS (5554) (let [host "127.0.0.1"] (tcp-server {:host host}) (udp-server {:host host}) (ws-server {:host host}) (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"})) ; Expire old events from the index every 5 seconds. (periodically-expire 5) (let [index (index)] ; Inbound events will be passed to these streams: (streams (default :ttl 60 ; Index all events immediately. ;index ; Index all events after a delay. (batch 1000 1/10 (sflatten index)) ; Log expired events. (expired (fn [event] (info "expired" event)))))) riemann-client-1.2.1/spec/client_spec.rb0000644000004100000410000000356514523635061020235 0ustar www-datawww-data# frozen_string_literal: true require 'riemann' require 'riemann/client' require 'spec_helper' require 'shared_examples' RSpec.describe 'Riemann::Client' do let(:client) do Riemann::Client.new(host: 'localhost', port: 5555) end let(:expected_rate) { 100 } context('with TLS transport') do let(:client) do Riemann::Client.new(host: 'localhost', port: 5554, ssl: true, key_file: '/etc/riemann/riemann_server.pkcs8', cert_file: '/etc/riemann/riemann_server.crt', ca_file: '/etc/riemann/riemann_server.crt', ssl_verify: true) end let(:client_with_transport) { client.tcp } it_behaves_like 'a riemann client' it_behaves_like 'a riemann client that acknowledge messages' end context 'with TCP transport' do let(:client_with_transport) { client.tcp } it_behaves_like 'a riemann client' it_behaves_like 'a riemann client that acknowledge messages' end context('with UDP transport') do let(:client_with_transport) { client.udp } let(:expected_rate) { 1000 } it_behaves_like 'a riemann client' it_behaves_like 'a riemann client that does not acknowledge messages' context 'when sending a message too large for UDP transport' do let(:large_message) do { data: 'X' * (Riemann::Client::UDP::MAX_SIZE + 10) } end before do allow(client.udp).to receive(:send_maybe_recv).and_call_original allow(client.tcp).to receive(:send_maybe_recv).and_call_original client << large_message end it 'has tried to send the message using UDP' do expect(client.udp).to have_received(:send_maybe_recv) end it 'has retried to send the message using TCP' do expect(client.tcp).to have_received(:send_maybe_recv) end end end end riemann-client-1.2.1/spec/spec_helper.rb0000644000004100000410000000276714523635061020241 0ustar www-datawww-data# frozen_string_literal: true RSpec.configure do |config| # Limits the available syntax to the non-monkey patched syntax that is # recommended. For more details, see: # https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode # config.disable_monkey_patching! # This setting enables warnings. It's recommended, but in some cases may # be too noisy due to issues in dependencies. config.warnings = true # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = :random # Seed global randomization in this process using the `--seed` CLI option. # Setting this allows you to use `--seed` to deterministically reproduce # test failures related to randomization by passing the same `--seed` value # as the one that triggered the failure. Kernel.srand config.seed # RSpec tries to be friendly to us by detecting deadlocks but this breaks CI # :-( # # Some tests want to start multiple thread in a let block, and this # thread-safety mechanism makes it impossible and raise an exception while # our code is working correctly. # # This issue seems the same as: # https://github.com/rspec/rspec-core/issues/2064 # # The feature we disable was introduced in: # https://github.com/rspec/rspec-core/commit/ffe00a1d4e369e312881e6b2c091c8b6fb7e6087 config.threadsafe = false end riemann-client-1.2.1/spec/shared_examples.rb0000644000004100000410000002747314523635061021115 0ustar www-datawww-data# frozen_string_literal: true require 'riemann' require 'riemann/client' require 'set' require 'timecop' INACTIVITY_TIME = 5 class Sequence include Singleton def initialize @nextval = 0 end def nextval @nextval += 1 end def current @nextval end end def next_message_id Sequence.instance.nextval "#{Process.pid}-#{Sequence.instance.current}" end def wait_for_message_with_id(message_id) wait_for { client[%(message_id = "#{message_id}")].first } end def wait_for(&block) tries = 0 while tries < 30 tries += 1 begin res = block.call return res if res rescue NoMethodError # If a query returns no result (#query retruns nil or #[] returns []), # calling #first on it will raise a NoMethodError. We can ignore it for # these tests. end sleep(0.1) end raise 'wait_for condition never realized' end def roundtrip_metric(metric) message_id = next_message_id client_with_transport << { service: 'metric-test', metric: metric, message_id: message_id } e = wait_for_message_with_id(message_id) expect(e.metric).to eq(metric) end RSpec.shared_examples 'a riemann client' do it 'is not connected before sending' do expect(client).not_to be_connected end context 'when given a block that raises' do let(:client) do res = nil begin Riemann::Client.new(host: 'localhost', port: 5555) do |c| res = c raise 'The Boom' end rescue StandardError # swallow the exception end res end it 'in not connected' do expect(client).not_to be_connected end end it 'is connected after sending' do client_with_transport << { state: 'ok', service: 'connected check' } expect(client_with_transport).to be_connected # NOTE: only single transport connected at this point, client.connected? is still false until all transports used end describe '#<<' do subject { wait_for_message_with_id(message_id) } let(:message_id) { next_message_id } before do client_with_transport << { state: 'ok', service: 'test', description: 'desc', metric_f: 1.0, message_id: message_id } end it 'finds the send message' do expect(subject.state).to eq('ok') end end it 'send longs' do roundtrip_metric(0) roundtrip_metric(-3) roundtrip_metric(5) roundtrip_metric(-(2**63)) roundtrip_metric(2**63 - 1) end it 'send doubles' do roundtrip_metric 0.0 roundtrip_metric 12.0 roundtrip_metric 1.2300000190734863 end context 'when sending custom attributes' do subject { wait_for_message_with_id(message_id) } before do event = Riemann::Event.new( service: 'custom', state: 'ok', cats: 'meow', env: 'prod', message_id: message_id ) event[:sneak] = 'attack' client_with_transport << event end let(:message_id) { next_message_id } it 'has the expected service' do expect(subject.service).to eq('custom') end it 'has the expected state' do expect(subject.state).to eq('ok') end it 'has the expected cats' do expect(subject[:cats]).to eq('meow') end it 'has the expected env' do expect(subject[:env]).to eq('prod') end it 'has the expected sneak' do expect(subject[:sneak]).to eq('attack') end end context 'when passing time' do subject { wait_for_message_with_id(message_id) } before do Timecop.freeze client_with_transport << { state: 'ok', service: 'test', time: t, message_id: message_id } end after do Timecop.return end let(:message_id) { next_message_id } let(:t) { (Time.now - 10).to_i } it 'has the expected time' do expect(subject.time).to eq(t) end it 'has the expected time_micros' do expect(subject.time_micros).to eq(t * 1_000_000) end end context 'when passing time_micros' do subject { wait_for_message_with_id(message_id) } before do Timecop.freeze client_with_transport << { state: 'ok', service: 'test', time_micros: t, message_id: message_id } end after do Timecop.return end let(:message_id) { next_message_id } let(:t) { ((Time.now - 10).to_f * 1_000_000).to_i } it 'has the expected time' do expect(subject.time).to eq((Time.now - 10).to_i) end it 'has the expected time_micros' do expect(subject.time_micros).to eq(t) end end context 'when passing no time nor time_micros' do let(:message_id) { next_message_id } let(:time_before) { (Time.now.to_f * 1_000_000).to_i } let(:event) do client_with_transport << { state: 'ok', service: 'timeless test', message_id: message_id } end let(:time_after) { (Time.now.to_f * 1_000_000).to_i } it 'has the expected time_micros' do time_before event time_after e = wait_for_message_with_id(message_id) expect([time_before, e.time_micros, time_after].sort).to eq([time_before, e.time_micros, time_after]) end end describe '#query' do before do message_id1 = next_message_id message_id2 = next_message_id message_id3 = next_message_id client_with_transport << { state: 'critical', service: '1', message_id: message_id1 } client_with_transport << { state: 'warning', service: '2', message_id: message_id2 } client_with_transport << { state: 'critical', service: '3', message_id: message_id3 } wait_for_message_with_id(message_id3) end let(:rate) do t1 = Time.now total = 1000 total.times do |_i| client.query('state = "critical"') end t2 = Time.now total / (t2 - t1) end it 'returns all events without parameters' do expect(client.query.events .map(&:service).to_set).to include(%w[1 2 3].to_set) end it 'returns matched events with parameters' do expect(client.query('state = "critical" and (service = "1" or service = "2" or service = "3")').events .map(&:service).to_set).to eq(%w[1 3].to_set) end it 'query quickly' do puts "\n #{format('%.2f', rate)} queries/sec (#{format('%.2f', (1000 / rate))}ms per query)" expect(rate).to be > 100 end end it '[]' do message_id = next_message_id # expect(client['state = "critical"']).to be_empty client_with_transport << { state: 'critical', message_id: message_id } e = wait_for_message_with_id(message_id) expect(e.state).to eq('critical') end describe '#bulk_send' do let(:message_id1) { next_message_id } let(:message_id2) { next_message_id } let(:event1) { wait_for_message_with_id(message_id1) } let(:event2) { wait_for_message_with_id(message_id2) } before do client_with_transport.bulk_send( [ { state: 'ok', service: 'foo', message_id: message_id1 }, { state: 'warning', service: 'bar', message_id: message_id2 } ] ) end it 'has send the first event' do expect(event2.state).to eq('warning') end it 'has send the second event' do expect(event1.state).to eq('ok') end end context 'when using multiple threads' do let!(:rate) do concurrency = 10 per_thread = 200 total = concurrency * per_thread t1 = Time.now concurrency.times.map do Thread.new do per_thread.times do client_with_transport << { state: 'ok', service: 'test', description: 'desc', metric_f: 1.0, message_id: next_message_id } end end end.each(&:join) t2 = Time.now total / (t2 - t1) end it 'is threadsafe' do puts "\n #{format('%.2f', rate)} inserts/sec (#{format('%.2f', (1000 / rate))}ms per insert)" expect(rate).to be > expected_rate end end end RSpec.shared_examples 'a riemann client that acknowledge messages' do describe '#<<' do subject do client_with_transport << { state: 'ok', service: 'test', description: 'desc', metric_f: 1.0 } end it 'acknowledge the message' do expect(subject.ok).to be_truthy end end context 'when inactive' do let(:message_id1) { next_message_id } let(:message1) do { state: 'warning', service: 'survive TCP inactivity', message_id: message_id1 } end let(:message_id2) { next_message_id } let(:message2) do { state: 'ok', service: 'survive TCP inactivity', message_id: message_id2 } end before do client_with_transport << message1 wait_for_message_with_id(message_id1) end it 'survive inactivity' do sleep INACTIVITY_TIME expect((client_with_transport << message2).ok).to be_truthy wait_for_message_with_id(message_id2) end end context 'when the connection is closed' do let(:message_id1) { next_message_id } let(:message1) do { state: 'warning', service: 'survive TCP local close', message_id: message_id1 } end let(:message_id2) { next_message_id } let(:message2) do { state: 'ok', service: 'survive TCP local close', message_id: message_id2 } end before do client_with_transport << message1 wait_for_message_with_id(message_id1) end it 'survive local close' do client.close expect((client_with_transport << message2).ok).to be_truthy wait_for_message_with_id(message_id2) end end end RSpec.shared_examples 'a riemann client that does not acknowledge messages' do describe '#<<' do subject do client_with_transport << { state: 'ok', service: 'test', description: 'desc', metric_f: 1.0 } end it 'does not acknowledge the message' do expect(subject).to be_nil end end context 'when inactive' do let(:message_id1) { next_message_id } let(:message1) do { state: 'warning', service: 'survive UDP inactivity', message_id: message_id1 } end let(:message_id2) { next_message_id } let(:message2) do { state: 'ok', service: 'survive UDP inactivity', message_id: message_id2 } end before do client_with_transport << message1 wait_for_message_with_id(message_id1) end it 'survive inactivity' do sleep INACTIVITY_TIME client_with_transport << message2 wait_for_message_with_id(message_id2) end end context 'when the connection is closed' do let(:message_id1) { next_message_id } let(:message1) do { state: 'warning', service: 'survive UDP local close', message_id: message_id1 } end let(:message_id2) { next_message_id } let(:message2) do { state: 'ok', service: 'survive UDP local close', message_id: message_id2 } end before do client_with_transport << message1 wait_for_message_with_id(message_id1) end it 'survive local close' do client.close client_with_transport << message2 wait_for_message_with_id(message_id2) end end it 'raise Riemann::Client::Unsupported exception on #[]' do expect { client_with_transport['service = "test"'] }.to raise_error(Riemann::Client::Unsupported) end it 'raise Riemann::Client::Unsupported exception on #query' do expect { client_with_transport.query('service = "test"') }.to raise_error(Riemann::Client::Unsupported) end end riemann-client-1.2.1/CHANGELOG.md0000644000004100000410000001474514523635061016301 0ustar www-datawww-data# Changelog ## [v1.2.1](https://github.com/riemann/riemann-ruby-client/tree/v1.2.1) (2023-07-30) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/v1.2.0...v1.2.1) **Fixed bugs:** - Fix sending large batch of events over TLS [\#51](https://github.com/riemann/riemann-ruby-client/pull/51) ([smortex](https://github.com/smortex)) ## [v1.2.0](https://github.com/riemann/riemann-ruby-client/tree/v1.2.0) (2023-06-28) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/v1.1.0...v1.2.0) **Implemented enhancements:** - Allow connection to Riemann using TLS 1.3 [\#49](https://github.com/riemann/riemann-ruby-client/pull/49) ([smortex](https://github.com/smortex)) ## [v1.1.0](https://github.com/riemann/riemann-ruby-client/tree/v1.1.0) (2023-01-23) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/v1.0.1...v1.1.0) **Implemented enhancements:** - Add support for sending events in bulk [\#44](https://github.com/riemann/riemann-ruby-client/pull/44) ([smortex](https://github.com/smortex)) **Fixed bugs:** - Fix UDP fallback to TCP on large messages [\#46](https://github.com/riemann/riemann-ruby-client/pull/46) ([smortex](https://github.com/smortex)) **Merged pull requests:** - Modernize unit tests [\#45](https://github.com/riemann/riemann-ruby-client/pull/45) ([smortex](https://github.com/smortex)) - Switch from Bacon to RSpec [\#43](https://github.com/riemann/riemann-ruby-client/pull/43) ([smortex](https://github.com/smortex)) - Create codeql-analysis.yml [\#40](https://github.com/riemann/riemann-ruby-client/pull/40) ([jamtur01](https://github.com/jamtur01)) ## [v1.0.1](https://github.com/riemann/riemann-ruby-client/tree/v1.0.1) (2022-06-25) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/v1.0.0...v1.0.1) **Merged pull requests:** - Setup Rubocop and lower required Ruby version [\#37](https://github.com/riemann/riemann-ruby-client/pull/37) ([smortex](https://github.com/smortex)) ## [v1.0.0](https://github.com/riemann/riemann-ruby-client/tree/v1.0.0) (2022-06-16) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/0.2.6...v1.0.0) **Implemented enhancements:** - Add support for micro-seconds resolution [\#34](https://github.com/riemann/riemann-ruby-client/pull/34) ([smortex](https://github.com/smortex)) - Add support for TLS [\#33](https://github.com/riemann/riemann-ruby-client/pull/33) ([smortex](https://github.com/smortex)) - Add support for IPv6 addresses [\#30](https://github.com/riemann/riemann-ruby-client/pull/30) ([dch](https://github.com/dch)) **Merged pull requests:** - Fix race conditions in CI [\#35](https://github.com/riemann/riemann-ruby-client/pull/35) ([smortex](https://github.com/smortex)) - Modernize and setup CI [\#32](https://github.com/riemann/riemann-ruby-client/pull/32) ([smortex](https://github.com/smortex)) - Bump beefcake dependency [\#29](https://github.com/riemann/riemann-ruby-client/pull/29) ([dch](https://github.com/dch)) ## [0.2.6](https://github.com/riemann/riemann-ruby-client/tree/0.2.6) (2015-11-18) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/0.2.5...0.2.6) **Merged pull requests:** - Client should yield self [\#22](https://github.com/riemann/riemann-ruby-client/pull/22) ([agile](https://github.com/agile)) - Allow TCP sockets to work on Windows [\#21](https://github.com/riemann/riemann-ruby-client/pull/21) ([sgran](https://github.com/sgran)) - README hash syntax fix [\#20](https://github.com/riemann/riemann-ruby-client/pull/20) ([squarism](https://github.com/squarism)) ## [0.2.5](https://github.com/riemann/riemann-ruby-client/tree/0.2.5) (2015-02-05) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/0.2.4...0.2.5) ## [0.2.4](https://github.com/riemann/riemann-ruby-client/tree/0.2.4) (2015-02-03) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/0.2.2...0.2.4) **Merged pull requests:** - Tightening beefcake requirement [\#19](https://github.com/riemann/riemann-ruby-client/pull/19) ([aphyr](https://github.com/aphyr)) - Fix for \#17, plus test and connection refactor [\#18](https://github.com/riemann/riemann-ruby-client/pull/18) ([RKelln](https://github.com/RKelln)) - Ensure that we close the connection if we got an error back [\#16](https://github.com/riemann/riemann-ruby-client/pull/16) ([eric](https://github.com/eric)) - String\#clear doesn't exist in 1.8 [\#15](https://github.com/riemann/riemann-ruby-client/pull/15) ([eric](https://github.com/eric)) - Tcp client with timeouts [\#14](https://github.com/riemann/riemann-ruby-client/pull/14) ([eric](https://github.com/eric)) ## [0.2.2](https://github.com/riemann/riemann-ruby-client/tree/0.2.2) (2013-05-28) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/0.2.0...0.2.2) **Merged pull requests:** - Update README with timeout information [\#11](https://github.com/riemann/riemann-ruby-client/pull/11) ([gsandie](https://github.com/gsandie)) - Add tcp socket timeouts [\#10](https://github.com/riemann/riemann-ruby-client/pull/10) ([gsandie](https://github.com/gsandie)) - Socket can not be opened in method connected? [\#9](https://github.com/riemann/riemann-ruby-client/pull/9) ([vadv](https://github.com/vadv)) ## [0.2.0](https://github.com/riemann/riemann-ruby-client/tree/0.2.0) (2013-04-02) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/version-0.0.7...0.2.0) **Merged pull requests:** - Get and set attributes using hash-style accessors [\#8](https://github.com/riemann/riemann-ruby-client/pull/8) ([jegt](https://github.com/jegt)) - Add extra attributes added to the Event constructor to the event as Attribute instances [\#7](https://github.com/riemann/riemann-ruby-client/pull/7) ([jegt](https://github.com/jegt)) - Change attribute name to attribute key [\#6](https://github.com/riemann/riemann-ruby-client/pull/6) ([b](https://github.com/b)) - Arbitrary attributes on events [\#5](https://github.com/riemann/riemann-ruby-client/pull/5) ([b](https://github.com/b)) ## [version-0.0.7](https://github.com/riemann/riemann-ruby-client/tree/version-0.0.7) (2012-04-16) [Full Changelog](https://github.com/riemann/riemann-ruby-client/compare/fe25a3b01681612defc39250006748069e06a172...version-0.0.7) **Merged pull requests:** - Add support for ruby 1.8 [\#1](https://github.com/riemann/riemann-ruby-client/pull/1) ([eric](https://github.com/eric)) \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* riemann-client-1.2.1/.rubocop.yml0000644000004100000410000000052714523635061016733 0ustar www-datawww-data--- AllCops: TargetRubyVersion: 2.6 require: - rubocop-rspec Metrics/AbcSize: Enabled: false Metrics/BlockLength: Enabled: false Metrics/ClassLength: Enabled: false Metrics/CyclomaticComplexity: Enabled: false Metrics/MethodLength: Enabled: false Metrics/PerceivedComplexity: Enabled: false Style/Documentation: Enabled: false riemann-client-1.2.1/.gitignore0000644000004100000410000000010614523635061016442 0ustar www-datawww-datapkg/ ._* *~ .DS_Store .*.swp *.log .bundle/ bin/ vendor/ Gemfile.lock riemann-client-1.2.1/riemann-client.gemspec0000644000004100000410000000236714523635061020737 0ustar www-datawww-data# frozen_string_literal: true require 'English' lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'riemann/version' Gem::Specification.new do |spec| spec.name = 'riemann-client' spec.version = Riemann::VERSION spec.author = 'Kyle Kingsbury' spec.email = 'aphyr@aphyr.com' spec.summary = 'Client for the distributed event system Riemann.' spec.description = 'Client for the distributed event system Riemann.' spec.homepage = 'https://github.com/aphyr/riemann-ruby-client' spec.license = 'MIT' spec.platform = Gem::Platform::RUBY spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.required_ruby_version = '>= 2.6.0' spec.add_development_dependency 'bundler', '>= 1.3' spec.add_development_dependency 'rspec' spec.add_development_dependency 'rubocop' spec.add_development_dependency 'rubocop-rspec' spec.add_development_dependency 'timecop' spec.add_dependency 'beefcake', ['>= 1.0.0 '] spec.add_dependency 'mtrc', '>= 0.0.4' end riemann-client-1.2.1/LICENSE0000644000004100000410000000206314523635061015463 0ustar www-datawww-dataThe MIT License Copyright (c) 2011 Kyle Kingsbury Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. riemann-client-1.2.1/Rakefile0000644000004100000410000000060314523635061016121 0ustar www-datawww-data# frozen_string_literal: true require 'riemann' require 'bundler/gem_tasks' require 'github_changelog_generator/task' GitHubChangelogGenerator::RakeTask.new :changelog do |config| config.user = 'riemann' config.project = 'riemann-ruby-client' config.exclude_labels = ['skip-changelog'] config.future_release = "v#{Riemann::VERSION}" config.add_issues_wo_labels = false end riemann-client-1.2.1/lib/0000755000004100000410000000000014523635061015223 5ustar www-datawww-datariemann-client-1.2.1/lib/riemann.rb0000644000004100000410000000042414523635061017201 0ustar www-datawww-data# frozen_string_literal: true module Riemann require 'rubygems' require 'beefcake' require 'timeout' require 'riemann/version' require 'riemann/state' require 'riemann/attribute' require 'riemann/event' require 'riemann/query' require 'riemann/message' end riemann-client-1.2.1/lib/riemann/0000755000004100000410000000000014523635061016654 5ustar www-datawww-datariemann-client-1.2.1/lib/riemann/metric_thread.rb0000644000004100000410000000245014523635061022014 0ustar www-datawww-data# frozen_string_literal: true module Riemann class MetricThread # A metric thread is simple: it wraps some metric object which responds to <<, # and every interval seconds, calls #flush which replaces the object and calls # a user specified function. INTERVAL = 10 attr_accessor :interval, :metric # client = Riemann::Client.new # m = MetricThread.new Mtrc::Rate do |rate| # client << rate # end # # loop do # sleep rand # m << rand # end def initialize(klass, *klass_args, &block) @klass = klass @klass_args = klass_args @block = block @interval = INTERVAL @metric = new_metric start end def <<(value) @metric.<<(value) end def new_metric @klass.new(*@klass_args) end def flush old = @metric @metric = new_metric @block[old] end def start raise 'already running' if @runner @running = true @runner = Thread.new do while @running sleep @interval begin flush rescue StandardError # ignore end end @runner = nil end end def stop stop! @runner.join end def stop! @running = false end end end riemann-client-1.2.1/lib/riemann/client/0000755000004100000410000000000014523635061020132 5ustar www-datawww-datariemann-client-1.2.1/lib/riemann/client/udp.rb0000644000004100000410000000202514523635061021246 0ustar www-datawww-data# frozen_string_literal: true module Riemann class Client class UDP < Client MAX_SIZE = 16_384 attr_accessor :host, :port, :max_size def initialize(opts = {}) # rubocop:disable Lint/MissingSuper @host = opts[:host] || HOST @port = opts[:port] || PORT @max_size = opts[:max_size] || MAX_SIZE @socket = nil end def socket return @socket if connected? @socket = UDPSocket.new end def close @socket.close if connected? @socket = nil end def connected? @socket && !@socket.closed? end # Read a message from a stream def read_message(_socket) raise Unsupported end def send_recv(_message) raise Unsupported end def send_maybe_recv(message) encoded_string = message.encode.to_s raise TooBig unless encoded_string.length < @max_size socket.send(encoded_string, 0, @host, @port) nil end end end end riemann-client-1.2.1/lib/riemann/client/tcp_socket.rb0000644000004100000410000002636214523635061022626 0ustar www-datawww-data# frozen_string_literal: true require 'socket' require 'fcntl' module Riemann class Client # Socket: A specialized socket that has been configure class TcpSocket class Error < Riemann::Client::Error; end class Timeout < Error; end # Internal: # The timeout for reading in seconds. Defaults to 2 attr_accessor :read_timeout # Internal: # The timeout for connecting in seconds. Defaults to 2 attr_reader :connect_timeout # Internal: # The timeout for writing in seconds. Defaults to 2 attr_reader :write_timeout # Internal: # The host this socket is connected to attr_reader :host # Internal: # The port this socket is connected to attr_reader :port # Internal # # Used for setting TCP_KEEPIDLE: overrides tcp_keepalive_time for a single # socket. # # http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html # # tcp_keepalive_time: # # The interval between the last data packet sent (simple ACKs are not # considered data) and the first keepalive probe; after the connection is # marked to need keepalive, this counter is not used any further. attr_reader :keepalive_idle # Internal # # Used for setting TCP_KEEPINTVL: overrides tcp_keepalive_intvl for a single # socket. # # http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html # # tcp_keepalive_intvl: # # The interval between subsequential keepalive probes, regardless of what # the connection has exchanged in the meantime. attr_reader :keepalive_interval # Internal # # Used for setting TCP_KEEPCNT: overrides tcp_keepalive_probes for a single # socket. # # http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html # # tcp_keepalive_probes: # # The number of unacknowledged probes to send before considering the # connection dead and notifying the application layer. attr_reader :keepalive_count # Internal: Create and connect to the given location. # # options, same as Constructor # # Returns an instance of KJess::Socket def self.connect(options = {}) s = new(options) s.connect s end # Internal: Creates a new KJess::Socket def initialize(options = {}) @host = options[:host] @port = options[:port] @connect_timeout = options[:connect_timeout] || options[:timeout] || 2 @read_timeout = options[:read_timeout] || options[:timeout] || 2 @write_timeout = options[:write_timeout] || options[:timeout] || 2 @keepalive_active = options.fetch(:keepalive_active, true) @keepalive_idle = options[:keepalive_idle] || 60 @keepalive_interval = options[:keepalive_interval] || 30 @keepalive_count = options[:keepalive_count] || 5 @socket = nil end # Internal: Return whether or not the keepalive_active flag is set. def keepalive_active? @keepalive_active end # Internal: Low level socket allocation and option configuration # # Using the options from the initializer, a new ::Socket is created that # is: # # TCP, autoclosing on exit, nagle's algorithm is disabled and has # TCP Keepalive options set if keepalive is supported. # # Returns a new ::Socket instance for def socket_factory(type) sock = ::Socket.new(type, ::Socket::SOCK_STREAM, 0) # close file descriptors if we exec if Fcntl.constants.include?(:F_SETFD) && Fcntl.constants.include?(:FD_CLOEXEC) sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) end # Disable Nagle's algorithm sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1) if using_keepalive? sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE, keepalive_idle) sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, keepalive_interval) sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT, keepalive_count) end sock end # Internal: Return the connected raw Socket. # # If the socket is closed or non-existent it will create and connect again. # # Returns a ::Socket def socket return @socket unless closed? @socket ||= connect end # Internal: Closes the internal ::Socket # # Returns nothing def close @socket.close unless closed? @socket = nil end # Internal: Return true the socket is closed. def closed? return true if @socket.nil? return true if @socket.closed? false end # Internal: # # Connect to the remote host in a non-blocking fashion. # # Raise Error if there is a failure connecting. # # Return the ::Socket on success def connect # Calculate our timeout deadline deadline = Time.now.to_f + connect_timeout # Lookup destination address, we only want TCP. addrs = ::Socket.getaddrinfo(host, port, nil, ::Socket::SOCK_STREAM) errors = [] conn_error = -> { raise errors.first } sock = nil # Sort it so we get AF_INET, IPv4 addrs.sort.find(conn_error) do |addr| sock = connect_or_error(addr, deadline, errors) end sock end # Internal: Connect to the destination or raise an error. # # Connect to the address or capture the error of the connection # # addr - An address returned from Socket.getaddrinfo() # deadline - the after which we should raise a timeout error # errors - a collection of errors to append an error too should we have one. # # Make an attempt to connect to the given address. If it is successful, # return the socket. # # Should the connection fail, append the exception to the errors array and # return false. # def connect_or_error(addr, deadline, errors) timeout = deadline - Time.now.to_f raise Timeout, "Could not connect to #{host}:#{port}" if timeout <= 0 connect_nonblock(addr, timeout) rescue Error => e errors << e false end # Internal: Connect to the give address within the timeout. # # Make an attempt to connect to a single address within the given timeout. # # Return the ::Socket when it is connected, or raise an Error if no # connection was possible. def connect_nonblock(addr, timeout) sockaddr = ::Socket.pack_sockaddr_in(addr[1], addr[3]) sock = socket_factory(addr[4]) sock.connect_nonblock(sockaddr) sock rescue Errno::EINPROGRESS if IO.select(nil, [sock], nil, timeout).nil? begin sock.close rescue StandardError nil end raise Timeout, "Could not connect to #{host}:#{port} within #{timeout} seconds" end connect_nonblock_finalize(sock, sockaddr) rescue StandardError => e begin sock.close rescue StandardError nil end raise Error, "Could not connect to #{host}:#{port}: #{e.class}: #{e.message}", e.backtrace end # Internal: Make sure that a non-blocking connect has truely connected. # # Ensure that the given socket is actually connected to the given adddress. # # Returning the socket if it is and raising an Error if it isn't. def connect_nonblock_finalize(sock, sockaddr) sock.connect_nonblock(sockaddr) sock rescue Errno::EISCONN sock rescue StandardError => e begin sock.close rescue StandardError nil end raise Error, "Could not connect to #{host}:#{port}: #{e.class}: #{e.message}", e.backtrace end # Internal: say if we are using TCP Keep Alive or not # # We will return true if the initialization options :keepalive_active is # set to true, and if all the constants that are necessary to use TCP keep # alive are defined. # # It may be the case that on some operating systems that the constants are # not defined, so in that case we do not want to attempt to use tcp keep # alive if we are unable to do so in any case. # # Returns true or false def using_keepalive? using = false if keepalive_active? using = %i[SOL_SOCKET SO_KEEPALIVE SOL_TCP TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT].all? do |c| ::Socket.const_defined? c end end using end # Reads length bytes from the socket # # length - the number of bytes to read from the socket # outbuf - an optional buffer to store the bytes in # # Returns the bytes read if no outbuf is specified def read(length, outbuf = nil) if outbuf outbuf.replace('') buf = outbuf else buf = String.new end while buf.length < length unless (rb = readpartial(length - buf.length)) break end buf << rb end buf end # Internal: Read up to a maxlen of data from the socket and store it in outbuf # # maxlen - the maximum number of bytes to read from the socket # outbuf - the buffer in which to store the bytes. # # Returns the bytes read def readpartial(maxlen, outbuf = nil) socket.read_nonblock(maxlen, outbuf) rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNRESET, IO::WaitReadable unless wait_readable(read_timeout) raise Timeout, "Could not read from #{host}:#{port} in #{read_timeout} seconds" end retry end # Internal: Write the given data to the socket # # buf - the data to write to the socket. # # Raises an error if it is unable to write the data to the socket within the # write_timeout. # # returns nothing def write(buf) until buf.nil? || buf.empty? written = socket.write_nonblock(buf) buf = buf[written, buf.length] end rescue Errno::EWOULDBLOCK, Errno::EINTR, Errno::EAGAIN, Errno::ECONNRESET, IO::WaitWritable unless wait_writable(write_timeout) raise Timeout, "Could not write to #{host}:#{port} in #{write_timeout} seconds" end retry rescue IO::WaitReadable # Also rescued for SSL renegotiation in OpenSSL::SSL::SSLSocket according to # https://ruby-doc.org/core-2.7.1/IO.html#method-c-select unless wait_readable(read_timeout) raise Timeout, "Could not write to #{host}:#{port} in #{write_timeout} seconds" end retry end def wait_writable(timeout = nil) IO.select(nil, [@socket], nil, timeout || write_timeout) end def wait_readable(timeout = nil) IO.select([@socket], nil, nil, timeout || read_timeout) end end end end riemann-client-1.2.1/lib/riemann/client/tcp.rb0000644000004100000410000000507014523635061021247 0ustar www-datawww-data# frozen_string_literal: true require 'monitor' require 'riemann/client/tcp_socket' require 'riemann/client/ssl_socket' module Riemann class Client class TCP < Client attr_accessor :host, :port # Public: Set a socket factory -- an object responding # to #call(options) that returns a Socket object class << self attr_writer :socket_factory end # Public: Return a socket factory def self.socket_factory @socket_factory ||= proc { |options| if options[:ssl] SSLSocket.connect(options) else TcpSocket.connect(options) end } end def initialize(options = {}) # rubocop:disable Lint/MissingSuper @options = options @locket = Monitor.new @socket = nil @pid = nil end def socket @locket.synchronize do close if @pid && @pid != Process.pid return @socket if connected? @socket = self.class.socket_factory.call(@options) @pid = Process.pid return @socket end end def close @locket.synchronize do @socket.close if connected? @socket = nil end end def connected? @locket.synchronize do !@socket.nil? && !@socket.closed? end end # Read a message from a stream def read_message(socket) unless (buffer = socket.read(4)) && (buffer.size == 4) raise InvalidResponse, 'unexpected EOF' end length = buffer.unpack1('N') begin str = socket.read length message = Riemann::Message.decode str rescue StandardError puts "Message was #{str.inspect}" raise end unless message.ok puts 'Failed' raise ServerError, message.error end message end def send_recv(message) with_connection do |socket| socket.write(message.encode_with_length) read_message(socket) end end alias send_maybe_recv send_recv # Yields a connection in the block. def with_connection tries = 0 @locket.synchronize do tries += 1 yield(socket) rescue IOError, Errno::EPIPE, Errno::ECONNREFUSED, InvalidResponse, Timeout::Error, Riemann::Client::TcpSocket::Error close raise if tries > 3 retry rescue StandardError close raise end end end end end riemann-client-1.2.1/lib/riemann/client/ssl_socket.rb0000644000004100000410000000344714523635061022640 0ustar www-datawww-data# frozen_string_literal: true require 'openssl' require_relative 'tcp_socket' module Riemann class Client # Socket: A specialized socket that has been configure class SSLSocket < TcpSocket def initialize(options = {}) super(options) @key_file = options[:key_file] @cert_file = options[:cert_file] @ca_file = options[:ca_file] @ssl_verify = options[:ssl_verify] end def ssl_context @ssl_context ||= OpenSSL::SSL::SSLContext.new.tap do |ctx| ctx.key = OpenSSL::PKey::RSA.new(File.read(@key_file)) ctx.cert = OpenSSL::X509::Certificate.new(File.read(@cert_file)) ctx.ca_file = @ca_file if @ca_file ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER if @ssl_verify end end # Internal: Connect to the give address within the timeout. # # Make an attempt to connect to a single address within the given timeout. # # Return the ::Socket when it is connected, or raise an Error if no # connection was possible. def connect_nonblock(addr, timeout) sock = super(addr, timeout) ssl_socket = OpenSSL::SSL::SSLSocket.new(sock, ssl_context) ssl_socket.sync = true begin ssl_socket.connect_nonblock rescue IO::WaitReadable unless IO.select([ssl_socket], nil, nil, timeout) raise Timeout, "Could not read from #{host}:#{port} in #{timeout} seconds" end retry rescue IO::WaitWritable unless IO.select(nil, [ssl_socket], nil, timeout) raise Timeout, "Could not write to #{host}:#{port} in #{timeout} seconds" end retry end ssl_socket end end end end riemann-client-1.2.1/lib/riemann/version.rb0000644000004100000410000000010614523635061020663 0ustar www-datawww-data# frozen_string_literal: true module Riemann VERSION = '1.2.1' end riemann-client-1.2.1/lib/riemann/query.rb0000644000004100000410000000020614523635061020344 0ustar www-datawww-data# frozen_string_literal: true module Riemann class Query include Beefcake::Message optional :string, :string, 1 end end riemann-client-1.2.1/lib/riemann/attribute.rb0000644000004100000410000000024714523635061021207 0ustar www-datawww-data# frozen_string_literal: true module Riemann class Attribute include Beefcake::Message required :key, :string, 1 optional :value, :string, 2 end end riemann-client-1.2.1/lib/riemann/message.rb0000644000004100000410000000057714523635061020636 0ustar www-datawww-data# frozen_string_literal: true module Riemann class Message include Beefcake::Message optional :ok, :bool, 2 optional :error, :string, 3 repeated :states, State, 4 optional :query, Query, 5 repeated :events, Event, 6 def encode_with_length encoded_string = encode.to_s [encoded_string.length].pack('N') << encoded_string end end end riemann-client-1.2.1/lib/riemann/client.rb0000644000004100000410000000515214523635061020462 0ustar www-datawww-data# frozen_string_literal: true require 'riemann' module Riemann class Client class Error < RuntimeError; end class InvalidResponse < Error; end class ServerError < Error; end class Unsupported < Error; end class TooBig < Unsupported; end require 'socket' require 'time' HOST = '127.0.0.1' PORT = 5555 TIMEOUT = 5 require 'riemann/client/tcp' require 'riemann/client/udp' attr_reader :tcp, :udp def initialize(opts = {}) @options = opts.dup @options[:host] ||= HOST @options[:port] ||= PORT @options[:timeout] ||= TIMEOUT @udp = UDP.new(@options) @tcp = TCP.new(@options) return unless block_given? begin yield self ensure close end end def host @options[:host] end def port @options[:port] end def timeout @options[:timeout] end # Send a state def <<(event) # Create state case event when Riemann::State, Riemann::Event, Hash # Noop else raise(ArgumentError, "Unsupported event class: #{event.class.name}") end bulk_send([event]) end def bulk_send(events) raise ArgumentError unless events.is_a?(Array) message = Riemann::Message.new(events: normalize_events(events)) send_maybe_recv(message) end def normalize_events(events) events.map do |event| case event when Riemann::State, Riemann::Event event when Hash e = if event.include?(:host) event else event.dup.merge(host: Socket.gethostname) end Riemann::Event.new(e) else raise(ArgumentError, "Unsupported event class: #{event.class.name}") end end end # Returns an array of states matching query. def [](query) response = query(query) (response.events || []) | (response.states || []) end def connect # NOTE: connections are made automatically on send warn 'Riemann client#connect is deprecated' end # Close both UDP and TCP sockets. def close @udp.close @tcp.close end def connected? tcp.connected? and udp.connected? end # Ask for states def query(string = 'true') send_recv Riemann::Message.new(query: Riemann::Query.new(string: string)) end def send_recv(message) @tcp.send_recv(message) end def send_maybe_recv(message) @udp.send_maybe_recv(message) rescue TooBig @tcp.send_maybe_recv(message) end end end riemann-client-1.2.1/lib/riemann/event.rb0000644000004100000410000001364114523635061020327 0ustar www-datawww-data# frozen_string_literal: true module Riemann class Event require 'set' include Beefcake::Message optional :time, :int64, 1 optional :state, :string, 2 optional :service, :string, 3 optional :host, :string, 4 optional :description, :string, 5 repeated :tags, :string, 7 optional :ttl, :float, 8 repeated :attributes, Attribute, 9 optional :time_micros, :int64, 10 optional :metric_sint64, :sint64, 13 optional :metric_d, :double, 14 optional :metric_f, :float, 15 # Fields which don't really exist in protobufs, but which are reserved # and can't be used as attributes. VIRTUAL_FIELDS = Set.new([:metric]) # Fields which are specially encoded in the Event protobuf--that is, they # can't be used as attributes. RESERVED_FIELDS = fields.map do |_i, field| field.name.to_sym end.reduce(VIRTUAL_FIELDS) do |set, field| # rubocop:disable Style/MultilineBlockChain set << field end def self.now (Time.now.to_f * 1_000_000).to_i end # Average a set of states together. Chooses the mean metric, the mode # state, mode service, and the mean time. If init is provided, its values # override (where present) the computed ones. def self.average(states, init = Event.new) init = case init when Event init.dup else Event.new init end # Metric init.metric_f ||= states.inject(0.0) do |a, state| a + (state.metric || 0) end / states.size init.metric_f = 0.0 if init.metric_f.nan? # Event init.state ||= mode states.map(&:state) init.service ||= mode states.map(&:service) # Time init.time_micros = begin times = states.map(&:time_micros).compact (times.inject(:+) / times.size).to_i rescue ZeroDivisionError nil end init.time_micros ||= now init end # Sum a set of states together. Adds metrics, takes the mode state, mode # service and the mean time. If init is provided, its values override # (where present) the computed ones. def self.sum(states, init = Event.new) init = case init when Event init.dup else Event.new init end # Metric init.metric_f ||= states.inject(0.0) do |a, state| a + (state.metric || 0) end init.metric_f = 0.0 if init.metric_f.nan? # Event init.state ||= mode states.map(&:state) init.service ||= mode states.map(&:service) # Time init.time_micros = begin times = states.map(&:time_micros).compact (times.inject(:+) / times.size).to_i rescue ZeroDivisionError nil end init.time_micros ||= now init end # Finds the maximum of a set of states. Metric is the maximum. Event is the # highest, as defined by Dash.config.state_order. Time is the mean. def self.max(states, init = Event.new) init = case init when Event init.dup else Event.new init end # Metric init.metric_f ||= states.inject(0.0) do |a, state| a + (state.metric || 0) end init.metric = 0.0 if init.metric.nan? # Event init.state ||= states.inject(nil) do |max, state| state.state if Dash.config[:state_order][state.state] > Dash.config[:state_order][max] end # Time init.time_micros = begin times = states.map(&:time_micros).compact (times.inject(:+) / times.size).to_i rescue ZeroDivisionError nil end init.time_micros ||= now init end def self.mode(array) array.each_with_object(Hash.new(0)) do |e, counts| counts[e] += 1 end.max_by { |_e, count| count }.first # rubocop:disable Style/MultilineBlockChain rescue StandardError nil end # Partition a list of states by a field # Returns a hash of field_value => state def self.partition(states, field) states.each_with_object({}) do |state, p| k = state.send field if p.include? k p[k] << state else p[k] = [state] end end end # Sorts states by a field. nil values first. def self.sort(states, field) states.sort do |a, b| a = a.send field b = b.send field if a.nil? -1 elsif b.nil? 1 else a <=> b end end end def initialize(hash = nil) if hash super hash self.metric = hash[:metric] if hash[:metric] # Add extra attributes to the event as Attribute instances with values # converted to String self.attributes = hash.map do |key, _value| unless RESERVED_FIELDS.include? key.to_sym Attribute.new(key: key.to_s, value: (hash[key] || hash[key.to_sym]).to_s) end end.compact else super() end @time_micros ||= self.class.now unless @time end def metric metric_d || metric_sint64 || metric_f end def metric=(value) if value.is_a?(Integer) && (-(2**63)...2**63).include?(value) # Long self.metric_sint64 = value else self.metric_d = value.to_f end self.metric_f = value.to_f end # Look up attributes def [](key) if RESERVED_FIELDS.include? key.to_sym super else attributes.find { |a| a.key.to_s == key.to_s }.value end end # Set attributes def []=(key, value) if RESERVED_FIELDS.include? key.to_sym super else attr = attributes.find { |a| a.key == key.to_s } if attr attr.value = value.to_s else attributes << Attribute.new(key: key.to_s, value: value.to_s) end end end end end riemann-client-1.2.1/lib/riemann/auto_state.rb0000644000004100000410000000474014523635061021356 0ustar www-datawww-data# frozen_string_literal: true module Riemann class AutoState # Binds together a state hash and a Client. Any change made here # sends the state to the client. Useful when updates to a state are made # decoherently, e.g. across many methods. Combine with MetricThread (or # just Thread.new { loop { autostate.flush; sleep n } }) to ensure regular # updates. # # example: # # class Job # def initialize # @state = AutoState.new # @state.service = 'job' # @state.state = 'starting up' # # run # end # # def run # loop do # begin # a # b # rescue Exception => e # @state.once( # state: 'error', # description: e.to_s # ) # end # end # end # # def a # @state.state = 'heavy lifting a' # ... # end # # def b # @state.state = 'heavy lifting b' # ... # end def initialize(client = Client.new, state = {}) @client = client @state = state end def description=(description) @state[:description] = description flush end def description @state[:description] end # Send state to client def flush @state[:time] = Time.now.to_i @client << @state end def host=(host) @state[:host] = host flush end def host @state[:host] end def metric=(metric) @state[:metric] = metric flush end alias metric_f= metric= def metric @state[:metric] end alias metric_f metric # Performs multiple updates, followed by flush. # Example: merge state: critical, metric_f: 10235.3 def merge(opts) @state.merge! opts flush end alias << merge # Issues an immediate update of the state with tag "once" # set, but does not update the local state. Useful for transient errors. # Opts are merged with the state. def once(opts) o = @state.merge opts o[:time] = Time.now.to_i o[:tags] = begin (o[:tags] | ['once']) rescue StandardError ['once'] end @client << o end def state=(state) @state[:state] = state flush end def state @state[:state] end def service=(service) @state[:service] = service flush end def service @state[:service] end end end riemann-client-1.2.1/lib/riemann/state.rb0000644000004100000410000000103014523635061020313 0ustar www-datawww-data# frozen_string_literal: true module Riemann class State include Beefcake::Message optional :time, :int64, 1 optional :state, :string, 2 optional :service, :string, 3 optional :host, :string, 4 optional :description, :string, 5 optional :once, :bool, 6 repeated :tags, :string, 7 optional :ttl, :float, 8 optional :metric_f, :float, 15 def initialize super @time ||= Time.now.to_i end def metric @metric || metric_f end attr_writer :metric end end riemann-client-1.2.1/README.markdown0000644000004100000410000000400414523635061017154 0ustar www-datawww-data# Riemann Ruby Client [![CI](https://github.com/riemann/riemann-ruby-client/actions/workflows/ci.yml/badge.svg)](https://github.com/riemann/riemann-ruby-client/actions/workflows/ci.yml) ## Installing ```shell gem install riemann-client ``` ## Use ```ruby require 'riemann/client' # Create a client. Host, port and timeout are optional. c = Riemann::Client.new host: 'localhost', port: 5555, timeout: 5 # Send a simple event c << {service: 'testing', metric: 2.5} # Or a more complex one c << { host: 'web3', service: 'api latency', state: 'warn', metric: 63.5, description: "63.5 milliseconds per request", time: Time.now.to_i - 10 } # :host defaults to gethostname(). :time defaults to current unix time. You # can explicitly override host... c << {host: nil, service: 'the cloud', state: 'nebulous'} # Get all the states from the server c['true'] # Or specific states matching a query c['host =~ "%.dc1" and (state = "critical" or state = "warning")'] ``` ## Transports Riemann::Client sends small events over UDP by default, and uses TCP for queries and large events. UDP sends are essentially "shouting into the void". They will not block your application and are roughly an order of magnitude faster than TCP, but you will not know if the server is down or encountered an error. You can specify what transport to use by selecting a subclient: ``` ruby c.udp << { :state => "ok" } # => nil c.tcp << { :state => "ok" } # => # c.tcp["true"] # => [#, ...] c.udp["true"] # => raise Riemann::Client::Unsupported ``` ## Client state management Riemann::Client provides some classes to make managing state updates easier. Riemann::MetricThread starts a thread to poll a metric periodically, which can be used to flush an accumulated value to ustate at regular intervals. Riemann::AutoState bundles a state and a client together. Any changes to the AutoState automatically send the new state to the client. ## License The MIT License Copyright (c) 2011-2022 Kyle Kingsbury riemann-client-1.2.1/Gemfile0000644000004100000410000000015014523635061015744 0ustar www-datawww-data# frozen_string_literal: true source 'https://rubygems.org' gemspec gem 'github_changelog_generator' riemann-client-1.2.1/.github/0000755000004100000410000000000014523635061016015 5ustar www-datawww-datariemann-client-1.2.1/.github/dependabot.yml0000644000004100000410000000123614523635061020647 0ustar www-datawww-data# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: # Open PR for gem updates - package-ecosystem: "bundler" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "daily" # Open PR for GitHub Actions updates - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" riemann-client-1.2.1/.github/workflows/0000755000004100000410000000000014523635061020052 5ustar www-datawww-datariemann-client-1.2.1/.github/workflows/codeql-analysis.yml0000644000004100000410000000524614523635061023674 0ustar www-datawww-data# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ "main" ] pull_request: # The branches below must be a subset of the branches above branches: [ "main" ] schedule: - cron: '33 17 * * 6' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'ruby' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v2 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # If the Autobuild fails above, remove it and uncomment the following three lines. # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. # - run: | # echo "Run, Build Application using script" # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 riemann-client-1.2.1/.github/workflows/ci.yml0000644000004100000410000000311514523635061021170 0ustar www-datawww-data--- name: CI on: push: branches: - main pull_request: branches: - main jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup ruby uses: ruby/setup-ruby@v1 with: ruby-version: '2.7' bundler-cache: true - name: Run rubocop run: bundle exec rubocop test: needs: lint runs-on: ubuntu-latest strategy: matrix: ruby-version: - '2.6' - '2.7' - '3.0' - '3.1' - '3.2' steps: - uses: actions/checkout@v3 - name: Setup Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - name: Install riemann run: | wget --quiet https://github.com/riemann/riemann/releases/download/0.3.8/riemann_0.3.8_all.deb sudo dpkg -i riemann_0.3.8_all.deb sudo systemctl stop riemann sudo openssl genrsa -out /etc/riemann/riemann_server.key 4096 sudo openssl pkcs8 -topk8 -nocrypt -in /etc/riemann/riemann_server.key -out /etc/riemann/riemann_server.pkcs8 sudo openssl req -x509 -new -nodes -key /etc/riemann/riemann_server.key -days 7 -out /etc/riemann/riemann_server.crt -subj '/CN=localhost' sudo chmod +r /etc/riemann/riemann_server.pkcs8 sudo cp -v spec/riemann.config /etc/riemann/ sudo systemctl start riemann while ! nc -z localhost 5555; do sleep 1; done - name: Run the test suite run: bundle exec rspec riemann-client-1.2.1/SECURITY.md0000644000004100000410000000430314523635061016246 0ustar www-datawww-data# Riemann Security and Disclosure Information This page describes Riemann security and disclosure information. ## Supported Versions The currently supported version of Riemann for security-patching purposes is always the latest version. ## Security Announcements Will be made on the [Riemann mailing list](https://groups.google.com/g/riemann-users?pli=1). ## Report a Vulnerability We're extremely grateful for security researchers and users that report vulnerabilities to Riemann. All reports are thoroughly investigated by the maintainers. To make a report, you should email the private security@riemann.io list with the details. ## When Should I Report a Vulnerability? * You think you discovered a potential security vulnerability in Riemann. * You are unsure how a vulnerability affects Riemann. * You think you discovered a vulnerability in another project that Riemann depends on For projects with their own vulnerability reporting and disclosure process, please report it directly there. ## When Should I NOT Report a Vulnerability? * You need help tuning Riemann components for security * You need help applying security related updates * Your issue is not security related ## Security Vulnerability Response Each report is acknowledged and analyzed within 5 working days. Any vulnerability information shared stays within Riemann project and will not be disseminated to other projects unless it is necessary to get the issue fixed. As the security issue moves from triage, to identified fix, to release planning we will keep the reporter updated. ## Public Disclosure Timing A public disclosure date is negotiated by the Riemann maintainers nd the bug submitter. We prefer to fully disclose the bug as soon as possible once a user mitigation is available. It is reasonable to delay disclosure when the bug or the fix is not yet fully understood, the solution is not well-tested, or for vendor coordination. The timeframe for disclosure is from immediate (especially if it's already publicly known) to a few weeks. For a vulnerability with a straightforward mitigation, we expect report date to disclosure date to be on the order of 7 days. The Riemann maintainers hold the final say when setting a disclosure date.