#!/bin/bash ############################################################################## # This script allows you to easily manage Platypush instances through Python # # virtual environment. You can build environments from a config.yaml file # # and automatically managed the required dependencies, as well as start, # # stop and remove them # # # # @author: Fabio Manganiello <fabio@platypush.tech> # # @licence: MIT # ############################################################################## workdir="$HOME/.local/share/platypush/venv" function build { cfgfile= while getopts ':c:' opt; do case ${opt} in c) cfgfile=$OPTARG;; \?) echo "Invalid option: -$OPTARG" >&2 exit 1;; :) echo "Option -$OPTARG requires the path to a Platypush configuration file" >&2 exit 1;; esac done if [[ -z "$cfgfile" ]]; then echo "Usage: $0 build -c <path-to-platypush-config-file>" >&2 exit 1 fi echo "Parsing configuration file" pip_cmd= pkg_cmd= includes=() cmd_exec=() while read -r line; do if echo "$line" | grep -E "^pip:\s*"; then pip_cmd="$(echo "$line" | sed -r -e 's/^pip:\s*(.*)'/\\1/)" elif echo "$line" | grep -E "^packages:\s*"; then pkg_cmd="$(echo "$line" | sed -r -e 's/^packages:\s*(.*)'/\\1/)" elif echo "$line" | grep -E "^exec:\s*"; then cmd_exec+=("$(echo "$line" | sed -r -e 's/^exec:\s*(.*)'/\\1/)") elif echo "$line" | grep -E "^include:\s*"; then includes+=("$(echo "$line" | sed -r -e 's/^include:\s*(.*)'/\\1/)") elif echo "$line" | grep -E "^device_id:\s*"; then device_id="$(echo "$line" | sed -r -e 's/^device_id:\s*(.*)'/\\1/)" fi done <<< "$(python <<EOF from platypush.config import Config from platypush.utils.manifest import get_install_commands_from_conf deps = get_install_commands_from_conf('$(realpath "${cfgfile}")') print(f'device_id: {Config.get("device_id")}') if deps.get('pip'): print(f'pip: {deps["pip"]}') if deps.get('packages'): print(f'packages: {deps["packages"]}') for cmd in deps.get('exec', []): print(f'exec: {cmd}') for include in Config._included_files: print(f'include: {include}') EOF )" envdir="${workdir}/${device_id}" etcdir="${envdir}/etc/platypush" echo "Preparing virtual environment for device $device_id" mkdir -p "$envdir" mkdir -p "$etcdir" srcdir=$(dirname "$cfgfile") for ((i=0; i < ${#includes[@]}; i++)); do incdir=$(dirname "${includes[$i]}") incdir=$(realpath --relative-to="$srcdir" "$incdir") destdir="$etcdir/$incdir" mkdir -p "$destdir" cp "${includes[$i]}" "$destdir" done cp "$cfgfile" "$etcdir/config.yaml" cfgfile="${etcdir}/config.yaml" python3 -m venv "${envdir}" cd "${envdir}" || exit 1 source "${envdir}/bin/activate" echo "Installing required dependencies" # shellcheck disable=SC2086 [ -n "${pkg_cmd}" ] && sudo ${pkg_cmd} [ -n "${pip_cmd}" ] && ${pip_cmd} for ((i=0; i < ${#cmd_exec[@]}; i++)); do ${cmd_exec[$i]} done pip install --upgrade git+https://git.platypush.tech/platypush/platypush.git echo "Platypush virtual environment prepared under $envdir" } function start { if [[ -z "$1" ]]; then echo "Usage: $0 start <env-name>" >&2 exit 1 fi env=$1 envdir="${workdir}/${env}" rundir="${envdir}/var/run" pidfile="${rundir}/platypush.pid" cfgfile="${envdir}/etc/platypush/config.yaml" if [[ ! -d "$envdir" ]]; then echo "No such directory: $envdir" >&2 exit 1 fi mkdir -p "${rundir}" if [[ -f "$pidfile" ]]; then if pgrep -F "${pidfile}"; then echo "Another instance (PID $(cat "${pidfile}")) is running, please stop that instance first" exit 1 fi echo "A PID file was found but the process does not seem to be running, starting anyway" rm -f "$pidfile" fi python3 -m venv "${envdir}" cd "${envdir}" || exit 1 source bin/activate bin/platypush -c "$cfgfile" -P "$pidfile" & start_time=$(date +'%s') timeout=30 while :; do [[ -f "$pidfile" ]] && break now=$(date +'%s') elapsed=$(( now-start_time )) if (( elapsed >= timeout )); then echo "Platypush instance '$env' did not start within $timeout seconds" >&2 exit 1 fi echo -n '.' sleep 1 done pid=$(cat "$pidfile") echo echo "Platypush environment $env started with PID $pid" wait "${pid}" echo "Platypush environment $env terminated" } function stop { if [[ -z "$1" ]]; then echo "Usage: $0 stop <env-name>" >&2 exit 1 fi env=$1 envdir="${workdir}/${env}" rundir="${envdir}/var/run" pidfile="${rundir}/platypush.pid" if [[ ! -d "$envdir" ]]; then echo "No such directory: $envdir" >&2 exit 1 fi if [[ ! -f "$pidfile" ]]; then echo "No pidfile found for instance \"${env}\"" exit 1 fi pid=$(cat "$pidfile") pids="$pid $(ps --no-headers -o pid= --ppid "$pid")" # shellcheck disable=SC2086 kill -9 ${pids} rm -f "$pidfile" echo "Instance '$env' with PID $pid stopped" } function rme { if [[ -z "$1" ]]; then echo "Usage: $0 rm <env-name>" >&2 exit 1 fi envdir="${workdir}/$1" rundir="${envdir}/var/run" pidfile="${rundir}/platypush.pid" if [[ ! -d "$envdir" ]]; then echo "No such directory: $envdir" >&2 exit 1 fi if [[ -f "$pidfile" ]]; then if pgrep -F "${pidfile}"; then echo "Another instance (PID $(cat "$pidfile")) is running, please stop that instance first" exit 1 fi echo "A PID file was found but the process does not seem to be running, removing anyway" fi echo "WARNING: This operation will permanently remove the Platypush environment $1" echo -n "Are you sure you want to continue? (y/N) " IFS= read -r answer echo "$answer" | grep -E '^[yY]' >/dev/null || exit 0 rm -rf "$envdir" echo "$envdir removed" } function usage { echo "Usage: $0 <build|start|stop|rm> [options]" >&2 exit 1 } if (( $# < 1 )); then usage fi action=$1 shift mkdir -p "${workdir}" # shellcheck disable=SC2048,SC2086 case ${action} in 'build') build $*;; 'start') start $*;; 'stop') stop $*;; 'rm') rme $*;; *) usage;; esac