summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorJoffrey F <joffrey@docker.com>2017-12-04 22:47:33 -0800
committerJoffrey F <joffrey@docker.com>2017-12-05 16:21:49 -0800
commit79b20eb53f3c580b5202000708e9c39fae8f2c92 (patch)
treeaa2d8a130c0486bf589946a5e663415703e4dbc7 /tests
parent6cd0bc48836cd06983c255aaad6be047ce4a0394 (diff)
Add support for mount syntax
Signed-off-by: Joffrey F <joffrey@docker.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/acceptance/cli_test.py22
-rw-r--r--tests/helpers.py13
-rw-r--r--tests/integration/project_test.py21
-rw-r--r--tests/integration/service_test.py82
-rw-r--r--tests/integration/testcases.py6
-rw-r--r--tests/unit/config/config_test.py53
-rw-r--r--tests/unit/service_test.py4
7 files changed, 181 insertions, 20 deletions
diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py
index 8ec3f9cd..2ce5bf83 100644
--- a/tests/acceptance/cli_test.py
+++ b/tests/acceptance/cli_test.py
@@ -428,13 +428,21 @@ class CLITestCase(DockerClientTestCase):
'timeout': '1s',
'retries': 5,
},
- 'volumes': [
- '/host/path:/container/path:ro',
- 'foobar:/container/volumepath:rw',
- '/anonymous',
- 'foobar:/container/volumepath2:nocopy'
- ],
-
+ 'volumes': [{
+ 'read_only': True,
+ 'source': '/host/path',
+ 'target': '/container/path',
+ 'type': 'bind'
+ }, {
+ 'source': 'foobar', 'target': '/container/volumepath', 'type': 'volume'
+ }, {
+ 'target': '/anonymous', 'type': 'volume'
+ }, {
+ 'source': 'foobar',
+ 'target': '/container/volumepath2',
+ 'type': 'volume',
+ 'volume': {'nocopy': True}
+ }],
'stop_grace_period': '20s',
},
},
diff --git a/tests/helpers.py b/tests/helpers.py
index a93de993..f151f9cd 100644
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -19,12 +19,8 @@ def build_config_details(contents, working_dir='working_dir', filename='filename
)
-def create_host_file(client, filename):
+def create_custom_host_file(client, filename, content):
dirname = os.path.dirname(filename)
-
- with open(filename, 'r') as fh:
- content = fh.read()
-
container = client.create_container(
'busybox:latest',
['sh', '-c', 'echo -n "{}" > {}'.format(content, filename)],
@@ -48,3 +44,10 @@ def create_host_file(client, filename):
return container_info['Node']['Name']
finally:
client.remove_container(container, force=True)
+
+
+def create_host_file(client, filename):
+ with open(filename, 'r') as fh:
+ content = fh.read()
+
+ return create_custom_host_file(client, filename, content)
diff --git a/tests/integration/project_test.py b/tests/integration/project_test.py
index 953dd52b..6686d96c 100644
--- a/tests/integration/project_test.py
+++ b/tests/integration/project_test.py
@@ -35,6 +35,7 @@ from tests.integration.testcases import is_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
@@ -436,6 +437,26 @@ class ProjectTest(DockerClientTestCase):
self.assertNotEqual(db_container.id, old_db_id)
self.assertEqual(db_container.get('Volumes./etc'), db_volume_path)
+ @v2_3_only()
+ def test_recreate_preserves_mounts(self):
+ web = self.create_service('web')
+ db = self.create_service('db', volumes=[types.MountSpec(type='volume', target='/etc')])
+ project = Project('composetest', [web, db], self.client)
+ project.start()
+ assert len(project.containers()) == 0
+
+ project.up(['db'])
+ assert len(project.containers()) == 1
+ old_db_id = project.containers()[0].id
+ db_volume_path = project.containers()[0].get_mount('/etc')['Source']
+
+ project.up(strategy=ConvergenceStrategy.always)
+ assert len(project.containers()) == 2
+
+ db_container = [c for c in project.containers() if 'db' in c.name][0]
+ assert db_container.id != old_db_id
+ assert db_container.get_mount('/etc')['Source'] == db_volume_path
+
def test_project_up_with_no_recreate_running(self):
web = self.create_service('web')
db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
diff --git a/tests/integration/service_test.py b/tests/integration/service_test.py
index 00bacebf..b9005b8e 100644
--- a/tests/integration/service_test.py
+++ b/tests/integration/service_test.py
@@ -19,6 +19,7 @@ from .testcases import pull_busybox
from .testcases import SWARM_SKIP_CONTAINERS_ALL
from .testcases import SWARM_SKIP_CPU_SHARES
from compose import __version__
+from compose.config.types import MountSpec
from compose.config.types import VolumeFromSpec
from compose.config.types import VolumeSpec
from compose.const import IS_WINDOWS_PLATFORM
@@ -37,6 +38,7 @@ from compose.service import NetworkMode
from compose.service import PidMode
from compose.service import Service
from compose.utils import parse_nanoseconds_int
+from tests.helpers import create_custom_host_file
from tests.integration.testcases import is_cluster
from tests.integration.testcases import no_cluster
from tests.integration.testcases import v2_1_only
@@ -276,6 +278,54 @@ class ServiceTest(DockerClientTestCase):
self.assertTrue(path.basename(actual_host_path) == path.basename(host_path),
msg=("Last component differs: %s, %s" % (actual_host_path, host_path)))
+ @v2_3_only()
+ def test_create_container_with_host_mount(self):
+ host_path = '/tmp/host-path'
+ container_path = '/container-path'
+
+ create_custom_host_file(self.client, path.join(host_path, 'a.txt'), 'test')
+
+ service = self.create_service(
+ 'db',
+ volumes=[
+ MountSpec(type='bind', source=host_path, target=container_path, read_only=True)
+ ]
+ )
+ container = service.create_container()
+ service.start_container(container)
+ mount = container.get_mount(container_path)
+ assert mount
+ assert path.basename(mount['Source']) == path.basename(host_path)
+ assert mount['RW'] is False
+
+ @v2_3_only()
+ def test_create_container_with_tmpfs_mount(self):
+ container_path = '/container-tmpfs'
+ service = self.create_service(
+ 'db',
+ volumes=[MountSpec(type='tmpfs', target=container_path)]
+ )
+ container = service.create_container()
+ service.start_container(container)
+ mount = container.get_mount(container_path)
+ assert mount
+ assert mount['Type'] == 'tmpfs'
+
+ @v2_3_only()
+ def test_create_container_with_volume_mount(self):
+ container_path = '/container-volume'
+ volume_name = 'composetest_abcde'
+ self.client.create_volume(volume_name)
+ service = self.create_service(
+ 'db',
+ volumes=[MountSpec(type='volume', source=volume_name, target=container_path)]
+ )
+ container = service.create_container()
+ service.start_container(container)
+ mount = container.get_mount(container_path)
+ assert mount
+ assert mount['Name'] == volume_name
+
def test_create_container_with_healthcheck_config(self):
one_second = parse_nanoseconds_int('1s')
healthcheck = {
@@ -439,6 +489,38 @@ class ServiceTest(DockerClientTestCase):
orig_container = new_container
+ @v2_3_only()
+ def test_execute_convergence_plan_recreate_twice_with_mount(self):
+ service = self.create_service(
+ 'db',
+ volumes=[MountSpec(target='/etc', type='volume')],
+ entrypoint=['top'],
+ command=['-d', '1']
+ )
+
+ orig_container = service.create_container()
+ service.start_container(orig_container)
+
+ orig_container.inspect() # reload volume data
+ volume_path = orig_container.get_mount('/etc')['Source']
+
+ # Do this twice to reproduce the bug
+ for _ in range(2):
+ new_container, = service.execute_convergence_plan(
+ ConvergencePlan('recreate', [orig_container])
+ )
+
+ assert new_container.get_mount('/etc')['Source'] == volume_path
+ if not is_cluster(self.client):
+ assert ('affinity:container==%s' % orig_container.id in
+ new_container.get('Config.Env'))
+ else:
+ # In Swarm, the env marker is consumed and the container should be deployed
+ # on the same node.
+ assert orig_container.get('Node.Name') == new_container.get('Node.Name')
+
+ orig_container = new_container
+
def test_execute_convergence_plan_when_containers_are_stopped(self):
service = self.create_service(
'db',
diff --git a/tests/integration/testcases.py b/tests/integration/testcases.py
index b72fb53a..9427f3d0 100644
--- a/tests/integration/testcases.py
+++ b/tests/integration/testcases.py
@@ -20,7 +20,7 @@ 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_3 as V3_3
+from compose.const import COMPOSEFILE_V3_5 as V3_5
from compose.const import LABEL_PROJECT
from compose.progress_stream import stream_output
from compose.service import Service
@@ -47,7 +47,7 @@ def get_links(container):
def engine_max_version():
if 'DOCKER_VERSION' not in os.environ:
- return V3_3
+ return V3_5
version = os.environ['DOCKER_VERSION'].partition('-')[0]
if version_lt(version, '1.10'):
return V1
@@ -57,7 +57,7 @@ def engine_max_version():
return V2_1
if version_lt(version, '17.06'):
return V3_2
- return V3_3
+ return V3_5
def min_version_skip(version):
diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py
index 00ba6c2c..d519deb9 100644
--- a/tests/unit/config/config_test.py
+++ b/tests/unit/config/config_test.py
@@ -1137,9 +1137,12 @@ class ConfigTest(unittest.TestCase):
details = config.ConfigDetails('.', [base_file, override_file])
service_dicts = config.load(details).services
svc_volumes = map(lambda v: v.repr(), service_dicts[0]['volumes'])
- assert sorted(svc_volumes) == sorted(
- ['/anonymous', '/c:/b:rw', 'vol:/x:ro']
- )
+ for vol in svc_volumes:
+ assert vol in [
+ '/anonymous',
+ '/c:/b:rw',
+ {'source': 'vol', 'target': '/x', 'type': 'volume', 'read_only': True}
+ ]
@mock.patch.dict(os.environ)
def test_volume_mode_override(self):
@@ -1223,6 +1226,50 @@ class ConfigTest(unittest.TestCase):
assert volume.external == 'data0028'
assert volume.is_named_volume
+ def test_volumes_long_syntax(self):
+ base_file = config.ConfigFile(
+ 'base.yaml', {
+ 'version': '2.3',
+ 'services': {
+ 'web': {
+ 'image': 'busybox:latest',
+ 'volumes': [
+ {
+ 'target': '/anonymous', 'type': 'volume'
+ }, {
+ 'source': '/abc', 'target': '/xyz', 'type': 'bind'
+ }, {
+ 'source': '\\\\.\\pipe\\abcd', 'target': '/named_pipe', 'type': 'npipe'
+ }, {
+ 'type': 'tmpfs', 'target': '/tmpfs'
+ }
+ ]
+ },
+ },
+ },
+ )
+ details = config.ConfigDetails('.', [base_file])
+ config_data = config.load(details)
+ volumes = config_data.services[0].get('volumes')
+ anon_volume = [v for v in volumes if v.target == '/anonymous'][0]
+ tmpfs_mount = [v for v in volumes if v.type == 'tmpfs'][0]
+ host_mount = [v for v in volumes if v.type == 'bind'][0]
+ npipe_mount = [v for v in volumes if v.type == 'npipe'][0]
+
+ assert anon_volume.type == 'volume'
+ assert not anon_volume.is_named_volume
+
+ assert tmpfs_mount.target == '/tmpfs'
+ assert not tmpfs_mount.is_named_volume
+
+ assert host_mount.source == os.path.normpath('/abc')
+ assert host_mount.target == '/xyz'
+ assert not host_mount.is_named_volume
+
+ assert npipe_mount.source == '\\\\.\\pipe\\abcd'
+ assert npipe_mount.target == '/named_pipe'
+ assert not npipe_mount.is_named_volume
+
def test_config_valid_service_names(self):
for valid_name in ['_', '-', '.__.', '_what-up.', 'what_.up----', 'whatup']:
services = config.load(
diff --git a/tests/unit/service_test.py b/tests/unit/service_test.py
index 16670cff..24ed60e9 100644
--- a/tests/unit/service_test.py
+++ b/tests/unit/service_test.py
@@ -939,7 +939,7 @@ class ServiceVolumesTest(unittest.TestCase):
VolumeSpec.parse('imagedata:/mnt/image/data:rw'),
]
- volumes = get_container_data_volumes(container, options, ['/dev/tmpfs'])
+ volumes, _ = get_container_data_volumes(container, options, ['/dev/tmpfs'], [])
assert sorted(volumes) == sorted(expected)
def test_merge_volume_bindings(self):
@@ -975,7 +975,7 @@ class ServiceVolumesTest(unittest.TestCase):
'existingvolume:/existing/volume:rw',
]
- binds, affinity = merge_volume_bindings(options, ['/dev/tmpfs'], previous_container)
+ binds, affinity = merge_volume_bindings(options, ['/dev/tmpfs'], previous_container, [])
assert sorted(binds) == sorted(expected)
assert affinity == {'affinity:container': '=cdefab'}