CFEngine migrates to FORTH language for policy in version 4.0.-4 (non-breaking change!)

Posted by Craig Comstock
April 1, 2020

Announcing CF4! (or is it CF-FORTH?!)

I imagine you didn’t expect such a big release so soon after our most recent release of 3.12.4 and 3.15.1 on March 26, but here it is: our alpha-release. Thus the reason for the .-4 in the version number. Of course choosing -4 has something to do with the fun of spelling FORTH without the ‘U’. Also, it’s nearly a palindrome and I imagine we’ll have a few alphas/betas before the final release is finished. (a good palindrome: a man a plan a canal panama)

We learned our lesson from the CFE2 to CFE3 transition and have completely preserved backwards compatibility while adding some amazing new language and runtime features.

A brand new(old) language: FORTH!

With CF4 we use the FORTH language invented by Charles H. Moore to really bring a lot of power to the policy language. Define your own words to create promises, bundles, variables, you name it!

  • Interactive REPL/debugger
  • Introspection of vars/classes/promises
  • Easy-to-construct DSL (domain specific language) for your policy. Create forth words to represent important aspects of your policy!
  • New language constructs provided by our transition to a Forth-based policy language
    • Control structures like IF THEN ENDIF, IF THEN, BEGIN UNTIL, REPEAT
    • Exception handling: CATCH THROW
    • Extensive artihmetics operations
    • Recursion

Let’s get started!

Download cf4 and run it like this:

$ cf4

This will present you with the following information and be ready for your input:

CFEngine Core 4.0.-4

(You might be confused by the lack of a prompt, but we feel keeping things simple is important, just as with my favorite text editor ed(1)). You can add a prompt if you wish (see the info pages).

At this point, you can start entering policy manually or loading policy from files, either directly including them or by requiring them.

include promises.cf4
S" promises.cf4" included
require my-library.cf4
S" promises.cf4" required

Here is a simple hello world example in CFE3:

bundle agent main
{
  reports:
    "Hello world!";
}

The equivalent CF4 policy is much more succinct and clear:

S" Hello world!" reports promise

By default in CF4, there is an assumed bundle agent main present. If you want to specify a different name simple add it to the end!

S" Hello world!" reports promise S" main" agent bundle

Now that it is entered into the system you can evaluate the promises with the word keep_promises:

% keep_promises
R: Hello world!

A more advanced example

Now let’s try a slightly more complicated example from Diego Zamboni’s excellent book: Learning CFEngine.

bundle agent edit_motd
{
  vars:
    "motd" string => "/etc/motd";
  files:
    "$(motd)"
      create => "true",
      edit_line => addmessage;
}
bundle edit_line addmessage
{
  insert_lines:
    "This system is managed by CFEngine 4!";
}

In CF4 you would first define a few words for the motd path and your edit_line bundle details:

: motd S" /etc/motd" ;
: addmessage S" This system is managed by CFEngine 4!" insert_lines edit_line bundle ;

Next, you would use those words to express the files promise:

motd files

The word files takes the string returned by the word motd and starts formulating a new promise. At this point, we can add any number of attributes before we “close out” the current promise.

S" true" create
addmessage edit_line

And finally, we close out the promise with the promise word

promise

And execute/evaluate the policy with keep_promises

keep_promises
    info: Created file '/etc/motd', mode 0600
    info: Edit file '/etc/motd'

Compare that CFE3 policy above with the precision of the equivalent CF4 policy we have built up! (this one includes the bundle and policy words for fun, not required)

: motd S" /etc/motd" ;
: addmessage S" This system is managed by CFEngine 4!" insert_lines edit_line bundle ;
motd files
true create
addmessage edit_line
promise
S" edit_motd" agent bundle
policy

At this point, you can examine vars, classes, promise outcomes and various other aspects of the system in real-time. When you are done simply type bye and you are done!

There is so much more to explore with CF4, but we’ll leave that for another time.

If you’d like to hack-a-long (and trust me, this is a SERIOUS hack at this point, not all the build steps are even documented right now! :O (I really need to learn autotools better))

git clone -b cf-forth --depth 1 --recursive git@github.com:craigcomstock/core.git

Cheers, be well, and trust in your promises.

This April Fools joke brought to you by the CFEngine Forth fan club!