#!/bin/bash
# Validate that a highstate works for all roles
# Takes the role name as an argument
set -u
role="$1"
[[ $(whoami) == 'root' ]] || { echo 'Please run this script as root'; exit 1; }
# sysctl: cannot stat /proc/sys/net/core/netdev_max_backlog (and some other /proc files): No such file or directory
( cd /sbin/ || exit 1 ; ln -sf /usr/bin/true sysctl )
source bin/get_colors.sh
test/setup/master_minion || exit 1
IDFILE="pillar/id/$HOSTNAME.sls"
out="$role.txt"
echo "START OF $role" > "$out"
echo_INFO "Testing role: $role"
printf 'roles:\n- %s' "$role" >> "$IDFILE"
if [ -x "test/setup/role/$role" ]
then
echo "Preparing test environment for role $role ..." >> "$out"
ROLE_SCRIPT="test/setup/role/$role"
if ! "$ROLE_SCRIPT"
then
echo "Execution of $ROLE_SCRIPT failed, refusing to proceed with role test."
exit 1
fi
fi
salt --out-file=setup.txt --out-file-append "$HOSTNAME" saltutil.refresh_pillar
salt-call --out-file="$out" --out-file-append --retcode-passthrough --state-output=full --output-diff state.apply test=True
rolestatus=$?
echo >> "$out"
if test $rolestatus = 0; then
echo_PASSED
else
echo_FAILED
fi
echo
echo "END OF $role" >> "$out"
# "log_granular_levels" is a thing, but regex is easier here
# lines matching PATTERNS will be stripped from the log files
PATTERNS=(
'/Marking .* as a jinja (filter|global|test)/'
'/The functions from module .* are/'
'/MasterEvent (PUB|PULL) socket URI/'
'/(Missing|Reading) configuration/'
'/Using cached minion ID/'
'/Override __(grains|utils)__/'
'/dmidecode/'
'/Grains refresh requested/'
'/LazyLoad/'
'/Loading static grains from/'
'/Authentication (request|accepted)/'
'/Worker binding to socket/'
'/Creating master /'
'/ReqServer (clients|worker)/'
'/Started .* with pid/'
)
PATTERNS=( "${PATTERNS[@]/%/||}" '/^xxx$/' )
filter_log () {
perl -ne "print unless ${PATTERNS[*]}" "$1" > "$2"
}
filter_log /var/log/salt/minion minion_log.txt
filter_log /var/log/salt/master master_log.txt
salt "$HOSTNAME" pillar.items > pillar.txt
mkdir render
render_log='render/_log.txt'
render_failures=( $(gawk 'match($0, /Rendering SLS '\''base:(.*)'\'' failed/, capture) { gsub(/\./, "/", capture[1]); print "salt/" capture[1] ".sls" }' minion_log.txt) )
if [ -n "${render_failures[*]}" ]
then
{
echo 'State files failed to render:'
echo "${render_failures[*]}"
echo
} | tee -a "$render_log"
for state in "${render_failures[@]}"
do
echo "Processing: $state" | tee -a "$render_log"
state_path="$PWD/$state"
# state rendering logic is pointless, as we operate on states which failed rendering before
# leaving for future reference
#state_out_dir="render/$(dirname "$state")"
#state_out_file="render/$state.txt"
#echo "Rendering: $state" | tee -a "$render_log"
#if [ ! -d "$state_out_dir" ]
#then
# mkdir -pv "$state_out_dir" >> "$render_log"
#fi
#salt-call --out-file="$state_out_file" slsutil.renderer "$state_path" default_renderer=jinja
pillar_references=( $(awk '$0 !~ /pillar\.get/{ next } /pillar\.get/match($0, /\(['\''|"]([[:alnum:]_:]+)['\''|"]/, capture) { printf capture[1] }' "$state_path") )
if [ -n "${pillar_references[*]}" ]
then
pillar_files=()
for pillar in "${pillar_references[@]}"
do
pillar_lines+=( ${pillar//:/ } )
while read file
do
for line in "${pillar_lines[@]}"
do
grep -q "$line" "$file" || continue 2
done
pillar_files+=("$file")
done < <(find pillar -type f -name '*.sls')
done
{
echo
echo 'Possibly related pillar files:'
echo "${pillar_files[*]}"
echo
} | tee -a "$render_log"
mkdir work
for pillar_file in "${pillar_files[@]}"
do
echo "Processing: $pillar_file" | tee -a "$render_log"
# traversing directories in the GitLab artifact browser is annoying, hence replacing slashes with underscores
pillar_out_file="render/${pillar_file//\//_}.txt"
if [ -f "$pillar_out_file" ]
then
echo "File $pillar_file already rendered." >> "$render_log"
else
echo "Rendering: $pillar_file" | tee -a "$render_log"
# https://github.com/saltstack/salt/issues/51835#issuecomment-1536442278
perl -pe \
's/(?<BEGIN>\{%-? )(?:(?<PRE>from ["'\''])(?<PATH>.*)(?<POST1>["'\''] import )(?<POST2>[\w\s]+)|(?<PRE>import_yaml ["'\''])(?<PATH>.*)(?<POST1>["'\''] ?.*? as \w+))(?<END> -?%\})$/$+{BEGIN}$+{PRE}\/srv\/pillar\/$+{PATH}$+{POST1}$+{POST2}$+{END}/' \
"$pillar_file" > work/this
grep srv work/this >> "$render_log"
salt-call --out-file="$pillar_out_file" slsutil.renderer "$PWD/work/this" default_renderer=jinja
rm work/this
fi
echo >> "$render_log"
done
fi
done
else
echo 'No render failures found (this is probably a good thing!)' > "$render_log"
fi
echo
echo 'Output and logs can be found in the job artifacts!'
exit $rolestatus
# vim:expandtab