From fee408be64909a2f503624a7affd7d6b34a17ca2 Mon Sep 17 00:00:00 2001 From: Florin Dragos Date: Wed, 29 Apr 2020 09:25:35 +0300 Subject: [PATCH] (FACT-2515) Define custom fact groups in facter.conf --- facter.conf | 4 +++ lib/facter.rb | 3 +- lib/framework/config/config_reader.rb | 4 +++ lib/framework/config/fact_groups.rb | 3 +- lib/framework/core/cache_manager.rb | 32 +++++++++---------- .../core/options/config_file_options.rb | 29 +++++++++++------ lib/framework/core/options/option_store.rb | 19 ++++++++--- spec/facter/facter_spec.rb | 9 +++--- .../utils/windows/network_utils_spec.rb | 3 -- spec/framework/config/config_reader_spec.rb | 29 +++++++++++++++++ spec/framework/config/fact_groups_spec.rb | 1 + .../core/options/option_store_spec.rb | 2 ++ 12 files changed, 95 insertions(+), 43 deletions(-) diff --git a/facter.conf b/facter.conf index bcfdce888..23bc1ade8 100644 --- a/facter.conf +++ b/facter.conf @@ -19,3 +19,7 @@ cli : { verbose : false, log-level : "warn" } + +fact-groups : { + cached-custom-facts : ["my_custom_fact"], +} \ No newline at end of file diff --git a/lib/facter.rb b/lib/facter.rb index 8cbcf5818..9a38cfb95 100644 --- a/lib/facter.rb +++ b/lib/facter.rb @@ -243,7 +243,6 @@ def to_user_output(cli_options, *args) Facter::Options.init_from_cli(cli_options, args) @logger.info("executed with command line: #{ARGV.drop(1).join(' ')}") log_blocked_facts - resolved_facts = Facter::FactManager.instance.resolve_facts(args) SessionCache.invalidate_all_caches fact_formatter = Facter::FormatterFactory.build(Facter::Options.get) @@ -324,7 +323,7 @@ def error_check(args, resolved_facts) # # @api private def log_blocked_facts - block_list = Facter::FactGroups.new(Facter::Options[:config]).block_list + block_list = Options[:block_list] return unless block_list.any? && Facter::Options[:block] @logger.debug("blocking collection of #{block_list.join("\s")} facts") diff --git a/lib/framework/config/config_reader.rb b/lib/framework/config/config_reader.rb index c8b85f4fb..ee62b6312 100644 --- a/lib/framework/config/config_reader.rb +++ b/lib/framework/config/config_reader.rb @@ -27,6 +27,10 @@ def cli @conf['cli'] end + def fact_groups + @conf['fact-groups'] + end + def refresh_config(config_path) @conf = File.readable?(config_path) ? Hocon.load(config_path) : {} end diff --git a/lib/framework/config/fact_groups.rb b/lib/framework/config/fact_groups.rb index 7932288dd..4c570e2bd 100644 --- a/lib/framework/config/fact_groups.rb +++ b/lib/framework/config/fact_groups.rb @@ -44,7 +44,8 @@ def get_group_ttls(group_name) def load_groups config = ConfigReader.init(Options[:config]) @block_list = config.block_list || {} - @groups_ttls = ConfigReader.init(Options[:config]).ttls || {} + @groups_ttls = config.ttls || {} + @groups.merge!(config.fact_groups) if config.fact_groups end def ttls_to_seconds(ttls) diff --git a/lib/framework/core/cache_manager.rb b/lib/framework/core/cache_manager.rb index 0fd5de747..5d1c2f82b 100644 --- a/lib/framework/core/cache_manager.rb +++ b/lib/framework/core/cache_manager.rb @@ -6,14 +6,11 @@ def initialize @groups = {} @log = Log.new(self) @fact_groups = Facter::FactGroups.new - end - - def cache_dir - LegacyFacter::Util::Config.facts_cache_dir + @cache_dir = LegacyFacter::Util::Config.facts_cache_dir end def resolve_facts(searched_facts) - return searched_facts, [] if !File.directory?(cache_dir) || !Options[:cache] + return searched_facts, [] if !File.directory?(@cache_dir) || !Options[:cache] facts = [] searched_facts.each do |fact| @@ -73,16 +70,16 @@ def cache_fact(fact) end def write_cache - unless File.directory?(cache_dir) + unless File.directory?(@cache_dir) require 'fileutils' - FileUtils.mkdir_p(cache_dir) + FileUtils.mkdir_p(@cache_dir) end @groups.each do |group_name, data| next if check_ttls(group_name).zero? @log.debug("caching values for #{group_name} facts") - cache_file_name = File.join(cache_dir, group_name) + cache_file_name = File.join(@cache_dir, group_name) File.write(cache_file_name, JSON.pretty_generate(data)) end end @@ -90,7 +87,7 @@ def write_cache def read_group_json(group_name) return @groups[group_name] if @groups.key?(group_name) - cache_file_name = File.join(cache_dir, group_name) + cache_file_name = File.join(@cache_dir, group_name) data = nil file = Util::FileHelper.safe_read(cache_file_name) begin @@ -111,20 +108,21 @@ def check_ttls(group_name) ttls = @fact_groups.get_group_ttls(group_name) return 0 unless ttls - cache_file_name = File.join(cache_dir, group_name) - return ttls unless File.readable?(cache_file_name) + cache_file_name = File.join(@cache_dir, group_name) + if File.readable?(cache_file_name) + file_time = File.mtime(cache_file_name) + expire_date = file_time + ttls + return expire_date.to_i unless expire_date < Time.now - file_time = File.mtime(cache_file_name) - expire_date = file_time + ttls - if expire_date < Time.now File.delete(cache_file_name) - return ttls end - expire_date.to_i + + @log.debug("#{group_name} facts cache file expired/missing") + ttls end def delete_cache(group_name) - cache_file_name = File.join(cache_dir, group_name) + cache_file_name = File.join(@cache_dir, group_name) File.delete(cache_file_name) if File.readable?(cache_file_name) end end diff --git a/lib/framework/core/options/config_file_options.rb b/lib/framework/core/options/config_file_options.rb index 44564fcfb..474f63a8b 100644 --- a/lib/framework/core/options/config_file_options.rb +++ b/lib/framework/core/options/config_file_options.rb @@ -9,6 +9,16 @@ def init(config_path = nil) augment_config_path(config_path) + augment_all + end + + def get + @options || {} + end + + private + + def augment_all if Options.cli? augment_cli(Facter::ConfigReader.cli) augment_ruby(Facter::ConfigReader.global) @@ -16,15 +26,9 @@ def init(config_path = nil) augment_custom(Facter::ConfigReader.global) augment_external(Facter::ConfigReader.global) augment_show_legacy(Facter::ConfigReader.global) - augment_facts(Facter::ConfigReader.ttls) + augment_facts(Facter::ConfigReader.ttls, Facter::ConfigReader.fact_groups) end - def get - @options || {} - end - - private - def augment_config_path(config_path) @options[:config] = config_path end @@ -72,11 +76,16 @@ def augment_show_legacy(global_conf) @options[:show_legacy] = global_conf['show-legacy'] unless global_conf['show-legacy'].nil? end - def augment_facts(ttls) - blocked_facts = Facter::FactGroups.new.blocked_facts - @options[:blocked_facts] = blocked_facts unless blocked_facts.nil? + def augment_facts(ttls, groups) + fact_groups = Facter::FactGroups.new + @options[:blocked_facts] = fact_groups.blocked_facts unless fact_groups.blocked_facts.nil? + @options[:block_list] = fact_groups.block_list @options[:ttls] = ttls unless ttls.nil? + + f_groups = fact_groups.groups || {} + f_groups = groups.merge(f_groups) unless groups.nil? + @options[:fact_groups] = f_groups end end end diff --git a/lib/framework/core/options/option_store.rb b/lib/framework/core/options/option_store.rb index 73a363ec0..ca40fcd37 100644 --- a/lib/framework/core/options/option_store.rb +++ b/lib/framework/core/options/option_store.rb @@ -21,6 +21,8 @@ class OptionStore @cache = true @blocked_facts = [] @user_query = [] + @block_list = {} + @fact_groups = {} class << self attr_reader :debug, :verbose, :log_level, :show_legacy, :trace, :ruby, @@ -28,7 +30,8 @@ class << self attr_accessor :config, :user_query, :strict, :json, :haml, :external_facts, :cache, :yaml, :puppet, :ttls, :block, :cli, :config_file_custom_dir, - :config_file_external_dir, :default_external_dir + :config_file_external_dir, :default_external_dir, :fact_groups, + :block_list attr_writer :external_dir @@ -152,16 +155,22 @@ def reset @log_level = :warn @show_legacy = true @block = true + @ruby = true + @user_query = [] + @cli = nil + @cache = true + reset_config + end + + def reset_config @custom_dir = [] @custom_facts = true @external_dir = [] @default_external_dir = [] @external_facts = true - @ruby = true @blocked_facts = [] - @user_query = [] - @cli = nil - @cache = true + @fact_groups = {} + @block_list = {} end def fallback_external_dir diff --git a/spec/facter/facter_spec.rb b/spec/facter/facter_spec.rb index fa7736fc4..f328f70a4 100644 --- a/spec/facter/facter_spec.rb +++ b/spec/facter/facter_spec.rb @@ -12,7 +12,6 @@ let(:fact_collection_spy) { instance_spy(Facter::FactCollection) } let(:key_error) { KeyError.new('key error') } let(:config_reader_double) { double(Facter::ConfigReader) } - let(:block_list_double) { instance_spy(Facter::FactGroups) } before do allow(Facter::ConfigReader).to receive(:init).and_return(config_reader_double) @@ -20,10 +19,10 @@ allow(config_reader_double).to receive(:global).and_return(nil) allow(config_reader_double).to receive(:ttls).and_return([]) allow(config_reader_double).to receive(:block_list).and_return([]) + allow(config_reader_double).to receive(:fact_groups).and_return({}) - allow(Facter::FactGroups).to receive(:instance).and_return(block_list_double) - allow(block_list_double).to receive(:blocked_facts).and_return([]) - allow(block_list_double).to receive(:block_list).and_return([]) + allow(Facter::Options).to receive(:[]).and_call_original + allow(Facter::Options).to receive(:[]).with(:block_list).and_return([]) Facter.instance_variable_set(:@logger, logger) Facter.clear @@ -99,7 +98,6 @@ def mock_collection(method, os_name = nil, error = nil) it 'returns no fact and status 1', resolved_fact: false do user_query = ['os.name', 'missing_fact'] expected_json_output = '{}' - allow(Facter::Options).to receive(:[]).and_call_original allow(Facter::Options).to receive(:[]).with(:strict).and_return(true) allow(OsDetector).to receive(:detect).and_return(:solaris) @@ -112,6 +110,7 @@ def mock_collection(method, os_name = nil, error = nil) user_query = 'os.name' expected_json_output = '{"os" : {"name": "ubuntu"}' allow(Facter::Options).to receive(:[]).with(anything) + allow(Facter::Options).to receive(:[]).with(:block_list).and_return([]) allow(Facter::Options).to receive(:[]).with(:strict).and_return(true) formated_facts = Facter.to_user_output({}, user_query) diff --git a/spec/facter/resolvers/utils/windows/network_utils_spec.rb b/spec/facter/resolvers/utils/windows/network_utils_spec.rb index c2798262c..4375ff3aa 100644 --- a/spec/facter/resolvers/utils/windows/network_utils_spec.rb +++ b/spec/facter/resolvers/utils/windows/network_utils_spec.rb @@ -34,9 +34,6 @@ let(:address) { double(FFI::MemoryPointer) } let(:error) { 0 } - before do - end - it 'returns an address' do expect(NetworkUtils.address_to_string(addr)).to eql('10.123.0.2') end diff --git a/spec/framework/config/config_reader_spec.rb b/spec/framework/config/config_reader_spec.rb index a32428689..4cfc19c51 100644 --- a/spec/framework/config/config_reader_spec.rb +++ b/spec/framework/config/config_reader_spec.rb @@ -159,4 +159,33 @@ end end end + + describe '#fact-groups' do + let(:os) { :linux } + + before do + allow(File).to receive(:readable?).and_return(true) + allow(Hocon).to receive(:load).and_return(config) + end + + context 'with empty config file' do + let(:config) { {} } + + it 'returns nil' do + config_reader.init + + expect(config_reader.fact_groups).to eq(nil) + end + end + + context 'with fact-groups in config file' do + let(:config) { { 'fact-groups' => { 'cached-custom-facts' => ['my_custom_fact'] } } } + + it 'returns fact-groups' do + config_reader.init + + expect(config_reader.fact_groups).to eq('cached-custom-facts' => ['my_custom_fact']) + end + end + end end diff --git a/spec/framework/config/fact_groups_spec.rb b/spec/framework/config/fact_groups_spec.rb index ccebccf97..b3ae3b35e 100644 --- a/spec/framework/config/fact_groups_spec.rb +++ b/spec/framework/config/fact_groups_spec.rb @@ -10,6 +10,7 @@ allow(Facter::ConfigReader).to receive(:init).and_return(config_reader) allow(config_reader).to receive(:block_list).and_return([]) allow(config_reader).to receive(:ttls).and_return([]) + allow(config_reader).to receive(:fact_groups).and_return({}) end describe '#initialize' do diff --git a/spec/framework/core/options/option_store_spec.rb b/spec/framework/core/options/option_store_spec.rb index 644feb0a6..d67a5de7d 100644 --- a/spec/framework/core/options/option_store_spec.rb +++ b/spec/framework/core/options/option_store_spec.rb @@ -15,6 +15,7 @@ it 'returns default values' do expect(option_store.all).to eq( block: true, + block_list: {}, blocked_facts: [], cli: nil, custom_dir: [], @@ -25,6 +26,7 @@ config_file_external_dir: [], default_external_dir: [], external_facts: true, + fact_groups: {}, log_level: :warn, ruby: true, show_legacy: true,