mirror of https://github.com/docker/docker-py.git
Merge branch 'master' of github.com:docker/docker-py
This commit is contained in:
commit
f044b5e3f7
|
@ -526,7 +526,7 @@ class Client(requests.Session):
|
|||
'exec_start instead', DeprecationWarning
|
||||
)
|
||||
create_res = self.exec_create(
|
||||
container, cmd, detach, stdout, stderr, tty
|
||||
container, cmd, stdout, stderr, tty
|
||||
)
|
||||
|
||||
return self.exec_start(create_res, detach, tty, stream)
|
||||
|
@ -730,7 +730,7 @@ class Client(requests.Session):
|
|||
raise errors.DeprecatedMethod(
|
||||
'insert is not available for API version >=1.12'
|
||||
)
|
||||
api_url = self._url("/images/{0}/insert".fornat(image))
|
||||
api_url = self._url("/images/{0}/insert".format(image))
|
||||
params = {
|
||||
'url': url,
|
||||
'path': path
|
||||
|
@ -1059,6 +1059,12 @@ class Client(requests.Session):
|
|||
url = self._url("/containers/{0}/start".format(container))
|
||||
if not start_config:
|
||||
start_config = None
|
||||
elif utils.compare_version('1.15', self._version) > 0:
|
||||
warnings.warn(
|
||||
'Passing host config parameters in start() is deprecated. '
|
||||
'Please use host_config in create_container instead!',
|
||||
DeprecationWarning
|
||||
)
|
||||
res = self._post_json(url, data=start_config)
|
||||
self._raise_for_status(res)
|
||||
|
||||
|
|
|
@ -577,6 +577,7 @@ def create_container_config(
|
|||
'Entrypoint': entrypoint,
|
||||
'CpuShares': cpu_shares,
|
||||
'Cpuset': cpuset,
|
||||
'CpusetCpus': cpuset,
|
||||
'WorkingDir': working_dir,
|
||||
'MemorySwap': memswap_limit,
|
||||
'HostConfig': host_config,
|
||||
|
|
83
docs/api.md
83
docs/api.md
|
@ -188,8 +188,7 @@ 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 and above of the Docker remote API. Those
|
||||
arguments should be passed to `start()` instead, or as part of the `host_config`
|
||||
dictionary.
|
||||
arguments should be passed as part of the `host_config` dictionary.
|
||||
|
||||
**Params**:
|
||||
|
||||
|
@ -715,83 +714,9 @@ Identical to the `docker search` command.
|
|||
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
|
||||
* extra_hosts (dict): custom host-to-IP mappings (host:ip)
|
||||
* pid_mode (str): if set to "host", use the host PID namespace inside the
|
||||
container
|
||||
* security_opt (list): A list of string values to customize labels for MLS systems, such as SELinux.
|
||||
* ulimits (list): A list of dicts or `docker.utils.Ulimit` objects.
|
||||
**Deprecation warning:** For API version > 1.15, it is highly recommended to
|
||||
provide host config options in the
|
||||
[`host_config` parameter of `create_container`](#create_container)
|
||||
|
||||
```python
|
||||
>>> from docker import Client
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
# 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
|
||||
the devices parameter in the `host_config` param in `Client.create_container`
|
||||
as shown below:
|
||||
|
||||
```python
|
||||
c.start(container_id, devices=['/dev/sda:/dev/xvda:rwm'])
|
||||
c.create_container(
|
||||
'busybox', 'true', host_config=docker.utils.create_host_config(devices=[
|
||||
'/dev/sda:/dev/xvda:rwm'
|
||||
])
|
||||
)
|
||||
```
|
||||
|
||||
Each string is a single mapping using the colon (':') as the separator. So the
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# HostConfig object
|
||||
|
||||
The Docker Remote API introduced [support for HostConfig in version 1.15](http://docs.docker.com/reference/api/docker_remote_api_v1.15/#create-a-container). This object contains all the parameters you can pass to `Client.start`.
|
||||
The Docker Remote API introduced [support for HostConfig in version 1.15](http://docs.docker.com/reference/api/docker_remote_api_v1.15/#create-a-container). This object contains all the parameters you could previously pass to `Client.start`.
|
||||
*It is highly recommended that users pass the HostConfig in the `host_config`*
|
||||
*param of `Client.create_container` instead of `Client.start`*
|
||||
|
||||
## HostConfig helper
|
||||
|
||||
|
|
|
@ -1,38 +1,39 @@
|
|||
# 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.
|
||||
Bindings are declared in the `host_config` parameter.
|
||||
|
||||
```python
|
||||
container_id = c.create_container('busybox', 'ls', ports=[1111, 2222])
|
||||
container_id = c.create_container(
|
||||
'busybox', 'ls', ports=[1111, 2222],
|
||||
host_config=docker.utils.create_host_config(port_bindings={
|
||||
1111: 4567,
|
||||
2222: None
|
||||
})
|
||||
)
|
||||
```
|
||||
|
||||
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)})
|
||||
docker.utils.create_host_config(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',)})
|
||||
docker.utils.create_host_config(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:
|
||||
If you wish to use UDP instead of TCP (default), you need to declare ports
|
||||
as such in both the config and host config:
|
||||
|
||||
```python
|
||||
container_id = c.create_container(
|
||||
'busybox',
|
||||
'ls',
|
||||
ports=[(1111, 'udp'), 2222]
|
||||
'busybox', 'ls', ports=[(1111, 'udp'), 2222],
|
||||
host_config=docker.utils.create_host_config(port_bindings={
|
||||
'1111/udp': 4567, 2222: None
|
||||
})
|
||||
)
|
||||
c.start(container_id, port_bindings={'1111/udp': 4567, 2222: None})
|
||||
```
|
||||
|
||||
|
|
|
@ -1,25 +1,21 @@
|
|||
# Using volumes
|
||||
|
||||
Volume declaration is done in two parts. First, you have to provide
|
||||
a list of mountpoints to the `Client().create_container()` method.
|
||||
Volume declaration is done in two parts. Provide a list of mountpoints to
|
||||
the `Client().create_container()` method, and declare mappings in the
|
||||
`host_config` section.
|
||||
|
||||
```python
|
||||
container_id = 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/':
|
||||
{
|
||||
container_id = c.create_container(
|
||||
'busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2'],
|
||||
host_config=docker.utils.create_host_config(binds={
|
||||
'/home/user1/': {
|
||||
'bind': '/mnt/vol2',
|
||||
'ro': False
|
||||
},
|
||||
'/var/www':
|
||||
{
|
||||
'/var/www': {
|
||||
'bind': '/mnt/vol1',
|
||||
'ro': True
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
```
|
||||
|
|
|
@ -242,113 +242,6 @@ class TestCreateContainerWithRoBinds(BaseTestCase):
|
|||
self.assertFalse(inspect_data['VolumesRW'][mount_dest])
|
||||
|
||||
|
||||
class TestStartContainerWithBinds(BaseTestCase):
|
||||
def runTest(self):
|
||||
mount_dest = '/mnt'
|
||||
mount_origin = tempfile.mkdtemp()
|
||||
self.tmp_folders.append(mount_origin)
|
||||
|
||||
filename = 'shared.txt'
|
||||
shared_file = os.path.join(mount_origin, filename)
|
||||
binds = {
|
||||
mount_origin: {
|
||||
'bind': mount_dest,
|
||||
'ro': False,
|
||||
},
|
||||
}
|
||||
|
||||
with open(shared_file, 'w'):
|
||||
container = self.client.create_container(
|
||||
'busybox', ['ls', mount_dest], volumes={mount_dest: {}}
|
||||
)
|
||||
container_id = container['Id']
|
||||
self.client.start(container_id, binds=binds)
|
||||
self.tmp_containers.append(container_id)
|
||||
exitcode = self.client.wait(container_id)
|
||||
self.assertEqual(exitcode, 0)
|
||||
logs = self.client.logs(container_id)
|
||||
|
||||
os.unlink(shared_file)
|
||||
if six.PY3:
|
||||
logs = logs.decode('utf-8')
|
||||
self.assertIn(filename, logs)
|
||||
inspect_data = self.client.inspect_container(container_id)
|
||||
self.assertIn('Volumes', inspect_data)
|
||||
self.assertIn(mount_dest, inspect_data['Volumes'])
|
||||
self.assertEqual(mount_origin, inspect_data['Volumes'][mount_dest])
|
||||
self.assertIn(mount_dest, inspect_data['VolumesRW'])
|
||||
self.assertTrue(inspect_data['VolumesRW'][mount_dest])
|
||||
|
||||
|
||||
class TestStartContainerWithRoBinds(BaseTestCase):
|
||||
def runTest(self):
|
||||
mount_dest = '/mnt'
|
||||
mount_origin = tempfile.mkdtemp()
|
||||
self.tmp_folders.append(mount_origin)
|
||||
|
||||
filename = 'shared.txt'
|
||||
shared_file = os.path.join(mount_origin, filename)
|
||||
binds = {
|
||||
mount_origin: {
|
||||
'bind': mount_dest,
|
||||
'ro': True,
|
||||
},
|
||||
}
|
||||
|
||||
with open(shared_file, 'w'):
|
||||
container = self.client.create_container(
|
||||
'busybox', ['ls', mount_dest], volumes={mount_dest: {}}
|
||||
)
|
||||
container_id = container['Id']
|
||||
self.client.start(container_id, binds=binds)
|
||||
self.tmp_containers.append(container_id)
|
||||
exitcode = self.client.wait(container_id)
|
||||
self.assertEqual(exitcode, 0)
|
||||
logs = self.client.logs(container_id)
|
||||
|
||||
os.unlink(shared_file)
|
||||
if six.PY3:
|
||||
logs = logs.decode('utf-8')
|
||||
self.assertIn(filename, logs)
|
||||
|
||||
inspect_data = self.client.inspect_container(container_id)
|
||||
self.assertIn('Volumes', inspect_data)
|
||||
self.assertIn(mount_dest, inspect_data['Volumes'])
|
||||
self.assertEqual(mount_origin, inspect_data['Volumes'][mount_dest])
|
||||
self.assertIn('VolumesRW', inspect_data)
|
||||
self.assertIn(mount_dest, inspect_data['VolumesRW'])
|
||||
self.assertFalse(inspect_data['VolumesRW'][mount_dest])
|
||||
|
||||
|
||||
class TestStartContainerWithFileBind(BaseTestCase):
|
||||
def runTest(self):
|
||||
mount_dest = '/myfile/myfile'
|
||||
mount_origin = tempfile.mktemp()
|
||||
try:
|
||||
binds = {
|
||||
mount_origin: {
|
||||
'bind': mount_dest,
|
||||
'ro': True
|
||||
},
|
||||
}
|
||||
|
||||
with open(mount_origin, 'w') as f:
|
||||
f.write('sakuya izayoi')
|
||||
|
||||
container = self.client.create_container(
|
||||
'busybox', ['cat', mount_dest], volumes={mount_dest: {}}
|
||||
)
|
||||
self.client.start(container, binds=binds)
|
||||
exitcode = self.client.wait(container)
|
||||
self.assertEqual(exitcode, 0)
|
||||
logs = self.client.logs(container)
|
||||
if six.PY3:
|
||||
logs = logs.decode('utf-8')
|
||||
self.assertIn('sakuya izayoi', logs)
|
||||
finally:
|
||||
os.unlink(mount_origin)
|
||||
|
||||
|
||||
class TestCreateContainerWithLogConfig(BaseTestCase):
|
||||
def runTest(self):
|
||||
config = docker.utils.LogConfig(
|
||||
|
@ -388,21 +281,6 @@ class TestCreateContainerReadOnlyFs(BaseTestCase):
|
|||
self.assertNotEqual(res, 0)
|
||||
|
||||
|
||||
@unittest.skipIf(not EXEC_DRIVER_IS_NATIVE, 'Exec driver not native')
|
||||
class TestStartContainerReadOnlyFs(BaseTestCase):
|
||||
def runTest(self):
|
||||
# Presumably a bug in 1.5.0
|
||||
# https://github.com/docker/docker/issues/10695
|
||||
ctnr = self.client.create_container(
|
||||
'busybox', ['mkdir', '/shrine'],
|
||||
)
|
||||
self.assertIn('Id', ctnr)
|
||||
self.tmp_containers.append(ctnr['Id'])
|
||||
self.client.start(ctnr, read_only=True)
|
||||
# res = self.client.wait(ctnr)
|
||||
# self.assertNotEqual(res, 0)
|
||||
|
||||
|
||||
class TestCreateContainerWithName(BaseTestCase):
|
||||
def runTest(self):
|
||||
res = self.client.create_container('busybox', 'true', name='foobar')
|
||||
|
@ -490,29 +368,6 @@ class TestCreateContainerPrivileged(BaseTestCase):
|
|||
self.assertEqual(inspect['Config']['Privileged'], True)
|
||||
|
||||
|
||||
class TestStartContainerPrivileged(BaseTestCase):
|
||||
def runTest(self):
|
||||
res = self.client.create_container('busybox', 'true')
|
||||
self.assertIn('Id', res)
|
||||
self.tmp_containers.append(res['Id'])
|
||||
self.client.start(res['Id'], privileged=True)
|
||||
inspect = self.client.inspect_container(res['Id'])
|
||||
self.assertIn('Config', inspect)
|
||||
self.assertIn('Id', inspect)
|
||||
self.assertTrue(inspect['Id'].startswith(res['Id']))
|
||||
self.assertIn('Image', inspect)
|
||||
self.assertIn('State', inspect)
|
||||
self.assertIn('Running', inspect['State'])
|
||||
if not inspect['State']['Running']:
|
||||
self.assertIn('ExitCode', inspect['State'])
|
||||
self.assertEqual(inspect['State']['ExitCode'], 0)
|
||||
# Since Nov 2013, the Privileged flag is no longer part of the
|
||||
# container's config exposed via the API (safety concerns?).
|
||||
#
|
||||
if 'Privileged' in inspect['Config']:
|
||||
self.assertEqual(inspect['Config']['Privileged'], True)
|
||||
|
||||
|
||||
class TestWait(BaseTestCase):
|
||||
def runTest(self):
|
||||
res = self.client.create_container('busybox', ['sleep', '3'])
|
||||
|
@ -754,34 +609,6 @@ class TestPort(BaseTestCase):
|
|||
self.client.kill(id)
|
||||
|
||||
|
||||
class TestStartWithPortBindings(BaseTestCase):
|
||||
def runTest(self):
|
||||
|
||||
port_bindings = {
|
||||
'1111': ('127.0.0.1', '4567'),
|
||||
'2222': ('127.0.0.1', '4568')
|
||||
}
|
||||
|
||||
container = self.client.create_container(
|
||||
'busybox', ['sleep', '60'], ports=list(port_bindings.keys())
|
||||
)
|
||||
id = container['Id']
|
||||
|
||||
self.client.start(container, port_bindings=port_bindings)
|
||||
|
||||
# Call the port function on each biding and compare expected vs actual
|
||||
for port in port_bindings:
|
||||
actual_bindings = self.client.port(container, port)
|
||||
port_binding = actual_bindings.pop()
|
||||
|
||||
ip, host_port = port_binding['HostIp'], port_binding['HostPort']
|
||||
|
||||
self.assertEqual(ip, port_bindings[port][0])
|
||||
self.assertEqual(host_port, port_bindings[port][1])
|
||||
|
||||
self.client.kill(id)
|
||||
|
||||
|
||||
class TestMacAddress(BaseTestCase):
|
||||
def runTest(self):
|
||||
mac_address_expected = "02:42:ac:11:00:0a"
|
||||
|
@ -949,101 +776,6 @@ class TestCreateContainerWithLinks(BaseTestCase):
|
|||
self.assertIn('{0}_ENV_FOO=1'.format(link_env_prefix2), logs)
|
||||
|
||||
|
||||
class TestStartContainerWithVolumesFrom(BaseTestCase):
|
||||
def runTest(self):
|
||||
vol_names = ['foobar_vol0', 'foobar_vol1']
|
||||
|
||||
res0 = self.client.create_container(
|
||||
'busybox', 'true',
|
||||
name=vol_names[0])
|
||||
container1_id = res0['Id']
|
||||
self.tmp_containers.append(container1_id)
|
||||
self.client.start(container1_id)
|
||||
|
||||
res1 = self.client.create_container(
|
||||
'busybox', 'true',
|
||||
name=vol_names[1])
|
||||
container2_id = res1['Id']
|
||||
self.tmp_containers.append(container2_id)
|
||||
self.client.start(container2_id)
|
||||
with self.assertRaises(docker.errors.DockerException):
|
||||
res2 = self.client.create_container(
|
||||
'busybox', 'cat',
|
||||
detach=True, stdin_open=True,
|
||||
volumes_from=vol_names)
|
||||
res2 = self.client.create_container(
|
||||
'busybox', 'cat',
|
||||
detach=True, stdin_open=True)
|
||||
container3_id = res2['Id']
|
||||
self.tmp_containers.append(container3_id)
|
||||
self.client.start(container3_id, volumes_from=vol_names)
|
||||
|
||||
info = self.client.inspect_container(res2['Id'])
|
||||
self.assertCountEqual(info['HostConfig']['VolumesFrom'], vol_names)
|
||||
|
||||
|
||||
class TestStartContainerWithUlimits(BaseTestCase):
|
||||
def runTest(self):
|
||||
ulimit = docker.utils.Ulimit(name='nofile', soft=4096, hard=4096)
|
||||
|
||||
res0 = self.client.create_container('busybox', 'true')
|
||||
container1_id = res0['Id']
|
||||
self.tmp_containers.append(container1_id)
|
||||
self.client.start(container1_id, ulimits=[ulimit])
|
||||
|
||||
info = self.client.inspect_container(container1_id)
|
||||
self.assertCountEqual(info['HostConfig']['Ulimits'], [ulimit])
|
||||
|
||||
|
||||
class TestStartContainerWithLinks(BaseTestCase):
|
||||
def runTest(self):
|
||||
res0 = self.client.create_container(
|
||||
'busybox', 'cat',
|
||||
detach=True, stdin_open=True,
|
||||
environment={'FOO': '1'})
|
||||
|
||||
container1_id = res0['Id']
|
||||
self.tmp_containers.append(container1_id)
|
||||
|
||||
self.client.start(container1_id)
|
||||
|
||||
res1 = self.client.create_container(
|
||||
'busybox', 'cat',
|
||||
detach=True, stdin_open=True,
|
||||
environment={'FOO': '1'})
|
||||
|
||||
container2_id = res1['Id']
|
||||
self.tmp_containers.append(container2_id)
|
||||
|
||||
self.client.start(container2_id)
|
||||
|
||||
# we don't want the first /
|
||||
link_path1 = self.client.inspect_container(container1_id)['Name'][1:]
|
||||
link_alias1 = 'mylink1'
|
||||
link_env_prefix1 = link_alias1.upper()
|
||||
|
||||
link_path2 = self.client.inspect_container(container2_id)['Name'][1:]
|
||||
link_alias2 = 'mylink2'
|
||||
link_env_prefix2 = link_alias2.upper()
|
||||
|
||||
res2 = self.client.create_container('busybox', 'env')
|
||||
container3_id = res2['Id']
|
||||
self.tmp_containers.append(container3_id)
|
||||
self.client.start(
|
||||
container3_id,
|
||||
links={link_path1: link_alias1, link_path2: link_alias2}
|
||||
)
|
||||
self.assertEqual(self.client.wait(container3_id), 0)
|
||||
|
||||
logs = self.client.logs(container3_id)
|
||||
if six.PY3:
|
||||
logs = logs.decode('utf-8')
|
||||
self.assertIn('{0}_NAME='.format(link_env_prefix1), logs)
|
||||
self.assertIn('{0}_ENV_FOO=1'.format(link_env_prefix1), logs)
|
||||
self.assertIn('{0}_NAME='.format(link_env_prefix2), logs)
|
||||
self.assertIn('{0}_ENV_FOO=1'.format(link_env_prefix2), logs)
|
||||
|
||||
|
||||
class TestRestartingContainer(BaseTestCase):
|
||||
def runTest(self):
|
||||
container = self.client.create_container(
|
||||
|
@ -1190,20 +922,6 @@ class TestCreateContainerWithHostPidMode(BaseTestCase):
|
|||
self.assertEqual(host_config['PidMode'], 'host')
|
||||
|
||||
|
||||
class TestStartContainerWithHostPidMode(BaseTestCase):
|
||||
def runTest(self):
|
||||
ctnr = self.client.create_container(
|
||||
'busybox', 'true'
|
||||
)
|
||||
self.assertIn('Id', ctnr)
|
||||
self.tmp_containers.append(ctnr['Id'])
|
||||
self.client.start(ctnr, pid_mode='host')
|
||||
inspect = self.client.inspect_container(ctnr)
|
||||
self.assertIn('HostConfig', inspect)
|
||||
host_config = inspect['HostConfig']
|
||||
self.assertIn('PidMode', host_config)
|
||||
self.assertEqual(host_config['PidMode'], 'host')
|
||||
|
||||
#################
|
||||
# LINKS TESTS #
|
||||
#################
|
||||
|
|
402
tests/test.py
402
tests/test.py
|
@ -47,6 +47,7 @@ except ImportError:
|
|||
DEFAULT_TIMEOUT_SECONDS = docker.client.constants.DEFAULT_TIMEOUT_SECONDS
|
||||
|
||||
warnings.simplefilter('error')
|
||||
warnings.filterwarnings('error')
|
||||
create_host_config = docker.utils.create_host_config
|
||||
|
||||
|
||||
|
@ -489,6 +490,7 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
|
|||
"StdinOnce": false,
|
||||
"NetworkDisabled": false,
|
||||
"Cpuset": "0,1",
|
||||
"CpusetCpus": "0,1",
|
||||
"MemorySwap": 0}'''))
|
||||
self.assertEqual(args[1]['headers'],
|
||||
{'Content-Type': 'application/json'})
|
||||
|
@ -972,244 +974,210 @@ class DockerClientTest(Cleanup, base.BaseTestCase):
|
|||
)
|
||||
|
||||
def test_start_container_with_lxc_conf(self):
|
||||
try:
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID,
|
||||
lxc_conf={'lxc.conf.k': 'lxc.conf.value'}
|
||||
)
|
||||
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']),
|
||||
{"LxcConf": [{"Value": "lxc.conf.value", "Key": "lxc.conf.k"}]}
|
||||
)
|
||||
self.assertEqual(
|
||||
args[1]['headers'],
|
||||
{'Content-Type': 'application/json'}
|
||||
)
|
||||
self.assertEqual(
|
||||
args[1]['timeout'],
|
||||
DEFAULT_TIMEOUT_SECONDS
|
||||
)
|
||||
if six.PY2:
|
||||
try:
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID,
|
||||
lxc_conf={'lxc.conf.k': 'lxc.conf.value'}
|
||||
)
|
||||
except DeprecationWarning as e:
|
||||
return
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
else:
|
||||
self.fail('Expected a DeprecationWarning')
|
||||
else:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID,
|
||||
lxc_conf={'lxc.conf.k': 'lxc.conf.value'}
|
||||
)
|
||||
|
||||
def test_start_container_with_lxc_conf_compat(self):
|
||||
try:
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID,
|
||||
lxc_conf=[{'Key': 'lxc.conf.k', 'Value': 'lxc.conf.value'}]
|
||||
)
|
||||
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']),
|
||||
{"LxcConf": [{"Key": "lxc.conf.k", "Value": "lxc.conf.value"}]}
|
||||
)
|
||||
self.assertEqual(args[1]['headers'],
|
||||
{'Content-Type': 'application/json'})
|
||||
self.assertEqual(
|
||||
args[1]['timeout'],
|
||||
DEFAULT_TIMEOUT_SECONDS
|
||||
)
|
||||
if six.PY2:
|
||||
try:
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID,
|
||||
lxc_conf=[{'Key': 'lxc.conf.k', 'Value': 'lxc.conf.value'}]
|
||||
)
|
||||
except DeprecationWarning as e:
|
||||
return
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
else:
|
||||
self.fail('Expected a DeprecationWarning')
|
||||
else:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID,
|
||||
lxc_conf=[{'Key': 'lxc.conf.k', 'Value': 'lxc.conf.value'}]
|
||||
)
|
||||
|
||||
def test_start_container_with_binds_ro(self):
|
||||
try:
|
||||
mount_dest = '/mnt'
|
||||
mount_origin = '/tmp'
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID,
|
||||
binds={mount_origin: {
|
||||
"bind": mount_dest,
|
||||
"ro": True
|
||||
}})
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
mount_dest = '/mnt'
|
||||
mount_origin = '/tmp'
|
||||
|
||||
args = fake_request.call_args
|
||||
self.assertEqual(args[0][0], url_prefix +
|
||||
'containers/3cc2351ab11b/start')
|
||||
self.assertEqual(
|
||||
json.loads(args[1]['data']), {"Binds": ["/tmp:/mnt:ro"]}
|
||||
)
|
||||
self.assertEqual(args[1]['headers'],
|
||||
{'Content-Type': 'application/json'})
|
||||
self.assertEqual(
|
||||
args[1]['timeout'],
|
||||
DEFAULT_TIMEOUT_SECONDS)
|
||||
if six.PY2:
|
||||
try:
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID, binds={
|
||||
mount_origin: {
|
||||
"bind": mount_dest,
|
||||
"ro": True
|
||||
}
|
||||
}
|
||||
)
|
||||
except DeprecationWarning as e:
|
||||
return
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
else:
|
||||
self.fail('Expected a DeprecationWarning')
|
||||
else:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID, binds={
|
||||
mount_origin: {
|
||||
"bind": mount_dest,
|
||||
"ro": True
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def test_start_container_with_binds_rw(self):
|
||||
try:
|
||||
mount_dest = '/mnt'
|
||||
mount_origin = '/tmp'
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID,
|
||||
binds={mount_origin: {
|
||||
"bind": mount_dest, "ro": False}})
|
||||
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']), {"Binds": ["/tmp:/mnt:rw"]}
|
||||
)
|
||||
self.assertEqual(args[1]['headers'],
|
||||
{'Content-Type': 'application/json'})
|
||||
self.assertEqual(
|
||||
args[1]['timeout'],
|
||||
DEFAULT_TIMEOUT_SECONDS
|
||||
)
|
||||
mount_dest = '/mnt'
|
||||
mount_origin = '/tmp'
|
||||
if six.PY2:
|
||||
try:
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID, binds={
|
||||
mount_origin: {"bind": mount_dest, "ro": False}
|
||||
}
|
||||
)
|
||||
except DeprecationWarning as e:
|
||||
return
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
else:
|
||||
self.fail('Expected a DeprecationWarning')
|
||||
else:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID, binds={
|
||||
mount_origin: {"bind": mount_dest, "ro": False}
|
||||
}
|
||||
)
|
||||
|
||||
def test_start_container_with_port_binds(self):
|
||||
self.maxDiff = None
|
||||
try:
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID, port_bindings={
|
||||
1111: None,
|
||||
2222: 2222,
|
||||
'3333/udp': (3333,),
|
||||
4444: ('127.0.0.1',),
|
||||
5555: ('127.0.0.1', 5555),
|
||||
6666: [('127.0.0.1',), ('192.168.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/3cc2351ab11b/start')
|
||||
data = json.loads(args[1]['data'])
|
||||
self.assertTrue('1111/tcp' in data['PortBindings'])
|
||||
self.assertTrue('2222/tcp' in data['PortBindings'])
|
||||
self.assertTrue('3333/udp' in data['PortBindings'])
|
||||
self.assertTrue('4444/tcp' in data['PortBindings'])
|
||||
self.assertTrue('5555/tcp' in data['PortBindings'])
|
||||
self.assertTrue('6666/tcp' in data['PortBindings'])
|
||||
self.assertEqual(
|
||||
[{"HostPort": "", "HostIp": ""}],
|
||||
data['PortBindings']['1111/tcp']
|
||||
)
|
||||
self.assertEqual(
|
||||
[{"HostPort": "2222", "HostIp": ""}],
|
||||
data['PortBindings']['2222/tcp']
|
||||
)
|
||||
self.assertEqual(
|
||||
[{"HostPort": "3333", "HostIp": ""}],
|
||||
data['PortBindings']['3333/udp']
|
||||
)
|
||||
self.assertEqual(
|
||||
[{"HostPort": "", "HostIp": "127.0.0.1"}],
|
||||
data['PortBindings']['4444/tcp']
|
||||
)
|
||||
self.assertEqual(
|
||||
[{"HostPort": "5555", "HostIp": "127.0.0.1"}],
|
||||
data['PortBindings']['5555/tcp']
|
||||
)
|
||||
self.assertEqual(len(data['PortBindings']['6666/tcp']), 2)
|
||||
self.assertEqual(args[1]['headers'],
|
||||
{'Content-Type': 'application/json'})
|
||||
self.assertEqual(
|
||||
args[1]['timeout'],
|
||||
DEFAULT_TIMEOUT_SECONDS
|
||||
)
|
||||
if six.PY2:
|
||||
try:
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID, port_bindings={
|
||||
1111: None,
|
||||
2222: 2222,
|
||||
'3333/udp': (3333,),
|
||||
4444: ('127.0.0.1',),
|
||||
5555: ('127.0.0.1', 5555),
|
||||
6666: [('127.0.0.1',), ('192.168.0.1',)]
|
||||
})
|
||||
except DeprecationWarning as e:
|
||||
return
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
else:
|
||||
self.fail('Expected a DeprecationWarning')
|
||||
else:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID, port_bindings={
|
||||
1111: None,
|
||||
2222: 2222,
|
||||
'3333/udp': (3333,),
|
||||
4444: ('127.0.0.1',),
|
||||
5555: ('127.0.0.1', 5555),
|
||||
6666: [('127.0.0.1',), ('192.168.0.1',)]
|
||||
})
|
||||
|
||||
def test_start_container_with_links(self):
|
||||
# one link
|
||||
try:
|
||||
link_path = 'path'
|
||||
alias = 'alias'
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID,
|
||||
links={link_path: alias})
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
link_path = 'path'
|
||||
alias = 'alias'
|
||||
|
||||
args = fake_request.call_args
|
||||
self.assertEqual(
|
||||
args[0][0],
|
||||
url_prefix + 'containers/3cc2351ab11b/start'
|
||||
)
|
||||
self.assertEqual(
|
||||
json.loads(args[1]['data']), {"Links": ["path:alias"]}
|
||||
)
|
||||
self.assertEqual(
|
||||
args[1]['headers'],
|
||||
{'Content-Type': 'application/json'}
|
||||
)
|
||||
if six.PY2:
|
||||
try:
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID,
|
||||
links={link_path: alias})
|
||||
except DeprecationWarning as e:
|
||||
return
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
else:
|
||||
self.fail('Expected a DeprecationWarning')
|
||||
else:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID, links={link_path: alias}
|
||||
)
|
||||
|
||||
def test_start_container_with_multiple_links(self):
|
||||
try:
|
||||
link_path = 'path'
|
||||
alias = 'alias'
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID,
|
||||
links={
|
||||
link_path + '1': alias + '1',
|
||||
link_path + '2': alias + '2'
|
||||
}
|
||||
)
|
||||
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']),
|
||||
{"Links": ["path1:alias1", "path2:alias2"]}
|
||||
)
|
||||
self.assertEqual(
|
||||
args[1]['headers'],
|
||||
{'Content-Type': 'application/json'}
|
||||
)
|
||||
link_path = 'path'
|
||||
alias = 'alias'
|
||||
if six.PY2:
|
||||
try:
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID,
|
||||
links={
|
||||
link_path + '1': alias + '1',
|
||||
link_path + '2': alias + '2'
|
||||
}
|
||||
)
|
||||
except DeprecationWarning as e:
|
||||
return
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
else:
|
||||
self.fail('Expected a DeprecationWarning')
|
||||
else:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.client.start(
|
||||
fake_api.FAKE_CONTAINER_ID,
|
||||
links={
|
||||
link_path + '1': alias + '1',
|
||||
link_path + '2': alias + '2'
|
||||
}
|
||||
)
|
||||
|
||||
def test_start_container_with_links_as_list_of_tuples(self):
|
||||
# one link
|
||||
try:
|
||||
link_path = 'path'
|
||||
alias = 'alias'
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID,
|
||||
links=[(link_path, alias)])
|
||||
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']), {"Links": ["path:alias"]}
|
||||
)
|
||||
self.assertEqual(
|
||||
args[1]['headers'], {'Content-Type': 'application/json'}
|
||||
)
|
||||
link_path = 'path'
|
||||
alias = 'alias'
|
||||
if six.PY2:
|
||||
try:
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID,
|
||||
links=[(link_path, alias)])
|
||||
except DeprecationWarning as e:
|
||||
return
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
else:
|
||||
self.fail('Expected a DeprecationWarning')
|
||||
else:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID,
|
||||
links=[(link_path, alias)])
|
||||
|
||||
def test_start_container_privileged(self):
|
||||
try:
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID, privileged=True)
|
||||
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']), {"Privileged": True})
|
||||
self.assertEqual(args[1]['headers'],
|
||||
{'Content-Type': 'application/json'})
|
||||
self.assertEqual(
|
||||
args[1]['timeout'],
|
||||
DEFAULT_TIMEOUT_SECONDS
|
||||
)
|
||||
if six.PY2:
|
||||
try:
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID, privileged=True)
|
||||
except DeprecationWarning as e:
|
||||
return
|
||||
except Exception as e:
|
||||
self.fail('Command should not raise exception: {0}'.format(e))
|
||||
else:
|
||||
self.fail('Expected a DeprecationWarning')
|
||||
else:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.client.start(fake_api.FAKE_CONTAINER_ID, privileged=True)
|
||||
|
||||
def test_start_container_with_dict_instead_of_id(self):
|
||||
try:
|
||||
|
|
Loading…
Reference in New Issue