From 717e08e2c155b633eda05c68ce9b451e85bb1a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Tarti=C3=A8re?= Date: Wed, 29 Apr 2020 04:55:45 -1000 Subject: [PATCH] (maint) Add support for *BSD load averages (#460) --- .rubocop_todo.yml | 6 +++-- lib/facts/bsd/load_averages.rb | 14 ++++++++++ lib/resolvers/bsd/ffi/ffi_helper.rb | 25 ++++++++++++++++++ lib/resolvers/bsd/load_averages_resolver.rb | 26 +++++++++++++++++++ spec/facter/facts/bsd/load_averages_spec.rb | 23 ++++++++++++++++ spec/facter/resolvers/bsd/ffi_helper_spec.rb | 26 +++++++++++++++++++ .../bsd/load_averages_resolver_spec.rb | 16 ++++++++++++ 7 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 lib/facts/bsd/load_averages.rb create mode 100644 lib/resolvers/bsd/ffi/ffi_helper.rb create mode 100644 lib/resolvers/bsd/load_averages_resolver.rb create mode 100644 spec/facter/facts/bsd/load_averages_spec.rb create mode 100644 spec/facter/resolvers/bsd/ffi_helper_spec.rb create mode 100644 spec/facter/resolvers/bsd/load_averages_resolver_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 8078b8503..af1940bb2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -6,7 +6,7 @@ # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 80 +# Offense count: 81 # Configuration parameters: CustomTransform, IgnoreMethods. RSpec/FilePath: Exclude: @@ -37,6 +37,7 @@ RSpec/FilePath: - 'spec/facter/resolvers/aix/architecture_resolver_spec.rb' - 'spec/facter/resolvers/aix/ffi_helper_spec.rb' - 'spec/facter/resolvers/aix/hardware_resolver_spec.rb' + - 'spec/facter/resolvers/bsd/ffi_helper_spec.rb' - 'spec/facter/resolvers/disk_resolver_spec.rb' - 'spec/facter/resolvers/dmi_resolver_spec.rb' - 'spec/facter/resolvers/filesystems_resolver_spec.rb' @@ -191,7 +192,7 @@ RSpec/SubjectStub: - 'spec/custom_facts/util/fact_spec.rb' - 'spec/custom_facts/util/resolution_spec.rb' -# Offense count: 169 +# Offense count: 180 # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: Exclude: @@ -227,6 +228,7 @@ RSpec/VerifiedDoubles: - 'spec/facter/resolvers/aix/architecture_resolver_spec.rb' - 'spec/facter/resolvers/aix/ffi_helper_spec.rb' - 'spec/facter/resolvers/aix/hardware_resolver_spec.rb' + - 'spec/facter/resolvers/bsd/ffi_helper_spec.rb' - 'spec/facter/resolvers/macosx/mountpoints_resolver_spec.rb' - 'spec/facter/resolvers/mountpoints_resolver_spec.rb' - 'spec/facter/resolvers/solaris/current_zone_resolver_spec.rb' diff --git a/lib/facts/bsd/load_averages.rb b/lib/facts/bsd/load_averages.rb new file mode 100644 index 000000000..4a6ea615f --- /dev/null +++ b/lib/facts/bsd/load_averages.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Facts + module Bsd + class LoadAverages + FACT_NAME = 'load_averages' + + def call_the_resolver + fact_value = Facter::Resolvers::Bsd::LoadAverages.resolve(:load_averages) + Facter::ResolvedFact.new(FACT_NAME, fact_value) + end + end + end +end diff --git a/lib/resolvers/bsd/ffi/ffi_helper.rb b/lib/resolvers/bsd/ffi/ffi_helper.rb new file mode 100644 index 000000000..bf013b33e --- /dev/null +++ b/lib/resolvers/bsd/ffi/ffi_helper.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'ffi' + +module Facter + module Bsd + module FfiHelper + module Libc + extend FFI::Library + + ffi_lib 'c' + attach_function :getloadavg, %i[pointer int], :int + end + + def self.read_load_averages + raw_loadavg = FFI::MemoryPointer.new(:double, 3) + + res = Libc.getloadavg(raw_loadavg, 3) + return unless res == 3 + + raw_loadavg.read_array_of_double(res) + end + end + end +end diff --git a/lib/resolvers/bsd/load_averages_resolver.rb b/lib/resolvers/bsd/load_averages_resolver.rb new file mode 100644 index 000000000..b77f3b77c --- /dev/null +++ b/lib/resolvers/bsd/load_averages_resolver.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Facter + module Resolvers + module Bsd + class LoadAverages < BaseResolver + @semaphore = Mutex.new + @fact_list ||= {} + class << self + private + + def post_resolve(fact_name) + @fact_list.fetch(fact_name) { read_load_averages(fact_name) } + end + + def read_load_averages(fact_name) + require_relative 'ffi/ffi_helper' + @fact_list[:load_averages] = %w[1m 5m 15m].zip(Facter::Bsd::FfiHelper.read_load_averages).to_h + + @fact_list[fact_name] + end + end + end + end + end +end diff --git a/spec/facter/facts/bsd/load_averages_spec.rb b/spec/facter/facts/bsd/load_averages_spec.rb new file mode 100644 index 000000000..8815ba90e --- /dev/null +++ b/spec/facter/facts/bsd/load_averages_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +describe Facts::Bsd::LoadAverages do + describe '#call_the_resolver' do + subject(:fact) { Facts::Bsd::LoadAverages.new } + + let(:value) { { '1m' => 0.01, '5m' => 0.02, '15m' => 0.03 } } + + before do + allow(Facter::Resolvers::Bsd::LoadAverages).to receive(:resolve).with(:load_averages).and_return(value) + end + + it 'calls Facter::Resolvers::Bsd::LoadAverages' do + fact.call_the_resolver + expect(Facter::Resolvers::Bsd::LoadAverages).to have_received(:resolve).with(:load_averages) + end + + it 'returns load_averages fact' do + expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \ + have_attributes(name: 'load_averages', value: value) + end + end +end diff --git a/spec/facter/resolvers/bsd/ffi_helper_spec.rb b/spec/facter/resolvers/bsd/ffi_helper_spec.rb new file mode 100644 index 000000000..8ddb6d429 --- /dev/null +++ b/spec/facter/resolvers/bsd/ffi_helper_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +describe Facter::Bsd::FfiHelper do + let(:averages) { double('FFI::MemoryPointer') } + + before do + allow(FFI::MemoryPointer).to receive(:new).with(:double, 3).and_return(averages) + allow(averages).to receive(:size).and_return(24) + end + + after do + Facter::Resolvers::Bsd::LoadAverages.invalidate_cache + end + + it 'returns load average' do + allow(Facter::Bsd::FfiHelper::Libc).to receive(:getloadavg).and_return(3) + allow(averages).to receive(:read_array_of_double).with(3).and_return([0.19482421875, 0.2744140625, 0.29296875]) + + expect(Facter::Bsd::FfiHelper.read_load_averages).to eq([0.19482421875, 0.2744140625, 0.29296875]) + end + + it 'does not return load average' do + allow(Facter::Bsd::FfiHelper::Libc).to receive(:getloadavg).and_return(-1) + expect(Facter::Bsd::FfiHelper.read_load_averages).to be_nil + end +end diff --git a/spec/facter/resolvers/bsd/load_averages_resolver_spec.rb b/spec/facter/resolvers/bsd/load_averages_resolver_spec.rb new file mode 100644 index 000000000..1fad37062 --- /dev/null +++ b/spec/facter/resolvers/bsd/load_averages_resolver_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +describe Facter::Resolvers::Bsd::LoadAverages do + let(:load_averages) { [0.01, 0.02, 0.03] } + + before do + allow(Facter::Bsd::FfiHelper).to receive(:read_load_averages) + .and_return(load_averages) + end + + it 'returns load average' do + result = Facter::Resolvers::Bsd::LoadAverages.resolve(:load_averages) + + expect(result).to eq('15m' => 0.03, '1m' => 0.01, '5m' => 0.02) + end +end