diff --git a/.rubocop.yml b/.rubocop.yml index 4c209957..4bf094ec 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,14 +1,22 @@ --- AllCops: + NewCops: enable Exclude: - lib/riemann/tools/*_parser.tab.rb - vendor/bundle/**/* +require: + - rubocop-rake + - rubocop-rspec +Gemspec/RequireMFA: + Enabled: false Gemspec/RequiredRubyVersion: Enabled: false Layout/HashAlignment: EnforcedHashRocketStyle: table Layout/LineLength: Enabled: false +Lint/EmptyBlock: + Enabled: false Metrics/AbcSize: Enabled: false Metrics/BlockLength: @@ -28,10 +36,22 @@ Naming/MethodParameterName: - az # availability zone - id - lb # load balancer +RSpec/ExampleLength: + Enabled: false +RSpec/MultipleExpectations: + Enabled: false +RSpec/NamedSubject: + Enabled: false +RSpec/NestedGroups: + Enabled: false +RSpec/SubjectStub: + Enabled: false Style/Documentation: Enabled: false Style/HashSyntax: EnforcedShorthandSyntax: never +Style/NegatedIfElseCondition: + Enabled: false Style/TrailingCommaInArguments: EnforcedStyleForMultiline: consistent_comma Style/TrailingCommaInArrayLiteral: diff --git a/Gemfile b/Gemfile index 61b3cf1d..28b89af7 100644 --- a/Gemfile +++ b/Gemfile @@ -4,3 +4,24 @@ source 'https://rubygems.org' # Specify your gem's dependencies in riemann-tools.gemspec gemspec + +gem 'github_changelog_generator' +gem 'racc' +gem 'rake' +gem 'rspec' +gem 'rubocop' +gem 'rubocop-rake' +gem 'rubocop-rspec' +gem 'sinatra' +gem 'webrick' + +if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7.0') + # XXX: Needed for Ruby 2.6 compatibility + # + # With Ruby 2.6 an older version of rakup is installed that cause other gems + # to be installed with a legacy version. + # + # Because rakup is only needed when using rack 3, we can just ignore this + # with Ruby 2.6. + gem 'rackup' +end diff --git a/Rakefile b/Rakefile index 8bb4555f..dec28cfa 100644 --- a/Rakefile +++ b/Rakefile @@ -20,7 +20,7 @@ task :rbuild do end end -task build: :gen_parser +Rake::Task['build'].enhance(['gen_parser']) desc 'Generate parsers' task gen_parser: [ diff --git a/bin/riemann-wrapper b/bin/riemann-wrapper index dde51ae2..5be7c618 100755 --- a/bin/riemann-wrapper +++ b/bin/riemann-wrapper @@ -116,7 +116,7 @@ if ARGV.size == 1 end require 'yaml' - config = YAML.safe_load(File.read(ARGV[0])) + config = YAML.safe_load_file(ARGV[0]) arguments = split_options(config['options']) config['tools'].each do |tool| diff --git a/lib/riemann/tools.rb b/lib/riemann/tools.rb index c7574461..89dfd4ae 100644 --- a/lib/riemann/tools.rb +++ b/lib/riemann/tools.rb @@ -51,10 +51,10 @@ def options alias opts options def attributes - @attributes ||= Hash[options[:attribute].map do |attr| - k, v = attr.split(/=/) + @attributes ||= options[:attribute].to_h do |attr| + k, v = attr.split('=') [k, v] if k && v - end] + end end def report(event) diff --git a/lib/riemann/tools/bench.rb b/lib/riemann/tools/bench.rb index 6e27d798..9699592d 100644 --- a/lib/riemann/tools/bench.rb +++ b/lib/riemann/tools/bench.rb @@ -19,8 +19,8 @@ def initialize end def evolve(state) - m = state[:metric] + (rand - 0.5) * 0.1 - m = [[0, m].max, 1].min + m = state[:metric] + ((rand - 0.5) * 0.1) + m = m.clamp(0, 1) s = case m when 0...0.75 diff --git a/lib/riemann/tools/diskstats.rb b/lib/riemann/tools/diskstats.rb index 5de1c2ab..53f0d9bb 100644 --- a/lib/riemann/tools/diskstats.rb +++ b/lib/riemann/tools/diskstats.rb @@ -16,7 +16,7 @@ def initialize def state f = File.read('/proc/diskstats') - state = f.split("\n").reject { |d| d =~ /(ram|loop)/ }.each_with_object({}) do |line, s| + state = f.split("\n").grep_v(/(ram|loop)/).each_with_object({}) do |line, s| next unless line =~ /^(?:\s+\d+){2}\s+([\w\d-]+) (.*)$/ dev = Regexp.last_match(1) @@ -43,13 +43,13 @@ def state # Filter interfaces if (is = opts[:devices]) state = state.select do |service, _value| - is.include? service.split(' ').first + is.include? service.split.first end end if (ign = opts[:ignore_devices]) state = state.reject do |service, _value| - ign.include? service.split(' ').first + ign.include? service.split.first end end @@ -80,7 +80,7 @@ def tick next unless service =~ /io time$/ report( - service: "diskstats #{service.gsub(/time/, 'util')}", + service: "diskstats #{service.gsub('time', 'util')}", metric: (delta.to_f / (opts[:interval] * 1000)), state: 'ok', ) diff --git a/lib/riemann/tools/health.rb b/lib/riemann/tools/health.rb index 3d4ab54f..a9f59a06 100644 --- a/lib/riemann/tools/health.rb +++ b/lib/riemann/tools/health.rb @@ -188,7 +188,7 @@ def linux_load end def linux_memory - m = File.read('/proc/meminfo').split(/\n/).each_with_object({}) do |line, info| + m = File.read('/proc/meminfo').split("\n").each_with_object({}) do |line, info| x = line.split(/:?\s+/) # Assume kB... info[x[0]] = x[1].to_i diff --git a/lib/riemann/tools/http_check.rb b/lib/riemann/tools/http_check.rb index 3fa8d355..0b2ef1cf 100644 --- a/lib/riemann/tools/http_check.rb +++ b/lib/riemann/tools/http_check.rb @@ -37,10 +37,15 @@ def initialize @resolve_queue = Queue.new @work_queue = Queue.new + @resolvers = [] + @workers = [] + opts[:resolvers].times do - Thread.new do + @resolvers << Thread.new do loop do uri = @resolve_queue.pop + Thread.exit unless uri + host = uri.host addresses = Resolv::DNS.new.getaddresses(host) @@ -59,9 +64,11 @@ def initialize end opts[:workers].times do - Thread.new do + @workers << Thread.new do loop do uri, addresses = @work_queue.pop + Thread.exit unless uri + test_uri_addresses(uri, addresses) end end @@ -70,6 +77,23 @@ def initialize super end + # Under normal operation, we have a single instance of this class for the + # lifetime of the process. But when testing, we create a new instance + # for each test, each with its resolvers and worker threads. The test + # process may end-up with a lot of running threads, hitting the OS limit + # of max threads by process and being unable to create more thread: + # + # ThreadError: can't create Thread: Resource temporarily unavailable + # + # To avoid this situation, we provide this method. + def shutdown + @resolve_queue.close + @resolvers.map(&:join) + + @work_queue.close + @workers.map(&:join) + end + def tick report( service: 'riemann http-check resolvers utilization', @@ -265,8 +289,8 @@ def report_http_endpoint_max_redirects(http, uri) end def latency_state(name, latency) - critical_threshold = opts["#{name}_latency_critical".to_sym] - warning_threshold = opts["#{name}_latency_warning".to_sym] + critical_threshold = opts[:"#{name}_latency_critical"] + warning_threshold = opts[:"#{name}_latency_warning"] return if critical_threshold.zero? || warning_threshold.zero? diff --git a/lib/riemann/tools/net.rb b/lib/riemann/tools/net.rb index 3fd92120..b91e2b71 100644 --- a/lib/riemann/tools/net.rb +++ b/lib/riemann/tools/net.rb @@ -118,13 +118,7 @@ def tick delta = metric - @old_state[service] svc_state = case service - when /drop$/ - if delta.positive? - 'warning' - else - 'ok' - end - when /errs$/ + when /drop$/, /errs$/ if delta.positive? 'warning' else diff --git a/lib/riemann/tools/nginx_status.rb b/lib/riemann/tools/nginx_status.rb index 3ded7839..de028bca 100644 --- a/lib/riemann/tools/nginx_status.rb +++ b/lib/riemann/tools/nginx_status.rb @@ -39,13 +39,13 @@ def initialize end def state(key, value) - if opts.key? "#{key}_critical".to_sym - critical_threshold = opts["#{key}_critical".to_sym] + if opts.key? :"#{key}_critical" + critical_threshold = opts[:"#{key}_critical"] return 'critical' if critical_threshold.positive? && (value >= critical_threshold) end - if opts.key? "#{key}_warning".to_sym - warning_threshold = opts["#{key}_warning".to_sym] + if opts.key? :"#{key}_warning" + warning_threshold = opts[:"#{key}_warning"] return 'warning' if warning_threshold.positive? && (value >= warning_threshold) end diff --git a/riemann-tools.gemspec b/riemann-tools.gemspec index 6c7e32e1..b373ecd9 100644 --- a/riemann-tools.gemspec +++ b/riemann-tools.gemspec @@ -40,14 +40,4 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'json', '>= 1.8' spec.add_runtime_dependency 'optimist', '~> 3.0', '>= 3.0.0' spec.add_runtime_dependency 'riemann-client', '~> 1.1' - - spec.add_development_dependency 'github_changelog_generator' - spec.add_development_dependency 'racc' - spec.add_development_dependency 'rake' - spec.add_development_dependency 'rspec' - spec.add_development_dependency 'rubocop' - spec.add_development_dependency 'rubocop-rake' - spec.add_development_dependency 'rubocop-rspec' - spec.add_development_dependency 'sinatra' - spec.add_development_dependency 'webrick' end diff --git a/spec/riemann/tools/haproxy_spec.rb b/spec/riemann/tools/haproxy_spec.rb index c3ce67c6..2478c0bd 100644 --- a/spec/riemann/tools/haproxy_spec.rb +++ b/spec/riemann/tools/haproxy_spec.rb @@ -3,7 +3,7 @@ require 'riemann/tools/haproxy' RSpec.describe Riemann::Tools::Haproxy do - context('#tick') do + describe('#tick') do before do ARGV.replace(['--stats-url', 'http://localhost']) diff --git a/spec/riemann/tools/health_spec.rb b/spec/riemann/tools/health_spec.rb index ba87308d..cc2c9d79 100644 --- a/spec/riemann/tools/health_spec.rb +++ b/spec/riemann/tools/health_spec.rb @@ -3,7 +3,7 @@ require 'riemann/tools/health' RSpec.describe Riemann::Tools::Health do - context('#disks') do + describe('#disks') do before do allow(subject).to receive(:df).and_return(<<~OUTPUT) Filesystem 512-blocks Used Avail Capacity Mounted on @@ -66,7 +66,7 @@ end end - context '#bsd_swap' do + describe '#bsd_swap' do context 'with swap devices' do before do allow(subject).to receive(:`).with('swapinfo').and_return(<<~OUTPUT) @@ -99,7 +99,7 @@ end end - context '#linux_swap' do + describe '#linux_swap' do context 'with swap devices' do before do allow(File).to receive(:read).with('/proc/swaps').and_return(<<~OUTPUT) @@ -115,6 +115,7 @@ expect(subject).to have_received(:report_pct).with(:swap, 0.339623244451355, 'used') end end + context 'without swap devices' do before do allow(File).to receive(:read).with('/proc/swaps').and_return(<<~OUTPUT) @@ -130,7 +131,7 @@ end end - context '#bsd_uptime' do + describe '#bsd_uptime' do context 'when given unexpected data' do before do allow(subject).to receive(:`).with('uptime').and_return(<<~DOCUMENT) diff --git a/spec/riemann/tools/http_check_spec.rb b/spec/riemann/tools/http_check_spec.rb index 21d0124e..fdc0591b 100644 --- a/spec/riemann/tools/http_check_spec.rb +++ b/spec/riemann/tools/http_check_spec.rb @@ -1,7 +1,13 @@ # frozen_string_literal: true require 'openssl' -require 'rack/handler/webrick' +begin + require 'rackup/handler/webrick' +rescue LoadError + # XXX: Needed for Ruby 2.6 compatibility + # Moved to the rackup gem in recent versions + require 'rack/handler/webrick' +end require 'sinatra/base' require 'webrick' require 'webrick/https' @@ -65,6 +71,7 @@ def protected! RSpec.describe Riemann::Tools::HttpCheck, if: Gem::Version.new(RUBY_VERSION) >= Gem::Version.new(Riemann::Tools::HttpCheck::REQUIRED_RUBY_VERSION) do describe '#endpoint_name' do subject { described_class.new.endpoint_name(address, port) } + let(:port) { 443 } context 'when using an IPv4 address' do @@ -92,6 +99,7 @@ def protected! let(:http_timeout) { 2.0 } context 'when using unencrypted http' do + # rubocop:disable RSpec/BeforeAfterAll, RSpec/InstanceVariable before(:all) do server_options = { Port: 0, @@ -109,8 +117,13 @@ def protected! after(:all) do @server&.shutdown end + # rubocop:enable RSpec/BeforeAfterAll, RSpec/InstanceVariable - let(:test_webserver_port) { @server.config[:Port] } + after do + subject.shutdown + end + + let(:test_webserver_port) { @server.config[:Port] } # rubocop:disable RSpec/InstanceVariable let(:reported_uri) { uri } @@ -236,6 +249,7 @@ def protected! end context 'when using encrypted https' do + # rubocop:disable RSpec/BeforeAfterAll, RSpec/InstanceVariable before(:all) do server_options = { Port: 0, @@ -257,6 +271,7 @@ def protected! after(:all) do @server&.shutdown end + # rubocop:enable RSpec/BeforeAfterAll, RSpec/InstanceVariable context 'with an encrypted uri' do let(:uri) { URI("https://example.com:#{test_webserver_port}/") } diff --git a/spec/riemann/tools/md_spec.rb b/spec/riemann/tools/md_spec.rb index f6f88c83..4faec6ef 100644 --- a/spec/riemann/tools/md_spec.rb +++ b/spec/riemann/tools/md_spec.rb @@ -3,7 +3,7 @@ require 'riemann/tools/md' RSpec.describe Riemann::Tools::Md do - context('#tick') do + describe('#tick') do context 'when all md devices are healthy' do before do allow(File).to receive(:read).with('/proc/mdstat').and_return(File.read('spec/fixtures/mdstat/example-8')) diff --git a/spec/riemann/tools/net_spec.rb b/spec/riemann/tools/net_spec.rb index cc820aba..8b86a24b 100644 --- a/spec/riemann/tools/net_spec.rb +++ b/spec/riemann/tools/net_spec.rb @@ -3,42 +3,43 @@ require 'riemann/tools/net' RSpec.describe Riemann::Tools::Net do - context('#report_interface?') do + describe('#report_interface?') do it 'selects interfaces by name' do subject.instance_variable_set(:@interfaces, %w[wlan0 wlan1]) subject.instance_variable_set(:@ignore_interfaces, %w[wlan1]) - expect(subject.report_interface?('wlan0')).to be_truthy - expect(subject.report_interface?('wlan1')).to be_truthy + expect(subject.report_interface?('wlan0')).to be(true) + expect(subject.report_interface?('wlan1')).to be(true) end it 'selects interfaces by regular expression' do subject.instance_variable_set(:@interfaces, %w[\Awlan\d+\z]) subject.instance_variable_set(:@ignore_interfaces, %w[wlan1]) - expect(subject.report_interface?('wlan0')).to be_truthy - expect(subject.report_interface?('wlan1')).to be_truthy + expect(subject.report_interface?('wlan0')).to be(true) + expect(subject.report_interface?('wlan1')).to be(true) end it 'reject interfaces by name' do subject.instance_variable_set(:@interfaces, []) subject.instance_variable_set(:@ignore_interfaces, %w[wlan1]) - expect(subject.report_interface?('wlan0')).to be_truthy - expect(subject.report_interface?('wlan1')).to be_falsey + expect(subject.report_interface?('wlan0')).to be(true) + expect(subject.report_interface?('wlan1')).to be(false) end it 'reject interfaces by regular expression' do subject.instance_variable_set(:@interfaces, []) subject.instance_variable_set(:@ignore_interfaces, %w[\Awlan\d+\z]) - expect(subject.report_interface?('wlan0')).to be_falsey - expect(subject.report_interface?('wlan1')).to be_falsey + expect(subject.report_interface?('wlan0')).to be(false) + expect(subject.report_interface?('wlan1')).to be(false) end end - context('#freebsd_state') do + describe('#freebsd_state') do let(:freebsd_state) { subject.freebsd_state } + it 'finds all interfaces' do allow(subject).to receive(:`).with('netstat -inb --libxo=json').and_return('{"statistics": {"interface": [{"name":"vtnet0","flags":"0x8863","mtu":1500,"network":"","address":"96:00:00:7b:2f:db","received-packets":14409097,"received-errors":0,"dropped-packets":0,"received-bytes":3661133356,"sent-packets":14006498,"send-errors":0,"sent-bytes":4606661018,"collisions":0}, {"name":"vtnet0","flags":"0x8863","network":"2a01:4f9:c010:e1dd::/64","address":"2a01:4f9:c010:e1dd::","received-packets":4072295,"received-bytes":4334205829,"sent-packets":2423184,"sent-bytes":473948371}, {"name":"vtnet0","flags":"0x8863","network":"fe80::%vtnet0/64","address":"fe80::9400:ff:fe7b:2fdb%vtnet0","received-packets":60737,"received-bytes":3887168,"sent-packets":60739,"sent-bytes":4373236}, {"name":"vtnet0","flags":"0x8863","network":"135.181.146.104/32","address":"135.181.146.104","received-packets":11897156,"received-bytes":2242504526,"sent-packets":12708744,"sent-bytes":4372534465}, {"name":"lo0","flags":"0x8049","mtu":16384,"network":"","address":"lo0","received-packets":18539596,"received-errors":0,"dropped-packets":0,"received-bytes":7314451024,"sent-packets":18539596,"send-errors":0,"sent-bytes":7314451024,"collisions":0}, {"name":"lo0","flags":"0x8049","network":"::1/128","address":"::1","received-packets":6726995,"received-bytes":2429478036,"sent-packets":8462601,"sent-bytes":5178849236}, {"name":"lo0","flags":"0x8049","network":"fe80::%lo0/64","address":"fe80::1%lo0","received-packets":0,"received-bytes":0,"sent-packets":0,"sent-bytes":0}, {"name":"lo0","flags":"0x8049","network":"127.0.0.0/8","address":"127.0.0.1","received-packets":9480200,"received-bytes":1729579367,"sent-packets":9480200,"sent-bytes":1729579367}]}}') @@ -55,8 +56,9 @@ end end - context('#linux_state') do + describe('#linux_state') do let(:linux_state) { subject.linux_state } + it 'finds all interfaces' do allow(File).to receive(:read).with('/proc/net/dev').and_return(<<~CONTENT) Inter-| Receive | Transmit diff --git a/spec/riemann/tools/utils_spec.rb b/spec/riemann/tools/utils_spec.rb index 652e8cb2..84cb8881 100644 --- a/spec/riemann/tools/utils_spec.rb +++ b/spec/riemann/tools/utils_spec.rb @@ -7,7 +7,7 @@ class TestClass end RSpec.describe Riemann::Tools::Utils do - context('#reverse_numeric_sort_with_header') do + describe('#reverse_numeric_sort_with_header') do subject { TestClass.new.reverse_numeric_sort_with_header(input) } let(:input) do @@ -30,15 +30,18 @@ class TestClass 6 INPUT end + it { is_expected.to eq("Header\n15\n14\n13\n12\n11\n10\n 9\n 8\n 7\n 6") } context 'with a number of data lines' do subject { TestClass.new.reverse_numeric_sort_with_header(input, count: 3) } + it { is_expected.to eq("Header\n15\n14\n13") } end context 'with a number of header lines' do subject { TestClass.new.reverse_numeric_sort_with_header(input, header: 3) } + it { is_expected.to eq("Header\n11\n 1\n15\n14\n13\n12\n10\n 9\n 8\n 7\n 6\n 5") } end end diff --git a/spec/riemann/tools/zpool_spec.rb b/spec/riemann/tools/zpool_spec.rb index 02b006da..95780581 100644 --- a/spec/riemann/tools/zpool_spec.rb +++ b/spec/riemann/tools/zpool_spec.rb @@ -3,7 +3,7 @@ require 'riemann/tools/zpool' RSpec.describe Riemann::Tools::Zpool do - context('#tick') do + describe('#tick') do before do process_status = double allow(process_status).to receive(:success?).and_return(true) diff --git a/tools/riemann-aws/lib/riemann/tools/aws/s3_status.rb b/tools/riemann-aws/lib/riemann/tools/aws/s3_status.rb index bda6e1f0..f6d8f7d9 100644 --- a/tools/riemann-aws/lib/riemann/tools/aws/s3_status.rb +++ b/tools/riemann-aws/lib/riemann/tools/aws/s3_status.rb @@ -21,7 +21,7 @@ class S3Status def base_metrics # get last 60 seconds - start_time = (Time.now.utc - 3600 * 24 * 1).iso8601 + start_time = (Time.now.utc - (3600 * 24 * 1)).iso8601 end_time = Time.now.utc.iso8601 # The base query that all metrics would get diff --git a/tools/riemann-aws/lib/riemann/tools/aws/status.rb b/tools/riemann-aws/lib/riemann/tools/aws/status.rb index bd6ae5e7..4b1630c4 100644 --- a/tools/riemann-aws/lib/riemann/tools/aws/status.rb +++ b/tools/riemann-aws/lib/riemann/tools/aws/status.rb @@ -61,7 +61,7 @@ def tick elsif opts[:event_warning] && (Date.today >= before - opts[:event_warning]) { state: 'warning' } else - { state: 'warning' } + {} end report ev.merge(ev2) diff --git a/tools/riemann-docker/lib/riemann/tools/docker.rb b/tools/riemann-docker/lib/riemann/tools/docker.rb index 51fcfc43..a20163b2 100644 --- a/tools/riemann-docker/lib/riemann/tools/docker.rb +++ b/tools/riemann-docker/lib/riemann/tools/docker.rb @@ -121,7 +121,7 @@ def memory(_id, name, stats) end def disk - `df -P`.split(/\n/).each do |r| + `df -P`.split("\n").each do |r| f = r.split(/\s+/) next if f[0] == 'Filesystem' next unless f[0] =~ %r{/} # Needs at least one slash in the mount path diff --git a/tools/riemann-riak/lib/riemann/tools/riak.rb b/tools/riemann-riak/lib/riemann/tools/riak.rb index 182efcd9..93c06a3e 100644 --- a/tools/riemann-riak/lib/riemann/tools/riak.rb +++ b/tools/riemann-riak/lib/riemann/tools/riak.rb @@ -151,7 +151,7 @@ def fsm_stat(type, property, percentile) # Returns the alerts state for the given fsm. def fsm_state(type, percentile, val) - limit = opts["#{type}_#{percentile}_warning".to_sym] + limit = opts[:"#{type}_#{percentile}_warning"] case val when 0..limit 'ok' @@ -201,7 +201,7 @@ def stats_riak_admin str = `riak-admin status` raise 'riak-admin failed' unless $CHILD_STATUS == 0 - Hash[str.split(/\n/).map { |i| i.split(/ : /) }] + str.split("\n").to_h { |i| i.split(' : ') } end # Get current stats as a hash