Facter 3 and 4 behave inconsistently with respect to fact values that return nil vs empty strings summarized as below: |Fact Type|Facter 3|Facter 4| |custom legacy|omitted|omitted| |custom structured|nil|nil| |external legacy|""|omitted| |external structured|omitted|nil|
{{custom}} means the fact is defined using the Ruby API {{Facter.add}} {{external}} means the fact is defined as a JSON data file {{legacy}} means the fact returns a single value {{structured}} means the fact's value is a Hash Puppet always converts nil fact values to "", see PUP-11446, so the "external legacy" case means puppet 7 may omit a fact that was previously included when requesting a catalog. For example:
{noformat} # puppet apply -e 'notice($external_legacy == undef)' Notice: Scope(Class[main]): false ... # puppet apply --facterng -e 'notice($external_legacy == undef)' Warning: Unknown variable: 'external_legacy'. (line: 1, column: 8) Notice: Scope(Class[main]): true ... {noformat}
If you're using {{strict_variables}} then catalog compilation will fail (since the fact is missing):
{noformat} # puppet apply --strict_variables --facterng -e 'notice($external_legacy == undef)' Error: Evaluation Error: Unknown variable: 'external_legacy'. (line: 1, column: 8) on node {noformat}
The "external structured" case has the opposite problem. Previously the fact was omitted and now it will be present with an empty value (due to Puppet converting nil to ""):
{noformat} # puppet apply -e 'notice($facts["external_structured"].keys)' Notice: Scope(Class[main]): [boolean, integer] ... # puppet apply --facterng -e 'notice($facts["external_structured"].keys)' Notice: Scope(Class[main]): [null, integer, boolean] ... # puppet apply --facterng -e 'notice($facts["external_structured"].values)' Notice: Scope(Class[main]): [, 42, false] ... # puppet apply --facterng -e 'notice($facts["external_structured"].values[0] =~ String)' Notice: Scope(Class[main]): true {noformat}
These might break puppet manifests that are not expecting to receive the "extra" key-value pair.
h3. Custom Legacy Facts
Facter 3 and 4 both omit legacy facts with nil values when collecting all facts {{ {} Facter.to_hash { }} } : {noformat} # cat custom/custom_legacy.rb Facter.add(:custom_legacy) { setcode { nil } } # /opt/puppetlabs/puppet/bin/facter -j --custom-dir /root/custom | grep custom_legacy # /opt/puppetlabs/puppet/bin/facter-ng -j --custom-dir /root/custom | grep custom_legacy # {noformat} One inconsistency is if you ask for the fact by name, Facter 3 returns a fact with an empty value, while Facter 4 returns nil: {noformat} # /opt/puppetlabs/puppet/bin/facter -j --custom-dir /root/custom custom_legacy { "custom_legacy": "" } # /opt/puppetlabs/puppet/bin/facter-ng -j --custom-dir /root/custom custom_legacy { "custom_legacy": null } {noformat} h3. Custom Structured Facts
Facter 3 and 4 are consistent when a structured fact contains a fact whose value is nil (the fact is present with a nil value) {noformat} # cat custom/custom_structured.rb Facter.add(:custom_structured) do setcode do { "null" => nil, "integer" => 42, "boolean" => false } end end # /opt/puppetlabs/puppet/bin/facter -j --custom-dir /root/custom custom_structured { "custom_structured": { "null": null, "integer": 42, "boolean": false } } # /opt/puppetlabs/puppet/bin/facter-ng -j --custom-dir /root/custom custom_structured { "custom_structured": { "boolean": false, "integer": 42, "null": null } } {noformat} h3. External Legacy Facts
Facter 3 returns an empty string, while Facter 4 omits the fact: {noformat} # cat external/external_legacy.txt external_legacy= # /opt/puppetlabs/puppet/bin/facter -j --external-dir /root/external | grep external_legacy "external_legacy": "", # /opt/puppetlabs/puppet/bin/facter-ng -j --external-dir /root/external | grep external_legacy # {noformat} h3. External Structured Facts
Facter 3 omits structured facts whose values are nil, while Facter 4 returns the fact with a nil value: {noformat} # cat external/external_structured.json { "external_structured": { "null": null, "integer": 42, "boolean": false } } # /opt/puppetlabs/puppet/bin/facter -j --external-dir /root/external external_structured { "external_structured": { "boolean": false, "integer": 42 } } # /opt/puppetlabs/puppet/bin/facter-ng -j --external-dir /root/external external_structured { "external_structured": { "boolean": false, "integer": 42, "null": null } } {noformat}
h3. Summary
In the "external legacy" case, facter 3 returns a {{nil}} value which puppet converts to "", see PUP-11446, while facter 4 omits the fact. For example:
{noformat} # puppet apply -e 'notice($external_legacy == undef)' Notice: Scope(Class[main]): false ... # puppet apply --facterng -e 'notice($external_legacy == undef)' Warning: Unknown variable: 'external_legacy'. (line: 1, column: 8) Notice: Scope(Class[main]): true ... {noformat}
If you're using strict_variables then catalog compilation will fail (since the fact is missing):
{noformat} # puppet apply --strict_variables --facterng -e 'notice($external_legacy == undef)' Error: Evaluation Error: Unknown variable: 'external_legacy'. (line: 1, column: 8) on node {noformat}
The "external structured" case has the opposite problem. Previously the fact was omitted and now it will be present with an empty value (due to Puppet converting nil to ""):
{noformat} # puppet apply -e 'notice($facts["external_structured"].keys)' Notice: Scope(Class[main]): [boolean, integer] ... # puppet apply --facterng -e 'notice($facts["external_structured"].keys)' Notice: Scope(Class[main]): [null, integer, boolean] ... # puppet apply --facterng -e 'notice($facts["external_structured"].values)' Notice: Scope(Class[main]): [, 42, false] ... # puppet apply --facterng -e 'notice($facts["external_structured"].values[0] =~ String)' Notice: Scope(Class[main]): true {noformat}
These might break puppet manifests that are not expecting to receive the "extra" key-value pair. |
|
|