Skip to content
This repository has been archived by the owner on Jun 19, 2020. It is now read-only.

Commit

Permalink
(FACT-2486) Add facts cache
Browse files Browse the repository at this point in the history
  • Loading branch information
florindragos committed Apr 2, 2020
1 parent b630686 commit 1b5e0c3
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 16 deletions.
4 changes: 4 additions & 0 deletions lib/custom_facts/util/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ def self.external_facts_dirs
@external_facts_dirs
end

def self.facts_cache_dir
@facts_cache_dir
end

def self.setup_default_ext_facts_dirs
if LegacyFacter::Util::Root.root?
windows_dir = windows_data_dir
Expand Down
17 changes: 16 additions & 1 deletion lib/framework/config/cache_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def get_fact_group(fact_name)
def get_group_ttls(group_name)
return unless (ttls = @groups_ttls.find { |g| g[group_name] })

ttls[group_name]
ttls_to_seconds(ttls[group_name])
end

private
Expand All @@ -30,5 +30,20 @@ def load_cache_groups
options = Options.instance
@groups_ttls = ConfigReader.new(options[:config]).ttls || {}
end

def ttls_to_seconds(ttls)
duration, unit = ttls.split(' ', 2)
time = duration.to_i
case unit
when 'seconds'
return time
when 'minutes'
return time * 60
when 'hours'
return time * 60 * 60
when 'days'
return time * 60 * 60 * 24
end
end
end
end
87 changes: 87 additions & 0 deletions lib/framework/core/cache_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# frozen_string_literal: true

module Facter
class CacheManager
def self.cache_dir
LegacyFacter::Util::Config.facts_cache_dir
end

def self.resolve_facts(searched_facts)
return searched_facts, [] unless File.directory?(cache_dir)

facts = []
searched_facts.each do |fact|
res = resolve_fact(fact)
facts << res unless res.nil?
end
facts.each do |fact|
searched_facts.delete_if { |f| f.name == fact.name }
end
[searched_facts, facts]
end

def self.resolve_fact(searched_fact)
group_name = Facter::CacheList.instance.get_fact_group(searched_fact.name)
return nil unless group_name

check_ttls(group_name)
data = read_group_json(group_name)
return nil if data.nil? || data[searched_fact.name].nil?

create_fact(searched_fact, data[searched_fact.name])
end

def self.create_fact(searched_fact, value)
resolved_fact = Facter::ResolvedFact.new(searched_fact.name, value, searched_fact.type)
resolved_fact.user_query = searched_fact.user_query
resolved_fact.filter_tokens = searched_fact.filter_tokens
resolved_fact
end

def self.cache_facts(resolved_facts)
unless File.directory?(cache_dir)
require 'fileutils'
FileUtils.mkdir_p(cache_dir)
end

resolved_facts.each do |fact|
cache_fact(fact)
end
end

def self.cache_fact(fact)
group_name = Facter::CacheList.instance.get_fact_group(fact.name)
return if group_name.nil? || fact.value.nil?

return unless check_ttls(group_name)

data = read_group_json(group_name) || {}
cache_file_name = File.join(cache_dir, group_name)
data[fact.name] = fact.value
File.write(cache_file_name, JSON.pretty_generate(data))
end

def self.read_group_json(group_name)
cache_file_name = File.join(cache_dir, group_name)
data = nil
if File.exist?(cache_file_name)
file = File.read(cache_file_name)
data = JSON.parse(file)
end
data
end

def self.check_ttls(group_name)
ttls = Facter::CacheList.instance.get_group_ttls(group_name)
return nil unless ttls

cache_file_name = File.join(cache_dir, group_name)
return nil unless File.exist?(cache_file_name)

file_time = File.mtime(cache_file_name)
expire_date = file_time + ttls
File.delete(cache_file_name) if expire_date < Time.now
ttls
end
end
end
5 changes: 5 additions & 0 deletions lib/framework/core/fact_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@ def resolve_facts(user_query = [])
loaded_facts = @fact_loader.load(Options.get)
searched_facts = QueryParser.parse(user_query, loaded_facts)

searched_facts, cached_facts = Facter::CacheManager.resolve_facts(searched_facts)
internal_facts = @internal_fact_mgr.resolve_facts(searched_facts)
external_facts = @external_fact_mgr.resolve_facts(searched_facts)

resolved_facts = override_core_facts(internal_facts, external_facts)

Facter::CacheManager.cache_facts(resolved_facts)
resolved_facts = resolved_facts.concat(cached_facts)

FactFilter.new.filter_facts!(resolved_facts)

resolved_facts
Expand Down
4 changes: 0 additions & 4 deletions lib/models/resolved_fact.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ class ResolvedFact
attr_accessor :user_query, :filter_tokens, :value

def initialize(name, value = '', type = :core)
unless type =~ /core|legacy|custom/
raise ArgumentError, 'The type provided for fact is not legacy, core or custom!'
end

@name = name
@value = Utils.deep_stringify_keys(value)
@type = type
Expand Down
8 changes: 0 additions & 8 deletions spec/facter/model/resolved_fact_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,4 @@
expect(resolved_fact.core?).to be(true)
end
end

context 'when is an invalid type' do
it 'raises an ArgumentError' do
expect do
Facter::ResolvedFact.new('fact_name', 'fact_value', :type)
end.to raise_error(ArgumentError, 'The type provided for fact is not legacy, core or custom!')
end
end
end
17 changes: 14 additions & 3 deletions spec/framework/core/fact_manager_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,30 @@

resolved_fact = mock_resolved_fact('os', 'Ubuntu', '', [])

seached_facts = [searched_fact1, searched_fact2]

allow(Facter::QueryParser)
.to receive(:parse)
.with(user_query, loaded_facts)
.and_return([searched_fact1, searched_fact2])
.and_return(seached_facts)

allow(internal_manager)
.to receive(:resolve_facts)
.with([searched_fact1, searched_fact2])
.with(seached_facts)
.and_return([resolved_fact])

allow(external_manager)
.to receive(:resolve_facts)
.with([searched_fact1, searched_fact2])
.with(seached_facts)

allow(Facter::CacheManager)
.to receive(:resolve_facts)
.with(seached_facts)
.and_return([seached_facts, []])

allow(Facter::CacheManager)
.to receive(:cache_facts)
.with([resolved_fact])

resolved_facts = Facter::FactManager.instance.resolve_facts(user_query)

Expand Down

0 comments on commit 1b5e0c3

Please sign in to comment.