Reference
Table of Contents
- Why
- Multicall modes
- Audit trail
- Readmes
- Environment variables
- Idempotence
- Hierarchy
- VARS
- Shell
- 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. taarr
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.
$ rrv make:yyy
29 Jun 24 11:55 +0800 rr 2.2.0 “Craving Detonator”
29 Jun 24 11:55 +0800 Running make:yyy via local…
29 Jun 24 11:55 +0800 Running yyy…
local │ -rw-r--r-- 1 root root 2439 Apr 6 10:39 /etc/passwd
local │ stdout last
29 Jun 24 11:55 +0800 Failure running script!
local ┌─ stderr
local │
local │ stderr
local │ next
local │ last
local │
local └─
local ┌─ debug
local │
local │ exit status 255
local │
local └─
29 Jun 24 11:55 +0800 Total run time: 1s. Something went wrong.
In this mode it also logs to the file LOG
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 3EB340F6 LOG | grep stdout | cut -f28 -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.
###
Log
When called as rrl
, reads the JSON file LOG
then shows a log of executions for audit trail.
##
Audit trail
A log is generated for each script invocation. The JSON file LOG
contains an audit trail of all script invocations.
The environment variable LOG
is logged as the log
field
. Without the variable the arguments are used for the field. If no variable or arguments passed then it is logged as UNDEFINED
.
This can be useful for audit trails or for changelogs. Before running a script you should set the LOG
environment variable.
LOG='Upgrade a thing for bugfix` rr program:upgrade
##
Readmes
###
Top-level
When invoked without any arguments, rr
prints the README
in the current working directory.
###
namespace/script
Any case insensitive file named readme*
in the namespace and script directories can be shown by invoking rr
in 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 STDERR in it’s own line and exit 0:
__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
>&2 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
├── .files <--- synced to any host
├── .files-avocado <--- synced to the avocado host
├── .lib <--- files sourced by all scripts
├── VARS <--- sourced by all scripts, ideal for frequently changed variables
├── LOG <--- Log generated (JSON); can be printed with `rrl`
├── HOSTS <--- custom SSH config
└── 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
├── script <--- the main shell script
├── script.pre <--- run before the main script
├── script.post <--- run after the main script
└── shell <--- shell interpreter for the script
##
VARS
The VARS
file is sourced by all scripts in all namespaces. It is ideal for variables that frequently change.
##
Shell
The shell used to run the script can be set within the file namespace/script/shell
. Example:
$ cat namespace/script/shell
/usr/bin/fish
##
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.