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
|
__version__ = version
|
||||||
__title__ = 'docker-py'
|
__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):
|
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):
|
timeout=DEFAULT_TIMEOUT_SECONDS, tls=False):
|
||||||
super(Client, self).__init__()
|
super(Client, self).__init__()
|
||||||
base_url = utils.parse_host(base_url)
|
base_url = utils.parse_host(base_url)
|
||||||
|
|
@ -50,15 +50,8 @@ class Client(requests.Session):
|
||||||
raise errors.TLSParameterError(
|
raise errors.TLSParameterError(
|
||||||
'If using TLS, the base_url argument must begin with '
|
'If using TLS, the base_url argument must begin with '
|
||||||
'"https://".')
|
'"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.base_url = base_url
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self._version = version
|
|
||||||
self._auth_configs = auth.load_config()
|
self._auth_configs = auth.load_config()
|
||||||
|
|
||||||
# Use SSLAdapter for the ability to specify SSL version
|
# Use SSLAdapter for the ability to specify SSL version
|
||||||
|
|
@ -69,6 +62,34 @@ class Client(requests.Session):
|
||||||
else:
|
else:
|
||||||
self.mount('http+unix://', unixconn.UnixAdapter(base_url, timeout))
|
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):
|
def _set_request_timeout(self, kwargs):
|
||||||
"""Prepare the kwargs for an HTTP request by inserting the timeout
|
"""Prepare the kwargs for an HTTP request by inserting the timeout
|
||||||
parameter, if not already present."""
|
parameter, if not already present."""
|
||||||
|
|
@ -84,8 +105,11 @@ class Client(requests.Session):
|
||||||
def _delete(self, url, **kwargs):
|
def _delete(self, url, **kwargs):
|
||||||
return self.delete(url, **self._set_request_timeout(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)
|
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):
|
def _raise_for_status(self, response, explanation=None):
|
||||||
"""Raises stored :class:`APIError`, if one occurred."""
|
"""Raises stored :class:`APIError`, if one occurred."""
|
||||||
|
|
@ -914,8 +938,9 @@ class Client(requests.Session):
|
||||||
u = self._url("/containers/{0}/top".format(container))
|
u = self._url("/containers/{0}/top".format(container))
|
||||||
return self._result(self._get(u), True)
|
return self._result(self._get(u), True)
|
||||||
|
|
||||||
def version(self):
|
def version(self, api_version=True):
|
||||||
return self._result(self._get(self._url("/version")), True)
|
url = self._url("/version", versioned_api=api_version)
|
||||||
|
return self._result(self._get(url), json=True)
|
||||||
|
|
||||||
def unpause(self, container):
|
def unpause(self, container):
|
||||||
if isinstance(container, dict):
|
if isinstance(container, dict):
|
||||||
|
|
@ -934,3 +959,13 @@ class Client(requests.Session):
|
||||||
if 'StatusCode' in json_:
|
if 'StatusCode' in json_:
|
||||||
return json_['StatusCode']
|
return json_['StatusCode']
|
||||||
return -1
|
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
|
* base_url (str): Refers to the protocol+hostname+port where the Docker server
|
||||||
is hosted.
|
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.
|
* timeout (int): The HTTP request timeout, in seconds.
|
||||||
* tls (bool or [TLSConfig](tls.md#TLSConfig)): Equivalent CLI options: `docker --tls ...`
|
* tls (bool or [TLSConfig](tls.md#TLSConfig)): Equivalent CLI options: `docker --tls ...`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,17 @@ FAKE_PATH = '/path'
|
||||||
# for clarity and readability
|
# 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():
|
def get_fake_version():
|
||||||
status_code = 200
|
status_code = 200
|
||||||
response = {'GoVersion': '1', 'Version': '1.1.1',
|
response = {'GoVersion': '1', 'Version': '1.1.1',
|
||||||
|
|
@ -347,6 +358,8 @@ def get_fake_stats():
|
||||||
# Maps real api url to fake response callback
|
# Maps real api url to fake response callback
|
||||||
prefix = 'http+unix://var/run/docker.sock'
|
prefix = 'http+unix://var/run/docker.sock'
|
||||||
fake_responses = {
|
fake_responses = {
|
||||||
|
'{0}/version'.format(prefix):
|
||||||
|
get_fake_raw_version,
|
||||||
'{1}/{0}/version'.format(CURRENT_VERSION, prefix):
|
'{1}/{0}/version'.format(CURRENT_VERSION, prefix):
|
||||||
get_fake_version,
|
get_fake_version,
|
||||||
'{1}/{0}/info'.format(CURRENT_VERSION, prefix):
|
'{1}/{0}/info'.format(CURRENT_VERSION, prefix):
|
||||||
|
|
|
||||||
|
|
@ -1415,6 +1415,28 @@ class TestLoadJSONConfig(BaseTestCase):
|
||||||
self.assertEqual(cfg.get('Auth'), None)
|
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):
|
class TestConnectionTimeout(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.timeout = 0.5
|
self.timeout = 0.5
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ class DockerClientTest(Cleanup, unittest.TestCase):
|
||||||
if not six.PY3:
|
if not six.PY3:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
str(e),
|
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
|
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):
|
def test_info(self):
|
||||||
try:
|
try:
|
||||||
self.client.info()
|
self.client.info()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue