Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[resotocore][feat] introduce refine-resource-data command #1937

Merged
merged 2 commits into from
Feb 22, 2024
Merged
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
68 changes: 68 additions & 0 deletions resotocore/resotocore/cli/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -6365,6 +6365,73 @@
return CLIFlow(detect_secrets_in)


@frozen
class ResourceRefinementMatch:
on: PropertyPath
value: JsonElement

def matches(self, js: JsonElement) -> bool:
return self.on.value_in(js) == self.value


@frozen
class ResourceRefinement:
kind: str
matches: ResourceRefinementMatch
path: List[str]
value: JsonElement


ResourceRefinements = [
ResourceRefinement(
kind="instance",
matches=ResourceRefinementMatch(PropertyPath.from_list(["reported", "instance_status"]), value="terminated"),
path=["kind", "metadata", "state-icon"],
value="instance_terminated",
),
]


class RefineResourceDataCommand(CLICommand, InternalPart):
"""
Internal command to adjust resource data for the UI to display.
Not intended for direct use.
"""

@property
def name(self) -> str:
return "refine-resource-data"

def args_info(self) -> ArgsInfo:
return []

Check warning on line 6406 in resotocore/resotocore/cli/command.py

View check run for this annotation

Codecov / codecov/patch

resotocore/resotocore/cli/command.py#L6406

Added line #L6406 was not covered by tests

def info(self) -> str:
return "Refine resource data"

Check warning on line 6409 in resotocore/resotocore/cli/command.py

View check run for this annotation

Codecov / codecov/patch

resotocore/resotocore/cli/command.py#L6409

Added line #L6409 was not covered by tests

def parse(self, arg: Optional[str] = None, ctx: CLIContext = EmptyContext, **kwargs: Any) -> CLIAction:
async def load_model() -> Model:
return await self.dependencies.model_handler.load_model(ctx.graph_name)

def setup_stream(in_stream: JsStream) -> JsStream:
def with_dependencies(model: Model) -> JsStream:
async def process_element(el: JsonElement) -> JsonElement:
if (
is_node(el)
and (fqn := value_in_path(el, NodePath.reported_kind))
and isinstance(kind := model.get(fqn), ComplexKind)
):
for refinement in ResourceRefinements:
if kind.is_a(refinement.kind) and refinement.matches.matches(el):
set_value_in_path(refinement.value, refinement.path, el) # type: ignore
return el

return in_stream | pipe.map(process_element) # type: ignore

return stream.call(load_model) | pipe.flatmap(with_dependencies) # type: ignore

return CLIFlow(setup_stream, required_permissions={Permission.read})


def all_commands(d: TenantDependencies) -> List[CLICommand]:
commands = [
AggregateCommand(d, "search"),
Expand Down Expand Up @@ -6397,6 +6464,7 @@
TemplatesCommand(d, "search", allowed_in_source_position=True),
PredecessorsPart(d, "search"),
ProtectCommand(d, "action"),
RefineResourceDataCommand(d, "action"),
ReportCommand(d, "misc", allowed_in_source_position=True),
SearchPart(d, "search", allowed_in_source_position=True),
SetDesiredCommand(d, "action"),
Expand Down
3 changes: 3 additions & 0 deletions resotocore/resotocore/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,9 @@ def is_root(self) -> bool:
def kind_hierarchy(self) -> Set[str]:
return self.__resolved_hierarchy

def is_a(self, kind: str) -> bool:
return kind in self.__resolved_hierarchy

def resolved_property_paths(self) -> List[ResolvedPropertyPath]:
return self.__property_by_path

Expand Down
36 changes: 34 additions & 2 deletions resotocore/tests/resotocore/cli/command_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@
from resotocore import version
from resotocore.cli import is_node, JsStream, list_sink
from resotocore.cli.cli import CLIService
from resotocore.cli.command import HttpCommand, JqCommand, AggregateCommand, all_commands
from resotocore.cli.command import (
HttpCommand,
JqCommand,
AggregateCommand,
all_commands,
ResourceRefinement,
ResourceRefinementMatch,
)
from resotocore.cli.model import CLIContext, WorkerCustomCommand, CLI, FilePath
from resotocore.cli.tip_of_the_day import generic_tips
from resotocore.console_renderer import ConsoleRenderer, ConsoleColorSystem
Expand All @@ -30,7 +37,7 @@
from resotocore.ids import InfraAppName, GraphName
from resotocore.infra_apps.package_manager import PackageManager
from resotocore.infra_apps.runtime import Runtime
from resotocore.model.model import Model
from resotocore.model.model import Model, PropertyPath
from resotocore.model.typed_model import to_js
from resotocore.query.model import Template, Query
from resotocore.report import Inspector
Expand Down Expand Up @@ -1435,6 +1442,31 @@ async def exec(cmd: str) -> List[JsonElement]:
assert len(res) == 1


@pytest.mark.asyncio
async def test_refine_resource_data(cli: CLI) -> None:
from resotocore.cli import command

# override the default resource refinements for testing
command.ResourceRefinements = [
ResourceRefinement(
kind="foo",
matches=ResourceRefinementMatch(PropertyPath.from_list(["id"]), value="c"),
path=["reported", "name"],
value="some",
),
]

async def exec(js: Json) -> List[JsonElement]:
res = await cli.execute_cli_command(f"json {json.dumps(js)} | refine-resource-data | dump", list_sink)
return cast(List[JsonElement], res[0])

base = {"id": "a", "reported": {"id": "a", "name": "b", "kind": "foo"}}
assert await exec(base) == [base]
assert await exec({**base, "id": "c"}) == [{"id": "c", "reported": {"id": "a", "name": "some", "kind": "foo"}}]

# assert await exec(base) == [{"id": "a", "reported": {"id": "b", "name": "d", "kind": "foo"}}]


@pytest.mark.asyncio
async def test_detect_secrets(cli: CLI) -> None:
async def detect(to_check: JsonElement) -> List[JsonElement]:
Expand Down
Loading