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 _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 _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 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 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)
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 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
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", )
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