diff --git a/oci/private/pull.bzl b/oci/private/pull.bzl index ff1b8b80..23672dd0 100644 --- a/oci/private/pull.bzl +++ b/oci/private/pull.bzl @@ -216,12 +216,10 @@ _SUPPORTED_MEDIA_TYPES = { def _is_tag(str): return str.find(":") == -1 -def _trim_hash_algorithm(identifier): - "Optionally remove the sha256: prefix from identifier, if present" - parts = identifier.split(":", 1) - if len(parts) != 2: - return identifier - return parts[1] +def _digest_into_blob_path(digest): + "convert sha256:deadbeef into sha256/deadbeef" + digest_path = digest.replace(":", "/", 1) + return "blobs/{}".format(digest_path) def _download(rctx, state, identifier, output, resource, download_fn = download.bazel, headers = {}, allow_fail = False): "Use the Bazel Downloader to fetch from the remote registry" @@ -325,31 +323,9 @@ def _create_downloader(rctx): ) _BUILD_FILE_TMPL = """\ -"Generated by oci_pull" +"Generated by oci_pull. DO NOT EDIT!" load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory") -load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file") -load("@aspect_bazel_lib//lib:jq.bzl", "jq") -load("@bazel_skylib//rules:write_file.bzl", "write_file") - -package(default_visibility = ["//visibility:public"]) - -copy_file( - name = "manifest", - src = "manifest.json", - out = "{image_digest}" -) - -copy_to_directory( - name = "blobs", - # TODO(https://github.com/bazel-contrib/rules_oci/issues/73): other hash algorithms - out = "blobs/sha256", - include_external_repositories = ["*"], - srcs = {tars} + [ - ":{image_digest}", - ":{config_file}", - ], -) copy_to_directory( name = "{target_name}", @@ -360,6 +336,7 @@ copy_to_directory( "oci-layout", "index.json", ], + visibility = ["//visibility:public"] ) """ @@ -385,7 +362,7 @@ def _oci_pull_impl(rctx): if manifest["mediaType"] in _SUPPORTED_MEDIA_TYPES["manifest"]: if rctx.attr.platform: fail("{}/{} is a single-architecture image, so attribute 'platforms' should not be set.".format(rctx.attr.registry, rctx.attr.repository)) - + elif manifest["mediaType"] in _SUPPORTED_MEDIA_TYPES["index"]: if not rctx.attr.platform: fail("{}/{} is a multi-architecture image, so attribute 'platforms' is required.".format(rctx.attr.registry, rctx.attr.repository)) @@ -398,34 +375,29 @@ def _oci_pull_impl(rctx): manifest, size, digest = downloader.download_manifest(matching_manifest["digest"], "manifest.json") else: fail("Unrecognized mediaType {} in manifest file".format(manifest["mediaType"])) + + # symlink manifest.json to blobs with it's digest. + # it is okay to use symlink here as copy_to_directory will dereference it when creating the TreeArtifact. + rctx.symlink("manifest.json", _digest_into_blob_path(digest)) # download the image config - config_file = _trim_hash_algorithm(manifest["config"]["digest"]) - downloader.download_blob(manifest["config"]["digest"], config_file) + downloader.download_blob(manifest["config"]["digest"], _digest_into_blob_path(manifest["config"]["digest"])) # download all layers # TODO: we should avoid eager-download of the layers ("shallow pull") - tars = [] for layer in manifest["layers"]: - blob_digest = _trim_hash_algorithm(layer["digest"]) - downloader.download_blob(layer["digest"], blob_digest) - - if blob_digest not in tars: - tars.append(blob_digest) + downloader.download_blob(layer["digest"], _digest_into_blob_path(layer["digest"])) rctx.file("index.json", util.build_manifest_json( media_type = manifest["mediaType"], size = size, digest = digest, - platform = rctx.attr.platform + platform = rctx.attr.platform, )) rctx.file("oci-layout", json.encode_indent({"imageLayoutVersion": "1.0.0"}, indent = " ")) - + rctx.file("BUILD.bazel", content = _BUILD_FILE_TMPL.format( - target_name = rctx.attr.target_name, - tars = tars, - image_digest = _trim_hash_algorithm(digest), - config_file = config_file, + target_name = rctx.attr.target_name )) oci_pull = repository_rule(