Skip to content

Commit

Permalink
Extend hidden_fields to allow more complicated field definitions
Browse files Browse the repository at this point in the history
This allows us to ignore e.g. the last-applied-configuration
annotation by specifying
`metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]`
  • Loading branch information
willthames committed Sep 4, 2023
1 parent 9e9962b commit 80d9316
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 50 deletions.
54 changes: 15 additions & 39 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,16 @@ jobs:
"ansible-version": "stable-2.9"
},
{
"ansible-version": "stable-2.12",
"python-version": "3.7"
},
{
"ansible-version": "stable-2.12",
"python-version": "3.11"
},
{
"ansible-version": "stable-2.13",
"python-version": "3.7"
},
{
"ansible-version": "stable-2.13",
"python-version": "3.11"
},
{
"ansible-version": "stable-2.14",
"python-version": "3.7"
},
{
"ansible-version": "stable-2.14",
"python-version": "3.8"
Expand All @@ -61,10 +52,6 @@ jobs:
"ansible-version": "stable-2.14",
"python-version": "3.11"
},
{
"ansible-version": "stable-2.15",
"python-version": "3.7"
},
{
"ansible-version": "stable-2.15",
"python-version": "3.8"
Expand All @@ -75,19 +62,19 @@ jobs:
},
{
"ansible-version": "milestone",
"python-version": "3.7"
"python-version": "3.8"
},
{
"ansible-version": "milestone",
"python-version": "3.8"
"python-version": "3.9"
},
{
"ansible-version": "devel",
"python-version": "3.7"
"python-version": "3.8"
},
{
"ansible-version": "devel",
"python-version": "3.8"
"python-version": "3.9"
}
]
unit-source:
Expand Down Expand Up @@ -138,13 +125,21 @@ jobs:
"ansible-version": "milestone",
"python-version": "3.8"
},
{
"ansible-version": "milestone",
"python-version": "3.9"
},
{
"ansible-version": "devel",
"python-version": "3.7"
},
{
"ansible-version": "devel",
"python-version": "3.8"
},
{
"ansible-version": "devel",
"python-version": "3.9"
}
]
collection_pre_install: ''
Expand Down Expand Up @@ -187,31 +182,12 @@ jobs:
fail-fast: false
matrix:
ansible-version:
- stable-2.12
- stable-2.15
- milestone
- devel
python-version:
- "3.8"
- "3.9"
exclude:
- ansible-version: stable-2.9
python-version: 3.9
- ansible-version: stable-2.9
python-version: 3.10
- ansible-version: stable-2.9
python-version: 3.11
- ansible-version: stable-2.12
python-version: 3.11
- ansible-version: stable-2.13
python-version: 3.11
- ansible-version: stable-2.14
python-version: 3.8
- ansible-version: stable-2.15
python-version: 3.8
- ansible-version: milestone
python-version: 3.8
- ansible-version: devel
python-version: 3.8
- "3.10"
- "3.11"
enable-turbo-mode:
- true
- false
Expand Down
51 changes: 42 additions & 9 deletions plugins/module_utils/k8s/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion plugins/modules/k8s.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion plugins/modules/k8s_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions tests/integration/targets/k8s_hide_fields/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}}"
Expand Down
76 changes: 76 additions & 0 deletions tests/unit/module_utils/test_hide_fields.py
Original file line number Diff line number Diff line change
@@ -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"]

0 comments on commit 80d9316

Please sign in to comment.