azext_edge/edge/providers/check/base/pod.py (180 lines of code) (raw):

# coding=utf-8 # ---------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License file in the project root for license information. # ---------------------------------------------------------------------------------------------- from knack.log import get_logger from rich.padding import Padding from rich.table import Table from kubernetes.client.models import V1Pod from typing import List, Tuple from .check_manager import CheckManager from .display import colorize_string from ..common import ( COLOR_STR_FORMAT, POD_CONDITION_TEXT_MAP, PodStatusConditionResult, PodStatusResult, ResourceOutputDetailLevel, ) from ....common import CheckTaskStatus, PodState logger = get_logger(__name__) def decorate_pod_phase(phase: str) -> Tuple[str, str]: from ....common import PodState status = PodState.map_to_status(phase) return COLOR_STR_FORMAT.format(color=status.color, value=phase), status.value def evaluate_pod_health( check_manager: CheckManager, target: str, namespace: str, padding: int, pods: List[V1Pod], detail_level: int = ResourceOutputDetailLevel.summary.value, ) -> None: if not pods: return # prep table table = Table(show_header=True, header_style="bold", show_lines=True, caption_justify="left") if detail_level != ResourceOutputDetailLevel.summary.value: for column_name, justify in [ ("Pod Name", "left"), ("Phase", "left"), ("Conditions", "left"), ]: table.add_column(column_name, justify=f"{justify}") else: table = Table.grid(padding=(0, 0, 0, 2)) add_footer = False pod_statuses = [] for pod in pods: pod_status_result: PodStatusResult = _process_pod_status( check_manager=check_manager, target=target, pod=pod, namespace=namespace, detail_level=detail_level, ) pod_statuses.append(pod_status_result.eval_status) table.add_row(*pod_status_result.display_strings) add_footer = not all(status == CheckTaskStatus.success.value for status in pod_statuses) check_manager.add_display( target_name=target, namespace=namespace, display=Padding(table, (0, 0, 0, padding)), ) if add_footer: footer = ":magnifying_glass_tilted_left:" + colorize_string( " See more details by attaching : --detail-level 1 or --detail-level 2" ) check_manager.add_display( target_name=target, namespace=namespace, display=Padding(footer, (0, 0, 0, padding)), ) def _process_pod_status( check_manager: CheckManager, target: str, pod: V1Pod, namespace: str, detail_level: int = ResourceOutputDetailLevel.summary.value, ) -> PodStatusResult: target_service_pod = f"pod/{pod.metadata.name}" conditions = [ f"{target_service_pod}.status.phase", f"{target_service_pod}.status.conditions", ] if check_manager.targets.get(target, {}).get(namespace, {}).get("conditions", None): check_manager.add_target_conditions(target_name=target, namespace=namespace, conditions=conditions) else: check_manager.set_target_conditions(target_name=target, namespace=namespace, conditions=conditions) pod_dict = pod.to_dict() pod_name = pod_dict["metadata"]["name"] pod_phase = pod_dict.get("status", {}).get("phase") pod_conditions: list = pod_dict.get("status", {}).get("conditions", []) pod_phase_deco, status = decorate_pod_phase(pod_phase) is_pod_succeeded = pod_phase.lower() == PodState.succeeded.value pod_eval_value = {} pod_eval_status = status pod_eval_value["status.phase"] = pod_phase conditions_readiness = True conditions_display_list: List[PodStatusConditionResult] = [] unknown_conditions_display_list: List[PodStatusConditionResult] = [] # When pod in obnormal state, sometimes the conditions are not available # When pod phase is succeeded, conditions check should be success disregarding the value if pod_conditions: known_condition_values = [value.replace(" ", "").lower() for value in POD_CONDITION_TEXT_MAP.values()] for condition in pod_conditions: type = condition["type"] condition_type = POD_CONDITION_TEXT_MAP.get(type) condition_reason = condition.get("reason", "N/A") if condition_type: condition_status = condition.get("status").lower() == "true" condition_status_eval = condition_status if is_pod_succeeded: status = CheckTaskStatus.success.value condition_status_eval = True conditions_readiness = conditions_readiness and condition_status_eval status = CheckTaskStatus.success.value if condition_status_eval else CheckTaskStatus.error.value pod_condition_deco = colorize_string(value=condition_status, color=CheckTaskStatus(status).color) pod_eval_status = status if status != CheckTaskStatus.success.value else pod_eval_status else: condition_type = type condition_status = condition.get("status") formatted_reason = "" if condition_reason: formatted_reason = f"[red]Reason: {condition_reason}[/red]" if status == CheckTaskStatus.error.value else f"Reason: {condition_reason}" if condition_type.replace(" ", "").lower() in known_condition_values: conditions_display_list.append( PodStatusConditionResult( condition_string=f"{condition_type}: {pod_condition_deco}", failed_reason=formatted_reason, eval_status=status, ) ) else: unknown_conditions_display_list.append( PodStatusConditionResult( condition_string=f"{condition_type}: {condition_status}", failed_reason=formatted_reason, eval_status=status, ) ) pod_eval_value[f"status.conditions.{type.lower()}"] = condition_status if not conditions_readiness: pod_eval_status = CheckTaskStatus.error.value elif unknown_conditions_display_list and pod_eval_status != CheckTaskStatus.error.value: pod_eval_status = CheckTaskStatus.warning.value check_manager.add_target_eval( target_name=target, status=pod_eval_status, value=pod_eval_value, namespace=namespace, resource_name=target_service_pod, ) # text to display in the table pod_health_text = f"Pod {{[bright_blue]{pod_name}[/bright_blue]}}" if detail_level == ResourceOutputDetailLevel.summary.value: status_obj = CheckTaskStatus(pod_eval_status) emoji = status_obj.emoji color = status_obj.color return PodStatusResult( display_strings=[colorize_string(value=emoji, color=color), pod_health_text], eval_status=pod_eval_status ) else: pod_conditions_text = "N/A" if pod_conditions: pod_conditions_text = "" if detail_level == ResourceOutputDetailLevel.detail.value and conditions_readiness: pod_conditions_text = "[green]Ready[/green]" if is_pod_succeeded: pod_conditions_text = "[green]Completed[/green]" # Only display the condition if it is not ready when detail level is 1, or the detail level is 2 for condition_result in conditions_display_list: condition_not_ready = condition_result.eval_status == CheckTaskStatus.error.value if ( detail_level == ResourceOutputDetailLevel.detail.value and condition_not_ready ) or detail_level == ResourceOutputDetailLevel.verbose.value: pod_conditions_text += f"{condition_result.condition_string}\n" if condition_result.failed_reason: pod_conditions_text += f"{condition_result.failed_reason}\n" if conditions_readiness: for condition_result in unknown_conditions_display_list: condition_text: str = ( f"[yellow]Irregular Condition {condition_result.condition_string} found.[/yellow]" ) pod_conditions_text += f"{condition_text}\n" if condition_result.failed_reason and detail_level == ResourceOutputDetailLevel.verbose.value: pod_conditions_text += f"{condition_result.failed_reason}\n" return PodStatusResult( display_strings=[pod_name, pod_phase_deco, pod_conditions_text], eval_status=pod_eval_status )