Skip to content
asperous edited this page Oct 18, 2012 · 25 revisions

By asperous (Andy Chase)

Reasons why CFEngine would benefit from YAML-based policy files.

  1. Easier to learn

When looking for a configuration managment/provisioning engine the biggest thing I saw about CFEngine was although it was very rooted in solid ideas, the syntax was too much of a road block. A road block, not just for the people who wanted to intregrate it into their systems, but very few sysadmins are alone in their sysadminhood.

Often there are others, lurking around the corners. Others that are not as intellectually advanced. The challange to teach these people .CF synax would be too great.

"Sissys (Sysadmins) already know the rubies, so give them the damn rubies (Chef)" - said no one ever. But perhaps thought by many:

"cfengine3 configuration syntax is more difficult than the other three"

"Admittedly, it is some kind of PITA to get to know Cfengine, i at least spent 24 Hours in migrating my puppet setup to Cfengine, but it was worth it!"

"cfengine for example has a learning curve that makes hard to sell in any meaningful way to an ops team. "

"There is a lot to know about CFEngine, which can make it hard for people new to the subject."

", the language was overly complicated and the learning curve much steeper at the beginning: not exactly the features you want for a tool that should prompt people to use it."

Since this is such a breaking point for many CFEngine Inc. would financially benefit from simplifying the syntax Switching to a YAML syntax would be a great start to that. In my implementation I plan to do a few things that will remove unnecessary details as well.

Life easier when you focus on teaching noobs the DSL of CFEngine and not have to teach them both a syntax & also a DSL. I understood yaml the first time I visited yaml.org. I feel like others may have a simaliar experience.

  1. Easier to transform

There are libraries for YAML. People can use those libraries to write or look over policies files a lot easier. This is a huge boon.

It could also be a con, since policy files should be well thought over and designed, not automashed together by a machine. I think the pros outweigh the cons here. If you give people too much rope to hang themselves, a few might die, but the rest will build a bridge.

  1. Easier for EYEs

Face the facts: reading yaml is easier. You get rid all the crud you don't need, and you allow yourself to experience a format that reads like a list.

Why is that good? Easier to read policies means more people will read them. More people reading them means that problems are more likely to be seen earlier. This is good.

Write your policies in Japanese and you begin to trust the forieners.

Plus, imagine a world were the policies are so easy to read you can just print them and your boss can read them.-... but that's impossible... right?

  1. Easier for IDEs

It's just easier when it's a standard format. I couldn't get that eclipse plugin working, and I certainly wouldn't go back to emacs. Writing non-autoindented structured text is painful at best.

Yaml. IDEs love it.

  1. Less to type

Not much to say here but YAML is just less to type. That's good.

Why Yaml?

It's my personal favorite. No really, it's the most human readable and easily writable format, I don't think anything else would justify doing this for. CF syntax is sparse compared to something like json or (dare I say its terrible name) xml.

Plus yaml is simaliar to .CF syntax. Just standardized and cleaner. You're still going to have the vars: and the any::, so don't worry about that Mark Burgess.

Also it's declarative. It's not a programming language or based on one. It's just structured data, and that's exactly what CF syntax is. It's really a great fit.

I don't really think CFEngine needs another dependency, so only implement the sub-set of yaml that is actually used in policies and just compile that in.

I AM NOT IN FAVOR OF A SWITCH I think the new formats should co-exists. I'm all for backwards compatibility.

So what am I going to do about it?

I have taken the freedom to spend many many hours implementing my idea exactly using the community code.

The only changes I made are to the yacc parser file and lex lexing file. My implementation is COMPLETLY backwards compatible. The two can coexist even in the same file.

What changes have I made specifically?

Remember that the goal is simplicity, ease of learning, as well as yaml-compliance.

  1. Body Common Control {} is now Main:

This is the most important Body declaration of the entire file. If people new to the file can't find the starting point of the rest of the system, they can't center themselves and they don't know what's going on.

It is the index that makes the rest fall into place.

I chose main because that's the standard entry point in many language like C. Other alternatives I considered was index & *. Star wasn't YAML compliant, and I figured sysadmins were more familiar to C than html.

  1. Bundlesequence is now promises

See below. It is for consistency with the new name change.

  1. Body is now attr and Bundles are now proms

First off I wanted a much bigger name difference between the two. They should be completely different.

Since bodies are just promise attribute declarations I thought it would make since to call them attr.

'Bundles' is the strangest term for "A collection of Promises". It's the wrong term, we should focus on what they are, the structure allows you to store promises. Anyway proms is short for promises if you didn't catch that already.

  1. => is now =:

This is for two reason. One for YAML compliancy. Two because it makes var assignment look more like an assignment. I couldn't make it just : because of how YACC works. It would be too ambigious.

Now is a good time to make an important note. I did not tokenize newlines or whitespace at all. Even though YAML looks at whitespace; I tried, believe me, but it was just too complicated to have YACC handle that kind of input.

  1. Lists can be made in YAML format

Pretty amazing if you ask me, but take a look at the code example below.

  1. Body & Bundle (Or Attrs & Proms) declarations now need parameters braces and a colon

The reason why I want to include parameters is to show that they can be added. The colon is for YAML.

NOTE: main: doesn't use parameter braces. Who would pass parameters to it?

  1. Body & Bundle (Or Attr & Proms) declarations don't need curly braces

For YAML conformance and for less typing.

  1. Promisers NEED curly braces, Promises need to end in a comma.

What? I ADDED extra syntax?! Crazy I know, but it's for a good reason. First of all it's important that CFEngine doesn't mix up promises and who promised them. Imagine if you forgot an attribute and a semicolon? CFEngine would just keep going!! That's not a safe parsing behavior.

Also it's for YAML compliance, this style in yaml is called a mapping 'flow style'.

Third, it's for the internal CFEngine parser. Since white space isn't look at, it can be ambiguous to have attributes and promise types right next to each other without a separator. Especially since CFEngine accepts any type of input in both of these places. This is were I tried to use new line tokenization, but it ended up being too complicated and require writing unsafe code.

And that's it! 7 changes that make CFEngine simpler, more secure, YAML-compliant, and easier for newbies.

So what does it look like?

###############################################################################
#
#   promises.cf - Basic Policy for Community
#
# Hash Comments still valud in yaml - anyway parts in comments show
###############################################################################

# body command control {     <- Special case, really draws it out.
main:
    

    promises =:     # <- Bundlesequence renamed to promises
        # Common bundles first for best practice
        -   "def"
        # Agent bundles from here
        -   "test"

    inputs =:
        # Global common bundles
          - "def.cf"
        # Control body for all agents
          - "controls/cf_agent.cf"
          - "controls/cf_execd.cf"
          - "controls/cf_monitord.cf"
          - "controls/cf_report.cf"
          - "controls/cf_runagent.cf"
          - "controls/cf_serverd.cf"
        # COPBL/Custom libraries
          - "libraries/cfengine_stdlib.cf"
        # User services from here
          - "services/init_msg.cf"

    version =: "Community Promises.cf 3.4.0"

# Bundles are renamed to promises (or proms), the Body tag is renamed to attr
proms agent test():
    methods:
        any::
            "INIT MSG" : { # <- Need a curly here for YAML compliance
                usebundle =: init_msg,
                comment =: "Just a pre-defined policy bundled with the package",
                handle =: "main_methods_any_init_msg"
            }

            "test": {
                 usebundle =: init_msg,
                 comment =: "Just a pre-defined policy bundled with the package",
                 handle =: "main_methods_any_init_msg"
            }
Clone this wiki locally