@@ -34,12 +34,15 @@ import argparse
3434import os
3535import errno
3636import fnmatch
37+ import functools
38+ import glob
3739import json
3840import urllib .request , urllib .error , urllib .parse
3941import hashlib
4042import traceback
4143import subprocess
42- import dockerhub
44+ from dxf import DXF
45+ import furl
4346import cleanup
4447import sqlitedict
4548
@@ -128,9 +131,6 @@ def main():
128131 singularity_rootfs = '/cvmfs/singularity.opensciencegrid.org'
129132 singularity_rootfs = os .path .abspath (singularity_rootfs )
130133
131- # Does the registry require a token?
132- doauth = not args .notoken
133-
134134 # Do we have a docker image specified?
135135 if not args .docker and not (args .filelist or args .filelist_path ):
136136 print ("No docker image or file list specified.." , file = sys .stderr )
@@ -140,9 +140,9 @@ def main():
140140 if args .docker :
141141 image = args .docker
142142 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 )
144144 else :
145- return verify_image (image , args .registry , doauth , manifest_cache )
145+ return verify_image (image , args .registry )
146146 else :
147147 final_retval = 0
148148 failed_images = []
@@ -161,7 +161,7 @@ def main():
161161
162162 if '*' in repo_tag : # Treat wildcards as a glob
163163 try :
164- tag_names = get_tags (namespace , repo_name , registry = registry , auth = doauth )
164+ tag_names = get_tags (namespace , repo_name , registry = registry )
165165 except Exception as ex :
166166 image = '%s/%s/%s' % (registry , namespace , repo_name )
167167 print ("Failed to get tags for image: {}" .format (image ))
@@ -189,7 +189,7 @@ def main():
189189 for i in range (tries ):
190190 if not args .dryrun :
191191 try :
192- retval = publish_image (image , singularity_rootfs , registry , doauth , manifest_cache )
192+ retval = publish_image (image , singularity_rootfs , registry , manifest_cache )
193193 except Exception as ex :
194194 if i < tries - 1 :
195195 print ("Failed to publish image: {}" .format (image ))
@@ -200,7 +200,7 @@ def main():
200200 print ("Tried {} times " .format (tries ) + "for image {}" .format (image ) + ", giving up" )
201201 else :
202202 try :
203- retval = verify_image (image , registry , doauth , manifest_cache )
203+ retval = verify_image (image , registry )
204204 except Exception as ex :
205205 if i < tries - 1 :
206206 print ("Failed to verify image: {}" .format (image ))
@@ -253,21 +253,56 @@ def start_txn(singularity_rootfs):
253253 if oe .errno != errno .EEXIST :
254254 raise
255255
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
263284 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
270288
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+ # ----------------------------------------------------------
271306def publish_txn ():
272307 global _in_txn
273308 if _in_txn :
@@ -355,18 +390,7 @@ def parse_image(image):
355390
356391 return registry , namespace , repo_name , repo_tag
357392
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 ):
370394
371395 # Tell the user the namespace, repo name and tag
372396 registry , namespace , repo_name , repo_tag = parse_image (image )
@@ -382,8 +406,7 @@ def publish_image(image, singularity_rootfs, registry, doauth, manifest_cache):
382406 if "://" not in registry :
383407 registry = "https://%s" % registry
384408 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 )
387410
388411 # Calculate a unique hash across all layers. We'll use that as the identifier
389412 # for the final image.
@@ -458,7 +481,7 @@ def publish_image(image, singularity_rootfs, registry, doauth, manifest_cache):
458481 # Publish CVMFS as necessary.
459482 return publish_txn ()
460483
461- def verify_image (image , registry , doauth , manifest_cache ):
484+ def verify_image (image , registry ):
462485
463486 # Tell the user the namespace, repo name and tag
464487 registry , namespace , repo_name , repo_tag = parse_image (image )
@@ -467,16 +490,9 @@ def verify_image(image, registry, doauth, manifest_cache):
467490# IMAGE METADATA -------------------------------------------
468491# Use Docker Registry API (version 2.0) to get images ids, manifest
469492
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 )
477493 retval = 0
478494 try :
479- hub . manifest (namespace , repo_name , repo_tag , head = True )
495+ get_digest (namespace , repo_name , repo_tag , registry = registry )
480496 print (repo_name + ":" + repo_tag + " manifest found" )
481497 retval = 0
482498 except Exception as ex :
0 commit comments