Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ packages = [{ include = "robusta_krr" }]

[tool.black]
line-length = 120
target-version = ['py39']
target-version = ['py310', 'py311', 'py312', 'py313']

[tool.isort]
line_length = 120
Expand All @@ -23,16 +23,19 @@ plugins = "numpy.typing.mypy_plugin,pydantic.mypy"
krr = "robusta_krr.main:run"

[tool.poetry.dependencies]
python = ">=3.10,<=3.12.9"
typer = { extras = ["all"], version = "^0.7.0" }
python = ">=3.10,<3.14"
typer = { extras = ["all"], version = ">=0.15.0,<1" }
pydantic = "^1.10.7"
kubernetes = "^26.1.0"
prometheus-api-client = "0.5.3"
numpy = ">=1.26.4,<1.27.0"
numpy = [
{version = ">=2.1.0,<3", python = ">=3.13"},
{version = ">=1.26.4,<3", python = "<3.13"},
]
alive-progress = "^3.1.2"
prometrix = "0.2.11"
slack-sdk = "^3.21.3"
pandas = "2.2.2"
pandas = ">=2.2.3,<4"
requests = ">2.32.4"
pyyaml = "6.0.1"
typing-extensions = "4.6.0"
Expand Down
14 changes: 9 additions & 5 deletions robusta_krr/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@

@app.command(rich_help_panel="Utils")
def version() -> None:
"""Print the current version of KRR."""
typer.echo(get_version())


def __process_type(_T: type) -> type:
"""Process type to a python literal"""
"""Process type annotation to a Python literal suitable for CLI parsing."""
if _T in (int, float, str, bool, datetime, UUID):
return _T
elif _T is Optional:
Expand All @@ -49,9 +50,11 @@ def __process_type(_T: type) -> type:


def load_commands() -> None:
"""Dynamically register CLI commands for all available strategies."""
for strategy_name, strategy_type in BaseStrategy.get_all().items(): # type: ignore
# NOTE: This wrapper here is needed to avoid the strategy_name being overwritten in the loop
def strategy_wrapper(_strategy_name: str = strategy_name):
"""Wrap strategy registration to capture the strategy name."""
def run_strategy(
ctx: typer.Context,
kubeconfig: Optional[str] = typer.Option(
Expand Down Expand Up @@ -259,7 +262,7 @@ def run_strategy(
),
show_severity: bool = typer.Option(
True,
" /--exclude-severity",
"--show-severity/--exclude-severity",
help="Whether to include the severity in the output or not",
rich_help_panel="Output Settings",
),
Expand Down Expand Up @@ -352,7 +355,7 @@ def run_strategy(
),
**strategy_args,
) -> None:
f"""Run KRR using the `{_strategy_name}` strategy"""
"""Run KRR using the configured strategy."""
if not show_severity and format != "csv":
raise click.BadOptionUsage("--exclude-severity", "--exclude-severity works only with format=csv")

Expand All @@ -362,8 +365,8 @@ def run_strategy(
impersonate_user=impersonate_user,
impersonate_group=impersonate_group,
clusters="*" if all_clusters else clusters,
namespaces="*" if "*" in namespaces else namespaces,
resources="*" if "*" in resources else resources,
namespaces="*" if namespaces and "*" in namespaces else namespaces,
resources="*" if resources and "*" in resources else resources,
selector=selector,
prometheus_url=prometheus_url,
prometheus_auth_header=prometheus_auth_header,
Expand Down Expand Up @@ -444,6 +447,7 @@ def run_strategy(


def run() -> None:
"""Entry point that loads commands and starts the Typer application."""
load_commands()
app()

Expand Down
6 changes: 5 additions & 1 deletion tests/test_krr.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
from robusta_krr.core.integrations.kubernetes import ClusterLoader
from robusta_krr.core.models.config import settings

runner = CliRunner(mix_stderr=False)
runner = CliRunner()
load_commands()

STRATEGY_NAME = "simple"


def test_help():
"""Verify that the help command exits successfully."""
result = runner.invoke(app, [STRATEGY_NAME, "--help"])
try:
assert result.exit_code == 0
Expand All @@ -23,6 +24,7 @@ def test_help():

@pytest.mark.parametrize("log_flag", ["-v", "-q"])
def test_run(log_flag: str):
"""Verify that a simple scan runs successfully with log flags."""
result = runner.invoke(app, [STRATEGY_NAME, log_flag, "--namespace", "default"])
try:
assert result.exit_code == 0, result.stdout
Expand All @@ -33,6 +35,7 @@ def test_run(log_flag: str):
@pytest.mark.parametrize("format", ["json", "yaml", "table", "pprint", "csv"])
@pytest.mark.parametrize("output", ["--logtostderr", "-q"])
def test_output_formats(format: str, output: str):
"""Verify that all supported output formats produce successful results."""
result = runner.invoke(app, [STRATEGY_NAME, output, "-f", format])
try:
assert result.exit_code == 0, result.exc_info
Expand Down Expand Up @@ -74,6 +77,7 @@ def test_cluster_namespace_list(
cluster_all_ns: list[str],
expected: Union[Literal["*"], list[str]],
):
"""Verify namespace filtering and regex matching for cluster loader."""
cluster = ClusterLoader()
with patch("robusta_krr.core.models.config.settings.namespaces", setting_namespaces):
with patch.object(
Expand Down
3 changes: 2 additions & 1 deletion tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from robusta_krr.main import app, load_commands

runner = CliRunner(mix_stderr=False)
runner = CliRunner()
load_commands()


Expand All @@ -17,5 +17,6 @@
],
)
def test_exclude_severity_option(args: list[str], expected_exit_code: int) -> None:
"""Verify that --exclude-severity flag behaves correctly across formats."""
result: Result = runner.invoke(app, ["simple", *args])
assert result.exit_code == expected_exit_code