Browse Source

Merge pull request #7588 from aiordache/rm_version

Merge V2 - V3 compose file formats (optional version field)
Anca Iordache 5 years ago
parent
commit
52d2fcc274

+ 2 - 14
compose/cli/command.py

@@ -66,7 +66,6 @@ def project_from_options(project_dir, options, additional_options=None):
         context=context,
         context=context,
         environment=environment,
         environment=environment,
         override_dir=override_dir,
         override_dir=override_dir,
-        compatibility=compatibility_from_options(project_dir, options, environment),
         interpolate=(not additional_options.get('--no-interpolate')),
         interpolate=(not additional_options.get('--no-interpolate')),
         environment_file=environment_file
         environment_file=environment_file
     )
     )
@@ -98,7 +97,6 @@ def get_config_from_options(base_dir, options, additional_options=None):
     )
     )
     return config.load(
     return config.load(
         config.find(base_dir, config_path, environment, override_dir),
         config.find(base_dir, config_path, environment, override_dir),
-        compatibility_from_options(config_path, options, environment),
         not additional_options.get('--no-interpolate')
         not additional_options.get('--no-interpolate')
     )
     )
 
 
@@ -120,14 +118,14 @@ def get_config_path_from_options(base_dir, options, environment):
 
 
 def get_project(project_dir, config_path=None, project_name=None, verbose=False,
 def get_project(project_dir, config_path=None, project_name=None, verbose=False,
                 context=None, environment=None, override_dir=None,
                 context=None, environment=None, override_dir=None,
-                compatibility=False, interpolate=True, environment_file=None):
+                interpolate=True, environment_file=None):
     if not environment:
     if not environment:
         environment = Environment.from_env_file(project_dir)
         environment = Environment.from_env_file(project_dir)
     config_details = config.find(project_dir, config_path, environment, override_dir)
     config_details = config.find(project_dir, config_path, environment, override_dir)
     project_name = get_project_name(
     project_name = get_project_name(
         config_details.working_dir, project_name, environment
         config_details.working_dir, project_name, environment
     )
     )
-    config_data = config.load(config_details, compatibility, interpolate)
+    config_data = config.load(config_details, interpolate)
 
 
     api_version = environment.get(
     api_version = environment.get(
         'COMPOSE_API_VERSION',
         'COMPOSE_API_VERSION',
@@ -188,13 +186,3 @@ def get_project_name(working_dir, project_name=None, environment=None):
         return normalize_name(project)
         return normalize_name(project)
 
 
     return 'default'
     return 'default'
-
-
-def compatibility_from_options(working_dir, options=None, environment=None):
-    """Get compose v3 compatibility from --compatibility option
-       or from COMPOSE_COMPATIBILITY environment variable."""
-
-    compatibility_option = options.get('--compatibility')
-    compatibility_environment = environment.get_boolean('COMPOSE_COMPATIBILITY')
-
-    return compatibility_option or compatibility_environment

+ 5 - 12
compose/cli/main.py

@@ -24,7 +24,6 @@ from ..config import resolve_build_args
 from ..config.environment import Environment
 from ..config.environment import Environment
 from ..config.serialize import serialize_config
 from ..config.serialize import serialize_config
 from ..config.types import VolumeSpec
 from ..config.types import VolumeSpec
-from ..const import COMPOSEFILE_V2_2 as V2_2
 from ..const import IS_WINDOWS_PLATFORM
 from ..const import IS_WINDOWS_PLATFORM
 from ..errors import StreamParseError
 from ..errors import StreamParseError
 from ..progress_stream import StreamOutputError
 from ..progress_stream import StreamOutputError
@@ -205,7 +204,7 @@ class TopLevelCommand(object):
       --project-directory PATH    Specify an alternate working directory
       --project-directory PATH    Specify an alternate working directory
                                   (default: the path of the Compose file)
                                   (default: the path of the Compose file)
       --compatibility             If set, Compose will attempt to convert keys
       --compatibility             If set, Compose will attempt to convert keys
-                                  in v3 files to their non-Swarm equivalent
+                                  in v3 files to their non-Swarm equivalent (DEPRECATED)
       --env-file PATH             Specify an alternate environment file
       --env-file PATH             Specify an alternate environment file
 
 
     Commands:
     Commands:
@@ -882,16 +881,10 @@ class TopLevelCommand(object):
         """
         """
         timeout = timeout_from_opts(options)
         timeout = timeout_from_opts(options)
 
 
-        if self.project.config_version == V2_2:
-            raise UserError(
-                'The scale command is incompatible with the v2.2 format. '
-                'Use the up command with the --scale flag instead.'
-            )
-        else:
-            log.warning(
-                'The scale command is deprecated. '
-                'Use the up command with the --scale flag instead.'
-            )
+        log.warning(
+            'The scale command is deprecated. '
+            'Use the up command with the --scale flag instead.'
+        )
 
 
         for service_name, num in parse_scale_args(options['SERVICE=NUM']).items():
         for service_name, num in parse_scale_args(options['SERVICE=NUM']).items():
             self.project.get_service(service_name).scale(num, timeout=timeout)
             self.project.get_service(service_name).scale(num, timeout=timeout)

+ 70 - 156
compose/config/config.py

@@ -12,18 +12,13 @@ import yaml
 from cached_property import cached_property
 from cached_property import cached_property
 
 
 from . import types
 from . import types
-from .. import const
+from ..const import COMPOSE_SPEC as VERSION
 from ..const import COMPOSEFILE_V1 as V1
 from ..const import COMPOSEFILE_V1 as V1
-from ..const import COMPOSEFILE_V2_1 as V2_1
-from ..const import COMPOSEFILE_V2_3 as V2_3
-from ..const import COMPOSEFILE_V3_0 as V3_0
-from ..const import COMPOSEFILE_V3_4 as V3_4
 from ..utils import build_string_dict
 from ..utils import build_string_dict
 from ..utils import json_hash
 from ..utils import json_hash
 from ..utils import parse_bytes
 from ..utils import parse_bytes
 from ..utils import parse_nanoseconds_int
 from ..utils import parse_nanoseconds_int
 from ..utils import splitdrive
 from ..utils import splitdrive
-from ..version import ComposeVersion
 from .environment import env_vars_from_file
 from .environment import env_vars_from_file
 from .environment import Environment
 from .environment import Environment
 from .environment import split_env
 from .environment import split_env
@@ -189,15 +184,28 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')):
 
 
     @cached_property
     @cached_property
     def version(self):
     def version(self):
-        if 'version' not in self.config:
+        version = self.config.get('version', None)
+        if not version:
+            # no version is specified in the config file
+            services = self.config.get('services', None)
+            networks = self.config.get('networks', None)
+            volumes = self.config.get('volumes', None)
+            if services or networks or volumes:
+                # validate V2/V3 structure
+                for section in ['services', 'networks', 'volumes']:
+                    validate_config_section(
+                        self.filename, self.config.get(section, {}), section)
+                return VERSION
+
+            # validate V1 structure
+            validate_config_section(
+                self.filename, self.config, 'services')
             return V1
             return V1
 
 
-        version = self.config['version']
-
         if isinstance(version, dict):
         if isinstance(version, dict):
             log.warning('Unexpected type for "version" key in "{}". Assuming '
             log.warning('Unexpected type for "version" key in "{}". Assuming '
                         '"version" is the name of a service, and defaulting to '
                         '"version" is the name of a service, and defaulting to '
-                        'Compose file version 1.'.format(self.filename))
+                        'Compose file version {}.'.format(self.filename, V1))
             return V1
             return V1
 
 
         if not isinstance(version, str):
         if not isinstance(version, str):
@@ -205,31 +213,32 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')):
                 'Version in "{}" is invalid - it should be a string.'
                 'Version in "{}" is invalid - it should be a string.'
                 .format(self.filename))
                 .format(self.filename))
 
 
-        if version == '1':
+        if isinstance(version, str):
+            version_pattern = re.compile(r"^[1-3]+(\.\d+)?$")
+            if not version_pattern.match(version):
+                raise ConfigurationError(
+                    'Version "{}" in "{}" is invalid.'
+                    .format(version, self.filename))
+
+            if version.startswith("1"):
+                version = V1
+            else:
+                version = VERSION
+
+        if version == V1:
             raise ConfigurationError(
             raise ConfigurationError(
                 'Version in "{}" is invalid. {}'
                 'Version in "{}" is invalid. {}'
                 .format(self.filename, VERSION_EXPLANATION)
                 .format(self.filename, VERSION_EXPLANATION)
             )
             )
-
-        version_pattern = re.compile(r"^[2-9]+(\.\d+)?$")
-        if not version_pattern.match(version):
-            raise ConfigurationError(
-                'Version "{}" in "{}" is invalid.'
-                .format(version, self.filename))
-
-        if version == '2':
-            return const.COMPOSEFILE_V2_0
-
-        if version == '3':
-            return const.COMPOSEFILE_V3_0
-
-        return ComposeVersion(version)
+        return version
 
 
     def get_service(self, name):
     def get_service(self, name):
         return self.get_service_dicts()[name]
         return self.get_service_dicts()[name]
 
 
     def get_service_dicts(self):
     def get_service_dicts(self):
-        return self.config if self.version == V1 else self.config.get('services', {})
+        if self.version == V1:
+            return self.config
+        return self.config.get('services', {})
 
 
     def get_volumes(self):
     def get_volumes(self):
         return {} if self.version == V1 else self.config.get('volumes', {})
         return {} if self.version == V1 else self.config.get('volumes', {})
@@ -238,10 +247,10 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')):
         return {} if self.version == V1 else self.config.get('networks', {})
         return {} if self.version == V1 else self.config.get('networks', {})
 
 
     def get_secrets(self):
     def get_secrets(self):
-        return {} if self.version < const.COMPOSEFILE_V3_1 else self.config.get('secrets', {})
+        return {} if self.version == V1 else self.config.get('secrets', {})
 
 
     def get_configs(self):
     def get_configs(self):
-        return {} if self.version < const.COMPOSEFILE_V3_3 else self.config.get('configs', {})
+        return {} if self.version == V1 else self.config.get('configs', {})
 
 
 
 
 class Config(namedtuple('_Config', 'version services volumes networks secrets configs')):
 class Config(namedtuple('_Config', 'version services volumes networks secrets configs')):
@@ -299,6 +308,7 @@ def find(base_dir, filenames, environment, override_dir=None):
 def validate_config_version(config_files):
 def validate_config_version(config_files):
     main_file = 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_files[1:]:
     for next_file in config_files[1:]:
         validate_top_level_object(next_file)
         validate_top_level_object(next_file)
 
 
@@ -355,7 +365,7 @@ def find_candidates_in_parent_dirs(filenames, path):
     return (candidates, path)
     return (candidates, path)
 
 
 
 
-def check_swarm_only_config(service_dicts, compatibility=False):
+def check_swarm_only_config(service_dicts):
     warning_template = (
     warning_template = (
         "Some services ({services}) use the '{key}' key, which will be ignored. "
         "Some services ({services}) use the '{key}' key, which will be ignored. "
         "Compose does not support '{key}' configuration - use "
         "Compose does not support '{key}' configuration - use "
@@ -371,18 +381,20 @@ def check_swarm_only_config(service_dicts, compatibility=False):
                     key=key
                     key=key
                 )
                 )
             )
             )
-    if not compatibility:
-        check_swarm_only_key(service_dicts, 'deploy')
+
+    check_swarm_only_key(service_dicts, 'deploy')
     check_swarm_only_key(service_dicts, 'configs')
     check_swarm_only_key(service_dicts, 'configs')
 
 
 
 
-def load(config_details, compatibility=False, interpolate=True):
+def load(config_details, interpolate=True):
     """Load the configuration from a working directory and a list of
     """Load the configuration from a working directory and a list of
     configuration files.  Files are loaded in order, and merged on top
     configuration files.  Files are loaded in order, and merged on top
     of each other to create the final configuration.
     of each other to create the final configuration.
 
 
     Return a fully interpolated, extended and validated configuration.
     Return a fully interpolated, extended and validated configuration.
     """
     """
+
+    # validate against latest version and if fails do it against v1 schema
     validate_config_version(config_details.config_files)
     validate_config_version(config_details.config_files)
 
 
     processed_files = [
     processed_files = [
@@ -404,15 +416,15 @@ def load(config_details, compatibility=False, interpolate=True):
     configs = load_mapping(
     configs = load_mapping(
         config_details.config_files, 'get_configs', 'Config', config_details.working_dir
         config_details.config_files, 'get_configs', 'Config', config_details.working_dir
     )
     )
-    service_dicts = load_services(config_details, main_file, compatibility, interpolate=interpolate)
+    service_dicts = load_services(config_details, main_file, interpolate=interpolate)
 
 
     if main_file.version != V1:
     if main_file.version != V1:
         for service_dict in service_dicts:
         for service_dict in service_dicts:
             match_named_volumes(service_dict, volumes)
             match_named_volumes(service_dict, volumes)
 
 
-    check_swarm_only_config(service_dicts, compatibility)
+    check_swarm_only_config(service_dicts)
 
 
-    version = V2_3 if compatibility and main_file.version >= V3_0 else main_file.version
+    version = main_file.version
 
 
     return Config(version, service_dicts, volumes, networks, secrets, configs)
     return Config(version, service_dicts, volumes, networks, secrets, configs)
 
 
@@ -449,14 +461,15 @@ def load_mapping(config_files, get_func, entity_type, working_dir=None):
 
 
 
 
 def validate_external(entity_type, name, config, version):
 def validate_external(entity_type, name, config, version):
-    if (version < V2_1 or (version >= V3_0 and version < V3_4)) and len(config.keys()) > 1:
-        raise ConfigurationError(
-            "{} {} declared as external but specifies additional attributes "
-            "({}).".format(
-                entity_type, name, ', '.join(k for k in config if k != 'external')))
+    for k in config.keys():
+        if k not in ['external', 'name']:
+            raise ConfigurationError(
+                "{} {} declared as external but specifies additional attributes "
+                "({}).".format(
+                    entity_type, name, ', '.join(k for k in config if k != 'external')))
 
 
 
 
-def load_services(config_details, config_file, compatibility=False, interpolate=True):
+def load_services(config_details, config_file, interpolate=True):
     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(
             config_details.working_dir,
             config_details.working_dir,
@@ -475,7 +488,6 @@ def load_services(config_details, config_file, compatibility=False, interpolate=
             service_names,
             service_names,
             config_file.version,
             config_file.version,
             config_details.environment,
             config_details.environment,
-            compatibility,
             interpolate
             interpolate
         )
         )
         return service_dict
         return service_dict
@@ -554,27 +566,25 @@ def process_config_file(config_file, environment, service_name=None, interpolate
             environment,
             environment,
             interpolate,
             interpolate,
         )
         )
-        if config_file.version >= const.COMPOSEFILE_V3_1:
-            processed_config['secrets'] = process_config_section(
-                config_file,
-                config_file.get_secrets(),
-                'secret',
-                environment,
-                interpolate,
-            )
-        if config_file.version >= const.COMPOSEFILE_V3_3:
-            processed_config['configs'] = process_config_section(
-                config_file,
-                config_file.get_configs(),
-                'config',
-                environment,
-                interpolate,
-            )
+        processed_config['secrets'] = process_config_section(
+            config_file,
+            config_file.get_secrets(),
+            'secret',
+            environment,
+            interpolate,
+        )
+        processed_config['configs'] = process_config_section(
+            config_file,
+            config_file.get_configs(),
+            'config',
+            environment,
+            interpolate,
+        )
     else:
     else:
         processed_config = services
         processed_config = services
 
 
     config_file = config_file._replace(config=processed_config)
     config_file = config_file._replace(config=processed_config)
-    validate_against_config_schema(config_file)
+    validate_against_config_schema(config_file, config_file.version)
 
 
     if service_name and service_name not in services:
     if service_name and service_name not in services:
         raise ConfigurationError(
         raise ConfigurationError(
@@ -876,7 +886,7 @@ def finalize_service_volumes(service_dict, environment):
     return service_dict
     return service_dict
 
 
 
 
-def finalize_service(service_config, service_names, version, environment, compatibility,
+def finalize_service(service_config, service_names, version, environment,
                      interpolate=True):
                      interpolate=True):
     service_dict = dict(service_config.config)
     service_dict = dict(service_config.config)
 
 
@@ -918,106 +928,10 @@ def finalize_service(service_config, service_names, version, environment, compat
 
 
     normalize_build(service_dict, service_config.working_dir, environment)
     normalize_build(service_dict, service_config.working_dir, environment)
 
 
-    if compatibility:
-        service_dict = translate_credential_spec_to_security_opt(service_dict)
-        service_dict, ignored_keys = translate_deploy_keys_to_container_config(
-            service_dict
-        )
-        if ignored_keys:
-            log.warning(
-                'The following deploy sub-keys are not supported in compatibility mode and have'
-                ' been ignored: {}'.format(', '.join(ignored_keys))
-            )
-
     service_dict['name'] = service_config.name
     service_dict['name'] = service_config.name
     return normalize_v1_service_format(service_dict)
     return normalize_v1_service_format(service_dict)
 
 
 
 
-def translate_resource_keys_to_container_config(resources_dict, service_dict):
-    if 'limits' in resources_dict:
-        service_dict['mem_limit'] = resources_dict['limits'].get('memory')
-        if 'cpus' in resources_dict['limits']:
-            service_dict['cpus'] = float(resources_dict['limits']['cpus'])
-    if 'reservations' in resources_dict:
-        service_dict['mem_reservation'] = resources_dict['reservations'].get('memory')
-        if 'cpus' in resources_dict['reservations']:
-            return ['resources.reservations.cpus']
-    return []
-
-
-def convert_restart_policy(name):
-    try:
-        return {
-            'any': 'always',
-            'none': 'no',
-            'on-failure': 'on-failure'
-        }[name]
-    except KeyError:
-        raise ConfigurationError('Invalid restart policy "{}"'.format(name))
-
-
-def convert_credential_spec_to_security_opt(credential_spec):
-    if 'file' in credential_spec:
-        return 'file://{file}'.format(file=credential_spec['file'])
-    return 'registry://{registry}'.format(registry=credential_spec['registry'])
-
-
-def translate_credential_spec_to_security_opt(service_dict):
-    result = []
-
-    if 'credential_spec' in service_dict:
-        spec = convert_credential_spec_to_security_opt(service_dict['credential_spec'])
-        result.append('credentialspec={spec}'.format(spec=spec))
-
-    if result:
-        service_dict['security_opt'] = result
-
-    return service_dict
-
-
-def translate_deploy_keys_to_container_config(service_dict):
-    if 'credential_spec' in service_dict:
-        del service_dict['credential_spec']
-    if 'configs' in service_dict:
-        del service_dict['configs']
-
-    if 'deploy' not in service_dict:
-        return service_dict, []
-
-    deploy_dict = service_dict['deploy']
-    ignored_keys = [
-        k for k in ['endpoint_mode', 'labels', 'update_config', 'rollback_config']
-        if k in deploy_dict
-    ]
-
-    if 'replicas' in deploy_dict and deploy_dict.get('mode', 'replicated') == 'replicated':
-        scale = deploy_dict.get('replicas', 1)
-        max_replicas = deploy_dict.get('placement', {}).get('max_replicas_per_node', scale)
-        service_dict['scale'] = min(scale, max_replicas)
-        if max_replicas < scale:
-            log.warning("Scale is limited to {} ('max_replicas_per_node' field).".format(
-                max_replicas))
-
-    if 'restart_policy' in deploy_dict:
-        service_dict['restart'] = {
-            'Name': convert_restart_policy(deploy_dict['restart_policy'].get('condition', 'any')),
-            'MaximumRetryCount': deploy_dict['restart_policy'].get('max_attempts', 0)
-        }
-        for k in deploy_dict['restart_policy'].keys():
-            if k != 'condition' and k != 'max_attempts':
-                ignored_keys.append('restart_policy.{}'.format(k))
-
-    ignored_keys.extend(
-        translate_resource_keys_to_container_config(
-            deploy_dict.get('resources', {}), service_dict
-        )
-    )
-
-    del service_dict['deploy']
-
-    return service_dict, ignored_keys
-
-
 def normalize_v1_service_format(service_dict):
 def normalize_v1_service_format(service_dict):
     if 'log_driver' in service_dict or 'log_opt' in service_dict:
     if 'log_driver' in service_dict or 'log_opt' in service_dict:
         if 'logging' not in service_dict:
         if 'logging' not in service_dict:

+ 277 - 87
compose/config/config_schema_v3.6.json → compose/config/config_schema_compose_spec.json

@@ -1,14 +1,14 @@
 {
 {
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v3.6.json",
+  "$schema": "http://json-schema.org/draft/2019-09/schema#",
+  "id": "config_schema_compose_spec.json",
   "type": "object",
   "type": "object",
-  "required": ["version"],
-
+  "title": "Compose Specification",
+  "description": "The Compose file is a YAML file defining a multi-containers based application.",
   "properties": {
   "properties": {
     "version": {
     "version": {
-      "type": "string"
+      "type": "string",
+      "description": "Version of the Compose specification used. Tools not implementing required version MUST reject the configuration file."
     },
     },
-
     "services": {
     "services": {
       "id": "#/properties/services",
       "id": "#/properties/services",
       "type": "object",
       "type": "object",
@@ -19,7 +19,6 @@
       },
       },
       "additionalProperties": false
       "additionalProperties": false
     },
     },
-
     "networks": {
     "networks": {
       "id": "#/properties/networks",
       "id": "#/properties/networks",
       "type": "object",
       "type": "object",
@@ -29,7 +28,6 @@
         }
         }
       }
       }
     },
     },
-
     "volumes": {
     "volumes": {
       "id": "#/properties/volumes",
       "id": "#/properties/volumes",
       "type": "object",
       "type": "object",
@@ -40,7 +38,6 @@
       },
       },
       "additionalProperties": false
       "additionalProperties": false
     },
     },
-
     "secrets": {
     "secrets": {
       "id": "#/properties/secrets",
       "id": "#/properties/secrets",
       "type": "object",
       "type": "object",
@@ -51,7 +48,6 @@
       },
       },
       "additionalProperties": false
       "additionalProperties": false
     },
     },
-
     "configs": {
     "configs": {
       "id": "#/properties/configs",
       "id": "#/properties/configs",
       "type": "object",
       "type": "object",
@@ -63,16 +59,12 @@
       "additionalProperties": false
       "additionalProperties": false
     }
     }
   },
   },
-
   "patternProperties": {"^x-": {}},
   "patternProperties": {"^x-": {}},
   "additionalProperties": false,
   "additionalProperties": false,
-
   "definitions": {
   "definitions": {
-
     "service": {
     "service": {
       "id": "#/definitions/service",
       "id": "#/definitions/service",
       "type": "object",
       "type": "object",
-
       "properties": {
       "properties": {
         "deploy": {"$ref": "#/definitions/deployment"},
         "deploy": {"$ref": "#/definitions/deployment"},
         "build": {
         "build": {
@@ -88,12 +80,42 @@
                 "cache_from": {"$ref": "#/definitions/list_of_strings"},
                 "cache_from": {"$ref": "#/definitions/list_of_strings"},
                 "network": {"type": "string"},
                 "network": {"type": "string"},
                 "target": {"type": "string"},
                 "target": {"type": "string"},
-                "shm_size": {"type": ["integer", "string"]}
+                "shm_size": {"type": ["integer", "string"]},
+                "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
+                "isolation": {"type": "string"}
               },
               },
-              "additionalProperties": false
+              "additionalProperties": false,
+              "patternProperties": {"^x-": {}}
             }
             }
           ]
           ]
         },
         },
+        "blkio_config": {
+          "type": "object",
+          "properties": {
+            "device_read_bps": {
+              "type": "array",
+              "items": {"$ref": "#/definitions/blkio_limit"}
+            },
+            "device_read_iops": {
+              "type": "array",
+              "items": {"$ref": "#/definitions/blkio_limit"}
+            },
+            "device_write_bps": {
+              "type": "array",
+              "items": {"$ref": "#/definitions/blkio_limit"}
+            },
+            "device_write_iops": {
+              "type": "array",
+              "items": {"$ref": "#/definitions/blkio_limit"}
+            },
+            "weight": {"type": "integer"},
+            "weight_device": {
+              "type": "array",
+              "items": {"$ref": "#/definitions/blkio_weight"}
+            }
+          },
+          "additionalProperties": false
+        },
         "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "cgroup_parent": {"type": "string"},
         "cgroup_parent": {"type": "string"},
@@ -116,19 +138,60 @@
                   "uid": {"type": "string"},
                   "uid": {"type": "string"},
                   "gid": {"type": "string"},
                   "gid": {"type": "string"},
                   "mode": {"type": "number"}
                   "mode": {"type": "number"}
-                }
+                },
+                "additionalProperties": false,
+                "patternProperties": {"^x-": {}}
               }
               }
             ]
             ]
           }
           }
         },
         },
         "container_name": {"type": "string"},
         "container_name": {"type": "string"},
-        "credential_spec": {"type": "object", "properties": {
-          "file": {"type": "string"},
-          "registry": {"type": "string"}
-        }},
-        "depends_on": {"$ref": "#/definitions/list_of_strings"},
+        "cpu_count": {"type": "integer", "minimum": 0},
+        "cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100},
+        "cpu_shares": {"type": ["number", "string"]},
+        "cpu_quota": {"type": ["number", "string"]},
+        "cpu_period": {"type": ["number", "string"]},
+        "cpu_rt_period": {"type": ["number", "string"]},
+        "cpu_rt_runtime": {"type": ["number", "string"]},
+        "cpus": {"type": "number", "minimum": 0},
+        "cpuset": {"type": "string"},
+        "credential_spec": {
+          "type": "object",
+          "properties": {
+            "config": {"type": "string"},
+            "file": {"type": "string"},
+            "registry": {"type": "string"}
+          },
+          "additionalProperties": false,
+          "patternProperties": {"^x-": {}}
+        },
+        "depends_on": {
+          "oneOf": [
+            {"$ref": "#/definitions/list_of_strings"},
+            {
+              "type": "object",
+              "additionalProperties": false,
+              "patternProperties": {
+                "^[a-zA-Z0-9._-]+$": {
+                  "type": "object",
+                  "additionalProperties": false,
+                  "properties": {
+                    "condition": {
+                      "type": "string",
+                      "enum": ["service_started", "service_healthy"]
+                    }
+                  },
+                  "required": ["condition"]
+                }
+              }
+            }
+          ]
+        },
+        "device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"},
         "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "dns": {"$ref": "#/definitions/string_or_list"},
         "dns": {"$ref": "#/definitions/string_or_list"},
+
+        "dns_opt": {"type": "array","items": {"type": "string"}, "uniqueItems": true},
         "dns_search": {"$ref": "#/definitions/string_or_list"},
         "dns_search": {"$ref": "#/definitions/string_or_list"},
         "domainname": {"type": "string"},
         "domainname": {"type": "string"},
         "entrypoint": {
         "entrypoint": {
@@ -149,34 +212,57 @@
           "uniqueItems": true
           "uniqueItems": true
         },
         },
 
 
+        "extends": {
+          "oneOf": [
+            {"type": "string"},
+            {
+              "type": "object",
+              "properties": {
+                "service": {"type": "string"},
+                "file": {"type": "string"}
+              },
+              "required": ["service"],
+              "additionalProperties": false
+            }
+          ]
+        },
         "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
         "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
+        "group_add": {
+          "type": "array",
+          "items": {
+            "type": ["string", "number"]
+          },
+          "uniqueItems": true
+        },
         "healthcheck": {"$ref": "#/definitions/healthcheck"},
         "healthcheck": {"$ref": "#/definitions/healthcheck"},
         "hostname": {"type": "string"},
         "hostname": {"type": "string"},
         "image": {"type": "string"},
         "image": {"type": "string"},
+        "init": {"type": "boolean"},
         "ipc": {"type": "string"},
         "ipc": {"type": "string"},
         "isolation": {"type": "string"},
         "isolation": {"type": "string"},
         "labels": {"$ref": "#/definitions/list_or_dict"},
         "labels": {"$ref": "#/definitions/list_or_dict"},
         "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-
         "logging": {
         "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": ["string", "number", "null"]}
-                  }
-                }
-            },
-            "additionalProperties": false
+          "type": "object",
+          "properties": {
+            "driver": {"type": "string"},
+            "options": {
+              "type": "object",
+              "patternProperties": {
+                "^.+$": {"type": ["string", "number", "null"]}
+              }
+            }
+          },
+          "additionalProperties": false,
+          "patternProperties": {"^x-": {}}
         },
         },
-
         "mac_address": {"type": "string"},
         "mac_address": {"type": "string"},
+        "mem_limit": {"type": ["number", "string"]},
+        "mem_reservation": {"type": ["string", "integer"]},
+        "mem_swappiness": {"type": "integer"},
+        "memswap_limit": {"type": ["number", "string"]},
         "network_mode": {"type": "string"},
         "network_mode": {"type": "string"},
-
         "networks": {
         "networks": {
           "oneOf": [
           "oneOf": [
             {"$ref": "#/definitions/list_of_strings"},
             {"$ref": "#/definitions/list_of_strings"},
@@ -190,9 +276,12 @@
                       "properties": {
                       "properties": {
                         "aliases": {"$ref": "#/definitions/list_of_strings"},
                         "aliases": {"$ref": "#/definitions/list_of_strings"},
                         "ipv4_address": {"type": "string"},
                         "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"}
+                        "ipv6_address": {"type": "string"},
+                        "link_local_ips": {"$ref": "#/definitions/list_of_strings"},
+                        "priority": {"type": "number"}
                       },
                       },
-                      "additionalProperties": false
+                      "additionalProperties": false,
+                      "patternProperties": {"^x-": {}}
                     },
                     },
                     {"type": "null"}
                     {"type": "null"}
                   ]
                   ]
@@ -202,8 +291,11 @@
             }
             }
           ]
           ]
         },
         },
+        "oom_kill_disable": {"type": "boolean"},
+        "oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000},
         "pid": {"type": ["string", "null"]},
         "pid": {"type": ["string", "null"]},
-
+        "pids_limit": {"type": ["number", "string"]},
+        "platform": {"type": "string"},
         "ports": {
         "ports": {
           "type": "array",
           "type": "array",
           "items": {
           "items": {
@@ -218,16 +310,26 @@
                   "published": {"type": "integer"},
                   "published": {"type": "integer"},
                   "protocol": {"type": "string"}
                   "protocol": {"type": "string"}
                 },
                 },
-                "additionalProperties": false
+                "additionalProperties": false,
+                "patternProperties": {"^x-": {}}
               }
               }
             ]
             ]
           },
           },
           "uniqueItems": true
           "uniqueItems": true
         },
         },
-
         "privileged": {"type": "boolean"},
         "privileged": {"type": "boolean"},
+        "pull_policy": {"type": "string", "enum": [
+          "always", "never", "if_not_present"
+        ]},
         "read_only": {"type": "boolean"},
         "read_only": {"type": "boolean"},
         "restart": {"type": "string"},
         "restart": {"type": "string"},
+        "runtime": {
+          "deprecated": true,
+          "type": "string"
+        },
+        "scale": {
+          "type": "integer"
+        },
         "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "shm_size": {"type": ["number", "string"]},
         "shm_size": {"type": ["number", "string"]},
         "secrets": {
         "secrets": {
@@ -243,7 +345,9 @@
                   "uid": {"type": "string"},
                   "uid": {"type": "string"},
                   "gid": {"type": "string"},
                   "gid": {"type": "string"},
                   "mode": {"type": "number"}
                   "mode": {"type": "number"}
-                }
+                },
+                "additionalProperties": false,
+                "patternProperties": {"^x-": {}}
               }
               }
             ]
             ]
           }
           }
@@ -261,13 +365,14 @@
               "oneOf": [
               "oneOf": [
                 {"type": "integer"},
                 {"type": "integer"},
                 {
                 {
-                  "type":"object",
+                  "type": "object",
                   "properties": {
                   "properties": {
                     "hard": {"type": "integer"},
                     "hard": {"type": "integer"},
                     "soft": {"type": "integer"}
                     "soft": {"type": "integer"}
                   },
                   },
                   "required": ["soft", "hard"],
                   "required": ["soft", "hard"],
-                  "additionalProperties": false
+                  "additionalProperties": false,
+                  "patternProperties": {"^x-": {}}
                 }
                 }
               ]
               ]
             }
             }
@@ -293,13 +398,17 @@
                     "type": "object",
                     "type": "object",
                     "properties": {
                     "properties": {
                       "propagation": {"type": "string"}
                       "propagation": {"type": "string"}
-                    }
+                    },
+                    "additionalProperties": false,
+                    "patternProperties": {"^x-": {}}
                   },
                   },
                   "volume": {
                   "volume": {
                     "type": "object",
                     "type": "object",
                     "properties": {
                     "properties": {
                       "nocopy": {"type": "boolean"}
                       "nocopy": {"type": "boolean"}
-                    }
+                    },
+                    "additionalProperties": false,
+                    "patternProperties": {"^x-": {}}
                   },
                   },
                   "tmpfs": {
                   "tmpfs": {
                     "type": "object",
                     "type": "object",
@@ -308,24 +417,32 @@
                         "type": "integer",
                         "type": "integer",
                         "minimum": 0
                         "minimum": 0
                       }
                       }
-                    }
+                    },
+                    "additionalProperties": false,
+                    "patternProperties": {"^x-": {}}
                   }
                   }
                 },
                 },
-                "additionalProperties": false
+                "additionalProperties": false,
+                "patternProperties": {"^x-": {}}
               }
               }
             ],
             ],
             "uniqueItems": true
             "uniqueItems": true
           }
           }
         },
         },
+        "volumes_from": {
+          "type": "array",
+          "items": {"type": "string"},
+          "uniqueItems": true
+        },
         "working_dir": {"type": "string"}
         "working_dir": {"type": "string"}
       },
       },
+      "patternProperties": {"^x-": {}},
       "additionalProperties": false
       "additionalProperties": false
     },
     },
 
 
     "healthcheck": {
     "healthcheck": {
       "id": "#/definitions/healthcheck",
       "id": "#/definitions/healthcheck",
       "type": "object",
       "type": "object",
-      "additionalProperties": false,
       "properties": {
       "properties": {
         "disable": {"type": "boolean"},
         "disable": {"type": "boolean"},
         "interval": {"type": "string", "format": "duration"},
         "interval": {"type": "string", "format": "duration"},
@@ -338,7 +455,9 @@
         },
         },
         "timeout": {"type": "string", "format": "duration"},
         "timeout": {"type": "string", "format": "duration"},
         "start_period": {"type": "string", "format": "duration"}
         "start_period": {"type": "string", "format": "duration"}
-      }
+      },
+      "additionalProperties": false,
+      "patternProperties": {"^x-": {}}
     },
     },
     "deployment": {
     "deployment": {
       "id": "#/definitions/deployment",
       "id": "#/definitions/deployment",
@@ -348,6 +467,21 @@
         "endpoint_mode": {"type": "string"},
         "endpoint_mode": {"type": "string"},
         "replicas": {"type": "integer"},
         "replicas": {"type": "integer"},
         "labels": {"$ref": "#/definitions/list_or_dict"},
         "labels": {"$ref": "#/definitions/list_or_dict"},
+        "rollback_config": {
+          "type": "object",
+          "properties": {
+            "parallelism": {"type": "integer"},
+            "delay": {"type": "string", "format": "duration"},
+            "failure_action": {"type": "string"},
+            "monitor": {"type": "string", "format": "duration"},
+            "max_failure_ratio": {"type": "number"},
+            "order": {"type": "string", "enum": [
+              "start-first", "stop-first"
+            ]}
+          },
+          "additionalProperties": false,
+          "patternProperties": {"^x-": {}}
+        },
         "update_config": {
         "update_config": {
           "type": "object",
           "type": "object",
           "properties": {
           "properties": {
@@ -360,7 +494,8 @@
               "start-first", "stop-first"
               "start-first", "stop-first"
             ]}
             ]}
           },
           },
-          "additionalProperties": false
+          "additionalProperties": false,
+          "patternProperties": {"^x-": {}}
         },
         },
         "resources": {
         "resources": {
           "type": "object",
           "type": "object",
@@ -371,7 +506,8 @@
                 "cpus": {"type": "string"},
                 "cpus": {"type": "string"},
                 "memory": {"type": "string"}
                 "memory": {"type": "string"}
               },
               },
-              "additionalProperties": false
+              "additionalProperties": false,
+              "patternProperties": {"^x-": {}}
             },
             },
             "reservations": {
             "reservations": {
               "type": "object",
               "type": "object",
@@ -380,10 +516,12 @@
                 "memory": {"type": "string"},
                 "memory": {"type": "string"},
                 "generic_resources": {"$ref": "#/definitions/generic_resources"}
                 "generic_resources": {"$ref": "#/definitions/generic_resources"}
               },
               },
-              "additionalProperties": false
+              "additionalProperties": false,
+              "patternProperties": {"^x-": {}}
             }
             }
           },
           },
-          "additionalProperties": false
+          "additionalProperties": false,
+          "patternProperties": {"^x-": {}}
         },
         },
         "restart_policy": {
         "restart_policy": {
           "type": "object",
           "type": "object",
@@ -393,7 +531,8 @@
             "max_attempts": {"type": "integer"},
             "max_attempts": {"type": "integer"},
             "window": {"type": "string", "format": "duration"}
             "window": {"type": "string", "format": "duration"}
           },
           },
-          "additionalProperties": false
+          "additionalProperties": false,
+          "patternProperties": {"^x-": {}}
         },
         },
         "placement": {
         "placement": {
           "type": "object",
           "type": "object",
@@ -406,16 +545,19 @@
                 "properties": {
                 "properties": {
                   "spread": {"type": "string"}
                   "spread": {"type": "string"}
                 },
                 },
-                "additionalProperties": false
+                "additionalProperties": false,
+                "patternProperties": {"^x-": {}}
               }
               }
-            }
+            },
+            "max_replicas_per_node": {"type": "integer"}
           },
           },
-          "additionalProperties": false
+          "additionalProperties": false,
+          "patternProperties": {"^x-": {}}
         }
         }
       },
       },
-      "additionalProperties": false
+      "additionalProperties": false,
+      "patternProperties": {"^x-": {}}
     },
     },
-
     "generic_resources": {
     "generic_resources": {
       "id": "#/definitions/generic_resources",
       "id": "#/definitions/generic_resources",
       "type": "array",
       "type": "array",
@@ -428,13 +570,14 @@
               "kind": {"type": "string"},
               "kind": {"type": "string"},
               "value": {"type": "number"}
               "value": {"type": "number"}
             },
             },
-            "additionalProperties": false
+            "additionalProperties": false,
+            "patternProperties": {"^x-": {}}
           }
           }
         },
         },
-        "additionalProperties": false
+        "additionalProperties": false,
+        "patternProperties": {"^x-": {}}
       }
       }
     },
     },
-
     "network": {
     "network": {
       "id": "#/definitions/network",
       "id": "#/definitions/network",
       "type": ["object", "null"],
       "type": ["object", "null"],
@@ -456,28 +599,46 @@
               "items": {
               "items": {
                 "type": "object",
                 "type": "object",
                 "properties": {
                 "properties": {
-                  "subnet": {"type": "string"}
-                },
-                "additionalProperties": false
-              }
+                  "subnet": {"type": "string", "format": "subnet_ip_address"},
+                  "ip_range": {"type": "string"},
+                  "gateway": {"type": "string"},
+                  "aux_addresses": {
+                    "type": "object",
+                    "additionalProperties": false,
+                    "patternProperties": {"^.+$": {"type": "string"}}
+                  }
+                }
+              },
+              "additionalProperties": false,
+              "patternProperties": {"^x-": {}}
+            },
+            "options": {
+              "type": "object",
+              "additionalProperties": false,
+              "patternProperties": {"^.+$": {"type": "string"}}
             }
             }
           },
           },
-          "additionalProperties": false
+          "additionalProperties": false,
+          "patternProperties": {"^x-": {}}
         },
         },
         "external": {
         "external": {
           "type": ["boolean", "object"],
           "type": ["boolean", "object"],
           "properties": {
           "properties": {
-            "name": {"type": "string"}
+            "name": {
+              "deprecated": true,
+              "type": "string"
+            }
           },
           },
-          "additionalProperties": false
+          "additionalProperties": false,
+          "patternProperties": {"^x-": {}}
         },
         },
         "internal": {"type": "boolean"},
         "internal": {"type": "boolean"},
         "attachable": {"type": "boolean"},
         "attachable": {"type": "boolean"},
         "labels": {"$ref": "#/definitions/list_or_dict"}
         "labels": {"$ref": "#/definitions/list_or_dict"}
       },
       },
-      "additionalProperties": false
+      "additionalProperties": false,
+      "patternProperties": {"^x-": {}}
     },
     },
-
     "volume": {
     "volume": {
       "id": "#/definitions/volume",
       "id": "#/definitions/volume",
       "type": ["object", "null"],
       "type": ["object", "null"],
@@ -493,15 +654,19 @@
         "external": {
         "external": {
           "type": ["boolean", "object"],
           "type": ["boolean", "object"],
           "properties": {
           "properties": {
-            "name": {"type": "string"}
+            "name": {
+              "deprecated": true,
+              "type": "string"
+            }
           },
           },
-          "additionalProperties": false
+          "additionalProperties": false,
+          "patternProperties": {"^x-": {}}
         },
         },
         "labels": {"$ref": "#/definitions/list_or_dict"}
         "labels": {"$ref": "#/definitions/list_or_dict"}
       },
       },
-      "additionalProperties": false
+      "additionalProperties": false,
+      "patternProperties": {"^x-": {}}
     },
     },
-
     "secret": {
     "secret": {
       "id": "#/definitions/secret",
       "id": "#/definitions/secret",
       "type": "object",
       "type": "object",
@@ -514,11 +679,19 @@
             "name": {"type": "string"}
             "name": {"type": "string"}
           }
           }
         },
         },
-        "labels": {"$ref": "#/definitions/list_or_dict"}
+        "labels": {"$ref": "#/definitions/list_or_dict"},
+        "driver": {"type": "string"},
+        "driver_opts": {
+          "type": "object",
+          "patternProperties": {
+            "^.+$": {"type": ["string", "number"]}
+          }
+        },
+        "template_driver": {"type": "string"}
       },
       },
-      "additionalProperties": false
+      "additionalProperties": false,
+      "patternProperties": {"^x-": {}}
     },
     },
-
     "config": {
     "config": {
       "id": "#/definitions/config",
       "id": "#/definitions/config",
       "type": "object",
       "type": "object",
@@ -528,27 +701,29 @@
         "external": {
         "external": {
           "type": ["boolean", "object"],
           "type": ["boolean", "object"],
           "properties": {
           "properties": {
-            "name": {"type": "string"}
+            "name": {
+              "deprecated": true,
+              "type": "string"
+            }
           }
           }
         },
         },
-        "labels": {"$ref": "#/definitions/list_or_dict"}
+        "labels": {"$ref": "#/definitions/list_or_dict"},
+        "template_driver": {"type": "string"}
       },
       },
-      "additionalProperties": false
+      "additionalProperties": false,
+      "patternProperties": {"^x-": {}}
     },
     },
-
     "string_or_list": {
     "string_or_list": {
       "oneOf": [
       "oneOf": [
         {"type": "string"},
         {"type": "string"},
         {"$ref": "#/definitions/list_of_strings"}
         {"$ref": "#/definitions/list_of_strings"}
       ]
       ]
     },
     },
-
     "list_of_strings": {
     "list_of_strings": {
       "type": "array",
       "type": "array",
       "items": {"type": "string"},
       "items": {"type": "string"},
       "uniqueItems": true
       "uniqueItems": true
     },
     },
-
     "list_or_dict": {
     "list_or_dict": {
       "oneOf": [
       "oneOf": [
         {
         {
@@ -563,7 +738,22 @@
         {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
         {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
       ]
       ]
     },
     },
-
+    "blkio_limit": {
+      "type": "object",
+      "properties": {
+        "path": {"type": "string"},
+        "rate": {"type": ["integer", "string"]}
+      },
+      "additionalProperties": false
+    },
+    "blkio_weight": {
+      "type": "object",
+      "properties": {
+        "path": {"type": "string"},
+        "weight": {"type": "integer"}
+      },
+      "additionalProperties": false
+    },
     "constraints": {
     "constraints": {
       "service": {
       "service": {
         "id": "#/definitions/constraints/service",
         "id": "#/definitions/constraints/service",

+ 0 - 424
compose/config/config_schema_v2.0.json

@@ -1,424 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v2.0.json",
-  "type": "object",
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "patternProperties": {"^x-": {}},
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "blkio_config": {
-          "type": "object",
-          "properties": {
-            "device_read_bps": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_read_iops": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_write_bps": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_write_iops": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "weight": {"type": "integer"},
-            "weight_device": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_weight"}
-            }
-          },
-          "additionalProperties": false
-        },
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "container_name": {"type": "string"},
-        "cpu_shares": {"type": ["number", "string"]},
-        "cpu_quota": {"type": ["number", "string"]},
-        "cpuset": {"type": "string"},
-        "depends_on": {"$ref": "#/definitions/list_of_strings"},
-        "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_opt": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "extends": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "object",
-
-              "properties": {
-                "service": {"type": "string"},
-                "file": {"type": "string"}
-              },
-              "required": ["service"],
-              "additionalProperties": false
-            }
-          ]
-        },
-
-        "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "ipc": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {"type": "object"}
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "mem_limit": {"type": ["number", "string"]},
-        "mem_reservation": {"type": ["string", "integer"]},
-        "mem_swappiness": {"type": "integer"},
-        "memswap_limit": {"type": ["number", "string"]},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"},
-                        "priority": {"type": "number"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000},
-        "group_add": {
-            "type": "array",
-            "items": {
-                "type": ["string", "number"]
-            },
-            "uniqueItems": true
-        },
-        "pid": {"type": ["string", "null"]},
-
-        "ports": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "ports"
-          },
-          "uniqueItems": true
-        },
-
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "shm_size": {"type": ["number", "string"]},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "volumes": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "volume_driver": {"type": "string"},
-        "volumes_from": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "working_dir": {"type": "string"}
-      },
-
-      "dependencies": {
-        "memswap_limit": ["mem_limit"]
-      },
-      "additionalProperties": false
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": "object",
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-            "type": "object",
-            "properties": {
-                "driver": {"type": "string"},
-                "config": {
-                    "type": "array",
-                    "items": {"$ref": "#/definitions/ipam_config"}
-                },
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": "string"}
-                  },
-                  "additionalProperties": false
-                }
-            },
-            "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"}
-      },
-      "additionalProperties": false
-    },
-
-    "ipam_config": {
-      "id": "#/definitions/ipam_config",
-      "type": "object",
-      "properties": {
-        "subnet": {"type": "string"},
-        "ip_range": {"type": "string"},
-        "gateway": {"type": "string"},
-        "aux_addresses": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": "string"}
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "blkio_limit": {
-      "type": "object",
-      "properties": {
-        "path": {"type": "string"},
-        "rate": {"type": ["integer", "string"]}
-      },
-      "additionalProperties": false
-    },
-    "blkio_weight": {
-      "type": "object",
-      "properties": {
-        "path": {"type": "string"},
-        "weight": {"type": "integer"}
-      },
-      "additionalProperties": false
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 480
compose/config/config_schema_v2.1.json

@@ -1,480 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v2.1.json",
-  "type": "object",
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "patternProperties": {"^x-": {}},
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "blkio_config": {
-          "type": "object",
-          "properties": {
-            "device_read_bps": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_read_iops": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_write_bps": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_write_iops": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "weight": {"type": "integer"},
-            "weight_device": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_weight"}
-            }
-          },
-          "additionalProperties": false
-        },
-
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"},
-                "labels": {"$ref": "#/definitions/labels"},
-                "isolation": {"type": "string"}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "container_name": {"type": "string"},
-        "cpu_shares": {"type": ["number", "string"]},
-        "cpu_quota": {"type": ["number", "string"]},
-        "cpu_period": {"type": ["number", "string"]},
-        "cpuset": {"type": "string"},
-        "depends_on": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "additionalProperties": false,
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "type": "object",
-                  "additionalProperties": false,
-                  "properties": {
-                    "condition": {
-                      "type": "string",
-                      "enum": ["service_started", "service_healthy"]
-                    }
-                  },
-                  "required": ["condition"]
-                }
-              }
-            }
-          ]
-        },
-        "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "dns_opt": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "extends": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "object",
-
-              "properties": {
-                "service": {"type": "string"},
-                "file": {"type": "string"}
-              },
-              "required": ["service"],
-              "additionalProperties": false
-            }
-          ]
-        },
-
-        "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "healthcheck": {"$ref": "#/definitions/healthcheck"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "ipc": {"type": "string"},
-        "isolation": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {"type": "object"}
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "mem_limit": {"type": ["number", "string"]},
-        "mem_reservation": {"type": ["string", "integer"]},
-        "mem_swappiness": {"type": "integer"},
-        "memswap_limit": {"type": ["number", "string"]},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"},
-                        "link_local_ips": {"$ref": "#/definitions/list_of_strings"},
-                        "priority": {"type": "number"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "oom_kill_disable": {"type": "boolean"},
-        "oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000},
-        "group_add": {
-            "type": "array",
-            "items": {
-                "type": ["string", "number"]
-            },
-            "uniqueItems": true
-        },
-        "pid": {"type": ["string", "null"]},
-
-        "ports": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "ports"
-          },
-          "uniqueItems": true
-        },
-
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "shm_size": {"type": ["number", "string"]},
-        "sysctls": {"$ref": "#/definitions/list_or_dict"},
-        "pids_limit": {"type": ["number", "string"]},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "storage_opt": {"type": "object"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "userns_mode": {"type": "string"},
-        "volumes": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "volume_driver": {"type": "string"},
-        "volumes_from": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "working_dir": {"type": "string"}
-      },
-
-      "dependencies": {
-        "memswap_limit": ["mem_limit"]
-      },
-      "additionalProperties": false
-    },
-
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {"type": "boolean"},
-        "interval": {"type": "string"},
-        "retries": {"type": "number"},
-        "test": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "timeout": {"type": "string"}
-      }
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": "object",
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-            "type": "object",
-            "properties": {
-                "driver": {"type": "string"},
-                "config": {
-                    "type": "array",
-                    "items": {"$ref": "#/definitions/ipam_config"}
-                },
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": "string"}
-                  },
-                  "additionalProperties": false
-                }
-            },
-            "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"},
-        "enable_ipv6": {"type": "boolean"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "name": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "ipam_config": {
-      "id": "#/definitions/ipam_config",
-      "type": "object",
-      "properties": {
-        "subnet": {"type": "string"},
-        "ip_range": {"type": "string"},
-        "gateway": {"type": "string"},
-        "aux_addresses": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": "string"}
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "labels": {"$ref": "#/definitions/labels"},
-        "name": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "blkio_limit": {
-      "type": "object",
-      "properties": {
-        "path": {"type": "string"},
-        "rate": {"type": ["integer", "string"]}
-      },
-      "additionalProperties": false
-    },
-    "blkio_weight": {
-      "type": "object",
-      "properties": {
-        "path": {"type": "string"},
-        "weight": {"type": "integer"}
-      },
-      "additionalProperties": false
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 489
compose/config/config_schema_v2.2.json

@@ -1,489 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v2.2.json",
-  "type": "object",
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "patternProperties": {"^x-": {}},
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "blkio_config": {
-          "type": "object",
-          "properties": {
-            "device_read_bps": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_read_iops": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_write_bps": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_write_iops": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "weight": {"type": "integer"},
-            "weight_device": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_weight"}
-            }
-          },
-          "additionalProperties": false
-        },
-
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"},
-                "labels": {"$ref": "#/definitions/labels"},
-                "cache_from": {"$ref": "#/definitions/list_of_strings"},
-                "network": {"type": "string"},
-                "isolation": {"type": "string"}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "container_name": {"type": "string"},
-        "cpu_count": {"type": "integer", "minimum": 0},
-        "cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100},
-        "cpu_shares": {"type": ["number", "string"]},
-        "cpu_quota": {"type": ["number", "string"]},
-        "cpu_period": {"type": ["number", "string"]},
-        "cpu_rt_period": {"type": ["number", "string"]},
-        "cpu_rt_runtime": {"type": ["number", "string"]},
-        "cpus": {"type": "number", "minimum": 0},
-        "cpuset": {"type": "string"},
-        "depends_on": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "additionalProperties": false,
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "type": "object",
-                  "additionalProperties": false,
-                  "properties": {
-                    "condition": {
-                      "type": "string",
-                      "enum": ["service_started", "service_healthy"]
-                    }
-                  },
-                  "required": ["condition"]
-                }
-              }
-            }
-          ]
-        },
-        "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "dns_opt": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "extends": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "object",
-
-              "properties": {
-                "service": {"type": "string"},
-                "file": {"type": "string"}
-              },
-              "required": ["service"],
-              "additionalProperties": false
-            }
-          ]
-        },
-
-        "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "healthcheck": {"$ref": "#/definitions/healthcheck"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "init": {"type": ["boolean", "string"]},
-        "ipc": {"type": "string"},
-        "isolation": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {"type": "object"}
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "mem_limit": {"type": ["number", "string"]},
-        "mem_reservation": {"type": ["string", "integer"]},
-        "mem_swappiness": {"type": "integer"},
-        "memswap_limit": {"type": ["number", "string"]},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"},
-                        "link_local_ips": {"$ref": "#/definitions/list_of_strings"},
-                        "priority": {"type": "number"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "oom_kill_disable": {"type": "boolean"},
-        "oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000},
-        "group_add": {
-            "type": "array",
-            "items": {
-                "type": ["string", "number"]
-            },
-            "uniqueItems": true
-        },
-        "pid": {"type": ["string", "null"]},
-
-        "ports": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "ports"
-          },
-          "uniqueItems": true
-        },
-
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "scale": {"type": "integer"},
-        "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "shm_size": {"type": ["number", "string"]},
-        "sysctls": {"$ref": "#/definitions/list_or_dict"},
-        "pids_limit": {"type": ["number", "string"]},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "storage_opt": {"type": "object"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "userns_mode": {"type": "string"},
-        "volumes": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "volume_driver": {"type": "string"},
-        "volumes_from": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "working_dir": {"type": "string"}
-      },
-
-      "dependencies": {
-        "memswap_limit": ["mem_limit"]
-      },
-      "additionalProperties": false
-    },
-
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {"type": "boolean"},
-        "interval": {"type": "string"},
-        "retries": {"type": "number"},
-        "test": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "timeout": {"type": "string"}
-      }
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": "object",
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-            "type": "object",
-            "properties": {
-                "driver": {"type": "string"},
-                "config": {
-                    "type": "array",
-                    "items": {"$ref": "#/definitions/ipam_config"}
-                },
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": "string"}
-                  },
-                  "additionalProperties": false
-                }
-            },
-            "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"},
-        "enable_ipv6": {"type": "boolean"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "name": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "ipam_config": {
-      "id": "#/definitions/ipam_config",
-      "type": "object",
-      "properties": {
-        "subnet": {"type": "string"},
-        "ip_range": {"type": "string"},
-        "gateway": {"type": "string"},
-        "aux_addresses": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": "string"}
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "labels": {"$ref": "#/definitions/labels"},
-        "name": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "blkio_limit": {
-      "type": "object",
-      "properties": {
-        "path": {"type": "string"},
-        "rate": {"type": ["integer", "string"]}
-      },
-      "additionalProperties": false
-    },
-    "blkio_weight": {
-      "type": "object",
-      "properties": {
-        "path": {"type": "string"},
-        "weight": {"type": "integer"}
-      },
-      "additionalProperties": false
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 533
compose/config/config_schema_v2.3.json

@@ -1,533 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v2.3.json",
-  "type": "object",
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "patternProperties": {"^x-": {}},
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "blkio_config": {
-          "type": "object",
-          "properties": {
-            "device_read_bps": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_read_iops": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_write_bps": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_write_iops": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "weight": {"type": "integer"},
-            "weight_device": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_weight"}
-            }
-          },
-          "additionalProperties": false
-        },
-
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"},
-                "labels": {"$ref": "#/definitions/labels"},
-                "cache_from": {"$ref": "#/definitions/list_of_strings"},
-                "network": {"type": "string"},
-                "target": {"type": "string"},
-                "shm_size": {"type": ["integer", "string"]},
-                "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-                "isolation": {"type": "string"}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"$ref": "#/definitions/list_of_strings"},
-        "cap_drop": {"$ref": "#/definitions/list_of_strings"},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "container_name": {"type": "string"},
-        "cpu_count": {"type": "integer", "minimum": 0},
-        "cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100},
-        "cpu_shares": {"type": ["number", "string"]},
-        "cpu_quota": {"type": ["number", "string"]},
-        "cpu_period": {"type": ["number", "string"]},
-        "cpu_rt_period": {"type": ["number", "string"]},
-        "cpu_rt_runtime": {"type": ["number", "string"]},
-        "cpus": {"type": "number", "minimum": 0},
-        "cpuset": {"type": "string"},
-        "depends_on": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "additionalProperties": false,
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "type": "object",
-                  "additionalProperties": false,
-                  "properties": {
-                    "condition": {
-                      "type": "string",
-                      "enum": ["service_started", "service_healthy"]
-                    }
-                  },
-                  "required": ["condition"]
-                }
-              }
-            }
-          ]
-        },
-        "device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"},
-        "devices": {"$ref": "#/definitions/list_of_strings"},
-        "dns_opt": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "extends": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "object",
-
-              "properties": {
-                "service": {"type": "string"},
-                "file": {"type": "string"}
-              },
-              "required": ["service"],
-              "additionalProperties": false
-            }
-          ]
-        },
-
-        "external_links": {"$ref": "#/definitions/list_of_strings"},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "healthcheck": {"$ref": "#/definitions/healthcheck"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "init": {"type": ["boolean", "string"]},
-        "ipc": {"type": "string"},
-        "isolation": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"$ref": "#/definitions/list_of_strings"},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {"type": "object"}
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "mem_limit": {"type": ["number", "string"]},
-        "mem_reservation": {"type": ["string", "integer"]},
-        "mem_swappiness": {"type": "integer"},
-        "memswap_limit": {"type": ["number", "string"]},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"},
-                        "link_local_ips": {"$ref": "#/definitions/list_of_strings"},
-                        "priority": {"type": "number"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "oom_kill_disable": {"type": "boolean"},
-        "oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000},
-        "group_add": {
-            "type": "array",
-            "items": {
-                "type": ["string", "number"]
-            },
-            "uniqueItems": true
-        },
-        "pid": {"type": ["string", "null"]},
-
-        "ports": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "ports"
-          },
-          "uniqueItems": true
-        },
-
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "runtime": {"type": "string"},
-        "scale": {"type": "integer"},
-        "security_opt": {"$ref": "#/definitions/list_of_strings"},
-        "shm_size": {"type": ["number", "string"]},
-        "sysctls": {"$ref": "#/definitions/list_or_dict"},
-        "pids_limit": {"type": ["number", "string"]},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "storage_opt": {"type": "object"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "userns_mode": {"type": "string"},
-        "volumes": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "required": ["type"],
-                "additionalProperties": false,
-                "properties": {
-                  "type": {"type": "string"},
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "read_only": {"type": "boolean"},
-                  "consistency": {"type": "string"},
-                  "bind": {
-                    "type": "object",
-                    "properties": {
-                      "propagation": {"type": "string"}
-                    }
-                  },
-                  "volume": {
-                    "type": "object",
-                    "properties": {
-                      "nocopy": {"type": "boolean"}
-                    }
-                  },
-                  "tmpfs": {
-                    "type": "object",
-                    "properties": {
-                      "size": {"type": ["integer", "string"]}
-                    }
-                  }
-                }
-              }
-            ],
-            "uniqueItems": true
-          }
-        },
-        "volume_driver": {"type": "string"},
-        "volumes_from": {"$ref": "#/definitions/list_of_strings"},
-        "working_dir": {"type": "string"}
-      },
-
-      "dependencies": {
-        "memswap_limit": ["mem_limit"]
-      },
-      "additionalProperties": false
-    },
-
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {"type": "boolean"},
-        "interval": {"type": "string"},
-        "retries": {"type": "number"},
-        "start_period": {"type": "string"},
-        "test": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "timeout": {"type": "string"}
-      }
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": "object",
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-            "type": "object",
-            "properties": {
-                "driver": {"type": "string"},
-                "config": {
-                    "type": "array",
-                    "items": {"$ref": "#/definitions/ipam_config"}
-                },
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": "string"}
-                  },
-                  "additionalProperties": false
-                }
-            },
-            "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"},
-        "enable_ipv6": {"type": "boolean"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "name": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "ipam_config": {
-      "id": "#/definitions/ipam_config",
-      "type": "object",
-      "properties": {
-        "subnet": {"type": "string"},
-        "ip_range": {"type": "string"},
-        "gateway": {"type": "string"},
-        "aux_addresses": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": "string"}
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "labels": {"$ref": "#/definitions/labels"},
-        "name": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "blkio_limit": {
-      "type": "object",
-      "properties": {
-        "path": {"type": "string"},
-        "rate": {"type": ["integer", "string"]}
-      },
-      "additionalProperties": false
-    },
-    "blkio_weight": {
-      "type": "object",
-      "properties": {
-        "path": {"type": "string"},
-        "weight": {"type": "integer"}
-      },
-      "additionalProperties": false
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 535
compose/config/config_schema_v2.4.json

@@ -1,535 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v2.4.json",
-  "type": "object",
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "patternProperties": {"^x-": {}},
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "blkio_config": {
-          "type": "object",
-          "properties": {
-            "device_read_bps": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_read_iops": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_write_bps": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "device_write_iops": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_limit"}
-            },
-            "weight": {"type": "integer"},
-            "weight_device": {
-              "type": "array",
-              "items": {"$ref": "#/definitions/blkio_weight"}
-            }
-          },
-          "additionalProperties": false
-        },
-
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"},
-                "labels": {"$ref": "#/definitions/labels"},
-                "cache_from": {"$ref": "#/definitions/list_of_strings"},
-                "network": {"type": "string"},
-                "target": {"type": "string"},
-                "shm_size": {"type": ["integer", "string"]},
-                "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-                "isolation": {"type": "string"}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"$ref": "#/definitions/list_of_strings"},
-        "cap_drop": {"$ref": "#/definitions/list_of_strings"},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "container_name": {"type": "string"},
-        "cpu_count": {"type": "integer", "minimum": 0},
-        "cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100},
-        "cpu_shares": {"type": ["number", "string"]},
-        "cpu_quota": {"type": ["number", "string"]},
-        "cpu_period": {"type": ["number", "string"]},
-        "cpu_rt_period": {"type": ["number", "string"]},
-        "cpu_rt_runtime": {"type": ["number", "string"]},
-        "cpus": {"type": "number", "minimum": 0},
-        "cpuset": {"type": "string"},
-        "depends_on": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "additionalProperties": false,
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "type": "object",
-                  "additionalProperties": false,
-                  "properties": {
-                    "condition": {
-                      "type": "string",
-                      "enum": ["service_started", "service_healthy"]
-                    }
-                  },
-                  "required": ["condition"]
-                }
-              }
-            }
-          ]
-        },
-        "device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"},
-        "devices": {"$ref": "#/definitions/list_of_strings"},
-        "dns_opt": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "extends": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "object",
-
-              "properties": {
-                "service": {"type": "string"},
-                "file": {"type": "string"}
-              },
-              "required": ["service"],
-              "additionalProperties": false
-            }
-          ]
-        },
-
-        "external_links": {"$ref": "#/definitions/list_of_strings"},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "group_add": {
-            "type": "array",
-            "items": {
-                "type": ["string", "number"]
-            },
-            "uniqueItems": true
-        },
-        "healthcheck": {"$ref": "#/definitions/healthcheck"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "init": {"type": ["boolean", "string"]},
-        "ipc": {"type": "string"},
-        "isolation": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"$ref": "#/definitions/list_of_strings"},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {"type": "object"}
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "mem_limit": {"type": ["number", "string"]},
-        "mem_reservation": {"type": ["string", "integer"]},
-        "mem_swappiness": {"type": "integer"},
-        "memswap_limit": {"type": ["number", "string"]},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"},
-                        "link_local_ips": {"$ref": "#/definitions/list_of_strings"},
-                        "priority": {"type": "number"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "oom_kill_disable": {"type": "boolean"},
-        "oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000},
-        "pid": {"type": ["string", "null"]},
-        "platform": {"type": "string"},
-        "ports": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "ports"
-          },
-          "uniqueItems": true
-        },
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "runtime": {"type": "string"},
-        "scale": {"type": "integer"},
-        "security_opt": {"$ref": "#/definitions/list_of_strings"},
-        "shm_size": {"type": ["number", "string"]},
-        "sysctls": {"$ref": "#/definitions/list_or_dict"},
-        "pids_limit": {"type": ["number", "string"]},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "storage_opt": {"type": "object"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "userns_mode": {"type": "string"},
-        "volumes": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "required": ["type"],
-                "additionalProperties": false,
-                "properties": {
-                  "type": {"type": "string"},
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "read_only": {"type": "boolean"},
-                  "consistency": {"type": "string"},
-                  "bind": {
-                    "type": "object",
-                    "properties": {
-                      "propagation": {"type": "string"}
-                    }
-                  },
-                  "volume": {
-                    "type": "object",
-                    "properties": {
-                      "nocopy": {"type": "boolean"}
-                    }
-                  },
-                  "tmpfs": {
-                    "type": "object",
-                    "properties": {
-                      "size": {"type": ["integer", "string"]}
-                    }
-                  }
-                }
-              }
-            ],
-            "uniqueItems": true
-          }
-        },
-        "volume_driver": {"type": "string"},
-        "volumes_from": {"$ref": "#/definitions/list_of_strings"},
-        "working_dir": {"type": "string"}
-      },
-
-      "dependencies": {
-        "memswap_limit": ["mem_limit"]
-      },
-      "patternProperties": {"^x-": {}},
-      "additionalProperties": false
-    },
-
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {"type": "boolean"},
-        "interval": {"type": "string"},
-        "retries": {"type": "number"},
-        "start_period": {"type": "string"},
-        "test": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "timeout": {"type": "string"}
-      }
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": "object",
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-            "type": "object",
-            "properties": {
-                "driver": {"type": "string"},
-                "config": {
-                    "type": "array",
-                    "items": {"$ref": "#/definitions/ipam_config"}
-                },
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": "string"}
-                  },
-                  "additionalProperties": false
-                }
-            },
-            "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"},
-        "enable_ipv6": {"type": "boolean"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "name": {"type": "string"}
-      },
-      "patternProperties": {"^x-": {}},
-      "additionalProperties": false
-    },
-
-    "ipam_config": {
-      "id": "#/definitions/ipam_config",
-      "type": "object",
-      "properties": {
-        "subnet": {"type": "string"},
-        "ip_range": {"type": "string"},
-        "gateway": {"type": "string"},
-        "aux_addresses": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": "string"}
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "labels": {"$ref": "#/definitions/labels"},
-        "name": {"type": "string"}
-      },
-      "patternProperties": {"^x-": {}},
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "blkio_limit": {
-      "type": "object",
-      "properties": {
-        "path": {"type": "string"},
-        "rate": {"type": ["integer", "string"]}
-      },
-      "additionalProperties": false
-    },
-    "blkio_weight": {
-      "type": "object",
-      "properties": {
-        "path": {"type": "string"},
-        "weight": {"type": "integer"}
-      },
-      "additionalProperties": false
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 399
compose/config/config_schema_v3.0.json

@@ -1,399 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v3.0.json",
-  "type": "object",
-  "required": ["version"],
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "deploy": {"$ref": "#/definitions/deployment"},
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "container_name": {"type": "string"},
-        "depends_on": {"$ref": "#/definitions/list_of_strings"},
-        "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "healthcheck": {"$ref": "#/definitions/healthcheck"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "ipc": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": ["string", "number", "null"]}
-                  }
-                }
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "pid": {"type": ["string", "null"]},
-
-        "ports": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "ports"
-          },
-          "uniqueItems": true
-        },
-
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "shm_size": {"type": ["number", "string"]},
-        "sysctls": {"$ref": "#/definitions/list_or_dict"},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "userns_mode": {"type": "string"},
-        "volumes": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "working_dir": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {"type": "boolean"},
-        "interval": {"type": "string"},
-        "retries": {"type": "number"},
-        "test": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "timeout": {"type": "string"}
-      }
-    },
-    "deployment": {
-      "id": "#/definitions/deployment",
-      "type": ["object", "null"],
-      "properties": {
-        "mode": {"type": "string"},
-        "replicas": {"type": "integer"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "update_config": {
-          "type": "object",
-          "properties": {
-            "parallelism": {"type": "integer"},
-            "delay": {"type": "string", "format": "duration"},
-            "failure_action": {"type": "string"},
-            "monitor": {"type": "string", "format": "duration"},
-            "max_failure_ratio": {"type": "number"}
-          },
-          "additionalProperties": false
-        },
-        "resources": {
-          "type": "object",
-          "properties": {
-            "limits": {"$ref": "#/definitions/resource"},
-            "reservations": {"$ref": "#/definitions/resource"}
-          },
-          "additionalProperties": false
-        },
-        "restart_policy": {
-          "type": "object",
-          "properties": {
-            "condition": {"type": "string"},
-            "delay": {"type": "string", "format": "duration"},
-            "max_attempts": {"type": "integer"},
-            "window": {"type": "string", "format": "duration"}
-          },
-          "additionalProperties": false
-        },
-        "placement": {
-          "type": "object",
-          "properties": {
-            "constraints": {"type": "array", "items": {"type": "string"}}
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "resource": {
-      "id": "#/definitions/resource",
-      "type": "object",
-      "properties": {
-        "cpus": {"type": "string"},
-        "memory": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-          "type": "object",
-          "properties": {
-            "driver": {"type": "string"},
-            "config": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "subnet": {"type": "string", "format": "subnet_ip_address"}
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"},
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 444
compose/config/config_schema_v3.1.json

@@ -1,444 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v3.1.json",
-  "type": "object",
-  "required": ["version"],
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "secrets": {
-      "id": "#/properties/secrets",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/secret"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "deploy": {"$ref": "#/definitions/deployment"},
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "container_name": {"type": "string"},
-        "depends_on": {"$ref": "#/definitions/list_of_strings"},
-        "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "healthcheck": {"$ref": "#/definitions/healthcheck"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "ipc": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": ["string", "number", "null"]}
-                  }
-                }
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "pid": {"type": ["string", "null"]},
-
-        "ports": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "ports"
-          },
-          "uniqueItems": true
-        },
-
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "shm_size": {"type": ["number", "string"]},
-        "secrets": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "properties": {
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "uid": {"type": "string"},
-                  "gid": {"type": "string"},
-                  "mode": {"type": "number"}
-                }
-              }
-            ]
-          }
-        },
-        "sysctls": {"$ref": "#/definitions/list_or_dict"},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "userns_mode": {"type": "string"},
-        "volumes": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "working_dir": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {"type": "boolean"},
-        "interval": {"type": "string"},
-        "retries": {"type": "number"},
-        "test": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "timeout": {"type": "string"}
-      }
-    },
-    "deployment": {
-      "id": "#/definitions/deployment",
-      "type": ["object", "null"],
-      "properties": {
-        "mode": {"type": "string"},
-        "replicas": {"type": "integer"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "update_config": {
-          "type": "object",
-          "properties": {
-            "parallelism": {"type": "integer"},
-            "delay": {"type": "string", "format": "duration"},
-            "failure_action": {"type": "string"},
-            "monitor": {"type": "string", "format": "duration"},
-            "max_failure_ratio": {"type": "number"}
-          },
-          "additionalProperties": false
-        },
-        "resources": {
-          "type": "object",
-          "properties": {
-            "limits": {"$ref": "#/definitions/resource"},
-            "reservations": {"$ref": "#/definitions/resource"}
-          },
-          "additionalProperties": false
-        },
-        "restart_policy": {
-          "type": "object",
-          "properties": {
-            "condition": {"type": "string"},
-            "delay": {"type": "string", "format": "duration"},
-            "max_attempts": {"type": "integer"},
-            "window": {"type": "string", "format": "duration"}
-          },
-          "additionalProperties": false
-        },
-        "placement": {
-          "type": "object",
-          "properties": {
-            "constraints": {"type": "array", "items": {"type": "string"}}
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "resource": {
-      "id": "#/definitions/resource",
-      "type": "object",
-      "properties": {
-        "cpus": {"type": "string"},
-        "memory": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-          "type": "object",
-          "properties": {
-            "driver": {"type": "string"},
-            "config": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "subnet": {"type": "string", "format": "subnet_ip_address"}
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"},
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "secret": {
-      "id": "#/definitions/secret",
-      "type": "object",
-      "properties": {
-        "file": {"type": "string"},
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          }
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 492
compose/config/config_schema_v3.2.json

@@ -1,492 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v3.2.json",
-  "type": "object",
-  "required": ["version"],
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "secrets": {
-      "id": "#/properties/secrets",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/secret"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "deploy": {"$ref": "#/definitions/deployment"},
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"},
-                "labels": {"$ref": "#/definitions/list_or_dict"},
-                "cache_from": {"$ref": "#/definitions/list_of_strings"}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "container_name": {"type": "string"},
-        "depends_on": {"$ref": "#/definitions/list_of_strings"},
-        "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "healthcheck": {"$ref": "#/definitions/healthcheck"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "ipc": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": ["string", "number", "null"]}
-                  }
-                }
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "pid": {"type": ["string", "null"]},
-
-        "ports": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "number", "format": "ports"},
-              {"type": "string", "format": "ports"},
-              {
-                "type": "object",
-                "properties": {
-                  "mode": {"type": "string"},
-                  "target": {"type": "integer"},
-                  "published": {"type": "integer"},
-                  "protocol": {"type": "string"}
-                },
-                "additionalProperties": false
-              }
-            ]
-          },
-          "uniqueItems": true
-        },
-
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "shm_size": {"type": ["number", "string"]},
-        "secrets": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "properties": {
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "uid": {"type": "string"},
-                  "gid": {"type": "string"},
-                  "mode": {"type": "number"}
-                }
-              }
-            ]
-          }
-        },
-        "sysctls": {"$ref": "#/definitions/list_or_dict"},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "userns_mode": {"type": "string"},
-        "volumes": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "required": ["type"],
-                "additionalProperties": false,
-                "properties": {
-                  "type": {"type": "string"},
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "read_only": {"type": "boolean"},
-                  "consistency": {"type": "string"},
-                  "bind": {
-                    "type": "object",
-                    "properties": {
-                      "propagation": {"type": "string"}
-                    }
-                  },
-                  "volume": {
-                    "type": "object",
-                    "properties": {
-                      "nocopy": {"type": "boolean"}
-                    }
-                  }
-                }
-              }
-            ],
-            "uniqueItems": true
-          }
-        },
-        "working_dir": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {"type": "boolean"},
-        "interval": {"type": "string"},
-        "retries": {"type": "number"},
-        "test": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "timeout": {"type": "string"}
-      }
-    },
-    "deployment": {
-      "id": "#/definitions/deployment",
-      "type": ["object", "null"],
-      "properties": {
-        "mode": {"type": "string"},
-        "endpoint_mode": {"type": "string"},
-        "replicas": {"type": "integer"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "update_config": {
-          "type": "object",
-          "properties": {
-            "parallelism": {"type": "integer"},
-            "delay": {"type": "string", "format": "duration"},
-            "failure_action": {"type": "string"},
-            "monitor": {"type": "string", "format": "duration"},
-            "max_failure_ratio": {"type": "number"}
-          },
-          "additionalProperties": false
-        },
-        "resources": {
-          "type": "object",
-          "properties": {
-            "limits": {"$ref": "#/definitions/resource"},
-            "reservations": {"$ref": "#/definitions/resource"}
-          },
-          "additionalProperties": false
-        },
-        "restart_policy": {
-          "type": "object",
-          "properties": {
-            "condition": {"type": "string"},
-            "delay": {"type": "string", "format": "duration"},
-            "max_attempts": {"type": "integer"},
-            "window": {"type": "string", "format": "duration"}
-          },
-          "additionalProperties": false
-        },
-        "placement": {
-          "type": "object",
-          "properties": {
-            "constraints": {"type": "array", "items": {"type": "string"}}
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "resource": {
-      "id": "#/definitions/resource",
-      "type": "object",
-      "properties": {
-        "cpus": {"type": "string"},
-        "memory": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-          "type": "object",
-          "properties": {
-            "driver": {"type": "string"},
-            "config": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "subnet": {"type": "string", "format": "subnet_ip_address"}
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"},
-        "attachable": {"type": "boolean"},
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "secret": {
-      "id": "#/definitions/secret",
-      "type": "object",
-      "properties": {
-        "file": {"type": "string"},
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          }
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 551
compose/config/config_schema_v3.3.json

@@ -1,551 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v3.3.json",
-  "type": "object",
-  "required": ["version"],
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "secrets": {
-      "id": "#/properties/secrets",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/secret"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "configs": {
-      "id": "#/properties/configs",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/config"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "deploy": {"$ref": "#/definitions/deployment"},
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"},
-                "labels": {"$ref": "#/definitions/labels"},
-                "cache_from": {"$ref": "#/definitions/list_of_strings"}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "configs": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "properties": {
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "uid": {"type": "string"},
-                  "gid": {"type": "string"},
-                  "mode": {"type": "number"}
-                }
-              }
-            ]
-          }
-        },
-        "container_name": {"type": "string"},
-        "credential_spec": {"type": "object", "properties": {
-          "file": {"type": "string"},
-          "registry": {"type": "string"}
-        }},
-        "depends_on": {"$ref": "#/definitions/list_of_strings"},
-        "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "healthcheck": {"$ref": "#/definitions/healthcheck"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "ipc": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": ["string", "number", "null"]}
-                  }
-                }
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "pid": {"type": ["string", "null"]},
-
-        "ports": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "number", "format": "ports"},
-              {"type": "string", "format": "ports"},
-              {
-                "type": "object",
-                "properties": {
-                  "mode": {"type": "string"},
-                  "target": {"type": "integer"},
-                  "published": {"type": "integer"},
-                  "protocol": {"type": "string"}
-                },
-                "additionalProperties": false
-              }
-            ]
-          },
-          "uniqueItems": true
-        },
-
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "shm_size": {"type": ["number", "string"]},
-        "secrets": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "properties": {
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "uid": {"type": "string"},
-                  "gid": {"type": "string"},
-                  "mode": {"type": "number"}
-                }
-              }
-            ]
-          }
-        },
-        "sysctls": {"$ref": "#/definitions/list_or_dict"},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "userns_mode": {"type": "string"},
-        "volumes": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "required": ["type"],
-                "additionalProperties": false,
-                "properties": {
-                  "type": {"type": "string"},
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "read_only": {"type": "boolean"},
-                  "consistency": {"type": "string"},
-                  "bind": {
-                    "type": "object",
-                    "properties": {
-                      "propagation": {"type": "string"}
-                    }
-                  },
-                  "volume": {
-                    "type": "object",
-                    "properties": {
-                      "nocopy": {"type": "boolean"}
-                    }
-                  }
-                }
-              }
-            ],
-            "uniqueItems": true
-          }
-        },
-        "working_dir": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {"type": "boolean"},
-        "interval": {"type": "string"},
-        "retries": {"type": "number"},
-        "test": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "timeout": {"type": "string"}
-      }
-    },
-    "deployment": {
-      "id": "#/definitions/deployment",
-      "type": ["object", "null"],
-      "properties": {
-        "mode": {"type": "string"},
-        "endpoint_mode": {"type": "string"},
-        "replicas": {"type": "integer"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "update_config": {
-          "type": "object",
-          "properties": {
-            "parallelism": {"type": "integer"},
-            "delay": {"type": "string", "format": "duration"},
-            "failure_action": {"type": "string"},
-            "monitor": {"type": "string", "format": "duration"},
-            "max_failure_ratio": {"type": "number"}
-          },
-          "additionalProperties": false
-        },
-        "resources": {
-          "type": "object",
-          "properties": {
-            "limits": {"$ref": "#/definitions/resource"},
-            "reservations": {"$ref": "#/definitions/resource"}
-          },
-          "additionalProperties": false
-        },
-        "restart_policy": {
-          "type": "object",
-          "properties": {
-            "condition": {"type": "string"},
-            "delay": {"type": "string", "format": "duration"},
-            "max_attempts": {"type": "integer"},
-            "window": {"type": "string", "format": "duration"}
-          },
-          "additionalProperties": false
-        },
-        "placement": {
-          "type": "object",
-          "properties": {
-            "constraints": {"type": "array", "items": {"type": "string"}},
-            "preferences": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "spread": {"type": "string"}
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "resource": {
-      "id": "#/definitions/resource",
-      "type": "object",
-      "properties": {
-        "cpus": {"type": "string"},
-        "memory": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-          "type": "object",
-          "properties": {
-            "driver": {"type": "string"},
-            "config": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "subnet": {"type": "string", "format": "subnet_ip_address"}
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"},
-        "attachable": {"type": "boolean"},
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "secret": {
-      "id": "#/definitions/secret",
-      "type": "object",
-      "properties": {
-        "file": {"type": "string"},
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          }
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "config": {
-      "id": "#/definitions/config",
-      "type": "object",
-      "properties": {
-        "file": {"type": "string"},
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          }
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 560
compose/config/config_schema_v3.4.json

@@ -1,560 +0,0 @@
-
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v3.4.json",
-  "type": "object",
-  "required": ["version"],
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "secrets": {
-      "id": "#/properties/secrets",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/secret"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "configs": {
-      "id": "#/properties/configs",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/config"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "patternProperties": {"^x-": {}},
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "deploy": {"$ref": "#/definitions/deployment"},
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"},
-                "labels": {"$ref": "#/definitions/labels"},
-                "cache_from": {"$ref": "#/definitions/list_of_strings"},
-                "network": {"type": "string"},
-                "target": {"type": "string"}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "configs": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "properties": {
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "uid": {"type": "string"},
-                  "gid": {"type": "string"},
-                  "mode": {"type": "number"}
-                }
-              }
-            ]
-          }
-        },
-        "container_name": {"type": "string"},
-        "credential_spec": {"type": "object", "properties": {
-          "file": {"type": "string"},
-          "registry": {"type": "string"}
-        }},
-        "depends_on": {"$ref": "#/definitions/list_of_strings"},
-        "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "healthcheck": {"$ref": "#/definitions/healthcheck"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "ipc": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": ["string", "number", "null"]}
-                  }
-                }
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "pid": {"type": ["string", "null"]},
-
-        "ports": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "number", "format": "ports"},
-              {"type": "string", "format": "ports"},
-              {
-                "type": "object",
-                "properties": {
-                  "mode": {"type": "string"},
-                  "target": {"type": "integer"},
-                  "published": {"type": "integer"},
-                  "protocol": {"type": "string"}
-                },
-                "additionalProperties": false
-              }
-            ]
-          },
-          "uniqueItems": true
-        },
-
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "shm_size": {"type": ["number", "string"]},
-        "secrets": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "properties": {
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "uid": {"type": "string"},
-                  "gid": {"type": "string"},
-                  "mode": {"type": "number"}
-                }
-              }
-            ]
-          }
-        },
-        "sysctls": {"$ref": "#/definitions/list_or_dict"},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "userns_mode": {"type": "string"},
-        "volumes": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "required": ["type"],
-                "additionalProperties": false,
-                "properties": {
-                  "type": {"type": "string"},
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "read_only": {"type": "boolean"},
-                  "consistency": {"type": "string"},
-                  "bind": {
-                    "type": "object",
-                    "properties": {
-                      "propagation": {"type": "string"}
-                    }
-                  },
-                  "volume": {
-                    "type": "object",
-                    "properties": {
-                      "nocopy": {"type": "boolean"}
-                    }
-                  }
-                }
-              }
-            ],
-            "uniqueItems": true
-          }
-        },
-        "working_dir": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {"type": "boolean"},
-        "interval": {"type": "string", "format": "duration"},
-        "retries": {"type": "number"},
-        "test": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "timeout": {"type": "string", "format": "duration"},
-        "start_period": {"type": "string", "format": "duration"}
-      }
-    },
-    "deployment": {
-      "id": "#/definitions/deployment",
-      "type": ["object", "null"],
-      "properties": {
-        "mode": {"type": "string"},
-        "endpoint_mode": {"type": "string"},
-        "replicas": {"type": "integer"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "update_config": {
-          "type": "object",
-          "properties": {
-            "parallelism": {"type": "integer"},
-            "delay": {"type": "string", "format": "duration"},
-            "failure_action": {"type": "string"},
-            "monitor": {"type": "string", "format": "duration"},
-            "max_failure_ratio": {"type": "number"},
-            "order": {"type": "string", "enum": [
-              "start-first", "stop-first"
-            ]}
-          },
-          "additionalProperties": false
-        },
-        "resources": {
-          "type": "object",
-          "properties": {
-            "limits": {"$ref": "#/definitions/resource"},
-            "reservations": {"$ref": "#/definitions/resource"}
-          },
-          "additionalProperties": false
-        },
-        "restart_policy": {
-          "type": "object",
-          "properties": {
-            "condition": {"type": "string"},
-            "delay": {"type": "string", "format": "duration"},
-            "max_attempts": {"type": "integer"},
-            "window": {"type": "string", "format": "duration"}
-          },
-          "additionalProperties": false
-        },
-        "placement": {
-          "type": "object",
-          "properties": {
-            "constraints": {"type": "array", "items": {"type": "string"}},
-            "preferences": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "spread": {"type": "string"}
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "resource": {
-      "id": "#/definitions/resource",
-      "type": "object",
-      "properties": {
-        "cpus": {"type": "string"},
-        "memory": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": ["object", "null"],
-      "properties": {
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-          "type": "object",
-          "properties": {
-            "driver": {"type": "string"},
-            "config": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "subnet": {"type": "string", "format": "subnet_ip_address"}
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"},
-        "attachable": {"type": "boolean"},
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "name": {"type": "string"},
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "secret": {
-      "id": "#/definitions/secret",
-      "type": "object",
-      "properties": {
-        "file": {"type": "string"},
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          }
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "config": {
-      "id": "#/definitions/config",
-      "type": "object",
-      "properties": {
-        "file": {"type": "string"},
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          }
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 588
compose/config/config_schema_v3.5.json

@@ -1,588 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v3.5.json",
-  "type": "object",
-  "required": ["version"],
-
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "secrets": {
-      "id": "#/properties/secrets",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/secret"
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "configs": {
-      "id": "#/properties/configs",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/config"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-
-  "patternProperties": {"^x-": {}},
-  "additionalProperties": false,
-
-  "definitions": {
-
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-
-      "properties": {
-        "deploy": {"$ref": "#/definitions/deployment"},
-        "build": {
-          "oneOf": [
-            {"type": "string"},
-            {
-              "type": "object",
-              "properties": {
-                "context": {"type": "string"},
-                "dockerfile": {"type": "string"},
-                "args": {"$ref": "#/definitions/list_or_dict"},
-                "labels": {"$ref": "#/definitions/labels"},
-                "cache_from": {"$ref": "#/definitions/list_of_strings"},
-                "network": {"type": "string"},
-                "target": {"type": "string"},
-                "shm_size": {"type": ["integer", "string"]}
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "cgroup_parent": {"type": "string"},
-        "command": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "configs": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "properties": {
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "uid": {"type": "string"},
-                  "gid": {"type": "string"},
-                  "mode": {"type": "number"}
-                }
-              }
-            ]
-          }
-        },
-        "container_name": {"type": "string"},
-        "credential_spec": {"type": "object", "properties": {
-          "file": {"type": "string"},
-          "registry": {"type": "string"}
-        }},
-        "depends_on": {"$ref": "#/definitions/list_of_strings"},
-        "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "dns": {"$ref": "#/definitions/string_or_list"},
-        "dns_search": {"$ref": "#/definitions/string_or_list"},
-        "domainname": {"type": "string"},
-        "entrypoint": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "env_file": {"$ref": "#/definitions/string_or_list"},
-        "environment": {"$ref": "#/definitions/list_or_dict"},
-
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": ["string", "number"],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-
-        "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
-        "healthcheck": {"$ref": "#/definitions/healthcheck"},
-        "hostname": {"type": "string"},
-        "image": {"type": "string"},
-        "ipc": {"type": "string"},
-        "isolation": {"type": "string"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-
-        "logging": {
-            "type": "object",
-
-            "properties": {
-                "driver": {"type": "string"},
-                "options": {
-                  "type": "object",
-                  "patternProperties": {
-                    "^.+$": {"type": ["string", "number", "null"]}
-                  }
-                }
-            },
-            "additionalProperties": false
-        },
-
-        "mac_address": {"type": "string"},
-        "network_mode": {"type": "string"},
-
-        "networks": {
-          "oneOf": [
-            {"$ref": "#/definitions/list_of_strings"},
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {"$ref": "#/definitions/list_of_strings"},
-                        "ipv4_address": {"type": "string"},
-                        "ipv6_address": {"type": "string"}
-                      },
-                      "additionalProperties": false
-                    },
-                    {"type": "null"}
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "pid": {"type": ["string", "null"]},
-
-        "ports": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "number", "format": "ports"},
-              {"type": "string", "format": "ports"},
-              {
-                "type": "object",
-                "properties": {
-                  "mode": {"type": "string"},
-                  "target": {"type": "integer"},
-                  "published": {"type": "integer"},
-                  "protocol": {"type": "string"}
-                },
-                "additionalProperties": false
-              }
-            ]
-          },
-          "uniqueItems": true
-        },
-
-        "privileged": {"type": "boolean"},
-        "read_only": {"type": "boolean"},
-        "restart": {"type": "string"},
-        "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
-        "shm_size": {"type": ["number", "string"]},
-        "secrets": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "properties": {
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "uid": {"type": "string"},
-                  "gid": {"type": "string"},
-                  "mode": {"type": "number"}
-                }
-              }
-            ]
-          }
-        },
-        "sysctls": {"$ref": "#/definitions/list_or_dict"},
-        "stdin_open": {"type": "boolean"},
-        "stop_grace_period": {"type": "string", "format": "duration"},
-        "stop_signal": {"type": "string"},
-        "tmpfs": {"$ref": "#/definitions/string_or_list"},
-        "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type":"object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {"type": "string"},
-        "userns_mode": {"type": "string"},
-        "volumes": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {"type": "string"},
-              {
-                "type": "object",
-                "required": ["type"],
-                "properties": {
-                  "type": {"type": "string"},
-                  "source": {"type": "string"},
-                  "target": {"type": "string"},
-                  "read_only": {"type": "boolean"},
-                  "consistency": {"type": "string"},
-                  "bind": {
-                    "type": "object",
-                    "properties": {
-                      "propagation": {"type": "string"}
-                    }
-                  },
-                  "volume": {
-                    "type": "object",
-                    "properties": {
-                      "nocopy": {"type": "boolean"}
-                    }
-                  }
-                },
-                "additionalProperties": false
-              }
-            ],
-            "uniqueItems": true
-          }
-        },
-        "working_dir": {"type": "string"}
-      },
-      "additionalProperties": false
-    },
-
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {"type": "boolean"},
-        "interval": {"type": "string", "format": "duration"},
-        "retries": {"type": "number"},
-        "test": {
-          "oneOf": [
-            {"type": "string"},
-            {"type": "array", "items": {"type": "string"}}
-          ]
-        },
-        "timeout": {"type": "string", "format": "duration"},
-        "start_period": {"type": "string", "format": "duration"}
-      }
-    },
-    "deployment": {
-      "id": "#/definitions/deployment",
-      "type": ["object", "null"],
-      "properties": {
-        "mode": {"type": "string"},
-        "endpoint_mode": {"type": "string"},
-        "replicas": {"type": "integer"},
-        "labels": {"$ref": "#/definitions/labels"},
-        "update_config": {
-          "type": "object",
-          "properties": {
-            "parallelism": {"type": "integer"},
-            "delay": {"type": "string", "format": "duration"},
-            "failure_action": {"type": "string"},
-            "monitor": {"type": "string", "format": "duration"},
-            "max_failure_ratio": {"type": "number"},
-            "order": {"type": "string", "enum": [
-              "start-first", "stop-first"
-            ]}
-          },
-          "additionalProperties": false
-        },
-        "resources": {
-          "type": "object",
-          "properties": {
-            "limits": {
-              "type": "object",
-              "properties": {
-                "cpus": {"type": "string"},
-                "memory": {"type": "string"}
-              },
-              "additionalProperties": false
-            },
-            "reservations": {
-              "type": "object",
-              "properties": {
-                "cpus": {"type": "string"},
-                "memory": {"type": "string"},
-                "generic_resources": {"$ref": "#/definitions/generic_resources"}
-              },
-              "additionalProperties": false
-            }
-          },
-          "additionalProperties": false
-        },
-        "restart_policy": {
-          "type": "object",
-          "properties": {
-            "condition": {"type": "string"},
-            "delay": {"type": "string", "format": "duration"},
-            "max_attempts": {"type": "integer"},
-            "window": {"type": "string", "format": "duration"}
-          },
-          "additionalProperties": false
-        },
-        "placement": {
-          "type": "object",
-          "properties": {
-            "constraints": {"type": "array", "items": {"type": "string"}},
-            "preferences": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "spread": {"type": "string"}
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-
-    "generic_resources": {
-      "id": "#/definitions/generic_resources",
-      "type": "array",
-      "items": {
-        "type": "object",
-        "properties": {
-          "discrete_resource_spec": {
-            "type": "object",
-            "properties": {
-              "kind": {"type": "string"},
-              "value": {"type": "number"}
-            },
-            "additionalProperties": false
-          }
-        },
-        "additionalProperties": false
-      }
-    },
-
-    "network": {
-      "id": "#/definitions/network",
-      "type": ["object", "null"],
-      "properties": {
-        "name": {"type": "string"},
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "ipam": {
-          "type": "object",
-          "properties": {
-            "driver": {"type": "string"},
-            "config": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "subnet": {"type": "string", "format": "subnet_ip_address"}
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "internal": {"type": "boolean"},
-        "attachable": {"type": "boolean"},
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": ["object", "null"],
-      "properties": {
-        "name": {"type": "string"},
-        "driver": {"type": "string"},
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {"type": ["string", "number"]}
-          }
-        },
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          },
-          "additionalProperties": false
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "secret": {
-      "id": "#/definitions/secret",
-      "type": "object",
-      "properties": {
-        "name": {"type": "string"},
-        "file": {"type": "string"},
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          }
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "config": {
-      "id": "#/definitions/config",
-      "type": "object",
-      "properties": {
-        "name": {"type": "string"},
-        "file": {"type": "string"},
-        "external": {
-          "type": ["boolean", "object"],
-          "properties": {
-            "name": {"type": "string"}
-          }
-        },
-        "labels": {"$ref": "#/definitions/labels"}
-      },
-      "additionalProperties": false
-    },
-
-    "string_or_list": {
-      "oneOf": [
-        {"type": "string"},
-        {"$ref": "#/definitions/list_of_strings"}
-      ]
-    },
-
-    "list_of_strings": {
-      "type": "array",
-      "items": {"type": "string"},
-      "uniqueItems": true
-    },
-
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": ["string", "number", "null"]
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "labels": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
-      ]
-    },
-
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {"required": ["build"]},
-          {"required": ["image"]}
-        ],
-        "properties": {
-          "build": {
-            "required": ["context"]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 1013
compose/config/config_schema_v3.7.json

@@ -1,1013 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v3.7.json",
-  "type": "object",
-  "required": [
-    "version"
-  ],
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    },
-    "secrets": {
-      "id": "#/properties/secrets",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/secret"
-        }
-      },
-      "additionalProperties": false
-    },
-    "configs": {
-      "id": "#/properties/configs",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/config"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-  "patternProperties": {
-    "^x-": {}
-  },
-  "additionalProperties": false,
-  "definitions": {
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-      "properties": {
-        "deploy": {
-          "$ref": "#/definitions/deployment"
-        },
-        "build": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "object",
-              "properties": {
-                "context": {
-                  "type": "string"
-                },
-                "dockerfile": {
-                  "type": "string"
-                },
-                "args": {
-                  "$ref": "#/definitions/list_or_dict"
-                },
-                "labels": {
-                  "$ref": "#/definitions/list_or_dict"
-                },
-                "cache_from": {
-                  "$ref": "#/definitions/list_of_strings"
-                },
-                "network": {
-                  "type": "string"
-                },
-                "target": {
-                  "type": "string"
-                },
-                "shm_size": {
-                  "type": [
-                    "integer",
-                    "string"
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "cap_drop": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "cgroup_parent": {
-          "type": "string"
-        },
-        "command": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "array",
-              "items": {
-                "type": "string"
-              }
-            }
-          ]
-        },
-        "configs": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {
-                "type": "string"
-              },
-              {
-                "type": "object",
-                "properties": {
-                  "source": {
-                    "type": "string"
-                  },
-                  "target": {
-                    "type": "string"
-                  },
-                  "uid": {
-                    "type": "string"
-                  },
-                  "gid": {
-                    "type": "string"
-                  },
-                  "mode": {
-                    "type": "number"
-                  }
-                }
-              }
-            ]
-          }
-        },
-        "container_name": {
-          "type": "string"
-        },
-        "credential_spec": {
-          "type": "object",
-          "properties": {
-            "file": {
-              "type": "string"
-            },
-            "registry": {
-              "type": "string"
-            }
-          }
-        },
-        "depends_on": {
-          "$ref": "#/definitions/list_of_strings"
-        },
-        "devices": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "dns": {
-          "$ref": "#/definitions/string_or_list"
-        },
-        "dns_search": {
-          "$ref": "#/definitions/string_or_list"
-        },
-        "domainname": {
-          "type": "string"
-        },
-        "entrypoint": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "array",
-              "items": {
-                "type": "string"
-              }
-            }
-          ]
-        },
-        "env_file": {
-          "$ref": "#/definitions/string_or_list"
-        },
-        "environment": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": [
-              "string",
-              "number"
-            ],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-        "external_links": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "extra_hosts": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "healthcheck": {
-          "$ref": "#/definitions/healthcheck"
-        },
-        "hostname": {
-          "type": "string"
-        },
-        "image": {
-          "type": "string"
-        },
-        "init": {
-          "type": "boolean"
-        },
-        "ipc": {
-          "type": "string"
-        },
-        "isolation": {
-          "type": "string"
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "links": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "logging": {
-          "type": "object",
-          "properties": {
-            "driver": {
-              "type": "string"
-            },
-            "options": {
-              "type": "object",
-              "patternProperties": {
-                "^.+$": {
-                  "type": [
-                    "string",
-                    "number",
-                    "null"
-                  ]
-                }
-              }
-            }
-          },
-          "additionalProperties": false
-        },
-        "mac_address": {
-          "type": "string"
-        },
-        "network_mode": {
-          "type": "string"
-        },
-        "networks": {
-          "oneOf": [
-            {
-              "$ref": "#/definitions/list_of_strings"
-            },
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {
-                          "$ref": "#/definitions/list_of_strings"
-                        },
-                        "ipv4_address": {
-                          "type": "string"
-                        },
-                        "ipv6_address": {
-                          "type": "string"
-                        }
-                      },
-                      "additionalProperties": false
-                    },
-                    {
-                      "type": "null"
-                    }
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "pid": {
-          "type": [
-            "string",
-            "null"
-          ]
-        },
-        "ports": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {
-                "type": "number",
-                "format": "ports"
-              },
-              {
-                "type": "string",
-                "format": "ports"
-              },
-              {
-                "type": "object",
-                "properties": {
-                  "mode": {
-                    "type": "string"
-                  },
-                  "target": {
-                    "type": "integer"
-                  },
-                  "published": {
-                    "type": "integer"
-                  },
-                  "protocol": {
-                    "type": "string"
-                  }
-                },
-                "additionalProperties": false
-              }
-            ]
-          },
-          "uniqueItems": true
-        },
-        "privileged": {
-          "type": "boolean"
-        },
-        "read_only": {
-          "type": "boolean"
-        },
-        "restart": {
-          "type": "string"
-        },
-        "security_opt": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "shm_size": {
-          "type": [
-            "number",
-            "string"
-          ]
-        },
-        "secrets": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {
-                "type": "string"
-              },
-              {
-                "type": "object",
-                "properties": {
-                  "source": {
-                    "type": "string"
-                  },
-                  "target": {
-                    "type": "string"
-                  },
-                  "uid": {
-                    "type": "string"
-                  },
-                  "gid": {
-                    "type": "string"
-                  },
-                  "mode": {
-                    "type": "number"
-                  }
-                }
-              }
-            ]
-          }
-        },
-        "sysctls": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "stdin_open": {
-          "type": "boolean"
-        },
-        "stop_grace_period": {
-          "type": "string",
-          "format": "duration"
-        },
-        "stop_signal": {
-          "type": "string"
-        },
-        "tmpfs": {
-          "$ref": "#/definitions/string_or_list"
-        },
-        "tty": {
-          "type": "boolean"
-        },
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {
-                  "type": "integer"
-                },
-                {
-                  "type": "object",
-                  "properties": {
-                    "hard": {
-                      "type": "integer"
-                    },
-                    "soft": {
-                      "type": "integer"
-                    }
-                  },
-                  "required": [
-                    "soft",
-                    "hard"
-                  ],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {
-          "type": "string"
-        },
-        "userns_mode": {
-          "type": "string"
-        },
-        "volumes": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {
-                "type": "string"
-              },
-              {
-                "type": "object",
-                "required": [
-                  "type"
-                ],
-                "properties": {
-                  "type": {
-                    "type": "string"
-                  },
-                  "source": {
-                    "type": "string"
-                  },
-                  "target": {
-                    "type": "string"
-                  },
-                  "read_only": {
-                    "type": "boolean"
-                  },
-                  "consistency": {
-                    "type": "string"
-                  },
-                  "bind": {
-                    "type": "object",
-                    "properties": {
-                      "propagation": {
-                        "type": "string"
-                      }
-                    }
-                  },
-                  "volume": {
-                    "type": "object",
-                    "properties": {
-                      "nocopy": {
-                        "type": "boolean"
-                      }
-                    }
-                  },
-                  "tmpfs": {
-                    "type": "object",
-                    "properties": {
-                      "size": {
-                        "type": "integer",
-                        "minimum": 0
-                      }
-                    }
-                  }
-                },
-                "additionalProperties": false
-              }
-            ],
-            "uniqueItems": true
-          }
-        },
-        "working_dir": {
-          "type": "string"
-        }
-      },
-      "patternProperties": {
-        "^x-": {}
-      },
-      "additionalProperties": false
-    },
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {
-          "type": "boolean"
-        },
-        "interval": {
-          "type": "string",
-          "format": "duration"
-        },
-        "retries": {
-          "type": "number"
-        },
-        "test": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "array",
-              "items": {
-                "type": "string"
-              }
-            }
-          ]
-        },
-        "timeout": {
-          "type": "string",
-          "format": "duration"
-        },
-        "start_period": {
-          "type": "string",
-          "format": "duration"
-        }
-      }
-    },
-    "deployment": {
-      "id": "#/definitions/deployment",
-      "type": [
-        "object",
-        "null"
-      ],
-      "properties": {
-        "mode": {
-          "type": "string"
-        },
-        "endpoint_mode": {
-          "type": "string"
-        },
-        "replicas": {
-          "type": "integer"
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "rollback_config": {
-          "type": "object",
-          "properties": {
-            "parallelism": {
-              "type": "integer"
-            },
-            "delay": {
-              "type": "string",
-              "format": "duration"
-            },
-            "failure_action": {
-              "type": "string"
-            },
-            "monitor": {
-              "type": "string",
-              "format": "duration"
-            },
-            "max_failure_ratio": {
-              "type": "number"
-            },
-            "order": {
-              "type": "string",
-              "enum": [
-                "start-first",
-                "stop-first"
-              ]
-            }
-          },
-          "additionalProperties": false
-        },
-        "update_config": {
-          "type": "object",
-          "properties": {
-            "parallelism": {
-              "type": "integer"
-            },
-            "delay": {
-              "type": "string",
-              "format": "duration"
-            },
-            "failure_action": {
-              "type": "string"
-            },
-            "monitor": {
-              "type": "string",
-              "format": "duration"
-            },
-            "max_failure_ratio": {
-              "type": "number"
-            },
-            "order": {
-              "type": "string",
-              "enum": [
-                "start-first",
-                "stop-first"
-              ]
-            }
-          },
-          "additionalProperties": false
-        },
-        "resources": {
-          "type": "object",
-          "properties": {
-            "limits": {
-              "type": "object",
-              "properties": {
-                "cpus": {
-                  "type": "string"
-                },
-                "memory": {
-                  "type": "string"
-                }
-              },
-              "additionalProperties": false
-            },
-            "reservations": {
-              "type": "object",
-              "properties": {
-                "cpus": {
-                  "type": "string"
-                },
-                "memory": {
-                  "type": "string"
-                },
-                "generic_resources": {
-                  "$ref": "#/definitions/generic_resources"
-                }
-              },
-              "additionalProperties": false
-            }
-          },
-          "additionalProperties": false
-        },
-        "restart_policy": {
-          "type": "object",
-          "properties": {
-            "condition": {
-              "type": "string"
-            },
-            "delay": {
-              "type": "string",
-              "format": "duration"
-            },
-            "max_attempts": {
-              "type": "integer"
-            },
-            "window": {
-              "type": "string",
-              "format": "duration"
-            }
-          },
-          "additionalProperties": false
-        },
-        "placement": {
-          "type": "object",
-          "properties": {
-            "constraints": {
-              "type": "array",
-              "items": {
-                "type": "string"
-              }
-            },
-            "preferences": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "spread": {
-                    "type": "string"
-                  }
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-    "generic_resources": {
-      "id": "#/definitions/generic_resources",
-      "type": "array",
-      "items": {
-        "type": "object",
-        "properties": {
-          "discrete_resource_spec": {
-            "type": "object",
-            "properties": {
-              "kind": {
-                "type": "string"
-              },
-              "value": {
-                "type": "number"
-              }
-            },
-            "additionalProperties": false
-          }
-        },
-        "additionalProperties": false
-      }
-    },
-    "network": {
-      "id": "#/definitions/network",
-      "type": [
-        "object",
-        "null"
-      ],
-      "properties": {
-        "name": {
-          "type": "string"
-        },
-        "driver": {
-          "type": "string"
-        },
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {
-              "type": [
-                "string",
-                "number"
-              ]
-            }
-          }
-        },
-        "ipam": {
-          "type": "object",
-          "properties": {
-            "driver": {
-              "type": "string"
-            },
-            "config": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "subnet": {
-                    "type": "string"
-                  }
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        },
-        "external": {
-          "type": [
-            "boolean",
-            "object"
-          ],
-          "properties": {
-            "name": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        "internal": {
-          "type": "boolean"
-        },
-        "attachable": {
-          "type": "boolean"
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        }
-      },
-      "patternProperties": {
-        "^x-": {}
-      },
-      "additionalProperties": false
-    },
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": [
-        "object",
-        "null"
-      ],
-      "properties": {
-        "name": {
-          "type": "string"
-        },
-        "driver": {
-          "type": "string"
-        },
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {
-              "type": [
-                "string",
-                "number"
-              ]
-            }
-          }
-        },
-        "external": {
-          "type": [
-            "boolean",
-            "object"
-          ],
-          "properties": {
-            "name": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        }
-      },
-      "patternProperties": {
-        "^x-": {}
-      },
-      "additionalProperties": false
-    },
-    "secret": {
-      "id": "#/definitions/secret",
-      "type": "object",
-      "properties": {
-        "name": {
-          "type": "string"
-        },
-        "file": {
-          "type": "string"
-        },
-        "external": {
-          "type": [
-            "boolean",
-            "object"
-          ],
-          "properties": {
-            "name": {
-              "type": "string"
-            }
-          }
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        }
-      },
-      "patternProperties": {
-        "^x-": {}
-      },
-      "additionalProperties": false
-    },
-    "config": {
-      "id": "#/definitions/config",
-      "type": "object",
-      "properties": {
-        "name": {
-          "type": "string"
-        },
-        "file": {
-          "type": "string"
-        },
-        "external": {
-          "type": [
-            "boolean",
-            "object"
-          ],
-          "properties": {
-            "name": {
-              "type": "string"
-            }
-          }
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        }
-      },
-      "patternProperties": {
-        "^x-": {}
-      },
-      "additionalProperties": false
-    },
-    "string_or_list": {
-      "oneOf": [
-        {
-          "type": "string"
-        },
-        {
-          "$ref": "#/definitions/list_of_strings"
-        }
-      ]
-    },
-    "list_of_strings": {
-      "type": "array",
-      "items": {
-        "type": "string"
-      },
-      "uniqueItems": true
-    },
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": [
-                "string",
-                "number",
-                "null"
-              ]
-            }
-          },
-          "additionalProperties": false
-        },
-        {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        }
-      ]
-    },
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {
-            "required": [
-              "build"
-            ]
-          },
-          {
-            "required": [
-              "image"
-            ]
-          }
-        ],
-        "properties": {
-          "build": {
-            "required": [
-              "context"
-            ]
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 1040
compose/config/config_schema_v3.8.json

@@ -1,1040 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "id": "config_schema_v3.8.json",
-  "type": "object",
-  "required": [
-    "version"
-  ],
-  "properties": {
-    "version": {
-      "type": "string"
-    },
-    "services": {
-      "id": "#/properties/services",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/service"
-        }
-      },
-      "additionalProperties": false
-    },
-    "networks": {
-      "id": "#/properties/networks",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/network"
-        }
-      }
-    },
-    "volumes": {
-      "id": "#/properties/volumes",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/volume"
-        }
-      },
-      "additionalProperties": false
-    },
-    "secrets": {
-      "id": "#/properties/secrets",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/secret"
-        }
-      },
-      "additionalProperties": false
-    },
-    "configs": {
-      "id": "#/properties/configs",
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z0-9._-]+$": {
-          "$ref": "#/definitions/config"
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-  "patternProperties": {
-    "^x-": {}
-  },
-  "additionalProperties": false,
-  "definitions": {
-    "service": {
-      "id": "#/definitions/service",
-      "type": "object",
-      "properties": {
-        "deploy": {
-          "$ref": "#/definitions/deployment"
-        },
-        "build": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "object",
-              "properties": {
-                "context": {
-                  "type": "string"
-                },
-                "dockerfile": {
-                  "type": "string"
-                },
-                "args": {
-                  "$ref": "#/definitions/list_or_dict"
-                },
-                "labels": {
-                  "$ref": "#/definitions/list_or_dict"
-                },
-                "cache_from": {
-                  "$ref": "#/definitions/list_of_strings"
-                },
-                "network": {
-                  "type": "string"
-                },
-                "target": {
-                  "type": "string"
-                },
-                "shm_size": {
-                  "type": [
-                    "integer",
-                    "string"
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "cap_add": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "cap_drop": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "cgroup_parent": {
-          "type": "string"
-        },
-        "command": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "array",
-              "items": {
-                "type": "string"
-              }
-            }
-          ]
-        },
-        "configs": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {
-                "type": "string"
-              },
-              {
-                "type": "object",
-                "properties": {
-                  "source": {
-                    "type": "string"
-                  },
-                  "target": {
-                    "type": "string"
-                  },
-                  "uid": {
-                    "type": "string"
-                  },
-                  "gid": {
-                    "type": "string"
-                  },
-                  "mode": {
-                    "type": "number"
-                  }
-                }
-              }
-            ]
-          }
-        },
-        "container_name": {
-          "type": "string"
-        },
-        "credential_spec": {
-          "type": "object",
-          "properties": {
-            "config": {
-              "type": "string"
-            },
-            "file": {
-              "type": "string"
-            },
-            "registry": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        "depends_on": {
-          "$ref": "#/definitions/list_of_strings"
-        },
-        "devices": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "dns": {
-          "$ref": "#/definitions/string_or_list"
-        },
-        "dns_search": {
-          "$ref": "#/definitions/string_or_list"
-        },
-        "domainname": {
-          "type": "string"
-        },
-        "entrypoint": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "array",
-              "items": {
-                "type": "string"
-              }
-            }
-          ]
-        },
-        "env_file": {
-          "$ref": "#/definitions/string_or_list"
-        },
-        "environment": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "expose": {
-          "type": "array",
-          "items": {
-            "type": [
-              "string",
-              "number"
-            ],
-            "format": "expose"
-          },
-          "uniqueItems": true
-        },
-        "external_links": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "extra_hosts": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "healthcheck": {
-          "$ref": "#/definitions/healthcheck"
-        },
-        "hostname": {
-          "type": "string"
-        },
-        "image": {
-          "type": "string"
-        },
-        "init": {
-          "type": "boolean"
-        },
-        "ipc": {
-          "type": "string"
-        },
-        "isolation": {
-          "type": "string"
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "links": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "logging": {
-          "type": "object",
-          "properties": {
-            "driver": {
-              "type": "string"
-            },
-            "options": {
-              "type": "object",
-              "patternProperties": {
-                "^.+$": {
-                  "type": [
-                    "string",
-                    "number",
-                    "null"
-                  ]
-                }
-              }
-            }
-          },
-          "additionalProperties": false
-        },
-        "mac_address": {
-          "type": "string"
-        },
-        "network_mode": {
-          "type": "string"
-        },
-        "networks": {
-          "oneOf": [
-            {
-              "$ref": "#/definitions/list_of_strings"
-            },
-            {
-              "type": "object",
-              "patternProperties": {
-                "^[a-zA-Z0-9._-]+$": {
-                  "oneOf": [
-                    {
-                      "type": "object",
-                      "properties": {
-                        "aliases": {
-                          "$ref": "#/definitions/list_of_strings"
-                        },
-                        "ipv4_address": {
-                          "type": "string"
-                        },
-                        "ipv6_address": {
-                          "type": "string"
-                        }
-                      },
-                      "additionalProperties": false
-                    },
-                    {
-                      "type": "null"
-                    }
-                  ]
-                }
-              },
-              "additionalProperties": false
-            }
-          ]
-        },
-        "pid": {
-          "type": [
-            "string",
-            "null"
-          ]
-        },
-        "ports": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {
-                "type": "number",
-                "format": "ports"
-              },
-              {
-                "type": "string",
-                "format": "ports"
-              },
-              {
-                "type": "object",
-                "properties": {
-                  "mode": {
-                    "type": "string"
-                  },
-                  "target": {
-                    "type": "integer"
-                  },
-                  "published": {
-                    "type": "integer"
-                  },
-                  "protocol": {
-                    "type": "string"
-                  }
-                },
-                "additionalProperties": false
-              }
-            ]
-          },
-          "uniqueItems": true
-        },
-        "privileged": {
-          "type": "boolean"
-        },
-        "read_only": {
-          "type": "boolean"
-        },
-        "restart": {
-          "type": "string"
-        },
-        "security_opt": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        },
-        "shm_size": {
-          "type": [
-            "number",
-            "string"
-          ]
-        },
-        "secrets": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {
-                "type": "string"
-              },
-              {
-                "type": "object",
-                "properties": {
-                  "source": {
-                    "type": "string"
-                  },
-                  "target": {
-                    "type": "string"
-                  },
-                  "uid": {
-                    "type": "string"
-                  },
-                  "gid": {
-                    "type": "string"
-                  },
-                  "mode": {
-                    "type": "number"
-                  }
-                }
-              }
-            ]
-          }
-        },
-        "sysctls": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "stdin_open": {
-          "type": "boolean"
-        },
-        "stop_grace_period": {
-          "type": "string",
-          "format": "duration"
-        },
-        "stop_signal": {
-          "type": "string"
-        },
-        "tmpfs": {
-          "$ref": "#/definitions/string_or_list"
-        },
-        "tty": {
-          "type": "boolean"
-        },
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {
-                  "type": "integer"
-                },
-                {
-                  "type": "object",
-                  "properties": {
-                    "hard": {
-                      "type": "integer"
-                    },
-                    "soft": {
-                      "type": "integer"
-                    }
-                  },
-                  "required": [
-                    "soft",
-                    "hard"
-                  ],
-                  "additionalProperties": false
-                }
-              ]
-            }
-          }
-        },
-        "user": {
-          "type": "string"
-        },
-        "userns_mode": {
-          "type": "string"
-        },
-        "volumes": {
-          "type": "array",
-          "items": {
-            "oneOf": [
-              {
-                "type": "string"
-              },
-              {
-                "type": "object",
-                "required": [
-                  "type"
-                ],
-                "properties": {
-                  "type": {
-                    "type": "string"
-                  },
-                  "source": {
-                    "type": "string"
-                  },
-                  "target": {
-                    "type": "string"
-                  },
-                  "read_only": {
-                    "type": "boolean"
-                  },
-                  "consistency": {
-                    "type": "string"
-                  },
-                  "bind": {
-                    "type": "object",
-                    "properties": {
-                      "propagation": {
-                        "type": "string"
-                      }
-                    }
-                  },
-                  "volume": {
-                    "type": "object",
-                    "properties": {
-                      "nocopy": {
-                        "type": "boolean"
-                      }
-                    }
-                  },
-                  "tmpfs": {
-                    "type": "object",
-                    "properties": {
-                      "size": {
-                        "type": "integer",
-                        "minimum": 0
-                      }
-                    }
-                  }
-                },
-                "additionalProperties": false
-              }
-            ],
-            "uniqueItems": true
-          }
-        },
-        "working_dir": {
-          "type": "string"
-        }
-      },
-      "patternProperties": {
-        "^x-": {}
-      },
-      "additionalProperties": false
-    },
-    "healthcheck": {
-      "id": "#/definitions/healthcheck",
-      "type": "object",
-      "additionalProperties": false,
-      "properties": {
-        "disable": {
-          "type": "boolean"
-        },
-        "interval": {
-          "type": "string",
-          "format": "duration"
-        },
-        "retries": {
-          "type": "number"
-        },
-        "test": {
-          "oneOf": [
-            {
-              "type": "string"
-            },
-            {
-              "type": "array",
-              "items": {
-                "type": "string"
-              }
-            }
-          ]
-        },
-        "timeout": {
-          "type": "string",
-          "format": "duration"
-        },
-        "start_period": {
-          "type": "string",
-          "format": "duration"
-        }
-      }
-    },
-    "deployment": {
-      "id": "#/definitions/deployment",
-      "type": [
-        "object",
-        "null"
-      ],
-      "properties": {
-        "mode": {
-          "type": "string"
-        },
-        "endpoint_mode": {
-          "type": "string"
-        },
-        "replicas": {
-          "type": "integer"
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "rollback_config": {
-          "type": "object",
-          "properties": {
-            "parallelism": {
-              "type": "integer"
-            },
-            "delay": {
-              "type": "string",
-              "format": "duration"
-            },
-            "failure_action": {
-              "type": "string"
-            },
-            "monitor": {
-              "type": "string",
-              "format": "duration"
-            },
-            "max_failure_ratio": {
-              "type": "number"
-            },
-            "order": {
-              "type": "string",
-              "enum": [
-                "start-first",
-                "stop-first"
-              ]
-            }
-          },
-          "additionalProperties": false
-        },
-        "update_config": {
-          "type": "object",
-          "properties": {
-            "parallelism": {
-              "type": "integer"
-            },
-            "delay": {
-              "type": "string",
-              "format": "duration"
-            },
-            "failure_action": {
-              "type": "string"
-            },
-            "monitor": {
-              "type": "string",
-              "format": "duration"
-            },
-            "max_failure_ratio": {
-              "type": "number"
-            },
-            "order": {
-              "type": "string",
-              "enum": [
-                "start-first",
-                "stop-first"
-              ]
-            }
-          },
-          "additionalProperties": false
-        },
-        "resources": {
-          "type": "object",
-          "properties": {
-            "limits": {
-              "type": "object",
-              "properties": {
-                "cpus": {
-                  "type": "string"
-                },
-                "memory": {
-                  "type": "string"
-                }
-              },
-              "additionalProperties": false
-            },
-            "reservations": {
-              "type": "object",
-              "properties": {
-                "cpus": {
-                  "type": "string"
-                },
-                "memory": {
-                  "type": "string"
-                },
-                "generic_resources": {
-                  "$ref": "#/definitions/generic_resources"
-                }
-              },
-              "additionalProperties": false
-            }
-          },
-          "additionalProperties": false
-        },
-        "restart_policy": {
-          "type": "object",
-          "properties": {
-            "condition": {
-              "type": "string"
-            },
-            "delay": {
-              "type": "string",
-              "format": "duration"
-            },
-            "max_attempts": {
-              "type": "integer"
-            },
-            "window": {
-              "type": "string",
-              "format": "duration"
-            }
-          },
-          "additionalProperties": false
-        },
-        "placement": {
-          "type": "object",
-          "properties": {
-            "constraints": {
-              "type": "array",
-              "items": {
-                "type": "string"
-              }
-            },
-            "preferences": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "spread": {
-                    "type": "string"
-                  }
-                },
-                "additionalProperties": false
-              }
-            },
-            "max_replicas_per_node": {
-              "type": "integer"
-            }
-          },
-          "additionalProperties": false
-        }
-      },
-      "additionalProperties": false
-    },
-    "generic_resources": {
-      "id": "#/definitions/generic_resources",
-      "type": "array",
-      "items": {
-        "type": "object",
-        "properties": {
-          "discrete_resource_spec": {
-            "type": "object",
-            "properties": {
-              "kind": {
-                "type": "string"
-              },
-              "value": {
-                "type": "number"
-              }
-            },
-            "additionalProperties": false
-          }
-        },
-        "additionalProperties": false
-      }
-    },
-    "network": {
-      "id": "#/definitions/network",
-      "type": [
-        "object",
-        "null"
-      ],
-      "properties": {
-        "name": {
-          "type": "string"
-        },
-        "driver": {
-          "type": "string"
-        },
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {
-              "type": [
-                "string",
-                "number"
-              ]
-            }
-          }
-        },
-        "ipam": {
-          "type": "object",
-          "properties": {
-            "driver": {
-              "type": "string"
-            },
-            "config": {
-              "type": "array",
-              "items": {
-                "type": "object",
-                "properties": {
-                  "subnet": {
-                    "type": "string"
-                  }
-                },
-                "additionalProperties": false
-              }
-            }
-          },
-          "additionalProperties": false
-        },
-        "external": {
-          "type": [
-            "boolean",
-            "object"
-          ],
-          "properties": {
-            "name": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        "internal": {
-          "type": "boolean"
-        },
-        "attachable": {
-          "type": "boolean"
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        }
-      },
-      "patternProperties": {
-        "^x-": {}
-      },
-      "additionalProperties": false
-    },
-    "volume": {
-      "id": "#/definitions/volume",
-      "type": [
-        "object",
-        "null"
-      ],
-      "properties": {
-        "name": {
-          "type": "string"
-        },
-        "driver": {
-          "type": "string"
-        },
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {
-              "type": [
-                "string",
-                "number"
-              ]
-            }
-          }
-        },
-        "external": {
-          "type": [
-            "boolean",
-            "object"
-          ],
-          "properties": {
-            "name": {
-              "type": "string"
-            }
-          },
-          "additionalProperties": false
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        }
-      },
-      "patternProperties": {
-        "^x-": {}
-      },
-      "additionalProperties": false
-    },
-    "secret": {
-      "id": "#/definitions/secret",
-      "type": "object",
-      "properties": {
-        "name": {
-          "type": "string"
-        },
-        "file": {
-          "type": "string"
-        },
-        "external": {
-          "type": [
-            "boolean",
-            "object"
-          ],
-          "properties": {
-            "name": {
-              "type": "string"
-            }
-          }
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "driver": {
-          "type": "string"
-        },
-        "driver_opts": {
-          "type": "object",
-          "patternProperties": {
-            "^.+$": {
-              "type": [
-                "string",
-                "number"
-              ]
-            }
-          }
-        },
-        "template_driver": {
-          "type": "string"
-        }
-      },
-      "patternProperties": {
-        "^x-": {}
-      },
-      "additionalProperties": false
-    },
-    "config": {
-      "id": "#/definitions/config",
-      "type": "object",
-      "properties": {
-        "name": {
-          "type": "string"
-        },
-        "file": {
-          "type": "string"
-        },
-        "external": {
-          "type": [
-            "boolean",
-            "object"
-          ],
-          "properties": {
-            "name": {
-              "type": "string"
-            }
-          }
-        },
-        "labels": {
-          "$ref": "#/definitions/list_or_dict"
-        },
-        "template_driver": {
-          "type": "string"
-        }
-      },
-      "patternProperties": {
-        "^x-": {}
-      },
-      "additionalProperties": false
-    },
-    "string_or_list": {
-      "oneOf": [
-        {
-          "type": "string"
-        },
-        {
-          "$ref": "#/definitions/list_of_strings"
-        }
-      ]
-    },
-    "list_of_strings": {
-      "type": "array",
-      "items": {
-        "type": "string"
-      },
-      "uniqueItems": true
-    },
-    "list_or_dict": {
-      "oneOf": [
-        {
-          "type": "object",
-          "patternProperties": {
-            ".+": {
-              "type": [
-                "string",
-                "number",
-                "null"
-              ]
-            }
-          },
-          "additionalProperties": false
-        },
-        {
-          "type": "array",
-          "items": {
-            "type": "string"
-          },
-          "uniqueItems": true
-        }
-      ]
-    },
-    "constraints": {
-      "service": {
-        "id": "#/definitions/constraints/service",
-        "anyOf": [
-          {
-            "required": [
-              "build"
-            ]
-          },
-          {
-            "required": [
-              "image"
-            ]
-          }
-        ],
-        "properties": {
-          "build": {
-            "required": [
-              "context"
-            ]
-          }
-        }
-      }
-    }
-  }
-}

+ 2 - 2
compose/config/interpolation.py

@@ -3,7 +3,7 @@ import re
 from string import Template
 from string import Template
 
 
 from .errors import ConfigurationError
 from .errors import ConfigurationError
-from compose.const import COMPOSEFILE_V2_0 as V2_0
+from compose.const import COMPOSEFILE_V1 as V1
 from compose.utils import parse_bytes
 from compose.utils import parse_bytes
 from compose.utils import parse_nanoseconds_int
 from compose.utils import parse_nanoseconds_int
 
 
@@ -25,7 +25,7 @@ class Interpolator(object):
 
 
 
 
 def interpolate_environment_variables(version, config, section, environment):
 def interpolate_environment_variables(version, config, section, environment):
-    if version <= V2_0:
+    if version == V1:
         interpolator = Interpolator(Template, environment)
         interpolator = Interpolator(Template, environment)
     else:
     else:
         interpolator = Interpolator(TemplateWithDefaults, environment)
         interpolator = Interpolator(TemplateWithDefaults, environment)

+ 6 - 25
compose/config/serialize.py

@@ -1,13 +1,8 @@
 import yaml
 import yaml
 
 
 from compose.config import types
 from compose.config import types
+from compose.const import COMPOSE_SPEC as VERSION
 from compose.const import COMPOSEFILE_V1 as V1
 from compose.const import COMPOSEFILE_V1 as V1
-from compose.const import COMPOSEFILE_V2_1 as V2_1
-from compose.const import COMPOSEFILE_V2_3 as V2_3
-from compose.const import COMPOSEFILE_V3_0 as V3_0
-from compose.const import COMPOSEFILE_V3_2 as V3_2
-from compose.const import COMPOSEFILE_V3_4 as V3_4
-from compose.const import COMPOSEFILE_V3_5 as V3_5
 
 
 
 
 def serialize_config_type(dumper, data):
 def serialize_config_type(dumper, data):
@@ -49,7 +44,7 @@ yaml.SafeDumper.add_representer(types.ServicePort, serialize_dict_type)
 
 
 
 
 def denormalize_config(config, image_digests=None):
 def denormalize_config(config, image_digests=None):
-    result = {'version': str(V2_1) if config.version == V1 else str(config.version)}
+    result = {'version': str(config.version)}
     denormalized_services = [
     denormalized_services = [
         denormalize_service_dict(
         denormalize_service_dict(
             service_dict,
             service_dict,
@@ -72,25 +67,11 @@ def denormalize_config(config, image_digests=None):
                 del conf['external_name']
                 del conf['external_name']
 
 
             if 'name' in conf:
             if 'name' in conf:
-                if config.version < V2_1 or (
-                        config.version >= V3_0 and config.version < v3_introduced_name_key(key)):
-                    del conf['name']
-                elif 'external' in conf:
+                if 'external' in conf:
                     conf['external'] = bool(conf['external'])
                     conf['external'] = bool(conf['external'])
-
-            if 'attachable' in conf and config.version < V3_2:
-                # For compatibility mode, this option is invalid in v2
-                del conf['attachable']
-
     return result
     return result
 
 
 
 
-def v3_introduced_name_key(key):
-    if key == 'volumes':
-        return V3_4
-    return V3_5
-
-
 def serialize_config(config, image_digests=None, escape_dollar=True):
 def serialize_config(config, image_digests=None, escape_dollar=True):
     if escape_dollar:
     if escape_dollar:
         yaml.SafeDumper.add_representer(str, serialize_string_escape_dollar)
         yaml.SafeDumper.add_representer(str, serialize_string_escape_dollar)
@@ -140,7 +121,7 @@ def denormalize_service_dict(service_dict, version, image_digest=None):
     if version == V1 and 'network_mode' not in service_dict:
     if version == V1 and 'network_mode' not in service_dict:
         service_dict['network_mode'] = 'bridge'
         service_dict['network_mode'] = 'bridge'
 
 
-    if 'depends_on' in service_dict and (version < V2_1 or version >= V3_0):
+    if 'depends_on' in service_dict:
         service_dict['depends_on'] = sorted([
         service_dict['depends_on'] = sorted([
             svc for svc in service_dict['depends_on'].keys()
             svc for svc in service_dict['depends_on'].keys()
         ])
         ])
@@ -162,10 +143,10 @@ def denormalize_service_dict(service_dict, version, image_digest=None):
 
 
     if 'ports' in service_dict:
     if 'ports' in service_dict:
         service_dict['ports'] = [
         service_dict['ports'] = [
-            p.legacy_repr() if p.external_ip or version < V3_2 else p
+            p.legacy_repr() if p.external_ip or version < VERSION else p
             for p in service_dict['ports']
             for p in service_dict['ports']
         ]
         ]
-    if 'volumes' in service_dict and (version < V2_3 or (version > V3_0 and version < V3_2)):
+    if 'volumes' in service_dict and (version == V1):
         service_dict['volumes'] = [
         service_dict['volumes'] = [
             v.legacy_repr() if isinstance(v, types.MountSpec) else v for v in service_dict['volumes']
             v.legacy_repr() if isinstance(v, types.MountSpec) else v for v in service_dict['volumes']
         ]
         ]

+ 26 - 9
compose/config/validation.py

@@ -282,7 +282,7 @@ def handle_error_for_schema_with_id(error, path):
             invalid_config_key = parse_key_from_error_msg(error)
             invalid_config_key = parse_key_from_error_msg(error)
             return get_unsupported_config_msg(path, invalid_config_key)
             return get_unsupported_config_msg(path, invalid_config_key)
 
 
-        if schema_id.startswith('config_schema_v'):
+        if schema_id.startswith('config_schema_'):
             invalid_config_key = parse_key_from_error_msg(error)
             invalid_config_key = parse_key_from_error_msg(error)
             return ('Invalid top-level property "{key}". Valid top-level '
             return ('Invalid top-level property "{key}". Valid top-level '
                     'sections for this Compose file are: {properties}, and '
                     'sections for this Compose file are: {properties}, and '
@@ -435,15 +435,29 @@ def process_config_schema_errors(error):
     return handle_generic_error(error, path)
     return handle_generic_error(error, path)
 
 
 
 
-def validate_against_config_schema(config_file):
-    schema = load_jsonschema(config_file)
+def keys_to_str(config_file):
+    """
+        Non-string keys may break validator with patterned fields.
+    """
+    d = {}
+    for k, v in config_file.items():
+        d[str(k)] = v
+        if isinstance(v, dict):
+            d[str(k)] = keys_to_str(v)
+    return d
+
+
+def validate_against_config_schema(config_file, version):
+    schema = load_jsonschema(version)
+    config = keys_to_str(config_file.config)
+
     format_checker = FormatChecker(["ports", "expose", "subnet_ip_address"])
     format_checker = FormatChecker(["ports", "expose", "subnet_ip_address"])
     validator = Draft4Validator(
     validator = Draft4Validator(
         schema,
         schema,
         resolver=RefResolver(get_resolver_path(), schema),
         resolver=RefResolver(get_resolver_path(), schema),
         format_checker=format_checker)
         format_checker=format_checker)
     handle_errors(
     handle_errors(
-        validator.iter_errors(config_file.config),
+        validator.iter_errors(config),
         process_config_schema_errors,
         process_config_schema_errors,
         config_file.filename)
         config_file.filename)
 
 
@@ -453,7 +467,7 @@ def validate_service_constraints(config, service_name, config_file):
         return process_service_constraint_errors(
         return process_service_constraint_errors(
             errors, service_name, config_file.version)
             errors, service_name, config_file.version)
 
 
-    schema = load_jsonschema(config_file)
+    schema = load_jsonschema(config_file.version)
     validator = Draft4Validator(schema['definitions']['constraints']['service'])
     validator = Draft4Validator(schema['definitions']['constraints']['service'])
     handle_errors(validator.iter_errors(config), handler, None)
     handle_errors(validator.iter_errors(config), handler, None)
 
 
@@ -472,16 +486,19 @@ def get_schema_path():
     return os.path.dirname(os.path.abspath(__file__))
     return os.path.dirname(os.path.abspath(__file__))
 
 
 
 
-def load_jsonschema(config_file):
+def load_jsonschema(version):
+    suffix = "compose_spec"
+    if version == V1:
+        suffix = "v1"
+
     filename = os.path.join(
     filename = os.path.join(
         get_schema_path(),
         get_schema_path(),
-        "config_schema_v{0}.json".format(config_file.version))
+        "config_schema_{0}.json".format(suffix))
 
 
     if not os.path.exists(filename):
     if not os.path.exists(filename):
         raise ConfigurationError(
         raise ConfigurationError(
             'Version in "{}" is unsupported. {}'
             'Version in "{}" is unsupported. {}'
-            .format(config_file.filename, VERSION_EXPLANATION))
-
+            .format(filename, VERSION_EXPLANATION))
     with open(filename, "r") as fh:
     with open(filename, "r") as fh:
         return json.load(fh)
         return json.load(fh)
 
 

+ 3 - 43
compose/const.py

@@ -24,56 +24,16 @@ SECRETS_PATH = '/run/secrets'
 WINDOWS_LONGPATH_PREFIX = '\\\\?\\'
 WINDOWS_LONGPATH_PREFIX = '\\\\?\\'
 
 
 COMPOSEFILE_V1 = ComposeVersion('1')
 COMPOSEFILE_V1 = ComposeVersion('1')
-COMPOSEFILE_V2_0 = ComposeVersion('2.0')
-COMPOSEFILE_V2_1 = ComposeVersion('2.1')
-COMPOSEFILE_V2_2 = ComposeVersion('2.2')
-COMPOSEFILE_V2_3 = ComposeVersion('2.3')
-COMPOSEFILE_V2_4 = ComposeVersion('2.4')
-
-COMPOSEFILE_V3_0 = ComposeVersion('3.0')
-COMPOSEFILE_V3_1 = ComposeVersion('3.1')
-COMPOSEFILE_V3_2 = ComposeVersion('3.2')
-COMPOSEFILE_V3_3 = ComposeVersion('3.3')
-COMPOSEFILE_V3_4 = ComposeVersion('3.4')
-COMPOSEFILE_V3_5 = ComposeVersion('3.5')
-COMPOSEFILE_V3_6 = ComposeVersion('3.6')
-COMPOSEFILE_V3_7 = ComposeVersion('3.7')
-COMPOSEFILE_V3_8 = ComposeVersion('3.8')
+COMPOSE_SPEC = ComposeVersion('3')
 
 
 # minimum DOCKER ENGINE API version needed to support
 # minimum DOCKER ENGINE API version needed to support
 # features for each compose schema version
 # features for each compose schema version
 API_VERSIONS = {
 API_VERSIONS = {
     COMPOSEFILE_V1: '1.21',
     COMPOSEFILE_V1: '1.21',
-    COMPOSEFILE_V2_0: '1.22',
-    COMPOSEFILE_V2_1: '1.24',
-    COMPOSEFILE_V2_2: '1.25',
-    COMPOSEFILE_V2_3: '1.30',
-    COMPOSEFILE_V2_4: '1.35',
-    COMPOSEFILE_V3_0: '1.25',
-    COMPOSEFILE_V3_1: '1.25',
-    COMPOSEFILE_V3_2: '1.25',
-    COMPOSEFILE_V3_3: '1.30',
-    COMPOSEFILE_V3_4: '1.30',
-    COMPOSEFILE_V3_5: '1.30',
-    COMPOSEFILE_V3_6: '1.36',
-    COMPOSEFILE_V3_7: '1.38',
-    COMPOSEFILE_V3_8: '1.38',
+    COMPOSE_SPEC: '1.38',
 }
 }
 
 
 API_VERSION_TO_ENGINE_VERSION = {
 API_VERSION_TO_ENGINE_VERSION = {
     API_VERSIONS[COMPOSEFILE_V1]: '1.9.0',
     API_VERSIONS[COMPOSEFILE_V1]: '1.9.0',
-    API_VERSIONS[COMPOSEFILE_V2_0]: '1.10.0',
-    API_VERSIONS[COMPOSEFILE_V2_1]: '1.12.0',
-    API_VERSIONS[COMPOSEFILE_V2_2]: '1.13.0',
-    API_VERSIONS[COMPOSEFILE_V2_3]: '17.06.0',
-    API_VERSIONS[COMPOSEFILE_V2_4]: '17.12.0',
-    API_VERSIONS[COMPOSEFILE_V3_0]: '1.13.0',
-    API_VERSIONS[COMPOSEFILE_V3_1]: '1.13.0',
-    API_VERSIONS[COMPOSEFILE_V3_2]: '1.13.0',
-    API_VERSIONS[COMPOSEFILE_V3_3]: '17.06.0',
-    API_VERSIONS[COMPOSEFILE_V3_4]: '17.06.0',
-    API_VERSIONS[COMPOSEFILE_V3_5]: '17.06.0',
-    API_VERSIONS[COMPOSEFILE_V3_6]: '18.02.0',
-    API_VERSIONS[COMPOSEFILE_V3_7]: '18.06.0',
-    API_VERSIONS[COMPOSEFILE_V3_8]: '18.06.0',
+    API_VERSIONS[COMPOSE_SPEC]: '18.06.0',
 }
 }

+ 116 - 0
compose/project.py

@@ -123,6 +123,18 @@ class Project(object):
                 service_dict.pop('secrets', None) or [],
                 service_dict.pop('secrets', None) or [],
                 config_data.secrets)
                 config_data.secrets)
 
 
+            service_dict['scale'] = project.get_service_scale(service_dict)
+
+            service_dict = translate_credential_spec_to_security_opt(service_dict)
+            service_dict, ignored_keys = translate_deploy_keys_to_container_config(
+                service_dict
+            )
+            if ignored_keys:
+                log.warning(
+                    'The following deploy sub-keys are not supported and have'
+                    ' been ignored: {}'.format(', '.join(ignored_keys))
+                )
+
             project.services.append(
             project.services.append(
                 Service(
                 Service(
                     service_dict.pop('name'),
                     service_dict.pop('name'),
@@ -262,6 +274,35 @@ class Project(object):
 
 
         return PidMode(pid_mode)
         return PidMode(pid_mode)
 
 
+    def get_service_scale(self, service_dict):
+        # service.scale for v2 and deploy.replicas for v3
+        scale = service_dict.get('scale', None)
+        deploy_dict = service_dict.get('deploy', None)
+        if not deploy_dict:
+            return 1 if scale is None else scale
+
+        if deploy_dict.get('mode', 'replicated') != 'replicated':
+            return 1 if scale is None else scale
+
+        replicas = deploy_dict.get('replicas', None)
+        if scale and replicas:
+            raise ConfigurationError(
+                "Both service.scale and service.deploy.replicas are set."
+                " Only one of them must be set."
+            )
+        if replicas:
+            scale = replicas
+        # deploy may contain placement constraints introduced in v3.8
+        max_replicas = deploy_dict.get('placement', {}).get(
+            'max_replicas_per_node',
+            scale)
+
+        scale = min(scale, max_replicas)
+        if max_replicas < scale:
+            log.warning("Scale is limited to {} ('max_replicas_per_node' field).".format(
+                max_replicas))
+        return scale
+
     def start(self, service_names=None, **options):
     def start(self, service_names=None, **options):
         containers = []
         containers = []
 
 
@@ -777,6 +818,81 @@ class Project(object):
         return container_operation_with_timeout
         return container_operation_with_timeout
 
 
 
 
+def translate_credential_spec_to_security_opt(service_dict):
+    result = []
+
+    if 'credential_spec' in service_dict:
+        spec = convert_credential_spec_to_security_opt(service_dict['credential_spec'])
+        result.append('credentialspec={spec}'.format(spec=spec))
+
+    if result:
+        service_dict['security_opt'] = result
+
+    return service_dict
+
+
+def translate_resource_keys_to_container_config(resources_dict, service_dict):
+    if 'limits' in resources_dict:
+        service_dict['mem_limit'] = resources_dict['limits'].get('memory')
+        if 'cpus' in resources_dict['limits']:
+            service_dict['cpus'] = float(resources_dict['limits']['cpus'])
+    if 'reservations' in resources_dict:
+        service_dict['mem_reservation'] = resources_dict['reservations'].get('memory')
+        if 'cpus' in resources_dict['reservations']:
+            return ['resources.reservations.cpus']
+    return []
+
+
+def convert_restart_policy(name):
+    try:
+        return {
+            'any': 'always',
+            'none': 'no',
+            'on-failure': 'on-failure'
+        }[name]
+    except KeyError:
+        raise ConfigurationError('Invalid restart policy "{}"'.format(name))
+
+
+def convert_credential_spec_to_security_opt(credential_spec):
+    if 'file' in credential_spec:
+        return 'file://{file}'.format(file=credential_spec['file'])
+    return 'registry://{registry}'.format(registry=credential_spec['registry'])
+
+
+def translate_deploy_keys_to_container_config(service_dict):
+    if 'credential_spec' in service_dict:
+        del service_dict['credential_spec']
+    if 'configs' in service_dict:
+        del service_dict['configs']
+
+    if 'deploy' not in service_dict:
+        return service_dict, []
+
+    deploy_dict = service_dict['deploy']
+    ignored_keys = [
+        k for k in ['endpoint_mode', 'labels', 'update_config', 'rollback_config']
+        if k in deploy_dict
+    ]
+
+    if 'restart_policy' in deploy_dict:
+        service_dict['restart'] = {
+            'Name': convert_restart_policy(deploy_dict['restart_policy'].get('condition', 'any')),
+            'MaximumRetryCount': deploy_dict['restart_policy'].get('max_attempts', 0)
+        }
+        for k in deploy_dict['restart_policy'].keys():
+            if k != 'condition' and k != 'max_attempts':
+                ignored_keys.append('restart_policy.{}'.format(k))
+
+    ignored_keys.extend(
+        translate_resource_keys_to_container_config(
+            deploy_dict.get('resources', {}), service_dict
+        )
+    )
+    del service_dict['deploy']
+    return service_dict, ignored_keys
+
+
 def get_volumes_from(project, service_dict):
 def get_volumes_from(project, service_dict):
     volumes_from = service_dict.pop('volumes_from', None)
     volumes_from = service_dict.pop('volumes_from', None)
     if not volumes_from:
     if not volumes_from:

+ 2 - 67
docker-compose.spec

@@ -23,73 +23,8 @@ exe = EXE(pyz,
                 'DATA'
                 'DATA'
             ),
             ),
             (
             (
-                'compose/config/config_schema_v2.0.json',
-                'compose/config/config_schema_v2.0.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v2.1.json',
-                'compose/config/config_schema_v2.1.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v2.2.json',
-                'compose/config/config_schema_v2.2.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v2.3.json',
-                'compose/config/config_schema_v2.3.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v2.4.json',
-                'compose/config/config_schema_v2.4.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.0.json',
-                'compose/config/config_schema_v3.0.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.1.json',
-                'compose/config/config_schema_v3.1.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.2.json',
-                'compose/config/config_schema_v3.2.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.3.json',
-                'compose/config/config_schema_v3.3.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.4.json',
-                'compose/config/config_schema_v3.4.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.5.json',
-                'compose/config/config_schema_v3.5.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.6.json',
-                'compose/config/config_schema_v3.6.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.7.json',
-                'compose/config/config_schema_v3.7.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.8.json',
-                'compose/config/config_schema_v3.8.json',
+                'compose/config/config_schema_compose_spec.json',
+                'compose/config/config_schema_compose_spec.json',
                 'DATA'
                 'DATA'
             ),
             ),
             (
             (

+ 2 - 67
docker-compose_darwin.spec

@@ -32,73 +32,8 @@ coll = COLLECT(exe,
                 'DATA'
                 'DATA'
             ),
             ),
             (
             (
-                'compose/config/config_schema_v2.0.json',
-                'compose/config/config_schema_v2.0.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v2.1.json',
-                'compose/config/config_schema_v2.1.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v2.2.json',
-                'compose/config/config_schema_v2.2.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v2.3.json',
-                'compose/config/config_schema_v2.3.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v2.4.json',
-                'compose/config/config_schema_v2.4.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.0.json',
-                'compose/config/config_schema_v3.0.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.1.json',
-                'compose/config/config_schema_v3.1.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.2.json',
-                'compose/config/config_schema_v3.2.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.3.json',
-                'compose/config/config_schema_v3.3.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.4.json',
-                'compose/config/config_schema_v3.4.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.5.json',
-                'compose/config/config_schema_v3.5.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.6.json',
-                'compose/config/config_schema_v3.6.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.7.json',
-                'compose/config/config_schema_v3.7.json',
-                'DATA'
-            ),
-            (
-                'compose/config/config_schema_v3.8.json',
-                'compose/config/config_schema_v3.8.json',
+                'compose/config/config_schema_compose_spec.json',
+                'compose/config/config_schema_compose_spec.json',
                 'DATA'
                 'DATA'
             ),
             ),
             (
             (

+ 29 - 60
tests/acceptance/cli_test.py

@@ -20,6 +20,8 @@ from ..helpers import BUSYBOX_IMAGE_WITH_TAG
 from ..helpers import create_host_file
 from ..helpers import create_host_file
 from compose.cli.command import get_project
 from compose.cli.command import get_project
 from compose.config.errors import DuplicateOverrideFileFound
 from compose.config.errors import DuplicateOverrideFileFound
+from compose.const import COMPOSE_SPEC as VERSION
+from compose.const import COMPOSEFILE_V1 as V1
 from compose.container import Container
 from compose.container import Container
 from compose.project import OneOffFilter
 from compose.project import OneOffFilter
 from compose.utils import nanoseconds_from_time_seconds
 from compose.utils import nanoseconds_from_time_seconds
@@ -29,10 +31,6 @@ from tests.integration.testcases import is_cluster
 from tests.integration.testcases import no_cluster
 from tests.integration.testcases import no_cluster
 from tests.integration.testcases import pull_busybox
 from tests.integration.testcases import pull_busybox
 from tests.integration.testcases import SWARM_SKIP_RM_VOLUMES
 from tests.integration.testcases import SWARM_SKIP_RM_VOLUMES
-from tests.integration.testcases import v2_1_only
-from tests.integration.testcases import v2_2_only
-from tests.integration.testcases import v2_only
-from tests.integration.testcases import v3_only
 
 
 DOCKER_COMPOSE_EXECUTABLE = 'docker-compose'
 DOCKER_COMPOSE_EXECUTABLE = 'docker-compose'
 
 
@@ -42,7 +40,7 @@ ProcessResult = namedtuple('ProcessResult', 'stdout stderr')
 BUILD_CACHE_TEXT = 'Using cache'
 BUILD_CACHE_TEXT = 'Using cache'
 BUILD_PULL_TEXT = 'Status: Image is up to date for busybox:1.27.2'
 BUILD_PULL_TEXT = 'Status: Image is up to date for busybox:1.27.2'
 COMPOSE_COMPATIBILITY_DICT = {
 COMPOSE_COMPATIBILITY_DICT = {
-    'version': '2.3',
+    'version': str(VERSION),
     'volumes': {'foo': {'driver': 'default'}},
     'volumes': {'foo': {'driver': 'default'}},
     'networks': {'bar': {}},
     'networks': {'bar': {}},
     'services': {
     'services': {
@@ -287,7 +285,7 @@ services:
 
 
         output = yaml.safe_load(result.stdout)
         output = yaml.safe_load(result.stdout)
         expected = {
         expected = {
-            'version': '2.0',
+            'version': str(VERSION),
             'volumes': {'data': {'driver': 'local'}},
             'volumes': {'data': {'driver': 'local'}},
             'networks': {'front': {}},
             'networks': {'front': {}},
             'services': {
             'services': {
@@ -311,7 +309,7 @@ services:
         self.base_dir = 'tests/fixtures/restart'
         self.base_dir = 'tests/fixtures/restart'
         result = self.dispatch(['config'])
         result = self.dispatch(['config'])
         assert yaml.safe_load(result.stdout) == {
         assert yaml.safe_load(result.stdout) == {
-            'version': '2.0',
+            'version': str(VERSION),
             'services': {
             'services': {
                 'never': {
                 'never': {
                     'image': 'busybox',
                     'image': 'busybox',
@@ -343,10 +341,12 @@ services:
         assert 'networks' in json_result
         assert 'networks' in json_result
         assert json_result['networks'] == {
         assert json_result['networks'] == {
             'networks_foo': {
             'networks_foo': {
-                'external': True  # {'name': 'networks_foo'}
+                'external': True,
+                'name': 'networks_foo'
             },
             },
             'bar': {
             'bar': {
-                'external': {'name': 'networks_bar'}
+                'external': True,
+                'name': 'networks_bar'
             }
             }
         }
         }
 
 
@@ -355,14 +355,14 @@ services:
         result = self.dispatch(['config'])
         result = self.dispatch(['config'])
         json_result = yaml.safe_load(result.stdout)
         json_result = yaml.safe_load(result.stdout)
         assert json_result == {
         assert json_result == {
+            'version': str(VERSION),
             'services': {
             'services': {
                 'web': {
                 'web': {
                     'command': 'true',
                     'command': 'true',
                     'image': 'alpine:latest',
                     'image': 'alpine:latest',
-                    'ports': ['5643/tcp', '9999/tcp']
+                    'ports': [{'target': 5643}, {'target': 9999}]
                 }
                 }
-            },
-            'version': '2.4'
+            }
         }
         }
 
 
     def test_config_with_env_file(self):
     def test_config_with_env_file(self):
@@ -370,14 +370,14 @@ services:
         result = self.dispatch(['--env-file', '.env2', 'config'])
         result = self.dispatch(['--env-file', '.env2', 'config'])
         json_result = yaml.safe_load(result.stdout)
         json_result = yaml.safe_load(result.stdout)
         assert json_result == {
         assert json_result == {
+            'version': str(VERSION),
             'services': {
             'services': {
                 'web': {
                 'web': {
                     'command': 'false',
                     'command': 'false',
                     'image': 'alpine:latest',
                     'image': 'alpine:latest',
-                    'ports': ['5644/tcp', '9998/tcp']
+                    'ports': [{'target': 5644}, {'target': 9998}]
                 }
                 }
-            },
-            'version': '2.4'
+            }
         }
         }
 
 
     def test_config_with_dot_env_and_override_dir(self):
     def test_config_with_dot_env_and_override_dir(self):
@@ -385,14 +385,14 @@ services:
         result = self.dispatch(['--project-directory', 'alt/', 'config'])
         result = self.dispatch(['--project-directory', 'alt/', 'config'])
         json_result = yaml.safe_load(result.stdout)
         json_result = yaml.safe_load(result.stdout)
         assert json_result == {
         assert json_result == {
+            'version': str(VERSION),
             'services': {
             'services': {
                 'web': {
                 'web': {
                     'command': 'echo uwu',
                     'command': 'echo uwu',
                     'image': 'alpine:3.10.1',
                     'image': 'alpine:3.10.1',
-                    'ports': ['3341/tcp', '4449/tcp']
+                    'ports': [{'target': 3341}, {'target': 4449}]
                 }
                 }
-            },
-            'version': '2.4'
+            }
         }
         }
 
 
     def test_config_external_volume_v2(self):
     def test_config_external_volume_v2(self):
@@ -403,11 +403,11 @@ services:
         assert json_result['volumes'] == {
         assert json_result['volumes'] == {
             'foo': {
             'foo': {
                 'external': True,
                 'external': True,
+                'name': 'foo',
             },
             },
             'bar': {
             'bar': {
-                'external': {
-                    'name': 'some_bar',
-                },
+                'external': True,
+                'name': 'some_bar',
             }
             }
         }
         }
 
 
@@ -435,11 +435,11 @@ services:
         assert json_result['volumes'] == {
         assert json_result['volumes'] == {
             'foo': {
             'foo': {
                 'external': True,
                 'external': True,
+                'name': 'foo',
             },
             },
             'bar': {
             'bar': {
-                'external': {
-                    'name': 'some_bar',
-                },
+                'external': True,
+                'name': 'some_bar',
             }
             }
         }
         }
 
 
@@ -479,7 +479,7 @@ services:
         self.base_dir = 'tests/fixtures/v1-config'
         self.base_dir = 'tests/fixtures/v1-config'
         result = self.dispatch(['config'])
         result = self.dispatch(['config'])
         assert yaml.safe_load(result.stdout) == {
         assert yaml.safe_load(result.stdout) == {
-            'version': '2.1',
+            'version': str(V1),
             'services': {
             'services': {
                 'net': {
                 'net': {
                     'image': 'busybox',
                     'image': 'busybox',
@@ -498,13 +498,11 @@ services:
             },
             },
         }
         }
 
 
-    @v3_only()
     def test_config_v3(self):
     def test_config_v3(self):
         self.base_dir = 'tests/fixtures/v3-full'
         self.base_dir = 'tests/fixtures/v3-full'
         result = self.dispatch(['config'])
         result = self.dispatch(['config'])
-
         assert yaml.safe_load(result.stdout) == {
         assert yaml.safe_load(result.stdout) == {
-            'version': '3.5',
+            'version': str(VERSION),
             'volumes': {
             'volumes': {
                 'foobar': {
                 'foobar': {
                     'labels': {
                     'labels': {
@@ -576,12 +574,14 @@ services:
             },
             },
         }
         }
 
 
+    @pytest.mark.skip(reason='deprecated option')
     def test_config_compatibility_mode(self):
     def test_config_compatibility_mode(self):
         self.base_dir = 'tests/fixtures/compatibility-mode'
         self.base_dir = 'tests/fixtures/compatibility-mode'
         result = self.dispatch(['--compatibility', 'config'])
         result = self.dispatch(['--compatibility', 'config'])
 
 
         assert yaml.load(result.stdout) == COMPOSE_COMPATIBILITY_DICT
         assert yaml.load(result.stdout) == COMPOSE_COMPATIBILITY_DICT
 
 
+    @pytest.mark.skip(reason='deprecated option')
     @mock.patch.dict(os.environ)
     @mock.patch.dict(os.environ)
     def test_config_compatibility_mode_from_env(self):
     def test_config_compatibility_mode_from_env(self):
         self.base_dir = 'tests/fixtures/compatibility-mode'
         self.base_dir = 'tests/fixtures/compatibility-mode'
@@ -590,6 +590,7 @@ services:
 
 
         assert yaml.load(result.stdout) == COMPOSE_COMPATIBILITY_DICT
         assert yaml.load(result.stdout) == COMPOSE_COMPATIBILITY_DICT
 
 
+    @pytest.mark.skip(reason='deprecated option')
     @mock.patch.dict(os.environ)
     @mock.patch.dict(os.environ)
     def test_config_compatibility_mode_from_env_and_option_precedence(self):
     def test_config_compatibility_mode_from_env_and_option_precedence(self):
         self.base_dir = 'tests/fixtures/compatibility-mode'
         self.base_dir = 'tests/fixtures/compatibility-mode'
@@ -1018,7 +1019,6 @@ services:
         result = self.dispatch(['down', '--rmi', 'bogus'], returncode=1)
         result = self.dispatch(['down', '--rmi', 'bogus'], returncode=1)
         assert '--rmi flag must be' in result.stderr
         assert '--rmi flag must be' in result.stderr
 
 
-    @v2_only()
     def test_down(self):
     def test_down(self):
         self.base_dir = 'tests/fixtures/v2-full'
         self.base_dir = 'tests/fixtures/v2-full'
 
 
@@ -1103,7 +1103,6 @@ services:
         assert '{} exited with code 0'.format(simple_name) in result.stdout
         assert '{} exited with code 0'.format(simple_name) in result.stdout
         assert '{} exited with code 0'.format(another_name) in result.stdout
         assert '{} exited with code 0'.format(another_name) in result.stdout
 
 
-    @v2_only()
     def test_up(self):
     def test_up(self):
         self.base_dir = 'tests/fixtures/v2-simple'
         self.base_dir = 'tests/fixtures/v2-simple'
         self.dispatch(['up', '-d'], None)
         self.dispatch(['up', '-d'], None)
@@ -1135,7 +1134,6 @@ services:
             for service in services:
             for service in services:
                 assert self.lookup(container, service.name)
                 assert self.lookup(container, service.name)
 
 
-    @v2_only()
     def test_up_no_start(self):
     def test_up_no_start(self):
         self.base_dir = 'tests/fixtures/v2-full'
         self.base_dir = 'tests/fixtures/v2-full'
         self.dispatch(['up', '--no-start'], None)
         self.dispatch(['up', '--no-start'], None)
@@ -1166,7 +1164,6 @@ services:
         ]
         ]
         assert len(remote_volumes) > 0
         assert len(remote_volumes) > 0
 
 
-    @v2_only()
     def test_up_no_start_remove_orphans(self):
     def test_up_no_start_remove_orphans(self):
         self.base_dir = 'tests/fixtures/v2-simple'
         self.base_dir = 'tests/fixtures/v2-simple'
         self.dispatch(['up', '--no-start'], None)
         self.dispatch(['up', '--no-start'], None)
@@ -1182,7 +1179,6 @@ services:
             stopped=True) + next.containers(stopped=True)), services)
             stopped=True) + next.containers(stopped=True)), services)
         assert len(stopped2) == 1
         assert len(stopped2) == 1
 
 
-    @v2_only()
     def test_up_no_ansi(self):
     def test_up_no_ansi(self):
         self.base_dir = 'tests/fixtures/v2-simple'
         self.base_dir = 'tests/fixtures/v2-simple'
         result = self.dispatch(['--no-ansi', 'up', '-d'], None)
         result = self.dispatch(['--no-ansi', 'up', '-d'], None)
@@ -1190,7 +1186,6 @@ services:
         assert "%c[1A" % 27 not in result.stderr
         assert "%c[1A" % 27 not in result.stderr
         assert "%c[1B" % 27 not in result.stderr
         assert "%c[1B" % 27 not in result.stderr
 
 
-    @v2_only()
     def test_up_with_default_network_config(self):
     def test_up_with_default_network_config(self):
         filename = 'default-network-config.yml'
         filename = 'default-network-config.yml'
 
 
@@ -1204,7 +1199,6 @@ services:
 
 
         assert networks[0]['Options']['com.docker.network.bridge.enable_icc'] == 'false'
         assert networks[0]['Options']['com.docker.network.bridge.enable_icc'] == 'false'
 
 
-    @v2_only()
     def test_up_with_network_aliases(self):
     def test_up_with_network_aliases(self):
         filename = 'network-aliases.yml'
         filename = 'network-aliases.yml'
         self.base_dir = 'tests/fixtures/networks'
         self.base_dir = 'tests/fixtures/networks'
@@ -1232,7 +1226,6 @@ services:
         assert 'forward_facing' in front_aliases
         assert 'forward_facing' in front_aliases
         assert 'ahead' in front_aliases
         assert 'ahead' in front_aliases
 
 
-    @v2_only()
     def test_up_with_network_internal(self):
     def test_up_with_network_internal(self):
         self.require_api_version('1.23')
         self.require_api_version('1.23')
         filename = 'network-internal.yml'
         filename = 'network-internal.yml'
@@ -1250,7 +1243,6 @@ services:
 
 
         assert networks[0]['Internal'] is True
         assert networks[0]['Internal'] is True
 
 
-    @v2_only()
     def test_up_with_network_static_addresses(self):
     def test_up_with_network_static_addresses(self):
         filename = 'network-static-addresses.yml'
         filename = 'network-static-addresses.yml'
         ipv4_address = '172.16.100.100'
         ipv4_address = '172.16.100.100'
@@ -1274,7 +1266,6 @@ services:
         assert ipv4_address in ipam_config.values()
         assert ipv4_address in ipam_config.values()
         assert ipv6_address in ipam_config.values()
         assert ipv6_address in ipam_config.values()
 
 
-    @v2_only()
     def test_up_with_networks(self):
     def test_up_with_networks(self):
         self.base_dir = 'tests/fixtures/networks'
         self.base_dir = 'tests/fixtures/networks'
         self.dispatch(['up', '-d'], None)
         self.dispatch(['up', '-d'], None)
@@ -1322,7 +1313,6 @@ services:
         # app has aliased db to "database"
         # app has aliased db to "database"
         assert self.lookup(app_container, "database")
         assert self.lookup(app_container, "database")
 
 
-    @v2_only()
     def test_up_missing_network(self):
     def test_up_missing_network(self):
         self.base_dir = 'tests/fixtures/networks'
         self.base_dir = 'tests/fixtures/networks'
 
 
@@ -1332,7 +1322,6 @@ services:
 
 
         assert 'Service "web" uses an undefined network "foo"' in result.stderr
         assert 'Service "web" uses an undefined network "foo"' in result.stderr
 
 
-    @v2_only()
     @no_cluster('container networks not supported in Swarm')
     @no_cluster('container networks not supported in Swarm')
     def test_up_with_network_mode(self):
     def test_up_with_network_mode(self):
         c = self.client.create_container(
         c = self.client.create_container(
@@ -1371,7 +1360,6 @@ services:
         assert not container_mode_container.get('NetworkSettings.Networks')
         assert not container_mode_container.get('NetworkSettings.Networks')
         assert container_mode_container.get('HostConfig.NetworkMode') == container_mode_source
         assert container_mode_container.get('HostConfig.NetworkMode') == container_mode_source
 
 
-    @v2_only()
     def test_up_external_networks(self):
     def test_up_external_networks(self):
         filename = 'external-networks.yml'
         filename = 'external-networks.yml'
 
 
@@ -1395,7 +1383,6 @@ services:
         container = self.project.containers()[0]
         container = self.project.containers()[0]
         assert sorted(list(container.get('NetworkSettings.Networks'))) == sorted(network_names)
         assert sorted(list(container.get('NetworkSettings.Networks'))) == sorted(network_names)
 
 
-    @v2_only()
     def test_up_with_external_default_network(self):
     def test_up_with_external_default_network(self):
         filename = 'external-default.yml'
         filename = 'external-default.yml'
 
 
@@ -1418,7 +1405,6 @@ services:
         container = self.project.containers()[0]
         container = self.project.containers()[0]
         assert list(container.get('NetworkSettings.Networks')) == [network_name]
         assert list(container.get('NetworkSettings.Networks')) == [network_name]
 
 
-    @v2_1_only()
     def test_up_with_network_labels(self):
     def test_up_with_network_labels(self):
         filename = 'network-label.yml'
         filename = 'network-label.yml'
 
 
@@ -1438,7 +1424,6 @@ services:
         assert 'label_key' in networks[0]['Labels']
         assert 'label_key' in networks[0]['Labels']
         assert networks[0]['Labels']['label_key'] == 'label_val'
         assert networks[0]['Labels']['label_key'] == 'label_val'
 
 
-    @v2_1_only()
     def test_up_with_volume_labels(self):
     def test_up_with_volume_labels(self):
         filename = 'volume-label.yml'
         filename = 'volume-label.yml'
 
 
@@ -1458,7 +1443,6 @@ services:
         assert 'label_key' in volumes[0]['Labels']
         assert 'label_key' in volumes[0]['Labels']
         assert volumes[0]['Labels']['label_key'] == 'label_val'
         assert volumes[0]['Labels']['label_key'] == 'label_val'
 
 
-    @v2_only()
     def test_up_no_services(self):
     def test_up_no_services(self):
         self.base_dir = 'tests/fixtures/no-services'
         self.base_dir = 'tests/fixtures/no-services'
         self.dispatch(['up', '-d'], None)
         self.dispatch(['up', '-d'], None)
@@ -1515,7 +1499,6 @@ services:
             bar_container.id
             bar_container.id
         )
         )
 
 
-    @v3_only()
     def test_up_with_healthcheck(self):
     def test_up_with_healthcheck(self):
         def wait_on_health_status(container, status):
         def wait_on_health_status(container, status):
             def condition():
             def condition():
@@ -1649,7 +1632,6 @@ services:
         os.kill(proc.pid, signal.SIGTERM)
         os.kill(proc.pid, signal.SIGTERM)
         wait_on_condition(ContainerCountCondition(self.project, 0))
         wait_on_condition(ContainerCountCondition(self.project, 0))
 
 
-    @v2_only()
     def test_up_handles_force_shutdown(self):
     def test_up_handles_force_shutdown(self):
         self.base_dir = 'tests/fixtures/sleeps-composefile'
         self.base_dir = 'tests/fixtures/sleeps-composefile'
         proc = start_process(self.base_dir, ['up', '-t', '200'])
         proc = start_process(self.base_dir, ['up', '-t', '200'])
@@ -1674,7 +1656,6 @@ services:
         proc.wait()
         proc.wait()
         assert proc.returncode == 1
         assert proc.returncode == 1
 
 
-    @v2_only()
     @no_cluster('Container PID mode does not work across clusters')
     @no_cluster('Container PID mode does not work across clusters')
     def test_up_with_pid_mode(self):
     def test_up_with_pid_mode(self):
         c = self.client.create_container(
         c = self.client.create_container(
@@ -1738,7 +1719,6 @@ services:
         assert stdout == "operator\n"
         assert stdout == "operator\n"
         assert stderr == ""
         assert stderr == ""
 
 
-    @v3_only()
     def test_exec_workdir(self):
     def test_exec_workdir(self):
         self.base_dir = 'tests/fixtures/links-composefile'
         self.base_dir = 'tests/fixtures/links-composefile'
         os.environ['COMPOSE_API_VERSION'] = '1.35'
         os.environ['COMPOSE_API_VERSION'] = '1.35'
@@ -1748,7 +1728,6 @@ services:
         stdout, stderr = self.dispatch(['exec', '-T', '--workdir', '/etc', 'console', 'ls'])
         stdout, stderr = self.dispatch(['exec', '-T', '--workdir', '/etc', 'console', 'ls'])
         assert 'passwd' in stdout
         assert 'passwd' in stdout
 
 
-    @v2_2_only()
     def test_exec_service_with_environment_overridden(self):
     def test_exec_service_with_environment_overridden(self):
         name = 'service'
         name = 'service'
         self.base_dir = 'tests/fixtures/environment-exec'
         self.base_dir = 'tests/fixtures/environment-exec'
@@ -1793,7 +1772,6 @@ services:
         assert len(db.containers()) == 1
         assert len(db.containers()) == 1
         assert len(console.containers()) == 0
         assert len(console.containers()) == 0
 
 
-    @v2_only()
     def test_run_service_with_dependencies(self):
     def test_run_service_with_dependencies(self):
         self.base_dir = 'tests/fixtures/v2-dependencies'
         self.base_dir = 'tests/fixtures/v2-dependencies'
         self.dispatch(['run', 'web', '/bin/true'], None)
         self.dispatch(['run', 'web', '/bin/true'], None)
@@ -2105,7 +2083,6 @@ services:
         container = service.containers(stopped=True, one_off=True)[0]
         container = service.containers(stopped=True, one_off=True)[0]
         assert workdir == container.get('Config.WorkingDir')
         assert workdir == container.get('Config.WorkingDir')
 
 
-    @v2_only()
     def test_run_service_with_use_aliases(self):
     def test_run_service_with_use_aliases(self):
         filename = 'network-aliases.yml'
         filename = 'network-aliases.yml'
         self.base_dir = 'tests/fixtures/networks'
         self.base_dir = 'tests/fixtures/networks'
@@ -2127,7 +2104,6 @@ services:
         assert 'forward_facing' in front_aliases
         assert 'forward_facing' in front_aliases
         assert 'ahead' in front_aliases
         assert 'ahead' in front_aliases
 
 
-    @v2_only()
     def test_run_interactive_connects_to_network(self):
     def test_run_interactive_connects_to_network(self):
         self.base_dir = 'tests/fixtures/networks'
         self.base_dir = 'tests/fixtures/networks'
 
 
@@ -2153,7 +2129,6 @@ services:
                 aliases = set(config['Aliases'] or []) - {container.short_id}
                 aliases = set(config['Aliases'] or []) - {container.short_id}
                 assert not aliases
                 assert not aliases
 
 
-    @v2_only()
     def test_run_detached_connects_to_network(self):
     def test_run_detached_connects_to_network(self):
         self.base_dir = 'tests/fixtures/networks'
         self.base_dir = 'tests/fixtures/networks'
         self.dispatch(['up', '-d'])
         self.dispatch(['up', '-d'])
@@ -2332,7 +2307,6 @@ services:
         assert 'failed' in result.stderr
         assert 'failed' in result.stderr
         assert 'No containers to start' in result.stderr
         assert 'No containers to start' in result.stderr
 
 
-    @v2_only()
     def test_up_logging(self):
     def test_up_logging(self):
         self.base_dir = 'tests/fixtures/logging-composefile'
         self.base_dir = 'tests/fixtures/logging-composefile'
         self.dispatch(['up', '-d'])
         self.dispatch(['up', '-d'])
@@ -2563,11 +2537,6 @@ services:
         assert len(project.get_service('simple').containers()) == 0
         assert len(project.get_service('simple').containers()) == 0
         assert len(project.get_service('another').containers()) == 0
         assert len(project.get_service('another').containers()) == 0
 
 
-    def test_scale_v2_2(self):
-        self.base_dir = 'tests/fixtures/scale'
-        result = self.dispatch(['scale', 'web=1'], returncode=1)
-        assert 'incompatible with the v2.2 format' in result.stderr
-
     def test_up_scale_scale_up(self):
     def test_up_scale_scale_up(self):
         self.base_dir = 'tests/fixtures/scale'
         self.base_dir = 'tests/fixtures/scale'
         project = self.project
         project = self.project

+ 3 - 86
tests/integration/project_test.py

@@ -21,11 +21,7 @@ from compose.config import ConfigurationError
 from compose.config import types
 from compose.config import types
 from compose.config.types import VolumeFromSpec
 from compose.config.types import VolumeFromSpec
 from compose.config.types import VolumeSpec
 from compose.config.types import VolumeSpec
-from compose.const import COMPOSEFILE_V2_0 as V2_0
-from compose.const import COMPOSEFILE_V2_1 as V2_1
-from compose.const import COMPOSEFILE_V2_2 as V2_2
-from compose.const import COMPOSEFILE_V2_3 as V2_3
-from compose.const import COMPOSEFILE_V3_1 as V3_1
+from compose.const import COMPOSE_SPEC as VERSION
 from compose.const import LABEL_PROJECT
 from compose.const import LABEL_PROJECT
 from compose.const import LABEL_SERVICE
 from compose.const import LABEL_SERVICE
 from compose.container import Container
 from compose.container import Container
@@ -37,16 +33,11 @@ from compose.service import ConvergenceStrategy
 from tests.integration.testcases import if_runtime_available
 from tests.integration.testcases import if_runtime_available
 from tests.integration.testcases import is_cluster
 from tests.integration.testcases import is_cluster
 from tests.integration.testcases import no_cluster
 from tests.integration.testcases import no_cluster
-from tests.integration.testcases import v2_1_only
-from tests.integration.testcases import v2_2_only
-from tests.integration.testcases import v2_3_only
-from tests.integration.testcases import v2_only
-from tests.integration.testcases import v3_only
 
 
 
 
 def build_config(**kwargs):
 def build_config(**kwargs):
     return config.Config(
     return config.Config(
-        version=kwargs.get('version'),
+        version=kwargs.get('version', VERSION),
         services=kwargs.get('services'),
         services=kwargs.get('services'),
         volumes=kwargs.get('volumes'),
         volumes=kwargs.get('volumes'),
         networks=kwargs.get('networks'),
         networks=kwargs.get('networks'),
@@ -106,7 +97,6 @@ class ProjectTest(DockerClientTestCase):
 
 
     def test_parallel_pull_with_no_image(self):
     def test_parallel_pull_with_no_image(self):
         config_data = build_config(
         config_data = build_config(
-            version=V2_3,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'build': {'context': '.'},
                 'build': {'context': '.'},
@@ -162,14 +152,12 @@ class ProjectTest(DockerClientTestCase):
         db = project.get_service('db')
         db = project.get_service('db')
         assert db._get_volumes_from() == [data_container.id + ':rw']
         assert db._get_volumes_from() == [data_container.id + ':rw']
 
 
-    @v2_only()
     @no_cluster('container networks not supported in Swarm')
     @no_cluster('container networks not supported in Swarm')
     def test_network_mode_from_service(self):
     def test_network_mode_from_service(self):
         project = Project.from_config(
         project = Project.from_config(
             name='composetest',
             name='composetest',
             client=self.client,
             client=self.client,
             config_data=load_config({
             config_data=load_config({
-                'version': str(V2_0),
                 'services': {
                 'services': {
                     'net': {
                     'net': {
                         'image': BUSYBOX_IMAGE_WITH_TAG,
                         'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -190,14 +178,12 @@ class ProjectTest(DockerClientTestCase):
         net = project.get_service('net')
         net = project.get_service('net')
         assert web.network_mode.mode == 'container:' + net.containers()[0].id
         assert web.network_mode.mode == 'container:' + net.containers()[0].id
 
 
-    @v2_only()
     @no_cluster('container networks not supported in Swarm')
     @no_cluster('container networks not supported in Swarm')
     def test_network_mode_from_container(self):
     def test_network_mode_from_container(self):
         def get_project():
         def get_project():
             return Project.from_config(
             return Project.from_config(
                 name='composetest',
                 name='composetest',
                 config_data=load_config({
                 config_data=load_config({
-                    'version': str(V2_0),
                     'services': {
                     'services': {
                         'web': {
                         'web': {
                             'image': BUSYBOX_IMAGE_WITH_TAG,
                             'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -448,7 +434,6 @@ class ProjectTest(DockerClientTestCase):
         assert db_container.id != old_db_id
         assert db_container.id != old_db_id
         assert db_container.get('Volumes./etc') == db_volume_path
         assert db_container.get('Volumes./etc') == db_volume_path
 
 
-    @v2_3_only()
     def test_recreate_preserves_mounts(self):
     def test_recreate_preserves_mounts(self):
         web = self.create_service('web')
         web = self.create_service('web')
         db = self.create_service('db', volumes=[types.MountSpec(type='volume', target='/etc')])
         db = self.create_service('db', volumes=[types.MountSpec(type='volume', target='/etc')])
@@ -656,10 +641,8 @@ class ProjectTest(DockerClientTestCase):
         service = project.get_service('web')
         service = project.get_service('web')
         assert len(service.containers()) == 1
         assert len(service.containers()) == 1
 
 
-    @v2_only()
     def test_project_up_networks(self):
     def test_project_up_networks(self):
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -701,10 +684,8 @@ class ProjectTest(DockerClientTestCase):
         foo_data = self.client.inspect_network('composetest_foo')
         foo_data = self.client.inspect_network('composetest_foo')
         assert foo_data['Driver'] == 'bridge'
         assert foo_data['Driver'] == 'bridge'
 
 
-    @v2_only()
     def test_up_with_ipam_config(self):
     def test_up_with_ipam_config(self):
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -761,10 +742,8 @@ class ProjectTest(DockerClientTestCase):
             }],
             }],
         }
         }
 
 
-    @v2_only()
     def test_up_with_ipam_options(self):
     def test_up_with_ipam_options(self):
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -796,10 +775,8 @@ class ProjectTest(DockerClientTestCase):
             "com.docker.compose.network.test": "9-29-045"
             "com.docker.compose.network.test": "9-29-045"
         }
         }
 
 
-    @v2_1_only()
     def test_up_with_network_static_addresses(self):
     def test_up_with_network_static_addresses(self):
         config_data = build_config(
         config_data = build_config(
-            version=V2_1,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -845,13 +822,11 @@ class ProjectTest(DockerClientTestCase):
         assert ipam_config.get('IPv4Address') == '172.16.100.100'
         assert ipam_config.get('IPv4Address') == '172.16.100.100'
         assert ipam_config.get('IPv6Address') == 'fe80::1001:102'
         assert ipam_config.get('IPv6Address') == 'fe80::1001:102'
 
 
-    @v2_3_only()
     def test_up_with_network_priorities(self):
     def test_up_with_network_priorities(self):
         mac_address = '74:6f:75:68:6f:75'
         mac_address = '74:6f:75:68:6f:75'
 
 
         def get_config_data(p1, p2, p3):
         def get_config_data(p1, p2, p3):
             return build_config(
             return build_config(
-                version=V2_3,
                 services=[{
                 services=[{
                     'name': 'web',
                     'name': 'web',
                     'image': BUSYBOX_IMAGE_WITH_TAG,
                     'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -910,11 +885,9 @@ class ProjectTest(DockerClientTestCase):
         net_config = service_container.inspect()['NetworkSettings']['Networks']['composetest_n3']
         net_config = service_container.inspect()['NetworkSettings']['Networks']['composetest_n3']
         assert net_config['MacAddress'] == mac_address
         assert net_config['MacAddress'] == mac_address
 
 
-    @v2_1_only()
     def test_up_with_enable_ipv6(self):
     def test_up_with_enable_ipv6(self):
         self.require_api_version('1.23')
         self.require_api_version('1.23')
         config_data = build_config(
         config_data = build_config(
-            version=V2_1,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -954,10 +927,8 @@ class ProjectTest(DockerClientTestCase):
                        get('IPAMConfig', {}))
                        get('IPAMConfig', {}))
         assert ipam_config.get('IPv6Address') == 'fe80::1001:102'
         assert ipam_config.get('IPv6Address') == 'fe80::1001:102'
 
 
-    @v2_only()
     def test_up_with_network_static_addresses_missing_subnet(self):
     def test_up_with_network_static_addresses_missing_subnet(self):
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -990,10 +961,8 @@ class ProjectTest(DockerClientTestCase):
         with pytest.raises(ProjectError):
         with pytest.raises(ProjectError):
             project.up()
             project.up()
 
 
-    @v2_1_only()
     def test_up_with_network_link_local_ips(self):
     def test_up_with_network_link_local_ips(self):
         config_data = build_config(
         config_data = build_config(
-            version=V2_1,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1025,10 +994,8 @@ class ProjectTest(DockerClientTestCase):
         assert 'LinkLocalIPs' in ipam_config
         assert 'LinkLocalIPs' in ipam_config
         assert ipam_config['LinkLocalIPs'] == ['169.254.8.8']
         assert ipam_config['LinkLocalIPs'] == ['169.254.8.8']
 
 
-    @v2_1_only()
     def test_up_with_custom_name_resources(self):
     def test_up_with_custom_name_resources(self):
         config_data = build_config(
         config_data = build_config(
-            version=V2_2,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'volumes': [VolumeSpec.parse('foo:/container-path')],
                 'volumes': [VolumeSpec.parse('foo:/container-path')],
@@ -1062,11 +1029,9 @@ class ProjectTest(DockerClientTestCase):
         assert network['Labels']['com.docker.compose.test_value'] == 'sharpdressedman'
         assert network['Labels']['com.docker.compose.test_value'] == 'sharpdressedman'
         assert volume['Labels']['com.docker.compose.test_value'] == 'thefuror'
         assert volume['Labels']['com.docker.compose.test_value'] == 'thefuror'
 
 
-    @v2_1_only()
     def test_up_with_isolation(self):
     def test_up_with_isolation(self):
         self.require_api_version('1.24')
         self.require_api_version('1.24')
         config_data = build_config(
         config_data = build_config(
-            version=V2_1,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1082,11 +1047,9 @@ class ProjectTest(DockerClientTestCase):
         service_container = project.get_service('web').containers(stopped=True)[0]
         service_container = project.get_service('web').containers(stopped=True)[0]
         assert service_container.inspect()['HostConfig']['Isolation'] == 'default'
         assert service_container.inspect()['HostConfig']['Isolation'] == 'default'
 
 
-    @v2_1_only()
     def test_up_with_invalid_isolation(self):
     def test_up_with_invalid_isolation(self):
         self.require_api_version('1.24')
         self.require_api_version('1.24')
         config_data = build_config(
         config_data = build_config(
-            version=V2_1,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1101,12 +1064,10 @@ class ProjectTest(DockerClientTestCase):
         with pytest.raises(ProjectError):
         with pytest.raises(ProjectError):
             project.up()
             project.up()
 
 
-    @v2_3_only()
     @if_runtime_available('runc')
     @if_runtime_available('runc')
     def test_up_with_runtime(self):
     def test_up_with_runtime(self):
         self.require_api_version('1.30')
         self.require_api_version('1.30')
         config_data = build_config(
         config_data = build_config(
-            version=V2_3,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1122,11 +1083,9 @@ class ProjectTest(DockerClientTestCase):
         service_container = project.get_service('web').containers(stopped=True)[0]
         service_container = project.get_service('web').containers(stopped=True)[0]
         assert service_container.inspect()['HostConfig']['Runtime'] == 'runc'
         assert service_container.inspect()['HostConfig']['Runtime'] == 'runc'
 
 
-    @v2_3_only()
     def test_up_with_invalid_runtime(self):
     def test_up_with_invalid_runtime(self):
         self.require_api_version('1.30')
         self.require_api_version('1.30')
         config_data = build_config(
         config_data = build_config(
-            version=V2_3,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1141,12 +1100,10 @@ class ProjectTest(DockerClientTestCase):
         with pytest.raises(ProjectError):
         with pytest.raises(ProjectError):
             project.up()
             project.up()
 
 
-    @v2_3_only()
     @if_runtime_available('nvidia')
     @if_runtime_available('nvidia')
     def test_up_with_nvidia_runtime(self):
     def test_up_with_nvidia_runtime(self):
         self.require_api_version('1.30')
         self.require_api_version('1.30')
         config_data = build_config(
         config_data = build_config(
-            version=V2_3,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1162,11 +1119,9 @@ class ProjectTest(DockerClientTestCase):
         service_container = project.get_service('web').containers(stopped=True)[0]
         service_container = project.get_service('web').containers(stopped=True)[0]
         assert service_container.inspect()['HostConfig']['Runtime'] == 'nvidia'
         assert service_container.inspect()['HostConfig']['Runtime'] == 'nvidia'
 
 
-    @v2_only()
     def test_project_up_with_network_internal(self):
     def test_project_up_with_network_internal(self):
         self.require_api_version('1.23')
         self.require_api_version('1.23')
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1188,14 +1143,12 @@ class ProjectTest(DockerClientTestCase):
 
 
         assert network['Internal'] is True
         assert network['Internal'] is True
 
 
-    @v2_1_only()
     def test_project_up_with_network_label(self):
     def test_project_up_with_network_label(self):
         self.require_api_version('1.23')
         self.require_api_version('1.23')
 
 
         network_name = 'network_with_label'
         network_name = 'network_with_label'
 
 
         config_data = build_config(
         config_data = build_config(
-            version=V2_1,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1223,12 +1176,10 @@ class ProjectTest(DockerClientTestCase):
         assert 'label_key' in networks[0]['Labels']
         assert 'label_key' in networks[0]['Labels']
         assert networks[0]['Labels']['label_key'] == 'label_val'
         assert networks[0]['Labels']['label_key'] == 'label_val'
 
 
-    @v2_only()
     def test_project_up_volumes(self):
     def test_project_up_volumes(self):
         vol_name = '{0:x}'.format(random.getrandbits(32))
         vol_name = '{0:x}'.format(random.getrandbits(32))
         full_vol_name = 'composetest_{0}'.format(vol_name)
         full_vol_name = 'composetest_{0}'.format(vol_name)
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1248,14 +1199,12 @@ class ProjectTest(DockerClientTestCase):
         assert volume_data['Name'].split('/')[-1] == full_vol_name
         assert volume_data['Name'].split('/')[-1] == full_vol_name
         assert volume_data['Driver'] == 'local'
         assert volume_data['Driver'] == 'local'
 
 
-    @v2_1_only()
     def test_project_up_with_volume_labels(self):
     def test_project_up_with_volume_labels(self):
         self.require_api_version('1.23')
         self.require_api_version('1.23')
 
 
         volume_name = 'volume_with_label'
         volume_name = 'volume_with_label'
 
 
         config_data = build_config(
         config_data = build_config(
-            version=V2_1,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1290,12 +1239,10 @@ class ProjectTest(DockerClientTestCase):
         assert 'label_key' in volumes[0]['Labels']
         assert 'label_key' in volumes[0]['Labels']
         assert volumes[0]['Labels']['label_key'] == 'label_val'
         assert volumes[0]['Labels']['label_key'] == 'label_val'
 
 
-    @v2_only()
     def test_project_up_logging_with_multiple_files(self):
     def test_project_up_logging_with_multiple_files(self):
         base_file = config.ConfigFile(
         base_file = config.ConfigFile(
             'base.yml',
             'base.yml',
             {
             {
-                'version': str(V2_0),
                 'services': {
                 'services': {
                     'simple': {'image': BUSYBOX_IMAGE_WITH_TAG, 'command': 'top'},
                     'simple': {'image': BUSYBOX_IMAGE_WITH_TAG, 'command': 'top'},
                     'another': {
                     'another': {
@@ -1314,7 +1261,6 @@ class ProjectTest(DockerClientTestCase):
         override_file = config.ConfigFile(
         override_file = config.ConfigFile(
             'override.yml',
             'override.yml',
             {
             {
-                'version': str(V2_0),
                 'services': {
                 'services': {
                     'another': {
                     'another': {
                         'logging': {
                         'logging': {
@@ -1342,12 +1288,10 @@ class ProjectTest(DockerClientTestCase):
         assert log_config
         assert log_config
         assert log_config.get('Type') == 'none'
         assert log_config.get('Type') == 'none'
 
 
-    @v2_only()
     def test_project_up_port_mappings_with_multiple_files(self):
     def test_project_up_port_mappings_with_multiple_files(self):
         base_file = config.ConfigFile(
         base_file = config.ConfigFile(
             'base.yml',
             'base.yml',
             {
             {
-                'version': str(V2_0),
                 'services': {
                 'services': {
                     'simple': {
                     'simple': {
                         'image': BUSYBOX_IMAGE_WITH_TAG,
                         'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1360,7 +1304,6 @@ class ProjectTest(DockerClientTestCase):
         override_file = config.ConfigFile(
         override_file = config.ConfigFile(
             'override.yml',
             'override.yml',
             {
             {
-                'version': str(V2_0),
                 'services': {
                 'services': {
                     'simple': {
                     'simple': {
                         'ports': ['1234:1234']
                         'ports': ['1234:1234']
@@ -1378,10 +1321,8 @@ class ProjectTest(DockerClientTestCase):
         containers = project.containers()
         containers = project.containers()
         assert len(containers) == 1
         assert len(containers) == 1
 
 
-    @v2_2_only()
     def test_project_up_config_scale(self):
     def test_project_up_config_scale(self):
         config_data = build_config(
         config_data = build_config(
-            version=V2_2,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1406,12 +1347,10 @@ class ProjectTest(DockerClientTestCase):
         project.up()
         project.up()
         assert len(project.containers()) == 3
         assert len(project.containers()) == 3
 
 
-    @v2_only()
     def test_initialize_volumes(self):
     def test_initialize_volumes(self):
         vol_name = '{0:x}'.format(random.getrandbits(32))
         vol_name = '{0:x}'.format(random.getrandbits(32))
         full_vol_name = 'composetest_{0}'.format(vol_name)
         full_vol_name = 'composetest_{0}'.format(vol_name)
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1430,12 +1369,10 @@ class ProjectTest(DockerClientTestCase):
         assert volume_data['Name'].split('/')[-1] == full_vol_name
         assert volume_data['Name'].split('/')[-1] == full_vol_name
         assert volume_data['Driver'] == 'local'
         assert volume_data['Driver'] == 'local'
 
 
-    @v2_only()
     def test_project_up_implicit_volume_driver(self):
     def test_project_up_implicit_volume_driver(self):
         vol_name = '{0:x}'.format(random.getrandbits(32))
         vol_name = '{0:x}'.format(random.getrandbits(32))
         full_vol_name = 'composetest_{0}'.format(vol_name)
         full_vol_name = 'composetest_{0}'.format(vol_name)
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1454,12 +1391,10 @@ class ProjectTest(DockerClientTestCase):
         assert volume_data['Name'].split('/')[-1] == full_vol_name
         assert volume_data['Name'].split('/')[-1] == full_vol_name
         assert volume_data['Driver'] == 'local'
         assert volume_data['Driver'] == 'local'
 
 
-    @v3_only()
     def test_project_up_with_secrets(self):
     def test_project_up_with_secrets(self):
         node = create_host_file(self.client, os.path.abspath('tests/fixtures/secrets/default'))
         node = create_host_file(self.client, os.path.abspath('tests/fixtures/secrets/default'))
 
 
         config_data = build_config(
         config_data = build_config(
-            version=V3_1,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1491,12 +1426,10 @@ class ProjectTest(DockerClientTestCase):
         output = container.logs()
         output = container.logs()
         assert output == b"This is the secret\n"
         assert output == b"This is the secret\n"
 
 
-    @v3_only()
     def test_project_up_with_added_secrets(self):
     def test_project_up_with_added_secrets(self):
         node = create_host_file(self.client, os.path.abspath('tests/fixtures/secrets/default'))
         node = create_host_file(self.client, os.path.abspath('tests/fixtures/secrets/default'))
 
 
         config_input1 = {
         config_input1 = {
-            'version': V3_1,
             'services': [
             'services': [
                 {
                 {
                     'name': 'web',
                     'name': 'web',
@@ -1545,12 +1478,11 @@ class ProjectTest(DockerClientTestCase):
         output = container.logs()
         output = container.logs()
         assert output == b"This is the secret\n"
         assert output == b"This is the secret\n"
 
 
-    @v2_only()
     def test_initialize_volumes_invalid_volume_driver(self):
     def test_initialize_volumes_invalid_volume_driver(self):
         vol_name = '{0:x}'.format(random.getrandbits(32))
         vol_name = '{0:x}'.format(random.getrandbits(32))
 
 
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
+            version=VERSION,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1566,14 +1498,12 @@ class ProjectTest(DockerClientTestCase):
         with pytest.raises(APIError if is_cluster(self.client) else config.ConfigurationError):
         with pytest.raises(APIError if is_cluster(self.client) else config.ConfigurationError):
             project.volumes.initialize()
             project.volumes.initialize()
 
 
-    @v2_only()
     @no_cluster('inspect volume by name defect on Swarm Classic')
     @no_cluster('inspect volume by name defect on Swarm Classic')
     def test_initialize_volumes_updated_driver(self):
     def test_initialize_volumes_updated_driver(self):
         vol_name = '{0:x}'.format(random.getrandbits(32))
         vol_name = '{0:x}'.format(random.getrandbits(32))
         full_vol_name = 'composetest_{0}'.format(vol_name)
         full_vol_name = 'composetest_{0}'.format(vol_name)
 
 
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1605,7 +1535,6 @@ class ProjectTest(DockerClientTestCase):
             vol_name
             vol_name
         ) in str(e.value)
         ) in str(e.value)
 
 
-    @v2_only()
     @no_cluster('inspect volume by name defect on Swarm Classic')
     @no_cluster('inspect volume by name defect on Swarm Classic')
     def test_initialize_volumes_updated_driver_opts(self):
     def test_initialize_volumes_updated_driver_opts(self):
         vol_name = '{0:x}'.format(random.getrandbits(32))
         vol_name = '{0:x}'.format(random.getrandbits(32))
@@ -1615,7 +1544,6 @@ class ProjectTest(DockerClientTestCase):
         driver_opts = {'o': 'bind', 'device': tmpdir, 'type': 'none'}
         driver_opts = {'o': 'bind', 'device': tmpdir, 'type': 'none'}
 
 
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1651,13 +1579,11 @@ class ProjectTest(DockerClientTestCase):
             vol_name, driver_opts['device']
             vol_name, driver_opts['device']
         ) in str(e.value)
         ) in str(e.value)
 
 
-    @v2_only()
     def test_initialize_volumes_updated_blank_driver(self):
     def test_initialize_volumes_updated_blank_driver(self):
         vol_name = '{0:x}'.format(random.getrandbits(32))
         vol_name = '{0:x}'.format(random.getrandbits(32))
         full_vol_name = 'composetest_{0}'.format(vol_name)
         full_vol_name = 'composetest_{0}'.format(vol_name)
 
 
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1688,7 +1614,6 @@ class ProjectTest(DockerClientTestCase):
         assert volume_data['Name'].split('/')[-1] == full_vol_name
         assert volume_data['Name'].split('/')[-1] == full_vol_name
         assert volume_data['Driver'] == 'local'
         assert volume_data['Driver'] == 'local'
 
 
-    @v2_only()
     @no_cluster('inspect volume by name defect on Swarm Classic')
     @no_cluster('inspect volume by name defect on Swarm Classic')
     def test_initialize_volumes_external_volumes(self):
     def test_initialize_volumes_external_volumes(self):
         # Use composetest_ prefix so it gets garbage-collected in tearDown()
         # Use composetest_ prefix so it gets garbage-collected in tearDown()
@@ -1696,7 +1621,6 @@ class ProjectTest(DockerClientTestCase):
         full_vol_name = 'composetest_{0}'.format(vol_name)
         full_vol_name = 'composetest_{0}'.format(vol_name)
         self.client.create_volume(vol_name)
         self.client.create_volume(vol_name)
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1715,12 +1639,10 @@ class ProjectTest(DockerClientTestCase):
         with pytest.raises(NotFound):
         with pytest.raises(NotFound):
             self.client.inspect_volume(full_vol_name)
             self.client.inspect_volume(full_vol_name)
 
 
-    @v2_only()
     def test_initialize_volumes_inexistent_external_volume(self):
     def test_initialize_volumes_inexistent_external_volume(self):
         vol_name = '{0:x}'.format(random.getrandbits(32))
         vol_name = '{0:x}'.format(random.getrandbits(32))
 
 
         config_data = build_config(
         config_data = build_config(
-            version=V2_0,
             services=[{
             services=[{
                 'name': 'web',
                 'name': 'web',
                 'image': BUSYBOX_IMAGE_WITH_TAG,
                 'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1740,7 +1662,6 @@ class ProjectTest(DockerClientTestCase):
             vol_name
             vol_name
         ) in str(e.value)
         ) in str(e.value)
 
 
-    @v2_only()
     def test_project_up_named_volumes_in_binds(self):
     def test_project_up_named_volumes_in_binds(self):
         vol_name = '{0:x}'.format(random.getrandbits(32))
         vol_name = '{0:x}'.format(random.getrandbits(32))
         full_vol_name = 'composetest_{0}'.format(vol_name)
         full_vol_name = 'composetest_{0}'.format(vol_name)
@@ -1748,7 +1669,6 @@ class ProjectTest(DockerClientTestCase):
         base_file = config.ConfigFile(
         base_file = config.ConfigFile(
             'base.yml',
             'base.yml',
             {
             {
-                'version': str(V2_0),
                 'services': {
                 'services': {
                     'simple': {
                     'simple': {
                         'image': BUSYBOX_IMAGE_WITH_TAG,
                         'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -1839,7 +1759,6 @@ class ProjectTest(DockerClientTestCase):
 
 
         mock_log.warning.assert_not_called()
         mock_log.warning.assert_not_called()
 
 
-    @v2_1_only()
     def test_project_up_healthy_dependency(self):
     def test_project_up_healthy_dependency(self):
         config_dict = {
         config_dict = {
             'version': '2.1',
             'version': '2.1',
@@ -1876,7 +1795,6 @@ class ProjectTest(DockerClientTestCase):
         assert 'svc1' in svc2.get_dependency_names()
         assert 'svc1' in svc2.get_dependency_names()
         assert svc1.is_healthy()
         assert svc1.is_healthy()
 
 
-    @v2_1_only()
     def test_project_up_unhealthy_dependency(self):
     def test_project_up_unhealthy_dependency(self):
         config_dict = {
         config_dict = {
             'version': '2.1',
             'version': '2.1',
@@ -1915,7 +1833,6 @@ class ProjectTest(DockerClientTestCase):
         with pytest.raises(HealthCheckFailed):
         with pytest.raises(HealthCheckFailed):
             svc1.is_healthy()
             svc1.is_healthy()
 
 
-    @v2_1_only()
     def test_project_up_no_healthcheck_dependency(self):
     def test_project_up_no_healthcheck_dependency(self):
         config_dict = {
         config_dict = {
             'version': '2.1',
             'version': '2.1',

+ 8 - 20
tests/integration/testcases.py

@@ -11,14 +11,8 @@ from compose.cli.docker_client import docker_client
 from compose.config.config import resolve_environment
 from compose.config.config import resolve_environment
 from compose.config.environment import Environment
 from compose.config.environment import Environment
 from compose.const import API_VERSIONS
 from compose.const import API_VERSIONS
+from compose.const import COMPOSE_SPEC as VERSION
 from compose.const import COMPOSEFILE_V1 as V1
 from compose.const import COMPOSEFILE_V1 as V1
-from compose.const import COMPOSEFILE_V2_0 as V2_0
-from compose.const import COMPOSEFILE_V2_0 as V2_1
-from compose.const import COMPOSEFILE_V2_2 as V2_2
-from compose.const import COMPOSEFILE_V2_3 as V2_3
-from compose.const import COMPOSEFILE_V3_0 as V3_0
-from compose.const import COMPOSEFILE_V3_2 as V3_2
-from compose.const import COMPOSEFILE_V3_5 as V3_5
 from compose.const import LABEL_PROJECT
 from compose.const import LABEL_PROJECT
 from compose.progress_stream import stream_output
 from compose.progress_stream import stream_output
 from compose.service import Service
 from compose.service import Service
@@ -45,17 +39,11 @@ def get_links(container):
 
 
 def engine_max_version():
 def engine_max_version():
     if 'DOCKER_VERSION' not in os.environ:
     if 'DOCKER_VERSION' not in os.environ:
-        return V3_5
+        return VERSION
     version = os.environ['DOCKER_VERSION'].partition('-')[0]
     version = os.environ['DOCKER_VERSION'].partition('-')[0]
     if version_lt(version, '1.10'):
     if version_lt(version, '1.10'):
         return V1
         return V1
-    if version_lt(version, '1.12'):
-        return V2_0
-    if version_lt(version, '1.13'):
-        return V2_1
-    if version_lt(version, '17.06'):
-        return V3_2
-    return V3_5
+    return VERSION
 
 
 
 
 def min_version_skip(version):
 def min_version_skip(version):
@@ -66,23 +54,23 @@ def min_version_skip(version):
 
 
 
 
 def v2_only():
 def v2_only():
-    return min_version_skip(V2_0)
+    return min_version_skip(VERSION)
 
 
 
 
 def v2_1_only():
 def v2_1_only():
-    return min_version_skip(V2_1)
+    return min_version_skip(VERSION)
 
 
 
 
 def v2_2_only():
 def v2_2_only():
-    return min_version_skip(V2_2)
+    return min_version_skip(VERSION)
 
 
 
 
 def v2_3_only():
 def v2_3_only():
-    return min_version_skip(V2_3)
+    return min_version_skip(VERSION)
 
 
 
 
 def v3_only():
 def v3_only():
-    return min_version_skip(V3_0)
+    return min_version_skip(VERSION)
 
 
 
 
 class DockerClientTestCase(unittest.TestCase):
 class DockerClientTestCase(unittest.TestCase):

+ 4 - 4
tests/unit/cli/errors_test.py

@@ -34,19 +34,19 @@ class TestHandleConnectionErrors(object):
 
 
     def test_api_error_version_mismatch(self, mock_logging):
     def test_api_error_version_mismatch(self, mock_logging):
         with pytest.raises(errors.ConnectionError):
         with pytest.raises(errors.ConnectionError):
-            with handle_connection_errors(mock.Mock(api_version='1.22')):
+            with handle_connection_errors(mock.Mock(api_version='1.38')):
                 raise APIError(None, None, b"client is newer than server")
                 raise APIError(None, None, b"client is newer than server")
 
 
         _, args, _ = mock_logging.error.mock_calls[0]
         _, args, _ = mock_logging.error.mock_calls[0]
-        assert "Docker Engine of version 1.10.0 or greater" in args[0]
+        assert "Docker Engine of version 18.06.0 or greater" in args[0]
 
 
     def test_api_error_version_mismatch_unicode_explanation(self, mock_logging):
     def test_api_error_version_mismatch_unicode_explanation(self, mock_logging):
         with pytest.raises(errors.ConnectionError):
         with pytest.raises(errors.ConnectionError):
-            with handle_connection_errors(mock.Mock(api_version='1.22')):
+            with handle_connection_errors(mock.Mock(api_version='1.38')):
                 raise APIError(None, None, u"client is newer than server")
                 raise APIError(None, None, u"client is newer than server")
 
 
         _, args, _ = mock_logging.error.mock_calls[0]
         _, args, _ = mock_logging.error.mock_calls[0]
-        assert "Docker Engine of version 1.10.0 or greater" in args[0]
+        assert "Docker Engine of version 18.06.0 or greater" in args[0]
 
 
     def test_api_error_version_other(self, mock_logging):
     def test_api_error_version_other(self, mock_logging):
         msg = b"Something broke!"
         msg = b"Something broke!"

+ 231 - 149
tests/unit/config/config_test.py

@@ -26,21 +26,13 @@ from compose.config.serialize import denormalize_service_dict
 from compose.config.serialize import serialize_config
 from compose.config.serialize import serialize_config
 from compose.config.serialize import serialize_ns_time_value
 from compose.config.serialize import serialize_ns_time_value
 from compose.config.types import VolumeSpec
 from compose.config.types import VolumeSpec
+from compose.const import COMPOSE_SPEC as VERSION
 from compose.const import COMPOSEFILE_V1 as V1
 from compose.const import COMPOSEFILE_V1 as V1
-from compose.const import COMPOSEFILE_V2_0 as V2_0
-from compose.const import COMPOSEFILE_V2_1 as V2_1
-from compose.const import COMPOSEFILE_V2_2 as V2_2
-from compose.const import COMPOSEFILE_V2_3 as V2_3
-from compose.const import COMPOSEFILE_V3_0 as V3_0
-from compose.const import COMPOSEFILE_V3_1 as V3_1
-from compose.const import COMPOSEFILE_V3_2 as V3_2
-from compose.const import COMPOSEFILE_V3_3 as V3_3
-from compose.const import COMPOSEFILE_V3_5 as V3_5
 from compose.const import IS_WINDOWS_PLATFORM
 from compose.const import IS_WINDOWS_PLATFORM
 from tests import mock
 from tests import mock
 from tests import unittest
 from tests import unittest
 
 
-DEFAULT_VERSION = V2_0
+DEFAULT_VERSION = VERSION
 
 
 
 
 def make_service_dict(name, service_dict, working_dir='.', filename=None):
 def make_service_dict(name, service_dict, working_dir='.', filename=None):
@@ -73,8 +65,10 @@ class ConfigTest(unittest.TestCase):
         service_dicts = config.load(
         service_dicts = config.load(
             build_config_details(
             build_config_details(
                 {
                 {
-                    'foo': {'image': 'busybox'},
-                    'bar': {'image': 'busybox', 'environment': ['FOO=1']},
+                    'services': {
+                        'foo': {'image': 'busybox'},
+                        'bar': {'image': 'busybox', 'environment': ['FOO=1']},
+                    }
                 },
                 },
                 'tests/fixtures/extends',
                 'tests/fixtures/extends',
                 'common.yml'
                 'common.yml'
@@ -169,23 +163,23 @@ class ConfigTest(unittest.TestCase):
     def test_valid_versions(self):
     def test_valid_versions(self):
         for version in ['2', '2.0']:
         for version in ['2', '2.0']:
             cfg = config.load(build_config_details({'version': version}))
             cfg = config.load(build_config_details({'version': version}))
-            assert cfg.version == V2_0
+            assert cfg.version == VERSION
 
 
         cfg = config.load(build_config_details({'version': '2.1'}))
         cfg = config.load(build_config_details({'version': '2.1'}))
-        assert cfg.version == V2_1
+        assert cfg.version == VERSION
 
 
         cfg = config.load(build_config_details({'version': '2.2'}))
         cfg = config.load(build_config_details({'version': '2.2'}))
-        assert cfg.version == V2_2
+        assert cfg.version == VERSION
 
 
         cfg = config.load(build_config_details({'version': '2.3'}))
         cfg = config.load(build_config_details({'version': '2.3'}))
-        assert cfg.version == V2_3
+        assert cfg.version == VERSION
 
 
         for version in ['3', '3.0']:
         for version in ['3', '3.0']:
             cfg = config.load(build_config_details({'version': version}))
             cfg = config.load(build_config_details({'version': version}))
-            assert cfg.version == V3_0
+            assert cfg.version == VERSION
 
 
         cfg = config.load(build_config_details({'version': '3.1'}))
         cfg = config.load(build_config_details({'version': '3.1'}))
-        assert cfg.version == V3_1
+        assert cfg.version == VERSION
 
 
     def test_v1_file_version(self):
     def test_v1_file_version(self):
         cfg = config.load(build_config_details({'web': {'image': 'busybox'}}))
         cfg = config.load(build_config_details({'web': {'image': 'busybox'}}))
@@ -197,7 +191,7 @@ class ConfigTest(unittest.TestCase):
         assert list(s['name'] for s in cfg.services) == ['version']
         assert list(s['name'] for s in cfg.services) == ['version']
 
 
     def test_wrong_version_type(self):
     def test_wrong_version_type(self):
-        for version in [None, 1, 2, 2.0]:
+        for version in [1, 2, 2.0]:
             with pytest.raises(ConfigurationError) as excinfo:
             with pytest.raises(ConfigurationError) as excinfo:
                 config.load(
                 config.load(
                     build_config_details(
                     build_config_details(
@@ -213,12 +207,12 @@ class ConfigTest(unittest.TestCase):
         with pytest.raises(ConfigurationError) as excinfo:
         with pytest.raises(ConfigurationError) as excinfo:
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
-                    {'version': '2.18'},
+                    {'version': '1'},
                     filename='filename.yml',
                     filename='filename.yml',
                 )
                 )
             )
             )
 
 
-        assert 'Version in "filename.yml" is unsupported' in excinfo.exconly()
+        assert 'Version in "filename.yml" is invalid' in excinfo.exconly()
         assert VERSION_EXPLANATION in excinfo.exconly()
         assert VERSION_EXPLANATION in excinfo.exconly()
 
 
     def test_version_1_is_invalid(self):
     def test_version_1_is_invalid(self):
@@ -328,7 +322,6 @@ class ConfigTest(unittest.TestCase):
                     }
                     }
                 }, 'working_dir', 'filename.yml')
                 }, 'working_dir', 'filename.yml')
             )
             )
-
         assert 'Unexpected type for "version" key in "filename.yml"' \
         assert 'Unexpected type for "version" key in "filename.yml"' \
             in mock_logging.warning.call_args[0][0]
             in mock_logging.warning.call_args[0][0]
 
 
@@ -376,7 +369,7 @@ class ConfigTest(unittest.TestCase):
         base_file = config.ConfigFile(
         base_file = config.ConfigFile(
             'base.yaml',
             'base.yaml',
             {
             {
-                'version': str(V2_1),
+                'version': '2',
                 'services': {
                 'services': {
                     'web': {
                     'web': {
                         'image': 'example/web',
                         'image': 'example/web',
@@ -511,7 +504,15 @@ class ConfigTest(unittest.TestCase):
         for invalid_name in ['?not?allowed', ' ', '', '!', '/', '\xe2']:
         for invalid_name in ['?not?allowed', ' ', '', '!', '/', '\xe2']:
             with pytest.raises(ConfigurationError) as exc:
             with pytest.raises(ConfigurationError) as exc:
                 config.load(build_config_details(
                 config.load(build_config_details(
-                    {invalid_name: {'image': 'busybox'}}))
+                    {
+                        'version': '2',
+                        'services': {
+                            invalid_name:
+                            {
+                                'image': 'busybox'
+                            }
+                        }
+                    }))
             assert 'Invalid service name \'%s\'' % invalid_name in exc.exconly()
             assert 'Invalid service name \'%s\'' % invalid_name in exc.exconly()
 
 
     def test_load_config_invalid_service_names_v2(self):
     def test_load_config_invalid_service_names_v2(self):
@@ -543,17 +544,24 @@ class ConfigTest(unittest.TestCase):
         with pytest.raises(ConfigurationError) as exc:
         with pytest.raises(ConfigurationError) as exc:
             config.load(build_config_details(
             config.load(build_config_details(
                 {
                 {
-                    'web': {'image': 'busybox', 'name': 'bogus'},
+                    'version': '2',
+                    'services': {
+                        'web': {'image': 'busybox', 'name': 'bogus'}
+                    }
                 },
                 },
                 'working_dir',
                 'working_dir',
                 'filename.yml',
                 'filename.yml',
             ))
             ))
-
-        assert "Unsupported config option for web: 'name'" in exc.exconly()
+        assert "Unsupported config option for services.web: 'name'" in exc.exconly()
 
 
     def test_load_invalid_service_definition(self):
     def test_load_invalid_service_definition(self):
         config_details = build_config_details(
         config_details = build_config_details(
-            {'web': 'wrong'},
+            {
+                'version': '2',
+                'services': {
+                    'web': 'wrong'
+                }
+            },
             'working_dir',
             'working_dir',
             'filename.yml')
             'filename.yml')
         with pytest.raises(ConfigurationError) as exc:
         with pytest.raises(ConfigurationError) as exc:
@@ -585,7 +593,10 @@ class ConfigTest(unittest.TestCase):
         with pytest.raises(ConfigurationError) as excinfo:
         with pytest.raises(ConfigurationError) as excinfo:
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
-                    {1: {'image': 'busybox'}},
+                    {
+                        'version': '2',
+                        'services': {1: {'image': 'busybox'}}
+                    },
                     'working_dir',
                     'working_dir',
                     'filename.yml'
                     'filename.yml'
                 )
                 )
@@ -836,10 +847,10 @@ class ConfigTest(unittest.TestCase):
     def test_load_with_multiple_files_and_invalid_override(self):
     def test_load_with_multiple_files_and_invalid_override(self):
         base_file = config.ConfigFile(
         base_file = config.ConfigFile(
             'base.yaml',
             'base.yaml',
-            {'web': {'image': 'example/web'}})
+            {'version': '2', 'services': {'web': {'image': 'example/web'}}})
         override_file = config.ConfigFile(
         override_file = config.ConfigFile(
             'override.yaml',
             'override.yaml',
-            {'bogus': 'thing'})
+            {'version': '2', 'services': {'bogus': 'thing'}})
         details = config.ConfigDetails('.', [base_file, override_file])
         details = config.ConfigDetails('.', [base_file, override_file])
 
 
         with pytest.raises(ConfigurationError) as exc:
         with pytest.raises(ConfigurationError) as exc:
@@ -977,7 +988,6 @@ class ConfigTest(unittest.TestCase):
         service = config.load(
         service = config.load(
             build_config_details(
             build_config_details(
                 {
                 {
-                    'version': str(V3_3),
                     'services': {
                     'services': {
                         'web': {
                         'web': {
                             'build': {
                             'build': {
@@ -1424,7 +1434,7 @@ class ConfigTest(unittest.TestCase):
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
                     {
                     {
-                        'version': str(V2_1),
+                        'version': str(VERSION),
                         'networks': {
                         'networks': {
                             'foo': {
                             'foo': {
                                 'driver': 'default',
                                 'driver': 'default',
@@ -1455,7 +1465,6 @@ class ConfigTest(unittest.TestCase):
         networks = config.load(
         networks = config.load(
             build_config_details(
             build_config_details(
                 {
                 {
-                    'version': str(V2_1),
                     'networks': {
                     'networks': {
                         'foo': {
                         'foo': {
                             'driver': 'default',
                             'driver': 'default',
@@ -1487,7 +1496,10 @@ class ConfigTest(unittest.TestCase):
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
                     {
                     {
-                        'foo': {'image': 'busybox', 'privilige': 'something'},
+                        'version': str(VERSION),
+                        'services': {
+                            'foo': {'image': 'busybox', 'privilige': 'something'},
+                        }
                     },
                     },
                     'tests/fixtures/extends',
                     'tests/fixtures/extends',
                     'filename.yml'
                     'filename.yml'
@@ -1508,7 +1520,10 @@ class ConfigTest(unittest.TestCase):
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
                     {
                     {
-                        'foo': {'image': 1},
+                        'version': str(VERSION),
+                        'services': {
+                            'foo': {'image': 1},
+                        }
                     },
                     },
                     'tests/fixtures/extends',
                     'tests/fixtures/extends',
                     'filename.yml'
                     'filename.yml'
@@ -1555,7 +1570,10 @@ class ConfigTest(unittest.TestCase):
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
                     {
                     {
-                        'foo': {'image': 'busybox', 'links': 'an_link'},
+                        'version': str(VERSION),
+                        'services': {
+                            'foo': {'image': 'busybox', 'links': 'an_link'},
+                        }
                     },
                     },
                     'tests/fixtures/extends',
                     'tests/fixtures/extends',
                     'filename.yml'
                     'filename.yml'
@@ -1583,7 +1601,10 @@ class ConfigTest(unittest.TestCase):
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
                     {
                     {
-                        'web': {'build': '.', 'devices': ['/dev/foo:/dev/foo', '/dev/foo:/dev/foo']}
+                        'version': str(VERSION),
+                        'services': {
+                            'web': {'build': '.', 'devices': ['/dev/foo:/dev/foo', '/dev/foo:/dev/foo']}
+                        }
                     },
                     },
                     'tests/fixtures/extends',
                     'tests/fixtures/extends',
                     'filename.yml'
                     'filename.yml'
@@ -1597,7 +1618,10 @@ class ConfigTest(unittest.TestCase):
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
                     {
                     {
-                        'web': {'build': '.', 'command': [1]}
+                        'version': str(VERSION),
+                        'services': {
+                            'web': {'build': '.', 'command': [1]}
+                        }
                     },
                     },
                     'tests/fixtures/extends',
                     'tests/fixtures/extends',
                     'filename.yml'
                     'filename.yml'
@@ -1622,10 +1646,13 @@ class ConfigTest(unittest.TestCase):
         with pytest.raises(ConfigurationError) as excinfo:
         with pytest.raises(ConfigurationError) as excinfo:
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
-                    {'web': {
-                        'image': 'busybox',
-                        'extra_hosts': 'somehost:162.242.195.82'
-                    }},
+                    {
+                        'version': str(VERSION),
+                        'services': {
+                            'web': {
+                                'image': 'busybox',
+                                'extra_hosts': 'somehost:162.242.195.82'}}
+                    },
                     'working_dir',
                     'working_dir',
                     'filename.yml'
                     'filename.yml'
                 )
                 )
@@ -1638,13 +1665,16 @@ class ConfigTest(unittest.TestCase):
         with pytest.raises(ConfigurationError) as excinfo:
         with pytest.raises(ConfigurationError) as excinfo:
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
-                    {'web': {
-                        'image': 'busybox',
-                        'extra_hosts': [
-                            {'somehost': '162.242.195.82'},
-                            {'otherhost': '50.31.209.229'}
-                        ]
-                    }},
+                    {
+                        'version': str(VERSION),
+                        'services': {
+                            'web': {
+                                'image': 'busybox',
+                                'extra_hosts': [
+                                    {'somehost': '162.242.195.82'},
+                                    {'otherhost': '50.31.209.229'}
+                                ]}}
+                    },
                     'working_dir',
                     'working_dir',
                     'filename.yml'
                     'filename.yml'
                 )
                 )
@@ -1658,13 +1688,16 @@ class ConfigTest(unittest.TestCase):
         with pytest.raises(ConfigurationError) as exc:
         with pytest.raises(ConfigurationError) as exc:
             config.load(build_config_details(
             config.load(build_config_details(
                 {
                 {
-                    'web': {
-                        'image': 'busybox',
-                        'ulimits': {
-                            'nofile': {
-                                "not_soft_or_hard": 100,
-                                "soft": 10000,
-                                "hard": 20000,
+                    'version': str(VERSION),
+                    'services': {
+                        'web': {
+                            'image': 'busybox',
+                            'ulimits': {
+                                'nofile': {
+                                    "not_soft_or_hard": 100,
+                                    "soft": 10000,
+                                    "hard": 20000,
+                                }
                             }
                             }
                         }
                         }
                     }
                     }
@@ -1679,9 +1712,12 @@ class ConfigTest(unittest.TestCase):
         with pytest.raises(ConfigurationError) as exc:
         with pytest.raises(ConfigurationError) as exc:
             config.load(build_config_details(
             config.load(build_config_details(
                 {
                 {
-                    'web': {
-                        'image': 'busybox',
-                        'ulimits': {'nofile': {"soft": 10000}}
+                    'version': str(VERSION),
+                    'services': {
+                        'web': {
+                            'image': 'busybox',
+                            'ulimits': {'nofile': {"soft": 10000}}
+                        }
                     }
                     }
                 },
                 },
                 'working_dir',
                 'working_dir',
@@ -1695,10 +1731,13 @@ class ConfigTest(unittest.TestCase):
         with pytest.raises(ConfigurationError) as exc:
         with pytest.raises(ConfigurationError) as exc:
             config.load(build_config_details(
             config.load(build_config_details(
                 {
                 {
-                    'web': {
-                        'image': 'busybox',
-                        'ulimits': {
-                            'nofile': {"soft": 10000, "hard": 1000}
+                    'version': str(VERSION),
+                    'services': {
+                        'web': {
+                            'image': 'busybox',
+                            'ulimits': {
+                                'nofile': {"soft": 10000, "hard": 1000}
+                            }
                         }
                         }
                     }
                     }
                 },
                 },
@@ -1711,10 +1750,12 @@ class ConfigTest(unittest.TestCase):
         for expose in expose_values:
         for expose in expose_values:
             service = config.load(
             service = config.load(
                 build_config_details(
                 build_config_details(
-                    {'web': {
-                        'image': 'busybox',
-                        'expose': expose
-                    }},
+                    {
+                        'version': str(VERSION),
+                        'services': {
+                            'web': {
+                                'image': 'busybox',
+                                'expose': expose}}},
                     'working_dir',
                     'working_dir',
                     'filename.yml'
                     'filename.yml'
                 )
                 )
@@ -1726,10 +1767,12 @@ class ConfigTest(unittest.TestCase):
         for entrypoint in entrypoint_values:
         for entrypoint in entrypoint_values:
             service = config.load(
             service = config.load(
                 build_config_details(
                 build_config_details(
-                    {'web': {
-                        'image': 'busybox',
-                        'entrypoint': entrypoint
-                    }},
+                    {
+                        'version': str(VERSION),
+                        'services': {
+                            'web': {
+                                'image': 'busybox',
+                                'entrypoint': entrypoint}}},
                     'working_dir',
                     'working_dir',
                     'filename.yml'
                     'filename.yml'
                 )
                 )
@@ -1738,9 +1781,12 @@ class ConfigTest(unittest.TestCase):
 
 
     def test_logs_warning_for_boolean_in_environment(self):
     def test_logs_warning_for_boolean_in_environment(self):
         config_details = build_config_details({
         config_details = build_config_details({
-            'web': {
-                'image': 'busybox',
-                'environment': {'SHOW_STUFF': True}
+            'version': str(VERSION),
+            'services': {
+                'web': {
+                    'image': 'busybox',
+                    'environment': {'SHOW_STUFF': True}
+                }
             }
             }
         })
         })
 
 
@@ -1752,10 +1798,12 @@ class ConfigTest(unittest.TestCase):
     def test_config_valid_environment_dict_key_contains_dashes(self):
     def test_config_valid_environment_dict_key_contains_dashes(self):
         services = config.load(
         services = config.load(
             build_config_details(
             build_config_details(
-                {'web': {
-                    'image': 'busybox',
-                    'environment': {'SPRING_JPA_HIBERNATE_DDL-AUTO': 'none'}
-                }},
+                {
+                    'version': str(VERSION),
+                    'services': {
+                        'web': {
+                            'image': 'busybox',
+                            'environment': {'SPRING_JPA_HIBERNATE_DDL-AUTO': 'none'}}}},
                 'working_dir',
                 'working_dir',
                 'filename.yml'
                 'filename.yml'
             )
             )
@@ -1794,9 +1842,12 @@ web:
     def test_validate_extra_hosts_invalid(self):
     def test_validate_extra_hosts_invalid(self):
         with pytest.raises(ConfigurationError) as exc:
         with pytest.raises(ConfigurationError) as exc:
             config.load(build_config_details({
             config.load(build_config_details({
-                'web': {
-                    'image': 'alpine',
-                    'extra_hosts': "www.example.com: 192.168.0.17",
+                'version': str(VERSION),
+                'services': {
+                    'web': {
+                        'image': 'alpine',
+                        'extra_hosts': "www.example.com: 192.168.0.17",
+                    }
                 }
                 }
             }))
             }))
         assert "web.extra_hosts contains an invalid type" in exc.exconly()
         assert "web.extra_hosts contains an invalid type" in exc.exconly()
@@ -1804,22 +1855,28 @@ web:
     def test_validate_extra_hosts_invalid_list(self):
     def test_validate_extra_hosts_invalid_list(self):
         with pytest.raises(ConfigurationError) as exc:
         with pytest.raises(ConfigurationError) as exc:
             config.load(build_config_details({
             config.load(build_config_details({
-                'web': {
-                    'image': 'alpine',
-                    'extra_hosts': [
-                        {'www.example.com': '192.168.0.17'},
-                        {'api.example.com': '192.168.0.18'}
-                    ],
+                'version': str(VERSION),
+                'services': {
+                    'web': {
+                        'image': 'alpine',
+                        'extra_hosts': [
+                            {'www.example.com': '192.168.0.17'},
+                            {'api.example.com': '192.168.0.18'}
+                        ],
+                    }
                 }
                 }
             }))
             }))
         assert "which is an invalid type" in exc.exconly()
         assert "which is an invalid type" in exc.exconly()
 
 
     def test_normalize_dns_options(self):
     def test_normalize_dns_options(self):
         actual = config.load(build_config_details({
         actual = config.load(build_config_details({
-            'web': {
-                'image': 'alpine',
-                'dns': '8.8.8.8',
-                'dns_search': 'domain.local',
+            'version': str(VERSION),
+            'services': {
+                'web': {
+                    'image': 'alpine',
+                    'dns': '8.8.8.8',
+                    'dns_search': 'domain.local',
+                }
             }
             }
         }))
         }))
         assert actual.services == [
         assert actual.services == [
@@ -1947,7 +2004,6 @@ web:
 
 
     def test_isolation_option(self):
     def test_isolation_option(self):
         actual = config.load(build_config_details({
         actual = config.load(build_config_details({
-            'version': str(V2_1),
             'services': {
             'services': {
                 'web': {
                 'web': {
                     'image': 'win10',
                     'image': 'win10',
@@ -1966,7 +2022,6 @@ web:
 
 
     def test_runtime_option(self):
     def test_runtime_option(self):
         actual = config.load(build_config_details({
         actual = config.load(build_config_details({
-            'version': str(V2_3),
             'services': {
             'services': {
                 'web': {
                 'web': {
                     'image': 'nvidia/cuda',
                     'image': 'nvidia/cuda',
@@ -2088,7 +2143,7 @@ web:
         }
         }
 
 
         actual = config.merge_service_dicts_from_files(
         actual = config.merge_service_dicts_from_files(
-            base, override, V3_2
+            base, override, VERSION
         )
         )
 
 
         assert actual['volumes'] == [
         assert actual['volumes'] == [
@@ -2135,7 +2190,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_0)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'alpine:edge',
             'image': 'alpine:edge',
             'logging': {
             'logging': {
@@ -2169,7 +2224,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_0)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'alpine:edge',
             'image': 'alpine:edge',
             'logging': {
             'logging': {
@@ -2201,7 +2256,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_0)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'alpine:edge',
             'image': 'alpine:edge',
             'logging': {
             'logging': {
@@ -2233,7 +2288,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_0)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'alpine:edge',
             'image': 'alpine:edge',
             'logging': {
             'logging': {
@@ -2262,7 +2317,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_0)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'alpine:edge',
             'image': 'alpine:edge',
             'logging': {
             'logging': {
@@ -2282,7 +2337,7 @@ web:
                 }
                 }
             }
             }
         }
         }
-        actual = config.merge_service_dicts(base, override, V2_0)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'alpine:edge',
             'image': 'alpine:edge',
             'logging': {
             'logging': {
@@ -2304,7 +2359,7 @@ web:
             }
             }
         }
         }
         override = {}
         override = {}
-        actual = config.merge_service_dicts(base, override, V2_0)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'alpine:edge',
             'image': 'alpine:edge',
             'logging': {
             'logging': {
@@ -2332,7 +2387,7 @@ web:
             'ports': ['1245:1245/udp']
             'ports': ['1245:1245/udp']
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V3_1)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': BUSYBOX_IMAGE_WITH_TAG,
             'image': BUSYBOX_IMAGE_WITH_TAG,
             'command': 'top',
             'command': 'top',
@@ -2348,7 +2403,7 @@ web:
             }
             }
         }
         }
         override = {}
         override = {}
-        actual = config.merge_service_dicts(base, override, V2_1)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == base
         assert actual == base
 
 
     def test_merge_depends_on_mixed_syntax(self):
     def test_merge_depends_on_mixed_syntax(self):
@@ -2363,7 +2418,7 @@ web:
             'depends_on': ['app3']
             'depends_on': ['app3']
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_1)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'busybox',
             'image': 'busybox',
             'depends_on': {
             'depends_on': {
@@ -2401,7 +2456,7 @@ web:
             'labels': {'com.docker.compose.test': 'yes'}
             'labels': {'com.docker.compose.test': 'yes'}
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_0)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'busybox',
             'image': 'busybox',
             'pid': 'host',
             'pid': 'host',
@@ -2417,7 +2472,7 @@ web:
         }
         }
         override = {'secrets': ['other-src.txt']}
         override = {'secrets': ['other-src.txt']}
 
 
-        actual = config.merge_service_dicts(base, override, V3_1)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert secret_sort(actual['secrets']) == secret_sort([
         assert secret_sort(actual['secrets']) == secret_sort([
             {'source': 'src.txt'},
             {'source': 'src.txt'},
             {'source': 'other-src.txt'}
             {'source': 'other-src.txt'}
@@ -2437,7 +2492,7 @@ web:
                 }
                 }
             ]
             ]
         }
         }
-        actual = config.merge_service_dicts(base, override, V3_1)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual['secrets'] == override['secrets']
         assert actual['secrets'] == override['secrets']
 
 
     def test_merge_different_configs(self):
     def test_merge_different_configs(self):
@@ -2449,7 +2504,7 @@ web:
         }
         }
         override = {'configs': ['other-src.txt']}
         override = {'configs': ['other-src.txt']}
 
 
-        actual = config.merge_service_dicts(base, override, V3_3)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert secret_sort(actual['configs']) == secret_sort([
         assert secret_sort(actual['configs']) == secret_sort([
             {'source': 'src.txt'},
             {'source': 'src.txt'},
             {'source': 'other-src.txt'}
             {'source': 'other-src.txt'}
@@ -2469,7 +2524,7 @@ web:
                 }
                 }
             ]
             ]
         }
         }
-        actual = config.merge_service_dicts(base, override, V3_3)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual['configs'] == override['configs']
         assert actual['configs'] == override['configs']
 
 
     def test_merge_deploy(self):
     def test_merge_deploy(self):
@@ -2484,7 +2539,7 @@ web:
                 }
                 }
             }
             }
         }
         }
-        actual = config.merge_service_dicts(base, override, V3_0)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual['deploy'] == override['deploy']
         assert actual['deploy'] == override['deploy']
 
 
     def test_merge_deploy_override(self):
     def test_merge_deploy_override(self):
@@ -2540,7 +2595,7 @@ web:
                 'update_config': {'max_failure_ratio': 0.712, 'parallelism': 4}
                 'update_config': {'max_failure_ratio': 0.712, 'parallelism': 4}
             }
             }
         }
         }
-        actual = config.merge_service_dicts(base, override, V3_5)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual['deploy'] == {
         assert actual['deploy'] == {
             'mode': 'replicated',
             'mode': 'replicated',
             'endpoint_mode': 'vip',
             'endpoint_mode': 'vip',
@@ -2596,7 +2651,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V3_3)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual['credential_spec'] == override['credential_spec']
         assert actual['credential_spec'] == override['credential_spec']
 
 
     def test_merge_scale(self):
     def test_merge_scale(self):
@@ -2609,7 +2664,7 @@ web:
             'scale': 4,
             'scale': 4,
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_2)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {'image': 'bar', 'scale': 4}
         assert actual == {'image': 'bar', 'scale': 4}
 
 
     def test_merge_blkio_config(self):
     def test_merge_blkio_config(self):
@@ -2644,7 +2699,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_2)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'bar',
             'image': 'bar',
             'blkio_config': {
             'blkio_config': {
@@ -2671,7 +2726,7 @@ web:
             'extra_hosts': ['bar:5.6.7.8', 'foo:127.0.0.1']
             'extra_hosts': ['bar:5.6.7.8', 'foo:127.0.0.1']
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_0)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual['extra_hosts'] == {
         assert actual['extra_hosts'] == {
             'foo': '127.0.0.1',
             'foo': '127.0.0.1',
             'bar': '5.6.7.8',
             'bar': '5.6.7.8',
@@ -2695,7 +2750,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_3)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual['healthcheck'] == {
         assert actual['healthcheck'] == {
             'start_period': base['healthcheck']['start_period'],
             'start_period': base['healthcheck']['start_period'],
             'test': override['healthcheck']['test'],
             'test': override['healthcheck']['test'],
@@ -2721,7 +2776,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_3)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual['healthcheck'] == {'disabled': True}
         assert actual['healthcheck'] == {'disabled': True}
 
 
     def test_merge_healthcheck_override_enables(self):
     def test_merge_healthcheck_override_enables(self):
@@ -2743,7 +2798,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_3)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual['healthcheck'] == override['healthcheck']
         assert actual['healthcheck'] == override['healthcheck']
 
 
     def test_merge_device_cgroup_rules(self):
     def test_merge_device_cgroup_rules(self):
@@ -2756,7 +2811,7 @@ web:
             'device_cgroup_rules': ['c 7:128 rwm', 'f 0:128 n']
             'device_cgroup_rules': ['c 7:128 rwm', 'f 0:128 n']
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_3)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert sorted(actual['device_cgroup_rules']) == sorted(
         assert sorted(actual['device_cgroup_rules']) == sorted(
             ['c 7:128 rwm', 'x 3:244 rw', 'f 0:128 n']
             ['c 7:128 rwm', 'x 3:244 rw', 'f 0:128 n']
         )
         )
@@ -2771,7 +2826,7 @@ web:
             'isolation': 'hyperv',
             'isolation': 'hyperv',
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_3)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual == {
         assert actual == {
             'image': 'bar',
             'image': 'bar',
             'isolation': 'hyperv',
             'isolation': 'hyperv',
@@ -2793,7 +2848,7 @@ web:
             }
             }
         }
         }
 
 
-        actual = config.merge_service_dicts(base, override, V2_3)
+        actual = config.merge_service_dicts(base, override, VERSION)
         assert actual['storage_opt'] == {
         assert actual['storage_opt'] == {
             'size': '2G',
             'size': '2G',
             'readonly': 'false',
             'readonly': 'false',
@@ -3350,6 +3405,7 @@ class PortsTest(unittest.TestCase):
 
 
             assert "non-unique" in exc.value.msg
             assert "non-unique" in exc.value.msg
 
 
+    @pytest.mark.skip(reason="Validator is one_off (generic error)")
     def test_config_invalid_ports_format_validation(self):
     def test_config_invalid_ports_format_validation(self):
         for invalid_ports in self.INVALID_PORT_MAPPINGS:
         for invalid_ports in self.INVALID_PORT_MAPPINGS:
             with pytest.raises(ConfigurationError) as exc:
             with pytest.raises(ConfigurationError) as exc:
@@ -3599,6 +3655,7 @@ class InterpolationTest(unittest.TestCase):
             assert 'BAR' in warnings[0]
             assert 'BAR' in warnings[0]
             assert 'FOO' in warnings[1]
             assert 'FOO' in warnings[1]
 
 
+    @pytest.mark.skip(reason='compatibility mode was removed internally')
     def test_compatibility_mode_warnings(self):
     def test_compatibility_mode_warnings(self):
         config_details = build_config_details({
         config_details = build_config_details({
             'version': '3.5',
             'version': '3.5',
@@ -3637,6 +3694,7 @@ class InterpolationTest(unittest.TestCase):
         assert 'restart_policy.delay' in warn_message
         assert 'restart_policy.delay' in warn_message
         assert 'restart_policy.window' in warn_message
         assert 'restart_policy.window' in warn_message
 
 
+    @pytest.mark.skip(reason='compatibility mode was removed internally')
     def test_compatibility_mode_load(self):
     def test_compatibility_mode_load(self):
         config_details = build_config_details({
         config_details = build_config_details({
             'version': '3.5',
             'version': '3.5',
@@ -4369,7 +4427,8 @@ class EnvTest(unittest.TestCase):
 
 
         service_dict = config.load(
         service_dict = config.load(
             build_config_details(
             build_config_details(
-                {'foo': {'build': '.', 'volumes': ['$HOSTENV:$CONTAINERENV']}},
+                {'services': {
+                    'foo': {'build': '.', 'volumes': ['$HOSTENV:$CONTAINERENV']}}},
                 "tests/fixtures/env",
                 "tests/fixtures/env",
             )
             )
         ).services[0]
         ).services[0]
@@ -4377,7 +4436,8 @@ class EnvTest(unittest.TestCase):
 
 
         service_dict = config.load(
         service_dict = config.load(
             build_config_details(
             build_config_details(
-                {'foo': {'build': '.', 'volumes': ['/opt${HOSTENV}:/opt${CONTAINERENV}']}},
+                {'services': {
+                    'foo': {'build': '.', 'volumes': ['/opt${HOSTENV}:/opt${CONTAINERENV}']}}},
                 "tests/fixtures/env",
                 "tests/fixtures/env",
             )
             )
         ).services[0]
         ).services[0]
@@ -4498,7 +4558,11 @@ class ExtendsTest(unittest.TestCase):
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
                     {
                     {
-                        'web': {'image': 'busybox', 'extends': {}},
+                        'version': '3',
+                        'services':
+                        {
+                            'web': {'image': 'busybox', 'extends': {}},
+                        }
                     },
                     },
                     'tests/fixtures/extends',
                     'tests/fixtures/extends',
                     'filename.yml'
                     'filename.yml'
@@ -4512,7 +4576,14 @@ class ExtendsTest(unittest.TestCase):
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
                     {
                     {
-                        'web': {'image': 'busybox', 'extends': {'file': 'common.yml'}},
+                        'version': '3',
+                        'services':
+                        {
+                            'web': {
+                                'image': 'busybox',
+                                'extends': {'file': 'common.yml'}
+                            }
+                        }
                     },
                     },
                     'tests/fixtures/extends',
                     'tests/fixtures/extends',
                     'filename.yml'
                     'filename.yml'
@@ -4526,14 +4597,18 @@ class ExtendsTest(unittest.TestCase):
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
                     {
                     {
-                        'web': {
-                            'image': 'busybox',
-                            'extends': {
-                                'file': 'common.yml',
-                                'service': 'web',
-                                'rogue_key': 'is not allowed'
-                            }
-                        },
+                        'version': '3',
+                        'services':
+                        {
+                            'web': {
+                                'image': 'busybox',
+                                'extends': {
+                                    'file': 'common.yml',
+                                    'service': 'web',
+                                    'rogue_key': 'is not allowed'
+                                }
+                            },
+                        }
                     },
                     },
                     'tests/fixtures/extends',
                     'tests/fixtures/extends',
                     'filename.yml'
                     'filename.yml'
@@ -4548,11 +4623,14 @@ class ExtendsTest(unittest.TestCase):
             config.load(
             config.load(
                 build_config_details(
                 build_config_details(
                     {
                     {
-                        'web': {
-                            'image': 'busybox',
-                            'extends': {
-                                'file': 1,
-                                'service': 'web',
+                        'version': '3',
+                        'services': {
+                            'web': {
+                                'image': 'busybox',
+                                'extends': {
+                                    'file': 1,
+                                    'service': 'web',
+                                }
                             }
                             }
                         },
                         },
                     },
                     },
@@ -5205,7 +5283,7 @@ class SerializeTest(unittest.TestCase):
             }
             }
         }
         }
 
 
-        assert denormalize_service_dict(service_dict, V3_0) == {
+        assert denormalize_service_dict(service_dict, VERSION) == {
             'image': 'busybox',
             'image': 'busybox',
             'command': 'true',
             'command': 'true',
             'depends_on': ['service2', 'service3']
             'depends_on': ['service2', 'service3']
@@ -5221,7 +5299,11 @@ class SerializeTest(unittest.TestCase):
             }
             }
         }
         }
 
 
-        assert denormalize_service_dict(service_dict, V2_1) == service_dict
+        assert denormalize_service_dict(service_dict, VERSION) == {
+            'image': 'busybox',
+            'command': 'true',
+            'depends_on': ['service2', 'service3']
+        }
 
 
     def test_serialize_time(self):
     def test_serialize_time(self):
         data = {
         data = {
@@ -5255,7 +5337,7 @@ class SerializeTest(unittest.TestCase):
         processed_service = config.process_service(config.ServiceConfig(
         processed_service = config.process_service(config.ServiceConfig(
             '.', 'test', 'test', service_dict
             '.', 'test', 'test', service_dict
         ))
         ))
-        denormalized_service = denormalize_service_dict(processed_service, V2_3)
+        denormalized_service = denormalize_service_dict(processed_service, VERSION)
         assert denormalized_service['healthcheck']['interval'] == '100s'
         assert denormalized_service['healthcheck']['interval'] == '100s'
         assert denormalized_service['healthcheck']['timeout'] == '30s'
         assert denormalized_service['healthcheck']['timeout'] == '30s'
         assert denormalized_service['healthcheck']['start_period'] == '2090ms'
         assert denormalized_service['healthcheck']['start_period'] == '2090ms'
@@ -5266,7 +5348,7 @@ class SerializeTest(unittest.TestCase):
         }
         }
         image_digest = 'busybox@sha256:abcde'
         image_digest = 'busybox@sha256:abcde'
 
 
-        assert denormalize_service_dict(service_dict, V3_0, image_digest) == {
+        assert denormalize_service_dict(service_dict, VERSION, image_digest) == {
             'image': 'busybox@sha256:abcde'
             'image': 'busybox@sha256:abcde'
         }
         }
 
 
@@ -5275,7 +5357,7 @@ class SerializeTest(unittest.TestCase):
             'image': 'busybox'
             'image': 'busybox'
         }
         }
 
 
-        assert denormalize_service_dict(service_dict, V3_0) == {
+        assert denormalize_service_dict(service_dict, VERSION) == {
             'image': 'busybox'
             'image': 'busybox'
         }
         }
 
 
@@ -5308,10 +5390,10 @@ class SerializeTest(unittest.TestCase):
         serialized_service = serialized_config['services']['web']
         serialized_service = serialized_config['services']['web']
         assert secret_sort(serialized_service['secrets']) == secret_sort(service_dict['secrets'])
         assert secret_sort(serialized_service['secrets']) == secret_sort(service_dict['secrets'])
         assert 'secrets' in serialized_config
         assert 'secrets' in serialized_config
-        assert serialized_config['secrets']['two'] == secrets_dict['two']
+        assert serialized_config['secrets']['two'] == {'external': True, 'name': 'two'}
 
 
     def test_serialize_ports(self):
     def test_serialize_ports(self):
-        config_dict = config.Config(version=V2_0, services=[
+        config_dict = config.Config(version=VERSION, services=[
             {
             {
                 'ports': [types.ServicePort('80', '8080', None, None, None)],
                 'ports': [types.ServicePort('80', '8080', None, None, None)],
                 'image': 'alpine',
                 'image': 'alpine',
@@ -5320,10 +5402,10 @@ class SerializeTest(unittest.TestCase):
         ], volumes={}, networks={}, secrets={}, configs={})
         ], volumes={}, networks={}, secrets={}, configs={})
 
 
         serialized_config = yaml.safe_load(serialize_config(config_dict))
         serialized_config = yaml.safe_load(serialize_config(config_dict))
-        assert '8080:80/tcp' in serialized_config['services']['web']['ports']
+        assert [{'published': 8080, 'target': 80}] == serialized_config['services']['web']['ports']
 
 
     def test_serialize_ports_with_ext_ip(self):
     def test_serialize_ports_with_ext_ip(self):
-        config_dict = config.Config(version=V3_5, services=[
+        config_dict = config.Config(version=VERSION, services=[
             {
             {
                 'ports': [types.ServicePort('80', '8080', None, None, '127.0.0.1')],
                 'ports': [types.ServicePort('80', '8080', None, None, '127.0.0.1')],
                 'image': 'alpine',
                 'image': 'alpine',
@@ -5363,7 +5445,7 @@ class SerializeTest(unittest.TestCase):
         serialized_service = serialized_config['services']['web']
         serialized_service = serialized_config['services']['web']
         assert secret_sort(serialized_service['configs']) == secret_sort(service_dict['configs'])
         assert secret_sort(serialized_service['configs']) == secret_sort(service_dict['configs'])
         assert 'configs' in serialized_config
         assert 'configs' in serialized_config
-        assert serialized_config['configs']['two'] == configs_dict['two']
+        assert serialized_config['configs']['two'] == {'external': True, 'name': 'two'}
 
 
     def test_serialize_bool_string(self):
     def test_serialize_bool_string(self):
         cfg = {
         cfg = {

+ 15 - 17
tests/unit/config/interpolation_test.py

@@ -8,9 +8,7 @@ from compose.config.interpolation import Interpolator
 from compose.config.interpolation import InvalidInterpolation
 from compose.config.interpolation import InvalidInterpolation
 from compose.config.interpolation import TemplateWithDefaults
 from compose.config.interpolation import TemplateWithDefaults
 from compose.config.interpolation import UnsetRequiredSubstitution
 from compose.config.interpolation import UnsetRequiredSubstitution
-from compose.const import COMPOSEFILE_V2_0 as V2_0
-from compose.const import COMPOSEFILE_V2_3 as V2_3
-from compose.const import COMPOSEFILE_V3_4 as V3_4
+from compose.const import COMPOSE_SPEC as VERSION
 
 
 
 
 @pytest.fixture
 @pytest.fixture
@@ -63,7 +61,7 @@ def test_interpolate_environment_variables_in_services(mock_env):
             }
             }
         }
         }
     }
     }
-    value = interpolate_environment_variables(V2_0, services, 'service', mock_env)
+    value = interpolate_environment_variables(VERSION, services, 'service', mock_env)
     assert value == expected
     assert value == expected
 
 
 
 
@@ -88,7 +86,7 @@ def test_interpolate_environment_variables_in_volumes(mock_env):
         },
         },
         'other': {},
         'other': {},
     }
     }
-    value = interpolate_environment_variables(V2_0, volumes, 'volume', mock_env)
+    value = interpolate_environment_variables(VERSION, volumes, 'volume', mock_env)
     assert value == expected
     assert value == expected
 
 
 
 
@@ -113,7 +111,7 @@ def test_interpolate_environment_variables_in_secrets(mock_env):
         },
         },
         'other': {},
         'other': {},
     }
     }
-    value = interpolate_environment_variables(V3_4, secrets, 'secret', mock_env)
+    value = interpolate_environment_variables(VERSION, secrets, 'secret', mock_env)
     assert value == expected
     assert value == expected
 
 
 
 
@@ -184,7 +182,7 @@ def test_interpolate_environment_services_convert_types_v2(mock_env):
         }
         }
     }
     }
 
 
-    value = interpolate_environment_variables(V2_3, entry, 'service', mock_env)
+    value = interpolate_environment_variables(VERSION, entry, 'service', mock_env)
     assert value == expected
     assert value == expected
 
 
 
 
@@ -257,7 +255,7 @@ def test_interpolate_environment_services_convert_types_v3(mock_env):
         }
         }
     }
     }
 
 
-    value = interpolate_environment_variables(V3_4, entry, 'service', mock_env)
+    value = interpolate_environment_variables(VERSION, entry, 'service', mock_env)
     assert value == expected
     assert value == expected
 
 
 
 
@@ -265,21 +263,21 @@ def test_interpolate_environment_services_convert_types_invalid(mock_env):
     entry = {'service1': {'privileged': '${POSINT}'}}
     entry = {'service1': {'privileged': '${POSINT}'}}
 
 
     with pytest.raises(ConfigurationError) as exc:
     with pytest.raises(ConfigurationError) as exc:
-        interpolate_environment_variables(V2_3, entry, 'service', mock_env)
+        interpolate_environment_variables(VERSION, entry, 'service', mock_env)
 
 
     assert 'Error while attempting to convert service.service1.privileged to '\
     assert 'Error while attempting to convert service.service1.privileged to '\
         'appropriate type: "50" is not a valid boolean value' in exc.exconly()
         'appropriate type: "50" is not a valid boolean value' in exc.exconly()
 
 
     entry = {'service1': {'cpus': '${TRUE}'}}
     entry = {'service1': {'cpus': '${TRUE}'}}
     with pytest.raises(ConfigurationError) as exc:
     with pytest.raises(ConfigurationError) as exc:
-        interpolate_environment_variables(V2_3, entry, 'service', mock_env)
+        interpolate_environment_variables(VERSION, entry, 'service', mock_env)
 
 
     assert 'Error while attempting to convert service.service1.cpus to '\
     assert 'Error while attempting to convert service.service1.cpus to '\
         'appropriate type: "True" is not a valid float' in exc.exconly()
         'appropriate type: "True" is not a valid float' in exc.exconly()
 
 
     entry = {'service1': {'ulimits': {'nproc': '${FLOAT}'}}}
     entry = {'service1': {'ulimits': {'nproc': '${FLOAT}'}}}
     with pytest.raises(ConfigurationError) as exc:
     with pytest.raises(ConfigurationError) as exc:
-        interpolate_environment_variables(V2_3, entry, 'service', mock_env)
+        interpolate_environment_variables(VERSION, entry, 'service', mock_env)
 
 
     assert 'Error while attempting to convert service.service1.ulimits.nproc to '\
     assert 'Error while attempting to convert service.service1.ulimits.nproc to '\
         'appropriate type: "0.145" is not a valid integer' in exc.exconly()
         'appropriate type: "0.145" is not a valid integer' in exc.exconly()
@@ -302,7 +300,7 @@ def test_interpolate_environment_network_convert_types(mock_env):
         }
         }
     }
     }
 
 
-    value = interpolate_environment_variables(V3_4, entry, 'network', mock_env)
+    value = interpolate_environment_variables(VERSION, entry, 'network', mock_env)
     assert value == expected
     assert value == expected
 
 
 
 
@@ -319,13 +317,13 @@ def test_interpolate_environment_external_resource_convert_types(mock_env):
         }
         }
     }
     }
 
 
-    value = interpolate_environment_variables(V3_4, entry, 'network', mock_env)
+    value = interpolate_environment_variables(VERSION, entry, 'network', mock_env)
     assert value == expected
     assert value == expected
-    value = interpolate_environment_variables(V3_4, entry, 'volume', mock_env)
+    value = interpolate_environment_variables(VERSION, entry, 'volume', mock_env)
     assert value == expected
     assert value == expected
-    value = interpolate_environment_variables(V3_4, entry, 'secret', mock_env)
+    value = interpolate_environment_variables(VERSION, entry, 'secret', mock_env)
     assert value == expected
     assert value == expected
-    value = interpolate_environment_variables(V3_4, entry, 'config', mock_env)
+    value = interpolate_environment_variables(VERSION, entry, 'config', mock_env)
     assert value == expected
     assert value == expected
 
 
 
 
@@ -356,7 +354,7 @@ def test_interpolate_service_name_uses_dot(mock_env):
         }
         }
     }
     }
 
 
-    value = interpolate_environment_variables(V3_4, entry, 'service', mock_env)
+    value = interpolate_environment_variables(VERSION, entry, 'service', mock_env)
     assert value == expected
     assert value == expected
 
 
 
 

+ 7 - 7
tests/unit/config/types_test.py

@@ -5,8 +5,8 @@ from compose.config.types import parse_extra_hosts
 from compose.config.types import ServicePort
 from compose.config.types import ServicePort
 from compose.config.types import VolumeFromSpec
 from compose.config.types import VolumeFromSpec
 from compose.config.types import VolumeSpec
 from compose.config.types import VolumeSpec
+from compose.const import COMPOSE_SPEC as VERSION
 from compose.const import COMPOSEFILE_V1 as V1
 from compose.const import COMPOSEFILE_V1 as V1
-from compose.const import COMPOSEFILE_V2_0 as V2_0
 
 
 
 
 def test_parse_extra_hosts_list():
 def test_parse_extra_hosts_list():
@@ -233,26 +233,26 @@ class TestVolumesFromSpec(object):
             VolumeFromSpec.parse('unknown:format:ro', self.services, V1)
             VolumeFromSpec.parse('unknown:format:ro', self.services, V1)
 
 
     def test_parse_v2_from_service(self):
     def test_parse_v2_from_service(self):
-        volume_from = VolumeFromSpec.parse('servicea', self.services, V2_0)
+        volume_from = VolumeFromSpec.parse('servicea', self.services, VERSION)
         assert volume_from == VolumeFromSpec('servicea', 'rw', 'service')
         assert volume_from == VolumeFromSpec('servicea', 'rw', 'service')
 
 
     def test_parse_v2_from_service_with_mode(self):
     def test_parse_v2_from_service_with_mode(self):
-        volume_from = VolumeFromSpec.parse('servicea:ro', self.services, V2_0)
+        volume_from = VolumeFromSpec.parse('servicea:ro', self.services, VERSION)
         assert volume_from == VolumeFromSpec('servicea', 'ro', 'service')
         assert volume_from == VolumeFromSpec('servicea', 'ro', 'service')
 
 
     def test_parse_v2_from_container(self):
     def test_parse_v2_from_container(self):
-        volume_from = VolumeFromSpec.parse('container:foo', self.services, V2_0)
+        volume_from = VolumeFromSpec.parse('container:foo', self.services, VERSION)
         assert volume_from == VolumeFromSpec('foo', 'rw', 'container')
         assert volume_from == VolumeFromSpec('foo', 'rw', 'container')
 
 
     def test_parse_v2_from_container_with_mode(self):
     def test_parse_v2_from_container_with_mode(self):
-        volume_from = VolumeFromSpec.parse('container:foo:ro', self.services, V2_0)
+        volume_from = VolumeFromSpec.parse('container:foo:ro', self.services, VERSION)
         assert volume_from == VolumeFromSpec('foo', 'ro', 'container')
         assert volume_from == VolumeFromSpec('foo', 'ro', 'container')
 
 
     def test_parse_v2_invalid_type(self):
     def test_parse_v2_invalid_type(self):
         with pytest.raises(ConfigurationError) as exc:
         with pytest.raises(ConfigurationError) as exc:
-            VolumeFromSpec.parse('bogus:foo:ro', self.services, V2_0)
+            VolumeFromSpec.parse('bogus:foo:ro', self.services, VERSION)
         assert "Unknown volumes_from type 'bogus'" in exc.exconly()
         assert "Unknown volumes_from type 'bogus'" in exc.exconly()
 
 
     def test_parse_v2_invalid(self):
     def test_parse_v2_invalid(self):
         with pytest.raises(ConfigurationError):
         with pytest.raises(ConfigurationError):
-            VolumeFromSpec.parse('unknown:format:ro', self.services, V2_0)
+            VolumeFromSpec.parse('unknown:format:ro', self.services, VERSION)

+ 28 - 31
tests/unit/project_test.py

@@ -13,10 +13,8 @@ from ..helpers import BUSYBOX_IMAGE_WITH_TAG
 from compose.config import ConfigurationError
 from compose.config import ConfigurationError
 from compose.config.config import Config
 from compose.config.config import Config
 from compose.config.types import VolumeFromSpec
 from compose.config.types import VolumeFromSpec
+from compose.const import COMPOSE_SPEC as VERSION
 from compose.const import COMPOSEFILE_V1 as V1
 from compose.const import COMPOSEFILE_V1 as V1
-from compose.const import COMPOSEFILE_V2_0 as V2_0
-from compose.const import COMPOSEFILE_V2_4 as V2_4
-from compose.const import COMPOSEFILE_V3_7 as V3_7
 from compose.const import DEFAULT_TIMEOUT
 from compose.const import DEFAULT_TIMEOUT
 from compose.const import LABEL_SERVICE
 from compose.const import LABEL_SERVICE
 from compose.container import Container
 from compose.container import Container
@@ -29,6 +27,17 @@ from compose.service import ImageType
 from compose.service import Service
 from compose.service import Service
 
 
 
 
+def build_config(**kwargs):
+    return Config(
+        version=kwargs.get('version', VERSION),
+        services=kwargs.get('services'),
+        volumes=kwargs.get('volumes'),
+        networks=kwargs.get('networks'),
+        secrets=kwargs.get('secrets'),
+        configs=kwargs.get('configs'),
+    )
+
+
 class ProjectTest(unittest.TestCase):
 class ProjectTest(unittest.TestCase):
     def setUp(self):
     def setUp(self):
         self.mock_client = mock.create_autospec(docker.APIClient)
         self.mock_client = mock.create_autospec(docker.APIClient)
@@ -36,7 +45,7 @@ class ProjectTest(unittest.TestCase):
         self.mock_client.api_version = docker.constants.DEFAULT_DOCKER_API_VERSION
         self.mock_client.api_version = docker.constants.DEFAULT_DOCKER_API_VERSION
 
 
     def test_from_config_v1(self):
     def test_from_config_v1(self):
-        config = Config(
+        config = build_config(
             version=V1,
             version=V1,
             services=[
             services=[
                 {
                 {
@@ -67,8 +76,7 @@ class ProjectTest(unittest.TestCase):
 
 
     @mock.patch('compose.network.Network.true_name', lambda n: n.full_name)
     @mock.patch('compose.network.Network.true_name', lambda n: n.full_name)
     def test_from_config_v2(self):
     def test_from_config_v2(self):
-        config = Config(
-            version=V2_0,
+        config = build_config(
             services=[
             services=[
                 {
                 {
                     'name': 'web',
                     'name': 'web',
@@ -174,8 +182,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=self.mock_client,
             client=self.mock_client,
-            config_data=Config(
-                version=V2_0,
+            config_data=build_config(
                 services=[{
                 services=[{
                     'name': 'test',
                     'name': 'test',
                     'image': BUSYBOX_IMAGE_WITH_TAG,
                     'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -202,8 +209,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=self.mock_client,
             client=self.mock_client,
-            config_data=Config(
-                version=V2_0,
+            config_data=build_config(
                 services=[
                 services=[
                     {
                     {
                         'name': 'vol',
                         'name': 'vol',
@@ -230,8 +236,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=None,
             client=None,
-            config_data=Config(
-                version=V2_0,
+            config_data=build_config(
                 services=[
                 services=[
                     {
                     {
                         'name': 'vol',
                         'name': 'vol',
@@ -540,7 +545,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=self.mock_client,
             client=self.mock_client,
-            config_data=Config(
+            config_data=build_config(
                 version=V1,
                 version=V1,
                 services=[
                 services=[
                     {
                     {
@@ -565,8 +570,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=self.mock_client,
             client=self.mock_client,
-            config_data=Config(
-                version=V2_0,
+            config_data=build_config(
                 services=[
                 services=[
                     {
                     {
                         'name': 'test',
                         'name': 'test',
@@ -596,8 +600,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=self.mock_client,
             client=self.mock_client,
-            config_data=Config(
-                version=V2_0,
+            config_data=build_config(
                 services=[
                 services=[
                     {
                     {
                         'name': 'aaa',
                         'name': 'aaa',
@@ -623,8 +626,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=self.mock_client,
             client=self.mock_client,
-            config_data=Config(
-                version=V2_0,
+            config_data=build_config(
                 services=[
                 services=[
                     {
                     {
                         'name': 'foo',
                         'name': 'foo',
@@ -644,8 +646,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=self.mock_client,
             client=self.mock_client,
-            config_data=Config(
-                version=V2_0,
+            config_data=build_config(
                 services=[
                 services=[
                     {
                     {
                         'name': 'foo',
                         'name': 'foo',
@@ -679,8 +680,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=self.mock_client,
             client=self.mock_client,
-            config_data=Config(
-                version=V2_0,
+            config_data=build_config(
                 services=[{
                 services=[{
                     'name': 'web',
                     'name': 'web',
                     'image': BUSYBOX_IMAGE_WITH_TAG,
                     'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -697,8 +697,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=self.mock_client,
             client=self.mock_client,
-            config_data=Config(
-                version=V2_0,
+            config_data=build_config(
                 services=[{
                 services=[{
                     'name': 'web',
                     'name': 'web',
                     'image': BUSYBOX_IMAGE_WITH_TAG,
                     'image': BUSYBOX_IMAGE_WITH_TAG,
@@ -748,8 +747,8 @@ class ProjectTest(unittest.TestCase):
             'name': 'web',
             'name': 'web',
             'image': BUSYBOX_IMAGE_WITH_TAG,
             'image': BUSYBOX_IMAGE_WITH_TAG,
         }
         }
-        config_data = Config(
-            version=V2_4, services=[service_config], networks={}, volumes={}, secrets=None, configs=None
+        config_data = build_config(
+            services=[service_config], networks={}, volumes={}, secrets=None, configs=None
         )
         )
 
 
         project = Project.from_config(name='test', client=self.mock_client, config_data=config_data)
         project = Project.from_config(name='test', client=self.mock_client, config_data=config_data)
@@ -770,8 +769,7 @@ class ProjectTest(unittest.TestCase):
         assert project.get_service('web').platform == 'linux/s390x'
         assert project.get_service('web').platform == 'linux/s390x'
 
 
     def test_build_container_operation_with_timeout_func_does_not_mutate_options_with_timeout(self):
     def test_build_container_operation_with_timeout_func_does_not_mutate_options_with_timeout(self):
-        config_data = Config(
-            version=V3_7,
+        config_data = build_config(
             services=[
             services=[
                 {'name': 'web', 'image': BUSYBOX_IMAGE_WITH_TAG},
                 {'name': 'web', 'image': BUSYBOX_IMAGE_WITH_TAG},
                 {'name': 'db', 'image': BUSYBOX_IMAGE_WITH_TAG, 'stop_grace_period': '1s'},
                 {'name': 'db', 'image': BUSYBOX_IMAGE_WITH_TAG, 'stop_grace_period': '1s'},
@@ -802,8 +800,7 @@ class ProjectTest(unittest.TestCase):
         project = Project.from_config(
         project = Project.from_config(
             name='test',
             name='test',
             client=self.mock_client,
             client=self.mock_client,
-            config_data=Config(
-                version=V2_0,
+            config_data=build_config(
                 services=[{
                 services=[{
                     'name': 'web',
                     'name': 'web',
                     'image': BUSYBOX_IMAGE_WITH_TAG,
                     'image': BUSYBOX_IMAGE_WITH_TAG,