kenn/sunzi — pull request #16 by hopsoft (2013-04-20 15:20:41+00:00)
Title: Added recipe generator and idempotence help.

This commit adds 2 additional bits of functionality.

First, it provides a simple idempotence solution.
I've introduced a sunzi.run_recipe method which checks for the
existence of a recipe file in /etc/sunzi/completed_recipes on the
machine being provisioned. The existence of this file indicates
that the recipe has already been run. A recipe file is created
in this directory upon the initial run for each recipe.

Second, it provides a recipe file generator.
This CLI helper helps keep projects organized over time because
server provisioning is very much an incremental and evolving process.
The generator simply creates empty files under sunzi/recipes in the
following format TIMESTAMP-ROLE-RECIPE_NAME.sh

#### Example

Consider that we need to provision a database server. We will install PostgresSQL, and a month later, we will recompile PostgreSQL to pick up some new features.

First lets create our db role script.

```
touch sunzi/roles/db.sh
```

Next lets create the initial install recipe using the CLI recipe generator.

```
sunzi recipe db initial-postgresql-install
```

This will create the following file.

```
|-sunzi
|-recipes
|-20130301002545-db-initial-postgresql-install.sh
```

Now lets update the role script to include our recipe by adding the line below to `sunzi/roles/db.sh`

```
sunzi.run_recipe "20130301002545-db-initial-postgresql-install.sh"
```

---

A month passes and we need to recompile PostgreSQL to pick up new features. Lets use the CLI recipe generator to create a new recipe.

```
sunzi recipe db postgresql-add-feature-x
```

Now our recipes directory looks like this.

```
|-sunzi
|-recipes
|-20130301002545-db-initial-postgresql-install.sh
|-20130401086536-db-postgresql-add-feature-x.sh
```

Now lets add the new recipe to `sunzi/roles/db.sh`

```
sunzi.run_recipe "20130301002545-db-initial-postgresql-install.sh"
sunzi.run_recipe "20130401086536-db-postgresql-add-feature-x.sh"
```

We've safely avoided naming collisions and can run the provisioning scripts multiple times safely. Also, pulling the idempotence concern out of the recipes allows us to keep them very simple and clean.