diff --git a/examples/snaps/v2-systems.json b/examples/snaps/v2-systems.json new file mode 100644 index 00000000..666861a5 --- /dev/null +++ b/examples/snaps/v2-systems.json @@ -0,0 +1,43 @@ +{ + "type": "sync", + "status-code": 200, + "status": "OK", + "result": { + "systems": [ + { + "current": true, + "label": "20240314", + "model": { + "model": "ubuntu-core-24-amd64-dangerous", + "brand-id": "canonical", + "display-name": "ubuntu-core-24-amd64-dangerous" + }, + "brand": { + "id": "canonical", + "username": "canonical", + "display-name": "Canonical", + "validation": "verified" + }, + "actions": [ + { + "title": "Reinstall", + "mode": "install" + }, + { + "title": "Recover", + "mode": "recover" + }, + { + "title": "Factory reset", + "mode": "factory-reset" + }, + { + "title": "Run normally", + "mode": "run" + } + ], + "default-recovery-system": true + } + ] + } +} diff --git a/subiquity/server/controllers/filesystem.py b/subiquity/server/controllers/filesystem.py index 1a58c0c0..84192cad 100644 --- a/subiquity/server/controllers/filesystem.py +++ b/subiquity/server/controllers/filesystem.py @@ -337,17 +337,26 @@ class FilesystemController(SubiquityController, FilesystemManipulator): self._source_handler = None async def _get_system(self, variation_name, label): - try: - await self._mount_systems_dir(variation_name) - except NoSnapdSystemsOnSource: - return None - try: - system = await self.app.snapdapi.v2.systems[label].GET() - except requests.exceptions.HTTPError as http_err: - log.warning("v2/systems/%s returned %s", label, http_err.response.text) - raise - finally: - await self._unmount_systems_dir() + systems = await self.app.snapdapi.v2.systems.GET() + labels = {system.label for system in systems.systems} + if label in labels: + try: + system = await self.app.snapdapi.v2.systems[label].GET() + except requests.exceptions.HTTPError as http_err: + log.warning("v2/systems/%s returned %s", label, http_err.response.text) + raise + else: + try: + await self._mount_systems_dir(variation_name) + except NoSnapdSystemsOnSource: + return None + try: + system = await self.app.snapdapi.v2.systems[label].GET() + except requests.exceptions.HTTPError as http_err: + log.warning("v2/systems/%s returned %s", label, http_err.response.text) + raise + finally: + await self._unmount_systems_dir() log.debug("got system %s", system) return system @@ -858,7 +867,10 @@ class FilesystemController(SubiquityController, FilesystemManipulator): async def guided_core_boot(self, disk: Disk): # Formatting for a core boot classic system relies on some curtin # features that are only available with v2 partitioning. - await self._mount_systems_dir(self._info.name) + systems = await self.app.snapdapi.v2.systems.GET() + labels = {system.label for system in systems.systems} + if self._info.label not in labels: + await self._mount_systems_dir(self._info.name) self.model.storage_version = 2 [volume] = self._info.system.volumes.values() self._on_volume = snapdapi.OnVolume.from_volume(volume) diff --git a/subiquity/server/controllers/tests/test_filesystem.py b/subiquity/server/controllers/tests/test_filesystem.py index bbad135f..0cc4ede5 100644 --- a/subiquity/server/controllers/tests/test_filesystem.py +++ b/subiquity/server/controllers/tests/test_filesystem.py @@ -432,6 +432,18 @@ class TestSubiquityControllerFilesystem(IsolatedAsyncioTestCase): }, } requests_mocker = requests_mock.Mocker() + requests_mocker.get( + "http+unix://snapd/v2/systems", + json={ + "type": "sync", + "status-code": 200, + "status": "OK", + "result": { + "systems": [], + }, + }, + status_code=200, + ) requests_mocker.get( "http+unix://snapd/v2/systems/enhanced-secureboot-desktop", json=json_body, @@ -1397,9 +1409,10 @@ class TestCoreBootInstallMethods(IsolatedAsyncioTestCase): name="foo", label="system", system=snapdapi.SystemDetails( + label="system", volumes={ "pc": snapdapi.Volume(schema="gpt", structure=structures), - } + }, ), ) diff --git a/subiquity/server/snapdapi.py b/subiquity/server/snapdapi.py index 5ae38382..2231cb61 100644 --- a/subiquity/server/snapdapi.py +++ b/subiquity/server/snapdapi.py @@ -203,6 +203,7 @@ class StorageEncryption: @attr.s(auto_attribs=True) class SystemDetails: + label: str current: bool = False volumes: Dict[str, Volume] = attr.Factory(dict) storage_encryption: Optional[StorageEncryption] = named_field( @@ -210,6 +211,11 @@ class SystemDetails: ) +@attr.s(auto_attribs=True) +class SystemsResponse: + systems: List[SystemDetails] = attr.Factory(list) + + class SystemAction(enum.Enum): INSTALL = "install" @@ -258,6 +264,9 @@ class SnapdAPI: ... class systems: + def GET() -> SystemsResponse: + ... + @path_parameter class label: def GET() -> SystemDetails: