def test_service_configuration(self): invalid_config = {"active": True, SERVICE_URL_PARAM: ""} valid_config = { "active": True, SERVICE_URL_PARAM: "https://service.domain", SERVICE_WORKSPACE_DIR_PARAM: "/" } # Should raise if the config does not include a required param with pytest.raises(ServiceConfigurationException): GoodService(ServiceFactory().settings, "GoodService", **invalid_config) # Should raise if the service does not define its required params with pytest.raises(NotImplementedError): BadService(ServiceFactory().settings, "BadService", **valid_config) # Should raise if a service defines an invalid param with pytest.raises(Exception): BadParamService("BadParamService", **valid_config) svc = GoodService(ServiceFactory().settings, "GoodService", **valid_config) assert getattr(svc, SERVICE_URL_PARAM) == valid_config[SERVICE_URL_PARAM] assert getattr(svc, SERVICE_WORKSPACE_DIR_PARAM ) == valid_config[SERVICE_WORKSPACE_DIR_PARAM]
def test_service_factory(self): # Test singleton inst1 = ServiceFactory().get_service("Magpie") inst2 = ServiceFactory().get_service("Magpie") assert inst1 is inst2 assert len(ServiceFactory().services) == 1 assert ServiceFactory().services["Magpie"] is inst1 # Test services config active_services = [ service.name for service in ServiceFactory().get_active_services() ] # Every active service should be in test data for service in active_services: assert service in TestServiceFactory.test_data["services"] assert TestServiceFactory.test_data["services"][service]["active"] # Every activated test service should be in the active services for test_service, config in TestServiceFactory.test_data[ "services"].items(): if config["active"]: assert test_service in active_services else: assert test_service not in active_services # Prioritize services should appear in the proper order for idx, svc in enumerate(self.priority): assert active_services[idx] == svc
def test_webhooks(self): with contextlib.ExitStack() as stack: stack.enter_context( mock.patch("cowbird.services.impl.magpie.Magpie", side_effect=utils.MockMagpieService)) data = { "event": "created", "user_name": "test_user", "callback_url": "http://magpie.domain.ca/tmp/109e1d0d-e27c-4601-9d45-984c9b61ebff" } resp = utils.test_request(self.app, "POST", "/webhooks/users", json=data) utils.check_response_basic_info(resp, 200, expected_method="POST") utils.check_response_basic_info(resp) magpie = ServiceFactory().get_service("Magpie") assert len(magpie.json()["event_users"]) == 1 assert magpie.json()["event_users"][0] == data["user_name"] data["event"] = "deleted" data.pop("callback_url") resp = utils.test_request(self.app, "POST", "/webhooks/users", json=data) utils.check_response_basic_info(resp, 200, expected_method="POST") assert len(magpie.json()["event_users"]) == 0 data = { "event": "created", "service_name": "string", "resource_id": "string", "resource_full_name": "thredds/birdhouse/file.nc", "name": "read", "access": "allow", "scope": "recursive", "user": "******", "group": "string" } resp = utils.test_request(self.app, "POST", "/webhooks/permissions", json=data) utils.check_response_basic_info(resp, 200, expected_method="POST") magpie = ServiceFactory().get_service("Magpie") assert len(magpie.json()["event_perms"]) == 1 assert magpie.json( )["event_perms"][0] == data["resource_full_name"] data["event"] = "deleted" resp = utils.test_request(self.app, "POST", "/webhooks/permissions", json=data) utils.check_response_basic_info(resp, 200, expected_method="POST") assert len(magpie.json()["event_perms"]) == 0
def dispatch(svc_fct): exceptions = [] event_name = inspect.getsource(svc_fct).split(":")[1].strip() for svc in ServiceFactory().get_active_services(): # Allow every service to be notified even if one of them throw an error try: LOGGER.info("Dispatching event [%s] for service [%s]", event_name, svc.name) svc_fct(svc) except Exception as exception: # noqa exceptions.append(exception) LOGGER.error("Exception raised while handling event [%s] for service [%s] : [%r]", event_name, svc.name, exception) if exceptions: raise Exception(exceptions)
def __init__(self, services, # type: SyncPointServicesType mapping # type: SyncPointMappingType ): # type: (...) -> None """ Init the sync point, holding services with their respective resources root and how access are mapped between them. @param services: Dict, where the service is the key and its resources root is the value @param mapping: List of dict where the service is the key and an access list is the value """ available_services = ServiceFactory().services_cfg.keys() # Make sure that only active services are used self.services = {svc: svc_cfg for svc, svc_cfg in services.items() if svc in available_services} self.mapping = [{svc: perms for svc, perms in mapping_pt.items() if svc in available_services} for mapping_pt in mapping]
def sync(self, perm_operation, permission): # type: (Callable[Permission], Permission) -> None """ Create or delete the same permission on each service sharing the same resource. @param perm_operation Magpie create_permission or delete_permission function @param permission Permission to synchronize with others services """ res_common_part_idx = len(self.services[permission.service_name]) for svc, perm_name in self.find_match(permission): new_permission = copy.copy(permission) new_permission.service_name = svc new_permission.resource_full_name = self.services[svc] + \ permission.resource_full_name[res_common_part_idx:] new_permission.resource_id = ServiceFactory().get_service(svc).get_resource_id( new_permission.resource_full_name) new_permission.name = perm_name perm_operation(new_permission)
def get_instance(): """ Return the Geoserver singleton instance from the class name used to retrieve the FSMonitor from the DB. """ return ServiceFactory().get_service("Geoserver")
def test_register_unregister_monitor(): with tempfile.TemporaryDirectory() as tmpdir: test_file = os.path.join(tmpdir, "testfile") test_subdir = os.path.join(tmpdir, "subdir") test_subdir_file = os.path.join(test_subdir, "test_subdir_file") mv_test_file = os.path.join(test_subdir, "moved_testfile") mv_test_subdir_file = os.path.join(tmpdir, "moved_test_subdir_file") os.mkdir(test_subdir) # Test registering directly a callback instance mon = TestMonitor() internal_mon = Monitoring().register(tmpdir, False, mon) assert internal_mon.callback_instance == mon # Test registering a callback via class name internal_mon2 = Monitoring().register(tmpdir, True, TestMonitor2) mon2 = internal_mon2.callback_instance internal_mon3 = Monitoring().register(test_subdir, False, TestMonitor) mon3 = internal_mon3.callback_instance assert isinstance(mon2, TestMonitor2) assert isinstance(mon3, TestMonitor) # Test collision when 2 monitors are registered using the same path and class name assert not internal_mon3.recursive internal_mon4 = Monitoring().register(test_subdir, True, TestMonitor) assert internal_mon4 is internal_mon3 assert internal_mon3.recursive # Recursive should take precedence when a collision occurs # monitors first level is distinct path to monitor : (tmp_dir and test_subdir) assert len(Monitoring().monitors) == 2 # monitors second level is distinct callback, for tmpdir : (TestMonitor and TestMonitor2) assert len(Monitoring().monitors[tmpdir]) == 2 # Do some io operations that should be picked by the monitors file_io(test_file, mv_test_file) file_io(test_subdir_file, mv_test_subdir_file) sleep(1) # Root dir non-recursive assert len(mon.created) == 2 assert mon.created[0] == test_file assert mon.created[1] == mv_test_subdir_file assert mon.created == mon.deleted assert sorted(set(mon.modified)) == [tmpdir, test_file] # Root dir recursive assert len(mon2.created) == 4 assert mon2.created[0] == test_file assert mon2.created[1] == mv_test_file assert mon2.created[2] == test_subdir_file assert mon2.created[3] == mv_test_subdir_file assert mon2.created == mon2.deleted assert sorted(set(mon2.modified)) == [tmpdir, test_subdir, test_subdir_file, test_file] # Subdir assert len(mon3.created) == 2 assert mon3.created[0] == mv_test_file assert mon3.created[1] == test_subdir_file assert mon3.created == mon3.deleted assert sorted(set(mon3.modified)) == [test_subdir, test_subdir_file] # Validate cleanup Monitoring().unregister(tmpdir, mon) Monitoring().unregister(tmpdir, mon2) assert not Monitoring().unregister(test_subdir, mon2) # Here we try to unregister a path with a bad class type Monitoring().unregister(test_subdir, mon3) # Here we have the good class type assert len(Monitoring().monitors) == 0 assert not Monitoring().unregister(tmpdir, mon) # Test registering a callback via a qualified class name string catalog_mon = Monitoring().register(tmpdir, False, "cowbird.services.impl.catalog.Catalog").callback_instance assert catalog_mon == ServiceFactory().get_service("Catalog")
def test_sync(self): """ This test parses the sync config and checks that when a permission is created in the `PermissionSynchronizer` the proper permission is created for every synchronized service. The `PermissionSynchronizer` `create_permission` function will first find if the applied permission exists in the config. Then for every configured service it will obtain the equivalent permission for this service and apply it to the mocked Magpie service. The `outbound_perm` dict of the mocked Magpie service is then checked to validate that every permission that should have been created in Magpie exists. """ with contextlib.ExitStack() as stack: stack.enter_context(mock.patch("cowbird.services.impl.magpie.Magpie", side_effect=utils.MockMagpieService)) stack.enter_context(mock.patch("cowbird.services.impl.geoserver.Geoserver", side_effect=utils.MockAnyService)) stack.enter_context(mock.patch("cowbird.services.impl.thredds.Thredds", side_effect=utils.MockAnyService)) magpie = ServiceFactory().get_service("Magpie") resource_name = "resource1" # Loop over every service having a permission that must be synchronized to another one for svc in self.test_services: for perm_name in self.sync_perm_name[svc]: # Create the permission for this service that would be provided by `Magpie` as a hook permission = Permission( service_name=svc, resource_id="0", resource_full_name=self.res_root[svc] + resource_name, name=perm_name, access="string1", scope="string2", user="******") # Apply this permission to the synchronizer (this is the function that is tested!) PermissionSynchronizer(magpie).create_permission(permission) # `magpie`, which is mocked, will store every permission request that should have been done to the # Magpie service in the `outbound_perms` dict. assert len(magpie.json()["outbound_perms"]) == len(self.sync_perm_name[self.mapped_service[svc]]) # Validate that the mocked `magpie` instance has received a permission request for every mapped # permission for idx, mapped_perm_name in enumerate(self.sync_perm_name[self.mapped_service[svc]]): outbound_perm = magpie.json()["outbound_perms"][idx] assert outbound_perm.service_name == self.mapped_service[svc] assert outbound_perm.resource_id == utils.MockAnyService.ResourceId assert outbound_perm.resource_full_name == \ self.res_root[self.mapped_service[svc]] + resource_name assert outbound_perm.name == mapped_perm_name assert outbound_perm.access == permission.access assert outbound_perm.scope == permission.scope assert outbound_perm.user == permission.user # This is the second function being tested which should make remove permission requests to `magpie` # for every mapped permission PermissionSynchronizer(magpie).delete_permission(permission) # Again the mocked `magpie` instance should remove every permission from its `outbound_perms` rather # than making the remove permission request to the Magpie service. assert len(magpie.json()["outbound_perms"]) == 0
def get_instance(): """ Return the Catalog singleton instance from the class name used to retrieve the FSMonitor from the DB. """ from cowbird.services.service_factory import ServiceFactory return ServiceFactory().get_service("Catalog")
def get_services(container): # type: (AnySettingsContainer) -> List[Service] """ Obtains the services managed by the application. """ return ServiceFactory().get_active_services()