summaryrefslogtreecommitdiff
path: root/roles/daq-node/deploy
diff options
context:
space:
mode:
Diffstat (limited to 'roles/daq-node/deploy')
-rwxr-xr-xroles/daq-node/deploy190
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 "$@"
Copyright 2019--2026 Marius PETER