When promises are actuated, a class can be defined based on its result. For example, if a promise modifies a file’s content, you could define a class that indicates it has been repaired. However, did you know that promises can have multiple outcomes concurrently?
That’s right! Native promises (but not custom promises) can have multiple outcomes. For example, a promise can be both kept and repaired at the same time. Let’s take a look.
There’s a users promise type for managing local users. However, did you know there is also a custom one for managing local groups?
You might have seen it mentioned in the CFEngine Build announcement, the blog post on Managing local groups, or in the announcement supporting custom bodies post. But let’s take another look. The easiest way to integrate the groups custom promise type is by using cfbs, simply cfbs add promise-type-groups in your project. Next, we need some policy that leverages the groups promise type. Let’s create groups.cf in the projects root directory and add it to the project with cfbs add ./groups.cf, selecting the option to add the groups bundle to the bundlesequence.
Generally, cf-agent runs as a privileged user. But did you know that you can also run as an unprivileged user?
A major benefit of running cf-agent unprivileged is the ability to prototype policies during development. However, attempting to execute cf-agent as an unprivileged user without proper configuration will result in errors. Let’s create /tmp/feature-friday-25.cf with the following content:
/tmp/feature-friday-25.cf bundle agent main { reports: "Happy Friday!"; } Now, let’s try running that policy with cf-agent as an unprivileged user:
You probably know about the def.json Augments file. However, are you familiar with host_specific.json?
The def.json Augments file is read, if it’s adjacent to the policy entry. As such, this file is generally distributed as part of the policy set. Its settings apply to all hosts that receive and run the policy. The host_specific.json Augments file, is on the other hand loaded from the $(sys.workdir)/data/ directory. And it is expected to be independent from the policy.
Did you know that CFEngine can simply warn about something not being in the desired state?
Traditionally with CFEngine, you define your desired state and CFEngine works towards making that happen. Sometimes you might not want CFEngine to take action and instead warn that a given promise wants to change something. Let’s take a look at a contrived example.
Say we want the file /tmp/feature-friday-22.txt to exist, we might write a policy that looks like this:
Who cares about that promise?
Today, I want to highlight one of the lightweight knowledge management features in CFEngine. That is, Promisees, also known as Stakeholders. Promisees are references to things that might care about a specific promise. And they can be attached to any promise. Let’s take a look at a contrived example:
bundle agent __main__ { methods: "example_promisees" -> { "Feature Friday #21" }; } bundle agent example_promisees { reports: "Happy Friday!"; } From the example above, we can see that the methods promise - promising to run the example_promisees bundle - has Feature Friday #21 defined as the only promisee. It provides us with a hint that Feature Friday #21 cares that the bundle will be run. Promisees have no effect on the execution of the policy. However, they provide breadcrumbs that can be useful when the policy is re-visited.
Did you know CFEngine has Macros?
They were first introduced in CFEngine 3.7 (back in 2015), and more have been introduced since then. Macros are convenient for preventing the parsing of a section of the policy. It is handy for protecting older binaries from getting tripped up on newer syntax the agent does not understand. Let’s take a look.
Currently there are 8 macros.
minimum_version - Prevent the section of policy from being parsed unless the agent meets a minimum version. maximum_version - Prevent the section of policy from being parsed when the agent exceeds a maximum version. at_version - Prevents the section of policy from being parsed unless the agent is of a specific version. between_versions - Prevents a section of policy from being parsed unless the agent is between (inclusive) a minimum and maximum version. before_version - Prevents a section of policy from being parsed unless the agent is below a specified version (not inclusive). after_version - Prevents a section of policy from being parsed unless the agent is above a specified version (not inclusive). else - Allows the agent to parse a section of policy only if the preceding macro is not applicable. feature - Prevents a section of policy from being parsed based on feature availability. You can find examples of use within the Masterfiles Policy Framework. For example, body action fresh_systemd_state uses the minimum_version macro to control the setting of ifelapsed => "0" to versions 3.18.1 and higher since versions below might produce a warning.
Ever want to manage subvalues in a configuration file? In episode 39 we talk about using field_edits in edit_line bundles.
Cody and Craig join Nick as he prototypes and iterates on some policy showing how to manage subvalues in KEY = quoted values. A question raised during one of our recent post show discussions.
Video The video recording is available on YouTube:
At the end of every webinar, we stop the recording for a nice and relaxed, off-the-record chat with attendees. Join the next webinar to not miss this discussion.
Do you know how to quickly see what variables and classes are defined?
Often, while developing CFEngine policy it’s convenient to emit a variable value or a report based on the presence of a class. For example:
bundle agent main { reports: "Unqualified hostname = '$(sys.uqhost)'"; linux:: "I am running on linux"; } In some cases, this is because you are exploring what classes are available. In other cases, it might be DEBUG-related reports helping you understand how a variable is resolved during policy evaluation. Often, you can get this information without writing CFEngine policy to emit it by using the --show-vars or --show-classes options to cf-promises.
Ever want to get some data into CFEngine? Have you heard about the def.json Augments file?1
Augments are JSON data files that allow you to define classes and variables very early during agent initialization, before policy. Augments are practical in a variety of ways. Perhaps most notably for overriding policy defaults.
Let’s look at a simple example.
Here we have a policy /tmp/feature-friday/18-0.cf
/tmp/feature-friday/18-0.cf bundle agent main { reports: "MyVariable $(with) defined '$(MyVariable)'" with => ifelse( isvariable( MyVariable ), "is", "is not" ); } Running it, we can see that MyVariable isn’t defined