-
Notifications
You must be signed in to change notification settings - Fork 45
Added dependency checker and unit test case #2375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mayankmani-sde
wants to merge
40
commits into
dev
Choose a base branch
from
Enhancement/2133/Dependency_checker
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 20 commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
74031fd
Added dependency checker and unit test case
mayankmani-sde 9be3bab
pushed requested changes
mayankmani-sde 5ab1feb
added header
mayankmani-sde 799542d
added python range 3.12-3.13
mayankmani-sde 0f2235d
updated mock test
mayankmani-sde e860507
updated version
mayankmani-sde 80f5d28
added header in test
mayankmani-sde 701d066
removed alias dc from test
mayankmani-sde f2718fd
added header in test
mayankmani-sde c550b0c
added comment in test
mayankmani-sde 7aa1be3
fixed sanity
mayankmani-sde a19b2b7
FIXED SANITY
mayankmani-sde 217012c
fixed sanity
mayankmani-sde 74d7afd
changed error message
mayankmani-sde 079ab14
checked for Ensure all versions are available and fixed error message
mayankmani-sde 7b37786
removed code for range check for max versions of python and zos
mayankmani-sde 90a2c07
added warning message
mayankmani-sde 5be0f12
enhanced error message
mayankmani-sde 14f0424
fixed sanity
mayankmani-sde 6edc75d
added comment in test
mayankmani-sde 519c0ef
fixed
mayankmani-sde bd1a5ee
typo error
mayankmani-sde d2b05e4
fixed
mayankmani-sde d8baed7
fixed
mayankmani-sde 1348507
typo error
mayankmani-sde ddb9b7f
enhamced code
mayankmani-sde 130963a
enhamced unit test case
mayankmani-sde ada03b2
fixed
mayankmani-sde a0d535e
fixed
mayankmani-sde 8018bef
fixed
mayankmani-sde 59a13d3
added integration of dependency_checker in zos_apf module
mayankmani-sde 27d8cf8
added integration of dependency_checker in zos_archive
mayankmani-sde c1617c9
added integration of dependency_checker module in zos_backup_restore
mayankmani-sde f0a7062
added integration for dependency_checker in zos_blockinfile
mayankmani-sde 8ccdb1f
added dependency_checker in all modules except zos_volume_init.py
mayankmani-sde 47db734
added dependency-checker in zos_volume_init.py
mayankmani-sde 2626457
Merge branch 'dev' into Enhancement/2133/Dependency_checker
mayankmani-sde d4d9fe8
FIXED
mayankmani-sde 665ea6b
enhanced
mayankmani-sde 874028b
updated dependency matrix from 1.3.6.0 to 1.4.0
mayankmani-sde File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| # -*- coding: utf-8 -*- | ||
| # Copyright (c) IBM Corporation 2025 | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # You may not use this file except in compliance with the License. | ||
| # You may obtain a copy at: | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| from __future__ import absolute_import, division, print_function | ||
| import sys | ||
| from ansible_collections.ibm.ibm_zos_core.plugins.module_utils import version | ||
|
|
||
| try: | ||
| from zoautil_py import zsystem | ||
| except ImportError: | ||
| zsystem = None | ||
|
|
||
| __metaclass__ = type | ||
|
|
||
| # ------------------------------------------------------------------------------ | ||
| # Compatibility Matrix by Collection Version | ||
| # ------------------------------------------------------------------------------ | ||
| COMPATIBILITY_MATRIX = { | ||
| "2.0.0": [ | ||
| {"zoau_version": "1.4.0", "min_python_version": "3.12", "min_zos_version": 2.5}, | ||
| {"zoau_version": "1.4.1", "min_python_version": "3.12", "min_zos_version": 2.5}, | ||
| {"zoau_version": "1.4.2", "min_python_version": "3.12", "min_zos_version": 2.5}, | ||
| ], | ||
| "2.1.0": [ | ||
| {"zoau_version": "1.4.1", "min_python_version": "3.12", "min_zos_version": 2.5}, | ||
| {"zoau_version": "1.4.2", "min_python_version": "3.12", "min_zos_version": 2.5}, | ||
| ], | ||
| "2.2.0": [ | ||
| {"zoau_version": "1.4.2", "min_python_version": "3.12", "min_zos_version": 2.5}, | ||
| ], | ||
| } | ||
|
|
||
| # ------------------------------------------------------------------------------ | ||
| # Version Fetchers | ||
| # ------------------------------------------------------------------------------ | ||
|
|
||
|
|
||
| def get_zoau_version(module=None): | ||
| try: | ||
| from zoautil_py import ZOAU_API_VERSION | ||
| return ZOAU_API_VERSION | ||
| except ImportError: | ||
| if module: | ||
| module.fail_json(msg="Unable to import ZOAU. Please check PYTHONPATH, LIBPATH, ZOAU_HOME and PATH environment variables.") | ||
| return None | ||
|
|
||
|
|
||
| def get_python_version_info(): | ||
| return sys.version_info.major, sys.version_info.minor | ||
|
|
||
|
|
||
| def get_python_version(): | ||
| return f"{sys.version_info.major}.{sys.version_info.minor}.0" | ||
|
|
||
|
|
||
| def get_zos_version(module=None): | ||
| if zsystem is None: | ||
| if module: | ||
| module.warn("Unable to import ZOAU zsystem module.") | ||
| return None | ||
| try: | ||
| sys_info = zsystem.zinfo("sys", json_format=True) | ||
| sys_data = sys_info.get("data", {}).get("sys_info", {}) | ||
| version = sys_data.get("product_version") | ||
| release = sys_data.get("product_release") | ||
| if version and release: | ||
| return f"{int(version)}.{int(release)}" | ||
| except Exception as e: | ||
| if module: | ||
| module.warn(f"Failed to fetch z/OS version: {e}") | ||
| return None | ||
|
|
||
| # ------------------------------------------------------------------------------ | ||
| # Dependency Validation | ||
| # ------------------------------------------------------------------------------ | ||
|
|
||
|
|
||
| def validate_dependencies(module): | ||
| zoau_version = get_zoau_version(module) | ||
| python_major, python_minor = get_python_version_info() | ||
| python_version_str = get_python_version() | ||
| zos_version_str = get_zos_version(module) | ||
| collection_version = version.__version__ | ||
|
|
||
| # Ensure critical versions are available | ||
| if not all([zoau_version, zos_version_str, python_version_str, collection_version]): | ||
| module.fail_json(msg="Unable to fetch one or more required dependencies. Depedencies checked are ZOAU, Python, z/OS.") | ||
|
|
||
| # Convert z/OS version to float if available | ||
| zos_version = None | ||
| if zos_version_str: | ||
| try: | ||
| zos_version = float(zos_version_str) | ||
| except Exception: | ||
| if module: | ||
| module.warn(f"Unable to parse z/OS version: {zos_version_str}") | ||
|
|
||
| compat_list = COMPATIBILITY_MATRIX.get(collection_version, []) | ||
| if not compat_list: | ||
| module.fail_json(msg=f"No compatibility information for collection version: {collection_version}") | ||
|
|
||
| def parse_py(v): | ||
mayankmani-sde marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return tuple(map(int, v.split("."))) | ||
|
|
||
| current_py = (python_major, python_minor) | ||
| min_py = None | ||
| min_zos = None | ||
|
|
||
| # Find matching ZOAU entry | ||
| for compat in compat_list: | ||
| if compat["zoau_version"] == zoau_version: | ||
| min_py = parse_py(compat["min_python_version"]) | ||
| min_zos = compat["min_zos_version"] | ||
| break | ||
|
|
||
| if not min_py or not min_zos: | ||
| module.fail_json(msg=f"Incompatible ZOAU version: {zoau_version}") | ||
mayankmani-sde marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # --- Validation logic --- | ||
| warnings = [] | ||
| max_py = (3, 13) | ||
| max_zos = 3.1 | ||
|
|
||
| # Too old = fail | ||
| if current_py < min_py: | ||
| module.fail_json(msg=f"Incompatible Python version: {python_version_str}. Minimum supported is {compat['min_python_version']}.") | ||
| if zos_version is not None and zos_version < min_zos: | ||
| module.fail_json(msg=f"Incompatible z/OS version: {zos_version_str}. Minimum supported is {min_zos}.") | ||
|
|
||
| # Too new = warn | ||
| if current_py > max_py: | ||
| msg = f"Python {python_version_str} exceeds the maximum tested version {max_py[0]}.{max_py[1]}." | ||
| warnings.append(msg) | ||
| module.warn(msg) | ||
|
|
||
| if zos_version is not None and zos_version > max_zos: | ||
| msg = f"z/OS {zos_version_str} exceeds the maximum tested version {max_zos}." | ||
| warnings.append(msg) | ||
| module.warn(msg) | ||
|
|
||
| # Exit module with warnings if any | ||
mayankmani-sde marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if warnings: | ||
| module.exit_json(changed=False, msg="Dependency check passed with warnings.", warnings=warnings) | ||
| else: | ||
| module.exit_json(changed=False, msg="Dependency compatibility check passed.") | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # -*- coding: utf-8 -*- | ||
|
|
||
| # Copyright (c) IBM Corporation 2025 | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # Update this version with each new release of the collection | ||
|
|
||
| __version__ = "2.0.0" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| # -*- coding: utf-8 -*- | ||
| # Copyright (c) IBM Corporation 2025 | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # You may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| import pytest | ||
| from unittest.mock import MagicMock, patch | ||
| from ansible_collections.ibm.ibm_zos_core.plugins.module_utils import dependency_checker | ||
| from ansible_collections.ibm.ibm_zos_core.plugins.module_utils import version | ||
|
|
||
| class FakeModule: | ||
| def __init__(self): | ||
| self.warned = [] | ||
| self.exited = None | ||
|
|
||
| def fail_json(self, **kwargs): | ||
| raise Exception(kwargs.get("msg", "fail_json called")) | ||
|
|
||
| def exit_json(self, **kwargs): | ||
| self.exited = kwargs | ||
| raise StopIteration(kwargs) | ||
|
|
||
| def warn(self, msg): | ||
| self.warned.append(msg) | ||
|
|
||
|
|
||
| # ------------------------------ | ||
| # Test: Python above max triggers warning | ||
| # ------------------------------ | ||
| def test_python_above_max(monkeypatch): | ||
| # This approach is preferred over using simple dictionaries because: | ||
| # 1. The validate_dependencies function relies on real functions to fetch versions. | ||
| # 2. Using monkeypatch allows us to simulate different runtime environments. | ||
| # 3. We can precisely test success, failure, and warning scenarios without affecting | ||
| # the real system or requiring specific Python/ZOAU/zOS versions. | ||
| monkeypatch.setattr(dependency_checker, "get_zoau_version", lambda mod=None: "1.4.2") | ||
| monkeypatch.setattr(dependency_checker, "get_python_version_info", lambda: (3, 14)) | ||
| monkeypatch.setattr(dependency_checker, "get_python_version", lambda: "3.14.0") | ||
| monkeypatch.setattr(dependency_checker, "get_zos_version", lambda mod=None: "2.6") | ||
| monkeypatch.setattr(version, "__version__", "2.0.0") | ||
|
|
||
| mod = FakeModule() | ||
| with pytest.raises(StopIteration): | ||
| dependency_checker.validate_dependencies(mod) | ||
| assert any("Python 3.14.0 exceeds the maximum tested version" in w for w in mod.warned) | ||
| assert mod.exited["msg"] == "Dependency check passed with warnings." | ||
|
|
||
|
|
||
| # ------------------------------ | ||
| # Test: z/OS above max triggers warning | ||
| # ------------------------------ | ||
| def test_zos_above_max(monkeypatch): | ||
| monkeypatch.setattr(dependency_checker, "get_zoau_version", lambda mod=None: "1.4.2") | ||
| monkeypatch.setattr(dependency_checker, "get_python_version_info", lambda: (3, 12)) | ||
| monkeypatch.setattr(dependency_checker, "get_python_version", lambda: "3.12.0") | ||
| monkeypatch.setattr(dependency_checker, "get_zos_version", lambda mod=None: "3.2") | ||
| monkeypatch.setattr(version, "__version__", "2.0.0") | ||
|
|
||
| mod = FakeModule() | ||
| with pytest.raises(StopIteration): | ||
| dependency_checker.validate_dependencies(mod) | ||
| assert any("z/OS 3.2 exceeds the maximum tested version" in w for w in mod.warned) | ||
| assert mod.exited["msg"] == "Dependency check passed with warnings." | ||
|
|
||
|
|
||
| # ------------------------------ | ||
| # Test: versions within range pass without warning | ||
| # ------------------------------ | ||
| def test_versions_within_range(monkeypatch): | ||
| monkeypatch.setattr(dependency_checker, "get_zoau_version", lambda mod=None: "1.4.2") | ||
| monkeypatch.setattr(dependency_checker, "get_python_version_info", lambda: (3, 12)) | ||
| monkeypatch.setattr(dependency_checker, "get_python_version", lambda: "3.12.0") | ||
| monkeypatch.setattr(dependency_checker, "get_zos_version", lambda mod=None: "2.6") | ||
| monkeypatch.setattr(version, "__version__", "2.0.0") | ||
|
|
||
| mod = FakeModule() | ||
| with pytest.raises(StopIteration): | ||
| dependency_checker.validate_dependencies(mod) | ||
| assert mod.warned == [] | ||
| assert mod.exited["msg"] == "Dependency compatibility check passed." | ||
|
|
||
|
|
||
| # ------------------------------ | ||
| # Test: Python below min fails | ||
| # ------------------------------ | ||
| def test_python_below_min(monkeypatch): | ||
| monkeypatch.setattr(dependency_checker, "get_zoau_version", lambda mod=None: "1.4.2") | ||
| monkeypatch.setattr(dependency_checker, "get_python_version_info", lambda: (3, 11)) | ||
| monkeypatch.setattr(dependency_checker, "get_python_version", lambda: "3.11.0") | ||
| monkeypatch.setattr(dependency_checker, "get_zos_version", lambda mod=None: "2.6") | ||
| monkeypatch.setattr(version, "__version__", "2.0.0") | ||
|
|
||
| mod = FakeModule() | ||
| with pytest.raises(Exception) as exc: | ||
| dependency_checker.validate_dependencies(mod) | ||
| assert "Incompatible Python version" in str(exc.value) | ||
|
|
||
|
|
||
| # ------------------------------ | ||
| # Test: z/OS below min fails | ||
| # ------------------------------ | ||
| def test_zos_below_min(monkeypatch): | ||
| monkeypatch.setattr(dependency_checker, "get_zoau_version", lambda mod=None: "1.4.2") | ||
| monkeypatch.setattr(dependency_checker, "get_python_version_info", lambda: (3, 12)) | ||
| monkeypatch.setattr(dependency_checker, "get_python_version", lambda: "3.12.0") | ||
| monkeypatch.setattr(dependency_checker, "get_zos_version", lambda mod=None: "2.4") | ||
| monkeypatch.setattr(version, "__version__", "2.0.0") | ||
|
|
||
| mod = FakeModule() | ||
| with pytest.raises(Exception) as exc: | ||
| dependency_checker.validate_dependencies(mod) | ||
| assert "Incompatible z/OS version" in str(exc.value) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.