#!/bin/bash # installUispToolsStandAlone.sh VERSION=0.0.2.2024-02-18:0002 echo installUispToolsStandAlone version $VERSION UISPTOOLS_USER="" UISPTOOLS_HOME_DIR="" GOOGLEAPIKEY="" NOCREATEUSER="false" MONGODBSERVERURL="" MONGODBUSERNAME="" MONGODBPASSWORD="" MONGODBDATABASE="" UNATTENDED=false UISPTOOLS_HTTPSPORT="" UISPTOOLS_HTTPPORT="" UISPTOOLS_MONGOPORT="" cliexit() { printf '%s\n' "$1" >&2 exit 1 } usage() { echo "$0 usage:" && grep " .)\ #" $0; exit 0; } while :; do case $1 in -username) # username to install under. if [ "$2" ]; then UISPTOOLS_USER=$2 shift else cliexit 'ERROR: "--username" requires a non-empty option argument.' fi ;; -httpport) # http port. if [ "$2" ]; then UISPTOOLS_HTTPPORT=$2 shift else cliexit 'ERROR: "--httpport" requires a non-empty option argument.' fi ;; -httpsport) # https port. if [ "$2" ]; then UISPTOOLS_HTTPSPORT=$2 shift else cliexit 'ERROR: "--httpsport" requires a non-empty option argument.' fi ;; -mongoport) # mongo port. if [ "$2" ]; then UISPTOOLS_MONGOPORT=$2 shift else cliexit 'ERROR: "--mongoport" requires a non-empty option argument.' fi ;; -googleapikey) # google api key. if [ "$2" ]; then GOOGLEAPIKEY=$2 shift else cliexit 'ERROR: "--googleapikey" requires a non-empty option argument.' fi ;; -alias) # Docker Process Allias. if [ "$2" ]; then UISPTOOLS_ALIAS=-$2 shift else cliexit 'ERROR: "--alias" requires a non-empty option argument.' fi ;; -installdir) # install Directory. if [ "$2" ]; then UISPTOOLS_HOME_DIR=$2 shift else cliexit 'ERROR: "--installdir" requires a non-empty option argument.' fi ;; -mongodbserverurl) # mongodb server url. if [ "$2" ]; then MONGODBSERVERURL=$2 shift else cliexit 'ERROR: "--mongodbserverurl" requires a non-empty option argument.' fi ;; -mongodbusername) # mongodb username. if [ "$2" ]; then MONGODBUSERNAME=$2 shift else cliexit 'ERROR: "--mongodbusername" requires a non-empty option argument.' fi ;; -mongodbpassword) # mongodb password. if [ "$2" ]; then MONGODBPASSWORD=$2 shift else cliexit 'ERROR: "--mongodbserverurl" requires a non-empty option argument.' fi ;; -mongodbdatabase) # mongodb database. if [ "$2" ]; then MONGODBDATABASE=$2 shift else cliexit 'ERROR: "--mongodbdatabase" requires a non-empty option argument.' fi ;; -nocreateuser) # do not create user in host operating system. NOCREATEUSER="true" ;; -h|-\?|--help) # Display help. usage exit 0 ;; --) # End of all options. shift break ;; -?*) printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 ;; *) # Default case: No more options, so break out of the loop. break esac shift done set -o nounset set -o errexit set -o pipefail SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" PATH="${PATH}:/usr/local/bin" TMP_INSTALL_DIR="${SCRIPT_DIR}" # prerequisites "command|package" PREREQUISITES=( "curl|curl" "sed|sed" "envsubst|gettext-base" ) COMPOSE_PROJECT_NAME="andrewiski/uisptools:latest" COMPOSE_CONTAINER_NAME="uisptools" USERNAME="${UISPTOOLS_USER:-$USER}" if [ ! -z "${UISPTOOLS_HOME_DIR}" ]; then echo "installdir is set to ${UISPTOOLS_HOME_DIR}" HOME_DIR="${UISPTOOLS_HOME_DIR}" else if [ -x "$(command -v getent)" ]; then if getent passwd "${USERNAME}" >/dev/null; then HOME_DIR="$(getent passwd "${USERNAME}" | cut -d: -f6)" else HOME_DIR="${UISPTOOLS_HOME_DIR:-"/home/${USERNAME}"}" fi else HOME_DIR=$(bash -c "cd ~$(printf %q "$USERNAME") && pwd") fi fi echo "Processing..." echo "UserName is $USERNAME" echo "Home Directoy is $HOME_DIR" echo "GOOGLEAPIKEY is $GOOGLEAPIKEY" echo "UISPTOOLS_ALIAS is $UISPTOOLS_ALIAS" echo "NOCREATEUSER is $NOCREATEUSER" # UISPTOOLS variables export UISPTOOLS_REPO="https://raw.githubusercontent.com/Andrewiski/uisp-tools/main" export UISPTOOLS_APP_DIR="${HOME_DIR}/uisptools/app" export UISPTOOLS_DATA_DIR="${HOME_DIR}/uisptools/data" export UISPTOOLS_DOCKER_COMPOSE_PATH="${UISPTOOLS_APP_DIR}/docker-compose.yml" if [ "${SCRIPT_DIR}" = "${UISPTOOLS_APP_DIR}" ]; then echo >&2 "Please don't run the installation script in the application directory ${UISPTOOLS_APP_DIR}" exit 1 fi #sudo mkdir -p /usr/src/UISPTOOLS #sudo chown "$USER":"docker" /usr/src/UISPTOOLS #mkdir -p /usr/src/UISPTOOLS/config #sudo chown "$USER":"docker" /usr/src/UISPTOOLS/config #mkdir -p /usr/src/UISPTOOLS/data/mongodb #sudo chown "$USER":"docker" /usr/src/UISPTOOLS/data #sudo chown "$USER":"docker" /usr/src/UISPTOOLS/data/mongodb cleanup() { # Cleanup temp install dir. if [ "${TMP_INSTALL_DIR}" != "${SCRIPT_DIR}" ] ; then #rm -rf "${TMP_INSTALL_DIR}" || true; echo "Cleanup Disabled for now" fi } fail() { echo -e "ERROR: $1" >&2 cleanup || true; exit 1 } pull_install_files(){ echo "downloading ${UISPTOOLS_REPO}/dockerCompose/docker-compose.yml" curl -LS "${UISPTOOLS_REPO}/dockerCompose/docker-compose.yml" -o "${UISPTOOLS_APP_DIR}/docker-compose.yml" echo "downloading ${UISPTOOLS_REPO}/mongodb/docker-entrypoint-initdb.d/01_createDatabase.js" curl -LS "${UISPTOOLS_REPO}/mongodb/docker-entrypoint-initdb.d/01_createDatabase.js" -o "${UISPTOOLS_DATA_DIR}/mongodb/docker-entrypoint-initdb.d/01_createDatabase.js" echo "downloading ${UISPTOOLS_REPO}/mongodb/docker-entrypoint-initdb.d/02_initWebServerPages.js" curl -LS "${UISPTOOLS_REPO}/mongodb/docker-entrypoint-initdb.d/02_initWebServerPages.js" -o "${UISPTOOLS_DATA_DIR}/mongodb/docker-entrypoint-initdb.d/02_initWebServerPages.js" } create_app_folder() { mkdir -p -m 770 "${UISPTOOLS_APP_DIR}" chown -R "${USERNAME}" "${UISPTOOLS_APP_DIR}" || true } create_data_volumes() { volumes=( "${UISPTOOLS_DATA_DIR}" "${UISPTOOLS_DATA_DIR}/config" "${UISPTOOLS_DATA_DIR}/logs" "${UISPTOOLS_DATA_DIR}/mongodb" "${UISPTOOLS_DATA_DIR}/mongodb/data" "${UISPTOOLS_DATA_DIR}/mongodb/data/db" "${UISPTOOLS_DATA_DIR}/mongodb/data/configdb" "${UISPTOOLS_DATA_DIR}/mongodb/docker-entrypoint-initdb.d" ) for volume in "${volumes[@]}"; do echo "Creating volume ${volume}" mkdir -p -m u+rwX,g+rw,o-wx "${volume}" || fail "Failed to create volume '${volume}'." if [ "${EUID}" -eq 0 ]; then chown "${USERNAME}:docker" "${volume}" || fail "Failed to change ownership of '${volume}'." fi done } create_user() { if [ -z "$(getent passwd ${USERNAME})" ]; then echo "Creating user ${USERNAME}, home dir '${HOME_DIR}'." if [ -z "$(getent group ${USERNAME})" ]; then useradd -m -d "${HOME_DIR}" -G docker "${USERNAME}" || fail "Failed to create user '${USERNAME}'" else useradd -m -d "${HOME_DIR}" -g "${USERNAME}" -G docker "${USERNAME}" || fail "Failed to create user '${USERNAME}'" fi elif ! getent group docker | grep -q "\b${USERNAME}\b" \ || ! [ -d "${HOME_DIR}" ] \ || [ "$(stat --format '%u' "${HOME_DIR}")" != "$(id -u "${USERNAME}")" ]; then echo >&2 "WARNING: User '${USERNAME}' already exists. We are going to ensure that the" echo >&2 "user is in the 'docker' group and that its home '${HOME_DIR}' dir exists and" echo >&2 "is owned by the user." #if ! [ "$UNATTENDED" = true ]; then # confirm "Would you like to continue with the installation?" || exit 1 #fi fi if ! getent group docker | grep -q "\b${USERNAME}\b"; then echo "Adding user '${USERNAME}' to docker group." if ! usermod -aG docker "${USERNAME}"; then echo >&2 "Failed to add user '${USERNAME}' to docker group." exit 1 fi fi if ! [ -d "${HOME_DIR}" ]; then echo "Creating home directory '${HOME_DIR}'." if ! mkdir -p "${HOME_DIR}"; then echo >&2 "Failed to create home directory '${HOME_DIR}'." exit 1 fi fi if [ "$(stat --format '%u' "${HOME_DIR}")" != "$(id -u "${USERNAME}")" ]; then chown "${USERNAME}" "${HOME_DIR}" fi export USER_ID=$(id -u "${USERNAME}") } remove_old_image() { local containerName="$1" local imageName="$2" local currentImage="$(docker ps --format "{{.Image}}" --filter name="^${containerName}$" || true)" if [ -z "${currentImage}" ]; then return 0; fi local allImages="$(docker images "${imageName}"* --format "{{.Repository}}:{{.Tag}}" || true)" if [ -z "${allImages}" ]; then return 0; fi for value in ${allImages}; do if [ "${value}" != "${currentImage}" ]; then echo "Removing old image '${value}'" if ! docker rmi "${value}"; then echo "Failed to remove old image '${value}'" fi fi done } get_latest_image() { echo "performing docker pull \"${COMPOSE_PROJECT_NAME}\"" docker pull "${COMPOSE_PROJECT_NAME}" } remove_old_images() { echo "Removing old images" danglingImages="$(docker images -qf "dangling=true")" if [ ! -z "${danglingImages}" ]; then echo "Removing dangling images" docker rmi ${danglingImages} || true; fi remove_old_image "${COMPOSE_PROJECT_NAME}" "${COMPOSE_CONTAINER_NAME}" } change_owner() { # only necessary when installing for the first time, as root if [ "${EUID}" -eq 0 ]; then cd "${HOME_DIR}" if ! chown -R "${USERNAME}" ./*; then echo >&2 "Failed to change config files owner" exit 1 fi fi } start_docker_containers() { echo "GOOGLEAPIKEY=$GOOGLEAPIKEY" > ${UISPTOOLS_APP_DIR}/uisptools.env echo "UISPTOOLS_ALIAS=$UISPTOOLS_ALIAS" >> ${UISPTOOLS_APP_DIR}/uisptools.env echo "UISPTOOLS_HTTPSPORT=$UISPTOOLS_HTTPSPORT" >> ${UISPTOOLS_APP_DIR}/uisptools.env echo "UISPTOOLS_HTTPPORT=$UISPTOOLS_HTTPPORT" >> ${UISPTOOLS_APP_DIR}/uisptools.env echo "UISPTOOLS_MONGOPORT=$UISPTOOLS_MONGOPORT" >> ${UISPTOOLS_APP_DIR}/uisptools.env echo "MONGODBUSERNAME=$MONGODBUSERNAME" >> ${UISPTOOLS_APP_DIR}/uisptools.env echo "MONGODBPASSWORD=$MONGODBPASSWORD" >> ${UISPTOOLS_APP_DIR}/uisptools.env echo "MONGODBDATABASE=$MONGODBDATABASE" >> ${UISPTOOLS_APP_DIR}/uisptools.env echo "MONGODBSERVERURL=$MONGODBSERVERURL" >> ${UISPTOOLS_APP_DIR}/uisptools.env echo "Starting UispTools docker containers." echo "docker compose -p "uisptools$UISPTOOLS_ALIAS" --env-file "${UISPTOOLS_APP_DIR}/uisptools.env" -f "${UISPTOOLS_DOCKER_COMPOSE_PATH}" up -d uisptools" docker compose -p "uisptools$UISPTOOLS_ALIAS" --env-file "${UISPTOOLS_APP_DIR}/uisptools.env" -f "${UISPTOOLS_DOCKER_COMPOSE_PATH}" up -d uisptools || fail "Failed to start docker containers" } confirm_success() { echo "Waiting for UISPTOOLS to start" n=0 until [ ${n} -ge 10 ] do sleep 3s UISPTOOLSRunning=true # env -i is to ensure that http[s]_proxy variables are not set # Otherwise the check would go through proxy. env -i curl -skL "http://127.0.0.1:${UISPTOOLS_HTTPPORT}" > /dev/null && break echo "." UISPTOOLSRunning=false n=$((n+1)) done docker ps if [ "${UISPTOOLSRunning}" = true ]; then echo "UISPTOOLS is running" else fail "UISPTOOLS is NOT running" fi } # prepare --silent option for curl curlSilent="" #if [ "${UNATTENDED}" = "true" ]; then # curlSilent="--silent" #fi if [ "$NOCREATEUSER" == "true" ] then echo "skipping create user" else create_user fi create_app_folder create_data_volumes pull_install_files change_owner get_latest_image start_docker_containers remove_old_images confirm_success exit 0