mirror of https://github.com/docker/compose.git
Validate that an extended config file has the same version as the base.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
b446c09735
commit
89e31f7a8d
|
@ -188,10 +188,10 @@ def find(base_dir, filenames):
|
||||||
[ConfigFile.from_filename(f) for f in filenames])
|
[ConfigFile.from_filename(f) for f in filenames])
|
||||||
|
|
||||||
|
|
||||||
def validate_config_version(config_details):
|
def validate_config_version(config_files):
|
||||||
main_file = config_details.config_files[0]
|
main_file = config_files[0]
|
||||||
validate_top_level_object(main_file)
|
validate_top_level_object(main_file)
|
||||||
for next_file in config_details.config_files[1:]:
|
for next_file in config_files[1:]:
|
||||||
validate_top_level_object(next_file)
|
validate_top_level_object(next_file)
|
||||||
|
|
||||||
if main_file.version != next_file.version:
|
if main_file.version != next_file.version:
|
||||||
|
@ -254,7 +254,7 @@ def load(config_details):
|
||||||
|
|
||||||
Return a fully interpolated, extended and validated configuration.
|
Return a fully interpolated, extended and validated configuration.
|
||||||
"""
|
"""
|
||||||
validate_config_version(config_details)
|
validate_config_version(config_details.config_files)
|
||||||
|
|
||||||
processed_files = [
|
processed_files = [
|
||||||
process_config_file(config_file)
|
process_config_file(config_file)
|
||||||
|
@ -267,9 +267,8 @@ def load(config_details):
|
||||||
networks = load_mapping(config_details.config_files, 'get_networks', 'Network')
|
networks = load_mapping(config_details.config_files, 'get_networks', 'Network')
|
||||||
service_dicts = load_services(
|
service_dicts = load_services(
|
||||||
config_details.working_dir,
|
config_details.working_dir,
|
||||||
main_file.filename,
|
main_file,
|
||||||
[file.get_service_dicts() for file in config_details.config_files],
|
[file.get_service_dicts() for file in config_details.config_files])
|
||||||
main_file.version)
|
|
||||||
return Config(main_file.version, service_dicts, volumes, networks)
|
return Config(main_file.version, service_dicts, volumes, networks)
|
||||||
|
|
||||||
|
|
||||||
|
@ -303,21 +302,21 @@ def load_mapping(config_files, get_func, entity_type):
|
||||||
return mapping
|
return mapping
|
||||||
|
|
||||||
|
|
||||||
def load_services(working_dir, filename, service_configs, version):
|
def load_services(working_dir, config_file, service_configs):
|
||||||
def build_service(service_name, service_dict, service_names):
|
def build_service(service_name, service_dict, service_names):
|
||||||
service_config = ServiceConfig.with_abs_paths(
|
service_config = ServiceConfig.with_abs_paths(
|
||||||
working_dir,
|
working_dir,
|
||||||
filename,
|
config_file.filename,
|
||||||
service_name,
|
service_name,
|
||||||
service_dict)
|
service_dict)
|
||||||
resolver = ServiceExtendsResolver(service_config, version)
|
resolver = ServiceExtendsResolver(service_config, config_file)
|
||||||
service_dict = process_service(resolver.run())
|
service_dict = process_service(resolver.run())
|
||||||
|
|
||||||
validate_service(service_dict, service_config.name, version)
|
validate_service(service_dict, service_config.name, config_file.version)
|
||||||
service_dict = finalize_service(
|
service_dict = finalize_service(
|
||||||
service_config._replace(config=service_dict),
|
service_config._replace(config=service_dict),
|
||||||
service_names,
|
service_names,
|
||||||
version)
|
config_file.version)
|
||||||
return service_dict
|
return service_dict
|
||||||
|
|
||||||
def build_services(service_config):
|
def build_services(service_config):
|
||||||
|
@ -333,7 +332,7 @@ def load_services(working_dir, filename, service_configs, version):
|
||||||
name: merge_service_dicts_from_files(
|
name: merge_service_dicts_from_files(
|
||||||
base.get(name, {}),
|
base.get(name, {}),
|
||||||
override.get(name, {}),
|
override.get(name, {}),
|
||||||
version)
|
config_file.version)
|
||||||
for name in all_service_names
|
for name in all_service_names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,11 +372,11 @@ def process_config_file(config_file, service_name=None):
|
||||||
|
|
||||||
|
|
||||||
class ServiceExtendsResolver(object):
|
class ServiceExtendsResolver(object):
|
||||||
def __init__(self, service_config, version, already_seen=None):
|
def __init__(self, service_config, config_file, already_seen=None):
|
||||||
self.service_config = service_config
|
self.service_config = service_config
|
||||||
self.working_dir = service_config.working_dir
|
self.working_dir = service_config.working_dir
|
||||||
self.already_seen = already_seen or []
|
self.already_seen = already_seen or []
|
||||||
self.version = version
|
self.config_file = config_file
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def signature(self):
|
def signature(self):
|
||||||
|
@ -404,8 +403,10 @@ class ServiceExtendsResolver(object):
|
||||||
config_path = self.get_extended_config_path(extends)
|
config_path = self.get_extended_config_path(extends)
|
||||||
service_name = extends['service']
|
service_name = extends['service']
|
||||||
|
|
||||||
|
extends_file = ConfigFile.from_filename(config_path)
|
||||||
|
validate_config_version([self.config_file, extends_file])
|
||||||
extended_file = process_config_file(
|
extended_file = process_config_file(
|
||||||
ConfigFile.from_filename(config_path),
|
extends_file,
|
||||||
service_name=service_name)
|
service_name=service_name)
|
||||||
service_config = extended_file.config[service_name]
|
service_config = extended_file.config[service_name]
|
||||||
return config_path, service_config, service_name
|
return config_path, service_config, service_name
|
||||||
|
@ -417,7 +418,7 @@ class ServiceExtendsResolver(object):
|
||||||
extended_config_path,
|
extended_config_path,
|
||||||
service_name,
|
service_name,
|
||||||
service_dict),
|
service_dict),
|
||||||
self.version,
|
self.config_file,
|
||||||
already_seen=self.already_seen + [self.signature])
|
already_seen=self.already_seen + [self.signature])
|
||||||
|
|
||||||
service_config = resolver.run()
|
service_config = resolver.run()
|
||||||
|
@ -425,13 +426,12 @@ class ServiceExtendsResolver(object):
|
||||||
validate_extended_service_dict(
|
validate_extended_service_dict(
|
||||||
other_service_dict,
|
other_service_dict,
|
||||||
extended_config_path,
|
extended_config_path,
|
||||||
service_name,
|
service_name)
|
||||||
)
|
|
||||||
|
|
||||||
return merge_service_dicts(
|
return merge_service_dicts(
|
||||||
other_service_dict,
|
other_service_dict,
|
||||||
self.service_config.config,
|
self.service_config.config,
|
||||||
self.version)
|
self.config_file.version)
|
||||||
|
|
||||||
def get_extended_config_path(self, extends_options):
|
def get_extended_config_path(self, extends_options):
|
||||||
"""Service we are extending either has a value for 'file' set, which we
|
"""Service we are extending either has a value for 'file' set, which we
|
||||||
|
|
|
@ -25,14 +25,15 @@ V1 = 1
|
||||||
|
|
||||||
|
|
||||||
def make_service_dict(name, service_dict, working_dir, filename=None):
|
def make_service_dict(name, service_dict, working_dir, filename=None):
|
||||||
|
"""Test helper function to construct a ServiceExtendsResolver
|
||||||
"""
|
"""
|
||||||
Test helper function to construct a ServiceExtendsResolver
|
resolver = config.ServiceExtendsResolver(
|
||||||
"""
|
config.ServiceConfig(
|
||||||
resolver = config.ServiceExtendsResolver(config.ServiceConfig(
|
|
||||||
working_dir=working_dir,
|
working_dir=working_dir,
|
||||||
filename=filename,
|
filename=filename,
|
||||||
name=name,
|
name=name,
|
||||||
config=service_dict), version=1)
|
config=service_dict),
|
||||||
|
config.ConfigFile(filename=filename, config={}))
|
||||||
return config.process_service(resolver.run())
|
return config.process_service(resolver.run())
|
||||||
|
|
||||||
|
|
||||||
|
@ -1888,6 +1889,28 @@ class ExtendsTest(unittest.TestCase):
|
||||||
|
|
||||||
assert config == expected
|
assert config == expected
|
||||||
|
|
||||||
|
def test_extends_with_mixed_versions_is_error(self):
|
||||||
|
tmpdir = py.test.ensuretemp('test_extends_with_mixed_version')
|
||||||
|
self.addCleanup(tmpdir.remove)
|
||||||
|
tmpdir.join('docker-compose.yml').write("""
|
||||||
|
version: 2
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
extends:
|
||||||
|
file: base.yml
|
||||||
|
service: base
|
||||||
|
image: busybox
|
||||||
|
""")
|
||||||
|
tmpdir.join('base.yml').write("""
|
||||||
|
base:
|
||||||
|
volumes: ['/foo']
|
||||||
|
ports: ['3000:3000']
|
||||||
|
""")
|
||||||
|
|
||||||
|
with pytest.raises(ConfigurationError) as exc:
|
||||||
|
load_from_filename(str(tmpdir.join('docker-compose.yml')))
|
||||||
|
assert 'Version mismatch' in exc.exconly()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(IS_WINDOWS_PLATFORM, reason='paths use slash')
|
@pytest.mark.xfail(IS_WINDOWS_PLATFORM, reason='paths use slash')
|
||||||
class ExpandPathTest(unittest.TestCase):
|
class ExpandPathTest(unittest.TestCase):
|
||||||
|
|
Loading…
Reference in New Issue