Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docker/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
kwargs_from_env, convert_filters, datetime_to_timestamp, create_host_config,
create_container_config, parse_bytes, ping_registry, parse_env_file,
version_lt, version_gte, decode_json_header, split_command,
create_ipam_config, create_ipam_pool,
create_ipam_config, create_ipam_pool, parse_devices
) # flake8: noqa

from .types import Ulimit, LogConfig # flake8: noqa
Expand Down
19 changes: 14 additions & 5 deletions docker/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ def parse_host(addr, platform=None):
port = int(port)
except Exception:
raise errors.DockerException(
"Invalid port: %s", addr
"Invalid port: {0}".format(addr)
)

elif proto in ("http", "https") and ':' not in addr:
Expand All @@ -417,7 +417,14 @@ def parse_host(addr, platform=None):
def parse_devices(devices):
device_list = []
for device in devices:
device_mapping = device.split(":")
if isinstance(device, dict):
device_list.append(device)
continue
if not isinstance(device, six.string_types):
raise errors.DockerException(
'Invalid device type {0}'.format(type(device))
)
device_mapping = device.split(':')
if device_mapping:
path_on_host = device_mapping[0]
if len(device_mapping) > 1:
Expand All @@ -428,9 +435,11 @@ def parse_devices(devices):
permissions = device_mapping[2]
else:
permissions = 'rwm'
device_list.append({"PathOnHost": path_on_host,
"PathInContainer": path_in_container,
"CgroupPermissions": permissions})
device_list.append({
'PathOnHost': path_on_host,
'PathInContainer': path_in_container,
'CgroupPermissions': permissions
})
return device_list


Expand Down
19 changes: 15 additions & 4 deletions docs/host-devices.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,18 @@ cli.create_container(
)
```

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.
Each string is a single mapping using the following format:
`<path_on_host>:<path_in_container>:<cgroup_permissions>`
The above example allows the container to have read-write access to
the host's `/dev/sda` via a node named `/dev/xvda` inside the container.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also document the dict form of elements in the devices list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added 👍


As a more verbose alternative, each host device definition can be specified as
a dictionary with the following keys:

```python
{
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/xvda',
'CgroupPermissions': 'rwm'
}
```
13 changes: 4 additions & 9 deletions docs/hostconfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,12 @@ for example:
* mem_swappiness (int): Tune a container's memory swappiness behavior.
Accepts number between 0 and 100.
* cpu_group (int): The length of a CPU period in microseconds.
* cpu_period (int): Microseconds of CPU time that the container can get in a CPU period.
* cpu_period (int): Microseconds of CPU time that the container can get in a
CPU period.
* group_add (list): List of additional group names and/or IDs that the
container process will run as.
* devices (list): A list of devices to add to the container specified as dicts
in the form:
```
{ "PathOnHost": "/dev/deviceName",
"PathInContainer": "/dev/deviceName",
"CgroupPermissions": "mrw"
}
```
* devices (list): Host device bindings. See [host devices](host-devices.md)
for more information.

**Returns** (dict) HostConfig dictionary

Expand Down
61 changes: 60 additions & 1 deletion tests/unit/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
parse_repository_tag, parse_host, convert_filters, kwargs_from_env,
create_host_config, Ulimit, LogConfig, parse_bytes, parse_env_file,
exclude_paths, convert_volume_binds, decode_json_header, tar,
split_command, create_ipam_config, create_ipam_pool,
split_command, create_ipam_config, create_ipam_pool, parse_devices,
)
from docker.utils.utils import create_endpoint_config
from docker.utils.ports import build_port_bindings, split_port
Expand Down Expand Up @@ -406,6 +406,65 @@ def test_private_reg_image_sha(self):
)


class ParseDeviceTest(base.BaseTestCase):
def test_dict(self):
devices = parse_devices([{
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'r'
}])
self.assertEqual(devices[0], {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'r'
})

def test_partial_string_definition(self):
devices = parse_devices(['/dev/sda1'])
self.assertEqual(devices[0], {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/sda1',
'CgroupPermissions': 'rwm'
})

def test_permissionless_string_definition(self):
devices = parse_devices(['/dev/sda1:/dev/mnt1'])
self.assertEqual(devices[0], {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'rwm'
})

def test_full_string_definition(self):
devices = parse_devices(['/dev/sda1:/dev/mnt1:r'])
self.assertEqual(devices[0], {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'r'
})

def test_hybrid_list(self):
devices = parse_devices([
'/dev/sda1:/dev/mnt1:rw',
{
'PathOnHost': '/dev/sda2',
'PathInContainer': '/dev/mnt2',
'CgroupPermissions': 'r'
}
])

self.assertEqual(devices[0], {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'rw'
})
self.assertEqual(devices[1], {
'PathOnHost': '/dev/sda2',
'PathInContainer': '/dev/mnt2',
'CgroupPermissions': 'r'
})


class UtilsTest(base.BaseTestCase):
longMessage = True

Expand Down