cfe: agent runs made easier

This is the third and final installment of a three-post series. In the first post, “git repository and deployment procedures for CFEngine policies”, I explained how we have structured our repository to efficiently manage many projects in one branch and share common policies across projects. In the second post, “cf-deploy: easier deployment of CFEngine policies“, I illustrated the script that we use to deploy the policies on our policy hubs and to preview the changes that would be applied to the policies themselves. In this final post I’ll illustrate yet another tool that helped us simplify the way we run the agent by hand on a node. Although one doesn’t need very often to run the agent by hand on production nodes, that is much more needed on test nodes and when debugging policies, and we do that often enough that it made sense to optimize the process. The principle is the same as the other script, cf-deploy: a makefile does the hard work, and we provide a convenient front-end to run make.

The problem

If you think at what are the actions that you commonly do when you run the agent by hand, they are at least two: update the policies at the latest version available (usually with cf-agent -KI -f update.cf) and force an agent run (usually with cf-agent -KI). Most of the time, the two things are combined in one single command line, like cf-agent -KI -f update.cf && cf-agent -KI. Such command line is long enough to deserve a shorter alias.

Then another thing is to enable/disable the agent on a node, what someone in the community calls the “cowboy mode“. As many others, we have implemented a policy that checks for a flag file: if the file is present, the agent aborts. The flag file can be created and removed by hand with common commands like touch and rm, or the agent can disable itself when run with the disable_cfengine class activated, re-enable itself when the enable_cfengine class is activated, or force a run when disabled when the class force_run is active. These are also good candidates for makefile targets.

It’s time for some sugar: when the policies are already updated and all you need is to run the agent several times to check something, it may be convenient to not waste time and CPU cycles on both the hub and the client and just run the policies: a “fast” run mode is yet another good candidate, as well as a fast-and-forced run mode.

And, last but not least: in our policies the agent collects information about itself and the system, along with contact details for the first-line engineers, and writes all in a text file that can be inspected in a crisis. A shortcut to display that file wherever it is on the filesystem is our last candidate for automation.

The Makefile solution

All these actions are easily codified in a Makefile:

CF_DIR=/var/cfengine
INFO_DIR=/etc/cfengine
HOSTINFO=$(INFO_DIR)/host-info

AGENT=$(CF_DIR)/bin/cf-agent
BASE_OPTS=-KI
UPDATE_OPTS=$(BASE_OPTS) -f update.cf

RUN_UPDATE=$(AGENT) $(UPDATE_OPTS)
RUN_AGENT=/usr/bin/env LC_ALL=C $(AGENT) $(BASE_OPTS)

.PHONY: info disable enable run fast force ff fastforce help

help:
	echo "Supported actions:"
	echo "  info:    display system information"
	echo "  run:     update the policies and run the CFEngine agent"
	echo "  fast:    run the CFEngine agent, don't update"
	echo "  enable:  re-enable CFEngine on this node and run"
	echo "  disable: disable CFEngine on this node"
	echo "  force:   update the policies and force an agent run even if disabled"
	echo "  ff:      (fastforce) force an agent run even if disabled, don't update"
	echo "  help:    show this help"
	echo "Default action: help"
	echo ""

info: $(HOSTINFO)
	cat $(HOSTINFO)

disable: $(AGENT)
	$(RUN_AGENT) -Ddisable_cfengine

enable: $(AGENT)
	$(RUN_AGENT) -Denable_cfengine

run: $(AGENT)
	$(RUN_UPDATE) && $(RUN_AGENT)

fast: $(AGENT)
	$(RUN_AGENT)

force: $(AGENT)
	$(RUN_UPDATE) && $(RUN_AGENT) -Dforce_run

ff fastforce: $(AGENT)
	$(RUN_AGENT) -Dforce_run

The frontend

The most convenient way to shortcut a long make command is, in this case, a simple shell alias that we called cfe:

alias cfe="make -s -C /var/cfengine/inputs/tools/host-info"

and the most convenient way to have this alias deployed for all users is to add a “fragment” shell script in /etc/profile.d/ (or equivalent) on all the systems that support it. Both the makefile and the shell alias can be deployed via simple CFEngine promises and, done that, one would use cfe run to update the policies and run the agent, or cfe fast to just run the agent, or cfe ff to force a fast run of a disabled agent and so forth. And, when in doubt, one can run cfe alone to get a list of all possible actions:

bronto@murray:~$ cfe
Supported actions:
  info:    display system information
  run:     update the policies and run the CFEngine agent
  fast:    run the CFEngine agent, don't update
  enable:  re-enable CFEngine on this node and run
  disable: disable CFEngine on this node
  force:   update the policies and force an agent run even if disabled
  ff:      (fastforce) force an agent run even if disabled, don't update
  help:    show this help
Default action: help

Conclusions

This small system is so convenient that I always get disappointed when I hit a system where, for some reason, this facility is not implemented. And, guess what? One of the goals in the short term is to make it available everywhere 🙂 I hope you enjoy cfe the same way I do. Cheers!

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.