From a5ae411c556dd7412cc14b3e65fad76707b4d5e1 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 5 Aug 2021 14:20:14 +1200 Subject: [PATCH] allow app state transition from NEEDS_CONFIRMATION to WAITING consider the following scenario, admittedly one that is not possible today: * There is an "install model" that is required for server and not desktop (let's say "mirror") * The user initially selects a desktop install and moves through the screens until they are asked for confirmation. * At this point the user moves back through the screens and selects a server variant. Now the application state of NEEDS_CONFIRMATION is misleading; the state needs to move back to WAITING until the mirror model is configured. This is all probably excessively general but I feel like the core control flow of the installer needs to be able to handle this sort of thing... --- subiquity/models/subiquity.py | 22 +++++++++++++++++++++- subiquity/server/controllers/install.py | 16 +++++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/subiquity/models/subiquity.py b/subiquity/models/subiquity.py index 98bd1ef4..93c93470 100644 --- a/subiquity/models/subiquity.py +++ b/subiquity/models/subiquity.py @@ -121,6 +121,7 @@ class SubiquityModel: self.userdata = {} self._confirmation = asyncio.Event() + self._confirmation_task = None self._configured_names = set() self._install_model_names = install_model_names @@ -136,6 +137,15 @@ class SubiquityModel: self._install_model_names.for_variant(variant) self._cur_postinstall_model_names = \ self._postinstall_model_names.for_variant(variant) + unconfigured_install_model_names = \ + self._cur_install_model_names - self._configured_names + if unconfigured_install_model_names: + if self._install_event.is_set(): + self._install_event = asyncio.Event() + if self._confirmation_task is not None: + self._confirmation_task.cancel() + else: + self._install_event.set() def configured(self, model_name): self._configured_names.add(model_name) @@ -163,7 +173,17 @@ class SubiquityModel: await self._postinstall_event.wait() async def wait_confirmation(self): - await self._confirmation.wait() + if self._confirmation_task is None: + self._confirmation_task = asyncio.get_event_loop().create_task( + self._confirmation.wait()) + try: + await self._confirmation_task + except asyncio.CancelledError: + return False + else: + return True + finally: + self._confirmation_task = None def is_postinstall_only(self, model_name): return model_name in self._cur_postinstall_model_names and \ diff --git a/subiquity/server/controllers/install.py b/subiquity/server/controllers/install.py index 747ba2e5..42a0f197 100644 --- a/subiquity/server/controllers/install.py +++ b/subiquity/server/controllers/install.py @@ -199,17 +199,19 @@ class InstallController(SubiquityController): async def install(self, *, context): context.set('is-install-context', True) try: - self.app.update_state(ApplicationState.WAITING) + while True: + self.app.update_state(ApplicationState.WAITING) - await self.model.wait_install() + await self.model.wait_install() - if not self.app.interactive: - if 'autoinstall' in self.app.kernel_cmdline: - self.model.confirm() + if not self.app.interactive: + if 'autoinstall' in self.app.kernel_cmdline: + self.model.confirm() - self.app.update_state(ApplicationState.NEEDS_CONFIRMATION) + self.app.update_state(ApplicationState.NEEDS_CONFIRMATION) - await self.model.wait_confirmation() + if await self.model.wait_confirmation(): + break self.app.update_state(ApplicationState.RUNNING)