diff --git a/src/server_manager/install_scripts/install_server.sh b/src/server_manager/install_scripts/install_server.sh index 8f591688..9d3f8bc7 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/src/server_manager/install_scripts/install_server.sh @@ -68,6 +68,17 @@ function run_step() { fi } +function confirm() { + echo -n "$1" + local RESPONSE + read RESPONSE + RESPONSE=$(echo "$RESPONSE" | tr '[A-Z]' '[a-z]') + if [[ -z "$RESPONSE" ]] || [[ "$RESPONSE" = "y" ]] || [[ "$RESPONSE" = "yes" ]]; then + return 0 + fi + return 1 +} + function command_exists { command -v "$@" > /dev/null 2>&1 } @@ -80,19 +91,111 @@ function log_for_sentry() { # Check to see if docker is installed. function verify_docker_installed() { - if ! command_exists docker; then - log_error "Docker CE must be installed, please run \"curl -sS https://get.docker.com/ | sh\" or visit https://docs.docker.com/install/" + if command_exists docker; then + return 0 + fi + log_error "NOT INSTALLED" + echo -n + if ! confirm "> Would you like to install Docker? This will run 'curl -sS https://get.docker.com/ | sh'. [Y/n] "; then + exit 0 + fi + if ! run_step "Installing Docker" install_docker; then + log_error "Docker installation failed, please visit https://docs.docker.com/install for instructions." exit 1 fi + echo -n "> Verifying Docker installation................ " + command_exists docker } function verify_docker_running() { - if ! docker info > /dev/null 2>&1 ; then - log_error "It seems like you may not have permission to run Docker. To solve this, you may need to add your user to the docker group. We recommend running \"sudo usermod -a -G docker $USER && newgrp docker\" and then attempting to install again." - exit 1 + local readonly STDERR_OUTPUT + STDERR_OUTPUT=$(safe_docker info 2>&1 >/dev/null) + local readonly RET=$? + if [[ $RET -eq 0 ]]; then + return 0 + elif [[ $STDERR_OUTPUT = *"Is the docker daemon running"* ]]; then + start_docker fi } +function verify_docker_permissions() { + if user_in_docker_group; then + return 0 + fi + log_error "FAILED" + local readonly PROMPT="> It seems like you may not have permission to run Docker. To solve this, we will attempt to add your user to the docker group by running 'sudo usermod -a -G docker $USER'. Would you like to proceed? [Y/n] " + if ! confirm "$PROMPT"; then + exit 0 + fi + if run_step "Adding $USER to docker group" add_user_to_docker_group; then + echo -n "> Docker ready................................. " + else + log_error "FAILED" + return 1 + fi +} + +function install_docker() { + curl -sS https://get.docker.com/ | sh > /dev/null 2>&1 +} + +function user_in_docker_group() { + groups $USER | grep -E '(^|\s)docker(\s|$)' > /dev/null 2>&1 +} + +function add_user_to_docker_group() { + sudo usermod -a -G docker $USER +} + +function start_docker() { + sudo systemctl start docker.service > /dev/null 2>&1 + sudo systemctl enable docker.service > /dev/null 2>&1 +} + +# Runs commands in the docker group. +function safe_docker() { + sg docker -c "docker $@" +} + +function docker_container_exists() { + safe_docker ps | grep $1 >/dev/null 2>&1 +} + +function remove_shadowbox_container() { + remove_docker_container shadowbox +} + +function remove_watchtower_container() { + remove_docker_container watchtower +} + +function remove_docker_container() { + safe_docker "rm -f $1" >/dev/null 2>&1 +} + +function handle_docker_container_conflict() { + local readonly CONTAINER_NAME=$1 + local readonly EXIT_ON_NEGATIVE_USER_RESPONSE=$2 + local PROMPT="> The container name \"$CONTAINER_NAME\" is already in use by another container. This may happen when running this script multiple times." + if $EXIT_ON_NEGATIVE_USER_RESPONSE; then + PROMPT="$PROMPT We will attempt to remove the existing container and restart it. Would you like to proceed? [Y/n] " + else + PROMPT="$PROMPT Would you like to replace this container? If you answer no, we will proceed with the remainder of the installation. [Y/n] " + fi + if ! confirm "$PROMPT"; then + if $EXIT_ON_NEGATIVE_USER_RESPONSE; then + exit 0 + fi + return 0 + fi + if run_step "Removing $CONTAINER_NAME container" remove_"$CONTAINER_NAME"_container ; then + echo -n "> Restarting $CONTAINER_NAME ........................ " + start_"$CONTAINER_NAME" + return $? + fi + return 1 +} + # Set trap which publishes error tag only if there is an error. function finish { EXIT_CODE=$? @@ -168,17 +271,43 @@ function start_shadowbox() { -e "SB_METRICS_URL=${SB_METRICS_URL:-}" -e "SB_DEFAULT_SERVER_NAME=${SB_DEFAULT_SERVER_NAME:-}" ) - docker run -d "${docker_shadowbox_flags[@]}" "${SB_IMAGE}" >/dev/null + local readonly RUN_DOCKER_ARGS="run -d ${docker_shadowbox_flags[@]} ${SB_IMAGE}" + local readonly STDERR_OUTPUT + STDERR_OUTPUT=$(safe_docker "$RUN_DOCKER_ARGS" 2>&1 >/dev/null) + local readonly RET=$? + if [[ $RET -eq 0 ]]; then + return 0 + fi + log_error "FAILED" + if docker_container_exists shadowbox; then + handle_docker_container_conflict shadowbox true + else + log_error "$STDERR_OUTPUT" + return 1 + fi } function start_watchtower() { # Start watchtower to automatically fetch docker image updates. # Set watchtower to refresh every 30 seconds if a custom SB_IMAGE is used (for # testing). Otherwise refresh every hour. - readonly WATCHTOWER_REFRESH_SECONDS="${WATCHTOWER_REFRESH_SECONDS:-3600}" + local WATCHTOWER_REFRESH_SECONDS="${WATCHTOWER_REFRESH_SECONDS:-3600}" declare -a docker_watchtower_flags=(--name watchtower --restart=always) docker_watchtower_flags+=(-v /var/run/docker.sock:/var/run/docker.sock) - docker run -d "${docker_watchtower_flags[@]}" v2tec/watchtower --cleanup --tlsverify --interval $WATCHTOWER_REFRESH_SECONDS >/dev/null + local readonly RUN_DOCKER_ARGS="run -d ${docker_watchtower_flags[@]} v2tec/watchtower --cleanup --tlsverify --interval $WATCHTOWER_REFRESH_SECONDS" + local readonly STDERR_OUTPUT + STDERR_OUTPUT=$(safe_docker "$RUN_DOCKER_ARGS" 2>&1 >/dev/null) + local readonly RET=$? + if [[ $RET -eq 0 ]]; then + return 0 + fi + log_error "FAILED" + if docker_container_exists watchtower; then + handle_docker_container_conflict watchtower false + else + log_error "$STDERR_OUTPUT" + return 1 + fi } # Waits for Shadowbox to be up and healthy @@ -201,9 +330,11 @@ function add_api_url_to_config() { } function check_firewall() { - local -r ACCESS_KEY_PORT=$(docker exec shadowbox node -e "console.log($(curl --insecure -s ${LOCAL_API_URL}/access-keys)['accessKeys'][0]['port'])") + local readonly GET_ACCESS_KEYS=$(curl --insecure -s ${LOCAL_API_URL}/access-keys) + local readonly GET_ACCESS_KEY_PORT="exec shadowbox node -e 'console.log($GET_ACCESS_KEYS[\"accessKeys\"][0][\"port\"])'" + local -r ACCESS_KEY_PORT=$(safe_docker "$GET_ACCESS_KEY_PORT") if ! curl --max-time 5 --cacert "${SB_CERTIFICATE_FILE}" -s "${PUBLIC_API_URL}/access-keys" >/dev/null; then - echo "BLOCKED" + log_error "BLOCKED" FIREWALL_STATUS="\ You won’t be able to access it externally, despite your server being correctly set up, because there's a firewall (in this machine, your router or cloud @@ -218,7 +349,7 @@ ${ACCESS_KEY_PORT}. " else FIREWALL_STATUS="\ -If have connection problems, it may be that your router or cloud provider +If you have connection problems, it may be that your router or cloud provider blocks inbound connections, even though your machine seems to allow them. - If you plan to have a single access key to access your server make sure @@ -233,6 +364,7 @@ blocks inbound connections, even though your machine seems to allow them. install_shadowbox() { run_step "Verifying that Docker is installed" verify_docker_installed + run_step "Verifying Docker permissions" verify_docker_permissions run_step "Verifying that Docker daemon is running" verify_docker_running log_for_sentry "Creating shadowbox directory"