diff --git a/docs/yml.md b/docs/yml.md index dedfa5c1d1c..4a80066a7e3 100644 --- a/docs/yml.md +++ b/docs/yml.md @@ -142,7 +142,7 @@ dns: - 9.9.9.9 ``` -### working\_dir, entrypoint, user, hostname, domainname, mem\_limit, privileged +### working\_dir, entrypoint, user, hostname, domainname, mem\_limit, privileged, cpuset, memswap_limit Each of these is a single value, analogous to its [docker run](https://docs.docker.com/reference/run/) counterpart. @@ -156,4 +156,6 @@ domainname: foo.com mem_limit: 1000000000 privileged: true +cpuset: “0-3,5” +memswap_limit: 1024 ``` diff --git a/fig/service.py b/fig/service.py index bbbef7bc43e..7d640af1f8c 100644 --- a/fig/service.py +++ b/fig/service.py @@ -15,7 +15,8 @@ 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', 'entrypoint', 'privileged', 'volumes_from', 'net', 'working_dir', 'cpuset', 'memswap_limit'] + DOCKER_CONFIG_HINTS = { 'link' : 'links', 'port' : 'ports', @@ -375,6 +376,8 @@ def _get_container_create_options(self, override_options, one_off=False): self.build() container_options['image'] = self._build_tag_name() + if 'cpuset' in container_options: + container_options['cpuset'] = parse_cpuset(container_options['cpuset']) # Delete options which are only used when starting for key in ['privileged', 'net', 'dns']: if key in container_options: @@ -436,6 +439,7 @@ def pull(self, insecure_registry=False): NAME_RE = re.compile(r'^([^_]+)_([^_]+)_(run_)?(\d+)$') +CPUSET_RE = re.compile(r'^[0-9]+((\s*,\s*[0-9]+)*|(\s*\-\s*[0-9]+((\s*,\s*[0-9]+)+|$)))*$') def is_valid_name(name, one_off=False): @@ -534,3 +538,11 @@ def resolve_env(key, val): return key, os.environ[key] else: return key, '' + + +def parse_cpuset(cpuset): + if not cpuset: + return None + if not CPUSET_RE.match(cpuset): + raise ConfigError("cpuset has incorrect format. should contain only comma separated numbers and ranges (1-3). ") + return cpuset diff --git a/requirements.txt b/requirements.txt index 59aa90f02f9..2ccdf59a258 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ PyYAML==3.10 -docker-py==0.5.3 +docker-py==0.6.0 dockerpty==0.3.2 docopt==0.6.1 requests==2.2.1 diff --git a/setup.py b/setup.py index fe839f5ad5b..1c0f0343c0f 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ def find_version(*file_paths): 'requests >= 2.2.1, < 3', 'texttable >= 0.8.1, < 0.9', 'websocket-client >= 0.11.0, < 0.12', - 'docker-py >= 0.5.3, < 0.6', + 'docker-py == 0.6', 'dockerpty >= 0.3.2, < 0.4', 'six >= 1.3.0, < 2', ] diff --git a/tests/unit/service_test.py b/tests/unit/service_test.py index 119b4144062..679c59fea26 100644 --- a/tests/unit/service_test.py +++ b/tests/unit/service_test.py @@ -17,6 +17,7 @@ parse_volume_spec, build_volume_binding, APIError, + parse_cpuset, ) @@ -205,6 +206,27 @@ def test_create_container_from_insecure_registry(self, mock_log): self.mock_client.pull.assert_called_once_with('someimage:sometag', insecure_registry=True, stream=True) mock_log.info.assert_called_once_with('Pulling image someimage:sometag...') + def test_valid_parse_cpusets(self): + valid_cpuset = "1,2,3-4,5" + valid_cpuset_spaced = "4, 2, 3-4 , 5" + self.assertEqual(parse_cpuset(None), None) + self.assertEqual(parse_cpuset(''), None) + self.assertEqual(parse_cpuset(valid_cpuset), valid_cpuset) + self.assertEqual(parse_cpuset(valid_cpuset_spaced), valid_cpuset_spaced) + + def test_invalid_parse_cpusets(self): + with self.assertRaises(ConfigError): + parse_cpuset("1-") + with self.assertRaises(ConfigError): + parse_cpuset("1-2,") + with self.assertRaises(ConfigError): + parse_cpuset("4,") + with self.assertRaises(ConfigError): + parse_cpuset("1-3-4") + with self.assertRaises(ConfigError): + parse_cpuset("1-3-4-1-2-2-") + with self.assertRaises(ConfigError): + parse_cpuset("1-2,2,a") class ServiceVolumesTest(unittest.TestCase):