def test_delete_all_workflow_runs( app, session, default_user, yadage_workflow_with_name ): """Test deletion of all runs of a given workflow.""" # add 5 workflows in the database with the same name for i in range(5): workflow = Workflow( id_=uuid.uuid4(), name=yadage_workflow_with_name["name"], owner_id=default_user.id_, reana_specification=yadage_workflow_with_name["reana_specification"], operational_options={}, type_=yadage_workflow_with_name["reana_specification"]["workflow"]["type"], logs="", ) session.add(workflow) if i == 4: workflow.status = RunStatus.running not_deleted_one = workflow.id_ session.commit() first_workflow = ( session.query(Workflow) .filter_by(name=yadage_workflow_with_name["name"]) .first() ) delete_workflow(first_workflow, all_runs=True) for workflow in session.query(Workflow).filter_by(name=first_workflow.name).all(): if not_deleted_one == workflow.id_: assert workflow.status == RunStatus.running else: assert workflow.status == RunStatus.deleted
def test_delete_workflow(app, session, default_user, sample_yadage_workflow_in_db, status): """Test deletion of a workflow in all possible statuses.""" sample_yadage_workflow_in_db.status = status session.add(sample_yadage_workflow_in_db) session.commit() delete_workflow(sample_yadage_workflow_in_db) assert sample_yadage_workflow_in_db.status == RunStatus.deleted
def test_workspace_permissions( app, session, default_user, sample_yadage_workflow_in_db, tmp_shared_volume_path ): """Test workspace dir permissions.""" create_workflow_workspace(sample_yadage_workflow_in_db.workspace_path) expected_worspace_permissions = "drwxrwxr-x" workspace_permissions = stat.filemode( os.stat(sample_yadage_workflow_in_db.workspace_path).st_mode ) assert os.path.exists(sample_yadage_workflow_in_db.workspace_path) assert workspace_permissions == expected_worspace_permissions delete_workflow(sample_yadage_workflow_in_db, workspace=True)
def test_deletion_of_workspace_of_an_already_deleted_workflow( app, session, default_user, sample_yadage_workflow_in_db ): """Test workspace deletion of an already deleted workflow.""" create_workflow_workspace(sample_yadage_workflow_in_db.workspace_path) # check that the workflow workspace exists assert os.path.exists(sample_yadage_workflow_in_db.workspace_path) delete_workflow(sample_yadage_workflow_in_db, workspace=False) assert os.path.exists(sample_yadage_workflow_in_db.workspace_path) delete_workflow(sample_yadage_workflow_in_db, workspace=True) assert not os.path.exists(sample_yadage_workflow_in_db.workspace_path)
def test_delete_workflow(app, session, default_user, sample_yadage_workflow_in_db, status, hard_delete): """Test deletion of a workflow in all possible statuses.""" sample_yadage_workflow_in_db.status = status session.add(sample_yadage_workflow_in_db) session.commit() delete_workflow(sample_yadage_workflow_in_db, hard_delete=hard_delete) if not hard_delete: assert sample_yadage_workflow_in_db.status == WorkflowStatus.deleted else: assert (session.query(Workflow).filter_by( id_=sample_yadage_workflow_in_db.id_).all() == [])
def test_workspace_permissions(app, session, default_user, sample_yadage_workflow_in_db, tmp_shared_volume_path): """Test workspace dir permissions.""" create_workflow_workspace(sample_yadage_workflow_in_db.workspace_path) expeted_worspace_permissions = 'drwxrwxr-x' absolute_workflow_workspace = os.path.join( tmp_shared_volume_path, sample_yadage_workflow_in_db.workspace_path) workspace_permissions = \ stat.filemode(os.stat(absolute_workflow_workspace).st_mode) assert os.path.exists(absolute_workflow_workspace) assert workspace_permissions == expeted_worspace_permissions delete_workflow(sample_yadage_workflow_in_db, hard_delete=True, workspace=True)
def test_workspace_deletion(app, session, default_user, sample_yadage_workflow_in_db, tmp_shared_volume_path, workspace, hard_delete): """Test workspace deletion.""" workflow = sample_yadage_workflow_in_db create_workflow_workspace(sample_yadage_workflow_in_db.workspace_path) absolute_workflow_workspace = os.path.join( tmp_shared_volume_path, workflow.workspace_path) # create a job for the workflow workflow_job = Job(id_=uuid.uuid4(), workflow_uuid=workflow.id_) job_cache_entry = JobCache(job_id=workflow_job.id_) session.add(workflow_job) session.add(job_cache_entry) session.commit() # create cached workspace cache_dir_path = os.path.abspath(os.path.join( absolute_workflow_workspace, os.pardir, 'archive', str(workflow_job.id_))) os.makedirs(cache_dir_path) # check that the workflow workspace exists assert os.path.exists(absolute_workflow_workspace) assert os.path.exists(cache_dir_path) delete_workflow(workflow, hard_delete=hard_delete, workspace=workspace) if hard_delete or workspace: assert not os.path.exists(absolute_workflow_workspace) # check that all cache entries for jobs # of the deleted workflow are removed cache_entries_after_delete = JobCache.query.filter_by( job_id=workflow_job.id_).all() assert not cache_entries_after_delete assert not os.path.exists(cache_dir_path)
def test_deletion_of_workspace_of_an_already_deleted_workflow( app, session, default_user, sample_yadage_workflow_in_db, tmp_shared_volume_path): """Test workspace deletion of an already deleted workflow.""" create_workflow_workspace(sample_yadage_workflow_in_db.workspace_path) absolute_workflow_workspace = os.path.join( tmp_shared_volume_path, sample_yadage_workflow_in_db.workspace_path) # check that the workflow workspace exists assert os.path.exists(absolute_workflow_workspace) delete_workflow(sample_yadage_workflow_in_db, hard_delete=False, workspace=False) assert os.path.exists(absolute_workflow_workspace) delete_workflow(sample_yadage_workflow_in_db, hard_delete=False, workspace=True) assert not os.path.exists(absolute_workflow_workspace) delete_workflow(sample_yadage_workflow_in_db, hard_delete=True, workspace=True)
def test_delete_all_workflow_runs(app, session, default_user, yadage_workflow_with_name, hard_delete): """Test deletion of all runs of a given workflow.""" # add 5 workflows in the database with the same name for i in range(5): workflow = Workflow(id_=uuid.uuid4(), name=yadage_workflow_with_name['name'], owner_id=default_user.id_, reana_specification=yadage_workflow_with_name[ 'reana_specification'], operational_options={}, type_=yadage_workflow_with_name[ 'reana_specification']['workflow']['type'], logs='') session.add(workflow) if i == 4: workflow.status = WorkflowStatus.running not_deleted_one = workflow.id_ session.commit() first_workflow = session.query(Workflow).\ filter_by(name=yadage_workflow_with_name['name']).first() delete_workflow(first_workflow, all_runs=True, hard_delete=hard_delete) if not hard_delete: for workflow in session.query(Workflow).\ filter_by(name=first_workflow.name).all(): if not_deleted_one == workflow.id_: assert workflow.status == WorkflowStatus.running else: assert workflow.status == WorkflowStatus.deleted else: # the one running should not be deleted assert len(session.query(Workflow). filter_by(name=first_workflow.name).all()) == 1
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 test_workspace_deletion( mock_update_user_quota, mock_update_workflow_quota, app, session, default_user, sample_yadage_workflow_in_db, workspace, ): """Test workspace deletion.""" workflow = sample_yadage_workflow_in_db create_workflow_workspace(sample_yadage_workflow_in_db.workspace_path) # Add file to the worskpace file_size = 123 file_path = os.path.join(sample_yadage_workflow_in_db.workspace_path, "temp.txt") with open(file_path, "w") as f: f.write("A" * file_size) # Get disk usage disk_usage = get_disk_usage_or_zero(sample_yadage_workflow_in_db.workspace_path) assert disk_usage # Update disk quotas store_workflow_disk_quota(sample_yadage_workflow_in_db) update_users_disk_quota(sample_yadage_workflow_in_db.owner) # create a job for the workflow workflow_job = Job(id_=uuid.uuid4(), workflow_uuid=workflow.id_) job_cache_entry = JobCache(job_id=workflow_job.id_) session.add(workflow_job) session.commit() session.add(job_cache_entry) session.commit() # create cached workspace cache_dir_path = os.path.join( sample_yadage_workflow_in_db.workspace_path, "..", "archive", str(workflow_job.id_), ) os.makedirs(cache_dir_path) # check that the workflow workspace exists assert os.path.exists(sample_yadage_workflow_in_db.workspace_path) assert os.path.exists(cache_dir_path) delete_workflow(workflow, workspace=workspace) if workspace: assert not os.path.exists(sample_yadage_workflow_in_db.workspace_path) mock_update_user_quota.assert_called_once_with( sample_yadage_workflow_in_db.owner, bytes_to_sum=-disk_usage, override_policy_checks=True, ) mock_update_workflow_quota.assert_called_once_with( sample_yadage_workflow_in_db, bytes_to_sum=-disk_usage, override_policy_checks=True, ) else: assert not mock_update_user_quota.called assert not mock_update_workflow_quota.called # check that all cache entries for jobs # of the deleted workflow are removed cache_entries_after_delete = JobCache.query.filter_by(job_id=workflow_job.id_).all() assert not cache_entries_after_delete assert not os.path.exists(cache_dir_path)