Merge branch 'TomasTomecek-autodetect-api-version'

This commit is contained in:
Joffrey F 2015-03-05 12:12:27 -08:00
commit 7c4ed86d8d
6 changed files with 99 additions and 15 deletions

View File

@ -17,4 +17,4 @@ from .version import version
__version__ = version
__title__ = 'docker-py'
from .client import Client # flake8: noqa
from .client import Client, AutoVersionClient # flake8: noqa

View File

@ -40,7 +40,7 @@ STREAM_HEADER_SIZE_BYTES = 8
class Client(requests.Session):
def __init__(self, base_url=None, version=DEFAULT_DOCKER_API_VERSION,
def __init__(self, base_url=None, version=None,
timeout=DEFAULT_TIMEOUT_SECONDS, tls=False):
super(Client, self).__init__()
base_url = utils.parse_host(base_url)
@ -50,15 +50,8 @@ class Client(requests.Session):
raise errors.TLSParameterError(
'If using TLS, the base_url argument must begin with '
'"https://".')
if not isinstance(version, six.string_types):
raise errors.DockerException(
'version parameter must be a string. Found {0}'.format(
type(version).__name__
)
)
self.base_url = base_url
self.timeout = timeout
self._version = version
self._auth_configs = auth.load_config()
# Use SSLAdapter for the ability to specify SSL version
@ -69,6 +62,34 @@ class Client(requests.Session):
else:
self.mount('http+unix://', unixconn.UnixAdapter(base_url, timeout))
# version detection needs to be after unix adapter mounting
if version is None:
self._version = DEFAULT_DOCKER_API_VERSION
elif isinstance(version, six.string_types):
if version.lower() == 'auto':
self._version = self._retrieve_server_version()
else:
self._version = version
else:
raise errors.DockerException(
'Version parameter must be a string or None. Found {0}'.format(
type(version).__name__
)
)
def _retrieve_server_version(self):
try:
return self.version(api_version=False)["ApiVersion"]
except KeyError:
raise errors.DockerException(
'Invalid response from docker daemon: key "ApiVersion"'
' is missing.'
)
except Exception as e:
raise errors.DockerException(
'Error while fetching server API version: {0}'.format(e)
)
def _set_request_timeout(self, kwargs):
"""Prepare the kwargs for an HTTP request by inserting the timeout
parameter, if not already present."""
@ -84,8 +105,11 @@ class Client(requests.Session):
def _delete(self, url, **kwargs):
return self.delete(url, **self._set_request_timeout(kwargs))
def _url(self, path):
return '{0}/v{1}{2}'.format(self.base_url, self._version, path)
def _url(self, path, versioned_api=True):
if versioned_api:
return '{0}/v{1}{2}'.format(self.base_url, self._version, path)
else:
return '{0}{1}'.format(self.base_url, path)
def _raise_for_status(self, response, explanation=None):
"""Raises stored :class:`APIError`, if one occurred."""
@ -914,8 +938,9 @@ class Client(requests.Session):
u = self._url("/containers/{0}/top".format(container))
return self._result(self._get(u), True)
def version(self):
return self._result(self._get(self._url("/version")), True)
def version(self, api_version=True):
url = self._url("/version", versioned_api=api_version)
return self._result(self._get(url), json=True)
def unpause(self, container):
if isinstance(container, dict):
@ -934,3 +959,13 @@ class Client(requests.Session):
if 'StatusCode' in json_:
return json_['StatusCode']
return -1
class AutoVersionClient(Client):
def __init__(self, *args, **kwargs):
if 'version' in kwargs and kwargs['version']:
raise errors.DockerException(
'Can not specify version for AutoVersionClient'
)
kwargs['version'] = 'auto'
super(AutoVersionClient, self).__init__(*args, **kwargs)

View File

@ -12,7 +12,8 @@ c = Client(base_url='unix://var/run/docker.sock')
* base_url (str): Refers to the protocol+hostname+port where the Docker server
is hosted.
* version (str): The version of the API the client will use
* version (str): The version of the API the client will use. Specify `'auto'`
to use the API version provided by the server.
* timeout (int): The HTTP request timeout, in seconds.
* tls (bool or [TLSConfig](tls.md#TLSConfig)): Equivalent CLI options: `docker --tls ...`

View File

@ -30,6 +30,17 @@ FAKE_PATH = '/path'
# for clarity and readability
def get_fake_raw_version():
status_code = 200
response = {
"ApiVersion": "1.17",
"GitCommit": "fake-commit",
"GoVersion": "go1.3.3",
"Version": "1.5.0"
}
return status_code, response
def get_fake_version():
status_code = 200
response = {'GoVersion': '1', 'Version': '1.1.1',
@ -347,6 +358,8 @@ def get_fake_stats():
# Maps real api url to fake response callback
prefix = 'http+unix://var/run/docker.sock'
fake_responses = {
'{0}/version'.format(prefix):
get_fake_raw_version,
'{1}/{0}/version'.format(CURRENT_VERSION, prefix):
get_fake_version,
'{1}/{0}/info'.format(CURRENT_VERSION, prefix):

View File

@ -1415,6 +1415,28 @@ class TestLoadJSONConfig(BaseTestCase):
self.assertEqual(cfg.get('Auth'), None)
class TestAutoDetectVersion(unittest.TestCase):
def test_client_init(self):
client = docker.Client(version='auto')
client_version = client._version
api_version = client.version(api_version=False)['ApiVersion']
self.assertEqual(client_version, api_version)
api_version_2 = client.version()['ApiVersion']
self.assertEqual(client_version, api_version_2)
client.close()
def test_auto_client(self):
client = docker.AutoVersionClient()
client_version = client._version
api_version = client.version(api_version=False)['ApiVersion']
self.assertEqual(client_version, api_version)
api_version_2 = client.version()['ApiVersion']
self.assertEqual(client_version, api_version_2)
client.close()
with self.assertRaises(docker.errors.DockerException):
docker.AutoVersionClient(version='1.11')
class TestConnectionTimeout(unittest.TestCase):
def setUp(self):
self.timeout = 0.5

View File

@ -130,7 +130,7 @@ class DockerClientTest(Cleanup, unittest.TestCase):
if not six.PY3:
self.assertEqual(
str(e),
'version parameter must be a string. Found float'
'Version parameter must be a string or None. Found float'
)
#########################
@ -147,6 +147,19 @@ class DockerClientTest(Cleanup, unittest.TestCase):
timeout=docker.client.DEFAULT_TIMEOUT_SECONDS
)
def test_retrieve_server_version(self):
client = docker.Client(version="auto")
self.assertTrue(isinstance(client._version, six.string_types))
self.assertFalse(client._version == "auto")
def test_auto_retrieve_server_version(self):
try:
version = self.client.retrieve_server_version()
except Exception as e:
self.fail('Command should not raise exception: {0}'.format(e))
else:
self.assertTrue(isinstance(version, six.string_types))
def test_info(self):
try:
self.client.info()