What’s the difference between an associative array and a data container in CFEngine?
CFEngine has two ways in which structured data can be used, associative arrays (sometimes called classic arrays) and data containers. Let’s take a look at a simple data structure.
Here we have two data structures, a_email
an associative array and d_email
a data container. The policy emits the JSON representation of each.
bundle agent __main__
{
vars:
"a_email[john@example.com][FirstName]" string => "John";
"a_email[john@example.com][LastName]" string => "Doe";
"d_email" data => '{ "john@example.com": { "FirstName": "John", "LastName": "Doe" } }';
reports:
"JSON representation of a_email (associateve array):$(const.n)$(with)"
with => storejson( a_email );
"JSON representation of d_email (data container):$(const.n)$(with)"
with => storejson( d_email );
}
Looking at the output, they are identical:
R: JSON representation of a_email (associateve array):
{
"john@example.com": {
"FirstName": "John",
"LastName": "Doe"
}
}
R: JSON representation of d_email (data container):
{
"john@example.com": {
"FirstName": "John",
"LastName": "Doe"
}
}
While their JSON representations are identical, the actually differ in a significant way. Notice that the associative array is defined key value pair by key value pair:
vars:
"a_email[john@example.com][FirstName]" string => "John";
"a_email[john@example.com][LastName]" string => "Doe";
Indeed, associative arrays in CFEngine are a collection of individual variables that are associated by their name, while a data container is an entire single unit.
"d_email" data => '{ "john@example.com": { "FirstName": "John", "LastName": "Doe" } }';
Both can be used and referenced similarly, but associative arrays being a collection of individual variables can be constructed dynamically. This can be a very useful capability, especially combined with CFEngine’s implicit list iteration.
Say we want to have a data structure representing files and we want to have a key with the files mtime, something like this:
{
"/tmp/file-1.txt": { "mtime": "1234" },
"/tmp/file-2.txt": { "mtime": "4321" }
}
This is trivial to achieve leveraging an associative array:
bundle agent __main__
{
vars:
"files" slist => { "/tmp/file-1.txt", "/tmp/file-2.txt" };
"file[$(files)][mtime]"
string => filestat( "$(files)", "mtime" );
reports:
"$(with)" with => storejson( file );
}
R: {
"/tmp/file-1.txt": {
"mtime": "1709940438"
},
"/tmp/file-2.txt": {
"mtime": "1709940820"
}
}
But that is much more challenging to do using data containers since the entire data container must be defined at once.
Happy Friday! 🎉