mirror of
https://github.com/docker/compose.git
synced 2025-06-06 14:50:20 +02:00
Implement extends
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
This commit is contained in:
parent
5dca6c232e
commit
4c582e4352
@ -52,34 +52,110 @@ DOCKER_CONFIG_HINTS = {
|
|||||||
|
|
||||||
def load(filename):
|
def load(filename):
|
||||||
working_dir = os.path.dirname(filename)
|
working_dir = os.path.dirname(filename)
|
||||||
return from_dictionary(load_yaml(filename), working_dir=working_dir)
|
return from_dictionary(load_yaml(filename), working_dir=working_dir, filename=filename)
|
||||||
|
|
||||||
|
|
||||||
def load_yaml(filename):
|
def from_dictionary(dictionary, working_dir=None, filename=None):
|
||||||
try:
|
|
||||||
with open(filename, 'r') as fh:
|
|
||||||
return yaml.safe_load(fh)
|
|
||||||
except IOError as e:
|
|
||||||
raise ConfigurationError(six.text_type(e))
|
|
||||||
|
|
||||||
|
|
||||||
def from_dictionary(dictionary, working_dir=None):
|
|
||||||
service_dicts = []
|
service_dicts = []
|
||||||
|
|
||||||
for service_name, service_dict in list(dictionary.items()):
|
for service_name, service_dict in list(dictionary.items()):
|
||||||
if not isinstance(service_dict, dict):
|
if not isinstance(service_dict, dict):
|
||||||
raise ConfigurationError('Service "%s" doesn\'t have any configuration options. All top level keys in your docker-compose.yml must map to a dictionary of configuration options.' % service_name)
|
raise ConfigurationError('Service "%s" doesn\'t have any configuration options. All top level keys in your docker-compose.yml must map to a dictionary of configuration options.' % service_name)
|
||||||
service_dict = make_service_dict(service_name, service_dict, working_dir=working_dir)
|
loader = ServiceLoader(working_dir=working_dir, filename=filename)
|
||||||
|
service_dict = loader.make_service_dict(service_name, service_dict)
|
||||||
service_dicts.append(service_dict)
|
service_dicts.append(service_dict)
|
||||||
|
|
||||||
return service_dicts
|
return service_dicts
|
||||||
|
|
||||||
|
|
||||||
def make_service_dict(name, options, working_dir=None):
|
def make_service_dict(name, service_dict, working_dir=None):
|
||||||
service_dict = options.copy()
|
return ServiceLoader(working_dir=working_dir).make_service_dict(name, service_dict)
|
||||||
service_dict['name'] = name
|
|
||||||
service_dict = resolve_environment(service_dict, working_dir=working_dir)
|
|
||||||
return process_container_options(service_dict, working_dir=working_dir)
|
class ServiceLoader(object):
|
||||||
|
def __init__(self, working_dir, filename=None, already_seen=None):
|
||||||
|
self.working_dir = working_dir
|
||||||
|
self.filename = filename
|
||||||
|
self.already_seen = already_seen or []
|
||||||
|
|
||||||
|
def make_service_dict(self, name, service_dict):
|
||||||
|
if self.signature(name) in self.already_seen:
|
||||||
|
raise CircularReference(self.already_seen)
|
||||||
|
|
||||||
|
service_dict = service_dict.copy()
|
||||||
|
service_dict['name'] = name
|
||||||
|
service_dict = resolve_environment(service_dict, working_dir=self.working_dir)
|
||||||
|
service_dict = self.resolve_extends(service_dict)
|
||||||
|
return process_container_options(service_dict, working_dir=self.working_dir)
|
||||||
|
|
||||||
|
def resolve_extends(self, service_dict):
|
||||||
|
if 'extends' not in service_dict:
|
||||||
|
return service_dict
|
||||||
|
|
||||||
|
extends_options = process_extends_options(service_dict['name'], service_dict['extends'])
|
||||||
|
|
||||||
|
if self.working_dir is None:
|
||||||
|
raise Exception("No working_dir passed to ServiceLoader()")
|
||||||
|
|
||||||
|
other_config_path = expand_path(self.working_dir, extends_options['file'])
|
||||||
|
other_working_dir = os.path.dirname(other_config_path)
|
||||||
|
other_already_seen = self.already_seen + [self.signature(service_dict['name'])]
|
||||||
|
other_loader = ServiceLoader(
|
||||||
|
working_dir=other_working_dir,
|
||||||
|
filename=other_config_path,
|
||||||
|
already_seen=other_already_seen,
|
||||||
|
)
|
||||||
|
|
||||||
|
other_config = load_yaml(other_config_path)
|
||||||
|
other_service_dict = other_config[extends_options['service']]
|
||||||
|
other_service_dict = other_loader.make_service_dict(
|
||||||
|
service_dict['name'],
|
||||||
|
other_service_dict,
|
||||||
|
)
|
||||||
|
validate_extended_service_dict(
|
||||||
|
other_service_dict,
|
||||||
|
filename=other_config_path,
|
||||||
|
service=extends_options['service'],
|
||||||
|
)
|
||||||
|
|
||||||
|
return merge_service_dicts(other_service_dict, service_dict)
|
||||||
|
|
||||||
|
def signature(self, name):
|
||||||
|
return (self.filename, name)
|
||||||
|
|
||||||
|
|
||||||
|
def process_extends_options(service_name, extends_options):
|
||||||
|
error_prefix = "Invalid 'extends' configuration for %s:" % service_name
|
||||||
|
|
||||||
|
if not isinstance(extends_options, dict):
|
||||||
|
raise ConfigurationError("%s must be a dictionary" % error_prefix)
|
||||||
|
|
||||||
|
if 'service' not in extends_options:
|
||||||
|
raise ConfigurationError(
|
||||||
|
"%s you need to specify a service, e.g. 'service: web'" % error_prefix
|
||||||
|
)
|
||||||
|
|
||||||
|
for k, _ in extends_options.items():
|
||||||
|
if k not in ['file', 'service']:
|
||||||
|
raise ConfigurationError(
|
||||||
|
"%s unsupported configuration option '%s'" % (error_prefix, k)
|
||||||
|
)
|
||||||
|
|
||||||
|
return extends_options
|
||||||
|
|
||||||
|
|
||||||
|
def validate_extended_service_dict(service_dict, filename, service):
|
||||||
|
error_prefix = "Cannot extend service '%s' in %s:" % (service, filename)
|
||||||
|
|
||||||
|
if 'links' in service_dict:
|
||||||
|
raise ConfigurationError("%s services with 'links' cannot be extended" % error_prefix)
|
||||||
|
|
||||||
|
if 'volumes_from' in service_dict:
|
||||||
|
raise ConfigurationError("%s services with 'volumes_from' cannot be extended" % error_prefix)
|
||||||
|
|
||||||
|
if 'net' in service_dict:
|
||||||
|
if get_service_name_from_net(service_dict['net']) is not None:
|
||||||
|
raise ConfigurationError("%s services with 'net: container' cannot be extended" % error_prefix)
|
||||||
|
|
||||||
|
|
||||||
def process_container_options(service_dict, working_dir=None):
|
def process_container_options(service_dict, working_dir=None):
|
||||||
@ -93,6 +169,29 @@ def process_container_options(service_dict, working_dir=None):
|
|||||||
return service_dict
|
return service_dict
|
||||||
|
|
||||||
|
|
||||||
|
def merge_service_dicts(base, override):
|
||||||
|
d = base.copy()
|
||||||
|
|
||||||
|
if 'environment' in base or 'environment' in override:
|
||||||
|
d['environment'] = merge_environment(
|
||||||
|
base.get('environment'),
|
||||||
|
override.get('environment'),
|
||||||
|
)
|
||||||
|
|
||||||
|
for k in ALLOWED_KEYS:
|
||||||
|
if k not in ['environment']:
|
||||||
|
if k in override:
|
||||||
|
d[k] = override[k]
|
||||||
|
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def merge_environment(base, override):
|
||||||
|
env = parse_environment(base)
|
||||||
|
env.update(parse_environment(override))
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
def parse_links(links):
|
def parse_links(links):
|
||||||
return dict(parse_link(l) for l in links)
|
return dict(parse_link(l) for l in links)
|
||||||
|
|
||||||
@ -190,9 +289,41 @@ def expand_path(working_dir, path):
|
|||||||
return os.path.abspath(os.path.join(working_dir, path))
|
return os.path.abspath(os.path.join(working_dir, path))
|
||||||
|
|
||||||
|
|
||||||
|
def get_service_name_from_net(net_config):
|
||||||
|
if not net_config:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not net_config.startswith('container:'):
|
||||||
|
return
|
||||||
|
|
||||||
|
_, net_name = net_config.split(':', 1)
|
||||||
|
return net_name
|
||||||
|
|
||||||
|
|
||||||
|
def load_yaml(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, 'r') as fh:
|
||||||
|
return yaml.safe_load(fh)
|
||||||
|
except IOError as e:
|
||||||
|
raise ConfigurationError(six.text_type(e))
|
||||||
|
|
||||||
|
|
||||||
class ConfigurationError(Exception):
|
class ConfigurationError(Exception):
|
||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.msg
|
return self.msg
|
||||||
|
|
||||||
|
|
||||||
|
class CircularReference(ConfigurationError):
|
||||||
|
def __init__(self, trail):
|
||||||
|
self.trail = trail
|
||||||
|
|
||||||
|
@property
|
||||||
|
def msg(self):
|
||||||
|
lines = [
|
||||||
|
"{} in {}".format(service_name, filename)
|
||||||
|
for (filename, service_name) in self.trail
|
||||||
|
]
|
||||||
|
return "Circular reference:\n {}".format("\n extends ".join(lines))
|
||||||
|
@ -3,7 +3,7 @@ from __future__ import absolute_import
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from .config import ConfigurationError
|
from .config import get_service_name_from_net, ConfigurationError
|
||||||
from .service import Service
|
from .service import Service
|
||||||
from .container import Container
|
from .container import Container
|
||||||
from docker.errors import APIError
|
from docker.errors import APIError
|
||||||
@ -11,17 +11,6 @@ from docker.errors import APIError
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_service_name_from_net(net_config):
|
|
||||||
if not net_config:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not net_config.startswith('container:'):
|
|
||||||
return
|
|
||||||
|
|
||||||
_, net_name = net_config.split(':', 1)
|
|
||||||
return net_name
|
|
||||||
|
|
||||||
|
|
||||||
def sort_service_dicts(services):
|
def sort_service_dicts(services):
|
||||||
# Topological sort (Cormen/Tarjan algorithm).
|
# Topological sort (Cormen/Tarjan algorithm).
|
||||||
unmarked = services[:]
|
unmarked = services[:]
|
||||||
|
77
docs/yml.md
77
docs/yml.md
@ -176,6 +176,83 @@ env_file:
|
|||||||
RACK_ENV: development
|
RACK_ENV: development
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### extends
|
||||||
|
|
||||||
|
Extend another service, in the current file or another, optionally overriding
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
Here's a simple example. Suppose we have 2 files - **common.yml** and
|
||||||
|
**development.yml**. We can use `extends` to define a service in
|
||||||
|
**development.yml** which uses configuration defined in **common.yml**:
|
||||||
|
|
||||||
|
**common.yml**
|
||||||
|
|
||||||
|
```
|
||||||
|
webapp:
|
||||||
|
build: ./webapp
|
||||||
|
environment:
|
||||||
|
- DEBUG=false
|
||||||
|
- SEND_EMAILS=false
|
||||||
|
```
|
||||||
|
|
||||||
|
**development.yml**
|
||||||
|
|
||||||
|
```
|
||||||
|
web:
|
||||||
|
extends:
|
||||||
|
file: common.yml
|
||||||
|
service: webapp
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
links:
|
||||||
|
- db
|
||||||
|
environment:
|
||||||
|
- DEBUG=true
|
||||||
|
db:
|
||||||
|
image: postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, the `web` service in **development.yml** inherits the configuration of
|
||||||
|
the `webapp` service in **common.yml** - the `build` and `environment` keys -
|
||||||
|
and adds `ports` and `links` configuration. It overrides one of the defined
|
||||||
|
environment variables (DEBUG) with a new value, and the other one
|
||||||
|
(SEND_EMAILS) is left untouched. It's exactly as if you defined `web` like
|
||||||
|
this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
web:
|
||||||
|
build: ./webapp
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
links:
|
||||||
|
- db
|
||||||
|
environment:
|
||||||
|
- DEBUG=true
|
||||||
|
- SEND_EMAILS=false
|
||||||
|
```
|
||||||
|
|
||||||
|
The `extends` option is great for sharing configuration between different
|
||||||
|
apps, or for configuring the same app differently for different environments.
|
||||||
|
You could write a new file for a staging environment, **staging.yml**, which
|
||||||
|
binds to a different port and doesn't turn on debugging:
|
||||||
|
|
||||||
|
```
|
||||||
|
web:
|
||||||
|
extends:
|
||||||
|
file: common.yml
|
||||||
|
service: webapp
|
||||||
|
ports:
|
||||||
|
- "80:8000"
|
||||||
|
links:
|
||||||
|
- db
|
||||||
|
db:
|
||||||
|
image: postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** When you extend a service, `links` and `volumes_from`
|
||||||
|
> configuration options are **not** inherited - you will have to define
|
||||||
|
> those manually each time you extend it.
|
||||||
|
|
||||||
### net
|
### net
|
||||||
|
|
||||||
Networking mode. Use the same values as the docker client `--net` parameter.
|
Networking mode. Use the same values as the docker client `--net` parameter.
|
||||||
|
12
tests/fixtures/extends/circle-1.yml
vendored
Normal file
12
tests/fixtures/extends/circle-1.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
foo:
|
||||||
|
image: busybox
|
||||||
|
bar:
|
||||||
|
image: busybox
|
||||||
|
web:
|
||||||
|
extends:
|
||||||
|
file: circle-2.yml
|
||||||
|
service: web
|
||||||
|
baz:
|
||||||
|
image: busybox
|
||||||
|
quux:
|
||||||
|
image: busybox
|
12
tests/fixtures/extends/circle-2.yml
vendored
Normal file
12
tests/fixtures/extends/circle-2.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
foo:
|
||||||
|
image: busybox
|
||||||
|
bar:
|
||||||
|
image: busybox
|
||||||
|
web:
|
||||||
|
extends:
|
||||||
|
file: circle-1.yml
|
||||||
|
service: web
|
||||||
|
baz:
|
||||||
|
image: busybox
|
||||||
|
quux:
|
||||||
|
image: busybox
|
6
tests/fixtures/extends/common.yml
vendored
Normal file
6
tests/fixtures/extends/common.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
web:
|
||||||
|
image: busybox
|
||||||
|
command: /bin/true
|
||||||
|
environment:
|
||||||
|
- FOO=1
|
||||||
|
- BAR=1
|
16
tests/fixtures/extends/docker-compose.yml
vendored
Normal file
16
tests/fixtures/extends/docker-compose.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
myweb:
|
||||||
|
extends:
|
||||||
|
file: common.yml
|
||||||
|
service: web
|
||||||
|
command: sleep 300
|
||||||
|
links:
|
||||||
|
- "mydb:db"
|
||||||
|
environment:
|
||||||
|
# leave FOO alone
|
||||||
|
# override BAR
|
||||||
|
BAR: "2"
|
||||||
|
# add BAZ
|
||||||
|
BAZ: "2"
|
||||||
|
mydb:
|
||||||
|
image: busybox
|
||||||
|
command: sleep 300
|
6
tests/fixtures/extends/nested-intermediate.yml
vendored
Normal file
6
tests/fixtures/extends/nested-intermediate.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
webintermediate:
|
||||||
|
extends:
|
||||||
|
file: common.yml
|
||||||
|
service: web
|
||||||
|
environment:
|
||||||
|
- "FOO=2"
|
6
tests/fixtures/extends/nested.yml
vendored
Normal file
6
tests/fixtures/extends/nested.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
myweb:
|
||||||
|
extends:
|
||||||
|
file: nested-intermediate.yml
|
||||||
|
service: webintermediate
|
||||||
|
environment:
|
||||||
|
- "BAR=2"
|
@ -427,3 +427,30 @@ class CLITestCase(DockerClientTestCase):
|
|||||||
containers = self.project.containers(stopped=True)
|
containers = self.project.containers(stopped=True)
|
||||||
self.assertEqual(len(containers), 1)
|
self.assertEqual(len(containers), 1)
|
||||||
self.assertIn("FOO=1", containers[0].get('Config.Env'))
|
self.assertIn("FOO=1", containers[0].get('Config.Env'))
|
||||||
|
|
||||||
|
def test_up_with_extends(self):
|
||||||
|
self.command.base_dir = 'tests/fixtures/extends'
|
||||||
|
self.command.dispatch(['up', '-d'], None)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
set([s.name for s in self.project.services]),
|
||||||
|
set(['mydb', 'myweb']),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Sort by name so we get [db, web]
|
||||||
|
containers = sorted(
|
||||||
|
self.project.containers(stopped=True),
|
||||||
|
key=lambda c: c.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(len(containers), 2)
|
||||||
|
web = containers[1]
|
||||||
|
|
||||||
|
self.assertEqual(set(web.links()), set(['db', 'mydb_1', 'extends_mydb_1']))
|
||||||
|
|
||||||
|
expected_env = set([
|
||||||
|
"FOO=1",
|
||||||
|
"BAR=2",
|
||||||
|
"BAZ=2",
|
||||||
|
])
|
||||||
|
self.assertTrue(expected_env <= set(web.get('Config.Env')))
|
||||||
|
@ -38,6 +38,8 @@ class ConfigTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
config.make_service_dict('foo', {'ports': ['8000']})
|
config.make_service_dict('foo', {'ports': ['8000']})
|
||||||
|
|
||||||
|
|
||||||
|
class EnvTest(unittest.TestCase):
|
||||||
def test_parse_environment_as_list(self):
|
def test_parse_environment_as_list(self):
|
||||||
environment =[
|
environment =[
|
||||||
'NORMAL=F1',
|
'NORMAL=F1',
|
||||||
@ -130,3 +132,112 @@ class ConfigTest(unittest.TestCase):
|
|||||||
service_dict['environment'],
|
service_dict['environment'],
|
||||||
{'FILE_DEF': 'F1', 'FILE_DEF_EMPTY': '', 'ENV_DEF': 'E3', 'NO_DEF': ''},
|
{'FILE_DEF': 'F1', 'FILE_DEF_EMPTY': '', 'ENV_DEF': 'E3', 'NO_DEF': ''},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ExtendsTest(unittest.TestCase):
|
||||||
|
def test_extends(self):
|
||||||
|
service_dicts = config.load('tests/fixtures/extends/docker-compose.yml')
|
||||||
|
|
||||||
|
service_dicts = sorted(
|
||||||
|
service_dicts,
|
||||||
|
key=lambda sd: sd['name'],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(service_dicts, [
|
||||||
|
{
|
||||||
|
'name': 'mydb',
|
||||||
|
'image': 'busybox',
|
||||||
|
'command': 'sleep 300',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'myweb',
|
||||||
|
'image': 'busybox',
|
||||||
|
'command': 'sleep 300',
|
||||||
|
'links': ['mydb:db'],
|
||||||
|
'environment': {
|
||||||
|
"FOO": "1",
|
||||||
|
"BAR": "2",
|
||||||
|
"BAZ": "2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_nested(self):
|
||||||
|
service_dicts = config.load('tests/fixtures/extends/nested.yml')
|
||||||
|
|
||||||
|
self.assertEqual(service_dicts, [
|
||||||
|
{
|
||||||
|
'name': 'myweb',
|
||||||
|
'image': 'busybox',
|
||||||
|
'command': '/bin/true',
|
||||||
|
'environment': {
|
||||||
|
"FOO": "2",
|
||||||
|
"BAR": "2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_circular(self):
|
||||||
|
try:
|
||||||
|
config.load('tests/fixtures/extends/circle-1.yml')
|
||||||
|
raise Exception("Expected config.CircularReference to be raised")
|
||||||
|
except config.CircularReference as e:
|
||||||
|
self.assertEqual(
|
||||||
|
[(os.path.basename(filename), service_name) for (filename, service_name) in e.trail],
|
||||||
|
[
|
||||||
|
('circle-1.yml', 'web'),
|
||||||
|
('circle-2.yml', 'web'),
|
||||||
|
('circle-1.yml', 'web'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_extends_validation(self):
|
||||||
|
dictionary = {'extends': None}
|
||||||
|
load_config = lambda: config.make_service_dict('myweb', dictionary, working_dir='tests/fixtures/extends')
|
||||||
|
|
||||||
|
self.assertRaisesRegexp(config.ConfigurationError, 'dictionary', load_config)
|
||||||
|
|
||||||
|
dictionary['extends'] = {}
|
||||||
|
self.assertRaises(config.ConfigurationError, load_config)
|
||||||
|
|
||||||
|
dictionary['extends']['file'] = 'common.yml'
|
||||||
|
self.assertRaisesRegexp(config.ConfigurationError, 'service', load_config)
|
||||||
|
|
||||||
|
dictionary['extends']['service'] = 'web'
|
||||||
|
self.assertIsInstance(load_config(), dict)
|
||||||
|
|
||||||
|
dictionary['extends']['what'] = 'is this'
|
||||||
|
self.assertRaisesRegexp(config.ConfigurationError, 'what', load_config)
|
||||||
|
|
||||||
|
def test_blacklisted_options(self):
|
||||||
|
def load_config():
|
||||||
|
return config.make_service_dict('myweb', {
|
||||||
|
'extends': {
|
||||||
|
'file': 'whatever',
|
||||||
|
'service': 'web',
|
||||||
|
}
|
||||||
|
}, '.')
|
||||||
|
|
||||||
|
with self.assertRaisesRegexp(config.ConfigurationError, 'links'):
|
||||||
|
other_config = {'web': {'links': ['db']}}
|
||||||
|
|
||||||
|
with mock.patch.object(config, 'load_yaml', return_value=other_config):
|
||||||
|
print load_config()
|
||||||
|
|
||||||
|
with self.assertRaisesRegexp(config.ConfigurationError, 'volumes_from'):
|
||||||
|
other_config = {'web': {'volumes_from': ['db']}}
|
||||||
|
|
||||||
|
with mock.patch.object(config, 'load_yaml', return_value=other_config):
|
||||||
|
print load_config()
|
||||||
|
|
||||||
|
with self.assertRaisesRegexp(config.ConfigurationError, 'net'):
|
||||||
|
other_config = {'web': {'net': 'container:db'}}
|
||||||
|
|
||||||
|
with mock.patch.object(config, 'load_yaml', return_value=other_config):
|
||||||
|
print load_config()
|
||||||
|
|
||||||
|
other_config = {'web': {'net': 'host'}}
|
||||||
|
|
||||||
|
with mock.patch.object(config, 'load_yaml', return_value=other_config):
|
||||||
|
print load_config()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user