From b2665c084e918dea46ece5e098ecf46472c64e44 Mon Sep 17 00:00:00 2001 From: beeankha Date: Wed, 10 Mar 2021 16:59:11 -0500 Subject: [PATCH 1/5] Loosen Collections v Tower version check --- awx_collection/plugins/module_utils/tower_api.py | 14 +++++++++++--- requirements/requirements.in | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/awx_collection/plugins/module_utils/tower_api.py b/awx_collection/plugins/module_utils/tower_api.py index 1ef663fb26..de81b6a662 100644 --- a/awx_collection/plugins/module_utils/tower_api.py +++ b/awx_collection/plugins/module_utils/tower_api.py @@ -7,6 +7,7 @@ from ansible.module_utils.urls import Request, SSLValidationError, ConnectionErr from ansible.module_utils.six import PY2 from ansible.module_utils.six.moves.urllib.error import HTTPError from ansible.module_utils.six.moves.http_cookiejar import CookieJar +import semver import time from json import loads, dumps @@ -259,10 +260,17 @@ class TowerAPIModule(TowerModule): tower_type = response.info().getheader('X-API-Product-Name', None) tower_version = response.info().getheader('X-API-Product-Version', None) + semver_collection_version = semver.VersionInfo.parse(self._COLLECTION_VERSION) + semver_tower_version = semver.VersionInfo.parse(tower_version) + if self._COLLECTION_TYPE not in self.collection_to_version or self.collection_to_version[self._COLLECTION_TYPE] != tower_type: - self.warn("You are using the {0} version of this collection but connecting to {1}".format(self._COLLECTION_TYPE, tower_type)) - elif self._COLLECTION_VERSION != tower_version: - self.warn("You are running collection version {0} but connecting to tower version {1}".format(self._COLLECTION_VERSION, tower_version)) + self.warn("You are using the {0} version of this collection but connecting to {1}".format( + self._COLLECTION_TYPE, tower_type + )) + elif semver_collection_version.major != semver_tower_version.major: + self.warn("You are running collection version {0} but connecting to tower version {1}".format( + self._COLLECTION_VERSION, tower_version + )) self.version_checked = True response_body = '' diff --git a/requirements/requirements.in b/requirements/requirements.in index 1970b215fb..4f22262c86 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -43,6 +43,7 @@ python3-saml python-ldap>=3.3.1 # https://github.com/python-ldap/python-ldap/issues/270 pyyaml>=5.4.1 # minimum to fix https://github.com/yaml/pyyaml/issues/478 schedule==0.6.0 +semver==2.13.0 social-auth-core==3.3.1 # see UPGRADE BLOCKERs social-auth-app-django==3.1.0 # see UPGRADE BLOCKERs redis From e2b290ff99d52ae89072db69a15b36198385561b Mon Sep 17 00:00:00 2001 From: beeankha Date: Tue, 23 Mar 2021 12:17:59 -0400 Subject: [PATCH 2/5] Use distutils instead of semver, add/update unit tests --- awx_collection/plugins/module_utils/tower_api.py | 16 ++++++---------- awx_collection/test/awx/test_module_utils.py | 16 +++++++++++++++- requirements/requirements.in | 1 - 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/awx_collection/plugins/module_utils/tower_api.py b/awx_collection/plugins/module_utils/tower_api.py index de81b6a662..02122b6c25 100644 --- a/awx_collection/plugins/module_utils/tower_api.py +++ b/awx_collection/plugins/module_utils/tower_api.py @@ -7,7 +7,7 @@ from ansible.module_utils.urls import Request, SSLValidationError, ConnectionErr from ansible.module_utils.six import PY2 from ansible.module_utils.six.moves.urllib.error import HTTPError from ansible.module_utils.six.moves.http_cookiejar import CookieJar -import semver +from distutils.version import LooseVersion as Version import time from json import loads, dumps @@ -260,17 +260,13 @@ class TowerAPIModule(TowerModule): tower_type = response.info().getheader('X-API-Product-Name', None) tower_version = response.info().getheader('X-API-Product-Version', None) - semver_collection_version = semver.VersionInfo.parse(self._COLLECTION_VERSION) - semver_tower_version = semver.VersionInfo.parse(tower_version) + collection_major = Version(self._COLLECTION_VERSION).version[0] + tower_major = Version(tower_version).version[0] if self._COLLECTION_TYPE not in self.collection_to_version or self.collection_to_version[self._COLLECTION_TYPE] != tower_type: - self.warn("You are using the {0} version of this collection but connecting to {1}".format( - self._COLLECTION_TYPE, tower_type - )) - elif semver_collection_version.major != semver_tower_version.major: - self.warn("You are running collection version {0} but connecting to tower version {1}".format( - self._COLLECTION_VERSION, tower_version - )) + self.warn("You are using the {0} version of this collection but connecting to {1}".format(self._COLLECTION_TYPE, tower_type)) + elif collection_major != tower_major: + self.warn("You are running collection version {0} but connecting to tower version {1}".format(self._COLLECTION_VERSION, tower_version)) self.version_checked = True response_body = '' diff --git a/awx_collection/test/awx/test_module_utils.py b/awx_collection/test/awx/test_module_utils.py index 473bfe9457..61a3b6d4bd 100644 --- a/awx_collection/test/awx/test_module_utils.py +++ b/awx_collection/test/awx/test_module_utils.py @@ -32,6 +32,20 @@ def mock_ping_response(self, method, url, **kwargs): def test_version_warning(collection_import, silence_warning): + TowerAPIModule = collection_import('plugins.module_utils.tower_api').TowerAPIModule + cli_data = {'ANSIBLE_MODULE_ARGS': {}} + testargs = ['module_file2.py', json.dumps(cli_data)] + with mock.patch.object(sys, 'argv', testargs): + with mock.patch('ansible.module_utils.urls.Request.open', new=mock_ping_response): + my_module = TowerAPIModule(argument_spec=dict()) + my_module._COLLECTION_VERSION = "2.0.0" + my_module._COLLECTION_TYPE = "not-junk" + my_module.collection_to_version['not-junk'] = 'not-junk' + my_module.get_endpoint('ping') + silence_warning.assert_called_once_with('You are running collection version 2.0.0 but connecting to tower version 1.2.3') + + +def test_version_warning_strictness(collection_import, silence_warning): TowerAPIModule = collection_import('plugins.module_utils.tower_api').TowerAPIModule cli_data = {'ANSIBLE_MODULE_ARGS': {}} testargs = ['module_file2.py', json.dumps(cli_data)] @@ -42,7 +56,7 @@ def test_version_warning(collection_import, silence_warning): my_module._COLLECTION_TYPE = "not-junk" my_module.collection_to_version['not-junk'] = 'not-junk' my_module.get_endpoint('ping') - silence_warning.assert_called_once_with('You are running collection version 1.0.0 but connecting to tower version 1.2.3') + silence_warning.assert_not_called() def test_type_warning(collection_import, silence_warning): diff --git a/requirements/requirements.in b/requirements/requirements.in index 4f22262c86..1970b215fb 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -43,7 +43,6 @@ python3-saml python-ldap>=3.3.1 # https://github.com/python-ldap/python-ldap/issues/270 pyyaml>=5.4.1 # minimum to fix https://github.com/yaml/pyyaml/issues/478 schedule==0.6.0 -semver==2.13.0 social-auth-core==3.3.1 # see UPGRADE BLOCKERs social-auth-app-django==3.1.0 # see UPGRADE BLOCKERs redis From bb43ecb0b510cb2200d7459c98a80a46cd6c3205 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Wed, 24 Mar 2021 10:16:53 -0400 Subject: [PATCH 3/5] Splitting out AWX and Tower versions --- .../plugins/module_utils/tower_api.py | 14 +++- awx_collection/test/awx/test_module_utils.py | 70 +++++++++++++++---- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/awx_collection/plugins/module_utils/tower_api.py b/awx_collection/plugins/module_utils/tower_api.py index 02122b6c25..beea9868d2 100644 --- a/awx_collection/plugins/module_utils/tower_api.py +++ b/awx_collection/plugins/module_utils/tower_api.py @@ -260,13 +260,21 @@ class TowerAPIModule(TowerModule): tower_type = response.info().getheader('X-API-Product-Name', None) tower_version = response.info().getheader('X-API-Product-Version', None) - collection_major = Version(self._COLLECTION_VERSION).version[0] - tower_major = Version(tower_version).version[0] + parsed_collection_version = Version(self._COLLECTION_VERSION).version + parsed_tower_version = Version(tower_version).version + if tower_type != 'AWX': + collection_compare_ver = parsed_collection_version[0] + tower_compare_ver = parsed_tower_version[0] + else: + collection_compare_ver = "{}.{}".format(parsed_collection_version[0], parsed_collection_version[1]) + tower_major = '{}.{}'.format(parsed_tower_version[0], parsed_tower_version[1]) + if self._COLLECTION_TYPE not in self.collection_to_version or self.collection_to_version[self._COLLECTION_TYPE] != tower_type: self.warn("You are using the {0} version of this collection but connecting to {1}".format(self._COLLECTION_TYPE, tower_type)) - elif collection_major != tower_major: + elif collection_compare_ver != tower_compare_ver: self.warn("You are running collection version {0} but connecting to tower version {1}".format(self._COLLECTION_VERSION, tower_version)) + self.version_checked = True response_body = '' diff --git a/awx_collection/test/awx/test_module_utils.py b/awx_collection/test/awx/test_module_utils.py index 61a3b6d4bd..168c5c7f27 100644 --- a/awx_collection/test/awx/test_module_utils.py +++ b/awx_collection/test/awx/test_module_utils.py @@ -10,8 +10,12 @@ from requests.models import Response from unittest import mock -def getheader(self, header_name, default): - mock_headers = {'X-API-Product-Name': 'not-junk', 'X-API-Product-Version': '1.2.3'} +def getTowerheader(self, header_name, default): + mock_headers = {'X-API-Product-Name': 'Red Hat Ansible Tower', 'X-API-Product-Version': '1.2.3'} + return mock_headers.get(header_name, default) + +def getAWXheader(self, header_name, default): + mock_headers = {'X-API-Product-Name': 'AWX', 'X-API-Product-Version': '1.2.3'} return mock_headers.get(header_name, default) @@ -23,9 +27,16 @@ def status(self): return 200 -def mock_ping_response(self, method, url, **kwargs): +def mock_tower_ping_response(self, method, url, **kwargs): r = Response() - r.getheader = getheader.__get__(r) + r.getheader = getTowerheader.__get__(r) + r.read = read.__get__(r) + r.status = status.__get__(r) + return r + +def mock_awx_ping_response(self, method, url, **kwargs): + r = Response() + r.getheader = getAWXheader.__get__(r) r.read = read.__get__(r) r.status = status.__get__(r) return r @@ -36,41 +47,70 @@ def test_version_warning(collection_import, silence_warning): cli_data = {'ANSIBLE_MODULE_ARGS': {}} testargs = ['module_file2.py', json.dumps(cli_data)] with mock.patch.object(sys, 'argv', testargs): - with mock.patch('ansible.module_utils.urls.Request.open', new=mock_ping_response): + with mock.patch('ansible.module_utils.urls.Request.open', new=mock_awx_ping_response): my_module = TowerAPIModule(argument_spec=dict()) my_module._COLLECTION_VERSION = "2.0.0" - my_module._COLLECTION_TYPE = "not-junk" - my_module.collection_to_version['not-junk'] = 'not-junk' + my_module._COLLECTION_TYPE = "awx" my_module.get_endpoint('ping') silence_warning.assert_called_once_with('You are running collection version 2.0.0 but connecting to tower version 1.2.3') -def test_version_warning_strictness(collection_import, silence_warning): +def test_version_warning_strictness_awx(collection_import, silence_warning): TowerAPIModule = collection_import('plugins.module_utils.tower_api').TowerAPIModule cli_data = {'ANSIBLE_MODULE_ARGS': {}} testargs = ['module_file2.py', json.dumps(cli_data)] + # Compare 1.0.0 to 1.2.3 (major matches) with mock.patch.object(sys, 'argv', testargs): - with mock.patch('ansible.module_utils.urls.Request.open', new=mock_ping_response): + with mock.patch('ansible.module_utils.urls.Request.open', new=mock_awx_ping_response): my_module = TowerAPIModule(argument_spec=dict()) my_module._COLLECTION_VERSION = "1.0.0" - my_module._COLLECTION_TYPE = "not-junk" - my_module.collection_to_version['not-junk'] = 'not-junk' + my_module._COLLECTION_TYPE = "awx" my_module.get_endpoint('ping') silence_warning.assert_not_called() + # Compare 1.2.0 to 1.2.3 (major matches minor does not count) + with mock.patch.object(sys, 'argv', testargs): + with mock.patch('ansible.module_utils.urls.Request.open', new=mock_awx_ping_response): + my_module = TowerAPIModule(argument_spec=dict()) + my_module._COLLECTION_VERSION = "1.2.0" + my_module._COLLECTION_TYPE = "awx" + my_module.get_endpoint('ping') + silence_warning.assert_not_called() + +def test_version_warning_strictness_tower(collection_import, silence_warning): + TowerAPIModule = collection_import('plugins.module_utils.tower_api').TowerAPIModule + cli_data = {'ANSIBLE_MODULE_ARGS': {}} + testargs = ['module_file2.py', json.dumps(cli_data)] + # Compare 1.2.0 to 1.2.3 (major/minor matches) + with mock.patch.object(sys, 'argv', testargs): + with mock.patch('ansible.module_utils.urls.Request.open', new=mock_tower_ping_response): + my_module = TowerAPIModule(argument_spec=dict()) + my_module._COLLECTION_VERSION = "1.2.0" + my_module.get_endpoint('ping') + silence_warning.assert_not_called() + + # Compare 1.0.0 to 1.2.3 (major/minor fail to match) + with mock.patch.object(sys, 'argv', testargs): + with mock.patch('ansible.module_utils.urls.Request.open', new=mock_tower_ping_response): + my_module = TowerAPIModule(argument_spec=dict()) + my_module._COLLECTION_VERSION = "1.0.0" + my_module._COLLECTION_TYPE = "tower" + my_module.get_endpoint('ping') + silence_warning.assert_called_once_with('You are running collection version 1.0.0 but connecting to tower version 1.2.3') + + def test_type_warning(collection_import, silence_warning): TowerAPIModule = collection_import('plugins.module_utils.tower_api').TowerAPIModule cli_data = {'ANSIBLE_MODULE_ARGS': {}} testargs = ['module_file2.py', json.dumps(cli_data)] with mock.patch.object(sys, 'argv', testargs): - with mock.patch('ansible.module_utils.urls.Request.open', new=mock_ping_response): + with mock.patch('ansible.module_utils.urls.Request.open', new=mock_awx_ping_response): my_module = TowerAPIModule(argument_spec={}) my_module._COLLECTION_VERSION = "1.2.3" - my_module._COLLECTION_TYPE = "junk" - my_module.collection_to_version['junk'] = 'junk' + my_module._COLLECTION_TYPE = "tower" my_module.get_endpoint('ping') - silence_warning.assert_called_once_with('You are using the junk version of this collection but connecting to not-junk') + silence_warning.assert_called_once_with('You are using the tower version of this collection but connecting to awx') def test_duplicate_config(collection_import, silence_warning): From aa9906ebae7a5bf86cb65fa54731a772a1c58db2 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Thu, 25 Mar 2021 08:58:26 -0400 Subject: [PATCH 4/5] Fixing issues --- awx_collection/plugins/module_utils/tower_api.py | 2 +- awx_collection/test/awx/test_module_utils.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/awx_collection/plugins/module_utils/tower_api.py b/awx_collection/plugins/module_utils/tower_api.py index beea9868d2..4fcdecfde1 100644 --- a/awx_collection/plugins/module_utils/tower_api.py +++ b/awx_collection/plugins/module_utils/tower_api.py @@ -267,7 +267,7 @@ class TowerAPIModule(TowerModule): tower_compare_ver = parsed_tower_version[0] else: collection_compare_ver = "{}.{}".format(parsed_collection_version[0], parsed_collection_version[1]) - tower_major = '{}.{}'.format(parsed_tower_version[0], parsed_tower_version[1]) + tower_compare_ver = '{}.{}'.format(parsed_tower_version[0], parsed_tower_version[1]) if self._COLLECTION_TYPE not in self.collection_to_version or self.collection_to_version[self._COLLECTION_TYPE] != tower_type: diff --git a/awx_collection/test/awx/test_module_utils.py b/awx_collection/test/awx/test_module_utils.py index 168c5c7f27..2c88e64826 100644 --- a/awx_collection/test/awx/test_module_utils.py +++ b/awx_collection/test/awx/test_module_utils.py @@ -9,13 +9,16 @@ from awx.main.models import Organization, Team, Project, Inventory from requests.models import Response from unittest import mock +awx_name = 'AWX' +tower_name = 'Red Hat Ansible Tower' +ping_version = '1.2.3' def getTowerheader(self, header_name, default): - mock_headers = {'X-API-Product-Name': 'Red Hat Ansible Tower', 'X-API-Product-Version': '1.2.3'} + mock_headers = {'X-API-Product-Name': tower_name, 'X-API-Product-Version': ping_version} return mock_headers.get(header_name, default) def getAWXheader(self, header_name, default): - mock_headers = {'X-API-Product-Name': 'AWX', 'X-API-Product-Version': '1.2.3'} + mock_headers = {'X-API-Product-Name': awx_name, 'X-API-Product-Version': ping_version} return mock_headers.get(header_name, default) @@ -52,7 +55,7 @@ def test_version_warning(collection_import, silence_warning): my_module._COLLECTION_VERSION = "2.0.0" my_module._COLLECTION_TYPE = "awx" my_module.get_endpoint('ping') - silence_warning.assert_called_once_with('You are running collection version 2.0.0 but connecting to tower version 1.2.3') + silence_warning.assert_called_once_with('You are running collection version {} but connecting to tower version {}'.format(my_module._COLLECTION_VERSION, ping_version)) def test_version_warning_strictness_awx(collection_import, silence_warning): @@ -86,6 +89,7 @@ def test_version_warning_strictness_tower(collection_import, silence_warning): with mock.patch('ansible.module_utils.urls.Request.open', new=mock_tower_ping_response): my_module = TowerAPIModule(argument_spec=dict()) my_module._COLLECTION_VERSION = "1.2.0" + my_module._COLLECTION_TYPE = "tower" my_module.get_endpoint('ping') silence_warning.assert_not_called() @@ -96,7 +100,7 @@ def test_version_warning_strictness_tower(collection_import, silence_warning): my_module._COLLECTION_VERSION = "1.0.0" my_module._COLLECTION_TYPE = "tower" my_module.get_endpoint('ping') - silence_warning.assert_called_once_with('You are running collection version 1.0.0 but connecting to tower version 1.2.3') + silence_warning.assert_called_once_with('You are running collection version {} but connecting to tower version {}'.format(my_module._COLLECTION_VERSION, ping_version)) @@ -107,10 +111,10 @@ def test_type_warning(collection_import, silence_warning): with mock.patch.object(sys, 'argv', testargs): with mock.patch('ansible.module_utils.urls.Request.open', new=mock_awx_ping_response): my_module = TowerAPIModule(argument_spec={}) - my_module._COLLECTION_VERSION = "1.2.3" + my_module._COLLECTION_VERSION = ping_version my_module._COLLECTION_TYPE = "tower" my_module.get_endpoint('ping') - silence_warning.assert_called_once_with('You are using the tower version of this collection but connecting to awx') + silence_warning.assert_called_once_with('You are using the {} version of this collection but connecting to {}'.format(my_module._COLLECTION_TYPE, awx_name)) def test_duplicate_config(collection_import, silence_warning): From 75a99bb1d50d30e39e107883822d7a4996b623c1 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Thu, 25 Mar 2021 09:33:46 -0400 Subject: [PATCH 5/5] Fixing version check --- awx_collection/plugins/module_utils/tower_api.py | 3 +-- awx_collection/test/awx/test_module_utils.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/awx_collection/plugins/module_utils/tower_api.py b/awx_collection/plugins/module_utils/tower_api.py index 4fcdecfde1..2edba2b502 100644 --- a/awx_collection/plugins/module_utils/tower_api.py +++ b/awx_collection/plugins/module_utils/tower_api.py @@ -262,14 +262,13 @@ class TowerAPIModule(TowerModule): parsed_collection_version = Version(self._COLLECTION_VERSION).version parsed_tower_version = Version(tower_version).version - if tower_type != 'AWX': + if tower_type == 'AWX': collection_compare_ver = parsed_collection_version[0] tower_compare_ver = parsed_tower_version[0] else: collection_compare_ver = "{}.{}".format(parsed_collection_version[0], parsed_collection_version[1]) tower_compare_ver = '{}.{}'.format(parsed_tower_version[0], parsed_tower_version[1]) - if self._COLLECTION_TYPE not in self.collection_to_version or self.collection_to_version[self._COLLECTION_TYPE] != tower_type: self.warn("You are using the {0} version of this collection but connecting to {1}".format(self._COLLECTION_TYPE, tower_type)) elif collection_compare_ver != tower_compare_ver: diff --git a/awx_collection/test/awx/test_module_utils.py b/awx_collection/test/awx/test_module_utils.py index 2c88e64826..8f2a8e52d5 100644 --- a/awx_collection/test/awx/test_module_utils.py +++ b/awx_collection/test/awx/test_module_utils.py @@ -13,10 +13,12 @@ awx_name = 'AWX' tower_name = 'Red Hat Ansible Tower' ping_version = '1.2.3' + def getTowerheader(self, header_name, default): mock_headers = {'X-API-Product-Name': tower_name, 'X-API-Product-Version': ping_version} return mock_headers.get(header_name, default) + def getAWXheader(self, header_name, default): mock_headers = {'X-API-Product-Name': awx_name, 'X-API-Product-Version': ping_version} return mock_headers.get(header_name, default) @@ -37,6 +39,7 @@ def mock_tower_ping_response(self, method, url, **kwargs): r.status = status.__get__(r) return r + def mock_awx_ping_response(self, method, url, **kwargs): r = Response() r.getheader = getAWXheader.__get__(r) @@ -55,7 +58,9 @@ def test_version_warning(collection_import, silence_warning): my_module._COLLECTION_VERSION = "2.0.0" my_module._COLLECTION_TYPE = "awx" my_module.get_endpoint('ping') - silence_warning.assert_called_once_with('You are running collection version {} but connecting to tower version {}'.format(my_module._COLLECTION_VERSION, ping_version)) + silence_warning.assert_called_once_with( + 'You are running collection version {} but connecting to tower version {}'.format(my_module._COLLECTION_VERSION, ping_version) + ) def test_version_warning_strictness_awx(collection_import, silence_warning): @@ -80,6 +85,7 @@ def test_version_warning_strictness_awx(collection_import, silence_warning): my_module.get_endpoint('ping') silence_warning.assert_not_called() + def test_version_warning_strictness_tower(collection_import, silence_warning): TowerAPIModule = collection_import('plugins.module_utils.tower_api').TowerAPIModule cli_data = {'ANSIBLE_MODULE_ARGS': {}} @@ -100,8 +106,9 @@ def test_version_warning_strictness_tower(collection_import, silence_warning): my_module._COLLECTION_VERSION = "1.0.0" my_module._COLLECTION_TYPE = "tower" my_module.get_endpoint('ping') - silence_warning.assert_called_once_with('You are running collection version {} but connecting to tower version {}'.format(my_module._COLLECTION_VERSION, ping_version)) - + silence_warning.assert_called_once_with( + 'You are running collection version {} but connecting to tower version {}'.format(my_module._COLLECTION_VERSION, ping_version) + ) def test_type_warning(collection_import, silence_warning):