def load_image(url, imageName=None, imageTag=None): """ Load docker image from URL as imageName:tag, if no imageName or tag is given it will use whatever is inside the zstd compressed tarball. Returns an object with properties 'image', 'tag' and 'layer'. """ # If imageName is given and we don't have an imageTag # we parse out the imageTag from imageName, or default it to 'latest' # if no imageName and no imageTag is given, 'repositories' won't be rewritten if imageName and not imageTag: if ':' in imageName: imageName, imageTag = imageName.split(':', 1) else: imageTag = 'latest' curl, zstd, docker = None, None, None image, tag, layer = None, None, None error = None try: # Setup piping: curl | zstd | tarin curl = Popen(['curl', '-#', '--fail', '-L', '--retry', '8', url], stdout=PIPE) zstd = Popen(['zstd', '-d'], stdin=curl.stdout, stdout=PIPE) tarin = tarfile.open(mode='r|', fileobj=zstd.stdout) # Seutp piping: tarout | docker docker = Popen(['docker', 'load'], stdin=PIPE) tarout = tarfile.open(mode='w|', fileobj=docker.stdin, format=tarfile.GNU_FORMAT) # Read from tarin and write to tarout for member in tarin: # Write non-file members directly (don't use extractfile on links) if not member.isfile(): tarout.addfile(member) continue # Open reader for the member reader = tarin.extractfile(member) # If member is repository, we parse and possibly rewrite the image tags if member.name == 'repositories': # Read and parse repositories repos = json.loads(reader.read()) reader.close() # If there is more than one image or tag, we can't handle it here if len(repos.keys()) > 1: raise Exception('file contains more than one image') image = repos.keys()[0] if len(repos[image].keys()) > 1: raise Exception('file contains more than one tag') tag = repos[image].keys()[0] layer = repos[image][tag] # Rewrite the repositories file data = json.dumps({imageName or image: {imageTag or tag: layer}}) reader = BytesIO(data) member.size = len(data) # Add member and reader tarout.addfile(member, reader) reader.close() tarout.close() except Exception: error = sys.exc_info()[0] finally: def trykill(proc): try: proc.kill() except: pass # Check that all subprocesses finished correctly if curl and curl.wait() != 0: trykill(zstd) trykill(docker) raise Exception('failed to download from url: {}'.format(url)) if zstd and zstd.wait() != 0: trykill(docker) raise Exception('zstd decompression failed') if docker: docker.stdin.close() if docker and docker.wait() != 0: raise Exception('loading into docker failed') if error: raise error # Check that we found a repositories file if not image or not tag or not layer: raise Exception('No repositories file found!') return {'image': image, 'tag': tag, 'layer': layer}