Skip to content

Commit 8128105

Browse files
committed
(FACT-2786) Fix fact caching if fact is defined in multiple groups
1 parent 202f8d2 commit 8128105

4 files changed

Lines changed: 78 additions & 28 deletions

File tree

acceptance/tests/custom_facts/cached_custom_fact.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373

7474
step 'should read from the cached file for a custom fact that has been cached' do
7575
on(agent, facter("#{custom_fact_name} --debug", environment: env)) do |facter_result|
76-
assert_match(/Loading cached custom facts from file ".+"|loading cached values for cached-custom-facts facts/, facter_result.stderr,
76+
assert_match(/Loading cached custom facts from file ".+"|loading cached values for random_custom_fact facts/, facter_result.stderr,
7777
'Expected debug message to state that cached custom facts are read from file')
7878
end
7979
end

lib/facter/framework/config/fact_groups.rb

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module Facter
44
class FactGroups
5-
attr_reader :groups, :block_list
5+
attr_reader :groups, :block_list, :facts_ttls
66

77
@groups_ttls = []
88

@@ -14,6 +14,7 @@ def initialize(group_list_path = nil)
1414
@groups ||= File.readable?(@groups_file_path) ? Hocon.load(@groups_file_path) : {}
1515
load_groups
1616
load_groups_from_options
17+
load_facts_ttls
1718
end
1819

1920
# Breakes down blocked groups in blocked facts
@@ -31,6 +32,9 @@ def blocked_facts
3132

3233
# Get the group name a fact is part of
3334
def get_fact_group(fact_name)
35+
fact = get_fact(fact_name)
36+
return fact[:group] if fact
37+
3438
@groups.detect { |k, v| break k if Array(v).find { |f| fact_name =~ /^#{f}.*/ } }
3539
end
3640

@@ -41,6 +45,15 @@ def get_group_ttls(group_name)
4145
ttls_to_seconds(ttls[group_name])
4246
end
4347

48+
def get_fact(fact_name)
49+
return @facts_ttls[fact_name] if @facts_ttls[fact_name]
50+
51+
fact = @facts_ttls.select { |k, v| break v if fact_name =~ /^#{k}\..*/ }
52+
return nil if fact == {}
53+
54+
fact
55+
end
56+
4457
private
4558

4659
def load_groups_from_options
@@ -55,10 +68,30 @@ def load_groups_from_options
5568
end
5669
end
5770

71+
def load_facts_ttls
72+
@facts_ttls ||= {}
73+
return if @groups_ttls == []
74+
75+
@groups_ttls.reduce(:merge).each do |group, ttls|
76+
ttls = ttls_to_seconds(ttls)
77+
if @groups[group]
78+
@groups[group].each do |fact|
79+
if @facts_ttls[fact]
80+
@facts_ttls[fact] = { ttls: ttls, group: group } if @facts_ttls[fact][:ttls] < ttls
81+
else
82+
@facts_ttls[fact] = { ttls: ttls, group: group }
83+
end
84+
end
85+
else
86+
@facts_ttls[group] = { ttls: ttls, group: group }
87+
end
88+
end
89+
end
90+
5891
def load_groups
5992
config = ConfigReader.init(Options[:config])
6093
@block_list = config.block_list || {}
61-
@groups_ttls = config.ttls || {}
94+
@groups_ttls = config.ttls || []
6295
@groups.merge!(config.fact_groups) if config.fact_groups
6396
end
6497

lib/facter/framework/core/cache_manager.rb

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,16 @@ def cache_facts(resolved_facts)
4040
end
4141
end
4242

43-
def group_cached?(group_name)
44-
cached = @fact_groups.get_group_ttls(group_name) ? true : false
45-
delete_cache(group_name) unless cached
43+
def fact_cache_enabled?(fact_name)
44+
fact = @fact_groups.get_fact(fact_name)
45+
cached = if fact
46+
!fact[:ttls].nil?
47+
else
48+
false
49+
end
50+
51+
fact_group = @fact_groups.get_fact_group(fact_name)
52+
delete_cache(fact_group) if fact_group && !cached
4653
cached
4754
end
4855

@@ -57,14 +64,14 @@ def resolve_fact(searched_fact)
5764

5865
return unless group_name
5966

60-
return unless group_cached?(group_name)
67+
return unless fact_cache_enabled?(searched_fact.name)
6168

62-
return unless check_ttls?(group_name)
69+
return unless check_ttls?(searched_fact.name)
6370

6471
data = read_group_json(group_name)
6572
return unless data
6673

67-
@log.debug("loading cached values for #{group_name} facts")
74+
@log.debug("loading cached values for #{searched_fact.name} facts")
6875

6976
create_facts(searched_fact, data)
7077
end
@@ -93,7 +100,7 @@ def cache_fact(fact)
93100
end
94101
return if !group_name || fact.value.nil?
95102

96-
return unless group_cached?(group_name)
103+
return unless fact_cache_enabled?(fact.name)
97104

98105
@groups[group_name] ||= {}
99106
@groups[group_name][fact.name] = fact.value
@@ -106,10 +113,10 @@ def write_cache
106113
end
107114

108115
@groups.each do |group_name, data|
109-
next unless check_ttls?(group_name)
116+
cache_file_name = File.join(@cache_dir, group_name)
117+
next if File.readable?(cache_file_name)
110118

111119
@log.debug("caching values for #{group_name} facts")
112-
cache_file_name = File.join(@cache_dir, group_name)
113120
File.write(cache_file_name, JSON.pretty_generate(data))
114121
end
115122
end
@@ -128,8 +135,12 @@ def read_group_json(group_name)
128135
@groups[group_name] = data
129136
end
130137

131-
def check_ttls?(group_name)
132-
ttls = @fact_groups.get_group_ttls(group_name)
138+
def check_ttls?(fact_name)
139+
fact = @fact_groups.get_fact(fact_name)
140+
return unless fact
141+
142+
ttls = fact[:ttls]
143+
group_name = fact[:group]
133144
return false unless ttls
134145

135146
cache_file_name = File.join(@cache_dir, group_name)
@@ -141,7 +152,7 @@ def check_ttls?(group_name)
141152
File.delete(cache_file_name)
142153
end
143154

144-
@log.debug("#{group_name} facts cache file expired/missing")
155+
@log.debug("#{fact_name} facts cache file expired/missing")
145156
true
146157
end
147158

spec/facter/cache_manager_spec.rb

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
let(:group_name) { 'operating system' }
2626
let(:cache_file_name) { File.join(cache_dir, group_name) }
2727
let(:fact_groups) { instance_spy(Facter::FactGroups) }
28+
let(:os_fact) { { ttls: 60, group: 'operating system' } }
29+
let(:external_fact) { { ttls: 60, group: 'ext_file.txt' } }
2830

2931
before do
3032
allow(LegacyFacter::Util::Config).to receive(:facts_cache_dir).and_return(cache_dir)
@@ -72,15 +74,17 @@
7274
allow(File).to receive(:directory?).with(cache_dir).and_return(true)
7375
allow(fact_groups).to receive(:get_fact_group).with('os').and_return(group_name)
7476
allow(fact_groups).to receive(:get_fact_group).with('my_custom_fact').and_return(nil)
77+
allow(fact_groups).to receive(:get_fact_group).with('ext_file.txt').and_return(nil)
7578
allow(fact_groups).to receive(:get_group_ttls).with('ext_file.txt').and_return(nil)
79+
allow(fact_groups).to receive(:get_fact).with('ext_file.txt').and_return(nil)
7680
allow(File).to receive(:readable?).with(cache_file_name).and_return(true)
7781
allow(File).to receive(:mtime).with(cache_file_name).and_return(Time.now)
7882
allow(Facter::Util::FileHelper).to receive(:safe_read).with(cache_file_name).and_return(cached_core_fact)
7983
allow(Facter::Options).to receive(:[]).with(:cache).and_return(true)
8084
end
8185

8286
it 'returns cached fact' do
83-
allow(fact_groups).to receive(:get_group_ttls).with(group_name).and_return(1000)
87+
allow(fact_groups).to receive(:get_fact).with('os').and_return(os_fact)
8488
allow(File).to receive(:readable?).with(cache_file_name).and_return(true)
8589
allow(File).to receive(:readable?).with(File.join(cache_dir, 'ext_file.txt')).and_return(false)
8690

@@ -91,7 +95,7 @@
9195
end
9296

9397
it 'returns searched fact' do
94-
allow(fact_groups).to receive(:get_group_ttls).with(group_name).and_return(1000)
98+
allow(fact_groups).to receive(:get_fact).with('os').and_return(os_fact)
9599
allow(File).to receive(:readable?).with(cache_file_name).and_return(true)
96100
allow(File).to receive(:readable?).with(File.join(cache_dir, 'ext_file.txt')).and_return(false)
97101

@@ -103,18 +107,19 @@
103107
end
104108

105109
it 'deletes cache file' do
106-
allow(fact_groups).to receive(:get_group_ttls).with(group_name).and_return(nil)
110+
allow(fact_groups).to receive(:get_fact).with('os').and_return(nil)
107111
allow(File).to receive(:readable?).with(cache_file_name).and_return(true)
108112
allow(File).to receive(:delete).with(cache_file_name)
113+
allow(fact_groups).to receive(:get_fact_group).with('os').and_return(group_name)
109114
allow(File).to receive(:readable?).with(File.join(cache_dir, 'ext_file.txt')).and_return(false)
110115

111116
cache_manager.resolve_facts(searched_facts)
112117
expect(File).to have_received(:delete).with(cache_file_name)
113118
end
114119

115120
it 'returns cached external facts' do
116-
allow(fact_groups).to receive(:get_group_ttls).with('ext_file.txt').and_return(1000)
117-
allow(fact_groups).to receive(:get_group_ttls).with(group_name).and_return(nil)
121+
allow(fact_groups).to receive(:get_fact).with('ext_file.txt').and_return(external_fact)
122+
allow(fact_groups).to receive(:get_fact).with('os').and_return(nil)
118123
allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
119124
allow(Facter::Util::FileHelper).to receive(:safe_read).with(File.join(cache_dir, 'ext_file.txt'))
120125
.and_return(cached_external_fact)
@@ -135,7 +140,7 @@
135140
before do
136141
allow(File).to receive(:directory?).with(cache_dir).and_return(true)
137142
allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
138-
allow(fact_groups).to receive(:get_group_ttls).with(group_name).and_return(nil)
143+
allow(fact_groups).to receive(:get_fact).with('os').and_return(nil)
139144
allow(fact_groups).to receive(:get_fact_group).with('os').and_return(group_name)
140145
allow(File).to receive(:write).with(cache_file_name, cached_core_fact)
141146
allow(Facter::Options).to receive(:[]).with(:cache).and_return(true)
@@ -150,10 +155,10 @@
150155
context 'with cache group' do
151156
before do
152157
allow(File).to receive(:directory?).with(cache_dir).and_return(true)
158+
allow(fact_groups).to receive(:get_fact).with('os').and_return(os_fact)
153159
allow(fact_groups).to receive(:get_fact_group).with('os').and_return(group_name)
154160
allow(fact_groups).to receive(:get_fact_group).with('my_custom_fact').and_return(nil)
155161
allow(fact_groups).to receive(:get_fact_group).with('my_external_fact').and_return(nil)
156-
allow(fact_groups).to receive(:get_group_ttls).with(group_name).and_return(1000)
157162
allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
158163
allow(File).to receive(:write).with(cache_file_name, cached_core_fact)
159164
allow(Facter::Options).to receive(:[]).with(:cache).and_return(true)
@@ -166,35 +171,36 @@
166171
end
167172
end
168173

169-
describe '#group_cached?' do
174+
describe '#fact_cache_enabled?' do
170175
context 'with ttls' do
171176
before do
172-
allow(fact_groups).to receive(:get_group_ttls).with(group_name).and_return(1000)
177+
allow(fact_groups).to receive(:get_fact).with('os').and_return(os_fact)
173178
allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
174179
end
175180

176181
it 'returns true' do
177-
result = cache_manager.group_cached?(group_name)
182+
result = cache_manager.fact_cache_enabled?('os')
178183
expect(result).to be true
179184
end
180185
end
181186

182187
context 'without ttls' do
183188
before do
184-
allow(fact_groups).to receive(:get_group_ttls).with(group_name).and_return(nil)
189+
allow(fact_groups).to receive(:get_fact).with('os').and_return(nil)
190+
allow(fact_groups).to receive(:get_fact_group).with('os').and_return(group_name)
185191
allow(Facter::Options).to receive(:[]).with(:cache).and_return(true)
186192
allow(File).to receive(:delete).with(cache_file_name)
187193
end
188194

189195
it 'returns false' do
190196
allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
191-
result = cache_manager.group_cached?(group_name)
197+
result = cache_manager.fact_cache_enabled?('os')
192198
expect(result).to be false
193199
end
194200

195201
it 'deletes invalid cache file' do
196202
allow(File).to receive(:readable?).with(cache_file_name).and_return(true)
197-
cache_manager.group_cached?(group_name)
203+
cache_manager.fact_cache_enabled?('os')
198204
expect(File).to have_received(:delete).with(cache_file_name)
199205
end
200206
end

0 commit comments

Comments
 (0)