This commit is contained in:
Alexandre Aubin 2024-09-01 17:13:17 +02:00
parent a98ec7d470
commit 45485f499a
2 changed files with 159 additions and 163 deletions

View file

@ -4,8 +4,7 @@
# "Low-level" logistic helpers # "Low-level" logistic helpers
#================================================= #=================================================
_STUFF_TO_RUN_BEFORE_INITIAL_SNAPSHOT() _STUFF_TO_RUN_BEFORE_INITIAL_SNAPSHOT() {
{
# Print the version of YunoHost from the LXC container # Print the version of YunoHost from the LXC container
log_small_title "YunoHost versions" log_small_title "YunoHost versions"
$lxc exec $LXC_NAME -t -- /bin/bash -c "yunohost --version" | tee -a "$full_log" $lxc exec $LXC_NAME -t -- /bin/bash -c "yunohost --version" | tee -a "$full_log"
@ -18,32 +17,28 @@ _STUFF_TO_RUN_BEFORE_INITIAL_SNAPSHOT()
[[ -e $package_path/manifest.toml ]] || log_critical "The app CI / package_check doesn't support testing packaging v1 apps anymore." [[ -e $package_path/manifest.toml ]] || log_critical "The app CI / package_check doesn't support testing packaging v1 apps anymore."
log_title "Basic bash syntax checks" log_title "Basic bash syntax checks"
local syntax_issue=false local syntax_issue=false
pushd $package_path/scripts >/dev/null pushd $package_path/scripts >/dev/null
for SCRIPT in $(ls _common.sh install remove upgrade backup restore change_url config 2>/dev/null) for SCRIPT in $(ls _common.sh install remove upgrade backup restore change_url config 2>/dev/null); do
do # bash -n / noexec option allows to find syntax issues without actually running the scripts
# bash -n / noexec option allows to find syntax issues without actually running the scripts # cf https://unix.stackexchange.com/questions/597743/bash-shell-noexec-option-usage-purpose
# cf https://unix.stackexchange.com/questions/597743/bash-shell-noexec-option-usage-purpose bash -n $SCRIPT 2>&1 | tee -a /proc/self/fd/3
bash -n $SCRIPT 2>&1 | tee -a /proc/self/fd/3 [[ ${PIPESTATUS[0]} == 0 ]] || syntax_issue=true
[[ ${PIPESTATUS[0]} == 0 ]] || syntax_issue=true done
done
popd >/dev/null popd >/dev/null
if [[ $syntax_issue == false ]] if [[ $syntax_issue == false ]]; then
then
log_report_test_success log_report_test_success
else else
echo "{'level': 0}" > $result_json echo "{'level': 0}" >$result_json
log_critical "Obvious syntax issues found which will make the scripts crash ... not running the actual tests until these are fixed" log_critical "Obvious syntax issues found which will make the scripts crash ... not running the actual tests until these are fixed"
fi fi
# We filter apt deps starting with $app_id to prevent stupid issues with for example cockpit and transmission where the apt package is not properly reinstalled on reinstall-after-remove test ... # We filter apt deps starting with $app_id to prevent stupid issues with for example cockpit and transmission where the apt package is not properly reinstalled on reinstall-after-remove test ...
local apt_deps=$(python3 -c "import toml, sys; t = toml.loads(sys.stdin.read()); P = t['resources'].get('apt', {}).get('packages', ''); P = P.replace(',', ' ').split() if isinstance(P, str) else P; P = [p for p in P if p != '$app_id' and not p.startswith('$app_id-')]; print(' '.join(P));" < $package_path/manifest.toml) local apt_deps=$(python3 -c "import toml, sys; t = toml.loads(sys.stdin.read()); P = t['resources'].get('apt', {}).get('packages', ''); P = P.replace(',', ' ').split() if isinstance(P, str) else P; P = [p for p in P if p != '$app_id' and not p.startswith('$app_id-')]; print(' '.join(P));" <$package_path/manifest.toml)
if [[ -n "$apt_deps" ]] if [[ -n "$apt_deps" ]]; then
then
log_title "Preinstalling apt dependencies before creating the initial snapshot..." log_title "Preinstalling apt dependencies before creating the initial snapshot..."
apt="LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get --assume-yes --quiet -o=Acquire::Retries=3 -o=Dpkg::Use-Pty=0" apt="LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get --assume-yes --quiet -o=Acquire::Retries=3 -o=Dpkg::Use-Pty=0"
@ -51,13 +46,11 @@ _STUFF_TO_RUN_BEFORE_INITIAL_SNAPSHOT()
fi fi
# Gotta generate the psql password even though apparently it's not even useful anymore these days but it otherwise trigger warnings ~_~ # Gotta generate the psql password even though apparently it's not even useful anymore these days but it otherwise trigger warnings ~_~
if echo "$apt_deps" | grep -q postgresql if echo "$apt_deps" | grep -q postgresql; then
then
$lxc exec $LXC_NAME -t -- /bin/bash -c "yunohost tools regen-conf postgresql" | tee -a "$full_log" >/dev/null $lxc exec $LXC_NAME -t -- /bin/bash -c "yunohost tools regen-conf postgresql" | tee -a "$full_log" >/dev/null
fi fi
} }
_RUN_YUNOHOST_CMD() { _RUN_YUNOHOST_CMD() {
log_debug "Running yunohost $1" log_debug "Running yunohost $1"
@ -75,16 +68,15 @@ _RUN_YUNOHOST_CMD() {
check_witness_files && return $returncode || return 2 check_witness_files && return $returncode || return 2
} }
_PREINSTALL () { _PREINSTALL() {
local preinstall_template="$(jq -r '.preinstall_template' $current_test_infos)" local preinstall_template="$(jq -r '.preinstall_template' $current_test_infos)"
# Exec the pre-install instruction, if there one # Exec the pre-install instruction, if there one
if [ -n "$preinstall_template" ] if [ -n "$preinstall_template" ]; then
then
log_small_title "Running pre-install steps" log_small_title "Running pre-install steps"
# Copy all the instructions into a script # Copy all the instructions into a script
local preinstall_script="$TEST_CONTEXT/preinstall.sh" local preinstall_script="$TEST_CONTEXT/preinstall.sh"
echo "$preinstall_template" > "$preinstall_script" echo "$preinstall_template" >"$preinstall_script"
# Hydrate the template with variables # Hydrate the template with variables
sed -i "s/\$USER/$TEST_USER/g" "$preinstall_script" sed -i "s/\$USER/$TEST_USER/g" "$preinstall_script"
sed -i "s/\$DOMAIN/$DOMAIN/g" "$preinstall_script" sed -i "s/\$DOMAIN/$DOMAIN/g" "$preinstall_script"
@ -97,17 +89,16 @@ _PREINSTALL () {
fi fi
} }
_PREUPGRADE () { _PREUPGRADE() {
local preupgrade_template="$(jq -r '.preupgrade_template' $current_test_infos)" local preupgrade_template="$(jq -r '.preupgrade_template' $current_test_infos)"
local commit=${1:-HEAD} local commit=${1:-HEAD}
# Exec the pre-upgrade instruction, if there one # Exec the pre-upgrade instruction, if there one
if [ -n "$preupgrade_template" ] if [ -n "$preupgrade_template" ]; then
then
log_small_title "Running pre-upgrade steps" log_small_title "Running pre-upgrade steps"
# Copy all the instructions into a script # Copy all the instructions into a script
local preupgrade_script="$TEST_CONTEXT/preupgrade.sh" local preupgrade_script="$TEST_CONTEXT/preupgrade.sh"
echo "$preupgrade_template" >> "$preupgrade_script" echo "$preupgrade_template" >>"$preupgrade_script"
# Hydrate the template with variables # Hydrate the template with variables
sed -i "s/\$USER/$TEST_USER/g" "$preupgrade_script" sed -i "s/\$USER/$TEST_USER/g" "$preupgrade_script"
sed -i "s/\$DOMAIN/$DOMAIN/g" "$preupgrade_script" sed -i "s/\$DOMAIN/$DOMAIN/g" "$preupgrade_script"
@ -123,14 +114,13 @@ _PREUPGRADE () {
} }
_TEST_CONFIG_PANEL() { _TEST_CONFIG_PANEL() {
if [[ -e "$package_path/config_panel.toml" ]] if [[ -e "$package_path/config_panel.toml" ]]; then
then
# Call app config get, but with no output, we just want to check that no error is raised # Call app config get, but with no output, we just want to check that no error is raised
_RUN_YUNOHOST_CMD "app config get $app_id" _RUN_YUNOHOST_CMD "app config get $app_id"
fi fi
} }
_INSTALL_APP () { _INSTALL_APP() {
local install_args="$(jq -r '.install_args' $current_test_infos)" local install_args="$(jq -r '.install_args' $current_test_infos)"
# Make sure we have a trailing & because that assumption is used in some sed regex later # Make sure we have a trailing & because that assumption is used in some sed regex later
@ -139,8 +129,7 @@ _INSTALL_APP () {
# We have default values for domain, admin and is_public, but these # We have default values for domain, admin and is_public, but these
# may still be overwritten by the args ($@) # may still be overwritten by the args ($@)
for arg_override in "domain=$SUBDOMAIN" "admin=$TEST_USER" "is_public=1" "init_main_permission=visitors" "$@" for arg_override in "domain=$SUBDOMAIN" "admin=$TEST_USER" "is_public=1" "init_main_permission=visitors" "$@"; do
do
key="$(echo $arg_override | cut -d '=' -f 1)" key="$(echo $arg_override | cut -d '=' -f 1)"
value="$(echo $arg_override | cut -d '=' -f 2-)" value="$(echo $arg_override | cut -d '=' -f 2-)"
@ -154,26 +143,25 @@ _INSTALL_APP () {
# because this also applies to upgrades ... ie older version may have different args and default values # because this also applies to upgrades ... ie older version may have different args and default values
# Fetch and loop over all manifest arg ... NB : we need to keep this as long as there are "upgrade from packaging v1" tests # Fetch and loop over all manifest arg ... NB : we need to keep this as long as there are "upgrade from packaging v1" tests
if [[ -e $package_path/manifest.json ]] if [[ -e $package_path/manifest.json ]]; then
then
local manifest_args="$(jq -r '.arguments.install[].name' $package_path/manifest.json)" local manifest_args="$(jq -r '.arguments.install[].name' $package_path/manifest.json)"
else else
local manifest_args="$(grep -oE '^\s*\[install\.\w+]' $package_path/manifest.toml | tr -d '[]' | awk -F. '{print $2}')" local manifest_args="$(grep -oE '^\s*\[install\.\w+]' $package_path/manifest.toml | tr -d '[]' | awk -F. '{print $2}')"
fi fi
for ARG in $manifest_args for ARG in $manifest_args; do
do
# If the argument is not yet in install args, add its default value # If the argument is not yet in install args, add its default value
if ! echo "$install_args" | grep -q -E "\<$ARG=" if ! echo "$install_args" | grep -q -E "\<$ARG="; then
then
# NB : we need to keep this as long as there are "upgrade from packaging v1" tests # NB : we need to keep this as long as there are "upgrade from packaging v1" tests
if [[ -e $package_path/manifest.json ]] if [[ -e $package_path/manifest.json ]]; then
then
local default_value=$(jq -e -r --arg ARG $ARG '.arguments.install[] | select(.name==$ARG) | .default' $package_path/manifest.json) local default_value=$(jq -e -r --arg ARG $ARG '.arguments.install[] | select(.name==$ARG) | .default' $package_path/manifest.json)
else else
local default_value=$(python3 -c "import toml, sys; t = toml.loads(sys.stdin.read()); d = t['install']['$ARG'].get('default'); assert d is not None, 'Missing default value'; print(d)" < $package_path/manifest.toml) local default_value=$(python3 -c "import toml, sys; t = toml.loads(sys.stdin.read()); d = t['install']['$ARG'].get('default'); assert d is not None, 'Missing default value'; print(d)" <$package_path/manifest.toml)
fi fi
[[ $? -eq 0 ]] || { log_error "Missing install arg $ARG ?"; return 1; } [[ $? -eq 0 ]] || {
log_error "Missing install arg $ARG ?"
return 1
}
[[ ${install_args: -1} == '&' ]] || install_args+="&" [[ ${install_args: -1} == '&' ]] || install_args+="&"
install_args+="$ARG=$default_value" install_args+="$ARG=$default_value"
fi fi
@ -186,8 +174,7 @@ _INSTALL_APP () {
local ret=$? local ret=$?
[ $ret -eq 0 ] && log_debug "Installation successful." || log_error "Installation failed." [ $ret -eq 0 ] && log_debug "Installation successful." || log_error "Installation failed."
if LXC_EXEC "su nobody -s /bin/bash -c \"test -r /var/www/$app_id || test -w /var/www/$app_id || test -x /var/www/$app_id\"" if LXC_EXEC "su nobody -s /bin/bash -c \"test -r /var/www/$app_id || test -w /var/www/$app_id || test -x /var/www/$app_id\""; then
then
log_error "It looks like anybody can read/enter /var/www/$app_id, which ain't super great from a security point of view ... Config files or other files may contain secrets or information that should in most case not be world-readable. You should remove all 'others' permissions with 'chmod o-rwx', and setup appropriate, exclusive permissions to the appropriate owner/group with chmod/chown." log_error "It looks like anybody can read/enter /var/www/$app_id, which ain't super great from a security point of view ... Config files or other files may contain secrets or information that should in most case not be world-readable. You should remove all 'others' permissions with 'chmod o-rwx', and setup appropriate, exclusive permissions to the appropriate owner/group with chmod/chown."
SET_RESULT "failure" install_dir_permissions SET_RESULT "failure" install_dir_permissions
fi fi
@ -195,14 +182,13 @@ _INSTALL_APP () {
return $ret return $ret
} }
_LOAD_SNAPSHOT_OR_INSTALL_APP () { _LOAD_SNAPSHOT_OR_INSTALL_APP() {
local check_path="$1" local check_path="$1"
local _install_type="$(path_to_install_type $check_path)" local _install_type="$(path_to_install_type $check_path)"
local snapname="snap_${_install_type}install" local snapname="snap_${_install_type}install"
if ! LXC_SNAPSHOT_EXISTS $snapname if ! LXC_SNAPSHOT_EXISTS $snapname; then
then
log_warning "Expected to find an existing snapshot $snapname but it doesn't exist yet .. will attempt to create it" log_warning "Expected to find an existing snapshot $snapname but it doesn't exist yet .. will attempt to create it"
LOAD_LXC_SNAPSHOT snap0 \ LOAD_LXC_SNAPSHOT snap0 \
&& _PREINSTALL \ && _PREINSTALL \
@ -215,8 +201,7 @@ _LOAD_SNAPSHOT_OR_INSTALL_APP () {
fi fi
} }
_REMOVE_APP() {
_REMOVE_APP () {
# Remove an application # Remove an application
break_before_continue break_before_continue
@ -231,7 +216,7 @@ _REMOVE_APP () {
return $ret return $ret
} }
_VALIDATE_THAT_APP_CAN_BE_ACCESSED () { _VALIDATE_THAT_APP_CAN_BE_ACCESSED() {
# Not checking this if this ain't relevant for the current app # Not checking this if this ain't relevant for the current app
this_is_a_web_app || return 0 this_is_a_web_app || return 0
@ -250,8 +235,7 @@ _VALIDATE_THAT_APP_CAN_BE_ACCESSED () {
# accessible *without* tweaking main permission... # accessible *without* tweaking main permission...
local has_public_arg=$(LXC_EXEC "cat /etc/ssowat/conf.json" | jq .permissions.\""$app_id_to_check.main"\".public) local has_public_arg=$(LXC_EXEC "cat /etc/ssowat/conf.json" | jq .permissions.\""$app_id_to_check.main"\".public)
if [[ $has_public_arg == "false" ]] if [[ $has_public_arg == "false" ]]; then
then
log_debug "Forcing public access using tools shell" log_debug "Forcing public access using tools shell"
# Force the public access by setting force=True, which is not possible with "yunohost user permission update" # Force the public access by setting force=True, which is not possible with "yunohost user permission update"
_RUN_YUNOHOST_CMD "tools shell -c 'from yunohost.permission import user_permission_update; user_permission_update(\"$app_id_to_check.main\", add=\"visitors\", force=True)'" _RUN_YUNOHOST_CMD "tools shell -c 'from yunohost.permission import user_permission_update; user_permission_update(\"$app_id_to_check.main\", add=\"visitors\", force=True)'"
@ -259,43 +243,38 @@ _VALIDATE_THAT_APP_CAN_BE_ACCESSED () {
log_small_title "Validating that the app $app_id_to_check can/can't be accessed with its URL..." log_small_title "Validating that the app $app_id_to_check can/can't be accessed with its URL..."
if [ -e "$package_path/tests.toml" ] if [ -e "$package_path/tests.toml" ]; then
then
local current_test_serie=$(jq -r '.test_serie' $testfile) local current_test_serie=$(jq -r '.test_serie' $testfile)
python3 -c "import toml, sys; t = toml.loads(sys.stdin.read()); print(toml.dumps(t['$current_test_serie'].get('curl_tests', {})))" < "$package_path/tests.toml" > $TEST_CONTEXT/curl_tests.toml python3 -c "import toml, sys; t = toml.loads(sys.stdin.read()); print(toml.dumps(t['$current_test_serie'].get('curl_tests', {})))" <"$package_path/tests.toml" >$TEST_CONTEXT/curl_tests.toml
# Upgrade from older versions may still be in packaging v1 without a tests.toml # Upgrade from older versions may still be in packaging v1 without a tests.toml
else else
echo "" > $TEST_CONTEXT/curl_tests.toml echo "" >$TEST_CONTEXT/curl_tests.toml
fi fi
DIST="$DIST" \ DIST="$DIST" \
DOMAIN="$DOMAIN" \ DOMAIN="$DOMAIN" \
SUBDOMAIN="$SUBDOMAIN" \ SUBDOMAIN="$SUBDOMAIN" \
USER="$TEST_USER" \ USER="$TEST_USER" \
PASSWORD="SomeSuperStrongPassword" \ PASSWORD="SomeSuperStrongPassword" \
LXC_IP="$LXC_IP" \ LXC_IP="$LXC_IP" \
BASE_URL="https://$domain_to_check$path_to_check" \ BASE_URL="https://$domain_to_check$path_to_check" \
python3 lib/curl_tests.py < $TEST_CONTEXT/curl_tests.toml | tee -a "$full_log" python3 lib/curl_tests.py <$TEST_CONTEXT/curl_tests.toml | tee -a "$full_log"
curl_result=${PIPESTATUS[0]} curl_result=${PIPESTATUS[0]}
# If we had a 50x error, try to display service info and logs to help debugging (but we don't display nginx logs because most of the time the issue ain't in nginx) # If we had a 50x error, try to display service info and logs to help debugging (but we don't display nginx logs because most of the time the issue ain't in nginx)
if [[ $curl_result == 5 ]] if [[ $curl_result == 5 ]]; then
then
LXC_EXEC "systemctl --no-pager --all" | grep "$app_id_to_check.*service" LXC_EXEC "systemctl --no-pager --all" | grep "$app_id_to_check.*service"
for SERVICE in $(LXC_EXEC "systemctl --no-pager -all" | grep -o "$app_id_to_check.*service") for SERVICE in $(LXC_EXEC "systemctl --no-pager -all" | grep -o "$app_id_to_check.*service"); do
do LXC_EXEC "journalctl --no-pager --no-hostname -n 30 -u $SERVICE"
LXC_EXEC "journalctl --no-pager --no-hostname -n 30 -u $SERVICE";
done done
LXC_EXEC "test -d /var/log/$app_id_to_check && ls -ld /var/log/$app_id_to_check && ls -l /var/log/$app_id_to_check && tail -v -n 15 /var/log/$app_id_to_check/*" LXC_EXEC "test -d /var/log/$app_id_to_check && ls -ld /var/log/$app_id_to_check && ls -l /var/log/$app_id_to_check && tail -v -n 15 /var/log/$app_id_to_check/*"
if grep -qi php $package_path/manifest.toml if grep -qi php $package_path/manifest.toml; then
then
LXC_EXEC "tail -v -n 15 /var/log/php* /var/log/nginx/error.log /var/log/nginx/$domain_to_check-error.log" LXC_EXEC "tail -v -n 15 /var/log/php* /var/log/nginx/error.log /var/log/nginx/$domain_to_check-error.log"
fi fi
fi fi
# Display nginx logs only if for non-50x errors (sor for example 404) to avoid poluting the debug log # Display nginx logs only if for non-50x errors (sor for example 404) to avoid poluting the debug log
if [[ $curl_result != 5 ]] && [[ $curl_result != 0 ]] if [[ $curl_result != 5 ]] && [[ $curl_result != 0 ]]; then
then
LXC_EXEC "tail -v -n 15 /var/log/nginx/*$domain_to_check*" LXC_EXEC "tail -v -n 15 /var/log/nginx/*$domain_to_check*"
fi fi
@ -303,38 +282,50 @@ _VALIDATE_THAT_APP_CAN_BE_ACCESSED () {
return $curl_result return $curl_result
} }
#================================================= #=================================================
# The # The
# Actual # Actual
# Tests # Tests
#================================================= #=================================================
TEST_PACKAGE_LINTER () { TEST_PACKAGE_LINTER() {
start_test "Package linter" start_test "Package linter"
# Execute package linter and linter_result gets the return code of the package linter # Execute package linter and linter_result gets the return code of the package linter
./package_linter/package_linter.py "$package_path" --json | tee -a "$full_log" > $current_test_results ./package_linter/package_linter.py "$package_path" --json | tee -a "$full_log" >$current_test_results
return ${PIPESTATUS[0]} return ${PIPESTATUS[0]}
} }
TEST_INSTALL () { TEST_INSTALL() {
local install_type=$1 local install_type=$1
# This is a separate case ... at least from an hystorical point of view ... # This is a separate case ... at least from an hystorical point of view ...
# but it helpers for semantic that the test is a "TEST_INSTALL" ... # but it helpers for semantic that the test is a "TEST_INSTALL" ...
[ "$install_type" = "multi" ] && { _TEST_MULTI_INSTANCE; return $?; } [ "$install_type" = "multi" ] && {
_TEST_MULTI_INSTANCE
return $?
}
local check_path="/" local check_path="/"
local is_public="1" local is_public="1"
local init_main_permission="visitors" local init_main_permission="visitors"
[ "$install_type" = "subdir" ] && { start_test "Installation in a sub path"; local check_path=/path; } [ "$install_type" = "subdir" ] && {
[ "$install_type" = "root" ] && { start_test "Installation on the root"; } start_test "Installation in a sub path"
[ "$install_type" = "nourl" ] && { start_test "Installation without URL access"; local check_path=""; } local check_path=/path
[ "$install_type" = "private" ] && { start_test "Installation in private mode"; local is_public="0"; local init_main_permission="all_users"; } }
[ "$install_type" = "root" ] && { start_test "Installation on the root"; }
[ "$install_type" = "nourl" ] && {
start_test "Installation without URL access"
local check_path=""
}
[ "$install_type" = "private" ] && {
start_test "Installation in private mode"
local is_public="0"
local init_main_permission="all_users"
}
local snapname=snap_${install_type}install local snapname=snap_${install_type}install
LOAD_LXC_SNAPSHOT snap0 LOAD_LXC_SNAPSHOT snap0
@ -363,13 +354,13 @@ TEST_INSTALL () {
# Remove and reinstall the application # Remove and reinstall the application
_REMOVE_APP \ _REMOVE_APP \
&& log_small_title "Reinstalling after removal." \ && log_small_title "Reinstalling after removal." \
&& _INSTALL_APP "path=$check_path" "is_public=$is_public" "init_main_permission=$init_main_permission" \ && _INSTALL_APP "path=$check_path" "is_public=$is_public" "init_main_permission=$init_main_permission" \
&& _VALIDATE_THAT_APP_CAN_BE_ACCESSED "$SUBDOMAIN" "$check_path" "$install_type" && _VALIDATE_THAT_APP_CAN_BE_ACCESSED "$SUBDOMAIN" "$check_path" "$install_type"
return $? return $?
} }
_TEST_MULTI_INSTANCE () { _TEST_MULTI_INSTANCE() {
start_test "Multi-instance installations" start_test "Multi-instance installations"
@ -392,12 +383,11 @@ _TEST_MULTI_INSTANCE () {
return $? return $?
} }
TEST_UPGRADE () { TEST_UPGRADE() {
local commit=$1 local commit=$1
if [ "$commit" == "" ] if [ "$commit" == "" ]; then
then
start_test "Upgrade from the same version" start_test "Upgrade from the same version"
else else
upgrade_name="$(jq -r '.extra.upgrade_name' $current_test_infos)" upgrade_name="$(jq -r '.extra.upgrade_name' $current_test_infos)"
@ -411,8 +401,7 @@ TEST_UPGRADE () {
# Install the application in a LXC container # Install the application in a LXC container
log_small_title "Preliminary install..." log_small_title "Preliminary install..."
if [ "$commit" == "" ] if [ "$commit" == "" ]; then
then
# If no commit is specified, use the current version. # If no commit is specified, use the current version.
_LOAD_SNAPSHOT_OR_INSTALL_APP "$check_path" _LOAD_SNAPSHOT_OR_INSTALL_APP "$check_path"
local ret=$? local ret=$?
@ -421,7 +410,10 @@ TEST_UPGRADE () {
# and Change to the specified commit # and Change to the specified commit
cp -a "$package_path" "${package_path}_back" cp -a "$package_path" "${package_path}_back"
pushd "$package_path" pushd "$package_path"
git checkout --force --quiet "$commit" || { log_error "Failed to checkout commit $commit ?"; return 1; } git checkout --force --quiet "$commit" || {
log_error "Failed to checkout commit $commit ?"
return 1
}
popd popd
LOAD_LXC_SNAPSHOT snap0 LOAD_LXC_SNAPSHOT snap0
@ -444,13 +436,19 @@ TEST_UPGRADE () {
fi fi
# Check if the install worked # Check if the install worked
[ $ret -eq 0 ] || { log_error "Initial install failed... upgrade test ignore"; return 1; } [ $ret -eq 0 ] || {
log_error "Initial install failed... upgrade test ignore"
return 1
}
log_small_title "Upgrade..." log_small_title "Upgrade..."
_PREUPGRADE "${commit}" _PREUPGRADE "${commit}"
ret=$? ret=$?
[ $ret -eq 0 ] || { log_error "Pre-upgrade instruction failed"; return 1; } [ $ret -eq 0 ] || {
log_error "Pre-upgrade instruction failed"
return 1
}
metrics_start metrics_start
@ -466,7 +464,7 @@ TEST_UPGRADE () {
return $ret return $ret
} }
TEST_PORT_ALREADY_USED () { TEST_PORT_ALREADY_USED() {
start_test "Port already used" start_test "Port already used"
@ -480,7 +478,7 @@ TEST_PORT_ALREADY_USED () {
# Build a service with netcat for use this port before the app. # Build a service with netcat for use this port before the app.
echo -e "[Service]\nExecStart=/bin/netcat -l -k -p $check_port\n echo -e "[Service]\nExecStart=/bin/netcat -l -k -p $check_port\n
[Install]\nWantedBy=multi-user.target" > $TEST_CONTEXT/netcat.service [Install]\nWantedBy=multi-user.target" >$TEST_CONTEXT/netcat.service
$lxc file push $TEST_CONTEXT/netcat.service $LXC_NAME/etc/systemd/system/netcat.service $lxc file push $TEST_CONTEXT/netcat.service $LXC_NAME/etc/systemd/system/netcat.service
@ -490,13 +488,13 @@ TEST_PORT_ALREADY_USED () {
_PREINSTALL _PREINSTALL
# Install the application in a LXC container # Install the application in a LXC container
_INSTALL_APP "path=$check_path" "port=$check_port" \ _INSTALL_APP "path=$check_path" "port=$check_port" \
&& _VALIDATE_THAT_APP_CAN_BE_ACCESSED $SUBDOMAIN "$check_path" && _VALIDATE_THAT_APP_CAN_BE_ACCESSED $SUBDOMAIN "$check_path"
return $? return $?
} }
TEST_BACKUP_RESTORE () { TEST_BACKUP_RESTORE() {
# Try to backup then restore the app # Try to backup then restore the app
@ -516,8 +514,7 @@ TEST_BACKUP_RESTORE () {
local main_result=0 local main_result=0
for check_path in "${check_paths[@]}" for check_path in "${check_paths[@]}"; do
do
# Install the application in a LXC container # Install the application in a LXC container
_LOAD_SNAPSHOT_OR_INSTALL_APP "$check_path" _LOAD_SNAPSHOT_OR_INSTALL_APP "$check_path"
@ -529,8 +526,7 @@ TEST_BACKUP_RESTORE () {
# BACKUP # BACKUP
# Made a backup if the installation succeed # Made a backup if the installation succeed
if [ $ret -ne 0 ] if [ $ret -ne 0 ]; then
then
log_error "Installation failed..." log_error "Installation failed..."
main_result=1 main_result=1
break_before_continue break_before_continue
@ -543,7 +539,11 @@ TEST_BACKUP_RESTORE () {
ret=$? ret=$?
fi fi
[ $ret -eq 0 ] || { main_result=1; break_before_continue; continue; } [ $ret -eq 0 ] || {
main_result=1
break_before_continue
continue
}
# Grab the backup archive into the LXC container, and keep a copy # Grab the backup archive into the LXC container, and keep a copy
$lxc file pull -r $LXC_NAME/home/yunohost.backup/archives $TEST_CONTEXT/ynh_backups $lxc file pull -r $LXC_NAME/home/yunohost.backup/archives $TEST_CONTEXT/ynh_backups
@ -551,19 +551,16 @@ TEST_BACKUP_RESTORE () {
# RESTORE # RESTORE
# Try the restore process in 2 times, first after removing the app, second after a restore of the container. # Try the restore process in 2 times, first after removing the app, second after a restore of the container.
local j=0 local j=0
for j in 0 1 for j in 0 1; do
do
# First, simply remove the application # First, simply remove the application
if [ $j -eq 0 ] if [ $j -eq 0 ]; then
then
# Remove the application # Remove the application
_REMOVE_APP _REMOVE_APP
log_small_title "Restore after removing the application..." log_small_title "Restore after removing the application..."
# Second, restore the whole container to remove completely the application # Second, restore the whole container to remove completely the application
elif [ $j -eq 1 ] elif [ $j -eq 1 ]; then
then
LOAD_LXC_SNAPSHOT snap0 LOAD_LXC_SNAPSHOT snap0
@ -595,7 +592,7 @@ TEST_BACKUP_RESTORE () {
return $main_result return $main_result
} }
TEST_CHANGE_URL () { TEST_CHANGE_URL() {
# Try the change_url script # Try the change_url script
start_test "Change URL" start_test "Change URL"
@ -617,8 +614,7 @@ TEST_CHANGE_URL () {
# Without modify the domain, root to path, path to path and path to root. # Without modify the domain, root to path, path to path and path to root.
# And then, same with a domain change # And then, same with a domain change
local i=0 local i=0
for i in $(seq 1 8) for i in $(seq 1 8); do
do
# Same domain, root to path # Same domain, root to path
if [ $i -eq 1 ]; then if [ $i -eq 1 ]; then
local new_path=/path local new_path=/path
@ -664,7 +660,7 @@ TEST_CHANGE_URL () {
continue continue
elif ! there_is_a_root_install_test && [ "$new_path" == "/" ]; then elif ! there_is_a_root_install_test && [ "$new_path" == "/" ]; then
continue continue
elif ! there_is_a_subdir_install_test && [ "$new_path" != "/" ]; then elif ! there_is_a_subdir_install_test && [ "$new_path" != "/" ]; then
continue continue
fi fi

View file

@ -37,44 +37,42 @@ run_all_tests() {
# Break after the first tests serie # Break after the first tests serie
if [ $interactive -eq 1 ]; then if [ $interactive -eq 1 ]; then
read -p "Press a key to start the tests..." < /dev/tty read -p "Press a key to start the tests..." </dev/tty
fi fi
if [ $dry_run -eq 1 ]; then if [ $dry_run -eq 1 ]; then
for FILE in $(ls $TEST_CONTEXT/tests/*.json) for FILE in $(ls $TEST_CONTEXT/tests/*.json); do
do
cat $FILE | jq cat $FILE | jq
done done
exit exit
fi fi
# Launch all tests successively # Launch all tests successively
cat $TEST_CONTEXT/tests/*.json >> /proc/self/fd/3 cat $TEST_CONTEXT/tests/*.json >>/proc/self/fd/3
# Reset and create a fresh container to work with # Reset and create a fresh container to work with
check_lxc_setup check_lxc_setup
LXC_RESET LXC_RESET
LXC_CREATE LXC_CREATE
LXC_EXEC "yunohost --version --output-as json" | jq -r .yunohost.version >> $TEST_CONTEXT/ynh_version LXC_EXEC "yunohost --version --output-as json" | jq -r .yunohost.version >>$TEST_CONTEXT/ynh_version
LXC_EXEC "yunohost --version --output-as json" | jq -r .yunohost.repo >> $TEST_CONTEXT/ynh_branch LXC_EXEC "yunohost --version --output-as json" | jq -r .yunohost.repo >>$TEST_CONTEXT/ynh_branch
echo $ARCH > $TEST_CONTEXT/architecture echo $ARCH >$TEST_CONTEXT/architecture
echo $app_id > $TEST_CONTEXT/app_id echo $app_id >$TEST_CONTEXT/app_id
# Init the value for the current test # Init the value for the current test
current_test_number=1 current_test_number=1
# The list of test contains for example "TEST_UPGRADE some_commit_id # The list of test contains for example "TEST_UPGRADE some_commit_id
for testfile in "$TEST_CONTEXT"/tests/*.json; for testfile in "$TEST_CONTEXT"/tests/*.json; do
do
TEST_LAUNCHER $testfile TEST_LAUNCHER $testfile
current_test_number=$((current_test_number+1)) current_test_number=$((current_test_number + 1))
done done
# Print the final results of the tests # Print the final results of the tests
log_title "Tests summary" log_title "Tests summary"
python3 lib/analyze_test_results.py $TEST_CONTEXT 2> $result_json python3 lib/analyze_test_results.py $TEST_CONTEXT 2>$result_json
[[ -e "$TEST_CONTEXT/summary.png" ]] && cp "$TEST_CONTEXT/summary.png" $summary_png || rm -f $summary_png [[ -e "$TEST_CONTEXT/summary.png" ]] && cp "$TEST_CONTEXT/summary.png" $summary_png || rm -f $summary_png
# Restore the started time for the timer # Restore the started time for the timer
@ -82,14 +80,13 @@ run_all_tests() {
# End the timer for the test # End the timer for the test
stop_timer all_tests stop_timer all_tests
if [[ "$IN_YUNORUNNER" != "1" ]] if [[ "$IN_YUNORUNNER" != "1" ]]; then
then
echo "You can find the complete log of these tests in $(realpath $full_log)" echo "You can find the complete log of these tests in $(realpath $full_log)"
fi fi
} }
TEST_LAUNCHER () { TEST_LAUNCHER() {
local testfile="$1" local testfile="$1"
# Start the timer for this test # Start the timer for this test
@ -101,8 +98,8 @@ TEST_LAUNCHER () {
current_test_infos="$TEST_CONTEXT/tests/$current_test_id.json" current_test_infos="$TEST_CONTEXT/tests/$current_test_id.json"
current_test_results="$TEST_CONTEXT/results/$current_test_id.json" current_test_results="$TEST_CONTEXT/results/$current_test_id.json"
current_test_log="$TEST_CONTEXT/logs/$current_test_id.log" current_test_log="$TEST_CONTEXT/logs/$current_test_id.log"
echo "{}" > $current_test_results echo "{}" >$current_test_results
echo "" > $current_test_log echo "" >$current_test_log
local test_type=$(jq -r '.test_type' $testfile) local test_type=$(jq -r '.test_type' $testfile)
local test_arg=$(jq -r '.test_arg' $testfile) local test_arg=$(jq -r '.test_arg' $testfile)
@ -119,15 +116,12 @@ TEST_LAUNCHER () {
# Check that we don't have this message characteristic of a file that got manually modified, # Check that we don't have this message characteristic of a file that got manually modified,
# which should not happen during tests because no human modified the file ... # which should not happen during tests because no human modified the file ...
if grep -q --extended-regexp 'has been manually modified since the installation or last upgrade. So it has been duplicated' $current_test_log if grep -q --extended-regexp 'has been manually modified since the installation or last upgrade. So it has been duplicated' $current_test_log; then
then
log_error "Apparently the log is telling that 'some file got manually modified' ... which should not happen, considering that no human modified the file ... ! This is usually symptomatic of something that modified a conf file after installing it with ynh_add_config. Maybe usigin ynh_store_file_checksum can help, or maybe the issue is more subtle!" log_error "Apparently the log is telling that 'some file got manually modified' ... which should not happen, considering that no human modified the file ... ! This is usually symptomatic of something that modified a conf file after installing it with ynh_add_config. Maybe usigin ynh_store_file_checksum can help, or maybe the issue is more subtle!"
if [[ "$test_type" == "TEST_UPGRADE" ]] && [[ "$test_arg" == "" ]] if [[ "$test_type" == "TEST_UPGRADE" ]] && [[ "$test_arg" == "" ]]; then
then
SET_RESULT "failure" file_manually_modified SET_RESULT "failure" file_manually_modified
fi fi
if [[ "$test_type" == "TEST_BACKUP_RESTORE" ]] if [[ "$test_type" == "TEST_BACKUP_RESTORE" ]]; then
then
SET_RESULT "failure" file_manually_modified SET_RESULT "failure" file_manually_modified
fi fi
fi fi
@ -135,10 +129,8 @@ TEST_LAUNCHER () {
# Check that the number of warning ain't higher than a treshold # Check that the number of warning ain't higher than a treshold
local n_warnings=$(grep --extended-regexp '^[0-9]+\s+.{1,15}WARNING' $current_test_log | wc -l) local n_warnings=$(grep --extended-regexp '^[0-9]+\s+.{1,15}WARNING' $current_test_log | wc -l)
# (we ignore this test for upgrade from older commits to avoid having to patch older commits for this) # (we ignore this test for upgrade from older commits to avoid having to patch older commits for this)
if [ "$n_warnings" -gt 30 ] && [ "$test_type" != "TEST_UPGRADE" -o "$test_arg" == "" ] if [ "$n_warnings" -gt 30 ] && [ "$test_type" != "TEST_UPGRADE" -o "$test_arg" == "" ]; then
then if [ "$n_warnings" -gt 100 ]; then
if [ "$n_warnings" -gt 100 ]
then
log_error "There's A SHITLOAD of warnings in the output ! If those warnings are coming from some app build step and ain't actual warnings, please redirect them to the standard output instead of the error output ...!" log_error "There's A SHITLOAD of warnings in the output ! If those warnings are coming from some app build step and ain't actual warnings, please redirect them to the standard output instead of the error output ...!"
log_report_test_failed log_report_test_failed
SET_RESULT "failure" too_many_warnings SET_RESULT "failure" too_many_warnings
@ -147,7 +139,7 @@ TEST_LAUNCHER () {
fi fi
fi fi
local test_duration=$(echo $(( $(date +%s) - $global_start_timer ))) local test_duration=$(echo $(($(date +%s) - $global_start_timer)))
SET_RESULT "$test_duration" test_duration SET_RESULT "$test_duration" test_duration
break_before_continue break_before_continue
@ -161,47 +153,44 @@ TEST_LAUNCHER () {
# Update the lock file with the date of the last finished test. # Update the lock file with the date of the last finished test.
# $$ is the PID of package_check itself. # $$ is the PID of package_check itself.
echo "$1 $2:$(date +%s):$$" > "$lock_file" echo "$1 $2:$(date +%s):$$" >"$lock_file"
} }
SET_RESULT() { SET_RESULT() {
local result=$1 local result=$1
local name=$2 local name=$2
if [ "$name" != "test_duration" ] if [ "$name" != "test_duration" ]; then
then
[ "$result" == "success" ] && log_report_test_success || log_report_test_failed [ "$result" == "success" ] && log_report_test_success || log_report_test_failed
fi fi
local current_results="$(cat $current_test_results)" local current_results="$(cat $current_test_results)"
echo "$current_results" | jq --arg result $result ".$name=\$result" > $current_test_results echo "$current_results" | jq --arg result $result ".$name=\$result" >$current_test_results
} }
#================================================= #=================================================
at_least_one_install_succeeded () { at_least_one_install_succeeded() {
for TEST in "$TEST_CONTEXT"/tests/*.json for TEST in "$TEST_CONTEXT"/tests/*.json; do
do
local test_id=$(basename $TEST | cut -d. -f1) local test_id=$(basename $TEST | cut -d. -f1)
jq -e '. | select(.test_type == "TEST_INSTALL")' $TEST >/dev/null \ jq -e '. | select(.test_type == "TEST_INSTALL")' $TEST >/dev/null \
&& jq -e '. | select(.main_result == "success")' $TEST_CONTEXT/results/$test_id.json >/dev/null \ && jq -e '. | select(.main_result == "success")' $TEST_CONTEXT/results/$test_id.json >/dev/null \
&& return 0 && return 0
done done
log_error "All installs failed, therefore the following tests cannot be performed..." log_error "All installs failed, therefore the following tests cannot be performed..."
return 1 return 1
} }
break_before_continue () { break_before_continue() {
if [ $interactive -eq 1 ] || [ $interactive_on_errors -eq 1 ] && [ ! $test_result -eq 0 ] if [ $interactive -eq 1 ] || [ $interactive_on_errors -eq 1 ] && [ ! $test_result -eq 0 ]; then
then
echo "To enter a shell on the lxc:" echo "To enter a shell on the lxc:"
echo " $lxc exec $LXC_NAME bash" echo " $lxc exec $LXC_NAME bash"
read -p "Press a key to delete the application and continue...." < /dev/tty read -p "Press a key to delete the application and continue...." </dev/tty
fi fi
} }
start_test () { start_test() {
local current_test_serie=$(jq -r '.test_serie' $testfile) local current_test_serie=$(jq -r '.test_serie' $testfile)
[[ "$current_test_serie" != "default" ]] \ [[ "$current_test_serie" != "default" ]] \
@ -216,10 +205,9 @@ start_test () {
there_is_an_install_type() { there_is_an_install_type() {
local install_type=$1 local install_type=$1
for TEST in $TEST_CONTEXT/tests/*.json for TEST in $TEST_CONTEXT/tests/*.json; do
do jq --arg install_type "$install_type" -e '. | select(.test_type == "TEST_INSTALL") | select(.test_arg == $install_type)' $TEST >/dev/null \
jq --arg install_type "$install_type" -e '. | select(.test_type == "TEST_INSTALL") | select(.test_arg == $install_type)' $TEST > /dev/null \ && return 0
&& return 0
done done
return 1 return 1
@ -233,30 +221,42 @@ there_is_a_subdir_install_test() {
return $(there_is_an_install_type "subdir") return $(there_is_an_install_type "subdir")
} }
this_is_a_web_app () { this_is_a_web_app() {
# An app is considered to be a webapp if there is a root or a subdir test # An app is considered to be a webapp if there is a root or a subdir test
return $(there_is_a_root_install_test) || $(there_is_a_subdir_install_test) return $(there_is_a_root_install_test) || $(there_is_a_subdir_install_test)
} }
root_path () { root_path() {
echo "/" echo "/"
} }
subdir_path () { subdir_path() {
echo "/path" echo "/path"
} }
default_install_path() { default_install_path() {
# All webapps should be installable at the root or in a subpath of a domain # All webapps should be installable at the root or in a subpath of a domain
there_is_a_root_install_test && { root_path; return; } there_is_a_root_install_test && {
there_is_a_subdir_install_test && { subdir_path; return; } root_path
return
}
there_is_a_subdir_install_test && {
subdir_path
return
}
echo "" echo ""
} }
path_to_install_type() { path_to_install_type() {
local check_path="$1" local check_path="$1"
[ -z "$check_path" ] && { echo "nourl"; return; } [ -z "$check_path" ] && {
[ "$check_path" == "/" ] && { echo "root"; return; } echo "nourl"
return
}
[ "$check_path" == "/" ] && {
echo "root"
return
}
echo "subdir" echo "subdir"
} }