From a3f2438e9d6aaa89098e54163613cdd5e5d01093 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:03:02 +0000 Subject: [PATCH] Ensure compatibility with Helm v4 for the collection (#1090) (#1096) This is a backport of PR #1090 as merged into main (e6076e5). SUMMARY Ensure compatibility with Helm v4 for modules helm_plugin and helm_plugin_info Partially addresses #1038 ISSUE TYPE Feature Pull Request COMPONENT NAME helm_plugin helm_plugin_info helm_info helm_pull helm_registry_auth helm helm_template Reviewed-by: Bikouo Aubin Reviewed-by: Matthew Johnson --- .gitignore | 1 + README.md | 2 +- ...pport-helm-v4-for-helm-plugin-modules.yaml | 9 + plugins/module_utils/helm.py | 46 +++-- plugins/modules/helm.py | 54 +++-- plugins/modules/helm_info.py | 4 +- plugins/modules/helm_plugin.py | 52 +++-- plugins/modules/helm_plugin_info.py | 25 +-- plugins/modules/helm_pull.py | 4 +- plugins/modules/helm_registry_auth.py | 42 +++- plugins/modules/helm_repository.py | 4 +- plugins/modules/helm_template.py | 6 +- tests/integration/targets/helm/aliases | 5 +- .../targets/helm/defaults/main.yml | 32 +-- .../targets/helm/library/helm_test_pending.py | 18 +- tests/integration/targets/helm/meta/main.yml | 2 - tests/integration/targets/helm/playbook.yaml | 7 - tests/integration/targets/helm/runme.sh | 5 - .../targets/helm/tasks/install.yml | 15 -- tests/integration/targets/helm/tasks/main.yml | 26 ++- .../targets/helm/tasks/run_test.yml | 42 ++-- .../helm/tasks/test_helm_not_installed.yml | 2 +- .../test_helm_reset_then_reuse_values.yml | 23 +++ .../helm/tasks/test_helm_reuse_values.yml | 15 ++ .../helm/tasks/test_helm_take_ownership.yml | 20 ++ .../helm/tasks/test_helm_uninstall.yml | 32 +-- .../targets/helm/tasks/test_helm_version.yml | 47 ----- .../targets/helm/tasks/tests_chart.yml | 51 +++-- .../tasks/tests_chart/from_repository.yml | 48 +++-- .../helm/tasks/tests_chart/from_url.yml | 9 +- .../targets/helm_diff/tasks/main.yml | 1 + .../tasks/reset_then_reuse_values.yml | 31 +-- .../targets/helm_kubeconfig/defaults/main.yml | 5 +- .../targets/helm_kubeconfig/meta/main.yml | 4 - .../tasks/from_kubeconfig_with_cacert.yml | 2 +- .../from_kubeconfig_with_validate_certs.yml | 2 +- .../targets/helm_kubeconfig/tasks/main.yml | 22 +- .../helm_kubeconfig/tasks/run_tests.yml | 15 ++ .../helm_kubeconfig/tasks/tests_helm_auth.yml | 11 +- .../targets/helm_plain_http/.gitignore | 1 + .../targets/helm_plain_http/inventory.ini | 3 +- .../helm_plain_http/playbooks/play.yaml | 3 +- .../helm_plain_http/playbooks/tasks/test.yaml | 4 - .../targets/helm_plugin/meta/main.yml | 3 - .../targets/helm_plugin/tasks/main.yml | 171 +-------------- .../targets/helm_plugin/tasks/run_tests.yml | 195 ++++++++++++++++++ .../targets/helm_pull/tasks/main.yml | 44 +--- .../helm_registry_auth/defaults/main.yaml | 6 +- .../targets/helm_registry_auth/meta/main.yml | 3 - .../helm_registry_auth/tasks/main.yaml | 182 ---------------- .../targets/helm_registry_auth/tasks/main.yml | 60 ++++++ .../helm_registry_auth/tasks/run_tests.yml | 108 ++++++++++ .../targets/helm_repository/aliases | 7 +- .../targets/helm_repository/defaults/main.yml | 4 +- .../targets/helm_repository/meta/main.yml | 5 - .../targets/helm_repository/tasks/main.yml | 105 +--------- .../helm_repository/tasks/run_tests.yml | 105 ++++++++++ .../targets/helm_set_values/defaults/main.yml | 4 +- .../targets/helm_set_values/tasks/main.yml | 77 +------ .../helm_set_values/tasks/run_tests.yml | 68 ++++++ .../integration/targets/helm_v3_15_4/aliases | 4 + .../targets/helm_v3_15_4/inventory.ini | 2 + .../targets/helm_v3_15_4/play.yaml | 11 + .../integration/targets/helm_v3_15_4/runme.sh | 4 + .../integration/targets/helm_v3_16_0/aliases | 4 + .../targets/helm_v3_16_0/inventory.ini | 2 + .../targets/helm_v3_16_0/play.yaml | 11 + .../integration/targets/helm_v3_16_0/runme.sh | 4 + .../integration/targets/helm_v3_17_0/aliases | 4 + .../targets/helm_v3_17_0/inventory.ini | 2 + .../targets/helm_v3_17_0/play.yaml | 11 + .../integration/targets/helm_v3_17_0/runme.sh | 4 + tests/integration/targets/helm_v4_0_0/aliases | 4 + .../targets/helm_v4_0_0/inventory.ini | 2 + .../integration/targets/helm_v4_0_0/play.yaml | 11 + .../integration/targets/helm_v4_0_0/runme.sh | 4 + .../targets/install_helm/defaults/main.yml | 2 +- .../targets/install_helm/tasks/main.yml | 33 ++- tests/sanity/ignore-2.16.txt | 1 - tests/sanity/ignore-2.17.txt | 1 - tests/sanity/ignore-2.18.txt | 1 - tests/sanity/ignore-2.19.txt | 1 - tests/sanity/ignore-2.20.txt | 1 - tests/sanity/ignore-2.21.txt | 1 - .../unit/module_utils/{ => helm}/test_helm.py | 12 +- .../helm/test_helm_plugin_list.py | 55 +++++ 86 files changed, 1161 insertions(+), 930 deletions(-) create mode 100644 changelogs/fragments/20260213-support-helm-v4-for-helm-plugin-modules.yaml delete mode 100644 tests/integration/targets/helm/playbook.yaml delete mode 100755 tests/integration/targets/helm/runme.sh delete mode 100644 tests/integration/targets/helm/tasks/install.yml delete mode 100644 tests/integration/targets/helm/tasks/test_helm_version.yml delete mode 100644 tests/integration/targets/helm_kubeconfig/meta/main.yml create mode 100644 tests/integration/targets/helm_kubeconfig/tasks/run_tests.yml create mode 100644 tests/integration/targets/helm_plain_http/.gitignore delete mode 100644 tests/integration/targets/helm_plugin/meta/main.yml create mode 100644 tests/integration/targets/helm_plugin/tasks/run_tests.yml delete mode 100644 tests/integration/targets/helm_registry_auth/meta/main.yml delete mode 100644 tests/integration/targets/helm_registry_auth/tasks/main.yaml create mode 100644 tests/integration/targets/helm_registry_auth/tasks/main.yml create mode 100644 tests/integration/targets/helm_registry_auth/tasks/run_tests.yml delete mode 100644 tests/integration/targets/helm_repository/meta/main.yml create mode 100644 tests/integration/targets/helm_repository/tasks/run_tests.yml create mode 100644 tests/integration/targets/helm_set_values/tasks/run_tests.yml create mode 100644 tests/integration/targets/helm_v3_15_4/aliases create mode 100644 tests/integration/targets/helm_v3_15_4/inventory.ini create mode 100644 tests/integration/targets/helm_v3_15_4/play.yaml create mode 100755 tests/integration/targets/helm_v3_15_4/runme.sh create mode 100644 tests/integration/targets/helm_v3_16_0/aliases create mode 100644 tests/integration/targets/helm_v3_16_0/inventory.ini create mode 100644 tests/integration/targets/helm_v3_16_0/play.yaml create mode 100755 tests/integration/targets/helm_v3_16_0/runme.sh create mode 100644 tests/integration/targets/helm_v3_17_0/aliases create mode 100644 tests/integration/targets/helm_v3_17_0/inventory.ini create mode 100644 tests/integration/targets/helm_v3_17_0/play.yaml create mode 100755 tests/integration/targets/helm_v3_17_0/runme.sh create mode 100644 tests/integration/targets/helm_v4_0_0/aliases create mode 100644 tests/integration/targets/helm_v4_0_0/inventory.ini create mode 100644 tests/integration/targets/helm_v4_0_0/play.yaml create mode 100755 tests/integration/targets/helm_v4_0_0/runme.sh rename tests/unit/module_utils/{ => helm}/test_helm.py (98%) create mode 100644 tests/unit/module_utils/helm/test_helm_plugin_list.py diff --git a/.gitignore b/.gitignore index 78177c01..6210f3b4 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ tests/integration/cloud-config-* # Helm charts tests/integration/*-chart-*.tgz +tests/integration/targets/*/*.tgz # ansible-test generated file tests/integration/inventory diff --git a/README.md b/README.md index 0a797b1d..010d9b8d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ PEP440 is the schema used to describe the versions of Ansible. ### Helm Version Compatibility -Helm modules in this collection are compatible with Helm v3.x and are not yet compatible with Helm v4. Individual modules and their parameters may support a more specific range of Helm versions. +This collection supports Helm v3.x and newer. Please note that specific modules or certain parameters may have additional version requirements. ### Python Support diff --git a/changelogs/fragments/20260213-support-helm-v4-for-helm-plugin-modules.yaml b/changelogs/fragments/20260213-support-helm-v4-for-helm-plugin-modules.yaml new file mode 100644 index 00000000..a590dbb3 --- /dev/null +++ b/changelogs/fragments/20260213-support-helm-v4-for-helm-plugin-modules.yaml @@ -0,0 +1,9 @@ +--- +minor_changes: + - helm_plugin - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038). + - helm_plugin_info - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038). + - helm_info - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038). + - helm_pull - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038). + - helm_registry_auth - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038). + - helm_registry_auth - add new option plain_http to allow insecure http connection when running ``helm registry login`` (https://github.com/ansible-collections/kubernetes.core/pull/1090). + - helm_repository - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038). diff --git a/plugins/module_utils/helm.py b/plugins/module_utils/helm.py index 3291e45a..7bac93ea 100644 --- a/plugins/module_utils/helm.py +++ b/plugins/module_utils/helm.py @@ -40,16 +40,20 @@ def parse_helm_plugin_list(output=None): if not output: return ret + parsing_grammar = None for line in output: if line.startswith("NAME"): + parsing_grammar = [s.strip().lower() for s in line.split("\t")] continue - name, version, description = line.split("\t", 3) - name = name.strip() - version = version.strip() - description = description.strip() - if name == "": + if parsing_grammar is None: continue - ret.append((name, version, description)) + plugin = { + parsing_grammar[i]: v.strip() + for i, v in enumerate(line.split("\t", len(parsing_grammar))) + } + if plugin["name"] == "": + continue + ret.append(plugin) return ret @@ -202,21 +206,35 @@ class AnsibleHelmModule(object): return m.group(1) return None - def validate_helm_version(self): + def is_helm_v4(self): + helm_version = self.get_helm_version() + if helm_version is None: + return False + return LooseVersion(helm_version) >= LooseVersion("4.0.0") + + def is_helm_version_compatible_with_helm_diff(self, helm_diff_version): """ - Validate that Helm version is >=3.0.0 and <4.0.0. - Helm 4 is not yet supported. + Return true if the helm version is compatible with the helm diff version + Helm v4 requires helm diff v3.14.0 + """ + if not helm_diff_version: + return False + if self.is_helm_v4(): + return LooseVersion(helm_diff_version) >= LooseVersion("3.14.0") + return True + + def validate_helm_version(self, version="3.0.0"): + """ + Validate that Helm version is >= version (default version=3.0.0). """ helm_version = self.get_helm_version() if helm_version is None: self.fail_json(msg="Unable to determine Helm version") - if (LooseVersion(helm_version) < LooseVersion("3.0.0")) or ( - LooseVersion(helm_version) >= LooseVersion("4.0.0") - ): + if LooseVersion(helm_version) < LooseVersion(version): self.fail_json( - msg="Helm version must be >=3.0.0,<4.0.0, current version is {0}".format( - helm_version + msg="Helm version must be >= {0}, current version is {1}".format( + version, helm_version ) ) diff --git a/plugins/modules/helm.py b/plugins/modules/helm.py index e3e02559..627d527a 100644 --- a/plugins/modules/helm.py +++ b/plugins/modules/helm.py @@ -21,7 +21,7 @@ author: - Matthieu Diehr (@d-matt) requirements: - - "helm (https://github.com/helm/helm/releases)" + - "helm >= 3.0.0 (https://github.com/helm/helm/releases)" - "yaml (https://pypi.org/project/PyYAML/)" description: @@ -500,9 +500,13 @@ def get_release_status(module, release_name, all_status=False): "--filter", release_name, ] - if all_status: + if all_status and not module.is_helm_v4(): + # --all has been removed from `helm list` command on helm v4 list_command.append("--all") - + elif not all_status: + # The default behavior to display only deployed releases has been removed from + # Helm v4 + list_command.append("--deployed") rc, out, err = module.run_helm_command(list_command) release = get_release(yaml.safe_load(out), release_name) @@ -739,8 +743,8 @@ def get_plugin_version(plugin): return None for line in out: - if line[0] == plugin: - return line[1] + if line["name"] == plugin: + return line["version"] return None @@ -928,7 +932,7 @@ def main(): if not IMP_YAML: module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR) - # Validate Helm version >=3.0.0,<4.0.0 + # Validate Helm version >=3.0.0 module.validate_helm_version() changed = False @@ -1010,8 +1014,7 @@ def main(): if wait: helm_version = module.get_helm_version() if LooseVersion(helm_version) < LooseVersion("3.7.0"): - opt_result["warnings"] = [] - opt_result["warnings"].append( + module.warn( "helm uninstall support option --wait for helm release >= 3.7.0" ) wait = False @@ -1099,14 +1102,21 @@ def main(): else: helm_diff_version = get_plugin_version("diff") - if helm_diff_version and ( - not chart_repo_url - or ( - chart_repo_url - and LooseVersion(helm_diff_version) >= LooseVersion("3.4.1") + helm_version_compatible = module.is_helm_version_compatible_with_helm_diff( + helm_diff_version + ) + if ( + helm_diff_version + and helm_version_compatible + and ( + not chart_repo_url + or ( + chart_repo_url + and LooseVersion(helm_diff_version) >= LooseVersion("3.4.1") + ) ) ): - (would_change, prepared) = helmdiff_check( + would_change, prepared = helmdiff_check( module, release_name, chart_ref, @@ -1127,10 +1137,18 @@ def main(): if would_change and module._diff: opt_result["diff"] = {"prepared": prepared} else: - module.warn( - "The default idempotency check can fail to report changes in certain cases. " - "Install helm diff >= 3.4.1 for better results." - ) + if helm_diff_version and not helm_version_compatible: + module.warn( + "Idempotency checks are currently disabled due to a version mismatch." + f" Helm version {module.get_helm_version()} requires helm-diff >= 3.14.0," + f" but the environment is currently running {helm_diff_version}." + " Please align the plugin versions to restore standard behavior." + ) + else: + module.warn( + "The default idempotency check can fail to report changes in certain cases. " + "Install helm diff >= 3.4.1 for better results." + ) would_change = default_check( release_status, chart_info, release_values, values_files ) diff --git a/plugins/modules/helm_info.py b/plugins/modules/helm_info.py index 17d3ebb8..048d8ca6 100644 --- a/plugins/modules/helm_info.py +++ b/plugins/modules/helm_info.py @@ -20,7 +20,7 @@ author: - Lucas Boisserie (@LucasBoisserie) requirements: - - "helm (https://github.com/helm/helm/releases)" + - "helm >= 3.0.0 (https://github.com/helm/helm/releases)" - "yaml (https://pypi.org/project/PyYAML/)" description: @@ -245,7 +245,7 @@ def main(): if not IMP_YAML: module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR) - # Validate Helm version >=3.0.0,<4.0.0 + # Validate Helm version >=3.0.0 module.validate_helm_version() release_name = module.params.get("release_name") diff --git a/plugins/modules/helm_plugin.py b/plugins/modules/helm_plugin.py index c4d05308..2ebaf126 100644 --- a/plugins/modules/helm_plugin.py +++ b/plugins/modules/helm_plugin.py @@ -16,7 +16,7 @@ version_added: 1.0.0 author: - Abhijeet Kasurde (@Akasurde) requirements: - - "helm (https://github.com/helm/helm/releases)" + - "helm >= 3.0.0 (https://github.com/helm/helm/releases)" description: - Manages Helm plugins. options: @@ -48,6 +48,14 @@ options: required: false type: str version_added: 2.3.0 + verify: + description: + - Verify the plugin signature before installing. + - This option requires helm version >= 4.0.0 + - Used with I(state=present). + type: bool + default: true + version_added: 6.4.0 extends_documentation_fragment: - kubernetes.core.helm_common_options """ @@ -118,6 +126,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common i HELM_AUTH_ARG_SPEC, HELM_AUTH_MUTUALLY_EXCLUSIVE, ) +from ansible_collections.kubernetes.core.plugins.module_utils.version import ( + LooseVersion, +) def argument_spec(): @@ -138,6 +149,10 @@ def argument_spec(): default="present", choices=["present", "absent", "latest"], ), + verify=dict( + type="bool", + default=True, + ), ) ) return arg_spec @@ -161,7 +176,7 @@ def main(): mutually_exclusive=mutually_exclusive(), ) - # Validate Helm version >=3.0.0,<4.0.0 + # Validate helm version >= 3.0.0 module.validate_helm_version() state = module.params.get("state") @@ -171,8 +186,19 @@ def main(): if state == "present": helm_cmd_common += " install %s" % module.params.get("plugin_path") plugin_version = module.params.get("plugin_version") + verify = module.params.get("verify") if plugin_version is not None: helm_cmd_common += " --version=%s" % plugin_version + if not verify: + helm_version = module.get_helm_version() + if LooseVersion(helm_version) < LooseVersion("4.0.0"): + module.warn( + "verify parameter requires helm >= 4.0.0, current version is {0}".format( + helm_version + ) + ) + else: + helm_cmd_common += " --verify=false" if not module.check_mode: rc, out, err = module.run_helm_command( helm_cmd_common, fails_on_error=False @@ -211,9 +237,9 @@ def main(): elif state == "absent": plugin_name = module.params.get("plugin_name") rc, output, err, command = module.get_helm_plugin_list() - out = parse_helm_plugin_list(output=output.splitlines()) + plugins = parse_helm_plugin_list(output=output.splitlines()) - if not out: + if not plugins: module.exit_json( failed=False, changed=False, @@ -224,12 +250,7 @@ def main(): rc=rc, ) - found = False - for line in out: - if line[0] == plugin_name: - found = True - break - if not found: + if all(plugin["name"] != plugin_name for plugin in plugins): module.exit_json( failed=False, changed=False, @@ -267,9 +288,9 @@ def main(): elif state == "latest": plugin_name = module.params.get("plugin_name") rc, output, err, command = module.get_helm_plugin_list() - out = parse_helm_plugin_list(output=output.splitlines()) + plugins = parse_helm_plugin_list(output=output.splitlines()) - if not out: + if not plugins: module.exit_json( failed=False, changed=False, @@ -280,12 +301,7 @@ def main(): rc=rc, ) - found = False - for line in out: - if line[0] == plugin_name: - found = True - break - if not found: + if all(plugin["name"] != plugin_name for plugin in plugins): module.exit_json( failed=False, changed=False, diff --git a/plugins/modules/helm_plugin_info.py b/plugins/modules/helm_plugin_info.py index 026ebfd8..59aa07ff 100644 --- a/plugins/modules/helm_plugin_info.py +++ b/plugins/modules/helm_plugin_info.py @@ -16,7 +16,7 @@ version_added: 1.0.0 author: - Abhijeet Kasurde (@Akasurde) requirements: - - "helm (https://github.com/helm/helm/releases)" + - "helm >= 3.0.0 (https://github.com/helm/helm/releases)" description: - Gather information about Helm plugins installed in namespace. options: @@ -98,29 +98,16 @@ def main(): supports_check_mode=True, ) - # Validate Helm version >=3.0.0,<4.0.0 + # Validate helm version >= 3.0.0 module.validate_helm_version() plugin_name = module.params.get("plugin_name") - plugin_list = [] - rc, output, err, command = module.get_helm_plugin_list() - out = parse_helm_plugin_list(output=output.splitlines()) - - for line in out: - if plugin_name is None: - plugin_list.append( - {"name": line[0], "version": line[1], "description": line[2]} - ) - continue - - if plugin_name == line[0]: - plugin_list.append( - {"name": line[0], "version": line[1], "description": line[2]} - ) - break + plugins = parse_helm_plugin_list(output=output.splitlines()) + if plugin_name is not None: + plugins = [plugin for plugin in plugins if plugin.get("name") == plugin_name] module.exit_json( changed=True, @@ -128,7 +115,7 @@ def main(): stdout=output, stderr=err, rc=rc, - plugin_list=plugin_list, + plugin_list=plugins, ) diff --git a/plugins/modules/helm_pull.py b/plugins/modules/helm_pull.py index 123ea31b..da4d4960 100644 --- a/plugins/modules/helm_pull.py +++ b/plugins/modules/helm_pull.py @@ -21,7 +21,7 @@ description: - There are options for unpacking the chart after download. requirements: - - "helm >= 3.0, <4.0.0 (https://github.com/helm/helm/releases)" + - "helm >= 3.0.0 (https://github.com/helm/helm/releases)" options: chart_ref: @@ -372,7 +372,7 @@ def main(): mutually_exclusive=[("chart_version", "chart_devel")], ) - # Validate Helm version >=3.0.0,<4.0.0 + # Validate Helm version >=3.0.0 module.validate_helm_version() helm_version = module.get_helm_version() diff --git a/plugins/modules/helm_registry_auth.py b/plugins/modules/helm_registry_auth.py index 107a9a7c..6346e571 100644 --- a/plugins/modules/helm_registry_auth.py +++ b/plugins/modules/helm_registry_auth.py @@ -20,7 +20,7 @@ author: - Yuriy Novostavskiy (@yurnov) requirements: - - "helm (https://github.com/helm/helm/releases) >= 3.8.0, <4.0.0" + - "helm (https://github.com/helm/helm/releases) >= 3.8.0" description: - Helm registry authentication module allows you to login C(helm registry login) and logout C(helm registry logout) from a Helm registry. @@ -75,6 +75,14 @@ options: - Path to the CA certificate SSL file for verify registry server certificate. required: false type: path + plain_http: + description: + - Use insecure HTTP connections for C(helm registry login). + - Requires Helm >= 3.18.0 + required: false + type: bool + default: False + version_added: 6.4.0 binary_path: description: - The path of a helm binary to use. @@ -148,6 +156,7 @@ def arg_spec(): key_file=dict(type="path", required=False), cert_file=dict(type="path", required=False), ca_file=dict(type="path", required=False), + plain_http=dict(type="bool", default=False), ) @@ -160,6 +169,7 @@ def login( key_file, cert_file, ca_file, + plain_http, ): login_command = command + " registry login " + host @@ -177,6 +187,8 @@ def login( if ca_file is not None: login_command += " --ca-file=" + ca_file + if plain_http: + login_command += " --plain-http" return login_command @@ -194,8 +206,8 @@ def main(): supports_check_mode=True, ) - # Validate Helm version >=3.0.0,<4.0.0 - module.validate_helm_version() + # Validate Helm version >=3.8.0 + module.validate_helm_version(version="3.8.0") changed = False @@ -207,6 +219,19 @@ def main(): key_file = module.params.get("key_file") cert_file = module.params.get("cert_file") ca_file = module.params.get("ca_file") + plain_http = module.params.get("plain_http") + + helm_version = module.get_helm_version() + + if plain_http: + if LooseVersion(helm_version) < LooseVersion("3.18.0"): + module.warn( + "plain_http option requires helm >= 3.18.0, current version is {0}".format( + helm_version + ) + ) + # reset option + plain_http = False helm_cmd = module.get_helm_binary() @@ -215,7 +240,15 @@ def main(): changed = True elif state == "present": helm_cmd = login( - helm_cmd, host, insecure, username, password, key_file, cert_file, ca_file + helm_cmd, + host, + insecure, + username, + password, + key_file, + cert_file, + ca_file, + plain_http, ) changed = True @@ -238,7 +271,6 @@ def main(): command=helm_cmd, ) - helm_version = module.get_helm_version() if LooseVersion(helm_version) >= LooseVersion("3.18.0") and state == "absent": # https://github.com/ansible-collections/kubernetes.core/issues/944 module.warn( diff --git a/plugins/modules/helm_repository.py b/plugins/modules/helm_repository.py index ce0d1a36..fabfae63 100644 --- a/plugins/modules/helm_repository.py +++ b/plugins/modules/helm_repository.py @@ -20,7 +20,7 @@ author: - Lucas Boisserie (@LucasBoisserie) requirements: - - "helm (https://github.com/helm/helm/releases)" + - "helm >= 3.0.0 (https://github.com/helm/helm/releases)" - "yaml (https://pypi.org/project/PyYAML/)" description: @@ -295,7 +295,7 @@ def main(): if not IMP_YAML: module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR) - # Validate Helm version >=3.0.0,<4.0.0 + # Validate Helm version >= 3.0.0 module.validate_helm_version() changed = False diff --git a/plugins/modules/helm_template.py b/plugins/modules/helm_template.py index 315ca694..0c2a91ab 100644 --- a/plugins/modules/helm_template.py +++ b/plugins/modules/helm_template.py @@ -21,6 +21,10 @@ author: description: - Render chart templates to an output directory or as text of concatenated yaml documents. +requirements: + - "helm >= 3.0.0 (https://github.com/helm/helm/releases)" + - "yaml (https://pypi.org/project/PyYAML/)" + options: binary_path: description: @@ -347,7 +351,7 @@ def main(): if not IMP_YAML: module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR) - # Validate Helm version >=3.0.0,<4.0.0 + # Validate Helm version >=3.0.0 module.validate_helm_version() helm_cmd = module.get_helm_binary() diff --git a/tests/integration/targets/helm/aliases b/tests/integration/targets/helm/aliases index fdc6dfc7..6ef13eba 100644 --- a/tests/integration/targets/helm/aliases +++ b/tests/integration/targets/helm/aliases @@ -1,4 +1 @@ -time=100 -helm_info -helm_repository -helm_template +disabled # used by test targets helm_vX_XX_XX diff --git a/tests/integration/targets/helm/defaults/main.yml b/tests/integration/targets/helm/defaults/main.yml index 3aa59c0e..1c5317ec 100644 --- a/tests/integration/targets/helm/defaults/main.yml +++ b/tests/integration/targets/helm/defaults/main.yml @@ -9,25 +9,25 @@ chart_test_version: 4.2.4 chart_test_version_local_path: 1.32.0 chart_test_version_upgrade: 4.2.5 chart_test_version_upgrade_local_path: 1.33.0 -chart_test_repo: "https://kubernetes.github.io/ingress-nginx" +chart_test_repo: "https://stenic.github.io/k8status/" chart_test_git_repo: "http://github.com/helm/charts.git" chart_test_values: revisionHistoryLimit: 0 myValue: "changed" test_namespace: - - "helm-test-crds" - - "helm-uninstall" - - "helm-read-envvars" - - "helm-dep-update" - - "helm-local-path-001" - - "helm-local-path-002" - - "helm-local-path-003" - - "helm-from-repository" - - "helm-from-url" - - "helm-reuse-values" - - "helm-chart-with-space-into-name" - - "helm-reset-then-reuse-values" - - "helm-insecure" - - "helm-test-take-ownership" - - "helm-skip-schema-validation" + - "helm-test-crds-{{ helm_version | replace('.', '-') }}" + - "helm-uninstall-{{ helm_version | replace('.', '-') }}" + - "helm-read-envvars-{{ helm_version | replace('.', '-') }}" + - "helm-dep-update-{{ helm_version | replace('.', '-') }}" + - "helm-local-path-001-{{ helm_version | replace('.', '-') }}" + - "helm-local-path-002-{{ helm_version | replace('.', '-') }}" + - "helm-local-path-003-{{ helm_version | replace('.', '-') }}" + - "helm-from-repository-{{ helm_version | replace('.', '-') }}" + - "helm-from-url-{{ helm_version | replace('.', '-') }}" + - "helm-reuse-values-{{ helm_version | replace('.', '-') }}" + - "helm-chart-with-space-into-name-{{ helm_version | replace('.', '-') }}" + - "helm-reset-then-reuse-values-{{ helm_version | replace('.', '-') }}" + - "helm-insecure-{{ helm_version | replace('.', '-') }}" + - "helm-test-take-ownership-{{ helm_version | replace('.', '-') }}" + - "helm-skip-schema-validation-{{ helm_version | replace('.', '-') }}" diff --git a/tests/integration/targets/helm/library/helm_test_pending.py b/tests/integration/targets/helm/library/helm_test_pending.py index cc8bde7f..39f51cef 100644 --- a/tests/integration/targets/helm/library/helm_test_pending.py +++ b/tests/integration/targets/helm/library/helm_test_pending.py @@ -52,7 +52,9 @@ import json import subprocess import time -from ansible.module_utils.basic import AnsibleModule +from ansible_collections.kubernetes.core.plugins.module_utils.helm import ( + AnsibleHelmModule, +) class HelmReleaseNotFoundError(Exception): @@ -60,7 +62,9 @@ class HelmReleaseNotFoundError(Exception): super().__init__(message) -def create_pending_install_release(helm_binary, chart_ref, chart_release, namespace): +def create_pending_install_release( + module, helm_binary, chart_ref, chart_release, namespace +): # create pending-install release command = [ helm_binary, @@ -78,13 +82,14 @@ def create_pending_install_release(helm_binary, chart_ref, chart_release, namesp command = [ helm_binary, "list", - "--all", "--output=json", "--namespace", namespace, "--filter", chart_release, ] + if not module.is_helm_v4(): + command.append("--all") cmd = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = cmd.communicate() @@ -92,11 +97,11 @@ def create_pending_install_release(helm_binary, chart_ref, chart_release, namesp if not data: error = "Release %s not found." % chart_release raise HelmReleaseNotFoundError(message=error) - return data[0]["status"] == "pending-install", data[0]["status"] + return data[0]["status"] in ("pending-install", "failed"), data[0]["status"] def main(): - module = AnsibleModule( + module = AnsibleHelmModule( argument_spec=dict( binary_path=dict(type="path", required=True), chart_ref=dict(type="str", required=True), @@ -106,6 +111,7 @@ def main(): ) params = dict( + module=module, helm_binary=module.params.get("binary_path"), chart_release=module.params.get("chart_release"), chart_ref=module.params.get("chart_ref"), @@ -116,7 +122,7 @@ def main(): result, status = create_pending_install_release(**params) if not result: module.fail_json( - msg="unable to create pending-install release, current status is %s" + msg="unable to create pending-install/failed release, current status is %s" % status ) module.exit_json(changed=True, msg="Release created with status '%s'" % status) diff --git a/tests/integration/targets/helm/meta/main.yml b/tests/integration/targets/helm/meta/main.yml index 79869fd3..d05af689 100644 --- a/tests/integration/targets/helm/meta/main.yml +++ b/tests/integration/targets/helm/meta/main.yml @@ -1,5 +1,3 @@ --- collections: - kubernetes.core -dependencies: - - remove_namespace diff --git a/tests/integration/targets/helm/playbook.yaml b/tests/integration/targets/helm/playbook.yaml deleted file mode 100644 index 92e2e48b..00000000 --- a/tests/integration/targets/helm/playbook.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -- connection: local - gather_facts: true - hosts: localhost - - roles: - - helm diff --git a/tests/integration/targets/helm/runme.sh b/tests/integration/targets/helm/runme.sh deleted file mode 100755 index 02ef1460..00000000 --- a/tests/integration/targets/helm/runme.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -set -eux -export ANSIBLE_CALLBACKS_ENABLED=profile_tasks -export ANSIBLE_ROLES_PATH=../ -ansible-playbook playbook.yaml "$@" \ No newline at end of file diff --git a/tests/integration/targets/helm/tasks/install.yml b/tests/integration/targets/helm/tasks/install.yml deleted file mode 100644 index 248925ee..00000000 --- a/tests/integration/targets/helm/tasks/install.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -- name: Init Helm folders - file: - path: /tmp/helm/ - state: directory - -- name: Unarchive Helm binary - unarchive: - src: 'https://get.helm.sh/{{ helm_archive_name | default(helm_default_archive_name) }}' - dest: /tmp/helm/ - remote_src: yes - retries: 10 - delay: 5 - register: result - until: result is not failed diff --git a/tests/integration/targets/helm/tasks/main.yml b/tests/integration/targets/helm/tasks/main.yml index 497bf575..3c8d3ff8 100644 --- a/tests/integration/targets/helm/tasks/main.yml +++ b/tests/integration/targets/helm/tasks/main.yml @@ -1,10 +1,22 @@ --- +- name: Ensure helm is not installed + file: + path: "{{ item }}" + state: absent + with_items: + - "/tmp/helm" + +- name: Check failed if helm is not installed + include_tasks: test_helm_not_installed.yml + +- name: Install Helm v4 + ansible.builtin.include_role: + name: install_helm + vars: + helm_version: v3.6.0 + +- name: Test helm uninstall + ansible.builtin.include_tasks: test_helm_uninstall.yml + - name: Run tests include_tasks: run_test.yml - loop_control: - loop_var: helm_version - with_items: - - "v3.15.4" - - "v3.16.0" - - "v3.17.0" - - "v4.0.0" diff --git a/tests/integration/targets/helm/tasks/run_test.yml b/tests/integration/targets/helm/tasks/run_test.yml index 01580a3e..54afbe1f 100644 --- a/tests/integration/targets/helm/tasks/run_test.yml +++ b/tests/integration/targets/helm/tasks/run_test.yml @@ -1,25 +1,19 @@ --- -- name: Ensure helm is not installed - file: - path: "{{ item }}" - state: absent - with_items: - - "/tmp/helm" - -- name: Check failed if helm is not installed - include_tasks: test_helm_not_installed.yml - - name: "Install {{ helm_version }}" include_role: name: install_helm -- name: Main helm tests with Helm v3 - when: helm_version != "v4.0.0" +- name: Main helm tests block: + - name: Install helm-diff plugin + helm_plugin: + binary_path: "{{ helm_binary }}" + plugin_path: https://github.com/databus23/helm-diff + plugin_version: "{{ helm_version is version('v4.0.0', '>=') | ternary('v3.14.0', 'v3.10.0') }}" + verify: false - name: "Ensure we honor the environment variables" include_tasks: test_read_envvars.yml - when: helm_version != "v4.0.0" - name: Deploy charts include_tasks: "tests_chart/{{ test_chart_type }}.yml" @@ -39,9 +33,6 @@ - name: test helm dependency update include_tasks: test_up_dep.yml - - name: Test helm uninstall - include_tasks: test_helm_uninstall.yml - - name: Test helm install with chart name containing space include_tasks: test_helm_with_space_into_chart_name.yml @@ -58,12 +49,15 @@ - name: Test helm skip_schema_validation include_tasks: test_skip_schema_validation.yml -- name: Test helm version - include_tasks: test_helm_version.yml + always: + - name: Remove helm-diff plugin + helm_plugin: + binary_path: "{{ helm_binary }}" + plugin_name: diff + state: absent + ignore_errors: true -- name: Clean helm install - file: - path: "{{ item }}" - state: absent - with_items: - - "/tmp/helm/" + - name: Clean helm install + ansible.builtin.file: + path: "/tmp/helm/" + state: absent diff --git a/tests/integration/targets/helm/tasks/test_helm_not_installed.yml b/tests/integration/targets/helm/tasks/test_helm_not_installed.yml index 27667a4d..02e1de52 100644 --- a/tests/integration/targets/helm/tasks/test_helm_not_installed.yml +++ b/tests/integration/targets/helm/tasks/test_helm_not_installed.yml @@ -5,7 +5,7 @@ name: test chart_ref: "{{ chart_test }}" namespace: "helm-test" - ignore_errors: yes + ignore_errors: true register: helm_missing_binary - name: Assert that helm is not installed diff --git a/tests/integration/targets/helm/tasks/test_helm_reset_then_reuse_values.yml b/tests/integration/targets/helm/tasks/test_helm_reset_then_reuse_values.yml index 13c67ac9..dab9d958 100644 --- a/tests/integration/targets/helm/tasks/test_helm_reset_then_reuse_values.yml +++ b/tests/integration/targets/helm/tasks/test_helm_reset_then_reuse_values.yml @@ -38,6 +38,28 @@ - '"--reset-then-reuse-values" not in install.command' - release_value["status"]["release_values"] == chart_release_values + # We need to provide the actual redis password otherwise the update command + # will fail with the following: + # Error: execution error at (redis/templates/replicas/application.yaml:55:35): + # PASSWORDS ERROR: You must provide your current passwords when upgrading the release. + # Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims. + # Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases + # 'global.redis.password' must not be empty, please add '--set global.redis.password=$REDIS_PASSWORD' to the command. To get the current value: + - name: Retrieve release password + kubernetes.core.k8s_info: + namespace: "{{ helm_namespace }}" + kind: Secret + name: test-redis + register: redis_secret + + - ansible.builtin.set_fact: + chart_reset_then_reuse_values: "{{ chart_reset_then_reuse_values | combine(redis_global_password) }}" + vars: + redis_global_password: + global: + redis: + password: "{{ redis_secret.resources.0.data['redis-password'] | b64decode }}" + - name: Upgrade chart using reset_then_reuse_values=true helm: binary_path: "{{ helm_binary }}" @@ -73,3 +95,4 @@ kind: Namespace name: "{{ helm_namespace }}" state: absent + wait: false diff --git a/tests/integration/targets/helm/tasks/test_helm_reuse_values.yml b/tests/integration/targets/helm/tasks/test_helm_reuse_values.yml index 9571c8ab..69cca3a8 100644 --- a/tests/integration/targets/helm/tasks/test_helm_reuse_values.yml +++ b/tests/integration/targets/helm/tasks/test_helm_reuse_values.yml @@ -38,6 +38,21 @@ - '"--reuse-values=True" not in install.command' - release_value["status"]["release_values"] == chart_release_values + - name: Retrieve release password + kubernetes.core.k8s_info: + namespace: "{{ helm_namespace }}" + kind: Secret + name: test-redis + register: redis_secret + + - ansible.builtin.set_fact: + chart_reuse_values: "{{ chart_reuse_values | combine(redis_global_password) }}" + vars: + redis_global_password: + global: + redis: + password: "{{ redis_secret.resources.0.data['redis-password'] | b64decode }}" + - name: Upgrade chart using reuse_values=true helm: binary_path: "{{ helm_binary }}" diff --git a/tests/integration/targets/helm/tasks/test_helm_take_ownership.yml b/tests/integration/targets/helm/tasks/test_helm_take_ownership.yml index 3e631c5b..84e2732a 100644 --- a/tests/integration/targets/helm/tasks/test_helm_take_ownership.yml +++ b/tests/integration/targets/helm/tasks/test_helm_take_ownership.yml @@ -19,6 +19,20 @@ - install is changed - '"--take-ownership" not in install.command' + # We need to provide the actual redis password otherwise the update command + # will fail with the following: + # Error: execution error at (redis/templates/replicas/application.yaml:55:35): + # PASSWORDS ERROR: You must provide your current passwords when upgrading the release. + # Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims. + # Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases + # 'global.redis.password' must not be empty, please add '--set global.redis.password=$REDIS_PASSWORD' to the command. To get the current value: + - name: Retrieve release password + kubernetes.core.k8s_info: + namespace: "{{ helm_namespace }}" + kind: Secret + name: test-take-ownership-redis + register: redis_secret + - name: Upgrade chart (take-onwership flag set) helm: binary_path: "{{ helm_binary }}" @@ -29,6 +43,9 @@ values: commonLabels: take-onwership: "set" + global: + redis: + password: "{{ redis_secret.resources.0.data['redis-password'] | b64decode }}" register: upgrade ignore_errors: true @@ -55,6 +72,9 @@ values: commonLabels: take-onwership: "not-set" + global: + redis: + password: "{{ redis_secret.resources.0.data['redis-password'] | b64decode }}" register: upgrade ignore_errors: true diff --git a/tests/integration/targets/helm/tasks/test_helm_uninstall.yml b/tests/integration/targets/helm/tasks/test_helm_uninstall.yml index 23e36fb0..16060e18 100644 --- a/tests/integration/targets/helm/tasks/test_helm_uninstall.yml +++ b/tests/integration/targets/helm/tasks/test_helm_uninstall.yml @@ -31,26 +31,18 @@ - name: assert warning has been raised assert: that: - - uninstall.warnings + - uninstall.warnings is defined + - '"helm uninstall support option --wait for helm release >= 3.7.0" in uninstall.warnings' - - name: Create temp directory - tempfile: - state: directory - suffix: .test - register: _result - - - set_fact: - helm_tmp_dir: "{{ _result.path }}" - - - name: Unarchive Helm binary - unarchive: - src: 'https://get.helm.sh/helm-v3.7.0-linux-amd64.tar.gz' - dest: "{{ helm_tmp_dir }}" - remote_src: yes + - name: Install Helm v4 + ansible.builtin.include_role: + name: install_helm + vars: + helm_version: v4.0.0 - name: Install chart helm: - binary_path: "{{ helm_tmp_dir }}/linux-amd64/helm" + binary_path: "{{ helm_binary }}" name: "{{ chart_name }}" chart_ref: "{{ chart_source }}" namespace: "{{ helm_namespace }}" @@ -59,7 +51,7 @@ - name: uninstall chart again using recent version helm: state: absent - binary_path: "{{ helm_tmp_dir }}/linux-amd64/helm" + binary_path: "{{ helm_binary }}" name: "{{ chart_name }}" namespace: "{{ helm_namespace }}" wait: yes @@ -96,12 +88,6 @@ - _info.status is undefined always: - - name: Delete temp directory - file: - path: "{{ helm_tmp_dir }}" - state: absent - ignore_errors: true - - name: Remove namespace k8s: kind: Namespace diff --git a/tests/integration/targets/helm/tasks/test_helm_version.yml b/tests/integration/targets/helm/tasks/test_helm_version.yml deleted file mode 100644 index 300927f9..00000000 --- a/tests/integration/targets/helm/tasks/test_helm_version.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- -- name: Test helm reuse_values - vars: - helm_namespace: "{{ test_namespace[14] }}" - chart_release_values: - replica: - replicaCount: 3 - master: - count: 1 - kind: Deployment - chart_reuse_values: - replica: - replicaCount: 1 - master: - count: 3 - block: - - name: Initial chart installation - helm: - binary_path: "{{ helm_binary }}" - chart_ref: oci://registry-1.docker.io/bitnamicharts/redis - release_name: test-redis - release_namespace: "{{ helm_namespace }}" - create_namespace: true - release_values: "{{ chart_release_values }}" - register: install - ignore_errors: true - when: helm_version == "v4.0.0" - - - name: Debug install result - debug: - var: install - when: helm_version == "v4.0.0" - - - name: Ensure helm installation was failed for v4.0.0 - assert: - that: - - install is failed - - "'Helm version must be >=3.0.0,<4.0.0' in install.msg" - when: helm_version == "v4.0.0" - - always: - - name: Remove helm namespace - k8s: - api_version: v1 - kind: Namespace - name: "{{ helm_namespace }}" - state: absent diff --git a/tests/integration/targets/helm/tasks/tests_chart.yml b/tests/integration/targets/helm/tasks/tests_chart.yml index ceac0e47..988365de 100644 --- a/tests/integration/targets/helm/tasks/tests_chart.yml +++ b/tests/integration/targets/helm/tasks/tests_chart.yml @@ -30,9 +30,9 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" - ignore_errors: yes + ignore_errors: true register: install_fail - name: "Assert that Install fail {{ chart_test }} from {{ source }}" @@ -46,7 +46,7 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" create_namespace: true register: install_check_mode @@ -64,17 +64,18 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" create_namespace: true register: install - - name: "Assert that {{ chart_test }} chart is installed from {{ source }}" + - name: "Assert that {{ chart_test }} chart version {{ chart_test_version }} is installed from {{ source }}" assert: that: - install is changed - install.status.chart == chart_test+"-"+chart_test_version - install.status.status | lower == 'deployed' + - install.status.release_values == {} - name: Check helm_info content helm_info: @@ -92,7 +93,7 @@ - deployed register: release_state_content_info - - name: "Assert that {{ chart_test }} is installed from {{ source }} with helm_info" + - name: "Assert that {{ chart_test }} chart version {{ chart_test_version }} is installed from {{ source }} with helm_info" assert: that: - content_info.status.chart == chart_test+"-"+chart_test_version @@ -104,9 +105,10 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" register: install + diff: true - name: Assert idempotency assert: @@ -120,7 +122,7 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" values: "{{ chart_test_values }}" register: install @@ -131,17 +133,18 @@ - install is changed - install.status.status | lower == 'deployed' - install.status.chart == chart_test+"-"+chart_test_version - - "install.status['release_values'].revisionHistoryLimit == 0" + - install.status['release_values'] == chart_test_values - name: Check idempotency after adding vars helm: binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" values: "{{ chart_test_values }}" register: install + diff: true - name: Assert idempotency after add vars assert: @@ -149,14 +152,14 @@ - install is not changed - install.status.status | lower == 'deployed' - install.status.chart == chart_test+"-"+chart_test_version - - "install.status['release_values'].revisionHistoryLimit == 0" + - install.status['release_values'] == chart_test_values - name: "Remove Vars to {{ chart_test }} from {{ source }}" helm: binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" register: install @@ -173,9 +176,10 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" register: install + diff: true - name: Assert idempotency after removing vars assert: @@ -190,7 +194,7 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source_upgrade | default(chart_source) }}" - chart_version: "{{ chart_source_version_upgrade | default(omit) }}" + chart_version: "{{ chart_test_version_upgrade }}" namespace: "{{ helm_namespace }}" register: install @@ -206,9 +210,10 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source_upgrade | default(chart_source) }}" - chart_version: "{{ chart_source_version_upgrade | default(omit) }}" + chart_version: "{{ chart_test_version_upgrade }}" namespace: "{{ helm_namespace }}" register: install + diff: true - name: Assert idempotency after upgrade assert: @@ -237,6 +242,7 @@ name: "{{ chart_release_name }}" namespace: "{{ helm_namespace }}" register: install + diff: true - name: Assert idempotency assert: @@ -249,7 +255,7 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_replaced_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" register: install @@ -277,7 +283,7 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_replaced_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" replace: True register: install @@ -305,7 +311,7 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" values_files: - "{{ role_path }}/files/values.yaml" @@ -324,7 +330,7 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" values_files: - "{{ role_path }}/files/values.yaml" @@ -346,7 +352,7 @@ helm_template: binary_path: "{{ helm_binary }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" output_dir: "{{ temp_dir }}" values_files: - "{{ role_path }}/files/values.yaml" @@ -372,7 +378,7 @@ helm_template: binary_path: "{{ helm_binary }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" disable_hook: True release_name: "myrelease" release_namespace: "myreleasenamespace" @@ -398,7 +404,7 @@ binary_path: "{{ helm_binary }}" name: "{{ chart_release_name }}" chart_ref: "{{ chart_source }}" - chart_version: "{{ chart_source_version | default(omit) }}" + chart_version: "{{ chart_test_version }}" namespace: "{{ helm_namespace }}" create_namespace: true context: does-not-exist @@ -417,6 +423,7 @@ state: absent path: "{{ temp_dir }}" ignore_errors: true + when: temp_dir is defined - name: Remove helm namespace k8s: diff --git a/tests/integration/targets/helm/tasks/tests_chart/from_repository.yml b/tests/integration/targets/helm/tasks/tests_chart/from_repository.yml index 0d8bae4f..4fd494f5 100644 --- a/tests/integration/targets/helm/tasks/tests_chart/from_repository.yml +++ b/tests/integration/targets/helm/tasks/tests_chart/from_repository.yml @@ -5,18 +5,38 @@ name: test_helm repo_url: "{{ chart_test_repo }}" -- name: Install Chart from repository - include_tasks: "../tests_chart.yml" - vars: - source: repository - chart_source: "test_helm/{{ chart_test }}" - chart_source_version: "{{ chart_test_version }}" - chart_source_version_upgrade: "{{ chart_test_version_upgrade }}" - helm_namespace: "{{ test_namespace[7] }}" +- name: Create temporary file to save values in + ansible.builtin.tempfile: + suffix: .helm_values + register: value_file -- name: Remove chart repo - helm_repository: - binary_path: "{{ helm_binary }}" - name: test_helm - repo_url: "{{ chart_test_repo }}" - state: absent +- vars: + source: repository + chart_test: k8status + chart_source: "test_helm/k8status" + chart_test_version: "0.16.1" + chart_test_version_upgrade: "0.16.2" + helm_namespace: "{{ test_namespace[7] }}" + chart_test_values: + replicaCount: 3 + block: + - name: Save values into file + ansible.builtin.copy: + content: "{{ chart_test_values }}" + dest: "{{ value_file.path }}" + + - name: Install Chart from repository + ansible.builtin.include_tasks: "../tests_chart.yml" + + always: + - name: Remove temporary file + ansible.builtin.file: + state: absent + path: "{{ value_file.path }}" + + - name: Remove chart repo + helm_repository: + binary_path: "{{ helm_binary }}" + name: test_helm + repo_url: "{{ chart_test_repo }}" + state: absent diff --git a/tests/integration/targets/helm/tasks/tests_chart/from_url.yml b/tests/integration/targets/helm/tasks/tests_chart/from_url.yml index fdd839d3..d9213735 100644 --- a/tests/integration/targets/helm/tasks/tests_chart/from_url.yml +++ b/tests/integration/targets/helm/tasks/tests_chart/from_url.yml @@ -3,6 +3,11 @@ include_tasks: "../tests_chart.yml" vars: source: url - chart_source: "https://github.com/kubernetes/ingress-nginx/releases/download/helm-chart-{{ chart_test_version }}/{{ chart_test }}-{{ chart_test_version }}.tgz" - chart_source_upgrade: "https://github.com/kubernetes/ingress-nginx/releases/download/helm-chart-{{ chart_test_version_upgrade }}/{{ chart_test }}-{{ chart_test_version_upgrade }}.tgz" + chart_test: "k8status" + chart_test_values: + replicaCount: 3 + chart_test_version: "0.16.1" + chart_test_version_upgrade: "0.16.2" + chart_source: https://github.com/stenic/k8status/releases/download/k8status-0.16.1/k8status-0.16.1.tgz + chart_source_upgrade: https://github.com/stenic/k8status/releases/download/k8status-0.16.2/k8status-0.16.2.tgz helm_namespace: "{{ test_namespace[8] }}" diff --git a/tests/integration/targets/helm_diff/tasks/main.yml b/tests/integration/targets/helm_diff/tasks/main.yml index a1f1f813..94ca81fd 100644 --- a/tests/integration/targets/helm_diff/tasks/main.yml +++ b/tests/integration/targets/helm_diff/tasks/main.yml @@ -239,6 +239,7 @@ vars: chart_local_path: '{{ _tmpd.path }}/test-chart-deployment-time' chart_repo_path: 'testing' + helm_binary_path: "{{ helm_binary }}" always: - name: Delete temporary directory ansible.builtin.file: diff --git a/tests/integration/targets/helm_diff/tasks/reset_then_reuse_values.yml b/tests/integration/targets/helm_diff/tasks/reset_then_reuse_values.yml index d86b9583..0de76eaa 100644 --- a/tests/integration/targets/helm_diff/tasks/reset_then_reuse_values.yml +++ b/tests/integration/targets/helm_diff/tasks/reset_then_reuse_values.yml @@ -92,25 +92,11 @@ path: /tmp/helm/ state: absent - - name: Init Helm folders - file: - path: /tmp/helm - state: directory - - - name: Set Helm old version - set_fact: - helm_archive_name: "helm-v3.8.0-linux-amd64.tar.gz" - helm_diff_old_version: "3.8.0" - - - name: Unarchive Helm binary - unarchive: - src: "https://get.helm.sh/{{ helm_archive_name | default(helm_default_archive_name) }}" - dest: /tmp/helm/ - remote_src: yes - retries: 10 - delay: 5 - register: result - until: result is not failed + - name: Install old version of helm + ansible.builtin.include_role: + name: install_helm + vars: + helm_version: "v3.8.0" - name: Upgrade helm release (with reset_then_reuse_values=true) kubernetes.core.helm: @@ -140,7 +126,7 @@ binary_path: "{{ helm_binary }}" state: present plugin_path: https://github.com/databus23/helm-diff - plugin_version: "{{ helm_diff_old_version }}" + plugin_version: "3.8.0" - name: Upgrade helm release (with reset_then_reuse_values=true) kubernetes.core.helm: @@ -166,6 +152,11 @@ - '"reset_then_reuse_values requires helm diff >= 3.9.12, current version is" in helm_upgrade.msg' always: + - name: Delete Helm folders + file: + path: /tmp/helm/ + state: absent + - name: Remove temporary directory file: path: "{{ helm_dir.path }}" diff --git a/tests/integration/targets/helm_kubeconfig/defaults/main.yml b/tests/integration/targets/helm_kubeconfig/defaults/main.yml index 0e30cb94..54f51267 100644 --- a/tests/integration/targets/helm_kubeconfig/defaults/main.yml +++ b/tests/integration/targets/helm_kubeconfig/defaults/main.yml @@ -1,7 +1,10 @@ --- -helm_binary: "/tmp/helm/{{ ansible_system | lower }}-amd64/helm" default_kubeconfig_path: "~/.kube/config" test_namespace: - "helm-in-memory-kubeconfig" - "helm-kubeconfig-with-ca-cert" - "helm-kubeconfig-with-insecure-skip-tls-verify" +helm_versions: + - v3.10.3 + - v3.16.4 + - v4.0.0 diff --git a/tests/integration/targets/helm_kubeconfig/meta/main.yml b/tests/integration/targets/helm_kubeconfig/meta/main.yml deleted file mode 100644 index 10d989e7..00000000 --- a/tests/integration/targets/helm_kubeconfig/meta/main.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -dependencies: - - remove_namespace - - install_helm diff --git a/tests/integration/targets/helm_kubeconfig/tasks/from_kubeconfig_with_cacert.yml b/tests/integration/targets/helm_kubeconfig/tasks/from_kubeconfig_with_cacert.yml index 0af0030a..9faf56a1 100644 --- a/tests/integration/targets/helm_kubeconfig/tasks/from_kubeconfig_with_cacert.yml +++ b/tests/integration/targets/helm_kubeconfig/tasks/from_kubeconfig_with_cacert.yml @@ -57,7 +57,7 @@ assert: that: - _install is failed - - '"Error: Kubernetes cluster unreachable" in _install.msg' + - '"error: kubernetes cluster unreachable" in _install.msg | lower()' - name: Test helm modules using in-memory kubeconfig include_tasks: "tests_helm_auth.yml" diff --git a/tests/integration/targets/helm_kubeconfig/tasks/from_kubeconfig_with_validate_certs.yml b/tests/integration/targets/helm_kubeconfig/tasks/from_kubeconfig_with_validate_certs.yml index 73c02a27..477b2031 100644 --- a/tests/integration/targets/helm_kubeconfig/tasks/from_kubeconfig_with_validate_certs.yml +++ b/tests/integration/targets/helm_kubeconfig/tasks/from_kubeconfig_with_validate_certs.yml @@ -48,7 +48,7 @@ assert: that: - _install is failed - - '"Error: Kubernetes cluster unreachable" in _install.msg' + - '"error: kubernetes cluster unreachable" in _install.msg | lower()' - name: Test helm modules using in-memory kubeconfig include_tasks: "tests_helm_auth.yml" diff --git a/tests/integration/targets/helm_kubeconfig/tasks/main.yml b/tests/integration/targets/helm_kubeconfig/tasks/main.yml index dc64c117..de7c170c 100644 --- a/tests/integration/targets/helm_kubeconfig/tasks/main.yml +++ b/tests/integration/targets/helm_kubeconfig/tasks/main.yml @@ -1,21 +1,5 @@ --- -- name: Test helm with in-memory kubeconfig - include_tasks: "from_in_memory_kubeconfig.yml" +- ansible.builtin.include_tasks: run_tests.yml + loop: "{{ helm_versions }}" loop_control: - loop_var: test_helm_version - with_items: - - "v3.10.3" - -- name: Test helm with custom kubeconfig and validate_certs=false - include_tasks: "from_kubeconfig_with_validate_certs.yml" - loop_control: - loop_var: test_helm_version - with_items: - - "v3.10.3" - -- name: Test helm with custom kubeconfig and ca_cert - include_tasks: "from_kubeconfig_with_cacert.yml" - loop_control: - loop_var: test_helm_version - with_items: - - "v3.10.3" + loop_var: helm_version diff --git a/tests/integration/targets/helm_kubeconfig/tasks/run_tests.yml b/tests/integration/targets/helm_kubeconfig/tasks/run_tests.yml new file mode 100644 index 00000000..be199de2 --- /dev/null +++ b/tests/integration/targets/helm_kubeconfig/tasks/run_tests.yml @@ -0,0 +1,15 @@ +--- +- name: Run tests with helm version "{{ helm_version }}" + block: + - name: "Install Helm" + ansible.builtin.include_role: + name: install_helm + + - name: Test helm with in-memory kubeconfig + ansible.builtin.include_tasks: "from_in_memory_kubeconfig.yml" + + - name: Test helm with custom kubeconfig and validate_certs=false + include_tasks: "from_kubeconfig_with_validate_certs.yml" + + - name: Test helm with custom kubeconfig and ca_cert + include_tasks: "from_kubeconfig_with_cacert.yml" diff --git a/tests/integration/targets/helm_kubeconfig/tasks/tests_helm_auth.yml b/tests/integration/targets/helm_kubeconfig/tasks/tests_helm_auth.yml index 5b666609..64dd2679 100644 --- a/tests/integration/targets/helm_kubeconfig/tasks/tests_helm_auth.yml +++ b/tests/integration/targets/helm_kubeconfig/tasks/tests_helm_auth.yml @@ -5,16 +5,6 @@ suffix: .helm register: _dir -- name: Install helm binary - block: - - name: "Install {{ test_helm_version }}" - include_role: - name: install_helm - vars: - helm_version: "{{ test_helm_version }}" - - when: test_helm_version is defined - - set_fact: saved_kubeconfig_path: "{{ _dir.path }}/config" @@ -44,6 +34,7 @@ ca_cert: "{{ test_ca_cert | default(omit) }}" state: present plugin_path: https://github.com/hydeenoble/helm-subenv + verify: false register: plugin - assert: diff --git a/tests/integration/targets/helm_plain_http/.gitignore b/tests/integration/targets/helm_plain_http/.gitignore new file mode 100644 index 00000000..2ea79720 --- /dev/null +++ b/tests/integration/targets/helm_plain_http/.gitignore @@ -0,0 +1 @@ +redis* diff --git a/tests/integration/targets/helm_plain_http/inventory.ini b/tests/integration/targets/helm_plain_http/inventory.ini index cdb5f7dc..13bcbe25 100644 --- a/tests/integration/targets/helm_plain_http/inventory.ini +++ b/tests/integration/targets/helm_plain_http/inventory.ini @@ -1,3 +1,4 @@ [all] helm-3.12.3 helm_version=v3.12.3 test_namespace=helm-plain-http-v3-12-3 tests_should_failed=true -helm-3.18.2 helm_version=v3.18.2 test_namespace=helm-plain-http-v3-18-2 tests_should_failed=false \ No newline at end of file +helm-3.18.2 helm_version=v3.18.2 test_namespace=helm-plain-http-v3-18-2 tests_should_failed=false +helm-4.0.0 helm_version=v4.0.0 test_namespace=helm-plain-http-v4-0-0 tests_should_failed=false \ No newline at end of file diff --git a/tests/integration/targets/helm_plain_http/playbooks/play.yaml b/tests/integration/targets/helm_plain_http/playbooks/play.yaml index 4ef540b9..1ae7f3d7 100644 --- a/tests/integration/targets/helm_plain_http/playbooks/play.yaml +++ b/tests/integration/targets/helm_plain_http/playbooks/play.yaml @@ -1,6 +1,7 @@ - name: Run test for helm plain http option hosts: all gather_facts: true + strategy: free vars: ansible_connection: local @@ -8,7 +9,7 @@ chart_test_oci: "oci://registry-1.docker.io/bitnamicharts/redis" roles: - - setup_namespace + - role: setup_namespace tasks: - ansible.builtin.include_tasks: tasks/test.yaml diff --git a/tests/integration/targets/helm_plain_http/playbooks/tasks/test.yaml b/tests/integration/targets/helm_plain_http/playbooks/tasks/test.yaml index e9bd2a2f..81e0bafc 100644 --- a/tests/integration/targets/helm_plain_http/playbooks/tasks/test.yaml +++ b/tests/integration/targets/helm_plain_http/playbooks/tasks/test.yaml @@ -13,10 +13,6 @@ vars: helm_install_path: "{{ install_path.path }}" - - name: Set helm binary path - ansible.builtin.set_fact: - helm_binary: "{{ install_path.path }}/{{ ansible_system | lower }}-amd64/helm" - # helm - name: Run helm with plain_http kubernetes.core.helm: diff --git a/tests/integration/targets/helm_plugin/meta/main.yml b/tests/integration/targets/helm_plugin/meta/main.yml deleted file mode 100644 index cf4590de..00000000 --- a/tests/integration/targets/helm_plugin/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -dependencies: - - install_helm diff --git a/tests/integration/targets/helm_plugin/tasks/main.yml b/tests/integration/targets/helm_plugin/tasks/main.yml index 72f0d7a3..1ea01e04 100644 --- a/tests/integration/targets/helm_plugin/tasks/main.yml +++ b/tests/integration/targets/helm_plugin/tasks/main.yml @@ -1,165 +1,8 @@ --- -- name: Install env plugin in check mode - helm_plugin: - binary_path: "{{ helm_binary }}" - state: present - plugin_path: https://github.com/adamreese/helm-env - register: check_install_env - check_mode: true - -- assert: - that: - - check_install_env.changed - -- name: Install env plugin - helm_plugin: - binary_path: "{{ helm_binary }}" - state: present - plugin_path: https://github.com/adamreese/helm-env - register: install_env - -- assert: - that: - - install_env.changed - -- name: Gather info about all plugin - helm_plugin_info: - binary_path: "{{ helm_binary }}" - register: plugin_info - -- assert: - that: - - plugin_info.plugin_list is defined - -- name: Install env plugin again - helm_plugin: - binary_path: "{{ helm_binary }}" - state: present - plugin_path: https://github.com/adamreese/helm-env - register: install_env - -- assert: - that: - - not install_env.changed - -- name: Uninstall env plugin in check mode - helm_plugin: - binary_path: "{{ helm_binary }}" - state: absent - plugin_name: env - register: check_uninstall_env - check_mode: true - -- assert: - that: - - check_uninstall_env.changed - -- name: Uninstall env plugin - helm_plugin: - binary_path: "{{ helm_binary }}" - state: absent - plugin_name: env - register: uninstall_env - -- assert: - that: - - uninstall_env.changed - -- name: Uninstall env plugin again - helm_plugin: - binary_path: "{{ helm_binary }}" - state: absent - plugin_name: env - register: uninstall_env - -- assert: - that: - - not uninstall_env.changed - -# https://github.com/ansible-collections/community.kubernetes/issues/399 -- block: - - name: Copy required plugin files - copy: - src: "files/sample_plugin" - dest: "/tmp/helm_plugin_test/" - - - name: Install sample_plugin from the directory - helm_plugin: - binary_path: "{{ helm_binary }}" - state: present - plugin_path: "/tmp/helm_plugin_test/sample_plugin" - register: sample_plugin_output - - - name: Assert that sample_plugin is installed or not - assert: - that: - - sample_plugin_output.changed - - - name: Gather Helm plugin info - helm_plugin_info: - binary_path: "{{ helm_binary }}" - register: r - - - name: Set sample_plugin version - set_fact: - plugin_version: "{{ ( r.plugin_list | selectattr('name', 'equalto', plugin_name) | list )[0].version }}" - vars: - plugin_name: "sample_plugin" - - - name: Assert if sample_plugin with multiline comment is installed - assert: - that: - - plugin_version == "0.0.1" - always: - - name: Uninstall sample_plugin - helm_plugin: - binary_path: "{{ helm_binary }}" - state: absent - plugin_name: sample_plugin - ignore_errors: yes - -- block: - - name: uninstall helm plugin secrets - helm_plugin: - binary_path: "{{ helm_binary }}" - plugin_name: secrets - state: absent - - - name: install helm-secrets on a specific version - helm_plugin: - binary_path: "{{ helm_binary }}" - plugin_path: https://github.com/jkroepke/helm-secrets - plugin_version: 3.4.1 - state: present - - - name: list helm plugin - helm_plugin_info: - plugin_name: secrets - binary_path: "{{ helm_binary }}" - register: plugin_list - - - name: assert that secrets has been installed with specified version - assert: - that: - - plugin_list.plugin_list[0].version == "3.4.1" - - - name: Update helm plugin version to latest - helm_plugin: - binary_path: "{{ helm_binary }}" - plugin_name: secrets - state: latest - register: _update - - - name: assert update was performed - assert: - that: - - _update.changed - - '"Updated plugin: secrets" in _update.stdout' - - always: - - name: Uninstall sample_plugin - helm_plugin: - binary_path: "{{ helm_binary }}" - state: absent - plugin_name: secrets - ignore_errors: yes +- name: Run tests + include_tasks: run_tests.yml + loop_control: + loop_var: helm_version + with_items: + - "v3.17.0" + - "v4.0.0" diff --git a/tests/integration/targets/helm_plugin/tasks/run_tests.yml b/tests/integration/targets/helm_plugin/tasks/run_tests.yml new file mode 100644 index 00000000..fd86a2de --- /dev/null +++ b/tests/integration/targets/helm_plugin/tasks/run_tests.yml @@ -0,0 +1,195 @@ +--- +- name: "Install {{ helm_version }}" + include_role: + name: install_helm + +- block: + - name: Install env plugin in check mode + helm_plugin: + binary_path: "{{ helm_binary }}" + state: present + plugin_path: https://github.com/adamreese/helm-env + verify: false + register: check_install_env + check_mode: true + + - assert: + that: + - check_install_env.changed + + - name: Install env plugin + helm_plugin: + binary_path: "{{ helm_binary }}" + state: present + plugin_path: https://github.com/adamreese/helm-env + verify: false + register: install_env + + - assert: + that: + - install_env.changed + + - name: Gather info about all plugin + helm_plugin_info: + binary_path: "{{ helm_binary }}" + register: plugin_info + + - assert: + that: + - plugin_info.plugin_list is defined + + - name: Install env plugin again + helm_plugin: + binary_path: "{{ helm_binary }}" + state: present + plugin_path: https://github.com/adamreese/helm-env + verify: false + register: install_env + + - assert: + that: + - not install_env.changed + + - name: Uninstall env plugin in check mode + helm_plugin: + binary_path: "{{ helm_binary }}" + state: absent + plugin_name: env + verify: false + register: check_uninstall_env + check_mode: true + + - assert: + that: + - check_uninstall_env.changed + + - name: Uninstall env plugin + helm_plugin: + binary_path: "{{ helm_binary }}" + state: absent + plugin_name: env + register: uninstall_env + + - assert: + that: + - uninstall_env.changed + + - name: Uninstall env plugin again + helm_plugin: + binary_path: "{{ helm_binary }}" + state: absent + plugin_name: env + register: uninstall_env + + - assert: + that: + - not uninstall_env.changed + + always: + - name: Uninstall env plugin + helm_plugin: + binary_path: "{{ helm_binary }}" + state: absent + plugin_name: env + +# https://github.com/ansible-collections/community.kubernetes/issues/399 +- block: + - name: Copy required plugin files + copy: + src: "files/sample_plugin" + dest: "/tmp/helm_plugin_test/" + + - name: Install sample_plugin from the directory + helm_plugin: + binary_path: "{{ helm_binary }}" + state: present + plugin_path: "/tmp/helm_plugin_test/sample_plugin" + register: sample_plugin_output + + - name: Assert that sample_plugin is installed or not + assert: + that: + - sample_plugin_output.changed + + - name: Gather Helm plugin info + helm_plugin_info: + binary_path: "{{ helm_binary }}" + register: r + + - name: Set sample_plugin version + set_fact: + plugin_version: "{{ ( r.plugin_list | selectattr('name', 'equalto', plugin_name) | list )[0].version }}" + vars: + plugin_name: "sample_plugin" + + - name: Assert if sample_plugin with multiline comment is installed + assert: + that: + - plugin_version == "0.0.1" + always: + - name: Uninstall sample_plugin + helm_plugin: + binary_path: "{{ helm_binary }}" + state: absent + plugin_name: sample_plugin + ignore_errors: true + +- block: + - name: uninstall helm plugin unittest + helm_plugin: + binary_path: "{{ helm_binary }}" + plugin_name: unittest + state: absent + + - name: install helm-unittest on a specific version + helm_plugin: + binary_path: "{{ helm_binary }}" + plugin_path: https://github.com/helm-unittest/helm-unittest + plugin_version: v1.0.1 + verify: false + state: present + + - name: list helm plugin + helm_plugin_info: + plugin_name: unittest + binary_path: "{{ helm_binary }}" + register: plugin_list + + - name: assert that unittest has been installed with specified version + assert: + that: + - plugin_list.plugin_list[0].version == "1.0.1" + + - name: Update helm plugin version to latest (check mode) + helm_plugin: + binary_path: "{{ helm_binary }}" + plugin_name: unittest + state: latest + register: _update_checkmode + check_mode: true + + - name: Assert that module reported change while running in check mode + assert: + that: + - _update_checkmode.changed + - '"Updated plugin: unittest" not in _update_checkmode.stdout' + + - name: Update helm plugin version to latest + helm_plugin: + binary_path: "{{ helm_binary }}" + plugin_name: unittest + state: latest + register: _update + + - name: assert update was performed + assert: + that: + - _update.changed + - '"Updated plugin: unittest" in _update.stdout' + + always: + - name: Uninstall sample_plugin + helm_plugin: + binary_path: "{{ helm_binary }}" + state: absent + plugin_name: unittest diff --git a/tests/integration/targets/helm_pull/tasks/main.yml b/tests/integration/targets/helm_pull/tasks/main.yml index d5e47711..8849c344 100644 --- a/tests/integration/targets/helm_pull/tasks/main.yml +++ b/tests/integration/targets/helm_pull/tasks/main.yml @@ -5,7 +5,7 @@ - 3.8.0 - 3.1.0 - 3.0.0 - - 2.3.0 + - 4.0.0 - block: - name: Create temp directory for helm tests @@ -20,37 +20,13 @@ - set_fact: destination: "{{ temp_dir }}" - - name: Create Helm directories - file: - state: directory - path: "{{ temp_dir }}/{{ item }}" - with_items: "{{ helm_versions }}" - - - name: Unarchive Helm binary - unarchive: - src: "https://get.helm.sh/helm-v{{ item }}-linux-amd64.tar.gz" - dest: "{{ temp_dir }}/{{ item }}" - remote_src: yes - with_items: "{{ helm_versions }}" - - # Testing helm pull with helm version == 2.3.0 - - block: - - name: Assert that helm pull failed with helm <= 3.0.0 - helm_pull: - binary_path: "{{ helm_path }}" - chart_ref: https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz - destination: "{{ destination }}" - ignore_errors: true - register: _result - - - name: assert that module failed with proper message - assert: - that: - - _result is failed - - _result.msg == "Helm version must be >=3.0.0,<4.0.0, current version is 2.3.0" - + - name: Install Helm versions + ansible.builtin.include_role: + name: install_helm + loop: "{{ helm_versions }}" vars: - helm_path: "{{ temp_dir }}/2.3.0/linux-amd64/helm" + helm_version: "v{{ item }}" + helm_install_path: "{{ temp_dir }}/{{ item }}" # Testing helm pull with helm version == 3.0.0 - block: @@ -103,7 +79,7 @@ - _result.msg == "Parameter chart_ca_cert requires helm >= 3.1.0, current version is 3.0.0" vars: - helm_path: "{{ temp_dir }}/3.0.0/linux-amd64/helm" + helm_path: "{{ temp_dir }}/3.0.0/helm" # Testing helm pull with helm version == 3.1.0 - block: @@ -143,7 +119,7 @@ - _result.msg == "Parameter skip_tls_certs_check requires helm >= 3.3.0, current version is 3.1.0" vars: - helm_path: "{{ temp_dir }}/3.1.0/linux-amd64/helm" + helm_path: "{{ temp_dir }}/3.1.0/helm" # Testing helm pull with helm version == 3.8.0 - block: @@ -317,7 +293,7 @@ - _chart_after_force.stat.isdir vars: - helm_path: "{{ temp_dir }}/3.8.0/linux-amd64/helm" + helm_path: "{{ temp_dir }}/3.8.0/helm" always: diff --git a/tests/integration/targets/helm_registry_auth/defaults/main.yaml b/tests/integration/targets/helm_registry_auth/defaults/main.yaml index da3f3368..fcb80984 100644 --- a/tests/integration/targets/helm_registry_auth/defaults/main.yaml +++ b/tests/integration/targets/helm_registry_auth/defaults/main.yaml @@ -5,5 +5,9 @@ username: testuser password: testpassword wrong_password: 'WrongPassword' registry_name: oci_registry -registry_port: 5000 +registry_port: 5002 test_chart: https://github.com/grafana/helm-charts/releases/download/k8s-monitoring-1.6.8/k8s-monitoring-1.6.8.tgz +helm_versions: + - v3.17.0 + - v3.20.0 + - v4.0.0 diff --git a/tests/integration/targets/helm_registry_auth/meta/main.yml b/tests/integration/targets/helm_registry_auth/meta/main.yml deleted file mode 100644 index cf4590de..00000000 --- a/tests/integration/targets/helm_registry_auth/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -dependencies: - - install_helm diff --git a/tests/integration/targets/helm_registry_auth/tasks/main.yaml b/tests/integration/targets/helm_registry_auth/tasks/main.yaml deleted file mode 100644 index 8e15452c..00000000 --- a/tests/integration/targets/helm_registry_auth/tasks/main.yaml +++ /dev/null @@ -1,182 +0,0 @@ ---- -- name: Run module test - # using a shell and command module to run the test as test can be non-idempotent - # and it allow to not install any additional dependencies - block: - - name: Ensure that helm is installed - ansible.builtin.shell: helm version --client --short | grep v3 - register: _helm_version - failed_when: _helm_version.rc != 0 - - - name: Ensure that Docker demon is running - ansible.builtin.command: "docker info" - register: _docker_info - failed_when: _docker_info.rc != 0 - - - name: Create a tmpfile htpasswd directory - ansible.builtin.tempfile: - state: directory - suffix: .httppasswd - register: _tmpfile - - - name: Copy htpasswd to the tmpfile directory - ansible.builtin.copy: - src: registry.password - dest: "{{ _tmpfile.path }}/registry.password" - - - name: Setup the registry - ansible.builtin.command: >- - docker run -d --rm - -p {{ registry_port }}:5000 - --name "{{ registry_name }}" - -v "{{ _tmpfile.path }}:/auth" - -e "REGISTRY_AUTH=htpasswd" - -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" - -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/registry.password - registry:2 - register: _setup_registry - failed_when: _setup_registry.rc != 0 - - - name: Ensure that the registry is running and rechable - ansible.builtin.wait_for: - host: localhost - port: "{{ registry_port }}" - - - name: Test the registry with correct credentials to ensure that the registry is running - ansible.builtin.shell: >- - echo {{ password | quote }} | helm registry login localhost:{{ registry_port }} - -u {{ username }} --password-stdin - register: _login_correct - failed_when: _login_correct.rc != 0 - - - name: Clean up credentials to run test on clean environment - ansible.builtin.shell: >- - helm registry logout localhost:{{ registry_port }} - register: _logout - failed_when: _logout.rc != 0 - - - name: Create directory for helm chart - ansible.builtin.tempfile: - state: directory - suffix: ".helm" - register: _destination - - - name: Pull test helm chart - ansible.builtin.uri: - url: "{{ test_chart }}" - dest: "{{ _destination.path }}/k8s-monitoring-1.6.8.tgz" - return_content: no - status_code: 200 - - - name: Test module helm_registry_auth with correct credentials - helm_registry_auth: - username: "{{ username }}" - password: "{{ password }}" - host: localhost:{{ registry_port }} - state: present - register: _helm_registry_auth_correct - - - name: Assert that the registry is logged in - # Helm binary prints the message to stderr, refence: https://github.com/helm/helm/issues/13464 - assert: - that: - - "'Login Succeeded' in _helm_registry_auth_correct.stderr" - - "'{{ password }}' not in _helm_registry_auth_correct.command" - - "'{{ password }}' not in _helm_registry_auth_correct.stdout" - - "'{{ password }}' not in _helm_registry_auth_correct.stderr" - - - name: Ensure that push to the registry is working - ansible.builtin.shell: >- - helm push "{{ _destination.path }}/k8s-monitoring-1.6.8.tgz" oci://localhost:{{ registry_port }}/test/ - register: _save_chart - failed_when: _save_chart.rc != 0 - - - name: Assert that the chart is saved - # Helm binary prints the message to stderr, refence: https://github.com/helm/helm/issues/13464 - assert: - that: "'Pushed: localhost:{{ registry_port }}/test/k8s-monitoring' in _save_chart.stderr" - - - - name: Test logout - helm_registry_auth: - host: localhost:{{ registry_port }} - state: absent - register: _helm_registry_auth_logout - - - name: Assert logout - # Helm binary prints the message to stderr - assert: - that: "'Removing login credentials' in _helm_registry_auth_logout.stderr" - - - name: Test idempotency of logout with helm < 3.18.0 - when: _helm_version.stdout is ansible.builtin.version('v3.18.0', '<') - block: - - - name: Test logout idempotency - helm_registry_auth: - host: localhost:{{ registry_port }} - state: absent - register: _helm_registry_auth_logout_idempotency - - - name: Assert logout operation did not report change - ansible.builtin.assert: - that: _helm_registry_auth_logout_idempotency is not changed - - - name: Ensure that not able to push to the registry - ansible.builtin.shell: >- - helm push "{{ _destination.path }}/k8s-monitoring-1.6.8.tgz" oci://localhost:{{ registry_port }}/test/ - register: _save_chart - failed_when: _save_chart.rc == 0 - - - name: Read content of ~/.config/helm/registry/config.json - ansible.builtin.slurp: - src: ~/.config/helm/registry/config.json - register: _config_json - - - name: Assert that auth data is remove and the chart is not saved - # Helm binary prints the message to stderr - ansible.builtin.assert: - that: - - "'push access denied' in _save_chart.stderr or 'basic credential not found' in _save_chart.stderr" - - "_save_chart.rc != 0" - - "'localhost:{{ registry_port }}' not in _config_json.content | b64decode" - - - name: Test module helm_registry_auth with wrong credentials - helm_registry_auth: - username: "{{ username }}" - password: "{{ wrong_password }}" - host: localhost:{{ registry_port }} - state: present - register: _helm_registry_auth_wrong - ignore_errors: true - - - name: Read content of ~/.config/helm/registry/config.json - ansible.builtin.slurp: - src: ~/.config/helm/registry/config.json - register: _config_json - - - name: Assert that the registry is not logged in and auth data is not saved - ansible.builtin.assert: - that: - - "'401' in _helm_registry_auth_wrong.stderr" - - "'unauthorized' in _helm_registry_auth_wrong.stderr | lower" - - "'{{ wrong_password }}' not in _helm_registry_auth_correct.command" - - "'{{ wrong_password }}' not in _helm_registry_auth_correct.stdout" - - "'{{ wrong_password }}' not in _helm_registry_auth_correct.stderr" - - "'localhost:{{ registry_port }}' not in _config_json.content | b64decode" - - # Clean up - always: - - name: Stop and remove the registry - ansible.builtin.command: docker stop {{ registry_name }} - ignore_errors: true - - - name: Remove the tmpfile - ansible.builtin.file: - state: absent - path: "{{ item }}" - force: true - loop: - - "{{ _tmpfile.path }}" - - "{{ _destination.path }}" - ignore_errors: true diff --git a/tests/integration/targets/helm_registry_auth/tasks/main.yml b/tests/integration/targets/helm_registry_auth/tasks/main.yml new file mode 100644 index 00000000..f33778fc --- /dev/null +++ b/tests/integration/targets/helm_registry_auth/tasks/main.yml @@ -0,0 +1,60 @@ +--- +- name: Run tests for helm_registry_auth with different helm versions + block: + - name: Create temporary directory to install helm binaries + ansible.builtin.tempfile: + state: directory + suffix: .helm + register: _tmpdir + + - name: Ensure that Docker demon is running + ansible.builtin.command: "docker info" + register: _docker_info + failed_when: _docker_info.rc != 0 + + - name: Copy htpasswd to the tmpfile directory + ansible.builtin.copy: + src: registry.password + dest: "{{ _tmpdir.path }}/registry.password" + + - name: Pull test helm chart + ansible.builtin.uri: + url: "{{ test_chart }}" + dest: "{{ _tmpdir.path }}/k8s-monitoring-1.6.8.tgz" + return_content: no + status_code: 200 + + - name: Setup the registry + ansible.builtin.command: >- + docker run -d --rm + -p {{ registry_port }}:5000 + --name "{{ registry_name }}" + -v "{{ _tmpdir.path }}:/auth" + -e "REGISTRY_AUTH=htpasswd" + -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" + -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/registry.password + registry:2 + register: _setup_registry + failed_when: _setup_registry.rc != 0 + + - name: Ensure that the registry is running and rechable + ansible.builtin.wait_for: + host: localhost + port: "{{ registry_port }}" + + - name: Run tests + ansible.builtin.include_tasks: run_tests.yml + loop: "{{ helm_versions }}" + loop_control: + loop_var: helm_version + + always: + - name: Stop and remove the registry + ansible.builtin.command: docker stop {{ registry_name }} + ignore_errors: true + + - name: Delete temporary directory + ansible.builtin.file: + state: absent + path: "{{ _tmpdir.path }}" + ignore_errors: true diff --git a/tests/integration/targets/helm_registry_auth/tasks/run_tests.yml b/tests/integration/targets/helm_registry_auth/tasks/run_tests.yml new file mode 100644 index 00000000..d4f4786a --- /dev/null +++ b/tests/integration/targets/helm_registry_auth/tasks/run_tests.yml @@ -0,0 +1,108 @@ +--- +- block: + - name: Install helm versions + ansible.builtin.include_role: + name: install_helm + + # - name: Test the registry with correct credentials to ensure that the registry is running + # ansible.builtin.shell: >- + # echo {{ password | quote }} | {{ helm_binary }} registry login localhost:{{ registry_port }} + # -u {{ username }} --password-stdin --plain-http + # register: _login_correct + # failed_when: _login_correct.rc != 0 + + # - name: Clean up credentials to run test on clean environment + # ansible.builtin.shell: >- + # {{ helm_binary }} registry logout localhost:{{ registry_port }} + # register: _logout + # failed_when: _logout.rc != 0 + + - name: Test module helm_registry_auth with correct credentials + helm_registry_auth: + binary_path: "{{ helm_binary }}" + username: "{{ username }}" + password: "{{ password }}" + host: localhost:{{ registry_port }} + plain_http: true + state: present + register: _helm_registry_auth_correct + + - name: Assert that the registry is logged in + # Helm binary prints the message to stderr, refence: https://github.com/helm/helm/issues/13464 + assert: + that: + - "'Login Succeeded' in _helm_registry_auth_correct.stdout_lines + _helm_registry_auth_correct.stderr_lines" + - password not in _helm_registry_auth_correct.command + - password not in _helm_registry_auth_correct.stdout + - password not in _helm_registry_auth_correct.stderr + + - name: Ensure that push to the registry is working + ansible.builtin.shell: >- + {{ helm_binary }} push --plain-http "{{ _tmpdir.path }}/k8s-monitoring-1.6.8.tgz" oci://localhost:{{ registry_port }}/test/ + register: _save_chart + failed_when: _save_chart.rc != 0 + + - name: Assert that the chart is saved + # Helm binary prints the message to stderr, refence: https://github.com/helm/helm/issues/13464 + assert: + that: "'Pushed: localhost:' + registry_port | string + '/test/k8s-monitoring' in _save_chart.stderr" + + - name: Test logout + helm_registry_auth: + binary_path: "{{ helm_binary }}" + host: localhost:{{ registry_port }} + state: absent + register: _helm_registry_auth_logout + + - name: Assert logout + # Helm binary prints the message to stderr + assert: + that: "'Removing login credentials' in _helm_registry_auth_logout.stderr" + + - name: Ensure that not able to push to the registry + ansible.builtin.shell: >- + {{ helm_binary }} push --plain-http "{{ _tmpdir.path }}/k8s-monitoring-1.6.8.tgz" oci://localhost:{{ registry_port }}/test/ + register: _save_chart + failed_when: _save_chart.rc == 0 + + - name: Assert that auth data is remove and the chart is not saved + # Helm binary prints the message to stderr + ansible.builtin.assert: + that: + - "'push access denied' in _save_chart.stderr or 'basic credential not found' in _save_chart.stderr" + - "_save_chart.rc != 0" + + - name: Test idempotency of logout with helm < 3.18.0 + when: helm_version is ansible.builtin.version('v3.18.0', '<') + block: + + - name: Test logout idempotency + helm_registry_auth: + binary_path: "{{ helm_binary }}" + host: localhost:{{ registry_port }} + state: absent + register: _helm_registry_auth_logout_idempotency + + - name: Assert logout operation did not report change + ansible.builtin.assert: + that: _helm_registry_auth_logout_idempotency is not changed + + - name: Test module helm_registry_auth with wrong credentials + helm_registry_auth: + binary_path: "{{ helm_binary }}" + username: "{{ username }}" + password: "{{ wrong_password }}" + host: localhost:{{ registry_port }} + state: present + plain_http: true + register: _helm_registry_auth_wrong + ignore_errors: true + + - name: Assert that the registry is not logged in and auth data is not saved + ansible.builtin.assert: + that: + - "'401' in _helm_registry_auth_wrong.stderr" + - "'unauthorized' in _helm_registry_auth_wrong.stderr | lower" + - "'{{ wrong_password }}' not in _helm_registry_auth_correct.command" + - "'{{ wrong_password }}' not in _helm_registry_auth_correct.stdout" + - "'{{ wrong_password }}' not in _helm_registry_auth_correct.stderr" diff --git a/tests/integration/targets/helm_repository/aliases b/tests/integration/targets/helm_repository/aliases index eb25b7b3..b7d2be3c 100644 --- a/tests/integration/targets/helm_repository/aliases +++ b/tests/integration/targets/helm_repository/aliases @@ -1,5 +1,2 @@ -time=20 -helm_repository -helm_info -helm -helm_template \ No newline at end of file +time=1 +helm_repository \ No newline at end of file diff --git a/tests/integration/targets/helm_repository/defaults/main.yml b/tests/integration/targets/helm_repository/defaults/main.yml index 5b60b517..c6bf63a3 100644 --- a/tests/integration/targets/helm_repository/defaults/main.yml +++ b/tests/integration/targets/helm_repository/defaults/main.yml @@ -1,3 +1,5 @@ --- chart_test_repo: "https://kubernetes.github.io/ingress-nginx" -helm_binary: "/tmp/helm/{{ ansible_system | lower }}-amd64/helm" +helm_versions: + - v3.20.0 + - v4.0.0 diff --git a/tests/integration/targets/helm_repository/meta/main.yml b/tests/integration/targets/helm_repository/meta/main.yml deleted file mode 100644 index c36db956..00000000 --- a/tests/integration/targets/helm_repository/meta/main.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -collections: - - kubernetes.core -dependencies: - - install_helm diff --git a/tests/integration/targets/helm_repository/tasks/main.yml b/tests/integration/targets/helm_repository/tasks/main.yml index 4f05b227..bd538a42 100644 --- a/tests/integration/targets/helm_repository/tasks/main.yml +++ b/tests/integration/targets/helm_repository/tasks/main.yml @@ -1,101 +1,6 @@ --- -- name: "Ensure test_helm_repo doesn't exist" - helm_repository: - binary_path: "{{ helm_binary }}" - name: test_helm_repo - state: absent - -- name: Add test_helm_repo chart repository - helm_repository: - binary_path: "{{ helm_binary }}" - name: test_helm_repo - repo_url: "{{ chart_test_repo }}" - register: repository - -- name: Assert that test_helm_repo repository is added - assert: - that: - - repository is changed - - '"--insecure-skip-tls-verify" not in repository.command' - -- name: Check idempotency - helm_repository: - binary_path: "{{ helm_binary }}" - name: test_helm_repo - repo_url: "{{ chart_test_repo }}" - register: repository - -- name: Assert idempotency - assert: - that: - - repository is not changed - -- name: Failed to add repository with the same name - helm_repository: - binary_path: "{{ helm_binary }}" - name: test_helm_repo - repo_url: "https://other-charts.url" - register: repository_errors - ignore_errors: yes - -- name: Assert that adding repository with the same name failed - assert: - that: - - repository_errors is failed - -- name: Succesfully add repository with the same name when forcing - helm_repository: - binary_path: "{{ helm_binary }}" - name: test_helm_repo - repo_url: "{{ chart_test_repo }}" - force: true - register: repository - -- name: Assert that test_helm_repo repository is changed - assert: - that: - - repository is changed - -- name: Remove test_helm_repo chart repository - helm_repository: - binary_path: "{{ helm_binary }}" - name: test_helm_repo - state: absent - register: repository - -- name: Assert that test_helm_repo repository is removed - assert: - that: - - repository is changed - -- name: Check idempotency after remove - helm_repository: - binary_path: "{{ helm_binary }}" - name: test_helm_repo - state: absent - register: repository - -- name: Assert idempotency - assert: - that: - - repository is not changed - -- name: Add test_helm_repo chart repository as insecure - helm_repository: - binary_path: "{{ helm_binary }}" - name: test_helm_repo - repo_url: "{{ chart_test_repo }}" - insecure_skip_tls_verify: true - register: repository - -- name: Assert that repository added and flag set - assert: - that: - - repository is changed - - '"--insecure-skip-tls-verify" in repository.command' - -- name: Clean test_helm_repo chart repository - helm_repository: - binary_path: "{{ helm_binary }}" - name: test_helm_repo - state: absent +- name: Run test for helm_repository module + ansible.builtin.include_tasks: run_tests.yml + loop: "{{ helm_versions }}" + loop_control: + loop_var: helm_version diff --git a/tests/integration/targets/helm_repository/tasks/run_tests.yml b/tests/integration/targets/helm_repository/tasks/run_tests.yml new file mode 100644 index 00000000..586b87a6 --- /dev/null +++ b/tests/integration/targets/helm_repository/tasks/run_tests.yml @@ -0,0 +1,105 @@ +--- +- name: "Install helm version {{ helm_version }}" + ansible.builtin.include_role: + name: install_helm + +- name: "Ensure test_helm_repo doesn't exist" + helm_repository: + binary_path: "{{ helm_binary }}" + name: test_helm_repo + state: absent + +- name: Add test_helm_repo chart repository + helm_repository: + binary_path: "{{ helm_binary }}" + name: test_helm_repo + repo_url: "{{ chart_test_repo }}" + register: repository + +- name: Assert that test_helm_repo repository is added + assert: + that: + - repository is changed + - '"--insecure-skip-tls-verify" not in repository.command' + +- name: Check idempotency + helm_repository: + binary_path: "{{ helm_binary }}" + name: test_helm_repo + repo_url: "{{ chart_test_repo }}" + register: repository + +- name: Assert idempotency + assert: + that: + - repository is not changed + +- name: Failed to add repository with the same name + helm_repository: + binary_path: "{{ helm_binary }}" + name: test_helm_repo + repo_url: "https://other-charts.url" + register: repository_errors + ignore_errors: true + +- name: Assert that adding repository with the same name failed + assert: + that: + - repository_errors is failed + +- name: Succesfully add repository with the same name when forcing + helm_repository: + binary_path: "{{ helm_binary }}" + name: test_helm_repo + repo_url: "{{ chart_test_repo }}" + force: true + register: repository + +- name: Assert that test_helm_repo repository is changed + assert: + that: + - repository is changed + +- name: Remove test_helm_repo chart repository + helm_repository: + binary_path: "{{ helm_binary }}" + name: test_helm_repo + state: absent + register: repository + +- name: Assert that test_helm_repo repository is removed + assert: + that: + - repository is changed + +- name: Check idempotency after remove + helm_repository: + binary_path: "{{ helm_binary }}" + name: test_helm_repo + state: absent + register: repository + +- name: Assert idempotency + assert: + that: + - repository is not changed + +- name: Add test_helm_repo chart repository as insecure + helm_repository: + binary_path: "{{ helm_binary }}" + name: test_helm_repo + repo_url: "{{ chart_test_repo }}" + insecure_skip_tls_verify: true + register: repository + +- name: Assert that repository added and flag set + assert: + that: + - repository is changed + - '"--insecure-skip-tls-verify" in repository.command' + +- name: Clean test_helm_repo chart repository + helm_repository: + binary_path: "{{ helm_binary }}" + name: test_helm_repo + state: absent diff --git a/tests/integration/targets/helm_set_values/defaults/main.yml b/tests/integration/targets/helm_set_values/defaults/main.yml index 73c5a0bb..6b710808 100644 --- a/tests/integration/targets/helm_set_values/defaults/main.yml +++ b/tests/integration/targets/helm_set_values/defaults/main.yml @@ -1,3 +1,5 @@ --- -helm_binary: "/tmp/helm/{{ ansible_system | lower }}-amd64/helm" helm_namespace: helm-set-values +helm_versions: + - v3.10.3 + - v4.0.0 diff --git a/tests/integration/targets/helm_set_values/tasks/main.yml b/tests/integration/targets/helm_set_values/tasks/main.yml index e22f6f8d..cee51166 100644 --- a/tests/integration/targets/helm_set_values/tasks/main.yml +++ b/tests/integration/targets/helm_set_values/tasks/main.yml @@ -25,79 +25,14 @@ - user_values.status["release_values"]["phase"] == "integration" - user_values.status["release_values"]["versioned"] is false -# install chart using set_values and release_values -- name: Install helm binary (> 3.10.0) requires to use set-json - include_role: - name: install_helm - vars: - helm_version: "v3.10.3" - -- name: Install helm using set_values parameters - helm: - binary_path: "{{ helm_binary }}" - chart_ref: oci://registry-1.docker.io/bitnamicharts/apache - release_name: test-apache - release_namespace: "{{ helm_namespace }}" - create_namespace: true - set_values: - - value: 'master.image={"registry": "docker.io", "repository": "bitnami/apache", "tag": "2.4.54-debian-11-r74"}' - value_type: json - release_values: - replicaCount: 3 - -- name: Get release info - helm_info: - binary_path: "{{ helm_binary }}" - release_name: test-apache - release_namespace: "{{ helm_namespace }}" - register: values - -- name: Assert that release was created with user-defined variables - assert: - that: - - values.status["release_values"].replicaCount == 3 - - values.status["release_values"].master.image.registry == "docker.io" - - values.status["release_values"].master.image.repository == "bitnami/apache" - - values.status["release_values"].master.image.tag == "2.4.54-debian-11-r74" - -# install chart using set_values and values_files -- name: create temporary file to save values in - tempfile: - suffix: .yml - register: ymlfile - - block: - - name: copy content into values file - copy: - content: | - --- - mode: distributed - dest: "{{ ymlfile.path }}" + - name: create temporary file to save values in + tempfile: + suffix: .yml + register: ymlfile - - name: Install helm using set_values parameters - helm: - binary_path: "{{ helm_binary }}" - chart_ref: oci://registry-1.docker.io/bitnamicharts/minio - release_name: test-minio - release_namespace: "{{ helm_namespace }}" - create_namespace: true - set_values: - - value: 'disableWebUI=true' - values_files: - - "{{ ymlfile.path }}" - - - name: Get release info - helm_info: - binary_path: "{{ helm_binary }}" - release_name: test-minio - release_namespace: "{{ helm_namespace }}" - register: values - - - name: Assert that release was created with user-defined variables - assert: - that: - - values.status["release_values"].mode == "distributed" - - values.status["release_values"].disableWebUI is true + - ansible.builtin.include_tasks: run_tests.yml + loop: "{{ helm_versions }}" always: - name: Delete temporary file diff --git a/tests/integration/targets/helm_set_values/tasks/run_tests.yml b/tests/integration/targets/helm_set_values/tasks/run_tests.yml new file mode 100644 index 00000000..31c5ace7 --- /dev/null +++ b/tests/integration/targets/helm_set_values/tasks/run_tests.yml @@ -0,0 +1,68 @@ +--- +# install chart using set_values and release_values +- name: 'Install helm binary (> 3.10.0) requires to use set-json (helm_version={{ item }})' + include_role: + name: install_helm + vars: + helm_version: "{{ item }}" + +- name: Install helm using set_values parameters + helm: + binary_path: "{{ helm_binary }}" + chart_ref: oci://registry-1.docker.io/bitnamicharts/apache + release_name: 'test-apache-{{ item }}' + release_namespace: "{{ helm_namespace }}" + create_namespace: true + set_values: + - value: 'master.image={"registry": "docker.io", "repository": "bitnami/apache", "tag": "2.4.54-debian-11-r74"}' + value_type: json + release_values: + replicaCount: 3 + +- name: Get release info + helm_info: + binary_path: "{{ helm_binary }}" + release_name: 'test-apache-{{ item }}' + release_namespace: "{{ helm_namespace }}" + register: values + +- name: Assert that release was created with user-defined variables + assert: + that: + - values.status["release_values"].replicaCount == 3 + - values.status["release_values"].master.image.registry == "docker.io" + - values.status["release_values"].master.image.repository == "bitnami/apache" + - values.status["release_values"].master.image.tag == "2.4.54-debian-11-r74" + +# install chart using set_values and values_files +- name: copy content into values file + copy: + content: | + --- + mode: distributed + dest: "{{ ymlfile.path }}" + +- name: Install helm using set_values parameters + helm: + binary_path: "{{ helm_binary }}" + chart_ref: oci://registry-1.docker.io/bitnamicharts/minio + release_name: 'test-minio-{{ item }}' + release_namespace: "{{ helm_namespace }}" + create_namespace: true + set_values: + - value: 'disableWebUI=true' + values_files: + - "{{ ymlfile.path }}" + +- name: Get release info + helm_info: + binary_path: "{{ helm_binary }}" + release_name: 'test-minio-{{ item }}' + release_namespace: "{{ helm_namespace }}" + register: values + +- name: Assert that release was created with user-defined variables + assert: + that: + - values.status["release_values"].mode == "distributed" + - values.status["release_values"].disableWebUI is true diff --git a/tests/integration/targets/helm_v3_15_4/aliases b/tests/integration/targets/helm_v3_15_4/aliases new file mode 100644 index 00000000..a80cfed0 --- /dev/null +++ b/tests/integration/targets/helm_v3_15_4/aliases @@ -0,0 +1,4 @@ +helm +helm_template +helm_info +helm_repository \ No newline at end of file diff --git a/tests/integration/targets/helm_v3_15_4/inventory.ini b/tests/integration/targets/helm_v3_15_4/inventory.ini new file mode 100644 index 00000000..6bf36611 --- /dev/null +++ b/tests/integration/targets/helm_v3_15_4/inventory.ini @@ -0,0 +1,2 @@ +[all] +v3.15.4 \ No newline at end of file diff --git a/tests/integration/targets/helm_v3_15_4/play.yaml b/tests/integration/targets/helm_v3_15_4/play.yaml new file mode 100644 index 00000000..5591456a --- /dev/null +++ b/tests/integration/targets/helm_v3_15_4/play.yaml @@ -0,0 +1,11 @@ +- name: Run tests for Helm v4.0.0 + hosts: all + connection: local + gather_facts: true + + vars: + ansible_python_interpreter: "{{ ansible_playbook_python }}" + helm_version: "{{ inventory_hostname }}" + + roles: + - role: helm diff --git a/tests/integration/targets/helm_v3_15_4/runme.sh b/tests/integration/targets/helm_v3_15_4/runme.sh new file mode 100755 index 00000000..2b5bb1b6 --- /dev/null +++ b/tests/integration/targets/helm_v3_15_4/runme.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eux +export ANSIBLE_ROLES_PATH=../ +ansible-playbook play.yaml -i inventory.ini "$@" \ No newline at end of file diff --git a/tests/integration/targets/helm_v3_16_0/aliases b/tests/integration/targets/helm_v3_16_0/aliases new file mode 100644 index 00000000..a80cfed0 --- /dev/null +++ b/tests/integration/targets/helm_v3_16_0/aliases @@ -0,0 +1,4 @@ +helm +helm_template +helm_info +helm_repository \ No newline at end of file diff --git a/tests/integration/targets/helm_v3_16_0/inventory.ini b/tests/integration/targets/helm_v3_16_0/inventory.ini new file mode 100644 index 00000000..c49ca863 --- /dev/null +++ b/tests/integration/targets/helm_v3_16_0/inventory.ini @@ -0,0 +1,2 @@ +[all] +v3.16.0 \ No newline at end of file diff --git a/tests/integration/targets/helm_v3_16_0/play.yaml b/tests/integration/targets/helm_v3_16_0/play.yaml new file mode 100644 index 00000000..5591456a --- /dev/null +++ b/tests/integration/targets/helm_v3_16_0/play.yaml @@ -0,0 +1,11 @@ +- name: Run tests for Helm v4.0.0 + hosts: all + connection: local + gather_facts: true + + vars: + ansible_python_interpreter: "{{ ansible_playbook_python }}" + helm_version: "{{ inventory_hostname }}" + + roles: + - role: helm diff --git a/tests/integration/targets/helm_v3_16_0/runme.sh b/tests/integration/targets/helm_v3_16_0/runme.sh new file mode 100755 index 00000000..2b5bb1b6 --- /dev/null +++ b/tests/integration/targets/helm_v3_16_0/runme.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eux +export ANSIBLE_ROLES_PATH=../ +ansible-playbook play.yaml -i inventory.ini "$@" \ No newline at end of file diff --git a/tests/integration/targets/helm_v3_17_0/aliases b/tests/integration/targets/helm_v3_17_0/aliases new file mode 100644 index 00000000..a80cfed0 --- /dev/null +++ b/tests/integration/targets/helm_v3_17_0/aliases @@ -0,0 +1,4 @@ +helm +helm_template +helm_info +helm_repository \ No newline at end of file diff --git a/tests/integration/targets/helm_v3_17_0/inventory.ini b/tests/integration/targets/helm_v3_17_0/inventory.ini new file mode 100644 index 00000000..ea3b890b --- /dev/null +++ b/tests/integration/targets/helm_v3_17_0/inventory.ini @@ -0,0 +1,2 @@ +[all] +v3.17.0 \ No newline at end of file diff --git a/tests/integration/targets/helm_v3_17_0/play.yaml b/tests/integration/targets/helm_v3_17_0/play.yaml new file mode 100644 index 00000000..5591456a --- /dev/null +++ b/tests/integration/targets/helm_v3_17_0/play.yaml @@ -0,0 +1,11 @@ +- name: Run tests for Helm v4.0.0 + hosts: all + connection: local + gather_facts: true + + vars: + ansible_python_interpreter: "{{ ansible_playbook_python }}" + helm_version: "{{ inventory_hostname }}" + + roles: + - role: helm diff --git a/tests/integration/targets/helm_v3_17_0/runme.sh b/tests/integration/targets/helm_v3_17_0/runme.sh new file mode 100755 index 00000000..2b5bb1b6 --- /dev/null +++ b/tests/integration/targets/helm_v3_17_0/runme.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eux +export ANSIBLE_ROLES_PATH=../ +ansible-playbook play.yaml -i inventory.ini "$@" \ No newline at end of file diff --git a/tests/integration/targets/helm_v4_0_0/aliases b/tests/integration/targets/helm_v4_0_0/aliases new file mode 100644 index 00000000..a80cfed0 --- /dev/null +++ b/tests/integration/targets/helm_v4_0_0/aliases @@ -0,0 +1,4 @@ +helm +helm_template +helm_info +helm_repository \ No newline at end of file diff --git a/tests/integration/targets/helm_v4_0_0/inventory.ini b/tests/integration/targets/helm_v4_0_0/inventory.ini new file mode 100644 index 00000000..066a9cee --- /dev/null +++ b/tests/integration/targets/helm_v4_0_0/inventory.ini @@ -0,0 +1,2 @@ +[all] +v4.0.0 \ No newline at end of file diff --git a/tests/integration/targets/helm_v4_0_0/play.yaml b/tests/integration/targets/helm_v4_0_0/play.yaml new file mode 100644 index 00000000..5591456a --- /dev/null +++ b/tests/integration/targets/helm_v4_0_0/play.yaml @@ -0,0 +1,11 @@ +- name: Run tests for Helm v4.0.0 + hosts: all + connection: local + gather_facts: true + + vars: + ansible_python_interpreter: "{{ ansible_playbook_python }}" + helm_version: "{{ inventory_hostname }}" + + roles: + - role: helm diff --git a/tests/integration/targets/helm_v4_0_0/runme.sh b/tests/integration/targets/helm_v4_0_0/runme.sh new file mode 100755 index 00000000..2b5bb1b6 --- /dev/null +++ b/tests/integration/targets/helm_v4_0_0/runme.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eux +export ANSIBLE_ROLES_PATH=../ +ansible-playbook play.yaml -i inventory.ini "$@" \ No newline at end of file diff --git a/tests/integration/targets/install_helm/defaults/main.yml b/tests/integration/targets/install_helm/defaults/main.yml index 19c3c290..d62c8fc5 100644 --- a/tests/integration/targets/install_helm/defaults/main.yml +++ b/tests/integration/targets/install_helm/defaults/main.yml @@ -1,4 +1,4 @@ --- helm_version: v3.16.4 helm_install_path: /tmp/helm -helm_default_archive_name: "helm-{{ helm_version }}-{{ ansible_system | lower }}-amd64.tar.gz" +helm_default_archive_name: "https://get.helm.sh/helm-{{ helm_version }}-{{ ansible_system | lower }}-{{ ansible_architecture | lower }}.tar.gz" diff --git a/tests/integration/targets/install_helm/tasks/main.yml b/tests/integration/targets/install_helm/tasks/main.yml index 49e36a46..3fce576f 100644 --- a/tests/integration/targets/install_helm/tasks/main.yml +++ b/tests/integration/targets/install_helm/tasks/main.yml @@ -4,12 +4,27 @@ path: "{{ helm_install_path }}" state: directory -- name: Unarchive Helm binary - unarchive: - src: "https://get.helm.sh/{{ helm_archive_name | default(helm_default_archive_name) }}" - dest: "{{ helm_install_path }}" - remote_src: yes - retries: 10 - delay: 5 - register: result - until: result is not failed +- ansible.builtin.set_fact: + os_path: "{{ lookup('env', 'PATH') }}" + +- name: Download the Helm install script + ansible.builtin.get_url: + url: "https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-{{ major_version }}" + dest: /tmp/get_helm.sh + mode: '0700' + vars: + major_version: "{{ helm_version | split('.') | first | replace('v', '') }}" + +- name: Run the install script (helm version = {{ helm_version }}) + ansible.builtin.command: /tmp/get_helm.sh + environment: + DESIRED_VERSION: "{{ helm_version }}" + HELM_INSTALL_DIR: "{{ helm_install_path }}" + PATH: "{{ os_path }}:{{ helm_install_path }}" + VERIFY_CHECKSUM: "false" + register: helm_install_result + changed_when: "'is already at the latest version' not in helm_install_result.stdout" + +- name: Save Helm binary path for later use + ansible.builtin.set_fact: + helm_binary: "{{ helm_install_path }}/helm" diff --git a/tests/sanity/ignore-2.16.txt b/tests/sanity/ignore-2.16.txt index e5003b5f..6d6103ec 100644 --- a/tests/sanity/ignore-2.16.txt +++ b/tests/sanity/ignore-2.16.txt @@ -30,5 +30,4 @@ plugins/modules/k8s_scale.py validate-modules:return-syntax-error plugins/modules/k8s_service.py validate-modules:return-syntax-error plugins/modules/k8s_taint.py validate-modules:return-syntax-error tests/integration/targets/helm_diff/files/test-chart-reuse-values/templates/configmap.yaml yamllint!skip -tests/integration/targets/helm_registry_auth/tasks/main.yaml yamllint!skip tests/integration/targets/helm_diff/files/test-chart-deployment-time/templates/configmap.yaml yamllint!skip diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt index e5003b5f..6d6103ec 100644 --- a/tests/sanity/ignore-2.17.txt +++ b/tests/sanity/ignore-2.17.txt @@ -30,5 +30,4 @@ plugins/modules/k8s_scale.py validate-modules:return-syntax-error plugins/modules/k8s_service.py validate-modules:return-syntax-error plugins/modules/k8s_taint.py validate-modules:return-syntax-error tests/integration/targets/helm_diff/files/test-chart-reuse-values/templates/configmap.yaml yamllint!skip -tests/integration/targets/helm_registry_auth/tasks/main.yaml yamllint!skip tests/integration/targets/helm_diff/files/test-chart-deployment-time/templates/configmap.yaml yamllint!skip diff --git a/tests/sanity/ignore-2.18.txt b/tests/sanity/ignore-2.18.txt index d6216adf..c804f90b 100644 --- a/tests/sanity/ignore-2.18.txt +++ b/tests/sanity/ignore-2.18.txt @@ -27,5 +27,4 @@ plugins/modules/k8s_scale.py validate-modules:return-syntax-error plugins/modules/k8s_service.py validate-modules:return-syntax-error plugins/modules/k8s_taint.py validate-modules:return-syntax-error tests/integration/targets/helm_diff/files/test-chart-reuse-values/templates/configmap.yaml yamllint!skip -tests/integration/targets/helm_registry_auth/tasks/main.yaml yamllint!skip tests/integration/targets/helm_diff/files/test-chart-deployment-time/templates/configmap.yaml yamllint!skip diff --git a/tests/sanity/ignore-2.19.txt b/tests/sanity/ignore-2.19.txt index d6216adf..c804f90b 100644 --- a/tests/sanity/ignore-2.19.txt +++ b/tests/sanity/ignore-2.19.txt @@ -27,5 +27,4 @@ plugins/modules/k8s_scale.py validate-modules:return-syntax-error plugins/modules/k8s_service.py validate-modules:return-syntax-error plugins/modules/k8s_taint.py validate-modules:return-syntax-error tests/integration/targets/helm_diff/files/test-chart-reuse-values/templates/configmap.yaml yamllint!skip -tests/integration/targets/helm_registry_auth/tasks/main.yaml yamllint!skip tests/integration/targets/helm_diff/files/test-chart-deployment-time/templates/configmap.yaml yamllint!skip diff --git a/tests/sanity/ignore-2.20.txt b/tests/sanity/ignore-2.20.txt index 2bd89903..bfe7532f 100644 --- a/tests/sanity/ignore-2.20.txt +++ b/tests/sanity/ignore-2.20.txt @@ -30,5 +30,4 @@ plugins/modules/k8s_scale.py validate-modules:return-syntax-error plugins/modules/k8s_service.py validate-modules:return-syntax-error plugins/modules/k8s_taint.py validate-modules:return-syntax-error tests/integration/targets/helm_diff/files/test-chart-reuse-values/templates/configmap.yaml yamllint!skip -tests/integration/targets/helm_registry_auth/tasks/main.yaml yamllint!skip tests/integration/targets/helm_diff/files/test-chart-deployment-time/templates/configmap.yaml yamllint!skip diff --git a/tests/sanity/ignore-2.21.txt b/tests/sanity/ignore-2.21.txt index 35967e23..932e7ef4 100644 --- a/tests/sanity/ignore-2.21.txt +++ b/tests/sanity/ignore-2.21.txt @@ -30,7 +30,6 @@ plugins/modules/k8s_scale.py validate-modules:return-syntax-error plugins/modules/k8s_service.py validate-modules:return-syntax-error plugins/modules/k8s_taint.py validate-modules:return-syntax-error tests/integration/targets/helm_diff/files/test-chart-reuse-values/templates/configmap.yaml yamllint!skip -tests/integration/targets/helm_registry_auth/tasks/main.yaml yamllint!skip tests/integration/targets/helm_diff/files/test-chart-deployment-time/templates/configmap.yaml yamllint!skip plugins/modules/helm.py validate-modules:bad-return-value-key plugins/modules/helm_info.py validate-modules:bad-return-value-key diff --git a/tests/unit/module_utils/test_helm.py b/tests/unit/module_utils/helm/test_helm.py similarity index 98% rename from tests/unit/module_utils/test_helm.py rename to tests/unit/module_utils/helm/test_helm.py index eef323b5..47eb1f69 100644 --- a/tests/unit/module_utils/test_helm.py +++ b/tests/unit/module_utils/helm/test_helm.py @@ -455,9 +455,9 @@ def test_module_get_helm_set_values_args(set_values, expected): ("3.17.0", False), ("2.9.0", True), ("2.17.0", True), - ("4.0.0", True), - ("4.1.0", True), - ("5.0.0", True), + ("4.0.0", False), + ("4.1.0", False), + ("5.0.0", False), ], ) def test_module_validate_helm_version(_ansible_helm_module, helm_version, should_fail): @@ -469,8 +469,10 @@ def test_module_validate_helm_version(_ansible_helm_module, helm_version, should _ansible_helm_module.validate_helm_version() _ansible_helm_module.fail_json.assert_called_once() call_args = _ansible_helm_module.fail_json.call_args - assert "Helm version must be >=3.0.0,<4.0.0" in call_args[1]["msg"] - assert helm_version in call_args[1]["msg"] + assert ( + f"Helm version must be >= 3.0.0, current version is {helm_version}" + == call_args[1]["msg"] + ) else: _ansible_helm_module.validate_helm_version() _ansible_helm_module.fail_json.assert_not_called() diff --git a/tests/unit/module_utils/helm/test_helm_plugin_list.py b/tests/unit/module_utils/helm/test_helm_plugin_list.py new file mode 100644 index 00000000..36cc49c0 --- /dev/null +++ b/tests/unit/module_utils/helm/test_helm_plugin_list.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2026, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import pytest +from ansible_collections.kubernetes.core.plugins.module_utils.helm import ( + parse_helm_plugin_list, +) + + +def test_parse_helm_plugin_list_empty(): + assert parse_helm_plugin_list() == [] + + +@pytest.mark.parametrize( + "output,expected", + [ + ( + """ +NAME VERSION TYPE APIVERSION PROVENANCE SOURCE +diff 3.4.1 cli/v1 legacy unknown unknown + + """, + [ + dict( + name="diff", + version="3.4.1", + type="cli/v1", + apiversion="legacy", + provenance="unknown", + source="unknown", + ) + ], + ), + ( + """ +NAME VERSION DESCRIPTION +diff 3.4.1 Preview helm upgrade changes as a diff + """, + [ + dict( + name="diff", + version="3.4.1", + description="Preview helm upgrade changes as a diff", + ) + ], + ), + ], +) +def test_parse_helm_plugin_list_values(output, expected): + assert parse_helm_plugin_list(output.split("\n")) == expected