浏览代码

Some more test adjustments for Swarm support

Signed-off-by: Joffrey F <[email protected]>
Joffrey F 8 年之前
父节点
当前提交
c923ea1320

+ 2 - 0
.dockerignore

@@ -7,3 +7,5 @@ coverage-html
 docs/_site
 venv
 .tox
+**/__pycache__
+*.pyc

+ 18 - 8
tests/acceptance/cli_test.py

@@ -20,8 +20,6 @@ from docker import errors
 
 from .. import mock
 from ..helpers import create_host_file
-from ..helpers import is_cluster
-from ..helpers import no_cluster
 from compose.cli.command import get_project
 from compose.config.errors import DuplicateOverrideFileFound
 from compose.container import Container
@@ -29,6 +27,8 @@ from compose.project import OneOffFilter
 from compose.utils import nanoseconds_from_time_seconds
 from tests.integration.testcases import DockerClientTestCase
 from tests.integration.testcases import get_links
+from tests.integration.testcases import is_cluster
+from tests.integration.testcases import no_cluster
 from tests.integration.testcases import pull_busybox
 from tests.integration.testcases import SWARM_SKIP_RM_VOLUMES
 from tests.integration.testcases import v2_1_only
@@ -116,7 +116,7 @@ class CLITestCase(DockerClientTestCase):
     def tearDown(self):
         if self.base_dir:
             self.project.kill()
-            self.project.remove_stopped()
+            self.project.down(None, True)
 
             for container in self.project.containers(stopped=True, one_off=OneOffFilter.only):
                 container.remove(force=True)
@@ -1214,6 +1214,7 @@ class CLITestCase(DockerClientTestCase):
         self.assertEqual(proc.returncode, 1)
 
     @v2_only()
+    @no_cluster('Container PID mode does not work across clusters')
     def test_up_with_pid_mode(self):
         c = self.client.create_container(
             'busybox', 'top', name='composetest_pid_mode_container',
@@ -1244,8 +1245,8 @@ class CLITestCase(DockerClientTestCase):
         self.assertEqual(len(self.project.containers()), 1)
 
         stdout, stderr = self.dispatch(['exec', '-T', 'console', 'ls', '-1d', '/'])
-        self.assertEqual(stdout, "/\n")
         self.assertEqual(stderr, "")
+        self.assertEqual(stdout, "/\n")
 
     def test_exec_custom_user(self):
         self.base_dir = 'tests/fixtures/links-composefile'
@@ -1826,7 +1827,13 @@ class CLITestCase(DockerClientTestCase):
 
         result = self.dispatch(['logs', '-f'])
 
-        assert result.stdout.count('\n') == 5
+        if not is_cluster(self.client):
+            assert result.stdout.count('\n') == 5
+        else:
+            # Sometimes logs are picked up from old containers that haven't yet
+            # been removed (removal in Swarm is async)
+            assert result.stdout.count('\n') >= 5
+
         assert 'simple' in result.stdout
         assert 'another' in result.stdout
         assert 'exited with code 0' in result.stdout
@@ -1882,7 +1889,10 @@ class CLITestCase(DockerClientTestCase):
         self.dispatch(['up'])
 
         result = self.dispatch(['logs', '--tail', '2'])
-        assert result.stdout.count('\n') == 3
+        assert 'c\n' in result.stdout
+        assert 'd\n' in result.stdout
+        assert 'a\n' not in result.stdout
+        assert 'b\n' not in result.stdout
 
     def test_kill(self):
         self.dispatch(['up', '-d'], None)
@@ -2045,8 +2055,8 @@ class CLITestCase(DockerClientTestCase):
             return result.stdout.rstrip()
 
         assert get_port(3000) == container.get_local_port(3000)
-        assert ':49152' in get_port(3001)
-        assert ':49153' in get_port(3002)
+        assert ':53222' in get_port(3001)
+        assert ':53223' in get_port(3002)
 
     def test_port_with_scale(self):
         self.base_dir = 'tests/fixtures/ports-composefile-scale'

+ 3 - 3
tests/fixtures/ports-composefile/expanded-notation.yml

@@ -6,10 +6,10 @@ services:
       ports:
         - target: 3000
         - target: 3001
-          published: 49152
+          published: 53222
         - target: 3002
-          published: 49153
+          published: 53223
           protocol: tcp
         - target: 3003
-          published: 49154
+          published: 53224
           protocol: udp

+ 0 - 35
tests/helpers.py

@@ -1,12 +1,8 @@
 from __future__ import absolute_import
 from __future__ import unicode_literals
 
-import functools
 import os
 
-from docker.errors import APIError
-from pytest import skip
-
 from compose.config.config import ConfigDetails
 from compose.config.config import ConfigFile
 from compose.config.config import load
@@ -48,34 +44,3 @@ def create_host_file(client, filename):
                 "Container exited with code {}:\n{}".format(exitcode, output))
     finally:
         client.remove_container(container, force=True)
-
-
-def is_cluster(client):
-    nodes = None
-
-    def get_nodes_number():
-        try:
-            return len(client.nodes())
-        except APIError:
-            # If the Engine is not part of a Swarm, the SDK will raise
-            # an APIError
-            return 0
-
-    if nodes is None:
-        # Only make the API call if the value hasn't been cached yet
-        nodes = get_nodes_number()
-
-    return nodes > 1
-
-
-def no_cluster(reason):
-    def decorator(f):
-        @functools.wraps(f)
-        def wrapper(self, *args, **kwargs):
-            if is_cluster(self.client):
-                skip("Test will not be run in cluster mode: %s" % reason)
-                return
-            return f(self, *args, **kwargs)
-        return wrapper
-
-    return decorator

+ 2 - 2
tests/integration/project_test.py

@@ -12,8 +12,6 @@ from docker.errors import NotFound
 from .. import mock
 from ..helpers import build_config as load_config
 from ..helpers import create_host_file
-from ..helpers import is_cluster
-from ..helpers import no_cluster
 from .testcases import DockerClientTestCase
 from .testcases import SWARM_SKIP_CONTAINERS_ALL
 from compose.config import config
@@ -33,6 +31,8 @@ from compose.errors import NoHealthCheckConfigured
 from compose.project import Project
 from compose.project import ProjectError
 from compose.service import ConvergenceStrategy
+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_only

+ 19 - 4
tests/integration/service_test.py

@@ -13,8 +13,6 @@ from six import StringIO
 from six import text_type
 
 from .. import mock
-from ..helpers import is_cluster
-from ..helpers import no_cluster
 from .testcases import DockerClientTestCase
 from .testcases import get_links
 from .testcases import pull_busybox
@@ -38,6 +36,8 @@ from compose.service import ConvergenceStrategy
 from compose.service import NetworkMode
 from compose.service import PidMode
 from compose.service import Service
+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_only
@@ -635,7 +635,10 @@ class ServiceTest(DockerClientTestCase):
         with open(os.path.join(base_dir, 'Dockerfile'), 'w') as f:
             f.write("FROM busybox\n")
 
-        self.create_service('web', build={'context': base_dir}).build()
+        service = self.create_service('web', build={'context': base_dir})
+        service.build()
+        self.addCleanup(self.client.remove_image, service.image_name)
+
         assert self.client.inspect_image('composetest_web')
 
     def test_build_non_ascii_filename(self):
@@ -648,7 +651,9 @@ class ServiceTest(DockerClientTestCase):
         with open(os.path.join(base_dir.encode('utf8'), b'foo\xE2bar'), 'w') as f:
             f.write("hello world\n")
 
-        self.create_service('web', build={'context': text_type(base_dir)}).build()
+        service = self.create_service('web', build={'context': text_type(base_dir)})
+        service.build()
+        self.addCleanup(self.client.remove_image, service.image_name)
         assert self.client.inspect_image('composetest_web')
 
     def test_build_with_image_name(self):
@@ -683,6 +688,7 @@ class ServiceTest(DockerClientTestCase):
                                       build={'context': text_type(base_dir),
                                              'args': {"build_version": "1"}})
         service.build()
+        self.addCleanup(self.client.remove_image, service.image_name)
         assert service.image()
         assert "build_version=1" in service.image()['ContainerConfig']['Cmd']
 
@@ -699,6 +705,8 @@ class ServiceTest(DockerClientTestCase):
                                       build={'context': text_type(base_dir),
                                              'args': {"build_version": "1"}})
         service.build(build_args_override={'build_version': '2'})
+        self.addCleanup(self.client.remove_image, service.image_name)
+
         assert service.image()
         assert "build_version=2" in service.image()['ContainerConfig']['Cmd']
 
@@ -714,9 +722,12 @@ class ServiceTest(DockerClientTestCase):
             'labels': {'com.docker.compose.test': 'true'}
         })
         service.build()
+        self.addCleanup(self.client.remove_image, service.image_name)
+
         assert service.image()
         assert service.image()['Config']['Labels']['com.docker.compose.test'] == 'true'
 
+    @no_cluster('Container networks not on Swarm')
     def test_build_with_network(self):
         base_dir = tempfile.mkdtemp()
         self.addCleanup(shutil.rmtree, base_dir)
@@ -739,6 +750,8 @@ class ServiceTest(DockerClientTestCase):
         })
 
         service.build()
+        self.addCleanup(self.client.remove_image, service.image_name)
+
         assert service.image()
 
     def test_start_container_stays_unprivileged(self):
@@ -1130,6 +1143,8 @@ class ServiceTest(DockerClientTestCase):
                                       build={'context': base_dir,
                                              'cache_from': ['build1']})
         service.build()
+        self.addCleanup(self.client.remove_image, service.image_name)
+
         assert service.image()
 
     @mock.patch.dict(os.environ)

+ 35 - 1
tests/integration/testcases.py

@@ -1,13 +1,14 @@
 from __future__ import absolute_import
 from __future__ import unicode_literals
 
+import functools
 import os
 
 import pytest
+from docker.errors import APIError
 from docker.utils import version_lt
 
 from .. import unittest
-from ..helpers import is_cluster
 from compose.cli.docker_client import docker_client
 from compose.config.config import resolve_environment
 from compose.config.environment import Environment
@@ -26,6 +27,7 @@ from compose.service import Service
 SWARM_SKIP_CONTAINERS_ALL = os.environ.get('SWARM_SKIP_CONTAINERS_ALL', '0') != '0'
 SWARM_SKIP_CPU_SHARES = os.environ.get('SWARM_SKIP_CPU_SHARES', '0') != '0'
 SWARM_SKIP_RM_VOLUMES = os.environ.get('SWARM_SKIP_RM_VOLUMES', '0') != '0'
+SWARM_ASSUME_MULTINODE = os.environ.get('SWARM_ASSUME_MULTINODE', '0') != '0'
 
 
 def pull_busybox(client):
@@ -142,3 +144,35 @@ class DockerClientTestCase(unittest.TestCase):
         volumes = self.client.volumes(filters={'name': volume_name})['Volumes']
         assert len(volumes) > 0
         return self.client.inspect_volume(volumes[0]['Name'])
+
+
+def is_cluster(client):
+    if SWARM_ASSUME_MULTINODE:
+        return True
+
+    def get_nodes_number():
+        try:
+            return len(client.nodes())
+        except APIError:
+            # If the Engine is not part of a Swarm, the SDK will raise
+            # an APIError
+            return 0
+
+    if not hasattr(is_cluster, 'nodes') or is_cluster.nodes is None:
+        # Only make the API call if the value hasn't been cached yet
+        is_cluster.nodes = get_nodes_number()
+
+    return is_cluster.nodes > 1
+
+
+def no_cluster(reason):
+    def decorator(f):
+        @functools.wraps(f)
+        def wrapper(self, *args, **kwargs):
+            if is_cluster(self.client):
+                pytest.skip("Test will not be run in cluster mode: %s" % reason)
+                return
+            return f(self, *args, **kwargs)
+        return wrapper
+
+    return decorator

+ 1 - 1
tests/integration/volume_test.py

@@ -3,8 +3,8 @@ from __future__ import unicode_literals
 
 from docker.errors import DockerException
 
-from ..helpers import no_cluster
 from .testcases import DockerClientTestCase
+from .testcases import no_cluster
 from compose.const import LABEL_PROJECT
 from compose.const import LABEL_VOLUME
 from compose.volume import Volume

+ 2 - 0
tox.ini

@@ -9,6 +9,8 @@ passenv =
     DOCKER_CERT_PATH
     DOCKER_TLS_VERIFY
     DOCKER_VERSION
+    SWARM_SKIP_*
+    SWARM_ASSUME_MULTINODE
 setenv =
     HOME=/tmp
 deps =