diff --git a/type/__single_binary_service/explorer/explorer-version b/type/__single_binary_service/explorer/explorer-version new file mode 100755 index 0000000..690cc5f --- /dev/null +++ b/type/__single_binary_service/explorer/explorer-version @@ -0,0 +1,10 @@ +#!/bin/sh -e + +BIN_PREFIX="/usr/local/bin" +SERVICE_NAME="${__object_id}" + +VERSION_FILE="${BIN_PREFIX}/.${SERVICE_NAME}.cdist.version" + +if [ -f "${VERSION_FILE}" ]; then + cat "${VERSION_FILE}" +fi diff --git a/type/__single_binary_service/man.rst b/type/__single_binary_service/man.rst new file mode 100644 index 0000000..cb40330 --- /dev/null +++ b/type/__single_binary_service/man.rst @@ -0,0 +1,190 @@ +cdist-type__evilham_single_binary_service(7) +============================================ + +NAME +---- +cdist-type__evilham_single_binary_service - Setup a single-binary service + + +DESCRIPTION +----------- +This type is designed to easily deploy and configure a single-binary service +named `${__object_id}`. + +A good example of this are Prometheus exporters. + +This type makes certain assumptions that might not be correct on your system. +If you need more flexibility, please get in touch and provide a use-case +(and hopefully a backwards-compatible patch). + +This type will place the downloaded binary and, if requested, other extra +binaries in `/usr/local/bin`. + +If a `--config-file-source` is provided, it will be placed under: +`/etc/${__object_id}.conf`. + +This type supports services managed by `__runit(7)` when `systemd` is not +the init system being used. + + +REQUIRED PARAMETERS +------------------- +checksum + This will be passed verbatim to `__download(7)`. + Use something like `sha256:...`. + +url + This will be passed verbatim to `__download(7)`. + +version + This type will use a thumbstone file with a "version" number to track + whether or not a service must be updated. + This thumbstone file is placed under + `/usr/local/bin/.${__object_id}.cdist.version`. + + +BOOLEAN PARAMETERS +------------------ +unpack + If present, the contents of `--url` will be treated as an archive to be + unpacked with `__unpack(7)`. + See also `--unpack-args` and `--extra-binary`. + +do-not-manage-user + Always considered present when `--user` is `root`. + If present, the user in `--user` will not be managed by this type with + `__user`, this means it *must* exist beforehand when installing the service + and it will not be removed by this type. + + +OPTIONAL PARAMETERS +------------------- +config-file-source + If present, this file's contents will be placed under + `/etc/${__object_id}.conf` with permissions `0440` and ownership assigned to + `--user` and `--group`. + If `-` is passed, this type's `stdin` will be used. + +user + The user under which the service will run. Defaults to `root`. + If this user is not `root` and `--do-not-manage-user` is not present, + this user will be created or removed as per the `--state` parameter. + +user-home-dir + Does not have an effect if `--do-not-manage-user` is used or `--user` is + `root`. + The home directory of the service user. It will be created. + Defaults to `/nonexistent`, in this case the home directory will not be + created. + +group + The group under which the service will run. Defaults to `--user`. + +state + Whether the service is to be `present` (default) or `absent`. + When `absent`, this type will clean any binaries listed in `--extra-binary` + and also the config file as described in `--config-file-source`. + +binary + This will be the binary name. Defaults to `${__object_id}`. + If `--unpack` is used, a binary with this name must be unpacked. + Otherwise, the contents of `--url` will be placed under this binary name. + +service-args + Any extra arguments to pass along with `--service-exec`. Beware that any + service-args having the format `--config=/etc/foo.cfg` should be + represented in the following way `--service-exec='--config=/etc/foo.cfg'` + +service-exec + The executable to use for this service. + Defaults to `/usr/local/bin/BINARY_NAME` where `BINARY_NAME` is the + resulting value of `--binary`. + +service-definition + The service definition to be used as an override. + Note that this type decides dinammically between runit and systemd, and + you can currently only define either a systemd unit or a runit script here. + Use this parameter only for testing and get in touch to discuss how your + particular use-case can be supported by the type. + +service-description + The service description to be used in, e.g. the systemd unit file. + Defaults to `cdist-managed '${__object_id}' service`. + +unpack-args + Only has an effect if `--unpack` is used. + These arguments will be passed verbatim to `__unpack(7)`. + Very useful as this type assumes the archive does not have the binaries in + subdirectories; that can be worked around with + `--unpack-args '--tar-strip 1'`. + +unpack-extension + Only has an effect if `--unpack` is used. + The file extension of the file to unpack, defaults to `.tar.gz`. + +working-directory + If set, the working directory with which the service will be started. + + +OPTIONAL MULTIPLE PARAMETERS +---------------------------- +extra-binary + Only useful with `--unpack`. + If passed, these binaries will also be installed when `--state` is `present` + and removed when `--state` is `absent`. + Handle with care :-). + + +EXAMPLES +-------- + +.. code-block:: sh + + # Install and enable the ipmi_exporter service + # The variables are defined in the manifest previously + __evilham_single_binary_service ipmi_exporter \ + --user "${USER}" \ + --service-args ' --config.file=/etc/ipmi_exporter.conf' \ + --version "${SHOULD_VERSION}" \ + --checksum "${CHECKSUM}" \ + --url "${DOWNLOAD_URL}" \ + --state "present" \ + --unpack \ + --unpack-args "--tar-strip 1" \ + --config-file-source '-' <<-EOF + # Remotely managed, changes will be lost + # [...] config contents goes here + EOF + + # Remove the ipmi_exporter service along with the user and its config + __evilham_single_binary_service ipmi_exporter \ + --user "${USER}" \ + --version "${SHOULD_VERSION}" \ + --checksum "${CHECKSUM}" \ + --url "${DOWNLOAD_URL}" \ + --state "absent" + + # Same, but the service was using my user! Let's not delete that! + __evilham_single_binary_service ipmi_exporter \ + --user "evilham" \ + --do-not-manage-user \ + --version "${SHOULD_VERSION}" \ + --checksum "${CHECKSUM}" \ + --url "${DOWNLOAD_URL}" \ + --state "absent" + + +SEE ALSO +-------- +- `__download(7)` +- `__unpack(7)` + + +AUTHORS +------- +Evilham + + +COPYING +------- +Copyright \(C) 2021 Evilham. diff --git a/type/__single_binary_service/manifest b/type/__single_binary_service/manifest new file mode 100755 index 0000000..8288b94 --- /dev/null +++ b/type/__single_binary_service/manifest @@ -0,0 +1,288 @@ +#!/bin/sh -e +SERVICE_NAME="${__object_id}" + +OS="$(cat "${__global}/explorer/os")" + +case "${OS}" in + debian|devuan) + SUPER_USER_GROUP=root + ETC_DIR="/etc" + ;; + *bsd) + SUPER_USER_GROUP=wheel + ETC_DIR="/usr/local/etc" + ;; + *) + echo "Your OS '${OS}' is currently not supported." >&2 + exit 1 + ;; +esac +INIT="$(cat "${__global}/explorer/init")" + +case "${INIT}" in + systemd) + service_definition_require="__systemd_unit/${SERVICE_NAME}.service" + service_command="service ${SERVICE_NAME} %s" + ;; + runit|sysvinit) + # We will use runit to manage these services + __runit + export require="__runit" + service_definition_require="__runit_service/${SERVICE_NAME}" + service_command="sv %s ${SERVICE_NAME}" + ;; + *) + echo "Init system ${INIT}' is currently not supported." >&2 + exit 1 + ;; +esac + +BIN_DIR="/usr/local/bin" + +# Ensure the target bin dir exists +# Care, we never want to remove it :-D +__directory "${BIN_DIR}" \ + --state "exists" \ + --mode 0755 +export require="${require} __directory${BIN_DIR}" + +STATE="$(cat "${__object}/parameter/state")" +USER="$(cat "${__object}/parameter/user")" +GROUP="$(cat "${__object}/parameter/group" 2>/dev/null || true)" +if [ -z "${GROUP}" ]; then + if [ "${USER}" != "root" ]; then + GROUP="${USER}" + else + GROUP="${SUPER_USER_GROUP}" + fi +fi + + +BINARY="$(cat "${__object}/parameter/binary" 2>/dev/null || true)" +if [ -z "${BINARY}" ]; then + BINARY="${SERVICE_NAME}" +fi +EXTRA_BINARIES="$(cat "${__object}/parameter/extra-binary" 2>/dev/null || true)" +# This only makes sense for file archives +if [ -n "${EXTRA_BINARIES}" ] && [ -f "${__object}/parameter/unpack" ]; then + cat >&2 <<-EOF + You cannot specify extra binaries without the --unpack argument. + Make sure that the --url argument points to a file archive. +EOF +fi + +SERVICE_EXEC="$(cat "${__object}/parameter/service-exec" 2>/dev/null || true)" +if [ -z "${SERVICE_EXEC}" ]; then + SERVICE_EXEC="${BIN_DIR}/${BINARY}" +fi +SERVICE_ARGS="$(cat "${__object}/parameter/service-args")" +SERVICE_EXEC="${SERVICE_EXEC} ${SERVICE_ARGS}" + +SERVICE_DESCRIPTION="$(cat "${__object}/parameter/service-description" \ + 2>/dev/null || true)" +if [ -z "${SERVICE_DESCRIPTION}" ]; then + SERVICE_DESCRIPTION="cdist-managed '${SERVICE_NAME}' service" +fi + +SERVICE_DEFINITION="$(cat "${__object}/parameter/service-definition" 2>/dev/null || true)" + +WORKING_DIRECTORY_PATH="$(cat "${__object}/parameter/working-directory" 2>/dev/null || true)" +if [ -n "${WORKING_DIRECTORY_PATH}" ]; then + WORKING_DIRECTORY_SYSTEMD="WorkingDirectory=${WORKING_DIRECTORY_PATH}" + WORKING_DIRECTORY_RUNIT="cd '${WORKING_DIRECTORY_PATH}'" +fi + +DOWNLOAD_URL="$(cat "${__object}/parameter/url")" +CHECKSUM="$(cat "${__object}/parameter/checksum")" +SHOULD_VERSION="$(cat "${__object}/parameter/version")" + +# Create a user for the service if it is not root +USER_HOME_DIR="/root" +if [ "${USER}" != "root" ] && \ + [ ! -f "${__object}/parameter/do-not-manage-user" ]; then + if [ "${STATE}" = "absent" ]; then + # When removing, ensure user is not being used + user_require="${service_definition_require}" + fi + USER_HOME_DIR="$(cat "${__object}/parameter/user-home-dir")" + if [ "${USER_HOME_DIR}" != "/nonexistent" ]; then + USER_CREATE_HOME="--create-home" + fi + require="${require} ${user_require}" __user "${USER}" \ + --system \ + --state "${STATE}" \ + --home "${USER_HOME_DIR}" \ + --comment "cdist-managed ${SERVICE_NAME} user" \ + ${USER_CREATE_HOME} + # Track dependencies + service_require="${service_require} __user/${USER}" +fi + +# Place config file if necessary +CONFIG_FILE_DEST="${ETC_DIR}/${SERVICE_NAME}.conf" +CONFIG_FILE_SOURCE="$(cat "${__object}/parameter/config-file-source" 2>/dev/null || true)" +if [ "${CONFIG_FILE_SOURCE}" = "-" ]; then + CONFIG_FILE_SOURCE="${__object}/stdin" +fi +if [ -n "${CONFIG_FILE_SOURCE}" ] && [ "${STATE}" = "present" ]; then + require="${require} __user/${USER}" __file \ + "${CONFIG_FILE_DEST}" \ + --owner "${USER}" \ + --group "${GROUP}" \ + --mode "0440" \ + --source "${CONFIG_FILE_SOURCE}" + service_require="${service_require} __file${CONFIG_FILE_DEST}" +fi + + + +# This should setup the object in $service_definition_require +# See above. +case "${INIT}" in + systemd) + if [ -z "${SERVICE_DEFINITION}" ]; then + SERVICE_DEFINITION="$(cat </dev/null || true)" + # Download packed file + __download "${TMP_PATH}${UNPACK_EXTENSION}" \ + --url "${DOWNLOAD_URL}" \ + --download remote \ + --sum "${CHECKSUM}" + + # Unpack file and also perform service upgrade + # shellcheck disable=SC2086 + require="__download${TMP_PATH}${UNPACK_EXTENSION}" \ + __unpack "${TMP_PATH}${UNPACK_EXTENSION}" \ + ${UNPACK_ARGS} \ + --destination "${TMP_PATH}" + version_bump_require="__unpack${TMP_PATH}${UNPACK_EXTENSION}" + else + # Create temp directory + __directory "${TMP_PATH}" + # Download binary directoy to the temp directory with the + # specified binary name + require="__directory${TMP_PATH}" __download \ + "${TMP_PATH}/${BINARY}" \ + --url "${DOWNLOAD_URL}" \ + --download remote \ + --sum "${CHECKSUM}" + version_bump_require="__download${TMP_PATH}/${BINARY}" + fi + + # Perform update of cdist-managed version file + # And also perform service upgrade + # This is a bug if service_upgrade fails >,< + printf "%s" "${SHOULD_VERSION}" | \ + require="${version_bump_require}" __file \ + "${VERSION_FILE}" \ + --onchange "${perform_service_upgrade}" \ + --source "-" +else + # We only restart here if there was a config change + # but there was not a version change + require="${service_require}" __check_messages \ + "single_binary_service_${__object_id}" \ + --pattern "^__file${CONFIG_FILE_DEST}" \ + --execute "$(sv_cmd restart)" +fi diff --git a/type/__single_binary_service/parameter/boolean b/type/__single_binary_service/parameter/boolean new file mode 100644 index 0000000..a779fd5 --- /dev/null +++ b/type/__single_binary_service/parameter/boolean @@ -0,0 +1,2 @@ +do-not-manage-user +unpack diff --git a/type/__single_binary_service/parameter/default/service-args b/type/__single_binary_service/parameter/default/service-args new file mode 100644 index 0000000..e69de29 diff --git a/type/__single_binary_service/parameter/default/state b/type/__single_binary_service/parameter/default/state new file mode 100644 index 0000000..e7f6134 --- /dev/null +++ b/type/__single_binary_service/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/type/__single_binary_service/parameter/default/unpack-extension b/type/__single_binary_service/parameter/default/unpack-extension new file mode 100644 index 0000000..c95e2e9 --- /dev/null +++ b/type/__single_binary_service/parameter/default/unpack-extension @@ -0,0 +1 @@ +.tar.gz \ No newline at end of file diff --git a/type/__single_binary_service/parameter/default/user b/type/__single_binary_service/parameter/default/user new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/type/__single_binary_service/parameter/default/user @@ -0,0 +1 @@ +root diff --git a/type/__single_binary_service/parameter/default/user-home-dir b/type/__single_binary_service/parameter/default/user-home-dir new file mode 100644 index 0000000..4d21ca6 --- /dev/null +++ b/type/__single_binary_service/parameter/default/user-home-dir @@ -0,0 +1 @@ +/nonexistent diff --git a/type/__single_binary_service/parameter/optional b/type/__single_binary_service/parameter/optional new file mode 100644 index 0000000..7c88cb4 --- /dev/null +++ b/type/__single_binary_service/parameter/optional @@ -0,0 +1,13 @@ +config-file-source +user +group +state +binary +service-args +service-exec +service-description +service-definition +unpack-extension +unpack-args +user-home-dir +working-directory diff --git a/type/__single_binary_service/parameter/optional_multiple b/type/__single_binary_service/parameter/optional_multiple new file mode 100644 index 0000000..e1ca562 --- /dev/null +++ b/type/__single_binary_service/parameter/optional_multiple @@ -0,0 +1 @@ +extra-binary diff --git a/type/__single_binary_service/parameter/required b/type/__single_binary_service/parameter/required new file mode 100644 index 0000000..b1e8d01 --- /dev/null +++ b/type/__single_binary_service/parameter/required @@ -0,0 +1,3 @@ +url +checksum +version