Introducing bodies with custom promise types

Posted by Lars Erik Wik
February 8, 2022

Last year we had a look at managing local groups with the custom groups promise type. As you may or may not recall, we used JSON-strings to imitate CFEngine bodies. This was due to the fact that the promise module protocol did not support bodies at that time. Today, on the other hand, we’re happy to announce that as of CFEngine 3.20, this will no longer be the case. In this blog post we’ll introduce the long awaited feature; custom bodies. We’ll have a look at it from both the policy writers- and the promise module developers point of view.

Dear policy writers

Let’s rewind back to the olden days, when we rode horse ’n carriage, and imitated custom bodies using JSON-strings. Back then we wrote our groups promises like this:

bundle agent __main__
{
  groups:
    "foo"
      policy => "present",
      members => '{ "include": ["alice", "bob"],
                    "exclude": ["malcom"] }';
}

It may have been somewhat ugly; but back then we managed to get by with what we had. More importantly; it worked!

We’ve come a long way since then. Nowadays, the new kids on the block writes policy utilizing custom bodies:

body members foo
{
  include => { "alice", "bob" };
  exclude => { "malcom" };
}

bundle agent __main__
{
  groups:
    "foo"
      policy => "present",
      members => foo;
}

It’s beautiful indeed!

Not only do custom bodies provide a cleaner way of writing custom policy, they also unlock access to powerful features like body inheritance, just like you would find in built-in policy.

body members parent
{
  include => { "alice", "bob" };
}

body members child(user)
{
  exclude => { "$(user)" };
  inherit_from => parent;
}

bundle agent __main__
{
  groups:
    "foo"
      policy => "present",
      members => child("malcom");
}

Okay. So, maybe you’re one of those old folks who hates change, or maybe you run multiple versions of CFEngine. No matter who you are, there is no need to fear. The groups promise type is currently compatible with both the new and the old way of doing things. If you were to run multiple versions of CFEngine; you could write your policy as follows:

@if minimum_version(3.20)
body members foo
{
  include => { "alice", "bob" };
  exclude => { "malcom" };
}
@endif

bundle agent __main__
{
  groups:
    "foo"
      policy => "present",
@if minimum_version(3.20)
      members => foo;
@else
      members => '{ "include": ["alice", "bob"],
                    "exclude": ["malcom"] }';
@endif
}

Or if you’re one of them old folks; you could just stick to the old way of doing things.

Promise module developers

For promise module developers, the only difference in the new and the old way of doing things, is that before, the “body” was sent as a JSON string to the promise module. And you had to manually parse it.

"attributes": {
  "policy": "present",
  "members": "{
    \"include\": [\"alice\", \"bob\"],
    \"exclude\": [\"malcom\"]
  }"
}

The difference in how custom bodies are sent to the promise module, is that they are sent as an actual JSON object.

"attributes": {
  "policy": "present",
  "members": {
    "include": ["alice", "bob"],
    "exclude": ["malcom"]
  }
}

Therefor there is essentially no difference in terms of what is sent by the protocol when using body or data container. In fact the attributes part of the JSON would be equivalent for both the foo and the bar promise.

body members foo_members
{
  include => { "alice", "bob" };
  exclude => { "malcom" };
}

bundle agent __main__
{
  vars:
      "bar_members"
        data => '{ "include": ["alice", "bob"],
                   "exclude": ["malcom"] }';

  groups:
      "foo"
        members => bar_members;
      "bar"
        members => "@bar_members";
}

For more information on how the JSON protocol works, please checkout the master documentation custom promise types.

Share your thoughts

By now you know all there is to know about custom bodies. If you have any questions or thoughts on this matter; please let us know over at GitHub Discussions. We are eager to hear your feedback.