#!/usr/bin/env bash

set -u -e -o pipefail

# Prints out usage information for the script.
function printUsage {
  echo -e "\e[1mrun-bazel-via-tunnel.sh\e[0m - Runs a bazel command using a saucelabs tunnel

  \e[1mUsage:\e[0m $0 --tunnel-id=<tunnel_id> \\
              --username=<saucelabs_username> --key=<saucelabs_key> <bazel command>

  \e[1mExample:\e[0m ./run-bazel-via-tunnel.sh --tunnel-id=<tunnel_id> \\
              --username=<saucelabs_username> --key=<saucelabs_key> \\
              yarn bazel test //src:everything

  Flags:
    --username: The saucelabs username
    --key: The saucelabs access key
    --tunnel-id: An identifier for the saucelabs tunnel";
}

# Ensures a file is created, creating directories for the full path as needed.
function touch-safe {
  for f in "$@"; do
    [ -d $f:h ] || mkdir -p $f:h && command touch $f
  done
}

# The root directory of the git project the script is running in.
readonly GIT_ROOT_DIR=$(git rev-parse --show-toplevel 2> /dev/null)
# Location for the saucelabs log file.
readonly SAUCE_LOG_FILE=/tmp/angular/sauce-connect.log
# Location for the saucelabs ready to connect lock file.
readonly SAUCE_READY_FILE=/tmp/angular/sauce-connect-ready-file.lock
# Location for the saucelabs ready to connection process id lock file.
readonly SAUCE_PID_FILE=/tmp/angular/sauce-connect-pid-file.lock
# Amount of seconds we wait for sauceconnect to establish a tunnel instance. In order to not
# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout.
readonly SAUCE_READY_FILE_TIMEOUT=120

# Create saucelabs log file if it doesn't already exist.
touch-safe $SAUCE_LOG_FILE;

# Handle configuration of script from command line flags and arguments
OPTIONS=$(getopt -u -l tunnel-id:,username:,key:,help --options "" -- "$@")
# Exit if flag parsing fails.
if [ $? != 0 ] ; then echo "Failed to parse flags, exiting" && printUsage >&2 ; exit 1 ; fi
set -- $OPTIONS
while true; do
  case "$1" in
    --tunnel-id)
        shift
        SAUCE_TUNNEL_IDENTIFIER=$1
        ;;
    --username)
        shift
        SAUCE_USERNAME=$1
        ;;
    --key)
        shift
        SAUCE_ACCESS_KEY=$1
        ;;
    --help)
        printUsage
        exit 2
        ;;
    --)
        shift
        USER_COMMAND=$@
        break
        ;;
    *)
        shift
        ;;
  esac
done

# Check each required flag and parameter
if [[ -z ${SAUCE_TUNNEL_IDENTIFIER+x} ]]; then
  echo "Missing required flag: --tunnel-id"
  badCommandSyntax=1
fi
if [[ -z ${SAUCE_USERNAME+x} ]]; then
  echo "Missing required flag: --username"
  badCommandSyntax=1
fi
if [[ -z ${SAUCE_ACCESS_KEY+x} ]]; then
  echo "Missing required flag: --key"
  badCommandSyntax=1
fi
if [[ "${USER_COMMAND}" == "" ]]; then
  echo "Missing required bazel command: Bazel command for running in saucelabs tunnel"
  badCommandSyntax=1
elif [[ ! $USER_COMMAND =~ ^(yarn bazel) ]]; then
  echo "The command provided must be a bazel command run via yarn, beginning with \"yarn bazel\""
  badCommandSyntax=1
fi

# If any required flag or parameter were found to be missing or incorrect, exit the script.
if [[ ${badCommandSyntax+x} ]]; then
  echo
  printUsage
  exit 1
fi


# Command arguments that will be passed to sauce-connect.
# By default we disable SSL bumping for all requests. This is because SSL bumping is
# not needed for our test setup and in order to perform the SSL bumping, Saucelabs
# intercepts all HTTP requests in the tunnel VM and modifies them. This can cause
# flakiness as it makes all requests dependent on the SSL bumping middleware.
# See: https://wiki.saucelabs.com/display/DOCS/Troubleshooting+Sauce+Connect#TroubleshootingSauceConnect-DisablingSSLBumping
sauceArgs="--no-ssl-bump-domains all"
sauceArgs="${sauceArgs} --logfile ${SAUCE_LOG_FILE}"
sauceArgs="${sauceArgs} --readyfile ${SAUCE_READY_FILE}"
sauceArgs="${sauceArgs} --pidfile ${SAUCE_PID_FILE}"
sauceArgs="${sauceArgs} --tunnel-identifier ${SAUCE_TUNNEL_IDENTIFIER}"
sauceArgs="${sauceArgs} -u ${SAUCE_USERNAME}"

#########################
# Open saucelabs tunnel #
#########################


${GIT_ROOT_DIR}/node_modules/sauce-connect/bin/sc -k $SAUCE_ACCESS_KEY ${sauceArgs} &


########################################
# Wait for saucelabs tunnel to connect #
########################################
counter=0

while [[ ! -f ${SAUCE_READY_FILE} ]]; do
  counter=$((counter + 1))

  # Counter needs to be multiplied by two because the while loop only sleeps a half second.
  # This has been made in favor of better progress logging (printing dots every half second)
  if [ $counter -gt $[${SAUCE_READY_FILE_TIMEOUT} * 2] ]; then
    echo "Timed out after ${SAUCE_READY_FILE_TIMEOUT} seconds waiting for tunnel ready file."
    echo "Printing logfile output:"
    echo ""
    cat ${SAUCE_LOG_FILE}
    exit 5
  fi

  printf "."
  sleep 0.5
done

#########################
# Execute Bazel command #
#########################

# Prevent immediate exit for Bazel test failures
set +e

(
  cd $GIT_ROOT_DIR && \
  # Run bazel command with saucelabs specific environment variables passed to the action
  # The KARMA_WEB_TEST_MODE and SAUCE_TUNNEL_IDENTIFIER environment variables provide
  # envirnment variables to be read in the karma configuration file to set correct
  # configurations for karma saucelabs and browser configs.
  # Usage of these envirnment variables can be seen in this repo in
  # /karma-js.conf.js and /browser-providers.conf.js
  eval "$USER_COMMAND --define=KARMA_WEB_TEST_MODE=SL_REQUIRED \
                      --action_env=SAUCE_USERNAME=$SAUCE_USERNAME \
                      --action_env=SAUCE_ACCESS_KEY=$SAUCE_ACCESS_KEY \
                      --action_env=SAUCE_READY_FILE=$SAUCE_READY_FILE \
                      --action_env=SAUCE_PID_FILE=$SAUCE_PID_FILE \
                      --action_env=SAUCE_TUNNEL_IDENTIFIER=$SAUCE_TUNNEL_IDENTIFIER"
)
BAZEL_EXIT_CODE=$?
echo "Exit code for bazel command was: $BAZEL_EXIT_CODE"

# Reenable immediate exit for failure exit code
set -e

##############################
# Close the saucelabs tunnel #
##############################

if [[ ! -f ${SAUCE_PID_FILE} ]]; then
  echo "Could not find Saucelabs tunnel PID file. Cannot stop tunnel.."
  exit 1
fi

echo "Shutting down Sauce Connect tunnel"

# The process id for the sauce-connect instance is stored inside of the pidfile.
tunnelProcessId=$(cat ${SAUCE_PID_FILE})

# Kill the process by using the PID that has been read from the pidfile. Note that
# we cannot use killall because CircleCI base container images don't have it installed.
kill ${tunnelProcessId}

while (ps -p ${tunnelProcessId} &> /dev/null); do
  printf "."
  sleep .5
done

echo ""
echo "Sauce Connect tunnel has been shut down"

exit $BAZEL_EXIT_CODE