# frozen_string_literal: true # Load facts on demand. module LegacyFacter module Util class Loader def initialize(environment_vars = ENV) @loaded = [] @environment_vars = environment_vars end # Load all resolutions for a single fact. # # @api public # @param name [Symbol] def load(fact) # Now load from the search path shortname = fact.to_s.downcase load_env(shortname) filename = shortname + '.rb' paths = search_path paths&.each do |dir| # Load individual files file = File.join(dir, filename) load_file(file) if FileTest.file?(file) end end # Load all facts from all directories. # # @api public def load_all return if defined?(@loaded_all) load_env paths = search_path paths&.each do |dir| # clean the search path of wrong slashes and backslashes dir = dir.gsub(%r{[\/\\]+}, File::SEPARATOR) # dir is already an absolute path Dir.glob(File.join(dir, '*.rb')).each do |path| # exclude dirs that end with .rb load_file(path) if FileTest.file?(path) end end @loaded_all = true end # List directories to search for fact files. # # Search paths are gathered from the following sources: # # 1. $LOAD_PATH entries are expanded to absolute paths # 2. ENV['FACTERLIB'] is split and used verbatim # 3. Entries from Facter.search_path are used verbatim # # @api public # @return [Array] def search_path search_paths = [] search_paths += $LOAD_PATH.map { |path| File.expand_path('facter', path) } if @environment_vars.include?('FACTERLIB') search_paths += @environment_vars['FACTERLIB'].split(File::PATH_SEPARATOR) end Facter::Options.custom_dir.each do |path| search_paths << path end search_paths.delete_if { |path| !File.directory?(path) } search_paths.uniq end private def log @log ||= Facter::Log.new(self) end # Load a file and record is paths to prevent duplicate loads. # # @api private # @params file [String] The *absolute path* to the file to load def load_file(file) return if @loaded.include? file # We have to specify Kernel.load, because we have a load method. begin # Store the file path so we don't try to reload it @loaded << file kernel_load(file) rescue ScriptError => e # Don't store the path if the file can't be loaded # in case it's loadable later on. @loaded.delete(file) Facter.log_exception(e, "Error loading fact #{file}: #{e.message}") rescue StandardError => e Facter.log_exception(e, "error while resolving custom facts in #{file} #{e.message}") end end # Load and execute the Ruby program specified in the file. This exists # for testing purposes. # # @api private # @return [Boolean] def kernel_load(file) Kernel.load(file) end # Load facts from the environment. If no name is provided, # all will be loaded. def load_env(fact = nil) # Load from the environment, if possible @environment_vars.each do |name, value| # Skip anything that doesn't match our regex. next unless name =~ /^facter_?(\w+)$/i env_name = Regexp.last_match(1).downcase # If a fact name was specified, skip anything that doesn't # match it. next if fact && (env_name != fact) LegacyFacter.add(env_name, fact_type: :external, is_env: true) do has_weight 1_000_000 setcode { value } end # Short-cut, if we are only looking for one value. break if fact end end end end end