Exemple #1
0
    def stream_generator(
        self,
        tag,
        parsed_manifest,
        synthetic_image_id,
        layer_iterator,
        tar_stream_getter_iterator,
        reporter=None,
    ):
        image_mtime = 0
        created = parsed_manifest.created_datetime
        if created is not None:
            image_mtime = calendar.timegm(created.utctimetuple())

        # ACI Format (.tar):
        #   manifest - The JSON manifest
        #   rootfs - The root file system

        # Yield the manifest.
        aci_manifest = json.dumps(
            DockerV1ToACIManifestTranslator.build_manifest(
                tag, parsed_manifest, synthetic_image_id))
        yield self.tar_file("manifest",
                            aci_manifest.encode("utf-8"),
                            mtime=image_mtime)

        # Yield the merged layer dtaa.
        yield self.tar_folder("rootfs", mtime=image_mtime)

        layer_merger = StreamLayerMerger(tar_stream_getter_iterator,
                                         path_prefix="rootfs/",
                                         reporter=reporter)
        for entry in layer_merger.get_generator():
            yield entry
Exemple #2
0
def squash_layers(layers, path_prefix=None):
    def getter_for_layer(layer):
        return lambda: BytesIO(layer)

    def layer_stream_getter():
        return [getter_for_layer(layer) for layer in layers]

    merger = StreamLayerMerger(layer_stream_getter, path_prefix=path_prefix)
    merged_data = b"".join(list(merger.get_generator()))
    return merged_data
Exemple #3
0
    def stream_generator(
        self,
        tag,
        parsed_manifest,
        synthetic_image_id,
        layer_iterator,
        tar_stream_getter_iterator,
        reporter=None,
    ):
        image_mtime = 0
        created = parsed_manifest.created_datetime
        if created is not None:
            image_mtime = calendar.timegm(created.utctimetuple())

        # Docker import V1 Format (.tar):
        #  repositories - JSON file containing a repo -> tag -> image map
        #  {image ID folder}:
        #     json - The layer JSON
        #     layer.tar - The tarballed contents of the layer
        #     VERSION - The docker import version: '1.0'
        layer_merger = StreamLayerMerger(tar_stream_getter_iterator,
                                         reporter=reporter)

        # Yield the repositories file:
        synthetic_layer_info = {}
        synthetic_layer_info[tag.name + ".squash"] = synthetic_image_id

        hostname = app.config["SERVER_HOSTNAME"]
        repositories = {}
        namespace = tag.repository.namespace_name
        repository = tag.repository.name
        repositories[hostname + "/" + namespace + "/" +
                     repository] = synthetic_layer_info

        yield self.tar_file("repositories",
                            json.dumps(repositories).encode("utf-8"),
                            mtime=image_mtime)

        # Yield the image ID folder.
        yield self.tar_folder(synthetic_image_id, mtime=image_mtime)

        # Yield the JSON layer data.
        layer_json = SquashedDockerImageFormatter._build_layer_json(
            parsed_manifest, synthetic_image_id)
        yield self.tar_file(synthetic_image_id + "/json",
                            json.dumps(layer_json).encode("utf-8"),
                            mtime=image_mtime)

        # Yield the VERSION file.
        yield self.tar_file(synthetic_image_id + "/VERSION",
                            b"1.0",
                            mtime=image_mtime)

        # Yield the merged layer data's header.
        estimated_file_size = 0
        for layer in layer_iterator:
            estimated_file_size += layer.estimated_size(
                SquashedDockerImageFormatter.SIZE_MULTIPLIER)

        # Make sure the estimated file size is an integer number of bytes.
        estimated_file_size = int(math.ceil(estimated_file_size))

        yield self.tar_file_header(synthetic_image_id + "/layer.tar",
                                   estimated_file_size,
                                   mtime=image_mtime)

        # Yield the contents of the merged layer.
        yielded_size = 0
        for entry in layer_merger.get_generator():
            yield entry
            yielded_size += len(entry)

        # If the yielded size is more than the estimated size (which is unlikely but possible), then
        # raise an exception since the tar header will be wrong.
        if yielded_size > estimated_file_size:
            leaf_image_id = parsed_manifest.leaf_layer_v1_image_id
            message = "For %s/%s:%s (%s:%s): Expected %s bytes, found %s bytes" % (
                namespace,
                repository,
                tag,
                parsed_manifest.digest,
                leaf_image_id,
                estimated_file_size,
                yielded_size,
            )
            raise FileEstimationException(message)

        # If the yielded size is less than the estimated size (which is likely), fill the rest with
        # zeros.
        if yielded_size < estimated_file_size:
            to_yield = estimated_file_size - yielded_size
            while to_yield > 0:
                yielded = min(to_yield, GZIP_BUFFER_SIZE)
                yield b"\0" * yielded
                to_yield -= yielded

        # Yield any file padding to 512 bytes that is necessary.
        yield self.tar_file_padding(estimated_file_size)

        # Last two records are empty in tar spec.
        yield b"\0" * 512
        yield b"\0" * 512