В веб-фреймворке Pyramid, как я могу получить конфиденциальные настройки в development.ini / production.ini из внешнего файла?

Я хотел бы сохранить development.ini а также production.ini под контролем версий, но по соображениям безопасности не хотел бы sqlalchemy.url строка подключения, которая будет сохранена, поскольку она будет содержать имя пользователя и пароль, используемые для подключения к базе данных.

Каким каноническим способом в Pyramid является получение этого параметра из дополнительного внешнего файла?

Редактировать В дополнение к решению, использующему переменную окружения, я придумал это решение после того, как расспросил о #pyramid:

def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
# Read db password from config file outside of version control
secret_cfg = ConfigParser()
secret_cfg.read(settings['secrets'])
dbpass = secret_cfg.get("secrets", "dbpass")
settings['sqlalchemy.url'] = settings['connstr'] % (dbpass,)

3 ответа

Решение

Я много смотрел на это и играл с разными подходами. Тем не менее, Пирамида настолько гибка, и .ini Парсер конфигурации настолько минимален в том, что он делает для вас, что, похоже, де-факто ответа не существует.

В моем сценарии я попытался production.example.ini сначала в системе контроля версий, которая была скопирована на рабочий сервер с заполненными деталями, но это стало проблематичным, поскольку обновления в примере не переводились в копию, и поэтому копию приходилось создавать заново каждый раз, когда изменение было сделано. Кроме того, я начал использовать Heroku, поэтому файлы, не находящиеся в управлении версиями, никогда не попадали в развертывание.

Затем есть зашифрованный подход конфигурации. Который, мне не нравится парадигма. Представьте, что системный администратор отвечает за поддержку производственной среды, но он или она не может изменить местоположение базы данных или настройки, относящейся к конкретной среде, без повторного запуска через систему контроля версий. Очень приятно иметь максимально возможное разделение между средой и кодом, чтобы эти изменения могли быть внесены на лету без изменений контроля версий.

Моим окончательным решением было иметь некоторые значения, которые выглядели бы так:

[app:main]

sqlalchemy.url = ${SQLALCHEMY_URL}

Затем на рабочем сервере я бы установил переменную среды SQLALCHEMY_URL указать на базу данных. Это даже позволило мне использовать один и тот же файл конфигурации для постановки и производства, что приятно.

В моей инициализации Pyramid я просто расширил значение переменной окружения, используя os.path.expandvars:

sqlalchemy_url = os.path.expandvars(settings.get('sqlalchemy.url'))
engine = create_engine(sqlalchemy_url)

И, если вы хотите поработать с ним и автоматически заменить все переменные окружения в вашем словаре настроек, я сделал этот маленький вспомогательный метод для своих проектов:

def expandvars_dict(settings):
    """Expands all environment variables in a settings dictionary."""
    return dict((key, os.path.expandvars(value)) for
                key, value in settings.iteritems())

Используйте это так в своем main точка входа в приложение:

settings = expandvars_dict(settings)

Смысл отдельных ini-файлов в Pyramid заключается в том, что вам не нужно контролировать все из них и что они могут содержать разные настройки для разных сценариев (разработка / производство / тестирование). Ваш production.ini почти всегда не должен быть в той же VCS, что и ваш исходный код.

Я нашел этот способ для загрузки секретов из дополнительной конфигурации и из env.

from pyramid.config import Configurator
from paste.deploy import appconfig
from os import path

__all__ = [ "main" ]


def _load_secrets(global_config, settings):
    """ Helper to load secrets from a secrets config and
        from env (in that order).
    """
    if "drawstack.secrets" in settings:
        secrets_config = appconfig('config:' + settings["drawstack.secrets"],
                                    relative_to=path.dirname(global_config['__file__']))

        for k, v in secrets_config.items():
            if k == "here" or k == "__file__":
                continue
            settings[k] = v

    if "ENV_DB_URL" in global_config:
        settings["sqlalchemy.url"] = global_config["ENV_DB_URL"]


def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """

    _load_secrets(global_config, settings)

    config = Configurator(settings=settings)
    config.include('pyramid_jinja2')
    config.include('.models')
    config.include('.routes')
    config.scan()
    return config.make_wsgi_app()

Код выше, будет загружать любые переменные из значения ключа конфигурации drawstack.secrets и после этого он пытается загрузить DB_URL из среды.

drawstack.secrets может быть относительно исходного файла конфигурации ИЛИ абсолютным.

Другие вопросы по тегам