@@ -34,12 +34,15 @@ import argparse
34
34
import os
35
35
import errno
36
36
import fnmatch
37
+ import functools
38
+ import glob
37
39
import json
38
40
import urllib .request , urllib .error , urllib .parse
39
41
import hashlib
40
42
import traceback
41
43
import subprocess
42
- import dockerhub
44
+ from dxf import DXF
45
+ import furl
43
46
import cleanup
44
47
import sqlitedict
45
48
import glob
@@ -129,9 +132,6 @@ def main():
129
132
singularity_rootfs = '/cvmfs/singularity.opensciencegrid.org'
130
133
singularity_rootfs = os .path .abspath (singularity_rootfs )
131
134
132
- # Does the registry require a token?
133
- doauth = not args .notoken
134
-
135
135
# Do we have a docker image specified?
136
136
if not args .docker and not (args .filelist or args .filelist_path ):
137
137
print ("No docker image or file list specified.." , file = sys .stderr )
@@ -141,9 +141,9 @@ def main():
141
141
if args .docker :
142
142
image = args .docker
143
143
if not args .dryrun :
144
- return publish_image (image , singularity_rootfs , args .registry , doauth , manifest_cache )
144
+ return publish_image (image , singularity_rootfs , args .registry , manifest_cache )
145
145
else :
146
- return verify_image (image , args .registry , doauth , manifest_cache )
146
+ return verify_image (image , args .registry )
147
147
else :
148
148
final_retval = 0
149
149
failed_images = []
@@ -162,7 +162,7 @@ def main():
162
162
163
163
if '*' in repo_tag : # Treat wildcards as a glob
164
164
try :
165
- tag_names = get_tags (namespace , repo_name , registry = registry , auth = doauth )
165
+ tag_names = get_tags (namespace , repo_name , registry = registry )
166
166
except Exception as ex :
167
167
image = '%s/%s/%s' % (registry , namespace , repo_name )
168
168
print ("Failed to get tags for image: {}" .format (image ))
@@ -190,7 +190,7 @@ def main():
190
190
for i in range (tries ):
191
191
if not args .dryrun :
192
192
try :
193
- retval = publish_image (image , singularity_rootfs , registry , doauth , manifest_cache )
193
+ retval = publish_image (image , singularity_rootfs , registry , manifest_cache )
194
194
except Exception as ex :
195
195
if i < tries - 1 :
196
196
print ("Failed to publish image: {}" .format (image ))
@@ -201,7 +201,7 @@ def main():
201
201
print ("Tried {} times " .format (tries ) + "for image {}" .format (image ) + ", giving up" )
202
202
else :
203
203
try :
204
- retval = verify_image (image , registry , doauth , manifest_cache )
204
+ retval = verify_image (image , registry )
205
205
except Exception as ex :
206
206
if i < tries - 1 :
207
207
print ("Failed to verify image: {}" .format (image ))
@@ -254,21 +254,56 @@ def start_txn(singularity_rootfs):
254
254
if oe .errno != errno .EEXIST :
255
255
raise
256
256
257
-
258
- def get_tags (username , repo , registry = None , auth = None ):
259
- if registry != "registry.hub.docker.com" :
260
- if "://" not in registry :
261
- registry = "https://%s" % registry
262
- auth = DOCKER_CREDS .get (registry , {})
263
- hub = dockerhub .DockerHub (url = registry , namespace = username , repo = repo , ** auth )
257
+ # REGISTRY -------------------------------------------------
258
+ # Reuse dxf object if possible. A token can be reused for access to all tags.
259
+ @functools .lru_cache (maxsize = None )
260
+ def get_dxf (registry , repo ):
261
+ return DXF (registry , repo , docker_auth )
262
+
263
+ def docker_auth (dxf , response ):
264
+ '''DXF auth handler, using DOCKER_CREDS global'''
265
+ origin = furl .furl (response .url ).origin
266
+ authvars = DOCKER_CREDS .get (origin , {})
267
+ dxf .authenticate (response = response , ** authvars )
268
+
269
+ def get_tags (namespace , repo_name , registry = 'registry.hub.docker.com' ):
270
+ '''Retrieve tag list. This API call is uncounted.'''
271
+ repo = namespace + '/' + repo_name
272
+ #dxf = DXF(registry, repo, docker_auth)
273
+ dxf = get_dxf (registry , repo )
274
+ return dxf .list_aliases ()
275
+
276
+ def get_manifest (namespace , repo_name , repo_tag , cache = {}, registry = 'registry.hub.docker.com' ):
277
+ '''Retrieve Docker manifest. If uncached, this counts as an API call.'''
278
+ repo = namespace + '/' + repo_name
279
+ #dxf = DXF(registry, repo, docker_auth)
280
+ dxf = get_dxf (registry , repo )
281
+ digest = dxf_get_digest (dxf , repo_tag )
282
+
283
+ if digest in cache :
284
+ return cache [digest ], digest
264
285
else :
265
- auth = DOCKER_CREDS .get ('https://registry.hub.docker.com' , {})
266
- hub = dockerhub .DockerHub (** auth )
267
- tag_names = []
268
- for tag in hub .tags (username , repo ):
269
- tag_names .append (tag ['name' ])
270
- return tag_names
286
+ manifest = dxf .get_manifest (repo_tag )
287
+ cache [digest ] = manifest
288
+ return manifest
271
289
290
+ def get_digest (namespace , repo_name , repo_tag , registry = 'registry.hub.docker.com' ):
291
+ '''Retrieve docker-content-digest of the manifest blob. This API call is uncounted.'''
292
+ repo = namespace + '/' + repo_name
293
+ #dxf = DXF(registry, repo, docker_auth)
294
+ dxf = get_dxf (registry , repo )
295
+ return dxf_get_digest (dxf , repo_tag )
296
+
297
+ def dxf_get_digest (dxf , repo_tag ):
298
+ # Harbor returns 404 on HEAD of /v2/{repo_name}/manifests/{repo_tag}
299
+ # without the ACCEPT header
300
+ headers = {
301
+ 'ACCEPT' : 'application/vnd.oci.image.manifest.v1+json' ,
302
+ }
303
+ ret = dxf ._request ('head' , 'manifests/' + repo_tag , headers = headers )
304
+ return ret .headers ['docker-content-digest' ]
305
+
306
+ # ----------------------------------------------------------
272
307
def publish_txn ():
273
308
global _in_txn
274
309
if _in_txn :
@@ -356,18 +391,7 @@ def parse_image(image):
356
391
357
392
return registry , namespace , repo_name , repo_tag
358
393
359
- def get_manifest (hub , namespace , repo_name , repo_tag , manifest_cache ):
360
- metadata = hub .manifest (namespace , repo_name , repo_tag , head = True )
361
- digest = metadata .headers ['docker-content-digest' ]
362
-
363
- if digest in manifest_cache :
364
- return manifest_cache [digest ]
365
- else :
366
- manifest = hub .manifest (namespace , repo_name , repo_tag )
367
- manifest_cache [digest ] = manifest
368
- return manifest
369
-
370
- def publish_image (image , singularity_rootfs , registry , doauth , manifest_cache ):
394
+ def publish_image (image , singularity_rootfs , registry , manifest_cache ):
371
395
372
396
# Tell the user the namespace, repo name and tag
373
397
registry , namespace , repo_name , repo_tag = parse_image (image )
@@ -383,8 +407,7 @@ def publish_image(image, singularity_rootfs, registry, doauth, manifest_cache):
383
407
if "://" not in registry :
384
408
registry = "https://%s" % registry
385
409
auth = DOCKER_CREDS .get (registry , {})
386
- hub = dockerhub .DockerHub (url = registry , namespace = namespace , repo = repo_name , ** auth )
387
- manifest = get_manifest (hub , namespace , repo_name , repo_tag , manifest_cache )
410
+ manifest = get_manifest (namespace , repo_name , repo_tag , registry = registry , cache = manifest_cache )
388
411
389
412
# Calculate a unique hash across all layers. We'll use that as the identifier
390
413
# for the final image.
@@ -459,7 +482,7 @@ def publish_image(image, singularity_rootfs, registry, doauth, manifest_cache):
459
482
# Publish CVMFS as necessary.
460
483
return publish_txn ()
461
484
462
- def verify_image (image , registry , doauth , manifest_cache ):
485
+ def verify_image (image , registry ):
463
486
464
487
# Tell the user the namespace, repo name and tag
465
488
registry , namespace , repo_name , repo_tag = parse_image (image )
@@ -468,16 +491,9 @@ def verify_image(image, registry, doauth, manifest_cache):
468
491
# IMAGE METADATA -------------------------------------------
469
492
# Use Docker Registry API (version 2.0) to get images ids, manifest
470
493
471
- # Get an image manifest - has image ids to parse, and will be
472
- # used later to get Cmd
473
- # Prepend "https://" to the registry
474
- if "://" not in registry :
475
- registry = "https://%s" % registry
476
- auth = DOCKER_CREDS .get (registry , {})
477
- hub = dockerhub .DockerHub (url = registry , namespace = namespace , repo = repo_name , ** auth )
478
494
retval = 0
479
495
try :
480
- hub . manifest (namespace , repo_name , repo_tag , head = True )
496
+ get_digest (namespace , repo_name , repo_tag , registry = registry )
481
497
print (repo_name + ":" + repo_tag + " manifest found" )
482
498
retval = 0
483
499
except Exception as ex :
0 commit comments