mirror of https://github.com/docker/docker-py.git
Fixed conflict
This commit is contained in:
commit
1803c4b83d
|
@ -8,3 +8,10 @@ dist
|
||||||
.tox
|
.tox
|
||||||
.coverage
|
.coverage
|
||||||
html/*
|
html/*
|
||||||
|
|
||||||
|
# Compiled Documentation
|
||||||
|
site/
|
||||||
|
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
.idea/
|
||||||
|
|
|
@ -2,4 +2,5 @@ include test-requirements.txt
|
||||||
include requirements.txt
|
include requirements.txt
|
||||||
include requirements3.txt
|
include requirements3.txt
|
||||||
include README.md
|
include README.md
|
||||||
include LICENSE
|
include LICENSE
|
||||||
|
recursive-include tests *.py
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
.PHONY: all build test integration-test unit-test
|
||||||
|
|
||||||
|
all: test
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker build -t docker-py .
|
||||||
|
|
||||||
|
test: unit-test integration-test
|
||||||
|
|
||||||
|
unit-test: build
|
||||||
|
docker run docker-py python tests/test.py
|
||||||
|
|
||||||
|
integration-test: build
|
||||||
|
docker run -v /var/run/docker.sock:/var/run/docker.sock docker-py python tests/integration_test.py
|
||||||
|
|
436
README.md
436
README.md
|
@ -6,440 +6,18 @@ docker-py
|
||||||
An API client for docker written in Python
|
An API client for docker written in Python
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
============
|
------------
|
||||||
|
|
||||||
Our latest stable is always available on PyPi.
|
Our latest stable is always available on PyPi.
|
||||||
|
|
||||||
pip install docker-py
|
pip install docker-py
|
||||||
|
|
||||||
API
|
Documentation
|
||||||
===
|
------------
|
||||||
|
|
||||||
To instantiate a `Client` class that will allow you to communicate with
|
Full documentation is available in the `/docs/` directory.
|
||||||
a Docker daemon, simply do:
|
|
||||||
|
|
||||||
```python
|
|
||||||
c = docker.Client(base_url='unix://var/run/docker.sock',
|
|
||||||
version='1.12',
|
|
||||||
timeout=10)
|
|
||||||
```
|
|
||||||
|
|
||||||
`base_url` refers to the protocol+hostname+port where the docker server
|
License
|
||||||
is hosted. `version` is the version of the API the client will use and
|
-------
|
||||||
`timeout` specifies the HTTP request timeout, in seconds.
|
Docker is licensed under the Apache License, Version 2.0. See LICENSE for full license text
|
||||||
|
|
||||||
```python
|
|
||||||
c.build(path=None, tag=None, quiet=False, fileobj=None, nocache=False,
|
|
||||||
rm=False, stream=False, timeout=None,
|
|
||||||
custom_context=False, encoding=None):
|
|
||||||
```
|
|
||||||
|
|
||||||
Similar to the `docker build` command. Either `path` or `fileobj` needs
|
|
||||||
to be set. `path` can be a local path (to a directory containing a
|
|
||||||
Dockerfile) or a remote URL. `fileobj` must be a readable file-like
|
|
||||||
object to a Dockerfile.
|
|
||||||
|
|
||||||
If you have a tar file for the docker build context (including a dockerfile)
|
|
||||||
already, pass a readable file-like object to `fileobj` and also pass
|
|
||||||
`custom_context=True`. If the stream is compressed also, set `encoding` to
|
|
||||||
the correct value (e.g `gzip`).
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.commit(container, repository=None, tag=None, message=None, author=None,
|
|
||||||
conf=None)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker commit` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.containers(quiet=False, all=False, trunc=True, latest=False, since=None,
|
|
||||||
before=None, limit=-1)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker ps` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.copy(container, resource)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker cp` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.create_container(image, command=None, hostname=None, user=None,
|
|
||||||
detach=False, stdin_open=False, tty=False, mem_limit=0,
|
|
||||||
ports=None, environment=None, dns=None, volumes=None,
|
|
||||||
volumes_from=None, network_disabled=False, name=None,
|
|
||||||
entrypoint=None, cpu_shares=None, working_dir=None,
|
|
||||||
memswap_limit=0)
|
|
||||||
```
|
|
||||||
|
|
||||||
Creates a container that can then be `start`ed. Parameters are similar
|
|
||||||
to those for the `docker run` command except it doesn't support the
|
|
||||||
attach options (`-a`). See "Port bindings" and "Using volumes" below for
|
|
||||||
more information on how to create port bindings and volume mappings.
|
|
||||||
|
|
||||||
`command` is the command to be run in the container. String or list types are
|
|
||||||
accepted.
|
|
||||||
|
|
||||||
The `environment` variable accepts a dictionary or a list of strings
|
|
||||||
in the following format `["PASSWORD=xxx"]` or `{"PASSWORD": "xxx"}`.
|
|
||||||
|
|
||||||
The `mem_limit` variable accepts float values (which represent the memory limit
|
|
||||||
of the created container in bytes) or a string with a units identification char
|
|
||||||
('100000b', 1000k', 128m', '1g'). If a string is specified without a units
|
|
||||||
character, bytes are assumed as an intended unit.
|
|
||||||
|
|
||||||
`volumes_from` and `dns` arguments raise TypeError exception if they are used
|
|
||||||
against v1.10 of docker remote API. Those arguments should be passed to
|
|
||||||
`start()` instead.
|
|
||||||
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.diff(container)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker diff` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.export(container)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker export` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.history(image)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker history` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.images(name=None, quiet=False, all=False, viz=False)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker images` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.import_image(src, data=None, repository=None, tag=None)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker import` command. If `src` is a string or
|
|
||||||
unicode string, it will be treated as a URL to fetch the image from. To
|
|
||||||
import an image from the local machine, `src` needs to be a file-like
|
|
||||||
object or bytes collection. To import from a tarball use your absolute
|
|
||||||
path to your tarball. To load arbitrary data as tarball use whatever
|
|
||||||
you want as src and your tarball content in data.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.info()
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker info` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.insert(image, url, path)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker insert` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.inspect_container(container)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker inspect` command, but only for containers.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.inspect_image(image_id)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker inspect` command, but only for images.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.kill(container, signal=None)
|
|
||||||
```
|
|
||||||
|
|
||||||
Kill a container. Similar to the `docker kill` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.login(username, password=None, email=None, registry=None)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker login` command (but non-interactive, obviously).
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.logs(container, stdout=True, stderr=True, stream=False, timestamps=False)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker logs` command. The `stream` parameter makes the
|
|
||||||
`logs` function return a blocking generator you can iterate over to
|
|
||||||
retrieve log output as it happens.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.attach(container, stdout=True, stderr=True, stream=False, logs=False)
|
|
||||||
```
|
|
||||||
|
|
||||||
The `logs` function is a wrapper around this one, which you can use
|
|
||||||
instead if you want to fetch/stream container output without first
|
|
||||||
retrieving the entire backlog.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.ping()
|
|
||||||
```
|
|
||||||
|
|
||||||
Hits the /_ping endpoint of the remote API and returns the result.
|
|
||||||
An exception will be raised if the endpoint isn't responding.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.port(container, private_port)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker port` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.pull(repository, tag=None, stream=False)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker pull` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.push(repository, tag=None, stream=False)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker push` command.
|
|
||||||
|
|
||||||
````python
|
|
||||||
c.remove_container(container, v=False, link=False)
|
|
||||||
```
|
|
||||||
|
|
||||||
Remove a container. Similar to the `docker rm` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.remove_image(image)
|
|
||||||
```
|
|
||||||
|
|
||||||
Remove an image. Similar to the `docker rmi` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.restart(container, timeout=10)
|
|
||||||
```
|
|
||||||
Restart a container. Similar to the `docker restart` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.search(term)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker search` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.start(container, binds=None, port_bindings=None, lxc_conf=None,
|
|
||||||
publish_all_ports=False, links=None, privileged=False,
|
|
||||||
dns=None, dns_search=None, volumes_from=None, network_mode=None,
|
|
||||||
restart_policy=None, cap_add=None, cap_drop=None)
|
|
||||||
```
|
|
||||||
|
|
||||||
Similar to the `docker start` command, but doesn't support attach
|
|
||||||
options. Use `docker logs` to recover `stdout`/`stderr`.
|
|
||||||
|
|
||||||
`binds` allows to bind a directory in the host to the container. See
|
|
||||||
"Using volumes" below for more information. `port_bindings` exposes
|
|
||||||
container ports to the host. See "Port bindings" below for more
|
|
||||||
information. `lxc_conf` allows to pass LXC configuration options using a
|
|
||||||
dictionary. `privileged` starts the container in privileged mode.
|
|
||||||
|
|
||||||
[Links](http://docs.docker.io/en/latest/use/working_with_links_names/)
|
|
||||||
can be specified with the `links` argument. They can either be
|
|
||||||
specified as a dictionary mapping name to alias or as a list of
|
|
||||||
`(name, alias)` tuples.
|
|
||||||
|
|
||||||
`dns` and `volumes_from` are only available if they are used with version v1.10
|
|
||||||
of docker remote API. Otherwise they are ignored.
|
|
||||||
|
|
||||||
`network_mode` is available since v1.11 and sets the Network mode for the
|
|
||||||
container ('bridge': creates a new network stack for the container on the
|
|
||||||
docker bridge, 'none': no networking for this container, 'container:[name|id]':
|
|
||||||
reuses another container network stack), 'host': use the host network stack
|
|
||||||
inside the container.
|
|
||||||
|
|
||||||
`restart_policy` is available since v1.2.0 and sets the RestartPolicy for how a container should or should not be
|
|
||||||
restarted on exit. By default the policy is set to no meaning do not restart the container when it exits.
|
|
||||||
The user may specify the restart policy as a dictionary for example:
|
|
||||||
for example:
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"MaximumRetryCount": 0,
|
|
||||||
"Name": "always"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
for always restarting the container on exit or can specify to restart the container to restart on failure and can limit
|
|
||||||
number of restarts.
|
|
||||||
for example:
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"MaximumRetryCount": 5,
|
|
||||||
"Name": "on-failure"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`cap_add` and `cap_drop` are available since v1.2.0 and can be used to add or drop certain capabilities.
|
|
||||||
The user may specify the capabilities as an array for example:
|
|
||||||
```
|
|
||||||
[
|
|
||||||
"SYS_ADMIN",
|
|
||||||
"MKNOD"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.stop(container, timeout=10)
|
|
||||||
```
|
|
||||||
|
|
||||||
Stops a container. Similar to the `docker stop` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.tag(image, repository, tag=None, force=False)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker tag` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.top(container)
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker top` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.version()
|
|
||||||
```
|
|
||||||
|
|
||||||
Identical to the `docker version` command.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.wait(container)
|
|
||||||
```
|
|
||||||
|
|
||||||
Wait for a container and return its exit code. Similar to the `docker
|
|
||||||
wait` command.
|
|
||||||
|
|
||||||
|
|
||||||
Port bindings
|
|
||||||
=============
|
|
||||||
|
|
||||||
Port bindings is done in two parts. Firstly, by providing a list of ports to
|
|
||||||
open inside the container in the `Client.create_container` method.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.create_container('busybox', 'ls', ports=[1111, 2222])
|
|
||||||
```
|
|
||||||
|
|
||||||
Bindings are then declared in the `Client.start` method.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.start(container_id, port_bindings={1111: 4567, 2222: None})
|
|
||||||
```
|
|
||||||
|
|
||||||
You can limit the host address on which the port will be exposed like such:
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.start(container_id, port_bindings={1111: ('127.0.0.1', 4567)})
|
|
||||||
```
|
|
||||||
|
|
||||||
Or without host port assignment:
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.start(container_id, port_bindings={1111: ('127.0.0.1',)})
|
|
||||||
```
|
|
||||||
|
|
||||||
If you wish to use UDP instead of TCP (default), you need to declare it
|
|
||||||
like such in both the `create_container()` and `start()` calls:
|
|
||||||
|
|
||||||
```python
|
|
||||||
container_id = c.create_container('busybox', 'ls', ports=[(1111, 'udp'), 2222])
|
|
||||||
c.start(container_id, port_bindings={'1111/udp': 4567, 2222: None})
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Using volumes
|
|
||||||
=============
|
|
||||||
|
|
||||||
Similarly, volume declaration is done in two parts. First, you have to provide
|
|
||||||
a list of mountpoints to the `Client.create_container` method.
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.create_container('busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2'])
|
|
||||||
```
|
|
||||||
|
|
||||||
Volume mappings are then declared inside the `Client.start` method like this:
|
|
||||||
|
|
||||||
```python
|
|
||||||
c.start(container_id, binds={
|
|
||||||
'/home/user1/':
|
|
||||||
{
|
|
||||||
'bind': '/mnt/vol2',
|
|
||||||
'ro': False
|
|
||||||
},
|
|
||||||
'/var/www':
|
|
||||||
{
|
|
||||||
'bind': '/mnt/vol1',
|
|
||||||
'ro': True
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Connection to daemon using HTTPS
|
|
||||||
================================
|
|
||||||
|
|
||||||
*These instructions are docker-py specific. Please refer to
|
|
||||||
http://docs.docker.com/articles/https/ first.*
|
|
||||||
|
|
||||||
* Authenticate server based on public/default CA pool
|
|
||||||
|
|
||||||
```python
|
|
||||||
client = docker.Client(base_url='<https_url>', tls=True)
|
|
||||||
```
|
|
||||||
|
|
||||||
Equivalent CLI options: `docker --tls ...`
|
|
||||||
|
|
||||||
If you want to use TLS but don't want to verify the server certificate
|
|
||||||
(for example when testing with a self-signed certificate):
|
|
||||||
|
|
||||||
```python
|
|
||||||
tls_config = docker.tls.TLSConfig(verify=False)
|
|
||||||
client = docker.Client(base_url='<https_url>', tls=tls_config)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Authenticate server based on given CA
|
|
||||||
|
|
||||||
```python
|
|
||||||
tls_config = docker.tls.TLSConfig(ca_cert='/path/to/ca.pem')
|
|
||||||
client = docker.Client(base_url='<https_url>', tls=tls_config)
|
|
||||||
```
|
|
||||||
|
|
||||||
Equivalent CLI options: `docker --tlsverify --tlscacert /path/to/ca.pem ...`
|
|
||||||
|
|
||||||
* Authenticate with client certificate, do not authenticate server
|
|
||||||
based on given CA
|
|
||||||
|
|
||||||
```python
|
|
||||||
tls_config = docker.tls.TLSConfig(
|
|
||||||
client_cert=('/path/to/client-cert.pem', '/path/to/client-key.pem')
|
|
||||||
)
|
|
||||||
client = docker.Client(base_url='<https_url>', tls=tls_config)
|
|
||||||
```
|
|
||||||
|
|
||||||
Equivalent CLI options:
|
|
||||||
`docker --tls --tlscert /path/to/client-cert.pem
|
|
||||||
--tlskey /path/to/client-key.pem ...`
|
|
||||||
|
|
||||||
* Authenticate with client certificate, authenticate server based on given CA
|
|
||||||
|
|
||||||
```python
|
|
||||||
tls_config = docker.tls.TLSConfig(
|
|
||||||
client_cert=('/path/to/client-cert.pem', '/path/to/client-key.pem'),
|
|
||||||
ca_cert='/path/to/ca.pem'
|
|
||||||
)
|
|
||||||
client = docker.Client(base_url='<https_url>', tls=tls_config)
|
|
||||||
```
|
|
||||||
|
|
||||||
Equivalent CLI options:
|
|
||||||
`docker --tlsverify --tlscert /path/to/client-cert.pem
|
|
||||||
--tlskey /path/to/client-key.pem --tlscacert /path/to/ca.pem ...`
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ from .tls import TLSConfig
|
||||||
if not six.PY3:
|
if not six.PY3:
|
||||||
import websocket
|
import websocket
|
||||||
|
|
||||||
DEFAULT_DOCKER_API_VERSION = '1.12'
|
DEFAULT_DOCKER_API_VERSION = '1.15'
|
||||||
DEFAULT_TIMEOUT_SECONDS = 60
|
DEFAULT_TIMEOUT_SECONDS = 60
|
||||||
STREAM_HEADER_SIZE_BYTES = 8
|
STREAM_HEADER_SIZE_BYTES = 8
|
||||||
|
|
||||||
|
@ -103,8 +103,8 @@ class Client(requests.Session):
|
||||||
mem_limit=0, ports=None, environment=None, dns=None,
|
mem_limit=0, ports=None, environment=None, dns=None,
|
||||||
volumes=None, volumes_from=None,
|
volumes=None, volumes_from=None,
|
||||||
network_disabled=False, entrypoint=None,
|
network_disabled=False, entrypoint=None,
|
||||||
cpu_shares=None, working_dir=None, domainname=None,
|
cpu_shares=None, working_dir=None,
|
||||||
memswap_limit=0):
|
domainname=None, memswap_limit=0, cpuset=None):
|
||||||
if isinstance(command, six.string_types):
|
if isinstance(command, six.string_types):
|
||||||
command = shlex.split(str(command))
|
command = shlex.split(str(command))
|
||||||
if isinstance(environment, dict):
|
if isinstance(environment, dict):
|
||||||
|
@ -217,6 +217,7 @@ class Client(requests.Session):
|
||||||
'NetworkDisabled': network_disabled,
|
'NetworkDisabled': network_disabled,
|
||||||
'Entrypoint': entrypoint,
|
'Entrypoint': entrypoint,
|
||||||
'CpuShares': cpu_shares,
|
'CpuShares': cpu_shares,
|
||||||
|
'Cpuset': cpuset,
|
||||||
'WorkingDir': working_dir,
|
'WorkingDir': working_dir,
|
||||||
'MemorySwap': memswap_limit
|
'MemorySwap': memswap_limit
|
||||||
}
|
}
|
||||||
|
@ -519,8 +520,8 @@ class Client(requests.Session):
|
||||||
mem_limit=0, ports=None, environment=None, dns=None,
|
mem_limit=0, ports=None, environment=None, dns=None,
|
||||||
volumes=None, volumes_from=None,
|
volumes=None, volumes_from=None,
|
||||||
network_disabled=False, name=None, entrypoint=None,
|
network_disabled=False, name=None, entrypoint=None,
|
||||||
cpu_shares=None, working_dir=None, domainname=None,
|
cpu_shares=None, working_dir=None,
|
||||||
memswap_limit=0):
|
domainname=None, memswap_limit=0, cpuset=None):
|
||||||
|
|
||||||
if isinstance(volumes, six.string_types):
|
if isinstance(volumes, six.string_types):
|
||||||
volumes = [volumes, ]
|
volumes = [volumes, ]
|
||||||
|
@ -528,7 +529,8 @@ class Client(requests.Session):
|
||||||
config = self._container_config(
|
config = self._container_config(
|
||||||
image, command, hostname, user, detach, stdin_open, tty, mem_limit,
|
image, command, hostname, user, detach, stdin_open, tty, mem_limit,
|
||||||
ports, environment, dns, volumes, volumes_from, network_disabled,
|
ports, environment, dns, volumes, volumes_from, network_disabled,
|
||||||
entrypoint, cpu_shares, working_dir, domainname, memswap_limit
|
entrypoint, cpu_shares, working_dir, domainname,
|
||||||
|
memswap_limit, cpuset
|
||||||
)
|
)
|
||||||
return self.create_container_from_config(config, name)
|
return self.create_container_from_config(config, name)
|
||||||
|
|
||||||
|
@ -549,6 +551,48 @@ class Client(requests.Session):
|
||||||
def events(self):
|
def events(self):
|
||||||
return self._stream_helper(self.get(self._url('/events'), stream=True))
|
return self._stream_helper(self.get(self._url('/events'), stream=True))
|
||||||
|
|
||||||
|
def execute(self, container, cmd, detach=False, stdout=True, stderr=True,
|
||||||
|
stream=False, tty=False):
|
||||||
|
if utils.compare_version('1.15', self._version) < 0:
|
||||||
|
raise Exception('Exec is not supported in API < 1.15!')
|
||||||
|
if isinstance(container, dict):
|
||||||
|
container = container.get('Id')
|
||||||
|
if isinstance(cmd, six.string_types):
|
||||||
|
cmd = shlex.split(str(cmd))
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'Container': container,
|
||||||
|
'User': '',
|
||||||
|
'Privileged': False,
|
||||||
|
'Tty': tty,
|
||||||
|
'AttachStdin': False,
|
||||||
|
'AttachStdout': stdout,
|
||||||
|
'AttachStderr': stderr,
|
||||||
|
'Detach': detach,
|
||||||
|
'Cmd': cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
# create the command
|
||||||
|
url = self._url('/containers/{0}/exec'.format(container))
|
||||||
|
res = self._post_json(url, data=data)
|
||||||
|
self._raise_for_status(res)
|
||||||
|
|
||||||
|
# start the command
|
||||||
|
cmd_id = res.json().get('Id')
|
||||||
|
res = self._post_json(self._url('/exec/{0}/start'.format(cmd_id)),
|
||||||
|
data=data, stream=stream)
|
||||||
|
self._raise_for_status(res)
|
||||||
|
if stream:
|
||||||
|
return self._multiplexed_socket_stream_helper(res)
|
||||||
|
elif six.PY3:
|
||||||
|
return bytes().join(
|
||||||
|
[x for x in self._multiplexed_buffer_helper(res)]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return str().join(
|
||||||
|
[x for x in self._multiplexed_buffer_helper(res)]
|
||||||
|
)
|
||||||
|
|
||||||
def export(self, container):
|
def export(self, container):
|
||||||
if isinstance(container, dict):
|
if isinstance(container, dict):
|
||||||
container = container.get('Id')
|
container = container.get('Id')
|
||||||
|
@ -714,6 +758,13 @@ class Client(requests.Session):
|
||||||
logs=True
|
logs=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def pause(self, container):
|
||||||
|
if isinstance(container, dict):
|
||||||
|
container = container.get('Id')
|
||||||
|
url = self._url('/containers/{0}/pause'.format(container))
|
||||||
|
res = self._post(url)
|
||||||
|
self._raise_for_status(res)
|
||||||
|
|
||||||
def ping(self):
|
def ping(self):
|
||||||
return self._result(self._get(self._url('/_ping')))
|
return self._result(self._get(self._url('/_ping')))
|
||||||
|
|
||||||
|
@ -832,7 +883,7 @@ class Client(requests.Session):
|
||||||
def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
|
def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
|
||||||
publish_all_ports=False, links=None, privileged=False,
|
publish_all_ports=False, links=None, privileged=False,
|
||||||
dns=None, dns_search=None, volumes_from=None, network_mode=None,
|
dns=None, dns_search=None, volumes_from=None, network_mode=None,
|
||||||
restart_policy=None, cap_add=None, cap_drop=None):
|
restart_policy=None, cap_add=None, cap_drop=None, devices=None):
|
||||||
if isinstance(container, dict):
|
if isinstance(container, dict):
|
||||||
container = container.get('Id')
|
container = container.get('Id')
|
||||||
|
|
||||||
|
@ -900,6 +951,9 @@ class Client(requests.Session):
|
||||||
if cap_drop:
|
if cap_drop:
|
||||||
start_config['CapDrop'] = cap_drop
|
start_config['CapDrop'] = cap_drop
|
||||||
|
|
||||||
|
if devices:
|
||||||
|
start_config['Devices'] = utils.parse_devices(devices)
|
||||||
|
|
||||||
url = self._url("/containers/{0}/start".format(container))
|
url = self._url("/containers/{0}/start".format(container))
|
||||||
res = self._post_json(url, data=start_config)
|
res = self._post_json(url, data=start_config)
|
||||||
self._raise_for_status(res)
|
self._raise_for_status(res)
|
||||||
|
@ -940,6 +994,13 @@ class Client(requests.Session):
|
||||||
def version(self):
|
def version(self):
|
||||||
return self._result(self._get(self._url("/version")), True)
|
return self._result(self._get(self._url("/version")), True)
|
||||||
|
|
||||||
|
def unpause(self, container):
|
||||||
|
if isinstance(container, dict):
|
||||||
|
container = container.get('Id')
|
||||||
|
url = self._url('/containers/{0}/unpause'.format(container))
|
||||||
|
res = self._post(url)
|
||||||
|
self._raise_for_status(res)
|
||||||
|
|
||||||
def wait(self, container):
|
def wait(self, container):
|
||||||
if isinstance(container, dict):
|
if isinstance(container, dict):
|
||||||
container = container.get('Id')
|
container = container.get('Id')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from .utils import (
|
from .utils import (
|
||||||
compare_version, convert_port_bindings, convert_volume_binds,
|
compare_version, convert_port_bindings, convert_volume_binds,
|
||||||
mkbuildcontext, ping, tar, parse_repository_tag, parse_host
|
mkbuildcontext, ping, tar, parse_repository_tag, parse_host, kwargs_from_env
|
||||||
) # flake8: noqa
|
) # flake8: noqa
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
|
import os.path
|
||||||
import tarfile
|
import tarfile
|
||||||
import tempfile
|
import tempfile
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
|
@ -23,6 +24,7 @@ import requests
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from .. import errors
|
from .. import errors
|
||||||
|
from .. import tls
|
||||||
|
|
||||||
DEFAULT_HTTP_HOST = "127.0.0.1"
|
DEFAULT_HTTP_HOST = "127.0.0.1"
|
||||||
DEFAULT_UNIX_SOCKET = "http+unix://var/run/docker.sock"
|
DEFAULT_UNIX_SOCKET = "http+unix://var/run/docker.sock"
|
||||||
|
@ -72,6 +74,10 @@ def tar(path, exclude=None):
|
||||||
for name in fnames:
|
for name in fnames:
|
||||||
arcname = os.path.join(relpath, name)
|
arcname = os.path.join(relpath, name)
|
||||||
t.add(os.path.join(path, arcname), arcname=arcname)
|
t.add(os.path.join(path, arcname), arcname=arcname)
|
||||||
|
for name in dirnames:
|
||||||
|
arcname = os.path.join(relpath, name)
|
||||||
|
t.add(os.path.join(path, arcname),
|
||||||
|
arcname=arcname, recursive=False)
|
||||||
t.close()
|
t.close()
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
return f
|
return f
|
||||||
|
@ -233,3 +239,43 @@ def parse_host(addr):
|
||||||
if proto == "http+unix":
|
if proto == "http+unix":
|
||||||
return "%s://%s" % (proto, host)
|
return "%s://%s" % (proto, host)
|
||||||
return "%s://%s:%d" % (proto, host, port)
|
return "%s://%s:%d" % (proto, host, port)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_devices(devices):
|
||||||
|
device_list = []
|
||||||
|
for device in devices:
|
||||||
|
device_mapping = device.split(",")
|
||||||
|
if device_mapping:
|
||||||
|
path_on_host = device_mapping[0]
|
||||||
|
if len(device_mapping) > 1:
|
||||||
|
path_in_container = device_mapping[1]
|
||||||
|
else:
|
||||||
|
path_in_container = path_on_host
|
||||||
|
if len(device_mapping) > 2:
|
||||||
|
permissions = device_mapping[2]
|
||||||
|
else:
|
||||||
|
permissions = 'rwm'
|
||||||
|
device_list.append({"PathOnHost": path_on_host,
|
||||||
|
"PathInContainer": path_in_container,
|
||||||
|
"CgroupPermissions": permissions})
|
||||||
|
return device_list
|
||||||
|
|
||||||
|
|
||||||
|
def kwargs_from_env(ssl_version=None, assert_hostname=None):
|
||||||
|
host = os.environ.get('DOCKER_HOST')
|
||||||
|
cert_path = os.environ.get('DOCKER_CERT_PATH')
|
||||||
|
tls_verify = os.environ.get('DOCKER_TLS_VERIFY')
|
||||||
|
|
||||||
|
params = {}
|
||||||
|
if host:
|
||||||
|
params['base_url'] = (host.replace('tcp://', 'https://')
|
||||||
|
if tls_verify else host)
|
||||||
|
if tls_verify and cert_path:
|
||||||
|
params['tls'] = tls.TLSConfig(
|
||||||
|
client_cert=(os.path.join(cert_path, 'cert.pem'),
|
||||||
|
os.path.join(cert_path, 'key.pem')),
|
||||||
|
ca_cert=os.path.join(cert_path, 'ca.pem'),
|
||||||
|
verify=True,
|
||||||
|
ssl_version=ssl_version,
|
||||||
|
assert_hostname=assert_hostname)
|
||||||
|
return params
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
mkdocs==0.9
|
|
@ -0,0 +1,736 @@
|
||||||
|
# Client API
|
||||||
|
|
||||||
|
To instantiate a `Client` class that will allow you to communicate with a
|
||||||
|
Docker daemon, simply do:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from docker import Client
|
||||||
|
c = Client(base_url='unix://var/run/docker.sock')
|
||||||
|
```
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* base_url (str): Refers to the protocol+hostname+port where the Docker server
|
||||||
|
is hosted.
|
||||||
|
* version (str): The version of the API the client will use
|
||||||
|
* timeout (int): The HTTP request timeout, in seconds.
|
||||||
|
* tls (bool or [TLSConfig](tls.md#TLSConfig)): Equivalent CLI options:
|
||||||
|
`docker --tls ...`
|
||||||
|
|
||||||
|
|
||||||
|
## attach
|
||||||
|
|
||||||
|
The `.logs()` function is a wrapper around this method, which you can use
|
||||||
|
instead if you want to fetch/stream container output without first retrieving
|
||||||
|
the entire backlog.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to attach to
|
||||||
|
* stdout (bool): Get STDOUT
|
||||||
|
* stderr (bool): Get STDERR
|
||||||
|
* stream (bool): Return an interator
|
||||||
|
* logs (bool): Get all previous output
|
||||||
|
|
||||||
|
**Returns** (generator or str): The logs or output for the image
|
||||||
|
|
||||||
|
## build
|
||||||
|
|
||||||
|
Similar to the `docker build` command. Either `path` or `fileobj` needs to be
|
||||||
|
set. `path` can be a local path (to a directory containing a Dockerfile) or a
|
||||||
|
remote URL. `fileobj` must be a readable file-like object to a Dockerfile.
|
||||||
|
|
||||||
|
If you have a tar file for the Docker build context (including a Dockerfile)
|
||||||
|
already, pass a readable file-like object to `fileobj` and also pass
|
||||||
|
`custom_context=True`. If the stream is compressed also, set `encoding` to the
|
||||||
|
correct value (e.g `gzip`).
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* path (str): Path to the directory containing the Dockerfile
|
||||||
|
* tag (str): A tag to add to the final image
|
||||||
|
* quiet (bool): Whether to return the status
|
||||||
|
* fileobj: A file object to use as the Dockerfile. (Or a file-like object)
|
||||||
|
* nocache (bool): Don't use the cache when set to `True`
|
||||||
|
* rm (bool): Remove intermediate containers
|
||||||
|
* stream (bool): Return a blocking generator you can iterate over to retrieve
|
||||||
|
build output as it happens
|
||||||
|
* timeout (int): HTTP timeout
|
||||||
|
* custom_context (bool): Optional if using `fileobj`
|
||||||
|
* encoding (str): The encoding for a stream. Set to `gzip` for compressing
|
||||||
|
|
||||||
|
**Returns** (generator): A generator of the build output
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from io import BytesIO
|
||||||
|
>>> from docker import Client
|
||||||
|
>>> dockerfile = '''
|
||||||
|
... # Shared Volume
|
||||||
|
... FROM busybox:buildroot-2014.02
|
||||||
|
... MAINTAINER first last, first.last@yourdomain.com
|
||||||
|
... VOLUME /data
|
||||||
|
... CMD ["/bin/sh"]
|
||||||
|
... '''
|
||||||
|
>>> f = BytesIO(dockerfile.encode('utf-8'))
|
||||||
|
>>> cli = Client(base_url='tcp://127.0.0.1:2375')
|
||||||
|
>>> response = [line for line in cli.build(
|
||||||
|
... fileobj=f, rm=True, tag='yourname/volume'
|
||||||
|
... )]
|
||||||
|
>>> response
|
||||||
|
['{"stream":" ---\\u003e a9eb17255234\\n"}',
|
||||||
|
'{"stream":"Step 1 : MAINTAINER first last, first.last@yourdomain.com\\n"}',
|
||||||
|
'{"stream":" ---\\u003e Running in 08787d0ee8b1\\n"}',
|
||||||
|
'{"stream":" ---\\u003e 23e5e66a4494\\n"}',
|
||||||
|
'{"stream":"Removing intermediate container 08787d0ee8b1\\n"}',
|
||||||
|
'{"stream":"Step 2 : VOLUME /data\\n"}',
|
||||||
|
'{"stream":" ---\\u003e Running in abdc1e6896c6\\n"}',
|
||||||
|
'{"stream":" ---\\u003e 713bca62012e\\n"}',
|
||||||
|
'{"stream":"Removing intermediate container abdc1e6896c6\\n"}',
|
||||||
|
'{"stream":"Step 3 : CMD [\\"/bin/sh\\"]\\n"}',
|
||||||
|
'{"stream":" ---\\u003e Running in dba30f2a1a7e\\n"}',
|
||||||
|
'{"stream":" ---\\u003e 032b8b2855fc\\n"}',
|
||||||
|
'{"stream":"Removing intermediate container dba30f2a1a7e\\n"}',
|
||||||
|
'{"stream":"Successfully built 032b8b2855fc\\n"}']
|
||||||
|
```
|
||||||
|
|
||||||
|
**Raises:** [TypeError](
|
||||||
|
https://docs.python.org/3.4/library/exceptions.html#TypeError) if `path` nor
|
||||||
|
`fileobj` are specified
|
||||||
|
|
||||||
|
## commit
|
||||||
|
|
||||||
|
Identical to the `docker commit` command.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The image hash of the container
|
||||||
|
* repository (str): The repository to push the image to
|
||||||
|
* tag (str): The tag to push
|
||||||
|
* message (str): A commit message
|
||||||
|
* author (str): The name of the author
|
||||||
|
* conf (dict): The configuraton for the container. See the [Docker remote api](
|
||||||
|
https://docs.docker.com/reference/api/docker_remote_api/) for full details.
|
||||||
|
|
||||||
|
## containers
|
||||||
|
|
||||||
|
List containers. Identical to the `docker ps` command.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* quiet (bool): Only display numeric Ids
|
||||||
|
* all (bool): Show all containers. Only running containers are shown by default
|
||||||
|
* trunc (bool): Truncate output
|
||||||
|
* latest (bool): Show only the latest created container, include non-running
|
||||||
|
ones.
|
||||||
|
* since (str): Show only containers created since Id or Name, include
|
||||||
|
non-running ones
|
||||||
|
* before (str): Show only container created before Id or Name, include
|
||||||
|
non-running ones
|
||||||
|
* limit (int): Show `limit` last created containers, include non-running ones
|
||||||
|
* size (bool): Display sizes
|
||||||
|
|
||||||
|
**Returns** (dict): The system's containers
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from docker import Client
|
||||||
|
>>> cli = Client(base_url='tcp://127.0.0.1:2375')
|
||||||
|
>>> cli.containers()
|
||||||
|
[{'Command': '/bin/sleep 30',
|
||||||
|
'Created': 1412574844,
|
||||||
|
'Id': '6e276c9e6e5759e12a6a9214efec6439f80b4f37618e1a6547f28a3da34db07a',
|
||||||
|
'Image': 'busybox:buildroot-2014.02',
|
||||||
|
'Names': ['/grave_mayer'],
|
||||||
|
'Ports': [],
|
||||||
|
'Status': 'Up 1 seconds'}]
|
||||||
|
```
|
||||||
|
|
||||||
|
## copy
|
||||||
|
Identical to the `docker cp` command. Get files/folders from the container.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to copy from
|
||||||
|
* resource (str): The path within the container
|
||||||
|
|
||||||
|
**Returns** (str): The contents of the file as a string
|
||||||
|
|
||||||
|
## create_container
|
||||||
|
|
||||||
|
Creates a container that can then be `.start()` ed. Parameters are similar to
|
||||||
|
those for the `docker run` command except it doesn't support the attach
|
||||||
|
options (`-a`).
|
||||||
|
|
||||||
|
See [Port bindings](port-bindings.md) and [Using volumes](volumes.md) for more
|
||||||
|
information on how to create port bindings and volume mappings.
|
||||||
|
|
||||||
|
The `mem_limit` variable accepts float values (which represent the memory limit
|
||||||
|
of the created container in bytes) or a string with a units identification char
|
||||||
|
('100000b', 1000k', 128m', '1g'). If a string is specified without a units
|
||||||
|
character, bytes are assumed as an intended unit.
|
||||||
|
|
||||||
|
`volumes_from` and `dns` arguments raise [TypeError](
|
||||||
|
https://docs.python.org/3.4/library/exceptions.html#TypeError) exception if
|
||||||
|
they are used against v1.10 of the Docker remote API. Those arguments should be
|
||||||
|
passed to `start()` instead.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* image (str): The image to run
|
||||||
|
* command (str or list): The command to be run in the container
|
||||||
|
* hostname (str): Optional hostname for the container
|
||||||
|
* user (str or int): Username or UID
|
||||||
|
* detach (bool): Detached mode: run container in the background and print new
|
||||||
|
container Id
|
||||||
|
* stdin_open (bool): Keep STDIN open even if not attached
|
||||||
|
* tty (bool): Allocate a pseudo-TTY
|
||||||
|
* mem_limit (float or str): Memory limit (format: [number][optional unit],
|
||||||
|
where unit = b, k, m, or g)
|
||||||
|
* ports (list of ints): A list of port numbers
|
||||||
|
* environment (dict or list): A dictionary or a list of strings in the
|
||||||
|
following format `["PASSWORD=xxx"]` or `{"PASSWORD": "xxx"}`.
|
||||||
|
* dns (list): DNS name servers
|
||||||
|
* volumes (str or list):
|
||||||
|
* volumes_from (str or list): List of container names or Ids to get volumes
|
||||||
|
from. Optionally a single string joining container id's with commas
|
||||||
|
* network_disabled (bool): Disable networking
|
||||||
|
* name (str): A name for the container
|
||||||
|
* entrypoint (str or list): An entrypoint
|
||||||
|
* cpu_shares (int or float): CPU shares (relative weight)
|
||||||
|
* working_dir (str): Path to the working directory
|
||||||
|
* domainname (str or list): Set custom DNS search domains
|
||||||
|
* memswap_limit:
|
||||||
|
|
||||||
|
**Returns** (dict): A dictionary with an image 'Id' key and a 'Warnings' key.
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from docker import Client
|
||||||
|
>>> cli = Client(base_url='tcp://127.0.0.1:2375')
|
||||||
|
>>> container = cli.create_container(image='busybox:latest', command='/bin/sleep 30')
|
||||||
|
>>> print(container)
|
||||||
|
{'Id': '8a61192da2b3bb2d922875585e29b74ec0dc4e0117fcbf84c962204e97564cd7',
|
||||||
|
'Warnings': None}
|
||||||
|
```
|
||||||
|
|
||||||
|
## diff
|
||||||
|
|
||||||
|
Inspect changes on a container's filesystem
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to diff
|
||||||
|
|
||||||
|
**Returns** (str):
|
||||||
|
|
||||||
|
## exec
|
||||||
|
|
||||||
|
```python
|
||||||
|
c.exec(container, cmd, detach=False, stdout=True, stderr=True,
|
||||||
|
stream=False, tty=False)
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute a command in a running container.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): can be a container dictionary (result of
|
||||||
|
running `inspect_container`), unique id or container name.
|
||||||
|
|
||||||
|
|
||||||
|
* cmd (str or list): representing the command and its arguments.
|
||||||
|
|
||||||
|
* detach (bool): flag to `True` will run the process in the background.
|
||||||
|
|
||||||
|
* stdout (bool): indicates which output streams to read from.
|
||||||
|
* stderr (bool): indicates which output streams to read from.
|
||||||
|
|
||||||
|
* stream (bool): indicates whether to return a generator which will yield
|
||||||
|
the streaming response in chunks.
|
||||||
|
|
||||||
|
## export
|
||||||
|
|
||||||
|
Export the contents of a filesystem as a tar archive to STDOUT
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to export
|
||||||
|
|
||||||
|
**Returns** (str): The filesystem tar archive as a str
|
||||||
|
|
||||||
|
## history
|
||||||
|
|
||||||
|
Show the history of an image
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* image (str): The image to show history for
|
||||||
|
|
||||||
|
**Returns** (str): The history of the image
|
||||||
|
|
||||||
|
## images
|
||||||
|
|
||||||
|
List images. Identical to the `docker images` command.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* name (str): Optional filter for a name
|
||||||
|
* quiet (bool): Only show numeric Ids. Returns a list
|
||||||
|
* all (bool): Show all images (by default filter out the intermediate image
|
||||||
|
layers)
|
||||||
|
* viz: *Depreciated*
|
||||||
|
|
||||||
|
**Returns** (dict or list): A list if `quiet=True`, otherwise a dict.
|
||||||
|
|
||||||
|
```python
|
||||||
|
[{'Created': 1401926735,
|
||||||
|
'Id': 'a9eb172552348a9a49180694790b33a1097f546456d041b6e82e4d7716ddb721',
|
||||||
|
'ParentId': '120e218dd395ec314e7b6249f39d2853911b3d6def6ea164ae05722649f34b16',
|
||||||
|
'RepoTags': ['busybox:buildroot-2014.02', 'busybox:latest'],
|
||||||
|
'Size': 0,
|
||||||
|
'VirtualSize': 2433303},
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## import_image
|
||||||
|
|
||||||
|
Identical to the `docker import` command. If `src` is a string or unicode
|
||||||
|
string, it will be treated as a URL to fetch the image from. To import an image
|
||||||
|
from the local machine, `src` needs to be a file-like object or bytes
|
||||||
|
collection. To import from a tarball use your absolute path to your tarball.
|
||||||
|
To load arbitrary data as tarball use whatever you want as src and your
|
||||||
|
tarball content in data.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* src (str or file): Path to tarfile or URL
|
||||||
|
* repository (str): The repository to create
|
||||||
|
* tag (str): The tag to apply
|
||||||
|
* image (str): Use another image like the `FROM` Dockerfile parameter
|
||||||
|
|
||||||
|
## info
|
||||||
|
|
||||||
|
Display system-wide information. Identical to the `docker info` command.
|
||||||
|
|
||||||
|
**Returns** (dict): The info as a dict
|
||||||
|
|
||||||
|
```
|
||||||
|
>>> from docker import Client
|
||||||
|
>>> cli = Client(base_url='tcp://127.0.0.1:2375')
|
||||||
|
>>> cli.info()
|
||||||
|
{'Containers': 3,
|
||||||
|
'Debug': 1,
|
||||||
|
'Driver': 'aufs',
|
||||||
|
'DriverStatus': [['Root Dir', '/mnt/sda1/var/lib/docker/aufs'],
|
||||||
|
['Dirs', '225']],
|
||||||
|
'ExecutionDriver': 'native-0.2',
|
||||||
|
'IPv4Forwarding': 1,
|
||||||
|
'Images': 219,
|
||||||
|
'IndexServerAddress': 'https://index.docker.io/v1/',
|
||||||
|
'InitPath': '/usr/local/bin/docker',
|
||||||
|
'InitSha1': '',
|
||||||
|
'KernelVersion': '3.16.1-tinycore64',
|
||||||
|
'MemoryLimit': 1,
|
||||||
|
'NEventsListener': 0,
|
||||||
|
'NFd': 11,
|
||||||
|
'NGoroutines': 12,
|
||||||
|
'OperatingSystem': 'Boot2Docker 1.2.0 (TCL 5.3);',
|
||||||
|
'SwapLimit': 1}
|
||||||
|
```
|
||||||
|
|
||||||
|
## insert
|
||||||
|
*DEPRECATED*
|
||||||
|
|
||||||
|
## inspect_container
|
||||||
|
|
||||||
|
Identical to the `docker inspect` command, but only for containers.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to inspect
|
||||||
|
|
||||||
|
**Returns** (dict): Nearly the same output as `docker inspect`, just as a
|
||||||
|
single dict
|
||||||
|
|
||||||
|
## inspect_image
|
||||||
|
|
||||||
|
Identical to the `docker inspect` command, but only for images
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* image_id (str): The image to inspect
|
||||||
|
|
||||||
|
**Returns** (dict): Nearly the same output as `docker inspect`, just as a
|
||||||
|
single dict
|
||||||
|
|
||||||
|
## kill
|
||||||
|
|
||||||
|
Kill a container or send a signal to a container
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to kill
|
||||||
|
* signal (str or int): The singal to send. Defaults to `SIGKILL`
|
||||||
|
|
||||||
|
## login
|
||||||
|
|
||||||
|
Nearly identical to the `docker login` command, but non-interactive.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* username (str): The registry username
|
||||||
|
* password (str): The plaintext password
|
||||||
|
* email (str): The email for the registry account
|
||||||
|
* registry (str): URL to the registry. Ex:`https://index.docker.io/v1/`
|
||||||
|
* reauth (bool): Whether refresh existing authentication on the docker server.
|
||||||
|
|
||||||
|
**Returns** (dict): The response from the login request
|
||||||
|
|
||||||
|
## logs
|
||||||
|
|
||||||
|
Identical to the `docker logs` command. The `stream` parameter makes the `logs`
|
||||||
|
function return a blocking generator you can iterate over to retrieve log
|
||||||
|
output as it happens.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to get logs from
|
||||||
|
* stdout (bool): Get STDOUT
|
||||||
|
* stderr (bool): Get STDERR
|
||||||
|
* stream (bool): Stream the response
|
||||||
|
* timestamps (bool): Show timestamps
|
||||||
|
|
||||||
|
**Returns** (generator or str):
|
||||||
|
|
||||||
|
## pause
|
||||||
|
|
||||||
|
Pauses all processes within a container.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to pause
|
||||||
|
|
||||||
|
|
||||||
|
## ping
|
||||||
|
|
||||||
|
Hits the `/_ping` endpoint of the remote API and returns the result. An
|
||||||
|
exception will be raised if the endpoint isn't responding.
|
||||||
|
|
||||||
|
**Returns** (bool)
|
||||||
|
|
||||||
|
## port
|
||||||
|
Lookup the public-facing port that is NAT-ed to `private_port`. Identical to
|
||||||
|
the `docker port` command.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to look up
|
||||||
|
* private_port (int): The private port to inspect
|
||||||
|
|
||||||
|
**Returns** (list of dict): The mapping for the host ports
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker run -d -p 80:80 ubuntu:14.04 /bin/sleep 30
|
||||||
|
7174d6347063a83f412fad6124c99cffd25ffe1a0807eb4b7f9cec76ac8cb43b
|
||||||
|
```
|
||||||
|
```python
|
||||||
|
>>> cli.port('7174d6347063', 80)
|
||||||
|
[{'HostIp': '0.0.0.0', 'HostPort': '80'}]
|
||||||
|
```
|
||||||
|
|
||||||
|
## pull
|
||||||
|
|
||||||
|
Identical to the `docker pull` command.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* repository (str): The repository to pull
|
||||||
|
* tag (str): The tag to pull
|
||||||
|
* stream (bool): Stream the output as a generator
|
||||||
|
* insecure_registry (bool): Use an insecure registry
|
||||||
|
|
||||||
|
**Returns** (generator or str): The output
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from docker import Client
|
||||||
|
>>> cli = Client(base_url='tcp://127.0.0.1:2375')
|
||||||
|
>>> for line in cli.pull('busybox', stream=True):
|
||||||
|
... print(json.dumps(json.loads(line), indent=4))
|
||||||
|
{
|
||||||
|
"status": "Pulling image (latest) from busybox",
|
||||||
|
"progressDetail": {},
|
||||||
|
"id": "e72ac664f4f0"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"status": "Pulling image (latest) from busybox, endpoint: ...",
|
||||||
|
"progressDetail": {},
|
||||||
|
"id": "e72ac664f4f0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## push
|
||||||
|
|
||||||
|
Push an image or a repository to the registry. Identical to the `docker push`
|
||||||
|
command
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* repository (str): The repository to push to
|
||||||
|
* tag (str): An optional tag to push
|
||||||
|
* stream (bool): Stream the output as a blocking generator
|
||||||
|
* insecure_registry (bool): Use `http://` to connect to the registry
|
||||||
|
|
||||||
|
**Returns** (generator or str): The output of the upload
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from docker import Client
|
||||||
|
>>> cli = Client(base_url='tcp://127.0.0.1:2375')
|
||||||
|
>>> response = [line for line in cli.push('yourname/app', stream=True)]
|
||||||
|
>>> response
|
||||||
|
['{"status":"Pushing repository yourname/app (1 tags)"}\\n',
|
||||||
|
'{"status":"Pushing","progressDetail":{},"id":"511136ea3c5a"}\\n',
|
||||||
|
'{"status":"Image already pushed, skipping","progressDetail":{},
|
||||||
|
"id":"511136ea3c5a"}\\n',
|
||||||
|
...
|
||||||
|
'{"status":"Pushing tag for rev [918af568e6e5] on {
|
||||||
|
https://cdn-registry-1.docker.io/v1/repositories/
|
||||||
|
yourname/app/tags/latest}"}\\n']
|
||||||
|
```
|
||||||
|
|
||||||
|
## remove_container
|
||||||
|
|
||||||
|
Remove a container. Similar to the `docker rm` command.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to remove
|
||||||
|
* v (bool): Remove the volumes associated with the container
|
||||||
|
* link (bool): Remove the specified link and not the underlying container
|
||||||
|
* force (bool): Force the removal of a running container (uses SIGKILL)
|
||||||
|
|
||||||
|
## remove_image
|
||||||
|
|
||||||
|
Remove an image. Similar to the `docker rmi` command.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* image (str): The image to remove
|
||||||
|
* force (bool): Force removal of the image
|
||||||
|
* noprune (bool): Do not delete untagged parents
|
||||||
|
|
||||||
|
## restart
|
||||||
|
|
||||||
|
Restart a container. Similar to the `docker restart` command.
|
||||||
|
|
||||||
|
If `container` a dict, the `Id` key is used.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str or dict): The container to restart
|
||||||
|
* timeout (int): Number of seconds to try to stop for before killing the
|
||||||
|
container. Once killed it will then be restarted. Default is 10 seconds.
|
||||||
|
|
||||||
|
## search
|
||||||
|
Identical to the `docker search` command.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* term (str): A term to search for
|
||||||
|
|
||||||
|
**Returns** (list of dicts): The response of the search
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from docker import Client
|
||||||
|
>>> cli = Client(base_url='tcp://127.0.0.1:2375')
|
||||||
|
>>> response = cli.search('nginx')
|
||||||
|
>>> response[:2]
|
||||||
|
[{'description': 'Official build of Nginx.',
|
||||||
|
'is_official': True,
|
||||||
|
'is_trusted': False,
|
||||||
|
'name': 'nginx',
|
||||||
|
'star_count': 266},
|
||||||
|
{'description': 'Trusted automated Nginx (http://nginx.org/) ...',
|
||||||
|
'is_official': False,
|
||||||
|
'is_trusted': True,
|
||||||
|
'name': 'dockerfile/nginx',
|
||||||
|
'star_count': 60},
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## start
|
||||||
|
|
||||||
|
Similar to the `docker start` command, but doesn't support attach options. Use
|
||||||
|
`.logs()` to recover `stdout`/`stderr`.
|
||||||
|
|
||||||
|
`binds` allows to bind a directory in the host to the container. See [Using
|
||||||
|
volumes](volumes.md) for more information. `port_bindings` exposes container
|
||||||
|
ports to the host. See [Port bindings](port-bindings.md) for more information.
|
||||||
|
`lxc_conf` allows to pass LXC configuration options using a dictionary.
|
||||||
|
`privileged` starts the container in privileged mode.
|
||||||
|
|
||||||
|
[Links](http://docs.docker.io/en/latest/use/working_with_links_names/) can be
|
||||||
|
specified with the `links` argument. They can either be specified as a
|
||||||
|
dictionary mapping name to alias or as a list of `(name, alias)` tuples.
|
||||||
|
|
||||||
|
`dns` and `volumes_from` are only available if they are used with version v1.10
|
||||||
|
of docker remote API. Otherwise they are ignored.
|
||||||
|
|
||||||
|
`network_mode` is available since v1.11 and sets the Network mode for the
|
||||||
|
container ('bridge': creates a new network stack for the container on the
|
||||||
|
Docker bridge, 'none': no networking for this container, 'container:[name|id]':
|
||||||
|
reuses another container network stack), 'host': use the host network stack
|
||||||
|
inside the container.
|
||||||
|
|
||||||
|
`restart_policy` is available since v1.2.0 and sets the RestartPolicy for how a
|
||||||
|
container should or should not be restarted on exit. By default the policy is
|
||||||
|
set to no meaning do not restart the container when it exits. The user may
|
||||||
|
specify the restart policy as a dictionary for example:
|
||||||
|
```python
|
||||||
|
{
|
||||||
|
"MaximumRetryCount": 0,
|
||||||
|
"Name": "always"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For always restarting the container on exit or can specify to restart the
|
||||||
|
container to restart on failure and can limit number of restarts. For example:
|
||||||
|
```python
|
||||||
|
{
|
||||||
|
"MaximumRetryCount": 5,
|
||||||
|
"Name": "on-failure"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`cap_add` and `cap_drop` are available since v1.2.0 and can be used to add or
|
||||||
|
drop certain capabilities. The user may specify the capabilities as an array
|
||||||
|
for example:
|
||||||
|
```python
|
||||||
|
[
|
||||||
|
"SYS_ADMIN",
|
||||||
|
"MKNOD"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to start
|
||||||
|
* binds: Volumes to bind
|
||||||
|
* port_bindings (dict): Port bindings. See note above
|
||||||
|
* lxc_conf (dict): LXC config
|
||||||
|
* publish_all_ports (bool): Whether to publish all ports to the host
|
||||||
|
* links (dict or list of tuples): See note above
|
||||||
|
* privileged (bool): Give extended privileges to this container
|
||||||
|
* dns (list): Set custom DNS servers
|
||||||
|
* dns_search (list): DNS search domains
|
||||||
|
* volumes_from (str or list): List of container names or Ids to get volumes
|
||||||
|
from. Optionally a single string joining container id's with commas
|
||||||
|
* network_mode (str): One of `['bridge', None, 'container:<name|id>',
|
||||||
|
'host']`
|
||||||
|
* restart_policy (dict): See note above. "Name" param must be one of
|
||||||
|
`['on-failure', 'always']`
|
||||||
|
* cap_add (list of str): See note above
|
||||||
|
* cap_drop (list of str): See note above
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from docker import Client
|
||||||
|
>>> cli = Client(base_url='tcp://127.0.0.1:2375')
|
||||||
|
>>> container = cli.create_container(
|
||||||
|
... image='busybox:latest',
|
||||||
|
... command='/bin/sleep 30')
|
||||||
|
>>> response = cli.start(container=container.get('Id'))
|
||||||
|
>>> print(response)
|
||||||
|
None
|
||||||
|
```
|
||||||
|
|
||||||
|
## stop
|
||||||
|
|
||||||
|
Stops a container. Similar to the `docker stop` command.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to stop
|
||||||
|
* timeout (int): Timeout in seconds to wait for the container to stop before
|
||||||
|
sending a `SIGKILL`
|
||||||
|
|
||||||
|
## tag
|
||||||
|
|
||||||
|
Tag an image into a repository. Identical to the `docker tag` command.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* image (str): The image to tag
|
||||||
|
* repository (str): The repository to set for the tag
|
||||||
|
* tag (str): The tag name
|
||||||
|
* force (bool): Force
|
||||||
|
|
||||||
|
**Returns** (bool): True if successful
|
||||||
|
|
||||||
|
## top
|
||||||
|
Display the running processes of a container
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to inspect
|
||||||
|
|
||||||
|
**Returns** (str): The output of the top
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from docker import Client
|
||||||
|
>>> cli = Client(base_url='tcp://127.0.0.1:2375')
|
||||||
|
>>> cli.create_container('busybox:latest', '/bin/sleep 30', name='sleeper')
|
||||||
|
>>> cli.start('sleeper')
|
||||||
|
>>> cli.top('sleeper')
|
||||||
|
{'Processes': [['952', 'root', '/bin/sleep 30']],
|
||||||
|
'Titles': ['PID', 'USER', 'COMMAND']}
|
||||||
|
```
|
||||||
|
|
||||||
|
## unpause
|
||||||
|
|
||||||
|
Unpauses all processes within a container.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str): The container to unpause
|
||||||
|
|
||||||
|
## version
|
||||||
|
Nearly identical to the `docker version` command.
|
||||||
|
|
||||||
|
**Returns** (dict): The server version information
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from docker import Client
|
||||||
|
>>> cli = Client(base_url='tcp://127.0.0.1:2375')
|
||||||
|
>>> cli.version()
|
||||||
|
{
|
||||||
|
"KernelVersion": "3.16.4-tinycore64",
|
||||||
|
"Arch": "amd64",
|
||||||
|
"ApiVersion": "1.15",
|
||||||
|
"Version": "1.3.0",
|
||||||
|
"GitCommit": "c78088f",
|
||||||
|
"Os": "linux",
|
||||||
|
"GoVersion": "go1.3.3"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## wait
|
||||||
|
Identical to the `docker wait` command. Block until a container stops, then
|
||||||
|
print its exit code. Returns the value `-1` if no `StatusCode` is returned by
|
||||||
|
the API.
|
||||||
|
|
||||||
|
If `container` a dict, the `Id` key is used.
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* container (str or dict): The container to wait on
|
||||||
|
|
||||||
|
**Returns** (int): The exit code of the container
|
||||||
|
|
||||||
|
|
||||||
|
<!---
|
||||||
|
TODO:
|
||||||
|
|
||||||
|
* events
|
||||||
|
* get_image
|
||||||
|
* load_image
|
||||||
|
* resize
|
||||||
|
|
||||||
|
-->
|
|
@ -1,5 +1,10 @@
|
||||||
ChangeLog
|
Change Log
|
||||||
=========
|
==========
|
||||||
|
|
||||||
|
0.5.4
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Added MkDocs documentation.
|
||||||
|
|
||||||
0.5.3
|
0.5.3
|
||||||
-----
|
-----
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Contributing
|
||||||
|
See the [Docker contributing guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md).
|
||||||
|
The following is specific to docker-py.
|
||||||
|
|
||||||
|
## Running the tests & Code Quality
|
||||||
|
|
||||||
|
|
||||||
|
To get the source source code and run the unit tests, run:
|
||||||
|
```
|
||||||
|
$ git clone git://github.com/docker/docker-py.git
|
||||||
|
$ cd docker-py
|
||||||
|
$ pip install tox
|
||||||
|
$ tox
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building the docs
|
||||||
|
Docs are built with [MkDocs](http://www.mkdocs.org/). For development, you can
|
||||||
|
run the following in the project directory:
|
||||||
|
```
|
||||||
|
$ pip install -r docs-requirements.txt
|
||||||
|
$ mkdocs serve
|
||||||
|
```
|
||||||
|
|
||||||
|
## Release Checklist
|
||||||
|
|
||||||
|
Before a new release, please go through the following checklist:
|
||||||
|
|
||||||
|
* Bump version in docker/version.py
|
||||||
|
* Add a release note in docs/change_log.md
|
||||||
|
* Git tag the version
|
||||||
|
* Upload to pypi
|
||||||
|
|
||||||
|
## Vulnerability Reporting
|
||||||
|
For any security issues, please do NOT file an issue or pull request on github!
|
||||||
|
Please contact [security@docker.com](mailto:security@docker.com) or read [the
|
||||||
|
Docker security page](https://www.docker.com/resources/security/).
|
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,13 @@
|
||||||
|
# Access to devices on the host
|
||||||
|
|
||||||
|
If you need to directly expose some host devices to a container, you can use
|
||||||
|
the devices parameter in the `Client.start` method as shown below
|
||||||
|
|
||||||
|
```python
|
||||||
|
c.start(container_id, devices=['/dev/sda:/dev/xvda:rwm'])
|
||||||
|
```
|
||||||
|
|
||||||
|
Each string is a single mapping using the colon (':') as the separator. So the
|
||||||
|
above example essentially allow the container to have read write permissions to
|
||||||
|
access the host's /dev/sda via a node named /dev/xvda in the container. The
|
||||||
|
devices parameter is a list to allow multiple devices to be mapped.
|
|
@ -0,0 +1,16 @@
|
||||||
|
# docker-py documentation
|
||||||
|
|
||||||
|
An API client for docker written in Python
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Our latest stable is always available on PyPi.
|
||||||
|
|
||||||
|
pip install docker-py
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
Full documentation is available in the `/docs/` directory.
|
||||||
|
|
||||||
|
## License
|
||||||
|
Docker is licensed under the Apache License, Version 2.0. See LICENSE for full
|
||||||
|
license text
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Port bindings
|
||||||
|
Port bindings is done in two parts. Firstly, by providing a list of ports to
|
||||||
|
open inside the container in the `Client().create_container()` method.
|
||||||
|
|
||||||
|
```python
|
||||||
|
c.create_container('busybox', 'ls', ports=[1111, 2222])
|
||||||
|
```
|
||||||
|
|
||||||
|
Bindings are then declared in the `Client.start` method.
|
||||||
|
|
||||||
|
```python
|
||||||
|
c.start(container_id, port_bindings={1111: 4567, 2222: None})
|
||||||
|
```
|
||||||
|
|
||||||
|
You can limit the host address on which the port will be exposed like such:
|
||||||
|
|
||||||
|
```python
|
||||||
|
c.start(container_id, port_bindings={1111: ('127.0.0.1', 4567)})
|
||||||
|
```
|
||||||
|
|
||||||
|
Or without host port assignment:
|
||||||
|
|
||||||
|
```python
|
||||||
|
c.start(container_id, port_bindings={1111: ('127.0.0.1',)})
|
||||||
|
```
|
||||||
|
|
||||||
|
If you wish to use UDP instead of TCP (default), you need to declare it
|
||||||
|
like such in both the `create_container()` and `start()` calls:
|
||||||
|
|
||||||
|
```python
|
||||||
|
container_id = c.create_container(
|
||||||
|
'busybox',
|
||||||
|
'ls',
|
||||||
|
ports=[(1111, 'udp'), 2222]
|
||||||
|
)
|
||||||
|
c.start(container_id, port_bindings={'1111/udp': 4567, 2222: None})
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
## Connection to daemon using HTTPS
|
||||||
|
|
||||||
|
**Note:** *These instructions are docker-py specific. Please refer to
|
||||||
|
[http://docs.docker.com/articles/https/](http://docs.docker.com/articles/https/)
|
||||||
|
first.*
|
||||||
|
|
||||||
|
## TLSConfig
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* client_cert (tuple of str): Path to client cert, path to client key
|
||||||
|
* ca_cert (str): Path to CA cert file
|
||||||
|
* verify (bool or str): This can be `False` or a path to a CA Cert file
|
||||||
|
* ssl_version (int): A valid [SSL version](
|
||||||
|
https://docs.python.org/3.4/library/ssl.html#ssl.PROTOCOL_TLSv1)
|
||||||
|
* assert_hostname (bool): Verify hostname of docker daemon
|
||||||
|
|
||||||
|
### configure_client
|
||||||
|
|
||||||
|
**Params**:
|
||||||
|
|
||||||
|
* client: ([Client](api.md#client-api)): A client to apply this config to
|
||||||
|
|
||||||
|
|
||||||
|
## Authenticate server based on public/default CA pool
|
||||||
|
|
||||||
|
```python
|
||||||
|
client = docker.Client(base_url='<https_url>', tls=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
Equivalent CLI options:
|
||||||
|
```bash
|
||||||
|
docker --tls ...
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to use TLS but don't want to verify the server certificate
|
||||||
|
(for example when testing with a self-signed certificate):
|
||||||
|
|
||||||
|
```python
|
||||||
|
tls_config = docker.tls.TLSConfig(verify=False)
|
||||||
|
client = docker.Client(base_url='<https_url>', tls=tls_config)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authenticate server based on given CA
|
||||||
|
|
||||||
|
```python
|
||||||
|
tls_config = docker.tls.TLSConfig(ca_cert='/path/to/ca.pem')
|
||||||
|
client = docker.Client(base_url='<https_url>', tls=tls_config)
|
||||||
|
```
|
||||||
|
|
||||||
|
Equivalent CLI options:
|
||||||
|
```bash
|
||||||
|
docker --tlsverify --tlscacert /path/to/ca.pem ...`
|
||||||
|
|
||||||
|
## Authenticate with client certificate, do not authenticate server based on given CA
|
||||||
|
|
||||||
|
```python
|
||||||
|
tls_config = docker.tls.TLSConfig(
|
||||||
|
client_cert=('/path/to/client-cert.pem', '/path/to/client-key.pem')
|
||||||
|
)
|
||||||
|
client = docker.Client(base_url='<https_url>', tls=tls_config)
|
||||||
|
```
|
||||||
|
|
||||||
|
Equivalent CLI options:
|
||||||
|
```bash
|
||||||
|
docker --tls --tlscert /path/to/client-cert.pem --tlskey /path/to/client-key.pem ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authenticate with client certificate, authenticate server based on given CA
|
||||||
|
|
||||||
|
```python
|
||||||
|
tls_config = docker.tls.TLSConfig(
|
||||||
|
client_cert=('/path/to/client-cert.pem', '/path/to/client-key.pem'),
|
||||||
|
ca_cert='/path/to/ca.pem'
|
||||||
|
)
|
||||||
|
client = docker.Client(base_url='<https_url>', tls=tls_config)
|
||||||
|
```
|
||||||
|
|
||||||
|
Equivalent CLI options:
|
||||||
|
```bash
|
||||||
|
docker --tlsverify \
|
||||||
|
--tlscert /path/to/client-cert.pem \
|
||||||
|
--tlskey /path/to/client-key.pem \
|
||||||
|
--tlscacert /path/to/ca.pem ...
|
||||||
|
```
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Using volumes
|
||||||
|
|
||||||
|
Volume declaration is done in two parts. First, you have to provide a list of
|
||||||
|
mountpoints to the `Client().create_container()` method.
|
||||||
|
|
||||||
|
```python
|
||||||
|
c.create_container('busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2'])
|
||||||
|
```
|
||||||
|
|
||||||
|
Volume mappings are then declared inside the `Client.start` method like this:
|
||||||
|
|
||||||
|
```python
|
||||||
|
c.start(container_id, binds={
|
||||||
|
'/home/user1/':
|
||||||
|
{
|
||||||
|
'bind': '/mnt/vol2',
|
||||||
|
'ro': False
|
||||||
|
},
|
||||||
|
'/var/www':
|
||||||
|
{
|
||||||
|
'bind': '/mnt/vol1',
|
||||||
|
'ro': True
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
|
@ -0,0 +1,15 @@
|
||||||
|
site_name: docker-py Documentation
|
||||||
|
site_description: An API client for Docker written in Python
|
||||||
|
site_favicon: favicon_whale.png
|
||||||
|
# site_url: docker-py.readthedocs.org
|
||||||
|
repo_url: https://github.com/docker/docker-py/
|
||||||
|
theme: readthedocs
|
||||||
|
pages:
|
||||||
|
- [index.md, Home]
|
||||||
|
- [api.md, Client API]
|
||||||
|
- [port-bindings.md, Port Bindings]
|
||||||
|
- [volumes.md, Using Volumes]
|
||||||
|
- [tls.md, Using TLS]
|
||||||
|
- [host-devices.md, Host devices]
|
||||||
|
- [change_log.md, Change Log]
|
||||||
|
- [contributing.md, Contributing]
|
|
@ -12,7 +12,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
CURRENT_VERSION = 'v1.12'
|
CURRENT_VERSION = 'v1.15'
|
||||||
|
|
||||||
FAKE_CONTAINER_ID = '3cc2351ab11b'
|
FAKE_CONTAINER_ID = '3cc2351ab11b'
|
||||||
FAKE_IMAGE_ID = 'e9aa60c60128'
|
FAKE_IMAGE_ID = 'e9aa60c60128'
|
||||||
|
@ -225,6 +225,20 @@ def get_fake_export():
|
||||||
return status_code, response
|
return status_code, response
|
||||||
|
|
||||||
|
|
||||||
|
def post_fake_execute():
|
||||||
|
status_code = 200
|
||||||
|
response = {'Id': FAKE_CONTAINER_ID}
|
||||||
|
return status_code, response
|
||||||
|
|
||||||
|
|
||||||
|
def post_fake_execute_start():
|
||||||
|
status_code = 200
|
||||||
|
response = (b'\x01\x00\x00\x00\x00\x00\x00\x11bin\nboot\ndev\netc\n'
|
||||||
|
b'\x01\x00\x00\x00\x00\x00\x00\x12lib\nmnt\nproc\nroot\n'
|
||||||
|
b'\x01\x00\x00\x00\x00\x00\x00\x0csbin\nusr\nvar\n')
|
||||||
|
return status_code, response
|
||||||
|
|
||||||
|
|
||||||
def post_fake_stop_container():
|
def post_fake_stop_container():
|
||||||
status_code = 200
|
status_code = 200
|
||||||
response = {'Id': FAKE_CONTAINER_ID}
|
response = {'Id': FAKE_CONTAINER_ID}
|
||||||
|
@ -237,6 +251,18 @@ def post_fake_kill_container():
|
||||||
return status_code, response
|
return status_code, response
|
||||||
|
|
||||||
|
|
||||||
|
def post_fake_pause_container():
|
||||||
|
status_code = 200
|
||||||
|
response = {'Id': FAKE_CONTAINER_ID}
|
||||||
|
return status_code, response
|
||||||
|
|
||||||
|
|
||||||
|
def post_fake_unpause_container():
|
||||||
|
status_code = 200
|
||||||
|
response = {'Id': FAKE_CONTAINER_ID}
|
||||||
|
return status_code, response
|
||||||
|
|
||||||
|
|
||||||
def post_fake_restart_container():
|
def post_fake_restart_container():
|
||||||
status_code = 200
|
status_code = 200
|
||||||
response = {'Id': FAKE_CONTAINER_ID}
|
response = {'Id': FAKE_CONTAINER_ID}
|
||||||
|
@ -330,10 +356,18 @@ fake_responses = {
|
||||||
get_fake_diff,
|
get_fake_diff,
|
||||||
'{1}/{0}/containers/3cc2351ab11b/export'.format(CURRENT_VERSION, prefix):
|
'{1}/{0}/containers/3cc2351ab11b/export'.format(CURRENT_VERSION, prefix):
|
||||||
get_fake_export,
|
get_fake_export,
|
||||||
|
'{1}/{0}/containers/3cc2351ab11b/exec'.format(CURRENT_VERSION, prefix):
|
||||||
|
post_fake_execute,
|
||||||
|
'{1}/{0}/exec/3cc2351ab11b/start'.format(CURRENT_VERSION, prefix):
|
||||||
|
post_fake_execute_start,
|
||||||
'{1}/{0}/containers/3cc2351ab11b/stop'.format(CURRENT_VERSION, prefix):
|
'{1}/{0}/containers/3cc2351ab11b/stop'.format(CURRENT_VERSION, prefix):
|
||||||
post_fake_stop_container,
|
post_fake_stop_container,
|
||||||
'{1}/{0}/containers/3cc2351ab11b/kill'.format(CURRENT_VERSION, prefix):
|
'{1}/{0}/containers/3cc2351ab11b/kill'.format(CURRENT_VERSION, prefix):
|
||||||
post_fake_kill_container,
|
post_fake_kill_container,
|
||||||
|
'{1}/{0}/containers/3cc2351ab11b/pause'.format(CURRENT_VERSION, prefix):
|
||||||
|
post_fake_pause_container,
|
||||||
|
'{1}/{0}/containers/3cc2351ab11b/unpause'.format(CURRENT_VERSION, prefix):
|
||||||
|
post_fake_unpause_container,
|
||||||
'{1}/{0}/containers/3cc2351ab11b/json'.format(CURRENT_VERSION, prefix):
|
'{1}/{0}/containers/3cc2351ab11b/json'.format(CURRENT_VERSION, prefix):
|
||||||
get_fake_port,
|
get_fake_port,
|
||||||
'{1}/{0}/containers/3cc2351ab11b/restart'.format(CURRENT_VERSION, prefix):
|
'{1}/{0}/containers/3cc2351ab11b/restart'.format(CURRENT_VERSION, prefix):
|
||||||
|
|
|
@ -627,6 +627,76 @@ class TestRestartingContainer(BaseTestCase):
|
||||||
res = [x for x in containers if 'Id' in x and x['Id'].startswith(id)]
|
res = [x for x in containers if 'Id' in x and x['Id'].startswith(id)]
|
||||||
self.assertEqual(len(res), 0)
|
self.assertEqual(len(res), 0)
|
||||||
|
|
||||||
|
|
||||||
|
class TestExecuteCommand(BaseTestCase):
|
||||||
|
def runTest(self):
|
||||||
|
container = self.client.create_container('busybox', 'cat',
|
||||||
|
detach=True, stdin_open=True)
|
||||||
|
id = container['Id']
|
||||||
|
self.client.start(id)
|
||||||
|
self.tmp_containers.append(id)
|
||||||
|
|
||||||
|
res = self.client.execute(id, ['echo', 'hello'])
|
||||||
|
expected = b'hello\n' if six.PY3 else 'hello\n'
|
||||||
|
self.assertEqual(res, expected)
|
||||||
|
|
||||||
|
|
||||||
|
class TestExecuteCommandString(BaseTestCase):
|
||||||
|
def runTest(self):
|
||||||
|
container = self.client.create_container('busybox', 'cat',
|
||||||
|
detach=True, stdin_open=True)
|
||||||
|
id = container['Id']
|
||||||
|
self.client.start(id)
|
||||||
|
self.tmp_containers.append(id)
|
||||||
|
|
||||||
|
res = self.client.execute(id, 'echo hello world', stdout=True)
|
||||||
|
expected = b'hello world\n' if six.PY3 else 'hello world\n'
|
||||||
|
self.assertEqual(res, expected)
|
||||||
|
|
||||||
|
|
||||||
|
class TestExecuteCommandStreaming(BaseTestCase):
|
||||||
|
def runTest(self):
|
||||||
|
container = self.client.create_container('busybox', 'cat',
|
||||||
|
detach=True, stdin_open=True)
|
||||||
|
id = container['Id']
|
||||||
|
self.client.start(id)
|
||||||
|
self.tmp_containers.append(id)
|
||||||
|
|
||||||
|
chunks = self.client.execute(id, ['echo', 'hello\nworld'], stream=True)
|
||||||
|
res = b'' if six.PY3 else ''
|
||||||
|
for chunk in chunks:
|
||||||
|
res += chunk
|
||||||
|
expected = b'hello\nworld\n' if six.PY3 else 'hello\nworld\n'
|
||||||
|
self.assertEqual(res, expected)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPauseUnpauseContainer(BaseTestCase):
|
||||||
|
def runTest(self):
|
||||||
|
container = self.client.create_container('busybox', ['sleep', '9999'])
|
||||||
|
id = container['Id']
|
||||||
|
self.client.pause(id)
|
||||||
|
container_info = self.client.inspect_container(id)
|
||||||
|
self.assertIn('State', container_info)
|
||||||
|
state = container_info['State']
|
||||||
|
self.assertIn('ExitCode', state)
|
||||||
|
self.assertEqual(state['ExitCode'], 0)
|
||||||
|
self.assertIn('Running', state)
|
||||||
|
self.assertEqual(state['Running'], True)
|
||||||
|
self.assertIn('Paused', state)
|
||||||
|
self.assertEqual(state['Paused'], True)
|
||||||
|
|
||||||
|
self.client.unpause(id)
|
||||||
|
container_info = self.client.inspect_container(id)
|
||||||
|
self.assertIn('State', container_info)
|
||||||
|
state = container_info['State']
|
||||||
|
self.assertIn('ExitCode', state)
|
||||||
|
self.assertEqual(state['ExitCode'], 0)
|
||||||
|
self.assertIn('Running', state)
|
||||||
|
self.assertEqual(state['Running'], True)
|
||||||
|
self.assertIn('Paused', state)
|
||||||
|
self.assertEqual(state['Paused'], False)
|
||||||
|
|
||||||
|
|
||||||
#################
|
#################
|
||||||
# LINKS TESTS #
|
# LINKS TESTS #
|
||||||
#################
|
#################
|
||||||
|
|
145
tests/test.py
145
tests/test.py
|
@ -349,6 +349,30 @@ class DockerClientTest(Cleanup, unittest.TestCase):
|
||||||
self.assertEqual(args[1]['headers'],
|
self.assertEqual(args[1]['headers'],
|
||||||
{'Content-Type': 'application/json'})
|
{'Content-Type': 'application/json'})
|
||||||
|
|
||||||
|
def test_create_container_with_cpuset(self):
|
||||||
|
try:
|
||||||
|
self.client.create_container('busybox', 'ls',
|
||||||
|
cpuset='0,1')
|
||||||
|
except Exception as e:
|
||||||
|
self.fail('Command should not raise exception: {0}'.format(e))
|
||||||
|
|
||||||
|
args = fake_request.call_args
|
||||||
|
self.assertEqual(args[0][0],
|
||||||
|
url_prefix + 'containers/create')
|
||||||
|
self.assertEqual(json.loads(args[1]['data']),
|
||||||
|
json.loads('''
|
||||||
|
{"Tty": false, "Image": "busybox",
|
||||||
|
"Cmd": ["ls"], "AttachStdin": false,
|
||||||
|
"Memory": 0,
|
||||||
|
"AttachStderr": true,
|
||||||
|
"AttachStdout": true, "OpenStdin": false,
|
||||||
|
"StdinOnce": false,
|
||||||
|
"NetworkDisabled": false,
|
||||||
|
"Cpuset": "0,1",
|
||||||
|
"MemorySwap": 0}'''))
|
||||||
|
self.assertEqual(args[1]['headers'],
|
||||||
|
{'Content-Type': 'application/json'})
|
||||||
|
|
||||||
def test_create_container_with_working_dir(self):
|
def test_create_container_with_working_dir(self):
|
||||||
try:
|
try:
|
||||||
self.client.create_container('busybox', 'ls',
|
self.client.create_container('busybox', 'ls',
|
||||||
|
@ -891,6 +915,41 @@ class DockerClientTest(Cleanup, unittest.TestCase):
|
||||||
docker.client.DEFAULT_TIMEOUT_SECONDS
|
docker.client.DEFAULT_TIMEOUT_SECONDS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_start_container_with_devices(self):
|
||||||
|
try:
|
||||||
|
self.client.start(fake_api.FAKE_CONTAINER_ID,
|
||||||
|
devices=['/dev/sda:/dev/xvda:rwm',
|
||||||
|
'/dev/sdb:/dev/xvdb',
|
||||||
|
'/dev/sdc'])
|
||||||
|
except Exception as e:
|
||||||
|
self.fail('Command should not raise exception: {0}'.format(e))
|
||||||
|
args = fake_request.call_args
|
||||||
|
self.assertEqual(
|
||||||
|
args[0][0],
|
||||||
|
url_prefix + 'containers/3cc2351ab11b/start'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
json.loads(args[1]['data']),
|
||||||
|
{"PublishAllPorts": False, "Privileged": False,
|
||||||
|
"Devices": [{'CgroupPermissions': 'rwm',
|
||||||
|
'PathInContainer': '/dev/sda:/dev/xvda:rwm',
|
||||||
|
'PathOnHost': '/dev/sda:/dev/xvda:rwm'},
|
||||||
|
{'CgroupPermissions': 'rwm',
|
||||||
|
'PathInContainer': '/dev/sdb:/dev/xvdb',
|
||||||
|
'PathOnHost': '/dev/sdb:/dev/xvdb'},
|
||||||
|
{'CgroupPermissions': 'rwm',
|
||||||
|
'PathInContainer': '/dev/sdc',
|
||||||
|
'PathOnHost': '/dev/sdc'}]}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
args[1]['headers'],
|
||||||
|
{'Content-Type': 'application/json'}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
args[1]['timeout'],
|
||||||
|
docker.client.DEFAULT_TIMEOUT_SECONDS
|
||||||
|
)
|
||||||
|
|
||||||
def test_resize_container(self):
|
def test_resize_container(self):
|
||||||
try:
|
try:
|
||||||
self.client.resize(
|
self.client.resize(
|
||||||
|
@ -1064,6 +1123,51 @@ class DockerClientTest(Cleanup, unittest.TestCase):
|
||||||
timeout=(docker.client.DEFAULT_TIMEOUT_SECONDS + timeout)
|
timeout=(docker.client.DEFAULT_TIMEOUT_SECONDS + timeout)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_execute_command(self):
|
||||||
|
try:
|
||||||
|
self.client.execute(fake_api.FAKE_CONTAINER_ID, ['ls', '-1'])
|
||||||
|
except Exception as e:
|
||||||
|
self.fail('Command should not raise exception: {0}'.format(e))
|
||||||
|
|
||||||
|
args = fake_request.call_args
|
||||||
|
self.assertEqual(args[0][0],
|
||||||
|
url_prefix + 'exec/3cc2351ab11b/start')
|
||||||
|
|
||||||
|
self.assertEqual(json.loads(args[1]['data']),
|
||||||
|
json.loads('''{
|
||||||
|
"Tty": false,
|
||||||
|
"AttachStderr": true,
|
||||||
|
"Container": "3cc2351ab11b",
|
||||||
|
"Cmd": ["ls", "-1"],
|
||||||
|
"AttachStdin": false,
|
||||||
|
"User": "",
|
||||||
|
"Detach": false,
|
||||||
|
"Privileged": false,
|
||||||
|
"AttachStdout": true}'''))
|
||||||
|
|
||||||
|
self.assertEqual(args[1]['headers'],
|
||||||
|
{'Content-Type': 'application/json'})
|
||||||
|
|
||||||
|
def test_pause_container(self):
|
||||||
|
try:
|
||||||
|
self.client.pause(fake_api.FAKE_CONTAINER_ID)
|
||||||
|
except Exception as e:
|
||||||
|
self.fail('Command should not raise exception: {0}'.format(e))
|
||||||
|
fake_request.assert_called_with(
|
||||||
|
url_prefix + 'containers/3cc2351ab11b/pause',
|
||||||
|
timeout=(docker.client.DEFAULT_TIMEOUT_SECONDS)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_unpause_container(self):
|
||||||
|
try:
|
||||||
|
self.client.unpause(fake_api.FAKE_CONTAINER_ID)
|
||||||
|
except Exception as e:
|
||||||
|
self.fail('Command should not raise exception: {0}'.format(e))
|
||||||
|
fake_request.assert_called_with(
|
||||||
|
url_prefix + 'containers/3cc2351ab11b/unpause',
|
||||||
|
timeout=(docker.client.DEFAULT_TIMEOUT_SECONDS)
|
||||||
|
)
|
||||||
|
|
||||||
def test_kill_container(self):
|
def test_kill_container(self):
|
||||||
try:
|
try:
|
||||||
self.client.kill(fake_api.FAKE_CONTAINER_ID)
|
self.client.kill(fake_api.FAKE_CONTAINER_ID)
|
||||||
|
@ -1615,16 +1719,47 @@ class DockerClientTest(Cleanup, unittest.TestCase):
|
||||||
f.write("content")
|
f.write("content")
|
||||||
|
|
||||||
for exclude, names in (
|
for exclude, names in (
|
||||||
(['*.py'], ['bar/a.txt', 'bar/other.png',
|
(['*.py'], ['bar', 'bar/a.txt', 'bar/other.png',
|
||||||
'test/foo/a.txt', 'test/foo/other.png']),
|
'test', 'test/foo', 'test/foo/a.txt',
|
||||||
(['*.png', 'bar'], ['test/foo/a.txt', 'test/foo/b.py']),
|
'test/foo/other.png']),
|
||||||
(['test/foo', 'a.txt'], ['bar/a.txt', 'bar/b.py',
|
(['*.png', 'bar'], ['test', 'test/foo', 'test/foo/a.txt',
|
||||||
'bar/other.png']),
|
'test/foo/b.py']),
|
||||||
|
(['test/foo', 'a.txt'], ['bar', 'bar/a.txt', 'bar/b.py',
|
||||||
|
'bar/other.png', 'test']),
|
||||||
):
|
):
|
||||||
archive = docker.utils.tar(base, exclude=exclude)
|
archive = docker.utils.tar(base, exclude=exclude)
|
||||||
tar = tarfile.open(fileobj=archive)
|
tar = tarfile.open(fileobj=archive)
|
||||||
self.assertEqual(sorted(tar.getnames()), names)
|
self.assertEqual(sorted(tar.getnames()), names)
|
||||||
|
|
||||||
|
def test_tar_with_empty_directory(self):
|
||||||
|
base = tempfile.mkdtemp()
|
||||||
|
self.addCleanup(shutil.rmtree, base)
|
||||||
|
for d in ['foo', 'bar']:
|
||||||
|
os.makedirs(os.path.join(base, d))
|
||||||
|
archive = docker.utils.tar(base)
|
||||||
|
tar = tarfile.open(fileobj=archive)
|
||||||
|
self.assertEqual(sorted(tar.getnames()), ['bar', 'foo'])
|
||||||
|
|
||||||
|
def test_tar_with_file_symlinks(self):
|
||||||
|
base = tempfile.mkdtemp()
|
||||||
|
self.addCleanup(shutil.rmtree, base)
|
||||||
|
with open(os.path.join(base, 'foo'), 'w') as f:
|
||||||
|
f.write("content")
|
||||||
|
os.makedirs(os.path.join(base, 'bar'))
|
||||||
|
os.symlink('../foo', os.path.join(base, 'bar/foo'))
|
||||||
|
archive = docker.utils.tar(base)
|
||||||
|
tar = tarfile.open(fileobj=archive)
|
||||||
|
self.assertEqual(sorted(tar.getnames()), ['bar', 'bar/foo', 'foo'])
|
||||||
|
|
||||||
|
def test_tar_with_directory_symlinks(self):
|
||||||
|
base = tempfile.mkdtemp()
|
||||||
|
self.addCleanup(shutil.rmtree, base)
|
||||||
|
for d in ['foo', 'bar']:
|
||||||
|
os.makedirs(os.path.join(base, d))
|
||||||
|
os.symlink('../foo', os.path.join(base, 'bar/foo'))
|
||||||
|
archive = docker.utils.tar(base)
|
||||||
|
tar = tarfile.open(fileobj=archive)
|
||||||
|
self.assertEqual(sorted(tar.getnames()), ['bar', 'bar/foo', 'foo'])
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from docker.errors import DockerException
|
from docker.errors import DockerException
|
||||||
from docker.utils import parse_repository_tag, parse_host
|
from docker.utils import parse_repository_tag, parse_host, kwargs_from_env
|
||||||
|
from docker.client import Client
|
||||||
|
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
class UtilsTest(unittest.TestCase):
|
class UtilsTest(unittest.TestCase):
|
||||||
longMessage = True
|
longMessage = True
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.os_environ = os.environ.copy()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
os.environ = self.os_environ
|
||||||
|
|
||||||
def test_parse_repository_tag(self):
|
def test_parse_repository_tag(self):
|
||||||
self.assertEqual(parse_repository_tag("root"),
|
self.assertEqual(parse_repository_tag("root"),
|
||||||
("root", None))
|
("root", None))
|
||||||
|
@ -53,5 +63,25 @@ class UtilsTest(unittest.TestCase):
|
||||||
for host, expected in valid_hosts.items():
|
for host, expected in valid_hosts.items():
|
||||||
self.assertEqual(parse_host(host), expected, msg=host)
|
self.assertEqual(parse_host(host), expected, msg=host)
|
||||||
|
|
||||||
|
def test_kwargs_from_env(self):
|
||||||
|
os.environ.update(DOCKER_HOST='tcp://192.168.59.103:2376',
|
||||||
|
DOCKER_CERT_PATH=os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
'testdata/certs'),
|
||||||
|
DOCKER_TLS_VERIFY='1')
|
||||||
|
kwargs = kwargs_from_env(assert_hostname=False)
|
||||||
|
self.assertEquals('https://192.168.59.103:2376', kwargs['base_url'])
|
||||||
|
self.assertIn('ca.pem', kwargs['tls'].verify)
|
||||||
|
self.assertIn('cert.pem', kwargs['tls'].cert[0])
|
||||||
|
self.assertIn('key.pem', kwargs['tls'].cert[1])
|
||||||
|
self.assertEquals(False, kwargs['tls'].assert_hostname)
|
||||||
|
try:
|
||||||
|
client = Client(**kwargs)
|
||||||
|
self.assertEquals(kwargs['base_url'], client.base_url)
|
||||||
|
self.assertEquals(kwargs['tls'].verify, client.verify)
|
||||||
|
self.assertEquals(kwargs['tls'].cert, client.cert)
|
||||||
|
except TypeError, e:
|
||||||
|
self.fail(e)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue