From fabf1b4fd2c1ae0245d2f0c6000ff8d9d197a29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B0=D0=B2=D0=B5=D0=BB=20=D0=96=D1=83=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= <33721692+LeaveMyYard@users.noreply.github.com> Date: Mon, 15 May 2023 18:19:57 +0300 Subject: [MAIN-169] Add strategy description --- robusta_krr/core/abstract/strategies.py | 6 +++++- robusta_krr/core/models/result.py | 1 + robusta_krr/core/runner.py | 3 ++- robusta_krr/formatters/table.py | 2 +- robusta_krr/strategies/simple.py | 23 +++++++++++++++++++++++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/robusta_krr/core/abstract/strategies.py b/robusta_krr/core/abstract/strategies.py index c175bd6..66d93fa 100644 --- a/robusta_krr/core/abstract/strategies.py +++ b/robusta_krr/core/abstract/strategies.py @@ -21,7 +21,7 @@ class ResourceRecommendation(pd.BaseModel): @classmethod def undefined(cls: type[Self]) -> Self: - return cls(request=Decimal('NaN'), limit=Decimal('NaN')) + return cls(request=float('NaN'), limit=float('NaN')) class StrategySettings(pd.BaseModel): @@ -61,6 +61,10 @@ class BaseStrategy(abc.ABC, Generic[_StrategySettings]): def __str__(self) -> str: return self.__display_name__.title() + @property + def description(self) -> str | None: + return None + @abc.abstractmethod def run(self, history_data: HistoryData, object_data: K8sObjectData) -> RunResult: """Run the strategy to calculate the recommendation""" diff --git a/robusta_krr/core/models/result.py b/robusta_krr/core/models/result.py index 7c686f7..e932fef 100644 --- a/robusta_krr/core/models/result.py +++ b/robusta_krr/core/models/result.py @@ -93,6 +93,7 @@ class Result(pd.BaseModel): scans: list[ResourceScan] score: int = 0 resources: list[str] = ["cpu", "memory"] + description: str | None = None def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) diff --git a/robusta_krr/core/runner.py b/robusta_krr/core/runner.py index 1c67a29..9e1096b 100644 --- a/robusta_krr/core/runner.py +++ b/robusta_krr/core/runner.py @@ -144,7 +144,8 @@ class Runner(Configurable): return Result( scans=[ ResourceScan.calculate(obj, recommended) for obj, recommended in zip(objects, resource_recommendations) - ] + ], + description=self._strategy.description, ) async def run(self) -> None: diff --git a/robusta_krr/formatters/table.py b/robusta_krr/formatters/table.py index 8a93f04..6918512 100644 --- a/robusta_krr/formatters/table.py +++ b/robusta_krr/formatters/table.py @@ -50,7 +50,7 @@ class TableFormatter(BaseFormatter): :rtype: str """ - table = Table(show_header=True, header_style="bold magenta", title=f"Scan result ({result.score} points)") + table = Table(show_header=True, header_style="bold magenta", title=f"Scan result ({result.score} points)", caption=result.description) table.add_column("Number", justify="right", no_wrap=True) table.add_column("Cluster", style="cyan") diff --git a/robusta_krr/strategies/simple.py b/robusta_krr/strategies/simple.py index aaf31d7..f528ba8 100644 --- a/robusta_krr/strategies/simple.py +++ b/robusta_krr/strategies/simple.py @@ -1,6 +1,7 @@ import pydantic as pd import numpy as np from numpy.typing import NDArray +from textwrap import dedent from robusta_krr.core.abstract.strategies import ( BaseStrategy, @@ -41,8 +42,30 @@ class SimpleStrategySettings(StrategySettings): class SimpleStrategy(BaseStrategy[SimpleStrategySettings]): + """ + A simple strategy that uses the {cpu_percentile} percentile for CPU and + the peak memory usage plus {memory_buffer_percentage}% buffer for memory. + (Exact numbers can be setup in the settings) + + For CPU, we set a request at the {cpu_percentile} percentile with no limit. + Meaning, in {cpu_percentile}% of the cases, your CPU request will be sufficient. + For the remaining - we set no limit. + + This means your pod can burst and use any CPU available on the node - e.g. + CPU that other pods requested but aren't using right now. + + For memory, we take the maximum value over the past week and add a {memory_buffer_percentage}% buffer. + """ + __display_name__ = "simple" + @property + def description(self) -> str | None: + if self.__doc__ is None: + return None + + return dedent(self.__doc__.format_map(self.settings.dict())).strip() + def run(self, history_data: HistoryData, object_data: K8sObjectData) -> RunResult: cpu_usage = self.settings.calculate_cpu_proposal(history_data[ResourceType.CPU]) memory_usage = self.settings.calculate_memory_proposal(history_data[ResourceType.Memory]) -- cgit v1.2.3