oem: make sure storage is configured before using is_core_boot_classic
Before using fs_controller.is_core_boot_classic(), we wait for the call to /meta/confirmation?tty=xxx. That said, in semi-automated desktop installs, sometimes the call to /meta/confirmation happens before marking storage configured. This leads to the following error: File "subiquity/server/controllers/oem.py", line 209, in apply_autoinstall_config await self.load_metapkgs_task File "subiquity/server/controllers/oem.py", line 81, in list_and_mark_configured await self.load_metapackages_list() File "subiquitycore/context.py", line 149, in decorated_async return await meth(self, **kw) File "subiquity/server/controllers/oem.py", line 136, in load_metapackages_list if fs_controller.is_core_boot_classic(): File "subiquity/server/controllers/filesystem.py", line 284, in is_core_boot_classic return self._info.is_core_boot_classic() AttributeError: 'NoneType' object has no attribute 'is_core_boot_classic' Receiving the confirmation before getting the storage configured is arguably wrong - but let's be prepared for it just in case. Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
parent
f951146d6a
commit
59849f7f45
|
@ -65,6 +65,7 @@ class OEMController(SubiquityController):
|
||||||
|
|
||||||
self.load_metapkgs_task: Optional[asyncio.Task] = None
|
self.load_metapkgs_task: Optional[asyncio.Task] = None
|
||||||
self.kernel_configured_event = asyncio.Event()
|
self.kernel_configured_event = asyncio.Event()
|
||||||
|
self.fs_configured_event = asyncio.Event()
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
self._wait_confirmation = asyncio.Event()
|
self._wait_confirmation = asyncio.Event()
|
||||||
|
@ -76,6 +77,9 @@ class OEMController(SubiquityController):
|
||||||
self.app.hub.subscribe(
|
self.app.hub.subscribe(
|
||||||
(InstallerChannels.CONFIGURED, "kernel"), self.kernel_configured_event.set
|
(InstallerChannels.CONFIGURED, "kernel"), self.kernel_configured_event.set
|
||||||
)
|
)
|
||||||
|
self.app.hub.subscribe(
|
||||||
|
(InstallerChannels.CONFIGURED, "filesystem"), self.fs_configured_event.set
|
||||||
|
)
|
||||||
|
|
||||||
async def list_and_mark_configured() -> None:
|
async def list_and_mark_configured() -> None:
|
||||||
await self.load_metapackages_list()
|
await self.load_metapackages_list()
|
||||||
|
@ -128,6 +132,12 @@ class OEMController(SubiquityController):
|
||||||
async def load_metapackages_list(self, context) -> None:
|
async def load_metapackages_list(self, context) -> None:
|
||||||
with context.child("wait_confirmation"):
|
with context.child("wait_confirmation"):
|
||||||
await self._wait_confirmation.wait()
|
await self._wait_confirmation.wait()
|
||||||
|
# In normal scenarios, the confirmation event comes after the
|
||||||
|
# storage/filesystem is configured. However, in semi automated desktop
|
||||||
|
# installs (especially in CI), it is possible that the events come in
|
||||||
|
# the reverse order. Let's be prepared for it by also waiting for the
|
||||||
|
# storage configured event.
|
||||||
|
await self.fs_configured_event.wait()
|
||||||
|
|
||||||
# Only look for OEM meta-packages on supported variants and if we are
|
# Only look for OEM meta-packages on supported variants and if we are
|
||||||
# not running core boot.
|
# not running core boot.
|
||||||
|
|
|
@ -1820,6 +1820,38 @@ class TestOEM(TestAPI):
|
||||||
source_id="ubuntu-desktop", expected=["oem-somerville-tentacool-meta"]
|
source_id="ubuntu-desktop", expected=["oem-somerville-tentacool-meta"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def test_confirmation_before_storage_configured(self):
|
||||||
|
# On ubuntu-desktop, the confirmation event sometimes comes before the
|
||||||
|
# storage configured event. This was known to cause OEM to fail with
|
||||||
|
# the following error:
|
||||||
|
# File "server/controllers/oem.py", in load_metapackages_list
|
||||||
|
# if fs_controller.is_core_boot_classic():
|
||||||
|
# File "server/controllers/filesystem.py", in is_core_boot_classic
|
||||||
|
# return self._info.is_core_boot_classic()
|
||||||
|
# AttributeError: 'NoneType' object has no attribute
|
||||||
|
# 'is_core_boot_classic'
|
||||||
|
with patch.dict(os.environ, {"SUBIQUITY_DEBUG": "has-drivers"}):
|
||||||
|
config = "examples/machines/simple.json"
|
||||||
|
args = ["--source-catalog", "examples/sources/mixed.yaml"]
|
||||||
|
async with start_server(config, extra_args=args) as inst:
|
||||||
|
await inst.post("/source", source_id="ubuntu-desktop")
|
||||||
|
names = [
|
||||||
|
"locale",
|
||||||
|
"keyboard",
|
||||||
|
"source",
|
||||||
|
"network",
|
||||||
|
"proxy",
|
||||||
|
"mirror",
|
||||||
|
"storage",
|
||||||
|
]
|
||||||
|
await inst.post("/meta/confirm", tty="/dev/tty1")
|
||||||
|
await inst.post("/meta/mark_configured", endpoint_names=names)
|
||||||
|
|
||||||
|
resp = await inst.get("/oem", wait=True)
|
||||||
|
self.assertEqual(
|
||||||
|
["oem-somerville-tentacool-meta"], resp["metapackages"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestSource(TestAPI):
|
class TestSource(TestAPI):
|
||||||
@timeout()
|
@timeout()
|
||||||
|
|
Loading…
Reference in New Issue