/// <summary> /// Downloads the manifest for an OCI artifact. /// </summary> /// <param name="options">Options for the download operation.</param> /// <param name="cancellationToken">The cancellation token to use.</param> /// <returns>The download manifest result.</returns> public virtual async Task <Response <DownloadManifestResult> > DownloadManifestAsync(DownloadManifestOptions options, CancellationToken cancellationToken = default) { Argument.AssertNotNull(options, nameof(options)); using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(ContainerRegistryBlobClient)}.{nameof(DownloadManifest)}"); scope.Start(); try { Response <ManifestWrapper> response = await _restClient.GetManifestAsync(_repositoryName, options.Tag ?? options.Digest, ManifestMediaType.OciManifest.ToString(), cancellationToken).ConfigureAwait(false); Response rawResponse = response.GetRawResponse(); rawResponse.Headers.TryGetValue("Docker-Content-Digest", out var digest); if (!ValidateDigest(rawResponse.ContentStream, digest)) { throw _clientDiagnostics.CreateRequestFailedException(rawResponse, new ResponseError(null, "The requested digest does not match the digest of the received manifest.")); } using var document = JsonDocument.Parse(rawResponse.ContentStream); var manifest = OciManifest.DeserializeOciManifest(document.RootElement); rawResponse.ContentStream.Position = 0; return(Response.FromValue(new DownloadManifestResult(digest, manifest, rawResponse.ContentStream), rawResponse)); } catch (Exception e) { scope.Failed(e); throw; } }
/// <summary> /// Uploads a manifest for an OCI Artifact. /// </summary> /// <param name="manifest">The manifest to upload.</param> /// <param name="options">Options for configuring the upload operation.</param> /// <param name="cancellationToken"> The cancellation token to use. </param> /// <returns></returns> public virtual Response <UploadManifestResult> UploadManifest(OciManifest manifest, UploadManifestOptions options = default, CancellationToken cancellationToken = default) { Argument.AssertNotNull(manifest, nameof(manifest)); options ??= new UploadManifestOptions(); using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(ContainerRegistryBlobClient)}.{nameof(UploadManifest)}"); scope.Start(); try { Stream manifestStream = SerializeManifest(manifest); string manifestDigest = OciBlobDescriptor.ComputeDigest(manifestStream); string tagOrDigest = options.Tag ?? manifestDigest; ResponseWithHeaders <ContainerRegistryCreateManifestHeaders> response = _restClient.CreateManifest(_repositoryName, tagOrDigest, manifestStream, ManifestMediaType.OciManifest.ToString(), cancellationToken); if (!manifestDigest.Equals(response.Headers.DockerContentDigest, StringComparison.Ordinal)) { throw _clientDiagnostics.CreateRequestFailedException(response, new ResponseError(null, "The digest in the response does not match the digest of the uploaded manifest.")); } return(Response.FromValue(new UploadManifestResult(response.Headers.DockerContentDigest), response.GetRawResponse())); } catch (Exception e) { scope.Failed(e); throw; } }
private static Stream SerializeManifest(OciManifest manifest) { MemoryStream stream = new(); Utf8JsonWriter jsonWriter = new(stream); ((IUtf8JsonSerializable)manifest).Write(jsonWriter); jsonWriter.Flush(); stream.Position = 0; return(stream); }
internal DownloadManifestResult(string digest, OciManifest manifest, Stream manifestStream) { Digest = digest; Manifest = manifest; ManifestStream = manifestStream; }
private static OciManifest DeserializeManifest(Stream stream) { using var document = JsonDocument.Parse(stream); return(OciManifest.DeserializeOciManifest(document.RootElement)); }