#!/usr/bin/bash
#
# Copyright (C) 2022-2025 Red Hat, 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.

set -e

TOPDIR=$(cd $(dirname $0); pwd)

if [[ $0 =~ .*-podman$ ]]; then
    SUFFIX=-podman
else
    SUFFIX=
fi

if [ -r "$TOPDIR/common$SUFFIX" ]; then
    BASEDIR="$TOPDIR"
else
    BASEDIR=/usr/share/dci-pipeline
fi

source "$BASEDIR/common$SUFFIX"
QUEUE="${DCI_QUEUE:-$DEFAULT_QUEUE}"

if [ $# = 0 -o "$1" = -h ]; then
    cat 1>&2 <<EOF
Usage: $(basename $0) [-F] [-S] [-Fr] [-Sr] [-f] [-n] (<GitHub PR URL>|<Gerrit review id>)
             [-p <pool name>] [-r <resource name>]
             [[<kubeconfig path>] <pipeline name> [(-p <pool name>|<pipeline name2>|<pipeline var>)*]]

    -F   Run in a loop until there's a failure
    -S   Run in a loop until succeeds
    -Fr  Run in a loop until there's a failure and remove the resource from the pool
    -Sr  Run in a loop until succeeds and remove the resource from the pool
    -f   Force a suffix to be added (uses PID as suffix)
    -p   Specify a pool name (default: $DEFAULT_QUEUE)
    -r   Specify a resource name
    -n   Do not launch the dci-pipeline command.
    -h   Print this help
EOF
    exit 1
fi

LAUNCHER=
DCI_PIPELINE_TAG=
QUEUES=

set -x

ARGS=("$@")

# -F: run using loop_until_failure
if [ "$1" = "-F" ]; then
    LAUNCHER="$BASEDIR/loop_until_failure$SUFFIX"
    shift
fi

# -S: run using loop_until_success
if [ "$1" = "-S" ]; then
    LAUNCHER="$BASEDIR/loop_until_success$SUFFIX"
    shift
fi

# -Fr: run using loop_until_failure -r
if [ "$1" = "-Fr" ]; then
    LAUNCHER="$BASEDIR/loop_until_failure$SUFFIX -r"
    shift
fi

# -Sr: run using loop_until_success -r
if [ "$1" = "-Sr" ]; then
    LAUNCHER="$BASEDIR/loop_until_success$SUFFIX -r"
    shift
fi

# force a job suffix to be added on -f
if [ "$1" = "-f" ]; then
    shift
    # manage recursive call (bootstrap): extract the directory name
    # from the path of the command
    if grep -Fq '.bootstrap' <<< $TOPDIR; then
        JOBSUFFIX=
        DIR=$(dirname $(dirname $TOPDIR))
        DIR=$(sed -e 's/\.bootstrap//' <<< $DIR)
    else
        JOBSUFFIX=-$(echo "$@"|md5sum|cut -f1|cut -f1 -d ' ')-$$
    fi
else
    JOBSUFFIX=-$(echo "$@"|md5sum|cut -f1|cut -f1 -d ' ')
    DIR=
fi

if [ "$1" = "-n" ]; then
    LAUNCHER=echo
    shift
fi

CHANGEID=$1
shift

# get an optional dci-queue pool name with -p
if [ "$1" = "-p" ]; then
    shift
    QUEUE="$1"
    shift
fi

# get an optional dci-queue resource name with -r
if [ "$1" = "-r" ]; then
    shift
    export DCI_QUEUE_RESOURCE="$1"
    shift
fi

# check that the KUBECONFIG is working
if [[ "$1" =~ .*/kubeconfig ]]; then
    KBCFG="$1"
    oc --kubeconfig "$KBCFG" version || exit 1
    oc --kubeconfig "$KBCFG" get nodes || exit 1
    shift
fi

OPT=("$@")

if [ -z "$OPT" ]; then
    echo "no specified pipeline" 1>&2
    exit 1
fi

case $CHANGEID in
    https://*)
        PR=$(sed -e 's@.*github.com/.*/\([^/]*\)/pull/\([0-9]*\)@\1-pr\2@' <<< $CHANGEID)
        if [ -z "$PR" ]; then
            echo "Unable to extract PR from $CHANGEID" 1>&2
            exit 1
        fi
        [ -n "$DIR" ] || DIR=$HOME/github/$PR$JOBSUFFIX
        TYPE=github
        # In podman mode, export the tag pointing to the image that
        # was built by the zuul pipeline.
        if [ -n "$SUFFIX" ]; then
            export DCI_PIPELINE_TAG="github-$PR"
        fi
        ;;
    *)
        [ -n "$DIR" ] || DIR=$HOME/gerrit/$CHANGEID$JOBSUFFIX
        TYPE=gerrit
        # In podman mode, export the tag pointing to the image that
        # was built by the zuul pipeline.
        if [ -n "$SUFFIX" ]; then
            export DCI_PIPELINE_TAG="gerrit-$CHANGEID"
        fi
        ;;
esac

if [ -d "$DIR" ]; then
    if  [ -n "$QUEUE" ]; then
        jobid="$(dci-queue searchdir $QUEUE $DIR)"
        if [ -z "$jobid" ]; then
            "$BASEDIR/send_status$SUFFIX" "$DIR" error "ERROR A job is already scheduled or running in $DIR (unable to find job id). Aborting."
            exit 1
        fi
        "dci-queue" unschedule "$QUEUE" "$jobid"
        if [ -d "$DIR" ]; then
            find "$DIR" -type d -not -perm -u=w -exec chmod u+w {} \;
            rm -rf "$DIR"
        fi
    else
        "$BASEDIR/send_status$SUFFIX" "$DIR" error "ERROR A job is already scheduled or running in $DIR. Aborting."
        exit 1
    fi
fi

mkdir -p "$DIR"
cd "$DIR" || exit 1

echo "Depends-On: $CHANGEID" | "$BASEDIR/extract-dependencies$SUFFIX" "$DIR"

# rerun check from the local repo
if [ -d dci-pipeline ]; then
    if [ $0 != $DIR.bootstrap/dci-pipeline/tools/$(basename $0) ]; then
        echo "re-exec"
        cd
        mv $DIR $DIR.bootstrap
        exec $DIR.bootstrap/dci-pipeline/tools/$(basename $0) "${ARGS[@]}"
    fi
fi

BASEDIR=$(sed -e 's/\.bootstrap//' <<< $BASEDIR)

if [ "$TYPE" = "github" ]; then
    mv *.json github.json
fi

# we should have only one directory and one or zero json file
MAINDIR="$(ls|grep -Fv .json)"
(
  # extract depends-on from a pr body
  jq -r .body *.json || :
  # extract depends-on from the last commit message
  cd "$MAINDIR"; git log -1
) | tr -d '\r' | sort -u | "$BASEDIR/extract-dependencies$SUFFIX" $DIR

# take test-runner from the local dir
if [ -d dci-pipeline ]; then
    BASEDIR=$PWD/dci-pipeline/tools
fi

# do launch through dci-queue only when there is no KUBECONFIG passed
if [ -z "$KBCFG" ]; then
    # if the resource is set explicitly or if there is no queue
    # defined, do not launch through dci-queue
    if [ -z "$DCI_QUEUE_RESOURCE" ] && [ -n "$QUEUE" ]; then
        # transform -p <pool name> args into -e <pool name> and save
        # it in the QUEUES variable for dci-queue and then replace them
        # with -p2, -p3, p4, etc.
        OPT=()
        NUM=2
        while [ -n "$1" ]; do
            case "$1" in
                -p)
                    QUEUES="$QUEUES -e $2"
                    shift 2
                    OPT+=("-p$NUM")
                    NUM=$((NUM + 1))
                    ;;
                *)
                    OPT+=("$1")
                    shift
                    ;;
            esac
        done
        PREFIX="dci-queue schedule $QUEUES $QUEUE -- env DCI_PIPELINE_TAG=$DCI_PIPELINE_TAG"
        RES="@RESOURCE"
    else
        PREFIX="env DCI_PIPELINE_TAG=$DCI_PIPELINE_TAG"
        RES=""
    fi
else
    # use the pipelines from the change if present
    PIPELINES_REPO=$(cd "$PIPELINES_DIR" && basename $(git config --local remote.origin.url) .git)
    if [ -d "$PIPELINES_REPO" ]; then
        PIPELINES_PATH=$(cd "$PIPELINES_DIR" && git rev-parse --show-prefix)
        PIPELINES_DIR="$PWD/$PIPELINES_REPO/$PIPELINES_PATH"
    fi
    # lookup the first pipeline file
    first_pipeline=
    for arg in "$@"; do
        case "$arg" in
            *:*|-*)
                ;;
            /*)
                first_pipeline="${arg}"
                break
                ;;
            *)
                first_pipeline="${PIPELINES_DIR}/${arg}-pipeline.yml"
                break
                ;;
        esac
    done
    if [ -z "$first_pipeline" ]; then
        "$BASEDIR/send_status$SUFFIX" "$DIR" error "ERROR unable to find a pipeline file" 1>&2
        rm -rf $DIR
        exit 1
    fi
    PREFIX="env DCI_QUEUE=$QUEUE DCI_PIPELINE_TAG=$DCI_PIPELINE_TAG"
    # find the name of the resource from the oc command
    # omit cluster names that are automatically generated when running `oc project <project_name>`,
    # with the format "api-<cluster_name>-<domain>:<port>"
    cluster_names=( $(oc --kubeconfig "$KBCFG" config view -o json | jq -r '.clusters[].name' | grep -v "api-.*:.*") )
    for cluster_name in "${cluster_names[@]}"; do
        INV=$("$BASEDIR/yaml2json$SUFFIX" "$first_pipeline" | jq -r '.[0].ansible_inventory' | \
            sed -e "s/@QUEUE/$QUEUE/" -e "s/@RESOURCE/${cluster_name}/" -e "s@^~/@${HOME}/@")
        if [ -f "$INV" ]; then
            RES=${cluster_name}
            break
        fi
    done

    if [ -z "$RES" ]; then
        "$BASEDIR/send_status$SUFFIX" "$DIR" error "ERROR Unable to start a job on distributed-ci.io: no matching cluster_name (${cluster_names[*]}) for queue $QUEUE" 1>&2
        rm -rf $DIR
        exit 1
    fi
fi

if [ -n "$LAUNCHER" ]; then
    CMD="$PREFIX $LAUNCHER $RES $BASEDIR/test-runner$SUFFIX $DIR $KBCFG"
else
    CMD="$PREFIX $BASEDIR/test-runner$SUFFIX $DIR $RES $KBCFG"
fi

# Use a suffix for el8 hosts
REQ_SUFFIX=""
source /etc/os-release
if [[ ${VERSION_ID%.*} -eq 8 ]]; then
    REQ_SUFFIX=".el8"
fi

# Create a python virtualenv if there is any project with a
# requirements.txt into the changes, giving priority to requirements
# with a suffix
set +e
REQS=($(ls -1 */requirements.txt{${REQ_SUFFIX},} 2> /dev/null | tail -1))
set -e

if [ -n "$REQS" ]; then
    rm -rf .venv
    # use --system-site-packages to get libselinux-python from the
    # system (not possible via pip)
    virtualenv --system-site-packages .venv
    source .venv/bin/activate

    # substitute dependencies from the changes into requirements.txt
    for req in $REQS; do
        reqdir=$(dirname $req)
        if [ -r $reqdir/setup.py ]; then
            module=$(sed -n -e 's/\s*name="\(.*\)",/\1/p' $reqdir/setup.py)
            if [ -n "$module" ]; then
                sed -i -e "s@$module.*@-e $PWD/$reqdir/@" */requirements.txt
            fi
        fi
    done

    for req in $REQS; do
        cat "$req"
    done

    # install python dependencies into the virtualenv
    for req in $REQS; do
        pip install -r $req
        cd $(dirname $req)
        python setup.py develop || :
        cd -
    done
    pip freeze
fi

if [ -d "$DIR.bootstrap" ]; then
    rm -rf "$DIR.bootstrap"
fi

if [ "$RES" = "@RESOURCE" ]; then
    "$BASEDIR/send_status$SUFFIX" "$DIR" pending "QUEUED"
    $CMD "${OPT[@]}"
    "dci-queue" list "$QUEUE"
else
    exec $CMD "${OPT[@]}"
fi

# dci-pipeline-check ends here
