@@ -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
4548import glob
@@ -129,9 +132,6 @@ def main():
129132 singularity_rootfs = '/cvmfs/singularity.opensciencegrid.org'
130133 singularity_rootfs = os .path .abspath (singularity_rootfs )
131134
132- # Does the registry require a token?
133- doauth = not args .notoken
134-
135135 # Do we have a docker image specified?
136136 if not args .docker and not (args .filelist or args .filelist_path ):
137137 print ("No docker image or file list specified.." , file = sys .stderr )
@@ -141,9 +141,9 @@ def main():
141141 if args .docker :
142142 image = args .docker
143143 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 )
145145 else :
146- return verify_image (image , args .registry , doauth , manifest_cache )
146+ return verify_image (image , args .registry )
147147 else :
148148 final_retval = 0
149149 failed_images = []
@@ -162,7 +162,7 @@ def main():
162162
163163 if '*' in repo_tag : # Treat wildcards as a glob
164164 try :
165- tag_names = get_tags (namespace , repo_name , registry = registry , auth = doauth )
165+ tag_names = get_tags (namespace , repo_name , registry = registry )
166166 except Exception as ex :
167167 image = '%s/%s/%s' % (registry , namespace , repo_name )
168168 print ("Failed to get tags for image: {}" .format (image ))
@@ -190,7 +190,7 @@ def main():
190190 for i in range (tries ):
191191 if not args .dryrun :
192192 try :
193- retval = publish_image (image , singularity_rootfs , registry , doauth , manifest_cache )
193+ retval = publish_image (image , singularity_rootfs , registry , manifest_cache )
194194 except Exception as ex :
195195 if i < tries - 1 :
196196 print ("Failed to publish image: {}" .format (image ))
@@ -201,7 +201,7 @@ def main():
201201 print ("Tried {} times " .format (tries ) + "for image {}" .format (image ) + ", giving up" )
202202 else :
203203 try :
204- retval = verify_image (image , registry , doauth , manifest_cache )
204+ retval = verify_image (image , registry )
205205 except Exception as ex :
206206 if i < tries - 1 :
207207 print ("Failed to verify image: {}" .format (image ))
@@ -254,21 +254,56 @@ def start_txn(singularity_rootfs):
254254 if oe .errno != errno .EEXIST :
255255 raise
256256
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
264285 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
271289
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+ # ----------------------------------------------------------
272307def publish_txn ():
273308 global _in_txn
274309 if _in_txn :
@@ -356,18 +391,7 @@ def parse_image(image):
356391
357392 return registry , namespace , repo_name , repo_tag
358393
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 ):
371395
372396 # Tell the user the namespace, repo name and tag
373397 registry , namespace , repo_name , repo_tag = parse_image (image )
@@ -383,8 +407,7 @@ def publish_image(image, singularity_rootfs, registry, doauth, manifest_cache):
383407 if "://" not in registry :
384408 registry = "https://%s" % registry
385409 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 )
388411
389412 # Calculate a unique hash across all layers. We'll use that as the identifier
390413 # for the final image.
@@ -459,7 +482,7 @@ def publish_image(image, singularity_rootfs, registry, doauth, manifest_cache):
459482 # Publish CVMFS as necessary.
460483 return publish_txn ()
461484
462- def verify_image (image , registry , doauth , manifest_cache ):
485+ def verify_image (image , registry ):
463486
464487 # Tell the user the namespace, repo name and tag
465488 registry , namespace , repo_name , repo_tag = parse_image (image )
@@ -468,16 +491,9 @@ def verify_image(image, registry, doauth, manifest_cache):
468491# IMAGE METADATA -------------------------------------------
469492# Use Docker Registry API (version 2.0) to get images ids, manifest
470493
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 )
478494 retval = 0
479495 try :
480- hub . manifest (namespace , repo_name , repo_tag , head = True )
496+ get_digest (namespace , repo_name , repo_tag , registry = registry )
481497 print (repo_name + ":" + repo_tag + " manifest found" )
482498 retval = 0
483499 except Exception as ex :
0 commit comments