Getting started¶
This page takes you from installing sadame to your first plan and
apply: install it, write a one-line recipe, look at the plan, apply it,
and learn to read the diff sadame prints when a machine has drifted from its
recipe.
Install¶
curl -fsSL https://raw.githubusercontent.com/enderkus/sadame/main/install.sh | sh
sadame --version
The examples on this page are in the repository; clone it to follow along:
git clone https://github.com/enderkus/sadame.git
cd sadame
The Install page has the other ways to install, including
running sadame from the checkout with Ruby 3.2 or newer (bin/sadame).
Your first recipe¶
A recipe is a Ruby file that declares resources: packages, files, directories, services, commands and users. The smallest one has a single resource:
# The smallest useful recipe: one file with fixed content.
file "/tmp/sadame-hello.txt", content: "Hello from sadame!\n"
It writes to /tmp, so it works without root.
Plan¶
sadame plan reads the current state of everything the recipe declares and
prints what apply would change. It changes nothing.
$ sadame plan examples/01-hello
sadame 0.5.1 plan on local (Debian GNU/Linux 12 (bookworm))
+ file[/tmp/sadame-hello.txt]
state: absent -> present
--- /dev/null
+++ /tmp/sadame-hello.txt (desired)
@@ -0,0 +1 @@
+Hello from sadame!
Plan: 1 to create, 0 to update, 0 to remove, 0 to run, 0 unchanged.
RECIPE can be a file or a directory that contains recipe.rb. Without it,
sadame looks for ./recipe.rb.
Apply¶
sadame apply makes the same comparison, resource by resource, and changes
only what differs:
$ sadame apply examples/01-hello
sadame 0.5.1 apply on local (Debian GNU/Linux 12 (bookworm))
changed file[/tmp/sadame-hello.txt]
state: absent -> present
--- /dev/null
+++ /tmp/sadame-hello.txt (desired)
@@ -0,0 +1 @@
+Hello from sadame!
Summary
changed 1 file[/tmp/sadame-hello.txt]
unchanged 0
failed 0
skipped 0
notified 0
Finished in 0.09s.
Run plan again and there is nothing left to do. That is what idempotent
means here: once the machine matches the recipe, running it again changes
nothing.
$ sadame plan examples/01-hello
sadame 0.5.1 plan on local (Debian GNU/Linux 12 (bookworm))
Plan: 0 to create, 0 to update, 0 to remove, 0 to run, 1 unchanged.
Read the diff¶
Now change the file by hand, the way a colleague or a package upgrade might:
$ echo 'Hello from someone else' > /tmp/sadame-hello.txt
$ sadame plan examples/01-hello
sadame 0.5.1 plan on local (Debian GNU/Linux 12 (bookworm))
~ file[/tmp/sadame-hello.txt]
--- /tmp/sadame-hello.txt
+++ /tmp/sadame-hello.txt (desired)
@@ -1 +1 @@
-Hello from someone else
+Hello from sadame!
Plan: 0 to create, 1 to update, 0 to remove, 0 to run, 0 unchanged.
Each resource that would change gets a header line with a symbol and the
resource's id, type[name]:
| Symbol | Meaning |
|---|---|
+ |
create: the resource does not exist yet |
~ |
update: it exists but some attributes differ |
- |
remove: the recipe says state: :absent and it exists |
! |
run: an exec command whose guards say it must run |
x |
failed: the current state could not be read; the error follows |
Below the header, each differing attribute is shown as current -> desired.
File content is shown as a unified diff, the same format as diff -u and
git diff: --- is the file on disk, +++ is what the recipe wants. In a
terminal, removed lines are red and added lines green:
--- /tmp/sadame-hello.txt
+++ /tmp/sadame-hello.txt (desired)
@@ -1 +1 @@
-Hello from someone else
+Hello from sadame!
Colors are turned off when output is not a terminal, with --no-color, or
when the NO_COLOR environment variable is set.
The last line counts resources by what would happen to them. Unchanged resources are counted but not listed.
Check for drift¶
sadame drift reads the same state as plan, but its exit status says
whether the machine still matches the recipe: 0 when it does, 1 when it
has drifted. That makes it the command for scripts, cron jobs and CI:
$ sadame drift examples/01-hello
sadame 0.5.1 drift on local (Debian GNU/Linux 12 (bookworm))
~ file[/tmp/sadame-hello.txt]
--- /tmp/sadame-hello.txt
+++ /tmp/sadame-hello.txt (desired)
@@ -1 +1 @@
-Hello from someone else
+Hello from sadame!
Drift: 1 of 1 resource differs from the recipe.
$ echo $?
1
Plan, apply, drift explains when to use which, and Drift in CI runs the check on a schedule.
JSON output¶
--json prints one JSON document instead, for scripts and CI. Every field
is described in the JSON output reference:
$ sadame plan examples/01-hello --json
{
"format_version": 1,
"sadame_version": "0.5.1",
"command": "plan",
"host": "local",
"platform": {
"name": "Debian GNU/Linux 12 (bookworm)",
"family": "debian",
"package_manager": "apt"
},
"tags": [],
"skip_tags": [],
"resources": [
{
"id": "file[/tmp/sadame-hello.txt]",
"type": "file",
"name": "/tmp/sadame-hello.txt",
"status": "update",
"changes": [
{
"attribute": "content",
"sensitive": false,
"diff": "--- current\n+++ desired\n@@ -1 +1 @@\n-Hello from someone else\n+Hello from sadame!\n"
}
],
"notifications": [],
"error": null
}
],
"notifications": [],
"summary": {
"create": 0,
"update": 1,
"remove": 0,
"run": 0,
"unchanged": 0,
"failed": 0
},
"drift": true,
"failed": false,
"exit_status": 0,
"duration_seconds": 0.05
}
Exit status¶
| Status | plan |
apply |
drift |
|---|---|---|---|
0 |
the plan was computed, with or without changes | every resource and notification succeeded | the machine matches the recipe |
1 |
not used | a resource or notification failed | the machine differs from the recipe |
2 |
usage or recipe error, or the state of a resource could not be read | usage or recipe error | usage or recipe error, or the state of a resource could not be read |
Your own project¶
sadame init writes the files a project starts from: a recipe with one
resource, vars.rb, an inventory with examples to uncomment, and a
template. sadame validate checks the recipe without touching a machine:
$ sadame init infra
created infra/recipe.rb
created infra/vars.rb
created infra/hosts.rb
created infra/templates/motd.erb
Next:
sadame validate infra check the recipe without connecting to anything
sudo sadame plan infra see what it would change on this machine
sadame plan infra -g all on your servers, once infra/hosts.rb lists them
$ sadame validate infra
sadame 0.5.1 validate infra/recipe.rb, as on Debian GNU/Linux 12 (bookworm)
local ok 1 resource
validate on 1 host: 1 ok.
Add --ci github or --ci gitlab to also get a daily
drift check for your CI, and set up your editor
with sadame lsp to see mistakes as you type.
Common mistakes¶
- Running without root. Most real recipes install packages or write
under
/etc. Run them withsudo sadame apply ...;planusually needs root too, to read files only root can read. - Expecting
applyto undo things it does not manage. Sadame only compares what the recipe declares. Deleting a resource from a recipe does not remove it from the machine; declare it withstate: :absentinstead. - Treating
planas a guarantee.plancompares each resource with the machine as it is now. Duringapply, earlier resources change the machine (a package creates a user, a file a service needs), so later resources are compared again right before they run.
See also¶
- Sadame: Configuration Management Made Simple, the beginner's guide: a free book that goes from a first recipe to a whole fleet, with a practice lab to follow along.
- Plan, apply, drift: the three commands and their exit statuses.
- Recipe basics: vars,
include_recipeand templates. - Remote hosts: run the same recipe on other machines over SSH, with an inventory.
- Resources: every resource and its options.
sadame --helpfor the full command line.