Skip to content

Commit 2111706

Browse files
committed
vmupdate: wait for VM shutdown before leaving update connection
1 parent 582536c commit 2111706

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

vmupdate/qube_connection.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
import shutil
2323
import signal
2424
import tempfile
25+
import asyncio
2526
import concurrent.futures
2627
from os.path import join
2728
from subprocess import CalledProcessError
2829
from typing import List
2930

3031
import qubesadmin
32+
from qubesadmin.events.utils import wait_for_domain_shutdown
3133
from vmupdate.agent.source.args import AgentArgs
3234
from vmupdate.agent.source.log_config import LOGPATH, LOG_FILE
3335
from vmupdate.agent.source.status import StatusInfo, FinalStatus
@@ -94,6 +96,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
9496
if self.qube.is_running() and not self._initially_running:
9597
self.logger.info('Shutdown %s', self.qube.name)
9698
self.qube.shutdown()
99+
asyncio.run(wait_for_domain_shutdown([self.qube]))
97100

98101
self.__connected = False
99102

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# coding=utf-8
2+
#
3+
# The Qubes OS Project, https://www.qubes-os.org
4+
#
5+
# Copyright (C) 2026
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License
9+
# as published by the Free Software Foundation; either version 2
10+
# of the License, or (at your option) any later version.
11+
#
12+
from unittest.mock import Mock, patch
13+
14+
from vmupdate.qube_connection import QubeConnection
15+
16+
17+
@patch("vmupdate.qube_connection.wait_for_domain_shutdown")
18+
@patch("vmupdate.qube_connection.asyncio.run")
19+
def test_wait_for_shutdown_when_vm_started_by_update(_arun, wait_shutdown):
20+
vm = Mock()
21+
vm.name = "hvm1"
22+
vm.is_running.side_effect = [False, True]
23+
status_notifier = Mock()
24+
logger = Mock()
25+
26+
with QubeConnection(
27+
vm, "/tmp/qubes-update", cleanup=False, logger=logger,
28+
show_progress=False, status_notifier=status_notifier):
29+
pass
30+
31+
vm.shutdown.assert_called_once_with()
32+
wait_shutdown.assert_called_once_with([vm])
33+
_arun.assert_called_once()
34+
35+
36+
@patch("vmupdate.qube_connection.wait_for_domain_shutdown")
37+
@patch("vmupdate.qube_connection.asyncio.run")
38+
def test_do_not_shutdown_if_vm_was_already_running(_arun, wait_shutdown):
39+
vm = Mock()
40+
vm.name = "hvm2"
41+
vm.is_running.return_value = True
42+
status_notifier = Mock()
43+
logger = Mock()
44+
45+
with QubeConnection(
46+
vm, "/tmp/qubes-update", cleanup=False, logger=logger,
47+
show_progress=False, status_notifier=status_notifier):
48+
pass
49+
50+
vm.shutdown.assert_not_called()
51+
wait_shutdown.assert_not_called()
52+
_arun.assert_not_called()

0 commit comments

Comments
 (0)