mirror of
https://github.com/docker/compose.git
synced 2025-07-20 04:04:29 +02:00
Merge pull request #2019 from mrfuxi/less-verbose-extends
Allow to extend service using shorthand notation. Closes #1989
This commit is contained in:
commit
6b68a84b9b
@ -269,15 +269,19 @@ class ServiceLoader(object):
|
|||||||
self.service_dict['environment'] = env
|
self.service_dict['environment'] = env
|
||||||
|
|
||||||
def validate_and_construct_extends(self):
|
def validate_and_construct_extends(self):
|
||||||
|
extends = self.service_dict['extends']
|
||||||
|
if not isinstance(extends, dict):
|
||||||
|
extends = {'service': extends}
|
||||||
|
|
||||||
validate_extends_file_path(
|
validate_extends_file_path(
|
||||||
self.service_name,
|
self.service_name,
|
||||||
self.service_dict['extends'],
|
extends,
|
||||||
self.filename
|
self.filename
|
||||||
)
|
)
|
||||||
self.extended_config_path = self.get_extended_config_path(
|
self.extended_config_path = self.get_extended_config_path(
|
||||||
self.service_dict['extends']
|
extends
|
||||||
)
|
)
|
||||||
self.extended_service_name = self.service_dict['extends']['service']
|
self.extended_service_name = extends['service']
|
||||||
|
|
||||||
full_extended_config = pre_process_config(
|
full_extended_config = pre_process_config(
|
||||||
load_yaml(self.extended_config_path)
|
load_yaml(self.extended_config_path)
|
||||||
|
@ -58,6 +58,11 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"extends": {
|
"extends": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -66,6 +71,8 @@
|
|||||||
},
|
},
|
||||||
"required": ["service"],
|
"required": ["service"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
||||||
|
@ -4,6 +4,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
import six
|
||||||
from docker.utils.ports import split_port
|
from docker.utils.ports import split_port
|
||||||
from jsonschema import Draft4Validator
|
from jsonschema import Draft4Validator
|
||||||
from jsonschema import FormatChecker
|
from jsonschema import FormatChecker
|
||||||
@ -162,10 +163,25 @@ def process_errors(errors, service_name=None):
|
|||||||
Inspecting the context value of a ValidationError gives us information about
|
Inspecting the context value of a ValidationError gives us information about
|
||||||
which sub schema failed and which kind of error it is.
|
which sub schema failed and which kind of error it is.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
required = [context for context in error.context if context.validator == 'required']
|
||||||
|
if required:
|
||||||
|
return required[0].message
|
||||||
|
|
||||||
|
additionalProperties = [context for context in error.context if context.validator == 'additionalProperties']
|
||||||
|
if additionalProperties:
|
||||||
|
invalid_config_key = _parse_key_from_error_msg(additionalProperties[0])
|
||||||
|
return "contains unsupported option: '{}'".format(invalid_config_key)
|
||||||
|
|
||||||
constraint = [context for context in error.context if len(context.path) > 0]
|
constraint = [context for context in error.context if len(context.path) > 0]
|
||||||
if constraint:
|
if constraint:
|
||||||
valid_types = _parse_valid_types_from_validator(constraint[0].validator_value)
|
valid_types = _parse_valid_types_from_validator(constraint[0].validator_value)
|
||||||
msg = "contains {}, which is an invalid type, it should be {}".format(
|
invalid_config_key = "".join(
|
||||||
|
"'{}' ".format(fragment) for fragment in constraint[0].path
|
||||||
|
if isinstance(fragment, six.string_types)
|
||||||
|
)
|
||||||
|
msg = "{}contains {}, which is an invalid type, it should be {}".format(
|
||||||
|
invalid_config_key,
|
||||||
constraint[0].instance,
|
constraint[0].instance,
|
||||||
valid_types
|
valid_types
|
||||||
)
|
)
|
||||||
|
15
tests/fixtures/extends/verbose-and-shorthand.yml
vendored
Normal file
15
tests/fixtures/extends/verbose-and-shorthand.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
base:
|
||||||
|
image: busybox
|
||||||
|
environment:
|
||||||
|
- "BAR=1"
|
||||||
|
|
||||||
|
verbose:
|
||||||
|
extends:
|
||||||
|
service: base
|
||||||
|
environment:
|
||||||
|
- "FOO=1"
|
||||||
|
|
||||||
|
shorthand:
|
||||||
|
extends: base
|
||||||
|
environment:
|
||||||
|
- "FOO=2"
|
@ -335,7 +335,7 @@ class ConfigTest(unittest.TestCase):
|
|||||||
self.assertTrue(expected_warning_msg in mock_logging.warn.call_args[0][0])
|
self.assertTrue(expected_warning_msg in mock_logging.warn.call_args[0][0])
|
||||||
|
|
||||||
def test_config_invalid_environment_dict_key_raises_validation_error(self):
|
def test_config_invalid_environment_dict_key_raises_validation_error(self):
|
||||||
expected_error_msg = "Service 'web' configuration key 'environment' contains an invalid type"
|
expected_error_msg = "Service 'web' configuration key 'environment' contains unsupported option: '---'"
|
||||||
|
|
||||||
with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
|
with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
|
||||||
config.load(
|
config.load(
|
||||||
@ -942,7 +942,10 @@ class ExtendsTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_extends_validation_invalid_key(self):
|
def test_extends_validation_invalid_key(self):
|
||||||
expected_error_msg = "Unsupported config option for 'web' service: 'rogue_key'"
|
expected_error_msg = (
|
||||||
|
"Service 'web' configuration key 'extends' "
|
||||||
|
"contains unsupported option: 'rogue_key'"
|
||||||
|
)
|
||||||
with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
|
with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
|
||||||
config.load(
|
config.load(
|
||||||
build_config_details(
|
build_config_details(
|
||||||
@ -962,7 +965,10 @@ class ExtendsTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_extends_validation_sub_property_key(self):
|
def test_extends_validation_sub_property_key(self):
|
||||||
expected_error_msg = "Service 'web' configuration key 'extends' 'file' contains an invalid type"
|
expected_error_msg = (
|
||||||
|
"Service 'web' configuration key 'extends' 'file' contains 1, "
|
||||||
|
"which is an invalid type, it should be a string"
|
||||||
|
)
|
||||||
with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
|
with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
|
||||||
config.load(
|
config.load(
|
||||||
build_config_details(
|
build_config_details(
|
||||||
@ -1100,6 +1106,26 @@ class ExtendsTest(unittest.TestCase):
|
|||||||
dicts = load_from_filename('tests/fixtures/extends/valid-common-config.yml')
|
dicts = load_from_filename('tests/fixtures/extends/valid-common-config.yml')
|
||||||
self.assertEqual(dicts[0]['environment'], {'FOO': '1'})
|
self.assertEqual(dicts[0]['environment'], {'FOO': '1'})
|
||||||
|
|
||||||
|
def test_extended_service_with_verbose_and_shorthand_way(self):
|
||||||
|
services = load_from_filename('tests/fixtures/extends/verbose-and-shorthand.yml')
|
||||||
|
self.assertEqual(service_sort(services), service_sort([
|
||||||
|
{
|
||||||
|
'name': 'base',
|
||||||
|
'image': 'busybox',
|
||||||
|
'environment': {'BAR': '1'},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'verbose',
|
||||||
|
'image': 'busybox',
|
||||||
|
'environment': {'BAR': '1', 'FOO': '1'},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'shorthand',
|
||||||
|
'image': 'busybox',
|
||||||
|
'environment': {'BAR': '1', 'FOO': '2'},
|
||||||
|
},
|
||||||
|
]))
|
||||||
|
|
||||||
|
|
||||||
@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…
x
Reference in New Issue
Block a user