diff --git a/bin/run_tests_locally.sh b/bin/run_tests_locally.sh
index 4d6bad3..88d6af5 100755
--- a/bin/run_tests_locally.sh
+++ b/bin/run_tests_locally.sh
@@ -1,56 +1,157 @@
-#!/bin/bash
+#!/bin/sh
+
+# Script to test the openSUSE infrastructure Salt code in a local container
+# Copyright (C) 2017-2024 openSUSE contributors
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
help() {
- echo "Runs all the tests on the user's workstation."
- echo "Needs to be run as normal user."
+ echo 'Run tests on your workstation.'
+ echo 'Do NOT run this as root.'
echo
echo "Arguments:"
- echo "-d Absolute path of the destination directory where the formulas are / should be cloned"
+ echo '-h - Print this text.'
+ echo
+ echo '-c - Use the specified existing container instead of instantiating a new one.'
+ echo '-k - Path to a public SSH key to use for authentication. If not specified, an insecure key will be used.'
+ echo '-p - Preserve container after tests finished running.'
echo
}
-[[ $(whoami) == 'root' ]] && help && exit 1
+print() {
+ printf '==> %s\n' "$1"
+}
+
+pprint() {
+ printf '==> %s %s ...\n' "$1" "$(gum style --foreground \#96cb5c $2)"
+}
+
+fail() {
+ echo "$1"
+ exit 1
+}
+
+pfail() {
+ printf '! %s\n' "$(gum style --foreground \#d11137 FAILED)"
+ exit 1
+}
-[[ $1 == '--help' ]] && help && exit
+[ "$(id -u)" == 0 ] && help && exit 1
+[ "$1" == '--help' ] && help && exit
-while getopts d:h arg; do
- case ${arg} in
- d) DESTINATION=${OPTARG} ;;
+CONTAINER=''
+CONTAINER_ARGS=()
+SSH_ARGS=( '-t' '-oStrictHostKeyChecking=no' '-oUserKnownHostsFile=/dev/null' '-oLogLevel=ERROR' '-lchecker' )
+PRESERVE=false
+
+while getopts h:c:k:p arg; do
+ case "$arg" in
h) help && exit ;;
+ c) CONTAINER="${OPTARG}" ;;
+ k) PUBKEY=${OPTARG} ;;
+ p) CONTAINER_ARGS+=('--rm') ; PRESERVE=true ;;
*) help && exit 1 ;;
esac
done
-[[ -z $DESTINATION ]] && help && exit 1
+print 'Pre-flight check ...'
+test -L FORMULAS.yaml || fail 'Please run this script from the salt.git repository root.'
+command -v podman >/dev/null || fail 'Please install Podman first.'
+command -v ssh >/dev/null || fail 'Please install the OpenSSH client first.'
+command -v rsync >/dev/null || fail 'Please install rsync first.'
+getent hosts ipv6-localhost >/dev/null || fail 'Please ensure ipv6-localhost maps to ::1 (default on openSUSE).'
+if [ -z "$PUBKEY" ] || ! file -bL "$PUBKEY" | grep -q 'OpenSSH .* public key'
+then
+ fail 'Please provide a public OpenSSH key using -k.'
+fi
-SALT_DIRS=(
- /etc/salt
- /var/log/salt
- /var/cache/salt
-)
+set -Cu
-# Prepare env
-for dir in ${SALT_DIRS[@]}; do
- sudo chown -R ${USER}: $dir
-done
-bin/prepare_test_env.sh -g -s
-bin/get_formulas.py --destination $DESTINATION --clone --symlink --update opensuse \
- --add-remote opensuse no_prefix gitlab@gitlab.infra.opensuse.org: saltstack-formulas
-ln -s ~/.gnupg /etc/salt/gpgkeys
-
-# Run tests
-echo "Running against upstream formulas"
-LC_ALL=C bin/test_validate.sh
-bin/get_formulas.py --destination $DESTINATION --checkout opensuse/production
-echo "Running against forked formulas"
-LC_ALL=C bin/test_show_highstate.sh
-
-# Cleanup
-bin/get_formulas.py --destination $DESTINATION --remove-symlinks --checkout origin/master
-for dir in ${SALT_DIRS[@]}; do
- sudo chown -R root: $dir
-done
-sudo rm /srv/{salt,pillar} /etc/salt/{grains,gpgkeys}
-sudo mkdir /srv/{salt,pillar}
-ID=$(hostname -f)
-rm pillar/id/${ID//./_}.sls
+if [ -z "$CONTAINER" ]
+then
+ print 'Pulling container ...'
+ podman pull -q registry.opensuse.org/opensuse/infrastructure/containers/heroes-salt-development-systemd \
+ || fail 'Failed to pull container'
+
+ print 'Preparing ...'
+ PORT=2222
+ while [ -n "$(ss -Htlno state listening sport = $PORT)" ]
+ do
+ PORT=$((PORT+1))
+ done
+ echo "$PORT"
+
+ print 'Starting ...'
+ CONTAINER=$( \
+ podman run -de SSH_KEY="$(cat $PUBKEY)" --health-interval 10s --health-start-period 15s "${CONTAINER_ARGS[@]}" -p "[::1]:$PORT:22" -v .:/home/geeko/salt-workspace:ro \
+ registry.opensuse.org/opensuse/infrastructure/containers/heroes-salt-development-systemd:latest \
+ )
+ timeout 90 podman wait --condition healthy "$CONTAINER" >/dev/null \
+ || fail 'Failed to start container'
+ podman logs "$CONTAINER"
+else
+ print "Preparing container $CONTAINER ..."
+ set -e
+ podman ps | grep -q $CONTAINER || podman start "$CONTAINER"
+ timeout 60 podman wait --condition healthy "$CONTAINER" >/dev/null \
+ || fail 'Failed to start container'
+ PORT=$(podman inspect -f '{{(index (index .NetworkSettings.Ports "22/tcp") 0).HostPort}}' $CONTAINER || echo null)
+ if [ "$PORT" == 'null' ] || ! podman ps | grep -q "$CONTAINER"
+ then
+ fail 'Cannot work with the specified container.'
+ fi
+ podman exec "$CONTAINER" test -f /var/adm/firstboot-ok || fail 'Existing container was not set up correctly or does not use the expected image.'
+ podman exec "$CONTAINER" sh -c "echo $(cat $PUBKEY) >> /home/checker/.ssh/authorized_keys"
+ set +e
+fi
+
+SSH_ARGS+=("-p$PORT")
+SSH="ssh ${SSH_ARGS[@]}"
+RSYNC_ARGS=('-l' '-r' '-e' "$SSH")
+SSH="$SSH ipv6-localhost"
+
+$SSH true || fail 'Container connection failed.'
+$SSH sudo install -do checker /srv/salt-formulas /srv/salt-testbed || fail 'Failed to create directories.'
+
+rsync "${RSYNC_ARGS[@]}" . ipv6-localhost:/srv/salt-testbed || fail 'Failed to transfer repository.'
+
+$SSH sh <<-EOS || echo 'Test suite returned with errors.'
+ $(typeset -f pprint)
+ $(typeset -f pfail)
+ pushd /srv/salt-testbed >/dev/null
+
+ pprint 'Preparing test environment'
+ sudo bin/prepare_test_env.sh -g -s || pfail
+
+ pprint 'Preparing formulas'
+ bin/get_formulas.py -c -d /srv/salt-formulas -s --clone-from https://gitlab.infra.opensuse.org/saltstack-formulas --clone-branch production || pfail
+
+ pprint Testing: validate
+ bin/test_validate.sh || pfail
+
+ pprint Testing: show_highstate
+ sudo bin/test_show_highstate.sh || pfail
+
+ popd >/dev/null
+ pprint 'All tests' completed
+EOS
+
+if [ "$PRESERVE" == 'false' ]
+then
+ print 'Removing container ...'
+ podman stop "$CONTAINER" >/dev/null || fail 'Failed to stop container.'
+ podman rm "$CONTAINER" >/dev/null || fail 'Failed to remove container.'
+fi
+
+echo Bye.