mirror of https://github.com/docker/docker-py.git
Merge pull request #916 from docker/container_update_feature
Support for container limits update
This commit is contained in:
commit
81d8caaf36
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
return 0
|
||||
|
||||
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:
|
||||
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()
|
||||
digits_part = s[:-1]
|
||||
|
||||
# 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]
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
# 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):
|
||||
|
|
22
docs/api.md
22
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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
)
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue