mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-22 07:38:08 +00:00
c6fc094c93
The Perl tests of the OpenEmbedded Genesis image ran its shell scripts
and C programs through system(), qx, pipes and fork, wrote fake
commands through copied read_file and write_file helpers, and matched
regexes against shell and C source. Many rejections checked only a
non-zero exit, so an earlier, unrelated failure also passed. The
quality gate judged 91% of their 762 checks bad.
The shell and C checks move to bats in xCAT-test/bats. The C programs
are compiled there and run: the plain console, test drivers for the
shared status view and the shell launcher, and genesis-udp-send against
a loopback listener. Each rejection now matches the message of its own
guard. The .t files keep the artifact checks (kas, recipes, kernel
fragments, units, README), read with XCAT::Test::File. Test files left
with no checks are deleted.
Claim map (file -> where its claims went):
- go_xcat_uninstall_installed_only.t -> go_xcat_uninstall_installed_only.bats
- genesis_readme_paths.t, genesis_base_sources_staged.t -> kept in the
.t (README read by slurp_repo_file, tarball read by Archive::Tar)
- genesis_openembedded_{export,hardware,runtime}.t -> the bats file of
the same name
- genesis_openembedded_{bmcsetup,metrics,extensions,qeth,actions,
discovery}.t -> scripts to the bats file of the same name, recipes
and units kept in the .t
- genesis_openembedded_console.t -> built and run checks, state.c,
plain_ui.c and shell.c to genesis_openembedded_console.bats; Newt
source checks kept in the .t
- genesis_openembedded.t -> oe/build to genesis_openembedded_build.bats;
shell matches to the runtime, hardware and extensions bats; console
matches to the console bats or the console .t; artifacts kept
Red proof: every moved or new test was run against a mutated copy of
the code it covers and went red, for example: rpm -q not checked,
tar without --mtime, VEX report not exported, support dir after PATH,
digest check off, layer2 forced, wildcard device allowed, arguments
eval'd, tcp probe skipped, SOURCE_PORT 302, AF_INET only, stage timer
compared, launch error ignored, kas run twice, status written in place.
One mutation stayed green: tar without --owner=0 when the test runs as
root.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
224 lines
8.6 KiB
Bash
224 lines
8.6 KiB
Bash
#!/usr/bin/env bats
|
|
#
|
|
# genesis-action runs in the OpenEmbedded Genesis image. It reads the node action (the destiny)
|
|
# from xCAT and runs it: discovery, a wait state, an approved command, a reboot into an
|
|
# install, or a poweroff. getdestiny and nextdestiny are scratch scripts that answer from a
|
|
# queue file. The other commands are scratch scripts that log their arguments.
|
|
|
|
load 'helpers/shell_source'
|
|
|
|
INIT_FILES='xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files'
|
|
|
|
setup()
|
|
{
|
|
ACTION_SCRIPT="$(require_repo_file "$INIT_FILES/genesis-action")"
|
|
root="$BATS_TEST_TMPDIR"
|
|
bin="$root/bin"
|
|
state_dir="$root/run"
|
|
status_dir="$state_dir/status"
|
|
approved_dir="$root/actions"
|
|
destiny_file="$state_dir/destiny"
|
|
command_log="$root/commands.log"
|
|
mkdir -p "$bin" "$status_dir" "$approved_dir"
|
|
printf '%s\n' XCATDEST=192.0.2.10:3001 XCATMASTER=192.0.2.10 XCATPORT=3001 \
|
|
XCAT_INTERFACE=eth0 XCAT_SOURCE_ADDRESS=192.0.2.98 >"$state_dir/genesis.env"
|
|
printf '42.00 80.00\n' >"$root/uptime"
|
|
|
|
cat >"$bin/getdestiny" <<'SH'
|
|
#!/bin/sh
|
|
printf 'getdestiny %s\n' "$*" >>"$XCAT_TEST_LOG"
|
|
metadata=
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--metadata) metadata=$2; shift 2 ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
[ -s "$XCAT_TEST_GETDESTINY_QUEUE" ] || exit 1
|
|
response=$(head -n 1 "$XCAT_TEST_GETDESTINY_QUEUE")
|
|
tail -n +2 "$XCAT_TEST_GETDESTINY_QUEUE" >"$XCAT_TEST_GETDESTINY_QUEUE.new"
|
|
mv "$XCAT_TEST_GETDESTINY_QUEUE.new" "$XCAT_TEST_GETDESTINY_QUEUE"
|
|
if [ -n "$metadata" ]; then
|
|
printf '%s\n' 'XCAT_NODE_NAME=node042' "XCAT_DESTINY=$response" >"$metadata"
|
|
fi
|
|
printf '%s\n' "$response"
|
|
SH
|
|
cat >"$bin/nextdestiny" <<'SH'
|
|
#!/bin/sh
|
|
printf 'nextdestiny %s\n' "$*" >>"$XCAT_TEST_LOG"
|
|
[ -s "$XCAT_TEST_NEXTDESTINY_QUEUE" ] || exit 1
|
|
response=$(head -n 1 "$XCAT_TEST_NEXTDESTINY_QUEUE")
|
|
tail -n +2 "$XCAT_TEST_NEXTDESTINY_QUEUE" >"$XCAT_TEST_NEXTDESTINY_QUEUE.new"
|
|
mv "$XCAT_TEST_NEXTDESTINY_QUEUE.new" "$XCAT_TEST_NEXTDESTINY_QUEUE"
|
|
printf '%s\n' "$response"
|
|
SH
|
|
printf '#!/bin/sh\nprintf "logger %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/logger"
|
|
printf '#!/bin/sh\nprintf "%%s\\n" discover >>"$XCAT_TEST_LOG"\n' >"$bin/discover"
|
|
printf '#!/bin/sh\nprintf "%%s\\n" getcert >>"$XCAT_TEST_LOG"\n: >"$XCAT_TEST_CERTIFICATE_FILE"\n' >"$bin/getcert"
|
|
printf '#!/bin/sh\nprintf "reboot-control %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/reboot-control"
|
|
printf '#!/bin/sh\nprintf "poweroff-control %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/poweroff-control"
|
|
printf '#!/bin/sh\nprintf "ipmitool %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n[ "${XCAT_TEST_IPMI-0}" = 1 ]\n' >"$bin/ipmitool"
|
|
printf '#!/bin/sh\nprintf inventory >>"$XCAT_TEST_LOG"\nprintf " <%%s>" "$@" >>"$XCAT_TEST_LOG"\nprintf "\\n" >>"$XCAT_TEST_LOG"\n' >"$approved_dir/inventory"
|
|
printf '#!/bin/sh\nprintf "%%s\\n" bmcsetup >>"$XCAT_TEST_LOG"\n' >"$approved_dir/bmcsetup"
|
|
chmod 0755 "$bin"/* "$approved_dir"/*
|
|
|
|
export PATH="$bin:$PATH"
|
|
export XCAT_STATE_DIR="$state_dir"
|
|
export XCAT_STATUS_COMMAND="$(require_repo_file "$INIT_FILES/genesis-status")"
|
|
export XCAT_STATUS_DIR="$status_dir"
|
|
export XCAT_DESTINY_FILE="$destiny_file"
|
|
export XCAT_METADATA_FILE="$state_dir/xcat-response.env"
|
|
export XCAT_NETWORK_FILE="$state_dir/genesis.env"
|
|
export XCAT_DISCOVER_COMMAND="$bin/discover"
|
|
export XCAT_CERTIFICATE_COMMAND="$bin/getcert"
|
|
export XCAT_CERTIFICATE_FILE="$root/cert.pem"
|
|
export XCAT_GETDESTINY_COMMAND="$bin/getdestiny"
|
|
export XCAT_NEXTDESTINY_COMMAND="$bin/nextdestiny"
|
|
export XCAT_ACTION_COMMAND_DIR="$approved_dir"
|
|
export XCAT_REBOOT_COMMAND="$bin/reboot-control"
|
|
export XCAT_POWEROFF_COMMAND="$bin/poweroff-control"
|
|
export XCAT_ACTION_POLL_SECONDS=0
|
|
export XCAT_ACTION_REQUEST_TIMEOUT=2
|
|
export XCAT_ACTION_OPERATION_TIMEOUT=2
|
|
export XCAT_UPTIME_FILE="$root/uptime"
|
|
export XCAT_GENESIS_FUNCTIONS="$(require_repo_file "$INIT_FILES/genesis-functions")"
|
|
export XCAT_TEST_LOG="$command_log"
|
|
export XCAT_TEST_GETDESTINY_QUEUE="$root/getdestiny.queue"
|
|
export XCAT_TEST_NEXTDESTINY_QUEUE="$root/nextdestiny.queue"
|
|
export XCAT_TEST_CERTIFICATE_FILE="$root/cert.pem"
|
|
}
|
|
|
|
# Run genesis-action for one destiny. GETDESTINY and NEXTDESTINY set the queued xCAT
|
|
# answers, NO_CERTIFICATE starts without a certificate, MAX_STEPS and IPMI pass through.
|
|
# Sets $status, $log (the command log) and $record (the published action status).
|
|
run_action()
|
|
{
|
|
printf '%s\n' "$1" >"$destiny_file"
|
|
printf '%s' "${GETDESTINY-$1
|
|
}" >"$XCAT_TEST_GETDESTINY_QUEUE"
|
|
printf '%s' "${NEXTDESTINY-standby
|
|
}" >"$XCAT_TEST_NEXTDESTINY_QUEUE"
|
|
rm -f "$command_log" "$XCAT_CERTIFICATE_FILE" "$status_dir/action.env"
|
|
[ -n "${NO_CERTIFICATE-}" ] || printf 'certificate\n' >"$XCAT_CERTIFICATE_FILE"
|
|
status=0
|
|
XCAT_ACTION_MAX_STEPS="${MAX_STEPS:-1}" XCAT_TEST_IPMI="${IPMI:-0}" \
|
|
/bin/bash "$ACTION_SCRIPT" </dev/null >/dev/null 2>&1 || status=$?
|
|
log="$(cat "$command_log" 2>/dev/null || true)"
|
|
record="$(cat "$status_dir/action.env" 2>/dev/null || true)"
|
|
}
|
|
|
|
has_line()
|
|
{
|
|
grep -Eq "$2" <<<"$1"
|
|
}
|
|
|
|
# Not "! has_line": bash ignores errexit for a command inverted with "!".
|
|
has_no_line()
|
|
{
|
|
! grep -Eq "$2" <<<"$1"
|
|
}
|
|
|
|
@test "discovery sends inventory, enrolls a certificate, then queries xCAT and loads the node action" {
|
|
GETDESTINY=$'standby\n' run_action discover
|
|
[ "$status" -eq 0 ]
|
|
has_line "$log" '^discover$'
|
|
has_line "$log" '^getcert$'
|
|
[ "$(grep -nx discover <<<"$log" | cut -d: -f1)" -lt "$(grep -n '^getdestiny ' <<<"$log" | head -n1 | cut -d: -f1)" ]
|
|
[ "$(cat "$destiny_file")" = standby ]
|
|
}
|
|
|
|
@test "shell becomes a managed wait state that accepts a remote action change, and opens no shell" {
|
|
GETDESTINY=$'install test-image\n' run_action shell
|
|
[ "$status" -eq 0 ]
|
|
has_no_line "$log" '(^|/)bash(\s|$)'
|
|
[ "$(cat "$destiny_file")" = 'install test-image' ]
|
|
}
|
|
|
|
@test "standby polls xCAT, obtains a missing certificate, and publishes an idle state" {
|
|
GETDESTINY=$'standby\nstandby\n' NO_CERTIFICATE=1 MAX_STEPS=2 run_action standby
|
|
[ "$status" -eq 0 ]
|
|
has_line "$log" '^getcert$'
|
|
has_line "$record" '^STATE=IDLE$'
|
|
has_line "$record" '^VERIFIED_SECONDS=42$'
|
|
}
|
|
|
|
@test "osimage and ondiscover complete and advance the action chain" {
|
|
for action in osimage ondiscover; do
|
|
run_action "$action"
|
|
[ "$status" -eq 0 ]
|
|
has_line "$log" '^nextdestiny '
|
|
done
|
|
}
|
|
|
|
@test "an approved command completes, gets its arguments unevaluated, and advances the chain" {
|
|
run_action 'runcmd=inventory storage safe;reboot'
|
|
[ "$status" -eq 0 ]
|
|
has_line "$log" '^inventory <storage> <safe;reboot>$'
|
|
has_line "$log" '^nextdestiny '
|
|
}
|
|
|
|
@test "sequential discovery can run BMC setup, which advances the chain" {
|
|
run_action runcmd=bmcsetup
|
|
[ "$status" -eq 0 ]
|
|
has_line "$log" '^bmcsetup$'
|
|
has_line "$log" '^nextdestiny '
|
|
}
|
|
|
|
@test "an unpackaged command is rejected with a specific failure code" {
|
|
run_action runcmd=unpackaged
|
|
[ "$status" -ne 0 ]
|
|
has_line "$record" '^CODE=ACTION_COMMAND_NOT_APPROVED$'
|
|
}
|
|
|
|
@test "runimage, configraid and sysclone fail closed and report their migration status" {
|
|
for pair in runimage:UNSAFE_LEGACY_ACTION configraid:LEGACY_STORAGE_ACTION \
|
|
sysclone:LEGACY_SYSCLONE_ACTION; do
|
|
run_action "${pair%%:*}"
|
|
[ "$status" -ne 0 ]
|
|
has_line "$record" "^CODE=${pair#*:}\$"
|
|
done
|
|
}
|
|
|
|
@test "boot and reboot advance the chain, request a one-time network boot and a reboot" {
|
|
for action in boot reboot; do
|
|
IPMI=1 run_action "$action"
|
|
[ "$status" -eq 0 ]
|
|
has_line "$log" '^nextdestiny '
|
|
has_line "$log" '^ipmitool chassis bootdev pxe$'
|
|
has_line "$log" '^reboot-control reboot$'
|
|
done
|
|
}
|
|
|
|
@test "install, netboot and statelite keep the server-selected chain position and reboot" {
|
|
for action in install netboot statelite; do
|
|
run_action "$action"
|
|
[ "$status" -eq 0 ]
|
|
has_no_line "$log" '^nextdestiny '
|
|
has_line "$log" '^reboot-control reboot$'
|
|
done
|
|
}
|
|
|
|
@test "shutdown requests a poweroff" {
|
|
run_action shutdown
|
|
[ "$status" -eq 0 ]
|
|
has_line "$log" '^poweroff-control poweroff$'
|
|
}
|
|
|
|
@test "an xCAT error action fails and stays visible" {
|
|
run_action 'error=policy denied'
|
|
[ "$status" -ne 0 ]
|
|
has_line "$record" '^CODE=XCAT_ACTION_ERROR$'
|
|
}
|
|
|
|
@test "an unknown action fails with a specific failure code" {
|
|
run_action unknown
|
|
[ "$status" -ne 0 ]
|
|
has_line "$record" '^CODE=XCAT_ACTION_UNSUPPORTED$'
|
|
}
|
|
|
|
@test "a failed refresh uses the previously received action, and the failed poll stays visible" {
|
|
GETDESTINY='' run_action standby
|
|
[ "$status" -eq 0 ]
|
|
has_line "$record" '^STATE=DEGRADED$'
|
|
}
|