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/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]: 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()