Skip to content

Commit a61aad5

Browse files
Surayya Huseyn ZadaSurayya Huseyn Zada
authored andcommitted
extract image index creation from DockerCli into ImageIndexGenerator
1 parent f2909c1 commit a61aad5

File tree

3 files changed

+70
-95
lines changed

3 files changed

+70
-95
lines changed

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

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Text.Json;
5-
using System.Text.Json.Nodes;
5+
using System.Text.Json.Serialization;
66
using Microsoft.NET.Build.Containers.Resources;
7-
using Microsoft.NET.Build.Containers.Tasks;
87

98
namespace Microsoft.NET.Build.Containers;
109

@@ -43,48 +42,85 @@ internal static (string, string) GenerateImageIndex(BuiltImage[] images)
4342

4443
if (manifestMediaType == SchemaTypes.DockerManifestV2)
4544
{
46-
return GenerateImageIndex(images, SchemaTypes.DockerManifestV2, SchemaTypes.DockerManifestListV2);
45+
return (GenerateImageIndex(images, SchemaTypes.DockerManifestV2, SchemaTypes.DockerManifestListV2), SchemaTypes.DockerManifestListV2);
4746
}
4847
else if (manifestMediaType == SchemaTypes.OciManifestV1)
4948
{
50-
return GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1);
49+
return (GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1), SchemaTypes.OciImageIndexV1);
5150
}
5251
else
5352
{
5453
throw new NotSupportedException(string.Format(Strings.UnsupportedMediaType, manifestMediaType));
5554
}
5655
}
5756

58-
private static (string, string) GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType)
57+
internal static string GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType)
5958
{
6059
// Here we are using ManifestListV2 struct, but we could use ImageIndexV1 struct as well.
6160
// We are filling the same fields, so we can use the same struct.
6261
var manifests = new PlatformSpecificManifest[images.Length];
62+
6363
for (int i = 0; i < images.Length; i++)
6464
{
65-
var image = images[i];
66-
67-
var manifest = new PlatformSpecificManifest
65+
manifests[i] = new PlatformSpecificManifest
6866
{
6967
mediaType = manifestMediaType,
70-
size = image.Manifest.Length,
71-
digest = image.ManifestDigest,
68+
size = images[i].Manifest.Length,
69+
digest = images[i].ManifestDigest,
7270
platform = new PlatformInformation
7371
{
74-
architecture = image.Architecture!,
75-
os = image.OS!
72+
architecture = images[i].Architecture!,
73+
os = images[i].OS!
7674
}
7775
};
78-
manifests[i] = manifest;
7976
}
8077

81-
var manifestList = new ManifestListV2
78+
var imageIndex = new ManifestListV2
8279
{
8380
schemaVersion = 2,
8481
mediaType = imageIndexMediaType,
8582
manifests = manifests
8683
};
8784

88-
return (JsonSerializer.SerializeToNode(manifestList)?.ToJsonString() ?? "", manifestList.mediaType);
85+
var options = new JsonSerializerOptions
86+
{
87+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
88+
};
89+
90+
return JsonSerializer.SerializeToNode(imageIndex, options)?.ToJsonString() ?? "";
91+
}
92+
93+
internal static string GenerateImageIndexWithAnnotations(string manifestMediaType, string manifestDigest, long manifestSize, string repository, string[] tags)
94+
{
95+
var manifests = new PlatformSpecificOciManifest[tags.Length];
96+
for (int i = 0; i < tags.Length; i++)
97+
{
98+
var tag = tags[i];
99+
manifests[i] = new PlatformSpecificOciManifest
100+
{
101+
mediaType = manifestMediaType,
102+
size = manifestSize,
103+
digest = manifestDigest,
104+
annotations = new Dictionary<string, string>
105+
{
106+
{ "io.containerd.image.name", $"{repository}:{tag}" },
107+
{ "org.opencontainers.image.ref.name", tag }
108+
}
109+
};
110+
}
111+
112+
var index = new ImageIndexV1
113+
{
114+
schemaVersion = 2,
115+
mediaType = SchemaTypes.OciImageIndexV1,
116+
manifests = manifests
117+
};
118+
119+
var options = new JsonSerializerOptions
120+
{
121+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
122+
};
123+
124+
return JsonSerializer.SerializeToNode(index, options)?.ToJsonString() ?? "";
89125
}
90126
}

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

Lines changed: 17 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#endif
88
using System.Text.Json;
99
using System.Text.Json.Nodes;
10-
using System.Text.Json.Serialization;
1110
using Microsoft.DotNet.Cli.Utils;
1211
using Microsoft.Extensions.Logging;
1312
using Microsoft.NET.Build.Containers.Resources;
@@ -461,35 +460,14 @@ private static async Task WriteIndexJsonForOciImage(
461460
{
462461
cancellationToken.ThrowIfCancellationRequested();
463462

464-
var manifests = new PlatformSpecificOciManifest[destinationReference.Tags.Length];
465-
for (int i = 0; i < destinationReference.Tags.Length; i++)
466-
{
467-
var tag = destinationReference.Tags[i];
468-
manifests[i] = new PlatformSpecificOciManifest
469-
{
470-
mediaType = SchemaTypes.OciManifestV1,
471-
size = image.Manifest.Length,
472-
digest = image.ManifestDigest,
473-
annotations = new Dictionary<string, string>
474-
{
475-
{ "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" },
476-
{ "org.opencontainers.image.ref.name", tag }
477-
}
478-
};
479-
}
463+
string indexJson = ImageIndexGenerator.GenerateImageIndexWithAnnotations(
464+
SchemaTypes.OciManifestV1,
465+
image.ManifestDigest,
466+
image.Manifest.Length,
467+
destinationReference.Repository,
468+
destinationReference.Tags);
480469

481-
var index = new ImageIndexV1
482-
{
483-
schemaVersion = 2,
484-
mediaType = SchemaTypes.OciImageIndexV1,
485-
manifests = manifests
486-
};
487-
488-
var options = new JsonSerializerOptions
489-
{
490-
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
491-
};
492-
using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index, options)!.ToJsonString())))
470+
using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(indexJson)))
493471
{
494472
PaxTarEntry indexEntry = new(TarEntryType.RegularFile, "index.json")
495473
{
@@ -548,31 +526,9 @@ private static async Task WriteIndexJsonForMultiArchOciImage(
548526
// 1. create manifest list for the blobs
549527
cancellationToken.ThrowIfCancellationRequested();
550528

551-
var manifests = new PlatformSpecificOciManifest[images.Length];
552-
for (int i = 0; i < images.Length; i++)
553-
{
554-
var manifest = new PlatformSpecificOciManifest
555-
{
556-
mediaType = SchemaTypes.OciManifestV1,
557-
size = images[i].Manifest.Length,
558-
digest = images[i].ManifestDigest,
559-
platform = new PlatformInformation { architecture = images[i].Architecture!, os = images[i].OS! }
560-
};
561-
manifests[i] = manifest;
562-
}
529+
// For multi-arch we publish only oci-formatted image tarballs.
530+
string manifestListJson = ImageIndexGenerator.GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1);
563531

564-
var manifestList = new ImageIndexV1
565-
{
566-
schemaVersion = 2,
567-
mediaType = SchemaTypes.OciImageIndexV1,
568-
manifests = manifests
569-
};
570-
571-
var options = new JsonSerializerOptions
572-
{
573-
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
574-
};
575-
var manifestListJson = JsonSerializer.SerializeToNode(manifestList, options)!.ToJsonString();
576532
var manifestListDigest = DigestUtils.GetDigest(manifestListJson);
577533
var manifestListSha = DigestUtils.GetShaFromDigest(manifestListDigest);
578534
var manifestListPath = $"{_blobsPath}/{manifestListSha}";
@@ -586,34 +542,17 @@ private static async Task WriteIndexJsonForMultiArchOciImage(
586542
await writer.WriteEntryAsync(indexEntry, cancellationToken).ConfigureAwait(false);
587543
}
588544

589-
// 2. create index json that points to manifest list in the blobs
545+
// 2. create index.json that points to manifest list in the blobs
590546
cancellationToken.ThrowIfCancellationRequested();
591547

592-
var manifestsIndexJson = new PlatformSpecificOciManifest[destinationReference.Tags.Length];
593-
for (int i = 0; i < destinationReference.Tags.Length; i++)
594-
{
595-
var tag = destinationReference.Tags[i];
596-
manifestsIndexJson[i] = new PlatformSpecificOciManifest
597-
{
598-
mediaType = SchemaTypes.OciImageIndexV1,
599-
size = manifestListJson.Length,
600-
digest = manifestListDigest,
601-
annotations = new Dictionary<string, string>
602-
{
603-
{ "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" },
604-
{ "org.opencontainers.image.ref.name", tag }
605-
}
606-
};
607-
}
608-
609-
var index = new ImageIndexV1
610-
{
611-
schemaVersion = 2,
612-
mediaType = SchemaTypes.OciImageIndexV1,
613-
manifests = manifestsIndexJson
614-
};
548+
string indexJson = ImageIndexGenerator.GenerateImageIndexWithAnnotations(
549+
SchemaTypes.OciImageIndexV1,
550+
manifestListDigest,
551+
manifestListJson.Length,
552+
destinationReference.Repository,
553+
destinationReference.Tags);
615554

616-
using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index, options)!.ToJsonString())))
555+
using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(indexJson)))
617556
{
618557
PaxTarEntry indexEntry = new(TarEntryType.RegularFile, "index.json")
619558
{

src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public void GenerateDockerManifestList()
9696
];
9797

9898
var (imageIndex, mediaType) = ImageIndexGenerator.GenerateImageIndex(images);
99-
Assert.Equal("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.docker.distribution.manifest.list.v2\\u002Bjson\",\"manifests\":[{\"mediaType\":\"application/vnd.docker.distribution.manifest.v2\\u002Bjson\",\"size\":3,\"digest\":\"sha256:digest1\",\"platform\":{\"architecture\":\"arch1\",\"os\":\"os1\",\"variant\":null,\"features\":null,\"os.version\":null}},{\"mediaType\":\"application/vnd.docker.distribution.manifest.v2\\u002Bjson\",\"size\":3,\"digest\":\"sha256:digest2\",\"platform\":{\"architecture\":\"arch2\",\"os\":\"os2\",\"variant\":null,\"features\":null,\"os.version\":null}}]}", imageIndex);
99+
Assert.Equal("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.docker.distribution.manifest.list.v2\\u002Bjson\",\"manifests\":[{\"mediaType\":\"application/vnd.docker.distribution.manifest.v2\\u002Bjson\",\"size\":3,\"digest\":\"sha256:digest1\",\"platform\":{\"architecture\":\"arch1\",\"os\":\"os1\"}},{\"mediaType\":\"application/vnd.docker.distribution.manifest.v2\\u002Bjson\",\"size\":3,\"digest\":\"sha256:digest2\",\"platform\":{\"architecture\":\"arch2\",\"os\":\"os2\"}}]}", imageIndex);
100100
Assert.Equal(SchemaTypes.DockerManifestListV2, mediaType);
101101
}
102102

@@ -130,7 +130,7 @@ public void GenerateOciImageIndex()
130130
];
131131

132132
var (imageIndex, mediaType) = ImageIndexGenerator.GenerateImageIndex(images);
133-
Assert.Equal("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.index.v1\\u002Bjson\",\"manifests\":[{\"mediaType\":\"application/vnd.oci.image.manifest.v1\\u002Bjson\",\"size\":3,\"digest\":\"sha256:digest1\",\"platform\":{\"architecture\":\"arch1\",\"os\":\"os1\",\"variant\":null,\"features\":null,\"os.version\":null}},{\"mediaType\":\"application/vnd.oci.image.manifest.v1\\u002Bjson\",\"size\":3,\"digest\":\"sha256:digest2\",\"platform\":{\"architecture\":\"arch2\",\"os\":\"os2\",\"variant\":null,\"features\":null,\"os.version\":null}}]}", imageIndex);
133+
Assert.Equal("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.index.v1\\u002Bjson\",\"manifests\":[{\"mediaType\":\"application/vnd.oci.image.manifest.v1\\u002Bjson\",\"size\":3,\"digest\":\"sha256:digest1\",\"platform\":{\"architecture\":\"arch1\",\"os\":\"os1\"}},{\"mediaType\":\"application/vnd.oci.image.manifest.v1\\u002Bjson\",\"size\":3,\"digest\":\"sha256:digest2\",\"platform\":{\"architecture\":\"arch2\",\"os\":\"os2\"}}]}", imageIndex);
134134
Assert.Equal(SchemaTypes.OciImageIndexV1, mediaType);
135135
}
136136
}

0 commit comments

Comments
 (0)