Ejemplo n.º 1
0
 async def execute_commands() -> None:
     # execute and collect all task commands
     results: Dict[TaskCommand, Any] = {}
     for command in commands:
         if isinstance(command, SendMessage):
             await self.message_bus.emit(command.message)
             results[command] = None
         elif isinstance(command, ExecuteOnCLI):
             # TODO: instead of executing it in process, we should do an http call here to a worker core.
             ctx = CLIContext({
                 **command.env,
                 **wi.descriptor.environment
             })
             result = await self.cli.execute_cli_command(
                 command.command, stream.list, ctx)
             results[command] = result
         else:
             raise AttributeError(
                 f"Does not understand this command: {wi.descriptor.name}:  {command}"
             )
     # The descriptor might be removed in the mean time. If this is the case stop execution.
     if wi.descriptor_alive:
         active_before_result = wi.is_active
         # before we move on, we need to store the current state of the task (or delete if it is done)
         await self.store_running_task_state(wi, origin_message)
         # inform the task about the result, which might trigger new tasks to execute
         new_commands = wi.handle_command_results(results)
         if new_commands:
             # note: recursion depth is defined by the number of steps in a job description and should be safe.
             await self.execute_task_commands(wi, new_commands)
         elif active_before_result and not wi.is_active:
             # if this was the last result the task was waiting for, delete the task
             await self.store_running_task_state(wi, origin_message)
Ejemplo n.º 2
0
async def test_configs_command(cli: CLI, tmp_directory: str) -> None:
    config_file = os.path.join(tmp_directory, "config.yml")

    async def check_file_is_yaml(res: Stream) -> None:
        async with res.stream() as streamer:
            async for s in streamer:
                with open(s, "r") as file:
                    yaml.safe_load(file.read())

    # create a new config entry
    create_result = await cli.execute_cli_command(
        "configs set test_config t1=1, t2=2, t3=3 ", stream.list)
    assert create_result[0][0] == "t1: 1\nt2: 2\nt3: 3\n"
    # show the entry - should be the same as the created one
    show_result = await cli.execute_cli_command("configs show test_config",
                                                stream.list)
    assert show_result[0][0] == "t1: 1\nt2: 2\nt3: 3\n"
    # list all configs: only one is defined
    list_result = await cli.execute_cli_command("configs list", stream.list)
    assert list_result[0] == ["test_config"]
    # edit the config: will make the config available as file
    await cli.execute_cli_command("configs edit test_config",
                                  check_file_is_yaml)
    # update the config
    update_doc = "a: '1'\nb: 2\nc: true\nd: null\n"
    with open(config_file, "w") as file:
        file.write(update_doc)
    ctx = CLIContext(uploaded_files={"config.yaml": config_file})
    update_result = await cli.execute_cli_command(
        f"configs update test_config {config_file}", stream.list, ctx)
    assert update_result == [[]]
    # show the entry - should be the same as the created one
    show_updated_result = await cli.execute_cli_command(
        "configs show test_config", stream.list)
    assert show_updated_result[0][0] == update_doc
Ejemplo n.º 3
0
async def test_jq_command(cli: CLI) -> None:
    ctx = CLIContext(env={"section": "reported"}, query=Query.by("test"))
    # .test -> .reported.test
    assert JqCommand.rewrite_props(".a,.b", ctx) == ".reported.a,.reported.b"
    # absolute paths are rewritten correctly
    assert JqCommand.rewrite_props("./reported", ctx) == ".reported"
    # object construction is supported
    assert JqCommand.rewrite_props("{a:.a, b:.b}",
                                   ctx) == "{a:.reported.a, b:.reported.b}"
    # no replacement after pipe
    assert JqCommand.rewrite_props(
        "map(.color) | {a:.a, b:.b}",
        ctx) == "map(.reported.color) | {a:.a, b:.b}"

    result = await cli.execute_cli_command('json {"a":{"b":1}} | jq ".a.b"',
                                           stream.list)
    assert len(result[0]) == 1
    assert result[0][0] == 1

    # allow absolute paths as json path
    result = await cli.execute_cli_command(
        'json {"id":"123", "reported":{"b":1}} | jq "./reported"', stream.list)
    assert result == [[{"b": 1}]]

    # jq .kind is rewritten as .reported.kind
    result = await cli.execute_cli_command("search is(foo) limit 2 | jq .kind",
                                           stream.list)
    assert result[0] == ["foo", "foo"]
Ejemplo n.º 4
0
    def __init__(
        self,
        running_task_db: RunningTaskDb,
        job_db: JobDb,
        message_bus: MessageBus,
        event_sender: AnalyticsEventSender,
        subscription_handler: SubscriptionHandler,
        scheduler: Scheduler,
        cli: CLI,
        config: CoreConfig,
    ):
        self.running_task_db = running_task_db
        self.job_db = job_db
        self.message_bus = message_bus
        self.event_sender = event_sender
        self.subscription_handler = subscription_handler
        self.scheduler = scheduler
        self.cli = cli
        self.cli_context = CLIContext(source="task_handler")
        self.config = config
        # note: the waiting queue is kept in memory and lost when the service is restarted.
        self.start_when_done: Dict[str, TaskDescription] = {}

        # Step1: define all workflows and jobs in code: later it will be persisted and read from database
        self.task_descriptions: Sequence[TaskDescription] = [*self.known_workflows(config), *self.known_jobs()]
        self.tasks: Dict[str, RunningTask] = {}
        self.message_bus_watcher: Optional[Task[None]] = None
        self.initial_start_workflow_task: Optional[Task[None]] = None
        self.timeout_watcher = Periodic("task_timeout_watcher", self.check_overdue_tasks, timedelta(seconds=10))
        self.registered_event_trigger: List[Tuple[EventTrigger, TaskDescription]] = []
        self.registered_event_trigger_by_message_type: Dict[str, List[Tuple[EventTrigger, TaskDescription]]] = {}
Ejemplo n.º 5
0
def test_alias_template() -> None:
    params = [
        AliasTemplateParameter("a", "some a"),
        AliasTemplateParameter("b", "some b", "bv")
    ]
    tpl = AliasTemplate("foo", "does foes", "{{a}} | {{b}}", params)
    assert tpl.render({"a": "test", "b": "bla"}) == "test | bla"
    assert tpl.rendered_help(CLIContext()) == dedent("""
        foo: does foes
        ```shell
        foo a=<value>, b=<value>
        ```
        ## Parameters
        - `a`: some a
        - `b` [default: bv]: some b

        ## Template
        ```shell
        > {{a}} | {{b}}
        ```

        ## Example
        ```shell
        # Executing this alias template
        > foo a="test_a"
        # Will expand to this command
        > test_a | bv
        ```
        """)
Ejemplo n.º 6
0
async def test_write_command(cli: CLI) -> None:
    async def check_file(res: Stream,
                         check_content: Optional[str] = None) -> None:
        async with res.stream() as streamer:
            only_one = True
            async for s in streamer:
                assert isinstance(s, str)
                p = Path(s)
                assert p.exists() and p.is_file()
                assert 1 < p.stat().st_size < 100000
                assert p.name.startswith("write_test")
                assert only_one
                only_one = False
                if check_content:
                    with open(s, "r") as file:
                        data = file.read()
                        assert data == check_content

    # result can be read as json
    await cli.execute_cli_command(
        "search all limit 3 | format --json | write write_test.json ",
        check_file)
    # result can be read as yaml
    await cli.execute_cli_command(
        "search all limit 3 | format --yaml | write write_test.yaml ",
        check_file)
    # write enforces unescaped output.
    env = {
        "now": utc_str()
    }  # fix the time, so that replacements will stay equal
    truecolor = CLIContext(console_renderer=ConsoleRenderer(
        80, 25, ConsoleColorSystem.truecolor, True),
                           env=env)
    monochrome = CLIContext(
        console_renderer=ConsoleRenderer.default_renderer(), env=env)
    # Make sure, that the truecolor output is different from monochrome output
    mono_out = await cli.execute_cli_command("help", stream.list, monochrome)
    assert await cli.execute_cli_command("help", stream.list,
                                         truecolor) != mono_out
    # We expect the content of the written file to contain monochrome output.
    assert await cli.execute_cli_command(
        "help | write write_test.txt",
        partial(check_file, check_content="".join(mono_out[0]) + "\n"),
        truecolor)
Ejemplo n.º 7
0
 def cli_context_from_request(request: Request) -> CLIContext:
     try:
         columns = int(request.headers.get("Resoto-Shell-Columns", "120"))
         rows = int(request.headers.get("Resoto-Shell-Rows", "50"))
         terminal = request.headers.get("Resoto-Shell-Terminal",
                                        "false") == "true"
         colors = ConsoleColorSystem.from_name(
             request.headers.get("Resoto-Shell-Color-System", "monochrome"))
         renderer = ConsoleRenderer(width=columns,
                                    height=rows,
                                    color_system=colors,
                                    terminal=terminal)
         return CLIContext(env=dict(request.query),
                           console_renderer=renderer)
     except Exception as ex:
         log.debug("Could not create CLI context.", exc_info=ex)
         return CLIContext(
             env=dict(request.query),
             console_renderer=ConsoleRenderer.default_renderer())
Ejemplo n.º 8
0
def test_supports_color() -> None:
    assert not CLIContext().supports_color()
    assert not CLIContext(console_renderer=ConsoleRenderer()).supports_color()
    assert not CLIContext(console_renderer=ConsoleRenderer(
        color_system=ConsoleColorSystem.monochrome)).supports_color()
    assert CLIContext(console_renderer=ConsoleRenderer(
        color_system=ConsoleColorSystem.standard)).supports_color()
    assert CLIContext(console_renderer=ConsoleRenderer(
        color_system=ConsoleColorSystem.eight_bit)).supports_color()
    assert CLIContext(console_renderer=ConsoleRenderer(
        color_system=ConsoleColorSystem.truecolor)).supports_color()
Ejemplo n.º 9
0
async def test_system_restore_command(cli: CLI, tmp_directory: str) -> None:
    backup = os.path.join(tmp_directory, "backup")

    async def move_backup(res: Stream) -> None:
        async with res.stream() as streamer:
            async for s in streamer:
                os.rename(s, backup)

    await cli.execute_cli_command("system backup create", move_backup)
    ctx = CLIContext(uploaded_files={"backup": backup})
    restore = await cli.execute_cli_command(
        f"BACKUP_NO_SYS_EXIT=true system backup restore {backup}", stream.list,
        ctx)
    assert restore == [[
        "Database has been restored successfully!",
        "Since all data has changed in the database eventually, this service needs to be restarted!",
    ]]
Ejemplo n.º 10
0
def test_format() -> None:
    context = CLIContext()
    fn = context.formatter("foo={foo} and bla={bla}: {bar}")
    assert fn({}) == "foo=null and bla=null: null"
    assert fn({"foo": 1, "bla": 2, "bar": 3}) == "foo=1 and bla=2: 3"
Ejemplo n.º 11
0
def test_context_format() -> None:
    context = CLIContext()
    fn, vs = context.formatter_with_variables("foo={foo} and bla={bla}: {bar}")
    assert vs == {"foo", "bla", "bar"}
    assert fn({}) == "foo=null and bla=null: null"
    assert fn({"foo": 1, "bla": 2, "bar": 3}) == "foo=1 and bla=2: 3"
Ejemplo n.º 12
0
 async def execute(cmd: str) -> List[List[JsonElement]]:
     ctx = CLIContext(cli.cli_env)
     return await cli.execute_cli_command(cmd, stream.list, ctx)
Ejemplo n.º 13
0
async def test_welcome(cli: CLI) -> None:
    ctx = CLIContext(console_renderer=ConsoleRenderer.default_renderer())
    result = await cli.execute_cli_command(f"welcome", stream.list, ctx)
    assert "Resoto" in result[0][0]