cli/check.py (61 lines of code) (raw):
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A base class for defining the interface of healthscan checks."""
import abc
import signal
import sys
from typing import Any
import click
class Check(abc.ABC):
"""A standard implementation of a healthscan check."""
def sigint_handler(
self,
signum: Any,
frame: Any,
) -> None:
"""Handler for SIGINT signal.
Args:
signum: The signal number.
frame: The current stack frame.
"""
print(f'Received {signum} signal on frame {frame}. Exiting...')
# Perform any necessary cleanup actions here
# For example: close file handlers, release resources, etc.
click.echo(
click.style(
'\nCLEANING UP...',
fg='red',
bold=True,
)
)
self.clean_up()
# Stops proceeding anything the CLI is doing
sys.exit(0)
def __init__(
self,
name: str,
description: str,
machine_type: str,
supported_machine_types: frozenset[str],
dry_run: bool = False,
):
"""Initialize a check to run on a cluster.
Args:
name: The name of the check.
description: The description of the check.
machine_type: The machine type of the cluster to run the check on.
supported_machine_types: The machine types supported by the check.
dry_run: Whether to run the check in dry run mode.
Raises:
click.Abort: If the machine type is not supported.
"""
self.name = name
self.description = description
self.machine_type = machine_type
self.supported_machine_types = supported_machine_types
self.dry_run = dry_run
# Handle SIGINT signal to clean up
signal.signal(
signal.SIGINT,
self.sigint_handler,
)
# Validate machine type before running check
if not self._is_supported_machine_type(self.machine_type):
click.echo(
click.style(
text=(
f'`{self.name}` check '
f'does not support machine type {self.machine_type}.'
),
fg='red',
bold=True,
)
)
raise click.Abort()
def _is_supported_machine_type(self, machine_type: str) -> bool:
"""Returns whether Check supports given machine type.
Args:
machine_type: The machine type to validate.
Returns:
True if the Check supports the given machine type, False otherwise.
"""
# Check explicitly stated supported machine types
return machine_type in self.supported_machine_types
@abc.abstractmethod
def set_up(self):
"""Set up for the check on a cluster."""
def clean_up(self) -> None:
"""Clean up after the check on a cluster."""
@abc.abstractmethod
def run(
self,
timeout_sec: int | None = None,
startup_sec: int = 30,
) -> str | None:
"""Run the check.
Args:
timeout_sec: The timeout in seconds for the check.
startup_sec: The time in seconds to wait for the health runner to start.
Returns:
The name of the health runner pod.
"""