Esempio n. 1
0
    def put(self, job_uuid, run_uuid):
        """Set the status of a pipeline run."""
        status_update = request.get_json()

        # The pipeline run has reached a final state, thus we can update
        # the job "completed_pipeline_runs" attribute.
        if status_update["status"] in ["SUCCESS", "FAILURE"]:
            job = models.Job.query.get_or_404(
                job_uuid,
                description="Job not found",
            )
            job.completed_pipeline_runs += 1

        filter_by = {
            "job_uuid": job_uuid,
            "run_uuid": run_uuid,
        }
        try:
            update_status_db(
                status_update,
                model=models.NonInteractivePipelineRun,
                filter_by=filter_by,
            )
            db.session.commit()
        except Exception:
            db.session.rollback()
            return {"message": "Failed update operation."}, 500

        return {"message": "Status was updated successfully"}, 200
def abort_environment_build(environment_build_uuid, is_running=False):
    """Aborts an environment build.

    Aborts an environment build by setting its state to ABORTED and
    sending a REVOKE and ABORT command to celery.

    Args:
        is_running:
        environment_build_uuid: uuid of the environment build to abort

    Returns:

    """
    filter_by = {
        "build_uuid": environment_build_uuid,
    }
    status_update = {"status": "ABORTED"}
    celery_app = make_celery(current_app)

    # Make use of both constructs (revoke, abort) so we cover both a
    # task that is pending and a task which is running.
    celery_app.control.revoke(environment_build_uuid, timeout=1.0)
    if is_running:
        res = AbortableAsyncResult(environment_build_uuid, app=celery_app)
        # It is responsibility of the task to terminate by reading it's
        # aborted status.
        res.abort()

    update_status_db(
        status_update,
        model=models.EnvironmentBuild,
        filter_by=filter_by,
    )
Esempio n. 3
0
    def put(self, project_uuid, environment_uuid, image_tag):
        """Set the status of a environment build."""
        status_update = request.get_json()

        filter_by = {
            "project_uuid": project_uuid,
            "environment_uuid": environment_uuid,
            "image_tag": int(image_tag),
        }
        try:
            update_status_db(
                status_update,
                model=models.EnvironmentImageBuild,
                filter_by=filter_by,
            )
            if status_update["status"] == "SUCCESS":
                digest = registry.get_manifest_digest(
                    _config.ENVIRONMENT_IMAGE_NAME.format(
                        project_uuid=project_uuid,
                        environment_uuid=environment_uuid),
                    image_tag,
                )
                db.session.add(
                    models.EnvironmentImage(
                        project_uuid=project_uuid,
                        environment_uuid=environment_uuid,
                        tag=int(image_tag),
                        digest=digest,
                    ))
            db.session.commit()
        except Exception:
            db.session.rollback()
            return {"message": "Failed update operation."}, 500

        return {"message": "Status was updated successfully."}, 200
Esempio n. 4
0
    def _transaction(self, run_uuid):
        """Abort a pipeline level at the db level.

        Args:
            run_uuid:

        Returns:
            True if the run state was set to ABORTED, false if the run
            did not exist or was not PENDING/STARTED.
        """

        # If the run is not abortable return false, abortion failed.
        # Set the state and return.
        filter_by = {"uuid": run_uuid}
        status_update = {"status": "ABORTED"}

        # _can_abort is set to True if any row was affected, that is,
        # the run was in a PENDING or STARTED state, since
        # update_status_db won't update it otherwise.
        can_abort = update_status_db(status_update,
                                     model=models.PipelineRun,
                                     filter_by=filter_by)
        # Do not attempt to update the status of the steps if the status
        # of the pipeline could not be updated.
        if can_abort:
            filter_by = {"run_uuid": run_uuid}
            update_status_db(status_update,
                             model=models.PipelineRunStep,
                             filter_by=filter_by)

        self.collateral_kwargs["run_uuid"] = run_uuid if can_abort else None

        return can_abort
Esempio n. 5
0
    def put(self, experiment_uuid, run_uuid):
        """Set the status of a pipeline run."""
        status_update = request.get_json()

        filter_by = {
            'experiment_uuid': experiment_uuid,
            'run_uuid': run_uuid,
        }
        update_status_db(status_update,
                         model=models.NonInteractiveRun,
                         filter_by=filter_by)

        return {'message': 'Status was updated successfully'}, 200
Esempio n. 6
0
    def put(self, job_uuid, run_uuid):
        """Set the status of a pipeline run."""
        status_update = request.get_json()

        try:

            filter_by = {
                "job_uuid": job_uuid,
                "uuid": run_uuid,
            }

            update_status_db(
                status_update,
                model=models.NonInteractivePipelineRun,
                filter_by=filter_by,
            )

            # See if the job is done running (all its runs are done).
            if status_update["status"] in ["SUCCESS", "FAILURE"]:

                # The job has 1 run for every parameters set.
                job = (db.session.query(
                    models.Job.schedule,
                    func.jsonb_array_length(models.Job.parameters),
                ).filter_by(uuid=job_uuid).one())

                # Only non recurring jobs terminate to SUCCESS.
                if job.schedule is None:

                    completed_runs = (
                        models.NonInteractivePipelineRun.query.filter_by(
                            job_uuid=job_uuid).filter(
                                models.NonInteractivePipelineRun.status.in_(
                                    ["SUCCESS", "FAILURE"])).count())

                    current_app.logger.info(
                        (f"Non recurring job {job_uuid} has completed "
                         f"{completed_runs}/{job[1]} runs."))

                    if completed_runs == job[1]:
                        models.Job.query.filter_by(uuid=job_uuid).update(
                            {"status": "SUCCESS"})

            db.session.commit()
        except Exception as e:
            db.session.rollback()
            current_app.logger.error(e)
            return {"message": "Failed update operation."}, 500

        return {"message": "Status was updated successfully"}, 200
    def put(self, environment_build_uuid):
        """Set the status of a environment build."""
        status_update = request.get_json()

        filter_by = {
            "build_uuid": environment_build_uuid,
        }
        update_status_db(
            status_update,
            model=models.EnvironmentBuild,
            filter_by=filter_by,
        )

        return {"message": "Status was updated successfully"}, 200
Esempio n. 8
0
    def _transaction(self, job_uuid: str):
        # To be later used by the collateral function.
        run_uuids = []
        # Assign asap since the function will return if there is nothing
        # to do.
        self.collateral_kwargs["run_uuids"] = run_uuids
        self.collateral_kwargs["job_uuid"] = job_uuid

        job = (models.Job.query.options(joinedload(
            models.Job.pipeline_runs)).filter_by(uuid=job_uuid).one_or_none())
        if job is None:
            return False

        # No op if the job is already in an end state.
        if job.status in ["SUCCESS", "FAILURE", "ABORTED"]:
            return

        job.status = "ABORTED"
        # This way a recurring job or a job which is scheduled to run
        # once in the future will not be scheduled anymore.
        job.next_scheduled_time = None

        # Store each uuid of runs that can still be aborted. These uuid
        # are the celery task uuid as well.
        for run in job.pipeline_runs:
            if run.status in ["PENDING", "STARTED"]:
                run_uuids.append(run.uuid)

        # Set the state of each run and related steps to ABORTED. Note
        # that the status of steps that have already been completed will
        # not be modified.
        for run_uuid in run_uuids:
            filter_by = {"uuid": run_uuid}
            status_update = {"status": "ABORTED"}

            update_status_db(
                status_update,
                model=models.NonInteractivePipelineRun,
                filter_by=filter_by,
            )

            filter_by = {"run_uuid": run_uuid}
            status_update = {"status": "ABORTED"}

            update_status_db(status_update,
                             model=models.PipelineRunStep,
                             filter_by=filter_by)

        return True
Esempio n. 9
0
    def put(self, experiment_uuid, run_uuid, step_uuid):
        """Set the status of a pipeline step of a pipeline run."""
        status_update = request.get_json()

        filter_by = {
            "run_uuid": run_uuid,
            "step_uuid": step_uuid,
        }
        update_status_db(
            status_update,
            model=models.PipelineRunStep,
            filter_by=filter_by,
        )

        return {"message": "Status was updated successfully"}, 200
Esempio n. 10
0
    def put(self, run_uuid):
        """Sets the status of a pipeline run."""

        filter_by = {"uuid": run_uuid}
        status_update = request.get_json()
        try:
            update_status_db(status_update,
                             model=models.PipelineRun,
                             filter_by=filter_by)
            db.session.commit()
        except Exception:
            db.session.rollback()
            return {"message": "Failed update operation."}, 500

        return {"message": "Status was updated successfully."}, 200
Esempio n. 11
0
def stop_experiment(experiment_uuid) -> bool:
    """Stop an experiment.

    Args:
        experiment_uuid:

    Returns:
        True if the experiment exists and was stopped, false
        if it did not exist or if it was already completed.
    """
    experiment = models.Experiment.query.filter_by(
        experiment_uuid=experiment_uuid).one_or_none()
    if experiment is None:
        return False

    run_uuids = [
        run.run_uuid for run in experiment.pipeline_runs
        if run.status in ["PENDING", "STARTED"]
    ]
    if len(run_uuids) == 0:
        return False

    # Aborts and revokes all pipeline runs and waits for a
    # reply for 1.0s.
    celery = make_celery(current_app)
    celery.control.revoke(run_uuids, timeout=1.0)

    # TODO: possibly set status of steps and Run to "ABORTED"
    #  note that a race condition would be present since the task
    # will try to set the status as well
    for run_uuid in run_uuids:
        res = AbortableAsyncResult(run_uuid, app=celery)
        # it is responsibility of the task to terminate by reading \
        # it's aborted status
        res.abort()

        filter_by = {"run_uuid": run_uuid}
        status_update = {"status": "ABORTED"}
        update_status_db(status_update,
                         model=models.NonInteractivePipelineRun,
                         filter_by=filter_by)
        update_status_db(status_update,
                         model=models.PipelineRunStep,
                         filter_by=filter_by)

    db.session.commit()
    return True
Esempio n. 12
0
    def put(self, run_uuid, step_uuid):
        """Sets the status of a pipeline step."""
        status_update = request.get_json()

        # TODO: first check the status and make sure it says PENDING or
        #       whatever. Because if is empty then this would write it
        #       and then get overwritten afterwards with "PENDING".
        filter_by = {"run_uuid": run_uuid, "step_uuid": step_uuid}
        try:
            update_status_db(status_update,
                             model=models.PipelineRunStep,
                             filter_by=filter_by)
            db.session.commit()
        except Exception:
            db.session.rollback()
            return {"message": "Failed update operation."}, 500

        return {"message": "Status was updated successfully."}, 200
Esempio n. 13
0
    def put(self, environment_build_uuid):
        """Set the status of a environment build."""
        status_update = request.get_json()

        filter_by = {
            "build_uuid": environment_build_uuid,
        }
        try:
            update_status_db(
                status_update,
                model=models.EnvironmentBuild,
                filter_by=filter_by,
            )
            db.session.commit()
        except Exception:
            db.session.rollback()
            return {"message": "Failed update operation."}, 500

        return {"message": "Status was updated successfully."}, 200
Esempio n. 14
0
    def put(self, run_uuid, step_uuid):
        """Sets the status of a pipeline step."""
        status_update = request.get_json()

        # TODO: don't we want to do this async? Since otherwise the API
        #       call might be blocking another since they both execute
        #       on the database? SQLite can only have one process write
        #       to the db. If this becomes an issue than we could also
        #       use an in-memory db (since that is a lot faster than
        #       disk). Otherwise we might have to use PostgreSQL.
        # TODO: first check the status and make sure it says PENDING or
        #       whatever. Because if is empty then this would write it
        #       and then get overwritten afterwards with "PENDING".
        filter_by = {"run_uuid": run_uuid, "step_uuid": step_uuid}
        update_status_db(status_update,
                         model=models.InteractiveRunPipelineStep,
                         filter_by=filter_by)

        return {"message": "Status was updated successfully"}, 200
Esempio n. 15
0
    def put(self, experiment_uuid, run_uuid):
        """Set the status of a pipeline run."""
        status_update = request.get_json()

        # The pipeline run has reached a final state, thus we can update
        # the experiment "completed_pipeline_runs" attribute.
        if status_update["status"] in ["SUCCESS", "FAILURE"]:
            experiment = models.Experiment.query.get_or_404(
                experiment_uuid,
                description="Experiment not found",
            )
            experiment.completed_pipeline_runs += 1

        filter_by = {
            "experiment_uuid": experiment_uuid,
            "run_uuid": run_uuid,
        }
        update_status_db(status_update,
                         model=models.NonInteractivePipelineRun,
                         filter_by=filter_by)

        return {"message": "Status was updated successfully"}, 200
Esempio n. 16
0
    def _transaction(self, job_uuid: str):
        # To be later used by the collateral function.
        run_uuids = []
        # Assign asap since the function will return if there is nothing
        # to do.
        self.collateral_kwargs["run_uuids"] = run_uuids

        job = models.Job.query.filter_by(job_uuid=job_uuid).one_or_none()
        if job is None:
            return False

        # Store each uuid of runs that can still be aborted. These uuid
        # are the celery task uuid as well.
        for run in job.pipeline_runs:
            if run.status in ["PENDING", "STARTED"]:
                run_uuids.append(run.run_uuid)

        if len(run_uuids) == 0:
            return False

        # Set the state of each run and related steps to ABORTED. Note
        # that the status of steps that have already been completed will
        # not be modified.
        for run_uuid in run_uuids:
            filter_by = {"run_uuid": run_uuid}
            status_update = {"status": "ABORTED"}

            update_status_db(
                status_update,
                model=models.NonInteractivePipelineRun,
                filter_by=filter_by,
            )

            update_status_db(status_update,
                             model=models.PipelineRunStep,
                             filter_by=filter_by)

        return True
Esempio n. 17
0
    def put(self, experiment_uuid, run_uuid):
        """Set the status of a pipeline run."""
        status_update = request.get_json()

        # The pipeline run has reached a final state, thus we can update
        # the experiment "completed_pipeline_runs" attribute.
        if status_update['status'] in ['SUCCESS', 'FAILURE']:
            experiment = models.Experiment.query.get_or_404(
                experiment_uuid,
                description='Experiment not found',
            )
            experiment.completed_pipeline_runs += 1
            db.session.commit()

        filter_by = {
            'experiment_uuid': experiment_uuid,
            'run_uuid': run_uuid,
        }
        update_status_db(status_update,
                         model=models.NonInteractiveRun,
                         filter_by=filter_by)

        return {'message': 'Status was updated successfully'}, 200
Esempio n. 18
0
    def _transaction(self, environment_build_uuid: str):

        filter_by = {
            "build_uuid": environment_build_uuid,
        }
        status_update = {"status": "ABORTED"}
        # Will return true if any row is affected, meaning that the
        # environment build was actually PENDING or STARTED.
        abortable = update_status_db(
            status_update,
            model=models.EnvironmentBuild,
            filter_by=filter_by,
        )

        self.collateral_kwargs["environment_build_uuid"] = (
            environment_build_uuid if abortable else None)
        return abortable
Esempio n. 19
0
    def _transaction(self, project_uuid: str, environment_uuid: str,
                     image_tag: str):

        filter_by = {
            "project_uuid": project_uuid,
            "environment_uuid": environment_uuid,
            "image_tag": int(image_tag),
        }
        status_update = {"status": "ABORTED"}
        # Will return true if any row is affected, meaning that the
        # environment build was actually PENDING or STARTED.
        abortable = update_status_db(
            status_update,
            model=models.EnvironmentImageBuild,
            filter_by=filter_by,
        )

        self.collateral_kwargs["celery_task_uuid"] = None
        if abortable:
            env_build = models.EnvironmentImageBuild.query.filter_by(
                **filter_by).one()
            self.collateral_kwargs[
                "celery_task_uuid"] = env_build.celery_task_uuid
        return abortable