Repository: cekit/cekit

Issue:
dependency handling references python2 dependencies
Similar to https://github.com/cekit/behave-test-steps/issues/40 there are references to Python2 dependencies e.g.
```
        deps["python-docker"] = {
            "library": "docker",
            "package": "python-docker-py",
            "fedora": {"package": "python3-docker"},
        }
```

EPEL 7 differs from EPEL 8 in terms of Python 3 packages (but within CEKit they are only recognised as both being under the CentOS platform). This could be partially resolved within CEKit if the package determination in tools.py also allowed <platform><release as well as <platform> e.g. centos7 , so we could then have:

```
        deps["python-docker"] = {
            "library": "docker",
            "package": "python3-docker",
            "centos7": {"package": "python36-docker"},
        }
```




Discussion:

Patch:
diff --git a/cekit/builders/docker_builder.py b/cekit/builders/docker_builder.py
index 903ffa5..38c59c3 100644
--- a/cekit/builders/docker_builder.py
+++ b/cekit/builders/docker_builder.py
@@ -53,8 +53,8 @@ class DockerBuilder(Builder):
 
         deps["python-docker"] = {
             "library": "docker",
-            "package": "python-docker-py",
-            "fedora": {"package": "python3-docker"},
+            "package": "python3-docker",
+            "centos7": {"package": "python36-docker"},
         }
 
         if params is not None and not params.no_squash:
diff --git a/cekit/tools.py b/cekit/tools.py
index 34821b1..0cac2de 100644
--- a/cekit/tools.py
+++ b/cekit/tools.py
@@ -400,6 +400,7 @@ class DependencyHandler(object):
     def __init__(self):
         self.os_release = {}
         self.platform = None
+        self.version = None
 
         os_release_path = "/etc/os-release"
 
@@ -425,6 +426,7 @@ class DependencyHandler(object):
             or "ID" not in self.os_release
             or "NAME" not in self.os_release
             or "VERSION" not in self.os_release
+            or "VERSION_ID" not in self.os_release
         ):
             logger.warning(
                 "You are running CEKit on an unknown platform. External dependencies suggestions may not work!"
@@ -432,6 +434,7 @@ class DependencyHandler(object):
             return
 
         self.platform = self.os_release["ID"]
+        self.version = self.os_release["VERSION_ID"]
 
         if self.os_release["ID"] not in DependencyHandler.KNOWN_OPERATING_SYSTEMS:
             logger.warning(
@@ -448,7 +451,7 @@ class DependencyHandler(object):
             )
         )
 
-    def _handle_dependencies(self, dependencies):
+    def _handle_dependencies(self, dependencies: dict) -> None:
         """
         The dependencies provided is expected to be a dict in following format:
 
@@ -456,7 +459,7 @@ class DependencyHandler(object):
             PACKAGE_ID: { 'package': PACKAGE_NAME, 'command': COMMAND_TO_TEST_FOR_PACKACGE_EXISTENCE },
         }
 
-        Additionally every package can contain platform specific information, for example:
+        Additionally, every package can contain platform specific information, for example:
 
         {
             'git': {
@@ -470,6 +473,7 @@ class DependencyHandler(object):
 
         If the platform on which CEKit is currently running is available, it takes precedence before
         defaults.
+        The platform may be a simple name like e.g. 'fedora' or combined with the OS version e.g. 'centos7'
         """
 
         if not dependencies:
@@ -489,6 +493,13 @@ class DependencyHandler(object):
                 executable = current_dependency[self.platform].get(
                     "executable", executable
                 )
+            platform_release = f"{self.platform}{self.version}"
+            if platform_release in current_dependency:
+                package = current_dependency[platform_release].get("package", package)
+                library = current_dependency[platform_release].get("library", library)
+                executable = current_dependency[platform_release].get(
+                    "executable", executable
+                )
 
             logger.debug(
                 "Checking if '{}' dependency is provided...".format(dependency)
@@ -527,6 +538,7 @@ class DependencyHandler(object):
 
         logger.debug("All dependencies provided!")
 
+    # noinspection PyMethodMayBeStatic
     def _check_for_library(self, library):
         library_found = False
 
@@ -597,7 +609,7 @@ class DependencyHandler(object):
         except ImportError:
             pass
 
-    def handle(self, o, params):
+    def handle(self, o, params) -> None:
         """
         Handles dependencies from selected object. If the object has 'dependencies' method,
         it will be called to retrieve a set of dependencies to check for.


Test patch:
diff --git a/cekit/test/behave_runner.py b/cekit/test/behave_runner.py
index 24cd435..4063c29 100644
--- a/cekit/test/behave_runner.py
+++ b/cekit/test/behave_runner.py
@@ -23,8 +23,7 @@ class BehaveTestRunner(object):
 
         deps["python-behave"] = {
             "library": "behave",
-            "package": "python2-behave",
-            "fedora": {"package": "python3-behave"},
+            "package": "python3-behave",
         }
 
         return deps
diff --git a/tests/test_unit_tools.py b/tests/test_unit_tools.py
index f337365..a2d5779 100644
--- a/tests/test_unit_tools.py
+++ b/tests/test_unit_tools.py
@@ -590,7 +590,27 @@ def test_get_brew_url_with_artifact_containing_dot(mocker):
 
 
 @contextmanager
-def mocked_dependency_handler(mocker, data="ID=fedora\nNAME=somefedora\nVERSION=123"):
+def mocked_dependency_handler_centos(
+    mocker, data="ID=centos\nNAME=somecentos\nVERSION=7 (Core)\nVERSION_ID=7"
+):
+    dh = None
+
+    with mocker.mock_module.patch("cekit.tools.os.path.exists") as exists_mock:
+        exists_mock.return_value = True
+        with mocker.mock_module.patch(
+            "cekit.tools.open", mocker.mock_open(read_data=data)
+        ):
+            dh = tools.DependencyHandler()
+    try:
+        yield dh
+    finally:
+        pass
+
+
+@contextmanager
+def mocked_dependency_handler(
+    mocker, data="ID=fedora\nNAME=somefedora\nVERSION=123 (Test)\nVERSION_ID=123"
+):
     dh = None
 
     with mocker.mock_module.patch("cekit.tools.os.path.exists") as exists_mock:
@@ -752,6 +772,33 @@ def test_dependency_handler_handle_dependencies_with_executable_and_package_on_k
     assert "All dependencies provided!" in caplog.text
 
 
+def test_dependency_handler_handle_dependencies_with_platform_and_version_specific_package(
+    mocker, caplog
+):
+    caplog.set_level(logging.DEBUG, logger="cekit")
+
+    deps = {}
+
+    deps["xyz"] = {
+        "executable": "xyz-aaa",
+        "package": "python-xyz-aaa",
+        "centos7": {"package": "python-centos-xyz-aaa"},
+    }
+
+    with mocked_dependency_handler_centos(mocker) as handler:
+        mocker.patch.object(handler, "_check_for_executable")
+        mocker.spy(handler, "_check_for_executable")
+        handler._handle_dependencies(deps)
+
+        print(caplog.text)
+        handler._check_for_executable.assert_called_once_with(
+            "xyz", "xyz-aaa", "python-centos-xyz-aaa"
+        )
+
+    assert "Checking if 'xyz' dependency is provided..." in caplog.text
+    assert "All dependencies provided!" in caplog.text
+
+
 def test_dependency_handler_handle_dependencies_with_platform_specific_package(
     mocker, caplog
 ):