From 5553793f43ba78c6289d13315af263748fb6ee83 Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Thu, 11 Apr 2024 09:39:41 +0200 Subject: [PATCH 1/2] mirror: allow to query or set if archive/mirror is used during install The /mirror GET and POST endpoints now include a boolean field named "use_during_installation". * if set to True, the mirror information will be used during installation to fetch packages online. * if set to False, we will only fetch packages from the pool. In either case, the mirror information will still be used to build the etc/apt/ directory in the target system. Currently, the way use_during_installation is implemented is coupled with the force_offline property of the network model. This means that Ubuntu Pro and other stuff will be disabled too if we're skipping the use of the archive. NOTE: the default value for "use_during_installation" in the POST endpoint is `null` ; which means that we should not change the current value of force_offline. Signed-off-by: Olivier Gayot --- subiquity/common/types.py | 4 ++++ subiquity/server/controllers/mirror.py | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/subiquity/common/types.py b/subiquity/common/types.py index 4c5ae938..5155a0e1 100644 --- a/subiquity/common/types.py +++ b/subiquity/common/types.py @@ -879,6 +879,7 @@ class MirrorPost: elected: Optional[str] = None candidates: Optional[List[str]] = None staged: Optional[str] = None + use_during_installation: Optional[bool] = None class MirrorPostResponse(enum.Enum): @@ -892,6 +893,9 @@ class MirrorGet: elected: Optional[str] candidates: List[str] staged: Optional[str] + # Tells whether the mirror will be used during the installation. + # When it is False, we will only fetch packages from the pool. + use_during_installation: bool class MirrorSelectionFallback(enum.Enum): diff --git a/subiquity/server/controllers/mirror.py b/subiquity/server/controllers/mirror.py index 3f85f1da..ac3a9560 100644 --- a/subiquity/server/controllers/mirror.py +++ b/subiquity/server/controllers/mirror.py @@ -368,7 +368,11 @@ class MirrorController(SubiquityController): # Skip the country-mirrors if they have not been resolved yet. candidates = [c.uri for c in compatibles if c.uri is not None] return MirrorGet( - relevant=relevant, elected=elected, candidates=candidates, staged=staged + relevant=relevant, + elected=elected, + candidates=candidates, + staged=staged, + use_during_installation=not self.app.base_model.network.force_offline, ) async def POST(self, data: Optional[MirrorPost]) -> MirrorPostResponse: @@ -420,6 +424,10 @@ class MirrorController(SubiquityController): ensure_elected_in_candidates() await self.configured() + + if data.use_during_installation is not None: + self.app.base_model.network.force_offline = not data.use_during_installation + return MirrorPostResponse.OK async def disable_components_GET(self) -> List[str]: From 0bb4915c9f77bb11ade1c1f49626a046d1e3024a Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Thu, 11 Apr 2024 10:55:07 +0200 Subject: [PATCH 2/2] mirror: if mirror test fails, suggest an offline install In the mirror screen, if the test fails and the user decides to ignore the failure, we used to continue the installation normally ; which in most scenarios resulted in an error at a later stage of the installation. Instead, we now revert to an installation without network (i.e., only packages from the pool are considered for installation) if the user decides to ignore the failure. Signed-off-by: Olivier Gayot --- subiquity/client/controllers/mirror.py | 29 +++++++++++++++++++++++--- subiquity/ui/views/mirror.py | 16 ++++++++------ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/subiquity/client/controllers/mirror.py b/subiquity/client/controllers/mirror.py index 26082660..aab2ea93 100644 --- a/subiquity/client/controllers/mirror.py +++ b/subiquity/client/controllers/mirror.py @@ -15,6 +15,7 @@ import asyncio import logging +from typing import Optional from subiquity.client.controller import SubiquityTuiController from subiquity.common.types import MirrorCheckStatus, MirrorGet, MirrorPost @@ -42,6 +43,15 @@ class MirrorController(SubiquityTuiController): # Just in case there is no candidate at all. # In practise, it should seldom happen. url = next(iter(mirror_response.candidates), "") + + if not mirror_response.use_during_installation: + # If the user comes back to the mirror screen after accepting to + # do an installation without the archive (i.e., only fetching from + # the pool), the call to /network/has_network will return false. We + # need to reset the force_offline value if we want to run another + # mirror test. + await self.endpoint.POST(MirrorPost(use_during_installation=True)) + has_network = await self.app.client.network.has_network.GET() if has_network: check = await self.endpoint.check_mirror.progress.GET() @@ -69,6 +79,19 @@ class MirrorController(SubiquityTuiController): def cancel(self): self.app.prev_screen() - def done(self, mirror): - log.debug("MirrorController.done next_screen mirror=%s", mirror) - self.app.next_screen(self.endpoint.POST(MirrorPost(elected=mirror))) + def done(self, mirror, skip_archive: Optional[bool]): + if skip_archive is not None: + use_during_installation = not skip_archive + else: + use_during_installation = None + + data = MirrorPost( + elected=mirror, use_during_installation=use_during_installation + ) + + log.debug( + "MirrorController.done next_screen mirror=%s, use_during_installation=%s", + mirror, + use_during_installation, + ) + self.app.next_screen(self.endpoint.POST(data)) diff --git a/subiquity/ui/views/mirror.py b/subiquity/ui/views/mirror.py index 7a581aa1..28bd99b9 100644 --- a/subiquity/ui/views/mirror.py +++ b/subiquity/ui/views/mirror.py @@ -47,8 +47,10 @@ MIRROR_CHECK_CONFIRMATION_TEXTS = { MirrorCheckStatus.FAILED: ( _("Mirror check failed"), _( - "The check of the mirror URL failed. You can continue, but it is very" - " likely that the installation will fail." + "The check of the mirror URL failed. If you decide to continue, only" + " packages present on the installation media will be considered for" + " installation. Remember to install security updates after booting" + " your newly installed system." ), ), None: ( @@ -217,7 +219,7 @@ class MirrorView(BaseView): self.last_status = check_state.status def done(self, result): - async def confirm_continue_anyway() -> None: + async def confirm_continue_anyway(continue_skip_archive: bool) -> None: title, question = MIRROR_CHECK_CONFIRMATION_TEXTS[self.last_status] confirmed = await self.ask_confirmation( title=title, @@ -227,7 +229,8 @@ class MirrorView(BaseView): ) if confirmed: - self.controller.done(result.url.value) + skip_archive = True if continue_skip_archive else None + self.controller.done(result.url.value, skip_archive=skip_archive) log.debug("User input: {}".format(result.as_data())) if self.has_network and self.last_status in [ @@ -235,9 +238,10 @@ class MirrorView(BaseView): MirrorCheckStatus.FAILED, None, ]: - async_helpers.run_bg_task(confirm_continue_anyway()) + status_is_failed = self.last_status == MirrorCheckStatus.FAILED + async_helpers.run_bg_task(confirm_continue_anyway(status_is_failed)) else: - self.controller.done(result.url.value) + self.controller.done(result.url.value, skip_archive=None) def cancel(self, result=None): self.controller.cancel()