Skip to content

Commit

Permalink
[resotocore][feat] introduce refine-resource-data command (#1937)
Browse files Browse the repository at this point in the history
* [resotocore][feat] introduce refine-resource-data command

* use a different path
  • Loading branch information
aquamatthias authored Feb 22, 2024
1 parent 1202408 commit 4011f20
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 2 deletions.
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 @@ async def detect_secrets_in(content: JsStream) -> JsGen:
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 []

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

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 @@ def all_commands(d: TenantDependencies) -> List[CLICommand]:
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

0 comments on commit 4011f20

Please sign in to comment.