diff --git a/README.rst b/README.rst index d35cfcb8..39445d7a 100644 --- a/README.rst +++ b/README.rst @@ -383,3 +383,31 @@ E.g. ``"scmurl": "git://pkgs.stg.fedoraproject.org/modules/testmodule.git?#020ea The toplevel directory containing the trees for each architecture of a module. This field is only present when a module finished building, i.e. with the states 'done' or 'ready'. + +Configuration +============= + +MBS configures itself according to the environment where it runs + according to +the following rules (all of them are evaluated from top to bottom): + +- DevConfiguration is the initial configuration chosen. +- If configuration file is found within its final installation location, + ProdConfiguration is assumed. +- If Flask app running within mod_wsgi is detected, + ProdConfiguration is assumed. +- If environment variables determining configuration file/section are found, + they are used for configuration. Following environment variables are + recognized: + + - `MBS_CONFIG_FILE`: Overrides default configuration file location, + typically `/etc/module-build-service/config.py`. + - `MBS_CONFIG_SECTION`: Overrides configuration section. + + It is possible to set these values in httpd using `SetEnv`, + anywhere in `/etc/profile.d/` etc. + +- If test-runtime environment is detected, + TestConfiguration is used, otherwise... +- if `MODULE_BUILD_SERVICE_DEVELOPER_ENV` is set to some reasonable + value, DevConfiguration is forced and `config.py` is used directly from the + MBS's develop instance. For more information see `docs/CONTRIBUTING.rst`. diff --git a/docs/CONTRIBUTING.rst b/docs/CONTRIBUTING.rst index 83c94fc3..95791af3 100644 --- a/docs/CONTRIBUTING.rst +++ b/docs/CONTRIBUTING.rst @@ -133,9 +133,11 @@ Environment The environment variable `MODULE_BUILD_SERVICE_DEVELOPER_ENV`, which if set to "1", indicates to the Module Build Service that the development configuration should be used. Docker and Vagrant are being run with this -environment variable set. +environment variable set. This overrides all configuration settings and forces +usage of DevConfiguration section in `conf/config.py` from MBS's develop +instance. -Anytime, you can do:: +Prior to starting MBS, you can force development mode:: $ export MODULE_BUILD_SERVICE_DEVELOPER_ENV=1 diff --git a/module_build_service/config.py b/module_build_service/config.py index ca264397..4cb79e2a 100644 --- a/module_build_service/config.py +++ b/module_build_service/config.py @@ -31,34 +31,77 @@ from os import sys from module_build_service import logger -try: - conf_module = 'mbs_runtime_config' - mbs_runtime_config = imp.load_source(conf_module, - '/etc/module-build-service/config.py') -except: - conf_module = 'conf.config' - def init_config(app): - _init_app_config(app) - return Config(app) - - -def _init_app_config(app): - """ Configure app + """ Configure MBS and the Flask app """ - app.config.from_envvar("MBS_SETTINGS", silent=True) - here = sys.path[0] + config_module = None + config_file = '/etc/module-build-service/config.py' + config_section = 'DevConfiguration' + + # automagically detect production environment: + # - existing and readable config_file presets ProdConfiguration + try: + with open(config_file): + config_section = 'ProdConfiguration' + except: + pass + # - Flask app within mod_wsgi presets ProdConfiguration + flask_app_env = hasattr(app, 'request') and hasattr(app.request, 'environ') + if flask_app_env and any([var.startswith('mod_wsgi.') + for var in app.request.environ]): + config_section = 'ProdConfiguration' + # try getting config_file from os.environ + if 'MBS_CONFIG_FILE' in os.environ: + config_file = os.environ['MBS_CONFIG_FILE'] + # try getting config_section from os.environ + if 'MBS_CONFIG_SECTION' in os.environ: + config_section = os.environ['MBS_CONFIG_SECTION'] + # preferably get these values from Flask app + if flask_app_env: + # try getting config_file from Flask app + if 'MBS_CONFIG_FILE' in app.request.environ: + config_file = app.request.environ['MBS_CONFIG_FILE'] + # try getting config_section from Flask app + if 'MBS_CONFIG_SECTION' in app.request.environ: + config_section = app.request.environ['MBS_CONFIG_SECTION'] + # TestConfiguration shall only be used for running tests, otherwise... if any(['nosetests' in arg or 'noserunner.py' in arg for arg in sys.argv]): - app.config.from_object('%s.TestConfiguration' % conf_module) - elif 'MODULE_BUILD_SERVICE_DEVELOPER_ENV' in os.environ and \ - os.environ['MODULE_BUILD_SERVICE_DEVELOPER_ENV'].lower() in ( - '1', 'on', 'true', 'y', 'yes'): - app.config.from_object('%s.DevConfiguration' % conf_module) - elif here not in ('/usr/bin', '/bin', '/usr/local/bin'): - app.config.from_object('%s.DevConfiguration' % conf_module) - else: - app.config.from_object('%s.ProdConfiguration' % conf_module) + config_section = 'TestConfiguration' + from conf import config + config_module = config + # ...MODULE_BUILD_SERVICE_DEVELOPER_ENV has always the last word + # and overrides anything previously set before! + # Again, check Flask app (preferably) or fallback to os.environ. + # In any of the following cases, use configuration directly from MBS package + # -> /conf/config.py. + elif (flask_app_env and + 'MODULE_BUILD_SERVICE_DEVELOPER_ENV' in app.request.environ): + if app.request.environ['MODULE_BUILD_SERVICE_DEVELOPER_ENV'].lower() in ( + '1', 'on', 'true', 'y', 'yes'): + config_section = 'DevConfiguration' + from conf import config + config_module = config + elif ('MODULE_BUILD_SERVICE_DEVELOPER_ENV' in os.environ and + os.environ['MODULE_BUILD_SERVICE_DEVELOPER_ENV'].lower() in ( + '1', 'on', 'true', 'y', 'yes')): + config_section = 'DevConfiguration' + from conf import config + config_module = config + # try loading configuration from file + if not config_module: + try: + config_module = imp.load_source('mbs_runtime_config', + config_file) + except: + raise SystemError("Configuration file {} was not found." + .format(config_file)) + + # finally configure MBS and the Flask app + config_section_obj = getattr(config_module, config_section) + conf = Config(config_section_obj) + app.config.from_object(config_section_obj) + return conf class Config(object): @@ -199,7 +242,7 @@ class Config(object): 'desc': 'The messaging system to use.'}, 'messaging_topic_prefix': { 'type': list, - 'default': 'org.fedoraproject.prod', + 'default': ['org.fedoraproject.prod'], 'desc': 'The messaging system topic prefixes which we are interested in.'}, 'amq_recv_addresses': { 'type': list, @@ -251,26 +294,23 @@ class Config(object): 'desc': 'Global network retry interval for read/write operations, in seconds.'}, } - def __init__(self, app=None): + def __init__(self, conf_section_obj): """ - Initialize the Config object with defaults. - - If Flask app is given, override/enrich the configuration defaults - with Flask config values/items. + Initialize the Config object with defaults and then override them + with runtime values. """ # set defaults for name, values in self._defaults.items(): self.set_item(name, values['default']) - # we don't check whether app is Flask instance, we simply assume it - # so there's no need of import flask - if app is not None: - # override defaults - for key, value in app.config.items(): - # lower keys - key = key.lower() - self.set_item(key, value) + # override defaults + for key in dir(conf_section_obj): + # skip keys starting with underscore + if key.startswith('_'): + continue + # set item (lower key) + self.set_item(key.lower(), getattr(conf_section_obj, key)) def set_item(self, key, value): """Set value for configuration item as self.key = value"""