diff --git a/README.md b/README.md index d399845..81bb2bd 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ runtime: /disco/NODE_NAME/current_module : This parameter defines the full name of the current module, such that a module definition file can access its personal parameters via without knowing its name, e.g.: - $(disco-param get /classes/$(disco-param get /current_module)/some/module/specific/path) + $(disco-param get $(hostname)/classes/$(disco-param get $(hostname)/current_module)/some/module/specific/path) How to deploy stuff ===== @@ -147,18 +147,26 @@ DISCO uses bash for a scripting and templating engine. Instead of writing a cust you specify operations (like Puppet did) or utilize a higher level language (like Chef did with ruby), DISCO just uses the proven bash shell. +Every time your module is executed (e.g. every time the disco client executes), all of the scripts +are executed. The order of execution is determined by alphabetically sorting the filenames, rc.d +style; so naming your scripts as 00-fix_perms.sh, 10-correct_nodes.sh, etc, will cause them to be +executed in the proper order. This prevents you from having to create a separate file that describes +the execution order. + Files vs Templates ===== Files and Templates are delivered exactly the same way - via rsync. Files are static files who are delivered on to the disk, and no more operations are done to them. +They are delivered with the same permissions that they are given by the rsync repository. Templates are bash scripts who are delivered on to the disk, and then they are executed, with their file contents replaced by their output. Templates are subject to all the same restrictions as scripts (be mindful of the constraints of $NOOP), and in addition, they are ALWAYS interpolated in the safe NOOP execution environment (file modifications will be discarded, and only rudimentary bash builtins -are enabled). Templates have access to all client parameters via the disco-param command. +are enabled). Templates have access to all client parameters via the disco-param command. Templates +will end up with the same permissions that rsync gives them. Definition Files ===== @@ -178,14 +186,12 @@ Module Layout A disco module (also called a "disco ball" for fun) looks like this: MODULE - ├__ defs - ___ ___ requires - │__ ├── scripts - │__ └── templates - ___ ___ parameters - ├── files - ├── scripts - └── templates + ___ requires + ___ parameters + ___ steps + ├── files/ + ├── scripts/ + └── templates/ Your module can theoretically pull files, scripts, and templates from any location that can be reached via rsync; however, it is generally considerd good form to include all things relevant @@ -195,23 +201,16 @@ relevant to its execution, and run them. ALL MODULE FILES, SCRIPTS, AND TEMPLATES ARE DELIVERED RELATIVE TO / ON THE CLIENT. -MODULE/defs/requires +MODULE/requires ===== This file lists, one name per line, the names of other modules that must be installed on this node in order for this module to install correctly. This is used to create a dependency graph, and thereby determine execution order. -MODULE/defs/scripts -===== +This file is optional. -This file simply lists the (local) location of commands to execute, for this module, once all scripts have -been fetched, and all templates have been interpolated. The scripts cannot accept arguments. They are -executed, in order. One script failing will not stop other scripts from failing unless told to do so in the -/MODULE_NAME/halt_on_failure parameter. Otherwise, errors are reported, but all scripts will be executed -regardless. - -MODULE/defs/parameters +MODULE/parameters ===== Each module can define default parameters which will be made available to all clients using the module. @@ -219,6 +218,8 @@ These parameters will be merged together on the client at module fetch time, and parameters will override any default parameters specified here (they are rsync'ed over the top of each other). These parameters will be rooted at /MODULE_NAME/... . +This tree is optional. + Server Side Setup ===== diff --git a/client/bin/disco-ball b/client/bin/disco-ball new file mode 100755 index 0000000..5b09ecc --- /dev/null +++ b/client/bin/disco-ball @@ -0,0 +1,15 @@ +#!/bin/bash + +function init() { + if [ "$1" == "" ]; then + echo "Must enter a path to initialize" >&2 + exit 1 + fi + mkdir -p $1/templates + mkdir -p $1/scripts + mkdir -p $1/files + touch $1/requires + mkdir -p $1/parameters/$(basename $1) +} + +$1 $2 \ No newline at end of file diff --git a/universe/bin/disco-param b/universe/bin/disco-param new file mode 100755 index 0000000..3e4f610 --- /dev/null +++ b/universe/bin/disco-param @@ -0,0 +1,101 @@ +#!/bin/bash + +PARAMROOT=/var/disco/parameters + +function check_names_regex() { + echo "$1" | grep -E '^[a-zA-Z0-9_-\./]*$' >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "error: Invalid characters in pathname '$1'. Valid pathname characters are [a-zA-Z0-9_-\.\/]*." >&2 + exit 1 + fi + echo "$1" | grep "^/" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "error: Pathnames cannot start with /" >&2 + exit 1 + fi +} + +function disco_delete() { + if [ "$1" == "--help" ] || [ "$1" == "" ]; then + echo "info: disco-param delete [PATH_NAME] [optarg]... deletes parameter trees (recursively, if optarg is '-r'). PATH_NAME must not be empty." >&2 + exit 1 + fi + check_names_regex "$1" || exit 1 + cd ${PARAMROOT} + if [ "$2" == "-r" ]; then + OUT=$(rm -rf ${1} 2>&1) + elif [ -d ${PARAMROOT}/${1} ]; then + # We want the error message from rmdir to have just the key path, not the param root in it + OUT=$(rmdir ${1} 2>&1) + else + OUT=$(rm -f ${1} 2>&1) + fi + if [ "$OUT" != "" ]; then + echo "error: $OUT" >&2 + exit 1 + fi + exit 0 +} + +function disco_set() { + if [ "$1" == "--help" ]; then + echo "info: disco-param set [PATH_NAME] [optarg] ... reads from stdin and sets PATH_NAME, unless [optarg] is {}, in which case a new hierarchy is made" >&2 + exit 1 + fi + check_names_regex "$1" || exit 1 + mkdir -p $(dirname ${PARAMROOT}/${1}) + if [ "$2" == "" ]; then + cat > ${PARAMROOT}/${1} + elif [ "$2" == "{}" ]; then + if [ -e ${PARAMROOT}/${1} ] && [ ! -d ${PARAMROOT}/${1} ]; then + echo "error: ${1} : existing type '$(stat -c '%F' ${PARAMROOT}/${1})' cannot be overriden with type 'directory'." >&2 + exit 1 + fi + mkdir -p ${PARAMROOT}/${1} + else + echo "error: Unknown optarg '$2'." >&2 + exit 1 + fi +} + +function disco_get() { + if [ "$1" == "--help" ]; then + echo "info: disco-param get [PATH_NAME]" >&2 + exit 1 + fi + VARPATH=$(echo $1 | sed s/"^\/*"//g) + check_names_regex "$VARPATH" || exit 1 + if [ -d ${PARAMROOT}/${VARPATH} ]; then + if [ "$RECURSING" == "" ]; then + echo "${VARPATH} = {}" + else + echo '{}' + fi + # Iterate over the path like a key/value dictionary + cd ${PARAMROOT}/${VARPATH} + for file in $(ls 2>/dev/null); + do + echo "${VARPATH}/${file} = $(RECURSING='true' disco_get ${VARPATH}/${file})" | sed s/"^\/*"//g + done + exit 0 + elif [ -e ${PARAMROOT}/${VARPATH} ]; then + cat ${PARAMROOT}/${VARPATH} + exit 0 + else + echo "error: ${VARPATH} is undefined." + exit 1 + fi +} + +if [ "$1" == "--help" ]; then + echo "info: disco-param [CMD] [OPTIONS] ... execute the given command with the given options, and return zero on success." + echo + $0 set --help + $0 get --help + $0 delete --help + exit 0 +fi + +disco_$1 "$2" "$3" "$4" "$5" "$6" +exit $? +# LocalWords: regex diff --git a/universe/bin/disco-param~ b/universe/bin/disco-param~ new file mode 100644 index 0000000..28258dc --- /dev/null +++ b/universe/bin/disco-param~ @@ -0,0 +1,26 @@ +#!/bin/bash + +PARAMROOT=/var/disco/parameters + +function set() +{ +} + +function get() +{ + if [ -d ${PARAMROOT}/${2} ]; then + echo '${PARAMROOT}/${2} = {}' + cd ${PARAMROOT}/${2} + for file in *; + do + FNAME=${PARAMROOT}/${2}/${file} + echo ${FNAME} = $(disco-param get ${FNAME}) + done + exit 0 + elif [ -f ${PARAMROOT}/${2} ] || [ -l ${PARAMROOT}/${2} ]; then + cat ${PARAMROOT}/${2} + exit 0 + else + exit 1 + fi +} \ No newline at end of file