def test_bind_failed(self, mock_bind): # Setup self.populate() # Test options = {} itinerary = bind_itinerary( self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, self.NOTIFY_AGENT, self.BINDING_CONFIG, options) call_reports = self.coordinator.execute_multiple_calls(itinerary) # Verify self.assertEqual(len(call_reports), 2) 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 was 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)
def test_bind(self): # Setup self.populate() # Test options = {} itinerary = bind_itinerary( self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, self.NOTIFY_AGENT, self.BINDING_CONFIG, options) call_reports = self.coordinator.execute_multiple_calls(itinerary) # Verify self.assertEqual(len(call_reports), 2) self.assertEqual(call_reports[0].call_request_tags, self.BIND_TAGS) self.assertEqual(call_reports[1].call_request_tags, self.AGENT_BIND_TAGS) for call in call_reports: self.assertNotEqual(call.state, dispatch_constants.CALL_REJECTED_RESPONSE) # run task #1 (actual bind) self.run_next() # verify bind created manager = factory.consumer_bind_manager() binds = manager.find_by_consumer(self.CONSUMER_ID) self.assertEquals(len(binds), 1) bind = binds[0] self.assertEqual(bind['consumer_id'], self.CONSUMER_ID) self.assertEqual(bind['repo_id'], self.REPO_ID) self.assertEqual(bind['distributor_id'], self.DISTRIBUTOR_ID) self.assertEqual(bind['notify_agent'], self.NOTIFY_AGENT) self.assertEqual(bind['binding_config'], self.BINDING_CONFIG) # run task #2 (notify consumer) self.run_next() # verify pending consumer request (pending) request_id = call_reports[1].call_request_id bind = manager.get_bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID) actions = bind['consumer_actions'] self.assertEqual(len(actions), 1) self.assertEqual(actions[0]['id'], request_id) self.assertEqual(actions[0]['action'], Bind.Action.BIND) self.assertEqual(actions[0]['status'], Bind.Status.PENDING) self.assertTrue(isinstance(actions[0]['timestamp'], float)) # verify agent notified self.assertTrue(mock_agent.Consumer.bind.called) # simulated asynchronous task result report = DispatchReport() self.coordinator.complete_call_success(request_id, report.dict()) # verify pending consumer request (confirmed) manager = factory.consumer_bind_manager() bind = manager.get_bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID) self.assertEqual(len(bind['consumer_actions']), 0)
def POST(self, consumer_id): """ Create a bind association between the specified consumer by id included in the URL path and a repo-distributor specified in the POST body: {repo_id:<str>, distributor_id:<str>}. Designed to be idempotent so only MissingResource is expected to be raised by manager. @param consumer_id: The consumer to bind. @type consumer_id: str @return: The list of call_reports @rtype: list """ # validate consumer consumer_manager = managers.consumer_manager() consumer_manager.get_consumer(consumer_id) # get other options and validate them body = self.params() repo_id = body.get('repo_id') distributor_id = body.get('distributor_id') binding_config = body.get('binding_config', None) options = body.get('options', {}) notify_agent = body.get('notify_agent', True) managers.repo_query_manager().get_repository(repo_id) managers.repo_distributor_manager().get_distributor( repo_id, distributor_id) # bind call_requests = bind_itinerary(consumer_id, repo_id, distributor_id, notify_agent, binding_config, options) execution.execute_multiple(call_requests)
def POST(self, consumer_id): """ Create a bind association between the specified consumer by id included in the URL path and a repo-distributor specified in the POST body: {repo_id:<str>, distributor_id:<str>}. Designed to be idempotent so only MissingResource is expected to be raised by manager. @param consumer_id: The consumer to bind. @type consumer_id: str @return: The list of call_reports @rtype: list """ # validate consumer consumer_manager = managers.consumer_manager() consumer_manager.get_consumer(consumer_id) # get other options and validate them body = self.params() repo_id = body.get('repo_id') distributor_id = body.get('distributor_id') binding_config = body.get('binding_config', None) options = body.get('options', {}) notify_agent = body.get('notify_agent', True) managers.repo_query_manager().get_repository(repo_id) managers.repo_distributor_manager().get_distributor(repo_id, distributor_id) # bind call_requests = bind_itinerary(consumer_id, repo_id, distributor_id, notify_agent, binding_config, options) execution.execute_multiple(call_requests)
def consumer_group_bind_itinerary(group_id, repo_id, distributor_id, notify_agent, binding_config, agent_options): """ Bind 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 agent_options: Bind options passed to the agent handler. :type agent_options: dict :param notify_agent: indicates if the agent should be sent a message about the new binding :type notify_agent: bool :param binding_config: configuration options to use when generating the payload for this binding :type binding_config: 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 = bind_itinerary(consumer_id=consumer_id, repo_id=repo_id, distributor_id=distributor_id, notify_agent=notify_agent, binding_config=binding_config, agent_options=agent_options) call_requests.extend(itinerary) return call_requests
def consumer_group_bind_itinerary(group_id, repo_id, distributor_id, notify_agent, binding_config, agent_options): """ Bind 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 agent_options: Bind options passed to the agent handler. :type agent_options: dict :param notify_agent: indicates if the agent should be sent a message about the new binding :type notify_agent: bool :param binding_config: configuration options to use when generating the payload for this binding :type binding_config: 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 = bind_itinerary( consumer_id=consumer_id, repo_id=repo_id, distributor_id=distributor_id, notify_agent=notify_agent, binding_config=binding_config, agent_options=agent_options, ) call_requests.extend(itinerary) return call_requests
def distributor_update_itinerary(repo_id, distributor_id, config): """ Get the itinerary for updating a repository distributor. 1. Update the distributor on the sever. 2. (re)bind 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 = [] # update the 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_UPDATE_OPERATION }, } tags = [ resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag("update_distributor"), ] update_request = CallRequest( manager.update_distributor_config, [repo_id, distributor_id], {"distributor_config": config}, resources=resources, tags=tags, archive=True, kwarg_blacklist=["distributor_config"], ) call_requests.append(update_request) # append unbind itineraries foreach bound consumer options = {} manager = managers.consumer_bind_manager() for bind in manager.find_by_distributor(repo_id, distributor_id): bind_requests = bind_itinerary( bind["consumer_id"], bind["repo_id"], bind["distributor_id"], bind["notify_agent"], bind["binding_config"], options, ) if bind_requests: bind_requests[0].depends_on(update_request.id) call_requests.extend(bind_requests) return call_requests
def distributor_update_itinerary(repo_id, distributor_id, config): """ Get the itinerary for updating a repository distributor. 1. Update the distributor on the sever. 2. (re)bind 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 = [] # update the 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_UPDATE_OPERATION } } tags = [ resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag('update_distributor') ] update_request = CallRequest(manager.update_distributor_config, [repo_id, distributor_id], {'distributor_config': config}, resources=resources, tags=tags, archive=True, kwarg_blacklist=['distributor_config']) call_requests.append(update_request) # append unbind itineraries foreach bound consumer options = {} manager = managers.consumer_bind_manager() for bind in manager.find_by_distributor(repo_id, distributor_id): bind_requests = bind_itinerary(bind['consumer_id'], bind['repo_id'], bind['distributor_id'], bind['notify_agent'], bind['binding_config'], options) if bind_requests: bind_requests[0].depends_on(update_request.id) call_requests.extend(bind_requests) return call_requests
def test_bind_no_notify_agent(self): # Setup self.populate() # Test options = {} itinerary = bind_itinerary( self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, False, self.BINDING_CONFIG, 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.BIND_TAGS) for call in call_reports: self.assertNotEqual(call.state, dispatch_constants.CALL_REJECTED_RESPONSE) # run task #1 (actual bind) self.run_next() # verify bind created manager = factory.consumer_bind_manager() binds = manager.find_by_consumer(self.CONSUMER_ID) self.assertEquals(len(binds), 1) bind = binds[0] self.assertEqual(bind['consumer_id'], self.CONSUMER_ID) self.assertEqual(bind['repo_id'], self.REPO_ID) self.assertEqual(bind['distributor_id'], self.DISTRIBUTOR_ID) self.assertEqual(bind['notify_agent'], False) self.assertEqual(bind['binding_config'], self.BINDING_CONFIG) # run task #2 (notify consumer) try: self.run_next() self.fail('Second task to bind found') except Exception: pass # the agent notify shouldn't trigger # verify agent was not notified self.assertTrue(not mock_agent.Consumer.bind.called)
def POST(self, consumer_id): """ Create a bind association between the specified consumer by id included in the URL path and a repo-distributor specified in the POST body: {repo_id:<str>, distributor_id:<str>}. Designed to be idempotent so only MissingResource is expected to be raised by manager. @param consumer_id: The consumer to bind. @type consumer_id: str @return: The list of call_reports @rtype: list """ # validate resources manager = managers.consumer_manager() manager.get_consumer(consumer_id) # bind body = self.params() repo_id = body.get('repo_id') distributor_id = body.get('distributor_id') options = body.get('options', {}) call_requests = bind_itinerary(consumer_id, repo_id, distributor_id, options) execution.execute_multiple(call_requests)
def distributor_update_itinerary(repo_id, distributor_id, config, delta=None): """ Get the itinerary for updating a repository distributor. 1. Update the distributor on the server. 2. (re)bind any bound consumers. :param repo_id: A repository ID. :type repo_id: str :param distributor_id: A unique distributor id :type distributor_id: str :param config: A configuration dictionary for a distributor instance. The contents of this dict depends on the type of distributor. :type config: dict :param delta: A dictionary used to change other saved configuration values for a distributor instance. This currently only supports the 'auto_publish' keyword, which should have a value of type bool :type delta: dict :return: A list of call_requests known as an itinerary. :rtype: list """ call_requests = [] # update the 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('update_distributor') ] # Retrieve configuration options from the delta auto_publish = None if delta is not None: auto_publish = delta.get('auto_publish') update_request = CallRequest(manager.update_distributor_config, [repo_id, distributor_id], {'distributor_config': config, 'auto_publish': auto_publish}, tags=tags, archive=True, kwarg_blacklist=['distributor_config', 'auto_publish']) update_request.updates_resource(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id) update_request.updates_resource(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id) call_requests.append(update_request) # append unbind itineraries foreach bound consumer options = {} manager = managers.consumer_bind_manager() for bind in manager.find_by_distributor(repo_id, distributor_id): bind_requests = bind_itinerary( bind['consumer_id'], bind['repo_id'], bind['distributor_id'], bind['notify_agent'], bind['binding_config'], options) if bind_requests: bind_requests[0].depends_on(update_request.id) call_requests.extend(bind_requests) return call_requests