diff --git a/compose/config/config.py b/compose/config/config.py index f1195c8ec..056847a85 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -52,8 +52,11 @@ DOCKER_CONFIG_KEYS = [ 'cap_drop', 'cgroup_parent', 'command', + 'cpu_count', + 'cpu_percent', 'cpu_quota', 'cpu_shares', + 'cpus', 'cpuset', 'detach', 'devices', diff --git a/compose/config/config_schema_v2.2.json b/compose/config/config_schema_v2.2.json index a178fccc4..bbf312c6a 100644 --- a/compose/config/config_schema_v2.2.json +++ b/compose/config/config_schema_v2.2.json @@ -74,8 +74,11 @@ ] }, "container_name": {"type": "string"}, + "cpu_count": {"type": "integer", "minimum": 0}, + "cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100}, "cpu_shares": {"type": ["number", "string"]}, "cpu_quota": {"type": ["number", "string"]}, + "cpus": {"type": ["number", "string"], "minimum": 0}, "cpuset": {"type": "string"}, "depends_on": { "oneOf": [ diff --git a/compose/service.py b/compose/service.py index edd0a3764..dc653b165 100644 --- a/compose/service.py +++ b/compose/service.py @@ -52,7 +52,10 @@ HOST_CONFIG_KEYS = [ 'cap_add', 'cap_drop', 'cgroup_parent', + 'cpu_count', + 'cpu_percent', 'cpu_quota', + 'cpus', 'devices', 'dns', 'dns_search', @@ -798,6 +801,10 @@ class Service(object): init_path = options.get('init') options['init'] = True + nano_cpus = None + if options.has_key('cpus'): + nano_cpus = int(options.get('cpus') * 1000000000) + return self.client.create_host_config( links=self._get_links(link_to_self=one_off), port_bindings=build_port_bindings( @@ -837,6 +844,9 @@ class Service(object): init=options.get('init', None), init_path=init_path, isolation=options.get('isolation'), + cpu_count=options.get('cpu_count'), + cpu_percent=options.get('cpu_percent'), + nano_cpus=nano_cpus, ) def get_secret_volumes(self): diff --git a/setup.py b/setup.py index 19a0d4aa0..8dbb337cc 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ install_requires = [ 'requests >= 2.6.1, != 2.11.0, < 2.12', 'texttable >= 0.8.1, < 0.9', 'websocket-client >= 0.32.0, < 1.0', - 'docker >= 2.2.1, < 3.0', + 'docker >= 2.3.0, < 3.0', 'dockerpty >= 0.4.1, < 0.5', 'six >= 1.3.0, < 2', 'jsonschema >= 2.5.1, < 3', diff --git a/tests/integration/service_test.py b/tests/integration/service_test.py index 87549c506..4ee9c3d19 100644 --- a/tests/integration/service_test.py +++ b/tests/integration/service_test.py @@ -33,6 +33,7 @@ from compose.service import ConvergenceStrategy from compose.service import NetworkMode from compose.service import Service from tests.integration.testcases import v2_1_only +from tests.integration.testcases import v2_2_only from tests.integration.testcases import v2_only from tests.integration.testcases import v3_only @@ -110,6 +111,30 @@ class ServiceTest(DockerClientTestCase): container.start() self.assertEqual(container.get('HostConfig.CpuQuota'), 40000) + @v2_2_only() + def test_create_container_with_cpu_count(self): + self.require_api_version('1.25') + service = self.create_service('db', cpu_count=2) + container = service.create_container() + service.start_container(container) + self.assertEqual(container.get('HostConfig.CpuCount'), 2) + + @v2_2_only() + def test_create_container_with_cpu_percent(self): + self.require_api_version('1.25') + service = self.create_service('db', cpu_percent=12) + container = service.create_container() + service.start_container(container) + self.assertEqual(container.get('HostConfig.CpuPercent'), 12) + + @v2_2_only() + def test_create_container_with_cpus(self): + self.require_api_version('1.25') + service = self.create_service('db', cpus=1) + container = service.create_container() + service.start_container(container) + self.assertEqual(container.get('HostConfig.NanoCpus'), 1000000000) + def test_create_container_with_shm_size(self): self.require_api_version('1.22') service = self.create_service('db', shm_size=67108864) diff --git a/tests/integration/testcases.py b/tests/integration/testcases.py index a5fe999d9..1bed6e8ff 100644 --- a/tests/integration/testcases.py +++ b/tests/integration/testcases.py @@ -15,6 +15,7 @@ from compose.const import API_VERSIONS from compose.const import COMPOSEFILE_V1 as V1 from compose.const import COMPOSEFILE_V2_0 as V2_0 from compose.const import COMPOSEFILE_V2_0 as V2_1 +from compose.const import COMPOSEFILE_V2_2 as V2_2 from compose.const import COMPOSEFILE_V3_2 as V3_2 from compose.const import LABEL_PROJECT from compose.progress_stream import stream_output @@ -69,9 +70,11 @@ def v2_only(): def v2_1_only(): return build_version_required_decorator((V1, V2_0)) +def v2_2_only(): + return build_version_required_decorator((V1, V2_0, V2_1)) def v3_only(): - return build_version_required_decorator((V1, V2_0, V2_1)) + return build_version_required_decorator((V1, V2_0, V2_1, V2_2)) class DockerClientTestCase(unittest.TestCase): diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index d3087fffe..e66e952f8 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -27,6 +27,7 @@ from compose.config.types import VolumeSpec from compose.const import COMPOSEFILE_V1 as V1 from compose.const import COMPOSEFILE_V2_0 as V2_0 from compose.const import COMPOSEFILE_V2_1 as V2_1 +from compose.const import COMPOSEFILE_V2_2 as V2_2 from compose.const import COMPOSEFILE_V3_0 as V3_0 from compose.const import COMPOSEFILE_V3_1 as V3_1 from compose.const import COMPOSEFILE_V3_2 as V3_2 @@ -174,6 +175,9 @@ class ConfigTest(unittest.TestCase): cfg = config.load(build_config_details({'version': '2.1'})) assert cfg.version == V2_1 + cfg = config.load(build_config_details({'version': '2.2'})) + assert cfg.version == V2_2 + for version in ['3', '3.0']: cfg = config.load(build_config_details({'version': version})) assert cfg.version == V3_0