Skip to content

Commit

Permalink
Merge pull request #1463 from docker/2.1.0-release
Browse files Browse the repository at this point in the history
2.1.0 release
  • Loading branch information
shin- authored Feb 17, 2017
2 parents 6661b7f + 5030a12 commit 5742774
Show file tree
Hide file tree
Showing 74 changed files with 2,057 additions and 328 deletions.
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

0 comments on commit 5742774

Please sign in to comment.