Checking out and editing CFEngine policy

Some general notes and information

  • Policies are exported from /var/cfengine/policy on umcfe or msucfe. Any directory under that location can be referenced by a client.
  • /var/cfengine/policy/T2 is reserved for "production" policy. Never put anything here you didn't test somewhere else first.
  • On clients, edit /var/cfengine/policy_path.dat to reference the policy directory of your choice. Put in a single line specifying the full path like /var/cfengine/policy/mytest. OR (better in my opinion) you can define a class indicating the temporary policy path. Use -DPolicyPath_somepath to temporarily point at a new policy location.
  • stash/firmware and stash/hostcerts are not synced. If needed for your work, /root/firmware.tar and /root/hostcert.tar should be extracted into those directories on the policy host. Seeing chmod errors on the hostcert dir is normal once has run once and set 420 on that dir.
  • The SVN web browser is at . This is a good way to browse policy and check on current configurations.
  • Policy servers will be referenced in order from /var/cfengine/policy_server.dat. Any number of servers can be in here. The client needs at least one server in this file.
  • In file copies, your source should be "@(policy.server)". This will be set by Using the slist passes all available servers to the file copy promise body and cfengine will handle any necessary failover.
  • For the file source, use "$(policy.stash)/bundlename". The variable is set in

Reusable Agent Bundles

For things that are done often it's easy to define and use a re-usable bundle. They should be defined in To use in your agent bundle:
     "any_promise_handle" usebundle => bundlename(arg1,arg2,arg3)

Here is a list of already created ones for your reference. Please feel free to create more.
copy_file(sourcedir,service,file,destdir,mode,owner,group) copy file into place from stash/$(source).
If source file is named with .tmpl it will be staged and run through edit_template, otherwise directly copied.
Do not add the .tmpl extension to the file argument given to bundle.
If files are in place then $(service)_configured will be set as a handle (can reference with "depends_on => handle" in other promises)
If files are repaired then restart_$(service) will be set.
copy_env(source,file) Copy a file from source/|csh(.tmpl) to /etc/profile.d/|csh.
Bundle will iterate through both .csh and .sh - specify file argument without any extension.
If source file is named with .tmpl extension it will be treated as template automatically.
Do not add the .tmpl extension to the file argument given to bundle.
copy_tool(source,tool) copy executable file from stash/source as /root/tools/tool.
Name template sources with .tmpl and they will be staged and expanded from template.
Do not add the .tmpl extension to the file argument given to bundle.
copy_cron(source,cron) copy cron from stash/source as /etc/cron.d/cron.
If source file is named with .tmpl extension it will staged and expanded from template.
Do not add the .tmpl extension to the file argument given to bundle.
install_packages(namez,packages) Install an slist of packages. Pass it a string that will be name of a fully qualified slist ("bundlename.listname").
Sets handle $(namez)_installed if promise is ok. Use as reference in dependent promises like "depends_on => handle".
Sets class $(namez)_initial_install if promise is repaired (ie, package was installed that was not before)

Language Tips

  • Iteration! Learn how it works in cfengine and use it everywhere you can:
  • If a class might be useful in more than one policy, define it in a bundle in For example, you'll find many classes and variables defined for OS type, revision, etc defined here. Likewise variables or classes for type of hardware (Dell).
  • Use the reports section, not "echo" to produce output.
  • Before writing new promise bodies check under inputs/lib/3.5/*.cf for something that does what you want. This is what used to be
  • In file copies, your source should be "@(policy.server)". This will be set by Using the slist passes all available servers to the file copy promise body and cfengine will handle any necessary failover.
  • For the file source, use "$(policy.stash)/bundlename". The variable is set in

Where to work

  • Anywhere - use to sync to policy host under /var/cfengine/policy/whateveryoulike
  • Directly on policy host under /var/cfengine/policy/whateverdiryoulike. This will be your svn working dir and where you point test clients at.

You should never directly edit files in /var/cfengine/policy/T2 You can put a copy of our policy in any other directory you choose and test with that directory before syncing to T2. The utility makes it easy to synchronize your working directory with any given policy export dir on our cfengine servers. You do not need to work as "root" in any case.

Check out the code

For minor updates to policy or new policies not radically altering existing policy you would usually want to version within the trunk. Creating a branch is covered later. Your first step is to checkout the code. You should have kerberos credentials to do this. You should also have afs authentication because svn will want to write small configurations into .subversion there (but it won't fail if it cannot).
svn co svn+ssh:// . 

The period at the end specifies you want the contents of trunk put into current directory. If you want it to create a directory "trunk" (or whatever the last dir of the URL is) then leave off the period.


Policy text is found in masterfiles/inputs. Other file requirements for bundles are under masterfiles/stash/bundlename. Please read the documentation on modules if you need an external executable/script to find some information.


To put your in-progress policy in place on a policy host use tools/ You'll want to have setup an ssh key and use ssh-agent or else you'll have to enter your ssh key password more often than is convenient. Examples of that are shown in the code below. Information on setting up ssh keys: SetupSSHKeys

(Assume we checked out trunk and are in trunk/masterfiles working)
(only have to do this one time)
ssh-agent /bin/bash

../tools/ -d umcfe:mytest 

By default this will run an rsync dry run. Give it a "-y" to really do the sync. "-h" shows more options. By default the util copies to /var/cfengine/policy. If you want to fully specify some other path then use "-D" for destination instead. You don't have to svn update every time but before going to production you should be doing a final test with your working directory fully updated from the svn trunk. As you edit, test your policy by referencing it in /var/cfengine/policy_path.dat on a client or use command line class definition to reference it:
cf-agent -f -DPolicyPath_mytest ; cf-agent -DPolicyPath_mytest

Into production

If you've fully tested your working directory then it is time to check in the changes:
 svn ci -m "Summary description of changes" 

Then you need to sync to production at both sites. This you will have to do as root by giving "-R" to You should also svn update one last time so the working directory doesn't show as modified and to verify you are not missing any recent checkins:

 svn update
../tools/ -d umcfe:T2 -R -y
../tools/ -d msucfe:T2 -R -y

Using branches

If you have more extensive changes you'd like to version as you work on them then you'll need a branch.

Note for your safety: Be sure in SVN ops you are at the same place in your working tree as given in your svn repository URL. Don't "switch" the masterfiles directory to the top level of the repository tree. It'll really do it, no problem!

The "^" symbol in examples below is svn shorthand for the repository URL. So telling svn "^/trunk" in a working dir is the same as writing "svn+ssh://".

Making a branch and keeping it up to date

Create a branch like this:

 svn copy svn+ssh://  \ 
svn+ssh:// -m "Log message of your choice" 

Then check your branch out someplace. You can also "svn switch" a working dir from trunk to new branch:
svn switch ^/branches/mybranch

After that it's a similar procedure to sync your branch to an exportable dir on the policy host, or check it out directly on the policy host and work from there.

To keep up with the trunk changes as you work, do this in your working dir. Be sure to match the directory levels...if you're in trunk/masterfiles then merge from trunk/masterfiles.
svn merge  ^/trunk

If there are conflicts this might help:

Reintegrating back to trunk and production

You cannot just sync your branch code to the production T2 directory. People were working on the trunk while you were off on that crazy branch.

To merge your branch back into trunk in preparation for making it production you need to have or checkout a copy of trunk and merge your branch into it. Then you test the result and check it in and sync to production. First you need to update the copy of your branch in svn.

(in branch working dir) 
svn ci -m "Last branch checkin before merge"
svn switch ^/trunk
svn merge --reintegrate ^/branches/mybranch  
Test, test, test, test. Note that --reintegrate won't work if you haven't merged all revisions of the trunk back into your branch (all revisions since making the branch). If it doesn't work you may have to spend some time in conflict resolution. Especially svn gets confused if you try to merge things that were deleted in trunk, and also deleted in your branch (even if they were deleted by the action of other merges). It's not a big deal - just do this on those "incoming add/delete upon merge"

svn resolve --accept=working path/to/conflict.  

Other conflicts are actual conflicting edits. You'll have to use --accept= as appropriate. Flags are tf,mf,tc,mc,working (theirs-full, mine-full, theirs-conflict,mine-conflict). During the merge you can get the full list. tf and mf mean "take the whole copy" of theirs or mine as the one to use. mc and tc means "where conflicted, prefer mine/theirs".

You could also use "svn co" and "svn merge" in a new directory if you prefer. It doesn't really matter how you get a working dir up to date from latest trunk rev.

The SVN manual has more extensive explanations on merging:

IMPORTANT: Once a --reintegrate merge is done from branch to trunk, the branch is no longer usable for further work. It's not able to correctly absorb new trunk changes, nor can it be properly reintegrated to trunk again. For this reason, if you want to keep working on your feature branch, we recommend destroying it and then re-creating it from the trunk:

Done testing? Then check back in and put it into production:
svn ci -m "Integrate mybranch back into trunk"
../tools/ -d umcfe:T2 -R

Notes on potential developments

  • We can consider arranging so that /var/cfengine/policy/T2 automatically mirrors svn. Any commits to trunk/masterfiles would trigger a post-commit hook that tells the policy hosts to do a checkout into policy/T2. Or a cron could automatically svn update on a schedule before agent runs.
  • Taking the above one step further, all svn checkins could cause updates on the policy hosts. Create a branch? Automatically a new dir on policy host that updates whenever you check in. This does have the overhead of requiring all editing to pass through svn to be placed on the server. We would probably want to restructure svn to eliminate the distinction between trunk and branches (just put them into the same dir).
  • Git is designed for easier branching and merging and we may wish to use it for that reason. There seem to be less steps involved in a git branch/merge operation. However, there doesn't seem to be anything at this point we can't do with svn that we could do with git

-- BenMeekhof - 27 Aug 2013
Topic revision: r9 - 11 May 2015, BenMeekhof
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback