Skip to content

Commit 674ee0e

Browse files
Surayya Huseyn ZadaSurayya Huseyn Zada
authored andcommitted
refactor to bring back GeneratedImageIndex; fix ImageIndexGeneratorTests
1 parent 299e6d1 commit 674ee0e

File tree

13 files changed

+107
-38
lines changed

13 files changed

+107
-38
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ internal static (string, string) GenerateImageIndex(BuiltImage[] images)
4747

4848
internal static string GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType)
4949
{
50+
if (images.Length == 0)
51+
{
52+
throw new ArgumentException(string.Format(Strings.ImagesEmpty));
53+
}
54+
5055
// Here we are using ManifestListV2 struct, but we could use ImageIndexV1 struct as well.
5156
// We are filling the same fields, so we can use the same struct.
5257
var manifests = new PlatformSpecificManifest[images.Length];

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

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ await PushToRemoteRegistryAsync(
5151
}
5252

5353
public static async Task PublishImageAsync(
54-
BuiltImage[] multiArchImage,
54+
MultiArchImage multiArchImage,
5555
SourceImageReference sourceImageReference,
5656
DestinationImageReference destinationImageReference,
5757
Microsoft.Build.Utilities.TaskLoggingHelper Log,
@@ -82,16 +82,7 @@ await PushToRemoteRegistryAsync(
8282
Log,
8383
BuildEngine,
8484
cancellationToken,
85-
async (images, source, destination, token) =>
86-
{
87-
(string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images);
88-
await destinationImageReference.RemoteRegistry!.PushManifestListAsync(
89-
destinationImageReference.Repository,
90-
destinationImageReference.Tags,
91-
imageIndex,
92-
mediaType,
93-
cancellationToken).ConfigureAwait(false);
94-
},
85+
destinationImageReference.RemoteRegistry!.PushManifestListAsync,
9586
Strings.ImageIndexUploadedToRegistry).ConfigureAwait(false);
9687
break;
9788
default:

src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen
4646
=> await LoadAsync(image, sourceReference, destinationReference, cancellationToken,
4747
DockerCli.WriteImageToStreamAsync);
4848

49-
public async Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference,
49+
public async Task LoadAsync(MultiArchImage multiArchImage, SourceImageReference sourceReference,
5050
DestinationImageReference destinationReference,
5151
CancellationToken cancellationToken)
52-
=> await LoadAsync(images, sourceReference, destinationReference, cancellationToken,
52+
=> await LoadAsync(multiArchImage, sourceReference, destinationReference, cancellationToken,
5353
DockerCli.WriteMultiArchOciImageToStreamAsync);
5454

5555
public Task<bool> IsAvailableAsync(CancellationToken cancellationToken) => Task.FromResult(true);

src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen
130130
// For loading to the local registry, we use the Docker format. Two reasons: one - compatibility with previous behavior before oci formatted publishing was available, two - Podman cannot load multi tag oci image tarball.
131131
=> await LoadAsync(image, sourceReference, destinationReference, WriteDockerImageToStreamAsync, cancellationToken);
132132

133-
public async Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken)
134-
=> await LoadAsync(images, sourceReference, destinationReference, WriteMultiArchOciImageToStreamAsync, cancellationToken, checkContainerdStore: true);
133+
public async Task LoadAsync(MultiArchImage multiArchImage, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken)
134+
=> await LoadAsync(multiArchImage, sourceReference, destinationReference, WriteMultiArchOciImageToStreamAsync, cancellationToken, checkContainerdStore: true);
135135

136136
public async Task<bool> IsAvailableAsync(CancellationToken cancellationToken)
137137
{
@@ -500,7 +500,7 @@ await WriteManifestForOciImage(writer, image, cancellationToken)
500500
}
501501

502502
public static async Task WriteMultiArchOciImageToStreamAsync(
503-
BuiltImage[] images,
503+
MultiArchImage multiArchImage,
504504
SourceImageReference sourceReference,
505505
DestinationImageReference destinationReference,
506506
Stream imageStream,
@@ -510,13 +510,13 @@ public static async Task WriteMultiArchOciImageToStreamAsync(
510510

511511
using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true);
512512

513-
foreach (var image in images)
513+
foreach (var image in multiArchImage.Images!)
514514
{
515515
await WriteOciImageToBlobs(writer, image, sourceReference, cancellationToken)
516516
.ConfigureAwait(false);
517517
}
518518

519-
await WriteIndexJsonForMultiArchOciImage(writer, images, destinationReference, cancellationToken)
519+
await WriteIndexJsonForMultiArchOciImage(writer, multiArchImage, destinationReference, cancellationToken)
520520
.ConfigureAwait(false);
521521

522522
await WriteOciLayout(writer, cancellationToken)
@@ -525,21 +525,18 @@ await WriteOciLayout(writer, cancellationToken)
525525

526526
private static async Task WriteIndexJsonForMultiArchOciImage(
527527
TarWriter writer,
528-
BuiltImage[] images,
528+
MultiArchImage multiArchImage,
529529
DestinationImageReference destinationReference,
530530
CancellationToken cancellationToken)
531531
{
532532
// 1. create manifest list for the blobs
533533
cancellationToken.ThrowIfCancellationRequested();
534534

535-
// For multi-arch we publish only oci-formatted image tarballs.
536-
string manifestListJson = ImageIndexGenerator.GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1);
537-
538-
var manifestListDigest = DigestUtils.GetDigest(manifestListJson);
535+
var manifestListDigest = DigestUtils.GetDigest(multiArchImage.ImageIndex);
539536
var manifestListSha = DigestUtils.GetShaFromDigest(manifestListDigest);
540537
var manifestListPath = $"{_blobsPath}/{manifestListSha}";
541538

542-
using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(manifestListJson)))
539+
using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(multiArchImage.ImageIndex)))
543540
{
544541
PaxTarEntry indexEntry = new(TarEntryType.RegularFile, manifestListPath)
545542
{
@@ -552,9 +549,9 @@ private static async Task WriteIndexJsonForMultiArchOciImage(
552549
cancellationToken.ThrowIfCancellationRequested();
553550

554551
string indexJson = ImageIndexGenerator.GenerateImageIndexWithAnnotations(
555-
SchemaTypes.OciImageIndexV1,
552+
multiArchImage.ImageIndexMediaType,
556553
manifestListDigest,
557-
manifestListJson.Length,
554+
multiArchImage.ImageIndex.Length,
558555
destinationReference.Repository,
559556
destinationReference.Tags);
560557

src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ internal interface ILocalRegistry {
1616
/// <summary>
1717
/// Loads a multi-arch image (presumably from a tarball) into the local registry.
1818
/// </summary>
19-
public Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken);
19+
public Task LoadAsync(MultiArchImage multiArchImage, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken);
2020

2121
/// <summary>
2222
/// Checks to see if the local registry is available. This is used to give nice errors to the user.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
namespace Microsoft.NET.Build.Containers;
5+
6+
/// <summary>
7+
/// Represents constructed image ready for further processing.
8+
/// </summary>
9+
internal sealed class MultiArchImage
10+
{
11+
internal required string ImageIndex { get; init; }
12+
13+
internal required string ImageIndexMediaType { get; init; }
14+
15+
internal BuiltImage[]? Images { get; init; }
16+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedArchiveOutputPath
3434
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Cancel() -> void
3535
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.CreateImageIndex() -> void
3636
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Dispose() -> void
37+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedImageIndex.get -> string!
38+
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedImageIndex.set -> void
3739
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ImageTags.get -> string![]!
3840
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ImageTags.set -> void
3941
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedContainers.get -> Microsoft.Build.Framework.ITaskItem![]!

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -525,13 +525,17 @@ private async Task UploadBlobAsync(string repository, string digest, Stream cont
525525

526526
}
527527

528-
public async Task PushManifestListAsync(string repositoryName, string[] tags, string manifestListJson, string mediaType, CancellationToken cancellationToken)
528+
public async Task PushManifestListAsync(
529+
MultiArchImage multiArchImage,
530+
SourceImageReference sourceImageReference,
531+
DestinationImageReference destinationImageReference,
532+
CancellationToken cancellationToken)
529533
{
530534
cancellationToken.ThrowIfCancellationRequested();
531-
foreach (var tag in tags)
535+
foreach (var tag in destinationImageReference.Tags)
532536
{
533537
_logger.LogInformation(Strings.Registry_TagUploadStarted, tag, RegistryName);
534-
await _registryAPI.Manifest.PutAsync(repositoryName, tag, manifestListJson, mediaType, cancellationToken).ConfigureAwait(false);
538+
await _registryAPI.Manifest.PutAsync(destinationImageReference.Repository, tag, multiArchImage.ImageIndex, multiArchImage.ImageIndexMediaType, cancellationToken).ConfigureAwait(false);
535539
_logger.LogInformation(Strings.Registry_TagUploaded, tag, RegistryName);
536540
}
537541
}

src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,18 @@ partial class CreateImageIndex
6161
[Required]
6262
public string[] ImageTags { get; set; }
6363

64+
/// <summary>
65+
/// The generated archive output path.
66+
/// </summary>
6467
[Output]
6568
public string GeneratedArchiveOutputPath { get; set; }
6669

70+
/// <summary>
71+
/// The generated image index (manifest list) in JSON format.
72+
/// </summary>
73+
[Output]
74+
public string GeneratedImageIndex { get; set; }
75+
6776
public CreateImageIndex()
6877
{
6978
BaseRegistry = string.Empty;
@@ -76,5 +85,6 @@ public CreateImageIndex()
7685
Repository = string.Empty;
7786
ImageTags = Array.Empty<string>();
7887
GeneratedArchiveOutputPath = string.Empty;
88+
GeneratedImageIndex = string.Empty;
7989
}
8090
}

src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,17 @@ internal async Task<bool> ExecuteAsync(CancellationToken cancellationToken)
7474
return false;
7575
}
7676

77+
var multiArchImage = CreateMultiArchImage(images, destinationImageReference.Kind);
78+
79+
GeneratedImageIndex = multiArchImage.ImageIndex;
80+
GeneratedArchiveOutputPath = ArchiveOutputPath;
81+
7782
logger.LogInformation(Strings.BuildingImageIndex, destinationImageReference, string.Join(", ", images.Select(i => i.ManifestDigest)));
7883

7984
var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log);
8085

81-
await ImagePublisher.PublishImageAsync(images, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken)
82-
.ConfigureAwait(false);
83-
84-
GeneratedArchiveOutputPath = ArchiveOutputPath;
86+
await ImagePublisher.PublishImageAsync(multiArchImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken)
87+
.ConfigureAwait(false);
8588

8689
return !Log.HasLoggedErrors;
8790
}
@@ -157,4 +160,29 @@ private BuiltImage[] ParseImages(DestinationImageReferenceKind destinationKind)
157160
}
158161
return (architecture, os);
159162
}
163+
164+
private static MultiArchImage CreateMultiArchImage(BuiltImage[] images, DestinationImageReferenceKind destinationImageKind)
165+
{
166+
switch (destinationImageKind)
167+
{
168+
case DestinationImageReferenceKind.LocalRegistry:
169+
return new MultiArchImage()
170+
{
171+
// For multi-arch we publish only oci-formatted image tarballs.
172+
ImageIndex = ImageIndexGenerator.GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1),
173+
ImageIndexMediaType = SchemaTypes.OciImageIndexV1,
174+
Images = images
175+
};
176+
case DestinationImageReferenceKind.RemoteRegistry:
177+
(string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images);
178+
return new MultiArchImage()
179+
{
180+
ImageIndex = imageIndex,
181+
ImageIndexMediaType = mediaType,
182+
// For remote registry we don't need individual images, as they should be pushed already
183+
};
184+
default:
185+
throw new ArgumentOutOfRangeException();
186+
}
187+
}
160188
}

0 commit comments

Comments
 (0)