Skip to content

Commit

Permalink
shutdown: do not try to unmount /target if install was not started
Browse files Browse the repository at this point in the history
If we ask for reboot before the installation has started (i.e., if
curtin install was not invoked at least once), the following call fails
and prevents the system from rebooting.

 $ umount --recursive /target

Make sure we check that /target exists and is mounted before calling
umount.

Another approach would be to check the return value of umount but the
values are not documented.

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
  • Loading branch information
ogayot committed Oct 2, 2023
1 parent bd52c48 commit 8f9f826
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 1 deletion.
12 changes: 11 additions & 1 deletion subiquity/server/controllers/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import logging
import os
import pathlib
import subprocess
import time
from typing import Any, Callable, Dict, List, Optional, Union

Expand Down Expand Up @@ -1532,6 +1533,15 @@ def make_autoinstall(self):

async def _pre_shutdown(self):
if not self.reset_partition_only:
await self.app.command_runner.run(["umount", "--recursive", "/target"])
# /target is mounted only if the installation was actually started.
try:
await self.app.command_runner.run(["mountpoint", "/target"])
except subprocess.CalledProcessError as cpe:
log.debug(
"/target does not exist or is not mounted,"
" skipping call to umount --recursive"
)
else:
await self.app.command_runner.run(["umount", "--recursive", "/target"])
if len(self.model._all(type="zpool")) > 0:
await self.app.command_runner.run(["zpool", "export", "-a"])
28 changes: 28 additions & 0 deletions subiquity/server/controllers/tests/test_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import copy
import subprocess
import uuid
from unittest import IsolatedAsyncioTestCase, mock

Expand Down Expand Up @@ -89,6 +90,7 @@ class TestSubiquityControllerFilesystem(IsolatedAsyncioTestCase):
def setUp(self):
self.app = make_app()
self.app.opts.bootloader = "UEFI"
self.app.command_runner = mock.AsyncMock()
self.app.report_start_event = mock.Mock()
self.app.report_finish_event = mock.Mock()
self.app.prober = mock.Mock()
Expand Down Expand Up @@ -343,6 +345,32 @@ async def test_v2_edit_partition_POST(self):
self.assertTrue(self.fsc.locked_probe_data)
handler.assert_called_once()

async def test__pre_shutdown_install_started(self):
self.fsc.reset_partition_only = False
run = mock.patch.object(self.app.command_runner, "run")
_all = mock.patch.object(self.fsc.model, "_all")
with run as mock_run, _all:
await self.fsc._pre_shutdown()
mock_run.assert_has_calls(
[
mock.call(["mountpoint", "/target"]),
mock.call(["umount", "--recursive", "/target"]),
]
)
self.assertEqual(len(mock_run.mock_calls), 2)

async def test__pre_shutdown_install_not_started(self):
async def fake_run(cmd, **kwargs):
if cmd == ["mountpoint", "/target"]:
raise subprocess.CalledProcessError(cmd=cmd, returncode=1)

self.fsc.reset_partition_only = False
run = mock.patch.object(self.app.command_runner, "run", side_effect=fake_run)
_all = mock.patch.object(self.fsc.model, "_all")
with run as mock_run, _all:
await self.fsc._pre_shutdown()
mock_run.assert_called_once_with(["mountpoint", "/target"])


class TestGuided(IsolatedAsyncioTestCase):
boot_expectations = [
Expand Down

0 comments on commit 8f9f826

Please sign in to comment.