Bodies

Table of Contents

While the idea of a promise is very simple, the definition of a promise can grow complicated. Complex promises are best understood by breaking them down into independent, re-usable components. The CFEngine reserved word body is used to encapsulate the details of complex promise attribute values. Bodies can optionally have parameters.

    bundle agent example
    {
      files:
        !windows::
          "/etc/passwd"
            handle => "example_files_not_windows_passwd",
            perms => system;

          "/home/bill/id_rsa.pub"
            handle => "example_files_not_windows_bills_priv_ssh_key",
            perms => mog("600", "bill", "sysop"),
            create => "true";
    }

The promisers in this example are the files /etc/passwd and /home/bill/id_rsa.pub. The promise is that the perms attribute type is associated with a named, user-defined promise body system and mog respectively.

    body perms system
    {
      mode => "644";
      owners => { "root" };
      groups => { "root" };
    }

    body perms mog(mode,user,group)
    {
      owners => { "$(user)" };
      groups => { "$(group)" };
      mode   => "$(mode)";
    }

Like bundles, bodies have a type. The type of the body has to match the left-hand side of the promise attribute in which it is used. In this case, files promises have an attribute perms that can be associated with any body of type perms.

The attributes within the body are then type specific. Bodies of type perms consist of the file permissions, the file owner, and the file group, which the instance system sets to 644, root and root, respectively.

Such bodies can be reused in multiple promises. Like bundles, bodies can have parameters. The body mog also consists of the file permissions, file owner, and file group, but the values of those attributes are passed in as parameters.

Body Inheritance

CFEngine 3.8 introduced body inheritance via the inherit_from attribute. It's a parameterized single-inheritance system, so a body can only inherit from one other body, and it can apply parameters. The two bodies must have the same type.

Let's see it with the system and mog bodies from earlier:

    body perms system
    {
      mode => "644";
      owners => { "root" };
      groups => { "root" };
    }

    body perms system_inherited
    {
      inherit_from => mog("644", "root", "root");
    }

The earlier system body and this system_inherited body have the same effect, eventually applying mode 644, owner root, and group root. But they are created differently: the first by explicitly listing the parameters; the other by applying parameters to the inherit_from chain of inheritance.

Which one is better? Usually, inheriting from a more generic specification is considered a better design pattern because it reduces horizontal complexity. But it's less explicit and some users and sites will prefer a more explicit listing of body attributes and their values, as in the system body. CFEngine will accomodate either.

Body parameters can be used in the inheritance chain. Here's another body that inherits from mog but takes a mode. All its other parameters are specified inside the body. So system_inherited_mode("234") is exactly like mog("234", "root", "root").

    body perms system_inherited_mode(mode)
    {
      inherit_from => mog($(mode), "root", "root");
    }

Again, whether you prefer this or directly calling the mog body is your choice. Keep in mind that if you want to maintain compatibility with 3.7 and earlier, inherit_from is not available.

Body inheritance simply copies attributes down the chain to the newest body. The latest wins. Let's see an example with a chain of inheritance from the system body from earlier:

    body perms system
    {
      mode => "644";
      owners => { "root" };
      groups => { "root" };
    }

    body perms system_once(x)
    {
      inherit_from => system;
      owners => { $(x) };
      mode => "645";
    }

    body perms system_twice
    {
      inherit_from => system_once("mark");
      mode => "646";
    }

The inheritance chain goes from system to system_once to system_twice. The owners attribute in system_once will be whatever $(x) is, overwriting the value from system. Then system_twice will inherit that same owners value which is now "mark".

The mode attribute will be overwritten to 645 in system_once and then overwritten to 646 in system_twice.

If this gets complicated, just think "latest wins".

Implicit, Control Bodies

A special case for bodies are the implicit promises that configure the basic operation of CFEngine. These are hard-coded to CFEngine and control the basic operation of the agents, such as cf-agent and cf-serverd. Each agent has a special body whose name is control.

    body agent control
    {
        bundlesequence => { "test" };
    }

This promise bodies configures the bundlesequence to execute on a cf-agent.

    body server control
    {
        allowconnects         => { "127.0.0.1" , "::1", @(def.acl) };
    }

This promise body defines the clients allowed to connect to a cf-serverd. For more information, see the reference documentation about the CFEngine Agents

Default bodies

CFEngine 3.9 introduced a way to create default bodies. It allows defining, for given promise and body types, a body that will be used each time no body is defined. To use a body as default, name it <promise_type>_<body_type> and put it in the bodydefault namespace. For example, a default action body for files promises will be named files_action, and in each files promise, if no action attribute is set, the files_action action will be used.

Note: The default bodies only apply to promises in the default namespace.

In the following example, we define a default action body for files promises, that specifies an action_policy => "warn" to prevent actually modifying files and to only warn about considered modifications. We define it once, and don't have to explicitly put this body in all our files promises.

body file control
{
   namespace => "bodydefault";
}

body action files_action
{
    action_policy => "warn";
}

body file control
{
    namespace => "default";
}