diff --git a/changelogs/fragments/629-add-hidden-fields-option.yaml b/changelogs/fragments/643-add-hidden-fields-option.yaml similarity index 100% rename from changelogs/fragments/629-add-hidden-fields-option.yaml rename to changelogs/fragments/643-add-hidden-fields-option.yaml diff --git a/plugins/module_utils/k8s/service.py b/plugins/module_utils/k8s/service.py index 95cf2136c9..0987ab70ac 100644 --- a/plugins/module_utils/k8s/service.py +++ b/plugins/module_utils/k8s/service.py @@ -538,14 +538,47 @@ def hide_fields(definition: dict, hidden_fields: Optional[list]) -> dict: return result -# hide_field is not hugely sophisticated and designed to cope -# with e.g. status or metadata.managedFields rather than e.g. -# spec.template.spec.containers[0].env[3].value +# hide_field should be able to cope with simple or more complicated +# field definitions +# e.g. status or metadata.managedFields or +# spec.template.spec.containers[0].env[3].value or +# metadata.annotations[kubectl.kubernetes.io/last-applied-configuration] def hide_field(definition: dict, hidden_field: str) -> dict: - split = hidden_field.split(".", 1) - if split[0] in definition: - if len(split) == 2: - definition[split[0]] = hide_field(definition[split[0]], split[1]) - else: - del definition[split[0]] + lbracket = hidden_field.find("[") + dot = hidden_field.find(".") + + def dict_contains_key(field: dict, key: str) -> bool: + return key in field + + def list_contains_key(field: list, key: str) -> bool: + return key < len(field) + + field_contains_key = dict_contains_key + + if lbracket != -1 and (dot == -1 or lbracket < dot): + # handle lists and dicts + rbracket = hidden_field.find("]") + key = hidden_field[lbracket + 1:rbracket] + field = hidden_field[:lbracket] + # skip past right bracket and any following dot + rest = hidden_field[rbracket + 2:] + + if key.isdecimal(): + key = int(key) + field_contains_key = list_contains_key + if field in definition and field_contains_key(definition[field], key): + if rest: + definition[field][key] = hide_field(definition[field][key], rest) + else: + del definition[field][key] + if not definition[field]: + del definition[field] + else: + # handle standard fields + split = hidden_field.split(".", 1) + if split[0] in definition: + if len(split) == 2: + definition[split[0]] = hide_field(definition[split[0]], split[1]) + else: + del definition[split[0]] return definition diff --git a/plugins/modules/k8s.py b/plugins/modules/k8s.py index baa28c0429..ffa6fe35f4 100644 --- a/plugins/modules/k8s.py +++ b/plugins/modules/k8s.py @@ -189,7 +189,8 @@ description: - Hide fields matching this option in the result - An example might be C(hidden_fields=[metadata.managedFields]) - - Only field definitions that don't reference list items are supported (so V(spec.containers[0]) would not work) + or C(hidden_fields=[spec.containers[0].env[3].value]) + or C(hidden_fields=[metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]]) type: list elements: str version_added: 2.5.0 diff --git a/plugins/modules/k8s_info.py b/plugins/modules/k8s_info.py index 7f144243c2..dda4723f98 100644 --- a/plugins/modules/k8s_info.py +++ b/plugins/modules/k8s_info.py @@ -48,7 +48,8 @@ description: - Hide fields matching any of the field definitions in the result - An example might be C(hidden_fields=[metadata.managedFields]) - - Only field definitions that don't reference list items are supported (so V(spec.containers[0]) would not work) + or C(hidden_fields=[spec.containers[0].env[3].value]) + or C(hidden_fields=[metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]]) type: list elements: str version_added: 2.5.0 diff --git a/tests/integration/targets/k8s_hide_fields/tasks/main.yml b/tests/integration/targets/k8s_hide_fields/tasks/main.yml index 4b361fb96b..d77342f318 100644 --- a/tests/integration/targets/k8s_hide_fields/tasks/main.yml +++ b/tests/integration/targets/k8s_hide_fields/tasks/main.yml @@ -77,6 +77,7 @@ definition: "{{ hide_fields_base_configmap | combine({'data':{'anew':'value'}}) }}" hidden_fields: - data + - metadata.annotations[kubectl.kubernetes.io/last-applied-configuration] apply: true register: hf6 diff: true @@ -86,6 +87,14 @@ that: - hf6.changed + - name: Ensure hidden fields are not present + assert: + that: + - >- + 'annotations' not in hf6.resources[0].metadata or + q'kubectl.kubernetes.io/last-applied-configuration' + not in hf6.resources[0].metadata.annotations + - name: Hidden field should not show up in deletion k8s: definition: "{{ hide_fields_base_configmap}}" diff --git a/tests/unit/module_utils/test_hide_fields.py b/tests/unit/module_utils/test_hide_fields.py new file mode 100644 index 0000000000..22cc88b794 --- /dev/null +++ b/tests/unit/module_utils/test_hide_fields.py @@ -0,0 +1,76 @@ +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import ( + hide_fields, +) + +tests = [ + dict( + output=dict( + kind="ConfigMap", metadata=dict(name="foo"), data=dict(one="1", two="2") + ), + hide_fields=["metadata"], + expected=dict(kind="ConfigMap", data=dict(one="1", two="2")), + ), + dict( + output=dict( + kind="ConfigMap", + metadata=dict( + name="foo", + annotations={ + "kubectl.kubernetes.io/last-applied-configuration": '{"testvalue"}' + }, + ), + data=dict(one="1", two="2"), + ), + hide_fields=[ + "metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]", + "data.one", + ], + expected=dict(kind="ConfigMap", metadata=dict(name="foo"), data=dict(two="2")), + ), + dict( + output=dict( + kind="Pod", + metadata=dict(name="foo"), + spec=dict( + containers=[ + dict( + name="containers", + image="busybox", + env=[ + dict(name="ENV1", value="env1"), + dict(name="ENV2", value="env2"), + dict(name="ENV3", value="env3"), + ], + ) + ] + ), + ), + hide_fields=["spec.containers[0].env[1].value"], + expected=dict( + kind="Pod", + metadata=dict(name="foo"), + spec=dict( + containers=[ + dict( + name="containers", + image="busybox", + env=[ + dict(name="ENV1", value="env1"), + dict(name="ENV2"), + dict(name="ENV3", value="env3"), + ], + ) + ] + ), + ), + ), +] + + +def test_hide_fields(): + for test in tests: + if hide_fields(test["output"], test["hide_fields"]) != test["expected"]: + print(test["output"]) + print(hide_fields(test["output"], test["hide_fields"])) + print(test["expected"]) + assert hide_fields(test["output"], test["hide_fields"]) == test["expected"]