Exemple #1
0
    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
Exemple #2
0
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
Exemple #3
0
    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])
Exemple #4
0
    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]
Exemple #5
0
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")
Exemple #7
0
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",
        )
Exemple #9
0
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