From 945cc04806aac8c042df653d01b7a90357876cf6 Mon Sep 17 00:00:00 2001 From: Valerii Kovalchuk Date: Thu, 22 Sep 2016 14:04:38 +0300 Subject: [PATCH] Allow to PUT an empty object model It was impossible to delete the last component from the env via CLI, because it was prohibited to make put request with empty body. APIImpact Change-Id: I462072ad7c90eec1790b74cae958809aa316c25c Closes-bug: #1511645 --- doc/source/specification/murano-api.rst | 87 +++++++++++++++++++ murano/api/v1/services.py | 5 +- murano/tests/unit/api/v1/test_services.py | 6 +- .../application_catalog_client.py | 13 +++ .../api/application_catalog/test_services.py | 38 ++++++++ .../test_services_negative.py | 26 ++++++ .../put-empty-body-d605c2083b239f76.yaml | 6 ++ 7 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/put-empty-body-d605c2083b239f76.yaml diff --git a/doc/source/specification/murano-api.rst b/doc/source/specification/murano-api.rst index 3449f52ba..fc6b6a342 100644 --- a/doc/source/specification/murano-api.rst +++ b/doc/source/specification/murano-api.rst @@ -716,6 +716,93 @@ Created application returned | 400 | Required header or body are not provided | +----------------+-----------------------------------------------------------+ +Update applications +------------------- + +Applications list for environment can be updated. + +*Request* + +**Content-Type** + application/json + ++----------------+-----------------------------------------------------------+------------------------------------+ +| Method | URI | Header | ++================+===========================================================+====================================+ +| PUT | /environments//services | X-Configuration-Session | ++----------------+-----------------------------------------------------------+------------------------------------+ + +:: + + [{ + "instance": { + "availabilityZone": "nova", + "name": "apache-instance", + "assignFloatingIp": true, + "keyname": "", + "flavor": "m1.small", + "image": "146d5523-7b2d-4abc-b0d0-2041f920ce26", + "?": { + "type": "io.murano.resources.LinuxMuranoInstance", + "id": "25185cb6f29b415fa2e438309827a797" + } + }, + "name": "ApacheHttpServer", + "enablePHP": true, + "?": { + "type": "com.example.apache.ApacheHttpServer", + "id": "6e66106d7dcb4748a5c570150a3df80f" + } + }] + + +*Response* + +Updated applications list returned + + +**Content-Type** + application/json + +:: + + [{ + "instance": { + "availabilityZone": "nova", + "name": "apache-instance", + "assignFloatingIp": true, + "keyname": "", + "flavor": "m1.small", + "image": "146d5523-7b2d-4abc-b0d0-2041f920ce26", + "?": { + "type": "io.murano.resources.LinuxMuranoInstance", + "id": "25185cb6f29b415fa2e438309827a797" + } + }, + "name": "ApacheHttpServer", + "enablePHP": true, + "?": { + "type": "com.example.apache.ApacheHttpServer", + "id": "6e66106d7dcb4748a5c570150a3df80f" + } + }] + ++----------------+-----------------------------------------------------------+ +| Code | Description | ++================+===========================================================+ +| 200 | Services are updated successfully | ++----------------+-----------------------------------------------------------+ +| 400 | Required header is not provided | ++----------------+-----------------------------------------------------------+ +| 401 | User is not authorized | ++----------------+-----------------------------------------------------------+ +| 403 | Session is in deploying state and could not be updated | +| | or user is not allowed to update services | ++----------------+-----------------------------------------------------------+ +| 404 | Not found. Specified environment and/or session do not | +| | exist | ++----------------+-----------------------------------------------------------+ + Delete application from environment ----------------------------------- diff --git a/murano/api/v1/services.py b/murano/api/v1/services.py index 5db9d034e..7089e1e54 100644 --- a/murano/api/v1/services.py +++ b/murano/api/v1/services.py @@ -92,10 +92,7 @@ class Controller(object): @normalize_path def put(self, request, environment_id, path, body=None): if not body: - msg = _('Request body is empty: please, provide ' - 'application object model') - LOG.error(msg) - raise exc.HTTPBadRequest(msg) + body = [] LOG.debug('Services:Put '.format(environment_id, body, path)) diff --git a/murano/tests/unit/api/v1/test_services.py b/murano/tests/unit/api/v1/test_services.py index f91ad1c4b..ef97295f0 100644 --- a/murano/tests/unit/api/v1/test_services.py +++ b/murano/tests/unit/api/v1/test_services.py @@ -110,8 +110,10 @@ class TestServicesApi(tb.ControllerTest, tb.MuranoApiTestCase): request.headers['X-Configuration-Session'] = str(session_id) request.context.session = session_id - self.assertRaises(exc.HTTPBadRequest, self.services_controller.put, - request, environment_id, path) + # Check that empty body can be put + response = self.services_controller.put(request, environment_id, + path, []) + self.assertEqual([], response) response = self.services_controller.put(request, environment_id, path, "test service") diff --git a/murano_tempest_tests/services/application_catalog/application_catalog_client.py b/murano_tempest_tests/services/application_catalog/application_catalog_client.py index 7876229e4..bb83d8ed9 100644 --- a/murano_tempest_tests/services/application_catalog/application_catalog_client.py +++ b/murano_tempest_tests/services/application_catalog/application_catalog_client.py @@ -196,6 +196,19 @@ class ApplicationCatalogClient(rest_client.RestClient): self.expected_success(200, resp.status) return self._parse_resp(body) + def update_services(self, environment_id, session_id, put_body=None): + headers = self.get_headers() + headers.update( + {'X-Configuration-Session': session_id} + ) + uri = 'v1/environments/{0}/services'.format(environment_id) + resp, body = self.put(uri, json.dumps(put_body), headers) + self.expected_success(200, resp.status) + # TODO(freerunner): Need to replace json.loads() to _parse_resp + # method, when fix for https://bugs.launchpad.net/tempest/+bug/1539927 + # will resolved and new version of tempest-lib released. + return json.loads(body) + def delete_service(self, environment_id, session_id, service_id): headers = self.get_headers() headers.update( diff --git a/murano_tempest_tests/tests/api/application_catalog/test_services.py b/murano_tempest_tests/tests/api/application_catalog/test_services.py index b46f7d650..37b384ad4 100644 --- a/murano_tempest_tests/tests/api/application_catalog/test_services.py +++ b/murano_tempest_tests/tests/api/application_catalog/test_services.py @@ -65,6 +65,44 @@ class TestServices(base.BaseApplicationCatalogTest): get_services_list(self.environment['id'], session['id']) self.assertEqual(len(services_list), len(services_list_)) + @testtools.testcase.attr('smoke') + def test_update_services_via_put(self): + session = self.application_catalog_client.\ + create_session(self.environment['id']) + self.addCleanup(self.application_catalog_client.delete_session, + self.environment['id'], session['id']) + put_body = [self._get_demo_app()] + self.application_catalog_client.\ + update_services(self.environment['id'], session['id'], put_body) + services_list = self.application_catalog_client.\ + get_services_list(self.environment['id'], session['id']) + self.assertEqual(1, len(services_list)) + + @testtools.testcase.attr('smoke') + def test_clear_services_via_put(self): + session = self.application_catalog_client.\ + create_session(self.environment['id']) + self.addCleanup(self.application_catalog_client.delete_session, + self.environment['id'], session['id']) + services_list = self.application_catalog_client.\ + get_services_list(self.environment['id'], session['id']) + post_body = self._get_demo_app() + self.application_catalog_client.\ + create_service(self.environment['id'], session['id'], post_body) + services_list_ = self.application_catalog_client.\ + get_services_list(self.environment['id'], session['id']) + self.assertEqual(len(services_list) + 1, len(services_list_)) + self.application_catalog_client.\ + update_services(self.environment['id'], session['id']) + services_list_ = self.application_catalog_client.\ + get_services_list(self.environment['id'], session['id']) + self.assertEqual(0, len(services_list_)) + self.application_catalog_client.\ + create_service(self.environment['id'], session['id'], post_body) + services_list_ = self.application_catalog_client.\ + get_services_list(self.environment['id'], session['id']) + self.assertEqual(1, len(services_list_)) + @testtools.testcase.attr('smoke') def test_get_service(self): session = self.application_catalog_client.\ diff --git a/murano_tempest_tests/tests/api/application_catalog/test_services_negative.py b/murano_tempest_tests/tests/api/application_catalog/test_services_negative.py index 623242305..62b8a48f3 100644 --- a/murano_tempest_tests/tests/api/application_catalog/test_services_negative.py +++ b/murano_tempest_tests/tests/api/application_catalog/test_services_negative.py @@ -159,6 +159,32 @@ class TestServicesNegative(base.BaseApplicationCatalogTest): session['id'], service['?']['id']) + @testtools.testcase.attr('negative') + def test_put_services_without_env_id(self): + session = self.application_catalog_client.\ + create_session(self.environment['id']) + self.addCleanup(self.application_catalog_client.delete_session, + self.environment['id'], session['id']) + put_body = [self._get_demo_app()] + self.assertRaises(exceptions.NotFound, + self.application_catalog_client.update_services, + None, + session['id'], + put_body) + + @testtools.testcase.attr('negative') + def test_put_services_without_sess_id(self): + session = self.application_catalog_client.\ + create_session(self.environment['id']) + self.addCleanup(self.application_catalog_client.delete_session, + self.environment['id'], session['id']) + put_body = [self._get_demo_app()] + self.assertRaises(exceptions.BadRequest, + self.application_catalog_client.update_services, + self.environment['id'], + "", + put_body) + class TestServicesNegativeTenantIsolation(base.BaseApplicationCatalogTest): diff --git a/releasenotes/notes/put-empty-body-d605c2083b239f76.yaml b/releasenotes/notes/put-empty-body-d605c2083b239f76.yaml new file mode 100644 index 000000000..efc452c3c --- /dev/null +++ b/releasenotes/notes/put-empty-body-d605c2083b239f76.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - It is now possible to make a PUT request with body equal to '[]' to + '/environments//services' endpoint. This will result in removing + all apps from current session. This allows deleting the last application + from environment from CLI.