Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/verify-split-files.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ on:
- "tests/mongodb-upgrade.sh"
- "tests/uv-required-version.sh"
- "tests/docker-lxcfs.sh"
- "tests/install-script-fetch.sh"
- "tests/java-memory.sh"
- "tests/sudo-env.sh"
- "tests/deb822-local-keyring.sh"
Expand Down Expand Up @@ -90,6 +91,7 @@ on:
- "tests/mongodb-upgrade.sh"
- "tests/uv-required-version.sh"
- "tests/docker-lxcfs.sh"
- "tests/install-script-fetch.sh"
- "tests/java-memory.sh"
- "tests/sudo-env.sh"
- "tests/deb822-local-keyring.sh"
Expand Down Expand Up @@ -173,6 +175,9 @@ jobs:
- name: Docker lxcfs visibility regression tests
run: bash tests/docker-lxcfs.sh

- name: Install script fetch tests
run: bash tests/install-script-fetch.sh

- name: Java memory drop-in tests
run: bash tests/java-memory.sh

Expand Down
53 changes: 34 additions & 19 deletions pve/backend.func
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,38 @@ _pve_ct_gone_during_setup() {
_CS_SETUP_GONE_RC=199
}

# ------------------------------------------------------------------------------
# _pve_fetch_install_script
#
# Before anything is created: a missing or unreachable install script turned up
# only once the container stood ready, with a fork hint whatever the cause.
# Leaves the script in _install_script, or says why not and returns 115.
# ------------------------------------------------------------------------------
_pve_fetch_install_script() {
local rel="install/${var_install}.sh" base="${COMMUNITY_SCRIPTS_ROOT:-${COMMUNITY_SCRIPTS_URL}}" out err rc=0 why
out="$(mktemp)" err="$(mktemp)"
_cs_shield_hangup _cs_fetch_text "$rel" >"$out" 2>"$err" || rc=$?
_install_script="$(cat "$out")"
why="$(grep -v '^[[:space:]]*$' "$err" | tail -n 1)" || why=""
rm -f "$out" "$err"
((rc == 0)) && [[ -n "$_install_script" ]] && return 0
_install_script=""
if [[ "$why" == *404* ]]; then
msg_error "${rel} is not in ${base}"
msg_custom "💡" "${YW}" "If ${APP} was retired or became an addon, its ct/ script only updates existing containers:"
msg_custom " " "${YW}" "https://community-scripts.org/scripts/${NSAPP}"
if [[ -z "${COMMUNITY_SCRIPTS_ROOT:-}" ]]; then
msg_custom "💡" "${YW}" "Running a fork? A ct/ script fetched with curl cannot tell the engine"
msg_custom " " "${YW}" "where it came from, so set the scripts base explicitly:"
msg_custom " " "${YW}" "COMMUNITY_SCRIPTS_URL=https://raw.githubusercontent.com/<you>/<repo>/<branch>"
fi
else
msg_error "Could not fetch ${rel} from ${base}"
[[ -n "$why" ]] && msg_custom "ℹ️" "${YW}" "$why"
fi
return 115
}

# ------------------------------------------------------------------------------
# _pve_wait_for_detached_install
#
Expand Down Expand Up @@ -759,6 +791,8 @@ $PCT_OPTIONS_STRING"
check_storage_health "$CONTAINER_STORAGE" "$DISK_SIZE"
fi

local _install_script=""
_pve_fetch_install_script || exit $?
create_lxc_container || exit $?
# Transition to 'configuring' — container created, now setting up OS/userland
post_progress_to_api "configuring"
Expand Down Expand Up @@ -1504,25 +1538,6 @@ PROFILE

export CONTAINER_INSTALLING=true

local _install_script
# Fetched explicitly rather than letting the ERR trap report it.
#
# A missing install script is almost always one thing: the ct/ script came
# from a fork but COMMUNITY_SCRIPTS_URL was not set, so the engine looked
# here -- upstream -- and found nothing. The trap reported that as
# "exit code 22 (Command failed with exit status 22)", which says neither
# what was missing nor where it was looked for.
if ! _install_script="$(_cs_fetch_text "install/${var_install}.sh")" ||
[[ -z "$_install_script" ]]; then
msg_error "Could not fetch install/${var_install}.sh"
msg_custom "📁" "${YW}" "Looked in: ${COMMUNITY_SCRIPTS_ROOT:-${COMMUNITY_SCRIPTS_URL}}"
if [[ -z "${COMMUNITY_SCRIPTS_ROOT:-}" ]]; then
msg_custom "💡" "${YW}" "Running a fork? A ct/ script fetched with curl cannot tell the engine"
msg_custom " " "${YW}" "where it came from, so set the scripts base explicitly:"
msg_custom " " "${YW}" "COMMUNITY_SCRIPTS_URL=https://raw.githubusercontent.com/<you>/<repo>/<branch>"
fi
exit 115
fi
# The install owns the terminal now; a block left open on the host would resume its spinner over it.
stop_spinner
_MSG_BLOCKS=()
Expand Down
81 changes: 81 additions & 0 deletions tests/install-script-fetch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# 26 runs a week failed on install/<app>-install.sh only after their container
# stood ready, all with a fork hint: retired scripts (dockge, dokploy), forks,
# and network errors whose reason never reached the log.
set -uo pipefail

CORE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
BACKEND="$CORE_DIR/pve/backend.func"
eval "$(awk '/^_cs_shield_hangup[(][)] [{]/,/^}/' "$CORE_DIR/core/core.func")"
eval "$(awk '/^_pve_fetch_install_script[(][)] [{]/,/^}/' "$BACKEND")"

TEST_DIR="$(mktemp -d)"
trap 'rm -rf -- "$TEST_DIR"' EXIT
MSG="$TEST_DIR/msg"
passed=0 failed=0

expect() {
if [[ "$2" == "$3" ]]; then
echo "PASS $1"
passed=$((passed + 1))
else
echo "FAIL $1: expected <$2>, got <$3>" >&2
failed=$((failed + 1))
fi
}

msg_error() { echo "ERROR $1" >>"$MSG"; }
msg_custom() { echo "$3" >>"$MSG"; }
YW="" APP="Dockge" NSAPP="dockge" var_install="dockge-install"
COMMUNITY_SCRIPTS_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main"
unset COMMUNITY_SCRIPTS_ROOT

fetch() {
: >"$MSG"
_install_script="stale"
_pve_fetch_install_script
echo "rc=$?"
}

_cs_fetch_text() { printf '#!/usr/bin/env bash\necho installing\n'; }
expect "a fetched script is kept" "rc=0" "$(fetch)"
fetch >/dev/null
expect "with its content" "echo installing" "$(tail -n1 <<<"$_install_script")"

_cs_fetch_text() { echo "curl: (22) The requested URL returned error: 404" >&2; return 22; }
expect "a missing script stops with 115" "rc=115" "$(fetch)"
fetch >/dev/null
expect "it says where it was not found" "ERROR install/dockge-install.sh is not in https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main" "$(head -n1 "$MSG")"
expect "it names a retired script as one cause" 1 "$(grep -c 'retired or became an addon' "$MSG")"
expect "and a fork as the other" 1 "$(grep -c 'Running a fork' "$MSG")"
expect "nothing is left to run" "" "$_install_script"

_cs_fetch_text() { echo "curl: (6) Could not resolve host: raw.githubusercontent.com" >&2; return 6; }
fetch >/dev/null
expect "a network failure says so" "ERROR Could not fetch install/dockge-install.sh from https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main" "$(head -n1 "$MSG")"
expect "with curl's reason" "curl: (6) Could not resolve host: raw.githubusercontent.com" "$(sed -n 2p "$MSG")"
expect "and no fork hint" 0 "$(grep -c 'fork' "$MSG" || true)"

_cs_fetch_text() { printf '#!/usr/bin/env bash\napt-get ins'; echo "curl: (18) transfer closed with 1024 bytes remaining" >&2; return 18; }
fetch >/dev/null
expect "a cut-off script is never run" "" "$_install_script"

_cs_fetch_text() { return 0; }
expect "an empty answer counts as missing" "rc=115" "$(fetch)"

rc="$(
set -eEo pipefail
trap 'echo ERR' ERR
_cs_fetch_text() { return 7; }
_pve_fetch_install_script >/dev/null || true
echo "rc=done"
)"
expect "no curl output at all trips neither errexit nor the ERR trap" "rc=done" "$rc"

fetch_line=$(grep -n '_pve_fetch_install_script || exit' "$BACKEND" | cut -d: -f1)
create_line=$(grep -n ' create_lxc_container || exit' "$BACKEND" | cut -d: -f1)
expect "the script is fetched before the container is created" yes "$([[ -n "$fetch_line" && -n "$create_line" ]] && ((fetch_line < create_line)) && echo yes || echo no)"
expect "and not fetched a second time before the install" 1 "$(grep -c '_cs_fetch_text "install/${var_install}.sh"' "$BACKEND")"

printf '%s passed, %s failed\n' "$passed" "$failed"
((failed == 0))
Loading