mustache template failing

41 views
Skip to first unread message

bofh...@gmail.com

unread,
Apr 10, 2017, 3:25:35 AM4/10/17
to help-cfengine
I have the following promise/mustache using community-3.10.0.  The output in the reports section is correct but why won't the mustache file render correctly?  If I put var "b" in the mustache file everything is OK but if I try to use the cmerge variable I don't get anything.  What I am doing wrong?

Thank you in advance,
Robin




Promise:
#!/var/cfengine/bin/cf-agent -K

body common control
{
  inputs => { "$(sys.libdir)/stdlib.cf" };
  bundlesequence => { "run" };
}

bundle agent run
{
  vars:

     "a"        data   => '[
                               { "num": "1", "host": "host1" }
                           ]',  meta => { "mymerge" };

     "b"        data   => '[
                               { "num": "2", "host": "host2" }, { "num": "3", "host": "host3" }
                           ]', meta => { "mymerge" };

     "c"        data   => '[
                               { "num": "4", "host": "host4" }, { "num": "5", "host": "host5" }
                           ]', meta => { "mymerge" };


     "my_hosts"   slist  => variablesmatching(".*", "mymerge");
     "merged"     string => format("%S", "cmerge.list");


  methods:
     "go" usebundle => cmerge("list", @(my_hosts));

  reports:
     "$(cmerge.list_str)";

  files:
    "/tmp/run.txt"
    create          => "true",
    edit_template   => "/tmp/run.mustache",
    template_method => "mustache";
}



Mustache File:
# Works with just b
{{#vars.run.b}}
        num={{num}}
        host={{host}}
{{/vars.run.b}}

# How does cmerge work?
{{#vars.run.cmerge.list}}
        num={{num}}
        host={{host}}
{{/vars.run.cmerge.list}}


Output:
hfob: # ./example.cf
R: [{"host":"host1","num":"1"},{"host":"host4","num":"4"},{"host":"host5","num":"5"},{"host":"host2","num":"2"},{"host":"host3","num":"3"}]


hfob: # cat /tmp/run.txt
# Works with just b
        num=2
        host=host2
        num=3
        host=host3

# How does cmerge work?

Nick Anderson

unread,
Apr 10, 2017, 1:49:11 PM4/10/17
to bofh...@gmail.com, Nick Anderson, help-cfengine

bofh...@gmail.com writes:

> I have the following promise/mustache using community-3.10.0. The output
> in the reports section is correct but why won't the mustache file render
> correctly? If I put var "b" in the mustache file everything is OK but if I
> try to use the cmerge variable I don't get anything. What I am doing wrong?

Hi,

bundle agent cmerge creates a data container (you can easily dump the
multiline json representation for a datacontainer using {{%VARNAME}}. I
shuffled the policy a bit but noteably in your original mustache
template you were trying to access the cmerge data from the run bundle.
cmerge is it's own bundle so you need to access its vars directly
(vars.cmerge.NAME). Also note,
variablesmatching_as_data()
Is a function that collects an merges data in a similar way.

Here is a working version of your example:

# DATASTATE
####
# Works with just b
{{#vars.init.b}}
        num={{num}}
        host={{host}}
{{/vars.init.b}}

# How does cmerge work?
# Bundle agent cmerge creates a data container:
{{%vars.cmerge.list}}

{{#vars.cmerge.list}}
  num={{{num}}}
  host={{{host}}}
{{/vars.cmerge.list}}
bundle agent main 
{
  methods:
    "init";
    "check";
}

bundle agent init
{
  vars:

     "a"        data   => '[
                               { "num": "1", "host": "host1" }
                           ]',  meta => { "mymerge" };

     "b"        data   => '[
                               { "num": "2", "host": "host2" }, { "num": "3", "host": "host3" }
                           ]', meta => { "mymerge" };

     "c"        data   => '[
                               { "num": "4", "host": "host4" }, { "num": "5", "host": "host5" }
                           ]', meta => { "mymerge" };


     "my_hosts"   slist  => variablesmatching(".*", "mymerge");
     "merged"     string => format("%S", "cmerge.list");


  methods:
     "go" usebundle => cmerge( "list", @(my_hosts) );
}

bundle agent check
{
  reports:
     "cmerge.list_str = $(cmerge.list_str)";

  
files:
    "/tmp/run.txt"
    create          => "true",
    edit_template   => "/tmp/run.mustache",
    template_method => "mustache"
;

  reports:
    "Content of /tmp/run.txt"
      printfile => cat("/tmp/run.txt");
}

I hope this helps!


Nick Anderson
Doer of things, CFEngine

Robin Friedrich

unread,
Apr 11, 2017, 1:40:21 AM4/11/17
to help-cfengine
Nick,

Thanks you for your help.  If I may impose I have two more questions.

1.  Why does this promise work when you moved files to the check bundle but when in the init bundle files doesn't work as I would expect.
2.  I figure this one can't be done with the JSON setup I have, but is it possible to get the num from a specific host?  I know the host but I need to get back the number.

Take "b" for example:
"b"        data   => '[
                               { "num": "2", "host": "host2" }, { "num": "3", "host": "host3" }
                           ]', meta => { "mymerge" };

I know that I am running this promise on host2 (or host3) how do I get num=2 (or num=3) respectively?



Thank you in advance,
Robin

Nick Anderson

unread,
Apr 19, 2017, 11:09:02 AM4/19/17
to Robin Friedrich, nick.a...@cfengine.com, help-cfengine

Robin Friedrich <bofh...@gmail.com> writes:

> Nick,
>
> Thanks you for your help. If I may impose I have two more questions.


> 1. Why does this promise work when you moved files to the check bundle
> but when in the init bundle files doesn't work as I would expect.

Well in the [[https://docs.cfengine.com/docs/3.10/reference-language-concepts-normal-ordering.html][Normal Order]] files type promises are actuated before methods type
promises. I am sure it's possible to do it in a single bundle, but you would
want to delay the files promise until after all of your data collection and
merging were done. Mixing data collection/preparation with a promise that uses
that collected or prepared data in the same bundle is fine for small and simple
cases, but as a general rule I try to separate the two things. First I try to
reach a consistent state with the data, then I try to use that data. When they
are done in the same bundle you have to start thinking about normal order and
which promises need to depend on what states, where as with seperate bundles,
you just actuate the bundle that prepares your data first, and then the bundle
that uses the data second.

I don't know if that's the issue or not, I just re-worked the policy to how you
how I would look to do it.

> 2. I figure this one can't be done with the JSON setup I have, but is it
> possible to get the num from a specific host? I know the host but I need
> to get back the number.
>
> Take "b" for example:
>
> "b" data => '[ { "num": "2", "host": "host2" }, { "num": "3", "host": "host3" } ]', meta => { "mymerge" };
>
> I know that I am running this promise on host2 (or host3) how do I get num=2 (or num=3) respectively?

You can iterate on the data and inspect its elements:

#+BEGIN_SRC cfengine3 :exports both :wrap EXAMPLE :results output
bundle agent main
{
vars:
"b"
data => '[{ "num": "2", "host": "host2" }, { "num": "3", "host": "host3" }, { "host": "nickanderson-thinkpad-w550s", "num": "42" }]',
meta => { "mymerge" };

"i" slist => getindices( b );
reports:

"The number associated with my hostname $(sys.uqhost) is $(b[$(i)][num])"
if => strcmp("$(b[$(i)][host])", $(sys.uqhost));
}
#+END_SRC

#+RESULTS:
#+BEGIN_EXAMPLE
R: The number associated with my hostname nickanderson-thinkpad-w550s is 42
#+END_EXAMPLE

You could also probably filter the data with =mapdata()= and a =jq= pipe.

Hope this helps!
--
Reply all
Reply to author
Forward
0 new messages