Skip to content

Commit 0f9b25d

Browse files
author
Mirroring
committed
Merge commit '76d396cdffbe0848ff16601e9f943a9718a09cfe'
2 parents f1376cb + 76d396c commit 0f9b25d

34 files changed

+1956
-43
lines changed

src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ internal ImageBuilder(ManifestV2 manifest, string manifestMediaType, ImageConfig
5050
/// </summary>
5151
public bool IsWindows => _baseImageConfig.IsWindows;
5252

53+
// For tests
54+
internal string ManifestConfigDigest => _manifest.Config.digest;
55+
5356
/// <summary>
5457
/// Builds the image configuration <see cref="BuiltImage"/> ready for further processing.
5558
/// </summary>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Text.Json;
5+
using System.Text.Json.Nodes;
6+
using Microsoft.NET.Build.Containers.Resources;
7+
using Microsoft.NET.Build.Containers.Tasks;
8+
9+
namespace Microsoft.NET.Build.Containers;
10+
11+
internal readonly struct ImageInfo
12+
{
13+
internal string Config { get; init; }
14+
internal string ManifestDigest { get; init; }
15+
internal string Manifest { get; init; }
16+
internal string ManifestMediaType { get; init; }
17+
18+
public override string ToString() => ManifestDigest;
19+
}
20+
21+
internal static class ImageIndexGenerator
22+
{
23+
/// <summary>
24+
/// Generates an image index from the given images.
25+
/// </summary>
26+
/// <param name="imageInfos"></param>
27+
/// <returns>Returns json string of image index and image index mediaType.</returns>
28+
/// <exception cref="ArgumentException"></exception>
29+
/// <exception cref="NotSupportedException"></exception>
30+
internal static (string, string) GenerateImageIndex(ImageInfo[] imageInfos)
31+
{
32+
if (imageInfos.Length == 0)
33+
{
34+
throw new ArgumentException(string.Format(Strings.ImagesEmpty));
35+
}
36+
37+
string manifestMediaType = imageInfos[0].ManifestMediaType;
38+
39+
if (!imageInfos.All(image => string.Equals(image.ManifestMediaType, manifestMediaType, StringComparison.OrdinalIgnoreCase)))
40+
{
41+
throw new ArgumentException(Strings.MixedMediaTypes);
42+
}
43+
44+
if (manifestMediaType == SchemaTypes.DockerManifestV2)
45+
{
46+
return GenerateImageIndex(imageInfos, SchemaTypes.DockerManifestV2, SchemaTypes.DockerManifestListV2);
47+
}
48+
else if (manifestMediaType == SchemaTypes.OciManifestV1)
49+
{
50+
return GenerateImageIndex(imageInfos, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1);
51+
}
52+
else
53+
{
54+
throw new NotSupportedException(string.Format(Strings.UnsupportedMediaType, manifestMediaType));
55+
}
56+
}
57+
58+
private static (string, string) GenerateImageIndex(ImageInfo[] images, string manifestMediaType, string imageIndexMediaType)
59+
{
60+
// Here we are using ManifestListV2 struct, but we could use ImageIndexV1 struct as well.
61+
// We are filling the same fiels, so we can use the same struct.
62+
var manifests = new PlatformSpecificManifest[images.Length];
63+
for (int i = 0; i < images.Length; i++)
64+
{
65+
var image = images[i];
66+
67+
var manifest = new PlatformSpecificManifest
68+
{
69+
mediaType = manifestMediaType,
70+
size = image.Manifest.Length,
71+
digest = image.ManifestDigest,
72+
platform = GetArchitectureAndOsFromConfig(image)
73+
};
74+
manifests[i] = manifest;
75+
}
76+
77+
var dockerManifestList = new ManifestListV2
78+
{
79+
schemaVersion = 2,
80+
mediaType = imageIndexMediaType,
81+
manifests = manifests
82+
};
83+
84+
return (JsonSerializer.SerializeToNode(dockerManifestList)?.ToJsonString() ?? "", dockerManifestList.mediaType);
85+
}
86+
87+
private static PlatformInformation GetArchitectureAndOsFromConfig(ImageInfo image)
88+
{
89+
var configJson = JsonNode.Parse(image.Config) as JsonObject ??
90+
throw new ArgumentException($"{nameof(image.Config)} should be a JSON object.", nameof(image.Config));
91+
92+
var architecture = configJson["architecture"]?.ToString() ??
93+
throw new ArgumentException($"{nameof(image.Config)} should contain 'architecture'.", nameof(image.Config));
94+
95+
var os = configJson["os"]?.ToString() ??
96+
throw new ArgumentException($"{nameof(image.Config)} should contain 'os'.", nameof(image.Config));
97+
98+
return new PlatformInformation { architecture = architecture, os = os };
99+
}
100+
}

src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerDigest.get
6262
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerDigest.set -> void
6363
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedArchiveOutputPath.get -> string!
6464
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedArchiveOutputPath.set -> void
65+
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerMediaType.get -> string!
66+
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerMediaType.set -> void
6567
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.Repository.get -> string!
6668
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.Repository.set -> void
6769
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageTags.get -> string![]!
@@ -123,8 +125,8 @@ Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsTrimmed.get
123125
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsTrimmed.set -> void
124126
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsSelfContained.get -> bool
125127
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsSelfContained.set -> void
126-
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifier.get -> string!
127-
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifier.set -> void
128+
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifiers.get -> string![]!
129+
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifiers.set -> void
128130
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.get -> bool
129131
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.set -> void
130132
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag

src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,27 @@ Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsTrimmed.get
1212
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsTrimmed.set -> void
1313
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsSelfContained.get -> bool
1414
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsSelfContained.set -> void
15-
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifier.get -> string!
16-
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifier.set -> void
15+
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifiers.get -> string![]!
16+
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifiers.set -> void
1717
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UserBaseImage.get -> string?
1818
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UserBaseImage.set -> void
1919
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.get -> bool
2020
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.set -> void
21+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex
22+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Cancel() -> void
23+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.CreateImageIndex() -> void
24+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Dispose() -> void
25+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedImageIndex.get -> string!
26+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedImageIndex.set -> void
27+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ImageTags.get -> string![]!
28+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ImageTags.set -> void
29+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedContainers.get -> Microsoft.Build.Framework.ITaskItem![]!
30+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedContainers.set -> void
31+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.OutputRegistry.get -> string!
32+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.OutputRegistry.set -> void
33+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Repository.get -> string!
34+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Repository.set -> void
35+
override Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Execute() -> bool
2136
static readonly Microsoft.NET.Build.Containers.Constants.Version -> string!
2237
Microsoft.NET.Build.Containers.ContainerHelpers
2338
Microsoft.NET.Build.Containers.ContainerHelpers.ParsePortError
@@ -196,6 +211,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerDigest.get
196211
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerDigest.set -> void
197212
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedArchiveOutputPath.get -> string!
198213
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedArchiveOutputPath.set -> void
214+
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerMediaType.get -> string!
215+
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerMediaType.set -> void
199216
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.Repository.get -> string!
200217
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.Repository.set -> void
201218
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageTags.get -> string![]!

src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultManifestOperations.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,9 @@ public async Task<HttpResponseMessage> GetAsync(string repositoryName, string re
3838
};
3939
}
4040

41-
public async Task PutAsync(string repositoryName, string reference, ManifestV2 manifest, string mediaType, CancellationToken cancellationToken)
41+
public async Task PutAsync(string repositoryName, string reference, string manifestJson, string mediaType, CancellationToken cancellationToken)
4242
{
43-
string jsonString = JsonSerializer.SerializeToNode(manifest)?.ToJsonString() ?? "";
44-
HttpContent manifestUploadContent = new StringContent(jsonString);
43+
HttpContent manifestUploadContent = new StringContent(manifestJson);
4544
manifestUploadContent.Headers.ContentType = new MediaTypeHeaderValue(mediaType);
4645

4746
HttpResponseMessage putResponse = await _client.PutAsync(new Uri(_baseUri, $"/v2/{repositoryName}/manifests/{reference}"), manifestUploadContent, cancellationToken).ConfigureAwait(false);

src/Containers/Microsoft.NET.Build.Containers/Registry/IManifestOperations.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ internal interface IManifestOperations
1414
{
1515
public Task<HttpResponseMessage> GetAsync(string repositoryName, string reference, CancellationToken cancellationToken);
1616

17-
public Task PutAsync(string repositoryName, string reference, ManifestV2 manifest, string mediaType, CancellationToken cancellationToken);
17+
public Task PutAsync(string repositoryName, string reference, string manifestListJson, string mediaType, CancellationToken cancellationToken);
1818
}

src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33

44
using Microsoft.Extensions.Logging;
55
using Microsoft.NET.Build.Containers.Resources;
6+
using NuGet.Packaging;
67
using NuGet.RuntimeModel;
78
using System.Diagnostics;
89
using System.Net.Http.Json;
910
using System.Text.Json;
1011
using System.Text.Json.Nodes;
11-
using System.Text.RegularExpressions;
1212

1313
namespace Microsoft.NET.Build.Containers;
1414

@@ -527,6 +527,17 @@ private async Task UploadBlobAsync(string repository, string digest, Stream cont
527527

528528
}
529529

530+
public async Task PushManifestListAsync(string repositoryName, string[] tags, string manifestListJson, string mediaType, CancellationToken cancellationToken)
531+
{
532+
cancellationToken.ThrowIfCancellationRequested();
533+
foreach (var tag in tags)
534+
{
535+
_logger.LogInformation(Strings.Registry_TagUploadStarted, tag, RegistryName);
536+
await _registryAPI.Manifest.PutAsync(repositoryName, tag, manifestListJson, mediaType, cancellationToken).ConfigureAwait(false);
537+
_logger.LogInformation(Strings.Registry_TagUploaded, tag, RegistryName);
538+
}
539+
}
540+
530541
public Task PushAsync(BuiltImage builtImage, SourceImageReference source, DestinationImageReference destination, CancellationToken cancellationToken)
531542
=> PushAsync(builtImage, source, destination, pushTags: true, cancellationToken);
532543

@@ -591,21 +602,22 @@ private async Task PushAsync(BuiltImage builtImage, SourceImageReference source,
591602
// Tags can refer to an image manifest or an image manifest list.
592603
// In the first case, we push tags to the registry.
593604
// In the second case, we push the manifest digest so the manifest list can refer to it.
605+
string manifestJson = JsonSerializer.SerializeToNode(builtImage.Manifest)?.ToJsonString() ?? "";
594606
if (pushTags)
595607
{
596608
Debug.Assert(destination.Tags.Length > 0);
597609
foreach (string tag in destination.Tags)
598610
{
599611
_logger.LogInformation(Strings.Registry_TagUploadStarted, tag, RegistryName);
600-
await _registryAPI.Manifest.PutAsync(destination.Repository, tag, builtImage.Manifest, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false);
612+
await _registryAPI.Manifest.PutAsync(destination.Repository, tag, manifestJson, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false);
601613
_logger.LogInformation(Strings.Registry_TagUploaded, tag, RegistryName);
602614
}
603615
}
604616
else
605617
{
606618
string manifestDigest = builtImage.Manifest.GetDigest();
607619
_logger.LogInformation(Strings.Registry_ManifestUploadStarted, RegistryName, manifestDigest);
608-
await _registryAPI.Manifest.PutAsync(destination.Repository, manifestDigest, builtImage.Manifest, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false);
620+
await _registryAPI.Manifest.PutAsync(destination.Repository, manifestDigest, manifestJson, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false);
609621
_logger.LogInformation(Strings.Registry_ManifestUploaded, RegistryName);
610622
}
611623
}

src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs

Lines changed: 63 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)