allow system definition to be in live layer

Rather than always assuming it has to be mounted in from the layer to be
installed (snapd will now populate the seed in the target system when
its install API is called if it is empty).
This commit is contained in:
Michael Hudson-Doyle 2024-05-06 14:41:38 +02:00
parent c6a1b583de
commit c7fd905c6b
4 changed files with 90 additions and 13 deletions

View File

@ -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
}
]
}
}

View File

@ -337,17 +337,26 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
self._source_handler = None self._source_handler = None
async def _get_system(self, variation_name, label): async def _get_system(self, variation_name, label):
try: systems = await self.app.snapdapi.v2.systems.GET()
await self._mount_systems_dir(variation_name) labels = {system.label for system in systems.systems}
except NoSnapdSystemsOnSource: if label in labels:
return None try:
try: system = await self.app.snapdapi.v2.systems[label].GET()
system = await self.app.snapdapi.v2.systems[label].GET() except requests.exceptions.HTTPError as http_err:
except requests.exceptions.HTTPError as http_err: log.warning("v2/systems/%s returned %s", label, http_err.response.text)
log.warning("v2/systems/%s returned %s", label, http_err.response.text) raise
raise else:
finally: try:
await self._unmount_systems_dir() 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) log.debug("got system %s", system)
return system return system
@ -858,7 +867,10 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
async def guided_core_boot(self, disk: Disk): async def guided_core_boot(self, disk: Disk):
# Formatting for a core boot classic system relies on some curtin # Formatting for a core boot classic system relies on some curtin
# features that are only available with v2 partitioning. # 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 self.model.storage_version = 2
[volume] = self._info.system.volumes.values() [volume] = self._info.system.volumes.values()
self._on_volume = snapdapi.OnVolume.from_volume(volume) self._on_volume = snapdapi.OnVolume.from_volume(volume)

View File

@ -432,6 +432,18 @@ class TestSubiquityControllerFilesystem(IsolatedAsyncioTestCase):
}, },
} }
requests_mocker = requests_mock.Mocker() 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( requests_mocker.get(
"http+unix://snapd/v2/systems/enhanced-secureboot-desktop", "http+unix://snapd/v2/systems/enhanced-secureboot-desktop",
json=json_body, json=json_body,
@ -1397,9 +1409,10 @@ class TestCoreBootInstallMethods(IsolatedAsyncioTestCase):
name="foo", name="foo",
label="system", label="system",
system=snapdapi.SystemDetails( system=snapdapi.SystemDetails(
label="system",
volumes={ volumes={
"pc": snapdapi.Volume(schema="gpt", structure=structures), "pc": snapdapi.Volume(schema="gpt", structure=structures),
} },
), ),
) )

View File

@ -203,6 +203,7 @@ class StorageEncryption:
@attr.s(auto_attribs=True) @attr.s(auto_attribs=True)
class SystemDetails: class SystemDetails:
label: str
current: bool = False current: bool = False
volumes: Dict[str, Volume] = attr.Factory(dict) volumes: Dict[str, Volume] = attr.Factory(dict)
storage_encryption: Optional[StorageEncryption] = named_field( 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): class SystemAction(enum.Enum):
INSTALL = "install" INSTALL = "install"
@ -258,6 +264,9 @@ class SnapdAPI:
... ...
class systems: class systems:
def GET() -> SystemsResponse:
...
@path_parameter @path_parameter
class label: class label:
def GET() -> SystemDetails: def GET() -> SystemDetails: