filesystem: mount snapd/seed/systems only if it exists

The "$source"/var/lib/snapd/seed/systems directory only exists in certain
scenarios related to TPM-backed FDE. When the directory does not exist,
attempting to bind-mount it to /var/lib/snapd/seed/systems crashes the
install with a CalledProcessError.

We now make sure the directory exists before trying to mount it. For
dry-run test cases, we added a configuration item that simulates the
presence (or the non-presence) of the systems directory on the source.

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2022-11-24 17:31:17 +01:00
parent 93f06eeb0f
commit 217ac98fc6
5 changed files with 25 additions and 1 deletions

View File

@ -1,4 +1,5 @@
#source-catalog: examples/tpm-sources.yaml
#dr-config: examples/tpm-dr-config.yaml
Source:
source: src-prefer-encrypted
Serial:

View File

@ -0,0 +1 @@
systems_dir_exists: true

View File

@ -19,6 +19,7 @@ import glob
import json
import logging
import os
import pathlib
import platform
import select
from typing import Dict, List, Optional
@ -110,6 +111,10 @@ than GPT, which is not currently supported.
""")
class NoSnapdSystemsOnSource(Exception):
pass
class FilesystemController(SubiquityController, FilesystemManipulator):
endpoint = API.storage
@ -171,6 +176,12 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
source_path = self.app.controllers.Source.source_path
cur_systems_dir = '/var/lib/snapd/seed/systems'
source_systems_dir = os.path.join(source_path, cur_systems_dir[1:])
if self.app.opts.dry_run:
systems_dir_exists = self.app.dr_cfg.systems_dir_exists
else:
systems_dir_exists = pathlib.Path(source_systems_dir).is_dir()
if not systems_dir_exists:
raise NoSnapdSystemsOnSource
self._system_mounter = Mounter(self.app)
await self._system_mounter.bind_mount_tree(
source_systems_dir, cur_systems_dir)
@ -182,7 +193,10 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
async def _get_system(self):
await self._unmount_system()
await self._mount_system()
try:
await self._mount_system()
except NoSnapdSystemsOnSource:
return
self._system = None
label = self.app.base_model.source.current.snapd_system_label
if label is not None:

View File

@ -38,6 +38,7 @@ from subiquity.models.tests.test_filesystem import (
)
from subiquity.server import snapdapi
from subiquity.server.controllers.filesystem import FilesystemController
from subiquity.server.dryrun import DRConfig
bootloaders = [(bl, ) for bl in list(Bootloader)]
bootloaders_and_ptables = [(bl, pt)
@ -472,6 +473,7 @@ class TestCoreBootInstallMethods(IsolatedAsyncioTestCase):
self.app.prober = mock.Mock()
self.app.snapdapi = snapdapi.make_api_client(
AsyncSnapd(get_fake_connection()))
self.app.dr_cfg = DRConfig()
self.fsc = FilesystemController(app=self.app)
self.fsc._configured = True
self.fsc.model = make_model(Bootloader.UEFI)
@ -565,6 +567,9 @@ class TestCoreBootInstallMethods(IsolatedAsyncioTestCase):
self.app.base_model.source.current.snapd_system_label = \
'prefer-encrypted'
self.app.controllers.Source.source_path = ''
self.app.dr_cfg.systems_dir_exists = True
await self.fsc._get_system_task.start()
await self.fsc._get_system_task.wait()

View File

@ -34,6 +34,9 @@ class DRConfig:
All variables here should have default values ; to indicate the behavior we
want by default in dry-run mode. """
# Tells whether "$source"/var/lib/snapd/seed/systems exists on the source.
systems_dir_exists: bool = False
@classmethod
def load(cls, stream):
data = yaml.safe_load(stream)