#!/bin/bash

# -------------------------------------------------------------------------- #
# 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.                                             #
#--------------------------------------------------------------------------- #

PROGNAME=`basename $0`
DIRNAME=`dirname $0`

# Prints the given message and exits denoting an execution error.
#
# @param message [String] message to be printed
function error_exit()
{
  printf "%s\n" "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
  exit 1
}

# Prints the given message and exits denoting a warning.
#
# @param message [String] message to be printed
function warn_exit()
{
  printf "%s\n" "${PROGNAME}: ${1:-"Unknown warning"}" 1>&2
  exit 0
}

# Prints the given message with help attached.
#
# @param message [String] message to be printed
function print_help_exit()
{
  if [ -n "$1" ]; then
    printf "%s\n\n" "${PROGNAME}: ${1}" 1>&2
  fi

  printf "%s\n\n" "Usage: ${PROGNAME} [OPTIONS]"
  printf "\t%s\n" "-e ENDPOINT  Server endpoint address without the trailing slash, defaults to \"http://localhost:3000\""
  printf "\t%s\n" "-n SCHEME    Authentication schema from {none, basic, x509, token}, defaults to \"none\""
  printf "\t%s\n" "-h           Show this help"
  printf "\t%s\n" "-u USERNAME  Username for basic authentication scheme"
  printf "\t%s\n" "-p PASSWORD  Password for basic authentication scheme"
  printf "\t%s\n" "-c PATH      CA path for x509 authentication scheme"
  printf "\t%s\n" "-f PATH      CA file for x509 authentication scheme"
  printf "\t%s\n" "-x PATH      Path to user credentials for x509 authentication scheme"
  printf "\t%s\n" "-i TOKEN     Token for token-based authentication scheme"
  printf "\t%s\n" "-X           Switch x509 authentication scheme to a VOMS-compatible mode"
  printf "\t%s\n" "-M TPL       os_tpl for the compute instantiation test"
  printf "\t%s\n" "-R TPL       resource_tpl for the compute instantiation test"
  printf "\t%s\n" "-d           Enable debug mode (log raw cURL commands to '/tmp/occi_smoketest.log')"

  if [ -n "$1" ]; then
    exit 1
  else
    exit 0
  fi
}

# Executes a cURL query with authentication and returns
# the response.
#
# @param method [String] HTTP method
# @param path [String] path to be queried
# @param media_type [String] media type
# @param fail ['yes', 'no'] expect fail or not
function curl_query()
{
  AUTH_STUFF=''

  case "$ROCCI_SERVER_AUTHENTICATION_SCHEME" in
    'none') ;;
    'basic') AUTH_STUFF="--basic -u '${ROCCI_SERVER_AUTHENTICATION_USERNAME}:${ROCCI_SERVER_AUTHENTICATION_PASSWORD}' -k";;
    'x509') AUTH_STUFF="--cert ${ROCCI_SERVER_AUTHENTICATION_CRED} --key ${ROCCI_SERVER_AUTHENTICATION_CRED} --capath ${ROCCI_SERVER_AUTHENTICATION_CA_PATH}";;
    'token') AUTH_STUFF="-H \"X-Auth-Token: ${ROCCI_SERVER_AUTHENTICATION_TOKEN}\" -k";;
    *) error_exit "Unknow authentication scheme \"$ROCCI_SERVER_AUTHENTICATION_SCHEME\" for curl!"
  esac

  CURL_CMD="curl -f -s ${AUTH_STUFF} -X $1 ${ROCCI_SERVER_ENDPOINT}$2"
  if [ -n "$3" ]; then
    CURL_CMD="$CURL_CMD -H \"Accept: $3\""
  fi

  if [ "$ROCCI_SERVER_DEBUG" == "yes" ]; then
    echo "$CURL_CMD" >> /tmp/occi_smoketest.log
  fi

  CURL_OUTPUT=`eval $CURL_CMD`
  CURL_RETVAL=$?
  if [ "$CURL_RETVAL" -ne "0" ] && [ "$4" != "yes" ]; then
    error_exit "cURL request failed: $CURL_CMD"
  fi
  if [ "$CURL_RETVAL" -eq "0" ] && [ "$4" == "yes" ]; then
    error_exit "cURL request mistakenly successful: $CURL_CMD"
  fi

  printf "%s" "$CURL_OUTPUT"
}

# set defaults
ROCCI_SERVER_ENDPOINT="http://localhost:3000"
ROCCI_SERVER_AUTHENTICATION_SCHEME="none"
ROCCI_SERVER_AUTHENTICATION_CA_PATH="/etc/grid-security/certificates"
ROCCI_SERVER_DEBUG="no"
ROCCI_SERVER_AUTHENTICATION_VOMS="no"

# parse arguments
while getopts ":e:n:u:p:c:f:x:i:dhXM:R:" flag; do
  case "$flag" in
    'e') ROCCI_SERVER_ENDPOINT=$OPTARG;;
    'n') ROCCI_SERVER_AUTHENTICATION_SCHEME=$OPTARG;;
    'u') ROCCI_SERVER_AUTHENTICATION_USERNAME=$OPTARG;;
    'p') ROCCI_SERVER_AUTHENTICATION_PASSWORD=$OPTARG;;
    'c') ROCCI_SERVER_AUTHENTICATION_CA_PATH=$OPTARG;;
    'f') ROCCI_SERVER_AUTHENTICATION_CA_FILE=$OPTARG;;
    'x') ROCCI_SERVER_AUTHENTICATION_CRED=$OPTARG;;
    'i') ROCCI_SERVER_AUTHENTICATION_TOKEN=$OPTARG;;
    'h') print_help_exit;;
    'd') ROCCI_SERVER_DEBUG='yes' && rm -f /tmp/occi_smoketest.log;;
    'X') ROCCI_SERVER_AUTHENTICATION_VOMS='yes';;
    'M') ROCCI_SERVER_COMPUTE_OS_TPL=$OPTARG;;
    'R') ROCCI_SERVER_COMPUTE_RESOURCE_TPL=$OPTARG;;
    '?') error_exit "Unknown option \"-${OPTARG}\"!";;
    ':') error_exit "Missing required argument \"-${OPTARG} VALUE\"!";;
    *) error_exit "Unknown option parsing error!"
  esac
done

# check basic pre-conditions && required options
if [ -z "$ROCCI_SERVER_ENDPOINT" ]; then
  print_help_exit "Endpoint is a required argument!"
else
  ROCCI_SERVER_ENDPOINT_ESCAPED=$(echo "$ROCCI_SERVER_ENDPOINT" | sed -e 's/[\/&]/\\&/g')
fi

if [ -z "$ROCCI_SERVER_AUTHENTICATION_SCHEME" ]; then
  print_help_exit "Authentication scheme is a required argument!"
fi

if [ "$ROCCI_SERVER_AUTHENTICATION_SCHEME" != "none" ] && [ "$ROCCI_SERVER_AUTHENTICATION_SCHEME" != "basic" ] && [ "$ROCCI_SERVER_AUTHENTICATION_SCHEME" != "x509" ] && [ "$ROCCI_SERVER_AUTHENTICATION_SCHEME" != "token" ]; then
  print_help_exit "Unsupported authentication scheme \"${ROCCI_SERVER_AUTHENTICATION_SCHEME}!\""
fi

if [ "$ROCCI_SERVER_AUTHENTICATION_SCHEME" == "basic" ]; then
  if [ -z "$ROCCI_SERVER_AUTHENTICATION_USERNAME" ] || [ -z "$ROCCI_SERVER_AUTHENTICATION_PASSWORD" ]; then
    print_help_exit "Authentication scheme \"basic\" requires username and password!"
  fi
fi

if [ "$ROCCI_SERVER_AUTHENTICATION_SCHEME" == "x509" ]; then
  if [ -z "$ROCCI_SERVER_AUTHENTICATION_CRED" ]; then
    print_help_exit "Authentication scheme \"x509\" requires user credentials!"
  fi
fi

if [ "$ROCCI_SERVER_AUTHENTICATION_SCHEME" == "token" ]; then
  if [ -z "$ROCCI_SERVER_AUTHENTICATION_TOKEN" ]; then
    print_help_exit "Authentication scheme \"token\" requires a token!"
  fi
fi

# check required external utilities
curl --help &> /dev/null
if [ "$?" -ne "0" ]; then
  print_help_exit "\"curl\" is not available!"
fi

# run fcional tests
printf "%s\n" "* Running a basic set of functionality tests on ${ROCCI_SERVER_ENDPOINT}"

printf "%s\n" "** Getting server model"
curl_query "GET" "/-/" "text/plain" > /dev/null

printf "%s\n" "** Getting various resource lists"
LINKS=$(curl_query "GET" "/" "text/uri-list")

printf "%s\n" "*** Getting descriptions for specific resources"
for LINK in $LINKS; do
  LINK_RELATIVE=`echo "$LINK" | sed "s/$ROCCI_SERVER_ENDPOINT_ESCAPED//g"`
  printf "%s\n" "**** Getting description for \"$LINK_RELATIVE\""
  curl_query "GET" "$LINK_RELATIVE" "text/plain" > /dev/null
done

printf "%s\n" "*** Testing responses to non-existing resources"
curl_query "GET" "/compute/not_there" "text/plain" "yes" > /dev/null
curl_query "GET" "/storage/not_there" "text/plain" "yes" > /dev/null
curl_query "GET" "/network/not_there" "text/plain" "yes" > /dev/null
curl_query "GET" "/not_there/" "text/plain" "yes" > /dev/null

printf "%s\n" "** Testing various media types"
printf "%s\n" "*** Testing text/plain"
curl_query "GET" "/-/" "text/plain" > /dev/null

printf "%s\n" "*** Testing text/occi"
curl_query "GET" "/-/" "text/occi" > /dev/null

printf "%s\n" "*** Testing application/occi+json"
curl_query "GET" "/-/" "application/occi+json" > /dev/null

printf "%s\n" "** Testing error responses"
printf "%s\n" "*** Testing responses to invalid requests"
curl_query "POST" "/compute/" "text/plain" "yes" > /dev/null
curl_query "POST" "/storage/" "text/plain" "yes" > /dev/null
curl_query "POST" "/network/" "text/plain" "yes" > /dev/null

printf "%s\n" "*** Testing responses to invalid media types"
curl_query "GET" "/-/" "application/rss" "yes" > /dev/null
curl_query "GET" "/-/" "text/uri-list" "yes" > /dev/null

# run a full cumpute instantiation test (including clean-up)
printf "%s\n" "* Running a compute instantiation test"
if [ "$ROCCI_SERVER_AUTHENTICATION_SCHEME" == "token" ]; then
  warn_exit "Skipping the instantiation test. Token-based authentication is not supported!"
fi

if [ -z "$ROCCI_SERVER_COMPUTE_OS_TPL" ] || [ -z "$ROCCI_SERVER_COMPUTE_RESOURCE_TPL" ]; then
  print_help_exit "Instantiation requires os_tpl and resource_tpl arguments!"
fi

if [ -e "/opt/occi-cli/embedded/bin/ruby" ] && [ -x "/opt/occi-cli/embedded/bin/ruby" ]; then
  if [ -e "$DIRNAME/check_occi_compute_create" ] && [ -x "$DIRNAME/check_occi_compute_create" ]; then
    PROBE_CMD="$DIRNAME/check_occi_compute_create --endpoint $ROCCI_SERVER_ENDPOINT --auth $ROCCI_SERVER_AUTHENTICATION_SCHEME --os_tpl $ROCCI_SERVER_COMPUTE_OS_TPL --resource_tpl $ROCCI_SERVER_COMPUTE_RESOURCE_TPL --compute-title 'SmokeTestVM' "

    case "$ROCCI_SERVER_AUTHENTICATION_SCHEME" in
      'none') ;;
      'basic') PROBE_CMD="$PROBE_CMD -u $ROCCI_SERVER_AUTHENTICATION_USERNAME -p '$ROCCI_SERVER_AUTHENTICATION_PASSWORD' ";;
      'x509') PROBE_CMD="$PROBE_CMD --user-cred $ROCCI_SERVER_AUTHENTICATION_CRED ";;
      *) error_exit "Unknow authentication scheme \"$ROCCI_SERVER_AUTHENTICATION_SCHEME\" for occi-cli!"
    esac

    if [ "$ROCCI_SERVER_AUTHENTICATION_VOMS" == "yes" ]; then
      PROBE_CMD="$PROBE_CMD --voms"
    fi

    eval $PROBE_CMD
  else
    error_exit "The check_occi_compute_create script is not present in the same directory as the occi_smoketest script!"
  fi
else
  warn_exit "To run extended tests, please, install rOCCI-cli from https://appdb.egi.eu/store/software/rocci.cli"
fi
