mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-06 04:52:37 +00:00
Provide a mechanism to hide fields from output (#629)
Provide a mechanism to hide fields from output
SUMMARY
The k8s and k8s_info modules can be a little noisy in verbose mode, and most of that is due to managedFields.
If we can provide a mechanism to hide managedFields, the output is a lot more useful.
ISSUE TYPE
Feature Pull Request
COMPONENT NAME
k8s, k8s_info
ADDITIONAL INFORMATION
Before
ANSIBLE_COLLECTIONS_PATH=../../.. ansible -m k8s_info -a 'kind=ConfigMap name=hide-fields-cm namespace=hide-fields' localhost
[WARNING]: No inventory was parsed, only implicit localhost is available
localhost | SUCCESS => {
"api_found": true,
"changed": false,
"resources": [
{
"apiVersion": "v1",
"data": {
"another": "value",
"hello": "world"
},
"kind": "ConfigMap",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"another\":\"value\",\"hello\":\"world\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{},\"name\":\"hide-fields-cm\",\"namespace\":\"hide-fields\"}}\n"
},
"creationTimestamp": "2023-06-13T01:47:47Z",
"managedFields": [
{
"apiVersion": "v1",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:data": {
".": {},
"f:another": {},
"f:hello": {}
},
"f:metadata": {
"f:annotations": {
".": {},
"f:kubectl.kubernetes.io/last-applied-configuration": {}
}
}
},
"manager": "kubectl-client-side-apply",
"operation": "Update",
"time": "2023-06-13T01:47:47Z"
}
],
"name": "hide-fields-cm",
"namespace": "hide-fields",
"resourceVersion": "2557394",
"uid": "f233da63-6374-4079-9825-3562c0ed123c"
}
}
]
}
After
ANSIBLE_COLLECTIONS_PATH=../../.. ansible -m k8s_info -a 'kind=ConfigMap name=hide-fields-cm namespace=hide-fields hidden_fields=metadata.managedFields' localhost
[WARNING]: No inventory was parsed, only implicit localhost is available
localhost | SUCCESS => {
"api_found": true,
"changed": false,
"resources": [
{
"apiVersion": "v1",
"data": {
"another": "value",
"hello": "world"
},
"kind": "ConfigMap",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"another\":\"value\",\"hello\":\"world\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{},\"name\":\"hide-fields-cm\",\"namespace\":\"hide-fields\"}}\n"
},
"creationTimestamp": "2023-06-13T01:47:47Z",
"name": "hide-fields-cm",
"namespace": "hide-fields",
"resourceVersion": "2557394",
"uid": "f233da63-6374-4079-9825-3562c0ed123c"
}
}
]
}
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Will Thames
This commit is contained in:
@@ -17,6 +17,7 @@ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource impor
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
K8sService,
|
||||
diff_objects,
|
||||
hide_fields,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
ResourceTimeout,
|
||||
@@ -137,6 +138,7 @@ def perform_action(svc, definition: Dict, params: Dict) -> Dict:
|
||||
state = params.get("state", None)
|
||||
kind = definition.get("kind")
|
||||
api_version = definition.get("apiVersion")
|
||||
hidden_fields = params.get("hidden_fields")
|
||||
|
||||
result = {"changed": False, "result": {}}
|
||||
instance = {}
|
||||
@@ -212,7 +214,7 @@ def perform_action(svc, definition: Dict, params: Dict) -> Dict:
|
||||
existing = existing.to_dict()
|
||||
else:
|
||||
existing = {}
|
||||
match, diffs = diff_objects(existing, instance)
|
||||
match, diffs = diff_objects(existing, instance, hidden_fields)
|
||||
if match and diffs:
|
||||
result.setdefault("warnings", []).append(
|
||||
"No meaningful diff was generated, but the API may not be idempotent "
|
||||
@@ -222,7 +224,7 @@ def perform_action(svc, definition: Dict, params: Dict) -> Dict:
|
||||
if svc.module._diff:
|
||||
result["diff"] = diffs
|
||||
|
||||
result["result"] = instance
|
||||
result["result"] = hide_fields(instance, hidden_fields)
|
||||
if not success:
|
||||
raise ResourceTimeout(
|
||||
'"{0}" "{1}": Timed out waiting on resource'.format(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright: (c) 2021, Red Hat | Ansible
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
import copy
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.hashes import (
|
||||
@@ -248,6 +249,7 @@ class K8sService:
|
||||
wait_timeout: Optional[int] = 120,
|
||||
state: Optional[str] = "present",
|
||||
condition: Optional[Dict] = None,
|
||||
hidden_fields: Optional[List] = None,
|
||||
) -> Dict:
|
||||
resource = self.find_resource(kind, api_version)
|
||||
api_found = bool(resource)
|
||||
@@ -310,7 +312,9 @@ class K8sService:
|
||||
instances = resources.get("items") or [resources]
|
||||
|
||||
if not wait:
|
||||
result["resources"] = instances
|
||||
result["resources"] = [
|
||||
hide_fields(instance, hidden_fields) for instance in instances
|
||||
]
|
||||
return result
|
||||
|
||||
# Now wait for the specified state of any resource instances we have found.
|
||||
@@ -329,7 +333,7 @@ class K8sService:
|
||||
"Failed to gather information about %s(s) even"
|
||||
" after waiting for %s seconds" % (res.get("kind"), duration)
|
||||
)
|
||||
result["resources"].append(res)
|
||||
result["resources"].append(hide_fields(res, hidden_fields))
|
||||
return result
|
||||
|
||||
def create(self, resource: Resource, definition: Dict) -> Dict:
|
||||
@@ -495,7 +499,9 @@ class K8sService:
|
||||
return k8s_obj
|
||||
|
||||
|
||||
def diff_objects(existing: Dict, new: Dict) -> Tuple[bool, Dict]:
|
||||
def diff_objects(
|
||||
existing: Dict, new: Dict, hidden_fields: Optional[list] = None
|
||||
) -> Tuple[bool, Dict]:
|
||||
result = {}
|
||||
diff = recursive_diff(existing, new)
|
||||
if not diff:
|
||||
@@ -517,4 +523,29 @@ def diff_objects(existing: Dict, new: Dict) -> Tuple[bool, Dict]:
|
||||
if not set(result["before"]["metadata"].keys()).issubset(ignored_keys):
|
||||
return False, result
|
||||
|
||||
result["before"] = hide_fields(result["before"], hidden_fields)
|
||||
result["after"] = hide_fields(result["after"], hidden_fields)
|
||||
|
||||
return True, result
|
||||
|
||||
|
||||
def hide_fields(definition: dict, hidden_fields: Optional[list]) -> dict:
|
||||
if not hidden_fields:
|
||||
return definition
|
||||
result = copy.deepcopy(definition)
|
||||
for hidden_field in hidden_fields:
|
||||
result = hide_field(result, hidden_field)
|
||||
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
|
||||
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]]
|
||||
return definition
|
||||
|
||||
Reference in New Issue
Block a user