#!/bin/sh
set -e

testdir=$(dirname "$(readlink -f "$0")")
. "$testdir/.env"

usage()
{
    cat <<EOF
usage: test/env [-cDhiKr] -f <IMAGE> -q <QENETH-DIR> <COMMAND> [<ARGS>...]
       test/env [-cDhiKr] -C -t <TOPOLOGY> <COMMAND> [<ARGS>...]

  Run <COMMAND> in a pre-packaged container with all the packages
  required for running the test suite installed.

  Options:

    -b <BASE-DIR>
     Use this directory as the top-level bind mount inside the container.
	 If not specified, Infix's top directory is used.

    -c
     Clean up cruft, lingering images, old containers, unused volumes.
     Used by GitHub action to prevent issues with running tests.

    -C
      Don't containerize the command, run it directly in the current
      namespaces

    -D
      Prefer Docker over podman. This is implied when -t is specified,
      as podman does not allow the necessary network permissions to be
      granted to the container

    -f <IMAGE>
       Specify images required for test, squashfs image is required
       if testing with kernel. bios and disk image is required if
       testing with bios.

    -h
      Show this help message

    -i
      Interactive mode, e.g., 'make test-sh'

    -K
      Even if /dev/kvm is usable, do not map it the container. This is
      useful to see how tests will run in a CI setting, where KVM is
      typically not available

    -p <BUNDLE>
      Infix RAUC bundle to use for upgrade tests.

    -q <QENETH-DIR>
      Directory containing a topology.dot.in, suitable for consumption
      by qeneth. A copy of this network is created, and launched. The
      resulting topology is used as the default physical topology when
      running tests

    -r
      Use random container names instead of "infamyN".  Useful for the
      unit tests running in CI environments which may see occasional
      name clashes for "infamy0" due to race conditions.

    -t <TOPOLOGY>
      Rather than starting a qeneth network, attach to this existing
      topology. If the command is containerized, it will be launched
      in the host's network namespace

EOF
}

get_base_img()
{
    local files="$1"
    local base_img_file
    base_img_file=$(echo "$files" | tr ' ' '\n' | grep -- '.qcow2$')
    echo "$envdir/qeneth/$(basename "$base_img_file")"
}

start_topology()
{
    qenethdir="$1"
    files="$*"

    [ "$files" ] || { true && return; }
    [ "$qenethdir" ] || { true && return; }

    rm -rf "$envdir/qeneth"
    cp -a "$qenethdir" "$envdir/qeneth"
    # Map files in to qeneth
    for f in $files; do
	filename="$(basename "$f")"
	file=$(readlink -f "$f")
	ln -sf "$file" "$envdir/qeneth/$filename"
    done

    base_img_qcow2=$(get_base_img "$files")
    base_img_disk="${base_img_qcow2%.qcow2}.disk"

    test_img_disk="${base_img_qcow2%.qcow2}-test.disk"
    test_img_qcow2="${test_img_disk%.disk}.qcow2"

    qemu-img convert -f qcow2 -O raw "$base_img_qcow2" "$base_img_disk"
    $testdir/inject-test-mode -b "$base_img_disk" -o "$test_img_disk"
    qemu-img convert -f raw -O qcow2 "$test_img_disk" "$test_img_qcow2"

    img_name=$(basename $test_img_qcow2)
    sed -i "s/qn_image=\".*\"/qn_image=\"$img_name\"/" "$envdir/qeneth/topology.dot.in"

    (cd "$envdir/qeneth/" && $qeneth generate && $qeneth start)
    INFAMY_ARGS="$INFAMY_ARGS $envdir/qeneth/topology.dot"
    cat <<EOF >"$envdir/bin/qeneth"
#!/bin/sh
cd $envdir/qeneth && exec $testdir/qeneth/qeneth "\$@"
EOF
    chmod +x "$envdir/bin/qeneth"
}

name()
{
    nm=infamy
    id=0

    if [ -n "$random" ] || [ -n "$CI" ]; then
	# Let podman/docker allocate random hostname and container name
	return
    fi

    names=$($(runner) ps -f name='infamy.*' --format '{{.Names}}')
    while true; do
	name="$nm$id"
	unset hit
	for n in $names; do
	    if [ "$name" = "$n" ]; then
		hit=true
		break;
	    fi
	done

	if [ -n "$hit" ]; then
	    id=$((id + 1))
	    continue
	fi
	break;
    done

    echo "--hostname $name --name $name"
}

# Global options
containerize=yes
[ -c /dev/kvm ] && kvm="--device=/dev/kvm"
files=
basedir="$ixdir"

while getopts "b:cCDf:hiKp:q:rt:" opt; do
    case ${opt} in
	b)
	    basedir="$OPTARG"
	    ;;
	c)
	    $(runner) image prune -af
            $(runner) volume prune -f
            $(runner) container prune -f
	    exit 0
	    ;;
	C)
	    containerize=
	    ;;
	D)
	    runners="docker podman"
	    ;;
	f)
	    files="$files $OPTARG"
	    ;;
	h)
	    usage && exit 0
	    ;;
	i)
	    interactive=--interactive
	    ;;
	K)
	    kvm=
	    ;;
	p)
	    version=$(unsquashfs -cat $OPTARG manifest.raucm | awk -F'=' '/^version=/ {print $2}')
	    INFAMY_ARGS="$INFAMY_ARGS -p $OPTARG"
	    ;;
	q)
	    qenethdir="$OPTARG"
	    network="--sysctl net.ipv6.conf.all.disable_ipv6=0"
	    ;;
	r)
	    random=true
	    ;;
	t)
	    topology="$OPTARG"
	    network="--network host --volume $topology:$topology:ro"
	    runners="docker podman"
	    ;;
	*)
	    >&2 echo "Unknown option -$opt"
	    usage
	    exit 1
	    ;;
    esac
done

if [ "$containerize" ]; then
    volumes="--volume $basedir:$basedir --workdir=$ixdir/test"
    case "$(runner)" in
	docker)
	    volumes="$volumes --env HOST_CHOWN_PATH=$ixdir/test"
	    volumes="$volumes --env HOST_CHOWN_UID=$(id -u)"
	    volumes="$volumes --env HOST_CHOWN_GID=$(id -g)"
	    ;;
	podman)
	    ;;
    esac

    mkdir -p "$HOME/.infix"
    touch "$HOME/.infix/.ash_history"

    # shellcheck disable=SC2016,SC2046,SC2086,SC2119
    exec $(runner) run \
	 --cap-add=NET_RAW \
	 --cap-add=NET_ADMIN \
	 --device=/dev/net/tun \
	 --env PYTHONHASHSEED=${PYTHONHASHSEED:-$(shuf -i 0-$(((1 << 32) - 1)) -n 1)} \
	 --env PYTHONPYCACHEDIR=/tmp/__pycache__ \
	 --env VIRTUAL_ENV_DISABLE_PROMPT=yes \
	 --env INFAMY_ARGS="$INFAMY_ARGS" \
	 --env INFAMY_EXTRA_ARGS="$INFAMY_EXTRA_ARGS" \
	 --env PROMPT_DIRTRIM="$ixdir" \
	 --env NINEPM_PROJ_CONFIG="$NINEPM_PROJ_CONFIG" \
	 --env QENETH_PATH="$testdir/templates:$testdir" \
	 --env PS1="$(build_ps1)" \
	 --env VERSION="$version" \
	 $extra_env \
	 --expose 9001-9010 --publish-all \
	 -v "$HOME/.infix/.ash_history":/root/.ash_history \
	 $(name) \
	 $interactive \
	 --rm \
	 --security-opt seccomp=unconfined \
	 $network \
	 --tty \
	 $volumes \
	 $kvm \
	 $INFIX_TEST \
	 "$0" -C "$@"
else
    if [ ! -f ~/.9pm.rc ]; then
       cat <<-EOF >~/.9pm.rc
		# Generated by Infix env
		SSH_OPTS: "-o StrictHostKeyChecking=no"
		LOG_PATH: "$logdir"
		EOF
    fi
fi

shift $((OPTIND - 1))

if [ $# -lt 1 ]; then
    usage && exit 1
fi

# shellcheck disable=SC1091
. "$envdir/bin/activate"
export PYTHONPATH="$testdir"
INFAMY_ARGS="$INFAMY_ARGS -y $envdir/yangdir"
start_topology "$qenethdir" "$files"
[ "$topology" ] && INFAMY_ARGS="$INFAMY_ARGS $topology"
export INFAMY_ARGS
if [ -n "$INFAMY_EXTRA_ARGS" ]; then
	exec "$@" -o="$INFAMY_EXTRA_ARGS"
fi

exec "$@"
