Advanced Walkthrough

This walkthrough will show how a Design Center sketch can be found, installed, configured, and executed as policy.

It requires a Unix that can run Perl and, of course, CFEngine itself.

Checkout Design Center

You can browse the source for examples, tools, sketches (generic reusable policy), or read through the Design Center wiki by browsing https://github.com/cfengine/design-center

After you have explored and you are ready to go, change to a directory that will not be cleaned up automatically (don't use /tmp for example; here we used ~/source) and run:

git clone git@github.com:cfengine/design-center.git

or, if the Git native protocol is blocked at your site:

git clone https://github.com/cfengine/design-center.git

You should end up with a directory called design-center. We'll call this directory the CHECKOUT directory and refer to it as $(CHECKOUT) from here on. In the examples that follow, CHECKOUT is ~/source/design-center (the tilde ~ refers to the current user's home directory).

You can save some typing by saying

export CHECKOUT=~/source/design-center

at the prompt. From that point on, all command-line interaction can use $CHECKOUT and it will expand to the installation directory.

You also need to make sure curl is installed on your system, since it is used by cf-sketch for accessing remote files when needed. Curl is readily available as a package in most Unix/Linux systems, and even installed by default on many of them.

Prepare config.json

To interact with Design Center, you need to tell its API where things were installed. Copy the config.json file to your CFEngine personal directory:

mkdir ~/.cfagent
cp $CHECKOUT/tools/cf-sketch/config.json ~/.cfagent/dc-api-config.json

(Note that there's also a config-root.json intended for privileged (root) usage. It sets things up under /var/cfengine but still expects the checkout under ~/source/design-center.)

Now if you look at this file, you'll see that it has some JSON data:

{
 log: "STDERR",
 log_level: 4,
 repolist: [ "~/.cfagent/inputs/sketches" ],
 recognized_sources: [ "~/source/design-center/sketches" ],
 runfile: { location: "~/.cfagent/inputs/api-runfile.cf", standalone: true, relocate_path: "sketches", filter_inputs: [] },
 vardata: "~/.cfagent/vardata.conf",
}

You can change any setting you want but it's probably safer to leave them all at the default.

The paths you see are all relative to your home directory. The log can be set to a file. The log_level can be lowered to 1 if you want less noise (here we'll work with it at 4).

vardata is where the Design Center API stores all the configuration.

runfile describes where the Design Center API will save an executable policy with all the sketches you have installed and activated.

The repolist is where sketches will be installed.

The recognized_sources is where sketches will be found. It can be a URL such as https://github.com/cfengine/design-center/blob/master/sketches/cfsketches.json.

You can save some typing by saying

export DCJ=~/.cfagent/dc-api-config.json    

at the prompt. From that point on, all command-line interaction can use $DCJ and it will expand to the filename above.

From this point on, we'll refer to this command

$CHECKOUT/tools/cf-sketch/cf-dc-api.pl

as $CFAPI for brevity.

You can save some typing by saying

export CFAPI=$CHECKOUT/tools/cf-sketch/cf-dc-api.pl

at the prompt. From that point on, all command-line interaction can use $CFAPI and it will expand to the command above.

Search for sketches

Time to search for sketches!!! This section will explain many details that the later sections in the walkthrough will skip for brevity.

Search for sketches with the Design Center API

This is what all the other Design Center tools use. You don't need to know this protocol or know that it's used.

echo '{ dc_api_version: "0.0.1", request: {search: true } }' | $CFAPI $DCJ

If you get errors here, you may be missing Perl modules or the CFEngine agent. Look at the Design Center wiki for possible solutions to your problem, at https://github.com/cfengine/design-center/wiki

Output:

DCAPI::log3(DCAPI.pm:173): Successfully loaded vardata file /home/tzz/.cfagent/vardata.conf
DCAPI::log(DCAPI.pm:376): Searching location ~/source/design-center/sketches for terms true
DCAPI::Sketch::matches(Repo.pm:118): sketch Applications::Memcached matched terms true
...
DCAPI::Sketch::matches(Repo.pm:118): sketch Webserver::Install matched terms true

All of the above is debugging output. With log set to a file name, the output will go to that file. With log_level set to 1, only the essential errors will be shown. Now for the actual API response:

{
  "api_ok":
  {
    "warnings":[],"success":true,"errors":[],"error_tags":{},
    "data":
    {
        "search":
        {
            "/home/tzz/source/design-center/sketches":
            {
              "System::config_resolver":"System::config_resolver", ... ,"System::set_hostname":"System::set_hostname"
            }
        }
    },
    "log":[],"tags":{}
  }
}

The response says (with many sketch names omitted for brevity): here, these are all my sketches.

Again, this is not what you would use daily.

Search for sketches with cf-sketch in expert mode

$CHECKOUT/tools/cf-sketch/cf-sketch.pl --expert --cfpath=/var/cfengine/bin --apiconfig $DCJ --search | sort

Output:

Applications::Memcached Sketch for installing, configuring, and starting memcached.
...
Yale::stdlib Yale standard library

You piped the command above through the standard sort command to sort the results. You can omit the sort.

Search for sketches with cf-sketch in interactive mode

Run

$CHECKOUT/tools/cf-sketch/cf-sketch.pl --apiconfig $DCJ

You'll see this prompt, or something like it:

Welcome to cf-sketch version 3.5.0b1.
CFEngine AS, 2013.

Enter any command to cf-sketch, use 'help' for help, or 'quit' or '^D' to quit.

cf-sketch> 

Now enter the search command:

cf-sketch> search

The following sketches are available:

Applications::Memcached Sketch for installing, configuring, and starting memcached.
...
Yale::stdlib Yale standard library

cf-sketch> 

Obviously that's easiest but you may want the expert or direct API interaction for specific purposes. We'll show all three along the way, don't worry.

Install a sketch

Install a sketch with the Design Center API

echo '{ dc_api_version: "0.0.1", request: {install: {sketch:"System::motd", force:true} } }' | $CFAPI $DCJ

The force parameter tells the Design Center API to overwrite the sketch even if it's installed already.

Output:

DCAPI::log3(DCAPI.pm:173): Successfully loaded vardata file /home/tzz/.cfagent/vardata.conf
...
DCAPI::log(DCAPI.pm:576): Installing sketch: {"source":["~/source/design-center/sketches"],"target":"~/.cfagent/inputs/sketches","sketch":"System::motd","force":true}
DCAPI::log4(Repo.pm:179): Installing sketch System::motd: copying /home/tzz/source/design-center/sketches/system/motd/README.md to /home/tzz/.cfagent/inputs/sketches/system/motd/README.md
DCAPI::log4(Repo.pm:179): Installing sketch System::motd: copying /home/tzz/source/design-center/sketches/system/motd/main.cf to /home/tzz/.cfagent/inputs/sketches/system/motd/main.cf
DCAPI::log4(Repo.pm:179): Installing sketch System::motd: copying /home/tzz/source/design-center/sketches/system/motd/params/debian_squeeze.json to /home/tzz/.cfagent/inputs/sketches/system/motd/params/debian_squeeze.json
DCAPI::log4(Repo.pm:179): Installing sketch System::motd: copying /home/tzz/source/design-center/sketches/system/motd/params/debian_wheezy.json to /home/tzz/.cfagent/inputs/sketches/system/motd/params/debian_wheezy.json
DCAPI::log4(Repo.pm:179): Installing sketch System::motd: copying /home/tzz/source/design-center/sketches/system/motd/params/example.json to /home/tzz/.cfagent/inputs/sketches/system/motd/params/example.json
DCAPI::log4(Repo.pm:179): Installing sketch System::motd: copying /home/tzz/source/design-center/sketches/system/motd/params/simple.json to /home/tzz/.cfagent/inputs/sketches/system/motd/params/simple.json
DCAPI::log4(Repo.pm:179): Installing sketch System::motd: copying /home/tzz/source/design-center/sketches/system/motd/test.cf to /home/tzz/.cfagent/inputs/sketches/system/motd/test.cf

After lots of fireworks (again, remember to drop down to log_level 1 or 0 if you want to skip all these messages) the sketch is installed!

Finally the API returns:

{
  "api_ok":
  {
    "warnings":[],"success":true,"errors":[],"error_tags":{},
    "data":
    {
        "install":
        {
          "System::motd":
          {
            "test.cf":"/home/tzz/.cfagent/inputs/sketches/system/motd/test.cf",
            "params/debian_squeeze.json":"/home/tzz/.cfagent/inputs/sketches/system/motd/params/debian_squeeze.json",
            "README.md":"/home/tzz/.cfagent/inputs/sketches/system/motd/README.md",
            "params/example.json":"/home/tzz/.cfagent/inputs/sketches/system/motd/params/example.json",
            "params/debian_wheezy.json":"/home/tzz/.cfagent/inputs/sketches/system/motd/params/debian_wheezy.json",
            "main.cf":"/home/tzz/.cfagent/inputs/sketches/system/motd/main.cf",
            "params/simple.json":"/home/tzz/.cfagent/inputs/sketches/system/motd/params/simple.json"
          },
          "~/.cfagent/inputs/sketches":{"System::motd":1}
        },
        "inventory_save":1
    },
    "log":[],"tags":{"System::motd":1,"installation":8}
  }
}

The above output says that System::motd was installed in /home/tzz/.cfagent/inputs/sketches/system/motd/ (because my config.json says so), and that the sketch inventory was saved afterwards.

Install a sketch with cf-sketch in expert mode

$CHECKOUT/tools/cf-sketch/cf-sketch.pl --expert --cfpath=/var/cfengine/bin --install System::motd --apiconfig $DCJ

Output:

Sketch System::motd is already in target repo; you must uninstall it first

OK, so we need to force it...

$CHECKOUT/tools/cf-sketch/cf-sketch.pl --expert --cfpath=/var/cfengine/bin --install System::motd --apiconfig $DCJ --force

Output: nothing! In expert mode, when everything is OK, nothing is printed. Only the command return code will tell you if everything went well.

OK, so we need to make it verbose...

$CHECKOUT/tools/cf-sketch/cf-sketch.pl --expert --cfpath=/var/cfengine/bin --install System::motd --apiconfig $DCJ --force --verbose

Output:

... lots of verbose output, including the API interaction, omitted ...
OK: Got successful result: ... the API result is here, omitted for brevity

The cf-sketch expert mode is a thin layer over the API for testing and unattended work, so the above verbose output is not really meant for everyday use.

Install a sketch with cf-sketch in interactive mode

Run

$CHECKOUT/tools/cf-sketch/cf-sketch.pl --apiconfig $DCJ

Now enter the uninstall System::motd and install System::motd commands, because just installing an already-installed sketch won't do anything interesting:

cf-sketch> uninstall System::motd

Deactivated System::motd.
Sketch 'System::motd' was uninstalled.

cf-sketch> install System::motd

Sketch System::motd installed under /home/tzz/.cfagent/inputs/sketches.

cf-sketch> 

Oh, and if you miss all that verbose output, you can still use --verbose with the interactive cf-sketch call and see all that wonderful output.

Activate a sketch

Activate a sketch with the Design Center API

We are going to define a run environment, which will tell the Design Center API that we want an activated sketch, not in test mode, and with verbose output:

echo '{ dc_api_version: "0.0.1", request: {define_environment: { walkthrough: { activated: true, test: false, verbose: true } } } }' | $CFAPI $DCJ

Then we will define the parameters for the System::motd sketch:

echo '{ dc_api_version: "0.0.1", request: {define: { "motd_params": { "System::motd": { "motd": "\\n ! System is under the control of CFEngine, local changes may by overwritten.\\n", "prepend_command": null } } } } }' | $CFAPI $DCJ

and finally, use the run environment and the parameters to activate the sketch:

echo '{ dc_api_version: "0.0.1", request: {activate: { "System::motd": { environment: "walkthrough", params: [ "motd_params" ] } } } }' | $CFAPI $DCJ

Output (omitting log lines and reformatted):

{"api_ok":{"warnings":[],"success":true,"errors":[],"error_tags":{},
           "data":{"define_environment":{"walkthrough":1}},
           "log":[],"tags":{"walkthrough":1}}}

{"api_ok":{"warnings":[],"success":true,"errors":[],"error_tags":{},
           "data":{"define":{"motd_params":1}},
           "log":[],"tags":{"motd_params":1}}}

{"api_ok":{"warnings":[],"success":true,"errors":[],"error_tags":{},
           "data":{"activate":{"System::motd":{"params":["motd_params"],"environment":"walkthrough"}}},
           "log":[],"tags":{"System::motd":1}}}

This tells us that the API has recorded that we want the sketch System::motd to run with the run environment walkthrough and the parameters motd_params.

Activate a sketch with cf-sketch in expert mode

We'll try the simple.json parameters that come with System::motd. You can look at that file, it's the same as the motd_params above.

$CHECKOUT/tools/cf-sketch/cf-sketch.pl --expert --apiconfig $DCJ --activate System::motd=$CHECKOUT/sketches/system/motd/params/simple.json --verbose --activated

Output:

...
DCAPI::log(DCAPI.pm:1061): Activations for sketch System::motd are now [{"params":["motd_params"],"environment":"walkthrough"},{"params":["parameter definition from /home/tzz/source/design-center/sketches/system/motd/params/simple.json"],"environment":"cf_sketch_testing","target":"/home/tzz/.cfagent/inputs/sketches"}]

This says we now have two activations! One from the API call above and one we just created. Let's undo the activations:

$CHECKOUT/tools/cf-sketch/cf-sketch.pl --expert --apiconfig $DCJ --deactivate-all
$CHECKOUT/tools/cf-sketch/cf-sketch.pl --expert --apiconfig $DCJ --activate System::motd=$CHECKOUT/sketches/system/motd/params/simple.json  --verbose --activated

Output:

...
DCAPI::log(DCAPI.pm:1403): Deactivating all activations: {"System::motd":[{"params":["motd_params"],"environment":"walkthrough"},{"params":["parameter definition from /home/tzz/source/design-center/sketches/system/motd/params/simple.json"],"environment":"cf_sketch_testing","target":"/home/tzz/.cfagent/inputs/sketches"}]}
...
OK: Got successful result: {"success":true,"warnings":[],"errors":[],"error_tags":{},"log":[],"data":{"activate":{"System::motd":{"environment":"cf_sketch_testing","params":["parameter definition from /home/tzz/source/design-center/sketches/system/motd/params/simple.json"],"target":"/home/tzz/.cfagent/inputs/sketches"}}},"tags":{"System::motd":1}}

Looks like it worked! The cf_sketch_testing environment is created by cf-sketch on the fly and will include the same things as the walkthrough run environment. The --activated and --verbose flags turn on the environment activated and verbose flags. There's also a --test flag, but we won't use it here.

Activate a sketch with cf-sketch in interactive mode

Run

$CHECKOUT/tools/cf-sketch/cf-sketch.pl

Then:

cf-sketch> define params System::motd

Please enter a name for the new parameter set (default: System::motd-entry-000): motd_params
Querying configuration for parameter set 'motd_params' for bundle 'entry'.

Please enter parameter motd (Message of the Day (aka motd), ).
motd : Hello there!

Please enter parameter motd_path (Location of the primary, often only, MotD file, ).
motd_path [/etc/motd]: /etc/motd

Please enter parameter prepend_command (Command output to prepend to MotD, ).
prepend_command [/bin/uname -snrvm]: /bin/uname -snrvm

Please enter parameter dynamic_path (Location of the dynamic part of the MotD file, ).
dynamic_path : null

Please enter parameter symlink_path (Location of the symlink to the motd file, ).
symlink_path : null

Defining parameter set 'motd_params' with the entered data.
Parameter set motd_params successfully defined.

cf-sketch> activate System::motd motd_params walkthrough
Using existing parameter definition 'motd_params'.
Using existing environment 'walkthrough'.
Activating sketch System::motd with parameters motd_params.
...
DCAPI::log(DCAPI.pm:1061): Activations for sketch System::motd are now [{"params":["parameter definition from /home/tzz/source/design-center/sketches/system/motd/params/simple.json"],"environment":"cf_sketch_testing","target":"/home/tzz/.cfagent/inputs/sketches"},{"params":["motd_params"],"environment":"walkthrough","target":"/home/tzz/.cfagent/inputs/sketches","identifier":"System::motd-1"}]

cf-sketch> 

As you can see the expert and interactive modes have completely different usage patterns.

Generate and execute the runfile

Generate and execute the runfile with the Design Center API

Generating the runfile is easy:

echo '{ dc_api_version: "0.0.1", request: {regenerate: { } } }' | $CFAPI $DCJ

Output:

DCAPI::log(DCAPI.pm:249): Saving runfile /home/tzz/.cfagent/inputs/api-runfile.cf
{"api_ok":{"warnings":[],"success":true,"errors":[],"error_tags":{},"data":{},"log":[],"tags":{}}}

Note there is no user control over the location of the runfile, it's entirely defined in the API's config.json file. This is by design.

Time to run the policy!!! We know the name of the runfile, let's go!

cf-agent -KI -f ~/.cfagent/inputs/api-runfile.cf

We are not in test mode, so we'll get errors if we run as a non-privileged user.

2013-06-04T20:12:04-0400     info: This agent is not bootstrapped
2013-06-04T20:12:04-0400     info: Running full policy integrity checks
2013-06-04T20:12:04-0400    error: Unable to open destination file '/etc/motd.cf-after-edit' for writing. (fopen: Permission denied)
2013-06-04T20:12:04-0400    error: /cfsketch_run/methods/'___001_System_motd_entry'/cfdc_motd:entry/files/'$(main_path)': Unable to save file '/etc/motd' after editing
2013-06-04T20:12:04-0400    error: chmod failed on '/etc/motd'. (chmod: Operation not permitted)
2013-06-04T20:12:04-0400   notice: R: cfdc_motd:entry: System::motd license = MIT
2013-06-04T20:12:04-0400   notice: R: cfdc_motd:entry: System::motd dependencies = CFEngine::dclib, CFEngine::stdlib
2013-06-04T20:12:04-0400   notice: R: cfdc_motd:entry: System::motd version 1.00 by Ben Heilman <bheilman@enova.com> starting up...
2013-06-04T20:12:04-0400   notice: R: cfdc_motd:entry: imported environment 'cf_sketch_testing' var 'activated' with value '1'
2013-06-04T20:12:04-0400   notice: R: cfdc_motd:entry: imported environment 'cf_sketch_testing' var 'test' with value ''
2013-06-04T20:12:04-0400   notice: R: cfdc_motd:entry: imported environment 'cf_sketch_testing' var 'verbose' with value '1'
2013-06-04T20:12:04-0400   notice: R: cfdc_motd:entry: imported environment 'cf_sketch_testing' class 'activated' because 'default:runenv_cf_sketch_testing_activated' was defined
2013-06-04T20:12:04-0400   notice: R: cfdc_motd:entry: imported environment 'cf_sketch_testing' class 'verbose' because 'default:runenv_cf_sketch_testing_verbose' was defined
2013-06-04T20:12:04-0400   notice: R: cfdc_motd:entry: running in verbose mode

That's it!

Generate and execute the runfile with cf-sketch in expert mode

$CHECKOUT/tools/cf-sketch/cf-sketch.pl --expert --apiconfig $DCJ --generate

Output:

...
DCAPI::log(DCAPI.pm:249): Saving runfile /home/tzz/.cfagent/inputs/api-runfile.cf

Run the runfile! Do it!!!

cf-agent -KI -f ~/.cfagent/inputs/api-runfile.cf

The output will be the same as in the previous section.

Generate and execute the runfile with cf-sketch in interactive mode

Run

$CHECKOUT/tools/cf-sketch/cf-sketch.pl

You'll see:

cf-sketch> generate

...
DCAPI::log(DCAPI.pm:249): Saving runfile /home/tzz/.cfagent/inputs/api-runfile.cf
Runfile /home/tzz/.cfagent/inputs/api-runfile.cf successfully generated.

There you go. Run it. Do it.

cf-agent -KI -f ~/.cfagent/inputs/api-runfile.cf

The output will be the same as in the previous section.