#!/bin/sh set -e testdir=$(dirname "$(readlink -f "$0")") . "$testdir/.env" usage() { cat < -q [...] test/env [-cDhiKr] -C -t [...] Run in a pre-packaged container with all the packages required for running the test suite installed. Options: -b 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 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 Infix RAUC bundle to use for upgrade tests. -q 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 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 -- '-disk.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=$(get_base_img "$files") test_img_name="${base_img%-disk.qcow2}-disk-test" base_img_name="${base_img%-disk.qcow2}-disk" test_img_raw="${test_img_name}.img" base_img_raw="${base_img_name}.img" test_img_qcow2="${test_img_name}.qcow2" qemu-img convert -f qcow2 -O raw "$base_img" "$base_img_raw" $testdir/inject-test-mode -b "$base_img_raw" -o "$test_img_raw" qemu-img convert -f raw -O qcow2 "$test_img_raw" "$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 <"$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" ]; 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 "$@"