Libraries - Wick

August 4, 2017 · View on GitHub

The lib/ directory in a wick contains files that are intended to be sourced into Wick's environment. (See Bash concepts for sourcing.) Each file should contain only one function and the filename should match the function name.

Library functions are loaded really early in the execution order, so they must only use other library functions. They can not reference anything defined in formulas. They are also used by wick-infect to generate a file that can be sourced into shell scripts on the target machine after configuring.

Libraries from all parents are loaded before the children, allowing children to override functions.

Here is a sample lib/sample-thing:

#!/usr/bin/env bash
# This is a sample function.  It is documented with TomDoc style comments.
#
# Returns nothing because this is just a sample.
sampleThing() {
    echo "I am a sample function"
}

Not too bad. It also doesn't do much, but other functions exist and you can look at how they operate. There are helper functions that do argument processing. Other functions can manipulate strings (eg. trimming a string or removing all whitespace), generate random filenames, decompress an archive or even run multiple commands in parallel. Anything you can do in shell is possible in a library function.

wickArgumentString()

Public: Convert a string into a safely quoted string that's safe to pass around as an argument. It is unlikely that this will be necessary for most scripting that's performed because one would need to use eval to parse this back to the original value.

  • $1 - Name of the environment variable that should get the result.
  • $2-@ - The value to escape.

Examples

wickArgumentString exampleResult one two three "four five"
set | fgrep "exampleResult"
# Output:
# exampleResult='one two three four\ five'

Returns nothing.

wickArrayFilter()

Public: Run a list of values through a filter. When the filter function returns an error, remove that element from the list.

  • $1 - Variable name where the final list will be placed.
  • $2 - Function or command to run. Receives one argument that is a single item's value from the list.
  • $3-@ - The elements in the list.

Examples

removeAnimals() {
    case \$1 in
        dog|cat|cow|moose)
            # Return failure and the elements will be removed
            return 1
            ;;
    esac

    return 0
}

words=(a dog and a cat chased a cow)
wickArrayFilter filtered removeAnimals "${words[@]}"

# Prints "a and a chased a"
# The hyphen is intentional so you can print the array when there
# are no elements in the array and when `set -u` is enabled.
echo "${filtered[@]-}"

That last line looks weird but it is to help you work with strict mode. There are other coding techniques that should be kept in mind; see strict mode for a full explanation.

wickArrayJoin()

Public: Combine an array of strings together into one string.

  • $1 - Name of variable that should get the result.
  • $2 - String to use for joining elements in the array.
  • $3-@ - Array values.

Examples

arr=(one two "three four" five)
wickArrayJoin dest "+" "${arr[@]}"

# Result: one+two+three four+five
echo "$dest"

Returns nothing.

wickArraySearch()

Public: Check if a value is in an array. Returns the index of the value if found. If it is not in the array, this function returns an error.

  • $1 - Destination value for storing the index that is found.
  • $2 - The value to seek in the array.
  • $3-@ - Array elements.

The destination variable is only set when the element is found.

Examples

local index list

list=(one two three "four four")

if wickArraySearch index one "${list[@]}"; then
    # This one works
    echo "one is found at index $index"
fi

if wickArraySearch index four "${list[@]}"; then
    echo "four should not be found"
    echo "'four four' with a space would be found"
    echo "\$index will not be updated"
fi

Returns success if $1 is found in the list of other arguments.

wickCommandExists()

Public: Tests to see if a command is in the path. Because some systems report failure in different ways, this is an attempt to abstract away those alternate error messages and provide a consistent interface.

This differs from using hash in that hash will return success if there is a function with the given name. Using which will return an error.

  • $1 - Name of the command you wish to execute. It would not make sense to use a full path here since the intent is to search the PATH for the command.

Examples

if ! wickCommandExists ls; then
    echo 'Oh no, ls does not exist!'
    echo 'How do you list your files?'

    exit 1
fi

# Show the difference between hash and which
hash wickCommandExists || echo "hash succeeds with function names"
wickCommandExists wickCommandExists || echo "this will fail with functions"

Returns 0 when found and an error status when the command was not found.

wickDebug()

Public: This logging function helps diagnose errors or provide additional details about what's happening in your scripts. Use this to log every action that is performed. Debug output can be enabled by setting the DEBUG environment variable. When enabled, debug output is written to stderr and are also passed to wickLog so they could be written to a log file. (See Bash concepts for more about stderr.) This also will colorize the output when the WICK_COLOR environment variable is set to a non-empty string.

  • $@ - The message to log. Arguments are joined into one line.
  • $DEBUG - Controls if logging is enabled and where it's enabled.
  • $WICK_COLOR - When set to non-empty string, ANSI color is enabled.

When the DEBUG environment variable is not set, debug is disabled. If DEBUG is set to *, all or true, this always logs. In all other cases, DEBUG is assumed to be either a name of a function or a list of function names that should have debug logging enabled. When FUNCNAME lists one of these functions, logging will happen. This means you can turn on logging for one function and logging is enabled for that function and for all code that the function will execute, so child functions will also write debug output.

Writing to stderr is intentional. This way you can enable debugging and still get valid output captured.

Examples

# Forcibly disable all logging
DEBUG= ./thing

# Enable all debugging
DEBUG=true ./thing
DEBUG="*" ./thing
DEBUG=all ./thing

# Enable logging for a function named "doMagic"
DEBUG=doMagic ./thing

# Enable logging for the functions "rainbow", "pony" and "unicorn"
DEBUG="rainbow pony unicorn" ./thing

# Enable debug logging for all of these commands
export DEBUG=true
./thing1
./thing2

# Enable debug output inside of a Wick formula
DEBUG=true
wickMakeFile config.ini /opt/my-app/

Returns nothing.

wickDebugExtreme()

Public: Turn on extreme logging. This will alert you to all commands that run. When a command returns a non-zero exit code, that also will get reported.

  • $1 - When this is "stop" it disables the extreme logging.

Examples

# This script may have errors, so turn on debugging
wickDebugExtreme

if ! grep -q needle haystack.txt; then
    echo "No needle in haystack.txt"
fi

# Stop the extreme logging
wickDebugExtreme stop

Returns nothing.

wickError()

Public: Logging function for errors. Use this to log error messages right before you exit the program or return a failure. All messages are written to stderr. Log messages are also passed to the wickLog function to be logged to a file. (See wickLog for information about log files. See Bash concepts for more about stderr.) This also will colorize the output when the WICK_COLOR environment variable is set to a non-empty string.

  • $@ - The message to log. Arguments are joined into one line.
  • $WICK_COLOR - Colorizes output when set to a non-empty string.

Examples

 wickError "Could not find some vital thing"

 exit 1

Returns nothing.

wickGetArgument()

Public: This retrieves a single argument from the list of arguments. It's similar to wickGetOption. This returns non-options that were passed to a function.

  • $1 - Name of the variable that should get the result.
  • $2 - Index of the non-option argument, starting with 0.
  • $3-@ - Command line arguments to parse. Typically you use "\$0" here.

Significantly better examples are explained in argument processing.

Examples

# Get the first non-option and place it into $name
# If the argument does not exist, sets $name to ""
wickGetArgument name 0 "$@"

Returns nothing.

wickGetArguments()

Public: Grab all non-option arguments and return them as an array.

  • $1 - Name of variable that should receive the result.
  • $2-@ - Command line arguments to parse. Typically this is "$@".

Significantly better examples are explained in argument processing.

Examples

# Any non-option arguments are placed into $arguments.
# If there were none, $arguments is set to an empty list.
wickGetArguments arguments "$@"

Returns nothing.

wickGetDefaultIface()

Public: Determines the IP address used by default for outgoing traffic that is destined for the internet.

  • $1 - Name of the variable that should receive the result.

Examples

wickGetDefaultIface iface
echo "$iface"

Returns 0 on success, non-zero on failure.

wickGetDest()

Public: Converts a destination (either a filename or a folder) into a standard format.

Below are possible destinations, organized by how they are handled. "unknown" means that there's no directory nor file with that name so it is impossible to tell if the destination should be a new directory or file.

(A) /dir/that/exists/
(A) /dir/missing/
(A) /missing/dir/
(B) /dir/that/exists
(C) /dir/unknown
(C) /unknown/some-name

A = Treat as a directory, append optional filename and be done
B = Add a slash then treat as (A)
C = Treat as a filename and do not append the optional filename parameter

The returned value will always have a "/" at the end if it signifies a directory and if the optional filename was not specified.

  • $1 - Variable for the resolved path.
  • $2 - Destination to resolve.
  • $3 - Optional filename as a target.

Examples:

# Anything ending in a slash is a folder.
#
# Result: $out is "/etc/server.config"
wickGetDest $out /etc/ server.config

# Result: $out is "/etc/"
wickGetDest $out /etc/

# When \$2 has no slash and it exists on the filesystem
# as a folder, this operates the same as above.  Note that a slash
# is added to the end for directories.
#
# Result: $out is "/etc/server.config"
wickGetDest $out /etc server.config

# Result: $out is "/etc/"
wickGetDest $out /etc

# When \$2 has no slash at the end and does not exist as a
# directory, it is assumed to be a destination file.
#
# Result: $out is "/some/thing/here"
wickGetDest $out /some/thing/here server.config

# Result: $out is "/some/thing/here"
wickGetDest $out /some/thing/here

Returns true if the name was resolved, non-zero on any error.

wickGetIfaceForIp()

Public: Looks up the IP address route and provides the network interface that would handle traffic for that destination.

  • $1 - Name of the variable that should receive the result.
  • $2 - The IP address to route.

Examples

# Find the default network device, RFC 7200
wickGetIfaceForIp iface 192.0.0.8
echo "Network interface: $iface"

Returns 0 on success, non-zero on failure. When there is a failure, the destination variable is not altered.

wickGetIfaceIp()

Public: Determines the IP address associated with a given network interface. If no interface is provided, this returns the IP address of the interface for the default route. If that does not work, the first IP address is returned, picked arbitrarily.

  • $1 - Name of the variable that should receive the result.
  • $2 - Optional, network interface name. If not specified, defaults to the first one returned by ifconfig.

Examples

if ! wickGetIfaceIp ip tun0; then
    echo "Tunnel is not yet established"

    exit 1
fi

echo "Tunnel IP:  $ip"

Returns 0 on success, non-zero on failure.

wickGetIps()

Public: Returns all IP addresses associated with the machine.

  • $1 - Name of the variable that should receive the array of IP addresses.

The order of IP addresses in the list is random.

Examples

wickGetIps allIps
set | grep ^allIps= # Displays all IP addresses

Returns 0 on success, including when no IP addresses are found. Returns non-zero on failure.

wickGetOption()

Public: Retrieve a named option from the list of arguments.

  • $1 - Name of variable where the value will be stored.
  • $2 - Option's name. May have leading hyphens (eg. --option-name and option-name are both valid).
  • $3-@ - Arguments to parse. Typically this is "$@".

This splits up single-hyphen options. Significantly better examples are explained in argument processing.

Examples

# If --verbose=XYZ is passed, $verboseOption will be set to "XYZ".
# If --verbose= is passed (empty vallue), $verboseOption is set to "".
# If --verbose is passed without a value, $verboseOption is set to "true".
# If --verbose is not passed at all, $verboseOption is set to "".
wickGetOption verboseOption verbose "$@"

# Split single-hyphen options:
# Assuming that -abc is passed in ...
wickGetOption letterC c "$@"
wickGetOption letterD d "$@"
echo "$letterC" # Echos "true"
echo "$letterD" # Echos nothing

Returns nothing.

wickGetOptions()

Public: Get all options from a list of arguments and return a list without any processing.

  • $1 - Name of variable where the array will be stored.
  • $2-@ - Arguments that are to be parsed. Typically this is "$@".

This does not split up single-hyphen options. Significantly better examples are explained in argument processing.

Examples:

wickGetOptions opts "$@"
# opts is now a list of all options that were passed to the script

Returns nothing.

wickGetUrl()

Public: Download a URL, writing it to stdout or optionally saving it to a file. Uses curl or wget if installed on the system.

  • $1 - URL to download.
  • $2 - Optional, filename to write with the resulting content. When not specified, this writes to stdout.
  • --progress - Show progress information during downloads.
  • --timeout=SECONDS - Give up after this many seconds elapse.
  • --username=USER - Username to use for request. Adds the --user "USER:PASS" option with curl.
  • --password=PASS - Password to use for request.

Examples

# Download shell script execute it
wickGetUrl --timeout=30 https://get.rvm.io/ | bash

# Download a large file and show progress information
wickGetUrl --progress http://example.com/file.tar.gz /tmp/file.tgz

Returns zero on success, non-zero if there is an error downloading.

wickGetUrlCurl()

Internal: Download a file with curl.

  • $DEST - Destination filename or empty string for stdout.
  • $PROGRESS - Show progress bar when not an empty string.
  • $TIMEOUT - Limit the command to run for the given number of seconds when this is not an empty string.
  • $URL - The URL to download.
  • $USERNAME - Username to use for authentication.
  • $PASSWORD - Password to use for authentication.

Returns non-zero on error.

wickGetUrlWget()

Internal: Download a file with curl.

  • $DEST - Destination filename or empty string for stdout.
  • $PROGRESS - Show progress bar when not an empty string.
  • $TIMEOUT - Limit the command to run for the given number of seconds when this is not an empty string.
  • $URL - The URL to download.
  • $USERNAME - Username to use for authentication.
  • $PASSWORD - Password to use for authentication.

Returns non-zero on error.

wickHash()

Hash a file and put its checksum into the destination variable.

  • $1 - Destination variable name.
  • $2 - Filename to hash.

Currently this generates an MD5 checksum of a file, though the hashing mechanism can change at any time. Only rely on the output from wickHash to detect when files change as opposed to determining if the contents are correct. It is entirely possible, for instance, that this hash will include the last modified time or file size as well.

Examples

wickHash hash /etc/passwd
echo "$hash"

Returns nothing.

wickHex()

Public: Encode a string as hex.

  • $1 - Destination variable for the hex encoded version of the data.
  • $2 - The string to encode as hex.

Returns nothing.

wickInArray()

Public: Check if a value is in an array. Returns success if it exists, failure otherwise.

  • $1 - The value to seek in the array.
  • $2-@ - Array elements.

Examples

# This example may look complex, but that is because of how newer versions
# of Bash ignore the error exit in specific contexts.
local list result

list=(one two three "four four")
wickStrictRun result wickInArray one "${list[@]}"

if [[ "$result" -eq 0 ]]; then
    # This one works
    echo "one is found"
fi

wickStrictRun result wickInArray four "${list[@]}"

if [[ "$result" -eq 0 ]]; then
    echo "four should not be found"
    echo "'four four' with a space would be found"
fi

Returns success if $1 is found in the list of other arguments.

wickIndirect()

Public: Set a variable in a parent environment to a single string. This is the mechanism that other functions use to return data to the calling function because Bash has no cleaner way to return a string.

  • $1 - Name of variable that should get the result.
  • $2 - Value to assign.

Examples

# This function just lists 5 files/directories in /tmp
# and stores the string result in the desired variable.
tempFiles() {
    local value

    value=$(ls /tmp | head -n 5)
    local "\$1" && wickIndirect "\$1" "$value"
}

files=""
tempFiles files
echo "$files"

# Example result:
# a_9789
# config-err-BhlXXp
# d20150217-10973-riqke0/
# d20150217-11044-6yj33c/
# d20150217-11571-14rt04k/

Returns nothing.

wickIndirectArray()

Public: Sets a variable in the parent environment to an array. When you need to return multiple values from a function, this is just like wickIndirect, but is a form made for only arrays.

  • $1 - Name of variable that should get the result.
  • $2-@ - Array elements.

If you have a sparse array then the array indices will be reordered. With associative arrays you will lose the keys.

Examples

# This function just lists 5 files/directories in /tmp
# and stores the array result in the desired variable.
tempFilesArray() {
    local file value

    value=()

    for file in /tmp; do
        [[ "${#value[@]}" -lt 5 ]] && value[${#value[@]}]=$file
    done

    # Be careful when `set -u` is enabled.  This next line is EXTREMELY
    # IMPORTANT to get right.  The variable at the end is intentionally
    # not quoted so this works with empty arrays and arrays that contain
    # empty strings.
    # shellcheck disable=SC2068
    local "\$1" && wickIndirectArray "\$1" ${value[@]+"${value[@]}"}
}

files=""
tempFilesAarray files
echo "Number of files returned:  ${#files}"

if [[ ${#files} -gt 0 ]]; then
    echo "File #1:  ${files[0]}"
fi

# Example result:
# Number of files returned:  5
# File #1:  a_9789

There's some interesting syntax in that example, such as using value[${#value[@]}]=$file to append to the array. This is so the code does not fail, as it will run in strict mode by default.

Returns nothing.

wickInfo()

Public: Writes an informational message to the logging system. Informational messages are written to stdout unless $WICK_LOG_QUIET is set to a non-empty value. The message is also sent to wickLog for possible output to a log file. See wickLog for information regarding logfiles. When $WICK_COLOR is set to a non-empty string, the output is also colorized. (Bash concepts explains stdout.)

  • $@ - The message to write. Arguments are joined into one line.
  • $WICK_COLOR - When set to a non-empty value, the output is colorized.
  • $WICK_LOG_QUIET - When set to a non-empty value, the output is suppressed.

Returns nothing.

wickIsVarSet()

Determine if a variable is assigned, even if it is assigned an empty value.

$1 = Variable name to check

Examples

unset missing
empty=""

if wickIsVarSet missing; then
    echo "This never is called."
fi

if wickIsVarSet empty; then
    echo "This is called even though $empty is set to an empty string."
fi

Returns true if the variable is set, 1 if the variable is unset.

wickKvGet()

Public: Retrieve a value from the KV store.

  • $1 - Destination variable name.
  • $2 - Key to retrieve.

Examples:

wickKvSet test.message "Hi"
wickKvGet MSG test.message
echo "$MSG"  # "Hi"

Returns true on success, 1 if the KV store is not initialized. Does not error if the key is not set.

wickKvInit()

Internal: Creates the KV store and sets the necessary environment variable to the directory where the KV store is located.

Examples

wickKvInit

Returns nothing.

wickKvIsReady()

Internal: Checks if the KV store has been initialized.

Examples

# Starting with this not ready. Prints "Not ready."
wickKvIsReady || echo "Not ready."

# Initialize
wickKvInit

# Now this is ready. Prints "Ready."
wickKvIsReady && echo "Ready."

Returns true if it is ready, anything else if it is not ready.

wickKvIsSet()

Public: Check if a key is set in the KV store.

  • $1 - Name of the key.

Examples:

# Start with it not set. This prints "Not set."
wickKvIsSet test.message || echo "Not set."

# Assign a value
wickKvSet test.message "Hi"

# Now it exists. Prints "Key is assigned a value."
wickKvIsSet test.message && echo "Key is assigned a value."

Returns true when the key exists, false otherwise.

wickKvSet()

Public: Assigns a value to the KV store.

  • $1 - Key to set.
  • $2 - Value to save.

Examples:

wickKvSet test.message "Hi"
wickKvGet MSG test.message
echo "$MSG"  # "Hi"

Returns true on success, 1 if the KV store is not initialized.

wickLog()

Internal: Write a log message to a file or to a logging system. Used by wickDebug, wickInfo, wickWarn and wickError. Should not be used directly.

  • $1 - Log level. One of ERROR WARN INFO DEBUG.
  • $2 - The message to write.
  • $WICK_LOGFILE - Destination for log messages.

Uses the environment variable $WICK_LOGFILE to determine where to log messages. Will write a timestamped messsage to that file, creating the file if necessary. If $WICK_LOGFILE starts with "syslog:", this will use the logger command to write a line to syslogd. The facility defaults to user, but you can use any facility; for example you can use the mail facility by setting WICK_LOGFILE="syslog:mail". Additionally you can set a the tag by adding a second ":" followed by the desired tag; for example you can set the tag to myapp by setting `WICK_LOGFILE="syslog::myapp".

Otherwise, $WICK_LOGFILE should be set to a filename. Messages are prefixed with a timestamp.

Examples

# Run a command that logs with other Wick functions.
# Send output to /var/log/messages
WICK_LOGFILE=/var/log/messages ./your-command

# Send messages to /var/log/syslog
WICK_LOGFILE=syslog:daemon ./my-background-thing

Returns nothing.

wickOnExit()

Public: Run a command when the currently executing script or subshell ends.

  • $1 - The command to execute.
  • $2-@ - Optional arguments to pass to the command.

Examples

 # Download a file
 wickGetUrl http://example.com/installer.tar.gz /tmp/installer.tar.gz

 # When done, clean it up
 wickOnExit rm -f /tmp/installer.tar.gz

Returns nothing.

wickOnExitTrap()

Internal: Function that runs the wickOnExit commands. This is set as the EXIT trap in Bash to ensure that it always executes at the end of a script or subshell.

  • $1 - Status code from last command

Examples

wickOnExitTrap 1

Returns nothing.

wickPortUp()

Public: Determines if a port is open or not. Works with TCP and UDP ports. If the port is open this returns an error code of 0. If the port is not open it returns 1. If there are any errors this returns 2 and writes an error message to stderr. (See Bash concepts for error codes and stderr.)

  • $1 - Protocol. One of TCP, tcp, UDP, udp.
  • $2 - Port number to test.

Examples

# Confirm a web server is listening.
if wickPortUp TCP 80; then
    echo "There is no web server listening on port 80."
fi

# Wait for a server to start
wickService start my-web-server

if ! wickWaitFor 120 wickPortUp TCP 80; then
    echo "Tried to wait for 2 minutes but nothing listened on port 80"

    exit 1
fi

Returns true on success and non-zero for any failure.

wickPrefixLines()

Public: Prepend a string before each line in a variable. Also converts all newlines to Unix-style newlines in case they weren't that way before.

  • $1 - Name of variable where the result will be stored.
  • $2 - Prefix string to add to the beginning of all lines.
  • $3 - The original string.

Example:

printf -v lines "one\ntwo\n"
# lines is three lines with nothing on the third line.

wickPrefixLines result "Look:  " "$lines"
# Result is "Look:  one\nLook:  two\nLook:  "
# Even the last line is (intentionally) prefixed.

Returns nothing.

wickRandomString()

Public: Generates a random alphanumeric string.

  • $1 - Name of variable that will receive the random string.
  • $2 - Integer length of the string to create.
  • $3 - Optional, set of characters for string generation. Defaults to all lowercase, uppercase and digits.

Examples

# Generate a random directory name
wickRandomString dirname 16
mkdir /tmp/$dirname

# Create a hex byte
wickRandomString hex 2 0123456789ABCDEF
echo "Hex byte: $hex"

Returns nothing.

wickSafeVariableName()

Public: Change a variable so it is a valid variable in bash. This is used primarily by argument parsing functions.

  • $1 - Name of variable that should receive the altered string.
  • $2 - The string we are making safe.

You can't set things like $ABC-DEF as a variable easily and so this function will turn that into something that could be an environment variable by replacing illegal characters with an underscore.

Examples

wickSafeVariableName fixed "ABC-DEF"
echo "$fixed"  # Outputs "ABC_DEF"

Returns nothing.

wickSetConfigLine()

Adds or updates a line in a config file. This is a very basic tool that ensures a line exists in a file, not that it is in any particular order.

  • $1 - File to update.
  • $2 - The full line to add. It will be placed at the end. If this is an empty string, no blank line will be added.
  • $3 - Optional; the key that we are setting. Defaults to a portion of $2.

The line is not repeatedly added to the config file. First, we attempt to get the "key" for the line, either automatically or use a value that is passed in. Most config files use a key value of some sort and this script usually can detect them - more on this later. Next, we remove any lines with the same key from the file and finally we append the line you want onto the file.

The key can be automatically detected. It is anything in LINE that is to the left of a space, colon, or equals. If you are having difficulty understanding what is used as the key, check out the examples. I have listed what wickSetConfigLine uses as the key.

This is not suitable for updating shell scripts and other similar-looking files. For instance, if you tried to modify /etc/rc.local and add two lines (bash /script1 and bash /script2) then only one would ever exist in the file because "bash" would be considered the key. That's why you can specify the key as $3. However, even specifying the key does not solve all problems because typically /etc/rc.local will have exit as the last line by default, thus your newly added lines will be after the exit command and would never be executed.

Example:

# This wipes out any previous settings for 127.0.0.1 and replaces them.
# Key is "127.0.0.1"
wickSetConfigLine /etc/hosts "127.0.0.1 localhost some-funky-name"

# Set the bind IP for mongo
# Detects the current IP using `wickGetIfaceIp`
# Key is "bind_ip"
wickSetConfigLine /etc/mongod.conf "bind_ip=$(wickGetIfaceIp)"

# Update cloud-init
# Key is "preserve_hostname"
wickSetConfigLine /etc/cloud/cloud.cfg "preserve_hostname: true"

# Update DHCP settings
# Key is "prepend nameservers" because we specify it as a third
# parameter.
wickSetConfigLine /etc/dhcp/dhclient.conf \
    "prepend nameservers 127.0.0.1" "prepend nameservers"

# Removes a line from the Redis config for the master password
wickSetConfigLine /etc/redis.conf "" "masterPassword"

Returns nothing.

wickShowUsage()

Shows a help message that is generated from the contents of a file. For this to work you must format your help message in the shell script (or I suppose embed this comment in other languages) using special markup.

  • $1 - The name of the file to parse for a usage message.

Examples

#!/usr/bin/env bash
#/ This is my special help text.
#/
#/ Usage:  the_command [ACTION]
#/
#/ ACTION - The action you want to perform (run, walk, or help)

# Load the library of tools
/usr/local/lib/wick-infect

case "\$1" in
    run)
        echo "Running"
        ;;
    walk)
        echo "Walking"
        ;;
    help)
        Show the help embedded inside this script
        wickShowUsage "\$0"
        ;;
    *)
        echo "Use '${0##*/} help' for usage information"
        ;;
esac

Returns nothing.

wickStrictMode()

Enables strict mode for Bash, based off unofficial bash strict mode. Errors will kill the program. Accessing undefined variables will cause errors (and exit the program). Commands in pipelines that return a non-zero status code will also cause errors and kill the program. An ERR trap is also enabled that will produce a stack trace when errors happen.

This is intended to be used at the beginning of your shell scripts in order to ensure correctness in your programming.

Please check out the detailed explanation of strict mode for further information.

Examples

#!/usr/bin/env bash
. /usr/local/lib/wick-infect
wickStrictMode

Returns nothing.

wickStrictModeFail()

Internal: The ERR trap calls this function to report on the error location right before dying. See wickStrictMode for further details.

  • $1 - Status from failed command.

Returns nothing.

wickStrictRun()

Public: Runs a command and captures its return code, even when strict mode is enabled. The variable name you specify is set to the return code of the command.

  • $1 - Name of variable that should get the return value / status code.
  • $2-@ - Command and arguments to run.

This is intended to be used along with wickStrictMode. It helps counter the newer Bash behavior where the error exit flag is suppressed in specific contexts. See the error context documentation for further info.

Execution of a command happens in a subshell. If you are running a function with wickStrictRun, please keep in mind that it can not export values to the parent context for use by the caller.

Example:

#!/usr/bin/env bash
. /usr/local/lib/wick-infect
wickStrictMode

wickStrictRun result grep "some-string" /etc/some-file.cfg > /dev/null 2>&1

if [[ "$result" -eq 0 ]]; then
    wickInfo "some-string was found"
else
    wickInfo "some-string was not found"
fi

Returns nothing.

wickStringSplit()

Split a string into an array

  • $1 - Destination variable name
  • $2 - The string to split
  • $3 - Optional, the delimeter, defaults to a space

Examples:

wickStringSplit DEST "a b c d"
set | grep ^DEST=
# DEST=([0]="a" [1]="b" [2]="c" [3]="d")

wickStringSplit DEST "one|two||three|" "|"
set | grep ^DEST=
# DEST=([0]="one" [1]="two" [2]="" [3]="three" [4]="")

Returns nothing.

wickTempDir()

Public: Creates a temporary directory. Automatically sets up a hook with wickOnExit to delete the directory when the shell script is finished.

  • $1 - Name of variable that will receive the path of the newly created temporary directory.

Examples

wickTempDir tempdir
(
    cd tempdir
    wickGetUrl http://install.example.com/ installer-script
    . installer-script
)

# Directory is automatically removed for you after this script.

Returns nothing.

wickTestForArgumentCount()

Test to ensure there are a sufficient number of non-empty arguments.

  • $1 - Number of elements that should be in $2.
  • $2-@ - Command line arguments to parse. Typically this is "$@".

Examples

# Ensure the $@ has 4 non-empty arguments.
wickTestForArgumentCount 4 "$@"

# This would fail the test and return 1.
array=(first "" third)
wickTestForArgumentCount 3 "${array[@]}"

Returns zero if arguments are present and have non-empty values, one if not.

wickTestForOptions()

Public: Guarantee that some options are passed to a script. This is typically used within a role or a formula's run or depends script. The error reporter can be overridden so the library function can be used in external scripts as well. If any options are missing, this function returns an error status code. (See Bash concepts for status codes.)

  • $1-x - Required option names.
  • -- - A double hyphen separates the required options from the argument list.
  • y@Thelistofarguments.Typicallythisis"y-@ - The list of arguments. Typically this is `"@"`.
  • $WICK_TEST_FOR_OPTIONS_FAILURE - Error reporting function or command. When not set, defaults to "wickTestForOptionsFailure".

When arguments are missing from the list, the error program ($WICK_TEST_FOR_OPTIONS_FAILURE) is called. You may include or omit the hyphens before the required option names.

To change the error reporting function, set $WICK_TEST_FOR_OPTIONS_FAILURE to the command that should be executed. The one and only parameter to this will an option that is required but is not specified.

Examples

# Test to make sure this function or file received both
# "access-key" and "secret-key"
wickTestForOptions access-key secret-key -- "$@"

# Same as above - you may specify hyphens before options.
# Either "--" or "" can be used to mark the end of the required options.
wickTestForOptions --access-key --secret-key "" "$@"

# Use your own reporter and make sure that both --mom and --dad are set.
# This would get called once for each option that is missing.
missingOption() {
    echo "Hey, you need to specify --\$1 as an argument"
}
WICK_TEST_FOR_OPTIONS_FAILURE=missingOption \
    wickTestForOptions mom dad -- "$@"

Returns zero if options exist, one if they do not.

wickTestForOptionsFailure()

Internal: Default failure reporter function for wickTestForOptions.

  • $1 - Option that was not specified. This is without the double-hyphens.
  • $FORMULA - Set by wick when running a formula.
  • $ROLE - Set by wick when running a role.

Returns nothing.

wickWaitFor()

Public: Wait a given amount of time for a shell script to return success, waiting 1 second between calls. If the time elapses without any successful response then this returns failure. Otherwise this returns success.

  • $1 - Timeout, specified in seconds.
  • $2-@ - The command and arguments to pass to the command.

If the timeout is reached, the running command is killed and wickWaitFor will return failure. Otherwise, wickWaitFor will return the status code from the command.

The command is executed directly. Bash built-ins will not work. Instead of using [[, you must resort to test, [ or writing a function that can be called.

Examples

/usr/local/bin/some-other-process &

# Wait up to 30 seconds for a file to exist
# You must use a shell command here.  Bash built-ins won't work,
# so use `[` or `test` instead of `[[`.
if ! wickWaitFor 30 [ -f /var/run/other-process/run.lock ]; then
    echo "File was not created in time"

    exit 1
fi

Returns the command's status code. The command may have been killed and will typically return failure in that scenario.

wickWarn()

Public: Logging for warnings, such as when problems are detected and they aren't severe enough to warrant an error and termination of the function. Warning messages are always written to stderr. See wickLog for information about writing log messages to files. This also colorizes the output when $WICK_COLOR is set to a non-empty value. (See Bash concepts regarding stderr.)

  • $@ - The text to log. Arguments are appended into a single line.
  • $WICK_COLOR - When set to a non-empty string the output is colorized.

Examples

if [[ -f /some/file ]]; then
    wickWarn "File exists when it should not."
    rm /some/file
fi

Returns nothing.