Classes don’t blink

blog-cfengine-logoThis post is about a CFEngine feature that is a bit counter-intuitive at first, and may leave you scratching your head for a while if you don’t know about that. And, seemingly, many don’t. I’ll talk about how CFEngine classe change their state, but especially I’ll talk about how they don’t change their state. Please note that when I say “a class is true” below, I mean that it is defined. And when I say “a class is false”, I mean that it is not defined (true/false is handier when one is going to use boolean expression, while defined/undefined is more CFEngine-ese).

I assume that you know that the agent runs three passes through each bundle, so to ensure convergence. Something can change at each pass, for example, a class can be undefined at the first pass, and become defined at the second pass. That can happen quite easily, as class A may be defined by an expression that includes other classes, say B and C, and those classes may be triggered by, say, a file that was repaired, or a package that was installed. So, in principle, one can expect that a class can “blink, that is: change it’s definiteness state at each pass. For example, you can expect that said class A can be false at the first pass, true at the second, and false again at the third, right?

Well, I have both good and bad news: the good is that it’s true: classes can actually blink; the bad is: they don’t do it in the way that you expect.

Let’s make an example, and take a class A defined as A = (not B) and C.

Let’s suppose that at the first pass both B and C are false. Due to C being false, the expression evaluates to false, and hence A is false: so far, so good.

Now let’s suppose that at the second pass C becomes true while B stays false: now the expression evaluates to true, due to the fact that C is true and “not B” is true because B is false. So A becomes true. Good.

Now let’s say that at the third pass B becomes also true, and C stays true. At this point the expression becomes false, but you know what? A stays true!

Why that? Because in CFEngine, once a class becomes defined, the only way to make it undefined is to cancel it.

A class can be canceled in two ways: either from the cf-agent command line, using -N, or using a classes clause, and a body classes that can cancel a class. For example, the following body can be used with a classes clause to cancel a class if a promise is repaired or kept:

# Cancel/undefine a class if the promise is OK (kept or repaired)
body classes cancel_if_ok(x)
        cancel_kept      => { "$(x)" } ;
        cancel_repaired  => { "$(x)" } ;

You may think that this is weird, or questionable, and you may actually have a point. But that’s how it is, so take it into account and be happy 😉 And if you’re curious to know how I found out, check the help-cfengine google group.


One thought on “Classes don’t blink

  1. Thanks for the reminder.
    Yes cfengine is what I call ‘accumulative’ in respect to ‘converging to at promised state’. So the code prefers to build up a configuration state and if you want remove the state later on you have equally to build up to the removal. One could also remember it / quote 2. law of thermodynamics in that situation (source: Wikipedia):
    An isolated system, if not already in its state of thermodynamic equilibrium (cfengine: promised state), spontaneously evolves towards it (cfengine: converges). Thermodynamic equilibrium has the greatest entropy (cfengine: maximized possible promises / promised classes) amongst the states accessible to the system. Perpetual motion machines (cfengine: blinking classes) of the second kind are thus impossible.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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