Jira (FACT-3003) Don't split keys for already structured json/yaml external facts

9 views
Skip to first unread message

Josh Cooper (Jira)

unread,
Mar 31, 2021, 5:35:04 PM3/31/21
to puppe...@googlegroups.com
Josh Cooper created an issue
 
Facter / Bug FACT-3003
Don't split keys for already structured json/yaml external facts
Issue Type: Bug Bug
Affects Versions: FACT 4.0.52
Assignee: Unassigned
Created: 2021/03/31 2:34 PM
Priority: Normal Normal
Reporter: Josh Cooper

Using facter 3, if I have an external fact (either executable or data), then keys are never split on dots:

[root@superb-gangster ~]# facter --version
3.14.17 (commit ce1f2bb4a91a1ac4ae5852091c96ae6ee3712e23)
[root@superb-gangster ~]# git clone https://github.com/joshcooper/fact_test
[root@superb-gangster ~]# cat fact_test/facts.d/data.json
{
    "dotdata.json": true
}
[root@superb-gangster ~]# puppet facts find --modulepath . | grep 'dotdata.json'
    "dotdata.json": true,

When using facter 4, the "dotdata.json" key is split producing a "double structured" hash:

[root@wide-catch ~]# rpm -qa | grep puppet-agent
puppet-agent-7.5.0.51.ged402e52f-1.el7.x86_64
[root@wide-catch ~]# puppet facts find --modulepath . | jq --sort-keys '.values | with_entries(select(.key | match("dotdata")))'
{
  "dotdata": {
    "flat": "true",
    "json": true,
    "yaml": true
  }
}

If an external fact (data or executable) produces YAML or JSON, then I would expect keys to be preserved (since the data is already structured) regardless of any facter configuration.

I would expect external facts that produce key=value pairs to continue to be split on dots (and merged with existing structured facts) unless escaped by quotes.

So in the example above, I'd expect:

{
  "dotdata": {
    "flat": "true",
  }
  "dotdata.json": true,
  "dotdata.yaml": true
}

Add Comment Add Comment
 
This message was sent by Atlassian Jira (v8.5.2#805002-sha1:a66f935)
Atlassian logo

Nick Walker (Jira)

unread,
Apr 1, 2021, 12:14:04 PM4/1/21
to puppe...@googlegroups.com

Nick Walker (Jira)

unread,
Apr 1, 2021, 12:31:02 PM4/1/21
to puppe...@googlegroups.com
Nick Walker commented on Bug FACT-3003
 
Re: Don't split keys for already structured json/yaml external facts

If the users intent is to make a structured fact with a dot in the name of the fact there are 2 ways

1. ruby custom fact

Facter.add( "a.b" ) do
  setcode do
    { c  = true } 
  end
end

2. an external fact ( either a script of a json/yaml file )

 

{
"a.b":

{ "c": true }

}

Given these two examples it does not make sense to treat a json/yaml file on disk differently than the Facter.add example. If the users intent is to have a structured fact with dots in the name we have no way of knowing that from either of these examples. Both examples appear the same and both will result in the fact a = { b =>

{ c => true } } } in Facter 4 which is new behavior compared to Facter 3 which would have made a.b = { c => true }

.

This ticket is closed as won't fix because we're assuming we want to keep the behavior of Facter 4 treating dots specially. If we want to revert that behavior it's a different request altogether.

Reid Vandewiele (Jira)

unread,
Apr 1, 2021, 1:42:02 PM4/1/21
to puppe...@googlegroups.com

I did a think-through of this and came up with the following framing, which I think describes how we have implemented this functionality today.

 

Adding a Fact

There are three primary ways to add facts to Facter.

  1. Via the Ruby API's Facter.add() method
  2. Via a text-emitting external fact
  3. Via a structured-object emitting external fact

For all three methods, the definition spec is the same.

Input: A dot-notation insertion spec indicating where in the Fact tree the new value should appear.

Output: The value which should appear at the designated insertion point.

Examples

Each of the following examples results in the following Fact tree.

{
  "tier": "production",
  "cmdb" {
    "owner": "Jaime",
    "raci": {
      "responsible": "Jaime",
      "informed": "Logan"
    }
  }
}

Ruby API

One insertion per call.

Facter.add("tier") do
  "production"
end
 
Facter.add("cmdb.owner") do
  "Jaime"
end
 
Facter.add("cmdb.raci") do
  { "responsible" => "Jaime", "informed" => "Logan" }
end

Text-emitting external fact

Text-emitting external facts can only set scalar values at given insertion points, since text does not have a clearly defined way to set arrays or objects. If object structure is required, multiple text insertions can be used together.

tier=production
cmdb.owner=Jaime
cmdb.raci.responsible=Jaime
cmdb.raci.informed=Logan

Structure-emitting external fact

Multiple insertions allowed in the same output. This example shows using multiple insertions to build cmdb; it would also be possible to build the entirety of cmdb with one insertion. Note however that doing so would potentially prevent other facts from contributing subkeys underneath cmdb.

{
  "tier": "production",
  "cmdb.owner": "Jaime",
  "cmdb.raci": {
    "responsible": "Jaime",
    "informed": "Logan"
  }
}

The JSON Schema that structured external facts should emit is roughly

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
 
  "title": "External Facts",
  "description": "One or more external facts to insert into the Facter tree",
  "type": "object",
 
  "patternProperties": {
    "^.+$": {
      "title": "Fact insertion spec",
      "description": "dot-notation insertion spec for a new fact, with value to insert",
      "type": ["string", "integer", "array,", "object", "null"]
    }
  }
}

Reply all
Reply to author
Forward
0 new messages