From 37beecab8080112418463640c97ae9a102d558ed Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Tue, 27 Oct 2026 15:57:41 +0100 Subject: [PATCH 1/4] Run reactor tests in jenkins Added the reactor tests to jenkins, to be able to run them in jenkins. This means we can test cf-reactor on all the different platforms. Signed-off-by: Victor Moene --- .gitignore | 1 + configure.ac | 1 + tests/Makefile.am | 2 +- tests/reactor/Makefile.am | 44 +++++++++ tests/reactor/lib.sh | 82 ++++++++++++++--- tests/reactor/run.sh | 65 +++---------- tests/reactor/run_test.sh | 92 +++++++++++++++++++ tests/reactor/tests/00_basics/009_interval.sh | 4 +- .../tests/00_basics/020_policy_reload.sh | 2 +- .../tests/02_then_bundles/010_processes.sh | 7 +- 10 files changed, 229 insertions(+), 71 deletions(-) create mode 100644 tests/reactor/Makefile.am create mode 100644 tests/reactor/run_test.sh diff --git a/.gitignore b/.gitignore index ff74fda4594..cb1a01a8fe5 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ stamp-h1 /tests/acceptance/25_cf-execd/Makefile /tests/acceptance/Makefile /tests/load/Makefile +/tests/reactor/Makefile /tests/unit/Makefile /tests/static-check/Makefile /tests/valgrind-check/Makefile diff --git a/configure.ac b/configure.ac index 5ed3b61a351..414aa1d4c66 100644 --- a/configure.ac +++ b/configure.ac @@ -2032,6 +2032,7 @@ AC_CONFIG_FILES([Makefile tests/acceptance/25_cf-execd/Makefile tests/unit/Makefile tests/load/Makefile + tests/reactor/Makefile tests/static-check/Makefile tests/valgrind-check/Makefile tests/asan-check/Makefile]) diff --git a/tests/Makefile.am b/tests/Makefile.am index 0f80e566120..8603fc839e3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -21,4 +21,4 @@ # (COSL) may apply to this file if you as a licensee so wish it. See # included file COSL.txt. # -SUBDIRS = unit load acceptance static-check valgrind-check asan-check +SUBDIRS = unit load acceptance reactor static-check valgrind-check asan-check diff --git a/tests/reactor/Makefile.am b/tests/reactor/Makefile.am new file mode 100644 index 00000000000..eb5325d0fbf --- /dev/null +++ b/tests/reactor/Makefile.am @@ -0,0 +1,44 @@ +# +# Copyright 2026 Northern.tech AS +# +# This file is part of CFEngine 3 - written and maintained by Northern.tech AS. +# +# 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; version 3. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +# +# To the extent this program is licensed as part of the Enterprise +# versions of CFEngine, the applicable Commercial Open Source License +# (COSL) may apply to this file if you as a licensee so wish it. See +# included file COSL.txt. +# + +# The test scripts, with their policy next to them +REACTOR_TEST_SH = $(sort $(wildcard \ + $(srcdir)/tests/*/*.sh \ + $(srcdir)/tests/*/unsafe/*.sh)) +REACTOR_TEST_CF = $(REACTOR_TEST_SH:.sh=.cf) + +# cf-reactor is not built with builtin extensions (e.g. on Windows) +if !BUILTIN_EXTENSIONS +TESTS = $(patsubst $(srcdir)/%,%,$(REACTOR_TEST_SH)) +endif + +# Each test is run by run_test.sh, with the binaries of this build. The ones +# in an unsafe/ directory are skipped unless UNSAFE_TESTS=1. +TESTS_ENVIRONMENT = env \ + CF_REACTOR=$(abs_top_builddir)/cf-reactor/cf-reactor \ + CF_PROMISES=$(abs_top_builddir)/cf-promises/cf-promises \ + $(SHELL) $(srcdir)/run_test.sh + +EXTRA_DIST = lib.cf lib.sh run.sh run_test.sh \ + $(patsubst $(srcdir)/%,%,$(REACTOR_TEST_SH) $(REACTOR_TEST_CF)) diff --git a/tests/reactor/lib.sh b/tests/reactor/lib.sh index f49a25905a2..d9754155079 100644 --- a/tests/reactor/lib.sh +++ b/tests/reactor/lib.sh @@ -1,21 +1,81 @@ # Helpers sourced by the tests. Expects CF_REACTOR and LOG to be exported by -# run.sh. +# run_test.sh. +# +# Only POSIX tools are used, as the tests are run on all the platforms (e.g. +# no timeout(1), grep -m or date +%s). + +# Seconds to wait for something before giving up +WAIT_TIMEOUT=60 # number of log lines already consumed by wait_for_log log_offset=0 +# Run command $@ until it succeeds, for at most WAIT_TIMEOUT seconds, polling +# every 0.1 second, or every second where sleep only takes whole seconds. +# Fails if it never succeeded. Runs in a subshell without tracing, so that +# set -x doesn't log every poll. +wait_until() { + ( + set +x + if sleep 0.1 2>/dev/null; then + interval=0.1 polls=$((WAIT_TIMEOUT * 10)) + else + interval=1 polls=$WAIT_TIMEOUT + fi + until "$@"; do + [ "$polls" -gt 0 ] || exit 1 + polls=$((polls - 1)) + sleep "$interval" + done + ) +} + +# Print the current time in seconds since the Epoch. date +%s is not POSIX; +# without it, srand() seeds with the time of day and returns the last seed. +now() { + t=$(date +%s 2>/dev/null) + case "$t" in + ''|*[!0-9]*) awk 'BEGIN { srand(); print srand() }' ;; + *) echo "$t" ;; + esac +} + +# Print the first line of the reactor log after line $1 that contains $2, +# prefixed with its number counted from line $1. Fails if there is none. +log_match() { + tail -n "+$(($1 + 1))" "$LOG" 2>/dev/null | grep -n -F -- "$2" | head -n 1 | grep . +} + +# Fail unless the reactor log contains $1. +log_contains() { + grep -q -F -- "$1" "$LOG" 2>/dev/null +} + +# Print the number of lines of file $1, 0 if it doesn't exist. +count_lines() { + if [ -f "$1" ]; then + # without the padding of some wc implementations + echo $(( $(wc -l < "$1") )) + else + echo 0 + fi +} + +# Fail unless file $1 has at least $2 lines. +has_lines() { + [ "$(count_lines "$1")" -ge "$2" ] +} + # Wait until the reactor log contains $1 in a line after the last match, then # skip past that line, so that the next wait only sees newer output. wait_for_log() { - if ! match=$(timeout 60 sh -c ' - until tail -n "+$(($2 + 1))" "$1" | grep -n -F -m 1 -- "$3"; do - sleep 0.1 - done' _ "$LOG" "$log_offset" "$1") + if ! wait_until log_match "$log_offset" "$1" >/dev/null then echo "Timed out waiting for '$1' in reactor log:" >&2 cat "$LOG" >&2 return 1 fi + match=$(log_match "$log_offset" "$1") log_offset=$((log_offset + ${match%%:*})) } @@ -23,10 +83,7 @@ wait_for_log() { # The watcher thread may log before this line, so search the whole log and # leave the offset of wait_for_log as is. wait_for_watchers() { - if ! timeout 60 sh -c ' - until grep -q -F -- "$2" "$1"; do - sleep 0.1 - done' _ "$LOG" "verbose: cf-reactor event watcher has $1 watcher(s)" + if ! wait_until log_contains "verbose: cf-reactor event watcher has $1 watcher(s)" then echo "Timed out waiting for $1 watcher(s) in reactor log:" >&2 cat "$LOG" >&2 @@ -36,17 +93,14 @@ wait_for_watchers() { # Wait until file $1 has at least $2 lines, then fail if it has more than $2. wait_for_lines() { - if ! timeout 60 sh -c ' - until [ "$(cat "$1" 2>/dev/null | wc -l)" -ge "$2" ]; do - sleep 0.1 - done' _ "$1" "$2" + if ! wait_until has_lines "$1" "$2" then echo "Timed out waiting for $2 line(s) in '$1', reactor log:" >&2 cat "$LOG" >&2 return 1 fi - lines=$(wc -l < "$1") + lines=$(count_lines "$1") if [ "$lines" -ne "$2" ] then echo "Expected $2 line(s) in '$1', got $lines" >&2 diff --git a/tests/reactor/run.sh b/tests/reactor/run.sh index 17e21f198c5..dcf07967ae5 100644 --- a/tests/reactor/run.sh +++ b/tests/reactor/run.sh @@ -2,10 +2,11 @@ # Usage: run.sh [--unsafe] # -# Must be run from the tests/reactor directory. +# Runs all the reactor tests, see run_test.sh. They are also run by +# "make check". # # Tests in an unsafe/ directory modify the system they are running on, so they -# are only run with --unsafe. +# are only run with --unsafe (or UNSAFE_TESTS=1). # # The binaries are taken from $CF_BINDIR if it is set, otherwise from the # CFEngine build tree this directory is part of if they are built, otherwise @@ -13,10 +14,10 @@ fail=0 -unsafe=0 if [ "$1" = "--unsafe" ]; then - unsafe=1 + UNSAFE_TESTS=1 fi +export UNSAFE_TESTS banner() { name=" $(basename "$(dirname "$1")")/$(basename "$1") " @@ -36,62 +37,22 @@ error() { fail=1 } -# Print the path of binary $1, see the usage above. -find_binary() { - if [ -n "$CF_BINDIR" ]; then - echo "$CF_BINDIR/$1" - elif [ -x "$PWD/../../$1/$1" ]; then - # in-tree build, may be a libtool wrapper script - echo "$PWD/../../$1/$1" - else - echo "/var/cfengine/bin/$1" - fi -} - -CF_REACTOR=$(find_binary cf-reactor) -CF_PROMISES=$(find_binary cf-promises) -export CF_REACTOR +dir=$(cd "$(dirname "$0")" && pwd) -for b in "$CF_REACTOR" "$CF_PROMISES"; do - if [ ! -x "$b" ]; then - echo "'$b' not found, build CFEngine or set CF_BINDIR" >&2 - exit 2 - fi -done -echo "Using $CF_REACTOR and $CF_PROMISES" - -for t in "$PWD"/tests/*/*.sh "$PWD"/tests/*/unsafe/*.sh; do +for t in "$dir"/tests/*/*.sh "$dir"/tests/*/unsafe/*.sh; do # skip the pattern itself if there are no unsafe tests [ -f "$t" ] || continue case "$t" in - */unsafe/*) [ "$unsafe" = 1 ] || continue ;; + */unsafe/*) [ "$UNSAFE_TESTS" = 1 ] || continue ;; esac banner "$t" - # create throwaway work dir - WORKDIR=$(mktemp -d) - mkdir -p "$WORKDIR/bin" "$WORKDIR/tmp" - ln -s "$CF_PROMISES" "$WORKDIR/bin/cf-promises" - - # Run a copy of the policy, keeping its path relative to lib.cf, as - # CFEngine refuses to read policy files writable by group or others, which - # those of a checkout made with a umask of 002 are - rel=${t#"$PWD"/} - policy="$WORKDIR/policy/${rel%.sh}.cf" - mkdir -p "$(dirname "$policy")" - cp lib.cf "$WORKDIR/policy/" - cp "${t%.sh}.cf" "$policy" - chmod 600 "$WORKDIR/policy/lib.cf" "$policy" - - CFENGINE_TEST_OVERRIDE_WORKDIR="$WORKDIR" - LOG="$WORKDIR/reactor.log" - PROMISE_NAME=$(basename "$t" .sh) - export WORKDIR CFENGINE_TEST_OVERRIDE_WORKDIR LOG PROMISE_NAME - - sh "$t" "$policy" || error "$t" - - rm -rf "$WORKDIR" + sh "$dir/run_test.sh" "$t" + case $? in + 0|77) ;; + *) error "$t" ;; + esac done exit $fail diff --git a/tests/reactor/run_test.sh b/tests/reactor/run_test.sh new file mode 100644 index 00000000000..22e2f299a14 --- /dev/null +++ b/tests/reactor/run_test.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +# Usage: run_test.sh TEST +# +# Runs the reactor test TEST (a tests/*/*.sh or tests/*/unsafe/*.sh file) in a +# throwaway work dir, and exits with its result: 0 if it passed, 77 if it was +# skipped, anything else if it failed. Used as the test driver by "make check" +# and by run.sh. +# +# Tests in an unsafe/ directory modify the system they are running on, so they +# are skipped unless UNSAFE_TESTS=1. +# +# The binaries are taken from $CF_REACTOR and $CF_PROMISES if they are set, +# otherwise from $CF_BINDIR if it is set, otherwise from the CFEngine build +# tree this directory is part of if they are built, otherwise from +# /var/cfengine/bin. + +if [ $# -ne 1 ]; then + echo "Usage: $0 TEST" >&2 + exit 2 +fi + +t=$1 + +# Explicitly use POSIX tools if needed (Solaris) +if [ -f /usr/xpg4/bin/grep ]; then + PATH=/usr/xpg4/bin:$PATH + export PATH +fi + +case "$t" in + */unsafe/*) + if [ "$UNSAFE_TESTS" != 1 ]; then + echo "Skipping unsafe test '$t', set UNSAFE_TESTS=1 to run it" + exit 77 + fi + ;; +esac + +# directory of lib.cf and lib.sh +REACTOR_TESTS_DIR=$(cd "$(dirname "$0")" && pwd) + +# Print the path of binary $1, see the usage above. +find_binary() { + if [ -n "$CF_BINDIR" ]; then + echo "$CF_BINDIR/$1" + elif [ -x "$REACTOR_TESTS_DIR/../../$1/$1" ]; then + # in-tree build, may be a libtool wrapper script + echo "$REACTOR_TESTS_DIR/../../$1/$1" + else + echo "/var/cfengine/bin/$1" + fi +} + +CF_REACTOR=${CF_REACTOR:-$(find_binary cf-reactor)} +CF_PROMISES=${CF_PROMISES:-$(find_binary cf-promises)} +export CF_REACTOR + +for b in "$CF_REACTOR" "$CF_PROMISES"; do + if [ ! -x "$b" ]; then + echo "'$b' not found, build CFEngine or set CF_BINDIR" >&2 + exit 2 + fi +done + +# Create the throwaway work dir, with mktemp -d where available +WORKDIR=$(mktemp -d "${TMPDIR:-/tmp}/cf-reactor-test.XXXXXX" 2>/dev/null) || { + WORKDIR="${TMPDIR:-/tmp}/cf-reactor-test.$$" + (umask 077 && mkdir "$WORKDIR") || exit 2 +} +WORKDIR=$(cd "$WORKDIR" && pwd) +trap 'rm -rf "$WORKDIR"' EXIT +mkdir -p "$WORKDIR/bin" "$WORKDIR/tmp" +ln -s "$CF_PROMISES" "$WORKDIR/bin/cf-promises" + +# Run a copy of the policy, keeping its path relative to lib.cf, as CFEngine +# refuses to read policy files writable by group or others, which those of a +# checkout made with a umask of 002 are +p="/$t" +rel="tests/${p##*/tests/}" +policy="$WORKDIR/policy/${rel%.sh}.cf" +mkdir -p "$(dirname "$policy")" +cp "$REACTOR_TESTS_DIR/lib.cf" "$WORKDIR/policy/" +cp "${t%.sh}.cf" "$policy" +chmod 600 "$WORKDIR/policy/lib.cf" "$policy" + +CFENGINE_TEST_OVERRIDE_WORKDIR="$WORKDIR" +LOG="$WORKDIR/reactor.log" +PROMISE_NAME=$(basename "$t" .sh) +export WORKDIR CFENGINE_TEST_OVERRIDE_WORKDIR LOG PROMISE_NAME + +sh "$t" "$policy" diff --git a/tests/reactor/tests/00_basics/009_interval.sh b/tests/reactor/tests/00_basics/009_interval.sh index 4a749849370..df73a4cf09d 100644 --- a/tests/reactor/tests/00_basics/009_interval.sh +++ b/tests/reactor/tests/00_basics/009_interval.sh @@ -19,7 +19,7 @@ wait_for_log "File '$TESTFILE' is present, watching for its deletion" rm "$TESTFILE" wait_for_lines "$EVENTS_FILE" 1 check_event "$EVENTS_FILE" 1 "$TESTFILE was deleted" -t1=$(date +%s) +t1=$(now) # recreate the file and wait for the watcher to see it before deleting it again touch "$TESTFILE" @@ -27,7 +27,7 @@ wait_for_log "File '$TESTFILE' is present, watching for its deletion" rm "$TESTFILE" wait_for_lines "$EVENTS_FILE" 2 check_event "$EVENTS_FILE" 2 "$TESTFILE was deleted" -t2=$(date +%s) +t2=$(now) # checking that the interval is at minimum 10 seconds delta=$((t2 - t1)) diff --git a/tests/reactor/tests/00_basics/020_policy_reload.sh b/tests/reactor/tests/00_basics/020_policy_reload.sh index 48348e72338..d6476072c9d 100644 --- a/tests/reactor/tests/00_basics/020_policy_reload.sh +++ b/tests/reactor/tests/00_basics/020_policy_reload.sh @@ -30,7 +30,7 @@ reload_and_wait_for_paused_watchers() { kill -HUP "$reactor_pid" wait_for_log "Rereading policy file" - if ! timeout 60 sh -c 'until [ -f "$1" ]; do sleep 0.1; done' _ "$RELOAD_MARKER" + if ! wait_until [ -f "$RELOAD_MARKER" ] then echo "Timed out waiting for the policy to be reloaded, reactor log:" >&2 cat "$LOG" >&2 diff --git a/tests/reactor/tests/02_then_bundles/010_processes.sh b/tests/reactor/tests/02_then_bundles/010_processes.sh index b653eb059e9..ea0aa104fef 100644 --- a/tests/reactor/tests/02_then_bundles/010_processes.sh +++ b/tests/reactor/tests/02_then_bundles/010_processes.sh @@ -21,8 +21,13 @@ start_process() { ( sleep 978654 & echo $! > "$PID_FILE" ) } +# Fail if process $1 still exists +process_gone() { + ! kill -0 "$1" 2>/dev/null +} + wait_for_process_killed() { - if ! timeout 60 sh -c 'while kill -0 "$1" 2>/dev/null; do sleep 0.1; done' _ "$(cat "$PID_FILE")" + if ! wait_until process_gone "$(cat "$PID_FILE")" then echo "Process $(cat "$PID_FILE") not killed, reactor log:" >&2 cat "$LOG" >&2 From 5f352d3ad9ddc2575d28d6a307b7b504d97f7e2d Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Tue, 27 Oct 2026 16:50:48 +0100 Subject: [PATCH 2/4] Added better logging of the cf-reactor tests Made the logs prettier and added a log file where the xtrace is printed Signed-off-by: Victor Moene --- tests/reactor/run.sh | 90 ++++++++++++++++++++++++--------------- tests/reactor/run_test.sh | 10 ++++- 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/tests/reactor/run.sh b/tests/reactor/run.sh index dcf07967ae5..8394314c5d6 100644 --- a/tests/reactor/run.sh +++ b/tests/reactor/run.sh @@ -2,57 +2,79 @@ # Usage: run.sh [--unsafe] # -# Runs all the reactor tests, see run_test.sh. They are also run by -# "make check". +# Runs all the reactor tests, see run_test.sh, and prints a summary of their +# results. The output of a test is only printed if it failed. They are also +# run by "make check". # # Tests in an unsafe/ directory modify the system they are running on, so they -# are only run with --unsafe (or UNSAFE_TESTS=1). +# are skipped unless run with --unsafe (or UNSAFE_TESTS=1). # # The binaries are taken from $CF_BINDIR if it is set, otherwise from the # CFEngine build tree this directory is part of if they are built, otherwise # from /var/cfengine/bin. -fail=0 - if [ "$1" = "--unsafe" ]; then UNSAFE_TESTS=1 fi export UNSAFE_TESTS -banner() { - name=" $(basename "$(dirname "$1")")/$(basename "$1") " - width=72 - left=$(( (width - ${#name}) / 2 )) - right=$(( width - ${#name} - left )) - [ "$left" -lt 0 ] && left=0 - [ "$right" -lt 0 ] && right=0 - printf '%s%s%s\n' \ - "$(printf "%${left}s" '' | tr ' ' '=')" \ - "$name" \ - "$(printf "%${right}s" '' | tr ' ' '=')" -} +dir=$(cd "$(dirname "$0")" && pwd) -error() { - echo "test '$1' FAILED" >&2 - fail=1 -} +# for now() +. "$dir/lib.sh" -dir=$(cd "$(dirname "$0")" && pwd) +passed=0 +failed=0 +skipped=0 +total=0 +failures="" +suite_start=$(now) -for t in "$dir"/tests/*/*.sh "$dir"/tests/*/unsafe/*.sh; do - # skip the pattern itself if there are no unsafe tests - [ -f "$t" ] || continue - case "$t" in - */unsafe/*) [ "$UNSAFE_TESTS" = 1 ] || continue ;; - esac +# Run test $1 and print its result, with its output if it failed +run_test() { + name=${1#"$dir/tests/"} + name=${name%.sh} + total=$((total + 1)) - banner "$t" + start=$(now) + output=$(sh "$dir/run_test.sh" "$1" 2>&1) + result=$? + elapsed=$(($(now) - start)) - sh "$dir/run_test.sh" "$t" - case $? in - 0|77) ;; - *) error "$t" ;; + case $result in + 0) + passed=$((passed + 1)) + echo "--- PASS: $name (${elapsed}s)" + ;; + 77) + skipped=$((skipped + 1)) + echo "--- SKIP: $name (${elapsed}s)" + ;; + *) + failed=$((failed + 1)) + failures="$failures $name +" + echo "--- FAIL: $name (${elapsed}s)" + echo "$output" + echo "---" + ;; esac +} + +for t in "$dir"/tests/*/*.sh "$dir"/tests/*/unsafe/*.sh; do + # skip the pattern itself if there are no unsafe tests + [ -f "$t" ] || continue + run_test "$t" done -exit $fail +echo "" +echo "==============================" +echo "Test Results: $passed passed, $failed failed, $skipped skipped (total: $total, $(($(now) - suite_start))s)" +if [ "$failed" -gt 0 ]; then + echo "" + echo "Failed tests:" + printf '%s' "$failures" + exit 1 +fi +echo "==============================" +echo "All reactor tests passed" diff --git a/tests/reactor/run_test.sh b/tests/reactor/run_test.sh index 22e2f299a14..dc4b66fb79b 100644 --- a/tests/reactor/run_test.sh +++ b/tests/reactor/run_test.sh @@ -7,6 +7,9 @@ # skipped, anything else if it failed. Used as the test driver by "make check" # and by run.sh. # +# The output of the test, with its set -x trace, is only printed if it didn't +# pass, so that the output of the tests that pass doesn't bury it. +# # Tests in an unsafe/ directory modify the system they are running on, so they # are skipped unless UNSAFE_TESTS=1. # @@ -89,4 +92,9 @@ LOG="$WORKDIR/reactor.log" PROMISE_NAME=$(basename "$t" .sh) export WORKDIR CFENGINE_TEST_OVERRIDE_WORKDIR LOG PROMISE_NAME -sh "$t" "$policy" +sh "$t" "$policy" > "$WORKDIR/test.log" 2>&1 +result=$? +if [ "$result" -ne 0 ]; then + cat "$WORKDIR/test.log" +fi +exit "$result" From 51a201433a28bc458d831795b4159217729f0b71 Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Wed, 28 Oct 2026 15:26:13 +0100 Subject: [PATCH 3/4] Fixed out of range scalar value for guest environments The guest environment cannot contain "-". On hosts containing "-", the promise fails. The host name is now canonofied. Signed-off-by: Victor Moene --- tests/reactor/tests/02_then_bundles/008_guest_environments.cf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reactor/tests/02_then_bundles/008_guest_environments.cf b/tests/reactor/tests/02_then_bundles/008_guest_environments.cf index 9cbb424c202..eb03bec6746 100644 --- a/tests/reactor/tests/02_then_bundles/008_guest_environments.cf +++ b/tests/reactor/tests/02_then_bundles/008_guest_environments.cf @@ -24,7 +24,7 @@ bundle reactor run_guest_environments # no classes body here: the test looks at the log instead. "test" environment_type => "test", - environment_host => "$(sys.host)", + environment_host => canonify("$(sys.host)"), environment_state => "running"; } From 045402d1fe23f327fd2ffcafaa951eb7787722ca Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Wed, 28 Oct 2026 16:12:38 +0100 Subject: [PATCH 4/4] Fixed cf-reactor users test failing when run as root The test promised the current user with policy "present" and expected the promise to be kept. For an existing user, "present" also means "not locked". When run as root, cf-reactor can read /etc/shadow, and if the account's password hash starts with '!' (as root's does on some systems), it is considered locked and gets unlocked. The promise was then repaired instead of kept, and the test modified the root account. As a normal user, /etc/shadow is unreadable, so the account was considered unlocked and the test passed. Instead, we modify the policy to always return "kept" Signed-off-by: Victor Moene --- tests/reactor/tests/02_then_bundles/005_users.cf | 16 ++++++++-------- tests/reactor/tests/02_then_bundles/005_users.sh | 3 --- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/reactor/tests/02_then_bundles/005_users.cf b/tests/reactor/tests/02_then_bundles/005_users.cf index 29bbb8063fd..d696e8ecefe 100644 --- a/tests/reactor/tests/02_then_bundles/005_users.cf +++ b/tests/reactor/tests/02_then_bundles/005_users.cf @@ -15,12 +15,15 @@ body when deleted(file) file_deleted => "$(file)"; } -bundle reactor run_users(user) +bundle reactor run_users { users: - # the user running the test, so that it is kept without being root - "$(user)" - policy => "present", + # a user that doesn't exist, so that the promise is kept without changing + # anything, whether or not the test runs as root. An existing user with + # policy "present" is unlocked if its password is locked, which happens + # to root on some systems. + "cf-reactor-test-no-such-user" + policy => "absent", classes => then_outcome("users"); } @@ -29,13 +32,10 @@ bundle reactor test vars: "test" string => getenv("TESTFILE", 999); "events_file" string => getenv("EVENTS_FILE", 999); - "user" string => getenv("TEST_USER", 999); "promise_name" string => basename("$(this.promise_filename)", ".cf"); events: "$(promise_name)" when => deleted("$(test)"), - then => { - run_users("$(user)"), record_then_outcome("$(events_file)", "users") - }; + then => { run_users, record_then_outcome("$(events_file)", "users") }; } diff --git a/tests/reactor/tests/02_then_bundles/005_users.sh b/tests/reactor/tests/02_then_bundles/005_users.sh index 84c8a509110..9601df9f8dd 100644 --- a/tests/reactor/tests/02_then_bundles/005_users.sh +++ b/tests/reactor/tests/02_then_bundles/005_users.sh @@ -13,9 +13,6 @@ EVENTS_FILE="$WORKDIR/tmp/events" export TESTFILE EVENTS_FILE -TEST_USER=$(id -un) -export TEST_USER - touch "$TESTFILE" start_reactor "$POLICY_FILE"