From deeb9aef2940f5509eee86cd19fcc9577939844b Mon Sep 17 00:00:00 2001 From: David Weinehall Date: Tue, 18 Jul 2023 22:14:47 +0300 Subject: [PATCH] cmu: Add an option to disable Kubernetes support It might seem counter-intuitive to disable Kubernetes support in a toolkit meant for managing Kubernetes clusters, but just in case you only want to use cmu to run Ansible playbooks and look at logs this option allows for removing the slowdown during startup spent while trying to connect to the API-server. Signed-off-by: David Weinehall --- cmu | 95 ++++++++++++++++++++++++++++++---------- tests/schemas/views.json | 3 +- views/__Context.yaml | 1 + 3 files changed, 75 insertions(+), 24 deletions(-) diff --git a/cmu b/cmu index aeee4387..68653534 100755 --- a/cmu +++ b/cmu @@ -115,6 +115,9 @@ initial_container = None # Is cmu running in read only-mode? read_only_mode = False +# Is Kubernetes support enabled +kubernetes_support = True + # Namespace selected_namespace = "" @@ -129,7 +132,9 @@ def init_kubernetes_client() -> None: """ global kh # pylint: disable=global-statement - kh = KubernetesHelper(about.PROGRAM_SUITE_NAME, about.PROGRAM_SUITE_VERSION, None) + + if kubernetes_support: + kh = KubernetesHelper(about.PROGRAM_SUITE_NAME, about.PROGRAM_SUITE_VERSION, None) override_tail_lines = None default_tail_lines = 4000 @@ -139,6 +144,9 @@ def gather_cluster_info() -> None: Gather information about the cluster necessary for running playbooks """ + if not kubernetes_support: + return + ansithemeprint([ANSIThemeString("[Gathering cluster information]\n", "phase")]) # Set global variables that need to be available when executing playbooks @@ -4180,12 +4188,16 @@ def get_inventory_list(**kwargs: Dict) -> Tuple[List[Dict], int, Any, Callable, control_plane_node, control_plane_name = get_control_plane() - node_vlist, status = kh.get_list_by_kind_namespace(("Node", ""), "") - for node in node_vlist: - roles = kh.get_node_roles(node) - if "control-plane" in roles: - continue - nodes.append(deep_get(node, DictPath("metadata#name"))) + if kubernetes_support: + node_vlist, status = kh.get_list_by_kind_namespace(("Node", ""), "") + for node in node_vlist: + roles = kh.get_node_roles(node) + if "control-plane" in roles: + continue + nodes.append(deep_get(node, DictPath("metadata#name"))) + else: + nodes = [] + status = 200 if len(nodes) > 0: ansible_add_hosts(ANSIBLE_INVENTORY, nodes, group = "nodes") @@ -4375,6 +4387,9 @@ def get_node_info(vlist, extra_vars) -> List[Type]: # To make the failure case easier, return both the ref and the name of the control plane def get_control_plane() -> Tuple[Dict, str]: + if not kubernetes_support: + return None, "" + vlist, status = kh.get_list_by_kind_namespace(("Node", ""), "") control_planes = [] @@ -11102,7 +11117,11 @@ def format_selection_list(uip: UIProps, refresh_apis = False): # Repopulate the list of views, on the offhand chance that a view file has been added populate_views(force_refresh = refresh_apis) curses.doupdate() - available_api_families, _status, _modified = kh.get_available_kinds() + + if kubernetes_support: + available_api_families, _status, _modified = kh.get_available_kinds() + else: + available_api_families = {} items = [] order = [] @@ -11295,15 +11314,19 @@ listviewactions = { infoviews = {} # type: ignore # These functions are used to check whether an API is available +def is_kubernetes_supported() -> bool: + return kubernetes_support + def is_cluster_reachable() -> bool: reachable = False - if kh is not None: + if kubernetes_support and kh is not None: reachable = kh.is_cluster_reachable() return reachable availability_checker_allowlist = { - "is_cluster_reachable": is_cluster_reachable + "is_cluster_reachable": is_cluster_reachable, + "is_kubernetes_supported": is_kubernetes_supported, } # action calls acceptable for direct use in view files @@ -11523,7 +11546,11 @@ def map_key(view_file: str, shortcut: str, activatedfun, key: str, modifier: str def populate_views(force_refresh: bool = False) -> None: global views # pylint: disable=global-statement - tmp_available_api_families, _status, _modified = kh.get_available_kinds(force_refresh = force_refresh) + if kubernetes_support: + tmp_available_api_families, _status, _modified = kh.get_available_kinds(force_refresh = force_refresh) + else: + tmp_available_api_families = {} + available_api_families = set() for kind, kind_data in tmp_available_api_families.items(): if deep_get(kind_data, DictPath("available"), False) == True: @@ -11560,17 +11587,14 @@ def populate_views(force_refresh: bool = False) -> None: if not filename.endswith((".yaml", ".yml")): continue - if len(available_api_families) > 0: - match_tmp = yaml_regex.match(filename) - tmp = match_tmp[1] - if "." in tmp: - kind, api_family = tmp.split(".", 1) - else: - kind = tmp - api_family = "" - if (kind, api_family) in available_api_families or kind.startswith("__"): - view_files.append(os.path.join(view_dir, filename)) + match_tmp = yaml_regex.match(filename) + tmp = match_tmp[1] + if "." in tmp: + kind, api_family = tmp.split(".", 1) else: + kind = tmp + api_family = "" + if (kind, api_family) in available_api_families or kind.startswith("__"): view_files.append(os.path.join(view_dir, filename)) for view_file in view_files: @@ -12037,7 +12061,9 @@ def populate_views(force_refresh: bool = False) -> None: # Add the new view infoviews[(kind, api_family)] = infoview_entry has_infoview = True - kh_update_api_status((kind, api_family), listview = has_listview, infoview = has_infoview) + + if kubernetes_support: + kh_update_api_status((kind, api_family), listview = has_listview, infoview = has_infoview) def setupui(stdscr: curses.window) -> None: # Hide the cursor @@ -12057,6 +12083,15 @@ def setupui(stdscr: curses.window) -> None: viewfunc = deep_get(views, DictPath(f"{defaultview}#viewfunc")) if viewfunc is None: viewfunc = genericlistloop + check_availability = deep_get(views, DictPath(f"{defaultview}#check_availability")) + if check_availability is not None and not check_availability(): + curses.endwin() + ansithemeprint([ANSIThemeString("Error", "error"), + ANSIThemeString(": The requested view “", "default"), + ANSIThemeString(defaultview, "argument"), + ANSIThemeString("“ is not available; ", "default"), + ANSIThemeString("the cluster may be offline or the API disabled.", "default")], stderr = True) + sys.exit(errno.ENOTSUP) viewfunc(stdscr, defaultview) else: curses.endwin() @@ -12295,6 +12330,7 @@ def open_view(options: List[Tuple[str, str]], args: List[str]) -> None: global read_only_mode # pylint: disable=global-statement global selected_namespace # pylint: disable=global-statement global defaultview # pylint: disable=global-statement + global kubernetes_support # pylint: disable=global-statement tmpdefaultview = deep_get(cmtlib.cmtconfig, DictPath("Global#defaultview"), "") @@ -12303,8 +12339,11 @@ def open_view(options: List[Tuple[str, str]], args: List[str]) -> None: selected_namespace = optarg elif opt == "--read-only": read_only_mode = True + elif opt == "--disable-kubernetes": + kubernetes_support = False init_kubernetes_client() + # Customises the list views ansithemeprint([ANSIThemeString("Populating list of supported views...\n", "default")]) @@ -12357,7 +12396,8 @@ def open_view(options: List[Tuple[str, str]], args: List[str]) -> None: init_kubernetes_client() ansithemeprint([ANSIThemeString("Checking available Kubernetes APIs\n", "default")]) - _available_api_families, _status, _modified = kh.get_available_kinds() + if kubernetes_support: + _available_api_families, _status, _modified = kh.get_available_kinds() # Customises the list views if necessary if len(infoviews) == 0: @@ -12586,6 +12626,15 @@ COMMANDLINE = { "--read-only": { "description": [ANSIThemeString("disable all commands that modify state", "description")], }, + "--disable-kubernetes": { + "description": [ANSIThemeString("disable Kubernetes support", "description")], + "extended_description": [ + [ANSIThemeString("This option disables Kubernetes support; ", "description")], + [ANSIThemeString("this is typically only useful if you use", "description")], + [ANSIThemeString(about.UI_PROGRAM_NAME, "programname"), + ANSIThemeString(" to manage an Ansible inventory", "description")], + ], + }, "--namespace": { "description": [ANSIThemeString("only show namespace ", "description"), ANSIThemeString("NAMESPACE", "argument")], diff --git a/tests/schemas/views.json b/tests/schemas/views.json index dfd719a1..ffaa5934 100644 --- a/tests/schemas/views.json +++ b/tests/schemas/views.json @@ -1133,7 +1133,8 @@ "check_availability": { "description": "Function to use to check whether the API is available", "enum": [ - "is_cluster_reachable" + "is_cluster_reachable", + "is_kubernetes_supported" ] }, "field_indexes": { diff --git a/views/__Context.yaml b/views/__Context.yaml index a07c3c86..91b16610 100644 --- a/views/__Context.yaml +++ b/views/__Context.yaml @@ -8,6 +8,7 @@ listview: listgetter_args: key: "server_address" retrigger_timeout: 300 + check_availability: "is_kubernetes_supported" on_activation: call: null name: "Contexts"