Bodies
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";
}