diff options
| author | Marius Peter <dev@marius-peter.com> | 2026-07-03 16:40:17 +0200 |
|---|---|---|
| committer | Marius Peter <dev@marius-peter.com> | 2026-07-03 16:40:17 +0200 |
| commit | 239de739d538966ccb900eb391759b5e14b8a217 (patch) | |
| tree | 54c160a19f1ce0ccc029493f4bfda0a765e01fe1 /roles/daq-node/deploy | |
| parent | b8881057b64c6d0eedcfa2b07c138908ef2b6f76 (diff) | |
Add role for daq-node.
Diffstat (limited to 'roles/daq-node/deploy')
| -rwxr-xr-x | roles/daq-node/deploy | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/roles/daq-node/deploy b/roles/daq-node/deploy new file mode 100755 index 0000000..1fa1a83 --- /dev/null +++ b/roles/daq-node/deploy @@ -0,0 +1,190 @@ +#!/usr/bin/env bash + +set -eEuo pipefail + +readonly REPO_DIR="/opt/fapg/fapg-daq" +readonly NODE_BIN="${REPO_DIR}/roles/sensor-node/bin/fapg-daq-node" + +readonly SERVICE_NAME="fapg-daq-node.service" +readonly SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}" + +readonly ENV_DIR="/etc/fapg-daq" +readonly ENV_FILE="${ENV_DIR}/daq.conf" + +readonly STATE_DIR="/var/lib/fapg-daq" +readonly RUN_DIR="/run/fapg-daq" + +readonly RUN_USER='fapg-daq' +readonly RUN_GROUP='fapg-daq' + +readonly SERIAL_DEVICE='/dev/ttyUSB0' +readonly READ_INTERVAL=5 +readonly MQTT_HOST='fapg-daq-five-01' +readonly MQTT_PORT='1883' +readonly MQTT_USERNAME='fapg_zero' +MQTT_PASSWORD='' + +function die() { + echo "ERROR: $*" >&2 + exit 1 +} + +function require_root() { + if [[ "$(id -u)" -ne 0 ]]; then + die "$0 requires superuser privileges." + fi +} + +function install_os_dependencies() { + local dependencies='perl libnet-mqtt-simple-perl libdevice-serialport-perl' + local missing_dependencies='' + + for dep in ${dependencies}; do + if dpkg --get-selections "${dep}" | grep -w install > /dev/null 2>&1; then + echo "${dep} is already installed." + else + echo "${dep} is not installed." + missing_dependencies+=" ${dep}" + fi + done + + if [[ -n "${missing_dependencies}" ]]; then + apt-get install -y ${missing_dependencies} + fi + + echo "Installed all OS dependencies." +} + +# function install_perl_dependencies() { +# local dependencies="Net::MQTT::Simple" +# for dep in ${dependencies}; do +# if ! perl -M"${dep}" -e '1' > /dev/null 2>&1; then +# cpanm "${dep}" +# fi +# done + +# echo "Installed all Perl dependencies." +# } + +create_runtime_user() { + if ! getent group "${RUN_GROUP}" > /dev/null; then + addgroup --system "${RUN_GROUP}" + fi + + if ! id "${RUN_USER}" > /dev/null 2>&1; then + adduser \ + --system \ + --ingroup "${RUN_GROUP}" \ + --home "${STATE_DIR}" \ + --no-create-home \ + --disabled-login \ + "${RUN_USER}" + fi + + # USB serial devices are normally accessible to members of dialout. + usermod -aG dialout "${RUN_USER}" + + echo "Created runtime user ${RUN_USER}, group ${RUN_GROUP}." +} + +check_repo_layout() { + [[ -d "${REPO_DIR}" ]] || die "Repo not found at ${REPO_DIR}." + [[ -x "${NODE_BIN}" ]] || die "${NODE_BIN} does not exist or is not executable." + + mkdir -p "${STATE_DIR}" "${RUN_DIR}" + chown "${RUN_USER}:${RUN_GROUP}" "${STATE_DIR}" "${RUN_DIR}" + + echo "Verified layout of repo at ${REPO_DIR}." +} + +function write_env_file() { + mkdir -p "${ENV_DIR}" + + # TODO: print diff of existing and proposed env file + if [[ -f "${ENV_FILE}" ]]; then + echo "${ENV_FILE} already exists, remove or edit manually if necessary." + return + fi + + if [[ -z "${MQTT_PASSWORD:-}" ]]; then + read -rsp "MQTT password for user '${MQTT_USERNAME}': " MQTT_PASSWORD + fi + + [[ -n "${MQTT_PASSWORD:-}" ]] || die "MQTT_PASSWORD is required." + + # TODO: unsafe! + cat > "${ENV_FILE}" <<EOF +MQTT_SIMPLE_ALLOW_INSECURE_LOGIN=1 + +SERIAL_DEVICE="${SERIAL_DEVICE}" +READ_INTERVAL=${READ_INTERVAL} +MQTT_HOST="${MQTT_HOST}" +MQTT_PORT="${MQTT_PORT}" +MQTT_USERNAME="${MQTT_USERNAME}" +MQTT_PASSWORD="${MQTT_PASSWORD}" +EOF + + chown root:"${RUN_GROUP}" "${ENV_FILE}" + chmod 0640 "${ENV_FILE}" +} + +function write_systemd_service() { + cat > "${SERVICE_FILE}" <<EOF +[Unit] +Description=FAPG DAQ sensor node +Wants=network-online.target +After=network-online.target + +[Service] +Type=simple +User=${RUN_USER} +Group=${RUN_GROUP} +SupplementaryGroups=dialout + +WorkingDirectory=${REPO_DIR} +EnvironmentFile=${ENV_FILE} + +ExecStart=${NODE_BIN} + +Restart=always +RestartSec=5 + +# Basic hardening. Do not enable PrivateDevices=true because this service needs /dev/ttyUSB0. +NoNewPrivileges=true +ProtectHome=true +ProtectSystem=strict +ReadWritePaths=${STATE_DIR} ${RUN_DIR} + +[Install] +WantedBy=multi-user.target +EOF + + chmod 0644 "$SERVICE_FILE" +} + +function enable_service() { + systemctl daemon-reload + systemctl enable --now "${SERVICE_NAME}" +} + +show_status() { + cat <<"EOF" +Deployment complete. +Current service state: +EOF + systemctl --no-pager --full status "${SERVICE_NAME}" +} + +function main() { + require_root + install_os_dependencies + # install_perl_dependencies + create_runtime_user + check_repo_layout + write_env_file + write_systemd_service + enable_service + show_status +} + +main "$@" |