Using Cfengine with CVS
From Cfwiki
Storing the cfengine master policy files in a revision control system (or RCS) seemed like a good idea to me. I used CVS as the revision control system, and put all of my policy files in it.
Here is how it is set up:
There is a CVS repository on the master policy server (named plmaim) in /data/aim-cvsroot (aim was supposed to be the name of this project). This repository is made available using the CVS pserver protocol.
Administrators use Tortoise CVS (on Windows workstations) or Emacs to make configuration changes to the cfengine policy files.
The master has the CVS project checked out in /export/aim/, and access to this area is granted through cfservd.conf:
admit:
policy_server::
/export/aim *.dom.ain
This area is updated by a shellcommand in update.conf:
shellcommands:
policy_server::
"/bin/sh -c 'cd /export/aim/config ; /apps/cvs/bin/cvs -q update -d '"
This worked for a little while, but then I realized that we needed a "testbed" to make sure that new cfengine policies would not break production systems. So, I created two tags in CVS: production and canary. (Canary as in "coal mine") The policy server checks the production branch into /export/aim/config as above, and the canary branch into /export/aim/canary/config using a shellcommand just like the above.
Certain pre-production systems (and all of the administrators' workstations) are tagged in update.conf and groups.conf as members of the "canary" group. They get the configroot variable set to /export/aim/canary rather than just /export/aim, and all cfengine files reference this variable.
# -- update.conf
control:
any::
AllowRedefinitionOf = ( configroot SplayTime )
configroot = ( /export/aim/config )
canary::
configroot = ( /export/aim/canary/config )
SplayTime = ( 0 ) # Speed things up a little
copy:
$(configroot)/cfengine server=$(policy_server)
dest=$(workdir)
recurs=inf ignore=CVS
Now, as administrators make changes to the cfengine policy, their changes are committed to the CVS HEAD (so that they are available to everyone for review, but are not applied to systems yet). Then, they must be tagged to the "canary" tag. This policy then applies to all of the canary systems, and we can (hopefully) quickly see if the change has broken anything. Finally, the change can be tagged as production.
These rules are enforced by a CVS taginfo rule:
# -- CVSROOT/taginfo ALL /export/aim/config/scripts/validate_cvstag
And the file config/scripts/validate_cvstag:
#!/usr/bin/ksh
# $Id: validate_cvstag,v 1.4 2004/10/12 13:46:20 jmoore Exp $
# Enforce rules on CVS tags, so that production <= canary
#
# Called by CVS automatically according to $CVSROOT/taginfo as
# validate_cvstag <tagname> <mov> </path/to/cvsdatadir> file 1.x
# Command line options
TARGET_TAG=$1
TAG_OP=$2
TARGET_DIR=$3
TARGET_FILE=$4
TAG_VERSION=$5
SYMBOL_LINE=`/usr/bin/sed -n '/^symbols/,/;/p' ${TARGET_DIR}/${TARGET_FILE},v`
CURRENT_CANARY=`echo "$SYMBOL_LINE" | grep canary: | cut -d: -f 2-`
CURRENT_PRODUCTION=`echo "$SYMBOL_LINE" | grep production: | cut -d: -f 2-`
if [ "$TARGET_TAG" = "canary" ]
then
COMPARE_LINE="$CURRENT_PRODUCTION\n$TAG_VERSION"
fi
if [ "$TARGET_TAG" = "production" ]
then
COMPARE_LINE="$TAG_VERSION\n$CURRENT_CANARY"
fi
echo "$COMPARE_LINE" | tr . _ | sort -t _ -nc -k1 -k2 > /dev/null 2>&1
SORT_RESULT=$?
if [ "$SORT_RESULT" != 0 ]
echo "Error updating tag $TARGET_FILE v$TAG_VERSION as $TARGET_TAG. production is not lower than canary. Current canary is $CURRENT_CANARY. Current production is $CURRENT_PRODUCTION"
fi
exit $SORT_RESULT
