diff --git a/fig/packages/docker/client.py b/fig/packages/docker/client.py index abcd4a17dec..9818e21b3a9 100644 --- a/fig/packages/docker/client.py +++ b/fig/packages/docker/client.py @@ -94,7 +94,7 @@ def _result(self, response, json=False, binary=False): def _container_config(self, image, command, 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, + volumes=None, volumes_from=None, devices=None, network_disabled=False, entrypoint=None, cpu_shares=None, working_dir=None, domainname=None, memswap_limit=0): @@ -123,6 +123,12 @@ def _container_config(self, image, command, hostname=None, user=None, volumes_dict[vol] = {} volumes = volumes_dict + if isinstance(devices, list): + devices_dict = {} + for device in devices: + devices_dict[device] = {} + devices = devices_dict + if volumes_from: if not isinstance(volumes_from, six.string_types): volumes_from = ','.join(volumes_from) @@ -169,6 +175,7 @@ def _container_config(self, image, command, hostname=None, user=None, 'Image': image, 'Volumes': volumes, 'VolumesFrom': volumes_from, + 'Devices': devices, 'NetworkDisabled': network_disabled, 'Entrypoint': entrypoint, 'CpuShares': cpu_shares, @@ -449,7 +456,7 @@ def copy(self, container, resource): def create_container(self, 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, + volumes=None, volumes_from=None, devices=None, network_disabled=False, name=None, entrypoint=None, cpu_shares=None, working_dir=None, domainname=None, memswap_limit=0): @@ -457,7 +464,8 @@ def create_container(self, image, command=None, hostname=None, user=None, 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 + entrypoint, cpu_shares, working_dir, domainname, memswap_limit, + devices ) return self.create_container_from_config(config, name) @@ -748,7 +756,8 @@ def search(self, term): def start(self, 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): + dns=None, dns_search=None, volumes_from=None, network_mode=None, + device_binds=None): if isinstance(container, dict): container = container.get('Id') @@ -769,6 +778,9 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None, port_bindings ) + if device_binds: + start_config['Devices'] = utils.convert_device_binds(device_binds) + start_config['PublishAllPorts'] = publish_all_ports if links: diff --git a/fig/packages/docker/utils/utils.py b/fig/packages/docker/utils/utils.py index da81bdc557f..dfc8f831a0f 100644 --- a/fig/packages/docker/utils/utils.py +++ b/fig/packages/docker/utils/utils.py @@ -134,6 +134,20 @@ def convert_volume_binds(binds): result.append('%s:%s:rw' % (k, v)) return result +def convert_device_binds(device_binds): + result = [] + for k, v in device_binds.items(): + device = {} + if isinstance(v, dict): + device['CgroupPermissions'] = 'rom' if v.get('rom', False) else 'rwm' + device['PathOnHost'] = v['bind'] + else: + device['CgroupPermissions'] = 'rwm' + device['PathOnHost'] = v + device['PathInContainer'] = k + result.append(device) + return result + def parse_repository_tag(repo): column_index = repo.rfind(':') diff --git a/fig/service.py b/fig/service.py index 48f63e6a5b2..0341b7aabe6 100644 --- a/fig/service.py +++ b/fig/service.py @@ -11,7 +11,7 @@ log = logging.getLogger(__name__) -DOCKER_CONFIG_KEYS = ['image', 'command', 'hostname', 'domainname', 'user', 'detach', 'stdin_open', 'tty', 'mem_limit', 'ports', 'environment', 'dns', 'volumes', 'entrypoint', 'privileged', 'volumes_from', 'net', 'working_dir'] +DOCKER_CONFIG_KEYS = ['image', 'command', 'hostname', 'domainname', 'user', 'detach', 'stdin_open', 'tty', 'mem_limit', 'ports', 'environment', 'dns', 'volumes', 'devices', 'entrypoint', 'privileged', 'volumes_from', 'net', 'working_dir'] DOCKER_CONFIG_HINTS = { 'link' : 'links', 'port' : 'ports', @@ -19,6 +19,7 @@ 'priviliged': 'privileged', 'privilige' : 'privileged', 'volume' : 'volumes', + 'device' : 'devices', 'workdir' : 'working_dir', } @@ -238,6 +239,22 @@ def start_container(self, container=None, intermediate_container=None, **overrid 'ro': False, } + device_bindings = {} + + if options.get('devices', None) is not None: + for device in options['devices']: + if ':' in device: + external_device, internal_device = device.split(':') + device_bindings[os.path.abspath(external_device)] = { + 'bind': internal_device, + 'rom': False, + } + else: + device_bindings[os.path.abspath(device)] = { + 'bind': device, + 'rom': False + } + privileged = options.get('privileged', False) net = options.get('net', 'bridge') dns = options.get('dns', None) @@ -246,6 +263,7 @@ def start_container(self, container=None, intermediate_container=None, **overrid links=self._get_links(link_to_self=override_options.get('one_off', False)), port_bindings=port_bindings, binds=volume_bindings, + device_binds=device_bindings, volumes_from=self._get_volumes_from(intermediate_container), privileged=privileged, network_mode=net, @@ -340,6 +358,9 @@ def _get_container_create_options(self, override_options, one_off=False): if 'volumes' in container_options: container_options['volumes'] = dict((split_volume(v)[1], {}) for v in container_options['volumes']) + if 'devices' in container_options: + container_options['devices'] = dict((split_device(v)[1], {}) for v in container_options['devices']) + if 'environment' in container_options: if isinstance(container_options['environment'], list): container_options['environment'] = dict(split_env(e) for e in container_options['environment']) @@ -443,6 +464,16 @@ def split_volume(v): else: return (None, v) +def split_device(d): + """ + If d is of the format EXTERNAL:INTERNAL, returns (EXTERNAL, INTERNAL). + If d is of the format INTERNAL, returns (None, INTERNAL). + """ + if ':' in d: + return d.split(':', 1) + else: + return (None, d) + def split_port(port): port = str(port)