From 882e672bc5999e11a49b8a68894b19555e74fe87 Mon Sep 17 00:00:00 2001 From: Bikouo Aubin <79859644+abikouo@users.noreply.github.com> Date: Thu, 14 Apr 2022 08:15:12 +0200 Subject: [PATCH] Remove omit from template resource (#432) Remove ``omit`` value from template args SUMMARY While defining resource using template parameter, the code does not remove the omit value if any. This fix adds a post process to remove any omit value from the resource definition. fixes #431 ISSUE TYPE Bugfix Pull Request COMPONENT NAME k8s* Reviewed-by: Mike Graves Reviewed-by: Abhijeet Kasurde --- ...x-issue-when-using-template-parameter.yaml | 3 + plugins/action/k8s_info.py | 32 +++++- .../targets/k8s_template/tasks/main.yml | 57 ++++++++++ .../k8s_template/templates/configmap.yml.j2 | 7 ++ tests/unit/action/test_remove_omit.py | 104 ++++++++++++++++++ 5 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 changelogs/fragments/432-fix-issue-when-using-template-parameter.yaml create mode 100644 tests/integration/targets/k8s_template/templates/configmap.yml.j2 create mode 100644 tests/unit/action/test_remove_omit.py diff --git a/changelogs/fragments/432-fix-issue-when-using-template-parameter.yaml b/changelogs/fragments/432-fix-issue-when-using-template-parameter.yaml new file mode 100644 index 00000000..ebbbe49d --- /dev/null +++ b/changelogs/fragments/432-fix-issue-when-using-template-parameter.yaml @@ -0,0 +1,3 @@ +--- +bugfixes: + - Remove `omit` placeholder when defining resource using template parameter (https://github.com/ansible-collections/kubernetes.core/issues/431). diff --git a/plugins/action/k8s_info.py b/plugins/action/k8s_info.py index 46ce64bd..cfcbb28d 100644 --- a/plugins/action/k8s_info.py +++ b/plugins/action/k8s_info.py @@ -12,7 +12,6 @@ import traceback import os from contextlib import contextmanager - from ansible.config.manager import ensure_type from ansible.errors import ( AnsibleError, @@ -26,6 +25,31 @@ from ansible.module_utils._text import to_text, to_bytes, to_native from ansible.plugins.action import ActionBase +class RemoveOmit(object): + def __init__(self, buffer, omit_value): + try: + import yaml + except ImportError: + raise AnsibleError("Failed to import the required Python library (PyYAML).") + self.data = yaml.safe_load_all(buffer) + self.omit = omit_value + + def remove_omit(self, data): + if isinstance(data, dict): + result = dict() + for key, value in iteritems(data): + if value == self.omit: + continue + result[key] = self.remove_omit(value) + return result + if isinstance(data, list): + return [self.remove_omit(v) for v in data if v != self.omit] + return data + + def output(self): + return [self.remove_omit(d) for d in self.data] + + class ActionModule(ActionBase): TRANSFERS_FILES = True @@ -180,6 +204,7 @@ class ActionModule(ActionBase): "'template' is only a supported parameter for the 'k8s' module." ) + omit_value = task_vars.get("omit") template_params = [] if isinstance(template, string_types) or isinstance(template, dict): template_params.append(self.get_template_args(template)) @@ -245,7 +270,10 @@ class ActionModule(ActionBase): preserve_trailing_newlines=True, escape_backslashes=False, ) - result_template.append(result) + if omit_value is not None: + result_template.extend(RemoveOmit(result, omit_value).output()) + else: + result_template.append(result) self._templar.available_variables = old_vars resource_definition = self._task.args.get("definition", None) if not resource_definition: diff --git a/tests/integration/targets/k8s_template/tasks/main.yml b/tests/integration/targets/k8s_template/tasks/main.yml index 2070f4e7..7aedecea 100644 --- a/tests/integration/targets/k8s_template/tasks/main.yml +++ b/tests/integration/targets/k8s_template/tasks/main.yml @@ -239,6 +239,63 @@ - resource.result.results | selectattr('changed') | list | length == 1 - resource.result.results | selectattr('error', 'defined') | list | length == 1 + # Test resource definition using template with 'omit' + - name: Deploy configmap using template + k8s: + namespace: "{{ template_namespace }}" + name: test-data + template: configmap.yml.j2 + + - name: Read configmap created + k8s_info: + kind: configmap + namespace: "{{ template_namespace }}" + name: test-data + register: _configmap + + - name: Validate that the configmap does not contains annotations + assert: + that: + - '"annotations" not in _configmap.resources.0.metadata' + + - name: Create resource once again + k8s: + namespace: "{{ template_namespace }}" + name: test-data + template: configmap.yml.j2 + register: _configmap + + - name: assert that nothing changed + assert: + that: + - _configmap is not changed + + - name: Create resource once again (using description) + k8s: + namespace: "{{ template_namespace }}" + name: test-data + template: configmap.yml.j2 + register: _configmap + vars: + k8s_configmap_desc: "This is a simple configmap used to test ansible k8s collection" + + - name: assert that configmap was changed + assert: + that: + - _configmap is changed + + - name: Read configmap created + k8s_info: + kind: configmap + namespace: "{{ template_namespace }}" + name: test-data + register: _configmap + + - name: Validate that the configmap does not contains annotations + assert: + that: + - _configmap.resources.0.metadata.annotations.description == "This is a simple configmap used to test ansible k8s collection" + always: - name: Remove namespace (Cleanup) kubernetes.core.k8s: diff --git a/tests/integration/targets/k8s_template/templates/configmap.yml.j2 b/tests/integration/targets/k8s_template/templates/configmap.yml.j2 new file mode 100644 index 00000000..bdca2e0b --- /dev/null +++ b/tests/integration/targets/k8s_template/templates/configmap.yml.j2 @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + annotations: + description: "{{ k8s_configmap_desc | default(omit) }}" +data: + key: "testing-template" \ No newline at end of file diff --git a/tests/unit/action/test_remove_omit.py b/tests/unit/action/test_remove_omit.py new file mode 100644 index 00000000..3432c19f --- /dev/null +++ b/tests/unit/action/test_remove_omit.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2022, 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 + +from datetime import datetime +from ansible_collections.kubernetes.core.plugins.action.k8s_info import RemoveOmit + + +def get_omit_token(): + return "__omit_place_holder__%s" % datetime.now().strftime("%Y%m%d%H%M%S") + + +def test_remove_omit_from_str(): + omit_token = get_omit_token() + src = """ + project: ansible + collection: {omit} + """.format( + omit=omit_token + ) + result = RemoveOmit(src, omit_value=omit_token).output() + assert len(result) == 1 + assert result[0] == dict(project="ansible") + + +def test_remove_omit_from_list(): + omit_token = get_omit_token() + src = """ + items: + - {omit} + """.format( + omit=omit_token + ) + result = RemoveOmit(src, omit_value=omit_token).output() + assert len(result) == 1 + assert result[0] == dict(items=[]) + + +def test_remove_omit_from_list_of_dict(): + omit_token = get_omit_token() + src = """ + items: + - owner: ansible + team: {omit} + - simple_list_item + """.format( + omit=omit_token + ) + result = RemoveOmit(src, omit_value=omit_token).output() + assert len(result) == 1 + assert result[0] == dict(items=[dict(owner="ansible"), "simple_list_item"]) + + +def test_remove_omit_combined(): + omit_token = get_omit_token() + src = """ + items: + - {omit} + - list_item_a + - list_item_b + parent: + child: + subchilda: {omit} + subchildb: + name: {omit} + age: 3 + """.format( + omit=omit_token + ) + result = RemoveOmit(src, omit_value=omit_token).output() + assert len(result) == 1 + assert result[0] == dict( + items=["list_item_a", "list_item_b"], + parent=dict(child=dict(subchildb=dict(age=3))), + ) + + +def test_remove_omit_mutiple_documents(): + omit_token = get_omit_token() + src = [ + """ + project: ansible + collection: {omit} + """.format( + omit=omit_token + ), + "---", + """ + project: kubernetes + environment: production + collection: {omit}""".format( + omit=omit_token + ), + ] + src = "\n".join(src) + print(src) + result = RemoveOmit(src, omit_value=omit_token).output() + assert len(result) == 2 + assert result[0] == dict(project="ansible") + assert result[1] == dict(project="kubernetes", environment="production")