diff --git a/docker/utils/utils.py b/docker/utils/utils.py index 39d0eba962..b9424f0f2c 100644 --- a/docker/utils/utils.py +++ b/docker/utils/utils.py @@ -469,16 +469,17 @@ def parse_bytes(s): return s -def create_host_config( - 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, - restart_policy=None, cap_add=None, cap_drop=None, devices=None, - extra_hosts=None, read_only=None, pid_mode=None, ipc_mode=None, - security_opt=None, ulimits=None, log_config=None, mem_limit=None, - memswap_limit=None, cgroup_parent=None, group_add=None, cpu_quota=None, - cpu_period=None, version=None -): +def create_host_config(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, restart_policy=None, cap_add=None, + cap_drop=None, devices=None, extra_hosts=None, + read_only=None, pid_mode=None, ipc_mode=None, + security_opt=None, ulimits=None, log_config=None, + mem_limit=None, memswap_limit=None, mem_swappiness=None, + cgroup_parent=None, group_add=None, cpu_quota=None, + cpu_period=None, version=None): + host_config = {} if not version: @@ -491,6 +492,7 @@ def create_host_config( if mem_limit is not None: if isinstance(mem_limit, six.string_types): mem_limit = parse_bytes(mem_limit) + host_config['Memory'] = mem_limit if memswap_limit is not None: @@ -498,6 +500,18 @@ def create_host_config( memswap_limit = parse_bytes(memswap_limit) host_config['MemorySwap'] = memswap_limit + if mem_swappiness is not None: + if version_lt(version, '1.20'): + raise errors.InvalidVersion( + 'mem_swappiness param not supported for API version < 1.20' + ) + if not isinstance(mem_swappiness, int): + raise TypeError( + 'Invalid type for mem_swappiness param: expected int but' + ' found {0}'.format(type(mem_swappiness)) + ) + host_config['MemorySwappiness'] = mem_swappiness + if pid_mode not in (None, 'host'): raise errors.DockerException( 'Invalid value for pid param: {0}'.format(pid_mode) diff --git a/docs/hostconfig.md b/docs/hostconfig.md index 4c17eb33da..40accce3d3 100644 --- a/docs/hostconfig.md +++ b/docs/hostconfig.md @@ -34,10 +34,11 @@ Docker bridge, 'none': no networking for this container, 'container:[name|id]': reuses another container network stack), 'host': use the host network stack inside the container. -`restart_policy` is available since v1.2.0 and sets the RestartPolicy for how a -container should or should not be restarted on exit. By default the policy is -set to no meaning do not restart the container when it exits. The user may -specify the restart policy as a dictionary for example: +`restart_policy` is available since v1.2.0 and sets the container's *RestartPolicy* +which defines the conditions under which a container should be restarted upon exit. +If no *RestartPolicy* is defined, the container will not be restarted when it exits. +The *RestartPolicy* is specified as a dict. For example, if the container +should always be restarted: ```python { "MaximumRetryCount": 0, @@ -45,8 +46,8 @@ specify the restart policy as a dictionary for example: } ``` -For always restarting the container on exit or can specify to restart the -container to restart on failure and can limit number of restarts. For example: +It is possible to restart the container only on failure as well as limit the number +of restarts. For example: ```python { "MaximumRetryCount": 5, @@ -95,10 +96,12 @@ for example: of ulimits to be set in the container. * log_config (`docker.utils.LogConfig` or dict): Logging configuration to container -* mem_limit (str or num): Maximum amount of memory container is allowed to - consume. (e.g. `'1g'`) -* memswap_limit (str or num): Maximum amount of memory + swap a container is +* mem_limit (str or int): Maximum amount of memory container is allowed to + consume. (e.g. `'1G'`) +* memswap_limit (str or int): Maximum amount of memory + swap a container is allowed to consume. +* mem_swappiness (int): Tune a container's memory swappiness behavior. + Accepts number between 0 and 100. * group_add (list): List of additional group names and/or IDs that the container process will run as. * devices (list): A list of devices to add to the container specified as dicts diff --git a/tests/integration/container_test.py b/tests/integration/container_test.py index 9606e14b92..d77b8469e0 100644 --- a/tests/integration/container_test.py +++ b/tests/integration/container_test.py @@ -337,6 +337,38 @@ def test_valid_no_config_specified(self): self.assertEqual(container_log_config['Type'], "json-file") self.assertEqual(container_log_config['Config'], {}) + def test_create_with_memory_constraints_with_str(self): + ctnr = self.client.create_container( + BUSYBOX, 'true', + host_config=self.client.create_host_config( + memswap_limit='1G', + mem_limit='700M' + ) + ) + self.assertIn('Id', ctnr) + self.tmp_containers.append(ctnr['Id']) + self.client.start(ctnr) + inspect = self.client.inspect_container(ctnr) + + self.assertIn('HostConfig', inspect) + host_config = inspect['HostConfig'] + for limit in ['Memory', 'MemorySwap']: + self.assertIn(limit, host_config) + + def test_create_with_memory_constraints_with_int(self): + ctnr = self.client.create_container( + BUSYBOX, 'true', + host_config=self.client.create_host_config(mem_swappiness=40) + ) + self.assertIn('Id', ctnr) + self.tmp_containers.append(ctnr['Id']) + self.client.start(ctnr) + inspect = self.client.inspect_container(ctnr) + + self.assertIn('HostConfig', inspect) + host_config = inspect['HostConfig'] + self.assertIn('MemorySwappiness', host_config) + class VolumeBindTest(api_test.BaseTestCase): def setUp(self):