From 97c0eab91850701009e01693969e2a79de42201c Mon Sep 17 00:00:00 2001 From: Michael Johnson Date: Fri, 16 Feb 2018 16:07:01 -0800 Subject: [PATCH] Fix calls to "DELETED" items Fixes the v2 API returning "DELETED" records until the amphora_expiry_age timeout expired. The API will now immediately return a 404 HTTP status code when deleted objects are requested. The API version has been raised to v2.1 to reflect this change. Change-Id: Iaf150240b0de32f75ba8cfe605293e3af086cc78 Story: 2001557 Task: 6501 --- octavia/api/root_controller.py | 4 +- octavia/api/v2/controllers/amphora.py | 5 +- octavia/api/v2/controllers/base.py | 55 ++++++++----- octavia/api/v2/controllers/health_monitor.py | 17 +--- octavia/api/v2/controllers/l7policy.py | 9 ++- octavia/api/v2/controllers/l7rule.py | 12 ++- octavia/api/v2/controllers/listener.py | 32 +++----- octavia/api/v2/controllers/load_balancer.py | 24 +++--- octavia/api/v2/controllers/member.py | 15 ++-- octavia/api/v2/controllers/pool.py | 9 +-- octavia/db/repositories.py | 15 +++- .../functional/api/test_root_controller.py | 4 +- octavia/tests/functional/api/v2/base.py | 17 ++-- .../tests/functional/api/v2/test_amphora.py | 28 +++++-- .../functional/api/v2/test_health_monitor.py | 35 +++++++-- .../tests/functional/api/v2/test_l7policy.py | 38 +++++++-- .../tests/functional/api/v2/test_l7rule.py | 42 ++++++++-- .../tests/functional/api/v2/test_listener.py | 65 ++++++++++++++-- .../functional/api/v2/test_load_balancer.py | 77 +++++++++++++++++-- .../tests/functional/api/v2/test_member.py | 51 ++++++------ octavia/tests/functional/api/v2/test_pool.py | 39 ++++++++-- .../notes/deleted-404-2cdd751e7afbe036.yaml | 7 ++ 22 files changed, 426 insertions(+), 174 deletions(-) create mode 100644 releasenotes/notes/deleted-404-2cdd751e7afbe036.yaml diff --git a/octavia/api/root_controller.py b/octavia/api/root_controller.py index 9a23c32dee..43e513118d 100644 --- a/octavia/api/root_controller.py +++ b/octavia/api/root_controller.py @@ -49,8 +49,8 @@ class RootController(rest.RestController): self._versions.append( { 'status': 'CURRENT', - 'updated': '2018-03-23T00:00:00Z', - 'id': 'v2.0' + 'updated': '2018-04-20T00:00:00Z', + 'id': 'v2.1' }) if not (v1_enabled or v2_enabled): LOG.warning("Both v1 and v2.0 API endpoints are disabled -- is " diff --git a/octavia/api/v2/controllers/amphora.py b/octavia/api/v2/controllers/amphora.py index f247fe0a51..f9412e714e 100644 --- a/octavia/api/v2/controllers/amphora.py +++ b/octavia/api/v2/controllers/amphora.py @@ -42,7 +42,7 @@ class AmphoraController(base.BaseController): def get_one(self, id): """Gets a single amphora's details.""" context = pecan.request.context.get('octavia_context') - db_amp = self._get_db_amp(context.session, id) + db_amp = self._get_db_amp(context.session, id, show_deleted=False) self._auth_validate_action(context, context.project_id, constants.RBAC_GET_ONE) @@ -98,7 +98,8 @@ class FailoverController(base.BaseController): """Fails over an amphora""" pcontext = pecan.request.context context = pcontext.get('octavia_context') - db_amp = self._get_db_amp(context.session, self.amp_id) + db_amp = self._get_db_amp(context.session, self.amp_id, + show_deleted=False) self._auth_validate_action( context, db_amp.load_balancer.project_id, diff --git a/octavia/api/v2/controllers/base.py b/octavia/api/v2/controllers/base.py index 217bd4d699..03f42e61e2 100644 --- a/octavia/api/v2/controllers/base.py +++ b/octavia/api/v2/controllers/base.py @@ -59,9 +59,9 @@ class BaseController(rest.RestController): return converted @staticmethod - def _get_db_obj(session, repo, data_model, id): + def _get_db_obj(session, repo, data_model, id, show_deleted=True): """Gets an object from the database and returns it.""" - db_obj = repo.get(session, id=id) + db_obj = repo.get(session, id=id, show_deleted=show_deleted) if not db_obj: LOG.exception('%(name)s %(id)s not found', {'name': data_model._name(), 'id': id}) @@ -69,51 +69,66 @@ class BaseController(rest.RestController): resource=data_model._name(), id=id) return db_obj - def _get_db_lb(self, session, id): + def _get_db_lb(self, session, id, show_deleted=True): """Get a load balancer from the database.""" return self._get_db_obj(session, self.repositories.load_balancer, - data_models.LoadBalancer, id) + data_models.LoadBalancer, id, + show_deleted=show_deleted) - def _get_db_listener(self, session, id): + def _get_db_listener(self, session, id, show_deleted=True): """Get a listener from the database.""" return self._get_db_obj(session, self.repositories.listener, - data_models.Listener, id) + data_models.Listener, id, + show_deleted=show_deleted) - def _get_db_pool(self, session, id): + def _get_db_pool(self, session, id, show_deleted=True): """Get a pool from the database.""" return self._get_db_obj(session, self.repositories.pool, - data_models.Pool, id) + data_models.Pool, id, + show_deleted=show_deleted) - def _get_db_member(self, session, id): + def _get_db_member(self, session, id, show_deleted=True): """Get a member from the database.""" return self._get_db_obj(session, self.repositories.member, - data_models.Member, id) + data_models.Member, id, + show_deleted=show_deleted) - def _get_db_l7policy(self, session, id): + def _get_db_hm(self, session, id, show_deleted=True): + """Get a health monitor from the database.""" + return self._get_db_obj(session, self.repositories.health_monitor, + data_models.HealthMonitor, id, + show_deleted=show_deleted) + + def _get_db_l7policy(self, session, id, show_deleted=True): """Get a L7 Policy from the database.""" return self._get_db_obj(session, self.repositories.l7policy, - data_models.L7Policy, id) + data_models.L7Policy, id, + show_deleted=show_deleted) - def _get_db_l7rule(self, session, id): + def _get_db_l7rule(self, session, id, show_deleted=True): """Get a L7 Rule from the database.""" return self._get_db_obj(session, self.repositories.l7rule, - data_models.L7Rule, id) + data_models.L7Rule, id, + show_deleted=show_deleted) - def _get_db_amp(self, session, id): + def _get_db_amp(self, session, id, show_deleted=True): """Gets an Amphora from the database.""" return self._get_db_obj(session, self.repositories.amphora, - data_models.Amphora, id) + data_models.Amphora, id, + show_deleted=show_deleted) - def _get_lb_project_id(self, session, id): + def _get_lb_project_id(self, session, id, show_deleted=True): """Get the project_id of the load balancer from the database.""" lb = self._get_db_obj(session, self.repositories.load_balancer, - data_models.LoadBalancer, id) + data_models.LoadBalancer, id, + show_deleted=show_deleted) return lb.project_id - def _get_l7policy_project_id(self, session, id): + def _get_l7policy_project_id(self, session, id, show_deleted=True): """Get the project_id of the load balancer from the database.""" l7policy = self._get_db_obj(session, self.repositories.l7policy, - data_models.LoadBalancer, id) + data_models.LoadBalancer, id, + show_deleted=show_deleted) return l7policy.project_id def _get_default_quotas(self, project_id): diff --git a/octavia/api/v2/controllers/health_monitor.py b/octavia/api/v2/controllers/health_monitor.py index 8cacfde9c4..8ed3fb1eec 100644 --- a/octavia/api/v2/controllers/health_monitor.py +++ b/octavia/api/v2/controllers/health_monitor.py @@ -42,23 +42,12 @@ class HealthMonitorController(base.BaseController): super(HealthMonitorController, self).__init__() self.handler = self.handler.health_monitor - def _get_db_hm(self, session, hm_id): - """Gets the current health monitor object from the database.""" - db_hm = self.repositories.health_monitor.get( - session, id=hm_id) - if not db_hm: - LOG.info("Health Monitor %s was not found", hm_id) - raise exceptions.NotFound( - resource=data_models.HealthMonitor._name(), - id=hm_id) - return db_hm - @wsme_pecan.wsexpose(hm_types.HealthMonitorRootResponse, wtypes.text, wtypes.text) def get_one(self, id): """Gets a single healthmonitor's details.""" context = pecan.request.context.get('octavia_context') - db_hm = self._get_db_hm(context.session, id) + db_hm = self._get_db_hm(context.session, id, show_deleted=False) self._auth_validate_action(context, db_hm.project_id, constants.RBAC_GET_ONE) @@ -206,7 +195,7 @@ class HealthMonitorController(base.BaseController): """Updates a health monitor.""" context = pecan.request.context.get('octavia_context') health_monitor = health_monitor_.healthmonitor - db_hm = self._get_db_hm(context.session, id) + db_hm = self._get_db_hm(context.session, id, show_deleted=False) self._auth_validate_action(context, db_hm.project_id, constants.RBAC_PUT) @@ -239,7 +228,7 @@ class HealthMonitorController(base.BaseController): def delete(self, id): """Deletes a health monitor.""" context = pecan.request.context.get('octavia_context') - db_hm = self._get_db_hm(context.session, id) + db_hm = self._get_db_hm(context.session, id, show_deleted=False) self._auth_validate_action(context, db_hm.project_id, constants.RBAC_DELETE) diff --git a/octavia/api/v2/controllers/l7policy.py b/octavia/api/v2/controllers/l7policy.py index 7ac1d1ac81..954eecf408 100644 --- a/octavia/api/v2/controllers/l7policy.py +++ b/octavia/api/v2/controllers/l7policy.py @@ -47,7 +47,8 @@ class L7PolicyController(base.BaseController): def get(self, id): """Gets a single l7policy's details.""" context = pecan.request.context.get('octavia_context') - db_l7policy = self._get_db_l7policy(context.session, id) + db_l7policy = self._get_db_l7policy(context.session, id, + show_deleted=False) self._auth_validate_action(context, db_l7policy.project_id, constants.RBAC_GET_ONE) @@ -216,7 +217,8 @@ class L7PolicyController(base.BaseController): if l7policy_dict.get('redirect_pool_id'): self._get_db_pool( context.session, l7policy_dict['redirect_pool_id']) - db_l7policy = self._get_db_l7policy(context.session, id) + db_l7policy = self._get_db_l7policy(context.session, id, + show_deleted=False) load_balancer_id, listener_id = self._get_listener_and_loadbalancer_id( db_l7policy) @@ -253,7 +255,8 @@ class L7PolicyController(base.BaseController): def delete(self, id): """Deletes a l7policy.""" context = pecan.request.context.get('octavia_context') - db_l7policy = self._get_db_l7policy(context.session, id) + db_l7policy = self._get_db_l7policy(context.session, id, + show_deleted=False) load_balancer_id, listener_id = self._get_listener_and_loadbalancer_id( db_l7policy) diff --git a/octavia/api/v2/controllers/l7rule.py b/octavia/api/v2/controllers/l7rule.py index 5f238e5679..79b8f1d931 100644 --- a/octavia/api/v2/controllers/l7rule.py +++ b/octavia/api/v2/controllers/l7rule.py @@ -45,7 +45,8 @@ class L7RuleController(base.BaseController): def get(self, id): """Gets a single l7rule's details.""" context = pecan.request.context.get('octavia_context') - db_l7rule = self._get_db_l7rule(context.session, id) + db_l7rule = self._get_db_l7rule(context.session, id, + show_deleted=False) self._auth_validate_action(context, db_l7rule.project_id, constants.RBAC_GET_ONE) @@ -61,7 +62,8 @@ class L7RuleController(base.BaseController): pcontext = pecan.request.context context = pcontext.get('octavia_context') - l7policy = self._get_db_l7policy(context.session, self.l7policy_id) + l7policy = self._get_db_l7policy(context.session, self.l7policy_id, + show_deleted=False) self._auth_validate_action(context, l7policy.project_id, constants.RBAC_GET_ALL) @@ -190,7 +192,8 @@ class L7RuleController(base.BaseController): """Updates a l7rule.""" l7rule = l7rule_.rule context = pecan.request.context.get('octavia_context') - db_l7rule = self._get_db_l7rule(context.session, id) + db_l7rule = self._get_db_l7rule(context.session, id, + show_deleted=False) new_l7rule = db_l7rule.to_dict() new_l7rule.update(l7rule.to_dict()) new_l7rule = data_models.L7Rule.from_dict(new_l7rule) @@ -228,7 +231,8 @@ class L7RuleController(base.BaseController): def delete(self, id): """Deletes a l7rule.""" context = pecan.request.context.get('octavia_context') - db_l7rule = self._get_db_l7rule(context.session, id) + db_l7rule = self._get_db_l7rule(context.session, id, + show_deleted=False) self._auth_validate_action(context, db_l7rule.project_id, constants.RBAC_DELETE) diff --git a/octavia/api/v2/controllers/listener.py b/octavia/api/v2/controllers/listener.py index 2bd9052fa7..29589d6f77 100644 --- a/octavia/api/v2/controllers/listener.py +++ b/octavia/api/v2/controllers/listener.py @@ -50,24 +50,16 @@ class ListenersController(base.BaseController): invoke_on_load=True, ).driver - def _get_db_listener(self, session, id): - """Gets a listener object from the database.""" - listener = super(ListenersController, self)._get_db_listener( - session, id) - load_balancer_id = listener.load_balancer_id - db_listener = self.repositories.listener.get( - session, load_balancer_id=load_balancer_id, id=id) - if not db_listener: - LOG.info("Listener %s not found.", id) - raise exceptions.NotFound( - resource=data_models.Listener._name(), id=id) - return db_listener - @wsme_pecan.wsexpose(listener_types.ListenerRootResponse, wtypes.text) def get_one(self, id): """Gets a single listener's details.""" context = pecan.request.context.get('octavia_context') - db_listener = self._get_db_listener(context.session, id) + db_listener = self._get_db_listener(context.session, id, + show_deleted=False) + + if not db_listener: + raise exceptions.NotFound(resource=data_models.Listener._name(), + id=id) self._auth_validate_action(context, db_listener.project_id, constants.RBAC_GET_ONE) @@ -288,7 +280,8 @@ class ListenersController(base.BaseController): """Updates a listener on a load balancer.""" listener = listener_.listener context = pecan.request.context.get('octavia_context') - db_listener = self._get_db_listener(context.session, id) + db_listener = self._get_db_listener(context.session, id, + show_deleted=False) load_balancer_id = db_listener.load_balancer_id self._auth_validate_action(context, db_listener.project_id, @@ -333,15 +326,13 @@ class ListenersController(base.BaseController): def delete(self, id): """Deletes a listener from a load balancer.""" context = pecan.request.context.get('octavia_context') - db_listener = self._get_db_listener(context.session, id) + db_listener = self._get_db_listener(context.session, id, + show_deleted=False) load_balancer_id = db_listener.load_balancer_id self._auth_validate_action(context, db_listener.project_id, constants.RBAC_DELETE) - if db_listener.provisioning_status == constants.DELETED: - return - self._test_lb_and_listener_statuses( context.session, load_balancer_id, id=id, listener_status=constants.PENDING_DELETE) @@ -382,7 +373,8 @@ class StatisticsController(base.BaseController, stats.StatsMixin): status_code=200) def get(self): context = pecan.request.context.get('octavia_context') - db_listener = self._get_db_listener(context.session, self.id) + db_listener = self._get_db_listener(context.session, self.id, + show_deleted=False) if not db_listener: LOG.info("Listener %s not found.", id) raise exceptions.NotFound( diff --git a/octavia/api/v2/controllers/load_balancer.py b/octavia/api/v2/controllers/load_balancer.py index ed253619b5..53259827d2 100644 --- a/octavia/api/v2/controllers/load_balancer.py +++ b/octavia/api/v2/controllers/load_balancer.py @@ -53,7 +53,13 @@ class LoadBalancersController(base.BaseController): def get_one(self, id): """Gets a single load balancer's details.""" context = pecan.request.context.get('octavia_context') - load_balancer = self._get_db_lb(context.session, id) + load_balancer = self._get_db_lb(context.session, id, + show_deleted=False) + + if not load_balancer: + raise exceptions.NotFound( + resource=data_models.LoadBalancer._name(), + id=id) self._auth_validate_action(context, load_balancer.project_id, constants.RBAC_GET_ONE) @@ -426,7 +432,7 @@ class LoadBalancersController(base.BaseController): """Updates a load balancer.""" load_balancer = load_balancer.loadbalancer context = pecan.request.context.get('octavia_context') - db_lb = self._get_db_lb(context.session, id) + db_lb = self._get_db_lb(context.session, id, show_deleted=False) self._auth_validate_action(context, db_lb.project_id, constants.RBAC_PUT) @@ -453,14 +459,11 @@ class LoadBalancersController(base.BaseController): """Deletes a load balancer.""" context = pecan.request.context.get('octavia_context') cascade = strutils.bool_from_string(cascade) - db_lb = self._get_db_lb(context.session, id) + db_lb = self._get_db_lb(context.session, id, show_deleted=False) self._auth_validate_action(context, db_lb.project_id, constants.RBAC_DELETE) - if db_lb.provisioning_status == constants.DELETED: - return - with db_api.get_lock_session() as lock_session: if (db_lb.listeners or db_lb.pools) and not cascade: msg = _("Cannot delete Load Balancer %s - " @@ -514,7 +517,8 @@ class StatusController(base.BaseController): status_code=200) def get(self): context = pecan.request.context.get('octavia_context') - load_balancer = self._get_db_lb(context.session, self.id) + load_balancer = self._get_db_lb(context.session, self.id, + show_deleted=False) if not load_balancer: LOG.info("Load balancer %s not found.", id) raise exceptions.NotFound( @@ -541,7 +545,8 @@ class StatisticsController(base.BaseController, stats.StatsMixin): status_code=200) def get(self): context = pecan.request.context.get('octavia_context') - load_balancer = self._get_db_lb(context.session, self.id) + load_balancer = self._get_db_lb(context.session, self.id, + show_deleted=False) if not load_balancer: LOG.info("Load balancer %s not found.", id) raise exceptions.NotFound( @@ -568,7 +573,8 @@ class FailoverController(LoadBalancersController): def put(self, **kwargs): """Fails over a loadbalancer""" context = pecan.request.context.get('octavia_context') - db_lb = self._get_db_lb(context.session, self.lb_id) + db_lb = self._get_db_lb(context.session, self.lb_id, + show_deleted=False) self._auth_validate_action(context, db_lb.project_id, constants.RBAC_PUT_FAILOVER) diff --git a/octavia/api/v2/controllers/member.py b/octavia/api/v2/controllers/member.py index d14d0a3119..6569622c51 100644 --- a/octavia/api/v2/controllers/member.py +++ b/octavia/api/v2/controllers/member.py @@ -46,7 +46,8 @@ class MemberController(base.BaseController): def get(self, id): """Gets a single pool member's details.""" context = pecan.request.context.get('octavia_context') - db_member = self._get_db_member(context.session, id) + db_member = self._get_db_member(context.session, id, + show_deleted=False) self._auth_validate_action(context, db_member.project_id, constants.RBAC_GET_ONE) @@ -62,7 +63,8 @@ class MemberController(base.BaseController): pcontext = pecan.request.context context = pcontext.get('octavia_context') - pool = self._get_db_pool(context.session, self.pool_id) + pool = self._get_db_pool(context.session, self.pool_id, + show_deleted=False) self._auth_validate_action(context, pool.project_id, constants.RBAC_GET_ALL) @@ -210,7 +212,8 @@ class MemberController(base.BaseController): """Updates a pool member.""" member = member_.member context = pecan.request.context.get('octavia_context') - db_member = self._get_db_member(context.session, id) + db_member = self._get_db_member(context.session, id, + show_deleted=False) self._auth_validate_action(context, db_member.project_id, constants.RBAC_PUT) @@ -242,14 +245,12 @@ class MemberController(base.BaseController): def delete(self, id): """Deletes a pool member.""" context = pecan.request.context.get('octavia_context') - db_member = self._get_db_member(context.session, id) + db_member = self._get_db_member(context.session, id, + show_deleted=False) self._auth_validate_action(context, db_member.project_id, constants.RBAC_DELETE) - if db_member.provisioning_status == constants.DELETED: - return - self._test_lb_and_listener_and_pool_statuses(context.session, member=db_member) self.repositories.member.update( diff --git a/octavia/api/v2/controllers/pool.py b/octavia/api/v2/controllers/pool.py index 6ff06a9182..6b5d661b3c 100644 --- a/octavia/api/v2/controllers/pool.py +++ b/octavia/api/v2/controllers/pool.py @@ -50,7 +50,7 @@ class PoolsController(base.BaseController): def get(self, id): """Gets a pool's details.""" context = pecan.request.context.get('octavia_context') - db_pool = self._get_db_pool(context.session, id) + db_pool = self._get_db_pool(context.session, id, show_deleted=False) self._auth_validate_action(context, db_pool.project_id, constants.RBAC_GET_ONE) @@ -253,7 +253,7 @@ class PoolsController(base.BaseController): """Updates a pool on a load balancer.""" pool = pool_.pool context = pecan.request.context.get('octavia_context') - db_pool = self._get_db_pool(context.session, id) + db_pool = self._get_db_pool(context.session, id, show_deleted=False) self._auth_validate_action(context, db_pool.project_id, constants.RBAC_PUT) @@ -290,7 +290,7 @@ class PoolsController(base.BaseController): def delete(self, id): """Deletes a pool from a load balancer.""" context = pecan.request.context.get('octavia_context') - db_pool = self._get_db_pool(context.session, id) + db_pool = self._get_db_pool(context.session, id, show_deleted=False) if len(db_pool.l7policies) > 0: raise exceptions.PoolInUseByL7Policy( id=db_pool.id, l7policy_id=db_pool.l7policies[0].id) @@ -298,9 +298,6 @@ class PoolsController(base.BaseController): self._auth_validate_action(context, db_pool.project_id, constants.RBAC_DELETE) - if db_pool.provisioning_status == constants.DELETED: - return - self._test_lb_and_listener_statuses( context.session, lb_id=db_pool.load_balancer_id, listener_ids=self._get_affected_listener_ids(db_pool)) diff --git a/octavia/db/repositories.py b/octavia/db/repositories.py index a54c77926b..3bbaa30759 100644 --- a/octavia/db/repositories.py +++ b/octavia/db/repositories.py @@ -100,9 +100,22 @@ class BaseRepository(object): :param filters: Filters to decide which entity should be retrieved. :returns: octavia.common.data_model """ - model = session.query(self.model_class).filter_by(**filters).first() + deleted = filters.pop('show_deleted', True) + model = session.query(self.model_class).filter_by(**filters) + + if not deleted: + if hasattr(self.model_class, 'status'): + model = model.filter( + self.model_class.status != consts.DELETED) + else: + model = model.filter( + self.model_class.provisioning_status != consts.DELETED) + + model = model.first() + if not model: return + return model.to_data_model() def get_all(self, session, pagination_helper=None, **filters): diff --git a/octavia/tests/functional/api/test_root_controller.py b/octavia/tests/functional/api/test_root_controller.py index 31b821cd89..2345291e8e 100644 --- a/octavia/tests/functional/api/test_root_controller.py +++ b/octavia/tests/functional/api/test_root_controller.py @@ -48,13 +48,13 @@ class TestRootController(base_db_test.OctaviaDBTestBase): version_ids = tuple(v.get('id') for v in versions) self.assertEqual(2, len(version_ids)) self.assertIn('v1', version_ids) - self.assertIn('v2.0', version_ids) + self.assertIn('v2.1', version_ids) def test_api_v1_disabled(self): versions = self._get_versions_with_config( api_v1_enabled=False, api_v2_enabled=True) self.assertEqual(1, len(versions)) - self.assertEqual('v2.0', versions[0].get('id')) + self.assertEqual('v2.1', versions[0].get('id')) def test_api_v2_disabled(self): versions = self._get_versions_with_config( diff --git a/octavia/tests/functional/api/v2/base.py b/octavia/tests/functional/api/v2/base.py index 4fd802d311..23a77b05d5 100644 --- a/octavia/tests/functional/api/v2/base.py +++ b/octavia/tests/functional/api/v2/base.py @@ -21,6 +21,7 @@ import pecan.testing from octavia.api import config as pconfig from octavia.common import constants +from octavia.common import exceptions from octavia.db import api as db_api from octavia.db import repositories from octavia.tests.functional.db import base as base_db_test @@ -387,7 +388,8 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase): op_status = db_lb.operating_status self._set_lb_and_children_statuses(lb_id, status, op_status, autodetect=not explicit_status) - return self.get(self.LB_PATH.format(lb_id=lb_id)).json + if status != constants.DELETED: + return self.get(self.LB_PATH.format(lb_id=lb_id)).json @staticmethod def set_object_status(repo, id_, provisioning_status=constants.ACTIVE, @@ -399,13 +401,14 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase): def assert_final_listener_statuses(self, lb_id, listener_id, delete=False): expected_prov_status = constants.ACTIVE expected_op_status = constants.ONLINE - if delete: - expected_prov_status = constants.DELETED - expected_op_status = constants.OFFLINE self.set_lb_status(lb_id, status=expected_prov_status) - self.assert_correct_listener_status(expected_prov_status, - expected_op_status, - listener_id) + try: + self.assert_correct_listener_status(expected_prov_status, + expected_op_status, + listener_id) + except exceptions.NotFound: + if not delete: + raise def assert_correct_lb_status(self, lb_id, operating_status, provisioning_status): diff --git a/octavia/tests/functional/api/v2/test_amphora.py b/octavia/tests/functional/api/v2/test_amphora.py index 8d97f9645d..79f9729a26 100644 --- a/octavia/tests/functional/api/v2/test_amphora.py +++ b/octavia/tests/functional/api/v2/test_amphora.py @@ -103,6 +103,13 @@ class TestAmphora(base.BaseAPITest): [mock.call(self.amp)] ) + def test_failover_deleted(self): + new_amp = self._create_additional_amp() + self.amphora_repo.update(self.session, new_amp.id, + status=constants.DELETED) + self.put(self.AMPHORA_FAILOVER_PATH.format( + amphora_id=new_amp.id), body={}, status=404) + def test_failover_bad_amp_id(self): self.put(self.AMPHORA_FAILOVER_PATH.format( amphora_id='asdf'), body={}, status=404) @@ -149,17 +156,12 @@ class TestAmphora(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json) - def test_get_hides_deleted(self): + def test_get_deleted_gives_404(self): new_amp = self._create_additional_amp() - response = self.get(self.AMPHORAE_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 2) self.amphora_repo.update(self.session, new_amp.id, status=constants.DELETED) - response = self.get(self.AMPHORAE_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 1) + self.get(self.AMPHORA_PATH.format(amphora_id=new_amp.id), status=404) def test_bad_get(self): self.get(self.AMPHORA_PATH.format( @@ -210,6 +212,18 @@ class TestAmphora(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, amps) + def test_get_all_hides_deleted(self): + new_amp = self._create_additional_amp() + + response = self.get(self.AMPHORAE_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 2) + self.amphora_repo.update(self.session, new_amp.id, + status=constants.DELETED) + response = self.get(self.AMPHORAE_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 1) + def test_get_by_loadbalancer_id(self): amps = self.get( self.AMPHORAE_PATH, diff --git a/octavia/tests/functional/api/v2/test_health_monitor.py b/octavia/tests/functional/api/v2/test_health_monitor.py index 02e9faa0d0..91b430d188 100644 --- a/octavia/tests/functional/api/v2/test_health_monitor.py +++ b/octavia/tests/functional/api/v2/test_health_monitor.py @@ -126,19 +126,15 @@ class TestHealthMonitor(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json) - def test_get_hides_deleted(self): + def test_get_deleted_gives_404(self): api_hm = self.create_health_monitor( self.pool_id, constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1).get(self.root_tag) - response = self.get(self.HMS_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 1) self.set_object_status(self.health_monitor_repo, api_hm.get('id'), provisioning_status=constants.DELETED) - response = self.get(self.HMS_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 0) + self.get(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')), + status=404) def test_bad_get(self): self.get(self.HM_PATH.format( @@ -323,6 +319,20 @@ class TestHealthMonitor(base.BaseAPITest): self.assertIn((hm2.get('id'), hm2.get('type')), hm_id_protocols) self.assertIn((hm3.get('id'), hm3.get('type')), hm_id_protocols) + def test_get_all_hides_deleted(self): + api_hm = self.create_health_monitor( + self.pool_id, constants.HEALTH_MONITOR_HTTP, + 1, 1, 1, 1).get(self.root_tag) + + response = self.get(self.HMS_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 1) + self.set_object_status(self.health_monitor_repo, api_hm.get('id'), + provisioning_status=constants.DELETED) + response = self.get(self.HMS_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 0) + def test_get_by_project_id(self): project1_id = uuidutils.generate_uuid() project2_id = uuidutils.generate_uuid() @@ -1035,6 +1045,15 @@ class TestHealthMonitor(base.BaseAPITest): self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')), body=self._build_body(new_hm), status=409) + def test_update_already_deleted(self): + api_hm = self.create_health_monitor( + self.pool_id, constants.HEALTH_MONITOR_HTTP, + 1, 1, 1, 1).get(self.root_tag) + # This updates the child objects + self.set_lb_status(self.lb_id, status=constants.DELETED) + self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')), + body=self._build_body({'max_retries': 2}), status=404) + def test_delete_when_lb_pending_delete(self): api_hm = self.create_health_monitor( self.pool_id, constants.HEALTH_MONITOR_HTTP, @@ -1052,4 +1071,4 @@ class TestHealthMonitor(base.BaseAPITest): # This updates the child objects self.set_lb_status(self.lb_id, status=constants.DELETED) self.delete(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')), - status=204) + status=404) diff --git a/octavia/tests/functional/api/v2/test_l7policy.py b/octavia/tests/functional/api/v2/test_l7policy.py index 3f22c28d9a..b8f4de1b0f 100644 --- a/octavia/tests/functional/api/v2/test_l7policy.py +++ b/octavia/tests/functional/api/v2/test_l7policy.py @@ -102,19 +102,15 @@ class TestL7Policy(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json) - def test_get_hides_deleted(self): + def test_get_deleted_gives_404(self): api_l7policy = self.create_l7policy( self.listener_id, constants.L7POLICY_ACTION_REJECT).get(self.root_tag) - response = self.get(self.L7POLICIES_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 1) self.set_object_status(self.l7policy_repo, api_l7policy.get('id'), provisioning_status=constants.DELETED) - response = self.get(self.L7POLICIES_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 0) + self.get(self.L7POLICY_PATH.format(l7policy_id=api_l7policy.get('id')), + status=404) def test_bad_get(self): self.get(self.L7POLICY_PATH.format( @@ -299,6 +295,20 @@ class TestL7Policy(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, policies) + def test_get_all_hides_deleted(self): + api_l7policy = self.create_l7policy( + self.listener_id, + constants.L7POLICY_ACTION_REJECT).get(self.root_tag) + + response = self.get(self.L7POLICIES_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 1) + self.set_object_status(self.l7policy_repo, api_l7policy.get('id'), + provisioning_status=constants.DELETED) + response = self.get(self.L7POLICIES_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 0) + def test_get_by_project_id(self): project1_id = uuidutils.generate_uuid() project2_id = uuidutils.generate_uuid() @@ -1007,6 +1017,18 @@ class TestL7Policy(base.BaseAPITest): l7policy_id=l7policy.get('id')), status=409) + def test_update_already_deleted(self): + l7policy = self.create_l7policy(self.listener_id, + constants.L7POLICY_ACTION_REJECT, + ).get(self.root_tag) + # This updates the child objects + self.set_lb_status(self.lb_id, status=constants.DELETED) + new_l7policy = { + 'action': constants.L7POLICY_ACTION_REDIRECT_TO_URL, + 'redirect_url': 'http://www.example.com'} + self.put(self.L7POLICY_PATH.format(l7policy_id=l7policy.get('id')), + body=self._build_body(new_l7policy), status=404) + def test_delete_already_deleted(self): l7policy = self.create_l7policy(self.listener_id, constants.L7POLICY_ACTION_REJECT, @@ -1015,4 +1037,4 @@ class TestL7Policy(base.BaseAPITest): self.set_lb_status(self.lb_id, status=constants.DELETED) self.delete(self.L7POLICY_PATH.format( l7policy_id=l7policy.get('id')), - status=204) + status=404) diff --git a/octavia/tests/functional/api/v2/test_l7rule.py b/octavia/tests/functional/api/v2/test_l7rule.py index 684777f14a..dfc515a56e 100644 --- a/octavia/tests/functional/api/v2/test_l7rule.py +++ b/octavia/tests/functional/api/v2/test_l7rule.py @@ -101,20 +101,16 @@ class TestL7Rule(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, response) - def test_get_hides_deleted(self): + def test_get_deleted_gives_404(self): api_l7rule = self.create_l7rule( self.l7policy_id, constants.L7RULE_TYPE_PATH, constants.L7RULE_COMPARE_TYPE_STARTS_WITH, '/api').get(self.root_tag) - response = self.get(self.l7rules_path) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 1) self.set_object_status(self.l7rule_repo, api_l7rule.get('id'), provisioning_status=constants.DELETED) - response = self.get(self.l7rules_path) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 0) + self.get(self.l7rule_path.format(l7rule_id=api_l7rule.get('id')), + status=404) def test_get_bad_parent_policy(self): bad_path = (self.L7RULES_PATH.format( @@ -344,6 +340,21 @@ class TestL7Rule(base.BaseAPITest): self.assertIsInstance(response, list) self.assertEqual(0, len(response)) + def test_get_all_hides_deleted(self): + api_l7rule = self.create_l7rule( + self.l7policy_id, constants.L7RULE_TYPE_PATH, + constants.L7RULE_COMPARE_TYPE_STARTS_WITH, + '/api').get(self.root_tag) + + response = self.get(self.l7rules_path) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 1) + self.set_object_status(self.l7rule_repo, api_l7rule.get('id'), + provisioning_status=constants.DELETED) + response = self.get(self.l7rules_path) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 0) + def test_create_host_name_rule(self): api_l7rule = self.create_l7rule( self.l7policy_id, constants.L7RULE_TYPE_HOST_NAME, @@ -907,6 +918,21 @@ class TestL7Rule(base.BaseAPITest): self.delete(self.l7rule_path.format(l7rule_id=l7rule.get('id')), status=409) + def test_update_already_deleted(self): + l7rule = self.create_l7rule( + self.l7policy_id, constants.L7RULE_TYPE_PATH, + constants.L7RULE_COMPARE_TYPE_STARTS_WITH, + '/api').get(self.root_tag) + # This updates the child objects + self.set_lb_status(self.lb_id, status=constants.DELETED) + new_l7rule = {'type': constants.L7RULE_TYPE_COOKIE, + 'compare_type': + constants.L7RULE_COMPARE_TYPE_ENDS_WITH, + 'value': 'some-string', + 'key': 'some-cookie'} + self.put(self.l7rule_path.format(l7rule_id=l7rule.get('id')), + body=self._build_body(new_l7rule), status=404) + def test_delete_already_deleted(self): l7rule = self.create_l7rule( self.l7policy_id, constants.L7RULE_TYPE_PATH, @@ -915,4 +941,4 @@ class TestL7Rule(base.BaseAPITest): # This updates the child objects self.set_lb_status(self.lb_id, status=constants.DELETED) self.delete(self.l7rule_path.format(l7rule_id=l7rule.get('id')), - status=204) + status=404) diff --git a/octavia/tests/functional/api/v2/test_listener.py b/octavia/tests/functional/api/v2/test_listener.py index 0428f6b24f..551e26f360 100644 --- a/octavia/tests/functional/api/v2/test_listener.py +++ b/octavia/tests/functional/api/v2/test_listener.py @@ -351,6 +351,19 @@ class TestListener(base.BaseAPITest): self.assertEqual(li1['id'], lis['listeners'][0]['id']) + def test_get_all_hides_deleted(self): + api_listener = self.create_listener( + constants.PROTOCOL_HTTP, 80, self.lb_id).get(self.root_tag) + + response = self.get(self.LISTENERS_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 1) + self.set_object_status(self.listener_repo, api_listener.get('id'), + provisioning_status=constants.DELETED) + response = self.get(self.LISTENERS_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 0) + def test_get(self): listener = self.create_listener( constants.PROTOCOL_HTTP, 80, self.lb_id).get(self.root_tag) @@ -407,18 +420,14 @@ class TestListener(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json) - def test_get_hides_deleted(self): + def test_get_deleted_gives_404(self): api_listener = self.create_listener( constants.PROTOCOL_HTTP, 80, self.lb_id).get(self.root_tag) - response = self.get(self.LISTENERS_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 1) self.set_object_status(self.listener_repo, api_listener.get('id'), provisioning_status=constants.DELETED) - response = self.get(self.LISTENERS_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 0) + self.get(self.LISTENER_PATH.format(listener_id=api_listener.get('id')), + status=404) def test_get_bad_listener_id(self): listener_path = self.listener_path @@ -1130,6 +1139,28 @@ class TestListener(base.BaseAPITest): listener_id=api_listener['id']) self.put(listener_path, body, status=409) + def test_update_deleted(self): + lb = self.create_load_balancer(uuidutils.generate_uuid(), + name='lb1', description='desc1', + admin_state_up=False) + lb_id = lb['loadbalancer'].get('id') + self.set_lb_status(lb_id) + lb_listener = {'name': 'listener1', 'description': 'desc1', + 'admin_state_up': False, + 'protocol': constants.PROTOCOL_HTTP, + 'protocol_port': 80, 'connection_limit': 10, + 'loadbalancer_id': lb_id} + body = self._build_body(lb_listener) + api_listener = self.post( + self.LISTENERS_PATH, body).json.get(self.root_tag) + # This updates the child objects + self.set_lb_status(lb_id, status=constants.DELETED) + lb_listener_put = {'name': 'listener1_updated'} + body = self._build_body(lb_listener_put) + listener_path = self.LISTENER_PATH.format( + listener_id=api_listener['id']) + self.put(listener_path, body, status=404) + def test_delete_pending_delete(self): lb = self.create_load_balancer(uuidutils.generate_uuid(), name='lb1', description='desc1', @@ -1169,7 +1200,7 @@ class TestListener(base.BaseAPITest): self.set_lb_status(lb_id, status=constants.DELETED) listener_path = self.LISTENER_PATH.format( listener_id=api_listener['id']) - self.delete(listener_path, status=204) + self.delete(listener_path, status=404) def test_create_with_tls_termination_data(self): cert_id = uuidutils.generate_uuid() @@ -1354,3 +1385,21 @@ class TestListener(base.BaseAPITest): listener_id=li['id'] + "/stats"), status=403) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, res.json) + + def test_statistics_get_deleted(self): + lb = self.create_load_balancer( + uuidutils.generate_uuid()).get('loadbalancer') + self.set_lb_status(lb['id']) + li = self.create_listener( + constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener') + amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id']) + self.create_listener_stats_dynamic( + listener_id=li.get('id'), + amphora_id=amphora.id, + bytes_in=random.randint(1, 9), + bytes_out=random.randint(1, 9), + total_connections=random.randint(1, 9), + request_errors=random.randint(1, 9)) + self.set_lb_status(lb['id'], status=constants.DELETED) + self.get(self.LISTENER_PATH.format( + listener_id=li.get('id') + "/stats"), status=404) diff --git a/octavia/tests/functional/api/v2/test_load_balancer.py b/octavia/tests/functional/api/v2/test_load_balancer.py index f9e40c1f90..6b1815ba00 100644 --- a/octavia/tests/functional/api/v2/test_load_balancer.py +++ b/octavia/tests/functional/api/v2/test_load_balancer.py @@ -1072,6 +1072,19 @@ class TestLoadBalancer(base.BaseAPITest): self.assertEqual(lb1['id'], lbs['loadbalancers'][0]['id']) + def test_get_all_hides_deleted(self): + api_lb = self.create_load_balancer( + uuidutils.generate_uuid()).get(self.root_tag) + + response = self.get(self.LBS_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 1) + self.set_object_status(self.lb_repo, api_lb.get('id'), + provisioning_status=constants.DELETED) + response = self.get(self.LBS_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 0) + def test_get(self): project_id = uuidutils.generate_uuid() subnet = network_models.Subnet(id=uuidutils.generate_uuid()) @@ -1108,18 +1121,14 @@ class TestLoadBalancer(base.BaseAPITest): self.assertEqual(network.id, response.get('vip_network_id')) self.assertEqual(port.id, response.get('vip_port_id')) - def test_get_hides_deleted(self): + def test_get_deleted_gives_404(self): api_lb = self.create_load_balancer( uuidutils.generate_uuid()).get(self.root_tag) - response = self.get(self.LBS_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 1) self.set_object_status(self.lb_repo, api_lb.get('id'), provisioning_status=constants.DELETED) - response = self.get(self.LBS_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 0) + + self.get(self.LB_PATH.format(lb_id=api_lb.get('id')), status=404) def test_get_bad_lb_id(self): path = self.LB_PATH.format(lb_id='SEAN-CONNERY') @@ -1447,6 +1456,19 @@ class TestLoadBalancer(base.BaseAPITest): self.delete(self.LB_PATH.format(lb_id=lb_dict.get('id'))) self.delete(self.LB_PATH.format(lb_id=lb_dict.get('id')), status=409) + def test_update_already_deleted(self): + project_id = uuidutils.generate_uuid() + lb = self.create_load_balancer(uuidutils.generate_uuid(), + name='lb1', + project_id=project_id, + description='desc1', + admin_state_up=False) + lb_dict = lb.get(self.root_tag) + lb = self.set_lb_status(lb_dict.get('id'), status=constants.DELETED) + lb_json = self._build_body({'name': 'John'}) + self.put(self.LB_PATH.format(lb_id=lb_dict.get('id')), + lb_json, status=404) + def test_delete_already_deleted(self): project_id = uuidutils.generate_uuid() lb = self.create_load_balancer(uuidutils.generate_uuid(), @@ -1456,7 +1478,7 @@ class TestLoadBalancer(base.BaseAPITest): admin_state_up=False) lb_dict = lb.get(self.root_tag) lb = self.set_lb_status(lb_dict.get('id'), status=constants.DELETED) - self.delete(self.LB_PATH.format(lb_id=lb_dict.get('id')), status=204) + self.delete(self.LB_PATH.format(lb_id=lb_dict.get('id')), status=404) def test_delete(self): project_id = uuidutils.generate_uuid() @@ -1773,6 +1795,20 @@ class TestLoadBalancer(base.BaseAPITest): self.app.put(path, status=202) self.conf.config(group='api_settings', auth_strategy=auth_strategy) + def test_failover_deleted(self): + project_id = uuidutils.generate_uuid() + lb = self.create_load_balancer(uuidutils.generate_uuid(), + name='lb1', + project_id=project_id, + description='desc1', + admin_state_up=False) + lb_dict = lb.get(self.root_tag) + lb = self.set_lb_status(lb_dict.get('id'), status=constants.DELETED) + + path = self._get_full_path(self.LB_PATH.format( + lb_id=lb_dict.get('id')) + "/failover") + self.app.put(path, status=404) + def test_create_with_bad_handler(self): self.handler_mock().load_balancer.create.side_effect = Exception() api_lb = self.create_load_balancer( @@ -2748,6 +2784,15 @@ class TestLoadBalancerGraph(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, res.json) + def test_statuses_get_deleted(self): + project_id = uuidutils.generate_uuid() + lb = self.create_load_balancer( + uuidutils.generate_uuid(), + project_id=project_id).get('loadbalancer') + self.set_lb_status(lb['id'], status=constants.DELETED) + self.get(self.LB_PATH.format(lb_id=lb['id'] + "/status"), + status=404) + def _getStats(self, lb_id): res = self.get(self.LB_PATH.format(lb_id=lb_id + "/stats")) return res.json.get('stats') @@ -2851,3 +2896,19 @@ class TestLoadBalancerGraph(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, res.json) + + def test_statistics_get_deleted(self): + lb = self.create_load_balancer( + uuidutils.generate_uuid()).get('loadbalancer') + self.set_lb_status(lb['id']) + li = self.create_listener( + constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener') + amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id']) + self.create_listener_stats_dynamic( + listener_id=li.get('id'), + amphora_id=amphora.id, + bytes_in=random.randint(1, 9), + bytes_out=random.randint(1, 9), + total_connections=random.randint(1, 9)) + self.set_lb_status(lb['id'], status=constants.DELETED) + self.get(self.LB_PATH.format(lb_id=lb['id'] + "/stats"), status=404) diff --git a/octavia/tests/functional/api/v2/test_member.py b/octavia/tests/functional/api/v2/test_member.py index 7c1d599049..f378194a59 100644 --- a/octavia/tests/functional/api/v2/test_member.py +++ b/octavia/tests/functional/api/v2/test_member.py @@ -110,18 +110,14 @@ class TestMember(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, response) - def test_get_hides_deleted(self): + def test_get_deleted_gives_404(self): api_member = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) - response = self.get(self.members_path) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 1) self.set_object_status(self.member_repo, api_member.get('id'), provisioning_status=constants.DELETED) - response = self.get(self.members_path) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 0) + self.get(self.member_path.format(member_id=api_member.get('id')), + status=404) def test_bad_get(self): self.get(self.member_path.format(member_id=uuidutils.generate_uuid()), @@ -148,6 +144,19 @@ class TestMember(base.BaseAPITest): for m in [api_m_1, api_m_2]: self.assertIn(m, response) + def test_get_all_hides_deleted(self): + api_member = self.create_member( + self.pool_id, '10.0.0.1', 80).get(self.root_tag) + + response = self.get(self.members_path) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 1) + self.set_object_status(self.member_repo, api_member.get('id'), + provisioning_status=constants.DELETED) + response = self.get(self.members_path) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 0) + def test_get_all_authorized(self): api_m_1 = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) @@ -890,13 +899,8 @@ class TestMember(base.BaseAPITest): member_prov_status=constants.PENDING_DELETE) self.set_lb_status(self.lb_id) - self.assert_correct_status( - lb_id=self.lb_id, listener_id=self.listener_id, - pool_id=self.pool_with_listener_id, member_id=member.get('id'), - lb_prov_status=constants.ACTIVE, - listener_prov_status=constants.ACTIVE, - pool_prov_status=constants.ACTIVE, - member_prov_status=constants.DELETED) + member = self.get(self.member_path_listener.format( + member_id=api_member.get('id')), status=404) def test_delete_authorized(self): api_member = self.create_member( @@ -944,13 +948,8 @@ class TestMember(base.BaseAPITest): member_prov_status=constants.PENDING_DELETE) self.set_lb_status(self.lb_id) - self.assert_correct_status( - lb_id=self.lb_id, listener_id=self.listener_id, - pool_id=self.pool_with_listener_id, member_id=member.get('id'), - lb_prov_status=constants.ACTIVE, - listener_prov_status=constants.ACTIVE, - pool_prov_status=constants.ACTIVE, - member_prov_status=constants.DELETED) + member = self.get(self.member_path_listener.format( + member_id=api_member.get('id')), status=404) def test_delete_not_authorized(self): api_member = self.create_member( @@ -1061,6 +1060,14 @@ class TestMember(base.BaseAPITest): self.put(self.member_path.format(member_id=member.get('id')), body=self._build_body({'name': "member2"}), status=409) + def test_update_when_deleted(self): + member = self.create_member( + self.pool_id, address="10.0.0.1", + protocol_port=80).get(self.root_tag) + self.set_lb_status(self.lb_id, status=constants.DELETED) + self.put(self.member_path.format(member_id=member.get('id')), + body=self._build_body({'name': "member2"}), status=404) + def test_delete_when_lb_pending_delete(self): member = self.create_member( self.pool_id, address="192.0.2.1", @@ -1077,4 +1084,4 @@ class TestMember(base.BaseAPITest): protocol_port=80).get(self.root_tag) self.set_lb_status(self.lb_id, status=constants.DELETED) self.delete(self.member_path.format( - member_id=member.get('id')), status=204) + member_id=member.get('id')), status=404) diff --git a/octavia/tests/functional/api/v2/test_pool.py b/octavia/tests/functional/api/v2/test_pool.py index eb5f0596e6..4e873a6e93 100644 --- a/octavia/tests/functional/api/v2/test_pool.py +++ b/octavia/tests/functional/api/v2/test_pool.py @@ -124,21 +124,16 @@ class TestPool(base.BaseAPITest): self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json) - def test_get_hides_deleted(self): + def test_get_deleted_gives_404(self): api_pool = self.create_pool( self.lb_id, constants.PROTOCOL_HTTP, constants.LB_ALGORITHM_ROUND_ROBIN, listener_id=self.listener_id).get(self.root_tag) - response = self.get(self.POOLS_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 1) self.set_object_status(self.pool_repo, api_pool.get('id'), provisioning_status=constants.DELETED) - response = self.get(self.POOLS_PATH) - objects = response.json.get(self.root_tag_list) - self.assertEqual(len(objects), 0) + self.get(self.POOL_PATH.format(pool_id=api_pool.get('id')), status=404) def test_bad_get(self): self.get(self.POOL_PATH.format(pool_id=uuidutils.generate_uuid()), @@ -156,6 +151,22 @@ class TestPool(base.BaseAPITest): self.assertEqual(1, len(pools)) self.assertEqual(api_pool.get('id'), pools[0].get('id')) + def test_get_all_hides_deleted(self): + api_pool = self.create_pool( + self.lb_id, + constants.PROTOCOL_HTTP, + constants.LB_ALGORITHM_ROUND_ROBIN, + listener_id=self.listener_id).get(self.root_tag) + + response = self.get(self.POOLS_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 1) + self.set_object_status(self.pool_repo, api_pool.get('id'), + provisioning_status=constants.DELETED) + response = self.get(self.POOLS_PATH) + objects = response.json.get(self.root_tag_list) + self.assertEqual(len(objects), 0) + def test_get_all_admin(self): project_id = uuidutils.generate_uuid() lb1 = self.create_load_balancer(uuidutils.generate_uuid(), name='lb1', @@ -1382,6 +1393,18 @@ class TestPool(base.BaseAPITest): self.delete(self.POOL_PATH.format(pool_id=api_pool.get('id')), status=409) + def test_update_already_deleted(self): + api_pool = self.create_pool( + self.lb_id, + constants.PROTOCOL_HTTP, + constants.LB_ALGORITHM_ROUND_ROBIN, + listener_id=self.listener_id).get(self.root_tag) + # This updates the child objects + self.set_lb_status(self.lb_id, status=constants.DELETED) + new_pool = {'admin_state_up': False} + self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')), + self._build_body(new_pool), status=404) + def test_delete_already_deleted(self): api_pool = self.create_pool( self.lb_id, @@ -1391,4 +1414,4 @@ class TestPool(base.BaseAPITest): # This updates the child objects self.set_lb_status(self.lb_id, status=constants.DELETED) self.delete(self.POOL_PATH.format(pool_id=api_pool.get('id')), - status=204) + status=404) diff --git a/releasenotes/notes/deleted-404-2cdd751e7afbe036.yaml b/releasenotes/notes/deleted-404-2cdd751e7afbe036.yaml new file mode 100644 index 0000000000..396017ef84 --- /dev/null +++ b/releasenotes/notes/deleted-404-2cdd751e7afbe036.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixes the v2 API returning "DELETED" records until the amphora_expiry_age + timeout expired. The API will now immediately return a 404 HTTP status + code when deleted objects are requested. The API version has been raised + to v2.1 to reflect this change.