class Facter::CacheManager

Public Class Methods

new() click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 5
def initialize
  @groups = {}
  @log = Log.new(self)
  @fact_groups = Facter::FactGroups.new
  @cache_dir = LegacyFacter::Util::Config.facts_cache_dir
end

Public Instance Methods

cache_facts(resolved_facts) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 29
def cache_facts(resolved_facts)
  return unless Options[:cache] && Options[:ttls].any?

  @groups = {}
  resolved_facts.each do |fact|
    cache_fact(fact)
  end

  begin
    write_cache unless @groups.empty?
  rescue Errno::EACCES => e
    @log.warn("Could not write cache: #{e.message}")
  end
end
fact_cache_enabled?(fact_name) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 44
def fact_cache_enabled?(fact_name)
  fact = @fact_groups.get_fact(fact_name)
  if fact
    !fact[:ttls].nil?
  else
    false
  end

  # fact_group = @fact_groups.get_fact_group(fact_name)
  # delete_cache(fact_group) if fact_group && !cached
end
resolve_facts(searched_facts) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 12
def resolve_facts(searched_facts)
  return searched_facts, [] if (!File.directory?(@cache_dir) || !Options[:cache]) && Options[:ttls].any?

  facts = []
  searched_facts.delete_if do |fact|
    res = resolve_fact(fact)
    if res
      facts << res
      true
    else
      false
    end
  end

  [searched_facts, facts.flatten]
end

Private Instance Methods

cache_fact(fact) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 142
def cache_fact(fact)
  fact_name = if fact.file
                File.basename(fact.file)
              else
                fact.name
              end

  group_name = @fact_groups.get_fact_group(fact_name)

  return unless group_name

  return unless fact_cache_enabled?(fact_name)

  @groups[group_name] ||= {}
  @groups[group_name][fact.name] = fact.value
end
check_ttls?(group_name, ttls) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 208
def check_ttls?(group_name, ttls)
  return false unless ttls

  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 true if expire_date > Time.now

    File.delete(cache_file_name)
  end

  @log.debug("#{group_name} facts cache file expired, missing or is corrupt")
  true
end
create_facts(searched_fact, data) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 118
def create_facts(searched_fact, data)
  if searched_fact.type == :file
    resolve_external_fact(searched_fact, data)
  else
    return unless data[searched_fact.name]

    [Facter::ResolvedFact.new(searched_fact.name, data[searched_fact.name], searched_fact.type,
                              searched_fact.user_query)]
  end
end
delete_cache(group_name) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 224
def delete_cache(group_name)
  cache_file_name = File.join(@cache_dir, group_name)

  begin
    File.delete(cache_file_name) if File.readable?(cache_file_name)
  rescue Errno::EACCES, Errno::EROFS => e
    @log.warn("Could not delete cache: #{e.message}")
  end
end
external_fact_in_custom_group?(searched_fact, fact_name, fact) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 78
def external_fact_in_custom_group?(searched_fact, fact_name, fact)
  if searched_fact.type == :file && fact[:group] != fact_name
    @log.error("Cannot cache '#{fact_name}' fact from '#{fact[:group]}' group. "\
                'Caching custom group is not supported for external facts.')
    return true
  end

  false
end
facts_already_cached?(cache_file_name, data) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 179
def facts_already_cached?(cache_file_name, data)
  if File.readable?(cache_file_name)
    file = Facter::Util::FileHelper.safe_read(cache_file_name)
    begin
      cached_data = JSON.parse(file) unless file.nil?
      return true if (data.keys - cached_data.keys).empty?
    rescue JSON::ParserError => e
      @log.debug("Failed to read cache file #{cache_file_name}. Detail: #{e.message}")
    rescue NoMethodError => e
      @log.debug("No keys found in #{cache_file_name}. Detail: #{e.message}")
    end
  end
  false
end
read_fact(searched_fact, fact_group) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 88
def read_fact(searched_fact, fact_group)
  data = nil
  Facter::Framework::Benchmarking::Timer.measure(searched_fact.name, 'cached') do
    data = read_group_json(fact_group)
  end
  return unless data

  unless searched_fact.file
    return unless valid_format_version?(searched_fact, data, fact_group)

    delete_cache(fact_group) unless data.keys.grep(/#{searched_fact.name}/).any?
    # data.fetch(searched_fact.name) { delete_cache(fact_group) }
  end

  @log.debug("loading cached values for #{searched_fact.name} facts")

  create_facts(searched_fact, data)
end
read_group_json(group_name) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 194
def read_group_json(group_name)
  return @groups[group_name] if @groups.key?(group_name)

  cache_file_name = File.join(@cache_dir, group_name)
  data = nil
  file = Facter::Util::FileHelper.safe_read(cache_file_name)
  begin
    data = JSON.parse(file) unless file.nil?
  rescue JSON::ParserError
    delete_cache(group_name)
  end
  @groups[group_name] = data
end
resolve_external_fact(searched_fact, data) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 129
def resolve_external_fact(searched_fact, data)
  facts = []
  data.each do |fact_name, fact_value|
    next if fact_name == 'cache_format_version'

    fact = Facter::ResolvedFact.new(fact_name, fact_value, searched_fact.type,
                                    searched_fact.user_query)
    fact.file = searched_fact.file
    facts << fact
  end
  facts
end
resolve_fact(searched_fact) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 58
def resolve_fact(searched_fact)
  fact_name = if searched_fact.file
                File.basename(searched_fact.file)
              else
                searched_fact.name
              end

  return unless fact_cache_enabled?(fact_name)

  fact = @fact_groups.get_fact(fact_name)

  return if external_fact_in_custom_group?(searched_fact, fact_name, fact)

  return unless fact

  return unless check_ttls?(fact[:group], fact[:ttls])

  read_fact(searched_fact, fact[:group])
end
valid_format_version?(searched_fact, data, fact_group) click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 107
    def valid_format_version?(searched_fact, data, fact_group)
      unless data['cache_format_version'] == 1
        @log.debug("The fact #{searched_fact.name} could not be read from the cache, \
cache_format_version is incorrect!")
        delete_cache(fact_group)
        return false
      end

      true
    end
write_cache() click to toggle source
# File lib/facter/framework/core/cache_manager.rb, line 159
def write_cache
  unless File.directory?(@cache_dir)
    require 'fileutils'
    FileUtils.mkdir_p(@cache_dir)
  end

  @groups.each do |group_name, data|
    next unless check_ttls?(group_name, @fact_groups.get_group_ttls(group_name))

    cache_file_name = File.join(@cache_dir, group_name)

    next if facts_already_cached?(cache_file_name, data)

    @log.debug("caching values for #{group_name} facts")

    data['cache_format_version'] = 1
    File.write(cache_file_name, JSON.pretty_generate(data))
  end
end