|
17 | 17 | ###############################################################################
|
18 | 18 |
|
19 | 19 | import json
|
| 20 | +import time |
20 | 21 |
|
21 | 22 | import docker
|
22 | 23 | from girder import logger
|
@@ -159,7 +160,7 @@ def jobPullAndLoad(job):
|
159 | 160 |
|
160 | 161 | try:
|
161 | 162 | stage = 'pulling'
|
162 |
| - pullDockerImage(docker_client, pullList) |
| 163 | + pullDockerImage(docker_client, pullList, job) |
163 | 164 | except DockerImageNotFoundError as err:
|
164 | 165 | errorState = True
|
165 | 166 | notExistSet = set(err.imageName)
|
@@ -325,19 +326,57 @@ def getCliData(name, client, job):
|
325 | 326 | raise DockerImageError('Error getting %s cli data from image ' % (name) + str(err))
|
326 | 327 |
|
327 | 328 |
|
328 |
| -def pullDockerImage(client, names): |
| 329 | +def pullDockerImage(client, names, job=None): |
329 | 330 | """
|
330 | 331 | Attempt to pull the docker images listed in names. Failure results in a
|
331 | 332 | DockerImageNotFoundError being raised
|
332 | 333 |
|
333 |
| - :params client: The docker python client |
334 |
| - :params names: A list of docker images to be pulled from the Dockerhub |
| 334 | + :param client: The docker python client |
| 335 | + :param names: A list of docker images to be pulled from the Dockerhub |
| 336 | + :param job: A job to update with status. |
335 | 337 | """
|
336 | 338 | imgNotExistList = []
|
337 | 339 | for name in names:
|
338 | 340 | try:
|
339 | 341 | logger.info('Pulling %s image', name)
|
340 |
| - client.images.pull(name) |
| 342 | + lastlog = time.time() |
| 343 | + stats = {} |
| 344 | + for line in client.api.pull(name, stream=True, decode=True): |
| 345 | + try: |
| 346 | + line.update(line.get('progressDetail', {})) |
| 347 | + if 'id' not in line or ('total' not in line and line['id'] not in stats): |
| 348 | + continue |
| 349 | + stats.setdefault(line['id'], line).update(line) |
| 350 | + if time.time() - lastlog >= 10: |
| 351 | + total = sum(record['total'] for record in stats.values()) |
| 352 | + downloaded = sum( |
| 353 | + record['total'] for record in stats.values() |
| 354 | + if record['status'] != 'Downloading') |
| 355 | + downloaded += sum( |
| 356 | + record['current'] for record in stats.values() |
| 357 | + if record['status'] == 'Downloading') |
| 358 | + extracted = sum( |
| 359 | + record['total'] for record in stats.values() |
| 360 | + if record['status'] == 'Pull complete') |
| 361 | + extracted += sum( |
| 362 | + record['current'] for record in stats.values() |
| 363 | + if record['status'] == 'Extracting') |
| 364 | + if total: |
| 365 | + msg = f'Pulling {name} image: ' |
| 366 | + if downloaded < total: |
| 367 | + val = downloaded |
| 368 | + msg += 'downloaded ' |
| 369 | + else: |
| 370 | + val = extracted |
| 371 | + msg += 'extracted ' |
| 372 | + msg += f'{val}/{total} ({val * 100 / total:4.2f}%)' |
| 373 | + logger.info(msg) |
| 374 | + if job: |
| 375 | + job = Job().updateJob(job, log=msg + '\n') |
| 376 | + lastlog = time.time() |
| 377 | + except Exception: |
| 378 | + # Don't fail if the log code has an issue |
| 379 | + pass |
341 | 380 | # some invalid image names will not be pulled but the pull method
|
342 | 381 | # will not throw an exception so the only way to confirm if a pull
|
343 | 382 | # succeeded is to attempt a docker inspect on the image
|
|
0 commit comments