mirror of https://github.com/docker/docker-py.git
Merge branch 'TomasTomecek-autodetect-api-version'
This commit is contained in:
commit
7c4ed86d8d
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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 ...`
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue