Source code for pebbles.config

"""
Pebbles is configured with a number of **variables**.

These variables come, in the order of precedence from

- environment variables
- a configuration file
- built-in defaults

Naming convention is `UPPERCASE_WORDS_WITH_UNDERSCORES`.

To see the complete list check out pebbles.config that houses the object.
Only some have been documented.

The idea is that you could have a single docker container with multiple
entry points. All containers can (or should) see the same configuration file
and then at start-up time environment variables can be set to e.g.
differentiate workers to run a particular driver.

"""
import os
import yaml
import functools

CONFIG_FILE = '/etc/pebbles/config.yaml'


def _parse_env_value(val):
    """
    Pars environment variables to bool, integer or float or default to string.

    :param val:
    :return: val coerced into a type if it looks to be of one
    """
    if val.lower() == "false":
        return False
    elif val.lower() == "true":
        return True
    try:
        return int(val)
    except ValueError:
        pass
    try:
        return float(val)
    except ValueError:
        pass
    return val


def resolve_configuration_value(key, default=None, *args, **kwargs):
    def get_key_from_config(config_file, key):
        return yaml.load(open(config_file)).get(key)

    # check environment
    pb_key = 'PB_' + key
    value = os.getenv(pb_key)
    if value is not None:
        return _parse_env_value(value)

    # then finally check system config file and given default
    if os.path.isfile(CONFIG_FILE):
        value = get_key_from_config(CONFIG_FILE, key)
        if value is not None:
            return value

    if default is not None:
        return default


def fields_to_properties(cls):
    for k, default in vars(cls).items():
        if type(default) == tuple and len(default) == 2:
            default, doc_ = default
        else:
            doc_ = ''
        if not k.startswith('_') and k.isupper():
            resolvef = functools.partial(resolve_configuration_value, k, default)
            prop = property(resolvef, doc=doc_)
            setattr(cls, k, prop)
    return cls


# each config can be documented by making the default value into a (value,
# docstring) tuple
@fields_to_properties
[docs]class BaseConfig(object): """ Stores the default key, value pairs for the system configuration. Rendered with a decorator which considers any environment variables, then the system level config file and finally the default values, in that order of precedence. """ DEBUG = ( True, 'Controls debug mode' ) SECRET_KEY = 'change_me' WTF_CSRF_ENABLED = False SSL_VERIFY = False # SQLALCHEMY_DATABASE_URI = 'sqlite:////tmp/change_me.db' SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:KskRu1V_qK1Z._ALGnh6nxFvhM288N2c@localhost/pebbles' M2M_CREDENTIAL_STORE = ( '/var/run/pebbles_m2m', 'Where to find the M2M credentials file' ) MESSAGE_QUEUE_URI = 'redis://redis:6379/0' INSTANCE_DATA_DIR = '/var/spool/pb_instances' INTERNAL_API_BASE_URL = 'https://api:1443/api/v1' PUBLIC_IPV4 = ( '127.0.0.1', 'used by Docker Driver to create access urls' ' so FQDN is actually expected in production' ) BASE_URL = 'https://localhost:8888' MAX_CONTENT_LENGTH = 1024 * 1024 # ToDo: what uses the below? -jyrsa 2016-11-28 FAKE_PROVISIONING = False SENDER_EMAIL = 'sender@example.org' MAIL_SERVER = 'smtp.example.org' MAIL_SUPPRESS_SEND = True MAIL_USE_TLS = False SKIP_TASK_QUEUE = False WRITE_PROVISIONING_LOGS = True INSTANCE_NAME_PREFIX = ( 'pb-', 'all spawned instance names will have this prefix' ) DEFAULT_QUOTA = 1.0 ENABLE_SHIBBOLETH_LOGIN = False INSTALLATION_NAME = 'Pebbles' INSTALLATION_DESCRIPTION = ('A tool for provisioning ' 'ephemeral private cloud resources.') SHORT_DESCRIPTION = 'Welcome to Notebooks' BRAND_IMAGE = ( 'img/Notebooks_neg300px.png', 'An image URL for branding the installation' ) COURSE_REQUEST_FORM = 'http://link-to-form' HAKA_INSTITUTION_LIST = ({}, 'Dictionary of institution types and corresponding domains') PLUGIN_WHITELIST = ( 'DummyDriver', 'A whitespace-separated case-sensitive' ' list of all enabled plugins' ) PRESERVE_CONTEXT_ON_EXCEPTION = False EXTERNAL_HTTPS_PORT = 443 PROVISIONING_NUM_WORKERS = 1 # enable access by [] def __getitem__(self, item): return getattr(self, item) def get(self, key): return getattr(self, key) def __contains__(self, item): try: getattr(self, item) except: return False return True
class TestConfig(BaseConfig): SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:' MAIL_SUPPRESS_SEND = True FAKE_PROVISIONING = True SKIP_TASK_QUEUE = True BCRYPT_LOG_ROUNDS = 12 WRITE_PROVISIONING_LOGS = False TEST_MODE = True INSTALLATION_NAME = 'Pebbles' class LiveTestConfig(TestConfig): """ Config for testing live. e.g. with Selenium. """ # Live testing setup spawns a subprocess for the live server so in-memory # is not easily achievable. # ToDo: we could use tempfile to create a temporary named file in __init__ # and close it in __del__. If we do it's important to log the location so # that the tester can access the db manually. SQLALCHEMY_DATABASE_URI = 'sqlite:////tmp/change_me.livetest.db' PRESERVE_CONTEXT_ON_EXCEPTION = False # bit of culture never hurt anybody INSTALLATION_NAME = 'Underworld Branding Iron' INSTALLATION_DESCRIPTION = 'Abandon all hope, ye who enter here.'