def stop_service_handler(url: URL, **kwargs): service_uuid = url.parts[-1] required_query_parameter = "save_state" if required_query_parameter not in kwargs["params"]: return CallbackResult( payload={ "error": f"stop_service endpoint was called without query parameter {required_query_parameter} {url}" }, status=500, ) try: service = next( s for s in _fake_running_services if s["service_uuid"] == service_uuid ) service["service_state"] = "complete" except StopIteration: return CallbackResult( payload={"error": "service not found"}, status=404 ) return CallbackResult(status=204)
def get_stacks_cb(url, **kwargs) -> CallbackResult: if not _check_auth(**kwargs): return CallbackResult(status=401) return CallbackResult( status=200, payload=portainer_stacks, )
def get_docker_swarm_cb(url, **kwargs) -> CallbackResult: if not _check_auth(**kwargs): return CallbackResult(status=401) # returns the docker API /swarm endpoint return CallbackResult( status=200, payload={"ID": "abajmipo7b4xz5ip2nrla6b11"}, )
def get_endpoints_cb(url, **kwargs) -> CallbackResult: if not _check_auth(**kwargs): return CallbackResult(status=401) return CallbackResult( status=200, payload=[ {"Name": valid_config["main"]["portainer"][0]["stack_name"], "Id": 1} ], )
def get_download_link_cb(url: URL, **kwargs) -> CallbackResult: file_id = url.path.rsplit("/files/")[1] return CallbackResult(status=200, payload={"data": { "link": f"file://{file_id}" }})
def get_service_by_key_version_handler(url: URL, **kwargs): service_key, service_version = url.parts[-2:] # TODO: validate against schema? # TODO: improve API: does it make sense to return multiple services services = [ s for s in _fake_project_services if s["key"] == service_key and s["version"] == service_version ] if services: return CallbackResult(payload={"data": services}, status=200) return CallbackResult( payload={ "error": f"Service {service_key}:{service_version} not found in project" }, status=404, )
def create_stack_cb(url, **kwargs) -> CallbackResult: if not _check_auth(**kwargs): return CallbackResult(status=401) if "json" not in kwargs: return CallbackResult(status=400) body = kwargs["json"] return CallbackResult( status=200, payload={ "SwarmID": body["SwarmID"], "Name": body["Name"], "EndpointID": url.query["endpointId"], "Type": url.query["type"], "Id": randint(1, 10), }, )
def start_service_handler(url: URL, **kwargs): assert int(url.query["user_id"]) == user_id assert url.query["project_id"] == project_id service_uuid = url.query["service_uuid"] if any(s["service_uuid"] == service_uuid for s in _get_running()): return CallbackResult( payload={ "error": f"A service with the same uuid {service_uuid} already running exists" }, status=409, ) service_key = url.query["service_key"] service_tag = url.query.get("service_tag") try: service = next( s for s in _fake_project_services if s["key"] == service_key and s["version"] == service_tag) except StopIteration: return CallbackResult( payload={ "error": f"Service {service_key}:{service_tag} not found in project" }, status=404, ) starting_service = fake_running_service_model( service_key=service["key"], service_version=service["version"], service_basepath=url.query.get( "service_basepath", "" ), # predefined basepath for the backend service otherwise uses root service_state="starting", # let's assume it pulls very fast :-) ) _fake_running_services.append(starting_service) return CallbackResult(payload={"data": starting_service}, status=201)
def creation_cb(url, **kwargs): assert "json" in kwargs, f"missing body in call to {url}" body = kwargs["json"] for param in ["user_id", "project_id"]: assert param in body, f"{param} is missing from body: {body}" state = (RunningState.PUBLISHED if "start_pipeline" in body and body["start_pipeline"] else RunningState.NOT_STARTED) return CallbackResult(status=201, payload={ "id": kwargs["json"]["project_id"], "state": state })
def creation_cb(url, **kwargs) -> CallbackResult: assert "json" in kwargs, f"missing body in call to {url}" body = kwargs["json"] for param in ["user_id", "project_id"]: assert param in body, f"{param} is missing from body: {body}" state = (RunningState.PUBLISHED if "start_pipeline" in body and body["start_pipeline"] else RunningState.NOT_STARTED) pipeline: Dict[str, List[str]] = FULL_PROJECT_PIPELINE_ADJACENCY node_states = FULL_PROJECT_NODE_STATES if body.get("subgraph"): # create some fake adjacency list pipeline = {} node_states = {} for node_id in body.get("subgraph"): pipeline[node_id] = [ "62237c33-8d6c-4709-aa92-c3cf693dd6d2", "0bdf824f-57cb-4e38-949e-fd12c184f000", ] node_states[node_id] = { "state": { "modified": True, "dependencies": [] } } node_states["62237c33-8d6c-4709-aa92-c3cf693dd6d2"] = { "modified": True, "dependencies": ["2f493631-30b4-4ad8-90f2-a74e4b46fe73"], } node_states["0bdf824f-57cb-4e38-949e-fd12c184f000"] = { "modified": True, "dependencies": [ "2f493631-30b4-4ad8-90f2-a74e4b46fe73", "62237c33-8d6c-4709-aa92-c3cf693dd6d2", ], } return CallbackResult( status=201, payload={ "id": kwargs["json"]["project_id"], "state": state, "pipeline_details": { "adjacency_list": pipeline, "node_states": node_states, }, }, )
def get_computation_cb(url, **kwargs) -> CallbackResult: state = RunningState.NOT_STARTED pipeline: Dict[str, List[str]] = FULL_PROJECT_PIPELINE_ADJACENCY node_states = FULL_PROJECT_NODE_STATES return CallbackResult( status=202, payload={ "id": Path(url.path).name, "state": state, "pipeline_details": { "adjacency_list": pipeline, "node_states": node_states, }, }, )
async def _start_get_stop_services( client, push_services, user_id, project_id, api_version_prefix: str, save_state: Optional[bool], expected_save_state_call: bool, mocker, ): params = {} web_response = await client.post( f"/{api_version_prefix}/running_interactive_services", params=params) assert web_response.status == 400 params = { "user_id": "None", "project_id": "None", "service_uuid": "sdlfkj4", "service_key": "None", "service_tag": "None", # optional "service_basepath": "None", # optional } web_response = await client.post( f"/{api_version_prefix}/running_interactive_services", params=params) data = await web_response.json() assert web_response.status == 400, data params["service_key"] = "simcore/services/comp/somfunkyname-nhsd" params["service_tag"] = "1.2.3" web_response = await client.post( f"/{api_version_prefix}/running_interactive_services", params=params) data = await web_response.json() assert web_response.status == 404, data created_services = await push_services(0, 2) assert len(created_services) == 2 for created_service in created_services: service_description = created_service["service_description"] params["user_id"] = user_id params["project_id"] = project_id params["service_key"] = service_description["key"] params["service_tag"] = service_description["version"] service_port = created_service["internal_port"] service_entry_point = created_service["entry_point"] params["service_basepath"] = "/i/am/a/basepath" params["service_uuid"] = str(uuid.uuid4()) # start the service web_response = await client.post( f"/{api_version_prefix}/running_interactive_services", params=params) assert web_response.status == 201 assert web_response.content_type == "application/json" running_service_enveloped = await web_response.json() assert isinstance(running_service_enveloped["data"], dict) assert all(k in running_service_enveloped["data"] for k in [ "service_uuid", "service_key", "service_version", "published_port", "entry_point", "service_host", "service_port", "service_basepath", ]) assert (running_service_enveloped["data"]["service_uuid"] == params["service_uuid"]) assert running_service_enveloped["data"]["service_key"] == params[ "service_key"] assert (running_service_enveloped["data"]["service_version"] == params["service_tag"]) assert running_service_enveloped["data"][ "service_port"] == service_port service_published_port = running_service_enveloped["data"][ "published_port"] assert not service_published_port assert service_entry_point == running_service_enveloped["data"][ "entry_point"] service_host = running_service_enveloped["data"]["service_host"] assert service_host == f"test_{params['service_uuid']}" service_basepath = running_service_enveloped["data"][ "service_basepath"] assert service_basepath == params["service_basepath"] # get the service web_response = await client.request( "GET", f"/{api_version_prefix}/running_interactive_services/{params['service_uuid']}", ) assert web_response.status == 200 text = await web_response.text() assert web_response.content_type == "application/json", text running_service_enveloped = await web_response.json() assert isinstance(running_service_enveloped["data"], dict) assert all(k in running_service_enveloped["data"] for k in [ "service_uuid", "service_key", "service_version", "published_port", "entry_point", ]) assert (running_service_enveloped["data"]["service_uuid"] == params["service_uuid"]) assert running_service_enveloped["data"]["service_key"] == params[ "service_key"] assert (running_service_enveloped["data"]["service_version"] == params["service_tag"]) assert (running_service_enveloped["data"]["published_port"] == service_published_port) assert running_service_enveloped["data"][ "entry_point"] == service_entry_point assert running_service_enveloped["data"][ "service_host"] == service_host assert running_service_enveloped["data"][ "service_port"] == service_port assert running_service_enveloped["data"][ "service_basepath"] == service_basepath # stop the service query_params = {} if save_state: query_params.update( {"save_state": "true" if save_state else "false"}) mocked_save_state_cb = mocker.MagicMock( return_value=CallbackResult(status=200, payload={})) PASSTHROUGH_REQUESTS_PREFIXES = [ "http://127.0.0.1", "http://localhost", "unix://", # docker engine "ws://", # websockets ] with aioresponses(passthrough=PASSTHROUGH_REQUESTS_PREFIXES) as mock: # POST /http://service_host:service_port service_basepath/state ------------------------------------------------- mock.post( f"http://{service_host}:{service_port}{service_basepath}/state", status=200, callback=mocked_save_state_cb, ) web_response = await client.delete( f"/{api_version_prefix}/running_interactive_services/{params['service_uuid']}", params=query_params, ) if expected_save_state_call: mocked_save_state_cb.assert_called_once() text = await web_response.text() assert web_response.status == 204, text assert web_response.content_type == "application/json" data = await web_response.json() assert data is None
def get_running_interactive_services_handler(url: URL, **kwargs): assert int(url.query.get("user_id", 0)) in (user_id, 0) assert url.query.get("project_id") in (project_id, None) return CallbackResult(payload={"data": _get_running()}, status=200)