Puppet versus CFEngine

Mike Weilgart <[email protected]> Mon, 11 Sep 2023 17:02:43 -0700 (PDT)
Newsgroups gmane.comp.sysutils.cfengine.general
Message-ID <[email protected]>
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 example taken from https://stackoverflow.com/q/25551718/5419599.)

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

-- 
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 [email protected].
To view this discussion on the web visit https://groups.google.com/d/msgid/help-cfengine/098fdfb9-d25f-4da0-9b65-26389083499cn%40googlegroups.com.