def test_unbind_agent_not_notified(self): # Setup self.populate() manager = factory.consumer_bind_manager() bind = manager.bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, False, self.BINDING_CONFIG) # Test options = {} itinerary = unbind_itinerary( self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, options) call_reports = self.coordinator.execute_multiple_calls(itinerary) # Verify self.assertEqual(len(call_reports), 1) self.assertEqual(call_reports[0].call_request_tags, self.UNBIND_TAGS) for call in call_reports: self.assertNotEqual(call.state, dispatch_constants.CALL_REJECTED_RESPONSE) # run task #1 (actual delete) self.run_next() # verify bind deleted collection = Bind.get_collection() bind = collection.find_one(self.QUERY) self.assertTrue(bind is None) # verify agent notified self.assertFalse(mock_agent.Consumer.unbind.called)
def DELETE(self, consumer_id, repo_id, distributor_id): """ Delete a bind association between the specified consumer and repo-distributor. Designed to be idempotent. @param consumer_id: A consumer ID. @type consumer_id: str @param repo_id: A repo ID. @type repo_id: str @param distributor_id: A distributor ID. @type distributor_id: str @return: The list of call_reports @rtype: list """ body = self.params() # validate resources manager = managers.consumer_bind_manager() binding = manager.get_bind(consumer_id, repo_id, distributor_id) notify_agent = binding['notify_agent'] # delete (unbind) forced = body.get('force', False) options = body.get('options', {}) if forced or not notify_agent: call_requests = forced_unbind_itinerary(consumer_id, repo_id, distributor_id, options) else: call_requests = unbind_itinerary(consumer_id, repo_id, distributor_id, options) execution.execute_multiple(call_requests)
def DELETE(self, consumer_id, repo_id, distributor_id): """ Delete a bind association between the specified consumer and repo-distributor. Designed to be idempotent. @param consumer_id: A consumer ID. @type consumer_id: str @param repo_id: A repo ID. @type repo_id: str @param distributor_id: A distributor ID. @type distributor_id: str @return: The list of call_reports @rtype: list """ body = self.params() # validate resources manager = managers.consumer_bind_manager() binding = manager.get_bind(consumer_id, repo_id, distributor_id) notify_agent = binding['notify_agent'] # delete (unbind) forced = body.get('force', False) options = body.get('options', {}) if forced or not notify_agent: call_requests = forced_unbind_itinerary( consumer_id, repo_id, distributor_id, options) else: call_requests = unbind_itinerary( consumer_id, repo_id, distributor_id, options) execution.execute_multiple(call_requests)
def repo_delete_itinerary(repo_id): """ Get the itinerary for deleting a repository. 1. Delete the repository on the sever. 2. Unbind any bound consumers. @param repo_id: A repository ID. @type repo_id: str @return: A list of call_requests known as an itinerary. @rtype list """ call_requests = [] # delete repository manager = managers.repo_manager() resources = {dispatch_constants.RESOURCE_REPOSITORY_TYPE: {repo_id: dispatch_constants.RESOURCE_DELETE_OPERATION}} tags = [resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), action_tag("delete")] delete_request = CallRequest(manager.delete_repo, [repo_id], resources=resources, tags=tags, archive=True) call_requests.append(delete_request) # append unbind itineraries foreach bound consumer options = {} manager = managers.consumer_bind_manager() for bind in manager.find_by_repo(repo_id): unbind_requests = unbind_itinerary(bind["consumer_id"], bind["repo_id"], bind["distributor_id"], options) if unbind_requests: unbind_requests[0].depends_on(delete_request.id) call_requests.extend(unbind_requests) return call_requests
def distributor_delete_itinerary(repo_id, distributor_id): """ Get the itinerary for deleting a repository distributor. 1. Delete the distributor on the sever. 2. Unbind any bound consumers. @param repo_id: A repository ID. @type repo_id: str @return: A list of call_requests known as an itinerary. @rtype list """ call_requests = [] # delete distributor manager = managers.repo_distributor_manager() resources = { dispatch_constants.RESOURCE_REPOSITORY_TYPE: { repo_id: dispatch_constants.RESOURCE_UPDATE_OPERATION }, dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE: { distributor_id: dispatch_constants.RESOURCE_DELETE_OPERATION } } tags = [ resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag('remove_distributor') ] delete_request = CallRequest(manager.remove_distributor, [repo_id, distributor_id], resources=resources, tags=tags, archive=True) call_requests.append(delete_request) # append unbind itineraries foreach bound consumer options = {} manager = managers.consumer_bind_manager() for bind in manager.find_by_distributor(repo_id, distributor_id): unbind_requests = unbind_itinerary(bind['consumer_id'], bind['repo_id'], bind['distributor_id'], options) if unbind_requests: unbind_requests[0].depends_on(delete_request.id) call_requests.extend(unbind_requests) return call_requests
def distributor_delete_itinerary(repo_id, distributor_id): """ Get the itinerary for deleting a repository distributor. 1. Delete the distributor on the sever. 2. Unbind any bound consumers. @param repo_id: A repository ID. @type repo_id: str @return: A list of call_requests known as an itinerary. @rtype list """ call_requests = [] # delete distributor manager = managers.repo_distributor_manager() tags = [ resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag('remove_distributor') ] delete_request = CallRequest( manager.remove_distributor, [repo_id, distributor_id], tags=tags, archive=True) delete_request.updates_resource(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id) delete_request.deletes_resource(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id) call_requests.append(delete_request) # append unbind itineraries foreach bound consumer options = {} manager = managers.consumer_bind_manager() for bind in manager.find_by_distributor(repo_id, distributor_id): unbind_requests = unbind_itinerary( bind['consumer_id'], bind['repo_id'], bind['distributor_id'], options) if unbind_requests: unbind_requests[0].depends_on(delete_request.id) call_requests.extend(unbind_requests) return call_requests
def test_unbind_failed(self, mock_bind): # Setup self.populate() manager = factory.consumer_bind_manager() manager.bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, self.NOTIFY_AGENT, self.BINDING_CONFIG) # Test options = {} itinerary = unbind_itinerary( self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, options) call_reports = self.coordinator.execute_multiple_calls(itinerary) # Verify self.assertEqual(len(call_reports), 3) for call in call_reports: self.assertNotEqual(call.state, dispatch_constants.CALL_REJECTED_RESPONSE) # run task #1 (actual bind) self.run_next() # run task #2 (notify consumer) self.run_next() # verify task #2 skipped request_id = call_reports[1].call_request_id call_report = self.coordinator.find_call_reports(call_request_id=request_id)[0] self.assertEqual(call_report.state, dispatch_constants.CALL_SKIPPED_STATE) # verify agent NOT notified self.assertFalse(mock_agent.Consumer.bind.called) # run task #3 (delete bind) self.run_next() # verify task #3 was skipped request_id = call_reports[2].call_request_id call_report = self.coordinator.find_call_reports(call_request_id=request_id)[0] self.assertEqual(call_report.state, dispatch_constants.CALL_SKIPPED_STATE) # verify bind still exists bind = manager.get_bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID) self.assertTrue(bind is not None)
def consumer_group_unbind_itinerary(group_id, repo_id, distributor_id, options): """ Unbind the members of the specified consumer group. :param group_id: A consumer group ID. :type group_id: str :param repo_id: A repository ID. :type repo_id: str :param distributor_id: A distributor ID. :type distributor_id: str :param options: Bind options passed to the agent handler. :type options: dict :return: A list of call_requests. :rtype list """ call_requests = [] manager = managers.consumer_group_query_manager() group = manager.get_group(group_id) for consumer_id in group["consumer_ids"]: itinerary = unbind_itinerary(consumer_id, repo_id, distributor_id, options) call_requests.extend(itinerary) return call_requests
def consumer_group_unbind_itinerary(group_id, repo_id, distributor_id, options): """ Unbind the members of the specified consumer group. :param group_id: A consumer group ID. :type group_id: str :param repo_id: A repository ID. :type repo_id: str :param distributor_id: A distributor ID. :type distributor_id: str :param options: Bind options passed to the agent handler. :type options: dict :return: A list of call_requests. :rtype list """ call_requests = [] manager = managers.consumer_group_query_manager() group = manager.get_group(group_id) for consumer_id in group['consumer_ids']: itinerary = unbind_itinerary(consumer_id, repo_id, distributor_id, options) call_requests.extend(itinerary) return call_requests
def test_unbind_failed_on_consumer(self): # Setup self.populate() manager = factory.consumer_bind_manager() bind = manager.bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, self.NOTIFY_AGENT, self.BINDING_CONFIG) # Test options = {} itinerary = unbind_itinerary( self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, options) call_reports = self.coordinator.execute_multiple_calls(itinerary) # Verify self.assertEqual(len(call_reports), 3) for call in call_reports: self.assertNotEqual(call.state, dispatch_constants.CALL_REJECTED_RESPONSE) # run task #1 (actual unbind) self.run_next() # verify bind marked deleted collection = Bind.get_collection() bind = collection.find_one(self.QUERY) self.assertTrue(bind['deleted']) # run task #2 (notify consumer) self.run_next() # verify agent notified self.assertTrue(mock_agent.Consumer.unbind.called) # verify consumer request (pending) request_id = call_reports[1].call_request_id collection = Bind.get_collection() bind = collection.find_one(self.QUERY) self.assertTrue(bind is not None) actions = bind['consumer_actions'] self.assertEqual(len(actions), 1) self.assertEqual(actions[0]['id'], request_id) self.assertEqual(actions[0]['action'], Bind.Action.UNBIND) self.assertEqual(actions[0]['status'], Bind.Status.PENDING) self.assertTrue(isinstance(actions[0]['timestamp'], float)) # simulated asynchronous task result report = DispatchReport() report.succeeded = False self.coordinator.complete_call_success(request_id, report.dict()) # verify not found (marked deleted) binds = manager.find_by_consumer(self.CONSUMER_ID) self.assertEquals(len(binds), 0) # run task #3 (bind actually deleted) self.run_next() # verify bind not deleted collection = Bind.get_collection() bind = collection.find_one(self.QUERY) self.assertTrue(bind is not None) actions = bind['consumer_actions'] self.assertEqual(len(actions), 1) self.assertEqual(actions[0]['id'], request_id) self.assertEqual(actions[0]['action'], Bind.Action.UNBIND) self.assertEqual(actions[0]['status'], Bind.Status.FAILED) self.assertTrue(isinstance(actions[0]['timestamp'], float))