mirror of https://github.com/docker/docker-py.git
Stop pinging registries from the client
The daemon already pings the registry, so doing it on our end is redundant and error-prone. The `insecure_registry` argument to `push()`, `pull()` and `login()` has been deprecated - in the latter case, it wasn't being used anyway. The `insecure` argument to `docker.auth.resolve_repository_name()` has also been deprecated. `docker.utils.ping_registry()` has been deprecated. `docker.auth.expand_registry_url()` has been removed. Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
This commit is contained in:
parent
946eb964ad
commit
33e1a58b60
|
@ -1,4 +1,5 @@
|
||||||
from .auth import (
|
from .auth import (
|
||||||
|
INDEX_NAME,
|
||||||
INDEX_URL,
|
INDEX_URL,
|
||||||
encode_header,
|
encode_header,
|
||||||
load_config,
|
load_config,
|
||||||
|
|
|
@ -16,38 +16,33 @@ import base64
|
||||||
import fileinput
|
import fileinput
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import warnings
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from ..utils import utils
|
|
||||||
from .. import errors
|
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')
|
DOCKER_CONFIG_FILENAME = os.path.join('.docker', 'config.json')
|
||||||
LEGACY_DOCKER_CONFIG_FILENAME = '.dockercfg'
|
LEGACY_DOCKER_CONFIG_FILENAME = '.dockercfg'
|
||||||
|
|
||||||
|
|
||||||
def expand_registry_url(hostname, insecure=False):
|
def resolve_repository_name(repo_name, insecure=False):
|
||||||
if hostname.startswith('http:') or hostname.startswith('https:'):
|
if insecure:
|
||||||
return hostname
|
warnings.warn(
|
||||||
if utils.ping_registry('https://' + hostname):
|
'The `insecure` argument to resolve_repository_name() '
|
||||||
return 'https://' + hostname
|
'is deprecated and non-functional. Please remove it.',
|
||||||
elif insecure:
|
DeprecationWarning
|
||||||
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 '://' in repo_name:
|
if '://' in repo_name:
|
||||||
raise errors.InvalidRepository(
|
raise errors.InvalidRepository(
|
||||||
'Repository name cannot contain a scheme ({0})'.format(repo_name))
|
'Repository name cannot contain a scheme ({0})'.format(repo_name))
|
||||||
parts = repo_name.split('/', 1)
|
parts = repo_name.split('/', 1)
|
||||||
if '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost':
|
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)
|
# 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:
|
if len(parts) < 2:
|
||||||
raise errors.InvalidRepository(
|
raise errors.InvalidRepository(
|
||||||
'Invalid repository name ({0})'.format(repo_name))
|
'Invalid repository name ({0})'.format(repo_name))
|
||||||
|
@ -57,7 +52,7 @@ def resolve_repository_name(repo_name, insecure=False):
|
||||||
'Invalid repository name, try "{0}" instead'.format(parts[1])
|
'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):
|
def resolve_authconfig(authconfig, registry=None):
|
||||||
|
@ -68,7 +63,7 @@ def resolve_authconfig(authconfig, registry=None):
|
||||||
Returns None if no match was found.
|
Returns None if no match was found.
|
||||||
"""
|
"""
|
||||||
# Default to the public index server
|
# 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:
|
if registry in authconfig:
|
||||||
return authconfig[registry]
|
return authconfig[registry]
|
||||||
|
@ -185,7 +180,7 @@ def load_config(config_path=None):
|
||||||
'Invalid or empty configuration file!')
|
'Invalid or empty configuration file!')
|
||||||
|
|
||||||
username, password = decode_auth(data[0])
|
username, password = decode_auth(data[0])
|
||||||
conf[INDEX_URL] = {
|
conf[INDEX_NAME] = {
|
||||||
'username': username,
|
'username': username,
|
||||||
'password': password,
|
'password': password,
|
||||||
'email': data[1],
|
'email': data[1],
|
||||||
|
|
|
@ -25,6 +25,7 @@ from . import constants
|
||||||
from . import errors
|
from . import errors
|
||||||
from .auth import auth
|
from .auth import auth
|
||||||
from .utils import utils, check_resource
|
from .utils import utils, check_resource
|
||||||
|
from .constants import INSECURE_REGISTRY_DEPRECATION_WARNING
|
||||||
|
|
||||||
|
|
||||||
class Client(clientbase.ClientBase):
|
class Client(clientbase.ClientBase):
|
||||||
|
@ -499,6 +500,12 @@ class Client(clientbase.ClientBase):
|
||||||
|
|
||||||
def login(self, username, password=None, email=None, registry=None,
|
def login(self, username, password=None, email=None, registry=None,
|
||||||
reauth=False, insecure_registry=False, dockercfg_path=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
|
# 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.
|
# one more time in case anything showed up in there.
|
||||||
# If dockercfg_path is passed check to see if the config file exists,
|
# If dockercfg_path is passed check to see if the config file exists,
|
||||||
|
@ -584,11 +591,15 @@ class Client(clientbase.ClientBase):
|
||||||
|
|
||||||
def pull(self, repository, tag=None, stream=False,
|
def pull(self, repository, tag=None, stream=False,
|
||||||
insecure_registry=False, auth_config=None):
|
insecure_registry=False, auth_config=None):
|
||||||
|
if insecure_registry:
|
||||||
|
warnings.warn(
|
||||||
|
INSECURE_REGISTRY_DEPRECATION_WARNING.format('pull()'),
|
||||||
|
DeprecationWarning
|
||||||
|
)
|
||||||
|
|
||||||
if not tag:
|
if not tag:
|
||||||
repository, tag = utils.parse_repository_tag(repository)
|
repository, tag = utils.parse_repository_tag(repository)
|
||||||
registry, repo_name = auth.resolve_repository_name(
|
registry, repo_name = auth.resolve_repository_name(repository)
|
||||||
repository, insecure=insecure_registry
|
|
||||||
)
|
|
||||||
if repo_name.count(":") == 1:
|
if repo_name.count(":") == 1:
|
||||||
repository, tag = repository.rsplit(":", 1)
|
repository, tag = repository.rsplit(":", 1)
|
||||||
|
|
||||||
|
@ -631,11 +642,15 @@ class Client(clientbase.ClientBase):
|
||||||
|
|
||||||
def push(self, repository, tag=None, stream=False,
|
def push(self, repository, tag=None, stream=False,
|
||||||
insecure_registry=False):
|
insecure_registry=False):
|
||||||
|
if insecure_registry:
|
||||||
|
warnings.warn(
|
||||||
|
INSECURE_REGISTRY_DEPRECATION_WARNING.format('push()'),
|
||||||
|
DeprecationWarning
|
||||||
|
)
|
||||||
|
|
||||||
if not tag:
|
if not tag:
|
||||||
repository, tag = utils.parse_repository_tag(repository)
|
repository, tag = utils.parse_repository_tag(repository)
|
||||||
registry, repo_name = auth.resolve_repository_name(
|
registry, repo_name = auth.resolve_repository_name(repository)
|
||||||
repository, insecure=insecure_registry
|
|
||||||
)
|
|
||||||
u = self._url("/images/{0}/push".format(repository))
|
u = self._url("/images/{0}/push".format(repository))
|
||||||
params = {
|
params = {
|
||||||
'tag': tag
|
'tag': tag
|
||||||
|
|
|
@ -4,3 +4,7 @@ STREAM_HEADER_SIZE_BYTES = 8
|
||||||
CONTAINER_LIMITS_KEYS = [
|
CONTAINER_LIMITS_KEYS = [
|
||||||
'memory', 'memswap', 'cpushares', 'cpusetcpus'
|
'memory', 'memswap', 'cpushares', 'cpusetcpus'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
INSECURE_REGISTRY_DEPRECATION_WARNING = \
|
||||||
|
'The `insecure_registry` argument to {} ' \
|
||||||
|
'is deprecated and non-functional. Please remove it.'
|
||||||
|
|
|
@ -19,6 +19,7 @@ import json
|
||||||
import shlex
|
import shlex
|
||||||
import tarfile
|
import tarfile
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import warnings
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -120,6 +121,11 @@ def compare_version(v1, v2):
|
||||||
|
|
||||||
|
|
||||||
def ping_registry(url):
|
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')
|
return ping(url + '/v2/', [401]) or ping(url + '/v1/_ping')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2424,9 +2424,9 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
|
||||||
f.write('auth = {0}\n'.format(auth_))
|
f.write('auth = {0}\n'.format(auth_))
|
||||||
f.write('email = sakuya@scarlet.net')
|
f.write('email = sakuya@scarlet.net')
|
||||||
cfg = docker.auth.load_config(dockercfg_path)
|
cfg = docker.auth.load_config(dockercfg_path)
|
||||||
self.assertTrue(docker.auth.INDEX_URL in cfg)
|
self.assertTrue(docker.auth.INDEX_NAME in cfg)
|
||||||
self.assertNotEqual(cfg[docker.auth.INDEX_URL], None)
|
self.assertNotEqual(cfg[docker.auth.INDEX_NAME], None)
|
||||||
cfg = cfg[docker.auth.INDEX_URL]
|
cfg = cfg[docker.auth.INDEX_NAME]
|
||||||
self.assertEqual(cfg['username'], 'sakuya')
|
self.assertEqual(cfg['username'], 'sakuya')
|
||||||
self.assertEqual(cfg['password'], 'izayoi')
|
self.assertEqual(cfg['password'], 'izayoi')
|
||||||
self.assertEqual(cfg['email'], 'sakuya@scarlet.net')
|
self.assertEqual(cfg['email'], 'sakuya@scarlet.net')
|
||||||
|
|
|
@ -9,7 +9,7 @@ from docker.utils import (
|
||||||
create_host_config, Ulimit, LogConfig, parse_bytes
|
create_host_config, Ulimit, LogConfig, parse_bytes
|
||||||
)
|
)
|
||||||
from docker.utils.ports import build_port_bindings, split_port
|
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
|
import base
|
||||||
|
|
||||||
|
@ -167,6 +167,61 @@ class UtilsTest(base.BaseTestCase):
|
||||||
type=LogConfig.types.JSON, config='helloworld'
|
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):
|
def test_resolve_authconfig(self):
|
||||||
auth_config = {
|
auth_config = {
|
||||||
'https://index.docker.io/v1/': {'auth': 'indexuser'},
|
'https://index.docker.io/v1/': {'auth': 'indexuser'},
|
||||||
|
@ -231,6 +286,40 @@ class UtilsTest(base.BaseTestCase):
|
||||||
resolve_authconfig(auth_config, 'does.not.exist') is None
|
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):
|
def test_split_port_with_host_ip(self):
|
||||||
internal_port, external_port = split_port("127.0.0.1:1000:2000")
|
internal_port, external_port = split_port("127.0.0.1:1000:2000")
|
||||||
self.assertEqual(internal_port, ["2000"])
|
self.assertEqual(internal_port, ["2000"])
|
||||||
|
|
Loading…
Reference in New Issue