Puppet versus CFEngine

60 views
Skip to first unread message

Mike Weilgart

unread,
Sep 11, 2023, 8:02:43 PM9/11/23
to help-cfengine
Hello all,

To those who know me, sorry for an absence of many years.  I was signed up on this list years ago with an email address I no longer have, and as I wasn't working with CFEngine on a daily basis by the time I lost access to that email, I haven't been here in forever.

I recently started a new job and am ramping up on Puppet, which is in use in my new work environment.

To tell the truth, I had no idea just how good I had it with CFEngine until I started coming to grips with the Puppet way of doing things.  I learned CFEngine "in a vacuum," as it were, fresh and from scratch, with no prior config management experience.  (And I learned it extremely well, as a perusal of my contributions to the help-cfengine list, and to the CFEngine bug tracker, will attest.)

Just in a couple weeks of working with Puppet, I'm running into several sharp corners, and learning about others.  For instance, it is impossible in Puppet to promise that:

1. /foo/bar should have perms 777 (but leave all files and subdirectories at whatever mode they may have, don't manage those), AND
2. /foo/bar and everything in it should have owner baz and group baz.


This of course is a trivial task for a first-day student of CFEngine policy writing.

Another pain point I encountered, if you want to promise the existence of a file, and you just do it the natural way in Puppet, you will likely get errors on the non-existence of the parent directory and other ancestor directories.  Because you're not writing a promise, as you would in CFEngine.  The language in Puppet is not truly declarative; you can't just say, "Here is what I want" and leave it to Puppet to figure out how to do it and to create the parent directories in order to fulfill the promise.

But okay, I thought, that's a bit annoying to have to do but I can write a separate stanza for each of the parent directories in /my/very/long/path and just promise their existence and I'm away.  Simple workaround, if kind of kludgy and something the Puppet language should really handle natively to be worthy of the name of a configuration management tool....

But not so fast!  This was all occurring in a directory whose permissions I was managing separately, that I wanted to have mode, owner, group 640, someuser, somegroup all the way down, recursively.  The fun part is, as soon as you specify ANY file resource in Puppet underneath that top-level directory resource, you overwrite the permissions recursively set on the top-level resource—even if you don't mention them again!

So in summary, if you want to ensure a particular file contents at /foo/bar/baz/bof/buz/bip/file.txt — you have to list out each of those subdirectories separately in your puppet manifest to ensure they all exist!  You can loop through them to save a bit of typing, but you have to name every one of them out.  And then if you ALSO want to ensure permissions recursively on /foo/bar and EVERYTHING below it, those permissions settings will have to also be set on each level below it AND have the recurse attribute set at each level, or you will leave holes in your permissions management.  Again, there is a shorter way you can use, to make "default" permissions attributes for every level, but that is just a way to save on typing them all out; the fundamental point that EVERY attribute has to be set at EVERY level is part of the conceptual structure of Puppet.

Some of this can be chalked up to needing to learn the language and the "typing saving" approaches, but the structure is really different from CFEngine's, and so far in my learning I have found it conceptually far inferior.

I haven't even mentioned how Puppet will take an MD5 hash of every file in the entire directory tree if you turn on recurse for some directory, even if you just want to manage the permissions and not the contents.  You can get around this by installing third-party modules for managing permissions recursively (akin to CFEngine's more recent addition of "custom promise types"), but Puppet can't do it natively and cleanly.

And don't even think about trying to do log rotation with Puppet natively, i.e. delete files older than a certain age or whatever.  I haven't dug deep on that one but I'm pretty sure the only sane way to do it in Puppet would be to just shell out—i.e. write a script and call it, because Puppet isn't mature enough to do its own log rotation.  (Perhaps there is a third-party module that can do it, but probably they would just shell out anyway.)  As anyone reading this probably knows, this is again a trivial piece of CFEngine policy.

Now I come to my real point in writing this post.

I would like to make the case for CFEngine versus Puppet.  I would like to have a nice collection of examples in CFEngine policy, that showcase how easy it is to do things with CFEngine.  There is of course a collection of examples here: https://github.com/cfengine/core/tree/master/examples but it isn't particularly well suited as a TECHNICAL MARKETING TOOL—which is really what is needed, in my observation, for CFEngine to start being adopted more widely in the industry and to become as popular as it deserves.

Like for instance, the fileperms.cf example (https://github.com/cfengine/core/blob/master/examples/fileperms.cf) is overly simple.  It could be done trivially in ANY config management tool with a single line.

What would be excellent is to keep simple examples, and ADD examples that are relatively easy to state and are not unrealistic, but that are impossible or extremely difficult with other config management tools than CFEngine.

When all I knew was CFEngine, I became intimately familiar with its various quirks and idiosyncrasies and I could be vocal in complaining about them.  But now in seeing the wider config management world, I see how CFEngine really is light-years ahead of its competition and whole categories of problems simply don't exist with CFEngine policy, because it is mature enough in its engineering to actually get the job done.

I invite anyone reading this to give your "oh wow" moments of realizing how easily CFEngine could handle something, or other pain points you've had with other config management tools (especially Puppet since that's what I'm facing) that are resolved trivially with CFEngine.  :)

Best,
--Mike Weilgart

P.S.: Here's another fun one: since the puppet manifest is compiled into a catalog on the puppet server, you can't condition code on the existence of a file local to the host.  A "find_file" function call gets evaluated on the puppet master, not on the host.  You can shell out and use "test -f", but get this—the "onlyif" attribute in Puppet is ONLY available for the "exec" resource!  (That's Puppet-speak for the equivalent of a commands promise.)  Getting around this requires weird language hacks like this one: https://serverfault.com/a/516919/313521

Nick Anderson

unread,
Sep 11, 2023, 9:53:55 PM9/11/23
to Mike Weilgart, help-cfengine
👏

--
You received this message because you are subscribed to the Google Groups "help-cfengine" group.
To unsubscribe from this group and stop receiving emails from it, send an email to help-cfengin...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/help-cfengine/098fdfb9-d25f-4da0-9b65-26389083499cn%40googlegroups.com.

Neil H. Watson

unread,
Sep 11, 2023, 10:34:57 PM9/11/23
to help-c...@googlegroups.com
Oh boy,

Wait 'til you learn how Puppet 'scales'.

Bas van der Vlies

unread,
Sep 12, 2023, 8:09:34 AM9/12/23
to help-c...@googlegroups.com
Hi Mike,

I do not know if you have seen this development but it is far more
easy to build "masterfiles" configuration with the add of cfbs:
* https://build.cfengine.com/

With the aid of above there are many modulus and you can easily
demonstrate snippets/code/...

I have written module for "easy" configuring services with the add of
json/mustache:
* https://github.com/basvandervlies/cf_surfsara_lib/
* docker_compose/docker_network promise types:
https://github.com/basvandervlies/scl_modules

Regards
> *Now I come to my real point in writing this post.*
> a catalog on the puppet /server/, you can't condition code on the
> existence of a file /local to the host/.  A "find_file" function call
> gets evaluated on the puppet master, not on the host.  You can shell out
> and use "test -f", but get this—the "onlyif" attribute in Puppet is ONLY
> available for the "exec" resource!  (That's Puppet-speak for the
> equivalent of a commands promise.)  Getting around this requires weird
> language hacks like this one: https://serverfault.com/a/516919/313521
>
> --
> You received this message because you are subscribed to the Google
> Groups "help-cfengine" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to help-cfengin...@googlegroups.com
> <mailto:help-cfengin...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/help-cfengine/098fdfb9-d25f-4da0-9b65-26389083499cn%40googlegroups.com <https://groups.google.com/d/msgid/help-cfengine/098fdfb9-d25f-4da0-9b65-26389083499cn%40googlegroups.com?utm_medium=email&utm_source=footer>.

--
--
Bas van der Vlies
| High Performance Computing & Visualization | SURF| Science Park 140 |
1098 XG Amsterdam
| T +31 (0) 20 800 1300 | bas.van...@surf.nl | www.surf.nl |

Marco Marongiu

unread,
Sep 12, 2023, 11:44:15 AM9/12/23
to help-c...@googlegroups.com

11 years old blog post, you decide if it's relevant: https://syslog.me/2012/06/17/why-i-gave-up-puppet-and-chose-cfengine-3/

Ciao!

On 12/09/2023 02:02, Mike Weilgart wrote:

Aleksey Tsalolikhin

unread,
Sep 17, 2023, 3:50:12 PM9/17/23
to Marco Marongiu, help-c...@googlegroups.com
On Tue, Sep 12, 2023 at 3:44 PM Marco Marongiu <bront...@gmail.com> wrote:

11 years old blog post, you decide if it's relevant: https://syslog.me/2012/06/17/why-i-gave-up-puppet-and-chose-cfengine-3/

In this blog post, Marco wrote:
< [After reading "Learning CFEngine 3"] all the things that were confusingly buzzing in my head started to line themselves orderly

I had a similar experience attending Mark's CFE3 tutorial at USENIX LISA.  I had tried to learn it from documentation earlier but had a lot of confusions and questions.  I remember sitting in the front row and I kept raising my hand with question after question, which Mark all patiently and kindly answered, and about halfway through the talk, something just clicked for me (the lightbulb went on) and all the ideas flying around settled aligned into this elegant towering structure and IT ALL MADE SENSE.

I'm working with Ansible at $WORK and we've been having issues with long-running hosts having a stale configuration (out of date relative to the current state of policy) because hosts are configured at provision time.  I explained how with CFEngine you have an agent on each host that runs _continuously_ so you'd never have state drift like that.  Brilliant design!

So an example, Mike, for the examples collection you suggestied, would be removing a user's account (or locking it) after they leave the organization.

Best,
-at
Reply all
Reply to author
Forward
0 new messages