Reference
>Table of Contents<
- Why
- Multicall Modes
- Audit Trail
- Readmes
- Environment Variables
- Idempotence
- Hierarchy
- Notes
- Decision Record
Why
I built my own Lua-based configuration management software. A little more than thousand commits in, I realized the oneshot nature of shell scripts more convenient.
CFEngine, Puppet, Chef and others did not offer advantages over a combination of rerun/bashing + drist. Cons mostly outweight the pros. Slow and complicated are the common complaints. If you’re just templating shell commands then plain shell scripts should suffice.
According to the Lindy effect; shell scripts, openssh and tar will outlive these mentioned CM software.
Huge shell scripts can be unwieldy. rr
will help you manage your scripts.
Multicall Modes
You can use a symlink to activate the modes.
Example for sudo mode:
ln -s rr rrs
Console
Verbose mode.
When called as rrv
or a console is detected it runs in verbose mode.
Thu, 22 Jul 2021 14:01:43 +0800 rr 0.10.0 "Kilowatt Triceps"
Thu, 22 Jul 2021 14:01:43 +0800 Running tmp:test via local...
Thu, 22 Jul 2021 14:01:43 +0800 Running test...
Thu, 22 Jul 2021 14:01:53 +0800 Done. Output:
local ┌─ stdout
local │
local │ 3
local │
local └─
Thu, 22 Jul 2021 14:01:53 +0800 Total run time: 10.059915355s. All OK.
In this mode it also logs to the file rr.json
in the current working directory.
NOTE: logs STDOUT and STDERR as a debug entry when no errors are detected
TIP: Example command line to extract STDOUT from the log file
grep 5DB2F900F1F1AF2B rr.json | grep stdout | cut -f12 -d\" | base64 -d
Silent
When called as rr
and a console is not detected it only shows errors as structured JSON.
{"level":"error","stdout":"ss\n","stderr":"ee\n","time":"2021-07-20T20:16:04+08:00","message":"Output"}
{"level":"error","elapsed":"1.798478ms","time":"2021-07-20T20:16:04+08:00","message":"Something went wrong."}
Dump
When called as rrd
, dumps the generated script. This is mainly for debugging and allows running scripts
generated from a managed namespace without the rr
executable.
Sudo
When called as rrs
, asks for a sudo
password for the remote user. Similar to the ansible options --become -K
.
Also turns on console mode since a TTY is expected anyway.
Audit Trail
The contents of the TASK
file logged as the message
field and task field
. Without the task
file
the default is UNDEFINED
. It can be overriden by the environment variable TASK
.
This can be useful for audit trails or for changelogs. Before running a script you should fill up TASK
.
echo 'Upgrade a thing for bugfix' > TASK
rr program:upgrade
READMEs
Any case insensitive file named readme*
in the namespace and script directories can be shown by invoking rr
by the
following ways:
Prints namespace/readme*
rr namespace
rr namespace/
Prints namespace/script/readme*
rr namespace/script
rr namespace/script/
Environment Variables
Any environment variable prefixed with rr__
are passed to the script.
For example, the rr__USERNAME=test
environment variable is passed to the script as USERNAME=test
$ cat tmp/env/script
echo $USERNAME
$ env rr__USERNAME=test rr tmp:env
Thu, 22 Jul 2021 18:13:32 +0800 rr 0.10.0 "Kilowatt Triceps"
Thu, 22 Jul 2021 18:13:32 +0800 Running tmp:env via local...
Thu, 22 Jul 2021 18:13:32 +0800 Running env...
Thu, 22 Jul 2021 18:13:32 +0800 Done. Output:
local ┌─ stdout
local │
local │ test
local │
local └─
Thu, 22 Jul 2021 18:13:32 +0800 Total run time: 101.648007ms. All OK.
Idempotence
The default behavior of scripts is to indicate two results: no errors(ok) and failure(failed). To emulate idempotence your script can print the following string to STDOUT in it’s own line:
+++++repaired+++++
When writing your script you have to check if an operation is needed to apply a change or not. Such a precondition can be written like this:
if test -d /some_dir
then
exit 0
else
{
mkdir /some_dir
} >/dev/null 2>&1 || exit 1
printf "+++++repaired+++++\\n"
exit 0
fi
In the JSON log and report mode the result value will say repaired.
No changes, script exited | –> | ok |
Script succesfully executed changes | –> | repaired |
Repair needed but unable to apply changes | –> | failed |
To borrow terminology from *CFEngine*. There are no changes when the **desired state** is already the expected outcome.
Any state | –> | Desired state |
Desired state | –> | Desired state |
Hierarchy
TOPLEVEL <--- Directory of namespaces or a project
├── rr.hosts <--- custom SSH config
├── .files <--- synced to any host
├── .files-avocado <--- synced to the avocado host
├── .lib <--- sourced by all scripts
└── namespace
├── .files <--- synced to any host when namespace:* is called
├── .files-avocado <--- synced to the avocado host when namespace:* is called
├── .lib <--- sourced along with namespace:* scripts
├── readme <--- Documentation for namespace
└── script directory
├── .files <--- synced to any host when namespace:script is called
├── .files-avocado <--- synced to the avocado host when namespace:script is called
├── .lib <--- sourced along with namespace:script scripts
├── readme <--- Documentation for script
├── task <--- Companion to readme, used to log current task
└── script <--- the actual shell script
Notes
Tested on Linux and macOS.
Remote host only requires OpenSSH server and tar(1)
.
Scripts should reference $@
if it wants to use arguments passed.
Decision Record
- Environment variables are not logged. They may contain secrets.