diff options
author | Aaron Ball <nullspoon@oper.io> | 2016-12-11 23:51:40 -0700 |
---|---|---|
committer | Aaron Ball <nullspoon@oper.io> | 2016-12-11 23:51:40 -0700 |
commit | 533cff3fadcf3b4bf2e18fe011459620a2e3b757 (patch) | |
tree | 689f3b67524f66656220fc77e363d872b954ef19 /lib | |
download | portimg-533cff3fadcf3b4bf2e18fe011459620a2e3b757.tar.gz portimg-533cff3fadcf3b4bf2e18fe011459620a2e3b757.tar.xz |
Initial commit
Includes the GPLv3 license and headers.
This version builds a basic composite port filesystem overlay from the
specified manifest list. It also provides a very basic file templating
system using a pseudo jinja syntax for variables (no conditional support
yet). There is also a basic logging library included that outputs
standardized log output indirectoy to stdout (to prevent mucking with
function return vals).
Diffstat (limited to 'lib')
-rw-r--r-- | lib/config.sh | 181 | ||||
-rw-r--r-- | lib/log.sh | 73 | ||||
-rw-r--r-- | lib/pkg.sh | 84 | ||||
-rw-r--r-- | lib/port.sh | 46 | ||||
-rw-r--r-- | lib/template.sh | 46 |
5 files changed, 430 insertions, 0 deletions
diff --git a/lib/config.sh b/lib/config.sh new file mode 100644 index 0000000..afc186c --- /dev/null +++ b/lib/config.sh @@ -0,0 +1,181 @@ +#!/usr/bin/env bash +# +# Libconfig provides basic global configuration key/value support +# Copyright (C) 2016 Aaron Ball <nullspoon@oper.io> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# +# libconfig +# --------- +# Libconfig provides support for a global configuration associative array +# called 'configs'. +# +# This provides an easy way to store configuration data in a unified location. +# With this data in a predictable unified location, it can easily be dumped and +# reviewed. +# +# This also provides support to easily load single-dimensional delimited +# configuration files through the load_config_with_delim function. This will +# populate the global configs associative array with any key-value pairs that +# are found. +# + +set -u + +# Header guard +if [[ -z ${!configs[@]} && -z ${__configssetup:-} ]]; then + # Configs associative array is undefined + # Define it along with the global reconstruction string, currently empty. + declare -xA configs + declare -x __configssetup='' +elif [[ ! -z ${__configssetup:-} ]]; then + # Configs undefined, but reconstruction code was found + # Reconstruct using __configssetup + # NOTE: This is a terrible hack to work around the bug in bash that disallows + # passing of arrays and associative arrays to subshells. + eval ${__configssetup} +fi + + +# +# Parses the specified file, loading into the global configs associative array, +# key-value pairs seperated by the specified delimiter. Leading and trailing +# whitespace is removed to allow for human-friendly indentation. Comments are +# also supported where the line starts with the common # comment character. +# +# Examples: +# +# key = value +# key-two = value2 +# +# - or - +# +# key: value +# key-two: value2 +# +# @param file Path to file to parse and load +# @param delim Delimiter to use for seperating keys and values. +# +function load_config_with_delim { + [[ -z ${1} ]] && log error "File argument required." && exit 1 + [[ -z ${2} ]] && log error "Delimiter argument required." && exit 1 + + local file=${1} + local delim=${2} + + [[ ! -f ${file} ]] && log error "Could not find config '${file}'" && exit 1 + + local contents=$(cat ${file} | grep -v '^ *#' | grep -v '^[ ]*$' | sed 's/^ \+//') + + local oldifs=${IFS} + export IFS=$'\n' + for i in ${contents[@]}; do + local key=$(echo ${i} | cut -d "${delim}" -f 1 | sed -e 's/ \+$//' -e 's/["'\'']//g') + local val=$(echo ${i} | cut -d "${delim}" -f 2 | sed -e 's/^ \+//' -e 's/["'\'']//g') + configs[$key]="${val}" + done + export IFS=${oldifs} +} + + +# +# Dumps the global associative array to stdout. Output format is an +# alphabetically sorted, space-delimited, aligned table (uses the column command +# for alignment). Output is cumbersome for computers to parse, but easy to read +# for humans. Useful for debugging. +# +# NOTE: Uses the 'ø' character for column delimiting because it is a very rare +# character in most languages. If a config key or value contains this +# character, output may be mangled. +# +function dump_configs { + # Using something really strange to reduce the chance that we split on a + # charcter that exists in a key or value. + local delim='ø' + + # Set the table header + local header="Key${delim}Value\n---${delim}-----\n" + local out='' + + # configs needs to be a global associative array + for key in ${!configs[@]}; do + out="${out}${key}${delim}${configs[$key]}\n" + done + + # Sort the variables + out="$(echo -e ${out} | sort)" + + # Output + echo -e "\nEnvironment:\n" + echo -e "${header}${out}" | column -s ${delim} -t + echo +} + + +# +# Setter for the global configs data structure. +# +# NOTE: It is *highly* recommended to use this function to update values in the +# global configs associative array because it will regenerate the +# reconstruction string used for serializing the global assoc array for +# passing to subshells. +# +# @param key Key to set +# @param val Value to set +# +function config_set { + local key=${1:-} + local val=${2:-} + + [[ -z ${key} ]] && log error "Key argument (1) required." && exit 1 + + configs[$key]="${val}" + + # Update the reconstruction string so it has the new value + __configssetup=$(declare -p configs) +} + + +# +# Getter for the global configs data structure. This function provides a bit of +# added safety over the bash square bracket query, since this distinguishes +# between unset and empty and will error if requested key could not be found. +# +# @param key Key to get +# +function config_get { + local key=${1:-} + + [[ -z ${key} ]] && log error "Key argument (1) required." && return 1 + + # Iterrate over the keys and return if a matching key is found. + # + # This is less efficient than just querying for the key using the bash square + # bracket construct. However, this provides more flexibility in that if a + # variable is not set, it allows us to exit gracefully with a log message + # instead of just exiting. + # This also allows the code to distinguish between unset and empty. The bash + # square bracket functionality does not make this distrinction. + for i in ${!configs[@]}; do + if [[ ${i} == ${key} ]]; then + echo ${configs[$key]} + return 0 + fi + done + + log error "Config ${key} is not set." + return 1 +} diff --git a/lib/log.sh b/lib/log.sh new file mode 100644 index 0000000..26bc5bb --- /dev/null +++ b/lib/log.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# +# Liblog provides basic bash logging support +# Copyright (C) 2016 Aaron Ball <nullspoon@oper.io> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + + +# +# Liblog +# ------ +# This is useful for ensuring log output consistency. All log levels are +# timestamped. Error logs also include the name of the calling function that +# triggered the error message. +# +# NOTE: Logging outputs to file descriptor 9, which is redirected to STDOUT. +# This allows functions to log output, while still having an echoed return +# value without return value conflicts. +# + +# Open additional file descriptor for stdout logging that won't conflict with +# function return values +exec 9>&1 + + +# +# Standard logging function. Prints standardized output. Timestamps all +# messages. Error messages automatically print calling function name. +# +# @param level Log level to print (error, warn, debug, info, etc) +# @param msg Message to log. Escape sequences allowed. Captures all arguments +# following this param, so multiple messages will be appended. +# +function log { + local level=${1} + shift + local msg=${@} + + local d=$(date '+%F %T') + local label='' + + if [[ ${level} == 'error' ]]; then + label="${d} ERROR Function ${FUNCNAME[1]}: " + elif [[ ${level} == 'warn' ]]; then + label="${d} WARN " + elif [[ ${level} == 'debug' ]]; then + label="${d} DEBUG " + elif [[ ${level} == 'info' ]]; then + label="${d} INFO " + fi + + echo -e "${label}${msg}" >&9 +} + + +# Some aliases for easier logging +function lerror { log error ${@}; } +function lwarn { log warn ${@}; } +function ldebug { log debug ${@}; } +function linfo { log info ${@}; } +function lfatal { log fatal ${@}; } diff --git a/lib/pkg.sh b/lib/pkg.sh new file mode 100644 index 0000000..9c1a5df --- /dev/null +++ b/lib/pkg.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# +# Portimg uses Crux port-like system for creating software deployment images. +# Copyright (C) 2016 Aaron Ball <nullspoon@oper.io> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +source ${LIBDIR}/log.sh # Logging support +source ${LIBDIR}/template.sh # Included so each port can use templates +source ${LIBDIR}/port.sh # Basic port functions + +export PORTSDIR=${PORTSDIR:-/usr/ports} # Path to port store +export PORTTMP='' # Path to the port temp dir + +function pkg_download_src { + local pkgsrc=${1} + shift + local src=${@} + + # Download from Pkgfile src spec + for file in ${src[@]}; do + # If the source file exists locally, just copy from here + if [[ -f ${file} ]]; then + cp -p ${file} ${pkgsrc} + else + linfo "Downloading ${file}" + download_src ${file} ${pkgsrc} + linfo "Extracting $(basename ${file}) to ${pkgsrc}" + archive_extract ${pkgsrc}/$(basename ${file}) ${pkgsrc} + fi + done +} + + +# +# TODO: Describe this +# +function pkgmk { + local port=${1:-} + + local PKG='' # Path to the port pkg dir (binary install dest) + local PKGSRC='' # Path to the port src dir + + # Change context to port dir + cd ${PORTSDIR}/${port} + + # Include the port Pkgfile + source Pkgfile + + # Create the port build and install directory structure + PORTTMP=$(mktemp -d /tmp/pkgmk-${port}.XXXX) + PKGSRC=${PORTTMP}/src + PKG=${PORTTMP}/pkg + mkdir -p {${PKGSRC},${PKG}} + + # Download the package source files + pkg_download_src "${PKGSRC}" "${src[@]}" + + cd ${PKGSRC} + + # Call the build process if defined + if [[ $(type -t pkgbuild) == 'function' ]]; then + linfo "Running ${port} build process" + pkgbuild + return $? + else + lerror "Port ${port} has no pkgbuild function. Aborting." + return 1 + fi + + return 0 +} diff --git a/lib/port.sh b/lib/port.sh new file mode 100644 index 0000000..0797e57 --- /dev/null +++ b/lib/port.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# +# Portimg uses Crux port-like system for creating software deployment images. +# Copyright (C) 2016 Aaron Ball <nullspoon@oper.io> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +function archive_extract { + local src=${1:-} + local dest=${2:-} + + local ext=${src##*.} + + if [[ ${ext} == 'tar' + || ${ext} == 'gz' + || ${ext} == 'tgz' + || ${ext} == 'xz' + || ${ext} == 'bz2' ]]; then + tar -xf ${src} -C ${dest} + elif [[ ${ext} == 'zip' ]]; then + unzip ${src} -d ${dest} + fi +} + + +function download_src { + local uri=${1:-} + local destdir=${2:-} + + local localname=$(basename ${uri}) + + curl -L -s -k -o "${destdir}/${localname}" "${uri}" +} + diff --git a/lib/template.sh b/lib/template.sh new file mode 100644 index 0000000..f9fc825 --- /dev/null +++ b/lib/template.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# +# Portimg uses Crux port-like system for creating software deployment images. +# Copyright (C) 2016 Aaron Ball <nullspoon@oper.io> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +source ${LIBDIR}/config.sh + +# +# Provides *very* basic templating functionality. Interpolates basic jinja +# variable syntax ( "{{ variable }}" ). +# +# @param src Path to template file to interpolate +# @param dest Destination to copy the interpolated template to +# +function template { + local src=${1:-} + local dest=${2:-} + + local delim=$'\001' + local tmp=$(mktemp tmp.template.XXXXXXXX) + + cp -p ${src} ${tmp} + for key in ${!configs[@]}; do + sed -i "s${delim}{{ ${key} }}${delim}${configs[$key]}${delim}g" ${tmp} + done + + # Create parent directory if it doesn't yet exist + [[ ! -d $(dirname ${dest}) ]] && mkdir -p $(dirname ${dest}) + + # Move the modified version into place + mv ${tmp} ${dest} +} |