Merge pull request #2476 from feliperuhland/add-search-images-limit
Add limit parameter to image search endpoint
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1f119c9..b692508 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,7 +8,7 @@
strategy:
max-parallel: 1
matrix:
- python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9]
+ python-version: [3.6, 3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v2
@@ -18,8 +18,8 @@
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
- python -m pip install --upgrade pip
- pip install -r test-requirements.txt -r requirements.txt
+ python3 -m pip install --upgrade pip
+ pip3 install -r test-requirements.txt -r requirements.txt
- name: Test with pytest
run: |
docker logout
diff --git a/.readthedocs.yml b/.readthedocs.yml
index 7679f80..32113fe 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -4,7 +4,7 @@
configuration: docs/conf.py
python:
- version: 3.5
+ version: 3.6
install:
- requirements: docs-requirements.txt
- requirements: requirements.txt
diff --git a/Dockerfile b/Dockerfile
index 7309a83..22732de 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,11 +1,7 @@
-ARG PYTHON_VERSION=2.7
+ARG PYTHON_VERSION=3.7
FROM python:${PYTHON_VERSION}
-# Add SSH keys and set permissions
-COPY tests/ssh-keys /root/.ssh
-RUN chmod -R 600 /root/.ssh
-
RUN mkdir /src
WORKDIR /src
diff --git a/Dockerfile-py3 b/Dockerfile-py3
deleted file mode 100644
index 22732de..0000000
--- a/Dockerfile-py3
+++ /dev/null
@@ -1,15 +0,0 @@
-ARG PYTHON_VERSION=3.7
-
-FROM python:${PYTHON_VERSION}
-
-RUN mkdir /src
-WORKDIR /src
-
-COPY requirements.txt /src/requirements.txt
-RUN pip install -r requirements.txt
-
-COPY test-requirements.txt /src/test-requirements.txt
-RUN pip install -r test-requirements.txt
-
-COPY . /src
-RUN pip install .
diff --git a/Jenkinsfile b/Jenkinsfile
index 471072b..f524ae7 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -1,7 +1,6 @@
#!groovy
def imageNameBase = "dockerpinata/docker-py"
-def imageNamePy2
def imageNamePy3
def imageDindSSH
def images = [:]
@@ -22,12 +21,10 @@
stage("build image") {
checkout(scm)
- imageNamePy2 = "${imageNameBase}:py2-${gitCommit()}"
imageNamePy3 = "${imageNameBase}:py3-${gitCommit()}"
imageDindSSH = "${imageNameBase}:sshdind-${gitCommit()}"
withDockerRegistry(credentialsId:'dockerbuildbot-index.docker.io') {
buildImage(imageDindSSH, "-f tests/Dockerfile-ssh-dind .", "")
- buildImage(imageNamePy2, "-f tests/Dockerfile --build-arg PYTHON_VERSION=2.7 .", "py2.7")
buildImage(imageNamePy3, "-f tests/Dockerfile --build-arg PYTHON_VERSION=3.7 .", "py3.7")
}
}
@@ -73,7 +70,7 @@
throw new Exception("Need Docker version to test, e.g.: `runTests(dockerVersion: '19.03.12')`")
}
if (!pythonVersion) {
- throw new Exception("Need Python version being tested, e.g.: `runTests(pythonVersion: 'py2.7')`")
+ throw new Exception("Need Python version being tested, e.g.: `runTests(pythonVersion: 'py3.7')`")
}
{ ->
diff --git a/Makefile b/Makefile
index 70d7083..60d9984 100644
--- a/Makefile
+++ b/Makefile
@@ -6,13 +6,9 @@
.PHONY: clean
clean:
- -docker rm -f dpy-dind-py2 dpy-dind-py3 dpy-dind-certs dpy-dind-ssl
+ -docker rm -f dpy-dind-py3 dpy-dind-certs dpy-dind-ssl
find -name "__pycache__" | xargs rm -rf
-.PHONY: build
-build:
- docker build -t docker-sdk-python -f tests/Dockerfile --build-arg PYTHON_VERSION=2.7 --build-arg APT_MIRROR .
-
.PHONY: build-dind-ssh
build-dind-ssh:
docker build -t docker-dind-ssh -f tests/Dockerfile-ssh-dind --build-arg ENGINE_VERSION=${TEST_ENGINE_VERSION} --build-arg API_VERSION=${TEST_API_VERSION} --build-arg APT_MIRROR .
@@ -30,20 +26,12 @@
docker build -t dpy-dind-certs -f tests/Dockerfile-dind-certs .
.PHONY: test
-test: flake8 unit-test unit-test-py3 integration-dind integration-dind-ssl
-
-.PHONY: unit-test
-unit-test: build
- docker run -t --rm docker-sdk-python py.test tests/unit
+test: flake8 unit-test-py3 integration-dind integration-dind-ssl
.PHONY: unit-test-py3
unit-test-py3: build-py3
docker run -t --rm docker-sdk-python3 py.test tests/unit
-.PHONY: integration-test
-integration-test: build
- docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock docker-sdk-python py.test -v tests/integration/${file}
-
.PHONY: integration-test-py3
integration-test-py3: build-py3
docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock docker-sdk-python3 py.test -v tests/integration/${file}
@@ -53,16 +41,7 @@
docker network inspect dpy-tests || docker network create dpy-tests
.PHONY: integration-dind
-integration-dind: integration-dind-py2 integration-dind-py3
-
-.PHONY: integration-dind-py2
-integration-dind-py2: build setup-network
- docker rm -vf dpy-dind-py2 || :
- docker run -d --network dpy-tests --name dpy-dind-py2 --privileged\
- docker:${TEST_ENGINE_VERSION}-dind dockerd -H tcp://0.0.0.0:2375 --experimental
- docker run -t --rm --env="DOCKER_HOST=tcp://dpy-dind-py2:2375" --env="DOCKER_TEST_API_VERSION=${TEST_API_VERSION}"\
- --network dpy-tests docker-sdk-python py.test tests/integration/${file}
- docker rm -vf dpy-dind-py2
+integration-dind: integration-dind-py3
.PHONY: integration-dind-py3
integration-dind-py3: build-py3 setup-network
@@ -73,16 +52,6 @@
--network dpy-tests docker-sdk-python3 py.test tests/integration/${file}
docker rm -vf dpy-dind-py3
-.PHONY: integration-ssh-py2
-integration-ssh-py2: build-dind-ssh build setup-network
- docker rm -vf dpy-dind-py2 || :
- docker run -d --network dpy-tests --name dpy-dind-py2 --privileged\
- docker-dind-ssh dockerd --experimental
- # start SSH daemon
- docker exec dpy-dind-py2 sh -c "/usr/sbin/sshd"
- docker run -t --rm --env="DOCKER_HOST=ssh://dpy-dind-py2" --env="DOCKER_TEST_API_VERSION=${TEST_API_VERSION}"\
- --network dpy-tests docker-sdk-python py.test tests/ssh/${file}
- docker rm -vf dpy-dind-py2
.PHONY: integration-ssh-py3
integration-ssh-py3: build-dind-ssh build-py3 setup-network
@@ -97,7 +66,7 @@
.PHONY: integration-dind-ssl
-integration-dind-ssl: build-dind-certs build build-py3
+integration-dind-ssl: build-dind-certs build-py3
docker rm -vf dpy-dind-certs dpy-dind-ssl || :
docker run -d --name dpy-dind-certs dpy-dind-certs
docker run -d --env="DOCKER_HOST=tcp://localhost:2375" --env="DOCKER_TLS_VERIFY=1"\
@@ -108,20 +77,17 @@
--tlskey=/certs/server-key.pem -H tcp://0.0.0.0:2375 --experimental
docker run -t --rm --volumes-from dpy-dind-ssl --env="DOCKER_HOST=tcp://docker:2375"\
--env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --env="DOCKER_TEST_API_VERSION=${TEST_API_VERSION}"\
- --network dpy-tests docker-sdk-python py.test tests/integration/${file}
- docker run -t --rm --volumes-from dpy-dind-ssl --env="DOCKER_HOST=tcp://docker:2375"\
- --env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --env="DOCKER_TEST_API_VERSION=${TEST_API_VERSION}"\
--network dpy-tests docker-sdk-python3 py.test tests/integration/${file}
docker rm -vf dpy-dind-ssl dpy-dind-certs
.PHONY: flake8
-flake8: build
- docker run -t --rm docker-sdk-python flake8 docker tests
+flake8: build-py3
+ docker run -t --rm docker-sdk-python3 flake8 docker tests
.PHONY: docs
docs: build-docs
docker run --rm -t -v `pwd`:/src docker-sdk-python-docs sphinx-build docs docs/_build
.PHONY: shell
-shell: build
- docker run -it -v /var/run/docker.sock:/var/run/docker.sock docker-sdk-python python
+shell: build-py3
+ docker run -it -v /var/run/docker.sock:/var/run/docker.sock docker-sdk-python3 python
diff --git a/README.md b/README.md
index 8ce684b..4fc31f7 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Docker SDK for Python
-[](https://travis-ci.org/docker/docker-py)
+[](https://github.com/docker/docker-py/actions/workflows/ci.yml/)
A Python library for the Docker Engine API. It lets you do anything the `docker` command does, but from within Python apps – run containers, manage containers, manage Swarms, etc.
diff --git a/docker/api/client.py b/docker/api/client.py
index 2b67291..ee9ad9c 100644
--- a/docker/api/client.py
+++ b/docker/api/client.py
@@ -1,10 +1,10 @@
import json
import struct
+import urllib
from functools import partial
import requests
import requests.exceptions
-import six
import websocket
from .. import auth
@@ -192,12 +192,12 @@
# version detection needs to be after unix adapter mounting
if version is None or (isinstance(
version,
- six.string_types
+ str
) and version.lower() == 'auto'):
self._version = self._retrieve_server_version()
else:
self._version = version
- if not isinstance(self._version, six.string_types):
+ if not isinstance(self._version, str):
raise DockerException(
'Version parameter must be a string or None. Found {0}'.format(
type(version).__name__
@@ -246,13 +246,13 @@
def _url(self, pathfmt, *args, **kwargs):
for arg in args:
- if not isinstance(arg, six.string_types):
+ if not isinstance(arg, str):
raise ValueError(
'Expected a string but found {0} ({1}) '
'instead'.format(arg, type(arg))
)
- quote_f = partial(six.moves.urllib.parse.quote, safe="/:")
+ quote_f = partial(urllib.parse.quote, safe="/:")
args = map(quote_f, args)
if kwargs.get('versioned_api', True):
@@ -284,7 +284,7 @@
# so we do this disgusting thing here.
data2 = {}
if data is not None and isinstance(data, dict):
- for k, v in six.iteritems(data):
+ for k, v in iter(data.items()):
if v is not None:
data2[k] = v
elif data is not None:
@@ -320,12 +320,10 @@
sock = response.raw._fp.fp.raw.sock
elif self.base_url.startswith('http+docker://ssh'):
sock = response.raw._fp.fp.channel
- elif six.PY3:
+ else:
sock = response.raw._fp.fp.raw
if self.base_url.startswith("https://"):
sock = sock._sock
- else:
- sock = response.raw._fp.fp._sock
try:
# Keep a reference to the response to stop it being garbage
# collected. If the response is garbage collected, it will
@@ -465,7 +463,7 @@
self._result(res, binary=True)
self._raise_for_status(res)
- sep = six.binary_type()
+ sep = b''
if stream:
return self._multiplexed_response_stream_helper(res)
else:
diff --git a/docker/api/container.py b/docker/api/container.py
index 754b5dc..369eba9 100644
--- a/docker/api/container.py
+++ b/docker/api/container.py
@@ -244,9 +244,9 @@
.. code-block:: python
- container_id = cli.create_container(
+ container_id = client.api.create_container(
'busybox', 'ls', ports=[1111, 2222],
- host_config=cli.create_host_config(port_bindings={
+ host_config=client.api.create_host_config(port_bindings={
1111: 4567,
2222: None
})
@@ -258,22 +258,22 @@
.. code-block:: python
- cli.create_host_config(port_bindings={1111: ('127.0.0.1', 4567)})
+ client.api.create_host_config(port_bindings={1111: ('127.0.0.1', 4567)})
Or without host port assignment:
.. code-block:: python
- cli.create_host_config(port_bindings={1111: ('127.0.0.1',)})
+ client.api.create_host_config(port_bindings={1111: ('127.0.0.1',)})
If you wish to use UDP instead of TCP (default), you need to declare
ports as such in both the config and host config:
.. code-block:: python
- container_id = cli.create_container(
+ container_id = client.api.create_container(
'busybox', 'ls', ports=[(1111, 'udp'), 2222],
- host_config=cli.create_host_config(port_bindings={
+ host_config=client.api.create_host_config(port_bindings={
'1111/udp': 4567, 2222: None
})
)
@@ -283,7 +283,7 @@
.. code-block:: python
- cli.create_host_config(port_bindings={
+ client.api.create_host_config(port_bindings={
1111: [1234, 4567]
})
@@ -291,7 +291,7 @@
.. code-block:: python
- cli.create_host_config(port_bindings={
+ client.api.create_host_config(port_bindings={
1111: [
('192.168.0.100', 1234),
('192.168.0.101', 1234)
@@ -307,9 +307,9 @@
.. code-block:: python
- container_id = cli.create_container(
+ container_id = client.api.create_container(
'busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2'],
- host_config=cli.create_host_config(binds={
+ host_config=client.api.create_host_config(binds={
'/home/user1/': {
'bind': '/mnt/vol2',
'mode': 'rw',
@@ -326,9 +326,9 @@
.. code-block:: python
- container_id = cli.create_container(
+ container_id = client.api.create_container(
'busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2'],
- host_config=cli.create_host_config(binds=[
+ host_config=client.api.create_host_config(binds=[
'/home/user1/:/mnt/vol2',
'/var/www:/mnt/vol1:ro',
])
@@ -346,15 +346,15 @@
.. code-block:: python
- networking_config = docker_client.create_networking_config({
- 'network1': docker_client.create_endpoint_config(
+ networking_config = client.api.create_networking_config({
+ 'network1': client.api.create_endpoint_config(
ipv4_address='172.28.0.124',
aliases=['foo', 'bar'],
links=['container2']
)
})
- ctnr = docker_client.create_container(
+ ctnr = client.api.create_container(
img, command, networking_config=networking_config
)
@@ -581,7 +581,7 @@
Example:
- >>> cli.create_host_config(privileged=True, cap_drop=['MKNOD'],
+ >>> client.api.create_host_config(privileged=True, cap_drop=['MKNOD'],
volumes_from=['nostalgic_newton'])
{'CapDrop': ['MKNOD'], 'LxcConf': None, 'Privileged': True,
'VolumesFrom': ['nostalgic_newton'], 'PublishAllPorts': False}
@@ -612,11 +612,11 @@
Example:
- >>> docker_client.create_network('network1')
- >>> networking_config = docker_client.create_networking_config({
- 'network1': docker_client.create_endpoint_config()
+ >>> client.api.create_network('network1')
+ >>> networking_config = client.api.create_networking_config({
+ 'network1': client.api.create_endpoint_config()
})
- >>> container = docker_client.create_container(
+ >>> container = client.api.create_container(
img, command, networking_config=networking_config
)
@@ -650,7 +650,7 @@
Example:
- >>> endpoint_config = client.create_endpoint_config(
+ >>> endpoint_config = client.api.create_endpoint_config(
aliases=['web', 'app'],
links={'app_db': 'db', 'another': None},
ipv4_address='132.65.0.123'
@@ -729,7 +729,7 @@
>>> c = docker.APIClient()
>>> f = open('./sh_bin.tar', 'wb')
- >>> bits, stat = c.get_archive(container, '/bin/sh')
+ >>> bits, stat = c.api.get_archive(container, '/bin/sh')
>>> print(stat)
{'name': 'sh', 'size': 1075464, 'mode': 493,
'mtime': '2018-10-01T15:37:48-07:00', 'linkTarget': ''}
@@ -916,7 +916,7 @@
.. code-block:: python
- >>> cli.port('7174d6347063', 80)
+ >>> client.api.port('7174d6347063', 80)
[{'HostIp': '0.0.0.0', 'HostPort': '80'}]
"""
res = self._get(self._url("/containers/{0}/json", container))
@@ -1095,10 +1095,10 @@
Example:
- >>> container = cli.create_container(
+ >>> container = client.api.create_container(
... image='busybox:latest',
... command='/bin/sleep 30')
- >>> cli.start(container=container.get('Id'))
+ >>> client.api.start(container=container.get('Id'))
"""
if args or kwargs:
raise errors.DeprecatedMethod(
diff --git a/docker/api/image.py b/docker/api/image.py
index 4658cde..772101f 100644
--- a/docker/api/image.py
+++ b/docker/api/image.py
@@ -31,7 +31,7 @@
Example:
- >>> image = cli.get_image("busybox:latest")
+ >>> image = client.api.get_image("busybox:latest")
>>> f = open('/tmp/busybox-latest.tar', 'wb')
>>> for chunk in image:
>>> f.write(chunk)
@@ -379,7 +379,7 @@
Example:
- >>> for line in cli.pull('busybox', stream=True, decode=True):
+ >>> for line in client.api.pull('busybox', stream=True, decode=True):
... print(json.dumps(line, indent=4))
{
"status": "Pulling image (latest) from busybox",
@@ -458,7 +458,7 @@
If the server returns an error.
Example:
- >>> for line in cli.push('yourname/app', stream=True, decode=True):
+ >>> for line in client.api.push('yourname/app', stream=True, decode=True):
... print(line)
{'status': 'Pushing repository yourname/app (1 tags)'}
{'status': 'Pushing','progressDetail': {}, 'id': '511136ea3c5a'}
@@ -554,7 +554,7 @@
Example:
- >>> client.tag('ubuntu', 'localhost:5000/ubuntu', 'latest',
+ >>> client.api.tag('ubuntu', 'localhost:5000/ubuntu', 'latest',
force=True)
"""
params = {
diff --git a/docker/context/context.py b/docker/context/context.py
index 2413b2e..f4aff6b 100644
--- a/docker/context/context.py
+++ b/docker/context/context.py
@@ -11,6 +11,7 @@
class Context:
"""A context."""
+
def __init__(self, name, orchestrator=None, host=None, endpoints=None,
tls=False):
if not name:
@@ -127,8 +128,12 @@
elif filename.startswith("key"):
key = os.path.join(tls_dir, endpoint, filename)
if all([ca_cert, cert, key]):
+ verify = None
+ if endpoint == "docker" and not self.endpoints["docker"].get(
+ "SkipTLSVerify", False):
+ verify = True
certs[endpoint] = TLSConfig(
- client_cert=(cert, key), ca_cert=ca_cert)
+ client_cert=(cert, key), ca_cert=ca_cert, verify=verify)
self.tls_cfg = certs
self.tls_path = tls_dir
diff --git a/docker/models/secrets.py b/docker/models/secrets.py
index ca11ede..e2ee88a 100644
--- a/docker/models/secrets.py
+++ b/docker/models/secrets.py
@@ -30,6 +30,7 @@
def create(self, **kwargs):
obj = self.client.api.create_secret(**kwargs)
+ obj.setdefault("Spec", {})["Name"] = kwargs.get("name")
return self.prepare_model(obj)
create.__doc__ = APIClient.create_secret.__doc__
diff --git a/docker/transport/sshconn.py b/docker/transport/sshconn.py
index a761ef5..fb5c6bb 100644
--- a/docker/transport/sshconn.py
+++ b/docker/transport/sshconn.py
@@ -53,7 +53,7 @@
signal.signal(signal.SIGINT, signal.SIG_IGN)
preexec_func = f
- env = dict(os.environ)
+ env = dict(os.environ)
# drop LD_LIBRARY_PATH and SSL_CERT_FILE
env.pop('LD_LIBRARY_PATH', None)
@@ -65,7 +65,7 @@
shell=True,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
- preexec_fn=preexec_func)
+ preexec_fn=None if constants.IS_WINDOWS_PLATFORM else preexec_func)
def _write(self, data):
if not self.proc or self.proc.stdin.closed:
diff --git a/docker/utils/ports.py b/docker/utils/ports.py
index a50cc02..10b19d7 100644
--- a/docker/utils/ports.py
+++ b/docker/utils/ports.py
@@ -3,7 +3,7 @@
PORT_SPEC = re.compile(
"^" # Match full string
"(" # External part
- r"((?P<host>[a-fA-F\d.:]+):)?" # Address
+ r"(\[?(?P<host>[a-fA-F\d.:]+)\]?:)?" # Address
r"(?P<ext>[\d]*)(-(?P<ext_end>[\d]+))?:" # External range
")?"
r"(?P<int>[\d]+)(-(?P<int_end>[\d]+))?" # Internal range
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index 1b195e2..f703cbd 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -7,8 +7,6 @@
from datetime import datetime
from distutils.version import StrictVersion
-import six
-
from .. import errors
from .. import tls
from ..constants import DEFAULT_HTTP_HOST
@@ -16,11 +14,7 @@
from ..constants import DEFAULT_NPIPE
from ..constants import BYTE_UNITS
-if six.PY2:
- from urllib import splitnport
- from urlparse import urlparse
-else:
- from urllib.parse import splitnport, urlparse
+from urllib.parse import splitnport, urlparse
def create_ipam_pool(*args, **kwargs):
@@ -39,8 +33,7 @@
def decode_json_header(header):
data = base64.b64decode(header)
- if six.PY3:
- data = data.decode('utf-8')
+ data = data.decode('utf-8')
return json.loads(data)
@@ -80,7 +73,7 @@
if len(binding) == 2:
result['HostPort'] = binding[1]
result['HostIp'] = binding[0]
- elif isinstance(binding[0], six.string_types):
+ elif isinstance(binding[0], str):
result['HostIp'] = binding[0]
else:
result['HostPort'] = binding[0]
@@ -104,7 +97,7 @@
def convert_port_bindings(port_bindings):
result = {}
- for k, v in six.iteritems(port_bindings):
+ for k, v in iter(port_bindings.items()):
key = str(k)
if '/' not in key:
key += '/tcp'
@@ -121,7 +114,7 @@
result = []
for k, v in binds.items():
- if isinstance(k, six.binary_type):
+ if isinstance(k, bytes):
k = k.decode('utf-8')
if isinstance(v, dict):
@@ -132,7 +125,7 @@
)
bind = v['bind']
- if isinstance(bind, six.binary_type):
+ if isinstance(bind, bytes):
bind = bind.decode('utf-8')
if 'ro' in v:
@@ -143,13 +136,13 @@
mode = 'rw'
result.append(
- six.text_type('{0}:{1}:{2}').format(k, bind, mode)
+ str('{0}:{1}:{2}').format(k, bind, mode)
)
else:
- if isinstance(v, six.binary_type):
+ if isinstance(v, bytes):
v = v.decode('utf-8')
result.append(
- six.text_type('{0}:{1}:rw').format(k, v)
+ str('{0}:{1}:rw').format(k, v)
)
return result
@@ -166,7 +159,7 @@
result = {}
for mount in tmpfs:
- if isinstance(mount, six.string_types):
+ if isinstance(mount, str):
if ":" in mount:
name, options = mount.split(":", 1)
else:
@@ -191,7 +184,7 @@
result = []
for n in networks:
- if isinstance(n, six.string_types):
+ if isinstance(n, str):
n = {'Target': n}
result.append(n)
return result
@@ -302,7 +295,7 @@
if isinstance(device, dict):
device_list.append(device)
continue
- if not isinstance(device, six.string_types):
+ if not isinstance(device, str):
raise errors.DockerException(
'Invalid device type {0}'.format(type(device))
)
@@ -372,13 +365,13 @@
def convert_filters(filters):
result = {}
- for k, v in six.iteritems(filters):
+ for k, v in iter(filters.items()):
if isinstance(v, bool):
v = 'true' if v else 'false'
if not isinstance(v, list):
v = [v, ]
result[k] = [
- str(item) if not isinstance(item, six.string_types) else item
+ str(item) if not isinstance(item, str) else item
for item in v
]
return json.dumps(result)
@@ -391,7 +384,7 @@
def parse_bytes(s):
- if isinstance(s, six.integer_types + (float,)):
+ if isinstance(s, (int, float,)):
return s
if len(s) == 0:
return 0
@@ -433,7 +426,7 @@
def normalize_links(links):
if isinstance(links, dict):
- links = six.iteritems(links)
+ links = iter(links.items())
return ['{0}:{1}'.format(k, v) if v else k for k, v in sorted(links)]
@@ -468,8 +461,6 @@
def split_command(command):
- if six.PY2 and not isinstance(command, six.binary_type):
- command = command.encode('utf-8')
return shlex.split(command)
@@ -477,22 +468,22 @@
def format_env(key, value):
if value is None:
return key
- if isinstance(value, six.binary_type):
+ if isinstance(value, bytes):
value = value.decode('utf-8')
return u'{key}={value}'.format(key=key, value=value)
- return [format_env(*var) for var in six.iteritems(environment)]
+ return [format_env(*var) for var in iter(environment.items())]
def format_extra_hosts(extra_hosts, task=False):
# Use format dictated by Swarm API if container is part of a task
if task:
return [
- '{} {}'.format(v, k) for k, v in sorted(six.iteritems(extra_hosts))
+ '{} {}'.format(v, k) for k, v in sorted(iter(extra_hosts.items()))
]
return [
- '{}:{}'.format(k, v) for k, v in sorted(six.iteritems(extra_hosts))
+ '{}:{}'.format(k, v) for k, v in sorted(iter(extra_hosts.items()))
]
diff --git a/docs/change-log.md b/docs/change-log.md
index 546e071..8db3fc5 100644
--- a/docs/change-log.md
+++ b/docs/change-log.md
@@ -1,6 +1,14 @@
Change log
==========
+4.4.4
+-----
+
+[List of PRs / issues for this release](https://github.com/docker/docker-py/milestone/73?closed=1)
+
+### Bugfixes
+- Remove `LD_LIBRARY_PATH` and `SSL_CERT_FILE` environment variables when shelling out to the ssh client
+
4.4.3
-----
diff --git a/requirements.txt b/requirements.txt
index 43a688f..1d0be30 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@
asn1crypto==0.22.0
backports.ssl-match-hostname==3.5.0.1
cffi==1.14.4
-cryptography==3.2
+cryptography==3.4.7
enum34==1.1.6
idna==2.5
ipaddress==1.0.18
@@ -13,6 +13,5 @@
pyparsing==2.2.0
pywin32==227; sys_platform == 'win32'
requests==2.20.0
-six==1.10.0
urllib3==1.24.3
websocket-client==0.56.0
diff --git a/setup.py b/setup.py
index 330ab3e..b692eab 100644
--- a/setup.py
+++ b/setup.py
@@ -11,18 +11,11 @@
SOURCE_DIR = os.path.join(ROOT_DIR)
requirements = [
- 'six >= 1.4.0',
'websocket-client >= 0.32.0',
'requests >= 2.14.2, != 2.18.0',
]
extras_require = {
- ':python_version < "3.5"': 'backports.ssl_match_hostname >= 3.5',
- # While not imported explicitly, the ipaddress module is required for
- # ssl_match_hostname to verify hosts match with certificates via
- # ServerAltname: https://pypi.python.org/pypi/backports.ssl_match_hostname
- ':python_version < "3.3"': 'ipaddress >= 1.0.16',
-
# win32 APIs if on Windows (required for npipe support)
':sys_platform == "win32"': 'pywin32==227',
@@ -34,7 +27,7 @@
# https://github.com/pypa/pip/issues/4391). Once that's fixed, instead of
# installing the extra dependencies, install the following instead:
# 'requests[security] >= 2.5.2, != 2.11.0, != 2.12.2'
- 'tls': ['pyOpenSSL>=17.5.0', 'cryptography>=1.3.4', 'idna>=2.0.0'],
+ 'tls': ['pyOpenSSL>=17.5.0', 'cryptography>=3.4.7', 'idna>=2.0.0'],
# Only required when connecting using the ssh:// protocol
'ssh': ['paramiko>=2.4.2'],
@@ -69,7 +62,7 @@
install_requires=requirements,
tests_require=test_requirements,
extras_require=extras_require,
- python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
+ python_requires='>=3.6',
zip_safe=False,
test_suite='tests',
classifiers=[
@@ -78,10 +71,7 @@
'Intended Audience :: Developers',
'Operating System :: OS Independent',
'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
diff --git a/test-requirements.txt b/test-requirements.txt
index 24078e2..40161bb 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,4 +1,4 @@
-setuptools==44.0.0 # last version with python 2.7 support
+setuptools==54.1.1
coverage==4.5.2
flake8==3.6.0
mock==1.0.1
diff --git a/tests/Dockerfile-dind-certs b/tests/Dockerfile-dind-certs
index 2ab87ef..8829ff7 100644
--- a/tests/Dockerfile-dind-certs
+++ b/tests/Dockerfile-dind-certs
@@ -1,4 +1,4 @@
-ARG PYTHON_VERSION=2.7
+ARG PYTHON_VERSION=3.6
FROM python:${PYTHON_VERSION}
RUN mkdir /tmp/certs
diff --git a/tests/integration/api_container_test.py b/tests/integration/api_container_test.py
index 65e611b..3087045 100644
--- a/tests/integration/api_container_test.py
+++ b/tests/integration/api_container_test.py
@@ -7,7 +7,6 @@
import pytest
import requests
-import six
import docker
from .. import helpers
@@ -35,7 +34,7 @@
assert len(retrieved) == 1
retrieved = retrieved[0]
assert 'Command' in retrieved
- assert retrieved['Command'] == six.text_type('true')
+ assert retrieved['Command'] == str('true')
assert 'Image' in retrieved
assert re.search(r'alpine:.*', retrieved['Image'])
assert 'Status' in retrieved
@@ -104,9 +103,7 @@
self.client.start(container3_id)
assert self.client.wait(container3_id)['StatusCode'] == 0
- logs = self.client.logs(container3_id)
- if six.PY3:
- logs = logs.decode('utf-8')
+ logs = self.client.logs(container3_id).decode('utf-8')
assert '{0}_NAME='.format(link_env_prefix1) in logs
assert '{0}_ENV_FOO=1'.format(link_env_prefix1) in logs
assert '{0}_NAME='.format(link_env_prefix2) in logs
@@ -227,9 +224,7 @@
self.client.start(container)
self.client.wait(container)
- logs = self.client.logs(container)
- if six.PY3:
- logs = logs.decode('utf-8')
+ logs = self.client.logs(container).decode('utf-8')
groups = logs.strip().split(' ')
assert '1000' in groups
assert '1001' in groups
@@ -244,9 +239,7 @@
self.client.start(container)
self.client.wait(container)
- logs = self.client.logs(container)
- if six.PY3:
- logs = logs.decode('utf-8')
+ logs = self.client.logs(container).decode('utf-8')
groups = logs.strip().split(' ')
assert '1000' in groups
@@ -515,10 +508,7 @@
TEST_IMG,
['ls', self.mount_dest],
)
- logs = self.client.logs(container)
-
- if six.PY3:
- logs = logs.decode('utf-8')
+ logs = self.client.logs(container).decode('utf-8')
assert self.filename in logs
inspect_data = self.client.inspect_container(container)
self.check_container_data(inspect_data, True)
@@ -534,10 +524,8 @@
TEST_IMG,
['ls', self.mount_dest],
)
- logs = self.client.logs(container)
+ logs = self.client.logs(container).decode('utf-8')
- if six.PY3:
- logs = logs.decode('utf-8')
assert self.filename in logs
inspect_data = self.client.inspect_container(container)
@@ -554,9 +542,7 @@
host_config=host_config
)
assert container
- logs = self.client.logs(container)
- if six.PY3:
- logs = logs.decode('utf-8')
+ logs = self.client.logs(container).decode('utf-8')
assert self.filename in logs
inspect_data = self.client.inspect_container(container)
self.check_container_data(inspect_data, True)
@@ -573,9 +559,7 @@
host_config=host_config
)
assert container
- logs = self.client.logs(container)
- if six.PY3:
- logs = logs.decode('utf-8')
+ logs = self.client.logs(container).decode('utf-8')
assert self.filename in logs
inspect_data = self.client.inspect_container(container)
self.check_container_data(inspect_data, False)
@@ -645,9 +629,8 @@
for d in strm:
destination.write(d)
destination.seek(0)
- retrieved_data = helpers.untar_file(destination, 'data.txt')
- if six.PY3:
- retrieved_data = retrieved_data.decode('utf-8')
+ retrieved_data = helpers.untar_file(destination, 'data.txt')\
+ .decode('utf-8')
assert data == retrieved_data.strip()
def test_get_file_stat_from_container(self):
@@ -683,9 +666,6 @@
self.client.start(ctnr)
self.client.wait(ctnr)
logs = self.client.logs(ctnr)
- if six.PY3:
- logs = logs.decode('utf-8')
- data = data.decode('utf-8')
assert logs.strip() == data
def test_copy_directory_to_container(self):
@@ -700,9 +680,7 @@
self.client.put_archive(ctnr, '/vol1', test_tar)
self.client.start(ctnr)
self.client.wait(ctnr)
- logs = self.client.logs(ctnr)
- if six.PY3:
- logs = logs.decode('utf-8')
+ logs = self.client.logs(ctnr).decode('utf-8')
results = logs.strip().split()
assert 'a.py' in results
assert 'b.py' in results
@@ -861,7 +839,7 @@
id = container['Id']
self.tmp_containers.append(id)
self.client.start(id)
- logs = six.binary_type()
+ logs = b''
for chunk in self.client.logs(id, stream=True, follow=True):
logs += chunk
@@ -881,7 +859,7 @@
id = container['Id']
self.tmp_containers.append(id)
self.client.start(id)
- logs = six.binary_type()
+ logs = b''
generator = self.client.logs(id, stream=True, follow=True)
threading.Timer(1, generator.close).start()
diff --git a/tests/integration/api_image_test.py b/tests/integration/api_image_test.py
index 37e26a3..d5f8989 100644
--- a/tests/integration/api_image_test.py
+++ b/tests/integration/api_image_test.py
@@ -7,9 +7,8 @@
import threading
import pytest
-import six
-from six.moves import BaseHTTPServer
-from six.moves import socketserver
+from http.server import SimpleHTTPRequestHandler
+import socketserver
import docker
@@ -33,7 +32,7 @@
def test_images_quiet(self):
res1 = self.client.images(quiet=True)
- assert type(res1[0]) == six.text_type
+ assert type(res1[0]) == str
class PullImageTest(BaseAPIIntegrationTest):
@@ -44,7 +43,7 @@
pass
res = self.client.pull('hello-world')
self.tmp_imgs.append('hello-world')
- assert type(res) == six.text_type
+ assert type(res) == str
assert len(self.client.images('hello-world')) >= 1
img_info = self.client.inspect_image('hello-world')
assert 'Id' in img_info
@@ -273,7 +272,7 @@
def temporary_http_file_server(self, stream):
'''Serve data from an IO stream over HTTP.'''
- class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
+ class Handler(SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'application/x-tar')
diff --git a/tests/integration/api_service_test.py b/tests/integration/api_service_test.py
index 7e5336e..1bee46e 100644
--- a/tests/integration/api_service_test.py
+++ b/tests/integration/api_service_test.py
@@ -5,7 +5,6 @@
import docker
import pytest
-import six
from ..helpers import (
force_leave_swarm, requires_api_version, requires_experimental
@@ -150,7 +149,7 @@
else:
break
- if six.PY3:
+ if log_line is not None:
log_line = log_line.decode('utf-8')
assert 'hello\n' in log_line
diff --git a/tests/unit/fake_api.py b/tests/unit/fake_api.py
index 27e463d..4fd4d11 100644
--- a/tests/unit/fake_api.py
+++ b/tests/unit/fake_api.py
@@ -17,6 +17,8 @@
FAKE_PATH = '/path'
FAKE_VOLUME_NAME = 'perfectcherryblossom'
FAKE_NODE_ID = '24ifsmvkjbyhk'
+FAKE_SECRET_ID = 'epdyrw4tsi03xy3deu8g8ly6o'
+FAKE_SECRET_NAME = 'super_secret'
# Each method is prefixed with HTTP method (get, post...)
# for clarity and readability
@@ -512,6 +514,12 @@
return 200, None
+def post_fake_secret():
+ status_code = 200
+ response = {'ID': FAKE_SECRET_ID}
+ return status_code, response
+
+
# Maps real api url to fake response callback
prefix = 'http+docker://localhost'
if constants.IS_WINDOWS_PLATFORM:
@@ -643,4 +651,6 @@
CURRENT_VERSION, prefix, FAKE_NETWORK_ID
), 'POST'):
post_fake_network_disconnect,
+ '{1}/{0}/secrets/create'.format(CURRENT_VERSION, prefix):
+ post_fake_secret,
}
diff --git a/tests/unit/fake_api_client.py b/tests/unit/fake_api_client.py
index e85001d..5825b6e 100644
--- a/tests/unit/fake_api_client.py
+++ b/tests/unit/fake_api_client.py
@@ -40,6 +40,7 @@
fake_api.post_fake_create_container()[1],
'create_host_config.side_effect': api_client.create_host_config,
'create_network.return_value': fake_api.post_fake_network()[1],
+ 'create_secret.return_value': fake_api.post_fake_secret()[1],
'exec_create.return_value': fake_api.post_fake_exec_create()[1],
'exec_start.return_value': fake_api.post_fake_exec_start()[1],
'images.return_value': fake_api.get_fake_images()[1],
diff --git a/tests/unit/models_secrets_test.py b/tests/unit/models_secrets_test.py
new file mode 100644
index 0000000..4ccf4c6
--- /dev/null
+++ b/tests/unit/models_secrets_test.py
@@ -0,0 +1,11 @@
+import unittest
+
+from .fake_api_client import make_fake_client
+from .fake_api import FAKE_SECRET_NAME
+
+
+class CreateServiceTest(unittest.TestCase):
+ def test_secrets_repr(self):
+ client = make_fake_client()
+ secret = client.secrets.create(name="super_secret", data="secret")
+ assert secret.__repr__() == "<Secret: '{}'>".format(FAKE_SECRET_NAME)
diff --git a/tests/unit/sshadapter_test.py b/tests/unit/sshadapter_test.py
index ddee592..874239a 100644
--- a/tests/unit/sshadapter_test.py
+++ b/tests/unit/sshadapter_test.py
@@ -2,31 +2,38 @@
import docker
from docker.transport.sshconn import SSHSocket
+
class SSHAdapterTest(unittest.TestCase):
- def test_ssh_hostname_prefix_trim(self):
- conn = docker.transport.SSHHTTPAdapter(base_url="ssh://user@hostname:1234", shell_out=True)
+ @staticmethod
+ def test_ssh_hostname_prefix_trim():
+ conn = docker.transport.SSHHTTPAdapter(
+ base_url="ssh://user@hostname:1234", shell_out=True)
assert conn.ssh_host == "user@hostname:1234"
- def test_ssh_parse_url(self):
+ @staticmethod
+ def test_ssh_parse_url():
c = SSHSocket(host="user@hostname:1234")
assert c.host == "hostname"
assert c.port == "1234"
assert c.user == "user"
- def test_ssh_parse_hostname_only(self):
+ @staticmethod
+ def test_ssh_parse_hostname_only():
c = SSHSocket(host="hostname")
assert c.host == "hostname"
- assert c.port == None
- assert c.user == None
+ assert c.port is None
+ assert c.user is None
- def test_ssh_parse_user_and_hostname(self):
+ @staticmethod
+ def test_ssh_parse_user_and_hostname():
c = SSHSocket(host="user@hostname")
assert c.host == "hostname"
- assert c.port == None
+ assert c.port is None
assert c.user == "user"
- def test_ssh_parse_hostname_and_port(self):
+ @staticmethod
+ def test_ssh_parse_hostname_and_port():
c = SSHSocket(host="hostname:22")
assert c.host == "hostname"
assert c.port == "22"
- assert c.user == None
\ No newline at end of file
+ assert c.user is None
diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py
index a53151c..0d6ff22 100644
--- a/tests/unit/utils_test.py
+++ b/tests/unit/utils_test.py
@@ -541,6 +541,12 @@
assert internal_port == ["2000"]
assert external_port == [("2001:abcd:ef00::2", "1000")]
+ def test_split_port_with_ipv6_square_brackets_address(self):
+ internal_port, external_port = split_port(
+ "[2001:abcd:ef00::2]:1000:2000")
+ assert internal_port == ["2000"]
+ assert external_port == [("2001:abcd:ef00::2", "1000")]
+
def test_split_port_invalid(self):
with pytest.raises(ValueError):
split_port("0.0.0.0:1000:2000:tcp")
diff --git a/tox.ini b/tox.ini
index df797f4..d35d41a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py27, py35, py36, py37, flake8
+envlist = py36, py37, flake8
skipsdist=True
[testenv]