diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..13803801 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "hosted-app-utils"] + path = hosted-app-utils + url = git@gitlab.com:signalytic/client-external/hosted-app-utils.git diff --git a/README.md b/README.md index 33d46113..587ed3fe 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,23 @@ Packaging and installation tools for Streamline's "Streamline EMR" application. -## Building -1. ensure you have docker access to this project's container registry on GitLab -2. run `package.sh` to generate a new application package -3. package will be stored to `build-packages/` +## Setup +1. login to the gitlab docker registry: `docker login registry.gitlab.com` +2. update submodules: `git submodule update --init` -Several defaults can be overridden with command line options or by setting appropriate environment variables. A help prompt can be accessed using `package.sh -h`. For example, `package.sh -t latest -p linux/arm64` will build only the `linux/arm64` platform and use the `latest` tag instead of the current repo version. +## Building +To build, simply run `./package` to generate a new application package. + +The `package` script will assign a few variables by default, specifically: +``` +CONFIG=${CONFIG:-${LOCATION}/config.json} +COMPOSE=${COMPOSE:-${LOCATION}/docker-compose.yml} +BUILD_DIR=${BUILD_DIR:-${LOCATION}/build-packages} +``` + +If necessary, each of these may be overridden by running with `VAR=VALUE ./package`. + +The `hosted-app-utils` project provides additional customization via command line arguments. This can be used to modify the package name, skip packaging of images, build for only one platform and more. For a list of options, run `./package -h`. ## Installing 1. extract the generated archive file in a temporary folder on the target system diff --git a/config.json b/config.json new file mode 100644 index 00000000..d06c4379 --- /dev/null +++ b/config.json @@ -0,0 +1,17 @@ +{ + "name": "streamline-emr", + "org": "streamline", + "version": "0.1", + "platforms": [ + "linux/arm64", + "linux/amd64" + ], + "images": { + "linux/arm64": { + "registry.gitlab.com/signalytic/client-external/streamline/streamline-emr/streamline-emr/arm64:latest": "streamline-emr:latest" + }, + "linux/amd64": { + "registry.gitlab.com/signalytic/client-external/streamline/streamline-emr/streamline-emr/amd64:latest": "streamline-emr:latest" + } + } +} \ No newline at end of file diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/docker-compose.yml b/docker-compose.yml similarity index 80% rename from root/var/signalytic/clientapps/streamline/streamline-emr/docker-compose.yml rename to docker-compose.yml index ab141424..b4f8e53a 100755 --- a/root/var/signalytic/clientapps/streamline/streamline-emr/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: image: ${APP_IMAGE:-streamline-emr:latest} container_name: ${APP_CONTAINER:-streamline-emr} volumes: - - "${DATA_DIR:-/var/signalytic/clientapps/streamline/streamline-emr/data}:/var/lib/mysql" + - "${DATA_DIR:-./data}:/var/lib/mysql" ports: - "${APP_PORT:-3000}:80" dns: 0.0.0.0 diff --git a/hosted-app-utils b/hosted-app-utils new file mode 160000 index 00000000..6f1a9493 --- /dev/null +++ b/hosted-app-utils @@ -0,0 +1 @@ +Subproject commit 6f1a9493c1bdce87a76db53c9d0f9879c5266030 diff --git a/images.csv b/images.csv deleted file mode 100644 index c47ec349..00000000 --- a/images.csv +++ /dev/null @@ -1,2 +0,0 @@ -linux/arm64,streamline-emr,registry.gitlab.com/signalytic/client-external/streamline/streamline-emr/streamline-emr/arm64 -linux/amd64,streamline-emr,registry.gitlab.com/signalytic/client-external/streamline/streamline-emr/streamline-emr/amd64 \ No newline at end of file diff --git a/install.sh b/install.sh deleted file mode 100755 index 1027c9c4..00000000 --- a/install.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -# -# Copyright (C) Wendepunkt Medical Innovations, Inc - All Rights Reserved. -# Unauthorized copying of this file, via any medium is strictly prohibited. -# Proprietary and confidential -# -# For information: dev@signalytic.ca -# - -# -# Install the Streamline "Streamline EMR" application and services -# -# The caller of this script must check the exit value and possibly do a -# roll-back if it is non-zero. If the exit value is non-zero services have not -# been restarted. - -LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" - -if [ "$EUID" -ne 0 ] - then echo "Please run as root" - exit 1 -fi - - -# stop any running systemd service if not running in a docker environment -if [ -f /.dockerenv ]; then - echo "Docker environment detected - skipping systemd installation" -else - echo "stopping system service" - systemctl stop streamline-emr -fi - -set -e - -# copy root files -echo "importing root files" -cp -rfv $LOCATION/root/* / - -# import docker images -for f in $(find $LOCATION/images -name "*.img" 2>/dev/null);do - echo "importing docker image: $f" - docker load -i $f -done -for f in $(find $LOCATION/images -name "*.img.xz" 2>/dev/null);do - echo "importing docker image: $f" - xz -d -c $f | docker load -done - -# install systemd service unless running in a docker environment -if [ ! -f /.dockerenv ]; then - echo "installing system service" - systemctl daemon-reload - systemctl enable streamline-emr - systemctl start streamline-emr -fi diff --git a/package b/package new file mode 100755 index 00000000..e4c54743 --- /dev/null +++ b/package @@ -0,0 +1,9 @@ +#!/bin/bash + +LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +CONFIG=${CONFIG:-${LOCATION}/config.json} +COMPOSE=${COMPOSE:-${LOCATION}/docker-compose.yml} +BUILD_DIR=${BUILD_DIR:-${LOCATION}/build-packages} + +${LOCATION}/hosted-app-utils/package ${CONFIG} ${COMPOSE} output=${BUILD_DIR} $@ \ No newline at end of file diff --git a/package.sh b/package.sh deleted file mode 100755 index 0595360b..00000000 --- a/package.sh +++ /dev/null @@ -1,206 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) Wendepunkt Medical Innovations, Inc - All Rights Reserved. -# Unauthorized copying of this file, via any medium is strictly prohibited. -# Proprietary and confidential -# -# For information: dev@signalytic.ca -# - -# basic path info -LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -PROJECT_ROOT="$(realpath $LOCATION)" - -# determine version name from git -# use ci tag name if available (e.g. "v1.2") -if [ $CI_COMMIT_TAG ]; then - VERSION_NAME="$CI_COMMIT_TAG" -# otherwise if ci, use BRANCH-COMMIT -elif [ $CI_COMMIT_BRANCH ]; then - VERSION_NAME="$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA" -# if not ci, try to use git -elif command -v git &> /dev/null; then - VERSION_NAME=$(git describe --always --tags --abbrev=7 --dirty) -# fallback -else - VERSION_NAME="latest" -fi - -# configurable options (override with env vars as needed) -PACKAGE_NAME=${PACKAGE_NAME-"streamline-emr"} -OUTPUT_DIR=${OUTPUT_DIR-"$PROJECT_ROOT/build-packages"} -PACKAGE_IMAGES=${PACKAGE_IMAGES:-1} -COMPRESS_IMAGES=${COMPRESS_IMAGES:-1} -IMAGE_TAG=${IMAGE_TAG:-${VERSION_NAME}} -IMAGES_LIST=${IMAGES_LIST:-${PROJECT_ROOT}/images.csv} - - -function print_usage { - echo "Streamline EMR packaging utility" - echo - echo "Usage: package.sh [ OPTIONS ]" - echo - echo "Options:" - echo " -h Print this help prompt and exit" - echo " -p PLATFORM Specify a docker platform to target, may be passed multiple" - echo " times. If none specified, all platforms built. Supported" - echo " platforms: 'linux/amd64', 'linux/arm64'" - echo " -t IMAGE_TAG Specify an image tag. It not specified, the tag will be" - echo " inferred from the current git version ('${IMAGE_TAG}')" - echo " -n PACKAGE_NAME Specify the package base name. Default: ${PACKAGE_NAME}" - echo " -o OUTPUT_DIR Specify a directory to save packages. Default: ${OUTPUT_DIR}" - echo " -l IMAGES_LIST Specify a file containing a list of images. Default: ${IMAGES_LIST}" - echo " -I Do not package images" - echo " -C Do not compress images" - echo -} - -# collect user args -PLATFORMS=() -while getopts ":p:t:n:o:l:ICh" options; do - case "${options}" in - p) - PLATFORMS+=(${OPTARG}) - ;; - t) - IMAGE_TAG=${OPTARG} - ;; - n) - PACKAGE_NAME=${OPTARG} - ;; - o) - OUTPUT_DIR=${OPTARG} - ;; - l) - IMAGES_LIST=${OPTARG} - ;; - I) - PACKAGE_IMAGES=0 - ;; - C) - COMPRESS_IMAGES=0 - ;; - h) - print_usage - exit 0 - ;; - *) - print_usage - exit 1 - ;; - esac -done -shift $((OPTIND -1)) - -# set defaults -if [ ${#PLATFORMS} -eq 0 ]; then - PLATFORMS=("linux/amd64" "linux/arm64") -fi - -echo "packaging $PACKAGE_NAME" -echo " output dir: $OUTPUT_DIR" -echo " package images: $PACKAGE_IMAGES" -echo " compress images: $COMPRESS_IMAGES" -echo " image tag: $IMAGE_TAG" -echo " image list: $IMAGES_LIST" -echo " platforms: ${PLATFORMS[@]}" - -# work from the project root -pushd $PROJECT_ROOT > /dev/null - -# Exit on error: -set -e -trap 'echo "Error: Last command failed with exit code $?."' ERR - -# build for each relevant platform -for platform in ${PLATFORMS[@]}; do - echo "preparing package for platform $platform" - if [ "$platform" == "linux/amd64" ];then - pkg_arch="x86_64" - elif [ "$platform" == "linux/arm64" ];then - pkg_arch="aarch64" - else - echo "unexpected platform '$platform'" - exit 1 - fi - OUTPUT_NAME="$PACKAGE_NAME-$VERSION_NAME-${pkg_arch}" - OUTPUT_FILE="$OUTPUT_NAME.tar.bz2" - OUTPUT_PATH="$OUTPUT_DIR/$OUTPUT_FILE" - - # create a new temp directory to stage files - for i in {1..9999};do - STAGING_DIR="tmp-staging-$i" - if [ ! -d $STAGING_DIR ]; then - break - fi - done - if [ -d $STAGING_DIR ]; then - echo "Failed to generate temp staging dir" - exit 1 - fi - - # add files - PACKAGE_ROOT=${STAGING_DIR}/${OUTPUT_NAME} - IMAGES_DIR=${PACKAGE_ROOT}/images - mkdir -p ${IMAGES_DIR} - cp ${PROJECT_ROOT}/install.sh ${PACKAGE_ROOT}/ - cp -a ${PROJECT_ROOT}/root ${PACKAGE_ROOT}/ - echo "${VERSION_NAME}" > ${PACKAGE_ROOT}/root/var/signalytic/clientapps/streamline/streamline-emr/version - - if [ "${PACKAGE_IMAGES}" = 1 ]; then - # add images - # read image list from file (e.g. 'images-aarch64') - # images are listed as SAVED_TAG=SOURCE_IMAGE - for image in $(cat ${IMAGES_LIST});do - IFS=, read -r img_platform dest source <<< "${image}" - # package only if the platforms match - if [ "${img_platform}" = "${platform}" ]; then - echo "pulling image '${source}' (${platform})" - docker pull --platform ${platform} ${source}:${IMAGE_TAG} - # re-tag the image with a more generic name - echo "tagging image as '${dest}:latest'" - docker tag ${source}:${IMAGE_TAG} ${dest}:latest - # save the image to file - if [ "${COMPRESS_IMAGES}" = 1 ]; then - path="${IMAGES_DIR}/${dest}.img.xz" - echo "saving to '${path}'" - # if installed, use pv to show progress - if command -v pv &> /dev/null; then - docker save ${dest}:latest | xz -9 - -c | pv -t -r -b -N "saving compressed image..." > ${path} - else - docker save ${dest}:latest | xz -9 - -c > ${path} - fi - else - path="${IMAGES_DIR}/${dest}.img" - echo "saving to '${path}'" - # if installed, use pv to show progress - if command -v pv &> /dev/null; then - docker save ${dest}:latest | pv -t -r -b -N "saving raw image..." > ${path} - else - docker save -o ${path} ${dest}:latest - fi - fi - fi - done - else - echo "skipping image packaging as requested" - fi - - # setup output directory and cleanup old files if necessary - mkdir -p $OUTPUT_DIR - rm -rf $OUTPUT_PATH - - pushd $STAGING_DIR > /dev/null - tar -cjf $OUTPUT_PATH ${OUTPUT_NAME} - popd > /dev/null - - # cleanup staging files - rm -r $STAGING_DIR - - echo "output: $OUTPUT_PATH" -done - -popd > /dev/null - -exit 0 diff --git a/root/etc/systemd/system/streamline-emr.service b/root/etc/systemd/system/streamline-emr.service deleted file mode 100644 index f097ab15..00000000 --- a/root/etc/systemd/system/streamline-emr.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=Run Streamline EMR -Requires=docker.service -After=docker.service - -[Service] -Type=oneshot -RemainAfterExit=yes -User=root -WorkingDirectory=/var/signalytic/clientapps/streamline/streamline-emr -ExecStart=/var/signalytic/clientapps/streamline/streamline-emr/start -ExecStop=/var/signalytic/clientapps/streamline/streamline-emr/stop -TimeoutStartSec=0 - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/autoheal b/root/var/signalytic/clientapps/streamline/streamline-emr/autoheal deleted file mode 100755 index 68a541b1..00000000 --- a/root/var/signalytic/clientapps/streamline/streamline-emr/autoheal +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" - -INTERVAL=${INTERVAL:-60} - -# track pid with a file `autoheal.pid`. If a previous instance is still running, exit early. -function cleanup() { - rm -f ${LOCATION}/autoheal.pid -} -trap cleanup EXIT -kill -0 $(cat ${LOCATION}/autoheal.pid 2> /dev/null) 2> /dev/null && echo "autoheal script already running" && exit 1 -echo $$ > ${LOCATION}/autoheal.pid - -# exit on SIGINT -trap exit INT - -# run at fixed interval, checking for and restarting any service marked as 'unhealthy' -pushd $LOCATION > /dev/null - while [ 1 ]; do - # get current state (running or not) and status (healthy, unhealthy, etc) - app_state=$(docker compose ps --format '{{.State}}' streamline-emr) - app_status=$(docker compose ps --format '{{.Status}}' streamline-emr) - - # check if app is running and healhty - if [ "${app_state}" = "running" ]; then - if [[ "${app_status}" = *\(unhealthy\) ]]; then - echo "Unhealthy service detected. Restarting 'streamline-emr'" - docker compose restart streamline-emr - fi - # app should be running - restart - else - echo "Container not running. Starting 'streamline-emr'" - docker compose up -d streamline-emr - fi - - # wait a little while before checking again - sleep $INTERVAL - done -popd > /dev/null \ No newline at end of file diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/monitor b/root/var/signalytic/clientapps/streamline/streamline-emr/monitor deleted file mode 100755 index 824a808e..00000000 --- a/root/var/signalytic/clientapps/streamline/streamline-emr/monitor +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" - -SYSTEMLOG=${SYSTEMLOG:-1} -if ! command -v bgsystemlog &> /dev/null; then - SYSTEMLOG=0 -fi - -# get container info -APP_CONTAINER=${APP_CONTAINER:-streamline-emr} -if grep -q "^APP_CONTAINER=" ${LOCATION}/.env > /dev/null 2>&1; then - APP_CONTAINER=$(sed -n 's/APP_CONTAINER=//p' ${LOCATION}/.env) -fi - -pushd $LOCATION > /dev/null - # get process status for each service - echo "checking streamline-emr service status" - for service in $(docker compose ps --services); do - status=$(docker compose ps --format '{"name":"{{.Names}}","created":"{{.CreatedAt}}","elapsed":"{{.RunningFor}}","state":"{{.State}}","status":"{{.Status}}"}' ${service}) - if [ "${status}" ]; then - echo $status - if [ "$SYSTEMLOG" -eq 1 ]; then - bgsystemlog -j streamline_emr_status -m ${status} - fi - else - echo "no status results found for service ${service}" - fi - done - # get resource usage - echo "checking streamline-emr resource usage" - status=$(docker stats --no-stream --format '{"name":"{{.Name}}","cpu_p":"{{.CPUPerc}}","mem_p":"{{.MemPerc}}","mem_u":"{{.MemUsage}}","net_io":"{{.NetIO}}","block_io":"{{.BlockIO}}"}' ${APP_CONTAINER}) - if [ "${status}" ]; then - echo $status - if [ "$SYSTEMLOG" -eq 1 ]; then - bgsystemlog -j streamline_emr_usage -m ${status} - fi - else - echo "no usage results found for container ${APP_CONTAINER}" - fi -popd > /dev/null - -echo "done." diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/start b/root/var/signalytic/clientapps/streamline/streamline-emr/start deleted file mode 100755 index 94157d34..00000000 --- a/root/var/signalytic/clientapps/streamline/streamline-emr/start +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" - -VERSION_FILE=${LOCATION}/version -version=$(head -n 1 ${VERSION_FILE} 2>/dev/null) -if [ $? -ne 0 ];then - version="unknown version" -fi -echo "Starting Streamline EMR: $version" - -# work from the install root -pushd $LOCATION > /dev/null - # make sure we're always working from a clean state - ./stop - - # start the application - echo "starting services" - docker compose up -d - - # start autoheal to restart unhealthy containers - ./autoheal & -popd > /dev/null - -echo "done." diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/stop b/root/var/signalytic/clientapps/streamline/streamline-emr/stop deleted file mode 100755 index 54701b32..00000000 --- a/root/var/signalytic/clientapps/streamline/streamline-emr/stop +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" - -echo "stopping streamline emr" -pushd $LOCATION > /dev/null - if [ -f autoheal.pid ];then - kill $(cat autoheal.pid) - fi - docker compose down -popd > /dev/null -echo "done." diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/template.app.conf b/root/var/signalytic/clientapps/streamline/streamline-emr/template.app.conf deleted file mode 100644 index 02158055..00000000 --- a/root/var/signalytic/clientapps/streamline/streamline-emr/template.app.conf +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "Streamline EMR", - "dbsync": { - "database": { - "type": "mysql", - "version": "8.0.23", - "name": "main", - "username": "", - "password": "", - "db_path": "/var/lib/mysql", - "logs_path": "/var/lib/mysql/logs", - "log_pattern": "binlog.[0-9]*" - }, - "blockgraph": { - "district": "", - "name": "", - "username": "", - "password": "" - }, - "instances": { - "streamline-emr": { - "source": "streamline-emr", - "node": "", - "location": "_default" - } - } - } -} diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/template.env b/root/var/signalytic/clientapps/streamline/streamline-emr/template.env deleted file mode 100644 index 9821b823..00000000 --- a/root/var/signalytic/clientapps/streamline/streamline-emr/template.env +++ /dev/null @@ -1,5 +0,0 @@ -COMPOSE_PROJECT_NAME=streamline-emr -DATA_DIR=/var/signalytic/clientapps/streamline/streamline-emr/data -APP_IMAGE=streamline-emr:latest -APP_CONTAINER=streamline-emr -APP_PORT=3000 \ No newline at end of file