From 3732d889a4c8e14b985143c6e70e675a98fa3d38 Mon Sep 17 00:00:00 2001 From: Josef Harte Date: Mon, 8 Dec 2025 15:16:28 +0000 Subject: [PATCH 1/8] fix upgrade path --- .../common_vars/compatibility_matrix.yml | 2 +- ibm/mas_devops/plugins/filter/filters.py | 29 ++++++++++++++++++- .../roles/aiservice_upgrade/tasks/main.yml | 11 ++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/ibm/mas_devops/common_vars/compatibility_matrix.yml b/ibm/mas_devops/common_vars/compatibility_matrix.yml index 62656a2ef..4434ee9d5 100644 --- a/ibm/mas_devops/common_vars/compatibility_matrix.yml +++ b/ibm/mas_devops/common_vars/compatibility_matrix.yml @@ -116,4 +116,4 @@ upgrade_path: 8.9.x: 8.10.x aiservice_upgrade_path: - 9.1.x: 9.2.x-feature + 9.1.x: [9.2.x-feature] diff --git a/ibm/mas_devops/plugins/filter/filters.py b/ibm/mas_devops/plugins/filter/filters.py index 1f3654739..da0f5c2e0 100644 --- a/ibm/mas_devops/plugins/filter/filters.py +++ b/ibm/mas_devops/plugins/filter/filters.py @@ -437,6 +437,32 @@ def get_ecr_repositories(image_mirror_output): repositories.append(repo_to_add) return repositories +def is_channel_upgrade_path_valid(current: str, target: str, valid_paths: dict) -> bool: + """ + Checks if a given current channel version can be upgraded to a target channel version. + :current: The current channel version. + :target: The target channel version to upgrade to. + :valid_paths: A dictionary of supported upgrade paths. See ibm/mas_devops/common_vars/compatibility_matrix.yml. + :return: True if the upgrade path is supported, False otherwise. + """ + valid = False + if current not in valid_paths.keys(): + print(f'Current channel {current} is not supported for upgrade') + else: + allowed_targets = valid_paths[current] + if isinstance(allowed_targets, str): + if target != allowed_targets: + print(f'Upgrading from channel {current} to {target} is not supported') + else: + valid = True + elif isinstance(allowed_targets, list): + if target not in allowed_targets: + print(f'Upgrading from channel {current} to {target} is not supported') + else: + valid = True + else: + print(f'Error: channel upgrade compatibility matrix is incorrectly defined') + return valid class FilterModule(object): def filters(self): @@ -459,5 +485,6 @@ def filters(self): 'format_pre_version_without_buildid': format_pre_version_without_buildid, 'format_pre_version_with_buildid': format_pre_version_with_buildid, 'get_db2_instance_name': get_db2_instance_name, - 'get_ecr_repositories': get_ecr_repositories + 'get_ecr_repositories': get_ecr_repositories, + 'is_channel_upgrade_path_valid': is_channel_upgrade_path_valid, } diff --git a/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml b/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml index dc9db0f4c..f6e3a7cd1 100644 --- a/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml +++ b/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml @@ -27,6 +27,15 @@ - "operators.coreos.com/ibm-aiservice.{{ aiservice_namespace }}" register: aiservice_sub_info +- name: "Set current subscription channel" + set_fact: + current_aiservice_channel: "{{ aiservice_sub_info.resources[0].spec.channel }}" + +- name: Show current subscription info + ansible.builtin.debug: + msg: + - "Currently installed AI Service channel ............. {{ current_aiservice_channel }}" + - name: "Set default upgrade target based on installed version of AI Service" when: - aiservice_channel is not defined or aiservice_channel == "" @@ -37,7 +46,7 @@ - name: "Set upgrade target explicitly" when: - aiservice_channel is defined and aiservice_channel != "" - - aiservice_channel in aiservice_upgrade_path + - current_aiservice_channel | ibm.mas_devops.is_channel_upgrade_path_valid(aiservice_channel, aiservice_upgrade_path) set_fact: target_aiservice_channel: "{{ aiservice_upgrade_path[aiservice_channel] }}" From 80918ab2734ecece480fa078a07005da31194385 Mon Sep 17 00:00:00 2001 From: Josef Harte Date: Mon, 8 Dec 2025 15:46:04 +0000 Subject: [PATCH 2/8] add comment --- ibm/mas_devops/common_vars/compatibility_matrix.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ibm/mas_devops/common_vars/compatibility_matrix.yml b/ibm/mas_devops/common_vars/compatibility_matrix.yml index 4434ee9d5..11990ceb7 100644 --- a/ibm/mas_devops/common_vars/compatibility_matrix.yml +++ b/ibm/mas_devops/common_vars/compatibility_matrix.yml @@ -115,5 +115,9 @@ upgrade_path: 8.10.x: 8.11.x 8.9.x: 8.10.x +# Value can be a version or list of versions: +# Example: +# 9.1.x: [9.2.x, 9.2.x-feature] +# 9.2.x: 9.3.x aiservice_upgrade_path: 9.1.x: [9.2.x-feature] From 6d4131be66eabd08f0a021e468dcba6ad214b3b0 Mon Sep 17 00:00:00 2001 From: Josef Harte Date: Mon, 8 Dec 2025 18:19:03 +0000 Subject: [PATCH 3/8] func to get default channel --- .../common_vars/compatibility_matrix.yml | 7 ++++++- ibm/mas_devops/plugins/filter/filters.py | 18 ++++++++++++++++++ .../roles/aiservice_upgrade/tasks/main.yml | 4 ++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/ibm/mas_devops/common_vars/compatibility_matrix.yml b/ibm/mas_devops/common_vars/compatibility_matrix.yml index 11990ceb7..bb3c74990 100644 --- a/ibm/mas_devops/common_vars/compatibility_matrix.yml +++ b/ibm/mas_devops/common_vars/compatibility_matrix.yml @@ -115,9 +115,14 @@ upgrade_path: 8.10.x: 8.11.x 8.9.x: 8.10.x -# Value can be a version or list of versions: +# The key is the current installed channel of AI Service +# and the value is the allowed upgrade versions. +# Value can be a single version or list of versions: # Example: # 9.1.x: [9.2.x, 9.2.x-feature] # 9.2.x: 9.3.x +# WARNING: if using a list the first value in +# the list is taken as the default if the user +# does not supply a target upgrade version. aiservice_upgrade_path: 9.1.x: [9.2.x-feature] diff --git a/ibm/mas_devops/plugins/filter/filters.py b/ibm/mas_devops/plugins/filter/filters.py index da0f5c2e0..847adc4a6 100644 --- a/ibm/mas_devops/plugins/filter/filters.py +++ b/ibm/mas_devops/plugins/filter/filters.py @@ -464,6 +464,23 @@ def is_channel_upgrade_path_valid(current: str, target: str, valid_paths: dict) print(f'Error: channel upgrade compatibility matrix is incorrectly defined') return valid +def get_default_upgrade_channel(current: str, valid_paths: dict) -> str: + """ + Gets the default target upgrade channel if the user has not supplied one. + :current: The current channel version. + :valid_paths: A dictionary of supported upgrade paths. See ibm/mas_devops/common_vars/compatibility_matrix.yml. + :return: The default target upgrade channel. + """ + default = None + allowed_targets = valid_paths[current] + if isinstance(allowed_targets, str): + default = allowed_targets + elif isinstance(allowed_targets, list): + default = allowed_targets[0] + else: + print(f'Error: channel upgrade compatibility matrix is incorrectly defined') + return default + class FilterModule(object): def filters(self): return { @@ -487,4 +504,5 @@ def filters(self): 'get_db2_instance_name': get_db2_instance_name, 'get_ecr_repositories': get_ecr_repositories, 'is_channel_upgrade_path_valid': is_channel_upgrade_path_valid, + 'get_default_upgrade_channel': get_default_upgrade_channel } diff --git a/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml b/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml index f6e3a7cd1..7956515a9 100644 --- a/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml +++ b/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml @@ -39,9 +39,9 @@ - name: "Set default upgrade target based on installed version of AI Service" when: - aiservice_channel is not defined or aiservice_channel == "" - - aiservice_sub_info.resources[0].spec.channel in aiservice_upgrade_path + - aiservice_sub_info.resources[0].spec.channel | ibm.mas_devops.is_channel_upgrade_path_valid(aiservice_channel, aiservice_upgrade_path) set_fact: - target_aiservice_channel: "{{ aiservice_upgrade_path[aiservice_sub_info.resources[0].spec.channel] }}" + target_aiservice_channel: "{{ current_aiservice_channel | get_default_upgrade_channel(aiservice_upgrade_path) }}" - name: "Set upgrade target explicitly" when: From af04dc758e3e88c3f40a959c4f5558c8f61a64ae Mon Sep 17 00:00:00 2001 From: Josef Harte Date: Tue, 9 Dec 2025 09:01:47 +0000 Subject: [PATCH 4/8] unit tests --- .gitignore | 13 ++++ ibm/mas_devops/plugins/filter/filters.py | 12 ++-- ibm/mas_devops/plugins/filter/test_filters.py | 70 +++++++++++++++++++ .../roles/aiservice_upgrade/tasks/main.yml | 3 +- 4 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 ibm/mas_devops/plugins/filter/test_filters.py diff --git a/.gitignore b/.gitignore index 151070fe7..5ab7a9c78 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,16 @@ cpd-cli-workspace/* /node_modules package-lock.json package.json + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +dist +*.egg-info +pandoc-*-amd64.deb +pandoc-*-windows-x86_64.msi +pandoc-*-x86_64-macOS.pkg +README.rst diff --git a/ibm/mas_devops/plugins/filter/filters.py b/ibm/mas_devops/plugins/filter/filters.py index 847adc4a6..117579d74 100644 --- a/ibm/mas_devops/plugins/filter/filters.py +++ b/ibm/mas_devops/plugins/filter/filters.py @@ -445,23 +445,23 @@ def is_channel_upgrade_path_valid(current: str, target: str, valid_paths: dict) :valid_paths: A dictionary of supported upgrade paths. See ibm/mas_devops/common_vars/compatibility_matrix.yml. :return: True if the upgrade path is supported, False otherwise. """ - valid = False + valid = True if current not in valid_paths.keys(): print(f'Current channel {current} is not supported for upgrade') - else: + valid = False + elif target: allowed_targets = valid_paths[current] if isinstance(allowed_targets, str): if target != allowed_targets: print(f'Upgrading from channel {current} to {target} is not supported') - else: - valid = True + valid = False elif isinstance(allowed_targets, list): if target not in allowed_targets: print(f'Upgrading from channel {current} to {target} is not supported') - else: - valid = True + valid = False else: print(f'Error: channel upgrade compatibility matrix is incorrectly defined') + valid = False return valid def get_default_upgrade_channel(current: str, valid_paths: dict) -> str: diff --git a/ibm/mas_devops/plugins/filter/test_filters.py b/ibm/mas_devops/plugins/filter/test_filters.py new file mode 100644 index 000000000..26927f2a0 --- /dev/null +++ b/ibm/mas_devops/plugins/filter/test_filters.py @@ -0,0 +1,70 @@ +# ----------------------------------------------------------- +# Licensed Materials - Property of IBM +# 5737-M66, 5900-AAA +# (C) Copyright IBM Corp. 2025 All Rights Reserved. +# US Government Users Restricted Rights - Use, duplication, or disclosure +# restricted by GSA ADP Schedule Contract with IBM Corp. +# ----------------------------------------------------------- + +from filters import * +import pytest + + +########################################################################## + + +def test_is_channel_upgrade_path_valid_for_blank_target_channel(): + paths = {'a': ['b', 'c']} + assert is_channel_upgrade_path_valid('a', None, paths) + + +def test_is_channel_upgrade_path_valid_for_invalid_target_channel(): + paths = {'a': ['b', 'c']} + assert not is_channel_upgrade_path_valid('a', 'd', paths) + + +def test_is_channel_upgrade_path_valid_for_valid_target_channel(): + paths = {'a': ['b', 'c']} + assert is_channel_upgrade_path_valid('a', 'c', paths) + + +def test_is_channel_upgrade_path_valid_for_invalid_current_channel(): + paths = {'a': ['b', 'c']} + assert not is_channel_upgrade_path_valid('d', 'c', paths) + + +def test_is_channel_upgrade_path_valid_for_string_target_channel(): + paths = {'a': 'b'} + assert is_channel_upgrade_path_valid('a', 'b', paths) + + +def test_is_channel_upgrade_path_valid_for_invalid_paths(): + paths = {'a': 1} + assert not is_channel_upgrade_path_valid('a', 'b', paths) + + +########################################################################## + + +def test_get_default_upgrade_channel_for_valid_current_channel(): + paths = {'a': ['b', 'c']} + assert 'b' == get_default_upgrade_channel('a', paths) + + +def test_get_default_upgrade_channel_for_string_target_channel(): + paths = {'a': 'b'} + assert 'b' == get_default_upgrade_channel('a', paths) + + +def test_get_default_upgrade_channel_for_invalid_current_channel(): + paths = {'a': ['b', 'c']} + with pytest.raises(KeyError): + get_default_upgrade_channel('b', paths) + + +def test_get_default_upgrade_channel_for_invalid_paths(): + paths = {'a': 1} + assert None == get_default_upgrade_channel('a', paths) + + +########################################################################## diff --git a/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml b/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml index 7956515a9..2faa7e34c 100644 --- a/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml +++ b/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml @@ -39,7 +39,6 @@ - name: "Set default upgrade target based on installed version of AI Service" when: - aiservice_channel is not defined or aiservice_channel == "" - - aiservice_sub_info.resources[0].spec.channel | ibm.mas_devops.is_channel_upgrade_path_valid(aiservice_channel, aiservice_upgrade_path) set_fact: target_aiservice_channel: "{{ current_aiservice_channel | get_default_upgrade_channel(aiservice_upgrade_path) }}" @@ -48,7 +47,7 @@ - aiservice_channel is defined and aiservice_channel != "" - current_aiservice_channel | ibm.mas_devops.is_channel_upgrade_path_valid(aiservice_channel, aiservice_upgrade_path) set_fact: - target_aiservice_channel: "{{ aiservice_upgrade_path[aiservice_channel] }}" + target_aiservice_channel: "{{ aiservice_channel }}" - name: "Assert upgrade target is defined" assert: From 3808bd05b7f31cb44391b572b392d10bac3588ee Mon Sep 17 00:00:00 2001 From: Josef Harte Date: Tue, 9 Dec 2025 09:05:44 +0000 Subject: [PATCH 5/8] update boolean check] --- ibm/mas_devops/plugins/filter/test_filters.py | 2 +- ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ibm/mas_devops/plugins/filter/test_filters.py b/ibm/mas_devops/plugins/filter/test_filters.py index 26927f2a0..b1642270d 100644 --- a/ibm/mas_devops/plugins/filter/test_filters.py +++ b/ibm/mas_devops/plugins/filter/test_filters.py @@ -15,7 +15,7 @@ def test_is_channel_upgrade_path_valid_for_blank_target_channel(): paths = {'a': ['b', 'c']} - assert is_channel_upgrade_path_valid('a', None, paths) + assert is_channel_upgrade_path_valid('a', '', paths) def test_is_channel_upgrade_path_valid_for_invalid_target_channel(): diff --git a/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml b/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml index 2faa7e34c..ab4e15021 100644 --- a/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml +++ b/ibm/mas_devops/roles/aiservice_upgrade/tasks/main.yml @@ -39,6 +39,7 @@ - name: "Set default upgrade target based on installed version of AI Service" when: - aiservice_channel is not defined or aiservice_channel == "" + - current_aiservice_channel | ibm.mas_devops.is_channel_upgrade_path_valid('', aiservice_upgrade_path) set_fact: target_aiservice_channel: "{{ current_aiservice_channel | get_default_upgrade_channel(aiservice_upgrade_path) }}" From 88689219aaa1d30c7f6bc158d89ed3b0b77d1b50 Mon Sep 17 00:00:00 2001 From: Josef Harte Date: Thu, 11 Dec 2025 12:59:41 +0000 Subject: [PATCH 6/8] remove list of targets --- ibm/mas_devops/common_vars/compatibility_matrix.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ibm/mas_devops/common_vars/compatibility_matrix.yml b/ibm/mas_devops/common_vars/compatibility_matrix.yml index bb3c74990..f95df9b6a 100644 --- a/ibm/mas_devops/common_vars/compatibility_matrix.yml +++ b/ibm/mas_devops/common_vars/compatibility_matrix.yml @@ -117,12 +117,5 @@ upgrade_path: # The key is the current installed channel of AI Service # and the value is the allowed upgrade versions. -# Value can be a single version or list of versions: -# Example: -# 9.1.x: [9.2.x, 9.2.x-feature] -# 9.2.x: 9.3.x -# WARNING: if using a list the first value in -# the list is taken as the default if the user -# does not supply a target upgrade version. aiservice_upgrade_path: - 9.1.x: [9.2.x-feature] + 9.1.x: 9.2.x-feature From e7a338b27ba6f34ec00418abeb7ce18e6f419574 Mon Sep 17 00:00:00 2001 From: Josef Harte Date: Fri, 12 Dec 2025 16:12:08 +0000 Subject: [PATCH 7/8] remove header --- ibm/mas_devops/plugins/filter/test_filters.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ibm/mas_devops/plugins/filter/test_filters.py b/ibm/mas_devops/plugins/filter/test_filters.py index b1642270d..f10ef5ecf 100644 --- a/ibm/mas_devops/plugins/filter/test_filters.py +++ b/ibm/mas_devops/plugins/filter/test_filters.py @@ -1,11 +1,3 @@ -# ----------------------------------------------------------- -# Licensed Materials - Property of IBM -# 5737-M66, 5900-AAA -# (C) Copyright IBM Corp. 2025 All Rights Reserved. -# US Government Users Restricted Rights - Use, duplication, or disclosure -# restricted by GSA ADP Schedule Contract with IBM Corp. -# ----------------------------------------------------------- - from filters import * import pytest From 90fefa628958687c11b3c08e1ac65505361c279f Mon Sep 17 00:00:00 2001 From: Josef Harte Date: Fri, 12 Dec 2025 18:03:07 +0000 Subject: [PATCH 8/8] remove header --- ibm/mas_devops/plugins/filter/filters.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ibm/mas_devops/plugins/filter/filters.py b/ibm/mas_devops/plugins/filter/filters.py index 117579d74..cbe33e3da 100644 --- a/ibm/mas_devops/plugins/filter/filters.py +++ b/ibm/mas_devops/plugins/filter/filters.py @@ -1,10 +1,3 @@ -# ----------------------------------------------------------- -# Licensed Materials - Property of IBM -# 5737-M66, 5900-AAA -# (C) Copyright IBM Corp. 2021 All Rights Reserved. -# US Government Users Restricted Rights - Use, duplication, or disclosure -# restricted by GSA ADP Schedule Contract with IBM Corp. -# ----------------------------------------------------------- import yaml import re