mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-06 21:12:37 +00:00
Limit compatibility to Helm =>v3.0.0,<4.0.0 (#1039)
SUMMARY
Helm v4 is a major version with backward-incompatible changes, including to the flags and output of the Helm CLI and to the SDK. This version is currently not supported in the kubernetes.core. This PR is related to #1038 and is a short-term solution to mark compatibility explicitly
ISSUE TYPE
Bugfix Pull Request
Docs Pull Request
COMPONENT NAME
helm
helm_template
helm_info
helm_repository
helm_pull
helm_registry_auth
helm_plugin
helm_plugin_info
ADDITIONAL INFORMATION
Added `validate_helm_version()`` method to AnsibleHelmModule that enforces version constraint >=3.0.0,<4.0.0.
Fails fast with clear error message: "Helm version must be >=3.0.0,<4.0.0, current version is {version}"
Some modules (i.e. helm_registry_auth) technically is compatible with Helm v4, but validation was added to all helm modules.
Partially coauthored by GitHub Copilot with Claude Sonnet 4 model.
Addresses issue #1038
Reviewed-by: GomathiselviS <gomathiselvi@gmail.com>
Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Alina Buzachis
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
(cherry picked from commit 13791ec7bf)
rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED
This commit is contained in:
committed by
GomathiselviS
parent
acc168e61f
commit
0f6b4c873a
@@ -43,15 +43,21 @@ class TestDependencyUpdateWithoutChartRepoUrlOption(unittest.TestCase):
|
||||
def test_dependency_update_option_not_defined(self):
|
||||
set_module_args({"chart_ref": "/tmp/path"})
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm_template.main()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm template /tmp/path", environ_update={}, data=None
|
||||
# Check the last call was the actual helm template command
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm template /tmp/path"
|
||||
)
|
||||
assert result.exception.args[0]["command"] == "/usr/bin/helm template /tmp/path"
|
||||
|
||||
@@ -64,17 +70,21 @@ class TestDependencyUpdateWithoutChartRepoUrlOption(unittest.TestCase):
|
||||
}
|
||||
)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm_template.main()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm template test --repo=https://charts.com/test",
|
||||
environ_update={},
|
||||
data=None,
|
||||
# Check the last call was the actual helm template command
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm template test --repo=https://charts.com/test"
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
@@ -86,17 +96,21 @@ class TestDependencyUpdateWithoutChartRepoUrlOption(unittest.TestCase):
|
||||
{"chart_ref": "https://charts/example.tgz", "dependency_update": True}
|
||||
)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm_template.main()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm template https://charts/example.tgz --dependency-update",
|
||||
environ_update={},
|
||||
data=None,
|
||||
# Check the last call was the actual helm template command
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm template https://charts/example.tgz --dependency-update"
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
|
||||
@@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from ansible.module_utils import basic
|
||||
from ansible_collections.kubernetes.core.plugins.modules import helm
|
||||
@@ -77,18 +77,22 @@ class TestDependencyUpdateWithoutChartRepoUrlOption(unittest.TestCase):
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
|
||||
helm.run_dep_update = MagicMock()
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm.main()
|
||||
helm.run_dep_update.assert_not_called()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm upgrade -i --reset-values test '/tmp/path'",
|
||||
environ_update={"HELM_NAMESPACE": "test"},
|
||||
data=None,
|
||||
# Check the last call (actual helm command, after version check)
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm upgrade -i --reset-values test '/tmp/path'"
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
@@ -108,18 +112,22 @@ class TestDependencyUpdateWithoutChartRepoUrlOption(unittest.TestCase):
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
|
||||
helm.run_dep_update = MagicMock()
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm.main()
|
||||
helm.run_dep_update.assert_not_called()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm upgrade -i --reset-values test '/tmp/path'",
|
||||
environ_update={"HELM_NAMESPACE": "test"},
|
||||
data=None,
|
||||
# Check the last call (actual helm command, after version check)
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm upgrade -i --reset-values test '/tmp/path'"
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
@@ -139,19 +147,23 @@ class TestDependencyUpdateWithoutChartRepoUrlOption(unittest.TestCase):
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_with_dep)
|
||||
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = 0, "configuration updated", ""
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with patch.object(basic.AnsibleModule, "warn") as mock_warn:
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm.main()
|
||||
mock_warn.assert_not_called()
|
||||
mock_run_command.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
"/usr/bin/helm upgrade -i --reset-values test '/tmp/path'",
|
||||
environ_update={"HELM_NAMESPACE": "test"},
|
||||
data=None,
|
||||
)
|
||||
]
|
||||
# Check calls include the actual helm command (after version check)
|
||||
assert any(
|
||||
"/usr/bin/helm upgrade -i --reset-values test '/tmp/path'" in str(call)
|
||||
for call in mock_run_command.call_args_list
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
@@ -170,23 +182,23 @@ class TestDependencyUpdateWithoutChartRepoUrlOption(unittest.TestCase):
|
||||
helm.get_release_status = MagicMock(return_value=None)
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with patch.object(basic.AnsibleModule, "warn") as mock_warn:
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm.main()
|
||||
mock_warn.assert_called_once()
|
||||
mock_run_command.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
"/usr/bin/helm upgrade -i --reset-values test '/tmp/path'",
|
||||
environ_update={"HELM_NAMESPACE": "test"},
|
||||
data=None,
|
||||
)
|
||||
]
|
||||
# Check calls include the actual helm command (after version check)
|
||||
assert any(
|
||||
"/usr/bin/helm upgrade -i --reset-values test '/tmp/path'" in str(call)
|
||||
for call in mock_run_command.call_args_list
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
@@ -245,17 +257,21 @@ class TestDependencyUpdateWithChartRepoUrlOption(unittest.TestCase):
|
||||
helm.get_release_status = MagicMock(return_value=None)
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm.main()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test 'chart1'",
|
||||
environ_update={"HELM_NAMESPACE": "test"},
|
||||
data=None,
|
||||
# Check the last call (actual helm command, after version check)
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test 'chart1'"
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
@@ -275,17 +291,21 @@ class TestDependencyUpdateWithChartRepoUrlOption(unittest.TestCase):
|
||||
helm.get_release_status = MagicMock(return_value=None)
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm.main()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test 'chart1'",
|
||||
environ_update={"HELM_NAMESPACE": "test"},
|
||||
data=None,
|
||||
# Check the last call (actual helm command, after version check)
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test 'chart1'"
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
@@ -305,11 +325,15 @@ class TestDependencyUpdateWithChartRepoUrlOption(unittest.TestCase):
|
||||
helm.get_release_status = MagicMock(return_value=None)
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_with_dep)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleFailJson) as result:
|
||||
helm.main()
|
||||
# mock_run_command.assert_called_once_with('/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test chart1',
|
||||
@@ -334,17 +358,21 @@ class TestDependencyUpdateWithChartRepoUrlOption(unittest.TestCase):
|
||||
helm.get_release_status = MagicMock(return_value=None)
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm.main()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm --repo=http://repo.example/charts install --dependency-update --replace test 'chart1'",
|
||||
environ_update={"HELM_NAMESPACE": "test"},
|
||||
data=None,
|
||||
# Check the last call (actual helm command, after version check)
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm --repo=http://repo.example/charts install --dependency-update --replace test 'chart1'"
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
@@ -402,17 +430,21 @@ class TestDependencyUpdateWithChartRefIsUrl(unittest.TestCase):
|
||||
helm.get_release_status = MagicMock(return_value=None)
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm.main()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm upgrade -i --reset-values test 'http://repo.example/charts/application.tgz'",
|
||||
environ_update={"HELM_NAMESPACE": "test"},
|
||||
data=None,
|
||||
# Check the last call (actual helm command, after version check)
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm upgrade -i --reset-values test 'http://repo.example/charts/application.tgz'"
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
@@ -431,17 +463,21 @@ class TestDependencyUpdateWithChartRefIsUrl(unittest.TestCase):
|
||||
helm.get_release_status = MagicMock(return_value=None)
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm.main()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm upgrade -i --reset-values test 'http://repo.example/charts/application.tgz'",
|
||||
environ_update={"HELM_NAMESPACE": "test"},
|
||||
data=None,
|
||||
# Check the last call (actual helm command, after version check)
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm upgrade -i --reset-values test 'http://repo.example/charts/application.tgz'"
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
@@ -460,11 +496,15 @@ class TestDependencyUpdateWithChartRefIsUrl(unittest.TestCase):
|
||||
helm.get_release_status = MagicMock(return_value=None)
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_with_dep)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleFailJson) as result:
|
||||
helm.main()
|
||||
# mock_run_command.assert_called_once_with('/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test chart1',
|
||||
@@ -488,17 +528,21 @@ class TestDependencyUpdateWithChartRefIsUrl(unittest.TestCase):
|
||||
helm.get_release_status = MagicMock(return_value=None)
|
||||
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
|
||||
with patch.object(basic.AnsibleModule, "run_command") as mock_run_command:
|
||||
mock_run_command.return_value = (
|
||||
0,
|
||||
"configuration updated",
|
||||
"",
|
||||
) # successful execution
|
||||
# Mock responses: first call is helm version, second is the actual command
|
||||
mock_run_command.side_effect = [
|
||||
(
|
||||
0,
|
||||
'version.BuildInfo{Version:"v3.10.0", GitCommit:"", GoVersion:"go1.18"}',
|
||||
"",
|
||||
),
|
||||
(0, "configuration updated", ""),
|
||||
]
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
helm.main()
|
||||
mock_run_command.assert_called_once_with(
|
||||
"/usr/bin/helm install --dependency-update --replace test 'http://repo.example/charts/application.tgz'",
|
||||
environ_update={"HELM_NAMESPACE": "test"},
|
||||
data=None,
|
||||
# Check the last call (actual helm command, after version check)
|
||||
assert (
|
||||
mock_run_command.call_args_list[-1][0][0]
|
||||
== "/usr/bin/helm install --dependency-update --replace test 'http://repo.example/charts/application.tgz'"
|
||||
)
|
||||
assert (
|
||||
result.exception.args[0]["command"]
|
||||
|
||||
Reference in New Issue
Block a user