This question was covered in The agent is in, Episode 27 - CFEngine Q&A: Policy questions.
Given the following JSON, how can I get a list containing just the values of name?
[
{ "name": "Aurora", "description": "Illuminating" },
{ "name": "Orion", "description": "Stellar" },
{ "name": "Luna", "description": "Serene" },
{ "name": "Phoenix", "description": "Resilient" },
{ "name": "Atlas", "description": "Strong" }
]
Using maparray()
The most concise and direct way to achieve something like this is to use the maparray() function. It iterates over a list or data container applying a pattern based on $(this.k)
and $(this.v)
of the currently iterated element to produce a list.
bundle agent __main__
{
methods:
"example";
}
bundle agent example
{
vars:
"d" data => '[
{ "name": "Aurora", "description": "Illuminating" },
{ "name": "Orion", "description": "Stellar" },
{ "name": "Luna", "description": "Serene" },
{ "name": "Phoenix", "description": "Resilient" },
{ "name": "Atlas", "description": "Strong" }
]';
"names"
slist => sort(
maparray( "$(d[$(this.k)][name])", d ),
lex
);
}
Here we run with --show-evaluated-vars=default:example
to show variable values at the end of the agent run filtered to show only variables in the default
namespace from bundles matching example
and --file /tmp/example-1.cf
to specify the policy entry:
cf-agent --show-evaluated-vars=default:example --file /tmp/example-1.cf
Variable name Variable value Meta tags Comment
default:example.d [{"description":"Illuminating","name":"Aurora"},{"description":"Stellar","name":"Orion"},{"description":"Serene","name":"Luna"},{"description":"Resilient","name":"Phoenix"},{"description":"Strong","name":"Atlas"}] source=promise
default:example.names {"Atlas","Aurora","Luna","Orion","Phoenix"} source=promise
Using an associative array
Associative arrays are a very useful and flexible construct. They allow you to build structured data up key value by key value, potentially applying specific rules at each step.
We need to iterate over the data container and extract the names prior to getting the consolidated list.
bundle agent __main__
{
methods:
"example";
}
bundle agent example
{
vars:
"d"
data => '[
{ "name": "Aurora", "description": "Illuminating" },
{ "name": "Orion", "description": "Stellar" },
{ "name": "Luna", "description": "Serene" },
{ "name": "Phoenix", "description": "Resilient" },
{ "name": "Atlas", "description": "Strong" }
]';
"d_keys"
slist => getindices( d );
"name[$(d_keys)]"
string => "$(d[$(d_keys)][name])";
"names"
slist => sort( getvalues( name ), lex );
}
Again, we run the agent with --show-evaluated-vars=default:example
to see these veriables:
cf-agent --show-evaluated-vars=default:example --file /tmp/example-2.cf
Variable name Variable value Meta tags Comment
default:example.d [{"description":"Illuminating","name":"Aurora"},{"description":"Stellar","name":"Orion"},{"description":"Serene","name":"Luna"},{"description":"Resilient","name":"Phoenix"},{"description":"Strong","name":"Atlas"}] source=promise
default:example.d_keys {"0","1","2","3","4"} source=promise
default:example.name[0] Aurora source=promise
default:example.name[1] Orion source=promise
default:example.name[2] Luna source=promise
default:example.name[3] Phoenix source=promise
default:example.name[4] Atlas source=promise
default:example.names {"Atlas","Aurora","Luna","Orion","Phoenix"} source=promise
In the above output you may wonder why default:example.d_keys
is a list of numbers. That’s because getindices()
returns the positions of each element when targeting a list, data container array, or data container object of anonymous objects.
Let’s take it a bit further, this time getting the list of names where description
begins with S
.
bundle agent __main__
{
methods:
"example";
}
bundle agent example
{
vars:
"d"
data => '[
{ "name": "Aurora", "description": "Illuminating" },
{ "name": "Orion", "description": "Stellar" },
{ "name": "Luna", "description": "Serene" },
{ "name": "Phoenix", "description": "Resilient" },
{ "name": "Atlas", "description": "Strong" }
]';
"d_keys"
slist => getindices( d );
"name[$(d_keys)]"
string => "$(d[$(d_keys)][name])",
if => regcmp( "S.*", "$(d[$(d_keys)][description])" );
"names"
slist => sort( getvalues( name ), lex );
}
And look at the result:
cf-agent --show-evaluated-vars=default:example --file /tmp/example-3.cf
Variable name Variable value Meta tags Comment
default:example.d [{"description":"Illuminating","name":"Aurora"},{"description":"Stellar","name":"Orion"},{"description":"Serene","name":"Luna"},{"description":"Resilient","name":"Phoenix"},{"description":"Strong","name":"Atlas"}] source=promise
default:example.d_keys {"0","1","2","3","4"} source=promise
default:example.name[1] Orion source=promise
default:example.name[2] Luna source=promise
default:example.name[4] Atlas source=promise
default:example.names {"Atlas","Luna","Orion"} source=promise
We can even do something like uppercase names where descriptions starting with S
have more than 6 characters and lowercase names where descriptions starting with S
have less than 7 characters.
bundle agent __main__
{
methods:
"example";
}
bundle agent example
{
vars:
"d"
data => '[
{ "name": "Aurora", "description": "Illuminating" },
{ "name": "Orion", "description": "Stellar" },
{ "name": "Luna", "description": "Serene" },
{ "name": "Phoenix", "description": "Resilient" },
{ "name": "Atlas", "description": "Strong" }
]';
"d_keys"
slist => getindices( d );
"name[$(d_keys)]"
string => string_upcase( "$(d[$(d_keys)][name])" ),
if => and(
regcmp( "S.*", "$(d[$(d_keys)][description])" ),
isgreaterthan(
string_length( "$(d[$(d_keys)][description])" ),
6));
"name[$(d_keys)]"
string => string_downcase( "$(d[$(d_keys)][name])" ),
if => and(
regcmp( "S.*", "$(d[$(d_keys)][description])" ),
islessthan(
string_length( "$(d[$(d_keys)][description])" ),
7));
"names"
slist => sort( getvalues( name ), lex );
}
Output:
cf-agent --no-lock --show-evaluated-vars=default:example --file /tmp/example-4.cf
Variable name Variable value Meta tags Comment
default:example.d [{"description":"Illuminating","name":"Aurora"},{"description":"Stellar","name":"Orion"},{"description":"Serene","name":"Luna"},{"description":"Resilient","name":"Phoenix"},{"description":"Strong","name":"Atlas"}] source=promise
default:example.d_keys {"0","1","2","3","4"} source=promise
default:example.name[1] ORION source=promise
default:example.name[2] luna source=promise
default:example.name[4] atlas source=promise
default:example.names {"ORION","atlas","luna"} source=promise