mirror: allow to combine different filters for the candidates

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2023-02-15 14:00:43 +01:00
parent 1350ef8d1b
commit 10a2e6ac22
1 changed files with 42 additions and 15 deletions

View File

@ -75,7 +75,10 @@ import abc
import copy import copy
import contextlib import contextlib
import logging import logging
from typing import Any, Dict, Iterator, List, Optional, Sequence, Set, Union from typing import (
Any, Callable, Dict, Iterator, List,
Optional, Sequence, Set, Union,
)
from urllib import parse from urllib import parse
import attr import attr
@ -130,6 +133,11 @@ class BasePrimaryEntry(abc.ABC):
def serialize_for_ai(self) -> Any: def serialize_for_ai(self) -> Any:
""" Serialize the entry for autoinstall. """ """ Serialize the entry for autoinstall. """
@abc.abstractmethod
def supports_arch(self, arch: str) -> bool:
""" Tells whether the mirror claims to support the architecture
specified. """
@attr.s(auto_attribs=True) @attr.s(auto_attribs=True)
class PrimaryEntry(BasePrimaryEntry): class PrimaryEntry(BasePrimaryEntry):
@ -159,8 +167,6 @@ class PrimaryEntry(BasePrimaryEntry):
return [{"uri": self.uri, "arches": ["default"]}] return [{"uri": self.uri, "arches": ["default"]}]
def supports_arch(self, arch: str) -> bool: def supports_arch(self, arch: str) -> bool:
""" Tells whether the mirror claims to support the architecture
specified. """
if self.arches is None: if self.arches is None:
return True return True
return arch in self.arches return arch in self.arches
@ -207,6 +213,11 @@ class LegacyPrimaryEntry(BasePrimaryEntry):
def serialize_for_ai(self) -> List[Any]: def serialize_for_ai(self) -> List[Any]:
return self.config return self.config
def supports_arch(self, arch: str) -> bool:
# Curtin will always find a mirror ; albeit with the ["default"]
# architectures.
return True
def countrify_uri(uri: str, cc: str) -> str: def countrify_uri(uri: str, cc: str) -> str:
""" Return a URL where the host is prefixed with a country code. """ """ Return a URL where the host is prefixed with a country code. """
@ -215,6 +226,18 @@ def countrify_uri(uri: str, cc: str) -> str:
return parse.urlunparse(new) return parse.urlunparse(new)
CandidateFilter = Callable[[BasePrimaryEntry], bool]
def filter_candidates(candidates: List[BasePrimaryEntry],
*, filters: Sequence[CandidateFilter]) \
-> Iterator[BasePrimaryEntry]:
candidates_iter = iter(candidates)
for filt in filters:
candidates_iter = filter(filt, candidates_iter)
return candidates_iter
class MirrorModel(object): class MirrorModel(object):
def __init__(self): def __init__(self):
@ -312,8 +335,12 @@ class MirrorModel(object):
# install. It will be placed in etc/apt/sources.list of the target # install. It will be placed in etc/apt/sources.list of the target
# system. # system.
with contextlib.suppress(StopIteration): with contextlib.suppress(StopIteration):
candidate = next(filter(lambda c: c.uri is not None, filters = [
self.compatible_primary_candidates())) lambda c: c.uri is not None,
lambda c: c.supports_arch(self.architecture),
]
candidate = next(filter_candidates(self.primary_candidates,
filters=filters))
return self._get_apt_config_using_candidate(candidate) return self._get_apt_config_using_candidate(candidate)
# Our last resort is to include no primary section. Curtin will use # Our last resort is to include no primary section. Curtin will use
# its own internal values. # its own internal values.
@ -357,20 +384,20 @@ class MirrorModel(object):
return next(self.country_mirror_candidates(), None) is not None return next(self.country_mirror_candidates(), None) is not None
def country_mirror_candidates(self) -> Iterator[BasePrimaryEntry]: def country_mirror_candidates(self) -> Iterator[BasePrimaryEntry]:
for candidate in self.primary_candidates: def filt(candidate):
if self.legacy_primary and candidate.mirror_is_default(): if self.legacy_primary and candidate.mirror_is_default():
yield candidate return True
elif not self.legacy_primary and candidate.country_mirror: elif not self.legacy_primary and candidate.country_mirror:
yield candidate return True
return False
return filter_candidates(self.primary_candidates, filters=[filt])
def compatible_primary_candidates(self) -> Iterator[BasePrimaryEntry]: def compatible_primary_candidates(self) -> Iterator[BasePrimaryEntry]:
for candidate in self.primary_candidates: def filt(candidate):
if self.legacy_primary: return candidate.supports_arch(self.architecture)
yield candidate
elif candidate.arches is None: return filter_candidates(self.primary_candidates, filters=[filt])
yield candidate
elif self.architecture in candidate.arches:
yield candidate
def render(self): def render(self):
return {} return {}