460 lines
12 KiB
Bash
Executable File
460 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -u -e -o pipefail
|
|
|
|
####################################################################################################
|
|
# Some helper funtions
|
|
|
|
@echo() {
|
|
echo "# $*"
|
|
}
|
|
|
|
@warn() {
|
|
@echo "Warning: $*" >&2
|
|
}
|
|
|
|
@fail() {
|
|
@echo "Error! $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
@remove() {
|
|
local f="$1"
|
|
if [[ -f ${f} ]]; then
|
|
@echo "Removing ${f}"
|
|
rm -f "${f}" || @fail "Can not delete ${f} file"
|
|
fi
|
|
}
|
|
|
|
@kill() {
|
|
for p in $1; do
|
|
if kill -0 ${p} >/dev/null 2>&1; then
|
|
kill ${p}
|
|
sleep 2
|
|
if kill -0 ${p} >/dev/null 2>&1; then
|
|
kill -9 ${p}
|
|
sleep 2
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
@wait_for() {
|
|
local m="$1"
|
|
local f="$2"
|
|
if [[ ! -f "${f}" ]]; then
|
|
printf "# ${m} (${f})"
|
|
while [[ ! -f "${f}" ]]; do
|
|
printf "."
|
|
sleep 0.5
|
|
done
|
|
printf "\n"
|
|
fi
|
|
}
|
|
|
|
####################################################################################################
|
|
# Sauce service functions
|
|
|
|
readonly SCRIPT_DIR=$(cd $(dirname $0); pwd)
|
|
readonly TMP_DIR="/tmp/angular/sauce-service"
|
|
mkdir -p ${TMP_DIR}
|
|
|
|
# Location for the saucelabs log file.
|
|
readonly SAUCE_LOG_FILE="${TMP_DIR}/sauce-connect.log"
|
|
|
|
# Location for the saucelabs ready to connection process id lock file.
|
|
readonly SAUCE_PID_FILE="${TMP_DIR}/sauce-connect.pid"
|
|
|
|
# Location for the saucelabs ready to connect lock file.
|
|
readonly SAUCE_READY_FILE="${TMP_DIR}/sauce-connect.lock"
|
|
|
|
# Location for the saucelabs params file for use by test runner.
|
|
readonly SAUCE_PARAMS_JSON_FILE="${TMP_DIR}/sauce-connect-params.json"
|
|
|
|
# Amount of seconds we wait for sauceconnect to establish a tunnel instance. In order to not
|
|
# acquire CircleCI instances for too long if sauceconnect fails, we need a connect timeout.
|
|
readonly SAUCE_READY_FILE_TIMEOUT=120
|
|
|
|
readonly SERVICE_LOCK_FILE="${TMP_DIR}/service.lock"
|
|
readonly SERVICE_START_FILE="${TMP_DIR}/service.start"
|
|
readonly SERVICE_PID_FILE="${TMP_DIR}/service.pid"
|
|
readonly SERVICE_LOG_FILE="${TMP_DIR}/service.log"
|
|
|
|
service-setup-command() {
|
|
if [[ -z "${SAUCE_USERNAME:-}" ]]; then
|
|
@fail "SAUCE_USERNAME environment variable required"
|
|
fi
|
|
|
|
if [[ -z "${SAUCE_ACCESS_KEY:-}" ]]; then
|
|
@fail "SAUCE_ACCESS_KEY environment variable required"
|
|
fi
|
|
|
|
if [[ -z "${SAUCE_TUNNEL_IDENTIFIER:-}" ]]; then
|
|
@fail "SAUCE_TUNNEL_IDENTIFIER environment variable required"
|
|
fi
|
|
|
|
local unameOut="$(uname -s)"
|
|
case "${unameOut}" in
|
|
Linux*) local machine=linux ;;
|
|
Darwin*) local machine=darwin ;;
|
|
CYGWIN*) local machine=windows ;;
|
|
MINGW*) local machine=windows ;;
|
|
MSYS_NT*) local machine=windows ;;
|
|
*) local machine=linux
|
|
printf "\nUnrecongized uname '${unameOut}'; defaulting to use node for linux.\n" >&2
|
|
printf "Please file an issue to https://github.com/bazelbuild/rules_nodejs/issues if \n" >&2
|
|
printf "you would like to add your platform to the supported rules_nodejs node platforms.\n\n" >&2
|
|
;;
|
|
esac
|
|
|
|
case "${machine}" in
|
|
# Path to sauce connect executable
|
|
linux)
|
|
if [[ -z "${BUILD_WORKSPACE_DIRECTORY:-}" ]]; then
|
|
# Started manually
|
|
SAUCE_CONNECT="${SCRIPT_DIR}/../../node_modules/sauce-connect/bin/sc"
|
|
else
|
|
# Started via `bazel run`
|
|
SAUCE_CONNECT="${BUILD_WORKSPACE_DIRECTORY}/node_modules/sauce-connect/bin/sc"
|
|
fi
|
|
;;
|
|
*)
|
|
if [[ -z "${SAUCE_CONNECT:-}" ]]; then
|
|
@fail "SAUCE_CONNECT environment variable is required on non-linux environments"
|
|
exit 1
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
if [[ ! -f ${SAUCE_CONNECT} ]]; then
|
|
@fail "sc binary not found at ${SAUCE_CONNECT}"
|
|
fi
|
|
|
|
echo "{ \"SAUCE_USERNAME\": \"${SAUCE_USERNAME}\", \"SAUCE_ACCESS_KEY\": \"${SAUCE_ACCESS_KEY}\", \"SAUCE_TUNNEL_IDENTIFIER\": \"${SAUCE_TUNNEL_IDENTIFIER}\", \"SAUCE_LOCALHOST_ALIAS_DOMAIN\": \"${SAUCE_LOCALHOST_ALIAS_DOMAIN:-}\" }" > ${SAUCE_PARAMS_JSON_FILE}
|
|
|
|
# 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
|
|
local sauce_args=(
|
|
"--no-ssl-bump-domains all"
|
|
"--logfile ${SAUCE_LOG_FILE}"
|
|
"--pidfile ${SAUCE_PID_FILE}"
|
|
"--readyfile ${SAUCE_READY_FILE}"
|
|
"--tunnel-identifier ${SAUCE_TUNNEL_IDENTIFIER}"
|
|
"--user ${SAUCE_USERNAME}"
|
|
# Don't add the --api-key here so we don't echo it out in service-pre-start
|
|
)
|
|
|
|
if [[ -n "${SAUCE_LOCALHOST_ALIAS_DOMAIN:-}" ]]; then
|
|
# Ensures that requests to the localhost alias domain are always resolved through the tunnel.
|
|
# This environment variable is usually configured on CI, and refers to a domain that has been
|
|
# locally configured in the current machine's hosts file (e.g. `/etc/hosts`). The domain should
|
|
# resolve to the current machine in Saucelabs VMs, so we need to ensure that it is resolved
|
|
# through the tunnel we going to create.
|
|
sauce_args+=("--tunnel-domains ${SAUCE_LOCALHOST_ALIAS_DOMAIN}")
|
|
fi
|
|
|
|
@echo "Sauce connect will be started with:"
|
|
echo " ${SAUCE_CONNECT} ${sauce_args[@]}"
|
|
SERVICE_COMMAND="${SAUCE_CONNECT} ${sauce_args[@]} --api-key ${SAUCE_ACCESS_KEY}"
|
|
}
|
|
|
|
# Called by pre-start & post-stop
|
|
service-cleanup() {
|
|
if [[ -f "${SAUCE_PID_FILE}" ]]; then
|
|
local p=$(cat "${SAUCE_PID_FILE}")
|
|
@echo "Stopping Sauce Connect (pid $p)..."
|
|
@kill $p
|
|
fi
|
|
@remove "${SAUCE_PID_FILE}"
|
|
@remove "${SAUCE_READY_FILE}"
|
|
@remove "${SAUCE_PARAMS_JSON_FILE}"
|
|
}
|
|
|
|
# Called before service is setup
|
|
service-pre-setup() {
|
|
service-cleanup
|
|
}
|
|
|
|
# Called after service is setup
|
|
service-post-setup() {
|
|
@echo " sauce params : ${SAUCE_PARAMS_JSON_FILE}"
|
|
}
|
|
|
|
# Called before service is started
|
|
service-pre-start() {
|
|
return
|
|
}
|
|
|
|
# Called after service is started
|
|
service-post-start() {
|
|
if [[ ! -f "${SAUCE_PID_FILE}" ]]; then
|
|
printf "# Waiting for Sauce Connect Proxy process (${SAUCE_PID_FILE})"
|
|
while [[ ! -f "${SAUCE_PID_FILE}" ]]; do
|
|
if ! @serviceStatus >/dev/null 2>&1; then
|
|
printf "\n"
|
|
@serviceStop
|
|
@echo "Service failed to start!"
|
|
service-failed-setup
|
|
exit 1
|
|
fi
|
|
printf "."
|
|
sleep 0.5
|
|
done
|
|
printf "\n"
|
|
fi
|
|
@echo "Sauce Connect Proxy started (pid $(cat "${SAUCE_PID_FILE}"))"
|
|
}
|
|
|
|
# Called if service fails to start
|
|
service-failed-setup() {
|
|
if [[ -f "${SERVICE_LOG_FILE}" ]]; then
|
|
@echo "tail ${SERVICE_LOG_FILE}:"
|
|
echo "--------------------------------------------------------------------------------"
|
|
tail "${SERVICE_LOG_FILE}"
|
|
echo "--------------------------------------------------------------------------------"
|
|
echo "^^^^^ ${SERVICE_LOG_FILE} ^^^^^"
|
|
fi
|
|
}
|
|
|
|
# Called by ready-wait action
|
|
service-ready-wait() {
|
|
if [[ ! -f "${SAUCE_PID_FILE}" ]]; then
|
|
@fail "Sauce Connect not running"
|
|
fi
|
|
if [[ ! -f "${SAUCE_READY_FILE}" ]]; then
|
|
# Wait for saucelabs tunnel to connect
|
|
printf "# Waiting for saucelabs tunnel to connect (${SAUCE_READY_FILE})"
|
|
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."
|
|
if [[ -f "${SAUCE_LOG_FILE}" ]]; then
|
|
echo "================================================================================"
|
|
echo "${SAUCE_LOG_FILE}:"
|
|
cat "${SAUCE_LOG_FILE}"
|
|
fi
|
|
exit 5
|
|
fi
|
|
|
|
printf "."
|
|
sleep 0.5
|
|
done
|
|
printf "\n"
|
|
@echo "Saucelabs tunnel connected"
|
|
else
|
|
@echo "Saucelabs tunnel already connected"
|
|
fi
|
|
}
|
|
|
|
# Called before service is stopped
|
|
service-pre-stop() {
|
|
return
|
|
}
|
|
|
|
# Called after service is stopped
|
|
service-post-stop() {
|
|
service-cleanup
|
|
}
|
|
|
|
####################################################################################################
|
|
# Generic service functions
|
|
# This uses functions setup above but nothing below should be specific to saucelabs
|
|
|
|
@serviceLock() {
|
|
# Check is Lock File exists, if not create it and set trap on exit
|
|
printf "# Waiting for service action lock (${SERVICE_LOCK_FILE})"
|
|
while true; do
|
|
if { set -C; 2>/dev/null >"${SERVICE_LOCK_FILE}"; }; then
|
|
trap "rm -f \"${SERVICE_LOCK_FILE}\"" EXIT
|
|
printf "\n"
|
|
break
|
|
fi
|
|
printf "."
|
|
sleep 0.5
|
|
done
|
|
@echo "Acquired service action lock"
|
|
}
|
|
|
|
@serviceStatus() {
|
|
if [ -f "${SERVICE_PID_FILE}" ] && [ ! -z "$(cat "${SERVICE_PID_FILE}")" ]; then
|
|
local p=$(cat "${SERVICE_PID_FILE}")
|
|
|
|
if kill -0 $p >/dev/null 2>&1; then
|
|
@echo "Service is running (pid $p)"
|
|
return 0
|
|
else
|
|
@echo "Service is not running (process PID $p not exists)"
|
|
return 1
|
|
fi
|
|
else
|
|
@echo "Service is not running"
|
|
return 2
|
|
fi
|
|
}
|
|
|
|
@serviceSetup() {
|
|
if @serviceStatus >/dev/null 2>&1; then
|
|
@echo "Service already running (pid $(cat "${SERVICE_PID_FILE}"))"
|
|
return 0
|
|
fi
|
|
|
|
@echo "Setting up service..."
|
|
@remove "${SERVICE_PID_FILE}"
|
|
@remove "${SERVICE_START_FILE}"
|
|
touch "${SERVICE_LOG_FILE}" >/dev/null 2>&1 || @fail "Can not create ${SERVICE_LOG_FILE} file"
|
|
@echo " service pid : ${SERVICE_PID_FILE}"
|
|
@echo " service logs : ${SERVICE_LOG_FILE}"
|
|
service-pre-setup
|
|
service-setup-command
|
|
|
|
(
|
|
(
|
|
if [[ -z "${SERVICE_COMMAND:-}" ]]; then
|
|
@fail "No SERVICE_COMMAND is set"
|
|
fi
|
|
@wait_for "Waiting for start file" "${SERVICE_START_FILE}"
|
|
${SERVICE_COMMAND}
|
|
) >>"${SERVICE_LOG_FILE}" 2>&1
|
|
) &
|
|
echo $! >"${SERVICE_PID_FILE}"
|
|
|
|
if @serviceStatus >/dev/null 2>&1; then
|
|
@echo "Service setup (pid $(cat "${SERVICE_PID_FILE}"))"
|
|
service-post-setup
|
|
else
|
|
@echo "Error setting up Service!"
|
|
service-failed-setup
|
|
exit 1
|
|
fi
|
|
|
|
return $?
|
|
}
|
|
|
|
@serviceStart() {
|
|
if @serviceStatus >/dev/null 2>&1; then
|
|
@echo "Service already setup (pid $(cat "${SERVICE_PID_FILE}"))"
|
|
else
|
|
@serviceSetup
|
|
fi
|
|
if [[ -f "${SERVICE_START_FILE}" ]]; then
|
|
@echo "Service already started"
|
|
else
|
|
@echo "Starting service..."
|
|
service-pre-start
|
|
touch "${SERVICE_START_FILE}" >/dev/null 2>&1 || @err "Can not create ${SERVICE_START_FILE} file"
|
|
service-post-start
|
|
@echo "Service started"
|
|
fi
|
|
}
|
|
|
|
@serviceStop() {
|
|
if @serviceStatus >/dev/null 2>&1; then
|
|
touch "${SERVICE_PID_FILE}" >/dev/null 2>&1 || @fail "Can not touch ${SERVICE_PID_FILE} file"
|
|
|
|
service-pre-stop
|
|
@echo "Stopping sevice (pid $(cat "${SERVICE_PID_FILE}"))..."
|
|
@kill $(cat "${SERVICE_PID_FILE}")
|
|
|
|
if @serviceStatus >/dev/null 2>&1; then
|
|
@fail "Error stopping Service! Service already running with PID $(cat "${SERVICE_PID_FILE}")"
|
|
else
|
|
@echo "Service stopped"
|
|
@remove "${SERVICE_PID_FILE}"
|
|
@remove "${SERVICE_START_FILE}"
|
|
service-post-stop
|
|
fi
|
|
|
|
return 0
|
|
else
|
|
@warn "Service is not running"
|
|
@remove "${SERVICE_PID_FILE}"
|
|
@remove "${SERVICE_START_FILE}"
|
|
service-post-stop
|
|
fi
|
|
}
|
|
|
|
@serviceStartReadyWait() {
|
|
@serviceStart
|
|
@serviceReadyWait
|
|
}
|
|
|
|
@serviceReadyWait() {
|
|
service-ready-wait
|
|
}
|
|
|
|
@serviceRestart() {
|
|
@serviceStop
|
|
@serviceStart
|
|
}
|
|
|
|
@serviceTail() {
|
|
@echo "tail ${SERVICE_LOG_FILE}:"
|
|
tail -f "${SERVICE_LOG_FILE}"
|
|
}
|
|
|
|
@serviceLog() {
|
|
@echo "cat ${SERVICE_LOG_FILE}:"
|
|
echo "--------------------------------------------------------------------------------"
|
|
cat "${SERVICE_LOG_FILE}"
|
|
echo "--------------------------------------------------------------------------------"
|
|
echo "^^^^^ ${SERVICE_LOG_FILE} ^^^^^"
|
|
}
|
|
|
|
case "${1:-}" in
|
|
setup)
|
|
@serviceLock
|
|
@serviceSetup
|
|
;;
|
|
start)
|
|
@serviceLock
|
|
@serviceStart
|
|
;;
|
|
start-ready-wait)
|
|
@serviceLock
|
|
@serviceStartReadyWait
|
|
;;
|
|
ready-wait)
|
|
@serviceLock
|
|
@serviceReadyWait
|
|
;;
|
|
stop)
|
|
@serviceLock
|
|
@serviceStop
|
|
;;
|
|
restart)
|
|
@serviceLock
|
|
@serviceRestart
|
|
;;
|
|
status)
|
|
@serviceLock
|
|
@serviceStatus
|
|
;;
|
|
run)
|
|
(
|
|
service-setup-command
|
|
if [[ -z "${SERVICE_COMMAND:-}" ]]; then
|
|
@fail "No SERVICE_COMMAND is set"
|
|
fi
|
|
${SERVICE_COMMAND}
|
|
)
|
|
;;
|
|
log)
|
|
@serviceLog
|
|
;;
|
|
tail)
|
|
@serviceTail
|
|
;;
|
|
*)
|
|
@echo "Actions: [setup|start|start-read-wait|ready-wait|stop|restart|status|run|tail]"
|
|
exit 1
|
|
;;
|
|
esac
|