Skip to content

Commit f23526e

Browse files
committed
Add API endpoint for background job progress
1 parent 1a9177c commit f23526e

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
@@ -27,6 +27,7 @@
2727
StorageRef,
2828
User,
2929
SuccessResponse,
30+
JobProgress,
3031
)
3132
from .pagination import DEFAULT_PAGE_SIZE, paginated_format
3233
from .utils import dt_now
@@ -518,6 +519,52 @@ def _get_job_by_type_from_data(self, data: dict[str, object]):
518519

519520
return DeleteOrgJob.from_dict(data)
520521

522+
async def get_job_progress(self, job_id: str) -> JobProgress:
523+
"""Return progress of background job for supported types"""
524+
job = await self.get_background_job(job_id)
525+
526+
if job.type != BgJobType.COPY_BUCKET:
527+
raise HTTPException(status_code=403, detail="job_type_not_supported")
528+
529+
if job.success is False:
530+
raise HTTPException(status_code=400, detail="job_failed")
531+
532+
if job.finished:
533+
return JobProgress(percentage=1.0)
534+
535+
log_tail = await self.crawl_manager.tail_background_job(job_id)
536+
if not log_tail:
537+
raise HTTPException(status_code=400, detail="job_log_not_available")
538+
539+
lines = log_tail.splitlines()
540+
reversed_lines = list(reversed(lines))
541+
542+
progress = JobProgress(percentage=0.0)
543+
544+
# Parse lines in reverse order until we find one with latest stats
545+
for line in reversed_lines:
546+
try:
547+
if "ETA" not in line:
548+
continue
549+
550+
stats_groups = line.split(",")
551+
for group in stats_groups:
552+
group = group.strip()
553+
if "%" in group:
554+
progress.percentage = float(group.strip("%")) / 100
555+
if "ETA" in group:
556+
eta_str = group.strip("ETA ")
557+
# Split on white space to remove byte mark rclone sometimes
558+
# adds to end of stats line
559+
eta_list = eta_str.split(" ")
560+
progress.eta = eta_list[0]
561+
562+
break
563+
except:
564+
continue
565+
566+
return progress
567+
521568
async def list_background_jobs(
522569
self,
523570
org: Organization,
@@ -733,6 +780,17 @@ async def get_background_job(
733780
"""Retrieve information for background job"""
734781
return await ops.get_background_job(job_id, org.id)
735782

783+
@router.get(
784+
"/{job_id}/progress",
785+
response_model=JobProgress,
786+
)
787+
async def get_job_progress(
788+
job_id: str,
789+
org: Organization = Depends(org_crawl_dep),
790+
):
791+
"""Return progress information for background job"""
792+
return await ops.get_job_progress(job_id)
793+
736794
@app.get("/orgs/all/jobs/{job_id}", response_model=AnyJob, tags=["jobs"])
737795
async def get_background_job_all_orgs(job_id: str, user: User = Depends(user_dep)):
738796
"""Get background job from any org"""

backend/btrixcloud/crawlmanager.py

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

383+
async def tail_background_job(self, job_id: str) -> str:
384+
"""Tail running background job pod"""
385+
pods = await self.core_api.list_namespaced_pod(
386+
namespace=self.namespace,
387+
label_selector=f"batch.kubernetes.io/job-name={job_id}",
388+
)
389+
390+
if not pods.items:
391+
return ""
392+
393+
pod_name = pods.items[0].metadata.name
394+
395+
return await self.core_api.read_namespaced_pod_log(
396+
pod_name, self.namespace, tail_lines=10
397+
)
398+
383399
# ========================================================================
384400
# Internal Methods
385401
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
@@ -2112,6 +2112,14 @@ class CopyBucketJob(BackgroundJob):
21122112
]
21132113

21142114

2115+
# ============================================================================
2116+
class JobProgress(BaseModel):
2117+
"""Model for reporting background job progress"""
2118+
2119+
percentage: float
2120+
eta: Optional[str] = None
2121+
2122+
21152123
# ============================================================================
21162124

21172125
### PAGES ###

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)