summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey F <f.joffrey@gmail.com>2016-02-24 18:00:06 -0800
committerJoffrey F <f.joffrey@gmail.com>2016-02-24 18:00:06 -0800
commit81d8caaf36159bf1accd86eab2e157bf8dd071a9 (patch)
treefe7d2d7ba279f076b57b6ce964d5c132a76a43a9
parentcdf6dc8c3c5a400721633cff0c0bdf056ec2b52a (diff)
parent7440603d98d2ceb1d0df372cbf4b1591588a6949 (diff)
downloaddocker-py-81d8caaf36159bf1accd86eab2e157bf8dd071a9.tar.gz
Merge pull request #916 from docker/container_update_feature
Support for container limits update
-rw-r--r--docker/api/container.py33
-rw-r--r--docker/utils/utils.py74
-rw-r--r--docs/api.md22
-rw-r--r--tests/integration/container_test.py18
-rw-r--r--tests/unit/container_test.py18
-rw-r--r--tests/unit/fake_api.py7
6 files changed, 132 insertions, 40 deletions
diff --git a/docker/api/container.py b/docker/api/container.py
index 8aa9aa2..ef17c27 100644
--- a/docker/api/container.py
+++ b/docker/api/container.py
@@ -398,6 +398,39 @@ class ContainerApiMixin(object):
res = self._post(url)
self._raise_for_status(res)
+ @utils.minimum_version('1.22')
+ @utils.check_resource
+ def update_container(
+ self, container, blkio_weight=None, cpu_period=None, cpu_quota=None,
+ cpu_shares=None, cpuset_cpus=None, cpuset_mems=None, mem_limit=None,
+ mem_reservation=None, memswap_limit=None, kernel_memory=None
+ ):
+ url = self._url('/containers/{0}/update', container)
+ data = {}
+ if blkio_weight:
+ data['BlkioWeight'] = blkio_weight
+ if cpu_period:
+ data['CpuPeriod'] = cpu_period
+ if cpu_shares:
+ data['CpuShares'] = cpu_shares
+ if cpu_quota:
+ data['CpuQuota'] = cpu_quota
+ if cpuset_cpus:
+ data['CpusetCpus'] = cpuset_cpus
+ if cpuset_mems:
+ data['CpusetMems'] = cpuset_mems
+ if mem_limit:
+ data['Memory'] = utils.parse_bytes(mem_limit)
+ if mem_reservation:
+ data['MemoryReservation'] = utils.parse_bytes(mem_reservation)
+ if memswap_limit:
+ data['MemorySwap'] = utils.parse_bytes(memswap_limit)
+ if kernel_memory:
+ data['KernelMemory'] = utils.parse_bytes(kernel_memory)
+
+ res = self._post_json(url, data=data)
+ return self._result(res, True)
+
@utils.check_resource
def wait(self, container, timeout=None):
url = self._url("/containers/{0}/wait", container)
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index 8182a43..bc26ce8 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -518,41 +518,43 @@ def longint(n):
def parse_bytes(s):
+ if isinstance(s, six.integer_types + (float,)):
+ return s
if len(s) == 0:
- s = 0
- else:
- if s[-2:-1].isalpha() and s[-1].isalpha():
- if s[-1] == "b" or s[-1] == "B":
- s = s[:-1]
- units = BYTE_UNITS
- suffix = s[-1].lower()
-
- # Check if the variable is a string representation of an int
- # without a units part. Assuming that the units are bytes.
- if suffix.isdigit():
- digits_part = s
- suffix = 'b'
- else:
- digits_part = s[:-1]
+ return 0
- if suffix in units.keys() or suffix.isdigit():
- try:
- digits = longint(digits_part)
- except ValueError:
- raise errors.DockerException(
- 'Failed converting the string value for memory ({0}) to'
- ' an integer.'.format(digits_part)
- )
+ if s[-2:-1].isalpha() and s[-1].isalpha():
+ if s[-1] == "b" or s[-1] == "B":
+ s = s[:-1]
+ units = BYTE_UNITS
+ suffix = s[-1].lower()
+
+ # Check if the variable is a string representation of an int
+ # without a units part. Assuming that the units are bytes.
+ if suffix.isdigit():
+ digits_part = s
+ suffix = 'b'
+ else:
+ digits_part = s[:-1]
- # Reconvert to long for the final result
- s = longint(digits * units[suffix])
- else:
+ if suffix in units.keys() or suffix.isdigit():
+ try:
+ digits = longint(digits_part)
+ except ValueError:
raise errors.DockerException(
- 'The specified value for memory ({0}) should specify the'
- ' units. The postfix should be one of the `b` `k` `m` `g`'
- ' characters'.format(s)
+ 'Failed converting the string value for memory ({0}) to'
+ ' an integer.'.format(digits_part)
)
+ # Reconvert to long for the final result
+ s = longint(digits * units[suffix])
+ else:
+ raise errors.DockerException(
+ 'The specified value for memory ({0}) should specify the'
+ ' units. The postfix should be one of the `b` `k` `m` `g`'
+ ' characters'.format(s)
+ )
+
return s
@@ -594,16 +596,10 @@ def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
version = constants.DEFAULT_DOCKER_API_VERSION
if mem_limit is not None:
- if isinstance(mem_limit, six.string_types):
- mem_limit = parse_bytes(mem_limit)
-
- host_config['Memory'] = mem_limit
+ host_config['Memory'] = parse_bytes(mem_limit)
if memswap_limit is not None:
- if isinstance(memswap_limit, six.string_types):
- memswap_limit = parse_bytes(memswap_limit)
-
- host_config['MemorySwap'] = memswap_limit
+ host_config['MemorySwap'] = parse_bytes(memswap_limit)
if mem_swappiness is not None:
if version_lt(version, '1.20'):
@@ -878,9 +874,9 @@ def create_container_config(
if isinstance(labels, list):
labels = dict((lbl, six.text_type('')) for lbl in labels)
- if isinstance(mem_limit, six.string_types):
+ if mem_limit is not None:
mem_limit = parse_bytes(mem_limit)
- if isinstance(memswap_limit, six.string_types):
+ if memswap_limit is not None:
memswap_limit = parse_bytes(memswap_limit)
if isinstance(ports, list):
diff --git a/docs/api.md b/docs/api.md
index 32952bf..3b23ebd 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -998,12 +998,32 @@ Display the running processes of a container.
## unpause
-Unpauses all processes within a container.
+Unpause all processes within a container.
**Params**:
* container (str): The container to unpause
+## update_container
+
+Update resource configs of one or more containers.
+
+**Params**:
+
+* container (str): The container to inspect
+* blkio_weight (int): Block IO (relative weight), between 10 and 1000
+* cpu_period (int): Limit CPU CFS (Completely Fair Scheduler) period
+* cpu_quota (int): Limit CPU CFS (Completely Fair Scheduler) quota
+* cpu_shares (int): CPU shares (relative weight)
+* cpuset_cpus (str): CPUs in which to allow execution
+* cpuset_mems (str): MEMs in which to allow execution
+* mem_limit (int or str): Memory limit
+* mem_reservation (int or str): Memory soft limit
+* memswap_limit (int or str): Total memory (memory + swap), -1 to disable swap
+* kernel_memory (int or str): Kernel memory limit
+
+**Returns** (dict): Dictionary containing a `Warnings` key.
+
## version
Nearly identical to the `docker version` command.
diff --git a/tests/integration/container_test.py b/tests/integration/container_test.py
index aae7ad7..6120135 100644
--- a/tests/integration/container_test.py
+++ b/tests/integration/container_test.py
@@ -1044,3 +1044,21 @@ class GetContainerStatsTest(helpers.BaseTestCase):
for key in ['read', 'network', 'precpu_stats', 'cpu_stats',
'memory_stats', 'blkio_stats']:
self.assertIn(key, chunk)
+
+
+class ContainerUpdateTest(helpers.BaseTestCase):
+ @requires_api_version('1.22')
+ def test_update_container(self):
+ old_mem_limit = 400 * 1024 * 1024
+ new_mem_limit = 300 * 1024 * 1024
+ container = self.client.create_container(
+ BUSYBOX, 'top', host_config=self.client.create_host_config(
+ mem_limit=old_mem_limit
+ ), cpu_shares=102
+ )
+ self.tmp_containers.append(container)
+ self.client.start(container)
+ self.client.update_container(container, mem_limit=new_mem_limit)
+ inspect_data = self.client.inspect_container(container)
+ self.assertEqual(inspect_data['HostConfig']['Memory'], new_mem_limit)
+ self.assertEqual(inspect_data['HostConfig']['CpuShares'], 102)
diff --git a/tests/unit/container_test.py b/tests/unit/container_test.py
index d66eeed..6e23f89 100644
--- a/tests/unit/container_test.py
+++ b/tests/unit/container_test.py
@@ -1452,3 +1452,21 @@ class ContainerTest(DockerClientTest):
params={'ps_args': 'waux'},
timeout=DEFAULT_TIMEOUT_SECONDS
)
+
+ @requires_api_version('1.22')
+ def test_container_update(self):
+ self.client.update_container(
+ fake_api.FAKE_CONTAINER_ID, mem_limit='2k', cpu_shares=124,
+ blkio_weight=345
+ )
+ args = fake_request.call_args
+ self.assertEqual(
+ args[0][1], url_prefix + 'containers/3cc2351ab11b/update'
+ )
+ self.assertEqual(
+ json.loads(args[1]['data']),
+ {'Memory': 2 * 1024, 'CpuShares': 124, 'BlkioWeight': 345}
+ )
+ self.assertEqual(
+ args[1]['headers']['Content-Type'], 'application/json'
+ )
diff --git a/tests/unit/fake_api.py b/tests/unit/fake_api.py
index 8852da0..9952595 100644
--- a/tests/unit/fake_api.py
+++ b/tests/unit/fake_api.py
@@ -441,6 +441,11 @@ def get_fake_volume():
def fake_remove_volume():
return 204, None
+
+def post_fake_update_container():
+ return 200, {'Warnings': []}
+
+
# Maps real api url to fake response callback
prefix = 'http+docker://localunixsocket'
fake_responses = {
@@ -478,6 +483,8 @@ fake_responses = {
get_fake_diff,
'{1}/{0}/containers/3cc2351ab11b/export'.format(CURRENT_VERSION, prefix):
get_fake_export,
+ '{1}/{0}/containers/3cc2351ab11b/update'.format(CURRENT_VERSION, prefix):
+ post_fake_update_container,
'{1}/{0}/containers/3cc2351ab11b/exec'.format(CURRENT_VERSION, prefix):
post_fake_exec_create,
'{1}/{0}/exec/d5d177f121dc/start'.format(CURRENT_VERSION, prefix):