def upload_archive(upload_data: UploadData,
                   requested_dir_path: str) -> (Path, ErrorCodeAndMessage):
    try:
        raw_content = base64.decodebytes(upload_data.base64_content.encode())
    except Error as e:
        return None, ErrorCodeAndMessageFormatter(INVALID_BASE_64, e)
    file_name = '{}.zip'.format(requested_dir_path)

    try:
        with open(file_name, 'wb') as f:
            f.write(raw_content)
    except OSError:
        return None, UNEXPECTED_ERROR
    try:
        with zipfile.ZipFile(file_name, mode='r') as zf:
            zf.extractall(path=requested_dir_path)
    except zipfile.BadZipFile as e:
        try:
            os.remove(file_name)
        except:
            pass
        return None, ErrorCodeAndMessageFormatter(NOT_AN_ARCHIVE, e)
    os.remove(file_name)
    path = Path.object_from_pathname(requested_dir_path)
    return path, None
    def put(self, user, execution_identifier):
        execution_db = get_execution(execution_identifier, db.session)
        if not execution_db:
            return ErrorCodeAndMessageFormatter(EXECUTION_NOT_FOUND,
                                                execution_identifier)
        if user.role != Role.admin and execution_db.creator_username != user.username:
            return UNAUTHORIZED

        if execution_db.status != ExecutionStatus.Running:
            return ErrorCodeAndMessageFormatter(
                CANNOT_KILL_NOT_RUNNING_EXECUTION, execution_db.status.name)

        # Look at its running processes
        execution_processes = get_execution_processes(execution_identifier,
                                                      db.session)

        if not execution_processes:  # Most probably due to the execution being in termination process
            return CANNOT_KILL_FINISHING_EXECUTION

        kill_all_execution_processes(execution_processes)

        # Mark the execution as "Killed" and delete the execution processes
        execution_db.status = ExecutionStatus.Killed
        execution_db.end_date = current_milli_time()
        for execution_process in execution_processes:
            db.session.delete(execution_process)
        db.session.commit()
예제 #3
0
def validate_request_model(model: Execution,
                           url_root: str) -> (bool, ErrorCodeAndMessage):
    if model.identifier:
        return False, EXECUTION_IDENTIFIER_MUST_NOT_BE_SET
    pipeline = get_pipeline(model.pipeline_identifier)
    if not pipeline:
        return False, INVALID_PIPELINE_IDENTIFIER
    files_exist, error = input_files_exist(model.input_values, pipeline,
                                           url_root)
    if not files_exist:
        error_code_and_message = ErrorCodeAndMessageFormatter(
            INVALID_INPUT_FILE, error)
        return False, error_code_and_message

    # Timeout validation
    min_authorized_execution_timeout = PLATFORM_PROPERTIES.get(
        "minAuthorizedExecutionTimeout", 0)
    max_authorized_execution_timeout = PLATFORM_PROPERTIES.get(
        "maxAuthorizedExecutionTimeout", 0)
    if model.timeout and ((max_authorized_execution_timeout > 0 and
                           model.timeout > max_authorized_execution_timeout) or
                          (model.timeout < min_authorized_execution_timeout)):
        error_code_and_message = ErrorCodeAndMessageFormatter(
            INVALID_EXECUTION_TIMEOUT, min_authorized_execution_timeout,
            max_authorized_execution_timeout or "(no maximum timeout)")
        return False, error_code_and_message
    return True, None
 def get(self, user, execution_identifier):
     execution_db = get_execution(execution_identifier, db.session)
     execution, error = get_execution_as_model(user.username, execution_db)
     if error:
         return ErrorCodeAndMessageFormatter(EXECUTION_NOT_FOUND,
                                             execution_identifier)
     return execution
예제 #5
0
 def test_get_invalid_execution(self, test_client):
     execution_id = "invalid"
     response = test_client.get('/executions/{}'.format(execution_id),
                                headers={"apiKey": standard_user().api_key})
     error = error_from_response(response)
     expected_error_code_and_message = ErrorCodeAndMessageFormatter(
         EXECUTION_NOT_FOUND, execution_id)
     assert error == expected_error_code_and_message
 def test_get_execution_std_err_invalid_execution_id(
         self, test_client, execution_id, write_std_err):
     invalid_execution_id = "NOT_{}".format(execution_id)
     response = test_client.get(
         '/executions/{}/stderr'.format(invalid_execution_id),
         headers={"apiKey": standard_user().api_key})
     error = error_from_response(response)
     expected_error_code_and_message = ErrorCodeAndMessageFormatter(
         EXECUTION_NOT_FOUND, invalid_execution_id)
     assert error == expected_error_code_and_message
    def get(self, user, execution_identifier):
        execution_db = get_execution(execution_identifier, db.session)
        if not execution_db:
            return ErrorCodeAndMessageFormatter(EXECUTION_NOT_FOUND,
                                                execution_identifier)
        if not is_safe_for_get(user, execution_db):
            return UNAUTHORIZED

        if execution_db.status not in EXECUTION_COMPLETED_STATUSES:
            return ErrorCodeAndMessageFormatter(
                CANNOT_GET_RESULT_NOT_COMPLETED_EXECUTION,
                execution_db.status.name)

        # We now know the execution has completed and can retrieve the output files
        output_files, error = get_output_files(execution_db.creator_username,
                                               execution_identifier)
        if error:
            return CORRUPTED_EXECUTION
        return output_files
    def put(self, user, execution_identifier):
        execution_db = get_execution(execution_identifier, db.session)
        if not execution_db:
            return ErrorCodeAndMessageFormatter(EXECUTION_NOT_FOUND,
                                                execution_identifier)
        if execution_db.creator_username != user.username:
            return UNAUTHORIZED

        if execution_db.status != ExecutionStatus.Initializing:
            return ErrorCodeAndMessageFormatter(CANNOT_REPLAY_EXECUTION,
                                                execution_db.status.name)

        execution, error = get_execution_as_model(user.username, execution_db)
        if error:
            return CORRUPTED_EXECUTION

        # Get the descriptor path
        descriptor_path = get_descriptor_path(user.username,
                                              execution.identifier)

        # Get appriopriate descriptor object
        descriptor = Descriptor.descriptor_factory_from_type(
            execution_db.descriptor)

        if not descriptor:
            # We don't have any descriptor defined for this pipeline type
            logger = logging.getLogger('server-error')
            logger.error(
                "Unsupported descriptor type extracted from file at {}".format(
                    descriptor_path))
            return ErrorCodeAndMessageFormatter(UNSUPPORTED_DESCRIPTOR_TYPE,
                                                execution_db.descriptor)

        modified_inputs_path = get_absolute_path_inputs_path(
            user.username, execution.identifier)
        if not os.path.isfile(modified_inputs_path):
            logger = logging.getLogger('server-error')
            logger.error("Absolute path inputs file not found at {}".format(
                descriptor_path))
            return UNEXPECTED_ERROR

        # The execution is valid and we are now ready to start it
        start_execution(user, execution, descriptor, modified_inputs_path)
    def put(self, model, execution_identifier, user):
        if model.identifier:
            return ErrorCodeAndMessageFormatter(CANNOT_MODIFY_PARAMETER,
                                                "identifier")
        if model.status:
            return ErrorCodeAndMessageFormatter(CANNOT_MODIFY_PARAMETER,
                                                "status")

        if model.name or model.timeout:
            execution_db = get_execution(execution_identifier, db.session)
            if not execution_db:
                return ErrorCodeAndMessageFormatter(EXECUTION_NOT_FOUND,
                                                    execution_identifier)
            if model.name:
                execution_db.name = model.name
            if model.timeout:
                execution_db.timeout = model.timeout
            db.session.add(execution_db)
            db.session.commit()
    def delete(self, user, execution_identifier):
        execution_db = get_execution(execution_identifier, db.session)
        if not execution_db:
            return ErrorCodeAndMessageFormatter(EXECUTION_NOT_FOUND,
                                                execution_identifier)
        if execution_db.creator_username != user.username:
            return UNAUTHORIZED

        deleteFiles = request.args.get(
            'deleteFiles', default=False, type=inputs.boolean)

        # Get all the execution running processes
        execution_processes = get_execution_processes(execution_identifier,
                                                      db.session)

        # Given delete is called to perform only a kill and encounter the same situation as kill, we do not kill the processes
        # See execution_kill for more information
        if execution_db.status == ExecutionStatus.Running and not execution_processes and not deleteFiles:
            return CANNOT_KILL_FINISHING_EXECUTION

        if execution_db.status != ExecutionStatus.Running and not deleteFiles:
            return ErrorCodeAndMessageFormatter(
                CANNOT_KILL_NOT_RUNNING_EXECUTION, execution_db.status.name)

        # Kill all the execution processed
        kill_all_execution_processes(execution_processes)
        for execution_process in execution_processes:
            db.session.delete(execution_process)
        # If the execution is not in a completed status, we mark it as killed
        if execution_db.status == ExecutionStatus.Running:
            execution_db.status = ExecutionStatus.Killed
            execution_db.end_date = current_milli_time()
        db.session.commit()

        # Free all resources associated with the execution if delete files is True
        if deleteFiles:
            execution_dir = get_execution_dir(user.username,
                                              execution_identifier)
            delete_execution_directory(execution_dir)
            db.session.delete(execution_db)
            db.session.commit()
예제 #11
0
def upload_file(upload_data: UploadData,
                requested_file_path: str) -> (Path, ErrorCodeAndMessage):
    try:
        raw_content = base64.decodebytes(upload_data.base64_content.encode())
    except Error as e:
        return None, ErrorCodeAndMessageFormatter(INVALID_BASE_64, e)
    try:
        with open(requested_file_path, 'wb') as f:
            f.write(raw_content)
    except OSError:
        return None, UNEXPECTED_ERROR
    path = Path.object_from_pathname(requested_file_path)
    return path, None
예제 #12
0
    def put(self, user, complete_path: str = ''):
        data = request.data
        requested_data_path = make_absolute(complete_path)

        if not is_safe_for_put(requested_data_path, user):
            return marshal(INVALID_PATH), 401

        if request.headers.get(
                'Content-Type',
                default='').lower() == 'application/carmin+json' and data:
            # Request data contains base64 encoding of file or archive
            data = request.get_json(force=True, silent=True)
            model, error = UploadDataSchema().load(data)
            if error:
                return marshal(
                    ErrorCodeAndMessageAdditionalDetails(
                        INVALID_MODEL_PROVIDED, error)), 400
            if model.upload_type == "File":
                if os.path.isdir(requested_data_path):
                    error = ErrorCodeAndMessageFormatter(
                        PATH_IS_DIRECTORY, complete_path)
                    return marshal(error), 400
                path, error = upload_file(model, requested_data_path)
                if error:
                    return marshal(error), 400
                return marshal(path), 201

            if model.upload_type == "Archive":
                path, error = upload_archive(model, requested_data_path)
                if error:
                    return marshal(error), 400
                return marshal(path), 201
        if data:
            # Content-Type is not 'application/carmin+json',
            # request data is taken as raw text
            try:
                with open(requested_data_path, 'w') as f:
                    f.write(data.decode('utf-8', errors='ignore'))
                return marshal(
                    PathModel.object_from_pathname(requested_data_path)), 201
            except OSError:
                return marshal(INVALID_PATH), 400
        if not data:
            path, error = create_directory(requested_data_path)
            if error:
                return marshal(error), 400
            file_location_header = {'Location': path.platform_path}
            string_path = json.dumps(PathSchema().dump(path).data)
            return make_response((string_path, 201, file_location_header))

        return marshal(INVALID_REQUEST), 400
예제 #13
0
    def post(self, model, user):
        already_existing_user = db.session.query(User).filter_by(
            username=model.username).first()

        if already_existing_user:
            return ErrorCodeAndMessageFormatter(USERNAME_ALREADY_EXISTS,
                                                model.username)
        result, error = register_user(model.username, model.password,
                                      Role.user, db.session)

        if error:
            if error.error_code != USERNAME_ALREADY_EXISTS.error_code:
                return UNEXPECTED_ERROR
            return error
예제 #14
0
def register_user(username: str, password: str, user_role: Role,
                  db_session) -> (bool, ErrorCodeAndMessage):
    try:
        new_user = User(
            username=username,
            password=generate_password_hash(password),
            role=user_role)

        db_session.add(new_user)
        path, error = create_user_directory(new_user.username)
        if error:
            db_session.rollback()
            return False, error

        db_session.commit()
        return True, None
    except IntegrityError:
        db_session.rollback()
        return False, ErrorCodeAndMessageFormatter(USERNAME_ALREADY_EXISTS,
                                                   username)
예제 #15
0
def std_file_resource(user, execution_identifier, path_to_file):
    execution_db = get_execution(execution_identifier, db.session)
    if not execution_db:
        error = ErrorCodeAndMessageFormatter(EXECUTION_NOT_FOUND,
                                             execution_identifier)
        return marshal(error), 400

    if not is_safe_for_get(user, execution_db):
        return UNAUTHORIZED

    execution, error = get_execution_as_model(execution_db.creator_username,
                                              execution_db)
    if error:
        return marshal(error), 400

    std, error = get_std_file(user.username, execution_identifier,
                              path_to_file)
    if error:
        return marshal(error), 400

    return Response(std, mimetype='text/plain')
        def wrapper(*args, **kwargs):

            model = func(*args, **kwargs)

            if (isinstance(model, ErrorCodeAndMessage)):
                return ErrorCodeAndMessageMarshaller(
                    model), 500 if model == UNEXPECTED_ERROR else 400

            if (schema is None):
                return '', 204

            json, errors = schema.dump(model)

            if errors:
                model_dumping_error = ErrorCodeAndMessageFormatter(
                    MODEL_DUMPING_ERROR,
                    type(model).__name__)
                model_dumping_error = ErrorCodeAndMessageAdditionalDetails(
                    model_dumping_error, errors)
                return ErrorCodeAndMessageMarshaller(model_dumping_error), 500

            return json
예제 #17
0
 def post(self, model, user):
     if model.password:
         if user.role == Role.admin and model.username:
             # Logged in user is an admin and wants to change
             # another user's password
             edit_user = db.session.query(User).filter_by(
                 username=model.username).first()
             if not edit_user:
                 return ErrorCodeAndMessageFormatter(
                     USER_DOES_NOT_EXIST, model.username)
         else:
             # Logged in user is not an admin
             if model.username and model.username != user.username:
                 return UNAUTHORIZED
             edit_user = db.session.query(User).filter_by(
                 username=user.username).first()
         edit_user.password = generate_password_hash(model.password)
         db.session.commit()
     else:
         # Password was not provided
         return ErrorCodeAndMessageAdditionalDetails(
             INVALID_MODEL_PROVIDED, "'password' is required")