diff --git a/compose/config/interpolation.py b/compose/config/interpolation.py index 45a5f9fc2..68d3be682 100644 --- a/compose/config/interpolation.py +++ b/compose/config/interpolation.py @@ -138,14 +138,24 @@ def to_int(s): # We must be able to handle octal representation for `mode` values notably if six.PY3 and re.match('^0[0-9]+$', s.strip()): s = '0o' + s[1:] - return int(s, base=0) + try: + return int(s, base=0) + except ValueError: + raise ValueError('"{}" is not a valid integer'.format(s)) + + +def to_float(s): + try: + return float(s) + except ValueError: + raise ValueError('"{}" is not a valid float'.format(s)) class ConversionMap(object): map = { service_path('blkio_config', 'weight'): to_int, service_path('blkio_config', 'weight_device', 'weight'): to_int, - service_path('cpus'): float, + service_path('cpus'): to_float, service_path('cpu_count'): to_int, service_path('configs', 'mode'): to_int, service_path('secrets', 'mode'): to_int, @@ -153,7 +163,7 @@ class ConversionMap(object): service_path('healthcheck', 'disable'): to_boolean, service_path('deploy', 'replicas'): to_int, service_path('deploy', 'update_config', 'parallelism'): to_int, - service_path('deploy', 'update_config', 'max_failure_ratio'): float, + service_path('deploy', 'update_config', 'max_failure_ratio'): to_float, service_path('deploy', 'restart_policy', 'max_attempts'): to_int, service_path('mem_swappiness'): to_int, service_path('oom_kill_disable'): to_boolean, @@ -181,7 +191,14 @@ class ConversionMap(object): def convert(self, path, value): for rexp in self.map.keys(): if rexp.match(path): - return self.map[rexp](value) + try: + return self.map[rexp](value) + except ValueError as e: + raise ConfigurationError( + 'Error while attempting to convert {} to appropriate type: {}'.format( + path, e + ) + ) return value diff --git a/tests/unit/config/interpolation_test.py b/tests/unit/config/interpolation_test.py index 516f5c9e9..702ea682d 100644 --- a/tests/unit/config/interpolation_test.py +++ b/tests/unit/config/interpolation_test.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import pytest from compose.config.environment import Environment +from compose.config.errors import ConfigurationError from compose.config.interpolation import interpolate_environment_variables from compose.config.interpolation import Interpolator from compose.config.interpolation import InvalidInterpolation @@ -254,6 +255,30 @@ def test_interpolate_environment_services_convert_types_v3(mock_env): assert value == expected +def test_interpolate_environment_services_convert_types_invalid(mock_env): + entry = {'service1': {'privileged': '${POSINT}'}} + + with pytest.raises(ConfigurationError) as exc: + interpolate_environment_variables(V2_3, entry, 'service', mock_env) + + assert 'Error while attempting to convert service.service1.privileged to '\ + 'appropriate type: "50" is not a valid boolean value' in exc.exconly() + + entry = {'service1': {'cpus': '${TRUE}'}} + with pytest.raises(ConfigurationError) as exc: + interpolate_environment_variables(V2_3, entry, 'service', mock_env) + + assert 'Error while attempting to convert service.service1.cpus to '\ + 'appropriate type: "True" is not a valid float' in exc.exconly() + + entry = {'service1': {'ulimits': {'nproc': '${FLOAT}'}}} + with pytest.raises(ConfigurationError) as exc: + interpolate_environment_variables(V2_3, entry, 'service', mock_env) + + assert 'Error while attempting to convert service.service1.ulimits.nproc to '\ + 'appropriate type: "0.145" is not a valid integer' in exc.exconly() + + def test_interpolate_environment_network_convert_types(mock_env): entry = { 'network1': {