Skip to content

Commit 38bd7d6

Browse files
committed
Add orphan cleanup prompt for Incus failures
Introduce PVE-style orphan handling for Incus containers that fail during create/configure: on exit, users now get a 60s prompt to delete or keep the incomplete container, with auto-remove as default. Added build state flags (`INCUS_CT_CREATED`, `INCUS_BUILD_OK`, `INCUS_ORPHAN_HANDLED`) to track lifecycle, mark successful installs, and prevent duplicate cleanup prompts when install recovery already handled the failure.
1 parent c7dc0e1 commit 38bd7d6

2 files changed

Lines changed: 68 additions & 1 deletion

File tree

misc/incus-backend.func

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,13 @@ incus_create_lxc_container() {
332332
fi
333333
fi
334334

335+
# Track for orphan cleanup on mid-create / mid-configure failures (EXIT trap).
336+
# Reset handled/ok so a rebuild after install-recovery can prompt again if needed.
337+
INCUS_CT_CREATED=1
338+
INCUS_ORPHAN_HANDLED=0
339+
INCUS_BUILD_OK=0
340+
export INCUS_CT_CREATED INCUS_ORPHAN_HANDLED INCUS_BUILD_OK CT_NAME CTID
341+
335342
# Optional configs that some Incus versions reject at launch time.
336343
if [[ "${CT_TYPE:-1}" == "1" ]]; then
337344
_incus_soft incus config set "${CT_NAME}" linux.sysctl.kernel.keys.maxkeys=2000 >>"$LOGFILE" 2>&1
@@ -958,12 +965,17 @@ incus_run_install_script_with_recovery() {
958965
if [[ "$install_exit_code" -eq 0 ]]; then
959966
msg_ok "Installation completed"
960967
CONTAINER_INSTALLING=false
968+
INCUS_BUILD_OK=1
969+
export INCUS_BUILD_OK
961970
return 0
962971
fi
963972

964973
msg_error "Installation failed in container ${CT_NAME} (exit code: ${install_exit_code})"
965974
_incus_build_combined_log
966975
post_update_to_api "failed" "$install_exit_code" 2>/dev/null || true
976+
# This menu owns cleanup — EXIT trap must not prompt again.
977+
INCUS_ORPHAN_HANDLED=1
978+
export INCUS_ORPHAN_HANDLED
967979

968980
if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then
969981
msg_dev "Keep mode active - container ${CT_NAME} preserved"
@@ -1093,6 +1105,8 @@ incus_run_install_script_with_recovery() {
10931105
msg_ok "Installation completed successfully after package repair!"
10941106
post_update_to_api "done" "0" "force" 2>/dev/null || true
10951107
CONTAINER_INSTALLING=false
1108+
INCUS_BUILD_OK=1
1109+
export INCUS_BUILD_OK
10961110
return 0
10971111
fi
10981112
msg_error "Installation still failed after repair (exit code: ${install_exit_code})"

misc/incus-build.func

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,54 @@ incus_delete_container() {
144144
msg_ok "Deleted ${name}"
145145
}
146146

147+
# PVE-parity: after create-phase / configure failures, offer delete (60s auto-remove).
148+
# Install-script failures already use incus_run_install_script_with_recovery.
149+
incus_offer_orphan_cleanup() {
150+
[[ -n "${_INCUS_INSIDE_CONTAINER:-}" ]] && return 0
151+
[[ "${INCUS_ORPHAN_HANDLED:-}" == "1" ]] && return 0
152+
[[ "${INCUS_CT_CREATED:-}" != "1" ]] && return 0
153+
[[ "${INCUS_BUILD_OK:-}" == "1" ]] && return 0
154+
[[ -z "${CT_NAME:-}" ]] && return 0
155+
command -v incus &>/dev/null || return 0
156+
declare -f incus_container_exists >/dev/null 2>&1 || return 0
157+
incus_container_exists "${CT_NAME}" || return 0
158+
[[ "${DEV_MODE_KEEP:-false}" == "true" ]] && return 0
159+
160+
INCUS_ORPHAN_HANDLED=1
161+
export INCUS_ORPHAN_HANDLED
162+
163+
# Avoid nested ERR noise while prompting / deleting
164+
set +e
165+
trap - ERR
166+
167+
echo ""
168+
msg_warn "Incomplete container ${CT_NAME} left behind (create/configure failed)"
169+
echo -e "${YW}What would you like to do?${CL}"
170+
echo ""
171+
echo -e " ${GN}1)${CL} Remove container and exit"
172+
echo -e " ${GN}2)${CL} Keep container for debugging"
173+
echo ""
174+
echo -en "${YW}Select option [1-2] (default: 1, auto-remove in 60s): ${CL}"
175+
if read -t 60 -r response </dev/tty 2>/dev/null || read -t 60 -r response; then
176+
case "${response:-1}" in
177+
2)
178+
echo -e "\n${TAB}${YW}Container ${CT_NAME} kept for debugging${CL}"
179+
echo -e "${TAB}${INFO} Access: ${GN}incus exec ${CT_NAME} -- bash${CL}"
180+
echo -e "${TAB}${INFO} Remove later: ${GN}incus delete ${CT_NAME} --force${CL}"
181+
;;
182+
*)
183+
echo -e "\n${TAB}${HOLD}${YW}Removing container ${CT_NAME}${CL}"
184+
incus_delete_container "${CT_NAME}"
185+
;;
186+
esac
187+
else
188+
echo ""
189+
msg_info "No response - removing container ${CT_NAME}"
190+
incus_delete_container "${CT_NAME}"
191+
msg_ok "Container ${CT_NAME} removed"
192+
fi
193+
}
194+
147195
# ==============================================================================
148196
# SECTION 3: SHARED UI + BACKEND
149197
# ==============================================================================
@@ -477,6 +525,10 @@ api_exit_script() {
477525
if [[ -z "${_INCUS_INSIDE_CONTAINER:-}" && "${CONTAINER_INSTALLING:-}" == "true" && -n "${CT_NAME:-}" ]]; then
478526
command -v incus &>/dev/null && incus stop "$CT_NAME" --force 2>/dev/null || true
479527
fi
528+
# Create/configure aborts (before install recovery menu): offer delete like PVE 60s prompt
529+
if declare -f incus_offer_orphan_cleanup >/dev/null 2>&1; then
530+
incus_offer_orphan_cleanup
531+
fi
480532
elif [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
481533
post_update_to_api "done" "0" 2>/dev/null || true
482534
fi
@@ -554,7 +606,8 @@ _incus_on_err() {
554606
_incus_on_signal() {
555607
local sig="$1" code="$2"
556608
post_update_to_api "failed" "$code" 2>/dev/null || true
557-
# Only stop instances from the Incus host — never inside a guest update run.
609+
# Only touch instances from the Incus host — never inside a guest update run.
610+
# EXIT trap (api_exit_script) offers 60s orphan cleanup / delete.
558611
if [[ -z "${_INCUS_INSIDE_CONTAINER:-}" ]] && command -v incus &>/dev/null && [[ -n "${CT_NAME:-}" ]]; then
559612
incus stop "$CT_NAME" --force 2>/dev/null || true
560613
fi

0 commit comments

Comments
 (0)