How Sysunit Works
Some of the things Sysunit does will the shell units can seem a bit magical, but there's only a couple of tricks going on here to get them to work.
Shell Execution
Sysunit simply pipes your scripts into a shell interpreter, potentially over some intermediary like SSH or Docker, runs its hooks, and parses its output to separate emitted values from regular output.
Execution of hooks from units is interleaved, since, for example, dependencies
need to be retrieved from a unit (which requires executing its deps hook)
before its check
and apply
or remove
hooks are run. These hooks are all
run in subshells, so they don't affect the environment of other units.
Hooks
The shell environment hooks run in is set up with someting like this:
exec 2>&1
meta() : ;
deps() : ;
check() : ;
apply() : ;
rollback() : ;
dep() _emit dep $@;
author() _emit meta.author $@;
desc() _emit meta.desc $@;
params() _emit meta.params $@;
emits() _ emit meta.emits $@;
present() _emit present true;
emit_value() {
local key="${1:?key must be provided to emit_value}"
shift
_emit "value.${key}" "$@"
}
_emit() {
local key=${1:?key must be provided to _emit}
shift
local val="$@";
printf "\n\001${key}\002${val}\003"
}
Beyond this, the subshells have set -e -u
(and maybe -x if you specify --debug) to
help catch errors, but there's really not much else going on for the execution of a single
hook.
Emits
Sysunit extracts various values from your units with emit messages, which are just bits of stdout output delimited with some non-printable characters so sysunit can differentiate it from diagnostic output that should be shown to the user.
These emit messages follow a format like this:
\001
<key>.<field>
\002
<message content>
\003
The whitespace is insignificant here. The key and field are used to indicate to Sysunit what kind of message is being emitted, and then the message content is handled appropriately to do things like set up dependencies or emit values to dependees.