Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for host config #428

Merged
merged 13 commits into from
Dec 17, 2014
55 changes: 36 additions & 19 deletions docker/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
if not six.PY3:
import websocket

DEFAULT_DOCKER_API_VERSION = '1.15'
DEFAULT_DOCKER_API_VERSION = '1.16'
DEFAULT_TIMEOUT_SECONDS = 60
STREAM_HEADER_SIZE_BYTES = 8

Expand Down Expand Up @@ -110,7 +110,8 @@ def _container_config(self, image, command, hostname=None, user=None,
volumes=None, volumes_from=None,
network_disabled=False, entrypoint=None,
cpu_shares=None, working_dir=None,
domainname=None, memswap_limit=0, cpuset=None):
domainname=None, memswap_limit=0, cpuset=None,
host_config=None):
if isinstance(command, six.string_types):
command = shlex.split(str(command))
if isinstance(environment, dict):
Expand Down Expand Up @@ -225,7 +226,8 @@ def _container_config(self, image, command, hostname=None, user=None,
'CpuShares': cpu_shares,
'Cpuset': cpuset,
'WorkingDir': working_dir,
'MemorySwap': memswap_limit
'MemorySwap': memswap_limit,
'HostConfig': host_config
}

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

def _warn_deprecated(self, arg_name, version):
warning_message = (
'{0!r} is deprecated for API version >= {1}'
).format(arg_name, version)
warnings.warn(warning_message, DeprecationWarning)

def _get_raw_response_socket(self, response):
self._raise_for_status(response)
if six.PY3:
Expand Down Expand Up @@ -536,17 +544,20 @@ def create_container(self, image, command=None, hostname=None, user=None,
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,
domainname=None, memswap_limit=0, cpuset=None):
cpu_shares=None, working_dir=None, domainname=None,
memswap_limit=0, cpuset=None, host_config=None):

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

if host_config and utils.compare_version('1.15', self._version) < 0:
raise errors.APIError('host_config is not supported in API < 1.15')

config = self._container_config(
image, command, hostname, user, detach, stdin_open, tty, mem_limit,
ports, environment, dns, volumes, volumes_from, network_disabled,
entrypoint, cpu_shares, working_dir, domainname,
memswap_limit, cpuset
memswap_limit, cpuset, host_config
)
return self.create_container_from_config(config, name)

Expand All @@ -570,7 +581,7 @@ def events(self):
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!')
raise errors.APIError('Exec is not supported in API < 1.15')
if isinstance(container, dict):
container = container.get('Id')
if isinstance(cmd, six.string_types):
Expand Down Expand Up @@ -911,6 +922,9 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
dns=None, dns_search=None, volumes_from=None, network_mode=None,
restart_policy=None, cap_add=None, cap_drop=None, devices=None,
extra_hosts=None):

start_config = {}

if isinstance(container, dict):
container = container.get('Id')

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

start_config = {
'LxcConf': lxc_conf
}
if lxc_conf:
start_config['LxcConf'] = lxc_conf

if binds:
start_config['Binds'] = utils.convert_volume_binds(binds)

Expand All @@ -931,7 +945,8 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
port_bindings
)

start_config['PublishAllPorts'] = publish_all_ports
if publish_all_ports:
start_config['PublishAllPorts'] = publish_all_ports

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

start_config['ExtraHosts'] = formatted_extra_hosts

start_config['Privileged'] = privileged
if privileged:
start_config['Privileged'] = privileged

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

if dns is not None:
warnings.warn(warning_message.format('dns'),
DeprecationWarning)
raise errors.APIError(
'dns is only supported for API version >= 1.10'
)
if volumes_from is not None:
warnings.warn(warning_message.format('volumes_from'),
DeprecationWarning)
raise errors.APIError(
'volumes_from is only supported for API version >= 1.10'
)
if dns_search:
start_config['DnsSearch'] = dns_search

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

url = self._url("/containers/{0}/start".format(container))
if not start_config:
start_config = None
res = self._post_json(url, data=start_config)
self._raise_for_status(res)

Expand Down
2 changes: 1 addition & 1 deletion docker/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .utils import (
compare_version, convert_port_bindings, convert_volume_binds,
mkbuildcontext, ping, tar, parse_repository_tag, parse_host,
kwargs_from_env, convert_filters
kwargs_from_env, convert_filters, create_host_config
) # flake8: noqa
67 changes: 67 additions & 0 deletions docker/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,70 @@ def convert_filters(filters):
v = [v, ]
result[k] = v
return json.dumps(result)


def create_host_config(
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, devices=None
):
host_config = {
'Privileged': privileged,
'PublishAllPorts': publish_all_ports,
}

if dns_search:
host_config['DnsSearch'] = dns_search

if network_mode:
host_config['NetworkMode'] = network_mode

if restart_policy:
host_config['RestartPolicy'] = restart_policy

if cap_add:
host_config['CapAdd'] = cap_add

if cap_drop:
host_config['CapDrop'] = cap_drop

if devices:
host_config['Devices'] = parse_devices(devices)

if dns is not None:
host_config['Dns'] = dns

if volumes_from is not None:
if isinstance(volumes_from, six.string_types):
volumes_from = volumes_from.split(',')
host_config['VolumesFrom'] = volumes_from

if binds:
host_config['Binds'] = convert_volume_binds(binds)

if port_bindings:
host_config['PortBindings'] = convert_port_bindings(
port_bindings
)

host_config['PublishAllPorts'] = publish_all_ports

if links:
if isinstance(links, dict):
links = six.iteritems(links)

formatted_links = [
'{0}:{1}'.format(k, v) for k, v in sorted(links)
]

host_config['Links'] = formatted_links

if isinstance(lxc_conf, dict):
formatted = []
for k, v in six.iteritems(lxc_conf):
formatted.append({'Key': k, 'Value': str(v)})
lxc_conf = formatted
host_config['LxcConf'] = lxc_conf

return host_config
16 changes: 11 additions & 5 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ 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.
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.

**Params**:

Expand All @@ -200,7 +201,8 @@ from. Optionally a single string joining container id's with commas
* 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:
* memswap_limit (int):
* host_config (dict): A [HostConfig](hostconfig.md) dictionary

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

Expand Down Expand Up @@ -563,9 +565,13 @@ 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.
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
Expand Down
91 changes: 91 additions & 0 deletions docs/hostconfig.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# 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`.

## HostConfig helper

### docker.utils.create_host_config

Creates a HostConfig dictionary to be used with `Client.create_container`.

`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. See [Using volumes](volumes.md) for more information.
* port_bindings (dict): Port bindings. See [Port bindings](port-bindings.md)
for more information.
* lxc_conf (dict): LXC config
* publish_all_ports (bool): Whether to publish all ports to the host
* links (dict or list of tuples): either as a dictionary mapping name to alias or
as a list of `(name, alias)` tuples
* 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): "Name" param must be one of `['on-failure', 'always']`
* cap_add (list of str): Add kernel capabilities
* cap_drop (list of str): Drop kernel capabilities

**Returns** (dict) HostConfig dictionary

```python
>>> from docker.utils import create_host_config
>>> create_host_config(privileged=True, cap_drop=['MKNOD'], volumes_from=['nostalgic_newton'])
{'CapDrop': ['MKNOD'], 'LxcConf': None, 'Privileged': True, 'VolumesFrom': ['nostalgic_newton'], 'PublishAllPorts': False}
```
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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
site_url: docker-py.readthedocs.org
repo_url: https://github.com/docker/docker-py/
theme: readthedocs
pages:
Expand Down
2 changes: 1 addition & 1 deletion tests/fake_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

CURRENT_VERSION = 'v1.15'
CURRENT_VERSION = 'v1.16'

FAKE_CONTAINER_ID = '3cc2351ab11b'
FAKE_IMAGE_ID = 'e9aa60c60128'
Expand Down
Loading