def test_constructor_configuration_manager_defined_no_configuration_manager_passed( self, reset_singleton): configuration_manager_mock = MagicMock(spec=ConfigurationManager) i_reg = InterestRegistry(configuration_manager_mock) # second time initializing InterestRegistry without configuration manager i_reg2 = InterestRegistry() assert hasattr(i_reg2, '_configuration_manager') assert isinstance(i_reg2._configuration_manager, ConfigurationManager) assert hasattr(i_reg2, '_registered_interests') assert len(i_reg._registered_interests) == 0
def test_constructor_configuration_manager_defined_configuration_manager_passed( self, reset_singleton): configuration_manager_mock = MagicMock(spec=ConfigurationManager) # second time initializing InterestRegistry with new configuration manager # works configuration_manager_mock2 = MagicMock(spec=ConfigurationManager) i_reg = InterestRegistry(configuration_manager_mock) i_reg2 = InterestRegistry(configuration_manager_mock2) assert hasattr(i_reg2, '_configuration_manager') # ignore new configuration manager assert isinstance(i_reg2._configuration_manager, ConfigurationManager) assert hasattr(i_reg2, '_registered_interests')
async def run(category_name): """ Callback run by configuration category to notify changes to interested microservices Note: this method is async as needed Args: configuration_name (str): name of category that was changed """ # get all interest records regarding category_name cfg_mgr = ConfigurationManager() interest_registry = InterestRegistry(cfg_mgr) try: interest_records = interest_registry.get(category_name=category_name) except interest_registry_exceptions.DoesNotExist: return category_value = await cfg_mgr.get_category_all_items(category_name) payload = {"category": category_name, "items": category_value} headers = {'content-type': 'application/json'} # for each microservice interested in category_name, notify change for i in interest_records: # get microservice management server info of microservice through service registry try: service_record = ServiceRegistry.get(idx=i._microservice_uuid)[0] except service_registry_exceptions.DoesNotExist: _LOGGER.exception( "Unable to notify microservice with uuid %s as it is not found in the service registry", i._microservice_uuid) continue url = "{}://{}:{}/foglamp/change".format( service_record._protocol, service_record._address, service_record._management_port) async with aiohttp.ClientSession() as session: try: async with session.post(url, data=json.dumps(payload, sort_keys=True), headers=headers) as resp: result = await resp.text() status_code = resp.status if status_code in range(400, 500): _LOGGER.error("Bad request error code: %d, reason: %s", status_code, resp.reason) if status_code in range(500, 600): _LOGGER.error("Server error code: %d, reason: %s", status_code, resp.reason) except Exception as ex: _LOGGER.exception( "Unable to notify microservice with uuid %s due to exception: %s", i._microservice_uuid, str(ex)) continue
async def test_run_general_exception(self): storage_client_mock = MagicMock(spec=StorageClientAsync) cfg_mgr = ConfigurationManager(storage_client_mock) with patch.object(ServiceRegistry._logger, 'info') as log_info: s_id_1 = ServiceRegistry.register('sname1', 'Storage', 'saddress1', 1, 1, 'http') assert 1 == log_info.call_count args, kwargs = log_info.call_args assert args[0].startswith('Registered service instance id=') assert args[0].endswith( ': <sname1, type=Storage, protocol=http, address=saddress1, service port=1, management port=1, status=1>' ) i_reg = InterestRegistry(cfg_mgr) i_reg.register(s_id_1, 'catname1') # used to mock client session context manager async def async_mock(return_value): return return_value class AsyncSessionContextManagerMock(MagicMock): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) async def __aenter__(self): raise Exception async def __aexit__(self, *args): return None with patch.object(ConfigurationManager, 'get_category_all_items', return_value=async_mock(None)) as cm_get_patch: with patch.object(aiohttp.ClientSession, 'post', return_value=AsyncSessionContextManagerMock() ) as post_patch: with patch.object(cb._LOGGER, 'exception') as exception_patch: await cb.run('catname1') exception_patch.assert_called_once_with( 'Unable to notify microservice with uuid %s due to exception: %s', s_id_1, '') post_patch.assert_has_calls([ call('http://saddress1:1/foglamp/change', data='{"category": "catname1", "items": null}', headers={'content-type': 'application/json'}) ]) cm_get_patch.assert_called_once_with('catname1')
async def test_get_interest(self, client): Server._storage_client = MagicMock(StorageClientAsync) Server._configuration_manager = ConfigurationManager( Server._storage_client) Server._interest_registry = InterestRegistry( Server._configuration_manager) data = [] category_name = 'test_Cat' muuid = '0c501cd3-c45a-439a-bec6-fc08d13f9699' reg_id = 'c6bbf3c8-f43c-4b0f-ac48-f597f510da0b' record = InterestRecord(reg_id, muuid, category_name) data.append(record) with patch.object(Server._interest_registry, 'get', return_value=data) as patch_get_interest_reg: resp = await client.get('/foglamp/interest') assert 200 == resp.status r = await resp.text() json_response = json.loads(r) assert { 'interests': [{ 'category': category_name, 'microserviceId': muuid, 'registrationId': reg_id }] } == json_response args, kwargs = patch_get_interest_reg.call_args assert {} == kwargs
async def test_register_interest(self, client): Server._storage_client = MagicMock(StorageClientAsync) Server._configuration_manager = ConfigurationManager( Server._storage_client) Server._interest_registry = InterestRegistry( Server._configuration_manager) request_data = { "category": "COAP", "service": "c6bbf3c8-f43c-4b0f-ac48-f597f510da0b" } reg_id = 'a404852d-d91c-47bd-8860-d4ff81b6e8cb' with patch.object(Server._interest_registry, 'register', return_value=reg_id) as patch_reg_interest_reg: resp = await client.post('/foglamp/interest', data=json.dumps(request_data)) assert 200 == resp.status r = await resp.text() json_response = json.loads(r) assert { 'id': reg_id, 'message': 'Interest registered successfully' } == json_response args, kwargs = patch_reg_interest_reg.call_args assert (request_data['service'], request_data['category']) == args
async def test_unregister_interest(self, client): Server._storage_client = MagicMock(StorageClientAsync) Server._configuration_manager = ConfigurationManager( Server._storage_client) Server._interest_registry = InterestRegistry( Server._configuration_manager) data = [] category_name = 'test_Cat' muuid = '0c501cd3-c45a-439a-bec6-fc08d13f9699' reg_id = 'c6bbf3c8-f43c-4b0f-ac48-f597f510da0b' record = InterestRecord(reg_id, muuid, category_name) data.append(record) with patch.object(Server._interest_registry, 'get', return_value=data) as patch_get_interest_reg: with patch.object(Server._interest_registry, 'unregister', return_value=[]) as patch_unregister_interest: resp = await client.delete( '/foglamp/interest/{}'.format(reg_id)) assert 200 == resp.status r = await resp.text() json_response = json.loads(r) assert { 'id': reg_id, 'message': 'Interest unregistered' } == json_response args, kwargs = patch_unregister_interest.call_args assert (reg_id, ) == args args1, kwargs1 = patch_get_interest_reg.call_args assert {'registration_id': reg_id} == kwargs1
def test_get(self, reset_singleton): configuration_manager_mock = MagicMock(spec=ConfigurationManager) i_reg = InterestRegistry(configuration_manager_mock) # get when empty microservice_uuid = 'muuid' category_name = 'catname' with pytest.raises(interest_registry_exceptions.DoesNotExist) as excinfo: i_reg.get(microservice_uuid=microservice_uuid, category_name=category_name) # get when there is a result (use patch on 'get') with patch.object(InterestRegistry, 'and_filter', return_value=[1]): ret_val = i_reg.get( microservice_uuid=microservice_uuid, category_name=category_name) assert ret_val is not None assert ret_val == [1]
def test_constructor_no_configuration_manager_defined_no_configuration_manager_passed( self, reset_singleton): # first time initializing InterestRegistry without configuration manager # produces error with pytest.raises(TypeError) as excinfo: InterestRegistry() assert 'Must be a valid ConfigurationManager object' in str( excinfo.value)
def _expunge(cls, service_id, service_status): """ removes the service instance from action :param service_id: a uuid of registered service :param service_status: service status to be marked :return: service_id on successful deregistration """ services = cls.get(idx=service_id) service_name = services[0]._name services[0]._status = service_status cls._remove_from_scheduler_records(service_name) # Remove interest registry records, if any interest_recs = InterestRegistry().get(microservice_uuid=service_id) for interest_rec in interest_recs: InterestRegistry().unregister(interest_rec._registration_id) return services[0]
def test_constructor_no_configuration_manager_defined_configuration_manager_passed( self, reset_singleton): # first time initializing InterestRegistry with configuration manager # works configuration_manager_mock = MagicMock(spec=ConfigurationManager) i_reg = InterestRegistry(configuration_manager_mock) assert hasattr(i_reg, '_configuration_manager') assert isinstance(i_reg._configuration_manager, ConfigurationManager) assert hasattr(i_reg, '_registered_interests')
async def test_get_interest_exception(self, client, params, message, expected_kwargs): Server._storage_client = MagicMock(StorageClient) Server._configuration_manager = ConfigurationManager(Server._storage_client) Server._interest_registry = InterestRegistry(Server._configuration_manager) with patch.object(Server._interest_registry, 'get', side_effect=interest_registry_exceptions.DoesNotExist) as patch_get_interest_reg: resp = await client.get('/foglamp/interest{}'.format(params)) assert 404 == resp.status assert message == resp.reason args, kwargs = patch_get_interest_reg.call_args assert expected_kwargs == kwargs
def test_register(self, reset_singleton): configuration_manager_mock = MagicMock(spec=ConfigurationManager) i_reg = InterestRegistry(configuration_manager_mock) # register the first interest microservice_uuid = 'muuid' category_name = 'catname' ret_val = i_reg.register(microservice_uuid, category_name) assert ret_val is not None assert len(i_reg._registered_interests) is 1 assert isinstance(i_reg._registered_interests[0], InterestRecord) assert i_reg._registered_interests[0]._registration_id is ret_val assert i_reg._registered_interests[0]._microservice_uuid is microservice_uuid assert i_reg._registered_interests[0]._category_name is category_name str_val = 'interest registration id={}: <microservice uuid={}, category_name={}>'.format( ret_val, microservice_uuid, category_name) assert str(i_reg._registered_interests[0]) == str_val # register an existing interest with pytest.raises(interest_registry_exceptions.ErrorInterestRegistrationAlreadyExists) as excinfo: ret_val = i_reg.register(microservice_uuid, category_name) assert ret_val is not None assert len(i_reg._registered_interests) is 1 assert isinstance(i_reg._registered_interests[0], InterestRecord) assert i_reg._registered_interests[0]._registration_id is ret_val assert i_reg._registered_interests[0]._microservice_uuid is microservice_uuid assert i_reg._registered_interests[0]._category_name is category_name str_val = 'interest registration id={}: <microservice uuid={}, category_name={}>'.format( ret_val, microservice_uuid, category_name) assert str(i_reg._registered_interests[0]) == str_val # register a second interest category_name2 = 'catname2' ret_val = i_reg.register(microservice_uuid, category_name2) assert ret_val is not None assert len(i_reg._registered_interests) is 2 assert isinstance(i_reg._registered_interests[1], InterestRecord) assert i_reg._registered_interests[1]._registration_id is ret_val assert i_reg._registered_interests[1]._microservice_uuid is microservice_uuid assert i_reg._registered_interests[1]._category_name is category_name2 str_val = 'interest registration id={}: <microservice uuid={}, category_name={}>'.format( ret_val, microservice_uuid, category_name2) assert str(i_reg._registered_interests[1]) == str_val
async def test_get_interest_with_filter(self, client, params, expected_kwargs): Server._storage_client = MagicMock(StorageClient) Server._configuration_manager = ConfigurationManager(Server._storage_client) Server._interest_registry = InterestRegistry(Server._configuration_manager) with patch.object(Server._interest_registry, 'get', return_value=[]) as patch_get_interest_reg: resp = await client.get('/foglamp/interest{}'.format(params)) assert 200 == resp.status r = await resp.text() json_response = json.loads(r) assert {'interests': []} == json_response args, kwargs = patch_get_interest_reg.call_args assert expected_kwargs == kwargs
async def test_bad_register_interest(self, client): Server._storage_client = MagicMock(StorageClient) Server._configuration_manager = ConfigurationManager(Server._storage_client) Server._interest_registry = InterestRegistry(Server._configuration_manager) request_data = {"category": "COAP", "service": "c6bbf3c8-f43c-4b0f-ac48-f597f510da0b"} with patch.object(Server._interest_registry, 'register', return_value=None) as patch_reg_interest_reg: resp = await client.post('/foglamp/interest', data=json.dumps(request_data)) assert 400 == resp.status assert 'Interest by microservice_uuid {} for category_name {} could not be registered'.format(request_data['service'], request_data['category']) == resp.reason args, kwargs = patch_reg_interest_reg.call_args assert (request_data['service'], request_data['category']) == args
async def test_unregister_interest_exception(self, client): Server._storage_client = MagicMock(StorageClient) Server._configuration_manager = ConfigurationManager(Server._storage_client) Server._interest_registry = InterestRegistry(Server._configuration_manager) reg_id = 'c6bbf3c8-f43c-4b0f-ac48-f597f510da0b' with patch.object(Server._interest_registry, 'get', side_effect=interest_registry_exceptions.DoesNotExist) as patch_get_interest_reg: resp = await client.delete('/foglamp/interest/{}'.format(reg_id)) assert 404 == resp.status assert 'InterestRecord with registration_id {} does not exist'.format(reg_id) == resp.reason args, kwargs = patch_get_interest_reg.call_args assert {'registration_id': reg_id} == kwargs
async def test_register_interest_exceptions(self, client): Server._storage_client = MagicMock(StorageClient) Server._configuration_manager = ConfigurationManager(Server._storage_client) Server._interest_registry = InterestRegistry(Server._configuration_manager) request_data = {"category": "COAP", "service": "c6bbf3c8-f43c-4b0f-ac48-f597f510da0b"} with patch.object(Server._interest_registry, 'register', side_effect=interest_registry_exceptions.ErrorInterestRegistrationAlreadyExists) as patch_reg_interest_reg: resp = await client.post('/foglamp/interest', data=json.dumps(request_data)) assert 400 == resp.status assert 'An InterestRecord already exists by microservice_uuid {} for category_name {}'.format(request_data['service'], request_data['category']) == resp.reason args, kwargs = patch_reg_interest_reg.call_args assert (request_data['service'], request_data['category']) == args
async def test_run_no_intrests_in_cat(self, reset_state): storage_client_mock = MagicMock(spec=StorageClient) cfg_mgr = ConfigurationManager(storage_client_mock) s_id_1 = ServiceRegistry.register('sname1', 'Storage', 'saddress1', 1, 1, 'http') s_id_2 = ServiceRegistry.register('sname2', 'Southbound', 'saddress2', 2, 2, 'http') s_id_3 = ServiceRegistry.register('sname3', 'Southbound', 'saddress3', 3, 3, 'http') i_reg = InterestRegistry(cfg_mgr) id_1_2 = i_reg.register(s_id_1, 'catname2') id_2_2 = i_reg.register(s_id_2, 'catname2') id_3_3 = i_reg.register(s_id_3, 'catname3') # used to mock client session context manager async def async_mock(return_value): return return_value class AsyncSessionContextManagerMock(MagicMock): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) async def __aenter__(self): client_response_mock = MagicMock(spec=aiohttp.ClientResponse) client_response_mock.text.side_effect = [async_mock(None)] status_mock = Mock() status_mock.side_effect = [200] client_response_mock.status = status_mock() return client_response_mock async def __aexit__(self, *args): return None with patch.object(ConfigurationManager, 'get_category_all_items') as cm_get_patch: with patch.object(aiohttp.ClientSession, 'post') as post_patch: await cb.run('catname1') cm_get_patch.assert_not_called() post_patch.assert_not_called()
async def test_run_missing_service_record(self, reset_state): storage_client_mock = MagicMock(spec=StorageClient) cfg_mgr = ConfigurationManager(storage_client_mock) s_id_1 = ServiceRegistry.register('sname1', 'Storage', 'saddress1', 1, 1, 'http') s_id_2 = ServiceRegistry.register('sname2', 'Southbound', 'saddress2', 2, 2, 'http') s_id_3 = ServiceRegistry.register('sname3', 'Southbound', 'saddress3', 3, 3, 'http') i_reg = InterestRegistry(cfg_mgr) id_fake_1 = i_reg.register('fakeid', 'catname1') id_1_1 = i_reg.register(s_id_1, 'catname1') id_1_2 = i_reg.register(s_id_1, 'catname2') id_2_1 = i_reg.register(s_id_2, 'catname1') id_2_2 = i_reg.register(s_id_2, 'catname2') id_3_3 = i_reg.register(s_id_3, 'catname3') # used to mock client session context manager async def async_mock(return_value): return return_value class AsyncSessionContextManagerMock(MagicMock): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) async def __aenter__(self): client_response_mock = MagicMock(spec=aiohttp.ClientResponse) client_response_mock.text.side_effect = [async_mock(None)] status_mock = Mock() status_mock.side_effect = [200] client_response_mock.status = status_mock() return client_response_mock async def __aexit__(self, *args): return None with patch.object(ConfigurationManager, 'get_category_all_items', return_value=async_mock(None)) as cm_get_patch: with patch.object(aiohttp.ClientSession, 'post', return_value=AsyncSessionContextManagerMock() ) as post_patch: with patch.object(cb._LOGGER, 'exception') as exception_patch: await cb.run('catname1') cm_get_patch.assert_called_once_with('catname1') exception_patch.assert_called_once_with( 'Unable to notify microservice with uuid %s as it is not found in the service registry', 'fakeid') post_patch.assert_has_calls([ call('http://saddress1:1/foglamp/change', data='{"category": "catname1", "items": null}', headers={'content-type': 'application/json'}), call('http://saddress2:2/foglamp/change', data='{"category": "catname1", "items": null}', headers={'content-type': 'application/json'}) ])
def test_get_with_and_filter(self, reset_singleton): configuration_manager_mock = MagicMock(spec=ConfigurationManager) i_reg = InterestRegistry(configuration_manager_mock) # register some interts id_1_1 = i_reg.register('muuid1', 'catname1') id_1_2 = i_reg.register('muuid1', 'catname2') id_2_1 = i_reg.register('muuid2', 'catname1') id_2_2 = i_reg.register('muuid2', 'catname2') id_3_3 = i_reg.register('muuid3', 'catname3') ret_val = i_reg.get(microservice_uuid='muuid1') assert len(ret_val) is 2 for i in ret_val: assert isinstance(i, InterestRecord) assert ret_val[0]._registration_id is id_1_1 assert ret_val[0]._microservice_uuid is 'muuid1' assert ret_val[0]._category_name is 'catname1' assert ret_val[1]._registration_id is id_1_2 assert ret_val[1]._microservice_uuid is 'muuid1' assert ret_val[1]._category_name is 'catname2' ret_val = i_reg.get(category_name='catname2') assert len(ret_val) is 2 for i in ret_val: assert isinstance(i, InterestRecord) assert ret_val[0]._registration_id is id_1_2 assert ret_val[0]._microservice_uuid is 'muuid1' assert ret_val[0]._category_name is 'catname2' assert ret_val[1]._registration_id is id_2_2 assert ret_val[1]._microservice_uuid is 'muuid2' assert ret_val[1]._category_name is 'catname2' ret_val = i_reg.get(category_name='catname2', microservice_uuid='muuid2') assert len(ret_val) is 1 for i in ret_val: assert isinstance(i, InterestRecord) assert ret_val[0]._registration_id is id_2_2 assert ret_val[0]._microservice_uuid is 'muuid2' assert ret_val[0]._category_name is 'catname2'
def test_unregister(self, reset_singleton): configuration_manager_mock = MagicMock(spec=ConfigurationManager) i_reg = InterestRegistry(configuration_manager_mock) # unregister when no items exists fake_uuid = 'bla' with pytest.raises(interest_registry_exceptions.DoesNotExist) as excinfo: ret_val = i_reg.unregister(fake_uuid) # register 2 interests, then unregister 1 id_1_1 = i_reg.register('muuid1', 'catname1') id_1_2 = i_reg.register('muuid1', 'catname2') ret_val = i_reg.unregister(id_1_1) assert ret_val == id_1_1 assert len(i_reg._registered_interests) is 1 assert isinstance(i_reg._registered_interests[0], InterestRecord) assert i_reg._registered_interests[0]._registration_id is id_1_2 assert i_reg._registered_interests[0]._microservice_uuid is 'muuid1' assert i_reg._registered_interests[0]._category_name is 'catname2' # unregister the second one ret_val = i_reg.unregister(id_1_2) assert ret_val == id_1_2 assert len(i_reg._registered_interests) is 0
def _start_core(cls, loop=None): _logger.info("start core") try: host = cls._host core_app = cls._make_core_app() core_server, core_server_handler = cls._start_app( loop, core_app, host, 0) address, cls.core_management_port = core_server.sockets[ 0].getsockname() _logger.info('Management API started on http://%s:%s', address, cls.core_management_port) # see http://<core_mgt_host>:<core_mgt_port>/foglamp/service for registered services _logger.info('Announce management API service') cls.management_announcer = ServiceAnnouncer( 'FogLAMP-Core', '_foglamp_core._tcp', cls.core_management_port, ['The FogLAMP Core REST API']) # start storage loop.run_until_complete(cls._start_storage(loop)) # get storage client loop.run_until_complete(cls._get_storage_client()) # obtain configuration manager and interest registry cls._configuration_manager = ConfigurationManager( cls._storage_client) cls._interest_registry = InterestRegistry( cls._configuration_manager) # start scheduler # see scheduler.py start def FIXME # scheduler on start will wait for storage service registration loop.run_until_complete(cls._start_scheduler()) # start monitor loop.run_until_complete(cls._start_service_monitor()) service_app = cls._make_app() loop.run_until_complete(cls.rest_api_config()) # print(cls.is_rest_server_tls_enabled, cls.rest_server_port) # ssl context ssl_ctx = None if not cls.is_rest_server_http_enabled: # ensure TLS 1.2 and SHA-256 # handle expiry? ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) cert, key = cls.get_certificates() _logger.info('Loading certificates %s and key %s', cert, key) ssl_ctx.load_cert_chain(cert, key) service_server, service_server_handler = cls._start_app( loop, service_app, host, cls.rest_server_port, ssl_ctx=ssl_ctx) address, service_server_port = service_server.sockets[ 0].getsockname() _logger.info( 'REST API Server started on %s://%s:%s', 'http' if cls.is_rest_server_http_enabled else 'https', address, service_server_port) cls.admin_announcer = ServiceAnnouncer( 'FogLAMP', '_foglamp._tcp', service_server_port, ['The FogLAMP Admin REST API']) cls.user_announcer = ServiceAnnouncer( 'FogLAMP', '_foglamp_app._tcp', service_server_port, ['The FogLAMP Application REST API']) # register core # a service with 2 web server instance, # registering now only when service_port is ready to listen the request # TODO: if ssl then register with protocol https cls._register_core(host, cls.core_management_port, service_server_port) print("(Press CTRL+C to quit)") try: loop.run_forever() except KeyboardInterrupt: pass finally: # graceful-shutdown # http://aiohttp.readthedocs.io/en/stable/web.html # TODO: FOGL-653 shutdown implementation # stop the scheduler loop.run_until_complete(cls._stop_scheduler()) # stop monitor loop.run_until_complete(cls.stop_service_monitor()) # stop the REST api (exposed on service port) service_server.close() loop.run_until_complete(service_server.wait_closed()) loop.run_until_complete(service_app.shutdown()) loop.run_until_complete(service_server_handler.shutdown(60.0)) loop.run_until_complete(service_app.cleanup()) # stop storage cls.stop_storage() # stop core management api core_server.close() loop.run_until_complete(core_server.wait_closed()) loop.run_until_complete(core_app.shutdown()) loop.run_until_complete(core_server_handler.shutdown(60.0)) loop.run_until_complete(core_app.cleanup()) loop.close() except (OSError, RuntimeError, TimeoutError) as e: sys.stderr.write('Error: ' + format(str(e)) + "\n") sys.exit(1) except Exception as e: sys.stderr.write('Error: ' + format(str(e)) + "\n") sys.exit(1)
async def test_run_good(self): storage_client_mock = MagicMock(spec=StorageClientAsync) cfg_mgr = ConfigurationManager(storage_client_mock) with patch.object(ServiceRegistry._logger, 'info') as log_info: s_id_1 = ServiceRegistry.register('sname1', 'Storage', 'saddress1', 1, 1, 'http') s_id_2 = ServiceRegistry.register('sname2', 'Southbound', 'saddress2', 2, 2, 'http') s_id_3 = ServiceRegistry.register('sname3', 'Southbound', 'saddress3', 3, 3, 'http') assert 3 == log_info.call_count i_reg = InterestRegistry(cfg_mgr) i_reg.register(s_id_1, 'catname1') i_reg.register(s_id_1, 'catname2') i_reg.register(s_id_2, 'catname1') i_reg.register(s_id_2, 'catname2') i_reg.register(s_id_3, 'catname3') # used to mock client session context manager async def async_mock(return_value): return return_value class AsyncSessionContextManagerMock(MagicMock): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) async def __aenter__(self): client_response_mock = MagicMock(spec=aiohttp.ClientResponse) client_response_mock.text.side_effect = [async_mock(None)] status_mock = Mock() status_mock.side_effect = [200] client_response_mock.status = status_mock() return client_response_mock async def __aexit__(self, *args): return None with patch.object(ConfigurationManager, 'get_category_all_items', return_value=async_mock(None)) as cm_get_patch: with patch.object(aiohttp.ClientSession, 'post', return_value=AsyncSessionContextManagerMock() ) as post_patch: await cb.run('catname1') post_patch.assert_has_calls([ call('http://saddress1:1/foglamp/change', data='{"category": "catname1", "items": null}', headers={'content-type': 'application/json'}), call('http://saddress2:2/foglamp/change', data='{"category": "catname1", "items": null}', headers={'content-type': 'application/json'}) ]) cm_get_patch.assert_called_once_with('catname1') with patch.object(ConfigurationManager, 'get_category_all_items', return_value=async_mock(None)) as cm_get_patch: with patch.object(aiohttp.ClientSession, 'post', return_value=AsyncSessionContextManagerMock() ) as post_patch: await cb.run('catname2') post_patch.assert_has_calls([ call('http://saddress1:1/foglamp/change', data='{"category": "catname2", "items": null}', headers={'content-type': 'application/json'}), call('http://saddress2:2/foglamp/change', data='{"category": "catname2", "items": null}', headers={'content-type': 'application/json'}) ]) cm_get_patch.assert_called_once_with('catname2') with patch.object(ConfigurationManager, 'get_category_all_items', return_value=async_mock(None)) as cm_get_patch: with patch.object(aiohttp.ClientSession, 'post', return_value=AsyncSessionContextManagerMock() ) as post_patch: await cb.run('catname3') post_patch.assert_called_once_with( 'http://saddress3:3/foglamp/change', data='{"category": "catname3", "items": null}', headers={'content-type': 'application/json'}) cm_get_patch.assert_called_once_with('catname3')
def _start_core(cls, loop=None): _logger.info("start core") try: host = cls._host cls.core_app = cls._make_core_app() cls.core_server, cls.core_server_handler = cls._start_app( loop, cls.core_app, host, 0) address, cls.core_management_port = cls.core_server.sockets[ 0].getsockname() _logger.info('Management API started on http://%s:%s', address, cls.core_management_port) # see http://<core_mgt_host>:<core_mgt_port>/foglamp/service for registered services # start storage loop.run_until_complete(cls._start_storage(loop)) # get storage client loop.run_until_complete(cls._get_storage_client()) # If readings table is empty, set last_object of all streams to 0 total_count_payload = payload_builder.PayloadBuilder().AGGREGATE( ["count", "*"]).ALIAS("aggregate", ("*", "count", "count")).payload() result = loop.run_until_complete( cls._storage_client_async.query_tbl_with_payload( 'readings', total_count_payload)) total_count = result['rows'][0]['count'] if (total_count == 0): _logger.info( "'foglamp.readings' table is empty, force reset of 'foglamp.streams' last_objects" ) payload = payload_builder.PayloadBuilder().SET( last_object=0, ts='now()').payload() loop.run_until_complete( cls._storage_client_async.update_tbl("streams", payload)) else: _logger.info( "'foglamp.readings' has " + str(total_count) + " rows, 'foglamp.streams' last_objects reset is not required" ) # obtain configuration manager and interest registry cls._configuration_manager = ConfigurationManager( cls._storage_client_async) cls._interest_registry = InterestRegistry( cls._configuration_manager) # start scheduler # see scheduler.py start def FIXME # scheduler on start will wait for storage service registration loop.run_until_complete(cls._start_scheduler()) # start monitor loop.run_until_complete(cls._start_service_monitor()) loop.run_until_complete(cls.rest_api_config()) cls.service_app = cls._make_app(auth_required=cls.is_auth_required) # ssl context ssl_ctx = None if not cls.is_rest_server_http_enabled: # ensure TLS 1.2 and SHA-256 # handle expiry? ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) cert, key = cls.get_certificates() _logger.info('Loading certificates %s and key %s', cert, key) ssl_ctx.load_cert_chain(cert, key) # Get the service data and advertise the management port of the core # to allow other microservices to find FogLAMP loop.run_until_complete(cls.service_config()) _logger.info('Announce management API service') cls.management_announcer = ServiceAnnouncer( 'core.{}'.format(cls._service_name), cls._MANAGEMENT_SERVICE, cls.core_management_port, ['The FogLAMP Core REST API']) cls.service_server, cls.service_server_handler = cls._start_app( loop, cls.service_app, host, cls.rest_server_port, ssl_ctx=ssl_ctx) address, service_server_port = cls.service_server.sockets[ 0].getsockname() # Write PID file with REST API details cls._write_pid(address, service_server_port) _logger.info( 'REST API Server started on %s://%s:%s', 'http' if cls.is_rest_server_http_enabled else 'https', address, service_server_port) # All services are up so now we can advertise the Admin and User REST API's cls.admin_announcer = ServiceAnnouncer(cls._service_name, cls._ADMIN_API_SERVICE, service_server_port, [cls._service_description]) cls.user_announcer = ServiceAnnouncer(cls._service_name, cls._USER_API_SERVICE, service_server_port, [cls._service_description]) # register core # a service with 2 web server instance, # registering now only when service_port is ready to listen the request # TODO: if ssl then register with protocol https cls._register_core(host, cls.core_management_port, service_server_port) # Everything is complete in the startup sequence, write the audit log entry cls._audit = AuditLogger(cls._storage_client_async) loop.run_until_complete(cls._audit.information('START', None)) loop.run_forever() except (OSError, RuntimeError, TimeoutError) as e: sys.stderr.write('Error: ' + format(str(e)) + "\n") sys.exit(1) except Exception as e: sys.stderr.write('Error: ' + format(str(e)) + "\n") sys.exit(1)