Move config handling into a provider class to make testing much easier

This commit is contained in:
Joseph Schorr 2015-01-09 16:23:31 -05:00
parent c0c27648ea
commit 6d604a656a
8 changed files with 207 additions and 121 deletions

View file

@ -7,35 +7,6 @@ def generate_secret_key():
return str(cryptogen.getrandbits(256))
def import_yaml(config_obj, config_file):
with open(config_file) as f:
c = yaml.safe_load(f)
if not c:
logger.debug('Empty YAML config file')
return
if isinstance(c, str):
raise Exception('Invalid YAML config file: ' + str(c))
for key in c.iterkeys():
if key.isupper():
config_obj[key] = c[key]
def export_yaml(config_obj, config_file):
with open(config_file, 'w') as f:
f.write(yaml.safe_dump(config_obj, encoding='utf-8', allow_unicode=True))
def set_config_value(config_file, config_key, value):
""" Loads the configuration from the given YAML config file, sets the given key to
the given value, and then writes it back out to the given YAML config file. """
config_obj = {}
import_yaml(config_obj, config_file)
config_obj[config_key] = value
export_yaml(config_obj, config_file)
def add_enterprise_config_defaults(config_obj, current_secret_key, hostname):
""" Adds/Sets the config defaults for enterprise registry config. """
# These have to be false.

139
util/config/provider.py Normal file
View file

@ -0,0 +1,139 @@
import os
import yaml
import logging
import json
logger = logging.getLogger(__name__)
def _import_yaml(config_obj, config_file):
with open(config_file) as f:
c = yaml.safe_load(f)
if not c:
logger.debug('Empty YAML config file')
return
if isinstance(c, str):
raise Exception('Invalid YAML config file: ' + str(c))
for key in c.iterkeys():
if key.isupper():
config_obj[key] = c[key]
return config_obj
def _export_yaml(config_obj, config_file):
with open(config_file, 'w') as f:
f.write(yaml.safe_dump(config_obj, encoding='utf-8', allow_unicode=True))
class BaseProvider(object):
""" A configuration provider helps to load, save, and handle config override in the application.
"""
def update_app_config(self, app_config):
""" Updates the given application config object with the loaded override config. """
raise NotImplementedError
def get_yaml(self):
""" Returns the contents of the YAML config override file, or None if none. """
raise NotImplementedError
def save_yaml(self, config_object):
""" Updates the contents of the YAML config override file to those given. """
raise NotImplementedError
def yaml_exists(self):
""" Returns true if a YAML config override file exists in the config volume. """
raise NotImplementedError
def volume_exists(self):
""" Returns whether the config override volume exists. """
raise NotImplementedError
def volume_file_exists(self, filename):
""" Returns whether the file with the given name exists under the config override volume. """
raise NotImplementedError
def save_volume_file(self, filename, flask_file):
""" Saves the given flask file to the config override volume, with the given
filename.
"""
raise NotImplementedError
class FileConfigProvider(BaseProvider):
""" Implementation of the config provider that reads the data from the file system. """
def __init__(self, config_volume, yaml_filename, py_filename):
self.config_volume = config_volume
self.yaml_filename = yaml_filename
self.py_filename = py_filename
self.yaml_path = os.path.join(config_volume, yaml_filename)
self.py_path = os.path.join(config_volume, py_filename)
def update_app_config(self, app_config):
if os.path.exists(self.py_path):
logger.debug('Applying config file: %s', self.py_path)
app_config.from_pyfile(self.py_path)
if os.path.exists(self.yaml_path):
logger.debug('Applying config file: %s', self.yaml_path)
_import_yaml(app_config, self.yaml_path)
def get_yaml(self):
if not os.path.exists(self.yaml_path):
return None
config_obj = {}
_import_yaml(config_obj, self.yaml_path)
return config_obj
def save_yaml(self, config_obj):
_export_yaml(config_obj, self.yaml_path)
def yaml_exists(self):
return self.volume_file_exists(self.yaml_filename)
def volume_exists(self):
return os.path.exists(self.config_volume)
def volume_file_exists(self, filename):
return os.path.exists(os.path.join(self.config_volume, filename))
def save_volume_file(self, filename, flask_file):
flask_file.save(os.path.join(self.config_volume, filename))
class TestConfigProvider(BaseProvider):
""" Implementation of the config provider for testing. Everything is kept in-memory instead on
the real file system. """
def __init__(self):
self.files = {}
def update_app_config(self, app_config):
pass
def get_yaml(self):
if not 'config.yaml' in self.files:
return None
return json.loads(self.files.get('config.yaml', '{}'))
def save_yaml(self, config_obj):
self.files['config.yaml'] = json.dumps(config_obj)
def yaml_exists(self):
return 'config.yaml' in self.files
def volume_exists(self):
return True
def volume_file_exists(self, filename):
return filename in self.files
def save_volume_file(self, filename, flask_file):
self.files[filename] = ''
def reset_for_test(self):
self.files = {}

View file

@ -9,7 +9,7 @@ from flask import Flask
from flask.ext.mail import Mail, Message
from data.database import validate_database_url, User
from storage import get_storage_driver
from app import app, OVERRIDE_CONFIG_DIRECTORY
from app import app, CONFIG_PROVIDER
from auth.auth_context import get_authenticated_user
from util.oauth import GoogleOAuthConfig, GithubOAuthConfig
@ -138,7 +138,7 @@ def _validate_ssl(config):
return
for filename in SSL_FILENAMES:
if not os.path.exists(os.path.join(OVERRIDE_CONFIG_DIRECTORY, filename)):
if not CONFIG_PROVIDER.volume_file_exists(filename):
raise Exception('Missing required SSL file: %s' % filename)