Affects Version/s: FACT 4.0.46
Fix Version/s: FACT 4.0.47
Sprint:ghost 25.11, ghost-2.12, ghost-9.12
Method Found:Needs Assessment
QA Risk Assessment:Needs Assessment
Up through facter 3.14.14 (Puppet 6.x), when facter loaded external facts from files in the /etc/facter/facts.d directory, if the same fact appeared in multiple files, the version of the fact that appeared in the file Puppet loaded last won.
Starting in facter 4.0.46 (Puppet 7.x), if a fact is duplicated in multiple files, facter appears to use the first value it sees for the ultimate value of that fact, not the last.
This is problematic in the case where events can dynamically add and remove files from the /etc/facter/facts.d directory.
Here's an example. I have a NetworkManager dispatch script, /etc/NetworkManager/dispatcher.d/50-facter, that drops facter facts into /etc/facter/facts.d that contribute interface information. E.g.:
Any time the dispatch script updates (adds, rewrites, removes) any of these files, it sends the Puppet agent a SIGUSR1 signal to immediately trigger a Puppet agent run. In the case where I have, say, connected to my work VPN, this will update various settings (proxy settings, nameserver settings, et. al.) to match the values that they must have in order for functional network settings while using the VPN.
Some of the settings conflict across files. For example, the DHCP domain name fact appears in both if_enp6s0.yaml and if_tun0.yaml. But I want the settings in if_tun0.yaml to win, and with facter's old behavior, that was what occurred.
facter appears to sort the entries in the /etc/facter/facts.d directory before loading them. From class Facter::Util::DotD in /opt/puppetlabs/puppet/cache/lib/facter/facter_dot_d.rb:
But if I strace puppet facts, it is clearly not processing the file entries in lexicographical order:
Because the if_tun0.yaml file will always be the last directory entry (because it is removed and added, while the other files are not), and because facter now uses the first fact definition it finds, instead of the last definition, there is no way to get the facts in if_tun0.yaml to override the facts in if_enp6so.yaml.
Even if I update my dispatch script to order the interface files in lexicographical order by priority:
It won't matter, because facter is using the directory order:
There are two simple changes that will resolve this issue:
- Make sure all facter code paths sort the files in /etc/facter/facts.d in lexicographical order before loading them.
- Clearly document how facter resolves loading external facts when the same fact is defined in multiple files. (Does the first fact definition win? The last?)
To be clear: #1 is not a recent issue; Puppet has never loaded the fact files in /etc/facter/facts.d in lexicographical order as far as I know. But without lexicographical ordering, changing the external fact duplicate resolution precedence from last to first breaks certain use cases.
Perhaps #2 is already documented, but if so, I was unable to locate it. (And I also saw nothing in the release notes that mentioned the external fact duplicate resolution precedence change.)