#!/bin/bash
#
#  Mint (C) 2017-2022 Minio, Inc.
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

CONTAINER_ID=$(grep -o -e '[0-f]\{12,\}' /proc/1/cpuset | awk '{print substr($1, 1, 12)}')
MINT_DATA_DIR=${MINT_DATA_DIR:-/mint/data}
MINT_MODE=${MINT_MODE:-core}
SERVER_REGION=${SERVER_REGION:-us-east-1}
ENABLE_HTTPS=${ENABLE_HTTPS:-0}
ENABLE_VIRTUAL_STYLE=${ENABLE_VIRTUAL_STYLE:-0}
RUN_ON_FAIL=${RUN_ON_FAIL:-0}

if [ -z "$SERVER_ENDPOINT" ]; then
	SERVER_ENDPOINT="play.minio.io:9000"
	ACCESS_KEY="Q3AM3UQ867SPQQA43P2F"
	SECRET_KEY="zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"
	ENABLE_HTTPS=1
fi

if [ "$ENABLE_VIRTUAL_STYLE" -eq 1 ]; then
	SERVER_IP="${SERVER_ENDPOINT%%:*}"
	SERVER_PORT="${SERVER_ENDPOINT/*:/}"
	# Check if SERVER_IP is actually IPv4 address
	IFS=. read -ra octets <<<"$SERVER_IP"
	if [ "${#octets[@]}" -ne 4 ]; then
		echo "$SERVER_IP must be an IP address"
		exit 1
	fi
	for octet in "${octets[@]}"; do
		if [ "$octet" -lt 0 ] 2>/dev/null || [ "$octet" -gt 255 ] 2>/dev/null; then
			echo "$SERVER_IP must be an IP address"
			exit 1
		fi
	done
fi

ROOT_DIR="$PWD"
TESTS_DIR="$ROOT_DIR/run/core"

BASE_LOG_DIR="$ROOT_DIR/log"
LOG_FILE="log.json"
ERROR_FILE="error.log"
mkdir -p "$BASE_LOG_DIR"

function humanize_time() {
	time="$1"
	days=$((time / 60 / 60 / 24))
	hours=$((time / 60 / 60 % 24))
	minutes=$((time / 60 % 60))
	seconds=$((time % 60))

	((days > 0)) && echo -n "$days days "
	((hours > 0)) && echo -n "$hours hours "
	((minutes > 0)) && echo -n "$minutes minutes "
	((days > 0 || hours > 0 || minutes > 0)) && echo -n "and "
	echo "$seconds seconds"
}

function run_test() {
	if [ ! -d "$1" ]; then
		return 1
	fi

	start=$(date +%s)

	mkdir -p "$BASE_LOG_DIR/$sdk_name"

	(cd "$sdk_dir" && ./run.sh "$BASE_LOG_DIR/$LOG_FILE" "$BASE_LOG_DIR/$sdk_name/$ERROR_FILE")
	rv=$?
	end=$(date +%s)
	duration=$(humanize_time $((end - start)))

	if [ "$rv" -eq 0 ]; then
		echo "done in $duration"
	else
		echo "FAILED in $duration"
		entry=$(tail -n 1 "$BASE_LOG_DIR/$LOG_FILE")
		status=$(jq -e -r .status <<<"$entry")
		jq_rv=$?
		if [ "$jq_rv" -ne 0 ]; then
			echo "$entry"
		fi
		## Show error.log when status is empty or not "FAIL".
		## This may happen when test run failed without providing logs.
		if [ "$jq_rv" -ne 0 ] || [ -z "$status" ] || { [ "$status" != "FAIL" ] && [ "$status" != "fail" ]; }; then
			cat "$BASE_LOG_DIR/$sdk_name/$ERROR_FILE"
		else
			jq . <<<"$entry"
		fi
	fi
	return $rv
}

function trust_s3_endpoint_tls_cert() {
	# Download the public certificate from the server
	openssl s_client -showcerts -verify 5 -connect "$SERVER_ENDPOINT" </dev/null |
		awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{ if(/BEGIN CERTIFICATE/){a++}; out="cert"a".pem"; print >out}'
	for cert in *.pem; do
		cat "${cert}" >>/etc/ssl/certs/ca-certificates.crt
	done

	# Ask different SDKs/tools to load system certificates
	export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
	export NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
	export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
	export AWS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
}

function main() {
	export MINT_DATA_DIR
	export MINT_MODE
	export SERVER_ENDPOINT
	export SERVER_IP
	export SERVER_PORT

	export ACCESS_KEY
	export SECRET_KEY
	export ENABLE_HTTPS
	export SERVER_REGION
	export ENABLE_VIRTUAL_STYLE
	export RUN_ON_FAIL

	echo "Running with"
	echo "SERVER_ENDPOINT:      $SERVER_ENDPOINT"
	echo "ACCESS_KEY:           $ACCESS_KEY"
	echo "SECRET_KEY:           ***REDACTED***"
	echo "ENABLE_HTTPS:         $ENABLE_HTTPS"
	echo "SERVER_REGION:        $SERVER_REGION"
	echo "MINT_DATA_DIR:        $MINT_DATA_DIR"
	echo "MINT_MODE:            $MINT_MODE"
	echo "ENABLE_VIRTUAL_STYLE: $ENABLE_VIRTUAL_STYLE"
	echo "RUN_ON_FAIL:          $RUN_ON_FAIL"
	echo
	echo "To get logs, run 'docker cp ${CONTAINER_ID}:/mint/log /tmp/mint-logs'"
	echo

	[ "$ENABLE_HTTPS" == "1" ] && trust_s3_endpoint_tls_cert

	declare -a run_list
	sdks=("$@")

	if [ "$#" -eq 0 ]; then
		cd "$TESTS_DIR" || exit
		sdks=(*)
		cd .. || exit
	fi

	for sdk in "${sdks[@]}"; do
		sdk=$(basename "$sdk")
		run_list=("${run_list[@]}" "$TESTS_DIR/$sdk")
	done

	count="${#run_list[@]}"
	i=0
	j=0
	for sdk_dir in "${run_list[@]}"; do
		sdk_name=$(basename "$sdk_dir")
		((i++))
		((j++))
		if [ ! -d "$sdk_dir" ]; then
			echo "Test $sdk_name not found. Exiting Mint."
			exit 1
		fi
		echo -n "($j/$count) Running $sdk_name tests ... "
		if ! run_test "$sdk_dir"; then
			((i--))
		fi
	done

	## Report when all tests in run_list are run
	if [ "$i" -eq "$count" ]; then
		echo -e "\nAll tests ran successfully"
	else
		echo -e "\nExecuted $i out of $count tests successfully."
		exit 1
	fi
}
main "$@"
