diff --git a/incus/backend.func b/incus/backend.func index 17b220a..55868cb 100644 --- a/incus/backend.func +++ b/incus/backend.func @@ -90,7 +90,19 @@ _incus_resolve_launch_image() { candidates+=("images:ubuntu/${ver}.04" "images:ubuntu/${ver}.04/cloud") fi - local cand local_match + local cand local_aliases + + # Local first: no round-trip, and works without internet. + local_aliases=$(incus image list -f csv,noheader -c l 2>/dev/null || true) + for cand in "${candidates[@]}"; do + if [[ -n "$local_aliases" ]] && + grep -qxF "${cand#images:}" <<<"$local_aliases" && + incus image info "${cand#images:}" &>/dev/null; then + INCUS_LAUNCH_IMAGE="${cand#images:}" + return 0 + fi + done + for cand in "${candidates[@]}"; do if incus image info "$cand" &>/dev/null; then INCUS_LAUNCH_IMAGE="$cand" @@ -98,16 +110,6 @@ _incus_resolve_launch_image() { fi done - # Local image fingerprint/alias match (already copied) - local_match=$(incus image list -f csv,noheader 2>/dev/null | - awk -F, -v os="$os" -v ver="$ver" ' - tolower($0) ~ os && $0 ~ ver { print $1; exit } - ' || true) - if [[ -n "$local_match" ]] && incus image info "$local_match" &>/dev/null; then - INCUS_LAUNCH_IMAGE="$local_match" - return 0 - fi - # Last resort: try remote launch of primary (incus may pull on demand) INCUS_LAUNCH_IMAGE="images:${os}/${ver}" return 1 diff --git a/pve/backend.func b/pve/backend.func index ce3718a..2d417fb 100644 --- a/pve/backend.func +++ b/pve/backend.func @@ -2350,25 +2350,49 @@ create_lxc_container() { ARCH="$(dpkg --print-architecture)" # Maps OS type + version to the release variant name used by ARM64 template sources. - arm64_template_variant() { - case "$1:$2" in - debian:12) echo "bookworm" ;; - debian:13) echo "trixie" ;; - debian:) echo "$DEBIAN_DEFAULT_CODENAME" ;; + _lxc_catalog_path() { + local os="${1,,}" ver="$2" arch="$3" + command -v jq >/dev/null 2>&1 || { + $STD apt-get update + $STD apt-get install -y jq || return 1 + } + curl -fsSL --max-time 30 "https://images.linuxcontainers.org/streams/v1/images.json" 2>/dev/null | + jq -r --arg os "$os" --arg ver "$ver" --arg arch "$arch" ' + .products | to_entries[] + | select(.value.arch == $arch and .value.variant == "default") + | select((.value.os | ascii_downcase) == $os) + | select(.value.release == $ver or ((.value.aliases // "") | split(",") | index($os + "/" + $ver))) + | .value.versions | to_entries | sort_by(.key) | last + | .value.items["root.tar.xz"].path // empty + ' 2>/dev/null | head -1 + } - ubuntu:24.04) echo "noble" ;; - ubuntu:26.04) echo "questing" ;; - ubuntu:) echo "$UBUNTU_DEFAULT_CODENAME" ;; + # Downloads an ARM64 LXC rootfs template to $1. + # Does Proxmox offer this OS/version/arch? Local first, then the catalog. + _pveam_offers_template() { + local search pattern + search="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu) pattern="-standard_" ;; + alpine | fedora | rocky | centos) pattern="-default_" ;; + *) pattern="" ;; + esac - alpine:*) echo "${2:-$ALPINE_DEFAULT_VERSION}" ;; + if pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v s="$search" -v p="$pattern" -v a="$ARCH" \ + '$1 ~ s && $1 ~ p && $1 ~ ("_" a "\\.(tar\\.zst|tar\\.xz|tar\\.gz)$") {found=1} + END {exit !found}'; then + return 0 + fi - *) return 1 ;; - esac + _pveam_update >/dev/null 2>&1 || true + _pveam_available | + grep -E "_${ARCH}\.(tar\.zst|tar\.xz|tar\.gz)([[:space:]]|$)" | + awk '{print $2}' | + grep -qE "^${search}.*${pattern}" } - # 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. + # Only reached when pveam has nothing for this arch. download_arm64_template() { local dest="$1" url @@ -2377,21 +2401,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://images.linuxcontainers.org/${CUSTOM_TEMPLATE_PATH}" msg_info "Downloading ${PCT_OSTYPE^} ${CUSTOM_TEMPLATE_VARIANT} ARM64 template" local patched="${dest%/*}/.${dest##*/}.patched" @@ -2602,17 +2612,19 @@ create_lxc_container() { # Template discovery & validation # ------------------------------------------------------------------------------ CUSTOM_TEMPLATE_VARIANT="" + CUSTOM_TEMPLATE_PATH="" - if [[ "$ARCH" == "arm64" ]]; then - # ARM64: use custom template download from linuxcontainers.org / GitHub - msg_info "Preparing ARM64 template" + if [[ "$ARCH" == "arm64" ]] && ! _pveam_offers_template; then + msg_info "No Proxmox template for ${PCT_OSTYPE} on ${ARCH}, using linuxcontainers.org" - CUSTOM_TEMPLATE_VARIANT=$(arm64_template_variant "$PCT_OSTYPE" "${PCT_OSVERSION:-}") || { - msg_error "No ARM64 template mapping for ${PCT_OSTYPE} ${PCT_OSVERSION:-latest}" + CUSTOM_TEMPLATE_PATH=$(_lxc_catalog_path "$PCT_OSTYPE" "${PCT_OSVERSION:-}" "$ARCH") + [[ -n "$CUSTOM_TEMPLATE_PATH" ]] || { + msg_error "linuxcontainers.org has no ${PCT_OSTYPE} ${PCT_OSVERSION:-latest} for ${ARCH}" exit 225 } - TEMPLATE="${PCT_OSTYPE}-${CUSTOM_TEMPLATE_VARIANT}-rootfs.tar.xz" + CUSTOM_TEMPLATE_VARIANT=$(awk -F/ '{print $3}' <<<"$CUSTOM_TEMPLATE_PATH") + TEMPLATE="${PCT_OSTYPE}-${CUSTOM_TEMPLATE_VARIANT}-${ARCH}-rootfs.tar.xz" TEMPLATE_SOURCE="custom-arm64" # Resolve template path @@ -2730,12 +2742,13 @@ 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}' | + 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 ) mapfile -t ONLINE_TEMPLATES < <( _pveam_available | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + grep -E "_${ARCH}\.(tar\.zst|tar\.xz|tar\.gz)([[:space:]]|$)" | awk '{print $2}' | grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true