From 5a0f5d6b93c8eced292f19d31c1fbbd6f569a667 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Tue, 20 Apr 2021 12:07:28 +0530 Subject: [PATCH] helm: handle multiline output (#64) Fixes: #399 Signed-off-by: Abhijeet Kasurde --- changelogs/fragments/399-helm_multiline.yml | 2 + .../helm/files/sample_plugin/plugin.yaml | 11 ++ .../roles/helm/tasks/tests_helm_plugin.yml | 33 ++++++ plugins/module_utils/helm.py | 41 +++++++ plugins/modules/helm.py | 23 ++-- plugins/modules/helm_plugin.py | 105 +++++++++--------- plugins/modules/helm_plugin_info.py | 66 ++++++----- 7 files changed, 187 insertions(+), 94 deletions(-) create mode 100644 changelogs/fragments/399-helm_multiline.yml create mode 100644 molecule/default/roles/helm/files/sample_plugin/plugin.yaml diff --git a/changelogs/fragments/399-helm_multiline.yml b/changelogs/fragments/399-helm_multiline.yml new file mode 100644 index 00000000..c4b8b4d0 --- /dev/null +++ b/changelogs/fragments/399-helm_multiline.yml @@ -0,0 +1,2 @@ +bugfixes: +- helm - handle multiline output of ``helm plugin list`` command (https://github.com/ansible-collections/community.kubernetes/issues/399). diff --git a/molecule/default/roles/helm/files/sample_plugin/plugin.yaml b/molecule/default/roles/helm/files/sample_plugin/plugin.yaml new file mode 100644 index 00000000..8703bc29 --- /dev/null +++ b/molecule/default/roles/helm/files/sample_plugin/plugin.yaml @@ -0,0 +1,11 @@ +name: "sample_plugin" +version: "0.0.1" +usage: "Sample Helm Plugin" +description: |- + This plugin provides sample plugin to Helm. + usage: + This is new line + This is another line +ignoreFlags: false +useTunnel: false +command: "$HELM_PLUGIN_DIR/main.sh" diff --git a/molecule/default/roles/helm/tasks/tests_helm_plugin.yml b/molecule/default/roles/helm/tasks/tests_helm_plugin.yml index 3582dcc4..31252326 100644 --- a/molecule/default/roles/helm/tasks/tests_helm_plugin.yml +++ b/molecule/default/roles/helm/tasks/tests_helm_plugin.yml @@ -75,3 +75,36 @@ - assert: that: - not uninstall_env.changed + +# https://github.com/ansible-collections/community.kubernetes/issues/399 +- 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: + 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" diff --git a/plugins/module_utils/helm.py b/plugins/module_utils/helm.py index 021a74da..533828f2 100644 --- a/plugins/module_utils/helm.py +++ b/plugins/module_utils/helm.py @@ -117,3 +117,44 @@ def write_temp_kubeconfig(server, validate_certs=True, ca_cert=None): with os.fdopen(_fd, 'w') as fp: yaml.dump(content, fp) return file_name + + +def get_helm_plugin_list(module, helm_bin=None): + """ + Return `helm plugin list` + """ + if not helm_bin: + return [] + helm_plugin_list = helm_bin + " list" + rc, out, err = run_helm(module, helm_plugin_list) + if rc != 0 or (out == '' and err == ''): + module.fail_json( + msg="Failed to get Helm plugin info", + command=helm_plugin_list, + stdout=out, + stderr=err, + rc=rc, + ) + return (rc, out, err) + + +def parse_helm_plugin_list(module, output=None): + """ + Parse `helm plugin list`, return list of plugins + """ + ret = [] + if not output: + return ret + + for line in output: + if line.startswith("NAME"): + continue + name, version, description = line.split('\t', 3) + name = name.strip() + version = version.strip() + description = description.strip() + if name == '': + continue + ret.append((name, version, description)) + + return ret diff --git a/plugins/modules/helm.py b/plugins/modules/helm.py index 489e9ace..89863225 100644 --- a/plugins/modules/helm.py +++ b/plugins/modules/helm.py @@ -272,7 +272,12 @@ except ImportError: IMP_YAML = False from ansible.module_utils.basic import AnsibleModule, missing_required_lib, env_fallback -from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm, get_values +from ansible_collections.kubernetes.core.plugins.module_utils.helm import ( + run_helm, + get_values, + get_helm_plugin_list, + parse_helm_plugin_list +) def get_release(state, release_name): @@ -413,13 +418,15 @@ def has_plugin(command, plugin): Check if helm plugin is installed. """ - cmd = command + " plugin list" - rc, out, err = run_helm(module, cmd) - for line in out.splitlines(): - if line.startswith("NAME"): - continue - name, _rest = line.split(None, 1) - if name == plugin: + cmd = command + " plugin" + rc, output, err = get_helm_plugin_list(module, helm_bin=cmd) + out = parse_helm_plugin_list(module, output=output.splitlines()) + + if not out: + return False + + for line in out: + if line[0] == plugin: return True return False diff --git a/plugins/modules/helm_plugin.py b/plugins/modules/helm_plugin.py index 430f91f2..357ee817 100644 --- a/plugins/modules/helm_plugin.py +++ b/plugins/modules/helm_plugin.py @@ -4,6 +4,7 @@ # Copyright: (c) 2020, 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 @@ -96,7 +97,11 @@ rc: ''' from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm +from ansible_collections.kubernetes.core.plugins.module_utils.helm import ( + run_helm, + get_helm_plugin_list, + parse_helm_plugin_list +) def main(): @@ -180,60 +185,58 @@ def main(): ) elif state == 'absent': plugin_name = module.params.get('plugin_name') - helm_plugin_list = helm_cmd_common + " list" - rc, out, err = run_helm(module, helm_plugin_list) - if rc != 0 or (out == '' and err == ''): - module.fail_json( - msg="Failed to get Helm plugin info", - command=helm_plugin_list, - stdout=out, + rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common) + out = parse_helm_plugin_list(module, output=output.splitlines()) + + if not out: + module.exit_json( + failed=False, + changed=False, + msg="Plugin not found or is already uninstalled", + command=helm_cmd_common + " list", + stdout=output, stderr=err, - rc=rc, + rc=rc ) - if out: - found = False - for line in out.splitlines(): - if line.startswith("NAME"): - continue - name, dummy, dummy = line.split('\t', 3) - name = name.strip() - if name == plugin_name: - found = True - break - if found: - helm_uninstall_cmd = "%s uninstall %s" % (helm_cmd_common, plugin_name) - if not module.check_mode: - rc, out, err = run_helm(module, helm_uninstall_cmd, fails_on_error=False) - else: - rc, out, err = (0, '', '') + found = False + for line in out: + if line[0] == plugin_name: + found = True + break + if not found: + module.exit_json( + failed=False, + changed=False, + msg="Plugin not found or is already uninstalled", + command=helm_cmd_common + " list", + stdout=output, + stderr=err, + rc=rc + ) - if rc == 0: - module.exit_json( - changed=True, - msg="Plugin uninstalled successfully", - command=helm_uninstall_cmd, - stdout=out, - stderr=err, - rc=rc - ) - module.fail_json( - msg="Failed to get Helm plugin uninstall", - command=helm_uninstall_cmd, - stdout=out, - stderr=err, - rc=rc, - ) - else: - module.exit_json( - failed=False, - changed=False, - msg="Plugin not found or is already uninstalled", - command=helm_plugin_list, - stdout=out, - stderr=err, - rc=rc - ) + helm_uninstall_cmd = "%s uninstall %s" % (helm_cmd_common, plugin_name) + if not module.check_mode: + rc, out, err = run_helm(module, helm_uninstall_cmd, fails_on_error=False) + else: + rc, out, err = (0, '', '') + + if rc == 0: + module.exit_json( + changed=True, + msg="Plugin uninstalled successfully", + command=helm_uninstall_cmd, + stdout=out, + stderr=err, + rc=rc + ) + module.fail_json( + msg="Failed to get Helm plugin uninstall", + command=helm_uninstall_cmd, + stdout=out, + stderr=err, + rc=rc, + ) if __name__ == '__main__': diff --git a/plugins/modules/helm_plugin_info.py b/plugins/modules/helm_plugin_info.py index db46d64d..b1b86b17 100644 --- a/plugins/modules/helm_plugin_info.py +++ b/plugins/modules/helm_plugin_info.py @@ -4,6 +4,7 @@ # Copyright: (c) 2020, 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 @@ -77,7 +78,10 @@ rc: ''' from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm +from ansible_collections.kubernetes.core.plugins.module_utils.helm import ( + get_helm_plugin_list, + parse_helm_plugin_list, +) def main(): @@ -117,46 +121,38 @@ def main(): helm_cmd_common += " plugin" plugin_name = module.params.get('plugin_name') - helm_plugin_list = helm_cmd_common + " list" - rc, out, err = run_helm(module, helm_plugin_list) - if rc != 0 or (out == '' and err == ''): - module.fail_json( - msg="Failed to get Helm plugin info", - command=helm_plugin_list, - stdout=out, - stderr=err, - rc=rc, - ) plugin_list = [] - if out: - for line in out.splitlines(): - if line.startswith("NAME"): - continue - name, version, description = line.split('\t', 3) - name = name.strip() - version = version.strip() - description = description.strip() - if plugin_name is None: - plugin_list.append({ - 'name': name, - 'version': version, - 'description': description, - }) - continue - if plugin_name == name: - plugin_list.append({ - 'name': name, - 'version': version, - 'description': description, - }) - break + rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common) + + out = parse_helm_plugin_list(module, 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 module.exit_json( changed=True, - command=helm_plugin_list, - stdout=out, + command=helm_cmd_common + " list", + stdout=output, stderr=err, rc=rc, plugin_list=plugin_list,