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 @@ -22,6 +22,7 @@ on:
- "vm/**"
- "pve/**"
- "incus/**"
- "tests/**"
push:
branches: [main]
paths:
Expand All @@ -34,6 +35,7 @@ on:
- "vm/**"
- "pve/**"
- "incus/**"
- "tests/**"
workflow_dispatch:

env:
Expand All @@ -54,6 +56,9 @@ jobs:
done < <(find $ENGINE_DIRS -name '*.func' -type f)
exit $fail

- name: ARM64 template routing
run: bash tests/arm64-template-routing.sh

- name: Prefetch lists cover every engine file
run: |
set -euo pipefail
Expand Down
77 changes: 40 additions & 37 deletions pve/backend.func
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ _PVE_BACKEND_FUNC_LOADED=1
# SECTION 8: CONTAINER CREATION & DEPLOYMENT
# ==============================================================================

_use_custom_arm64_template() {
[[ "${1:-}" == "arm64" && "${2:-}" != "debian" ]]
}

_filter_templates_by_arch() {
local arch="${1:-}"
[[ -n "$arch" ]] || return 1
grep -E "_${arch}\\.(tar\\.zst|tar\\.xz|tar\\.gz)([[:space:]]|$)"
}

# ------------------------------------------------------------------------------
# _apply_http_proxy_in_container()
# ------------------------------------------------------------------------------
Expand Down Expand Up @@ -2037,6 +2047,7 @@ create_lxc_container() {
pveam list "$TEMPLATE_STORAGE" 2>/dev/null |
awk -v os="$os_type" -v pat="$tpl_pattern" '$1 ~ ("^"os"|/"os) && $1 ~ pat {print $1}' |
sed 's|.*/||' |
_filter_templates_by_arch "$ARCH" |
sed -E "s/^${os_type}-([0-9]+(\.[0-9]+)?).*/\1/" |
sort -u -V || true
)
Expand All @@ -2048,6 +2059,7 @@ create_lxc_container() {
awk '{print $2}' |
grep -E "^${os_type}-[0-9]" |
{ [[ -n "$tpl_pattern" ]] && grep "$tpl_pattern" || cat; } |
_filter_templates_by_arch "$ARCH" |
sed -E "s/^${os_type}-([0-9]+(\.[0-9]+)?).*/\1/" |
sort -u -V 2>/dev/null || true
)
Expand All @@ -2072,7 +2084,9 @@ create_lxc_container() {
mapfile -t _fb_local < <(
pveam list "$TEMPLATE_STORAGE" 2>/dev/null |
awk -v search="$fallback_search" -v pat="$tpl_pattern" '$1 ~ search && $1 ~ pat {print $1}' |
sed 's|.*/||' | sort -t - -k 2 -V || true
sed 's|.*/||' |
_filter_templates_by_arch "$ARCH" |
sort -t - -k 2 -V || true
)
if [[ ${#_fb_local[@]} -gt 0 ]]; then
fallback_template="${_fb_local[-1]}"
Expand All @@ -2081,6 +2095,7 @@ create_lxc_container() {
_pveam_available |
awk '{print $2}' |
grep -E "^${fallback_search}.*${tpl_pattern}" |
_filter_templates_by_arch "$ARCH" |
sort -t - -k 2 -V 2>/dev/null || true
)
[[ ${#_fb_online[@]} -gt 0 ]] && fallback_template="${_fb_online[-1]}"
Expand Down Expand Up @@ -2147,6 +2162,7 @@ create_lxc_container() {
sed 's|.*/||' |
grep -E "^${os_type}-[0-9]" |
{ [[ -n "$tpl_pattern" ]] && grep "$tpl_pattern" || cat; } |
_filter_templates_by_arch "$ARCH" |
sed -E "s/^${os_type}-([0-9]+(\.[0-9]+)?).*/\1/" |
sort -u -V || true
)
Expand All @@ -2158,6 +2174,7 @@ create_lxc_container() {
awk '{print $2}' |
grep -E "^${os_type}-[0-9]" |
{ [[ -n "$tpl_pattern" ]] && grep "$tpl_pattern" || cat; } |
_filter_templates_by_arch "$ARCH" |
sed -E "s/^${os_type}-([0-9]+(\.[0-9]+)?).*/\1/" |
sort -u -V 2>/dev/null || true
)
Expand Down Expand Up @@ -2277,7 +2294,8 @@ create_lxc_container() {

ARCH="$(dpkg --print-architecture)"

# Maps OS type + version to the release variant name used by ARM64 template sources.
# Maps OS type + version to the release variant name used by custom ARM64
# template sources. Debian uses the official pveam catalog below.
arm64_template_variant() {
case "$1:$2" in
debian:12) echo "bookworm" ;;
Expand All @@ -2294,9 +2312,8 @@ create_lxc_container() {
esac
}

# Downloads an ARM64 LXC rootfs template to $1.
# Debian: fetches latest release from community-scripts/debian-arm64-lxc on GitHub.
# Others: fetches from jenkins.linuxcontainers.org.
# Downloads a custom ARM64 LXC rootfs template to $1. Debian uses the
# official pveam catalog; this fallback is retained for other distributions.
download_arm64_template() {
local dest="$1" url

Expand All @@ -2305,21 +2322,7 @@ create_lxc_container() {
exit 217
}

if [[ "$PCT_OSTYPE" == "debian" ]]; then
url=$(
curl -fsSL "https://api.github.com/repos/community-scripts/debian-arm64-lxc/releases/latest" |
jq -r --arg v "$CUSTOM_TEMPLATE_VARIANT" \
'.assets[].browser_download_url | select(test("debian-" + $v + "-arm64-rootfs\\.tar\\.xz$"))' |
head -n1
)

[[ -n "$url" ]] || {
msg_error "Could not find Debian ${CUSTOM_TEMPLATE_VARIANT} ARM64 template URL."
exit 225
}
else
url="https://jenkins.linuxcontainers.org/job/image-${PCT_OSTYPE}/architecture=arm64,release=${CUSTOM_TEMPLATE_VARIANT},variant=default/lastStableBuild/artifact/rootfs.tar.xz"
fi
url="https://jenkins.linuxcontainers.org/job/image-${PCT_OSTYPE}/architecture=arm64,release=${CUSTOM_TEMPLATE_VARIANT},variant=default/lastStableBuild/artifact/rootfs.tar.xz"

msg_info "Downloading ${PCT_OSTYPE^} ${CUSTOM_TEMPLATE_VARIANT} ARM64 template"
local patched="${dest%/*}/.${dest##*/}.patched"
Expand Down Expand Up @@ -2356,7 +2359,7 @@ create_lxc_container() {

download_template() {
local dest="${1:-$TEMPLATE_PATH}"
if [[ "$ARCH" == "arm64" ]]; then
if [[ "$TEMPLATE_SOURCE" == "custom-arm64" ]]; then
download_arm64_template "$dest"
else
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 || {
Expand Down Expand Up @@ -2531,8 +2534,10 @@ create_lxc_container() {
# ------------------------------------------------------------------------------
CUSTOM_TEMPLATE_VARIANT=""

if [[ "$ARCH" == "arm64" ]]; then
# ARM64: use custom template download from linuxcontainers.org / GitHub
if _use_custom_arm64_template "$ARCH" "$PCT_OSTYPE"; then
# Use a custom ARM64 template only when the Proxmox catalog does not supply
# the distribution. Debian ARM64 is available through pveam and follows the
# same architecture-filtered discovery path as Debian AMD64.
msg_info "Preparing ARM64 template"

CUSTOM_TEMPLATE_VARIANT=$(arm64_template_variant "$PCT_OSTYPE" "${PCT_OSVERSION:-}") || {
Expand Down Expand Up @@ -2583,9 +2588,11 @@ create_lxc_container() {
# Step 1: Check local templates first (instant)
mapfile -t LOCAL_TEMPLATES < <(
pveam list "$TEMPLATE_STORAGE" 2>/dev/null |
awk -v search="${TEMPLATE_SEARCH}" -v pattern="${TEMPLATE_PATTERN}" -v arch="${ARCH}" \
'$1 ~ search && $1 ~ pattern && $1 ~ ("_" arch "\\.(tar\\.zst|tar\\.xz|tar\\.gz)$") {print $1}' |
sed 's|.*/||' | sort -t - -k 2 -V || true
awk -v search="${TEMPLATE_SEARCH}" -v pattern="${TEMPLATE_PATTERN}" \
'$1 ~ search && $1 ~ pattern {print $1}' |
sed 's|.*/||' |
_filter_templates_by_arch "$ARCH" |
sort -t - -k 2 -V || true
)

# Step 2: If local template found, use it immediately (skip pveam update)
Expand Down Expand Up @@ -2630,14 +2637,7 @@ create_lxc_container() {
msg_custom "ℹ️" "${YW}" "Select a different available version or press Enter to cancel."

AVAILABLE_VERSIONS=()
mapfile -t AVAILABLE_VERSIONS < <(
_pveam_available |
grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' |
awk -F'\t' '{print $1}' |
grep "^${PCT_OSTYPE}-" |
sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" |
sort -u -V 2>/dev/null || true
)
mapfile -t AVAILABLE_VERSIONS < <(_list_os_versions "$PCT_OSTYPE")

if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then
echo ""
Expand All @@ -2659,13 +2659,16 @@ create_lxc_container() {
mapfile -t LOCAL_TEMPLATES < <(
pveam list "$TEMPLATE_STORAGE" 2>/dev/null |
awk -v search="${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' |
sed 's|.*/||' | sort -t - -k 2 -V || true
sed 's|.*/||' |
_filter_templates_by_arch "$ARCH" |
sort -t - -k 2 -V || true
)
mapfile -t ONLINE_TEMPLATES < <(
_pveam_available |
grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' |
awk '{print $2}' |
grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" |
_filter_templates_by_arch "$ARCH" |
sort -t - -k 2 -V 2>/dev/null || true
)
ONLINE_TEMPLATE=""
Expand Down Expand Up @@ -2868,7 +2871,7 @@ create_lxc_container() {
download_template
msg_ok "Template downloaded"
elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then
if [[ "$ARCH" == "arm64" || -n "$ONLINE_TEMPLATE" ]]; then
if [[ "$TEMPLATE_SOURCE" == "custom-arm64" || -n "$ONLINE_TEMPLATE" ]]; then
msg_info "Template appears corrupted – re-downloading"
rm -f "$TEMPLATE_PATH"
download_template
Expand Down Expand Up @@ -2954,7 +2957,7 @@ create_lxc_container() {
if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then
msg_ok "Trying local storage fallback"
msg_info "Downloading template to local"
if [[ "$ARCH" == "arm64" ]]; then
if [[ "$TEMPLATE_SOURCE" == "custom-arm64" ]]; then
download_arm64_template "$LOCAL_TEMPLATE_PATH"
else
pveam download local "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1
Expand Down
107 changes: 107 additions & 0 deletions tests/arm64-template-routing.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env bash
set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"

# shellcheck source=../ui/defaults.func
source "$repo_root/ui/defaults.func"

fail() {
printf 'FAIL: %s\n' "$1" >&2
exit 1
}

msg_warn() { :; }
msg_ok() { :; }

MOCK_ARCH="arm64"
dpkg() {
[[ "${1:-}" == "--print-architecture" ]] || return 1
printf '%s\n' "$MOCK_ARCH"
}

_pveam_available() {
cat <<'EOF'
system debian-13-standard_13.6-1_amd64.tar.zst
system debian-13-standard_13.6-1_arm64.tar.zst
system ubuntu-24.04-standard_24.04-1_arm64.tar.zst
EOF
}

pveam() {
if [[ "${1:-}" == "list" ]]; then
printf '%s\n' 'local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst'
return 0
fi
return 1
}

# Without a PVE backend hook, ARM64 version resolution remains unchanged for
# Incus and any other platform-specific template source.
var_os="debian"
var_version="12"
export var_os var_version
resolve_os_version
[[ "$var_version" == "12" ]] || fail "Non-PVE ARM64 version resolution changed"

# shellcheck source=../pve/backend.func
source "$repo_root/pve/backend.func"

if _use_custom_arm64_template arm64 debian; then
fail "Debian ARM64 must use the official pveam catalog"
fi
if ! _use_custom_arm64_template arm64 ubuntu; then
fail "Ubuntu ARM64 must retain the custom-template fallback"
fi
if ! _use_custom_arm64_template arm64 alpine; then
fail "Alpine ARM64 must retain the custom-template fallback"
fi
if _use_custom_arm64_template amd64 debian; then
fail "AMD64 must not use the custom ARM64 path"
fi

mapfile -t templates < <(_list_templates "debian-13" "-standard_")
[[ "${#templates[@]}" -eq 1 ]] || fail "Expected exactly one Debian 13 ARM64 template"
[[ "${templates[0]}" == "debian-13-standard_13.6-1_arm64.tar.zst" ]] ||
fail "Template discovery selected the wrong architecture"

mapfile -t filtered < <(
printf '%s\n' \
'debian-13-standard_13.6-1_amd64.tar.zst' \
'debian-13-standard_13.6-1_arm64.tar.zst' \
'debian-12-standard_12.12-1_amd64.tar.gz' \
'debian-12-standard_12.12-1_arm64.tar.xz' |
_filter_templates_by_arch arm64
)
[[ "${#filtered[@]}" -eq 2 ]] || fail "ARM64 fallback filtering returned the wrong number of templates"
[[ "${filtered[0]}" == *'_arm64.tar.zst' && "${filtered[1]}" == *'_arm64.tar.xz' ]] ||
fail "ARM64 fallback filtering selected another architecture"

# Debian ARM64 now resolves through pveam. An amd64-only local Debian 12
# template must not prevent selecting the available ARM64 Debian 13 template.
export PHS_SILENT=1
export PVEVERSION="test"
export TEMPLATE_STORAGE="local"
var_os="debian"
var_version="12"
resolve_os_version
[[ "$var_version" == "13" ]] || fail "Debian ARM64 did not resolve through the architecture-filtered catalog"

# Non-Debian ARM64 distributions continue to use their custom template source.
var_os="ubuntu"
var_version="22.04"
resolve_os_version
[[ "$var_version" == "22.04" ]] || fail "Ubuntu ARM64 unexpectedly used the pveam catalog"

# AMD64 keeps the existing pveam resolution behavior.
MOCK_ARCH="amd64"
var_os="debian"
var_version="12"
resolve_os_version
[[ "$var_version" == "12" ]] || fail "AMD64 local template resolution changed"

if grep -qF 'community-scripts/debian-arm64-lxc' "$repo_root/pve/backend.func"; then
fail "The removed Debian ARM64 repository reference returned"
fi

printf 'ARM64 template routing checks passed\n'
14 changes: 11 additions & 3 deletions ui/defaults.func
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,15 @@ resolve_os_version() {
local ostype="${var_os:-}" want="${var_version:-}"
[[ -n "$ostype" && -n "$want" ]] || return 0

# ARM64 pulls from linuxcontainers.org, the pveam catalog says nothing there
[[ "$(dpkg --print-architecture 2>/dev/null)" == "arm64" ]] && return 0
local arch
arch="$(dpkg --print-architecture 2>/dev/null || echo amd64)"

# ARM64 normally pulls from a platform-specific source, so the pveam catalog
# says nothing useful there. The PVE backend can opt an OS into its catalog.
if [[ "$arch" == "arm64" ]] &&
{ ! declare -F _use_custom_arm64_template >/dev/null || _use_custom_arm64_template "$arch" "$ostype"; }; then
return 0
fi

local -a versions=()
mapfile -t versions < <(_list_os_versions "$ostype")
Expand All @@ -141,7 +148,8 @@ resolve_os_version() {

# Already downloaded counts as available even if the catalog dropped it
if [[ -n "${TEMPLATE_STORAGE:-}" ]] &&
pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "/${ostype}-${want}-"; then
pveam list "$TEMPLATE_STORAGE" 2>/dev/null |
grep -Eq "/${ostype}-${want}-.*_${arch}\\.(tar\\.zst|tar\\.xz|tar\\.gz)$"; then
return 0
fi

Expand Down
Loading