diff --git a/compose/config/config.py b/compose/config/config.py index 38e887417..0b5c7df0a 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -72,6 +72,7 @@ DOCKER_CONFIG_KEYS = [ 'cpus', 'cpuset', 'detach', + 'device_cgroup_rules', 'devices', 'dns', 'dns_search', @@ -1045,7 +1046,7 @@ def merge_service_dicts(base, override, version): for field in [ 'cap_add', 'cap_drop', 'expose', 'external_links', - 'security_opt', 'volumes_from', + 'security_opt', 'volumes_from', 'device_cgroup_rules', ]: md.merge_field(field, merge_unique_items_lists, default=[]) diff --git a/compose/config/config_schema_v2.3.json b/compose/config/config_schema_v2.3.json index 2d28df77a..33840dba5 100644 --- a/compose/config/config_schema_v2.3.json +++ b/compose/config/config_schema_v2.3.json @@ -99,8 +99,8 @@ } ] }, - "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, - "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "cap_add": {"$ref": "#/definitions/list_of_strings"}, + "cap_drop": {"$ref": "#/definitions/list_of_strings"}, "cgroup_parent": {"type": "string"}, "command": { "oneOf": [ @@ -137,7 +137,8 @@ } ] }, - "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"}, + "devices": {"$ref": "#/definitions/list_of_strings"}, "dns_opt": { "type": "array", "items": { @@ -184,7 +185,7 @@ ] }, - "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "external_links": {"$ref": "#/definitions/list_of_strings"}, "extra_hosts": {"$ref": "#/definitions/list_or_dict"}, "healthcheck": {"$ref": "#/definitions/healthcheck"}, "hostname": {"type": "string"}, @@ -193,7 +194,7 @@ "ipc": {"type": "string"}, "isolation": {"type": "string"}, "labels": {"$ref": "#/definitions/labels"}, - "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "links": {"$ref": "#/definitions/list_of_strings"}, "logging": { "type": "object", @@ -264,7 +265,7 @@ "restart": {"type": "string"}, "runtime": {"type": "string"}, "scale": {"type": "integer"}, - "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "security_opt": {"$ref": "#/definitions/list_of_strings"}, "shm_size": {"type": ["number", "string"]}, "sysctls": {"$ref": "#/definitions/list_or_dict"}, "pids_limit": {"type": ["number", "string"]}, @@ -335,7 +336,7 @@ } }, "volume_driver": {"type": "string"}, - "volumes_from": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "volumes_from": {"$ref": "#/definitions/list_of_strings"}, "working_dir": {"type": "string"} }, diff --git a/compose/service.py b/compose/service.py index 37368d64d..4a1cde88d 100644 --- a/compose/service.py +++ b/compose/service.py @@ -66,6 +66,7 @@ HOST_CONFIG_KEYS = [ 'cpu_shares', 'cpus', 'cpuset', + 'device_cgroup_rules', 'devices', 'dns', 'dns_search', @@ -944,6 +945,7 @@ class Service(object): device_write_bps=blkio_config.get('device_write_bps'), device_write_iops=blkio_config.get('device_write_iops'), mounts=options.get('mounts'), + device_cgroup_rules=options.get('device_cgroup_rules'), ) def get_secret_volumes(self): diff --git a/tests/integration/service_test.py b/tests/integration/service_test.py index d1a704199..2b6b7711e 100644 --- a/tests/integration/service_test.py +++ b/tests/integration/service_test.py @@ -265,6 +265,11 @@ class ServiceTest(DockerClientTestCase): service.start_container(container) assert container.inspect()['Config']['MacAddress'] == '02:42:ac:11:65:43' + def test_create_container_with_device_cgroup_rules(self): + service = self.create_service('db', device_cgroup_rules=['c 7:128 rwm']) + container = service.create_container() + assert container.get('HostConfig.DeviceCgroupRules') == ['c 7:128 rwm'] + def test_create_container_with_specified_volume(self): host_path = '/tmp/host-path' container_path = '/container-path' diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index d72fae2f5..e03298221 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -2558,6 +2558,21 @@ class ConfigTest(unittest.TestCase): actual = config.merge_service_dicts(base, override, V2_3) assert actual['healthcheck'] == override['healthcheck'] + def test_merge_device_cgroup_rules(self): + base = { + 'image': 'bar', + 'device_cgroup_rules': ['c 7:128 rwm', 'x 3:244 rw'] + } + + override = { + 'device_cgroup_rules': ['c 7:128 rwm', 'f 0:128 n'] + } + + actual = config.merge_service_dicts(base, override, V2_3) + assert sorted(actual['device_cgroup_rules']) == sorted( + ['c 7:128 rwm', 'x 3:244 rw', 'f 0:128 n'] + ) + def test_external_volume_config(self): config_details = build_config_details({ 'version': '2',