@@ -921,6 +921,29 @@ impl Client {
921921 self . _pull_image_manifest ( image) . await
922922 }
923923
924+ /// Pull a manifest from the remote OCI Distribution service.
925+ ///
926+ /// The client will check if it's already been authenticated and if
927+ /// not will attempt to do.
928+ ///
929+ /// Returns `(image_manifest, manifest_digest, Option<manifest_list_digest>)`.
930+ /// The manifest list digest is `Some` when the original reference pointed to
931+ /// an image index / manifest list; `None` when it pointed directly to a
932+ /// single-platform image manifest.
933+ ///
934+ /// If a multi-platform Image Index manifest is encountered, a platform-specific
935+ /// Image manifest will be selected using the client's default platform resolution.
936+ pub async fn pull_image_manifest_and_list_digest (
937+ & self ,
938+ image : & Reference ,
939+ auth : & RegistryAuth ,
940+ ) -> Result < ( OciImageManifest , String , Option < String > ) > {
941+ self . store_auth_if_needed ( image. resolve_registry ( ) , auth)
942+ . await ;
943+
944+ self . _pull_image_manifest_and_list_digest ( image) . await
945+ }
946+
924947 /// Pull a manifest from the remote OCI Distribution service without parsing it.
925948 ///
926949 /// The client will check if it's already been authenticated and if
@@ -966,25 +989,48 @@ impl Client {
966989 /// If a multi-platform Image Index manifest is encountered, a platform-specific
967990 /// Image manifest will be selected using the client's default platform resolution.
968991 async fn _pull_image_manifest ( & self , image : & Reference ) -> Result < ( OciImageManifest , String ) > {
992+ let ( manifest, digest, _list_digest) =
993+ self . _pull_image_manifest_and_list_digest ( image) . await ?;
994+ Ok ( ( manifest, digest) )
995+ }
996+
997+ /// Pull an image manifest from the remote OCI Distribution service,
998+ /// also returning the manifest list digest if the image is multi-arch.
999+ ///
1000+ /// If the connection has already gone through authentication, this will
1001+ /// use the bearer token. Otherwise, this will attempt an anonymous pull.
1002+ ///
1003+ /// Returns `(image_manifest, manifest_digest, Option<manifest_list_digest>)`.
1004+ /// The manifest list digest is `Some` when the original reference pointed to
1005+ /// an image index / manifest list; `None` when it pointed directly to a
1006+ /// single-platform image manifest.
1007+ async fn _pull_image_manifest_and_list_digest (
1008+ & self ,
1009+ image : & Reference ,
1010+ ) -> Result < ( OciImageManifest , String , Option < String > ) > {
9691011 let ( manifest, digest) = self . _pull_manifest ( image) . await ?;
9701012 match manifest {
971- OciManifest :: Image ( image_manifest) => Ok ( ( image_manifest, digest) ) ,
1013+ OciManifest :: Image ( image_manifest) => Ok ( ( image_manifest, digest, None ) ) ,
9721014 OciManifest :: ImageIndex ( image_index_manifest) => {
1015+ let list_digest = digest;
9731016 debug ! ( "Inspecting Image Index Manifest" ) ;
974- let digest = if let Some ( resolver) = & self . config . platform_resolver {
1017+ let platform_digest = if let Some ( resolver) = & self . config . platform_resolver {
9751018 resolver ( & image_index_manifest. manifests )
9761019 } else {
9771020 return Err ( OciDistributionError :: ImageIndexParsingNoPlatformResolverError ) ;
9781021 } ;
9791022
980- match digest {
981- Some ( digest) => {
982- debug ! ( "Selected manifest entry with digest: {}" , digest) ;
983- let manifest_entry_reference = image. clone_with_digest ( digest. clone ( ) ) ;
1023+ match platform_digest {
1024+ Some ( platform_digest) => {
1025+ debug ! ( "Selected manifest entry with digest: {}" , platform_digest) ;
1026+ let manifest_entry_reference =
1027+ image. clone_with_digest ( platform_digest. clone ( ) ) ;
9841028 self . _pull_manifest ( & manifest_entry_reference)
9851029 . await
9861030 . and_then ( |( manifest, _digest) | match manifest {
987- OciManifest :: Image ( manifest) => Ok ( ( manifest, digest) ) ,
1031+ OciManifest :: Image ( manifest) => {
1032+ Ok ( ( manifest, platform_digest, Some ( list_digest) ) )
1033+ }
9881034 OciManifest :: ImageIndex ( _) => {
9891035 Err ( OciDistributionError :: ImageManifestNotFoundError (
9901036 "received Image Index manifest instead" . to_string ( ) ,
@@ -1095,25 +1141,77 @@ impl Client {
10951141 digest,
10961142 String :: from_utf8 ( config. data . into ( ) ) . map_err ( |e| {
10971143 OciDistributionError :: GenericError ( Some ( format ! (
1098- "Cannot not UTF8 compliant : {e}"
1144+ "Cannot parse config as UTF-8 string : {e}"
10991145 ) ) )
11001146 } ) ?,
11011147 ) )
11021148 } )
11031149 }
11041150
1151+ /// Pull a manifest and its config from the remote OCI Distribution service.
1152+ ///
1153+ /// The client will check if it's already been authenticated and if
1154+ /// not will attempt to do.
1155+ ///
1156+ /// Returns `(image_manifest, manifest_digest, config_json, Option<manifest_list_digest>)`.
1157+ /// The manifest list digest is `Some` when the original reference pointed to
1158+ /// an image index / manifest list; `None` when it pointed directly to a
1159+ /// single-platform image manifest.
1160+ ///
1161+ /// If a multi-platform Image Index manifest is encountered, a platform-specific
1162+ /// Image manifest will be selected using the client's default platform resolution.
1163+ pub async fn pull_manifest_and_config_and_list_digest (
1164+ & self ,
1165+ image : & Reference ,
1166+ auth : & RegistryAuth ,
1167+ ) -> Result < ( OciImageManifest , String , String , Option < String > ) > {
1168+ self . store_auth_if_needed ( image. resolve_registry ( ) , auth)
1169+ . await ;
1170+
1171+ self . _pull_manifest_and_config_and_list_digest ( image)
1172+ . await
1173+ . and_then ( |( manifest, digest, config, list_digest) | {
1174+ Ok ( (
1175+ manifest,
1176+ digest,
1177+ String :: from_utf8 ( config. data . into ( ) ) . map_err ( |e| {
1178+ OciDistributionError :: GenericError ( Some ( format ! (
1179+ "Cannot parse config as UTF-8 string: {e}"
1180+ ) ) )
1181+ } ) ?,
1182+ list_digest,
1183+ ) )
1184+ } )
1185+ }
1186+
11051187 async fn _pull_manifest_and_config (
11061188 & self ,
11071189 image : & Reference ,
11081190 ) -> Result < ( OciImageManifest , String , Config ) > {
1109- let ( manifest, digest) = self . _pull_image_manifest ( image) . await ?;
1191+ let ( manifest, digest, config, _list_digest) = self
1192+ . _pull_manifest_and_config_and_list_digest ( image)
1193+ . await ?;
1194+ Ok ( ( manifest, digest, config) )
1195+ }
1196+
1197+ async fn _pull_manifest_and_config_and_list_digest (
1198+ & self ,
1199+ image : & Reference ,
1200+ ) -> Result < ( OciImageManifest , String , Config , Option < String > ) > {
1201+ let ( manifest, digest, list_digest) =
1202+ self . _pull_image_manifest_and_list_digest ( image) . await ?;
11101203
11111204 let mut out: Vec < u8 > = Vec :: new ( ) ;
11121205 debug ! ( "Pulling config layer" ) ;
11131206 self . pull_blob ( image, & manifest. config , & mut out) . await ?;
11141207 let media_type = manifest. config . media_type . clone ( ) ;
11151208 let annotations = manifest. annotations . clone ( ) ;
1116- Ok ( ( manifest, digest, Config :: new ( out, media_type, annotations) ) )
1209+ Ok ( (
1210+ manifest,
1211+ digest,
1212+ Config :: new ( out, media_type, annotations) ,
1213+ list_digest,
1214+ ) )
11171215 }
11181216
11191217 /// Push a manifest list to an OCI registry.
0 commit comments