summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PKG-INFO2
-rw-r--r--docker/auth/__init__.py1
-rw-r--r--docker/auth/auth.py38
-rw-r--r--docker/client.py46
-rw-r--r--docker/clientbase.py4
-rw-r--r--docker/constants.py4
-rw-r--r--docker/errors.py4
-rw-r--r--docker/utils/utils.py12
-rw-r--r--docker/version.py2
-rw-r--r--docker_py.egg-info/PKG-INFO2
-rw-r--r--tests/integration_test.py11
-rw-r--r--tests/test.py8
-rw-r--r--tests/utils_test.py91
13 files changed, 172 insertions, 53 deletions
diff --git a/PKG-INFO b/PKG-INFO
index 2e130e7..eb4e26c 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: docker-py
-Version: 1.3.0
+Version: 1.3.1
Summary: Python client for Docker.
Home-page: https://github.com/docker/docker-py/
Author: UNKNOWN
diff --git a/docker/auth/__init__.py b/docker/auth/__init__.py
index d068b7f..6fc83f8 100644
--- a/docker/auth/__init__.py
+++ b/docker/auth/__init__.py
@@ -1,4 +1,5 @@
from .auth import (
+ INDEX_NAME,
INDEX_URL,
encode_header,
load_config,
diff --git a/docker/auth/auth.py b/docker/auth/auth.py
index 1c29615..4af741e 100644
--- a/docker/auth/auth.py
+++ b/docker/auth/auth.py
@@ -16,38 +16,34 @@ import base64
import fileinput
import json
import os
+import warnings
import six
-from ..utils import utils
+from .. import constants
from .. import errors
-INDEX_URL = 'https://index.docker.io/v1/'
+INDEX_NAME = 'index.docker.io'
+INDEX_URL = 'https://{0}/v1/'.format(INDEX_NAME)
DOCKER_CONFIG_FILENAME = os.path.join('.docker', 'config.json')
LEGACY_DOCKER_CONFIG_FILENAME = '.dockercfg'
-def expand_registry_url(hostname, insecure=False):
- if hostname.startswith('http:') or hostname.startswith('https:'):
- return hostname
- if utils.ping_registry('https://' + hostname):
- return 'https://' + hostname
- elif insecure:
- return 'http://' + hostname
- else:
- raise errors.DockerException(
- "HTTPS endpoint unresponsive and insecure mode isn't enabled."
+def resolve_repository_name(repo_name, insecure=False):
+ if insecure:
+ warnings.warn(
+ constants.INSECURE_REGISTRY_DEPRECATION_WARNING.format(
+ 'resolve_repository_name()'
+ ), DeprecationWarning
)
-
-def resolve_repository_name(repo_name, insecure=False):
if '://' in repo_name:
raise errors.InvalidRepository(
'Repository name cannot contain a scheme ({0})'.format(repo_name))
parts = repo_name.split('/', 1)
if '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost':
# This is a docker index repo (ex: foo/bar or ubuntu)
- return INDEX_URL, repo_name
+ return INDEX_NAME, repo_name
if len(parts) < 2:
raise errors.InvalidRepository(
'Invalid repository name ({0})'.format(repo_name))
@@ -57,7 +53,7 @@ def resolve_repository_name(repo_name, insecure=False):
'Invalid repository name, try "{0}" instead'.format(parts[1])
)
- return expand_registry_url(parts[0], insecure), parts[1]
+ return parts[0], parts[1]
def resolve_authconfig(authconfig, registry=None):
@@ -68,7 +64,7 @@ def resolve_authconfig(authconfig, registry=None):
Returns None if no match was found.
"""
# Default to the public index server
- registry = convert_to_hostname(registry) if registry else INDEX_URL
+ registry = convert_to_hostname(registry) if registry else INDEX_NAME
if registry in authconfig:
return authconfig[registry]
@@ -102,12 +98,6 @@ def encode_header(auth):
return base64.b64encode(auth_json)
-def encode_full_header(auth):
- """ Returns the given auth block encoded for the X-Registry-Config header.
- """
- return encode_header({'configs': auth})
-
-
def parse_auth(entries):
"""
Parses authentication entries
@@ -185,7 +175,7 @@ def load_config(config_path=None):
'Invalid or empty configuration file!')
username, password = decode_auth(data[0])
- conf[INDEX_URL] = {
+ conf[INDEX_NAME] = {
'username': username,
'password': password,
'email': data[1],
diff --git a/docker/client.py b/docker/client.py
index e52cb53..e4712c2 100644
--- a/docker/client.py
+++ b/docker/client.py
@@ -25,6 +25,7 @@ from . import constants
from . import errors
from .auth import auth
from .utils import utils, check_resource
+from .constants import INSECURE_REGISTRY_DEPRECATION_WARNING
class Client(clientbase.ClientBase):
@@ -139,9 +140,14 @@ class Client(clientbase.ClientBase):
if self._auth_configs:
if headers is None:
headers = {}
- headers['X-Registry-Config'] = auth.encode_full_header(
- self._auth_configs
- )
+ if utils.compare_version('1.19', self._version) >= 0:
+ headers['X-Registry-Config'] = auth.encode_header(
+ self._auth_configs
+ )
+ else:
+ headers['X-Registry-Config'] = auth.encode_header({
+ 'configs': self._auth_configs
+ })
response = self._post(
u,
@@ -267,10 +273,12 @@ class Client(clientbase.ClientBase):
'filters': filters
}
- return self._stream_helper(self.get(self._url('/events'),
- params=params, stream=True),
- decode=decode)
+ return self._stream_helper(
+ self.get(self._url('/events'), params=params, stream=True),
+ decode=decode
+ )
+ @check_resource
def exec_create(self, container, cmd, stdout=True, stderr=True, tty=False,
privileged=False):
if utils.compare_version('1.15', self._version) < 0:
@@ -499,6 +507,12 @@ class Client(clientbase.ClientBase):
def login(self, username, password=None, email=None, registry=None,
reauth=False, insecure_registry=False, dockercfg_path=None):
+ if insecure_registry:
+ warnings.warn(
+ INSECURE_REGISTRY_DEPRECATION_WARNING.format('login()'),
+ DeprecationWarning
+ )
+
# If we don't have any auth data so far, try reloading the config file
# one more time in case anything showed up in there.
# If dockercfg_path is passed check to see if the config file exists,
@@ -584,11 +598,15 @@ class Client(clientbase.ClientBase):
def pull(self, repository, tag=None, stream=False,
insecure_registry=False, auth_config=None):
+ if insecure_registry:
+ warnings.warn(
+ INSECURE_REGISTRY_DEPRECATION_WARNING.format('pull()'),
+ DeprecationWarning
+ )
+
if not tag:
repository, tag = utils.parse_repository_tag(repository)
- registry, repo_name = auth.resolve_repository_name(
- repository, insecure=insecure_registry
- )
+ registry, repo_name = auth.resolve_repository_name(repository)
if repo_name.count(":") == 1:
repository, tag = repository.rsplit(":", 1)
@@ -631,11 +649,15 @@ class Client(clientbase.ClientBase):
def push(self, repository, tag=None, stream=False,
insecure_registry=False):
+ if insecure_registry:
+ warnings.warn(
+ INSECURE_REGISTRY_DEPRECATION_WARNING.format('push()'),
+ DeprecationWarning
+ )
+
if not tag:
repository, tag = utils.parse_repository_tag(repository)
- registry, repo_name = auth.resolve_repository_name(
- repository, insecure=insecure_registry
- )
+ registry, repo_name = auth.resolve_repository_name(repository)
u = self._url("/images/{0}/push".format(repository))
params = {
'tag': tag
diff --git a/docker/clientbase.py b/docker/clientbase.py
index c1ae813..ce52ffa 100644
--- a/docker/clientbase.py
+++ b/docker/clientbase.py
@@ -99,6 +99,8 @@ class ClientBase(requests.Session):
try:
response.raise_for_status()
except requests.exceptions.HTTPError as e:
+ if e.response.status_code == 404:
+ raise errors.NotFound(e, response, explanation=explanation)
raise errors.APIError(e, response, explanation=explanation)
def _result(self, response, json=False, binary=False):
@@ -215,7 +217,7 @@ class ClientBase(requests.Session):
break
_, length = struct.unpack('>BxxxL', header)
if not length:
- break
+ continue
data = response.raw.read(length)
if not data:
break
diff --git a/docker/constants.py b/docker/constants.py
index f99f192..10a2fee 100644
--- a/docker/constants.py
+++ b/docker/constants.py
@@ -4,3 +4,7 @@ STREAM_HEADER_SIZE_BYTES = 8
CONTAINER_LIMITS_KEYS = [
'memory', 'memswap', 'cpushares', 'cpusetcpus'
]
+
+INSECURE_REGISTRY_DEPRECATION_WARNING = \
+ 'The `insecure_registry` argument to {} ' \
+ 'is deprecated and non-functional. Please remove it.'
diff --git a/docker/errors.py b/docker/errors.py
index d15e332..066406a 100644
--- a/docker/errors.py
+++ b/docker/errors.py
@@ -53,6 +53,10 @@ class DockerException(Exception):
pass
+class NotFound(APIError):
+ pass
+
+
class InvalidVersion(DockerException):
pass
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index 175a7e0..a714c97 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -19,6 +19,7 @@ import json
import shlex
import tarfile
import tempfile
+import warnings
from distutils.version import StrictVersion
from fnmatch import fnmatch
from datetime import datetime
@@ -120,6 +121,11 @@ def compare_version(v1, v2):
def ping_registry(url):
+ warnings.warn(
+ 'The `ping_registry` method is deprecated and will be removed.',
+ DeprecationWarning
+ )
+
return ping(url + '/v2/', [401]) or ping(url + '/v1/_ping')
@@ -333,9 +339,9 @@ def convert_filters(filters):
return json.dumps(result)
-def datetime_to_timestamp(dt=datetime.now()):
- """Convert a datetime in local timezone to a unix timestamp"""
- delta = dt - datetime.fromtimestamp(0)
+def datetime_to_timestamp(dt):
+ """Convert a UTC datetime to a Unix timestamp"""
+ delta = dt - datetime.utcfromtimestamp(0)
return delta.seconds + delta.days * 24 * 3600
diff --git a/docker/version.py b/docker/version.py
index 42ddd98..bc778b9 100644
--- a/docker/version.py
+++ b/docker/version.py
@@ -1,2 +1,2 @@
-version = "1.3.0"
+version = "1.3.1"
version_info = tuple([int(d) for d in version.split("-")[0].split(".")])
diff --git a/docker_py.egg-info/PKG-INFO b/docker_py.egg-info/PKG-INFO
index 2e130e7..eb4e26c 100644
--- a/docker_py.egg-info/PKG-INFO
+++ b/docker_py.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: docker-py
-Version: 1.3.0
+Version: 1.3.1
Summary: Python client for Docker.
Home-page: https://github.com/docker/docker-py/
Author: UNKNOWN
diff --git a/tests/integration_test.py b/tests/integration_test.py
index ac4a871..226ea34 100644
--- a/tests/integration_test.py
+++ b/tests/integration_test.py
@@ -242,6 +242,7 @@ class TestCreateContainerWithRoBinds(BaseTestCase):
self.assertFalse(inspect_data['VolumesRW'][mount_dest])
+@unittest.skipIf(NOT_ON_HOST, 'Tests running inside a container; no syslog')
class TestCreateContainerWithLogConfig(BaseTestCase):
def runTest(self):
config = docker.utils.LogConfig(
@@ -1356,8 +1357,8 @@ class TestLoadConfig(BaseTestCase):
f.write('email = sakuya@scarlet.net')
f.close()
cfg = docker.auth.load_config(cfg_path)
- self.assertNotEqual(cfg[docker.auth.INDEX_URL], None)
- cfg = cfg[docker.auth.INDEX_URL]
+ self.assertNotEqual(cfg[docker.auth.INDEX_NAME], None)
+ cfg = cfg[docker.auth.INDEX_NAME]
self.assertEqual(cfg['username'], 'sakuya')
self.assertEqual(cfg['password'], 'izayoi')
self.assertEqual(cfg['email'], 'sakuya@scarlet.net')
@@ -1386,7 +1387,7 @@ class TestLoadJSONConfig(BaseTestCase):
class TestAutoDetectVersion(unittest.TestCase):
def test_client_init(self):
- client = docker.Client(version='auto')
+ client = docker.Client(base_url=DEFAULT_BASE_URL, version='auto')
client_version = client._version
api_version = client.version(api_version=False)['ApiVersion']
self.assertEqual(client_version, api_version)
@@ -1395,7 +1396,7 @@ class TestAutoDetectVersion(unittest.TestCase):
client.close()
def test_auto_client(self):
- client = docker.AutoVersionClient()
+ client = docker.AutoVersionClient(base_url=DEFAULT_BASE_URL)
client_version = client._version
api_version = client.version(api_version=False)['ApiVersion']
self.assertEqual(client_version, api_version)
@@ -1403,7 +1404,7 @@ class TestAutoDetectVersion(unittest.TestCase):
self.assertEqual(client_version, api_version_2)
client.close()
with self.assertRaises(docker.errors.DockerException):
- docker.AutoVersionClient(version='1.11')
+ docker.AutoVersionClient(base_url=DEFAULT_BASE_URL, version='1.11')
class TestConnectionTimeout(unittest.TestCase):
diff --git a/tests/test.py b/tests/test.py
index f6535b2..9e12bb8 100644
--- a/tests/test.py
+++ b/tests/test.py
@@ -221,7 +221,7 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
def test_events_with_since_until(self):
ts = 1356048000
- now = datetime.datetime.fromtimestamp(ts)
+ now = datetime.datetime.utcfromtimestamp(ts)
since = now - datetime.timedelta(seconds=10)
until = now + datetime.timedelta(seconds=10)
try:
@@ -2424,9 +2424,9 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
f.write('auth = {0}\n'.format(auth_))
f.write('email = sakuya@scarlet.net')
cfg = docker.auth.load_config(dockercfg_path)
- self.assertTrue(docker.auth.INDEX_URL in cfg)
- self.assertNotEqual(cfg[docker.auth.INDEX_URL], None)
- cfg = cfg[docker.auth.INDEX_URL]
+ self.assertTrue(docker.auth.INDEX_NAME in cfg)
+ self.assertNotEqual(cfg[docker.auth.INDEX_NAME], None)
+ cfg = cfg[docker.auth.INDEX_NAME]
self.assertEqual(cfg['username'], 'sakuya')
self.assertEqual(cfg['password'], 'izayoi')
self.assertEqual(cfg['email'], 'sakuya@scarlet.net')
diff --git a/tests/utils_test.py b/tests/utils_test.py
index 716cde5..1c8729c 100644
--- a/tests/utils_test.py
+++ b/tests/utils_test.py
@@ -9,7 +9,7 @@ from docker.utils import (
create_host_config, Ulimit, LogConfig, parse_bytes
)
from docker.utils.ports import build_port_bindings, split_port
-from docker.auth import resolve_authconfig
+from docker.auth import resolve_repository_name, resolve_authconfig
import base
@@ -167,6 +167,61 @@ class UtilsTest(base.BaseTestCase):
type=LogConfig.types.JSON, config='helloworld'
))
+ def test_resolve_repository_name(self):
+ # docker hub library image
+ self.assertEqual(
+ resolve_repository_name('image'),
+ ('index.docker.io', 'image'),
+ )
+
+ # docker hub image
+ self.assertEqual(
+ resolve_repository_name('username/image'),
+ ('index.docker.io', 'username/image'),
+ )
+
+ # private registry
+ self.assertEqual(
+ resolve_repository_name('my.registry.net/image'),
+ ('my.registry.net', 'image'),
+ )
+
+ # private registry with port
+ self.assertEqual(
+ resolve_repository_name('my.registry.net:5000/image'),
+ ('my.registry.net:5000', 'image'),
+ )
+
+ # private registry with username
+ self.assertEqual(
+ resolve_repository_name('my.registry.net/username/image'),
+ ('my.registry.net', 'username/image'),
+ )
+
+ # no dots but port
+ self.assertEqual(
+ resolve_repository_name('hostname:5000/image'),
+ ('hostname:5000', 'image'),
+ )
+
+ # no dots but port and username
+ self.assertEqual(
+ resolve_repository_name('hostname:5000/username/image'),
+ ('hostname:5000', 'username/image'),
+ )
+
+ # localhost
+ self.assertEqual(
+ resolve_repository_name('localhost/image'),
+ ('localhost', 'image'),
+ )
+
+ # localhost with username
+ self.assertEqual(
+ resolve_repository_name('localhost/username/image'),
+ ('localhost', 'username/image'),
+ )
+
def test_resolve_authconfig(self):
auth_config = {
'https://index.docker.io/v1/': {'auth': 'indexuser'},
@@ -231,6 +286,40 @@ class UtilsTest(base.BaseTestCase):
resolve_authconfig(auth_config, 'does.not.exist') is None
)
+ def test_resolve_registry_and_auth(self):
+ auth_config = {
+ 'https://index.docker.io/v1/': {'auth': 'indexuser'},
+ 'my.registry.net': {'auth': 'privateuser'},
+ }
+
+ # library image
+ image = 'image'
+ self.assertEqual(
+ resolve_authconfig(auth_config, resolve_repository_name(image)[0]),
+ {'auth': 'indexuser'},
+ )
+
+ # docker hub image
+ image = 'username/image'
+ self.assertEqual(
+ resolve_authconfig(auth_config, resolve_repository_name(image)[0]),
+ {'auth': 'indexuser'},
+ )
+
+ # private registry
+ image = 'my.registry.net/image'
+ self.assertEqual(
+ resolve_authconfig(auth_config, resolve_repository_name(image)[0]),
+ {'auth': 'privateuser'},
+ )
+
+ # unauthenticated registry
+ image = 'other.registry.net/image'
+ self.assertEqual(
+ resolve_authconfig(auth_config, resolve_repository_name(image)[0]),
+ None,
+ )
+
def test_split_port_with_host_ip(self):
internal_port, external_port = split_port("127.0.0.1:1000:2000")
self.assertEqual(internal_port, ["2000"])