Skip to content

Commit 63ca2b9

Browse files
committed
Add API endpoint for background job progress
1 parent b4b1443 commit 63ca2b9

File tree

4 files changed

+85
-2
lines changed

4 files changed

+85
-2
lines changed

backend/btrixcloud/background_jobs.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
User,
3131
SuccessResponse,
3232
SuccessResponseId,
33+
JobProgress,
3334
)
3435
from .pagination import DEFAULT_PAGE_SIZE, paginated_format
3536
from .utils import dt_now
@@ -639,6 +640,52 @@ def _get_job_by_type_from_data(self, data: dict[str, object]):
639640

640641
return DeleteOrgJob.from_dict(data)
641642

643+
async def get_job_progress(self, job_id: str) -> JobProgress:
644+
"""Return progress of background job for supported types"""
645+
job = await self.get_background_job(job_id)
646+
647+
if job.type != BgJobType.COPY_BUCKET:
648+
raise HTTPException(status_code=403, detail="job_type_not_supported")
649+
650+
if job.success is False:
651+
raise HTTPException(status_code=400, detail="job_failed")
652+
653+
if job.finished:
654+
return JobProgress(percentage=1.0)
655+
656+
log_tail = await self.crawl_manager.tail_background_job(job_id)
657+
if not log_tail:
658+
raise HTTPException(status_code=400, detail="job_log_not_available")
659+
660+
lines = log_tail.splitlines()
661+
reversed_lines = list(reversed(lines))
662+
663+
progress = JobProgress(percentage=0.0)
664+
665+
# Parse lines in reverse order until we find one with latest stats
666+
for line in reversed_lines:
667+
try:
668+
if "ETA" not in line:
669+
continue
670+
671+
stats_groups = line.split(",")
672+
for group in stats_groups:
673+
group = group.strip()
674+
if "%" in group:
675+
progress.percentage = float(group.strip("%")) / 100
676+
if "ETA" in group:
677+
eta_str = group.strip("ETA ")
678+
# Split on white space to remove byte mark rclone sometimes
679+
# adds to end of stats line
680+
eta_list = eta_str.split(" ")
681+
progress.eta = eta_list[0]
682+
683+
break
684+
except:
685+
continue
686+
687+
return progress
688+
642689
async def list_background_jobs(
643690
self,
644691
org: Optional[Organization] = None,
@@ -894,6 +941,17 @@ async def get_org_background_job(
894941
"""Retrieve information for background job"""
895942
return await ops.get_background_job(job_id, org.id)
896943

944+
@router.get(
945+
"/{job_id}/progress",
946+
response_model=JobProgress,
947+
)
948+
async def get_job_progress(
949+
job_id: str,
950+
org: Organization = Depends(org_crawl_dep),
951+
):
952+
"""Return progress information for background job"""
953+
return await ops.get_job_progress(job_id)
954+
897955
@app.get("/orgs/all/jobs/{job_id}", response_model=AnyJob, tags=["jobs"])
898956
async def get_background_job_all_orgs(job_id: str, user: User = Depends(user_dep)):
899957
"""Get background job from any org"""

backend/btrixcloud/crawlmanager.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,22 @@ async def delete_crawl_config_by_id(self, cid: str) -> None:
415415
"""Delete all crawl configs by id"""
416416
await self._delete_crawl_configs(f"btrix.crawlconfig={cid}")
417417

418+
async def tail_background_job(self, job_id: str) -> str:
419+
"""Tail running background job pod"""
420+
pods = await self.core_api.list_namespaced_pod(
421+
namespace=self.namespace,
422+
label_selector=f"batch.kubernetes.io/job-name={job_id}",
423+
)
424+
425+
if not pods.items:
426+
return ""
427+
428+
pod_name = pods.items[0].metadata.name
429+
430+
return await self.core_api.read_namespaced_pod_log(
431+
pod_name, self.namespace, tail_lines=10
432+
)
433+
418434
# ========================================================================
419435
# Internal Methods
420436
async def _delete_crawl_configs(self, label) -> None:

backend/btrixcloud/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2651,6 +2651,14 @@ class CopyBucketJob(BackgroundJob):
26512651
]
26522652

26532653

2654+
# ============================================================================
2655+
class JobProgress(BaseModel):
2656+
"""Model for reporting background job progress"""
2657+
2658+
percentage: float
2659+
eta: Optional[str] = None
2660+
2661+
26542662
# ============================================================================
26552663

26562664
### GENERIC RESPONSE MODELS ###

chart/app-templates/copy_job.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ kind: Job
33
metadata:
44
name: "{{ id }}"
55
labels:
6+
job-id: "{{ id }}"
67
role: "background-job"
78
job_type: {{ job_type }}
89
btrix.org: {{ oid }}
910

1011
spec:
11-
ttlSecondsAfterFinished: 60
12+
ttlSecondsAfterFinished: 30
1213
backoffLimit: 3
1314
template:
1415
spec:
@@ -86,7 +87,7 @@ spec:
8687
- name: RCLONE_CONFIG_NEW_ENDPOINT
8788
value: "{{ new_endpoint }}"
8889

89-
command: ["rclone", "-vv", "--progress", "copy", "--checksum", "--use-mmap", "--transfers=2", "--checkers=2", "prev:{{ prev_bucket }}{{ oid }}", "new:{{ new_bucket }}{{ oid }}"]
90+
command: ["rclone", "-v", "--stats-one-line", "--stats", "10s", "copy", "--checksum", "--use-mmap", "--transfers=2", "--checkers=2", "prev:{{ prev_bucket }}{{ oid }}", "new:{{ new_bucket }}{{ oid }}"]
9091
resources:
9192
limits:
9293
memory: "350Mi"

0 commit comments

Comments
 (0)