Skip to content

Commit b461113

Browse files
committed
Add API endpoint for background job progress
1 parent af0d966 commit b461113

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
@@ -537,6 +538,52 @@ def _get_job_by_type_from_data(self, data: dict[str, object]):
537538

538539
return DeleteOrgJob.from_dict(data)
539540

541+
async def get_job_progress(self, job_id: str) -> JobProgress:
542+
"""Return progress of background job for supported types"""
543+
job = await self.get_background_job(job_id)
544+
545+
if job.type != BgJobType.COPY_BUCKET:
546+
raise HTTPException(status_code=403, detail="job_type_not_supported")
547+
548+
if job.success is False:
549+
raise HTTPException(status_code=400, detail="job_failed")
550+
551+
if job.finished:
552+
return JobProgress(percentage=1.0)
553+
554+
log_tail = await self.crawl_manager.tail_background_job(job_id)
555+
if not log_tail:
556+
raise HTTPException(status_code=400, detail="job_log_not_available")
557+
558+
lines = log_tail.splitlines()
559+
reversed_lines = list(reversed(lines))
560+
561+
progress = JobProgress(percentage=0.0)
562+
563+
# Parse lines in reverse order until we find one with latest stats
564+
for line in reversed_lines:
565+
try:
566+
if "ETA" not in line:
567+
continue
568+
569+
stats_groups = line.split(",")
570+
for group in stats_groups:
571+
group = group.strip()
572+
if "%" in group:
573+
progress.percentage = float(group.strip("%")) / 100
574+
if "ETA" in group:
575+
eta_str = group.strip("ETA ")
576+
# Split on white space to remove byte mark rclone sometimes
577+
# adds to end of stats line
578+
eta_list = eta_str.split(" ")
579+
progress.eta = eta_list[0]
580+
581+
break
582+
except:
583+
continue
584+
585+
return progress
586+
540587
async def list_background_jobs(
541588
self,
542589
org: Organization,
@@ -753,6 +800,17 @@ async def get_background_job(
753800
"""Retrieve information for background job"""
754801
return await ops.get_background_job(job_id, org.id)
755802

803+
@router.get(
804+
"/{job_id}/progress",
805+
response_model=JobProgress,
806+
)
807+
async def get_job_progress(
808+
job_id: str,
809+
org: Organization = Depends(org_crawl_dep),
810+
):
811+
"""Return progress information for background job"""
812+
return await ops.get_job_progress(job_id)
813+
756814
@app.get("/orgs/all/jobs/{job_id}", response_model=AnyJob, tags=["jobs"])
757815
async def get_background_job_all_orgs(job_id: str, user: User = Depends(user_dep)):
758816
"""Get background job from any org"""

backend/btrixcloud/crawlmanager.py

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

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

21222122

2123+
# ============================================================================
2124+
class JobProgress(BaseModel):
2125+
"""Model for reporting background job progress"""
2126+
2127+
percentage: float
2128+
eta: Optional[str] = None
2129+
2130+
21232131
# ============================================================================
21242132

21252133
### 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)