|
@@ -13,7 +13,6 @@ from .errors import ConfigurationError
|
|
|
from .interpolation import interpolate_environment_variables
|
|
|
from .validation import validate_against_fields_schema
|
|
|
from .validation import validate_against_service_schema
|
|
|
-from .validation import validate_extended_service_exists
|
|
|
from .validation import validate_extends_file_path
|
|
|
from .validation import validate_top_level_object
|
|
|
|
|
@@ -99,6 +98,10 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')):
|
|
|
:type config: :class:`dict`
|
|
|
"""
|
|
|
|
|
|
+ @classmethod
|
|
|
+ def from_filename(cls, filename):
|
|
|
+ return cls(filename, load_yaml(filename))
|
|
|
+
|
|
|
|
|
|
def find(base_dir, filenames):
|
|
|
if filenames == ['-']:
|
|
@@ -114,7 +117,7 @@ def find(base_dir, filenames):
|
|
|
log.debug("Using configuration files: {}".format(",".join(filenames)))
|
|
|
return ConfigDetails(
|
|
|
os.path.dirname(filenames[0]),
|
|
|
- [ConfigFile(f, load_yaml(f)) for f in filenames])
|
|
|
+ [ConfigFile.from_filename(f) for f in filenames])
|
|
|
|
|
|
|
|
|
def get_default_config_files(base_dir):
|
|
@@ -183,12 +186,10 @@ def load(config_details):
|
|
|
validate_paths(service_dict)
|
|
|
return service_dict
|
|
|
|
|
|
- def load_file(filename, config):
|
|
|
- processed_config = interpolate_environment_variables(config)
|
|
|
- validate_against_fields_schema(processed_config)
|
|
|
+ def build_services(filename, config):
|
|
|
return [
|
|
|
build_service(filename, name, service_config)
|
|
|
- for name, service_config in processed_config.items()
|
|
|
+ for name, service_config in config.items()
|
|
|
]
|
|
|
|
|
|
def merge_services(base, override):
|
|
@@ -200,16 +201,27 @@ def load(config_details):
|
|
|
for name in all_service_names
|
|
|
}
|
|
|
|
|
|
- config_file = config_details.config_files[0]
|
|
|
- validate_top_level_object(config_file.config)
|
|
|
+ config_file = process_config_file(config_details.config_files[0])
|
|
|
for next_file in config_details.config_files[1:]:
|
|
|
- validate_top_level_object(next_file.config)
|
|
|
+ next_file = process_config_file(next_file)
|
|
|
+
|
|
|
+ config = merge_services(config_file.config, next_file.config)
|
|
|
+ config_file = config_file._replace(config=config)
|
|
|
+
|
|
|
+ return build_services(config_file.filename, config_file.config)
|
|
|
+
|
|
|
+
|
|
|
+def process_config_file(config_file, service_name=None):
|
|
|
+ validate_top_level_object(config_file.config)
|
|
|
+ processed_config = interpolate_environment_variables(config_file.config)
|
|
|
+ validate_against_fields_schema(processed_config)
|
|
|
|
|
|
- config_file = ConfigFile(
|
|
|
- config_file.filename,
|
|
|
- merge_services(config_file.config, next_file.config))
|
|
|
+ if service_name and service_name not in processed_config:
|
|
|
+ raise ConfigurationError(
|
|
|
+ "Cannot extend service '{}' in {}: Service not found".format(
|
|
|
+ service_name, config_file.filename))
|
|
|
|
|
|
- return load_file(config_file.filename, config_file.config)
|
|
|
+ return config_file._replace(config=processed_config)
|
|
|
|
|
|
|
|
|
class ServiceLoader(object):
|
|
@@ -259,22 +271,13 @@ class ServiceLoader(object):
|
|
|
if not isinstance(extends, dict):
|
|
|
extends = {'service': extends}
|
|
|
|
|
|
- validate_extends_file_path(self.service_name, extends, self.filename)
|
|
|
config_path = self.get_extended_config_path(extends)
|
|
|
service_name = extends['service']
|
|
|
|
|
|
- config = load_yaml(config_path)
|
|
|
- validate_top_level_object(config)
|
|
|
- full_extended_config = interpolate_environment_variables(config)
|
|
|
-
|
|
|
- validate_extended_service_exists(
|
|
|
- service_name,
|
|
|
- full_extended_config,
|
|
|
- config_path
|
|
|
- )
|
|
|
- validate_against_fields_schema(full_extended_config)
|
|
|
-
|
|
|
- service_config = full_extended_config[service_name]
|
|
|
+ extended_file = process_config_file(
|
|
|
+ ConfigFile.from_filename(config_path),
|
|
|
+ service_name=service_name)
|
|
|
+ service_config = extended_file.config[service_name]
|
|
|
return config_path, service_config, service_name
|
|
|
|
|
|
def resolve_extends(self, extended_config_path, service_config, service_name):
|
|
@@ -304,6 +307,7 @@ class ServiceLoader(object):
|
|
|
need to obtain a full path too or we are extending from a service
|
|
|
defined in our own file.
|
|
|
"""
|
|
|
+ validate_extends_file_path(self.service_name, extends_options, self.filename)
|
|
|
if 'file' in extends_options:
|
|
|
return expand_path(self.working_dir, extends_options['file'])
|
|
|
return self.filename
|