Managing ports in FreeBSD with CFEngine3

06 Aug 2012

CFEngine is a fantastic tool for managing configuration files and packages across heterogeneous environments. I figured I could use it to maintain a basic set of installed packages across my Debian, OpenBSD and FreeBSD systems.

Except that the FreeBSD package_method promises in the cfengine standard library suck.

They use FreeBSD packages, and unless you have a box building your own sets of FreeBSD packages…packages suck. They are built with all of the bells and whistles, ergo they pull in a ton of shit you probably don’t need on your system.

So I wrote a package_method promise that instead uses portmaster to install and remove packages. Here’s the code:

body package_method freebsd_portmaster
    package_changes => "individual";

    package_list_command => "/usr/sbin/pkg_info";

    package_list_name_regex    => "([^\s]+)-.*";
    package_list_version_regex => "[^\s]+-([^\s]+).*";

    package_installed_regex => ".*";

    package_name_convention => "$(name)";
    package_delete_convention => "$(name)-$(version)";

    package_file_repositories => {

    package_add_command => "/usr/local/sbin/portmaster -D -G --no-confirm";
    package_update_command => "/usr/local/sbin/portmaster -D -G --no-confirm";
    package_delete_command => "/usr/local/sbin/portmaster --no-confirm -e";

    expireafter => 240;

The only interesting thing about the body of the promise is that I’m using package_file_repositories in a way that it was never intended to be used. Since the port names needed to be provided to portmaster in a

/ format, I'm leveraging a cfengine trick to kind of fake that. If you provide a package\_file\_repositories clause, cfengine will prepend a variable called $(firstrepo) to the name of every package that it adds to the package\_add\_command line. Armed with this knowledge, we just add a package\_file\_repositories entry for each port category directory, and the problem's been solved.


blog comments powered by Disqus