Пример #1
0
 def test_string_validation(self):
     # Default string validation
     ConfigManager(folders=self.folders,
                   static_items=[
                       Config(name="some-string",
                              default_value="Any default is OK")
                   ])
Пример #2
0
 def test_invalid_config_name(self):
     try:
         # Invalid config name
         ConfigManager(folders=self.folders,
                       static_items=[Config(name="WithCapitals")])
         raise AssertionError("Shouldn't get here")
     except RpcException as e:
         assert e.rc == ResultCode.ERROR_PARAM_INVALID
Пример #3
0
    def test_folder_without_file_default(self):
        # A simple config manager -- shouldn't be disturbed is folder is non-null, but no config file inside
        ConfigManager(static_items=[SampleConfig],
                      folders=Folders(workspace=self.workspace_path,
                                      system=Path("/missing/folder")))

        # Check default value
        assert SampleConfig.INT_ITEM.int_val == 12
Пример #4
0
    def test_system_default(self, system_config):
        # A simple config manager
        ConfigManager(static_items=[SampleConfig],
                      folders=Folders(workspace=self.workspace_path,
                                      system=system_config))

        # Check default value
        assert SampleConfig.INT_ITEM.int_val == -78
Пример #5
0
 def test_invalid_default(self):
     # Try with invalid default value
     try:
         ConfigManager(folders=self.folders,
                       static_items=[SampleConfig],
                       cli_config={"my-int-config": "zzz"})
         raise AssertionError("Shouldn't get here")
     except RpcException as e:
         assert e.rc == ResultCode.ERROR_PARAM_INVALID
Пример #6
0
 def test_conflicting_configs(self):
     # Try with some conflicting items between statis and user lists
     try:
         ConfigManager(
             folders=self.folders,
             static_items=[SampleConfig],
             user_items=[Config(name="my-int-config", default_value="zzz")])
         raise AssertionError("Shouldn't get here")
     except RpcException as e:
         assert e.rc == ResultCode.ERROR_MODEL_INVALID
Пример #7
0
    def test_pos_int_validation(self):
        try:
            # Negative integer while expecting a positive one
            ConfigManager(
                folders=self.folders,
                static_items=[
                    Config(name="some-pos-int",
                           validator=ConfigValidator.CONFIG_VALID_POS_INT,
                           default_value="-69")
                ])
            raise AssertionError("Shouldn't get here")
        except RpcException as e:
            assert e.rc == ResultCode.ERROR_PARAM_INVALID

        # Same with correct default value
        ConfigManager(static_items=[
            Config(name="some-pos-int",
                   validator=ConfigValidator.CONFIG_VALID_POS_INT,
                   default_value="69")
        ])
Пример #8
0
 def test_missing_custom_validator(self):
     try:
         # Missing custom validator in config definition
         ConfigManager(
             folders=self.folders,
             static_items=[
                 Config(name="ok",
                        validator=ConfigValidator.CONFIG_VALID_CUSTOM)
             ])
         raise AssertionError("Shouldn't get here")
     except RpcException as e:
         assert e.rc == ResultCode.ERROR_PARAM_MISSING
Пример #9
0
    def test_cli_default(self, system_config, user_config, env_config):
        # A simple config manager
        ConfigManager(
            static_items=[SampleConfig],
            folders=Folders(workspace=self.workspace_path,
                            system=system_config,
                            user=user_config),
            cli_config={"my-int-config": "357"},
        )

        # Check default value
        assert SampleConfig.INT_ITEM.int_val == 357
Пример #10
0
 def test_float_validation(self):
     try:
         # Non-float value
         ConfigManager(folders=self.folders,
                       static_items=[
                           Config(
                               name="some-float",
                               validator=ConfigValidator.CONFIG_VALID_FLOAT,
                               default_value="not a float")
                       ])
         raise AssertionError("Shouldn't get here")
     except RpcException as e:
         assert e.rc == ResultCode.ERROR_PARAM_INVALID
Пример #11
0
    def test_invalid_json_model(self, system_config):
        # Dump non-object Json in config
        with (system_config / "config.json").open("w") as f:
            f.write('{"unclosed item')
        try:
            ConfigManager(static_items=[SampleConfig],
                          folders=Folders(workspace=self.workspace_path,
                                          system=system_config))
            raise AssertionError("Shouldn't get here")
        except RpcException as e:
            assert e.rc == ResultCode.ERROR_MODEL_INVALID

        # Dump object with non-string values Json in config
        with (system_config / "config.json").open("w") as f:
            json.dump({"some-int": 45}, f)
        try:
            ConfigManager(static_items=[SampleConfig],
                          folders=Folders(workspace=self.workspace_path,
                                          system=system_config))
            raise AssertionError("Shouldn't get here")
        except RpcException as e:
            assert e.rc == ResultCode.ERROR_MODEL_INVALID
Пример #12
0
 def test_empty_default(self):
     # Config item with empty default
     cm = ConfigManager(folders=self.folders,
                        static_items=[Config(name="ok", can_be_empty=True)])
     assert cm.static_items["ok"].str_val == ""
Пример #13
0
    def test_hard_coded_default(self):
        # A simple config manager
        ConfigManager(folders=self.folders, static_items=[SampleConfig])

        # Check default value
        assert SampleConfig.INT_ITEM.int_val == 12
Пример #14
0
    def __init__(
        self,
        port: int,
        descriptors: List[RpcServiceDescriptor],
        folders: Folders = None,
        cli_config: Dict[str, str] = None,
        static_items: List[Config] = None,
        user_items: List[Config] = None,
        with_events: bool = False,
        with_debug_signal: bool = True,
    ):
        RpcManager.__init__(self, PROXY_FILE)
        self.__port = port
        self.calls = []
        self.__shutdown_event = Event()

        # Prepare config manager
        # (by the way, add our own static items)
        config_m = ConfigManager(
            folders, cli_config, (static_items + [RpcStaticConfig])
            if static_items is not None else [RpcStaticConfig], user_items)

        # Create server instance, disabling port reuse
        self.__server = server(futures.ThreadPoolExecutor(
            max_workers=RpcStaticConfig.MAX_WORKERS.int_val),
                               options=[("grpc.so_reuseport", 0)])

        # Systematically add services:
        # - to handle server basic operations
        # - to handle remote user configuration update
        # - to handle remote loggers configuration update
        # - to handle events (if required)
        base_descriptors = [
            RpcServiceDescriptor(grpc_helper, "srv", ServerApiVersion, self,
                                 add_RpcServerServiceServicer_to_server,
                                 RpcServerServiceStub),
            RpcServiceDescriptor(grpc_helper, "config", ConfigApiVersion,
                                 config_m, add_ConfigServiceServicer_to_server,
                                 ConfigServiceStub),
            RpcServiceDescriptor(grpc_helper, "log", LoggerApiVersion,
                                 LogsManager(),
                                 add_LoggerServiceServicer_to_server,
                                 LoggerServiceStub),
        ]
        if with_events:
            base_descriptors.append(
                RpcServiceDescriptor(grpc_helper, "events", EventApiVersion,
                                     EventsManager(),
                                     add_EventServiceServicer_to_server,
                                     EventServiceStub))
        self.descriptors = {d.name: d for d in base_descriptors}

        # Get servicers
        self.descriptors.update({d.name: d for d in descriptors})

        # Load persisted proxies model
        proxies_model = self._load_config(folders.workspace)

        # Prepare auto clients stubs map
        stubs_map = {}

        # Register everything
        self.__info = {}
        for descriptor in self.descriptors.values():
            # Prepare folders and logger (only for non-proxy)
            if not descriptor.is_proxy:
                descriptor.manager._init_folders_n_logger(folders, port)

            # Persisted model?
            persisted_model = proxies_model[
                descriptor.name] if descriptor.name in proxies_model else None

            # Build info for service
            versions = list(descriptor.api_version.values())
            versions.remove(0)
            current_version = max(versions)
            info = ServiceInfo(
                name=descriptor.name,
                version=persisted_model[ProxyModel.VERSION]
                if persisted_model is not None else
                f"{descriptor.module.__title__}:{descriptor.module.__version__}",
                current_api_version=current_version,
                supported_api_version=min(versions),
                is_proxy=descriptor.is_proxy,
                proxy_port=persisted_model[ProxyModel.PORT]
                if persisted_model is not None else None,
                proxy_host=persisted_model[ProxyModel.HOST]
                if persisted_model is not None else None,
            )
            self.logger.debug(
                f"Registering service in RPC server: {trace_buffer(info)}")

            # Remember info
            self.__info[info.name] = info

            # Register servicer in RPC server
            descriptor.register_method(
                RpcServicer(descriptor.manager, descriptor.client_stub, info,
                            self), self.__server)

            # Contribute to auto-client stubs map
            stubs_map[descriptor.name] = (descriptor.client_stub,
                                          current_version)

        # Setup port and start
        self.logger.debug(f"Starting RPC server on port {self.__port}")
        try:
            self.__server.add_insecure_port(f"[::]:{self.__port}")
        except Exception as e:  # NOQA: B902
            msg = f"Failed to start RPC server on port {self.__port}: {e}"
            self.logger.error(msg)
            raise RpcException(msg, rc=ResultCode.ERROR_PORT_BUSY)
        self.__server.start()
        self.logger.debug(f"RPC server started on port {self.__port}")

        # Hook debug signal if required
        if with_debug_signal and os.name == "posix":
            signal.signal(signal.SIGUSR2, self.__dump_debug)

        # Load all managers (including client pointing to all served services)
        to_raise = None
        for descriptor in self.__real_descriptors:
            client = RpcClient(
                "localhost",
                self.__port,
                stubs_map,
                name=f"{descriptor.name}-client",
                timeout=RpcStaticConfig.CLIENT_TIMEOUT.float_val,
                logger=descriptor.manager.logger,
            )
            try:
                descriptor.manager._preload(client, folders)
            except Exception as e:
                self.logger.error(
                    f"Error occurred during {descriptor.name} manager loading: {e}\n"
                    + "".join(traceback.format_tb(e.__traceback__)))
                to_raise = e
                break

        # If something bad happened during managers loading, shutdown and raise exception
        if to_raise is not None:
            self.shutdown()
            raise to_raise