Source code for thoth.adviser.sieves.thoth_s2i_abi_compat

#!/usr/bin/env python3
# thoth-adviser
# Copyright(C) 2019-2021 Kevin Postlehtwait, Fridolin Pokorny
#
# This program is free software: you can redistribute it and / or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Filter out stacks which have require non-existent ABI symbols in Thoth's s2i base image."""

import logging
from typing import Any
from typing import Dict
from typing import Generator
from typing import Set
from typing import Tuple
from typing import TYPE_CHECKING

import attr
from thoth.common import get_justification_link as jl
from thoth.python import PackageVersion

from ..sieve import Sieve

if TYPE_CHECKING:
    from ..pipeline_builder import PipelineBuilderContext

_LOGGER = logging.getLogger(__name__)


[docs]@attr.s(slots=True) class ThothS2IAbiCompatibilitySieve(Sieve): """Remove packages if the Thoth's s2i image being used doesn't have necessary ABI.""" _THOTH_S2I_PREFIX = "quay.io/thoth-station/" CONFIGURATION_DEFAULT = {"package_name": None} image_symbols = attr.ib(type=Set[str], factory=set, init=False) _messages_logged = attr.ib(type=Set[Tuple[str, str, str]], factory=set, init=False) _LINK = jl("abi_missing") _LINK_NO_ABI = jl("no_abi") _LINK_BAD_IMAGE = jl("bad_base_image")
[docs] @classmethod def should_include(cls, builder_context: "PipelineBuilderContext") -> Generator[Dict[str, Any], None, None]: """Register if the base image provided is Thoth's s2i.""" base_image = builder_context.project.runtime_environment.base_image if not builder_context.is_included(cls) and base_image and base_image.startswith(cls._THOTH_S2I_PREFIX): yield {} return None yield from () return None
[docs] def pre_run(self) -> None: """Initialize image_symbols.""" base_image = self.context.project.runtime_environment.base_image parsed_base_image = self.get_base_image(base_image, raise_on_error=False) if parsed_base_image is None: error_msg = ( f"Cannot determine Thoth s2i version information from {base_image}, " "recommendations specific for ABI used will not be taken into account" ) _LOGGER.warning("%s - see %s", error_msg, self._LINK_BAD_IMAGE) self.context.stack_info.append( { "type": "WARNING", "message": error_msg, "link": self._LINK_BAD_IMAGE, } ) self.image_symbols = set() super().pre_run() return thoth_s2i_image_name, thoth_s2i_image_version = parsed_base_image self.image_symbols = set( self.context.graph.get_thoth_s2i_analyzed_image_symbols_all( thoth_s2i_image_name=thoth_s2i_image_name, thoth_s2i_image_version=thoth_s2i_image_version, is_external=False, ) ) if not self.image_symbols: message = f"No ABI symbols found for {thoth_s2i_image_name!r} in version {thoth_s2i_image_version!r}" _LOGGER.warning("%s - see %s", message, self._LINK_NO_ABI) self.context.stack_info.append({"type": "WARNING", "message": message, "link": self._LINK_NO_ABI}) self._messages_logged.clear() _LOGGER.debug("Analyzed image has the following symbols: %r", self.image_symbols) super().pre_run()
[docs] def run(self, package_versions: Generator[PackageVersion, None, None]) -> Generator[PackageVersion, None, None]: """If package requires non-present symbols remove it.""" if not self.image_symbols: # No image symbols or the version was not provided, the warning is produced in pre_run method. yield from package_versions return None for pkg_vers in package_versions: package_symbols = set( self.context.graph.get_python_package_required_symbols( package_name=pkg_vers.name, package_version=pkg_vers.locked_version, index_url=pkg_vers.index.url, ) ) # Shortcut if package requires no symbols if not package_symbols: yield pkg_vers continue missing_symbols = package_symbols - self.image_symbols if not missing_symbols: yield pkg_vers else: # Log removed package package_tuple = pkg_vers.to_tuple() if package_tuple not in self._messages_logged: message = f"Package {package_tuple} was removed due to missing ABI symbols in the environment" _LOGGER.warning("%s - see %s", message, self._LINK) self._messages_logged.add(package_tuple) _LOGGER.debug("The following symbols are not present: %r", missing_symbols) continue