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