def test_correctness(self, tmpdir): config_file = os.path.join(str(tmpdir), "config.yaml") logging_config_file = os.path.join(str(tmpdir), "logging.json") beer_garden.config.generate( ["-c", config_file, "-l", logging_config_file]) spec = YapconfSpec(beer_garden.config._SPECIFICATION) spec.add_source(label="config_file", source_type="yaml", filename=config_file) config = spec.load_config("config_file") # Defaults from spec assert config.log.fallback_file is None assert config.log.fallback_level == "INFO" # Value passed in assert config.log.config_file == logging_config_file # Ensure that bootstrap items were not written to file assert config.configuration.file is None with open(config_file) as f: yaml_config = yaml.safe_load(f) assert "configuration" not in yaml_config
def _safe_migrate(spec: YapconfSpec, filename: str) -> None: """Copy existing file to backup before migrating configuration Args: spec: Yapconf specification filename: Config filename Returns: None """ tmp_filename = filename + ".tmp" try: spec.migrate_config_file( filename, current_file_type="yaml", output_file_name=tmp_filename, output_file_type="yaml", include_bootstrap=False, ) except Exception: import logging # Logging isn't configured yet so this will use the last-chance logger, STDERR logging.getLogger(__name__).warning( "Could not successfully migrate application configuration. " "Will attempt to load the previous configuration.", exc_info=True, ) return if _is_new_config(filename, tmp_filename): _backup_previous_config(filename, tmp_filename) else: os.remove(tmp_filename)
def main(): signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) spec = YapconfSpec(SPECIFICATION, env_prefix="BG_") parser = ArgumentParser() spec.add_arguments(parser) args = parser.parse_args(sys.argv[1:]) bartender.setup_bartender(spec=spec, cli_args=vars(args)) # Ensure we have a brew-view connection progressive_backoff( partial(bartender.bv_client.can_connect, timeout=5), bartender.application, "Unable to connect to brew-view, is it started?", ) # Ensure we have a mongo connection progressive_backoff( partial(setup_database, bartender.config), bartender.application, "Unable to connect to mongo, is it started?", ) # Ensure we have message queue connections progressive_backoff( bartender.application.clients["pika"].is_alive, bartender.application, "Unable to connect to rabbitmq, is it started?", ) progressive_backoff( bartender.application.clients["pyrabbit"].is_alive, bartender.application, "Unable to connect to rabbitmq admin interface. " "Is the management plugin enabled?", ) # Since we wait for RabbitMQ and brew-view we could already be shutting down # In that case we don't want to start if not bartender.application.stopped(): # Make sure that the bartender user has admin permissions bartender.ensure_admin() bartender.logger.info("Hi, what can I get you to drink?") bartender.application.start() bartender.logger.info("Let me know if you need anything else!") # You may be wondering why we don't just call bartender.application.join() or .wait(). # Well, you're in luck because I'm going to tell you why. Either of these methods # cause the main python thread to lock out our signal handler, which means we cannot # shut down gracefully in some circumstances. So instead we simply use pause() to wait # for a signal to be sent to us. If you choose to change this please test thoroughly # when deployed via system packages (apt/yum) as well as python packages and docker. # Thanks! signal.pause() bartender.logger.info("Don't forget to drive safe!")
def spec_with_dicts(): """YapconfSpec for testing YapconfDictItem variations""" return YapconfSpec({ 'database': { 'type': 'dict', 'required': True, 'items': { 'name': {'type': 'str', 'required': True, }, 'host': {'type': 'str', 'required': True, }, 'port': {'type': 'int', 'required': True, }, } }, 'foo': { 'type': 'dict', 'required': True, 'items': { 'bar': { 'type': 'dict', 'required': True, 'items': { 'baz': { 'type': 'str', 'required': True, }, }, }, 'bat': { 'type': 'bool', } }, }, })
def _parse_args(args: Sequence[str]) -> Tuple[YapconfSpec, dict]: """Construct a spec and parse command line arguments Args: args: Command line arguments Returns: Config object with only the named items """ spec = YapconfSpec(_SPECIFICATION, env_prefix="BG_") parser = ArgumentParser() spec.add_arguments(parser) cli_vars = vars(parser.parse_args(args)) return spec, cli_vars
def _setup_config_sources(spec: YapconfSpec, cli_vars: Iterable[str]) -> List[str]: """Sets the sources for configuration loading Args: spec: Yapconf specification cli_vars: Command line arguments Returns: List of configuration sources """ spec.add_source("cli_args", "dict", data=cli_vars) spec.add_source("ENVIRONMENT", "environment") config_sources = ["cli_args", "ENVIRONMENT"] # Load bootstrap items to see if there's a config file temp_config = spec.load_config(*config_sources, bootstrap=True) config_filename = temp_config.configuration.file if config_filename: _safe_migrate(spec, config_filename) spec.add_source(config_filename, "yaml", filename=config_filename) config_sources.insert(1, config_filename) return config_sources
def spec(): return YapconfSpec({ "log": { "type": "dict", "items": { "config_file": { "type": "str", "description": "Path to a logging config file.", "required": False, "cli_short_name": "l", "previous_names": ["log_config"], "alt_env_names": ["LOG_CONFIG"], }, "file": { "type": "str", "description": "File you would like the application to log to", "required": False, "previous_names": ["log_file"], }, "level": { "type": "str", "description": "Log level for the application", "default": "INFO", "choices": [ "DEBUG", "INFO", "WARN", "WARNING", "ERROR", "CRITICAL", ], "previous_names": ["log_level"], }, }, }, "configuration": { "type": "dict", "bootstrap": True, "items": { "file": { "required": False, "bootstrap": True, "cli_short_name": "c", }, "type": { "required": False, "bootstrap": True, "cli_short_name": "t", }, }, }, })
def main(): signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) spec = YapconfSpec(SPECIFICATION, env_prefix="BG_") parser = ArgumentParser() spec.add_arguments(parser) args = parser.parse_args(sys.argv[1:]) # Logging isn't set up until after this... brew_view.setup(spec, vars(args)) # Schedule things to happen after the ioloop comes up brew_view.io_loop.add_callback(brew_view.startup) brew_view.logger.info("Starting IO loop") brew_view.io_loop.start() brew_view.logger.info("Application is shut down. Goodbye!")
def _load_config(args, kwargs): """Load a config based on the CONNECTION section of the Brewtils Specification This will load a configuration with the following source precedence: 1. kwargs 2. kwargs with "old" names ("host", "port", "url_prefix") 3. host and port passed as positional arguments 4. the global configuration (brewtils.plugin.CONFIG) Args: args (deprecated): host and port kwargs: Standard connection arguments to be used Returns: The resolved configuration object """ spec = YapconfSpec(_CONNECTION_SPEC) renamed = {} for key in ["host", "port", "url_prefix"]: if kwargs.get(key): renamed["bg_" + key] = kwargs.get(key) positional = {} if len(args) > 0: _deprecate( "Heads up - passing bg_host as a positional argument is deprecated " "and will be removed in version 4.0", stacklevel=kwargs.get("stacklevel", 3), ) positional["bg_host"] = args[0] if len(args) > 1: _deprecate( "Heads up - passing bg_port as a positional argument is deprecated " "and will be removed in version 4.0", stacklevel=kwargs.get("stacklevel", 3), ) positional["bg_port"] = args[1] return spec.load_config( *[kwargs, renamed, positional, brewtils.plugin.CONFIG])
def fallback_spec(): return YapconfSpec({ 'defaults': { 'type': 'dict', 'items': { 'str': {'type': 'str', 'default': 'default_str'}, 'int': {'type': 'int', 'default': 123}, 'long': {'type': 'long', 'default': 123}, 'float': {'type': 'float', 'default': 123.123}, 'bool': {'type': 'bool', 'default': True}, 'complex': {'type': 'complex', 'default': 1j}, 'list': { 'type': 'list', 'default': [1, 2, 3], 'items': { 'list_item': {'type': 'int', 'default': 1} }, }, 'dict': { 'type': 'dict', 'default': {'foo': 'dict_default'}, 'items': { 'foo': {'type': 'str', 'default': 'item_default'} }, }, }, }, 'str': {'type': 'str', 'fallback': 'defaults.str'}, 'int': {'type': 'int', 'fallback': 'defaults.int'}, 'long': {'type': 'long', 'fallback': 'defaults.long'}, 'float': {'type': 'float', 'fallback': 'defaults.float'}, 'bool': {'type': 'bool', 'fallback': 'defaults.bool'}, 'complex': {'type': 'complex', 'fallback': 'defaults.complex'}, 'list': { 'type': 'list', 'fallback': 'defaults.list', 'items': { 'list_item': { 'type': 'int', 'fallback': 'defaults.list.list_item' }, }, }, 'dict': { 'type': 'dict', 'fallback': 'defaults.dict', 'items': { 'foo': { 'type': 'str', 'fallback': 'defaults.dict.foo' }, }, }, })
def setUpClass(cls): db_patcher = patch('bg_utils.setup_database') db_patcher.start() server_patch = patch('brew_view.HTTPServer') server_patch.start() spec = YapconfSpec(SPECIFICATION) brew_view.setup(spec, {}) connect('beer_garden', host='mongomock://localhost') cls.app = brew_view.tornado_app
def test_create_garden_with_empty_connection_params(self, bg_garden): """create_garden should explicitly load default HTTP configs from brewtils when empty""" config_map = { "bg_host": "host", "bg_port": "port", "ssl_enabled": "ssl", "bg_url_prefix": "url_prefix", "ca_cert": "ca_cert", "ca_verify": "ca_verify", "client_cert": "client_cert", } spec = YapconfSpec(_CONNECTION_SPEC) # bg_host is required by brewtils garden spec defaults = spec.load_config({"bg_host": ""}) garden = create_garden(bg_garden) for key in config_map: assert garden.connection_params["http"][ config_map[key]] == defaults[key]
def simple_spec(): """Simple YapconfSpec for all YapconfItem variations""" return YapconfSpec( { 'my_string': {'type': 'str', 'required': True, }, 'my_int': {'type': 'int', 'required': True, }, 'my_long': {'type': 'long', 'required': True, }, 'my_float': {'type': 'float', 'required': True, }, 'my_bool': {'type': 'bool', 'required': True, }, 'my_complex': {'type': 'complex', 'required': True, }, } )
def setUp(self): self.config = YapconfSpec(SPECIFICATION).load_config() bartender.config = self.config self.app = BartenderApp() self.thrift_server = Mock() self.queue_manager = Mock() self.local_monitor = Mock() self.status_monitor = Mock() self.plugin_manager = Mock() self.plugin_loader = Mock() self.clients = MagicMock() self.mongo_pruner = Mock()
def setUpClass(cls): db_patcher = patch("brew_view.setup_database") db_patcher.start() server_patch = patch("brew_view.HTTPServer") server_patch.start() spec = YapconfSpec(SPECIFICATION) brew_view.setup(spec, {}) # Setup anonymous user for testing. brew_view.anonymous_principal = brew_view.load_anonymous() connect("beer_garden", host="mongomock://localhost") cls.app = brew_view.tornado_app
def create_garden(garden: Garden) -> Garden: """Create a new Garden Args: garden: The Garden to create Returns: The created Garden """ # Explicitly load default config options into garden params spec = YapconfSpec(_CONNECTION_SPEC) # bg_host is required to load brewtils garden spec defaults = spec.load_config({"bg_host": ""}) config_map = { "bg_host": "host", "bg_port": "port", "ssl_enabled": "ssl", "bg_url_prefix": "url_prefix", "ca_cert": "ca_cert", "ca_verify": "ca_verify", "client_cert": "client_cert", } if garden.connection_params is None: garden.connection_params = {} garden.connection_params.setdefault("http", {}) for key in config_map: garden.connection_params["http"].setdefault(config_map[key], defaults[key]) garden.status_info["heartbeat"] = datetime.utcnow() return db.create(garden)
def spec_with_lists(): """YapconfSpec for testing YapconfListItem variations""" return YapconfSpec( { 'simple_list': { 'type': 'list', 'required': True, 'items': { 'list_item': { 'type': 'str', 'required': True } } }, 'top_list': { 'type': 'list', 'required': True, 'items': { 'nested_list': { 'type': 'list', 'required': True, 'items': { 'nested_list_items': { 'type': 'int', 'required': True } } } } }, 'list_of_dictionaries': { 'type': 'list', 'required': True, 'items': { 'list_item': { 'type': 'dict', 'required': True, 'items': { 'foo': {'type': 'str', 'required': True, }, 'bar': {'type': 'str', 'required': False, } } } } } } )
def get_argument_parser(): """Get an ArgumentParser pre-populated with Brewtils arguments This is helpful if you're expecting additional command line arguments to a plugin startup script. This enables doing something like:: def main(): parser = get_argument_parser() parser.add_argument('positional_arg') parsed_args = parser.parse_args(sys.argv[1:]) # Now you can use the extra argument client = MyClient(parsed_args.positional_arg) # But you'll need to be careful when using the 'normal' Brewtils # configuration loading methods: # Option 1: Tell Brewtils about your customized parser connection = get_connection_info(cli_args=sys.argv[1:], argument_parser=parser) # Option 2: Use the parsed CLI as a dictionary connection = get_connection_info(**vars(parsed_args)) # Now specify connection kwargs like normal plugin = RemotePlugin(client, name=... **connection) IMPORTANT: Note that in both cases the returned ``connection`` object **will not** contain your new value. Both options just prevent normal CLI parsing from failing on the unknown argument. Returns: :ArgumentParser: Argument parser with Brewtils arguments loaded """ parser = ArgumentParser() YapconfSpec(SPECIFICATION).add_arguments(parser) return parser
def example_spec(): return YapconfSpec( { 'foo': {}, 'emoji': {}, u'💩': {}, 'db': { 'type': 'dict', 'items': { 'name': {}, 'port': {'type': 'int'} } }, 'items': { 'type': 'list', 'items': { 'item': {'type': 'int'}, }, }, } )
def generate_config(): spec = YapconfSpec(SPECIFICATION, env_prefix='BG_') bg_utils.generate_config_file(spec, sys.argv[1:])
def test_no_config_file(self): beer_garden.config.load([], force=True) spec = YapconfSpec(beer_garden.config._SPECIFICATION) assert beer_garden.config._CONFIG.to_dict() == spec.defaults
def load_config(cli_args=None, argument_parser=None, **kwargs): """Load configuration using Yapconf Configuration will be loaded from these sources, with earlier sources having higher priority: 1. ``**kwargs`` passed to this method 2. ``cli_args`` passed to this method 3. Environment variables using the ``BG_`` prefix 4. Default values in the brewtils specification Args: cli_args (list, optional): List of command line arguments for configuration loading argument_parser (ArgumentParser, optional): Argument parser to use when parsing cli_args. Supplying this allows adding additional arguments prior to loading the configuration. This can be useful if your startup script takes additional arguments. See get_argument_parser for additional information. **kwargs: Additional configuration overrides Returns: :obj:`box.Box`: The resolved configuration object """ spec = YapconfSpec(SPECIFICATION, env_prefix="BG_") sources = [] if kwargs: # Do a little kwarg massaging for backwards compatibility if "bg_host" not in kwargs and "host" in kwargs: warnings.warn( "brewtils.load_config called with 'host' keyword " "argument. This name will be removed in version 3.0, " "please use 'bg_host' instead.", DeprecationWarning, stacklevel=2, ) kwargs["bg_host"] = kwargs.pop("host") if "bg_port" not in kwargs and "port" in kwargs: warnings.warn( "brewtils.load_config called with 'port' keyword " "argument. This name will be removed in version 3.0, " "please use 'bg_port' instead.", DeprecationWarning, stacklevel=2, ) kwargs["bg_port"] = kwargs.pop("port") sources.append(("kwargs", kwargs)) if cli_args: if not argument_parser: argument_parser = ArgumentParser() spec.add_arguments(argument_parser) parsed_args = argument_parser.parse_args(cli_args) sources.append(("cli_args", vars(parsed_args))) sources.append("ENVIRONMENT") try: config = spec.load_config(*sources) except YapconfItemNotFound as ex: if ex.item.name == "bg_host": raise ValidationError( "Unable to create a plugin without a " "beer-garden host. Please specify one on the " "command line (--bg-host), in the " "environment (BG_HOST), or in kwargs " "(bg_host)" ) raise # Make sure the url_prefix is normal config.url_prefix = normalize_url_prefix(config.url_prefix) return config
def load_config(cli_args=True, environment=True, argument_parser=None, bootstrap=False, **kwargs): """Load configuration using Yapconf Configuration will be loaded from these sources, with earlier sources having higher priority: 1. ``**kwargs`` passed to this method 2. Command line arguments (if ``cli_args`` argument is not False) 3. Environment variables using the ``BG_`` prefix (if ``environment`` argument is not False) 4. Default values in the brewtils specification Args: cli_args (Union[bool, list], optional): Specifies whether command line should be used as a configuration source - True: Argparse will use the standard sys.argv[1:] - False: Command line arguments will be ignored when loading configuration - List of strings: Will be parsed as CLI args (instead of using sys.argv) environment (bool): Specifies whether environment variables (with the ``BG_`` prefix) should be used when loading configuration argument_parser (ArgumentParser, optional, deprecated): Argument parser to use when parsing cli_args. Supplying this allows adding additional arguments prior to loading the configuration. This can be useful if your startup script takes additional arguments. See get_argument_parser for additional information. **kwargs: Additional configuration overrides Returns: box.Box: The resolved configuration object """ spec = YapconfSpec(SPECIFICATION, env_prefix="BG_") sources = [] if kwargs: # First deprecate / translate items with multiple names mangled_kwargs = _translate_kwargs(**kwargs) # Metadata is a little weird because yapconf doesn't support raw dicts, so we # need to make it a json string in that case metadata = kwargs.get("metadata") if isinstance(metadata, dict): mangled_kwargs["metadata"] = json.dumps(metadata) sources.append(("kwargs", mangled_kwargs)) if cli_args: if cli_args is True: sources.append("CLI") else: if not argument_parser: argument_parser = ArgumentParser() spec.add_arguments(argument_parser) parsed_args, unknown = argument_parser.parse_known_args(cli_args) sources.append(("cli_args", vars(parsed_args))) if environment: sources.append("ENVIRONMENT") try: config = spec.load_config(*sources, bootstrap=bootstrap) except YapconfItemNotFound as ex: if ex.item.name == "bg_host": raise ValidationError( "Unable to create a plugin without a Beer-garden host. Please specify " "one on the command line (--bg-host), in the environment (BG_HOST), or " "in kwargs (bg_host).") raise # Make sure the url_prefix is normal if "bg_url_prefix" in config: config.bg_url_prefix = normalize_url_prefix(config.bg_url_prefix) return config
def real_world_spec(): """YapconfSpec based on a 'real-world' example""" current_dir = os.path.abspath(os.path.dirname(__file__)) filename = os.path.join(current_dir, 'files', 'real_world', 'spec.yaml') return YapconfSpec(filename, file_type='yaml', env_prefix='MY_APP_')
def generate_logging_config(): spec = YapconfSpec(SPECIFICATION, env_prefix="BG_") bg_utils.generate_logging_config_file(spec, get_default_logging_config, sys.argv[1:])
def migrate_config(): spec = YapconfSpec(SPECIFICATION, env_prefix="BG_") bg_utils.update_config_file(spec, sys.argv[1:])
class HttpInitTest(unittest.TestCase): def setUp(self): self.spec = YapconfSpec(SPECIFICATION) bg.config = None bg.io_loop = None bg.logger = None bg.thrift_context = None def tearDown(self): bg.config = None bg.io_loop = None bg.logger = None bg.thrift_context = None @patch("bg_utils.setup_application_logging", Mock()) @patch("brew_view.setup_database", Mock()) @patch("brew_view.load_plugin_logging_config", Mock()) @patch("brew_view.HTTPServer.listen", Mock()) def test_setup_no_file(self): bg.setup(self.spec, {}) self.assertIsInstance(bg.config, Box) self.assertIsInstance(bg.logger, logging.Logger) self.assertIsNotNone(bg.thrift_context) self.assertIsInstance(bg.io_loop, IOLoop) def test_setup_tornado_app(self): bg.config = self.spec.load_config({"web": {"url_prefix": "/"}}) app = bg._setup_tornado_app() self.assertIsInstance(app, Application) def test_setup_tornado_app_debug_true(self): bg.config = self.spec.load_config({ "debug_mode": True, "web": { "url_prefix": "/" } }) app = bg._setup_tornado_app() self.assertTrue(app.settings.get("autoreload")) def test_setup_ssl_context_ssl_not_enabled(self): bg.config = self.spec.load_config({"web": {"ssl": {"enabled": False}}}) server_ssl, client_ssl = bg._setup_ssl_context() self.assertIsNone(server_ssl) self.assertIsNone(client_ssl) @patch("brew_view.ssl") def test_setup_ssl_context_ssl_enabled(self, ssl_mock): bg.config = self.spec.load_config({ "web": { "ssl": { "enabled": True, "public_key": "/path/to/public.key", "private_key": "/path/to/private.key", "ca_cert": "/path/to/ca/file", "ca_path": "/path/to/ca/path", } } }) server_context = Mock() client_context = Mock() ssl_mock.create_default_context.side_effect = [ server_context, client_context ] bg._setup_ssl_context() server_context.load_cert_chain.assert_called_with( certfile="/path/to/public.key", keyfile="/path/to/private.key") client_context.load_cert_chain.assert_called_with( certfile="/path/to/public.key", keyfile="/path/to/private.key") server_context.load_verify_locations.assert_called_with( cafile="/path/to/ca/file", capath="/path/to/ca/path") client_context.load_verify_locations.assert_called_with( cafile="/path/to/ca/file", capath="/path/to/ca/path") @patch("brew_view.PluginLoggingLoader") def test_load_plugin_logging_config(self, PluginLoggingLoaderMock): app_config = Mock() app_config.plugin_logging.config_file = "plugin_log_config" app_config.plugin_logging.level = "INFO" bg.app_logging_config = "app_logging_config" loader_mock = Mock() PluginLoggingLoaderMock.return_value = loader_mock bg.load_plugin_logging_config(app_config) loader_mock.load.assert_called_with( filename="plugin_log_config", level="INFO", default_config="app_logging_config", )
class BeerGardenTest(unittest.TestCase): def setUp(self): self.spec = YapconfSpec(SPECIFICATION) bg.config = None bg.io_loop = None bg.logger = None bg.thrift_context = None def tearDown(self): bg.config = None bg.io_loop = None bg.logger = None bg.thrift_context = None @patch('bg_utils.setup_application_logging', Mock()) @patch('bg_utils.setup_database', Mock()) @patch('brew_view.load_plugin_logging_config', Mock()) @patch('brew_view.HTTPServer.listen', Mock()) def test_setup_no_file(self): bg.setup(self.spec, {}) self.assertIsInstance(bg.config, Box) self.assertIsInstance(bg.logger, logging.Logger) self.assertIsNotNone(bg.thrift_context) self.assertIsInstance(bg.io_loop, IOLoop) def test_setup_tornado_app(self): bg.config = self.spec.load_config({'web': {'url_prefix': '/'}}) app = bg._setup_tornado_app() self.assertIsInstance(app, Application) def test_setup_tornado_app_debug_true(self): bg.config = self.spec.load_config({ 'debug_mode': True, 'web': { 'url_prefix': '/' } }) app = bg._setup_tornado_app() self.assertTrue(app.settings.get('autoreload')) def test_setup_ssl_context_ssl_not_enabled(self): bg.config = self.spec.load_config({'web': {'ssl': {'enabled': False}}}) server_ssl, client_ssl = bg._setup_ssl_context() self.assertIsNone(server_ssl) self.assertIsNone(client_ssl) @patch('brew_view.ssl') def test_setup_ssl_context_ssl_enabled(self, ssl_mock): bg.config = self.spec.load_config({ 'web': { 'ssl': { 'enabled': True, 'public_key': '/path/to/public.key', 'private_key': '/path/to/private.key', 'ca_cert': '/path/to/ca/file', 'ca_path': '/path/to/ca/path', } } }) server_context = Mock() client_context = Mock() ssl_mock.create_default_context.side_effect = [ server_context, client_context ] bg._setup_ssl_context() server_context.load_cert_chain.assert_called_with( certfile='/path/to/public.key', keyfile='/path/to/private.key') client_context.load_cert_chain.assert_called_with( certfile='/path/to/public.key', keyfile='/path/to/private.key') server_context.load_verify_locations.assert_called_with( cafile='/path/to/ca/file', capath='/path/to/ca/path') client_context.load_verify_locations.assert_called_with( cafile='/path/to/ca/file', capath='/path/to/ca/path') @patch('brew_view.PluginLoggingLoader') def test_load_plugin_logging_config(self, PluginLoggingLoaderMock): app_config = Mock() app_config.plugin_logging.config_file = "plugin_log_config" app_config.plugin_logging.level = "INFO" bg.app_logging_config = "app_logging_config" loader_mock = Mock() PluginLoggingLoaderMock.return_value = loader_mock bg.load_plugin_logging_config(app_config) loader_mock.load.assert_called_with( filename="plugin_log_config", level="INFO", default_config="app_logging_config")
def setUp(self): self.spec = YapconfSpec(SPECIFICATION) bg.config = None bg.io_loop = None bg.logger = None bg.thrift_context = None
def basic_spec(simple_item_spec): """Most basic spec you can have""" return YapconfSpec(simple_item_spec)