def set_workflow_status(workflow_id_or_name): # noqa r"""Set workflow status. --- put: summary: Set workflow status. description: >- This resource sets the status of workflow. operationId: set_workflow_status produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string - name: status in: query description: Required. New status. required: true type: string enum: - start - stop - deleted - name: parameters in: body description: >- Optional. Additional input parameters and operational options for workflow execution. Possible parameters are `CACHE=on/off`, passed to disable caching of results in serial workflows, `all_runs=True/False` deletes all runs of a given workflow if status is set to deleted, `workspace=True/False` which deletes the workspace of a workflow and finally `hard_delete=True` which removes completely the workflow data from the database and the workspace from the shared filesystem. required: false schema: type: object properties: CACHE: type: string all_runs: type: boolean workspace: type: boolean hard_delete: type: boolean responses: 200: description: >- Request succeeded. Info about workflow, including the status is returned. schema: type: object properties: message: type: string workflow_id: type: string workflow_name: type: string status: type: string user: type: string examples: application/json: { "message": "Workflow successfully launched", "workflow_id": "256b25f4-4cfb-4684-b7a8-73872ef455a1", "workflow_name": "mytest-1", "status": "running", "user": "******" } 400: description: >- Request failed. The incoming data specification seems malformed. examples: application/json: { "message": "Malformed request." } 403: description: >- Request failed. User is not allowed to access workflow. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 is not allowed to access workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1" } 404: description: >- Request failed. Either User or Workflow does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist" } 409: description: >- Request failed. The workflow could not be started due to a conflict. examples: application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 could not be started because it is already running." } 500: description: >- Request failed. Internal controller error. 501: description: >- Request failed. The specified status change is not implemented. examples: application/json: { "message": "Status resume is not supported yet." } 502: description: >- Request failed. Connection to a third party system has failed. examples: application/json: { "message": "Connection to database timed out, please retry." } """ try: user_uuid = request.args["user"] workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) status = request.args.get("status") if not (status in STATUSES): return ( jsonify({ "message": "Status {0} is not one of: {1}".format( status, ", ".join(STATUSES)) }), 400, ) if not str(workflow.owner_id) == user_uuid: return ( jsonify({ "message": "User {} is not allowed to access workflow {}".format( user_uuid, workflow_id_or_name) }), 403, ) parameters = {} if request.json: parameters = request.json if status == START: start_workflow(workflow, parameters) return ( jsonify({ "message": "Workflow successfully launched", "workflow_id": str(workflow.id_), "workflow_name": get_workflow_name(workflow), "status": workflow.status.name, "user": str(workflow.owner_id), }), 200, ) elif status == DELETED: all_runs = True if request.json.get("all_runs") else False hard_delete = True if request.json.get("hard_delete") else False workspace = True if hard_delete or request.json.get( "workspace") else False return delete_workflow(workflow, all_runs, hard_delete, workspace) if status == STOP: stop_workflow(workflow) return ( jsonify({ "message": "Workflow successfully stopped", "workflow_id": workflow.id_, "workflow_name": get_workflow_name(workflow), "status": workflow.status.name, "user": str(workflow.owner_id), }), 200, ) else: raise NotImplementedError( "Status {} is not supported yet".format(status)) except ValueError: return ( jsonify({ "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) }), 404, ) except REANAWorkflowControllerError as e: return jsonify({"message": str(e)}), 409 except REANAWorkflowStatusError as e: return jsonify({"message": str(e)}), 404 except KeyError as e: return jsonify({"message": str(e)}), 400 except NotImplementedError as e: return jsonify({"message": str(e)}), 501 except REANAExternalCallError as e: return jsonify({"message": str(e)}), 502 except Exception as e: return jsonify({"message": str(e)}), 500
def get_workflow_logs(workflow_id_or_name): # noqa r"""Get workflow logs from a workflow engine. --- get: summary: Returns logs of a specific workflow from a workflow engine. description: >- This resource is expecting a workflow UUID and a filename to return its outputs. operationId: get_workflow_logs produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string - name: steps in: body description: Steps of a workflow. required: false schema: type: array description: List of step names to get logs for. items: type: string description: Step name. responses: 200: description: >- Request succeeded. Info about workflow, including the status is returned. schema: type: object properties: workflow_id: type: string workflow_name: type: string logs: type: string user: type: string examples: application/json: { "workflow_id": "256b25f4-4cfb-4684-b7a8-73872ef455a1", "workflow_name": "mytest-1", "logs": "{'workflow_logs': string, 'job_logs': { '256b25f4-4cfb-4684-b7a8-73872ef455a2': string, '256b25f4-4cfb-4684-b7a8-73872ef455a3': string, }, 'engine_specific': object, }", "user": "******" } 400: description: >- Request failed. The incoming data specification seems malformed. 404: description: >- Request failed. User does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } 500: description: >- Request failed. Internal controller error. examples: application/json: { "message": "Internal workflow controller error." } """ try: user_uuid = request.args["user"] workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) if not str(workflow.owner_id) == user_uuid: return ( jsonify({ "message": "User {} is not allowed to access workflow {}".format( user_uuid, workflow_id_or_name) }), 403, ) steps = None if request.json: steps = request.json if steps: workflow_logs = { "workflow_logs": None, "job_logs": build_workflow_logs(workflow, steps), "engine_specific": None, } else: workflow_logs = { "workflow_logs": workflow.logs, "job_logs": build_workflow_logs(workflow), "engine_specific": workflow.engine_specific, } return ( jsonify({ "workflow_id": workflow.id_, "workflow_name": get_workflow_name(workflow), "logs": json.dumps(workflow_logs), "user": user_uuid, }), 200, ) except ValueError: return ( jsonify({ "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) }), 404, ) except KeyError as e: return jsonify({"message": str(e)}), 400 except Exception as e: return jsonify({"message": str(e)}), 500
def get_workflow_diff(workflow_id_or_name_a, workflow_id_or_name_b): # noqa r"""Get differences between two workflows. --- get: summary: Get diff between two workflows. description: >- This resource shows the differences between the assets of two workflows. Resource is expecting two workflow UUIDs or names. operationId: get_workflow_diff produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name_a in: path description: Required. Analysis UUID or name of the first workflow. required: true type: string - name: workflow_id_or_name_b in: path description: Required. Analysis UUID or name of the second workflow. required: true type: string - name: brief in: query description: Optional flag. If set, file contents are examined. required: false type: boolean default: false - name: context_lines in: query description: Optional parameter. Sets number of context lines for workspace diff output. required: false type: string default: '5' responses: 200: description: >- Request succeeded. Info about a workflow, including the status is returned. schema: type: object properties: reana_specification: type: string workspace_listing: type: string examples: application/json: { "reana_specification": ["- nevents: 100000\n+ nevents: 200000"], "workspace_listing": {"Only in workspace a: code"} } 400: description: >- Request failed. The incoming payload seems malformed. examples: application/json: { "message": "Malformed request." } 403: description: >- Request failed. User is not allowed to access workflow. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 is not allowed to access workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1" } 404: description: >- Request failed. Either user or workflow does not exist. examples: application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist." } 500: description: >- Request failed. Internal controller error. """ try: user_uuid = request.args["user"] brief = json.loads(request.args.get("brief", "false").lower()) context_lines = request.args.get("context_lines", 5) workflow_a_exists = False workflow_a = _get_workflow_with_uuid_or_name(workflow_id_or_name_a, user_uuid) workflow_a_exists = True workflow_b = _get_workflow_with_uuid_or_name(workflow_id_or_name_b, user_uuid) if not workflow_id_or_name_a or not workflow_id_or_name_b: raise ValueError("Workflow id or name is not supplied") specification_diff = get_specification_diff(workflow_a, workflow_b) try: workspace_diff = get_workspace_diff(workflow_a, workflow_b, brief, context_lines) except ValueError as e: workspace_diff = str(e) response = { "reana_specification": json.dumps(specification_diff), "workspace_listing": json.dumps(workspace_diff), } return jsonify(response) except REANAWorkflowControllerError as e: return jsonify({"message": str(e)}), 409 except ValueError as e: wrong_workflow = (workflow_id_or_name_b if workflow_a_exists else workflow_id_or_name_a) return ( jsonify({ "message": "Workflow {0} does not exist.".format(wrong_workflow) }), 404, ) except KeyError as e: return jsonify({"message": str(e)}), 400 except ValueError as e: return jsonify({"message": str(e)}), 400 except json.JSONDecodeError: return jsonify({"message": "Your request contains not valid JSON."}), 400 except Exception as e: return jsonify({"message": str(e)}), 500
def get_workflow_status(workflow_id_or_name): # noqa r"""Get workflow status. --- get: summary: Get workflow status. description: >- This resource reports the status of workflow. operationId: get_workflow_status produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string responses: 200: description: >- Request succeeded. Info about workflow, including the status is returned. schema: type: object properties: id: type: string name: type: string created: type: string status: type: string user: type: string logs: type: string progress: type: object examples: application/json: { "id": "256b25f4-4cfb-4684-b7a8-73872ef455a1", "name": "mytest-1", "created": "2018-06-13T09:47:35.66097", "status": "running", "user": "******" } 400: description: >- Request failed. The incoming data specification seems malformed. examples: application/json: { "message": "Malformed request." } 403: description: >- Request failed. User is not allowed to access workflow. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 is not allowed to access workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1" } 404: description: >- Request failed. Either User or Workflow does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist" } 500: description: >- Request failed. Internal controller error. """ try: user_uuid = request.args["user"] workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) workflow_logs = build_workflow_logs(workflow) if not str(workflow.owner_id) == user_uuid: return ( jsonify({ "message": "User {} is not allowed to access workflow {}".format( user_uuid, workflow_id_or_name) }), 403, ) return ( jsonify({ "id": workflow.id_, "name": get_workflow_name(workflow), "created": workflow.created.strftime(WORKFLOW_TIME_FORMAT), "status": workflow.status.name, "progress": get_workflow_progress(workflow), "user": user_uuid, "logs": json.dumps(workflow_logs), }), 200, ) except ValueError: return ( jsonify({ "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) }), 404, ) except KeyError as e: return jsonify({"message": str(e)}), 400 except Exception as e: return jsonify({"message": str(e)}), 500
def move_files(workflow_id_or_name): # noqa r"""Move files within workspace. --- put: summary: Move files within workspace. description: >- This resource moves files within the workspace. Resource is expecting a workflow UUID. operationId: move_files consumes: - application/json produces: - application/json parameters: - name: workflow_id_or_name in: path description: Required. Analysis UUID or name. required: true type: string - name: source in: query description: Required. Source file(s). required: true type: string - name: target in: query description: Required. Target file(s). required: true type: string - name: user in: query description: Required. UUID of workflow owner.. required: true type: string responses: 200: description: >- Request succeeded. Message about successfully moved files is returned. schema: type: object properties: message: type: string workflow_id: type: string workflow_name: type: string examples: application/json: { "message": "Files were successfully moved", "workflow_id": "256b25f4-4cfb-4684-b7a8-73872ef455a1", "workflow_name": "mytest.1", } 400: description: >- Request failed. The incoming payload seems malformed. examples: application/json: { "message": "Malformed request." } 403: description: >- Request failed. User is not allowed to access workflow. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 is not allowed to access workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1" } 404: description: >- Request failed. Either User or Workflow does not exist. examples: application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist" } 500: description: >- Request failed. Internal controller error. """ try: user_uuid = request.args["user"] workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) source = request.args["source"] target = request.args["target"] if workflow.status == "running": return ( jsonify({"message": "Workflow is running, files can not be " "moved"}), 400, ) if not str(workflow.owner_id) == user_uuid: return ( jsonify( { "message": "User {} is not allowed to access workflow {}".format( user_uuid, workflow_id_or_name ) } ), 403, ) mv_files(source, target, workflow) message = "File(s) {} were successfully moved".format(source) return ( jsonify( { "message": message, "workflow_id": workflow.id_, "workflow_name": get_workflow_name(workflow), } ), 200, ) except ValueError: return ( jsonify( { "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) } ), 404, ) except REANAWorkflowControllerError as e: return jsonify({"message": str(e)}), 409 except KeyError as e: return jsonify({"message": str(e)}), 400 except NotImplementedError as e: return jsonify({"message": str(e)}), 501 except Exception as e: return jsonify({"message": str(e)}), 500
def get_workflow_parameters(workflow_id_or_name): # noqa r"""Get workflow input parameters. --- get: summary: Get workflow parameters. description: >- This resource reports the input parameters of workflow. operationId: get_workflow_parameters produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string responses: 200: description: >- Request succeeded. Workflow input parameters, including the status are returned. schema: type: object properties: id: type: string name: type: string type: type: string parameters: type: object examples: application/json: { 'id': 'dd4e93cf-e6d0-4714-a601-301ed97eec60', 'name': 'workflow.24', 'type': 'serial', 'parameters': {'helloworld': 'code/helloworld.py', 'inputfile': 'data/names.txt', 'outputfile': 'results/greetings.txt', 'sleeptime': 2} } 400: description: >- Request failed. The incoming data specification seems malformed. examples: application/json: { "message": "Malformed request." } 403: description: >- Request failed. User is not allowed to access workflow. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 is not allowed to access workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1" } 404: description: >- Request failed. Either User or Workflow does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist" } 500: description: >- Request failed. Internal controller error. """ try: user_uuid = request.args["user"] workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) if not str(workflow.owner_id) == user_uuid: return ( jsonify({ "message": "User {} is not allowed to access workflow {}".format( user_uuid, workflow_id_or_name) }), 403, ) workflow_parameters = workflow.get_input_parameters() return ( jsonify({ "id": workflow.id_, "name": get_workflow_name(workflow), "type": workflow.reana_specification["workflow"]["type"], "parameters": workflow_parameters, }), 200, ) except ValueError: return ( jsonify({ "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) }), 404, ) except KeyError as e: return jsonify({"message": str(e)}), 400 except Exception as e: return jsonify({"message": str(e)}), 500
def get_files(workflow_id_or_name, paginate=None): # noqa r"""List all files contained in a workspace. --- get: summary: Returns the workspace file list. description: >- This resource retrieves the file list of a workspace, given its workflow UUID. operationId: get_files produces: - multipart/form-data parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string - name: file_name in: query description: File name(s) (glob) to list. required: false type: string - name: page in: query description: Results page number (pagination). required: false type: integer - name: size in: query description: Number of results per page (pagination). required: false type: integer - name: search in: query description: Filter workflow workspace files. required: false type: string responses: 200: description: >- Requests succeeded. The list of code|input|output files has been retrieved. schema: type: object properties: total: type: integer items: type: array items: type: object properties: name: type: string last-modified: type: string size: type: object properties: raw: type: number human_readable: type: string 400: description: >- Request failed. The incoming data specification seems malformed. 404: description: >- Request failed. Workflow does not exist. examples: application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist." } 500: description: >- Request failed. Internal controller error. examples: application/json: { "message": "Internal workflow controller error." } """ try: user_uuid = request.args["user"] search = request.args.get("search") user = User.query.filter(User.id_ == user_uuid).first() if not user: return jsonify({"message": "User {} does not exist".format(user)}), 404 workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) file_name = request.args.get("file_name") if search: search = json.loads(search) if file_name: file_list = list_files_recursive_wildcard( workflow.workspace_path, file_name, search=search ) else: file_list = list_directory_files(workflow.workspace_path, search=search) pagination_dict = paginate(file_list) return jsonify(pagination_dict), 200 except ValueError: return ( jsonify( { "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) } ), 404, ) except KeyError: return jsonify({"message": "Malformed request."}), 400 except CreateFailed: return jsonify({"message": "Workspace does not exist."}), 404 except Exception as e: return jsonify({"message": str(e)}), 500
def upload_file(workflow_id_or_name): r"""Upload file to workspace. --- post: summary: Adds a file to the workspace. description: >- This resource is expecting a workflow UUID and a file to place in the workspace. operationId: upload_file consumes: - application/octet-stream produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string - name: file in: body description: Required. File to add to the workspace. required: true schema: type: string - name: file_name in: query description: Required. File name. required: true type: string responses: 200: description: >- Request succeeded. The file has been added to the workspace. schema: type: object properties: message: type: string examples: application/json: { "message": "`file_name` has been successfully uploaded.", } 400: description: >- Request failed. The incoming data specification seems malformed 404: description: >- Request failed. Workflow does not exist. schema: type: object properties: message: type: string examples: application/json: { "message": "Workflow cdcf48b1-c2f3-4693-8230-b066e088c6ac does not exist", } 500: description: >- Request failed. Internal controller error. """ try: if not ("application/octet-stream" in request.headers.get("Content-Type")): return ( jsonify( { "message": f"Wrong Content-Type " f'{request.headers.get("Content-Type")} ' f"use application/octet-stream" } ), 400, ) user_uuid = request.args["user"] full_file_name = request.args["file_name"] if not full_file_name: raise ValueError("The file transferred needs to have name.") workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) filename = full_file_name.split("/")[-1] # Remove starting '/' in path if full_file_name[0] == "/": full_file_name = full_file_name[1:] elif ".." in full_file_name.split("/"): raise REANAUploadPathError('Path cannot contain "..".') absolute_workspace_path = workflow.workspace_path if len(full_file_name.split("/")) > 1: dirs = full_file_name.split("/")[:-1] absolute_workspace_path = os.path.join( workflow.workspace_path, "/".join(dirs) ) if not os.path.exists(absolute_workspace_path): os.makedirs(absolute_workspace_path) absolute_file_path = os.path.join(absolute_workspace_path, filename) FileStorage(request.stream).save(absolute_file_path, buffer_size=32768) # update user and workflow resource disk quota store_workflow_disk_quota(workflow, bytes_to_sum=request.content_length) update_users_disk_quota(workflow.owner, bytes_to_sum=request.content_length) return ( jsonify( {"message": "{} has been successfully uploaded.".format(full_file_name)} ), 200, ) except ValueError: return ( jsonify( { "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment" "variable appropriately.".format(workflow_id_or_name) } ), 404, ) except KeyError as e: return jsonify({"message": str(e)}), 400 except Exception as e: return jsonify({"message": str(e)}), 500
def download_file(workflow_id_or_name, file_name): # noqa r"""Download a file from the workspace. --- get: summary: Returns the requested file. description: >- This resource is expecting a workflow UUID and a filename existing inside the workspace to return its content. operationId: download_file produces: - multipart/form-data parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name required: true type: string - name: file_name in: path description: Required. Name (or path) of the file to be downloaded. required: true type: string - name: preview in: query description: >- Optional flag to return a previewable response of the file (corresponding mime-type). required: false type: boolean responses: 200: description: >- Requests succeeded. The file has been downloaded. schema: type: file 400: description: >- Request failed. The incoming data specification seems malformed. 404: description: >- Request failed. `file_name` does not exist. examples: application/json: { "message": "input.csv does not exist" } 500: description: >- Request failed. Internal controller error. examples: application/json: { "message": "Internal workflow controller error." } """ try: user_uuid = request.args["user"] user = User.query.filter(User.id_ == user_uuid).first() if not user: return jsonify({"message": "User {} does not exist".format(user)}), 404 workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) workflow_name = workflow.get_full_workflow_name() return download_files_recursive_wildcard( workflow_name, workflow.workspace_path, file_name ) except ValueError: return ( jsonify( { "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) } ), 404, ) except KeyError: return jsonify({"message": "Malformed request."}), 400 except NotFound: return jsonify({"message": "{0} does not exist.".format(file_name)}), 404 except Exception as e: return jsonify({"message": str(e)}), 500
def delete_file(workflow_id_or_name, file_name): # noqa r"""Delete a file from the workspace. --- delete: summary: Delete the specified file. description: >- This resource is expecting a workflow UUID and a filename existing inside the workspace to be deleted. operationId: delete_file produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name required: true type: string - name: file_name in: path description: Required. Name (or path) of the file to be deleted. required: true type: string responses: 200: description: >- Requests succeeded. The file has been downloaded. schema: type: file 404: description: >- Request failed. `file_name` does not exist. examples: application/json: { "message": "input.csv does not exist" } 500: description: >- Request failed. Internal controller error. examples: application/json: { "message": "Internal workflow controller error." } """ try: user_uuid = request.args["user"] user = User.query.filter(User.id_ == user_uuid).first() if not user: return jsonify({"message": "User {} does not exist".format(user)}), 404 workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) deleted = remove_files_recursive_wildcard(workflow.workspace_path, file_name) # update user and workflow resource disk quota freed_up_bytes = sum( size.get("size", 0) for size in deleted["deleted"].values() ) store_workflow_disk_quota(workflow, bytes_to_sum=-freed_up_bytes) update_users_disk_quota(user, bytes_to_sum=-freed_up_bytes) return jsonify(deleted), 200 except ValueError: return ( jsonify( { "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) } ), 404, ) except KeyError: return jsonify({"message": "Malformed request."}), 400 except NotFound: return jsonify({"message": "{0} does not exist.".format(file_name)}), 404 except OSError: return jsonify({"message": "Error while deleting {}.".format(file_name)}), 500 except Exception as e: return jsonify({"message": str(e)}), 500
def open_interactive_session(workflow_id_or_name, interactive_session_type): # noqa r"""Start an interactive session inside the workflow workspace. --- post: summary: Start an interactive session inside the workflow workspace. description: >- This resource is expecting a workflow to start an interactive session within its workspace. operationId: open_interactive_session consumes: - application/json produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string - name: interactive_session_type in: path description: >- Optional. Type of interactive session to use, by default Jupyter Notebook. required: false type: string - name: interactive_session_configuration in: body description: >- Interactive session configuration. required: false schema: type: object properties: image: type: string description: >- Replaces the default Docker image of an interactive session. responses: 200: description: >- Request succeeded. The interactive session has been opened. schema: type: object properties: path: type: string examples: application/json: { "path": "/dd4e93cf-e6d0-4714-a601-301ed97eec60", } 400: description: >- Request failed. The incoming data specification seems malformed. examples: application/json: { "message": "Malformed request." } 404: description: >- Request failed. Either User or Workflow does not exist. examples: application/json: { "message": "Interactive session type terminl not found, try with one of: [jupyter]" } application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist" } 500: description: >- Request failed. Internal controller error. """ try: if interactive_session_type not in INTERACTIVE_SESSION_TYPES: return jsonify({ "message": "Interactive session type {0} not found, try " "with one of: {1}".format(interactive_session_type, INTERACTIVE_SESSION_TYPES) }), 404 interactive_session_configuration = request.json or {} user_uuid = request.args["user"] workflow = None workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) kwrm = KubernetesWorkflowRunManager(workflow) access_path = kwrm.start_interactive_session( interactive_session_type, image=interactive_session_configuration.get("image", None)) return jsonify({"path": "{}".format(access_path)}), 200 except (KeyError, ValueError) as e: status_code = 400 if workflow else 404 return jsonify({"message": str(e)}), status_code except Exception as e: return jsonify({"message": str(e)}), 500
def close_interactive_session(workflow_id_or_name): # noqa r"""Close an interactive workflow session. --- post: summary: Close an interactive workflow session. description: >- This resource is expecting a workflow to close an interactive session within its workspace. operationId: close_interactive_session consumes: - application/json produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string responses: 200: description: >- Request succeeded. The interactive session has been closed. schema: type: object properties: message: type: string examples: application/json: { "message": "The interactive session has been closed", } 400: description: >- Request failed. The incoming data specification seems malformed. examples: application/json: { "message": "Malformed request." } 404: description: >- Request failed. Either User or Workflow does not exist. examples: application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist" } 500: description: >- Request failed. Internal controller error. """ try: user_uuid = request.args["user"] workflow = None workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) if workflow.interactive_session_name is None: return jsonify({ "message": "Workflow - {} has no open interactive session.".format( workflow_id_or_name) }), 404 kwrm = KubernetesWorkflowRunManager(workflow) kwrm.stop_interactive_session() return jsonify({"message": "The interactive session has been closed"}), 200 except (KeyError, ValueError) as e: status_code = 400 if workflow else 404 return jsonify({"message": str(e)}), status_code except Exception as e: return jsonify({"message": str(e)}), 500
def get_files(workflow_id_or_name): # noqa r"""List all files contained in a workspace. --- get: summary: Returns the workspace file list. description: >- This resource retrieves the file list of a workspace, given its workflow UUID. operationId: get_files produces: - multipart/form-data parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string responses: 200: description: >- Requests succeeded. The list of code|input|output files has been retrieved. schema: type: array items: type: object properties: name: type: string last-modified: type: string size: type: integer 400: description: >- Request failed. The incoming data specification seems malformed. 404: description: >- Request failed. Workflow does not exist. examples: application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist." } 500: description: >- Request failed. Internal controller error. examples: application/json: { "message": "Internal workflow controller error." } """ try: user_uuid = request.args["user"] user = User.query.filter(User.id_ == user_uuid).first() if not user: return jsonify({"message": "User {} does not exist".format(user)}), 404 file_type = ( request.args.get("file_type") if request.args.get("file_type") else "input" ) workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) file_list = list_directory_files( os.path.join( current_app.config["SHARED_VOLUME_PATH"], workflow.workspace_path ) ) return jsonify(file_list), 200 except ValueError: return ( jsonify( { "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) } ), 404, ) except KeyError: return jsonify({"message": "Malformed request."}), 400 except CreateFailed: return jsonify({"message": "Workspace does not exist."}), 404 except Exception as e: return jsonify({"message": str(e)}), 500
def download_file(workflow_id_or_name, file_name): # noqa r"""Download a file from the workspace. --- get: summary: Returns the requested file. description: >- This resource is expecting a workflow UUID and a filename existing inside the workspace to return its content. operationId: download_file produces: - multipart/form-data parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name required: true type: string - name: file_name in: path description: Required. Name (or path) of the file to be downloaded. required: true type: string - name: preview in: query description: >- Optional flag to return a previewable response of the file (corresponding mime-type). required: false type: boolean responses: 200: description: >- Requests succeeded. The file has been downloaded. schema: type: file 400: description: >- Request failed. The incoming data specification seems malformed. 404: description: >- Request failed. `file_name` does not exist. examples: application/json: { "message": "input.csv does not exist" } 500: description: >- Request failed. Internal controller error. examples: application/json: { "message": "Internal workflow controller error." } """ try: user_uuid = request.args["user"] user = User.query.filter(User.id_ == user_uuid).first() if not user: return jsonify({"message": "User {} does not exist".format(user)}), 404 workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) absolute_workflow_workspace_path = os.path.join( current_app.config["SHARED_VOLUME_PATH"], workflow.workspace_path ) preview = json.loads(request.args.get("preview", "false").lower()) response_mime_type = "multipart/form-data" file_mime_type = mimetypes.guess_type(file_name)[0] # Only display image files as preview if preview and file_mime_type and file_mime_type.startswith("image"): response_mime_type = file_mime_type return ( send_from_directory( absolute_workflow_workspace_path, file_name, mimetype=response_mime_type, as_attachment=True, ), 200, ) except ValueError: return ( jsonify( { "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) } ), 404, ) except KeyError: return jsonify({"message": "Malformed request."}), 400 except NotFound as e: return jsonify({"message": "{0} does not exist.".format(file_name)}), 404 except Exception as e: return jsonify({"message": str(e)}), 500
def delete_file(workflow_id_or_name, file_name): # noqa r"""Delete a file from the workspace. --- delete: summary: Delete the specified file. description: >- This resource is expecting a workflow UUID and a filename existing inside the workspace to be deleted. operationId: delete_file produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name required: true type: string - name: file_name in: path description: Required. Name (or path) of the file to be deleted. required: true type: string responses: 200: description: >- Requests succeeded. The file has been downloaded. schema: type: file 404: description: >- Request failed. `file_name` does not exist. examples: application/json: { "message": "input.csv does not exist" } 500: description: >- Request failed. Internal controller error. examples: application/json: { "message": "Internal workflow controller error." } """ try: user_uuid = request.args['user'] user = User.query.filter(User.id_ == user_uuid).first() if not user: return jsonify( {'message': 'User {} does not exist'.format(user)}), 404 workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) abs_path_to_workspace = os.path.join( current_app.config['SHARED_VOLUME_PATH'], workflow.workspace_path) deleted = remove_files_recursive_wildcard( abs_path_to_workspace, file_name) return jsonify(deleted), 200 except ValueError: return jsonify({'message': 'REANA_WORKON is set to {0}, but ' 'that workflow does not exist. ' 'Please set your REANA_WORKON environment ' 'variable appropriately.'. format(workflow_id_or_name)}), 404 except KeyError: return jsonify({"message": "Malformed request."}), 400 except NotFound as e: return jsonify( {"message": "{0} does not exist.".format(file_name)}), 404 except OSError as e: return jsonify( {"message": "Error while deleting {}.".format(file_name)}), 500 except Exception as e: return jsonify({"message": str(e)}), 500