summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey F <joffrey@docker.com>2015-09-15 00:00:06 +0200
committerJoffrey F <joffrey@docker.com>2015-09-16 12:55:13 -0700
commitacd5e634ffa62428562c945878dcb67edd6ecf61 (patch)
treebdebbbd0de22c9ef72c542df496ddd4a73681ed7
parentba6df5a2c09da68fe594e07dc2a62cabbe6667d7 (diff)
downloaddocker-py-acd5e634ffa62428562c945878dcb67edd6ecf61.tar.gz
Generic skip decorator for low API version accessible to all tests
Add simpler version comparison functions Add decorator to enforce minimum version in API methods Fix utils imports Add minimum_version decorators on API methods that needed it GroupAdd test requires API version >= 1.20 Signed-off-by: Joffrey F <joffrey@docker.com>
-rw-r--r--docker/api/__init__.py2
-rw-r--r--docker/api/build.py4
-rw-r--r--docker/api/container.py54
-rw-r--r--docker/api/exec_api.py18
-rw-r--r--docker/api/image.py14
-rw-r--r--docker/api/volume.py24
-rw-r--r--docker/utils/__init__.py5
-rw-r--r--docker/utils/decorators.py16
-rw-r--r--docker/utils/utils.py8
-rw-r--r--tests/base.py12
-rw-r--r--tests/integration_test.py16
-rw-r--r--tests/test.py24
12 files changed, 98 insertions, 99 deletions
diff --git a/docker/api/__init__.py b/docker/api/__init__.py
index 3fb8120..7979634 100644
--- a/docker/api/__init__.py
+++ b/docker/api/__init__.py
@@ -4,4 +4,4 @@ from .container import ContainerApiMixin
from .daemon import DaemonApiMixin
from .exec_api import ExecApiMixin
from .image import ImageApiMixin
-from .volume import VolumeApiMixin \ No newline at end of file
+from .volume import VolumeApiMixin
diff --git a/docker/api/build.py b/docker/api/build.py
index ce6fd46..b303ba6 100644
--- a/docker/api/build.py
+++ b/docker/api/build.py
@@ -4,8 +4,8 @@ import re
from .. import constants
from .. import errors
-from ..auth import auth
-from ..utils import utils
+from .. import auth
+from .. import utils
log = logging.getLogger(__name__)
diff --git a/docker/api/container.py b/docker/api/container.py
index f90e8a1..e7ddd73 100644
--- a/docker/api/container.py
+++ b/docker/api/container.py
@@ -2,11 +2,11 @@ import six
import warnings
from .. import errors
-from ..utils import utils, check_resource
+from .. import utils
class ContainerApiMixin(object):
- @check_resource
+ @utils.check_resource
def attach(self, container, stdout=True, stderr=True,
stream=False, logs=False):
params = {
@@ -20,7 +20,7 @@ class ContainerApiMixin(object):
return self._get_result(container, stream, response)
- @check_resource
+ @utils.check_resource
def attach_socket(self, container, params=None, ws=False):
if params is None:
params = {
@@ -36,7 +36,7 @@ class ContainerApiMixin(object):
return self._get_raw_response_socket(self.post(
u, None, params=self._attach_params(params), stream=True))
- @check_resource
+ @utils.check_resource
def commit(self, container, repository=None, tag=None, message=None,
author=None, conf=None):
params = {
@@ -73,7 +73,7 @@ class ContainerApiMixin(object):
x['Id'] = x['Id'][:12]
return res
- @check_resource
+ @utils.check_resource
def copy(self, container, resource):
res = self._post_json(
self._url("/containers/{0}/copy".format(container)),
@@ -131,13 +131,13 @@ class ContainerApiMixin(object):
kwargs['version'] = self._version
return utils.create_host_config(*args, **kwargs)
- @check_resource
+ @utils.check_resource
def diff(self, container):
return self._result(
self._get(self._url("/containers/{0}/changes", container)), True
)
- @check_resource
+ @utils.check_resource
def export(self, container):
res = self._get(
self._url("/containers/{0}/export", container), stream=True
@@ -145,13 +145,13 @@ class ContainerApiMixin(object):
self._raise_for_status(res)
return res.raw
- @check_resource
+ @utils.check_resource
def inspect_container(self, container):
return self._result(
self._get(self._url("/containers/{0}/json", container)), True
)
- @check_resource
+ @utils.check_resource
def kill(self, container, signal=None):
url = self._url("/containers/{0}/kill", container)
params = {}
@@ -161,7 +161,7 @@ class ContainerApiMixin(object):
self._raise_for_status(res)
- @check_resource
+ @utils.check_resource
def logs(self, container, stdout=True, stderr=True, stream=False,
timestamps=False, tail='all'):
if utils.compare_version('1.11', self._version) >= 0:
@@ -185,13 +185,13 @@ class ContainerApiMixin(object):
logs=True
)
- @check_resource
+ @utils.check_resource
def pause(self, container):
url = self._url('/containers/{0}/pause', container)
res = self._post(url)
self._raise_for_status(res)
- @check_resource
+ @utils.check_resource
def port(self, container, private_port):
res = self._get(self._url("/containers/{0}/json", container))
self._raise_for_status(res)
@@ -211,7 +211,7 @@ class ContainerApiMixin(object):
return h_ports
- @check_resource
+ @utils.check_resource
def remove_container(self, container, v=False, link=False, force=False):
params = {'v': v, 'link': link, 'force': force}
res = self._delete(
@@ -219,32 +219,29 @@ class ContainerApiMixin(object):
)
self._raise_for_status(res)
- @check_resource
+ @utils.minimum_version('1.17')
+ @utils.check_resource
def rename(self, container, name):
- if utils.compare_version('1.17', self._version) < 0:
- raise errors.InvalidVersion(
- 'rename was only introduced in API version 1.17'
- )
url = self._url("/containers/{0}/rename", container)
params = {'name': name}
res = self._post(url, params=params)
self._raise_for_status(res)
- @check_resource
+ @utils.check_resource
def resize(self, container, height, width):
params = {'h': height, 'w': width}
url = self._url("/containers/{0}/resize", container)
res = self._post(url, params=params)
self._raise_for_status(res)
- @check_resource
+ @utils.check_resource
def restart(self, container, timeout=10):
params = {'t': timeout}
url = self._url("/containers/{0}/restart", container)
res = self._post(url, params=params)
self._raise_for_status(res)
- @check_resource
+ @utils.check_resource
def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
publish_all_ports=None, links=None, privileged=None,
dns=None, dns_search=None, volumes_from=None, network_mode=None,
@@ -312,16 +309,13 @@ class ContainerApiMixin(object):
res = self._post_json(url, data=start_config)
self._raise_for_status(res)
- @check_resource
+ @utils.minimum_version('1.17')
+ @utils.check_resource
def stats(self, container, decode=None):
- if utils.compare_version('1.17', self._version) < 0:
- raise errors.InvalidVersion(
- 'Stats retrieval is not supported in API < 1.17!')
-
url = self._url("/containers/{0}/stats", container)
return self._stream_helper(self._get(url, stream=True), decode=decode)
- @check_resource
+ @utils.check_resource
def stop(self, container, timeout=10):
params = {'t': timeout}
url = self._url("/containers/{0}/stop", container)
@@ -330,18 +324,18 @@ class ContainerApiMixin(object):
timeout=(timeout + (self.timeout or 0)))
self._raise_for_status(res)
- @check_resource
+ @utils.check_resource
def top(self, container):
u = self._url("/containers/{0}/top", container)
return self._result(self._get(u), True)
- @check_resource
+ @utils.check_resource
def unpause(self, container):
url = self._url('/containers/{0}/unpause', container)
res = self._post(url)
self._raise_for_status(res)
- @check_resource
+ @utils.check_resource
def wait(self, container, timeout=None):
url = self._url("/containers/{0}/wait", container)
res = self._post(url, timeout=timeout)
diff --git a/docker/api/exec_api.py b/docker/api/exec_api.py
index 87eb143..c66b9dd 100644
--- a/docker/api/exec_api.py
+++ b/docker/api/exec_api.py
@@ -3,15 +3,14 @@ import shlex
import six
from .. import errors
-from ..utils import utils, check_resource
+from .. import utils
class ExecApiMixin(object):
- @check_resource
+ @utils.minimum_version('1.15')
+ @utils.check_resource
def exec_create(self, container, cmd, stdout=True, stderr=True, tty=False,
privileged=False, user=''):
- if utils.compare_version('1.15', self._version) < 0:
- raise errors.InvalidVersion('Exec is not supported in API < 1.15')
if privileged and utils.compare_version('1.19', self._version) < 0:
raise errors.InvalidVersion(
'Privileged exec is not supported in API < 1.19'
@@ -38,19 +37,15 @@ class ExecApiMixin(object):
res = self._post_json(url, data=data)
return self._result(res, True)
+ @utils.minimum_version('1.16')
def exec_inspect(self, exec_id):
- if utils.compare_version('1.16', self._version) < 0:
- raise errors.InvalidVersion(
- 'exec_inspect is not supported in API < 1.16'
- )
if isinstance(exec_id, dict):
exec_id = exec_id.get('Id')
res = self._get(self._url("/exec/{0}/json", exec_id))
return self._result(res, True)
+ @utils.minimum_version('1.15')
def exec_resize(self, exec_id, height=None, width=None):
- if utils.compare_version('1.15', self._version) < 0:
- raise errors.InvalidVersion('Exec is not supported in API < 1.15')
if isinstance(exec_id, dict):
exec_id = exec_id.get('Id')
@@ -59,9 +54,8 @@ class ExecApiMixin(object):
res = self._post(url, params=params)
self._raise_for_status(res)
+ @utils.minimum_version('1.15')
def exec_start(self, exec_id, detach=False, tty=False, stream=False):
- if utils.compare_version('1.15', self._version) < 0:
- raise errors.InvalidVersion('Exec is not supported in API < 1.15')
if isinstance(exec_id, dict):
exec_id = exec_id.get('Id')
diff --git a/docker/api/image.py b/docker/api/image.py
index c6939ef..f891e21 100644
--- a/docker/api/image.py
+++ b/docker/api/image.py
@@ -4,7 +4,7 @@ import warnings
from ..auth import auth
from ..constants import INSECURE_REGISTRY_DEPRECATION_WARNING
-from ..utils import utils, check_resource
+from .. import utils
from .. import errors
log = logging.getLogger(__name__)
@@ -12,13 +12,13 @@ log = logging.getLogger(__name__)
class ImageApiMixin(object):
- @check_resource
+ @utils.check_resource
def get_image(self, image):
res = self._get(self._url("/images/{0}/get", image), stream=True)
self._raise_for_status(res)
return res.raw
- @check_resource
+ @utils.check_resource
def history(self, image):
res = self._get(self._url("/images/{0}/history", image))
return self._result(res, True)
@@ -124,7 +124,7 @@ class ImageApiMixin(object):
return self._result(
self._post(u, data=None, params=params))
- @check_resource
+ @utils.check_resource
def insert(self, image, url, path):
if utils.compare_version('1.12', self._version) >= 0:
raise errors.DeprecatedMethod(
@@ -137,7 +137,7 @@ class ImageApiMixin(object):
}
return self._result(self._post(api_url, params=params))
- @check_resource
+ @utils.check_resource
def inspect_image(self, image):
return self._result(
self._get(self._url("/images/{0}/json", image)), True
@@ -246,7 +246,7 @@ class ImageApiMixin(object):
return self._result(response)
- @check_resource
+ @utils.check_resource
def remove_image(self, image, force=False, noprune=False):
params = {'force': force, 'noprune': noprune}
res = self._delete(self._url("/images/{0}", image), params=params)
@@ -258,7 +258,7 @@ class ImageApiMixin(object):
True
)
- @check_resource
+ @utils.check_resource
def tag(self, image, repository, tag=None, force=False):
params = {
'tag': tag,
diff --git a/docker/api/volume.py b/docker/api/volume.py
index 708cbe5..e9e7127 100644
--- a/docker/api/volume.py
+++ b/docker/api/volume.py
@@ -1,22 +1,8 @@
-import functools
-
-from .. import errors
-from ..utils import utils
-
-
-def check_api_version(f):
- @functools.wraps(f)
- def wrapped(self, *args, **kwargs):
- if utils.compare_version('1.21', self._version) < 0:
- raise errors.InvalidVersion(
- 'The volume API is not available for API version < 1.21'
- )
- return f(self, *args, **kwargs)
- return wrapped
+from .. import utils
class VolumeApiMixin(object):
- @check_api_version
+ @utils.minimum_version('1.21')
def volumes(self, filters=None):
params = {
'filter': utils.convert_filters(filters) if filters else None
@@ -24,7 +10,7 @@ class VolumeApiMixin(object):
url = self._url('/volumes')
return self._result(self._get(url, params=params), True)
- @check_api_version
+ @utils.minimum_version('1.21')
def create_volume(self, name, driver=None, driver_opts=None):
url = self._url('/volumes')
if driver_opts is not None and not isinstance(driver_opts, dict):
@@ -37,12 +23,12 @@ class VolumeApiMixin(object):
}
return self._result(self._post_json(url, data=data), True)
- @check_api_version
+ @utils.minimum_version('1.21')
def inspect_volume(self, name):
url = self._url('/volumes/{0}', name)
return self._result(self._get(url), True)
- @check_api_version
+ @utils.minimum_version('1.21')
def remove_volume(self, name):
url = self._url('/volumes/{0}', name)
resp = self._delete(url)
diff --git a/docker/utils/__init__.py b/docker/utils/__init__.py
index deab01d..fd0ef5c 100644
--- a/docker/utils/__init__.py
+++ b/docker/utils/__init__.py
@@ -2,8 +2,9 @@ from .utils import (
compare_version, convert_port_bindings, convert_volume_binds,
mkbuildcontext, tar, exclude_paths, parse_repository_tag, parse_host,
kwargs_from_env, convert_filters, create_host_config,
- create_container_config, parse_bytes, ping_registry, parse_env_file
+ create_container_config, parse_bytes, ping_registry, parse_env_file,
+ version_lt, version_gte
) # flake8: noqa
from .types import Ulimit, LogConfig # flake8: noqa
-from .decorators import check_resource #flake8: noqa
+from .decorators import check_resource, minimum_version #flake8: noqa
diff --git a/docker/utils/decorators.py b/docker/utils/decorators.py
index 3c42fe4..7d3b01a 100644
--- a/docker/utils/decorators.py
+++ b/docker/utils/decorators.py
@@ -1,6 +1,7 @@
import functools
from .. import errors
+from . import utils
def check_resource(f):
@@ -19,3 +20,18 @@ def check_resource(f):
)
return f(self, resource_id, *args, **kwargs)
return wrapped
+
+
+def minimum_version(version):
+ def decorator(f):
+ @functools.wraps(f)
+ def wrapper(self, *args, **kwargs):
+ if utils.version_lt(self._version, version):
+ raise errors.InvalidVersion(
+ '{0} is not available for version < {1}'.format(
+ f.__name__, version
+ )
+ )
+ return f(self, *args, **kwargs)
+ return wrapper
+ return decorator
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index a251341..46b3516 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -164,6 +164,14 @@ def compare_version(v1, v2):
return 1
+def version_lt(v1, v2):
+ return compare_version(v1, v2) > 0
+
+
+def version_gte(v1, v2):
+ return not version_lt(v1, v2)
+
+
def ping_registry(url):
warnings.warn(
'The `ping_registry` method is deprecated and will be removed.',
diff --git a/tests/base.py b/tests/base.py
index 1d5a370..51b2300 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -1,11 +1,23 @@
import sys
import unittest
+import pytest
import six
+import docker
+
class BaseTestCase(unittest.TestCase):
def assertIn(self, object, collection):
if six.PY2 and sys.version_info[1] <= 6:
return self.assertTrue(object in collection)
return super(BaseTestCase, self).assertIn(object, collection)
+
+
+def requires_api_version(version):
+ return pytest.mark.skipif(
+ docker.utils.version_lt(
+ docker.constants.DEFAULT_DOCKER_API_VERSION, version
+ ),
+ reason="API version is too low (< {0})".format(version)
+ )
diff --git a/tests/integration_test.py b/tests/integration_test.py
index 8b15b14..fc49797 100644
--- a/tests/integration_test.py
+++ b/tests/integration_test.py
@@ -27,17 +27,18 @@ import time
import unittest
import warnings
-import docker
-from docker.utils import kwargs_from_env
+import pytest
import six
-
from six.moves import BaseHTTPServer
from six.moves import socketserver
-from .test import Cleanup
+import docker
from docker.errors import APIError
+from docker.utils import kwargs_from_env
+
+from .base import requires_api_version
+from .test import Cleanup
-import pytest
# FIXME: missing tests for
# export; history; insert; port; push; tag; get; load; stats
@@ -285,6 +286,7 @@ class TestCreateContainerWithRoBinds(BaseTestCase):
self.assertFalse(inspect_data['VolumesRW'][mount_dest])
+@requires_api_version('1.20')
class CreateContainerWithGroupAddTest(BaseTestCase):
def test_group_id_ints(self):
container = self.client.create_container(
@@ -1385,9 +1387,7 @@ class TestImportFromURL(ImportTestCase):
# VOLUMES TESTS #
#################
-@pytest.mark.skipif(docker.utils.compare_version(
- '1.21', docker.constants.DEFAULT_DOCKER_API_VERSION
-) < 0, reason="Volume API available for version >=1.21")
+@requires_api_version('1.21')
class TestVolumes(BaseTestCase):
def test_create_volume(self):
name = 'perfectcherryblossom'
diff --git a/tests/test.py b/tests/test.py
index 16ade24..8ed5c14 100644
--- a/tests/test.py
+++ b/tests/test.py
@@ -2107,9 +2107,7 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
# VOLUMES TESTS #
###################
- @pytest.mark.skipif(docker.utils.compare_version(
- '1.21', docker.constants.DEFAULT_DOCKER_API_VERSION
- ) < 0, reason="API version too low")
+ @base.requires_api_version('1.21')
def test_list_volumes(self):
volumes = self.client.volumes()
self.assertIn('Volumes', volumes)
@@ -2119,9 +2117,7 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
self.assertEqual(args[0][0], 'GET')
self.assertEqual(args[0][1], url_prefix + 'volumes')
- @pytest.mark.skipif(docker.utils.compare_version(
- '1.21', docker.constants.DEFAULT_DOCKER_API_VERSION
- ) < 0, reason="API version too low")
+ @base.requires_api_version('1.21')
def test_create_volume(self):
name = 'perfectcherryblossom'
result = self.client.create_volume(name)
@@ -2137,9 +2133,7 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
'Name': name, 'Driver': None, 'DriverOpts': None
})
- @pytest.mark.skipif(docker.utils.compare_version(
- '1.21', docker.constants.DEFAULT_DOCKER_API_VERSION
- ) < 0, reason="API version too low")
+ @base.requires_api_version('1.21')
def test_create_volume_with_driver(self):
name = 'perfectcherryblossom'
driver_name = 'sshfs'
@@ -2151,9 +2145,7 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
self.assertIn('Driver', args[1]['data'])
self.assertEqual(args[1]['data']['Driver'], driver_name)
- @pytest.mark.skipif(docker.utils.compare_version(
- '1.21', docker.constants.DEFAULT_DOCKER_API_VERSION
- ) < 0, reason="API version too low")
+ @base.requires_api_version('1.21')
def test_create_volume_invalid_opts_type(self):
with pytest.raises(TypeError):
self.client.create_volume(
@@ -2170,9 +2162,7 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
'perfectcherryblossom', driver_opts=''
)
- @pytest.mark.skipif(docker.utils.compare_version(
- '1.21', docker.constants.DEFAULT_DOCKER_API_VERSION
- ) < 0, reason="API version too low")
+ @base.requires_api_version('1.21')
def test_inspect_volume(self):
name = 'perfectcherryblossom'
result = self.client.inspect_volume(name)
@@ -2185,9 +2175,7 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
self.assertEqual(args[0][0], 'GET')
self.assertEqual(args[0][1], '{0}volumes/{1}'.format(url_prefix, name))
- @pytest.mark.skipif(docker.utils.compare_version(
- '1.21', docker.constants.DEFAULT_DOCKER_API_VERSION
- ) < 0, reason="API version too low")
+ @base.requires_api_version('1.21')
def test_remove_volume(self):
name = 'perfectcherryblossom'
result = self.client.remove_volume(name)