From ef8897f58bc959505be105c499d0bfbe5e872cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Guillot?= Date: Fri, 12 May 2017 13:28:10 -0400 Subject: [PATCH] Add API versioning - All OpenStack projects have API versioning - Existing endpoints are now prefixed with /v1 - Still fully backward compatible with old endpoints - No HTTP redirects is used to avoid unexpected behaviors with existing clients Change-Id: If51f3291c44615991b3378b711dffacc1bd2591f --- almanach/api/v1/routes.py | 22 +++++- .../tests/tempest/services/almanach_client.py | 23 ++++--- .../unit/api/v1/test_api_authentication.py | 2 +- almanach/tests/unit/api/v1/test_api_entity.py | 26 ++++--- almanach/tests/unit/api/v1/test_api_info.py | 8 ++- .../tests/unit/api/v1/test_api_instance.py | 68 ++++++++----------- almanach/tests/unit/api/v1/test_api_volume.py | 55 ++++++++++----- .../tests/unit/api/v1/test_api_volume_type.py | 10 +-- doc/source/index.rst | 44 ++++++------ 9 files changed, 149 insertions(+), 109 deletions(-) diff --git a/almanach/api/v1/routes.py b/almanach/api/v1/routes.py index c73feff..4fa8c52 100644 --- a/almanach/api/v1/routes.py +++ b/almanach/api/v1/routes.py @@ -24,7 +24,7 @@ from werkzeug import wrappers from almanach.core import exception LOG = log.getLogger(__name__) -api = flask.Blueprint("api", __name__) +api = flask.Blueprint('v1', __name__) instance_ctl = None volume_ctl = None volume_type_ctl = None @@ -85,12 +85,14 @@ def authenticated(api_call): return decorator +@api.route("/v1/info", methods=["GET"]) @api.route("/info", methods=["GET"]) @to_json def get_info(): return app_ctl.get_application_info() +@api.route("/v1/project//instance", methods=["POST"]) @api.route("/project//instance", methods=["POST"]) @authenticated @to_json @@ -112,6 +114,7 @@ def create_instance(project_id): return flask.Response(status=201) +@api.route("/v1/instance/", methods=["DELETE"]) @api.route("/instance/", methods=["DELETE"]) @authenticated @to_json @@ -126,6 +129,7 @@ def delete_instance(instance_id): return flask.Response(status=202) +@api.route("/v1/instance//resize", methods=["PUT"]) @api.route("/instance//resize", methods=["PUT"]) @authenticated @to_json @@ -141,6 +145,7 @@ def resize_instance(instance_id): return flask.Response(status=200) +@api.route("/v1/instance//rebuild", methods=["PUT"]) @api.route("/instance//rebuild", methods=["PUT"]) @authenticated @to_json @@ -158,6 +163,7 @@ def rebuild_instance(instance_id): return flask.Response(status=200) +@api.route("/v1/project//instances", methods=["GET"]) @api.route("/project//instances", methods=["GET"]) @authenticated @to_json @@ -167,6 +173,7 @@ def list_instances(project_id): return instance_ctl.list_instances(project_id, start, end) +@api.route("/v1/project//volume", methods=["POST"]) @api.route("/project//volume", methods=["POST"]) @authenticated @to_json @@ -186,6 +193,7 @@ def create_volume(project_id): return flask.Response(status=201) +@api.route("/v1/volume/", methods=["DELETE"]) @api.route("/volume/", methods=["DELETE"]) @authenticated @to_json @@ -200,6 +208,7 @@ def delete_volume(volume_id): return flask.Response(status=202) +@api.route("/v1/volume//resize", methods=["PUT"]) @api.route("/volume//resize", methods=["PUT"]) @authenticated @to_json @@ -215,6 +224,7 @@ def resize_volume(volume_id): return flask.Response(status=200) +@api.route("/v1/volume//attach", methods=["PUT"]) @api.route("/volume//attach", methods=["PUT"]) @authenticated @to_json @@ -230,6 +240,7 @@ def attach_volume(volume_id): return flask.Response(status=200) +@api.route("/v1/volume//detach", methods=["PUT"]) @api.route("/volume//detach", methods=["PUT"]) @authenticated @to_json @@ -245,6 +256,7 @@ def detach_volume(volume_id): return flask.Response(status=200) +@api.route("/v1/project//volumes", methods=["GET"]) @api.route("/project//volumes", methods=["GET"]) @authenticated @to_json @@ -254,6 +266,7 @@ def list_volumes(project_id): return volume_ctl.list_volumes(project_id, start, end) +@api.route("/v1/project//entities", methods=["GET"]) @api.route("/project//entities", methods=["GET"]) @authenticated @to_json @@ -263,6 +276,7 @@ def list_entity(project_id): return entity_ctl.list_entities(project_id, start, end) +@api.route("/v1/entity/instance/", methods=["PUT"]) @api.route("/entity/instance/", methods=["PUT"]) @authenticated @to_json @@ -277,6 +291,7 @@ def update_instance_entity(instance_id): return result +@api.route("/v1/entity/", methods=["HEAD"]) @api.route("/entity/", methods=["HEAD"]) @authenticated def entity_exists(entity_id): @@ -287,6 +302,7 @@ def entity_exists(entity_id): return response +@api.route("/v1/entity/", methods=["GET"]) @api.route("/entity/", methods=["GET"]) @authenticated @to_json @@ -294,6 +310,7 @@ def get_entity(entity_id): return entity_ctl.get_all_entities_by_id(entity_id) +@api.route("/v1/volume_types", methods=["GET"]) @api.route("/volume_types", methods=["GET"]) @authenticated @to_json @@ -302,6 +319,7 @@ def list_volume_types(): return volume_type_ctl.list_volume_types() +@api.route("/v1/volume_type/", methods=["GET"]) @api.route("/volume_type/", methods=["GET"]) @authenticated @to_json @@ -310,6 +328,7 @@ def get_volume_type(volume_type_id): return volume_type_ctl.get_volume_type(volume_type_id) +@api.route("/v1/volume_type", methods=["POST"]) @api.route("/volume_type", methods=["POST"]) @authenticated @to_json @@ -323,6 +342,7 @@ def create_volume_type(): return flask.Response(status=201) +@api.route("/v1/volume_type/", methods=["DELETE"]) @api.route("/volume_type/", methods=["DELETE"]) @authenticated @to_json diff --git a/almanach/tests/tempest/services/almanach_client.py b/almanach/tests/tempest/services/almanach_client.py index 8349bad..b07ec8f 100644 --- a/almanach/tests/tempest/services/almanach_client.py +++ b/almanach/tests/tempest/services/almanach_client.py @@ -19,6 +19,7 @@ CONF = config.CONF class AlmanachClient(rest_client.RestClient): + api_version = 'v1' def __init__(self, auth_provider): super(AlmanachClient, self).__init__( @@ -32,11 +33,11 @@ class AlmanachClient(rest_client.RestClient): return resp, response_body def create_server(self, tenant_id, body): - resp, response_body = self.post('/project/{}/instance'.format(tenant_id), body=body) + resp, response_body = self.post('project/{}/instance'.format(tenant_id), body=body) return resp, response_body def delete_server(self, instance_id, body): - resp, response_body = self.delete('/instance/{}'.format(instance_id), body=body) + resp, response_body = self.delete('instance/{}'.format(instance_id), body=body) return resp, response_body def get_volume_type(self, volume_type_id): @@ -44,28 +45,28 @@ class AlmanachClient(rest_client.RestClient): return resp, response_body def create_volume_type(self, body): - resp, response_body = self.post('/volume_type', body) + resp, response_body = self.post('volume_type', body) return resp, response_body def create_volume(self, tenant_id, body): - url = '/project/{}/volume'.format(tenant_id) + url = 'project/{}/volume'.format(tenant_id) resp, response_body = self.post(url, body) return resp, response_body def delete_volume(self, volume_id, body): - resp, response_body = self.delete('/volume/{}'.format(volume_id), body=body) + resp, response_body = self.delete('volume/{}'.format(volume_id), body=body) return resp, response_body def resize_volume(self, volume_id, body): - resp, response_body = self.put('/volume/{}/resize'.format(volume_id), body) + resp, response_body = self.put('volume/{}/resize'.format(volume_id), body) return resp, response_body def attach_volume(self, volume_id, body): - resp, response_body = self.put('/volume/{}/attach'.format(volume_id), body) + resp, response_body = self.put('volume/{}/attach'.format(volume_id), body) return resp, response_body def detach_volume(self, volume_id, body): - resp, response_body = self.put('/volume/{}/detach'.format(volume_id), body) + resp, response_body = self.put('volume/{}/detach'.format(volume_id), body) return resp, response_body def get_tenant_entities(self, tenant_id): @@ -74,16 +75,16 @@ class AlmanachClient(rest_client.RestClient): return resp, response_body def update_server(self, instance_id, body): - url = '/entity/instance/{}'.format(instance_id) + url = 'entity/instance/{}'.format(instance_id) resp, response_body = self.put(url, body) return resp, response_body def rebuild(self, instance_id, body): - update_instance_rebuild_query = "/instance/{}/rebuild".format(instance_id) + update_instance_rebuild_query = "instance/{}/rebuild".format(instance_id) resp, response_body = self.put(update_instance_rebuild_query, body) return resp, response_body def resize(self, instance_id, body): - url = "/instance/{}/resize".format(instance_id) + url = "instance/{}/resize".format(instance_id) resp, response_body = self.put(url, body) return resp, response_body diff --git a/almanach/tests/unit/api/v1/test_api_authentication.py b/almanach/tests/unit/api/v1/test_api_authentication.py index 241c2cd..f0a52fc 100644 --- a/almanach/tests/unit/api/v1/test_api_authentication.py +++ b/almanach/tests/unit/api/v1/test_api_authentication.py @@ -26,7 +26,7 @@ class TestApiAuthentication(base_api.BaseApi): self.auth_adapter.validate.side_effect = exception.AuthenticationFailureException('Unauthorized') query_string = {'start': '2014-01-01 00:00:00.0000', 'end': '2014-02-01 00:00:00.0000'} - code, result = self.api_get(url='/project/TENANT_ID/entities', + code, result = self.api_get(url='/v1/project/TENANT_ID/entities', query_string=query_string, headers={'X-Auth-Token': 'wrong token'}) diff --git a/almanach/tests/unit/api/v1/test_api_entity.py b/almanach/tests/unit/api/v1/test_api_entity.py index c7af136..e5188b3 100644 --- a/almanach/tests/unit/api/v1/test_api_entity.py +++ b/almanach/tests/unit/api/v1/test_api_entity.py @@ -35,7 +35,7 @@ class TestApiEntity(base_api.BaseApi): end = '2016-03-03 00:00:00.000000' code, result = self.api_put( - '/entity/instance/INSTANCE_ID', + '/v1/entity/instance/INSTANCE_ID', headers={'X-Auth-Token': 'some token value'}, query_string={ 'start': start, @@ -63,7 +63,7 @@ class TestApiEntity(base_api.BaseApi): self.entity_ctl.update_active_instance_entity.return_value = an_instance code, result = self.api_put( - '/entity/instance/INSTANCE_ID', + '/v1/entity/instance/INSTANCE_ID', headers={'X-Auth-Token': 'some token value'}, data=data, ) @@ -87,11 +87,9 @@ class TestApiEntity(base_api.BaseApi): 'flavor': 'A_FLAVOR', } - code, result = self.api_put( - '/entity/instance/INSTANCE_ID', - data=data, - headers={'X-Auth-Token': 'some token value'} - ) + code, result = self.api_put('/v1/entity/instance/INSTANCE_ID', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.entity_ctl.update_active_instance_entity.assert_called_once_with(instance_id=instance_id, **data) self.assertIn("error", result) @@ -115,11 +113,9 @@ class TestApiEntity(base_api.BaseApi): 'flavor': 'A_FLAVOR', } - code, result = self.api_put( - '/entity/instance/INSTANCE_ID', - data=data, - headers={'X-Auth-Token': 'some token value'} - ) + code, result = self.api_put('/v1/entity/instance/INSTANCE_ID', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.entity_ctl.update_active_instance_entity.assert_called_once_with(instance_id=instance_id, **data) self.assertIn("error", result) @@ -130,7 +126,8 @@ class TestApiEntity(base_api.BaseApi): self.entity_ctl.entity_exists.return_value = True entity_id = "entity_id" - code, result = self.api_head('/entity/{id}'.format(id=entity_id), headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_head('/v1/entity/{id}'.format(id=entity_id), + headers={'X-Auth-Token': 'some token value'}) self.entity_ctl.entity_exists.assert_called_once_with(entity_id=entity_id) self.assertEqual(code, 200) @@ -139,7 +136,8 @@ class TestApiEntity(base_api.BaseApi): self.entity_ctl.entity_exists.return_value = False entity_id = "entity_id" - code, result = self.api_head('/entity/{id}'.format(id=entity_id), headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_head('/v1/entity/{id}'.format(id=entity_id), + headers={'X-Auth-Token': 'some token value'}) self.entity_ctl.entity_exists.assert_called_once_with(entity_id=entity_id) self.assertEqual(code, 404) diff --git a/almanach/tests/unit/api/v1/test_api_info.py b/almanach/tests/unit/api/v1/test_api_info.py index fd16a04..8ce92f5 100644 --- a/almanach/tests/unit/api/v1/test_api_info.py +++ b/almanach/tests/unit/api/v1/test_api_info.py @@ -18,10 +18,16 @@ from almanach.tests.unit.api.v1 import base_api class TestApiInfo(base_api.BaseApi): def test_info(self): + self.assert_info_call('/v1/info') + + def test_info_with_legacy_url(self): + self.assert_info_call('/info') + + def assert_info_call(self, url): info = {'info': {'version': '1.0'}, 'database': {'all_entities': 10, 'active_entities': 2}} self.app_ctl.get_application_info.return_value = info - code, result = self.api_get('/info') + code, result = self.api_get(url) self.app_ctl.get_application_info.assert_called_once() self.assertEqual(code, 200) diff --git a/almanach/tests/unit/api/v1/test_api_instance.py b/almanach/tests/unit/api/v1/test_api_instance.py index 66508a2..4c24026 100644 --- a/almanach/tests/unit/api/v1/test_api_instance.py +++ b/almanach/tests/unit/api/v1/test_api_instance.py @@ -22,7 +22,7 @@ class TestApiInstance(base_api.BaseApi): def test_get_instances(self): self.instance_ctl.list_instances.return_value = [a(instance().with_id('123'))] - code, result = self.api_get('/project/TENANT_ID/instances', + code, result = self.api_get('/v1/project/TENANT_ID/instances', query_string={ 'start': '2014-01-01 00:00:00.0000', 'end': '2014-02-01 00:00:00.0000' @@ -47,11 +47,9 @@ class TestApiInstance(base_api.BaseApi): os_distro="A_DISTRIBUTION", os_version="AN_OS_VERSION") - code, result = self.api_post( - '/project/PROJECT_ID/instance', - data=data, - headers={'X-Auth-Token': 'some token value'} - ) + code, result = self.api_post('/v1/project/PROJECT_ID/instance', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.instance_ctl.create_instance.assert_called_once_with( tenant_id="PROJECT_ID", @@ -73,11 +71,9 @@ class TestApiInstance(base_api.BaseApi): os_type="AN_OS_TYPE", os_version="AN_OS_VERSION") - code, result = self.api_post( - '/project/PROJECT_ID/instance', - data=data, - headers={'X-Auth-Token': 'some token value'} - ) + code, result = self.api_post('/v1/project/PROJECT_ID/instance', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.instance_ctl.create_instance.assert_not_called() self.assertEqual(result["error"], "The 'os_distro' param is mandatory for the request you have made.") @@ -93,11 +89,9 @@ class TestApiInstance(base_api.BaseApi): os_distro="A_DISTRIBUTION", os_version="AN_OS_VERSION") - code, result = self.api_post( - '/project/PROJECT_ID/instance', - data=data, - headers={'X-Auth-Token': 'some token value'} - ) + code, result = self.api_post('/v1/project/PROJECT_ID/instance', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.instance_ctl.create_instance.assert_called_once_with( tenant_id="PROJECT_ID", @@ -122,7 +116,7 @@ class TestApiInstance(base_api.BaseApi): flavor="A_FLAVOR") code, result = self.api_put( - '/instance/INSTANCE_ID/resize', + '/v1/instance/INSTANCE_ID/resize', data=data, headers={'X-Auth-Token': 'some token value'} ) @@ -137,7 +131,9 @@ class TestApiInstance(base_api.BaseApi): def test_successfull_instance_delete(self): data = dict(date="DELETE_DATE") - code, result = self.api_delete('/instance/INSTANCE_ID', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_delete('/v1/instance/INSTANCE_ID', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.instance_ctl.delete_instance.assert_called_once_with( instance_id="INSTANCE_ID", @@ -147,7 +143,7 @@ class TestApiInstance(base_api.BaseApi): def test_instance_delete_missing_a_param_returns_bad_request_code(self): code, result = self.api_delete( - '/instance/INSTANCE_ID', + '/v1/instance/INSTANCE_ID', data=dict(), headers={'X-Auth-Token': 'some token value'} ) @@ -160,7 +156,7 @@ class TestApiInstance(base_api.BaseApi): self.assertEqual(code, 400) def test_instance_delete_no_data_bad_request_code(self): - code, result = self.api_delete('/instance/INSTANCE_ID', headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_delete('/v1/instance/INSTANCE_ID', headers={'X-Auth-Token': 'some token value'}) self.instance_ctl.delete_instance.assert_not_called() self.assertIn( @@ -173,7 +169,9 @@ class TestApiInstance(base_api.BaseApi): data = dict(date="A_BAD_DATE") self.instance_ctl.delete_instance.side_effect = exception.DateFormatException - code, result = self.api_delete('/instance/INSTANCE_ID', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_delete('/v1/instance/INSTANCE_ID', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.instance_ctl.delete_instance.assert_called_once_with( instance_id="INSTANCE_ID", @@ -189,7 +187,7 @@ class TestApiInstance(base_api.BaseApi): def test_instance_resize_missing_a_param_returns_bad_request_code(self): data = dict(date="UPDATED_AT") code, result = self.api_put( - '/instance/INSTANCE_ID/resize', + '/v1/instance/INSTANCE_ID/resize', data=data, headers={'X-Auth-Token': 'some token value'} ) @@ -206,7 +204,7 @@ class TestApiInstance(base_api.BaseApi): data = dict(date="A_BAD_DATE", flavor="A_FLAVOR") code, result = self.api_put( - '/instance/INSTANCE_ID/resize', + '/v1/instance/INSTANCE_ID/resize', data=data, headers={'X-Auth-Token': 'some token value'} ) @@ -231,11 +229,9 @@ class TestApiInstance(base_api.BaseApi): 'os_type': 'AN_OS_TYPE', 'rebuild_date': 'UPDATE_DATE', } - code, result = self.api_put( - '/instance/INSTANCE_ID/rebuild', - data=data, - headers={'X-Auth-Token': 'some token value'} - ) + code, result = self.api_put('/v1/instance/INSTANCE_ID/rebuild', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.instance_ctl.rebuild_instance.assert_called_once_with( instance_id=instance_id, @@ -251,11 +247,9 @@ class TestApiInstance(base_api.BaseApi): 'distro': 'A_DISTRIBUTION', 'rebuild_date': 'UPDATE_DATE', } - code, result = self.api_put( - '/instance/INSTANCE_ID/rebuild', - data=data, - headers={'X-Auth-Token': 'some token value'} - ) + code, result = self.api_put('/v1/instance/INSTANCE_ID/rebuild', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.instance_ctl.rebuild_instance.assert_not_called() self.assertIn( @@ -273,11 +267,9 @@ class TestApiInstance(base_api.BaseApi): 'os_type': 'AN_OS_TYPE', 'rebuild_date': 'A_BAD_UPDATE_DATE', } - code, result = self.api_put( - '/instance/INSTANCE_ID/rebuild', - data=data, - headers={'X-Auth-Token': 'some token value'} - ) + code, result = self.api_put('/v1/instance/INSTANCE_ID/rebuild', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.instance_ctl.rebuild_instance.assert_called_once_with( instance_id=instance_id, diff --git a/almanach/tests/unit/api/v1/test_api_volume.py b/almanach/tests/unit/api/v1/test_api_volume.py index 5cbb8ca..7ccc84f 100644 --- a/almanach/tests/unit/api/v1/test_api_volume.py +++ b/almanach/tests/unit/api/v1/test_api_volume.py @@ -29,7 +29,7 @@ class TestApiVolume(base_api.BaseApi): attached_to=["INSTANCE_ID"]) code, result = self.api_post( - '/project/PROJECT_ID/volume', + '/v1/project/PROJECT_ID/volume', data=data, headers={'X-Auth-Token': 'some token value'} ) @@ -48,7 +48,7 @@ class TestApiVolume(base_api.BaseApi): attached_to=[]) code, result = self.api_post( - '/project/PROJECT_ID/volume', + '/v1/project/PROJECT_ID/volume', data=data, headers={'X-Auth-Token': 'some token value'} ) @@ -70,7 +70,7 @@ class TestApiVolume(base_api.BaseApi): attached_to=["INSTANCE_ID"]) code, result = self.api_post( - '/project/PROJECT_ID/volume', + '/v1/project/PROJECT_ID/volume', data=data, headers={'X-Auth-Token': 'some token value'} ) @@ -89,7 +89,9 @@ class TestApiVolume(base_api.BaseApi): def test_successful_volume_delete(self): data = dict(date="DELETE_DATE") - code, result = self.api_delete('/volume/VOLUME_ID', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_delete('/v1/volume/VOLUME_ID', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.volume_ctl.delete_volume.assert_called_once_with( volume_id="VOLUME_ID", @@ -98,7 +100,9 @@ class TestApiVolume(base_api.BaseApi): self.assertEqual(code, 202) def test_volume_delete_missing_a_param_returns_bad_request_code(self): - code, result = self.api_delete('/volume/VOLUME_ID', data=dict(), headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_delete('/v1/volume/VOLUME_ID', + data=dict(), + headers={'X-Auth-Token': 'some token value'}) self.assertIn( "The 'date' param is mandatory for the request you have made.", @@ -108,7 +112,8 @@ class TestApiVolume(base_api.BaseApi): self.assertEqual(code, 400) def test_volume_delete_no_data_bad_request_code(self): - code, result = self.api_delete('/volume/VOLUME_ID', headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_delete('/v1/volume/VOLUME_ID', + headers={'X-Auth-Token': 'some token value'}) self.assertIn( "Invalid parameter or payload", @@ -121,7 +126,9 @@ class TestApiVolume(base_api.BaseApi): self.volume_ctl.delete_volume.side_effect = exception.DateFormatException data = dict(date="A_BAD_DATE") - code, result = self.api_delete('/volume/VOLUME_ID', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_delete('/v1/volume/VOLUME_ID', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.assertIn( "The provided date has an invalid format. " @@ -138,7 +145,9 @@ class TestApiVolume(base_api.BaseApi): data = dict(date="UPDATED_AT", size="NEW_SIZE") - code, result = self.api_put('/volume/VOLUME_ID/resize', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_put('/v1/volume/VOLUME_ID/resize', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.volume_ctl.resize_volume.assert_called_once_with( volume_id="VOLUME_ID", @@ -150,7 +159,9 @@ class TestApiVolume(base_api.BaseApi): def test_volume_resize_missing_a_param_returns_bad_request_code(self): data = dict(date="A_DATE") - code, result = self.api_put('/volume/VOLUME_ID/resize', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_put('/v1/volume/VOLUME_ID/resize', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.assertIn( "The 'size' param is mandatory for the request you have made.", @@ -164,7 +175,9 @@ class TestApiVolume(base_api.BaseApi): data = dict(date="BAD_DATE", size="NEW_SIZE") - code, result = self.api_put('/volume/VOLUME_ID/resize', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_put('/v1/volume/VOLUME_ID/resize', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.assertIn( "The provided date has an invalid format. " @@ -182,7 +195,9 @@ class TestApiVolume(base_api.BaseApi): data = dict(date="UPDATED_AT", attachments=[uuidutils.generate_uuid()]) - code, result = self.api_put('/volume/VOLUME_ID/attach', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_put('/v1/volume/VOLUME_ID/attach', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.volume_ctl.attach_volume.assert_called_once_with( volume_id="VOLUME_ID", @@ -195,7 +210,7 @@ class TestApiVolume(base_api.BaseApi): data = dict(date="A_DATE") code, result = self.api_put( - '/volume/VOLUME_ID/attach', + '/v1/volume/VOLUME_ID/attach', data=data, headers={'X-Auth-Token': 'some token value'} ) @@ -212,7 +227,9 @@ class TestApiVolume(base_api.BaseApi): data = dict(date="A_BAD_DATE", attachments=[uuidutils.generate_uuid()]) - code, result = self.api_put('/volume/VOLUME_ID/attach', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_put('/v1/volume/VOLUME_ID/attach', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.assertIn( "The provided date has an invalid format. " @@ -230,7 +247,9 @@ class TestApiVolume(base_api.BaseApi): data = dict(date="UPDATED_AT", attachments=[uuidutils.generate_uuid()]) - code, result = self.api_put('/volume/VOLUME_ID/detach', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_put('/v1/volume/VOLUME_ID/detach', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.volume_ctl.detach_volume.assert_called_once_with( volume_id="VOLUME_ID", @@ -242,7 +261,9 @@ class TestApiVolume(base_api.BaseApi): def test_volume_detach_missing_a_param_returns_bad_request_code(self): data = dict(date="A_DATE") - code, result = self.api_put('/volume/VOLUME_ID/detach', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_put('/v1/volume/VOLUME_ID/detach', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.assertIn( "The 'attachments' param is mandatory for the request you have made.", @@ -256,7 +277,9 @@ class TestApiVolume(base_api.BaseApi): data = dict(date="A_BAD_DATE", attachments=[uuidutils.generate_uuid()]) - code, result = self.api_put('/volume/VOLUME_ID/detach', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_put('/v1/volume/VOLUME_ID/detach', + data=data, + headers={'X-Auth-Token': 'some token value'}) self.assertIn( "The provided date has an invalid format. " diff --git a/almanach/tests/unit/api/v1/test_api_volume_type.py b/almanach/tests/unit/api/v1/test_api_volume_type.py index c98605c..a5da9fa 100644 --- a/almanach/tests/unit/api/v1/test_api_volume_type.py +++ b/almanach/tests/unit/api/v1/test_api_volume_type.py @@ -25,7 +25,7 @@ class TestApiVolumeType(base_api.BaseApi): a(volume_type().with_volume_type_name('some_volume_type_name')) ] - code, result = self.api_get('/volume_types', headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_get('/v1/volume_types', headers={'X-Auth-Token': 'some token value'}) self.volume_type_ctl.list_volume_types.assert_called_once() self.assertEqual(code, 200) @@ -39,7 +39,7 @@ class TestApiVolumeType(base_api.BaseApi): type_name="A_VOLUME_TYPE_NAME" ) - code, result = self.api_post('/volume_type', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_post('/v1/volume_type', data=data, headers={'X-Auth-Token': 'some token value'}) self.volume_type_ctl.create_volume_type.assert_called_once_with( volume_type_id=data['type_id'], @@ -50,14 +50,14 @@ class TestApiVolumeType(base_api.BaseApi): def test_volume_type_create_missing_a_param_returns_bad_request_code(self): data = dict(type_name="A_VOLUME_TYPE_NAME") - code, result = self.api_post('/volume_type', data=data, headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_post('/v1/volume_type', data=data, headers={'X-Auth-Token': 'some token value'}) self.volume_type_ctl.create_volume_type.assert_not_called() self.assertEqual(result["error"], "The 'type_id' param is mandatory for the request you have made.") self.assertEqual(code, 400) def test_volume_type_delete_with_authentication(self): - code, result = self.api_delete('/volume_type/A_VOLUME_TYPE_ID', headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_delete('/v1/volume_type/A_VOLUME_TYPE_ID', headers={'X-Auth-Token': 'some token value'}) self.volume_type_ctl.delete_volume_type.assert_called_once_with('A_VOLUME_TYPE_ID') self.assertEqual(code, 202) @@ -65,7 +65,7 @@ class TestApiVolumeType(base_api.BaseApi): def test_volume_type_delete_not_in_database(self): self.volume_type_ctl.delete_volume_type.side_effect = exception.AlmanachException("An exception occurred") - code, result = self.api_delete('/volume_type/A_VOLUME_TYPE_ID', headers={'X-Auth-Token': 'some token value'}) + code, result = self.api_delete('/v1/volume_type/A_VOLUME_TYPE_ID', headers={'X-Auth-Token': 'some token value'}) self.volume_type_ctl.delete_volume_type.assert_called_once_with('A_VOLUME_TYPE_ID') self.assertIn("An exception occurred", result["error"]) diff --git a/doc/source/index.rst b/doc/source/index.rst index aa5561a..06f1db0 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -244,10 +244,10 @@ Almanach will process those events: - :code:`volume.exists` - :code:`volume_type.create` -API documentation ------------------ +API v1 Documentation +-------------------- -:code:`GET /volume_types` +:code:`GET /v1/volume_types` List volume types. @@ -260,7 +260,7 @@ API documentation .. literalinclude:: api_examples/output/volume_types.json :language: json -:code:`GET /volume_type/` +:code:`GET /v1/volume_type/` Get a volume type. @@ -291,7 +291,7 @@ API documentation .. literalinclude:: api_examples/output/volume_type.json :language: json -:code:`POST /volume_type` +:code:`POST /v1/volume_type` Create a volume type. @@ -324,7 +324,7 @@ API documentation .. literalinclude:: api_examples/input/create_volume_type-body.json :language: json -:code:`DELETE /volume_type/` +:code:`DELETE /v1/volume_type/` Delete a volume type. @@ -348,7 +348,7 @@ API documentation - uuid - The Volume Type Uuid -:code:`GET /info` +:code:`GET /v1/info` Display information about the current version and entity counts. @@ -361,7 +361,7 @@ API documentation .. literalinclude:: api_examples/output/info.json :language: json -:code:`POST /project//instance` +:code:`POST /v1/project//instance` Create an instance. @@ -419,7 +419,7 @@ API documentation .. literalinclude:: api_examples/input/create_instance-body.json :language: json -:code:`DELETE /instance/` +:code:`DELETE /v1/instance/` Delete an instance. @@ -453,7 +453,7 @@ API documentation .. literalinclude:: api_examples/input/delete_instance-body.json :language: json -:code:`PUT /instance//resize` +:code:`PUT /v1/instance//resize` Re-size an instance. @@ -491,7 +491,7 @@ API documentation .. literalinclude:: api_examples/input/resize_instance-body.json :language: json -:code:`PUT /instance//rebuild` +:code:`PUT /v1/instance//rebuild` Rebuild an instance. @@ -537,7 +537,7 @@ API documentation .. literalinclude:: api_examples/input/rebuild_instance-body.json :language: json -:code:`GET /project//instances` +:code:`GET /v1/project//instances` List instances for a tenant. @@ -575,7 +575,7 @@ API documentation .. literalinclude:: api_examples/output/instances.json :language: json -:code:`POST /project//volume` +:code:`POST /v1/project//volume` Create a volume. @@ -629,7 +629,7 @@ API documentation .. literalinclude:: api_examples/input/create_volume-body.json :language: json -:code:`DELETE /volume/` +:code:`DELETE /v1/volume/` Delete a volume. @@ -663,7 +663,7 @@ API documentation .. literalinclude:: api_examples/input/delete_volume-body.json :language: json -:code:`PUT /volume//resize` +:code:`PUT /v1/volume//resize` Re-size a volume. @@ -701,7 +701,7 @@ API documentation .. literalinclude:: api_examples/input/resize_volume-body.json :language: json -:code:`PUT /volume//attach` +:code:`PUT /v1/volume//attach` Update the attachments for a volume. @@ -739,7 +739,7 @@ API documentation .. literalinclude:: api_examples/input/attach_volume-body.json :language: json -:code:`PUT /volume//detach` +:code:`PUT /v1/volume//detach` Detach a volume. @@ -777,7 +777,7 @@ API documentation .. literalinclude:: api_examples/input/detach_volume-body.json :language: json -:code:`GET /project//volumes` +:code:`GET /v1/project//volumes` List volumes for a tenant. @@ -815,7 +815,7 @@ API documentation .. literalinclude:: api_examples/output/volumes.json :language: json -:code:`GET /project//entities` +:code:`GET /v1/project//entities` List entities for a tenant. @@ -853,7 +853,7 @@ API documentation .. literalinclude:: api_examples/output/entities.json :language: json -:code:`PUT /entity/instance/` +:code:`PUT /v1/entity/instance/` Update an instance. @@ -896,7 +896,7 @@ API documentation .. literalinclude:: api_examples/output/update_instance_entity.json :language: json -:code:`HEAD /entity/` +:code:`HEAD /v1/entity/` Verify that an entity exists. @@ -925,7 +925,7 @@ API documentation .. literalinclude:: api_examples/output/entity.json :language: json -:code:`GET /entity/` +:code:`GET /v1/entity/` Get an entity.