Skip to content

Commit ec5a28a

Browse files
committed
Merge pull request #428 from docker/host_config
Support for host config
2 parents f2ba7f9 + 5711ac4 commit ec5a28a

10 files changed

+772
-170
lines changed

docker/client.py

+36-19
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
if not six.PY3:
3535
import websocket
3636

37-
DEFAULT_DOCKER_API_VERSION = '1.15'
37+
DEFAULT_DOCKER_API_VERSION = '1.16'
3838
DEFAULT_TIMEOUT_SECONDS = 60
3939
STREAM_HEADER_SIZE_BYTES = 8
4040

@@ -110,7 +110,8 @@ def _container_config(self, image, command, hostname=None, user=None,
110110
volumes=None, volumes_from=None,
111111
network_disabled=False, entrypoint=None,
112112
cpu_shares=None, working_dir=None,
113-
domainname=None, memswap_limit=0, cpuset=None):
113+
domainname=None, memswap_limit=0, cpuset=None,
114+
host_config=None):
114115
if isinstance(command, six.string_types):
115116
command = shlex.split(str(command))
116117
if isinstance(environment, dict):
@@ -225,7 +226,8 @@ def _container_config(self, image, command, hostname=None, user=None,
225226
'CpuShares': cpu_shares,
226227
'Cpuset': cpuset,
227228
'WorkingDir': working_dir,
228-
'MemorySwap': memswap_limit
229+
'MemorySwap': memswap_limit,
230+
'HostConfig': host_config
229231
}
230232

231233
def _post_json(self, url, data, **kwargs):
@@ -263,6 +265,12 @@ def _attach_websocket(self, container, params=None):
263265
def _create_websocket_connection(self, url):
264266
return websocket.create_connection(url)
265267

268+
def _warn_deprecated(self, arg_name, version):
269+
warning_message = (
270+
'{0!r} is deprecated for API version >= {1}'
271+
).format(arg_name, version)
272+
warnings.warn(warning_message, DeprecationWarning)
273+
266274
def _get_raw_response_socket(self, response):
267275
self._raise_for_status(response)
268276
if six.PY3:
@@ -536,17 +544,20 @@ def create_container(self, image, command=None, hostname=None, user=None,
536544
mem_limit=0, ports=None, environment=None, dns=None,
537545
volumes=None, volumes_from=None,
538546
network_disabled=False, name=None, entrypoint=None,
539-
cpu_shares=None, working_dir=None,
540-
domainname=None, memswap_limit=0, cpuset=None):
547+
cpu_shares=None, working_dir=None, domainname=None,
548+
memswap_limit=0, cpuset=None, host_config=None):
541549

542550
if isinstance(volumes, six.string_types):
543551
volumes = [volumes, ]
544552

553+
if host_config and utils.compare_version('1.15', self._version) < 0:
554+
raise errors.APIError('host_config is not supported in API < 1.15')
555+
545556
config = self._container_config(
546557
image, command, hostname, user, detach, stdin_open, tty, mem_limit,
547558
ports, environment, dns, volumes, volumes_from, network_disabled,
548559
entrypoint, cpu_shares, working_dir, domainname,
549-
memswap_limit, cpuset
560+
memswap_limit, cpuset, host_config
550561
)
551562
return self.create_container_from_config(config, name)
552563

@@ -570,7 +581,7 @@ def events(self):
570581
def execute(self, container, cmd, detach=False, stdout=True, stderr=True,
571582
stream=False, tty=False):
572583
if utils.compare_version('1.15', self._version) < 0:
573-
raise Exception('Exec is not supported in API < 1.15!')
584+
raise errors.APIError('Exec is not supported in API < 1.15')
574585
if isinstance(container, dict):
575586
container = container.get('Id')
576587
if isinstance(cmd, six.string_types):
@@ -911,6 +922,9 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
911922
dns=None, dns_search=None, volumes_from=None, network_mode=None,
912923
restart_policy=None, cap_add=None, cap_drop=None, devices=None,
913924
extra_hosts=None):
925+
926+
start_config = {}
927+
914928
if isinstance(container, dict):
915929
container = container.get('Id')
916930

@@ -920,9 +934,9 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
920934
formatted.append({'Key': k, 'Value': str(v)})
921935
lxc_conf = formatted
922936

923-
start_config = {
924-
'LxcConf': lxc_conf
925-
}
937+
if lxc_conf:
938+
start_config['LxcConf'] = lxc_conf
939+
926940
if binds:
927941
start_config['Binds'] = utils.convert_volume_binds(binds)
928942

@@ -931,7 +945,8 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
931945
port_bindings
932946
)
933947

934-
start_config['PublishAllPorts'] = publish_all_ports
948+
if publish_all_ports:
949+
start_config['PublishAllPorts'] = publish_all_ports
935950

936951
if links:
937952
if isinstance(links, dict):
@@ -953,7 +968,8 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
953968

954969
start_config['ExtraHosts'] = formatted_extra_hosts
955970

956-
start_config['Privileged'] = privileged
971+
if privileged:
972+
start_config['Privileged'] = privileged
957973

958974
if utils.compare_version('1.10', self._version) >= 0:
959975
if dns is not None:
@@ -963,16 +979,15 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
963979
volumes_from = volumes_from.split(',')
964980
start_config['VolumesFrom'] = volumes_from
965981
else:
966-
warning_message = ('{0!r} parameter is discarded. It is only'
967-
' available for API version greater or equal'
968-
' than 1.10')
969982

970983
if dns is not None:
971-
warnings.warn(warning_message.format('dns'),
972-
DeprecationWarning)
984+
raise errors.APIError(
985+
'dns is only supported for API version >= 1.10'
986+
)
973987
if volumes_from is not None:
974-
warnings.warn(warning_message.format('volumes_from'),
975-
DeprecationWarning)
988+
raise errors.APIError(
989+
'volumes_from is only supported for API version >= 1.10'
990+
)
976991
if dns_search:
977992
start_config['DnsSearch'] = dns_search
978993

@@ -992,6 +1007,8 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
9921007
start_config['Devices'] = utils.parse_devices(devices)
9931008

9941009
url = self._url("/containers/{0}/start".format(container))
1010+
if not start_config:
1011+
start_config = None
9951012
res = self._post_json(url, data=start_config)
9961013
self._raise_for_status(res)
9971014

docker/utils/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from .utils import (
22
compare_version, convert_port_bindings, convert_volume_binds,
33
mkbuildcontext, ping, tar, parse_repository_tag, parse_host,
4-
kwargs_from_env, convert_filters
4+
kwargs_from_env, convert_filters, create_host_config
55
) # flake8: noqa

docker/utils/utils.py

+67
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,70 @@ def convert_filters(filters):
294294
v = [v, ]
295295
result[k] = v
296296
return json.dumps(result)
297+
298+
299+
def create_host_config(
300+
binds=None, port_bindings=None, lxc_conf=None,
301+
publish_all_ports=False, links=None, privileged=False,
302+
dns=None, dns_search=None, volumes_from=None, network_mode=None,
303+
restart_policy=None, cap_add=None, cap_drop=None, devices=None
304+
):
305+
host_config = {
306+
'Privileged': privileged,
307+
'PublishAllPorts': publish_all_ports,
308+
}
309+
310+
if dns_search:
311+
host_config['DnsSearch'] = dns_search
312+
313+
if network_mode:
314+
host_config['NetworkMode'] = network_mode
315+
316+
if restart_policy:
317+
host_config['RestartPolicy'] = restart_policy
318+
319+
if cap_add:
320+
host_config['CapAdd'] = cap_add
321+
322+
if cap_drop:
323+
host_config['CapDrop'] = cap_drop
324+
325+
if devices:
326+
host_config['Devices'] = parse_devices(devices)
327+
328+
if dns is not None:
329+
host_config['Dns'] = dns
330+
331+
if volumes_from is not None:
332+
if isinstance(volumes_from, six.string_types):
333+
volumes_from = volumes_from.split(',')
334+
host_config['VolumesFrom'] = volumes_from
335+
336+
if binds:
337+
host_config['Binds'] = convert_volume_binds(binds)
338+
339+
if port_bindings:
340+
host_config['PortBindings'] = convert_port_bindings(
341+
port_bindings
342+
)
343+
344+
host_config['PublishAllPorts'] = publish_all_ports
345+
346+
if links:
347+
if isinstance(links, dict):
348+
links = six.iteritems(links)
349+
350+
formatted_links = [
351+
'{0}:{1}'.format(k, v) for k, v in sorted(links)
352+
]
353+
354+
host_config['Links'] = formatted_links
355+
356+
if isinstance(lxc_conf, dict):
357+
formatted = []
358+
for k, v in six.iteritems(lxc_conf):
359+
formatted.append({'Key': k, 'Value': str(v)})
360+
lxc_conf = formatted
361+
host_config['LxcConf'] = lxc_conf
362+
363+
return host_config

docs/api.md

+11-5
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,9 @@ character, bytes are assumed as an intended unit.
172172

173173
`volumes_from` and `dns` arguments raise [TypeError](
174174
https://docs.python.org/3.4/library/exceptions.html#TypeError) exception if
175-
they are used against v1.10 of the Docker remote API. Those arguments should be
176-
passed to `start()` instead.
175+
they are used against v1.10 and above of the Docker remote API. Those
176+
arguments should be passed to `start()` instead, or as part of the `host_config`
177+
dictionary.
177178

178179
**Params**:
179180

@@ -200,7 +201,8 @@ from. Optionally a single string joining container id's with commas
200201
* cpu_shares (int or float): CPU shares (relative weight)
201202
* working_dir (str): Path to the working directory
202203
* domainname (str or list): Set custom DNS search domains
203-
* memswap_limit:
204+
* memswap_limit (int):
205+
* host_config (dict): A [HostConfig](hostconfig.md) dictionary
204206

205207
**Returns** (dict): A dictionary with an image 'Id' key and a 'Warnings' key.
206208

@@ -563,9 +565,13 @@ Similar to the `docker start` command, but doesn't support attach options. Use
563565
`.logs()` to recover `stdout`/`stderr`.
564566

565567
`binds` allows to bind a directory in the host to the container. See [Using
566-
volumes](volumes.md) for more information. `port_bindings` exposes container
567-
ports to the host. See [Port bindings](port-bindings.md) for more information.
568+
volumes](volumes.md) for more information.
569+
570+
`port_bindings` exposes container ports to the host.
571+
See [Port bindings](port-bindings.md) for more information.
572+
568573
`lxc_conf` allows to pass LXC configuration options using a dictionary.
574+
569575
`privileged` starts the container in privileged mode.
570576

571577
[Links](http://docs.docker.io/en/latest/use/working_with_links_names/) can be

docs/hostconfig.md

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# HostConfig object
2+
3+
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`.
4+
5+
## HostConfig helper
6+
7+
### docker.utils.create_host_config
8+
9+
Creates a HostConfig dictionary to be used with `Client.create_container`.
10+
11+
`binds` allows to bind a directory in the host to the container. See [Using
12+
volumes](volumes.md) for more information.
13+
14+
`port_bindings` exposes container ports to the host.
15+
See [Port bindings](port-bindings.md) for more information.
16+
17+
`lxc_conf` allows to pass LXC configuration options using a dictionary.
18+
19+
`privileged` starts the container in privileged mode.
20+
21+
[Links](http://docs.docker.io/en/latest/use/working_with_links_names/) can be
22+
specified with the `links` argument. They can either be specified as a
23+
dictionary mapping name to alias or as a list of `(name, alias)` tuples.
24+
25+
`dns` and `volumes_from` are only available if they are used with version v1.10
26+
of docker remote API. Otherwise they are ignored.
27+
28+
`network_mode` is available since v1.11 and sets the Network mode for the
29+
container ('bridge': creates a new network stack for the container on the
30+
Docker bridge, 'none': no networking for this container, 'container:[name|id]':
31+
reuses another container network stack), 'host': use the host network stack
32+
inside the container.
33+
34+
`restart_policy` is available since v1.2.0 and sets the RestartPolicy for how a
35+
container should or should not be restarted on exit. By default the policy is
36+
set to no meaning do not restart the container when it exits. The user may
37+
specify the restart policy as a dictionary for example:
38+
```python
39+
{
40+
"MaximumRetryCount": 0,
41+
"Name": "always"
42+
}
43+
```
44+
45+
For always restarting the container on exit or can specify to restart the
46+
container to restart on failure and can limit number of restarts. For example:
47+
```python
48+
{
49+
"MaximumRetryCount": 5,
50+
"Name": "on-failure"
51+
}
52+
```
53+
54+
`cap_add` and `cap_drop` are available since v1.2.0 and can be used to add or
55+
drop certain capabilities. The user may specify the capabilities as an array
56+
for example:
57+
```python
58+
[
59+
"SYS_ADMIN",
60+
"MKNOD"
61+
]
62+
```
63+
64+
65+
**Params**
66+
67+
* container (str): The container to start
68+
* binds: Volumes to bind. See [Using volumes](volumes.md) for more information.
69+
* port_bindings (dict): Port bindings. See [Port bindings](port-bindings.md)
70+
for more information.
71+
* lxc_conf (dict): LXC config
72+
* publish_all_ports (bool): Whether to publish all ports to the host
73+
* links (dict or list of tuples): either as a dictionary mapping name to alias or
74+
as a list of `(name, alias)` tuples
75+
* privileged (bool): Give extended privileges to this container
76+
* dns (list): Set custom DNS servers
77+
* dns_search (list): DNS search domains
78+
* volumes_from (str or list): List of container names or Ids to get volumes
79+
from. Optionally a single string joining container id's with commas
80+
* network_mode (str): One of `['bridge', None, 'container:<name|id>', 'host']`
81+
* restart_policy (dict): "Name" param must be one of `['on-failure', 'always']`
82+
* cap_add (list of str): Add kernel capabilities
83+
* cap_drop (list of str): Drop kernel capabilities
84+
85+
**Returns** (dict) HostConfig dictionary
86+
87+
```python
88+
>>> from docker.utils import create_host_config
89+
>>> create_host_config(privileged=True, cap_drop=['MKNOD'], volumes_from=['nostalgic_newton'])
90+
{'CapDrop': ['MKNOD'], 'LxcConf': None, 'Privileged': True, 'VolumesFrom': ['nostalgic_newton'], 'PublishAllPorts': False}
91+
```

mkdocs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
site_name: docker-py Documentation
22
site_description: An API client for Docker written in Python
33
site_favicon: favicon_whale.png
4-
# site_url: docker-py.readthedocs.org
4+
site_url: docker-py.readthedocs.org
55
repo_url: https://github.com/docker/docker-py/
66
theme: readthedocs
77
pages:

tests/fake_api.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
CURRENT_VERSION = 'v1.15'
15+
CURRENT_VERSION = 'v1.16'
1616

1717
FAKE_CONTAINER_ID = '3cc2351ab11b'
1818
FAKE_IMAGE_ID = 'e9aa60c60128'

0 commit comments

Comments
 (0)