async def test_config_change_emits_event(config_handler: ConfigHandler,
                                         all_events: List[Message]) -> None:
    # Put a config
    all_events.clear()
    config_id = ConfigId("foo")
    cfg = await config_handler.put_config(ConfigEntity(config_id,
                                                       dict(test=1)))
    message = await wait_for_message(all_events, CoreMessage.ConfigUpdated,
                                     Event)
    assert message.data["id"] == cfg.id
    assert message.data["revision"] == cfg.revision

    # Patch a config
    all_events.clear()
    cfg = await config_handler.patch_config(
        ConfigEntity(config_id, dict(foo=2)))
    message = await wait_for_message(all_events, CoreMessage.ConfigUpdated,
                                     Event)
    assert message.data["id"] == cfg.id
    assert message.data["revision"] == cfg.revision

    # Delete a config
    all_events.clear()
    await config_handler.delete_config(config_id)
    message = await wait_for_message(all_events, CoreMessage.ConfigDeleted,
                                     Event)
    assert message.data["id"] == config_id
    assert "revision" not in message.data
Пример #2
0
    async def __update_config(self) -> None:
        # in case the internal configuration holds new properties, we update the existing config always.
        try:
            existing = await self.config_handler.get_config(ResotoCoreConfigId)
            empty = empty_config().json()
            updated = deep_merge(empty, existing.config) if existing else empty
            updated = migrate_config(updated)
            if existing is None or updated != existing.config:
                await self.config_handler.put_config(
                    ConfigEntity(ResotoCoreConfigId, updated), False)
                log.info("Default resoto config updated.")
        except Exception as ex:
            log.error(f"Could not update resoto default configuration: {ex}",
                      exc_info=ex)

        # make sure there is a default command configuration
        # note: this configuration is only created one time and never updated
        try:
            existing_commands = await self.config_handler.get_config(
                ResotoCoreCommandsConfigId)
            if existing_commands is None:
                await self.config_handler.put_config(
                    ConfigEntity(ResotoCoreCommandsConfigId,
                                 CustomCommandsConfig().json()), False)
                log.info("Default resoto commands config updated.")
        except Exception as ex:
            log.error(f"Could not update resoto command configuration: {ex}",
                      exc_info=ex)
async def test_config(config_handler: ConfigHandler) -> None:
    # list is empty on start
    assert [a async for a in config_handler.list_config_ids()] == []

    config_id = ConfigId("test")
    # add one entry
    entity = ConfigEntity(config_id, {"test": True})
    assert await config_handler.put_config(entity) == entity

    # get one entry
    assert await config_handler.get_config(config_id) == entity

    # patch the config
    assert await config_handler.patch_config(
        ConfigEntity(config_id,
                     {"rest": False})) == ConfigEntity(config_id, {
                         "test": True,
                         "rest": False
                     })

    # list all configs
    assert [a async for a in config_handler.list_config_ids()] == ["test"]

    # delete the config
    assert await config_handler.delete_config(config_id) is None

    # list all configs
    assert [a async for a in config_handler.list_config_ids()] == []
async def test_config_validation(config_handler: ConfigHandler,
                                 config_model: List[Kind]) -> None:
    await config_handler.update_configs_model(config_model)
    valid_config = {
        "section": {
            "some_number": 32,
            "some_string": "test",
            "some_sub": {
                "num": 32
            }
        }
    }

    # define the model
    await config_handler.put_config_validation(ConfigValidation("test", True))

    # list all available models
    assert [a async for a in config_handler.list_config_validation_ids()
            ] == ["test"]

    # get the model
    model: ConfigValidation = await config_handler.get_config_validation(
        "test")  # type: ignore
    assert model.external_validation is True

    # check the config against the model
    invalid_config = {"section": {"some_number": "no number"}}
    invalid_config_id = ConfigId("invalid_config")
    with pytest.raises(AttributeError) as reason:
        await config_handler.put_config(
            ConfigEntity(invalid_config_id, invalid_config))
    assert "some_number is not valid: Expected type int32 but got str" in str(
        reason.value)

    # External validation turned on: config with name "invalid_config" is rejected by the configured worker
    await config_handler.put_config_validation(
        ConfigValidation(invalid_config_id, True))
    with pytest.raises(AttributeError) as reason:
        # The config is actually valid, but the external validation will fail
        await config_handler.put_config(
            ConfigEntity(invalid_config_id, valid_config))
    assert "Error executing task: Invalid Config ;)" in str(reason)

    # If external validation is turned off, the configuration can be updated
    await config_handler.put_config_validation(
        ConfigValidation(invalid_config_id, False))
    await config_handler.put_config(
        ConfigEntity(invalid_config_id, valid_config))
Пример #5
0
 async def patch_config(self, request: Request) -> StreamResponse:
     config_id = request.match_info["config_id"]
     patch = await self.json_from_request(request)
     updated = await self.config_handler.patch_config(
         ConfigEntity(config_id, patch))
     headers = {"Resoto-Config-Revision": updated.revision}
     return await single_result(request, updated.config, headers)
Пример #6
0
 async def patch_config(self, cfg: ConfigEntity) -> ConfigEntity:
     current = await self.cfg_db.get(cfg.id)
     current_config = current.config if current else {}
     coerced = await self.coerce_and_check_model(cfg.id, {**current_config, **cfg.config})
     result = await self.cfg_db.update(ConfigEntity(cfg.id, coerced, current.revision if current else None))
     await self.message_bus.emit_event(CoreMessage.ConfigUpdated, dict(id=result.id, revision=result.revision))
     return result
Пример #7
0
def configs() -> List[ConfigEntity]:
    return [
        ConfigEntity(f"id_{a}", {
            "some": a,
            "config": "test"
        }) for a in range(0, 10)
    ]
Пример #8
0
 async def put_config(self, request: Request) -> StreamResponse:
     config_id = request.match_info["config_id"]
     validate = request.query.get("validate", "true").lower() != "false"
     config = await self.json_from_request(request)
     result = await self.config_handler.put_config(
         ConfigEntity(config_id, config), validate)
     headers = {"Resoto-Config-Revision": result.revision}
     return await single_result(request, result.config, headers)
Пример #9
0
 async def put_config(self, cfg: ConfigEntity, validate: bool = True) -> ConfigEntity:
     coerced = await self.coerce_and_check_model(cfg.id, cfg.config, validate)
     existing = await self.cfg_db.get(cfg.id)
     if not existing or existing.config != cfg.config:
         result = await self.cfg_db.update(ConfigEntity(cfg.id, coerced, cfg.revision))
         await self.message_bus.emit_event(CoreMessage.ConfigUpdated, dict(id=result.id, revision=result.revision))
         return result
     else:
         return existing
async def test_config_yaml(config_handler: ConfigHandler,
                           config_model: List[Kind]) -> None:
    await config_handler.update_configs_model(config_model)
    config = {
        "some_number": 32,
        "some_string": "test",
        "some_sub": {
            "num": 32
        }
    }
    expect_comment = dedent("""
        section:
          # Some number.
          # And some description.
          some_number: 32
          # Some string.
          # And some description.
          some_string: 'test'
          # Some sub.
          # And some description.
          some_sub:
            # Some arbitrary number.
            num: 32
        """).strip()
    expect_no_comment = dedent("""
        another_section:
          some_number: 32
          some_string: test
          some_sub:
            num: 32
        """).strip()
    # config has section with attached model
    test_config_id = ConfigId("test")
    await config_handler.put_config(
        ConfigEntity(test_config_id, {"section": config}))
    assert expect_comment in (await config_handler.config_yaml(test_config_id)
                              or "")
    # different section with no attached model
    nomodel_config_id = ConfigId("no_model")
    await config_handler.put_config(
        ConfigEntity(nomodel_config_id, {"another_section": config}))
    assert expect_no_comment in (await
                                 config_handler.config_yaml(nomodel_config_id)
                                 or "")
def test_config_entity_roundtrip() -> None:
    entity = ConfigEntity(ConfigId("test"), {"test": 1}, "test")
    again = from_js(to_js(entity), ConfigEntity)
    assert entity == again