Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ae5cf78
Re-add docker.utils.types module for backwards compatibility
shin- Sep 9, 2016
9655847
Name is not required when creating a docker volume
rmb938 Sep 29, 2016
8634326
increase logs performance, do not copy bytes object
psviderski Dec 7, 2016
f7bc6e7
Scope is added in volume after docker 1.12
pacoxu Dec 6, 2016
e55f644
provide best practice for Image.save
realityone Jan 6, 2017
9efba40
case PyPI correctly
Jan 13, 2017
32bb58c
Update dockerVersions
shin- Jan 20, 2017
bc99217
Ignore socket files in utils.tar
shin- Jan 25, 2017
e82d7eb
Remove duplicate line in exec_run documentation
bayazee Jan 25, 2017
105ce5b
Fix ImageNotFound detection
shin- Jan 25, 2017
c727de2
Fix Swarm model init to correctly pass arguments through to init_swarm
shin- Jan 25, 2017
7fe6b4b
Add support for auto_remove in HostConfig
shin- Jan 26, 2017
123e430
Allow configuring API version for integration test with env var
shin- Jan 26, 2017
c40536c
Implement cachefrom
thomaschaaf Jan 9, 2017
1f7b7a2
Add cachefrom to build docstring
Jan 26, 2017
f704082
Remove integration test for APIClient.search method
shin- Jan 26, 2017
32bc17e
Add 'force' parameter in remove_volume
shin- Jan 25, 2017
db0df65
Add stop_timeout to create_container
shin- Jan 27, 2017
334f8f1
Add support for max_failure_ratio and monitor in UpdateConfig
shin- Jan 28, 2017
5e66559
Add support for force_update in TaskTemplate
shin- Jan 28, 2017
1aa1995
Change "Remote API" to "Engine API"
bfirsh Dec 14, 2016
5e06ca7
Optional name on VolumeCollection.create
shin- Jan 30, 2017
2d9f5bd
Improve robustness of remove_network integration test
shin- Jan 31, 2017
a154092
Add prune_containers method
shin- Jan 28, 2017
6d8dff3
Add prune_images method
shin- Jan 28, 2017
0984c7c
Add prune_volumes method
shin- Jan 31, 2017
6422133
Add prune_networks method
shin- Jan 31, 2017
11c2d29
Reference new methods in docs
shin- Jan 31, 2017
c262dfe
APIClient implementation of plugin methods
shin- Feb 1, 2017
b2aa220
Add plugin API implementation to DockerClient
shin- Feb 4, 2017
b4c94c0
Plugins API documentation
shin- Feb 4, 2017
bed7d65
Fix _post_json behavior
shin- Feb 4, 2017
06838ff
Fix volume path passed by run to create_container
bfirsh Feb 7, 2017
7532533
Fix passing volumes to run with no host path
bfirsh Feb 7, 2017
4e55628
Bump test engine version
shin- Feb 9, 2017
4b636c3
Add create_plugin implementation
shin- Feb 9, 2017
f0d8fe0
Implement secrets API
shin- Feb 10, 2017
18e61e8
Add support for secrets in ContainerSpec
shin- Feb 10, 2017
b54a325
Add tests for secret API implementation
shin- Feb 10, 2017
7382eb1
Add support for recursive wildcard pattern in .dockerignore
shin- Feb 15, 2017
161bfba
Add support for storage_opt in host_config
shin- Feb 16, 2017
40290fc
Add xfail mark to storageopt test
shin- Feb 16, 2017
b1d6e01
Rename cachefrom -> cache_from
shin- Feb 16, 2017
784172b
Add missing secrets doc
shin- Feb 17, 2017
5030a12
Bump version 2.1.0
shin- Feb 17, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ def imageNameBase = "dockerbuildbot/docker-py"
def imageNamePy2
def imageNamePy3
def images = [:]
def dockerVersions = ["1.12.0", "1.13.0-rc3"]

// Note: Swarm in dind seem notoriously flimsy with 1.12.1+, which is why we're
// sticking with 1.12.0 for the 1.12 series
def dockerVersions = ["1.12.0", "1.13.1"]

def buildImage = { name, buildargs, pyTag ->
img = docker.image(name)
Expand All @@ -31,10 +34,16 @@ def buildImages = { ->
}
}

def getAPIVersion = { engineVersion ->
def versionMap = ['1.12': '1.24', '1.13': '1.25']
return versionMap[engineVersion.substring(0, 4)]
}

def runTests = { Map settings ->
def dockerVersion = settings.get("dockerVersion", null)
def pythonVersion = settings.get("pythonVersion", null)
def testImage = settings.get("testImage", null)
def apiVersion = getAPIVersion(dockerVersion)

if (!testImage) {
throw new Exception("Need test image object, e.g.: `runTests(testImage: img)`")
Expand All @@ -50,15 +59,16 @@ def runTests = { Map settings ->
wrappedNode(label: "ubuntu && !zfs && amd64", cleanWorkspace: true) {
stage("test python=${pythonVersion} / docker=${dockerVersion}") {
checkout(scm)
def dindContainerName = "dpy-dind-\$BUILD_NUMBER-\$EXECUTOR_NUMBER"
def testContainerName = "dpy-tests-\$BUILD_NUMBER-\$EXECUTOR_NUMBER"
def dindContainerName = "dpy-dind-\$BUILD_NUMBER-\$EXECUTOR_NUMBER-${pythonVersion}-${dockerVersion}"
def testContainerName = "dpy-tests-\$BUILD_NUMBER-\$EXECUTOR_NUMBER-${pythonVersion}-${dockerVersion}"
try {
sh """docker run -d --name ${dindContainerName} -v /tmp --privileged \\
dockerswarm/dind:${dockerVersion} docker daemon -H tcp://0.0.0.0:2375
"""
sh """docker run \\
--name ${testContainerName} --volumes-from ${dindContainerName} \\
-e 'DOCKER_HOST=tcp://docker:2375' \\
-e 'DOCKER_TEST_API_VERSION=${apiVersion}' \\
--link=${dindContainerName}:docker \\
${testImage} \\
py.test -v -rxs tests/integration
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ include README.rst
include LICENSE
recursive-include tests *.py
recursive-include tests/unit/testdata *
recursive-include tests/integration/testdata *
16 changes: 8 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,27 @@ integration-test-py3: build-py3
.PHONY: integration-dind
integration-dind: build build-py3
docker rm -vf dpy-dind || :
docker run -d --name dpy-dind --privileged dockerswarm/dind:1.13.0-rc3 docker daemon\
docker run -d --name dpy-dind --privileged dockerswarm/dind:1.13.0 docker daemon\
-H tcp://0.0.0.0:2375
docker run --rm --env="DOCKER_HOST=tcp://docker:2375" --link=dpy-dind:docker docker-sdk-python\
py.test tests/integration
docker run --rm --env="DOCKER_HOST=tcp://docker:2375" --link=dpy-dind:docker docker-sdk-python3\
py.test tests/integration
docker run --rm --env="DOCKER_HOST=tcp://docker:2375" --env="DOCKER_TEST_API_VERSION=1.25"\
--link=dpy-dind:docker docker-sdk-python py.test tests/integration
docker run --rm --env="DOCKER_HOST=tcp://docker:2375" --env="DOCKER_TEST_API_VERSION=1.25"\
--link=dpy-dind:docker docker-sdk-python3 py.test tests/integration
docker rm -vf dpy-dind

.PHONY: integration-dind-ssl
integration-dind-ssl: build-dind-certs build build-py3
docker run -d --name dpy-dind-certs dpy-dind-certs
docker run -d --env="DOCKER_HOST=tcp://localhost:2375" --env="DOCKER_TLS_VERIFY=1"\
--env="DOCKER_CERT_PATH=/certs" --volumes-from dpy-dind-certs --name dpy-dind-ssl\
-v /tmp --privileged dockerswarm/dind:1.13.0-rc3 docker daemon --tlsverify\
-v /tmp --privileged dockerswarm/dind:1.13.0 docker daemon --tlsverify\
--tlscacert=/certs/ca.pem --tlscert=/certs/server-cert.pem\
--tlskey=/certs/server-key.pem -H tcp://0.0.0.0:2375
docker run --rm --volumes-from dpy-dind-ssl --env="DOCKER_HOST=tcp://docker:2375"\
--env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs"\
--env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --env="DOCKER_TEST_API_VERSION=1.25"\
--link=dpy-dind-ssl:docker docker-sdk-python py.test tests/integration
docker run --rm --volumes-from dpy-dind-ssl --env="DOCKER_HOST=tcp://docker:2375"\
--env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs"\
--env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --env="DOCKER_TEST_API_VERSION=1.25"\
--link=dpy-dind-ssl:docker docker-sdk-python3 py.test tests/integration
docker rm -vf dpy-dind-ssl dpy-dind-certs

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A Python library for the Docker Engine API. It lets you do anything the `docker`

## Installation

The latest stable version [is available on PyPi](https://pypi.python.org/pypi/docker/). Either add `docker` to your `requirements.txt` file or install with pip:
The latest stable version [is available on PyPI](https://pypi.python.org/pypi/docker/). Either add `docker` to your `requirements.txt` file or install with pip:

pip install docker

Expand Down
16 changes: 13 additions & 3 deletions docker/api/build.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import json
import logging
import os
import re
import json

from .. import auth
from .. import constants
from .. import errors
from .. import auth
from .. import utils


Expand All @@ -18,7 +18,7 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
custom_context=False, encoding=None, pull=False,
forcerm=False, dockerfile=None, container_limits=None,
decode=False, buildargs=None, gzip=False, shmsize=None,
labels=None):
labels=None, cache_from=None):
"""
Similar to the ``docker build`` command. Either ``path`` or ``fileobj``
needs to be set. ``path`` can be a local path (to a directory
Expand Down Expand Up @@ -92,6 +92,8 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
shmsize (int): Size of `/dev/shm` in bytes. The size must be
greater than 0. If omitted the system uses 64MB.
labels (dict): A dictionary of labels to set on the image.
cache_from (list): A list of images used for build cache
resolution.

Returns:
A generator for the build output.
Expand Down Expand Up @@ -188,6 +190,14 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
'labels was only introduced in API version 1.23'
)

if cache_from:
if utils.version_gte(self._version, '1.25'):
params.update({'cachefrom': json.dumps(cache_from)})
else:
raise errors.InvalidVersion(
'cache_from was only introduced in API version 1.25'
)

if context is not None:
headers = {'Content-Type': 'application/tar'}
if encoding:
Expand Down
16 changes: 12 additions & 4 deletions docker/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from .exec_api import ExecApiMixin
from .image import ImageApiMixin
from .network import NetworkApiMixin
from .plugin import PluginApiMixin
from .secret import SecretApiMixin
from .service import ServiceApiMixin
from .swarm import SwarmApiMixin
from .volume import VolumeApiMixin
Expand Down Expand Up @@ -46,11 +48,13 @@ class APIClient(
ExecApiMixin,
ImageApiMixin,
NetworkApiMixin,
PluginApiMixin,
SecretApiMixin,
ServiceApiMixin,
SwarmApiMixin,
VolumeApiMixin):
"""
A low-level client for the Docker Remote API.
A low-level client for the Docker Engine API.

Example:

Expand Down Expand Up @@ -225,10 +229,12 @@ def _post_json(self, url, data, **kwargs):
# Go <1.1 can't unserialize null to a string
# so we do this disgusting thing here.
data2 = {}
if data is not None:
if data is not None and isinstance(data, dict):
for k, v in six.iteritems(data):
if v is not None:
data2[k] = v
elif data is not None:
data2 = data

if 'headers' not in kwargs:
kwargs['headers'] = {}
Expand Down Expand Up @@ -302,11 +308,13 @@ def _multiplexed_buffer_helper(self, response):
"""A generator of multiplexed data blocks read from a buffered
response."""
buf = self._result(response, binary=True)
buf_length = len(buf)
walker = 0
while True:
if len(buf[walker:]) < 8:
if buf_length - walker < STREAM_HEADER_SIZE_BYTES:
break
_, length = struct.unpack_from('>BxxxL', buf[walker:])
header = buf[walker:walker + STREAM_HEADER_SIZE_BYTES]
_, length = struct.unpack_from('>BxxxL', header)
start = walker + STREAM_HEADER_SIZE_BYTES
end = start + length
walker = end
Expand Down
46 changes: 37 additions & 9 deletions docker/api/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def commit(self, container, repository=None, tag=None, message=None,
author (str): The name of the author
changes (str): Dockerfile instructions to apply while committing
conf (dict): The configuration for the container. See the
`Remote API documentation
`Engine API documentation
<https://docs.docker.com/reference/api/docker_remote_api/>`_
for full details.

Expand Down Expand Up @@ -238,7 +238,7 @@ def create_container(self, image, command=None, hostname=None, user=None,
memswap_limit=None, cpuset=None, host_config=None,
mac_address=None, labels=None, volume_driver=None,
stop_signal=None, networking_config=None,
healthcheck=None):
healthcheck=None, stop_timeout=None):
"""
Creates a container. Parameters are similar to those for the ``docker
run`` command except it doesn't support the attach options (``-a``).
Expand Down Expand Up @@ -313,9 +313,10 @@ def create_container(self, image, command=None, hostname=None, user=None,

**Using volumes**

Volume declaration is done in two parts. Provide a list of mountpoints
to the with the ``volumes`` parameter, and declare mappings in the
``host_config`` section.
Volume declaration is done in two parts. Provide a list of
paths to use as mountpoints inside the container with the
``volumes`` parameter, and declare mappings from paths on the host
in the ``host_config`` section.

.. code-block:: python

Expand Down Expand Up @@ -392,7 +393,8 @@ def create_container(self, image, command=None, hostname=None, user=None,
version 1.10. Use ``host_config`` instead.
dns_opt (:py:class:`list`): Additional options to be added to the
container's ``resolv.conf`` file
volumes (str or list):
volumes (str or list): List of paths inside the container to use
as volumes.
volumes_from (:py:class:`list`): List of container names or Ids to
get volumes from.
network_disabled (bool): Disable networking
Expand All @@ -411,6 +413,8 @@ def create_container(self, image, command=None, hostname=None, user=None,
volume_driver (str): The name of a volume driver/plugin.
stop_signal (str): The stop signal to use to stop the container
(e.g. ``SIGINT``).
stop_timeout (int): Timeout to stop the container, in seconds.
Default: 10
networking_config (dict): A networking configuration generated
by :py:meth:`create_networking_config`.

Expand All @@ -437,6 +441,7 @@ def create_container(self, image, command=None, hostname=None, user=None,
network_disabled, entrypoint, cpu_shares, working_dir, domainname,
memswap_limit, cpuset, host_config, mac_address, labels,
volume_driver, stop_signal, networking_config, healthcheck,
stop_timeout
)
return self.create_container_from_config(config, name)

Expand All @@ -457,6 +462,8 @@ def create_host_config(self, *args, **kwargs):
:py:meth:`create_container`.

Args:
auto_remove (bool): enable auto-removal of the container on daemon
side when the container's process exits.
binds (dict): Volumes to bind. See :py:meth:`create_container`
for more information.
blkio_weight_device: Block IO weight (relative device weight) in
Expand Down Expand Up @@ -542,6 +549,8 @@ def create_host_config(self, *args, **kwargs):
security_opt (:py:class:`list`): A list of string values to
customize labels for MLS systems, such as SELinux.
shm_size (str or int): Size of /dev/shm (e.g. ``1G``).
storage_opt (dict): Storage driver options per container as a
key-value mapping.
sysctls (dict): Kernel parameters to set in the container.
tmpfs (dict): Temporary filesystems to mount, as a dictionary
mapping a path inside the container to options for that path.
Expand Down Expand Up @@ -906,16 +915,35 @@ def put_archive(self, container, path, data):
Raises:
:py:class:`docker.errors.APIError`
If the server returns an error.

Raises:
:py:class:`~docker.errors.APIError` If an error occurs.
"""
params = {'path': path}
url = self._url('/containers/{0}/archive', container)
res = self._put(url, params=params, data=data)
self._raise_for_status(res)
return res.status_code == 200

@utils.minimum_version('1.25')
def prune_containers(self, filters=None):
"""
Delete stopped containers

Args:
filters (dict): Filters to process on the prune list.

Returns:
(dict): A dict containing a list of deleted container IDs and
the amount of disk space reclaimed in bytes.

Raises:
:py:class:`docker.errors.APIError`
If the server returns an error.
"""
params = {}
if filters:
params['filters'] = utils.convert_filters(filters)
url = self._url('/containers/prune')
return self._result(self._post(url, params=params), True)

@utils.check_resource
def remove_container(self, container, v=False, link=False, force=False):
"""
Expand Down
25 changes: 25 additions & 0 deletions docker/api/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,31 @@ def load_image(self, data):
res = self._post(self._url("/images/load"), data=data)
self._raise_for_status(res)

@utils.minimum_version('1.25')
def prune_images(self, filters=None):
"""
Delete unused images

Args:
filters (dict): Filters to process on the prune list.
Available filters:
- dangling (bool): When set to true (or 1), prune only
unused and untagged images.

Returns:
(dict): A dict containing a list of deleted image IDs and
the amount of disk space reclaimed in bytes.

Raises:
:py:class:`docker.errors.APIError`
If the server returns an error.
"""
url = self._url("/images/prune")
params = {}
if filters is not None:
params['filters'] = utils.convert_filters(filters)
return self._result(self._post(url, params=params), True)

def pull(self, repository, tag=None, stream=False,
insecure_registry=False, auth_config=None, decode=False):
"""
Expand Down
22 changes: 22 additions & 0 deletions docker/api/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,28 @@ def create_network(self, name, driver=None, options=None, ipam=None,
res = self._post_json(url, data=data)
return self._result(res, json=True)

@minimum_version('1.25')
def prune_networks(self, filters=None):
"""
Delete unused networks

Args:
filters (dict): Filters to process on the prune list.

Returns:
(dict): A dict containing a list of deleted network names and
the amount of disk space reclaimed in bytes.

Raises:
:py:class:`docker.errors.APIError`
If the server returns an error.
"""
params = {}
if filters:
params['filters'] = utils.convert_filters(filters)
url = self._url('/networks/prune')
return self._result(self._post(url, params=params), True)

@minimum_version('1.21')
def remove_network(self, net_id):
"""
Expand Down
Loading