Originally from: garden bombing 

Keeping documentation about system changes is always a good idea, but it is especially important when using a tool as powerful as Cfengine. When I began using Cfengine I wanted to follow Mark Burgess’s recommendation about establishing a good change management process for policy files.

My goal was to have a simple process that forced me to utilize version control for the main purpose of documenting changes. This is a bit of documentation on how I have setup a process for working with Cfengine policy using git.

Policy Definition Point

In my current environment, policy is both defined on and distributed from the same virtual server. This server is dedicated to hosting the master policy files, other master content, and running a cfengine server daemon.

Cfengine policy is defined and managed in the directory /srv/cfengine/policy. This directory is configured as a git repository so that changes in the files can be tracked. (Creating git repositories)

Policy Distribution Point

Policy is downloaded from the /var/cfengine/masterfiles directory by clients. To facilitate the ease of updating policy and to establish a repeatable change process, this directory is configured as a git mirror of the policy definition repository:

git clone --local --no-hardlinks /srv/cfengine/policy/ /var/cfengine/masterfiles/

A key understanding is that the files in this distribution location are NEVER edited directly. In fact, after this process was established I don’t have a need to even enter this directory.

(Also, this means that update.cf is configured to ignore the .git subdirectory.)


The essential process mechanism is a simple git pull executed from within the distribution directory. The git-pull command fetches and merges the branch head – in this case the master policy – into the current branch. Because the repository mirror under /var/cfengine/masterfiles is left alone on the master branch, this process works well (as it achieves the same end result as using copy/rsync).

I have found that using git mirrors in this fashion provides some key benefits in assisting me keep a good change management process:

  1. Any modifications to policy must be committed to version control before they can be presented to the clients. This ensures that each change has an owner and comment. It also documents that policy presentation is a human decision, which is an important part of the Cfengine philosophy.
  2. This process is easily expandable to multiple distribution servers as the environment grows – just add more mirror repositories.
  3. Policy changes are made easily viewable through gitweb.
  4. Because policy is contained in git, development can be spun off and reintegrated as desired.

cfengine git change management

Process Steps

Policy files are edited/created in /srv/cfengine/policy

Changes are committed to git:

  1. git add file1 file2 ...
  2. git commit -m "description of change" --author="First Last"

Policy is then “authorized” to be presented to the clients:

  1. cd /srv/cfengine/policy
  2. ./authorize.sh

Non-policy files

I have chosen to keep master files or “golden copies” of non-policy files in a separate directory: /srv/cfengine/master. This directory is its own git repository and contains any files that the cfengine policy would direct the clients to download (templates, scripts, etc.). This directory is different from /srv/cfengine/policy in that I have no authorization process configured to ensure that changes are logged to version control before being made available.

Edit: As I have been documenting this it has become clear to me that there is little reason not to configure the same authorization process for these non-policy files. I will most likely update this in the future.


The authorize script is simply a wrapper for implementing the git pull from the distribution directory. It provides some additional security and helps to remind me of the task I am preforming – authorizing policy – not just refreshing a git repository.

# Ben Bomgardner
# Copy authorized policy from definition point to a cfengine
#   distribution server location on the same physical server
if [ `pwd` != "$POLICYDEF" ]; then
echo "You may only authorize from the policy definition location: $POLICYDEF"
exit 1
COMMIT=`/usr/bin/git show --pretty=format:'%H' | head -1`
CFCHECK=`/var/cfengine/bin/cf-promises -f $POLICYDEF/promises.cf | wc -l`
if [ "$CFCHECK" -gt 0 ]; then
echo "There appear to be errors with your policy.  Please correct and continue:"
/var/cfengine/bin/cf-promises -f $POLICYDEF/promises.cf
exit 1
read -t 10 -p "Did you commit your changes? The HEAD of this repo will overwrite current policy! (y/n): " ANSWER
if [ "$ANSWER" == "y" ]; then
if [ `pwd` != "$POLICYDIST" ]; then
echo "Error: Policy distribution location cannot be found, $POLICYDIST. Cannot continue!"
exit 1
echo "Updating policy distribution location..."
umask 0077
/usr/bin/git pull &> $TMPFILE
umask $UMASK
TEST=`grep "Already up-to-date." $TMPFILE`
if [ $? -ne 0 ]; then
logger -s "New cfengine policy has been authorized: $URL"
echo "Use 'git branch' to view the current branch."
echo "Use 'git status' to view changed or new files."
echo "To add files for a commit, use 'git add '."
echo "To commit changes, use 'git commit -m \"reasonable description\" --author=\"User Name \"'."

Permalink | Leave a comment  »