Merge "Chore: Updates to improve overall robustness of robot framework tooling"
diff --git a/jjb/integration/include-raw-integration-install-robotframework-py3.sh b/jjb/integration/include-raw-integration-install-robotframework-py3.sh
index 6d0b496..efeb0f7 100644
--- a/jjb/integration/include-raw-integration-install-robotframework-py3.sh
+++ b/jjb/integration/include-raw-integration-install-robotframework-py3.sh
@@ -10,24 +10,70 @@
 ##############################################################################
 # vim: sw=4 ts=4 sts=4 et ft=sh :
 
-set -eu pipefail
+set -euxo pipefail
 
-# shellcheck disable=SC1090
-. ~/lf-env.sh
+echo "---> install-robotframework-py3.sh"
 
-# Create a virtual environment for robot tests and make sure setuptools & wheel
-# are up-to-date in addition to pip
-lf-activate-venv --python python3 --venv-file "${WORKSPACE}/.robot3_venv" \
-    setuptools \
-    wheel
+### Common variables
 
-# Save the virtual environment in ROBOT_VENV
-ROBOT3_VENV="$(cat "${WORKSPACE}/.robot3_venv")"
-echo ROBOT3_VENV="${ROBOT3_VENV}" >> "${WORKSPACE}/env.properties"
+REQUIRED_PYTHON="3.7.0"
 
-set -exu
+### Common functions
 
-echo "Installing Python Requirements"
+# Allows for the comparison of two Python version strings
+ver_cmp()
+{
+    local IFS=.
+    # shellcheck disable=SC2206
+    local V1=($1) V2=($2) I
+    for ((I=0 ; I<${#V1[*]} || I<${#V2[*]} ; I++)) ; do
+        [[ ${V1[$I]:-0} -lt ${V2[$I]:-0} ]] && echo -1 && return
+        [[ ${V1[$I]:-0} -gt ${V2[$I]:-0} ]] && echo 1 && return
+    done
+    echo 0
+}
+# Checks if first version/string is greater than or equal to the second
+ver_ge()
+{
+    [[ ! $(ver_cmp "$1" "$2") -eq -1 ]]
+}
+
+### Main script entry point
+
+# Check for required Python versions and activate/warn appropriately
+# Use PYENV for selecting the latest python version, if available
+if [[ -d "/opt/pyenv" ]]; then
+    echo "Setup pyenv:"
+    export PYENV_ROOT="/opt/pyenv"
+    export PATH="$PYENV_ROOT/bin:$PATH"
+    pyenv versions
+    if command -v pyenv 1>/dev/null 2>&1; then
+        eval "$(pyenv init - --no-rehash)"
+        # Choose the latest numeric Python version from installed list
+        version=$(pyenv versions --bare | sed '/^[^0-9]/d' |\
+            sort -V | tail -n 1)
+        pyenv local "${version}"
+    fi
+fi
+
+# Store the active/current Python3 version
+PYTHON_VERSION=$(python3 --version | awk '{print $2}')
+
+#  Check that the required minimum version has been met
+if ! (ver_ge "${PYTHON_VERSION}" "${REQUIRED_PYTHON}"); then
+    echo "Warning: possible Python version problem"
+    echo "Python ${PYTHON_VERSION} does not meet requirement: ${REQUIRED_PYTHON}"
+fi
+
+if (python3 -m robot.run --version > /dev/null 2>&1); then
+    echo "Working robot framework found; no installation necessary"
+    echo "Installed under Python version: ${PYTHON_VERSION}"
+    exit 0
+fi
+
+
+# Create a requirements file; keep it around for potential later use
+# Versions and dependencies below have been carefully tested for Python3
 cat << 'EOF' > "requirements.txt"
 paramiko
 six
@@ -79,6 +125,49 @@
 odltools
 EOF
 
-python3 -m pip install -r requirements.txt
+
+if [[ -f ~/lf-env.sh ]]; then
+    echo "Installing robot-framework using LF common tooling"
+    # shellcheck disable=SC1090
+    source ~/lf-env.sh
+
+    # Create a virtual environment for robot tests and make sure setuptools & wheel
+    # are up-to-date in addition to pip
+    lf-activate-venv --python python3 --venv-file "${WORKSPACE}/.robot3_venv" \
+    setuptools \
+    pip \
+    wheel
+
+    # Install the robot framework and other dependencies
+    python3 -m pip install -r requirements.txt
+
+    # Save the virtual environment in ROBOT3_VENV
+    ROBOT3_VENV="$(cat "${WORKSPACE}/.robot3_venv")"
+
+else
+    echo "Installing robot-framework in a virtual environment"
+    if [[ -z "${WORKSPACE}" ]]; then
+        # Use a temporary folder location
+        WORKSPACE="/tmp"
+        ROBOT3_VENV=$(mktemp -d --suffix=-robot3_venv)
+    else
+        ROBOT3_VENV="${WORKSPACE}/.robot3_venv"
+    fi
+
+    # The --system-site-packages parameter allows us to pick up system level
+    # installed packages. This allows us to bake matplotlib which takes very long
+    # to install into the image.
+    python3 -m venv --system-site-packages "${ROBOT3_VENV}"
+    source "${ROBOT3_VENV}/bin/activate"
+
+    echo "Installing robot-framework using basic methods"
+    python3 -m pip install -r requirements.txt
+fi
+
+# Store the virtual environment location
+echo "ROBOT3_VENV=${ROBOT3_VENV}" >> "${WORKSPACE}/env.properties"
+
+# Display versioning/debugging output
+python3 --version
 python3 -m pip freeze
 python3 -m robot.run --version || :
diff --git a/jjb/integration/prepare-csit.sh b/jjb/integration/prepare-csit.sh
index 1052bff..0db140f 100644
--- a/jjb/integration/prepare-csit.sh
+++ b/jjb/integration/prepare-csit.sh
@@ -24,18 +24,48 @@
 
 ROBOT_INSTALLER='include-raw-integration-install-robotframework-py3.sh'
 
-if !(which git > /dev/null 2>&1); then
-    echo "GIT binary not found current PATH"
-    echo $PATH; exit 1
+# Allows testing for root permissions
+REQ_USER=$(id -un)
+
+if ! (which git > /dev/null 2>&1); then
+    echo "GIT binary not found in current PATH"
+    # Add missing package to prevent script/job failures
+    if (grep Ubuntu /etc/os-release > /dev/null 2>&1) || \
+    (grep Debian /etc/os-release > /dev/null 2>&1); then
+        echo "Installing package dependency for Ubuntu/Debian"
+        if [[ "${REQ_USER}" == 'root' ]]; then
+            apt-get update
+            apt-get install -y git
+        else
+            sudo apt-get update
+            sudo apt-get install -y git
+        fi
+    elif (grep RedHat /etc/os-release > /dev/null 2>&1) || \
+    (grep CentOS /etc/os-release > /dev/null 2>&1); then
+        echo "Installing package dependency for CentOS/RedHat"
+        if [[ "${REQ_USER}" == 'root' ]]; then
+            yum install -y git
+        else
+            sudo yum install -y git
+        fi
+    else
+        echo "Warning: unmatched OS/distribution"
+        echo "Missing software will not be installed"
+    fi
 fi
 
-if [ -z "$WORKSPACE" ]; then
-    # shellcheck disable=SC2155
-    export WORKSPACE=`git rev-parse --show-toplevel`
+if [[ -z "${WORKSPACE}" ]]; then
+    if (git rev-parse --show-toplevel > /dev/null 2>&1); then
+        WORKSPACE=$(git rev-parse --show-toplevel)
+        export WORKSPACE
+    else
+        WORKSPACE=$(pwd)
+        export WORKSPACE
+    fi
 fi
 
 # shellcheck disable=SC2034
-TESTPLANDIR=${WORKSPACE}/${TESTPLAN}
+TESTPLANDIR="${WORKSPACE}/${TESTPLAN}"
 
 # Python version should match that used to setup
 #  robot-framework in other jobs/stages
@@ -56,22 +86,26 @@
 
 # Assume that if ROBOT3_VENV is set, virtualenv
 #  with system site packages can be activated
-if [ -f ${WORKSPACE}/env.properties ]; then
-    source ${WORKSPACE}/env.properties
+if [[ -f "${WORKSPACE}/env.properties" ]]; then
+    source "${WORKSPACE}/env.properties"
+elif [[ -f /tmp/env.properties ]]; then
+    source /tmp/env.properties
 fi
-if [ -f ${ROBOT3_VENV}/bin/activate ]; then
-    source ${ROBOT3_VENV}/bin/activate
+
+if [[ -f "${ROBOT3_VENV}/bin/activate" ]]; then
+    source "${ROBOT3_VENV}/bin/activate"
 else
     # Robot framework was not found
     #  clone ci-management repository and use install script
     git clone "https://gerrit.onap.org/r/ci-management" \
         /tmp/ci-management
-    source /tmp/ci-management/jjb/integration/${ROBOT_INSTALLER}
+    # shellcheck disable=SC1090
+    source "/tmp/ci-management/jjb/integration/${ROBOT_INSTALLER}"
 fi
 
 # install eteutils
-mkdir -p ${ROBOT3_VENV}/src/onap
-rm -rf ${ROBOT3_VENV}/src/onap/testsuite
+mkdir -p "${ROBOT3_VENV}/src/onap"
+rm -rf "${ROBOT3_VENV}/src/onap/testsuite"
 # Source from the Nexus repository
 python3 -m pip install --upgrade \
     --extra-index-url="https://nexus3.onap.org/repository/PyPi.staging/simple" \
diff --git a/jjb/integration/run-csit.sh b/jjb/integration/run-csit.sh
index bd35ac4..877cebb 100644
--- a/jjb/integration/run-csit.sh
+++ b/jjb/integration/run-csit.sh
@@ -15,13 +15,35 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-# $1 project/functionality
-# $2 robot options
+# $1 project/functionality {TESTPLAN}
+# $2 robot options         {TESTOPTIONS}
 
 echo "---> run-csit.sh"
 
 WORKDIR=$(mktemp -d --suffix=-robot-workdir)
 
+# Exit if no arguments are provided and required variables not set
+if [[ $# -eq 0 ]] && [[ -z "${TESTPLAN}" ]] && [[ -z "${TESTOPTIONS}" ]]; then
+    echo
+    echo "Usage: $0 plans/<project>/<functionality> [<robot-options>]"
+    echo
+    echo " <project>, <functionality>, <robot-options>:  "
+    echo "  The same values as for the JJB job template:"
+    echo '  {project}-csit-{functionality}'
+    echo
+    exit 1
+
+elif [[ $# -ne 2 ]] && [[ -z "${TESTPLAN}" ]] && [[ -z "${TESTOPTIONS}" ]]; then
+    echo
+    echo "Script called without arguments, but the following variables"
+    echo " must be set: {TESTPLAN} {TESTOPTIONS}"
+    echo
+    exit 1
+
+elif [[ $# -eq 2 ]]; then
+    export TESTPLAN=$1; export TESTOPTIONS=$2
+fi
+
 # Python version should match that used to setup
 #  robot-framework in other jobs/stages
 # Use pyenv for selecting the python version
@@ -43,12 +65,52 @@
 # functions
 #
 
+# load the saved set options
+function load_set {
+    _setopts="$-"
+
+    # bash shellopts
+    for i in $(echo "$SHELLOPTS" | tr ':' ' ') ; do
+        set +o "${i}"
+    done
+    for i in $(echo "$RUN_CSIT_SHELLOPTS" | tr ':' ' ') ; do
+        set -o "${i}"
+    done
+
+    # other options
+    for i in $(echo "$_setopts" | sed 's/./& /g') ; do
+        set +"${i}"
+    done
+    set -"${RUN_CSIT_SAVE_SET}"
+}
+
+# set options for quick bailout when error
+function harden_set {
+    set -xeo pipefail
+    set +u # enabled it would probably fail too many often
+}
+
+# relax set options so the sourced file will not fail
+# the responsibility is shifted to the sourced file...
+function relax_set {
+    set +e
+    set +o pipefail
+}
+
+# save current set options
+function save_set {
+    RUN_CSIT_SAVE_SET="$-"
+    RUN_CSIT_SHELLOPTS="$SHELLOPTS"
+}
+
 # wrapper for sourcing a file
 function source_safely {
-    [ -z "$1" ] && return 1
-    relax_set
-    . "$1"
-    load_set
+    if [[ -z "$1" ]] && return 1; then
+        relax_set
+        # shellcheck disable=SC1090
+        source "$1"
+        load_set
+    fi
 }
 
 function on_exit {
@@ -66,11 +128,10 @@
     # Run teardown script plan if it exists
     cd "${TESTPLANDIR}"
     TEARDOWN="${TESTPLANDIR}/teardown.sh"
-    if [ -f "${TEARDOWN}" ]; then
+    if [[ -f "${TEARDOWN}" ]]; then
         echo "Running teardown script ${TEARDOWN}"
         source_safely "${TEARDOWN}"
     fi
-    # TODO: do something with the output
     exit $rc
 }
 # ensure that teardown and other finalizing steps are always executed
@@ -96,44 +157,6 @@
     echo
 }
 
-# save current set options
-function save_set {
-    RUN_CSIT_SAVE_SET="$-"
-    RUN_CSIT_SHELLOPTS="$SHELLOPTS"
-}
-
-# load the saved set options
-function load_set {
-    _setopts="$-"
-
-    # bash shellopts
-    for i in $(echo "$SHELLOPTS" | tr ':' ' ') ; do
-        set +o ${i}
-    done
-    for i in $(echo "$RUN_CSIT_SHELLOPTS" | tr ':' ' ') ; do
-        set -o ${i}
-    done
-
-    # other options
-    for i in $(echo "$_setopts" | sed 's/./& /g') ; do
-        set +${i}
-    done
-    set -${RUN_CSIT_SAVE_SET}
-}
-
-# set options for quick bailout when error
-function harden_set {
-    set -xeo pipefail
-    set +u # enabled it would probably fail too many often
-}
-
-# relax set options so the sourced file will not fail
-# the responsibility is shifted to the sourced file...
-function relax_set {
-    set +e
-    set +o pipefail
-}
-
 #
 # main
 #
@@ -141,29 +164,21 @@
 # set and save options for quick failure
 harden_set && save_set
 
-if [ $# -eq 0 ]; then
-    echo
-    echo "Usage: $0 plans/<project>/<functionality> [<robot-options>]"
-    echo
-    echo "    <project>, <functionality>, <robot-options>:  "
-    echo "        The same values as for the '{project}-csit-{functionality}' JJB job template."
-    echo
-    exit 1
+if [[ -z "${WORKSPACE}" ]]; then
+    if (git rev-parse --show-toplevel > /dev/null 2>&1); then
+        WORKSPACE=$(git rev-parse --show-toplevel)
+        export WORKSPACE
+    else
+        WORKSPACE=$(pwd)
+        export WORKSPACE
+    fi
 fi
 
-if [ -z "$WORKSPACE" ]; then
-    export WORKSPACE=$(git rev-parse --show-toplevel)
-fi
-
-if [ -f "${WORKSPACE}/${1}/testplan.txt" ]; then
-    export TESTPLAN="${1}"
-else
+if [[ ! -f "${WORKSPACE}/${TESTPLAN}/testplan.txt" ]]; then
     echo "testplan not found: ${WORKSPACE}/${TESTPLAN}/testplan.txt"
     exit 2
 fi
 
-export TESTOPTIONS="${2}"
-
 rm -rf "$WORKSPACE/archives/$TESTPLAN"
 mkdir -p "$WORKSPACE/archives/$TESTPLAN"
 
@@ -178,7 +193,7 @@
 cd "${WORKDIR}"
 
 # Add csit scripts to PATH
-export PATH="${PATH}:${WORKSPACE}/docker/scripts:${WORKSPACE}/scripts:${ROBOT_VENV}/bin"
+export PATH="${PATH}:${WORKSPACE}/docker/scripts:${WORKSPACE}/scripts:${ROBOT3_VENV}/bin"
 export SCRIPTS="${WORKSPACE}/scripts"
 export ROBOT_VARIABLES=
 
@@ -199,7 +214,8 @@
 # Run test plan
 cd "$WORKDIR"
 echo "Reading the testplan:"
-cat "${TESTPLANDIR}/testplan.txt" | egrep -v '(^[[:space:]]*#|^[[:space:]]*$)' | sed "s|^|${WORKSPACE}/tests/|" > testplan.txt
+grep -E -v '(^[[:space:]]*#|^[[:space:]]*$)' "${TESTPLANDIR}/testplan.txt" |\
+    sed "s|^|${WORKSPACE}/tests/|" > testplan.txt
 cat testplan.txt
 SUITES=$( xargs -a testplan.txt )
 
@@ -212,10 +228,9 @@
 pip freeze
 python3 -m robot.run --version || :
 
-python -m robot.run -N ${TESTPLAN} -v WORKSPACE:/tmp ${ROBOT_VARIABLES} ${TESTOPTIONS} ${SUITES}
+python -m robot.run -N "${TESTPLAN}" -v WORKSPACE:/tmp "${ROBOT_VARIABLES}" "${TESTOPTIONS}" "${SUITES}"
 RESULT=$?
 load_set
 echo "RESULT: $RESULT"
 # Note that the final steps are done in on_exit function after this exit!
 exit $RESULT
-