From beb53652db8c67f79f322c72f2195180cd20c54a Mon Sep 17 00:00:00 2001 From: Mike Graves Date: Wed, 15 Jun 2022 09:26:24 -0400 Subject: [PATCH] Ensure CoreExceptions are handled gracefully (#476) Ensure CoreExceptions are handled gracefully SUMMARY CoreExceptions, when raised, should have a reasonably helpful and actionable message associated with them. This adds a final check in module execution to gracefully fail from these exceptions. A new fail_from_exception method is added both to simplify exiting the module, and to ensure that any chained exceptions are available when using -vvv. ISSUE TYPE COMPONENT NAME ADDITIONAL INFORMATION Reviewed-by: Alina Buzachis Reviewed-by: Joseph Torcasso --- plugins/module_utils/k8s/client.py | 13 ++++++++++--- plugins/module_utils/k8s/core.py | 9 +++++++++ plugins/module_utils/k8s/runner.py | 9 ++++++++- plugins/modules/k8s.py | 8 +++++++- plugins/modules/k8s_cluster_info.py | 8 +++++++- plugins/modules/k8s_cp.py | 8 +++++++- plugins/modules/k8s_drain.py | 13 ++++++++++--- plugins/modules/k8s_exec.py | 10 ++++++++-- plugins/modules/k8s_info.py | 12 +++++++++--- plugins/modules/k8s_json_patch.py | 10 ++++++++-- plugins/modules/k8s_log.py | 2 +- plugins/modules/k8s_rollback.py | 9 ++++++--- plugins/modules/k8s_scale.py | 7 +++++-- plugins/modules/k8s_service.py | 12 +++++++++--- 14 files changed, 104 insertions(+), 26 deletions(-) diff --git a/plugins/module_utils/k8s/client.py b/plugins/module_utils/k8s/client.py index 7087e236..c9986d3c 100644 --- a/plugins/module_utils/k8s/client.py +++ b/plugins/module_utils/k8s/client.py @@ -15,6 +15,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import ( requires as _requires, ) +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import ( + CoreException, +) try: from ansible_collections.kubernetes.core.plugins.module_utils import ( @@ -335,9 +338,13 @@ def get_api_client(module=None, **kwargs: Optional[Any]) -> K8SClient: if auth_spec.get("no_proxy"): requires("kubernetes", "19.15.0", "to use the no_proxy feature") - configuration = _create_configuration(auth_spec) - headers = _create_headers(module, **kwargs) - client = create_api_client(configuration, **headers) + try: + configuration = _create_configuration(auth_spec) + headers = _create_headers(module, **kwargs) + client = create_api_client(configuration, **headers) + except kubernetes.config.ConfigException as e: + msg = "Could not create API client: {0}".format(e) + raise CoreException(msg) from e k8s_client = K8SClient( configuration=configuration, diff --git a/plugins/module_utils/k8s/core.py b/plugins/module_utils/k8s/core.py index b6bdd505..95815dee 100644 --- a/plugins/module_utils/k8s/core.py +++ b/plugins/module_utils/k8s/core.py @@ -1,3 +1,5 @@ +import traceback + from typing import Optional from ansible_collections.kubernetes.core.plugins.module_utils.version import ( @@ -72,6 +74,13 @@ class AnsibleK8SModule: def fail_json(self, *args, **kwargs): return self._module.fail_json(*args, **kwargs) + def fail_from_exception(self, exception): + msg = to_text(exception) + tb = "".join( + traceback.format_exception(None, exception, exception.__traceback__) + ) + return self.fail_json(msg=msg, exception=tb) + def has_at_least( self, dependency: str, minimum: Optional[str] = None, warn: bool = False ) -> bool: diff --git a/plugins/module_utils/k8s/runner.py b/plugins/module_utils/k8s/runner.py index 84b76c77..438b3211 100644 --- a/plugins/module_utils/k8s/runner.py +++ b/plugins/module_utils/k8s/runner.py @@ -8,6 +8,9 @@ from ansible.module_utils._text import to_native from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import ( get_api_client, ) +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import ( + CoreException, +) from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import ( create_definitions, ) @@ -48,7 +51,11 @@ def run_module(module) -> None: changed = False client = get_api_client(module) svc = K8sService(client, module) - definitions = create_definitions(module.params) + try: + definitions = create_definitions(module.params) + except Exception as e: + msg = "Failed to load resource definition: {0}".format(e) + raise CoreException(msg) from e for definition in definitions: result = {"changed": False, "result": {}} diff --git a/plugins/modules/k8s.py b/plugins/modules/k8s.py index 4b3c7462..31e5099e 100644 --- a/plugins/modules/k8s.py +++ b/plugins/modules/k8s.py @@ -394,6 +394,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import ( AnsibleK8SModule, ) +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import ( + CoreException, +) from ansible_collections.kubernetes.core.plugins.module_utils.k8s.runner import ( run_module, ) @@ -458,7 +461,10 @@ def main(): mutually_exclusive=mutually_exclusive, supports_check_mode=True, ) - run_module(module) + try: + run_module(module) + except CoreException as e: + module.fail_from_exception(e) if __name__ == "__main__": diff --git a/plugins/modules/k8s_cluster_info.py b/plugins/modules/k8s_cluster_info.py index 1fb53713..9cd2ac17 100644 --- a/plugins/modules/k8s_cluster_info.py +++ b/plugins/modules/k8s_cluster_info.py @@ -157,6 +157,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule impo from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import ( AnsibleK8SModule, ) +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import ( + CoreException, +) from ansible_collections.kubernetes.core.plugins.module_utils.args_common import ( AUTH_ARG_SPEC, ) @@ -219,7 +222,10 @@ def main(): get_api_client, ) - execute_module(module, client=get_api_client(module=module)) + try: + execute_module(module, client=get_api_client(module=module)) + except CoreException as e: + module.fail_from_exception(e) if __name__ == "__main__": diff --git a/plugins/modules/k8s_cp.py b/plugins/modules/k8s_cp.py index 810cf953..cd277018 100644 --- a/plugins/modules/k8s_cp.py +++ b/plugins/modules/k8s_cp.py @@ -148,6 +148,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import ( AnsibleK8SModule, ) +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import ( + CoreException, +) from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import ( K8sService, ) @@ -212,7 +215,10 @@ def main(): supports_check_mode=True, ) - execute_module(module) + try: + execute_module(module) + except CoreException as e: + module.fail_from_exception(e) if __name__ == "__main__": diff --git a/plugins/modules/k8s_drain.py b/plugins/modules/k8s_drain.py index 7ef12bd5..31596d8c 100644 --- a/plugins/modules/k8s_drain.py +++ b/plugins/modules/k8s_drain.py @@ -142,6 +142,10 @@ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import ( AnsibleK8SModule, ) +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import ( + CoreException, +) + from ansible.module_utils._text import to_native try: @@ -497,9 +501,12 @@ def main(): error=to_native(k8s_import_exception), ) - client = get_api_client(module=module) - k8s_drain = K8sDrainAnsible(module, client.client) - k8s_drain.execute_module() + try: + client = get_api_client(module=module) + k8s_drain = K8sDrainAnsible(module, client.client) + k8s_drain.execute_module() + except CoreException as e: + module.fail_from_exception(e) if __name__ == "__main__": diff --git a/plugins/modules/k8s_exec.py b/plugins/modules/k8s_exec.py index a1901321..c54c23c0 100644 --- a/plugins/modules/k8s_exec.py +++ b/plugins/modules/k8s_exec.py @@ -144,6 +144,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import ( from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import ( get_api_client, ) +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import ( + CoreException, +) try: from kubernetes.client.apis import core_v1_api @@ -240,8 +243,11 @@ def main(): supports_check_mode=True, ) - client = get_api_client(module) - execute_module(module, client.client) + try: + client = get_api_client(module) + execute_module(module, client.client) + except CoreException as e: + module.fail_from_exception(e) if __name__ == "__main__": diff --git a/plugins/modules/k8s_info.py b/plugins/modules/k8s_info.py index 7d831d1d..4b29be11 100644 --- a/plugins/modules/k8s_info.py +++ b/plugins/modules/k8s_info.py @@ -161,6 +161,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import ( from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import ( get_api_client, ) +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import ( + CoreException, +) from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import ( K8sService, ) @@ -202,9 +205,12 @@ def main(): module = AnsibleK8SModule( module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True ) - client = get_api_client(module) - svc = K8sService(client, module) - execute_module(module, svc) + try: + client = get_api_client(module) + svc = K8sService(client, module) + execute_module(module, svc) + except CoreException as e: + module.fail_from_exception(e) if __name__ == "__main__": diff --git a/plugins/modules/k8s_json_patch.py b/plugins/modules/k8s_json_patch.py index 61595454..5ea8dbc9 100644 --- a/plugins/modules/k8s_json_patch.py +++ b/plugins/modules/k8s_json_patch.py @@ -142,6 +142,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import ( AnsibleK8SModule, ) +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import ( + CoreException, +) from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import ( diff_objects, ) @@ -283,8 +286,11 @@ def main(): module = AnsibleK8SModule( module_class=AnsibleModule, argument_spec=args, supports_check_mode=True ) - client = get_api_client(module) - execute_module(module, client) + try: + client = get_api_client(module) + execute_module(module, client) + except CoreException as e: + module.fail_from_exception(e) if __name__ == "__main__": diff --git a/plugins/modules/k8s_log.py b/plugins/modules/k8s_log.py index e55bd6e5..94e5d5f9 100644 --- a/plugins/modules/k8s_log.py +++ b/plugins/modules/k8s_log.py @@ -272,7 +272,7 @@ def main(): result = execute_module(svc, module.params) module.exit_json(**result) except CoreException as e: - module.fail_json(msg=e) + module.fail_from_exception(e) if __name__ == "__main__": diff --git a/plugins/modules/k8s_rollback.py b/plugins/modules/k8s_rollback.py index 335b6ee5..8dfab459 100644 --- a/plugins/modules/k8s_rollback.py +++ b/plugins/modules/k8s_rollback.py @@ -262,9 +262,12 @@ def main(): module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True ) - client = get_api_client(module=module) - svc = K8sService(client, module) - execute_module(svc) + try: + client = get_api_client(module=module) + svc = K8sService(client, module) + execute_module(svc) + except CoreException as e: + module.fail_from_exception(e) if __name__ == "__main__": diff --git a/plugins/modules/k8s_scale.py b/plugins/modules/k8s_scale.py index 561e11ca..01edfa6d 100644 --- a/plugins/modules/k8s_scale.py +++ b/plugins/modules/k8s_scale.py @@ -411,8 +411,11 @@ def main(): supports_check_mode=True, ) - client = get_api_client(module=module) - execute_module(client, module) + try: + client = get_api_client(module=module) + execute_module(client, module) + except CoreException as e: + module.fail_from_exception(e) if __name__ == "__main__": diff --git a/plugins/modules/k8s_service.py b/plugins/modules/k8s_service.py index eba278b1..4eb280bf 100644 --- a/plugins/modules/k8s_service.py +++ b/plugins/modules/k8s_service.py @@ -160,6 +160,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import ( from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import ( get_api_client, ) +from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import ( + CoreException, +) from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import ( K8sService, ) @@ -274,9 +277,12 @@ def main(): supports_check_mode=True, ) - client = get_api_client(module=module) - svc = K8sService(client, module) - execute_module(svc) + try: + client = get_api_client(module=module) + svc = K8sService(client, module) + execute_module(svc) + except CoreException as e: + module.fail_from_exception(e) if __name__ == "__main__":