What’s autorun?
Autorun is a feature of the Masterfiles Policy Framework (MPF)1 that simplifies the process of adding and executing new policy.
We have talked about Modular policies with autorun and the Augments before. This time, we dig into autorun a bit deeper to explore some of its current features and look at how to implement your own as we did during The agent is in, Episode 15 - Extending autorun
Note: All paths unless otherwise noted are relative to the root of your policy set (typically /var/cfengine/masterfiles
is the distribution point). cf-agent
and other commands are run as the root
user.
When enabled (by defining the default:services_autorun
class) .cf
suffixed files located in services/autorun
are automatically included in inputs
2 and bundles tagged with autorun
are actuated in lexical order (a bundle gets tagged by having a meta promise, "tags"
, defined as a list with "autorun"
as a value).
Let’s try it out.
Taking autorun for a spin
First, add the following policy in services/autorun/example.cf
.
bundle agent __main__
# @brief Drive default bundlesequence if called as policy entry
{
methods:
"illustrating autorun"
usebundle => example();
}
bundle agent example()
# @brief example illustrating autorun
{
meta:
"tags" slist => { "autorun" };
reports:
"Hello World! from '$(this.namespace):$(this.bundle)' in '$(this.promise_filename)'";
}
Now, let’s run the policy with cf-agent --no-lock --file update.cf; cf-agent --no-lock
.
$ cf-agent --no-lock --file update.cf; cf-agent --no-lock
Nothing happened, as expected since we neglected to define the services_autorun
class. Let’s try it again, this time with the appropriate class defined.
$ cf-agent --no-lock --file update.cf; cf-agent --no-lock --define services_autorun
R: Hello World! from 'default:example' in '/var/cfengine/inputs/services/autorun/example.cf'
Great! It works.
But, what if we want to load and run policy files that live in some other directory?
Additional autorun directories
When autorun is enabled the MPF will also look for .cf
suffixed files in directories defined in default:def.mpf_extra_autorun_inputs
in addition to services/autorun
.
Let’s move our example policy file to a different directory. Create a MyAutorun
directory and move the example policy there.
$ mkdir -p /var/cfengine/masterfiles/MyAutorun
$ mv /var/cfengine/masterfiles/services/autorun/example.cf /var/cfengine/masterfiles/MyAutorun/
Next, let’s use Augments3 to enable autorun and define the path to our additional directory. Populate def.json
with the following content.
{
"classes": {
"default:services_autorun": {
"class_expressions": ["any::"],
"comment": "This simplifies the process of integrating new policy. Simply drop it in place."
}
},
"variables": {
"default:def.mpf_extra_autorun_inputs": {
"value": ["MyAutorun"],
"comment": "Unqualified directories relative to policy entry dirname and fully qualified directories that we should load policy files from"
}
}
}
Note: In the above JSON we use an Augments format that is valid for CFEngine agents version 3.18.0 and greater which allow us to be explicit in targeting the namespace a class should be defined in, and the namespace and bundle variables should be defined in, as well as applying other meta-data like tags and comments.
This is an equivalent version that works for older versions as well.
{
"classes": {
"services_autorun": ["any::"]
},
"vars": {
"mpf_extra_autorun_inputs": ["MyAutorun"]
}
}
Now that autorun is configured, run the policy again, this time without having to explicitly define services_autorun
since we defined it in Augments.
$ cf-agent --no-lock --file update.cf; cf-agent --no-lock
R: Hello World! from 'default:example' in '/var/cfengine/inputs/MyAutorun/./example.cf'
Note, you are not limited to including policy that is within your main policy set. Policy can be included from anywhere. Create /etc/cfengine/inputs-annex/another-example.cf
with the following content.
bundle agent another_example()
{
meta:
"tags" slist => { "autorun" };
reports:
"Hello World! from '$(this.namespace):$(this.bundle)' in '$(this.promise_filename)'";
}
Now, this policy file is outside of our policy set so be sure to set appropriate permissions on the file.
$ mkdir -p /etc/cfengine/inputs-annex
$ vi /etc/cfengine/inputs-annex/another-example.cf
$ chmod 600 /etc/cfengine/inputs-annex/another-example.cf
Edit def.json
in the root of your policy set and add the new directory.
{
"classes": {
"default:services_autorun": {
"class_expressions": ["any::"],
"comment": "This simplifies the process of integrating new policy. Simply drop it in place."
}
},
"variables": {
"default:def.mpf_extra_autorun_inputs": {
"value": ["MyAutorun", "/etc/cfengine/inputs-annex"],
"comment": "Unqualified directories relative to policy entry dirname and fully qualified directories that we should load policy files from"
}
}
}
Run the policy again to prove that the new policy file outside the policy set was picked up as expected.
$ cf-agent --no-lock --file update.cf; cf-agent --no-lock
R: Hello World! from 'default:another_example' in '/etc/cfengine/inputs-annex/./another-example.cf'
R: Hello World! from 'default:example' in '/var/cfengine/inputs/MyAutorun/./example.cf'
Great, it works! But, what if you only want part of that behavior, for example to only find and load policy files but not automatically actuate any bundles?
Auto inputs (without autorun)
Note: This feature was first introduced to the MPF in CFEngine 3.18.1. Unfortunately, a bug prevented it from working until 3.18.3 (which has yet to be released, so please use nightlies if you would like to enjoy this feature before then).
Let’s amend def.json
and switch from default:services_autorun
to default:services_autorun_inputs
.
After editing it should look similar to the following.
{
"classes": {
"default:services_autorun_inputs": {
"class_expressions": ["any::"],
"comment": "This simplifies the process of integrating new policy inputs. Simply drop them in services/autorun or any of the directories listed in default:def.mpf_extra_autorun_inputs."
}
},
"variables": {
"default:def.mpf_extra_autorun_inputs": {
"value": ["MyAutorun", "/etc/cfengine/inputs-annex"],
"comment": "Unqualified directories relative to policy entry dirname and fully qualified directories that we should load policy files from"
}
}
}
With that in place, we can see that running cf-agent --no-lock --file update.cf; cf-agent --no-lock
does not result in the same reports being emitted as we saw previously.
$ cf-agent --no-lock --file update.cf; cf-agent --no-lock
Now, let’s explicitly run default:another_example
to run by editing services/main.cf
as shown below.
###############################################################################
#
# bundle agent main
# - User/Site policy entry
#
###############################################################################
bundle agent main
# User Defined Service Catalogue
{
methods:
# Activate your custom policies here
"Actuating a bundle in a file that was autoloaded"
usebundle => default:another_example;
}
Now, when we run the policy, we see that the bundle from an automatically included file was found and emitted the expected report.
$ cf-agent --no-lock --file update.cf; cf-agent --no-lock
R: Hello World! from 'default:another_example' in '/etc/cfengine/inputs-annex/./another-example.cf'
Some people (including yours truly) are uncomfortable with dynamically loading policy files and prefer to explicitly enumerate all policy inputs, let’s take a look at automatically running bundles without automatically loading inputs.
Auto run bundle (without inputs)
The MPF supports this methodology via the default:services_autorun_bundles
class. When defined (assuming that neither services_autorun
or services_autorun_inputs
is defined) no policy files will be automatically added to inputs. However, any bundle tagged with autorun
will be actuated in lexical order.
Edit def.json
to explicitly include MyAutorun/example.cf
and switch from defining the class default:services_autorun_inputs
to default:services_autorun_bundles
as shown below.
{
"inputs": ["MyAutorun/example.cf"],
"classes": {
"default:services_autorun_bundles": {
"class_expressions": ["any::"],
"comment": "This simplifies the process of running bundles."
}
},
"variables": {
"default:def.mpf_extra_autorun_inputs": {
"value": ["/etc/cfengine/inputs-annex"],
"comment": "Unqualified directories relative to policy entry dirname and fully qualified directories that we should load policy files from"
}
}
}
Comment out or remove the promise to run default:another_example
from services/main.cf
or you will get errors due to another-example.cf
no longer being loaded. For example, if you neglect to comment out or remove the promise, expect to get an error similar to this:
error: Apparent bundle 'default:another_example' was undeclared, but used in a promise near line 14 of /var/cfengine/inputs/services/main.cf (possible unquoted literal value)
error: A method attempted to use a bundle 'default:another_example' that was apparently not defined
error: Fatal CFEngine error: Aborting due to missing bundle 'default:another_example'
Even though we left default:def.mpf_extra_autorun_inputs
defined, since neither of the classes default:services_autorun
or default:services_autorun_inputs
are defined, the policy file was not parsed as part of inputs and thus the bundles from those files were not found. When executing the policy we find that only the bundle from the explicitly included file is actuated and emits a report.
$ cf-agent --no-lock --file update.cf; cf-agent --no-lock
R: Hello World! from 'default:example' in '/var/cfengine/inputs/MyAutorun/example.cf'
But, what if you want more? What if you want to descend into sub-directories to find policy files or use other logic to select which policy files should be loaded and which bundles should be actuated? For that, consider implementing your own autorun.
Implementing your own autorun
To implement your own autorun mechanism, you need a few things:
- Some policy files to find and load as part of inputs
- A bundle to find the policy files
- A file control body to load the found files
- A bundle to find and run bundles
- Configuration to integrate your custom autorun policy
Policy files to load
So that we have something to work with, let’s generate some contrived policy and structure. Please do not consider any of this arrangement as an endorsement of any specific layout, it’s simply something to work with.
All of these policy files will share this common pattern:
# Generated contrived policy file ¯\_(ツ)_/¯
bundle agent __main__
{
methods:
"canonified_policy_filename";
}
bundle agent canonified_policy_filename
{
meta:
"tags"
slist => { "my_autorun", "<odd|even>", "<unqualified_dirname_of_policy_file>"};
reports:
"From bundle '$(this.namespace):$(this.bundle)' tagged with '$(with)' in '$(this.promise_filename)'"
with => join( ", ", "$(this.namespace):$(this.bundle)_meta.tags" );
}
This will allow us to easily see which bundles get automatically run and where they are from.
exec 2>&1
rm -rf /tmp/services
for each in /tmp/services/MyPolicies/onefish/twofish.cf \
/tmp/services/MyPolicies/SomeWhere/OutThere.cf \
/tmp/services/MyPolicies/beneath_the_pale/moonlight.cf \
/tmp/services/MyPolicies/DOW/Monday/monday.cf \
/tmp/services/MyPolicies/DOW/Tuesday/patch-tuesday.cf \
/tmp/services/MyPolicies/DOW/Wednesday/wednesda.cf \
/tmp/services/MyPolicies/DOW/Wednesday/am/wed-morn.cf \
/tmp/services/MyPolicies/DOW/Wednesday/pm/wed-afternoon.cf \
/tmp/services/MyPolicies/DOW/Thursday/thursday.cf \
/tmp/services/MyPolicies/DOW/Friday/then.cf \
/tmp/services/MyPolicies/DOW/Saturday/sat.cf \
/tmp/services/MyPolicies/DOW/Sunday/what.cf \
/tmp/services/MyPolicies/Platform/linux/three.cf \
/tmp/services/MyPolicies/more/enabled/four.cf \
/tmp/services/MyPolicies/more/disabled/five.cf; do
chars=$(echo -n ${each} | wc -c )
dirname=$(dirname ${each})
dir_basename=$(basename ${dirname})
bundlename=$(basename --suffix=".cf" ${each} | tr [[:punct:][:space:]] _ | sed 's/_$//')
if [ $((chars%2)) -eq 0 ]; then
odd_even="even"
else
odd_even="odd"
fi
mkdir -p $(dirname $each)
printf "# Generated contrived policy file ¯\_(ツ)_/¯
bundle agent __main__
{
methods: \"%s\";
}
bundle agent %s
{
meta:
\"tags\" slist => { \"my_autorun\", \"%s\", \"%s\" };
reports:
\"From bundle '\$(this.namespace):\$(this.bundle)' tagged with '\$(with)' in '\$(this.promise_filename)'\"
with => join( \", \", \"\$(this.namespace):\$(this.bundle)_meta.tags\" );
}
" ${bundlename} ${bundlename} ${odd_even} ${dir_basename} > ${each}
chmod 600 ${each}
lastfile=${each}
printf "Created '${each}'\n"
done
printf "Preview '${each}':\n$(cat ${each})\n"
printf "Done generating contrived policy"
:
Now that we have some policy files to find let’s get to it.
A bundle to find policy files
All of the policy files generated previously are within services/MyPolicies
. Let’s start by recursively searching for .cf
suffixed files.
bundle agent find_my_policies_recursively
{
vars:
"found" slist => findfiles( "/tmp/services/MyPolicies/**/*.cf" );
reports:
"Found '$(found)'";
}
Running the policy, we see that all the various policy files are found as desired.
$ cf-agent --no-lock --log-level info --bundlesequence find_my_policies_recursively --file find_my_policies_recursively.cf
info: Using command line specified bundlesequence
R: Found '/tmp/services/MyPolicies/DOW/Saturday/sat.cf'
R: Found '/tmp/services/MyPolicies/DOW/Tuesday/patch-tuesday.cf'
R: Found '/tmp/services/MyPolicies/beneath_the_pale/moonlight.cf'
R: Found '/tmp/services/MyPolicies/DOW/Monday/monday.cf'
R: Found '/tmp/services/MyPolicies/Platform/linux/three.cf'
R: Found '/tmp/services/MyPolicies/SomeWhere/OutThere.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/wednesda.cf'
R: Found '/tmp/services/MyPolicies/more/disabled/five.cf'
R: Found '/tmp/services/MyPolicies/more/enabled/four.cf'
R: Found '/tmp/services/MyPolicies/DOW/Friday/then.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/pm/wed-afternoon.cf'
R: Found '/tmp/services/MyPolicies/DOW/Sunday/what.cf'
R: Found '/tmp/services/MyPolicies/DOW/Thursday/thursday.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/am/wed-morn.cf'
R: Found '/tmp/services/MyPolicies/onefish/twofish.cf'
Now, let’s take a look at some variations on the idea.
Find policy files that are relevant for this day of the week
Here we look for files in directories named for the current day of the week. This can be useful if you have policies that you only want to run during certain periods of time, the rest of the time don’t bother loading the policy files you won’t use.
bundle agent find_my_policies_for_this_dow
{
vars:
"today"
string => strftime( "localtime", "%A", now() );
"found"
slist => findfiles( "/tmp/services/MyPolicies/DOW/$(today)/**.cf" );
reports:
"$(today) Found '$(found)'";
}
Running the policy, we see only the policy files under a directory named for the current day of the week are found.
$ cf-agent --bundlesequence find_my_policies_for_this_dow --no-lock --log-level info --file find_my_policies_for_this_dow.cf
info: Using command line specified bundlesequence
R: Monday Found '/tmp/services/MyPolicies/DOW/Monday/monday.cf'
Find policy files that are relevant for the current platform
How about some policies that are platform specific, for example that only run on linux.
bundle agent find_my_policies_for_my_platform
{
vars:
"found"
slist => findfiles( "/tmp/services/MyPolicies/**/$(sys.class)/*.cf" );
reports:
"Found '$(found)'";
}
We can see from running the policy that only policies under a directory named linux
are found.
$ cf-agent --bundlesequence find_my_policies_for_my_platform --no-lock --log-level info --file find_my_policies_for_my_platform.cf
info: Using command line specified bundlesequence
R: Found '/tmp/services/MyPolicies/Platform/linux/three.cf'
Tip: sys.os_release
contains more specific info on most modern linux systems where /etc/os-release
is present.
Find policy files who’s parent directory maps to a class suffixed with enabled
In this variation, we only want to use a file if we have a class that indicates we need it. This is a handy pattern for only parsing files which are needed by an individual host and reducing execution overhead.
bundle agent find_my_policies_in_enabled_dirs
{
classes:
# Classes defined here for examples sake. Consider using Augments
# so that your classification information is ready from the very
# start of cf-agent initialization
"SomeWhere_enabled";
"beneath_the_pale_enabled";
vars:
"candidates"
slist => findfiles( "/tmp/services/MyPolicies/**/*.cf" );
"picked[$(candidates)]"
string => "$(candidates)",
with => lastnode( dirname( $(candidates) ), "/" ),
if => "$(with)_enabled";
"found"
slist => getindices( picked );
reports:
"Found '$(found)'";
}
Executing the policy we see that only policy files below the directories named for the classes with _enabled
suffixes were found.
$ cf-agent --bundlesequence find_my_policies_in_enabled_dirs --no-lock --log-level info --file find_my_policies_in_enabled_dirs.cf
info: Using command line specified bundlesequence
R: Found '/tmp/services/MyPolicies/beneath_the_pale/moonlight.cf'
R: Found '/tmp/services/MyPolicies/SomeWhere/OutThere.cf'
Find policy files that are not in a directory named disabled
How about omitting policies that are in directories named disabled
?
bundle agent find_my_policies_for_my_platform
{
vars:
"candidates"
slist => findfiles( "/tmp/services/MyPolicies/**/*.cf" );
"found"
slist => filter(".*/disabled/.*", candidates, true, true, inf );
reports:
"Found '$(found)'";
}
Again, we see that none of the found policy files live inside a directory named disabled.
$ cf-agent --bundlesequence find_my_policies_for_my_platform --no-lock --log-level info --file find_my_policies_for_my_platform.cf
info: Using command line specified bundlesequence
R: Found '/tmp/services/MyPolicies/DOW/Saturday/sat.cf'
R: Found '/tmp/services/MyPolicies/DOW/Tuesday/patch-tuesday.cf'
R: Found '/tmp/services/MyPolicies/beneath_the_pale/moonlight.cf'
R: Found '/tmp/services/MyPolicies/DOW/Monday/monday.cf'
R: Found '/tmp/services/MyPolicies/Platform/linux/three.cf'
R: Found '/tmp/services/MyPolicies/SomeWhere/OutThere.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/wednesda.cf'
R: Found '/tmp/services/MyPolicies/more/enabled/four.cf'
R: Found '/tmp/services/MyPolicies/DOW/Friday/then.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/pm/wed-afternoon.cf'
R: Found '/tmp/services/MyPolicies/DOW/Sunday/what.cf'
R: Found '/tmp/services/MyPolicies/DOW/Thursday/thursday.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/am/wed-morn.cf'
R: Found '/tmp/services/MyPolicies/onefish/twofish.cf'
Hopefully this gives you some ideas about the possibilities.
body file control to load the found files
Once the files we want to include in inputs
have been found we need to get them parsed along with the rest of inputs
. For that, we use body file control
and simply reference the variable that holds the list of file paths we want to include. Here we use the first example to find policy files recursively.
bundle agent find_my_policies_recursively
{
vars:
"found"
slist => findfiles( "/tmp/services/MyPolicies/**/*.cf" );
reports:
"Found '$(found)'";
}
body file control
{
agent::
inputs => { @(default:find_my_policies_recursively.found) };
}
Note: We guard the inclusion of the files with the class agent
, this protects other components like cf-serverd
, cf-monitord
, and cf-execd
from trying to parse the files.
A bundle to find and run bundles
Now that we have included the files in inputs
we can proceed to make a bundle to run our bundles. Note: this methodology does not restrict itself to bundles that live within a specific directory. Bundles for running need to be found based on their name and any tags they have and the bundles must not take any parameters.
bundle agent find_and_run_bundles_tagged_my_autorun
{
vars:
"b"
slist => sort( bundlesmatching( ".*", "my_autorun" ), lex );
methods:
"$(b)";
}
When we combine that with our bundle to find policy files and body file control to load the found policy files to inputs
we get the following.
bundle agent find_my_policies_recursively
{
vars:
"found" slist => findfiles( "/tmp/services/MyPolicies/**/*.cf" );
reports:
"Found '$(found)'";
}
bundle agent find_and_run_bundles_tagged_my_autorun
{
vars:
"b" slist => sort( bundlesmatching( ".*", "my_autorun" ), lex );
methods:
"$(b)";
}
body file control
{
agent::
inputs => { @(default:find_my_policies_recursively.found) };
}
If we run it we can see that all the policy files are found as before and this time all the bundles tagged with my_autorun
were actuated.
$ cf-agent --no-lock --log-level info --bundlesequence find_my_policies_recursively,find_and_run_bundles_tagged_my_autorun --file find_my_policies_recursively_with_find_and_run_bundles_tagged_my_autorun.cf
info: Using command line specified bundlesequence
R: Found '/tmp/services/MyPolicies/DOW/Saturday/sat.cf'
R: Found '/tmp/services/MyPolicies/DOW/Tuesday/patch-tuesday.cf'
R: Found '/tmp/services/MyPolicies/beneath_the_pale/moonlight.cf'
R: Found '/tmp/services/MyPolicies/DOW/Monday/monday.cf'
R: Found '/tmp/services/MyPolicies/Platform/linux/three.cf'
R: Found '/tmp/services/MyPolicies/SomeWhere/OutThere.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/wednesda.cf'
R: Found '/tmp/services/MyPolicies/more/disabled/five.cf'
R: Found '/tmp/services/MyPolicies/more/enabled/four.cf'
R: Found '/tmp/services/MyPolicies/DOW/Friday/then.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/pm/wed-afternoon.cf'
R: Found '/tmp/services/MyPolicies/DOW/Sunday/what.cf'
R: Found '/tmp/services/MyPolicies/DOW/Thursday/thursday.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/am/wed-morn.cf'
R: Found '/tmp/services/MyPolicies/onefish/twofish.cf'
R: From bundle 'default:OutThere' tagged with 'my_autorun, even, SomeWhere' in '/tmp/services/MyPolicies/SomeWhere/OutThere.cf'
R: From bundle 'default:five' tagged with 'my_autorun, even, disabled' in '/tmp/services/MyPolicies/more/disabled/five.cf'
R: From bundle 'default:four' tagged with 'my_autorun, odd, enabled' in '/tmp/services/MyPolicies/more/enabled/four.cf'
R: From bundle 'default:monday' tagged with 'my_autorun, odd, Monday' in '/tmp/services/MyPolicies/DOW/Monday/monday.cf'
R: From bundle 'default:moonlight' tagged with 'my_autorun, even, beneath_the_pale' in '/tmp/services/MyPolicies/beneath_the_pale/moonlight.cf'
R: From bundle 'default:patch_tuesday' tagged with 'my_autorun, odd, Tuesday' in '/tmp/services/MyPolicies/DOW/Tuesday/patch-tuesday.cf'
R: From bundle 'default:sat' tagged with 'my_autorun, even, Saturday' in '/tmp/services/MyPolicies/DOW/Saturday/sat.cf'
R: From bundle 'default:then' tagged with 'my_autorun, odd, Friday' in '/tmp/services/MyPolicies/DOW/Friday/then.cf'
R: From bundle 'default:three' tagged with 'my_autorun, even, linux' in '/tmp/services/MyPolicies/Platform/linux/three.cf'
R: From bundle 'default:thursday' tagged with 'my_autorun, odd, Thursday' in '/tmp/services/MyPolicies/DOW/Thursday/thursday.cf'
R: From bundle 'default:twofish' tagged with 'my_autorun, odd, onefish' in '/tmp/services/MyPolicies/onefish/twofish.cf'
R: From bundle 'default:wed_afternoon' tagged with 'my_autorun, even, pm' in '/tmp/services/MyPolicies/DOW/Wednesday/pm/wed-afternoon.cf'
R: From bundle 'default:wed_morn' tagged with 'my_autorun, odd, am' in '/tmp/services/MyPolicies/DOW/Wednesday/am/wed-morn.cf'
R: From bundle 'default:wednesda' tagged with 'my_autorun, even, Wednesday' in '/tmp/services/MyPolicies/DOW/Wednesday/wednesda.cf'
R: From bundle 'default:what' tagged with 'my_autorun, odd, Sunday' in '/tmp/services/MyPolicies/DOW/Sunday/what.cf'
As with finding files, we can take it further. Let’s try a variation.
Find and run bundles tagged with my_autorun & odd
Let’s find and run the bundles that are tagged with both my_autorun
and odd
.
Since bundlesmatching()
function takes n
tags that are OR’d we need to do two searches and find the intersecting bundles.
bundle agent find_and_run_bundles_tagged_my_autorun_and_odd
{
vars:
"b_my_autorun"
slist => sort( bundlesmatching( ".*", "my_autorun" ), lex );
"b_odd"
slist => sort( bundlesmatching( ".*", "odd" ), lex );
"b"
slist => sort( intersection( b_my_autorun, b_odd ) );
methods:
"$(b)";
}
Running the policy we can see that it finds all the same policy files but only the subset tagged with both my_autorun
and odd
emit the reports indicating they were actuated.
$ cf-agent --no-lock --log-level info --bundlesequence find_my_policies_recursively,find_and_run_bundles_tagged_my_autorun_and_odd --file find_my_policies_recursively_with_find_and_run_bundles_tagged_my_autorun_and_odd.cf
info: Using command line specified bundlesequence
R: Found '/tmp/services/MyPolicies/DOW/Saturday/sat.cf'
R: Found '/tmp/services/MyPolicies/DOW/Tuesday/patch-tuesday.cf'
R: Found '/tmp/services/MyPolicies/beneath_the_pale/moonlight.cf'
R: Found '/tmp/services/MyPolicies/DOW/Monday/monday.cf'
R: Found '/tmp/services/MyPolicies/Platform/linux/three.cf'
R: Found '/tmp/services/MyPolicies/SomeWhere/OutThere.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/wednesda.cf'
R: Found '/tmp/services/MyPolicies/more/disabled/five.cf'
R: Found '/tmp/services/MyPolicies/more/enabled/four.cf'
R: Found '/tmp/services/MyPolicies/DOW/Friday/then.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/pm/wed-afternoon.cf'
R: Found '/tmp/services/MyPolicies/DOW/Sunday/what.cf'
R: Found '/tmp/services/MyPolicies/DOW/Thursday/thursday.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/am/wed-morn.cf'
R: Found '/tmp/services/MyPolicies/onefish/twofish.cf'
R: From bundle 'default:four' tagged with 'my_autorun, odd, enabled' in '/tmp/services/MyPolicies/more/enabled/four.cf'
R: From bundle 'default:monday' tagged with 'my_autorun, odd, Monday' in '/tmp/services/MyPolicies/DOW/Monday/monday.cf'
R: From bundle 'default:patch_tuesday' tagged with 'my_autorun, odd, Tuesday' in '/tmp/services/MyPolicies/DOW/Tuesday/patch-tuesday.cf'
R: From bundle 'default:then' tagged with 'my_autorun, odd, Friday' in '/tmp/services/MyPolicies/DOW/Friday/then.cf'
R: From bundle 'default:thursday' tagged with 'my_autorun, odd, Thursday' in '/tmp/services/MyPolicies/DOW/Thursday/thursday.cf'
R: From bundle 'default:twofish' tagged with 'my_autorun, odd, onefish' in '/tmp/services/MyPolicies/onefish/twofish.cf'
R: From bundle 'default:wed_morn' tagged with 'my_autorun, odd, am' in '/tmp/services/MyPolicies/DOW/Wednesday/am/wed-morn.cf'
R: From bundle 'default:what' tagged with 'my_autorun, odd, Sunday' in '/tmp/services/MyPolicies/DOW/Sunday/what.cf'
We can even compress this down into a single statement.
bundle agent find_and_run_bundles_tagged_my_autorun_and_odd
{
vars:
"b"
slist => sort( intersection(
sort( bundlesmatching( ".*", "my_autorun" ), lex ),
sort( bundlesmatching( ".*", "odd" ), lex ) )
);
methods:
"$(b)";
}
Running the policy again, we get the same result.
$ cf-agent --no-lock --log-level info --bundlesequence find_my_policies_recursively,find_and_run_bundles_tagged_my_autorun_and_odd --file find_my_policies_recursively_with_find_and_run_bundles_tagged_my_autorun_and_odd.cf
info: Using command line specified bundlesequence
R: Found '/tmp/services/MyPolicies/DOW/Saturday/sat.cf'
R: Found '/tmp/services/MyPolicies/DOW/Tuesday/patch-tuesday.cf'
R: Found '/tmp/services/MyPolicies/beneath_the_pale/moonlight.cf'
R: Found '/tmp/services/MyPolicies/DOW/Monday/monday.cf'
R: Found '/tmp/services/MyPolicies/Platform/linux/three.cf'
R: Found '/tmp/services/MyPolicies/SomeWhere/OutThere.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/wednesda.cf'
R: Found '/tmp/services/MyPolicies/more/disabled/five.cf'
R: Found '/tmp/services/MyPolicies/more/enabled/four.cf'
R: Found '/tmp/services/MyPolicies/DOW/Friday/then.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/pm/wed-afternoon.cf'
R: Found '/tmp/services/MyPolicies/DOW/Sunday/what.cf'
R: Found '/tmp/services/MyPolicies/DOW/Thursday/thursday.cf'
R: Found '/tmp/services/MyPolicies/DOW/Wednesday/am/wed-morn.cf'
R: Found '/tmp/services/MyPolicies/onefish/twofish.cf'
R: From bundle 'default:four' tagged with 'my_autorun, odd, enabled' in '/tmp/services/MyPolicies/more/enabled/four.cf'
R: From bundle 'default:monday' tagged with 'my_autorun, odd, Monday' in '/tmp/services/MyPolicies/DOW/Monday/monday.cf'
R: From bundle 'default:patch_tuesday' tagged with 'my_autorun, odd, Tuesday' in '/tmp/services/MyPolicies/DOW/Tuesday/patch-tuesday.cf'
R: From bundle 'default:then' tagged with 'my_autorun, odd, Friday' in '/tmp/services/MyPolicies/DOW/Friday/then.cf'
R: From bundle 'default:thursday' tagged with 'my_autorun, odd, Thursday' in '/tmp/services/MyPolicies/DOW/Thursday/thursday.cf'
R: From bundle 'default:twofish' tagged with 'my_autorun, odd, onefish' in '/tmp/services/MyPolicies/onefish/twofish.cf'
R: From bundle 'default:wed_morn' tagged with 'my_autorun, odd, am' in '/tmp/services/MyPolicies/DOW/Wednesday/am/wed-morn.cf'
R: From bundle 'default:what' tagged with 'my_autorun, odd, Sunday' in '/tmp/services/MyPolicies/DOW/Sunday/what.cf'
More variations are left to the reader, if you come up with some clever patterns, please share them on GitHub discussions , the mailing list and or chat via #cfengine on irc.libera.chat or gitter.im/cfengine.core (also available via Matrix).
Configuration to activate your custom autorun policy
Now the last step is to configure the policies so they are run as part of the normal scheduled executions. For that, we use Augments, def.json
.
We place the finding of policy files early in the bundlesequence
using default:def.control_common_bundlesequence_classification
and then find and run bundles at the end of the main bundlesequence
using default:def.control_common_bundlesequence_end
. If you have autorun policies for classification, consider running those early as well so that the variables and classes are resolved by the time they get to any of your other policies that use them.
{
"inputs": [
"find_my_policies_recursively_with_find_and_run_bundles_tagged_my_autorun_and_odd_short.cf"
],
"variables": {
"default:def.control_common_bundlesequence_classification": {
"value": ["find_my_policies_recursively"],
"comment": "Unqualified directories relative to policy entry dirname and fully qualified directories that we should load policy files from"
},
"default:def.control_common_bundlesequence_end": {
"value": ["find_and_run_bundles_tagged_my_autorun_and_odd_short"],
"comment": "Find and run bundles tagged with both 'my_autorun' and 'odd'"
}
}
}
-
The Masterfiles Policy framework is the default policy set, with documentation here: https://docs.cfengine.com/docs/master/reference-masterfiles-policy-framework.html. ↩︎
-
inputs
is the list of policy files which will be loaded by CFEngine. After a policy is loaded, you still need some way to specify which bundles inside it should be run. This can be achieved throughmethods
promises,autorun
(tags), or modifying thebundlesequence
. ↩︎ -
Augments, sometimes referred to as
def.json
, allows you to easily define classes, variables, input files, and more, through a JSON file without modifying.cf
policy files; https://docs.cfengine.com/docs/master/reference-language-concepts-augments.html. ↩︎