From fda6fa43f9b0a1e4fa4462118aff25dc65823223 Mon Sep 17 00:00:00 2001 From: rajat29 Date: Fri, 8 Jun 2018 15:29:28 +0530 Subject: [PATCH] Introduce container resize API Currently, the container resize is implemented at the PATCH /containers/ This PS seperating container resize (i.e update cpu/memory of container) from PATCH api Closes-Bug: #1759458 Change-Id: Ibcf9913b0dd31013abadf1f9eb70c761870a2c84 --- zun/api/controllers/v1/containers.py | 26 +++++++++++++++ zun/api/controllers/versions.py | 3 +- zun/api/rest_api_version_history.rst | 6 ++++ zun/common/policies/container.py | 11 +++++++ zun/common/utils.py | 4 ++- zun/compute/api.py | 3 ++ zun/compute/manager.py | 7 ++++ zun/compute/rpcapi.py | 4 +++ zun/tests/unit/api/base.py | 2 +- zun/tests/unit/api/controllers/test_root.py | 4 +-- .../api/controllers/v1/test_containers.py | 32 ++++++++++++++----- 11 files changed, 89 insertions(+), 13 deletions(-) diff --git a/zun/api/controllers/v1/containers.py b/zun/api/controllers/v1/containers.py index 74c7d4d44..511cfa4f8 100644 --- a/zun/api/controllers/v1/containers.py +++ b/zun/api/controllers/v1/containers.py @@ -168,6 +168,7 @@ class ContainersController(base.Controller): 'rename': ['POST'], 'attach': ['GET'], 'resize': ['POST'], + 'resize_container': ['POST'], 'top': ['GET'], 'get_archive': ['GET'], 'put_archive': ['POST'], @@ -597,6 +598,31 @@ class ContainersController(base.Controller): return view.format_container(context, pecan.request.host_url, container) + @base.Controller.api_version("1.19") + @pecan.expose('json') + @exception.wrap_pecan_controller_exception + @validation.validated(schema.container_update) + def resize_container(self, container_ident, **kwargs): + """Resize an existing container. + + :param container_ident: UUID or name of a container. + :param kwargs: cpu/memory to be updated. + """ + container = utils.get_container(container_ident) + check_policy_on_container(container.as_dict(), + "container:resize_container") + utils.validate_container_state(container, 'resize_container') + if 'memory' in kwargs: + kwargs['memory'] = str(kwargs['memory']) + if 'cpu' in kwargs: + kwargs['cpu'] = float(kwargs['cpu']) + context = pecan.request.context + compute_api = pecan.request.compute_api + compute_api.resize_container(context, container, kwargs) + pecan.response.status = 202 + return view.format_container(context, pecan.request.host_url, + container) + @pecan.expose('json') @exception.wrap_pecan_controller_exception @validation.validate_query_param(pecan.request, schema.query_param_delete) diff --git a/zun/api/controllers/versions.py b/zun/api/controllers/versions.py index 1ae9c6aeb..07cf5e6e7 100644 --- a/zun/api/controllers/versions.py +++ b/zun/api/controllers/versions.py @@ -51,10 +51,11 @@ REST_API_VERSION_HISTORY = """REST API Version History: * 1.16 - Modify restart_policy to capsule spec content * 1.17 - Add support for detaching ports * 1.18 - Modify the response of network list + * 1.19 - Intoduce container resize API """ BASE_VER = '1.1' -CURRENT_MAX_VER = '1.18' +CURRENT_MAX_VER = '1.19' class Version(object): diff --git a/zun/api/rest_api_version_history.rst b/zun/api/rest_api_version_history.rst index 0409c71d2..a77819ae2 100644 --- a/zun/api/rest_api_version_history.rst +++ b/zun/api/rest_api_version_history.rst @@ -161,3 +161,9 @@ user documentation. } ] } + +1.19 +---- + + Introduce an API endpoint for resizing a container, such as changing the + CPU or memory of the container. diff --git a/zun/common/policies/container.py b/zun/common/policies/container.py index 9e21fba84..1ab4af6dc 100644 --- a/zun/common/policies/container.py +++ b/zun/common/policies/container.py @@ -418,6 +418,17 @@ rules = [ } ] ), + policy.DocumentedRuleDefault( + name=CONTAINER % 'resize_container', + check_str=base.RULE_ADMIN_OR_OWNER, + description='Resize an existing container.', + operations=[ + { + 'path': '/v1/containers/{container_ident}/resize_container', + 'method': 'POST' + } + ] + ), ] diff --git a/zun/common/utils.py b/zun/common/utils.py index aeaa509b0..fd6e10b2a 100644 --- a/zun/common/utils.py +++ b/zun/common/utils.py @@ -80,7 +80,9 @@ VALID_STATES = { 'add_security_group': [consts.CREATED, consts.RUNNING, consts.STOPPED, consts.PAUSED], 'remove_security_group': [consts.CREATED, consts.RUNNING, consts.STOPPED, - consts.PAUSED] + consts.PAUSED], + 'resize_container': [consts.CREATED, consts.RUNNING, consts.STOPPED, + consts.PAUSED] } VALID_CONTAINER_FILED = { diff --git a/zun/compute/api.py b/zun/compute/api.py index b0e6889b3..ee4ea3a9a 100644 --- a/zun/compute/api.py +++ b/zun/compute/api.py @@ -236,3 +236,6 @@ class API(object): def network_create(self, context, network): return self.rpcapi.network_create(context, network) + + def resize_container(self, context, container, *args): + return self.rpcapi.resize_container(context, container, *args) diff --git a/zun/compute/manager.py b/zun/compute/manager.py index af800bc5f..22a93c3b1 100644 --- a/zun/compute/manager.py +++ b/zun/compute/manager.py @@ -1217,3 +1217,10 @@ class Manager(periodic_task.PeriodicTasks): docker_network = self.driver.create_network(context, network) network.network_id = docker_network['Id'] network.save() + + def resize_container(self, context, container, patch): + @utils.synchronized(container.uuid) + def do_container_resize(): + self.container_update(context, container, patch) + + utils.spawn_n(do_container_resize) diff --git a/zun/compute/rpcapi.py b/zun/compute/rpcapi.py index 647c72147..01988ce65 100644 --- a/zun/compute/rpcapi.py +++ b/zun/compute/rpcapi.py @@ -123,6 +123,10 @@ class API(rpc_service.API): return self._call(container.host, 'container_update', container=container, patch=patch) + def resize_container(self, context, container, patch): + self._cast(container.host, 'resize_container', + container=container, patch=patch) + @check_container_host def container_attach(self, context, container): return self._call(container.host, 'container_attach', diff --git a/zun/tests/unit/api/base.py b/zun/tests/unit/api/base.py index 6bd2cc0c3..db985e73e 100644 --- a/zun/tests/unit/api/base.py +++ b/zun/tests/unit/api/base.py @@ -26,7 +26,7 @@ from zun.tests.unit.db import base PATH_PREFIX = '/v1' -CURRENT_VERSION = "container 1.18" +CURRENT_VERSION = "container 1.19" class FunctionalTest(base.DbTestCase): diff --git a/zun/tests/unit/api/controllers/test_root.py b/zun/tests/unit/api/controllers/test_root.py index fbfbf9321..1605957c9 100644 --- a/zun/tests/unit/api/controllers/test_root.py +++ b/zun/tests/unit/api/controllers/test_root.py @@ -28,7 +28,7 @@ class TestRootController(api_base.FunctionalTest): 'default_version': {'id': 'v1', 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], - 'max_version': '1.18', + 'max_version': '1.19', 'min_version': '1.1', 'status': 'CURRENT'}, 'description': 'Zun is an OpenStack project which ' @@ -37,7 +37,7 @@ class TestRootController(api_base.FunctionalTest): 'versions': [{'id': 'v1', 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], - 'max_version': '1.18', + 'max_version': '1.19', 'min_version': '1.1', 'status': 'CURRENT'}]} diff --git a/zun/tests/unit/api/controllers/v1/test_containers.py b/zun/tests/unit/api/controllers/v1/test_containers.py index 5809d6ca7..070c24dba 100644 --- a/zun/tests/unit/api/controllers/v1/test_containers.py +++ b/zun/tests/unit/api/controllers/v1/test_containers.py @@ -1524,10 +1524,8 @@ class TestContainerController(api_base.FunctionalTest): @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_resize') @patch('zun.objects.Container.get_by_name') - def test_resize_container_by_uuid(self, - mock_get_by_uuid, - mock_container_resize, - mock_validate): + def test_resize_by_uuid(self, mock_get_by_uuid, mock_container_resize, + mock_validate): test_container_obj = objects.Container(self.context, **utils.get_test_container()) mock_container_resize.return_value = test_container_obj @@ -1546,10 +1544,8 @@ class TestContainerController(api_base.FunctionalTest): @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_resize') @patch('zun.objects.Container.get_by_uuid') - def test_resize_container_with_exception(self, - mock_get_by_uuid, - mock_container_resize, - mock_validate): + def test_resize_with_exception(self, mock_get_by_uuid, + mock_container_resize, mock_validate): mock_container_resize.return_value = "" test_container = utils.get_test_container() test_container_obj = objects.Container(self.context, **test_container) @@ -1563,6 +1559,26 @@ class TestContainerController(api_base.FunctionalTest): (container_uuid, 'resize'), body) self.assertTrue(mock_container_resize.called) + @patch('zun.common.utils.validate_container_state') + @patch('zun.compute.api.API.resize_container') + @patch('zun.objects.Container.get_by_name') + def test_resize_container(self, mock_get_by_uuid, + mock_resize_container, mock_validate): + test_container_obj = objects.Container(self.context, + **utils.get_test_container()) + mock_resize_container.return_value = test_container_obj + test_container = utils.get_test_container() + test_container_obj = objects.Container(self.context, **test_container) + mock_get_by_uuid.return_value = test_container_obj + + container_name = test_container.get('name') + url = '/v1/containers/%s/resize_container/' % container_name + params = {'cpu': 1, 'memory': '512'} + response = self.post(url, params) + self.assertEqual(202, response.status_int) + mock_resize_container.assert_called_once_with( + mock.ANY, test_container_obj, params) + @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_top') @patch('zun.objects.Container.get_by_uuid')