diff --git a/pve/vm-core.func b/pve/vm-core.func index 85381b2..330c7df 100644 --- a/pve/vm-core.func +++ b/pve/vm-core.func @@ -1678,7 +1678,9 @@ vm_image_cache_path() { # A cached image that no longer matches usually means the upstream was rebuilt, # not that anything is wrong. Offer the choice instead of silently re-downloading -# several hundred MB. Unattended or without a terminal, take the new one. +# several hundred MB. Unattended or without a terminal, take the new one. A +# "Keep cached" answer is remembered in .keep with the upstream checksum +# it was given against, so the question returns only when the upstream changes. _vm_cache_choice() { local file="$1" url="$2" local_size local_date remote_size remote_date head @@ -1742,13 +1744,23 @@ vm_fetch_image() { fi if [[ "$cache" == yes && -e "$target" ]]; then + local keep="${target}.keep" if _vm_image_ok "$target" "$exact_bytes" "$min_bytes" "$sha_tool" "$sha_want" "$xz"; then + rm -f "$keep" msg_ok "Using cached ${CL}${BL}$(basename "$target")${CL}" return 0 fi + if [[ -n "$sha_want" && -f "$keep" && "$(<"$keep")" == "${sha_tool}:${sha_want}" ]]; then + msg_ok "Using cached ${CL}${BL}$(basename "$target")${CL} (kept; the upstream has not changed)" + return 0 + fi if _vm_cache_choice "$target" "$url"; then msg_warn "Replacing the cached $(basename "$target")" - rm -f "$target" + rm -f "$target" "$keep" + elif [[ -n "$sha_want" ]]; then + printf '%s' "${sha_tool}:${sha_want}" >"$keep" + msg_ok "Using cached ${CL}${BL}$(basename "$target")${CL} (kept until the upstream changes)" + return 0 else msg_ok "Using cached ${CL}${BL}$(basename "$target")${CL}" return 0 diff --git a/tests/vm-helpers.sh b/tests/vm-helpers.sh index c6fa09f..7205b71 100644 --- a/tests/vm-helpers.sh +++ b/tests/vm-helpers.sh @@ -182,6 +182,23 @@ vm_fetch_image "https://dl.test/openwrt.img.gz" "$TEST_DIR/cache/nosums.img" --c assert_contains "$WARNINGS" "checking the size only" ok "digest case is ignored; missing checksum list warns and continues" +CHOICE_CALLS="$TEST_DIR/choice-calls" +: >"$CHOICE_CALLS" +_vm_cache_choice() { echo keep >>"$CHOICE_CALLS"; return 1; } +KEPT="$TEST_DIR/cache/kept.img" +mkdir -p "$TEST_DIR/cache" && printf 'old build\n' >"$KEPT" +vm_fetch_image "https://dl.test/openwrt.img.gz" "$KEPT" --cache --sha256 "$IMAGE_SHA256" || fail "keep cached" +[[ -f "$KEPT.keep" ]] || fail "a kept cache gets a marker" +vm_fetch_image "https://dl.test/openwrt.img.gz" "$KEPT" --cache --sha256 "$IMAGE_SHA256" || fail "kept cache reused" +assert_eq "$(grep -c keep "$CHOICE_CALLS")" 1 "asked once while the upstream is unchanged" +vm_fetch_image "https://dl.test/openwrt.img.gz" "$KEPT" --cache --sha256 "$(printf '%064d' 1)" || fail "keep again after a change" +assert_eq "$(grep -c keep "$CHOICE_CALLS")" 2 "asked again once the upstream changed" +_vm_cache_choice() { return 0; } +vm_fetch_image "https://dl.test/openwrt.img.gz" "$KEPT" --cache --sha256 "$IMAGE_SHA256" || fail "replace cached" +[[ ! -f "$KEPT.keep" ]] || fail "the marker goes with the replaced file" +assert_eq "$(sha256sum "$KEPT" | cut -d' ' -f1)" "$IMAGE_SHA256" "fresh download in place" +ok "a kept cache is reused until the upstream checksum changes" + # ── vm_import_disk ────────────────────────────────────────────────────────── VMID=100 QM_MODERN=yes