117 lines
2.9 KiB
Python
117 lines
2.9 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import uuid
|
|
|
|
from sqlmodel import Session, select
|
|
|
|
from ..core.time import utc_now
|
|
from ..db.models import JobRun
|
|
|
|
|
|
def list_jobs(session: Session):
|
|
return session.exec(select(JobRun).order_by(JobRun.started_at.desc())).all()
|
|
|
|
|
|
def get_job(session: Session, job_id: str) -> JobRun | None:
|
|
return session.get(JobRun, job_id)
|
|
|
|
|
|
def create_job(
|
|
session: Session,
|
|
*,
|
|
action_id: str,
|
|
target: str,
|
|
source: str,
|
|
risk: str,
|
|
policy_decision: str,
|
|
steps: list[str],
|
|
audit_event_id: str | None = None,
|
|
) -> JobRun:
|
|
job = prepare_job(
|
|
action_id=action_id,
|
|
target=target,
|
|
source=source,
|
|
risk=risk,
|
|
policy_decision=policy_decision,
|
|
steps=steps,
|
|
audit_event_id=audit_event_id,
|
|
)
|
|
session.add(job)
|
|
session.commit()
|
|
session.refresh(job)
|
|
return job
|
|
|
|
|
|
def prepare_job(
|
|
*,
|
|
action_id: str,
|
|
target: str,
|
|
source: str,
|
|
risk: str,
|
|
policy_decision: str,
|
|
steps: list[str],
|
|
audit_event_id: str | None = None,
|
|
) -> JobRun:
|
|
"""Build a job without committing it so admission can be atomic."""
|
|
now = utc_now()
|
|
return JobRun(
|
|
id=f'job-{uuid.uuid4().hex[:12]}',
|
|
type=action_id,
|
|
status='queued',
|
|
source=source,
|
|
action_id=action_id,
|
|
target=target,
|
|
risk=risk,
|
|
policy_decision=policy_decision,
|
|
steps_json=json.dumps(steps),
|
|
progress=0,
|
|
started_at=now,
|
|
audit_event_id=audit_event_id,
|
|
)
|
|
|
|
|
|
def start_job(session: Session, job: JobRun) -> JobRun:
|
|
job.status = 'running'
|
|
job.started_at = job.started_at or utc_now()
|
|
job.progress = 5
|
|
session.add(job)
|
|
session.commit()
|
|
session.refresh(job)
|
|
return job
|
|
|
|
|
|
def complete_job(session: Session, job: JobRun, *, status: str, result_summary: str) -> JobRun:
|
|
job.status = status
|
|
job.finished_at = utc_now()
|
|
job.result_summary = result_summary
|
|
job.progress = 100 if status == 'succeeded' else job.progress
|
|
if job.started_at:
|
|
job.duration_ms = int((job.finished_at - job.started_at).total_seconds() * 1000)
|
|
session.add(job)
|
|
session.commit()
|
|
session.refresh(job)
|
|
return job
|
|
|
|
|
|
def cancel_job(session: Session, job: JobRun, *, result_summary: str = 'Cancelled by operator.') -> JobRun:
|
|
job.status = 'cancelled'
|
|
job.finished_at = utc_now()
|
|
job.result_summary = result_summary
|
|
if job.started_at:
|
|
job.duration_ms = int((job.finished_at - job.started_at).total_seconds() * 1000)
|
|
session.add(job)
|
|
session.commit()
|
|
session.refresh(job)
|
|
return job
|
|
|
|
|
|
def set_job_progress(session: Session, job: JobRun, progress: int, result_summary: str | None = None) -> JobRun:
|
|
job.progress = max(0, min(100, int(progress)))
|
|
if result_summary:
|
|
job.result_summary = result_summary
|
|
session.add(job)
|
|
session.commit()
|
|
session.refresh(job)
|
|
return job
|