From 775959c3f9cc90a989b0d6d48005d15e1e4dd6a0 Mon Sep 17 00:00:00 2001 From: Matteo Danelon <92927530+DaruZero@users.noreply.github.com> Date: Thu, 12 Jun 2025 12:39:40 +0200 Subject: [PATCH] Add `plain_http` parameter to `helm`, `helm_pull` and `helm_template` (#934) SUMMARY This change introduces the plain_http parameter to modules that can interact with OCI registries. This in needed in cases where the OCI registry does not use SSL encryption, forcing Helm to send HTTP requests instead of HTTPS ISSUE TYPE Feature Pull Request COMPONENT NAME helm, helm_pull and helm_template ADDITIONAL INFORMATION This is the output when trying to use an OCI registry that is not configured to use SSL certs. fatal: [localhost]: FAILED! => {"changed": false, "command": "/usr/local/bin/helm show chart 'oci:///charts/foo'", "msg": "Failure when executing Helm command. Exited 1.\nstdout: \nstderr: Error: Get \"https:///v2/charts/foo/tags/list\": http: server gave HTTP response to HTTPS client\n", "stderr": "Error: Get \"https:///v2/charts/foo/tags/list\": http: server gave HTTP response to HTTPS client\n", "stderr_lines": ["Error: Get \"https:///v2/charts/foo/tags/list\": http: server gave HTTP response to HTTPS client"], "stdout": "", "stdout_lines": []} Reviewed-by: Bikouo Aubin Reviewed-by: Matteo Danelon --- ...522-add-plain-http-for-oci-registries.yaml | 4 + docs/kubernetes.core.helm_module.rst | 20 ++++ docs/kubernetes.core.helm_pull_module.rst | 20 ++++ docs/kubernetes.core.helm_template_module.rst | 20 ++++ plugins/modules/helm.py | 61 +++++++++++- plugins/modules/helm_pull.py | 10 ++ plugins/modules/helm_template.py | 26 +++++ .../targets/helm_plain_http/aliases | 3 + .../targets/helm_plain_http/inventory.ini | 3 + .../helm_plain_http/playbooks/play.yaml | 14 +++ .../helm_plain_http/playbooks/tasks/test.yaml | 99 +++++++++++++++++++ .../targets/helm_plain_http/runme.sh | 4 + 12 files changed, 282 insertions(+), 2 deletions(-) create mode 100644 changelogs/fragments/20250522-add-plain-http-for-oci-registries.yaml create mode 100644 tests/integration/targets/helm_plain_http/aliases create mode 100644 tests/integration/targets/helm_plain_http/inventory.ini create mode 100644 tests/integration/targets/helm_plain_http/playbooks/play.yaml create mode 100644 tests/integration/targets/helm_plain_http/playbooks/tasks/test.yaml create mode 100755 tests/integration/targets/helm_plain_http/runme.sh diff --git a/changelogs/fragments/20250522-add-plain-http-for-oci-registries.yaml b/changelogs/fragments/20250522-add-plain-http-for-oci-registries.yaml new file mode 100644 index 00000000..7b170280 --- /dev/null +++ b/changelogs/fragments/20250522-add-plain-http-for-oci-registries.yaml @@ -0,0 +1,4 @@ +minor_changes: + - helm - Parameter plain_http added for working with insecure OCI registries (https://github.com/ansible-collections/kubernetes.core/pull/934). + - helm_pull - Parameter plain_http added for working with insecure OCI registries (https://github.com/ansible-collections/kubernetes.core/pull/934). + - helm_template - Parameter plain_http added for working with insecure OCI registries (https://github.com/ansible-collections/kubernetes.core/pull/934). diff --git a/docs/kubernetes.core.helm_module.rst b/docs/kubernetes.core.helm_module.rst index e7df00d3..b81eb983 100644 --- a/docs/kubernetes.core.helm_module.rst +++ b/docs/kubernetes.core.helm_module.rst @@ -716,6 +716,26 @@ Parameters
The use of wait_timeout to wait for kubernetes commands to complete has been deprecated and will be removed after 2022-12-01.
+ + +
+ plain_http + +
+ boolean +
+
added in 5.1.0
+ + +
    Choices: +
  • no ←
  • +
  • yes
  • +
+ + +
Use HTTP instead of HTTPS when working with OCI registries
+ +
diff --git a/docs/kubernetes.core.helm_pull_module.rst b/docs/kubernetes.core.helm_pull_module.rst index ac3cc5c5..0ccdec76 100644 --- a/docs/kubernetes.core.helm_pull_module.rst +++ b/docs/kubernetes.core.helm_pull_module.rst @@ -336,6 +336,26 @@ Parameters
location of public keys used for verification.
+ + +
+ plain_http + +
+ boolean +
+
added in 5.1.0
+ + +
    Choices: +
  • no ←
  • +
  • yes
  • +
+ + +
Use HTTP instead of HTTPS when working with OCI registries
+ +
diff --git a/docs/kubernetes.core.helm_template_module.rst b/docs/kubernetes.core.helm_template_module.rst index 983616d3..9bb5398f 100644 --- a/docs/kubernetes.core.helm_template_module.rst +++ b/docs/kubernetes.core.helm_template_module.rst @@ -361,6 +361,26 @@ Parameters
Paths are evaluated in the order the paths are specified.
+ + +
+ plain_http + +
+ boolean +
+
added in 5.1.0
+ + +
    Choices: +
  • no ←
  • +
  • yes
  • +
+ + +
Use HTTP instead of HTTPS when working with OCI registries
+ +
diff --git a/plugins/modules/helm.py b/plugins/modules/helm.py index 8940158d..8d8ecc25 100644 --- a/plugins/modules/helm.py +++ b/plugins/modules/helm.py @@ -237,6 +237,13 @@ options: default: False aliases: [ skip_tls_certs_check ] version_added: 5.3.0 + plain_http: + description: + - Use HTTP instead of HTTPS when working with OCI registries + - Requires Helm >= 3.13.0 + type: bool + default: False + version_added: 6.1.0 extends_documentation_fragment: - kubernetes.core.helm_common_options """ @@ -319,6 +326,12 @@ EXAMPLES = r""" chart_ref: "https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz" release_namespace: monitoring +- name: Deploy Bitnami's MongoDB latest chart from OCI registry + kubernetes.core.helm: + name: test + chart_ref: "oci://registry-1.docker.io/bitnamicharts/mongodb" + release_namespace: database + # Using complex Values - name: Deploy new-relic client chart kubernetes.core.helm: @@ -495,7 +508,9 @@ def run_dep_update(module, chart_ref): rc, out, err = module.run_helm_command(dep_update) -def fetch_chart_info(module, command, chart_ref, insecure_skip_tls_verify=False): +def fetch_chart_info( + module, command, chart_ref, insecure_skip_tls_verify=False, plain_http=False +): """ Get chart info """ @@ -504,6 +519,17 @@ def fetch_chart_info(module, command, chart_ref, insecure_skip_tls_verify=False) if insecure_skip_tls_verify: inspect_command += " --insecure-skip-tls-verify" + if plain_http: + helm_version = module.get_helm_version() + if LooseVersion(helm_version) < LooseVersion("3.13.0"): + module.fail_json( + msg="plain_http requires helm >= 3.13.0, current version is {0}".format( + helm_version + ) + ) + else: + inspect_command += " --plain-http" + rc, out, err = module.run_helm_command(inspect_command) return yaml.safe_load(out) @@ -533,6 +559,7 @@ def deploy( reset_values=True, reset_then_reuse_values=False, insecure_skip_tls_verify=False, + plain_http=False, ): """ Install/upgrade/rollback release chart @@ -595,6 +622,9 @@ def deploy( else: deploy_command += " --insecure-skip-tls-verify" + if plain_http: + deploy_command += " --plain-http" + if values_files: for value_file in values_files: deploy_command += " --values=" + value_file @@ -690,6 +720,7 @@ def helmdiff_check( reset_values=True, reset_then_reuse_values=False, insecure_skip_tls_verify=False, + plain_http=False, ): """ Use helm diff to determine if a release would change by upgrading a chart. @@ -745,6 +776,17 @@ def helmdiff_check( if insecure_skip_tls_verify: cmd += " --insecure-skip-tls-verify" + if plain_http: + helm_version = module.get_helm_version() + if LooseVersion(helm_version) < LooseVersion("3.13.0"): + module.fail_json( + msg="plain_http requires helm >= 3.13.0, current version is {0}".format( + helm_version + ) + ) + else: + cmd += " --plain-http" + rc, out, err = module.run_helm_command(cmd) return (len(out.strip()) > 0, out.strip()) @@ -808,6 +850,7 @@ def argument_spec(): insecure_skip_tls_verify=dict( type="bool", default=False, aliases=["skip_tls_certs_check"] ), + plain_http=dict(type="bool", default=False), ) ) return arg_spec @@ -862,6 +905,7 @@ def main(): reset_values = module.params.get("reset_values") reset_then_reuse_values = module.params.get("reset_then_reuse_values") insecure_skip_tls_verify = module.params.get("insecure_skip_tls_verify") + plain_http = module.params.get("plain_http") if update_repo_cache: run_repo_update(module) @@ -871,6 +915,16 @@ def main(): release_status = get_release_status(module, release_name, all_status=all_status) helm_cmd = module.get_helm_binary() + + if plain_http: + helm_version = module.get_helm_version() + if LooseVersion(helm_version) < LooseVersion("3.13.0"): + module.fail_json( + msg="plain_http requires helm >= 3.13.0, current version is {0}".format( + helm_version + ) + ) + opt_result = {} if release_state == "absent" and release_status is not None: # skip release statuses 'uninstalled' and 'uninstalling' @@ -900,7 +954,7 @@ def main(): # Fetch chart info to have real version and real name for chart_ref from archive, folder or url chart_info = fetch_chart_info( - module, helm_cmd, chart_ref, insecure_skip_tls_verify + module, helm_cmd, chart_ref, insecure_skip_tls_verify, plain_http ) if dependency_update: @@ -962,6 +1016,7 @@ def main(): reset_values=reset_values, reset_then_reuse_values=reset_then_reuse_values, insecure_skip_tls_verify=insecure_skip_tls_verify, + plain_http=plain_http, ) changed = True @@ -989,6 +1044,7 @@ def main(): reset_values=reset_values, reset_then_reuse_values=reset_then_reuse_values, insecure_skip_tls_verify=insecure_skip_tls_verify, + plain_http=plain_http, ) if would_change and module._diff: opt_result["diff"] = {"prepared": prepared} @@ -1026,6 +1082,7 @@ def main(): reset_values=reset_values, reset_then_reuse_values=reset_then_reuse_values, insecure_skip_tls_verify=insecure_skip_tls_verify, + plain_http=plain_http, ) changed = True diff --git a/plugins/modules/helm_pull.py b/plugins/modules/helm_pull.py index b6ec273e..ebf414d3 100644 --- a/plugins/modules/helm_pull.py +++ b/plugins/modules/helm_pull.py @@ -114,6 +114,13 @@ options: - The path of a helm binary to use. required: false type: path + plain_http: + description: + - Use HTTP instead of HTTPS when working with OCI registries + - Requires Helm >= 3.13.0 + type: bool + default: False + version_added: 6.1.0 """ EXAMPLES = r""" @@ -201,6 +208,7 @@ def main(): chart_ssl_cert_file=dict(type="path"), chart_ssl_key_file=dict(type="path"), binary_path=dict(type="path"), + plain_http=dict(type="bool", default=False), ) module = AnsibleHelmModule( argument_spec=argspec, @@ -225,6 +233,7 @@ def main(): chart_ca_cert="3.1.0", chart_ssl_cert_file="3.1.0", chart_ssl_key_file="3.1.0", + plain_http="3.13.0", ) def test_version_requirement(opt): @@ -264,6 +273,7 @@ def main(): skip_tls_certs_check=dict(key="insecure-skip-tls-verify"), chart_devel=dict(key="devel"), untar_chart=dict(key="untar"), + plain_http=dict(key="plain-http"), ) for k, v in helm_flag_args.items(): diff --git a/plugins/modules/helm_template.py b/plugins/modules/helm_template.py index 16a1131c..b0f0079e 100644 --- a/plugins/modules/helm_template.py +++ b/plugins/modules/helm_template.py @@ -147,6 +147,13 @@ options: - json - file version_added: 2.4.0 + plain_http: + description: + - Use HTTP instead of HTTPS when working with OCI registries + - Requires Helm >= 3.13.0 + type: bool + default: False + version_added: 6.1.0 """ EXAMPLES = r""" @@ -218,6 +225,9 @@ from ansible.module_utils.basic import missing_required_lib from ansible_collections.kubernetes.core.plugins.module_utils.helm import ( AnsibleHelmModule, ) +from ansible_collections.kubernetes.core.plugins.module_utils.version import ( + LooseVersion, +) def template( @@ -236,6 +246,7 @@ def template( values_files=None, include_crds=False, set_values=None, + plain_http=False, ): cmd += " template " @@ -262,6 +273,9 @@ def template( if insecure_registry: cmd += " --insecure-skip-tls-verify" + if plain_http: + cmd += " --plain-http" + if show_only: for template in show_only: cmd += " -s " + template @@ -307,6 +321,7 @@ def main(): values_files=dict(type="list", default=[], elements="str"), update_repo_cache=dict(type="bool", default=False), set_values=dict(type="list", elements="dict"), + plain_http=dict(type="bool", default=False), ), supports_check_mode=True, ) @@ -327,12 +342,22 @@ def main(): values_files = module.params.get("values_files") update_repo_cache = module.params.get("update_repo_cache") set_values = module.params.get("set_values") + plain_http = module.params.get("plain_http") if not IMP_YAML: module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR) helm_cmd = module.get_helm_binary() + if plain_http: + helm_version = module.get_helm_version() + if LooseVersion(helm_version) < LooseVersion("3.13.0"): + module.fail_json( + msg="plain_http requires helm >= 3.13.0, current version is {0}".format( + helm_version + ) + ) + if update_repo_cache: update_cmd = helm_cmd + " repo update" module.run_helm_command(update_cmd) @@ -357,6 +382,7 @@ def main(): values_files=values_files, include_crds=include_crds, set_values=set_values_args, + plain_http=plain_http, ) if not check_mode: diff --git a/tests/integration/targets/helm_plain_http/aliases b/tests/integration/targets/helm_plain_http/aliases new file mode 100644 index 00000000..069f0553 --- /dev/null +++ b/tests/integration/targets/helm_plain_http/aliases @@ -0,0 +1,3 @@ +helm_template +helm_pull +helm \ No newline at end of file diff --git a/tests/integration/targets/helm_plain_http/inventory.ini b/tests/integration/targets/helm_plain_http/inventory.ini new file mode 100644 index 00000000..cdb5f7dc --- /dev/null +++ b/tests/integration/targets/helm_plain_http/inventory.ini @@ -0,0 +1,3 @@ +[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 diff --git a/tests/integration/targets/helm_plain_http/playbooks/play.yaml b/tests/integration/targets/helm_plain_http/playbooks/play.yaml new file mode 100644 index 00000000..4ef540b9 --- /dev/null +++ b/tests/integration/targets/helm_plain_http/playbooks/play.yaml @@ -0,0 +1,14 @@ +- name: Run test for helm plain http option + hosts: all + gather_facts: true + + vars: + ansible_connection: local + ansible_python_interpreter: "{{ ansible_playbook_python }}" + chart_test_oci: "oci://registry-1.docker.io/bitnamicharts/redis" + + roles: + - 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 new file mode 100644 index 00000000..e9bd2a2f --- /dev/null +++ b/tests/integration/targets/helm_plain_http/playbooks/tasks/test.yaml @@ -0,0 +1,99 @@ +--- +- name: Run test for helm + block: + - name: Create temporary directory to install chart In + ansible.builtin.tempfile: + state: directory + suffix: .helm + register: install_path + + - name: Install required helm version + ansible.builtin.include_role: + name: install_helm + 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: + binary_path: "{{ helm_binary }}" + chart_ref: "{{ chart_test_oci }}" + release_name: test-secure + release_namespace: "{{ test_namespace }}" + create_namespace: true + plain_http: true + register: install_chart + ignore_errors: true + + - name: Ensure module failed as expected + ansible.builtin.assert: + that: + - install_chart is failed + - '"plain_http requires helm >= 3.13.0" in install_chart.msg' + when: tests_should_failed | bool + + - name: Ensure the result command contains the expected option + ansible.builtin.assert: + that: + - install_chart is not failed + - '"--plain-http" in install_chart.command' + when: not (tests_should_failed | bool) + + # helm_pull + - name: Trying to download helm chart with option plain_http + kubernetes.core.helm_pull: + chart_ref: "{{ chart_test_oci }}" + destination: "{{ playbook_dir }}" + binary_path: "{{ helm_binary }}" + plain_http: true + register: pull_chart + ignore_errors: true + + - name: Ensure module failed as expected + ansible.builtin.assert: + that: + - pull_chart is failed + - '"plain_http requires helm >= 3.13.0" in pull_chart.msg' + when: tests_should_failed | bool + + - name: Ensure the result command contains the expected option + ansible.builtin.assert: + that: + - pull_chart is not failed + - '"--plain-http" in pull_chart.command' + when: not (tests_should_failed | bool) + + # helm_template + - name: Test helm render template + kubernetes.core.helm_template: + binary_path: "{{ helm_binary }}" + chart_ref: "{{ chart_test_oci }}" + output_dir: "{{ playbook_dir }}" + plain_http: true + register: template + ignore_errors: true + + - name: Ensure module failed as expected + ansible.builtin.assert: + that: + - template is failed + - '"plain_http requires helm >= 3.13.0" in template.msg' + when: tests_should_failed | bool + + - name: Ensure the result command contains the expected option + ansible.builtin.assert: + that: + - template is not failed + - '"--plain-http" in template.command' + when: not (tests_should_failed | bool) + + always: + - name: Delete temporary file + ansible.builtin.file: + path: "{{ install_path.path }}" + state: absent + ignore_errors: true diff --git a/tests/integration/targets/helm_plain_http/runme.sh b/tests/integration/targets/helm_plain_http/runme.sh new file mode 100755 index 00000000..3cfa5280 --- /dev/null +++ b/tests/integration/targets/helm_plain_http/runme.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eux +export ANSIBLE_ROLES_PATH=../ +ansible-playbook playbooks/play.yaml -i inventory.ini "$@" \ No newline at end of file