Compare commits

..

8 commits

Author SHA1 Message Date
977b530dab
[__single_binary_service] Update manpage to remove __evilham prefix 2022-04-28 17:22:19 +02:00
1865ff9dce Add 'type/__single_binary_service/' from commit '1af7e960fa882efc7202cad5cc01d3136886fa0a'
git-subtree-dir: type/__single_binary_service
git-subtree-mainline: 67bc8aa02b
git-subtree-split: 1af7e960fa
2022-04-28 17:20:02 +02:00
1af7e960fa [__single_binary_service] Many improvements + runit support
Amongst other things compressed files can be of a type other than .tar.gz (it
remains the default) and we now properly support runit services, FreeBSD and
Devuan.
2021-10-30 15:38:26 +02:00
3e77fbbb43 [__single_binary_service] Do not use echo echo echo 2021-08-04 21:02:37 +02:00
afa48b1028 [__single_binary_service] Support customisation of systemd units
Requested by pedro
2021-08-04 21:00:52 +02:00
c5929f397d [__single_binary_service] Adapt bug fixes proposed by pedro
there are several typos, some style issues and now there is at most one service
restart in all cases.

Submitted by:   pedro <git2021@cas.cat>
2021-08-04 20:27:08 +02:00
d5b552ddb4 [__single_binary_service] Add manpage, config-file and better absent
With these changes the type is good for general consumption (modulo the
limitations mentioned in the manpage under TODO).
2021-06-18 22:01:45 +02:00
51d0b817fe [__single_binary_service] Type to manage very simple services. 2021-06-18 20:52:58 +02:00
12 changed files with 511 additions and 0 deletions

View file

@ -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

View file

@ -0,0 +1,190 @@
cdist-type__single_binary_service(7)
====================================
NAME
----
cdist-type__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
__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
__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!
__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 <contact@evilham.com>
COPYING
-------
Copyright \(C) 2022 Evilham.

View file

@ -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 <<EOF
[Unit]
Description=${SERVICE_DESCRIPTION}
After=network.target
[Service]
Type=simple
User=${USER}
Group=${GROUP}
ExecStart=${SERVICE_EXEC}
Restart=always
${WORKING_DIRECTORY_SYSTEMD}
[Install]
WantedBy=multi-user.target
EOF
)"
fi
__systemd_unit "${SERVICE_NAME}.service" \
--source "-" \
--state "${STATE}" \
--enablement-state "enabled" <<EOF
${SERVICE_DEFINITION}
EOF
;;
runit|sysvinit)
if [ -z "${SERVICE_DEFINITION}" ]; then
SERVICE_DEFINITION="$(cat <<EOF
#!/bin/sh -e
${WORKING_DIRECTORY_RUNIT}
export HOME="\$(getent passwd '${USER}' | cut -d: -f6)"
export USER="${USER}"
export GROUP="${GROUP}"
exec chpst -u "${USER}:${GROUP}" ${SERVICE_EXEC}
EOF
)"
fi
__runit_service "${SERVICE_NAME}" \
--state "${STATE}" \
--log \
--source - <<EOF
${SERVICE_DEFINITION}
EOF
;;
esac
service_require="${service_require} ${service_definition_require}"
# Proceed after user and service description have been prepared
export require="${require} ${service_require}"
VERSION_FILE="${BIN_DIR}/.${SERVICE_NAME}.cdist.version"
IS_VERSION="$(cat "${__object}/explorer/explorer-version")"
if [ "${STATE}" = "absent" ]; then
# Perform cleanup of generated files
for bin_file in ${BINARY} ${EXTRA_BINARIES}; do
__file "${BIN_DIR}/${bin_file}" --state "absent"
done
__file "${VERSION_FILE}" --state "absent"
__file "${CONFIG_FILE_DEST}" --state "absent"
fi
if [ "${STATE}" != "present" ]; then
exit
fi
sv_cmd() {
# This is intentional
# shellcheck disable=SC2059
printf "${service_command}" "$1"
}
if [ "${SHOULD_VERSION}" != "${IS_VERSION}" ]; then
# We are installing the service and there has been a version change
# (or it is first-time install)
TMP_PATH="/tmp/${SERVICE_NAME}-${SHOULD_VERSION}"
# This is what will stop the service, replace the binaries and
# start the service again
perform_service_upgrade="$(cat <<EOF
$(sv_cmd stop) || true
if [ -f '${TMP_PATH}' ]; then
chown root:${SUPER_USER_GROUP} '${TMP_PATH}'
chmod 0555 '${TMP_PATH}'
cp -af '${TMP_PATH}' '${BIN_DIR}/${BINARY}'
else
for bin_file in ${BINARY} ${EXTRA_BINARIES}; do
bin_path="${TMP_PATH}/\${bin_file}"
chown root:${SUPER_USER_GROUP} "\${bin_path}"
chmod 0555 "\${bin_path}"
cp -af "\${bin_path}" "${BIN_DIR}/\${bin_file}"
done
fi
$(sv_cmd start) || true
EOF
)"
if [ -f "${__object}/parameter/unpack" ]; then
UNPACK_EXTENSION="$(cat "${__object}/parameter/unpack-extension")"
UNPACK_ARGS="$(cat "${__object}/parameter/unpack-args" \
2>/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

View file

@ -0,0 +1,2 @@
do-not-manage-user
unpack

View file

@ -0,0 +1 @@
present

View file

@ -0,0 +1 @@
.tar.gz

View file

@ -0,0 +1 @@
root

View file

@ -0,0 +1 @@
/nonexistent

View file

@ -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

View file

@ -0,0 +1 @@
extra-binary

View file

@ -0,0 +1,3 @@
url
checksum
version