mergedata

Table of Contents

Prototype: mergedata(one, two, etc)

Return type: data

Description: Returns the merger of any named data containers or lists. Can also wrap and unwrap data containers.

The returned data container will have the keys from each of the named data containers, arrays, or lists.

If all the data containers are JSON arrays, they are merged into a single array, as you'd expect from merging two arrays.

If any of the data containers are JSON objects, all the containers are treated as JSON objects (for arrays, the key is the element's offset).

If a key inside a data container is specified (container[key]), the value under that key is extracted and merged. The key can be a string for JSON objects or a number for JSON arrays. Note that a single data container, CFEngine array, or slist can be named, in which case you're simply extracting the contents of the data container into a new data container.

If any list (slist, ilist, or rlist) is named, it's first converted to a JSON array, then merged as above.

If any CFEngine "classic" array (array[key]) is named, it's first converted to a JSON object, then merged as above.

mergedata() is thus a convenient way, together with getindices() and getvalues(), to bridge the gap between data container and the traditional list and array data types in CFEngine.

If any of the above-mentioned variables are named inside brackets like [ thing ] then the thing will be wrapped in a JSON array and then merged..

If any of the above-mentioned variables are named inside braces with a key like { "newkey": thing } then the thing will be wrapped in a JSON object under key newkey and then merged.

Example:

body common control
{
      bundlesequence => { "test", "test2" };
}

bundle agent test
{
  vars:
      "d1" data => parsejson('{ "a": [1,2,3], "b": [] }');
      "d2" data => parsejson('{ "b": [4,5,6] }');
      "d3" data => parsejson('[4,5,6]');
      "list1" slist => { "element1", "element2" };
      "array1[mykey]" slist => { "array_element1", "array_element2" };
      "array2[otherkey]" string => "hello";

      "merged_d1_d2" data => mergedata("d1", "d2");
      "merged_d1_d3" data => mergedata("d1", "d3");
      "merged_d3_list1" data => mergedata("d3", "list1");

      "merged_d1_array1" data => mergedata("d1", "array1");
      "merged_d2_array2" data => mergedata("d2", "array2");

      "merged_d1_wrap_array_d2" data => mergedata("d1", "[ d2 ]");
      "merged_d1_wrap_map_d2" data => mergedata("d1", '{ "newkey": d2 }');

      "merged_d1_d2_str" string => format("merging %S with %S produced %S", d1, d2, merged_d1_d2);
      "merged_d1_wrap_array_d2_str" string => format("merging %S with wrapped [ %S ] produced %S", d1, d2, merged_d1_wrap_array_d2);
      "merged_d1_wrap_map_d2_str" string => format('merging %S with wrapped { "newkey": %S produced %S', d1, d2, merged_d1_wrap_map_d2);
      "merged_d1_d3_str" string => format("merging %S with %S produced %S", d1, d3, merged_d1_d3);
      "merged_d3_list1_str" string => format("merging %S with %S produced %S", d3, list1, merged_d3_list1);

      "merged_d1_array1_str" string => format("merging %S with %s produced %S", d1, array1, merged_d1_array1);
      "merged_d2_array2_str" string => format("merging %S with %s produced %S", d2, array2, merged_d2_array2);
  reports:
      "$(merged_d1_d2_str)";
      "$(merged_d1_wrap_array_d2_str)";
      "$(merged_d1_wrap_map_d2_str)";
      "$(merged_d1_d3_str)";
      "$(merged_d3_list1_str)";
      "$(merged_d1_array1_str)";
      "$(merged_d2_array2_str)";
}

bundle agent test2
{
  vars:
      "a"       data  => parsejson('{ "a": "1" }'), meta => { "mymerge" };
      "b"       data  => parsejson('{ "b": "2" }'), meta => { "mymerge" };
      "c"       data  => parsejson('{ "c": "3" }'), meta => { "mymerge" };
      "d"       data  => parsejson('{ "d": "4" }'), meta => { "mymerge" };
      "todo"    slist => variablesmatching(".*", "mymerge");

  methods:
      "go" usebundle => cmerge(@(todo)); # a, b, c, d

  reports:
      "$(this.bundle): merged containers with cmerge = $(cmerge.all_str)";
}

bundle agent cmerge(varlist)
{
  vars:
      "all"     data => parsejson('[]'),            policy => "free";
      "all"     data => mergedata(all, $(varlist)), policy => "free";
      "all_str" string => format("%S", all),        policy => "free";
}

Output:

R: merging {"a":[1,2,3],"b":[]} with {"b":[4,5,6]} produced {"a":[1,2,3],"b":[4,5,6]}
R: merging {"a":[1,2,3],"b":[]} with wrapped [ {"b":[4,5,6]} ] produced {"0":{"b":[4,5,6]},"a":[1,2,3],"b":[]}
R: merging {"a":[1,2,3],"b":[]} with wrapped { "newkey": {"b":[4,5,6]} produced {"a":[1,2,3],"b":[],"newkey":{"b":[4,5,6]}}
R: merging {"a":[1,2,3],"b":[]} with [4,5,6] produced {"0":4,"1":5,"2":6,"a":[1,2,3],"b":[]}
R: merging [4,5,6] with { "element1", "element2" } produced [4,5,6,"element1","element2"]
R: merging {"a":[1,2,3],"b":[]} with array1 produced {"a":[1,2,3],"b":[],"mykey":["array_element1","array_element2"]}
R: merging {"b":[4,5,6]} with array2 produced {"b":[4,5,6],"otherkey":"hello"}
R: test2: merged containers with cmerge = {"a":"1","b":"2","c":"3","d":"4"}

See also: getindices(), getvalues(), readjson(), parsejson(), readyaml(), parseyaml(), and data documentation.