From 81bcbf3a23c7cc576659d900486de23e13b22de2 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Tue, 28 Jan 2025 20:46:40 +0100 Subject: [PATCH 01/74] fix e2e test after checking option 'Use containerd for pulling and storing images' --- .../EndToEndTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index 3b0015a10f0a..1cef040a5e34 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -1033,6 +1033,8 @@ public void EndToEndMultiArch_RemoteRegistry() CommandResult processResultX64 = ContainerCli.RunCommand( _testOutput, "--rm", + "--platform", + "linux/amd64", "--name", $"test-container-{imageName}-x64", imageX64Tagged) @@ -1056,6 +1058,8 @@ public void EndToEndMultiArch_RemoteRegistry() CommandResult processResultArm64 = ContainerCli.RunCommand( _testOutput, "--rm", + "--platform", + "linux/arm64", "--name", $"test-container-{imageName}-arm64", imageArm64Tagged) From 01a9c07e64901baf22a85ab43ff99c76cdecc992 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 29 Jan 2025 13:24:00 +0100 Subject: [PATCH 02/74] refactor BuiltImage --- .../Microsoft.NET.Build.Containers/BuiltImage.cs | 15 ++++++++++----- .../ImageBuilder.cs | 8 +++++--- .../LocalDaemons/DockerCli.cs | 13 +++++-------- .../Registry/Registry.cs | 8 +++----- .../Tasks/CreateNewImage.cs | 4 ++-- .../EndToEndTests.cs | 3 ++- 6 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs index 64d4e9a528ef..c4572b878340 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs @@ -24,20 +24,25 @@ internal readonly struct BuiltImage internal required string ImageSha { get; init; } /// - /// Gets image size. + /// Gets image manifest. /// - internal required long ImageSize { get; init; } + internal required string Manifest { get; init; } /// - /// Gets image manifest. + /// Gets manifest digest. /// - internal required ManifestV2 Manifest { get; init; } + internal required string ManifestDigest { get; init; } /// /// Gets manifest mediaType. /// internal required string ManifestMediaType { get; init; } + /// + /// Gets image layers. + /// + internal required List Layers { get; init; } + /// /// Gets layers descriptors. /// @@ -45,7 +50,7 @@ internal IEnumerable LayerDescriptors { get { - List layersNode = Manifest.Layers ?? throw new NotImplementedException("Tried to get layer information but there is no layer node?"); + List layersNode = Layers ?? throw new NotImplementedException("Tried to get layer information but there is no layer node?"); foreach (ManifestLayer layer in layersNode) { yield return new(layer.mediaType, layer.digest, layer.size); diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs index bf0876344f28..633ca8e98860 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs @@ -5,6 +5,7 @@ using Microsoft.NET.Build.Containers.Resources; using Microsoft.Extensions.Logging; using System.Text.RegularExpressions; +using System.Text.Json; namespace Microsoft.NET.Build.Containers; @@ -86,9 +87,10 @@ internal BuiltImage Build() Config = imageJsonStr, ImageDigest = imageDigest, ImageSha = imageSha, - ImageSize = imageSize, - Manifest = newManifest, - ManifestMediaType = ManifestMediaType + Manifest = JsonSerializer.SerializeToNode(newManifest)?.ToJsonString() ?? "", + ManifestDigest = newManifest.GetDigest(), + ManifestMediaType = ManifestMediaType, + Layers = _manifest.Layers }; } diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 7e779e9ec3c0..dc151bd7836f 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -279,7 +279,7 @@ public static async Task WriteImageToStreamAsync(BuiltImage image, SourceImageRe } else { - throw new ArgumentException(Resource.FormatString(nameof(Strings.UnsupportedMediaTypeForTarball), image.Manifest.MediaType)); + throw new ArgumentException(Resource.FormatString(nameof(Strings.UnsupportedMediaTypeForTarball), image.ManifestMediaType)); } } @@ -441,12 +441,9 @@ private static async Task WriteManifestForOciImage( { cancellationToken.ThrowIfCancellationRequested(); - string manifestContent = JsonSerializer.SerializeToNode(image.Manifest)!.ToJsonString(); - string manifestDigest = image.Manifest.GetDigest(); - // 1. add manifest to blobs - string manifestPath = $"{_blobsPath}/{manifestDigest.Substring("sha256:".Length)}"; - using (MemoryStream manifestStream = new MemoryStream(Encoding.UTF8.GetBytes(manifestContent))) + string manifestPath = $"{_blobsPath}/{image.ManifestDigest.Substring("sha256:".Length)}"; + using (MemoryStream manifestStream = new MemoryStream(Encoding.UTF8.GetBytes(image.Manifest))) { PaxTarEntry manifestEntry = new(TarEntryType.RegularFile, manifestPath) { @@ -467,8 +464,8 @@ private static async Task WriteManifestForOciImage( new PlatformSpecificOciManifest { mediaType = SchemaTypes.OciManifestV1, - size = manifestContent.Length, - digest = manifestDigest, + size = image.Manifest.Length, + digest = image.ManifestDigest, annotations = new Dictionary { { "org.opencontainers.image.ref.name", $"{destinationReference.Repository}:{destinationReference.Tags[0]}" } } } ] diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs index f09cfd6fd4a3..5ab07dc9713e 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs @@ -602,22 +602,20 @@ private async Task PushAsync(BuiltImage builtImage, SourceImageReference source, // Tags can refer to an image manifest or an image manifest list. // In the first case, we push tags to the registry. // In the second case, we push the manifest digest so the manifest list can refer to it. - string manifestJson = JsonSerializer.SerializeToNode(builtImage.Manifest)?.ToJsonString() ?? ""; if (pushTags) { Debug.Assert(destination.Tags.Length > 0); foreach (string tag in destination.Tags) { _logger.LogInformation(Strings.Registry_TagUploadStarted, tag, RegistryName); - await _registryAPI.Manifest.PutAsync(destination.Repository, tag, manifestJson, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false); + await _registryAPI.Manifest.PutAsync(destination.Repository, tag, builtImage.Manifest, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false); _logger.LogInformation(Strings.Registry_TagUploaded, tag, RegistryName); } } else { - string manifestDigest = builtImage.Manifest.GetDigest(); - _logger.LogInformation(Strings.Registry_ManifestUploadStarted, RegistryName, manifestDigest); - await _registryAPI.Manifest.PutAsync(destination.Repository, manifestDigest, manifestJson, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false); + _logger.LogInformation(Strings.Registry_ManifestUploadStarted, RegistryName, builtImage.ManifestDigest); + await _registryAPI.Manifest.PutAsync(destination.Repository, builtImage.ManifestDigest, builtImage.Manifest, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false); _logger.LogInformation(Strings.Registry_ManifestUploaded, RegistryName); } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index 25ba26080af9..7368fd337596 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -170,9 +170,9 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) cancellationToken.ThrowIfCancellationRequested(); // at this point we're done with modifications and are just pushing the data other places - GeneratedContainerManifest = JsonSerializer.Serialize(builtImage.Manifest); + GeneratedContainerManifest = builtImage.Manifest; GeneratedContainerConfiguration = builtImage.Config; - GeneratedContainerDigest = builtImage.Manifest.GetDigest(); + GeneratedContainerDigest = builtImage.ManifestDigest; GeneratedArchiveOutputPath = ArchiveOutputPath; GeneratedContainerMediaType = builtImage.ManifestMediaType; GeneratedContainerNames = destinationImageReference.FullyQualifiedImageNames().Select(name => new Microsoft.Build.Utilities.TaskItem(name)).ToArray(); diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index 1cef040a5e34..2d562b4feaa7 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -235,9 +235,10 @@ private BuiltImage ConvertToOciImage(BuiltImage builtImage) Config = builtImage.Config, ImageDigest = builtImage.ImageDigest, ImageSha = builtImage.ImageSha, - ImageSize = builtImage.ImageSize, Manifest = builtImage.Manifest, + ManifestDigest = builtImage.ManifestDigest, ManifestMediaType = SchemaTypes.OciManifestV1, + Layers = builtImage.Layers }; return ociImage; From d973c0e7d210d71d922f6b33e43c92a968519f8a Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 29 Jan 2025 18:43:37 +0100 Subject: [PATCH 03/74] refactor single-arch oci tarball publishing --- .../LocalDaemons/DockerCli.cs | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index dc151bd7836f..68e06d044c3c 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -7,6 +7,7 @@ #endif using System.Text.Json; using System.Text.Json.Nodes; +using System.Text.Json.Serialization; using Microsoft.DotNet.Cli.Utils; using Microsoft.Extensions.Logging; using Microsoft.NET.Build.Containers.Resources; @@ -396,12 +397,13 @@ private static async Task WriteOciImageToStreamAsync( Stream imageStream, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + if (destinationReference.Tags.Length > 1) { throw new ArgumentException(Resource.FormatString(nameof(Strings.OciImageMultipleTagsNotSupported))); } - cancellationToken.ThrowIfCancellationRequested(); using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true); await WriteOciLayout(writer, cancellationToken) @@ -413,7 +415,10 @@ await WriteImageLayers(writer, image, sourceReference, d => $"{_blobsPath}/{d.Su await WriteImageConfig(writer, image, $"{_blobsPath}/{image.ImageSha}", cancellationToken) .ConfigureAwait(false); - await WriteManifestForOciImage(writer, image, destinationReference, cancellationToken) + await WriteManifestForOciImage(writer, image, cancellationToken) + .ConfigureAwait(false); + + await WriteIndexJsonForOciImage(writer, image, destinationReference, cancellationToken) .ConfigureAwait(false); } @@ -436,12 +441,10 @@ private static async Task WriteOciLayout(TarWriter writer, CancellationToken can private static async Task WriteManifestForOciImage( TarWriter writer, BuiltImage image, - DestinationImageReference destinationReference, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - // 1. add manifest to blobs string manifestPath = $"{_blobsPath}/{image.ManifestDigest.Substring("sha256:".Length)}"; using (MemoryStream manifestStream = new MemoryStream(Encoding.UTF8.GetBytes(image.Manifest))) { @@ -451,10 +454,18 @@ private static async Task WriteManifestForOciImage( }; await writer.WriteEntryAsync(manifestEntry, cancellationToken).ConfigureAwait(false); } + } + private static async Task WriteIndexJsonForOciImage( + TarWriter writer, + BuiltImage image, + DestinationImageReference destinationReference, + CancellationToken cancellationToken) + { cancellationToken.ThrowIfCancellationRequested(); - // 2. add index.json + string tag = destinationReference.Tags[0]; + var index = new ImageIndexV1 { schemaVersion = 2, @@ -466,11 +477,20 @@ private static async Task WriteManifestForOciImage( mediaType = SchemaTypes.OciManifestV1, size = image.Manifest.Length, digest = image.ManifestDigest, - annotations = new Dictionary { { "org.opencontainers.image.ref.name", $"{destinationReference.Repository}:{destinationReference.Tags[0]}" } } + annotations = new Dictionary + { + { "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" }, + { "org.opencontainers.image.ref.name", tag } + } } ] }; - using (MemoryStream indexStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index)!.ToJsonString()))) + + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index, options)!.ToJsonString()))) { PaxTarEntry indexEntry = new(TarEntryType.RegularFile, "index.json") { From 2363f9449ef36156c88ab30a450a80a91b74e08f Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Fri, 31 Jan 2025 13:00:23 +0100 Subject: [PATCH 04/74] implement loading multi-arch image as a tarball and to a local registry --- .../BuiltImage.cs | 10 ++ .../DigestUtils.cs | 10 ++ .../LocalDaemons/ArchiveFileRegistry.cs | 29 ++-- .../LocalDaemons/DockerCli.cs | 152 ++++++++++++++++-- .../LocalDaemons/ILocalRegistry.cs | 5 + 5 files changed, 183 insertions(+), 23 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs index c4572b878340..84d3b11c42c9 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs @@ -43,6 +43,16 @@ internal readonly struct BuiltImage /// internal required List Layers { get; init; } + /// + /// Gets image OS. + /// + internal string OS { get; init; } + + /// + /// Gets image architecture. + /// + internal string Architecture { get; init; } + /// /// Gets layers descriptors. /// diff --git a/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs b/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs index e549defd4635..4171589c025d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs @@ -17,6 +17,16 @@ internal sealed class DigestUtils /// internal static string GetDigestFromSha(string sha) => $"sha256:{sha}"; + internal static string GetShaFromDigest(string digest) + { + if (!digest.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException("Invalid digest format. Digest must start with 'sha256:'."); + } + + return digest.Substring("sha256:".Length); + } + /// /// Gets the SHA of . /// diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs index a12d15790d38..5af3edd5a546 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Extensions.Logging; using Microsoft.NET.Build.Containers.Resources; namespace Microsoft.NET.Build.Containers.LocalDaemons; @@ -15,9 +14,9 @@ public ArchiveFileRegistry(string archiveOutputPath) ArchiveOutputPath = archiveOutputPath; } - public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReference, - DestinationImageReference destinationReference, - CancellationToken cancellationToken) + private async Task LoadAsync(T image, SourceImageReference sourceReference, + DestinationImageReference destinationReference, CancellationToken cancellationToken, + Func writeStreamFunc) { var fullPath = Path.GetFullPath(ArchiveOutputPath); @@ -36,19 +35,27 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen ArchiveOutputPath = fullPath; await using var fileStream = File.Create(fullPath); - await DockerCli.WriteImageToStreamAsync( - image, - sourceReference, - destinationReference, - fileStream, - cancellationToken).ConfigureAwait(false); + + // Call the delegate to write the image to the stream + await writeStreamFunc(image, sourceReference, destinationReference, fileStream, cancellationToken).ConfigureAwait(false); } + public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReference, + DestinationImageReference destinationReference, + CancellationToken cancellationToken) + => await LoadAsync(image, sourceReference, destinationReference, cancellationToken, + DockerCli.WriteImageToStreamAsync); + + public async Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference, + DestinationImageReference destinationReference, + CancellationToken cancellationToken) + => await LoadAsync(images, sourceReference, destinationReference, cancellationToken, + DockerCli.WriteMultiArchOciImageToStreamAsync); + public Task IsAvailableAsync(CancellationToken cancellationToken) => Task.FromResult(true); public bool IsAvailable() => true; - public override string ToString() { return string.Format(Strings.ArchiveRegistry_PushInfo, ArchiveOutputPath); diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 68e06d044c3c..112887aa5a6e 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -83,7 +83,12 @@ private async ValueTask FindFullCommandPath(CancellationToken cancellati return _fullCommandPath; } - public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken) + private async Task LoadAsync( + T image, + SourceImageReference sourceReference, + DestinationImageReference destinationReference, + Func writeStreamFunc, + CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -95,16 +100,12 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen loadInfo.RedirectStandardOutput = true; loadInfo.RedirectStandardError = true; - using Process? loadProcess = Process.Start(loadInfo); - - if (loadProcess is null) - { + using Process? loadProcess = Process.Start(loadInfo) ?? throw new NotImplementedException(Resource.FormatString(Strings.ContainerRuntimeProcessCreationFailed, commandPath)); - } - // Create new stream tarball - // We want to be able to export to docker, even oci images. - await WriteDockerImageToStreamAsync(image, sourceReference, destinationReference, loadProcess.StandardInput.BaseStream, cancellationToken).ConfigureAwait(false); + // Call the delegate to write the image to the stream + await writeStreamFunc(image, sourceReference, destinationReference, loadProcess.StandardInput.BaseStream, cancellationToken) + .ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); @@ -120,6 +121,12 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen } } + public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken) + => await LoadAsync(image, sourceReference, destinationReference, WriteDockerImageToStreamAsync, cancellationToken); + + public async Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken) + => await LoadAsync(images, sourceReference, destinationReference, WriteMultiArchOciImageToStreamAsync, cancellationToken); + public async Task IsAvailableAsync(CancellationToken cancellationToken) { bool commandPathWasUnknown = this._command is null; // avoid running the version command twice. @@ -390,6 +397,35 @@ private static async Task WriteManifestForDockerImage( } } + public static async Task WriteMultiArchOciImageToStreamAsync( + BuiltImage[] images, + SourceImageReference sourceReference, + DestinationImageReference destinationReference, + Stream imageStream, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (destinationReference.Tags.Length > 1) + { + throw new ArgumentException(Resource.FormatString(nameof(Strings.OciImageMultipleTagsNotSupported))); + } + + using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true); + + foreach (var image in images) + { + await WriteOciImageToBlobs(writer, image, sourceReference, cancellationToken) + .ConfigureAwait(false); + } + + await WriteIndexJsonForMultiArchOciImage(writer, images, destinationReference, cancellationToken) + .ConfigureAwait(false); + + await WriteOciLayout(writer, cancellationToken) + .ConfigureAwait(false); + } + private static async Task WriteOciImageToStreamAsync( BuiltImage image, SourceImageReference sourceReference, @@ -406,9 +442,22 @@ private static async Task WriteOciImageToStreamAsync( using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true); + await WriteOciImageToBlobs(writer, image, sourceReference, cancellationToken) + .ConfigureAwait(false); + + await WriteIndexJsonForOciImage(writer, image, destinationReference, cancellationToken) + .ConfigureAwait(false); + await WriteOciLayout(writer, cancellationToken) .ConfigureAwait(false); + } + private static async Task WriteOciImageToBlobs( + TarWriter writer, + BuiltImage image, + SourceImageReference sourceReference, + CancellationToken cancellationToken) + { await WriteImageLayers(writer, image, sourceReference, d => $"{_blobsPath}/{d.Substring("sha256:".Length)}", cancellationToken) .ConfigureAwait(false); @@ -417,9 +466,6 @@ await WriteImageConfig(writer, image, $"{_blobsPath}/{image.ImageSha}", cancella await WriteManifestForOciImage(writer, image, cancellationToken) .ConfigureAwait(false); - - await WriteIndexJsonForOciImage(writer, image, destinationReference, cancellationToken) - .ConfigureAwait(false); } private static async Task WriteOciLayout(TarWriter writer, CancellationToken cancellationToken) @@ -456,6 +502,88 @@ private static async Task WriteManifestForOciImage( } } + private static async Task WriteIndexJsonForMultiArchOciImage( + TarWriter writer, + BuiltImage[] images, + DestinationImageReference destinationReference, + CancellationToken cancellationToken) + { + // 1. create manifest list for the blobs + cancellationToken.ThrowIfCancellationRequested(); + + string tag = destinationReference.Tags[0]; + + var manifests = new PlatformSpecificOciManifest[images.Length]; + for (int i = 0; i < images.Length; i++) + { + var manifest = new PlatformSpecificOciManifest + { + mediaType = SchemaTypes.OciManifestV1, + size = images[i].Manifest.Length, + digest = images[i].ManifestDigest, + platform = new PlatformInformation { architecture = images[i].Architecture, os = images[i].OS } + }; + manifests[i] = manifest; + } + + var manifestList = new ImageIndexV1 + { + schemaVersion = 2, + mediaType = SchemaTypes.OciImageIndexV1, + manifests = manifests + }; + + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + var manifestListJson = JsonSerializer.SerializeToNode(manifestList, options)!.ToJsonString(); + var manifestListDigest = DigestUtils.GetDigest(manifestListJson); + var manifestListSha = DigestUtils.GetShaFromDigest(manifestListDigest); + var manifestListPath = $"{_blobsPath}/{manifestListSha}"; + + using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(manifestListJson))) + { + PaxTarEntry indexEntry = new(TarEntryType.RegularFile, manifestListPath) + { + DataStream = indexStream + }; + await writer.WriteEntryAsync(indexEntry, cancellationToken).ConfigureAwait(false); + } + + // 2. create index json that points to manifest list in the blobs + cancellationToken.ThrowIfCancellationRequested(); + + var index = new ImageIndexV1 + { + schemaVersion = 2, + mediaType = SchemaTypes.OciImageIndexV1, + manifests = + [ + new PlatformSpecificOciManifest + { + mediaType = SchemaTypes.OciImageIndexV1, + size = manifestListJson.Length, + digest = manifestListDigest, + annotations = new Dictionary + { + { "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" }, + { "org.opencontainers.image.ref.name", tag } + }, + } + ] + }; + + using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index, options)!.ToJsonString()))) + { + PaxTarEntry indexEntry = new(TarEntryType.RegularFile, "index.json") + { + DataStream = indexStream + }; + await writer.WriteEntryAsync(indexEntry, cancellationToken).ConfigureAwait(false); + } + } + private static async Task WriteIndexJsonForOciImage( TarWriter writer, BuiltImage image, diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs index c06150fe2425..34ccb13d093d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs @@ -13,6 +13,11 @@ internal interface ILocalRegistry { /// public Task LoadAsync(BuiltImage image, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken); + /// + /// Loads a multi-arch image (presumably from a tarball) into the local registry. + /// + public Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken); + /// /// Checks to see if the local registry is available. This is used to give nice errors to the user. /// From 068d0738769c1a62b27014b484df9817f952abfc Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Fri, 31 Jan 2025 17:55:09 +0100 Subject: [PATCH 05/74] implement publishing to tarball/local daemon/remote registry in CreateImageIndex --- .../BuiltImage.cs | 2 +- .../ImageIndexGenerator.cs | 42 +-- .../PublicAPI/net8.0/PublicAPI.Unshipped.txt | 12 + .../Registry/Registry.cs | 2 - .../Tasks/CreateImageIndex.cs | 322 ++++++++++++++++-- .../Microsoft.NET.Build.Containers.targets | 15 +- .../EndToEndTests.cs | 6 +- .../ImageIndexGeneratorTests.cs | 150 ++++---- 8 files changed, 394 insertions(+), 157 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs index 84d3b11c42c9..0a76191db727 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs @@ -41,7 +41,7 @@ internal readonly struct BuiltImage /// /// Gets image layers. /// - internal required List Layers { get; init; } + internal List? Layers { get; init; } /// /// Gets image OS. diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs index bc23073080ea..b0cedc4a60af 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs @@ -23,31 +23,31 @@ internal static class ImageIndexGenerator /// /// Generates an image index from the given images. /// - /// + /// /// Returns json string of image index and image index mediaType. /// /// - internal static (string, string) GenerateImageIndex(ImageInfo[] imageInfos) + internal static (string, string) GenerateImageIndex(BuiltImage[] images) { - if (imageInfos.Length == 0) + if (images.Length == 0) { throw new ArgumentException(string.Format(Strings.ImagesEmpty)); } - string manifestMediaType = imageInfos[0].ManifestMediaType; + string manifestMediaType = images[0].ManifestMediaType; - if (!imageInfos.All(image => string.Equals(image.ManifestMediaType, manifestMediaType, StringComparison.OrdinalIgnoreCase))) + if (!images.All(image => string.Equals(image.ManifestMediaType, manifestMediaType, StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException(Strings.MixedMediaTypes); } if (manifestMediaType == SchemaTypes.DockerManifestV2) { - return GenerateImageIndex(imageInfos, SchemaTypes.DockerManifestV2, SchemaTypes.DockerManifestListV2); + return GenerateImageIndex(images, SchemaTypes.DockerManifestV2, SchemaTypes.DockerManifestListV2); } else if (manifestMediaType == SchemaTypes.OciManifestV1) { - return GenerateImageIndex(imageInfos, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1); + return GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1); } else { @@ -55,10 +55,10 @@ internal static (string, string) GenerateImageIndex(ImageInfo[] imageInfos) } } - private static (string, string) GenerateImageIndex(ImageInfo[] images, string manifestMediaType, string imageIndexMediaType) + private static (string, string) GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType) { // Here we are using ManifestListV2 struct, but we could use ImageIndexV1 struct as well. - // We are filling the same fiels, so we can use the same struct. + // We are filling the same fields, so we can use the same struct. var manifests = new PlatformSpecificManifest[images.Length]; for (int i = 0; i < images.Length; i++) { @@ -69,32 +69,22 @@ private static (string, string) GenerateImageIndex(ImageInfo[] images, string ma mediaType = manifestMediaType, size = image.Manifest.Length, digest = image.ManifestDigest, - platform = GetArchitectureAndOsFromConfig(image) + platform = new PlatformInformation + { + architecture = image.Architecture, + os = image.OS + } }; manifests[i] = manifest; } - var dockerManifestList = new ManifestListV2 + var manifestList = new ManifestListV2 { schemaVersion = 2, mediaType = imageIndexMediaType, manifests = manifests }; - return (JsonSerializer.SerializeToNode(dockerManifestList)?.ToJsonString() ?? "", dockerManifestList.mediaType); - } - - private static PlatformInformation GetArchitectureAndOsFromConfig(ImageInfo image) - { - var configJson = JsonNode.Parse(image.Config) as JsonObject ?? - throw new ArgumentException($"{nameof(image.Config)} should be a JSON object.", nameof(image.Config)); - - var architecture = configJson["architecture"]?.ToString() ?? - throw new ArgumentException($"{nameof(image.Config)} should contain 'architecture'.", nameof(image.Config)); - - var os = configJson["os"]?.ToString() ?? - throw new ArgumentException($"{nameof(image.Config)} should contain 'os'.", nameof(image.Config)); - - return new PlatformInformation { architecture = architecture, os = os }; + return (JsonSerializer.SerializeToNode(manifestList)?.ToJsonString() ?? "", manifestList.mediaType); } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt index b19e4fa81718..f7e2955bd01f 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -19,6 +19,18 @@ Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UserBaseImage. Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.get -> bool Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.set -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseRegistry.get -> string! +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseRegistry.set -> void +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageName.get -> string! +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageName.set -> void +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageTag.get -> string! +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageTag.set -> void +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ArchiveOutputPath.get -> string! +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ArchiveOutputPath.set -> void +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.LocalRegistry.get -> string! +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.LocalRegistry.set -> void +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedArchiveOutputPath.get -> string! +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedArchiveOutputPath.set -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Cancel() -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.CreateImageIndex() -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Dispose() -> void diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs index 5ab07dc9713e..2e754986334a 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs @@ -3,11 +3,9 @@ using Microsoft.Extensions.Logging; using Microsoft.NET.Build.Containers.Resources; -using NuGet.Packaging; using NuGet.RuntimeModel; using System.Diagnostics; using System.Net.Http.Json; -using System.Text.Json; using System.Text.Json.Nodes; namespace Microsoft.NET.Build.Containers; diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 0894686603d2..09128838f212 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -2,8 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Text.Json; +using System.Text.Json.Nodes; using Microsoft.Build.Framework; using Microsoft.Extensions.Logging; +using Microsoft.NET.Build.Containers.LocalDaemons; using Microsoft.NET.Build.Containers.Logging; using Microsoft.NET.Build.Containers.Resources; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -14,6 +17,27 @@ namespace Microsoft.NET.Build.Containers.Tasks; public sealed class CreateImageIndex : Microsoft.Build.Utilities.Task, ICancelableTask, IDisposable { #region Parameters + /// + /// The base registry to pull from. + /// Ex: mcr.microsoft.com + /// + [Required] + public string BaseRegistry { get; set; } + + /// + /// The base image to pull. + /// Ex: dotnet/runtime + /// + [Required] + public string BaseImageName { get; set; } + + /// + /// The base image tag. + /// Ex: 6.0 + /// + [Required] + public string BaseImageTag { get; set; } + /// /// Manifests to include in the image index. /// @@ -23,9 +47,18 @@ public sealed class CreateImageIndex : Microsoft.Build.Utilities.Task, ICancelab /// /// The registry to push the image index to. /// - [Required] public string OutputRegistry { get; set; } + /// + /// The file path to which to write a tar.gz archive of the container image. + /// + public string ArchiveOutputPath { get; set; } + + /// + /// The kind of local registry to use, if any. + /// + public string LocalRegistry { get; set; } + /// /// The name of the output image index (manifest list) that will be pushed to the registry. /// @@ -38,6 +71,9 @@ public sealed class CreateImageIndex : Microsoft.Build.Utilities.Task, ICancelab [Required] public string[] ImageTags { get; set; } + [Output] + public string GeneratedArchiveOutputPath { get; set; } + /// /// The generated image index (manifest list) in JSON format. /// @@ -46,10 +82,16 @@ public sealed class CreateImageIndex : Microsoft.Build.Utilities.Task, ICancelab public CreateImageIndex() { + BaseRegistry = string.Empty; + BaseImageName = string.Empty; + BaseImageTag = string.Empty; GeneratedContainers = Array.Empty(); OutputRegistry = string.Empty; + ArchiveOutputPath = string.Empty; + LocalRegistry = string.Empty; Repository = string.Empty; ImageTags = Array.Empty(); + GeneratedArchiveOutputPath = string.Empty; GeneratedImageIndex = string.Empty; } #endregion @@ -63,6 +105,8 @@ public void Dispose() _cancellationTokenSource.Dispose(); } + private bool IsLocalPull => string.IsNullOrEmpty(BaseRegistry); + public override bool Execute() { try @@ -84,25 +128,120 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - var images = ParseImages(); + using MSBuildLoggerProvider loggerProvider = new(Log); + ILoggerFactory msbuildLoggerFactory = new LoggerFactory(new[] { loggerProvider }); + ILogger logger = msbuildLoggerFactory.CreateLogger(); + + // Look up in CreateNewImage how the image is published to registry/local daemon/tarball + + RegistryMode sourceRegistryMode = BaseRegistry.Equals(OutputRegistry, StringComparison.InvariantCultureIgnoreCase) ? RegistryMode.PullFromOutput : RegistryMode.Pull; + Registry? sourceRegistry = IsLocalPull ? null : new Registry(BaseRegistry, logger, sourceRegistryMode); + SourceImageReference sourceImageReference = new(sourceRegistry, BaseImageName, BaseImageTag); + + DestinationImageReference destinationImageReference = DestinationImageReference.CreateFromSettings( + Repository, + ImageTags, + msbuildLoggerFactory, + ArchiveOutputPath, + OutputRegistry, + LocalRegistry); + + var images = ParseImages(destinationImageReference.Kind); + if (Log.HasLoggedErrors) { return false; } - using MSBuildLoggerProvider loggerProvider = new(Log); - ILoggerFactory msbuildLoggerFactory = new LoggerFactory(new[] { loggerProvider }); - ILogger logger = msbuildLoggerFactory.CreateLogger(); + var telemetry = CreateTelemetryContext(sourceImageReference, destinationImageReference); + + switch (destinationImageReference.Kind) + { + case DestinationImageReferenceKind.LocalRegistry: + await PushToLocalRegistryAsync(images, + sourceImageReference, + destinationImageReference, + telemetry, + cancellationToken).ConfigureAwait(false); + break; + case DestinationImageReferenceKind.RemoteRegistry: + await PushToRemoteRegistryAsync(images, + sourceImageReference, + destinationImageReference, + telemetry, + cancellationToken).ConfigureAwait(false); + break; + default: + throw new ArgumentOutOfRangeException(); + } - logger.LogInformation(Strings.BuildingImageIndex, GetRepositoryAndTagsString(), string.Join(", ", images.Select(i => i.ManifestDigest))); + telemetry.LogPublishSuccess(); + return !Log.HasLoggedErrors; + } + + private async Task PushToLocalRegistryAsync(BuiltImage[] images, SourceImageReference sourceImageReference, + DestinationImageReference destinationImageReference, + Telemetry telemetry, + CancellationToken cancellationToken) + { + ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; + if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) + { + telemetry.LogMissingLocalBinary(); + Log.LogErrorWithCodeFromResources(nameof(Strings.LocalRegistryNotAvailable)); + return; + } try { - (string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images); + await localRegistry.LoadAsync(images, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); + SafeLog(Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); - GeneratedImageIndex = imageIndex; + if (localRegistry is ArchiveFileRegistry archive) + { + GeneratedArchiveOutputPath = archive.ArchiveOutputPath; + } + } + catch (ContainerHttpException e) + { + if (BuildEngine != null) + { + Log.LogErrorFromException(e, true); + } + } + catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle) + { + telemetry.LogLocalLoadError(); + Log.LogErrorFromException(dle, showStackTrace: false); + } + catch (ArgumentException argEx) + { + Log.LogErrorFromException(argEx, showStackTrace: false); + } + } - await PushToRemoteRegistry(GeneratedImageIndex, mediaType, logger, cancellationToken); + private async Task PushToRemoteRegistryAsync(BuiltImage[] images, SourceImageReference sourceImageReference, + DestinationImageReference destinationImageReference, + Telemetry telemetry, + CancellationToken cancellationToken) + { + try + { + (string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images); + await destinationImageReference.RemoteRegistry!.PushManifestListAsync( + destinationImageReference.Repository, + destinationImageReference.Tags, + imageIndex, + mediaType, + cancellationToken).ConfigureAwait(false); + SafeLog(Strings.ImageIndexUploadedToRegistry, destinationImageReference, OutputRegistry); + } + catch (UnableToAccessRepositoryException) + { + if (BuildEngine != null) + { + Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); + } } catch (ContainerHttpException e) { @@ -111,17 +250,24 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) Log.LogErrorFromException(e, true); } } - catch (ArgumentException ex) + catch (Exception e) { - Log.LogErrorFromException(ex); + if (BuildEngine != null) + { + Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); + Log.LogMessage(MessageImportance.Low, "Details: {0}", e); + } } + } - return !Log.HasLoggedErrors; + private void SafeLog(string message, params object[] formatParams) + { + if (BuildEngine != null) Log.LogMessage(MessageImportance.High, message, formatParams); } - private ImageInfo[] ParseImages() + private BuiltImage[] ParseImages(DestinationImageReferenceKind destinationKind) { - var images = new ImageInfo[GeneratedContainers.Length]; + var images = new BuiltImage[GeneratedContainers.Length]; for (int i = 0; i < GeneratedContainers.Length; i++) { @@ -132,38 +278,160 @@ private ImageInfo[] ParseImages() string manifest = unparsedImage.GetMetadata("Manifest"); string manifestMediaType = unparsedImage.GetMetadata("ManifestMediaType"); + //TODO: add manifestmedia type to the error message if (string.IsNullOrEmpty(config) || string.IsNullOrEmpty(manifestDigest) || string.IsNullOrEmpty(manifest)) { Log.LogError(Strings.InvalidImageMetadata, unparsedImage.ItemSpec); break; } - images[i] = new ImageInfo + var manifestV2 = JsonSerializer.Deserialize(manifest); + if (manifestV2 == null) + { + //TODO: log new error about manifest not deserealized + Log.LogError(Strings.InvalidImageMetadata, unparsedImage.ItemSpec); + break; + } + + string imageDigest = manifestV2.Config.digest; + string imageSha = DigestUtils.GetShaFromDigest(imageDigest); + // We don't need layers for remote registry, as the individual images should be pushed already + var layers = destinationKind == DestinationImageReferenceKind.RemoteRegistry ? null : manifestV2.Layers; + (string architecture, string os) = GetArchitectureAndOsFromConfig(config); + + images[i] = new BuiltImage() { Config = config, - ManifestDigest = manifestDigest, + ImageDigest = imageDigest, + ImageSha = imageSha, Manifest = manifest, - ManifestMediaType = manifestMediaType + ManifestDigest = manifestDigest, + ManifestMediaType = manifestMediaType, + Layers = layers, + OS = os, + Architecture = architecture }; } return images; } - private async Task PushToRemoteRegistry(string manifestList, string mediaType, ILogger logger, CancellationToken cancellationToken) + private static (string, string) GetArchitectureAndOsFromConfig(string config) { - cancellationToken.ThrowIfCancellationRequested(); - Debug.Assert(ImageTags.Length > 0); - var registry = new Registry(OutputRegistry, logger, RegistryMode.Push); - await registry.PushManifestListAsync(Repository, ImageTags, manifestList, mediaType, cancellationToken).ConfigureAwait(false); - logger.LogInformation(Strings.ImageIndexUploadedToRegistry, GetRepositoryAndTagsString(), OutputRegistry); + var configJson = JsonNode.Parse(config) as JsonObject ?? + throw new ArgumentException("Image config should be a JSON object."); + + var architecture = configJson["architecture"]?.ToString() ?? + throw new ArgumentException("Image config should contain 'architecture'."); + + var os = configJson["os"]?.ToString() ?? + throw new ArgumentException("Image config should contain 'os'."); + + return (architecture, os); } - private string? _repositoryAndTagsString = null; + private Telemetry CreateTelemetryContext(SourceImageReference source, DestinationImageReference destination) + { + var context = new PublishTelemetryContext( + source.Registry is not null ? GetRegistryType(source.Registry) : null, + null, // we don't support local pull yet, but we may in the future + destination.RemoteRegistry is not null ? GetRegistryType(destination.RemoteRegistry) : null, + destination.LocalRegistry is not null ? GetLocalStorageType(destination.LocalRegistry) : null); + return new Telemetry(Log, context); + } + + private RegistryType GetRegistryType(Registry r) + { + if (r.IsMcr) return RegistryType.MCR; + if (r.IsGithubPackageRegistry) return RegistryType.GitHub; + if (r.IsAmazonECRRegistry) return RegistryType.AWS; + if (r.IsAzureContainerRegistry) return RegistryType.Azure; + if (r.IsGoogleArtifactRegistry) return RegistryType.Google; + if (r.IsDockerHub) return RegistryType.DockerHub; + return RegistryType.Other; + } - private string GetRepositoryAndTagsString() + private LocalStorageType GetLocalStorageType(ILocalRegistry r) { - _repositoryAndTagsString ??= $"{Repository}:{string.Join(", ", ImageTags)}"; - return _repositoryAndTagsString; + if (r is ArchiveFileRegistry) return LocalStorageType.Tarball; + var d = r as DockerCli; + System.Diagnostics.Debug.Assert(d != null, "Unknown local registry type"); + if (d.GetCommand() == DockerCli.DockerCommand) return LocalStorageType.Docker; + else return LocalStorageType.Podman; + } + + /// + /// Interesting data about the container publish - used to track the usage rates of various sources/targets of the process + /// and to help diagnose issues with the container publish overall. + /// + /// If the base image came from a remote registry, what kind of registry was it? + /// If the base image came from a local store of some kind, what kind of store was it? + /// If the new image is being pushed to a remote registry, what kind of registry is it? + /// If the new image is being stored in a local store of some kind, what kind of store is it? + private record class PublishTelemetryContext(RegistryType? RemotePullType, LocalStorageType? LocalPullType, RegistryType? RemotePushType, LocalStorageType? LocalPushType); + private enum RegistryType { Azure, AWS, Google, GitHub, DockerHub, MCR, Other } + private enum LocalStorageType { Docker, Podman, Tarball } + + private class Telemetry(Microsoft.Build.Utilities.TaskLoggingHelper Log, PublishTelemetryContext context) + { + private IDictionary ContextProperties() => new Dictionary + { + { nameof(context.RemotePullType), context.RemotePullType?.ToString() }, + { nameof(context.LocalPullType), context.LocalPullType?.ToString() }, + { nameof(context.RemotePushType), context.RemotePushType?.ToString() }, + { nameof(context.LocalPushType), context.LocalPushType?.ToString() } + }; + + public void LogPublishSuccess() + { + Log.LogTelemetry("sdk/container/publish/success", ContextProperties()); + } + + public void LogUnknownRepository() + { + var props = ContextProperties(); + props.Add("error", "unknown_repository"); + Log.LogTelemetry("sdk/container/publish/error", props); + } + + public void LogCredentialFailure(SourceImageReference _) + { + var props = ContextProperties(); + props.Add("error", "credential_failure"); + props.Add("direction", "pull"); + Log.LogTelemetry("sdk/container/publish/error", props); + } + + public void LogCredentialFailure(DestinationImageReference d) + { + var props = ContextProperties(); + props.Add("error", "credential_failure"); + props.Add("direction", "push"); + Log.LogTelemetry("sdk/container/publish/error", props); + } + + public void LogRidMismatch(string desiredRid, string[] availableRids) + { + var props = ContextProperties(); + props.Add("error", "rid_mismatch"); + props.Add("target_rid", desiredRid); + props.Add("available_rids", string.Join(",", availableRids)); + Log.LogTelemetry("sdk/container/publish/error", props); + } + + public void LogMissingLocalBinary() + { + var props = ContextProperties(); + props.Add("error", "missing_binary"); + Log.LogTelemetry("sdk/container/publish/error", props); + } + + public void LogLocalLoadError() + { + var props = ContextProperties(); + props.Add("error", "local_load"); + Log.LogTelemetry("sdk/container/publish/error", props); + } + } } diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets index 2a006d665a6b..5309bdbeaf8b 100644 --- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets +++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets @@ -340,16 +340,15 @@ - - <_SkipCreateImageIndex>false - <_SkipCreateImageIndex Condition="'$(ContainerRegistry)' == ''">true - - - + ImageTags="@(ContainerImageTags)" + BaseRegistry="$(ContainerBaseRegistry)" + BaseImageName="$(ContainerBaseName)" + BaseImageTag="$(ContainerBaseTag)"> diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index 2d562b4feaa7..40083f8fc58e 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -1010,9 +1010,9 @@ public void EndToEndMultiArch_RemoteRegistry() commandResult.Should().Pass() .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-x64")) .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-arm64")) - .And.HaveStdOutContaining($"Pushed image '{imageX64}' to registry") - .And.HaveStdOutContaining($"Pushed image '{imageArm64}' to registry") - .And.HaveStdOutContaining($"Pushed image index '{imageIndex}' to registry '{registry}'"); + .And.HaveStdOutContaining($"Pushed image '{imageX64}' to registry '{registry}'.") + .And.HaveStdOutContaining($"Pushed image '{imageArm64}' to registry '{registry}'.") + .And.HaveStdOutContaining($"Pushed image index '{imageIndex}' to registry '{registry}'."); // Check that the containers can be run diff --git a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs index ef6e5bb84b2a..9bc573b5b0e7 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs @@ -10,7 +10,7 @@ public class ImageIndexGeneratorTests [Fact] public void ImagesCannotBeEmpty() { - ImageInfo[] images = Array.Empty(); + BuiltImage[] images = Array.Empty(); var ex = Assert.Throws(() => ImageIndexGenerator.GenerateImageIndex(images)); Assert.Equal(Strings.ImagesEmpty, ex.Message); } @@ -18,95 +18,49 @@ public void ImagesCannotBeEmpty() [Fact] public void UnsupportedMediaTypeThrows() { - ImageInfo[] images = new ImageInfo[] - { - new ImageInfo + BuiltImage[] images = + [ + new BuiltImage { + Config = "", + ImageDigest = "", + ImageSha = "", + Manifest = "", + ManifestDigest = "", ManifestMediaType = "unsupported" } - }; + ]; var ex = Assert.Throws(() => ImageIndexGenerator.GenerateImageIndex(images)); Assert.Equal(string.Format(Strings.UnsupportedMediaType, "unsupported"), ex.Message); } - [Theory] - [InlineData(SchemaTypes.DockerManifestV2)] - [InlineData(SchemaTypes.OciManifestV1)] - public void ConfigIsNotJsonObjectThrows(string supportedMediaType) - { - ImageInfo[] images = new ImageInfo[] - { - new ImageInfo - { - Config = "[]", - Manifest = "", - ManifestMediaType = supportedMediaType - } - }; - - var ex = Assert.Throws(() => ImageIndexGenerator.GenerateImageIndex(images)); - Assert.Equal($"Config should be a JSON object. (Parameter 'Config')", ex.Message); - } - - [Theory] - [InlineData(SchemaTypes.DockerManifestV2)] - [InlineData(SchemaTypes.OciManifestV1)] - public void ConfigDoesNotContainArchitectureThrows(string supportedMediaType) - { - ImageInfo[] images = new ImageInfo[] - { - new ImageInfo - { - Config = "{}", - Manifest = "", - ManifestMediaType = supportedMediaType - } - }; - - var ex = Assert.Throws(() => ImageIndexGenerator.GenerateImageIndex(images)); - Assert.Equal($"Config should contain 'architecture'. (Parameter 'Config')", ex.Message); - } - - [Theory] - [InlineData(SchemaTypes.DockerManifestV2)] - [InlineData(SchemaTypes.OciManifestV1)] - public void ConfigDoesNotContainOsThrows(string supportedMediaType) - { - ImageInfo[] images = new ImageInfo[] - { - new ImageInfo - { - Config = "{\"architecture\":\"arch1\"}", - Manifest = "", - ManifestMediaType = supportedMediaType - } - }; - - var ex = Assert.Throws(() => ImageIndexGenerator.GenerateImageIndex(images)); - Assert.Equal($"Config should contain 'os'. (Parameter 'Config')", ex.Message); - } - [Theory] [InlineData(SchemaTypes.DockerManifestV2)] [InlineData(SchemaTypes.OciManifestV1)] public void ImagesWithMixedMediaTypes(string supportedMediaType) { - ImageInfo[] images = new ImageInfo[] - { - new ImageInfo + BuiltImage[] images = + [ + new BuiltImage { - Config = "{\"architecture\":\"arch1\",\"os\":\"os1\"}", - Manifest = "", - ManifestMediaType = supportedMediaType + Config = "", + ImageDigest = "", + ImageSha = "", + Manifest = "", + ManifestDigest = "", + ManifestMediaType = supportedMediaType, }, - new ImageInfo + new BuiltImage { Config = "", + ImageDigest = "", + ImageSha = "", Manifest = "", + ManifestDigest = "", ManifestMediaType = "anotherMediaType" } - }; + ]; var ex = Assert.Throws(() => ImageIndexGenerator.GenerateImageIndex(images)); Assert.Equal(Strings.MixedMediaTypes, ex.Message); @@ -115,21 +69,29 @@ public void ImagesWithMixedMediaTypes(string supportedMediaType) [Fact] public void GenerateDockerManifestList() { - ImageInfo[] images = + BuiltImage[] images = [ - new ImageInfo + new BuiltImage { - Config = "{\"architecture\":\"arch1\",\"os\":\"os1\"}", - ManifestDigest = "sha256:digest1", + Config = "", + ImageDigest = "", + ImageSha = "", Manifest = "123", - ManifestMediaType = SchemaTypes.DockerManifestV2 + ManifestDigest = "sha256:digest1", + ManifestMediaType = SchemaTypes.DockerManifestV2, + Architecture = "arch1", + OS = "os1" }, - new ImageInfo + new BuiltImage { - Config = "{\"architecture\":\"arch2\",\"os\":\"os2\"}", - ManifestDigest = "sha256:digest2", + Config = "", + ImageDigest = "", + ImageSha = "", Manifest = "123", - ManifestMediaType = SchemaTypes.DockerManifestV2 + ManifestDigest = "sha256:digest2", + ManifestMediaType = SchemaTypes.DockerManifestV2, + Architecture = "arch2", + OS = "os2" } ]; @@ -141,23 +103,31 @@ public void GenerateDockerManifestList() [Fact] public void GenerateOciImageIndex() { - ImageInfo[] images = new ImageInfo[] - { - new ImageInfo + BuiltImage[] images = + [ + new BuiltImage { - Config = "{\"architecture\":\"arch1\",\"os\":\"os1\"}", - ManifestDigest = "sha256:digest1", + Config = "", + ImageDigest = "", + ImageSha = "", Manifest = "123", - ManifestMediaType = SchemaTypes.OciManifestV1 + ManifestDigest = "sha256:digest1", + ManifestMediaType = SchemaTypes.OciManifestV1, + Architecture = "arch1", + OS = "os1" }, - new ImageInfo + new BuiltImage { - Config = "{\"architecture\":\"arch2\",\"os\":\"os2\"}", - ManifestDigest = "sha256:digest2", + Config = "", + ImageDigest = "", + ImageSha = "", Manifest = "123", - ManifestMediaType = SchemaTypes.OciManifestV1 + ManifestDigest = "sha256:digest2", + ManifestMediaType = SchemaTypes.OciManifestV1, + Architecture = "arch2", + OS = "os2" } - }; + ]; var (imageIndex, mediaType) = ImageIndexGenerator.GenerateImageIndex(images); 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); From 8d5289972963cb275fba6786d3c2555b1002eedb Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Fri, 31 Jan 2025 19:01:46 +0100 Subject: [PATCH 06/74] move Telemetry into a seperate file --- .../ImagePublisher.cs | 121 ++++++++++++++++++ .../Tasks/CreateImageIndex.cs | 114 +---------------- .../Tasks/CreateNewImage.cs | 109 +--------------- .../Tasks/Telemetry.cs | 116 +++++++++++++++++ 4 files changed, 240 insertions(+), 220 deletions(-) create mode 100644 src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs create mode 100644 src/Containers/Microsoft.NET.Build.Containers/Tasks/Telemetry.cs diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs new file mode 100644 index 000000000000..c877d2c004ba --- /dev/null +++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs @@ -0,0 +1,121 @@ +// // Licensed to the .NET Foundation under one or more agreements. +// // The .NET Foundation licenses this file to you under the MIT license. + +// using Microsoft.NET.Build.Containers.LocalDaemons; +// using Microsoft.NET.Build.Containers.Resources; + +// namespace Microsoft.NET.Build.Containers; + + +// internal static class ImagePublisher +// { +// public static async Task PublishImage(BuiltImage builtImage, SourceImageReference sourceImageReference, +// DestinationImageReference destinationImageReference, +// Telemetry telemetry, +// CancellationToken cancellationToken) +// { +// cancellationToken.ThrowIfCancellationRequested(); + +// switch (destinationImageReference.Kind) +// { +// case DestinationImageReferenceKind.LocalRegistry: +// await PushToLocalRegistryAsync(builtImage, +// sourceImageReference, +// destinationImageReference, +// telemetry, +// cancellationToken).ConfigureAwait(false); +// break; +// case DestinationImageReferenceKind.RemoteRegistry: +// await PushToRemoteRegistryAsync(builtImage, +// sourceImageReference, +// destinationImageReference, +// cancellationToken).ConfigureAwait(false); +// break; +// default: +// throw new ArgumentOutOfRangeException(); +// } + +// telemetry.LogPublishSuccess(); +// } + +// private static async Task PushToLocalRegistryAsync( +// BuiltImage builtImage, +// SourceImageReference sourceImageReference, +// DestinationImageReference destinationImageReference, +// Telemetry telemetry, +// CancellationToken cancellationToken) +// { +// ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; +// if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) +// { +// telemetry.LogMissingLocalBinary(); +// Log.LogErrorWithCodeFromResources(nameof(Strings.LocalRegistryNotAvailable)); +// return; +// } +// try +// { +// await localRegistry.LoadAsync(builtImage, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); +// SafeLog(Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); + +// if (localRegistry is ArchiveFileRegistry archive) +// { +// GeneratedArchiveOutputPath = archive.ArchiveOutputPath; +// } +// } +// catch (ContainerHttpException e) +// { +// if (BuildEngine != null) +// { +// Log.LogErrorFromException(e, true); +// } +// } +// catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle) +// { +// telemetry.LogLocalLoadError(); +// Log.LogErrorFromException(dle, showStackTrace: false); +// } +// catch (ArgumentException argEx) +// { +// Log.LogErrorFromException(argEx, showStackTrace: false); +// } +// } + +// private static async Task PushToRemoteRegistryAsync( +// BuiltImage builtImage, +// SourceImageReference sourceImageReference, +// DestinationImageReference destinationImageReference, +// CancellationToken cancellationToken) +// { +// try +// { +// await destinationImageReference.RemoteRegistry!.PushAsync( +// builtImage, +// sourceImageReference, +// destinationImageReference, +// cancellationToken).ConfigureAwait(false); +// SafeLog(Strings.ContainerBuilder_ImageUploadedToRegistry, destinationImageReference, OutputRegistry); +// } +// catch (UnableToAccessRepositoryException) +// { +// if (BuildEngine != null) +// { +// Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); +// } +// } +// catch (ContainerHttpException e) +// { +// if (BuildEngine != null) +// { +// Log.LogErrorFromException(e, true); +// } +// } +// catch (Exception e) +// { +// if (BuildEngine != null) +// { +// Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); +// Log.LogMessage(MessageImportance.Low, "Details: {0}", e); +// } +// } +// } +// } \ No newline at end of file diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 09128838f212..e315adef8921 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -132,8 +132,6 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) ILoggerFactory msbuildLoggerFactory = new LoggerFactory(new[] { loggerProvider }); ILogger logger = msbuildLoggerFactory.CreateLogger(); - // Look up in CreateNewImage how the image is published to registry/local daemon/tarball - RegistryMode sourceRegistryMode = BaseRegistry.Equals(OutputRegistry, StringComparison.InvariantCultureIgnoreCase) ? RegistryMode.PullFromOutput : RegistryMode.Pull; Registry? sourceRegistry = IsLocalPull ? null : new Registry(BaseRegistry, logger, sourceRegistryMode); SourceImageReference sourceImageReference = new(sourceRegistry, BaseImageName, BaseImageTag); @@ -153,7 +151,7 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) return false; } - var telemetry = CreateTelemetryContext(sourceImageReference, destinationImageReference); + var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log); switch (destinationImageReference.Kind) { @@ -166,9 +164,7 @@ await PushToLocalRegistryAsync(images, break; case DestinationImageReferenceKind.RemoteRegistry: await PushToRemoteRegistryAsync(images, - sourceImageReference, destinationImageReference, - telemetry, cancellationToken).ConfigureAwait(false); break; default: @@ -220,9 +216,8 @@ private async Task PushToLocalRegistryAsync(BuiltImage[] images, SourceImageRefe } } - private async Task PushToRemoteRegistryAsync(BuiltImage[] images, SourceImageReference sourceImageReference, + private async Task PushToRemoteRegistryAsync(BuiltImage[] images, DestinationImageReference destinationImageReference, - Telemetry telemetry, CancellationToken cancellationToken) { try @@ -329,109 +324,4 @@ private static (string, string) GetArchitectureAndOsFromConfig(string config) return (architecture, os); } - - private Telemetry CreateTelemetryContext(SourceImageReference source, DestinationImageReference destination) - { - var context = new PublishTelemetryContext( - source.Registry is not null ? GetRegistryType(source.Registry) : null, - null, // we don't support local pull yet, but we may in the future - destination.RemoteRegistry is not null ? GetRegistryType(destination.RemoteRegistry) : null, - destination.LocalRegistry is not null ? GetLocalStorageType(destination.LocalRegistry) : null); - return new Telemetry(Log, context); - } - - private RegistryType GetRegistryType(Registry r) - { - if (r.IsMcr) return RegistryType.MCR; - if (r.IsGithubPackageRegistry) return RegistryType.GitHub; - if (r.IsAmazonECRRegistry) return RegistryType.AWS; - if (r.IsAzureContainerRegistry) return RegistryType.Azure; - if (r.IsGoogleArtifactRegistry) return RegistryType.Google; - if (r.IsDockerHub) return RegistryType.DockerHub; - return RegistryType.Other; - } - - private LocalStorageType GetLocalStorageType(ILocalRegistry r) - { - if (r is ArchiveFileRegistry) return LocalStorageType.Tarball; - var d = r as DockerCli; - System.Diagnostics.Debug.Assert(d != null, "Unknown local registry type"); - if (d.GetCommand() == DockerCli.DockerCommand) return LocalStorageType.Docker; - else return LocalStorageType.Podman; - } - - /// - /// Interesting data about the container publish - used to track the usage rates of various sources/targets of the process - /// and to help diagnose issues with the container publish overall. - /// - /// If the base image came from a remote registry, what kind of registry was it? - /// If the base image came from a local store of some kind, what kind of store was it? - /// If the new image is being pushed to a remote registry, what kind of registry is it? - /// If the new image is being stored in a local store of some kind, what kind of store is it? - private record class PublishTelemetryContext(RegistryType? RemotePullType, LocalStorageType? LocalPullType, RegistryType? RemotePushType, LocalStorageType? LocalPushType); - private enum RegistryType { Azure, AWS, Google, GitHub, DockerHub, MCR, Other } - private enum LocalStorageType { Docker, Podman, Tarball } - - private class Telemetry(Microsoft.Build.Utilities.TaskLoggingHelper Log, PublishTelemetryContext context) - { - private IDictionary ContextProperties() => new Dictionary - { - { nameof(context.RemotePullType), context.RemotePullType?.ToString() }, - { nameof(context.LocalPullType), context.LocalPullType?.ToString() }, - { nameof(context.RemotePushType), context.RemotePushType?.ToString() }, - { nameof(context.LocalPushType), context.LocalPushType?.ToString() } - }; - - public void LogPublishSuccess() - { - Log.LogTelemetry("sdk/container/publish/success", ContextProperties()); - } - - public void LogUnknownRepository() - { - var props = ContextProperties(); - props.Add("error", "unknown_repository"); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - public void LogCredentialFailure(SourceImageReference _) - { - var props = ContextProperties(); - props.Add("error", "credential_failure"); - props.Add("direction", "pull"); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - public void LogCredentialFailure(DestinationImageReference d) - { - var props = ContextProperties(); - props.Add("error", "credential_failure"); - props.Add("direction", "push"); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - public void LogRidMismatch(string desiredRid, string[] availableRids) - { - var props = ContextProperties(); - props.Add("error", "rid_mismatch"); - props.Add("target_rid", desiredRid); - props.Add("available_rids", string.Join(",", availableRids)); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - public void LogMissingLocalBinary() - { - var props = ContextProperties(); - props.Add("error", "missing_binary"); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - public void LogLocalLoadError() - { - var props = ContextProperties(); - props.Add("error", "local_load"); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index 7368fd337596..89131d696e46 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -72,7 +72,7 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) OutputRegistry, LocalRegistry); - var telemetry = CreateTelemetryContext(sourceImageReference, destinationImageReference); + var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log); ImageBuilder? imageBuilder; if (sourceRegistry is { } registry) @@ -190,7 +190,6 @@ await PushToLocalRegistryAsync(builtImage, await PushToRemoteRegistryAsync(builtImage, sourceImageReference, destinationImageReference, - telemetry, cancellationToken).ConfigureAwait(false); break; default: @@ -244,7 +243,6 @@ private async Task PushToLocalRegistryAsync(BuiltImage builtImage, SourceImageRe private async Task PushToRemoteRegistryAsync(BuiltImage builtImage, SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, - Telemetry telemetry, CancellationToken cancellationToken) { try @@ -349,109 +347,4 @@ public void Dispose() logWarning: s => Log.LogWarningWithCodeFromResources(s), logError: (s, a) => { if (a is null) Log.LogErrorWithCodeFromResources(s); else Log.LogErrorWithCodeFromResources(s, a); }); } - - private Telemetry CreateTelemetryContext(SourceImageReference source, DestinationImageReference destination) - { - var context = new PublishTelemetryContext( - source.Registry is not null ? GetRegistryType(source.Registry) : null, - null, // we don't support local pull yet, but we may in the future - destination.RemoteRegistry is not null ? GetRegistryType(destination.RemoteRegistry) : null, - destination.LocalRegistry is not null ? GetLocalStorageType(destination.LocalRegistry) : null); - return new Telemetry(Log, context); - } - - private RegistryType GetRegistryType(Registry r) - { - if (r.IsMcr) return RegistryType.MCR; - if (r.IsGithubPackageRegistry) return RegistryType.GitHub; - if (r.IsAmazonECRRegistry) return RegistryType.AWS; - if (r.IsAzureContainerRegistry) return RegistryType.Azure; - if (r.IsGoogleArtifactRegistry) return RegistryType.Google; - if (r.IsDockerHub) return RegistryType.DockerHub; - return RegistryType.Other; - } - - private LocalStorageType GetLocalStorageType(ILocalRegistry r) - { - if (r is ArchiveFileRegistry) return LocalStorageType.Tarball; - var d = r as DockerCli; - System.Diagnostics.Debug.Assert(d != null, "Unknown local registry type"); - if (d.GetCommand() == DockerCli.DockerCommand) return LocalStorageType.Docker; - else return LocalStorageType.Podman; - } - - /// - /// Interesting data about the container publish - used to track the usage rates of various sources/targets of the process - /// and to help diagnose issues with the container publish overall. - /// - /// If the base image came from a remote registry, what kind of registry was it? - /// If the base image came from a local store of some kind, what kind of store was it? - /// If the new image is being pushed to a remote registry, what kind of registry is it? - /// If the new image is being stored in a local store of some kind, what kind of store is it? - private record class PublishTelemetryContext(RegistryType? RemotePullType, LocalStorageType? LocalPullType, RegistryType? RemotePushType, LocalStorageType? LocalPushType); - private enum RegistryType { Azure, AWS, Google, GitHub, DockerHub, MCR, Other } - private enum LocalStorageType { Docker, Podman, Tarball } - - private class Telemetry(Microsoft.Build.Utilities.TaskLoggingHelper Log, PublishTelemetryContext context) - { - private IDictionary ContextProperties() => new Dictionary - { - { nameof(context.RemotePullType), context.RemotePullType?.ToString() }, - { nameof(context.LocalPullType), context.LocalPullType?.ToString() }, - { nameof(context.RemotePushType), context.RemotePushType?.ToString() }, - { nameof(context.LocalPushType), context.LocalPushType?.ToString() } - }; - - public void LogPublishSuccess() - { - Log.LogTelemetry("sdk/container/publish/success", ContextProperties()); - } - - public void LogUnknownRepository() - { - var props = ContextProperties(); - props.Add("error", "unknown_repository"); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - public void LogCredentialFailure(SourceImageReference _) - { - var props = ContextProperties(); - props.Add("error", "credential_failure"); - props.Add("direction", "pull"); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - public void LogCredentialFailure(DestinationImageReference d) - { - var props = ContextProperties(); - props.Add("error", "credential_failure"); - props.Add("direction", "push"); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - public void LogRidMismatch(string desiredRid, string[] availableRids) - { - var props = ContextProperties(); - props.Add("error", "rid_mismatch"); - props.Add("target_rid", desiredRid); - props.Add("available_rids", string.Join(",", availableRids)); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - public void LogMissingLocalBinary() - { - var props = ContextProperties(); - props.Add("error", "missing_binary"); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - public void LogLocalLoadError() - { - var props = ContextProperties(); - props.Add("error", "local_load"); - Log.LogTelemetry("sdk/container/publish/error", props); - } - - } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/Telemetry.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/Telemetry.cs new file mode 100644 index 000000000000..df4ee99ba950 --- /dev/null +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/Telemetry.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.NET.Build.Containers.LocalDaemons; + +namespace Microsoft.NET.Build.Containers.Tasks; + +internal class Telemetry +{ + /// + /// Interesting data about the container publish - used to track the usage rates of various sources/targets of the process + /// and to help diagnose issues with the container publish overall. + /// + /// If the base image came from a remote registry, what kind of registry was it? + /// If the base image came from a local store of some kind, what kind of store was it? + /// If the new image is being pushed to a remote registry, what kind of registry is it? + /// If the new image is being stored in a local store of some kind, what kind of store is it? + private record class PublishTelemetryContext(RegistryType? RemotePullType, LocalStorageType? LocalPullType, RegistryType? RemotePushType, LocalStorageType? LocalPushType); + private enum RegistryType { Azure, AWS, Google, GitHub, DockerHub, MCR, Other } + private enum LocalStorageType { Docker, Podman, Tarball } + + private readonly Microsoft.Build.Utilities.TaskLoggingHelper Log; + private readonly PublishTelemetryContext Context; + + internal Telemetry( + SourceImageReference source, + DestinationImageReference destination, + Microsoft.Build.Utilities.TaskLoggingHelper Log) + { + this.Log = Log; + Context = new PublishTelemetryContext( + source.Registry is not null ? GetRegistryType(source.Registry) : null, + null, // we don't support local pull yet, but we may in the future + destination.RemoteRegistry is not null ? GetRegistryType(destination.RemoteRegistry) : null, + destination.LocalRegistry is not null ? GetLocalStorageType(destination.LocalRegistry) : null); + } + + private RegistryType GetRegistryType(Registry r) + { + if (r.IsMcr) return RegistryType.MCR; + if (r.IsGithubPackageRegistry) return RegistryType.GitHub; + if (r.IsAmazonECRRegistry) return RegistryType.AWS; + if (r.IsAzureContainerRegistry) return RegistryType.Azure; + if (r.IsGoogleArtifactRegistry) return RegistryType.Google; + if (r.IsDockerHub) return RegistryType.DockerHub; + return RegistryType.Other; + } + + private LocalStorageType GetLocalStorageType(ILocalRegistry r) + { + if (r is ArchiveFileRegistry) return LocalStorageType.Tarball; + var d = r as DockerCli; + System.Diagnostics.Debug.Assert(d != null, "Unknown local registry type"); + if (d.GetCommand() == DockerCli.DockerCommand) return LocalStorageType.Docker; + else return LocalStorageType.Podman; + } + + private IDictionary ContextProperties() => new Dictionary + { + { nameof(Context.RemotePullType), Context.RemotePullType?.ToString() }, + { nameof(Context.LocalPullType), Context.LocalPullType?.ToString() }, + { nameof(Context.RemotePushType), Context.RemotePushType?.ToString() }, + { nameof(Context.LocalPushType), Context.LocalPushType?.ToString() } + }; + + public void LogPublishSuccess() + { + Log.LogTelemetry("sdk/container/publish/success", ContextProperties()); + } + + public void LogUnknownRepository() + { + var props = ContextProperties(); + props.Add("error", "unknown_repository"); + Log.LogTelemetry("sdk/container/publish/error", props); + } + + public void LogCredentialFailure(SourceImageReference _) + { + var props = ContextProperties(); + props.Add("error", "credential_failure"); + props.Add("direction", "pull"); + Log.LogTelemetry("sdk/container/publish/error", props); + } + + public void LogCredentialFailure(DestinationImageReference d) + { + var props = ContextProperties(); + props.Add("error", "credential_failure"); + props.Add("direction", "push"); + Log.LogTelemetry("sdk/container/publish/error", props); + } + + public void LogRidMismatch(string desiredRid, string[] availableRids) + { + var props = ContextProperties(); + props.Add("error", "rid_mismatch"); + props.Add("target_rid", desiredRid); + props.Add("available_rids", string.Join(",", availableRids)); + Log.LogTelemetry("sdk/container/publish/error", props); + } + + public void LogMissingLocalBinary() + { + var props = ContextProperties(); + props.Add("error", "missing_binary"); + Log.LogTelemetry("sdk/container/publish/error", props); + } + + public void LogLocalLoadError() + { + var props = ContextProperties(); + props.Add("error", "local_load"); + Log.LogTelemetry("sdk/container/publish/error", props); + } +} \ No newline at end of file From 0399310797094f4e0a260625c398f89aaec62533 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Fri, 31 Jan 2025 19:11:53 +0100 Subject: [PATCH 07/74] move Telemetry class to another folder --- .../Microsoft.NET.Build.Containers/{Tasks => }/Telemetry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/Containers/Microsoft.NET.Build.Containers/{Tasks => }/Telemetry.cs (99%) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/Telemetry.cs b/src/Containers/Microsoft.NET.Build.Containers/Telemetry.cs similarity index 99% rename from src/Containers/Microsoft.NET.Build.Containers/Tasks/Telemetry.cs rename to src/Containers/Microsoft.NET.Build.Containers/Telemetry.cs index df4ee99ba950..009b93d7717c 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/Telemetry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Telemetry.cs @@ -3,7 +3,7 @@ using Microsoft.NET.Build.Containers.LocalDaemons; -namespace Microsoft.NET.Build.Containers.Tasks; +namespace Microsoft.NET.Build.Containers; internal class Telemetry { From ec26765bdddf53f3796cf0e72321f40ffbc0001e Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Fri, 31 Jan 2025 21:40:40 +0100 Subject: [PATCH 08/74] extract publishing image logic into a seperate class --- .../ImagePublisher.cs | 365 ++++++++++++------ .../Tasks/CreateImageIndex.cs | 107 +---- .../Tasks/CreateNewImage.cs | 111 +----- 3 files changed, 263 insertions(+), 320 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs index c877d2c004ba..e2b76e461867 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs @@ -1,121 +1,262 @@ -// // Licensed to the .NET Foundation under one or more agreements. -// // The .NET Foundation licenses this file to you under the MIT license. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. -// using Microsoft.NET.Build.Containers.LocalDaemons; -// using Microsoft.NET.Build.Containers.Resources; +using Microsoft.Build.Framework; +using Microsoft.NET.Build.Containers.Resources; -// namespace Microsoft.NET.Build.Containers; +namespace Microsoft.NET.Build.Containers; +internal static class ImagePublisher +{ + public static async Task PublishImage( + BuiltImage image, + SourceImageReference sourceImageReference, + DestinationImageReference destinationImageReference, + Microsoft.Build.Utilities.TaskLoggingHelper Log, + IBuildEngine? BuildEngine, + Telemetry telemetry, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); -// internal static class ImagePublisher -// { -// public static async Task PublishImage(BuiltImage builtImage, SourceImageReference sourceImageReference, -// DestinationImageReference destinationImageReference, -// Telemetry telemetry, -// CancellationToken cancellationToken) -// { -// cancellationToken.ThrowIfCancellationRequested(); + switch (destinationImageReference.Kind) + { + case DestinationImageReferenceKind.LocalRegistry: + await PushToLocalRegistryAsync( + image, + sourceImageReference, + destinationImageReference, + Log, + BuildEngine, + telemetry, + cancellationToken).ConfigureAwait(false); + break; + case DestinationImageReferenceKind.RemoteRegistry: + await PushToRemoteRegistryAsync( + image, + sourceImageReference, + destinationImageReference, + Log, + BuildEngine, + cancellationToken).ConfigureAwait(false); + break; + default: + throw new ArgumentOutOfRangeException(); + } -// switch (destinationImageReference.Kind) -// { -// case DestinationImageReferenceKind.LocalRegistry: -// await PushToLocalRegistryAsync(builtImage, -// sourceImageReference, -// destinationImageReference, -// telemetry, -// cancellationToken).ConfigureAwait(false); -// break; -// case DestinationImageReferenceKind.RemoteRegistry: -// await PushToRemoteRegistryAsync(builtImage, -// sourceImageReference, -// destinationImageReference, -// cancellationToken).ConfigureAwait(false); -// break; -// default: -// throw new ArgumentOutOfRangeException(); -// } + telemetry.LogPublishSuccess(); + } -// telemetry.LogPublishSuccess(); -// } + public static async Task PublishImage( + BuiltImage[] images, + SourceImageReference sourceImageReference, + DestinationImageReference destinationImageReference, + Microsoft.Build.Utilities.TaskLoggingHelper Log, + IBuildEngine? BuildEngine, + Telemetry telemetry, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); -// private static async Task PushToLocalRegistryAsync( -// BuiltImage builtImage, -// SourceImageReference sourceImageReference, -// DestinationImageReference destinationImageReference, -// Telemetry telemetry, -// CancellationToken cancellationToken) -// { -// ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; -// if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) -// { -// telemetry.LogMissingLocalBinary(); -// Log.LogErrorWithCodeFromResources(nameof(Strings.LocalRegistryNotAvailable)); -// return; -// } -// try -// { -// await localRegistry.LoadAsync(builtImage, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); -// SafeLog(Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); + switch (destinationImageReference.Kind) + { + case DestinationImageReferenceKind.LocalRegistry: + await PushToLocalRegistryAsync( + images, + sourceImageReference, + destinationImageReference, + Log, + BuildEngine, + telemetry, + cancellationToken).ConfigureAwait(false); + break; + case DestinationImageReferenceKind.RemoteRegistry: + await PushToRemoteRegistryAsync( + images, + sourceImageReference, + destinationImageReference, + Log, + BuildEngine, + cancellationToken).ConfigureAwait(false); + break; + default: + throw new ArgumentOutOfRangeException(); + } -// if (localRegistry is ArchiveFileRegistry archive) -// { -// GeneratedArchiveOutputPath = archive.ArchiveOutputPath; -// } -// } -// catch (ContainerHttpException e) -// { -// if (BuildEngine != null) -// { -// Log.LogErrorFromException(e, true); -// } -// } -// catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle) -// { -// telemetry.LogLocalLoadError(); -// Log.LogErrorFromException(dle, showStackTrace: false); -// } -// catch (ArgumentException argEx) -// { -// Log.LogErrorFromException(argEx, showStackTrace: false); -// } -// } + telemetry.LogPublishSuccess(); + } -// private static async Task PushToRemoteRegistryAsync( -// BuiltImage builtImage, -// SourceImageReference sourceImageReference, -// DestinationImageReference destinationImageReference, -// CancellationToken cancellationToken) -// { -// try -// { -// await destinationImageReference.RemoteRegistry!.PushAsync( -// builtImage, -// sourceImageReference, -// destinationImageReference, -// cancellationToken).ConfigureAwait(false); -// SafeLog(Strings.ContainerBuilder_ImageUploadedToRegistry, destinationImageReference, OutputRegistry); -// } -// catch (UnableToAccessRepositoryException) -// { -// if (BuildEngine != null) -// { -// Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); -// } -// } -// catch (ContainerHttpException e) -// { -// if (BuildEngine != null) -// { -// Log.LogErrorFromException(e, true); -// } -// } -// catch (Exception e) -// { -// if (BuildEngine != null) -// { -// Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); -// Log.LogMessage(MessageImportance.Low, "Details: {0}", e); -// } -// } -// } -// } \ No newline at end of file + private static async Task PushToLocalRegistryAsync( + BuiltImage image, + SourceImageReference sourceImageReference, + DestinationImageReference destinationImageReference, + Microsoft.Build.Utilities.TaskLoggingHelper Log, + IBuildEngine? BuildEngine, + Telemetry telemetry, + CancellationToken cancellationToken) + { + ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; + if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) + { + telemetry.LogMissingLocalBinary(); + Log.LogErrorWithCodeFromResources(nameof(Strings.LocalRegistryNotAvailable)); + return; + } + try + { + await localRegistry.LoadAsync(image, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); + if (BuildEngine != null) + { + Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); + } + } + catch (ContainerHttpException e) + { + if (BuildEngine != null) + { + Log.LogErrorFromException(e, true); + } + } + catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle) + { + telemetry.LogLocalLoadError(); + Log.LogErrorFromException(dle, showStackTrace: false); + } + catch (ArgumentException argEx) + { + Log.LogErrorFromException(argEx, showStackTrace: false); + } + } + + private static async Task PushToLocalRegistryAsync( + BuiltImage[] images, + SourceImageReference sourceImageReference, + DestinationImageReference destinationImageReference, + Microsoft.Build.Utilities.TaskLoggingHelper Log, + IBuildEngine? BuildEngine, + Telemetry telemetry, + CancellationToken cancellationToken) + { + ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; + if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) + { + telemetry.LogMissingLocalBinary(); + Log.LogErrorWithCodeFromResources(nameof(Strings.LocalRegistryNotAvailable)); + return; + } + try + { + await localRegistry.LoadAsync(images, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); + if (BuildEngine != null) + { + Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); + } + } + catch (ContainerHttpException e) + { + if (BuildEngine != null) + { + Log.LogErrorFromException(e, true); + } + } + catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle) + { + telemetry.LogLocalLoadError(); + Log.LogErrorFromException(dle, showStackTrace: false); + } + catch (ArgumentException argEx) + { + Log.LogErrorFromException(argEx, showStackTrace: false); + } + } + + private static async Task PushToRemoteRegistryAsync( + BuiltImage image, + SourceImageReference sourceImageReference, + DestinationImageReference destinationImageReference, + Microsoft.Build.Utilities.TaskLoggingHelper Log, + IBuildEngine? BuildEngine, + CancellationToken cancellationToken) + { + try + { + await destinationImageReference.RemoteRegistry!.PushAsync( + image, + sourceImageReference, + destinationImageReference, + cancellationToken).ConfigureAwait(false); + if (BuildEngine != null) + { + Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToRegistry, destinationImageReference, destinationImageReference.RemoteRegistry!.RegistryName); + } + } + catch (UnableToAccessRepositoryException) + { + if (BuildEngine != null) + { + Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); + } + } + catch (ContainerHttpException e) + { + if (BuildEngine != null) + { + Log.LogErrorFromException(e, true); + } + } + catch (Exception e) + { + if (BuildEngine != null) + { + Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); + Log.LogMessage(MessageImportance.Low, "Details: {0}", e); + } + } + } + + private static async Task PushToRemoteRegistryAsync( + BuiltImage[] images, + SourceImageReference sourceImageReference, + DestinationImageReference destinationImageReference, + Microsoft.Build.Utilities.TaskLoggingHelper Log, + IBuildEngine? BuildEngine, + CancellationToken cancellationToken) + { + try + { + (string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images); + await destinationImageReference.RemoteRegistry!.PushManifestListAsync( + destinationImageReference.Repository, + destinationImageReference.Tags, + imageIndex, + mediaType, + cancellationToken).ConfigureAwait(false); + if (BuildEngine != null) + { + Log.LogMessage(MessageImportance.High, Strings.ImageIndexUploadedToRegistry, destinationImageReference, destinationImageReference.RemoteRegistry!.RegistryName); + } + } + catch (UnableToAccessRepositoryException) + { + if (BuildEngine != null) + { + Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); + } + } + catch (ContainerHttpException e) + { + if (BuildEngine != null) + { + Log.LogErrorFromException(e, true); + } + } + catch (Exception e) + { + if (BuildEngine != null) + { + Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); + Log.LogMessage(MessageImportance.Low, "Details: {0}", e); + } + } + } +} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index e315adef8921..49326cd20cf9 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -151,115 +151,16 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) return false; } - var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log); + GeneratedArchiveOutputPath = ArchiveOutputPath; - switch (destinationImageReference.Kind) - { - case DestinationImageReferenceKind.LocalRegistry: - await PushToLocalRegistryAsync(images, - sourceImageReference, - destinationImageReference, - telemetry, - cancellationToken).ConfigureAwait(false); - break; - case DestinationImageReferenceKind.RemoteRegistry: - await PushToRemoteRegistryAsync(images, - destinationImageReference, - cancellationToken).ConfigureAwait(false); - break; - default: - throw new ArgumentOutOfRangeException(); - } + var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log); - telemetry.LogPublishSuccess(); + await ImagePublisher.PublishImage(images, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) + .ConfigureAwait(false); return !Log.HasLoggedErrors; } - private async Task PushToLocalRegistryAsync(BuiltImage[] images, SourceImageReference sourceImageReference, - DestinationImageReference destinationImageReference, - Telemetry telemetry, - CancellationToken cancellationToken) - { - ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; - if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) - { - telemetry.LogMissingLocalBinary(); - Log.LogErrorWithCodeFromResources(nameof(Strings.LocalRegistryNotAvailable)); - return; - } - try - { - await localRegistry.LoadAsync(images, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); - SafeLog(Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); - - if (localRegistry is ArchiveFileRegistry archive) - { - GeneratedArchiveOutputPath = archive.ArchiveOutputPath; - } - } - catch (ContainerHttpException e) - { - if (BuildEngine != null) - { - Log.LogErrorFromException(e, true); - } - } - catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle) - { - telemetry.LogLocalLoadError(); - Log.LogErrorFromException(dle, showStackTrace: false); - } - catch (ArgumentException argEx) - { - Log.LogErrorFromException(argEx, showStackTrace: false); - } - } - - private async Task PushToRemoteRegistryAsync(BuiltImage[] images, - DestinationImageReference destinationImageReference, - CancellationToken cancellationToken) - { - try - { - (string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images); - await destinationImageReference.RemoteRegistry!.PushManifestListAsync( - destinationImageReference.Repository, - destinationImageReference.Tags, - imageIndex, - mediaType, - cancellationToken).ConfigureAwait(false); - SafeLog(Strings.ImageIndexUploadedToRegistry, destinationImageReference, OutputRegistry); - } - catch (UnableToAccessRepositoryException) - { - if (BuildEngine != null) - { - Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); - } - } - catch (ContainerHttpException e) - { - if (BuildEngine != null) - { - Log.LogErrorFromException(e, true); - } - } - catch (Exception e) - { - if (BuildEngine != null) - { - Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); - Log.LogMessage(MessageImportance.Low, "Details: {0}", e); - } - } - } - - private void SafeLog(string message, params object[] formatParams) - { - if (BuildEngine != null) Log.LogMessage(MessageImportance.High, message, formatParams); - } - private BuiltImage[] ParseImages(DestinationImageReferenceKind destinationKind) { var images = new BuiltImage[GeneratedContainers.Length]; diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index 89131d696e46..ba9eea570872 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -1,10 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Text.Json; using Microsoft.Build.Framework; using Microsoft.Extensions.Logging; -using Microsoft.NET.Build.Containers.LocalDaemons; using Microsoft.NET.Build.Containers.Logging; using Microsoft.NET.Build.Containers.Resources; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -122,7 +120,10 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) return !Log.HasLoggedErrors; } - SafeLog(Strings.ContainerBuilder_StartBuildingImage, Repository, String.Join(",", ImageTags), sourceImageReference); + if (BuildEngine != null) + { + Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_StartBuildingImage, Repository, String.Join(",", ImageTags), sourceImageReference); + } Layer newLayer = Layer.FromDirectory(PublishDirectory, WorkingDirectory, imageBuilder.IsWindows, imageBuilder.ManifestMediaType); imageBuilder.AddLayer(newLayer); @@ -177,107 +178,12 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) GeneratedContainerMediaType = builtImage.ManifestMediaType; GeneratedContainerNames = destinationImageReference.FullyQualifiedImageNames().Select(name => new Microsoft.Build.Utilities.TaskItem(name)).ToArray(); - switch (destinationImageReference.Kind) - { - case DestinationImageReferenceKind.LocalRegistry: - await PushToLocalRegistryAsync(builtImage, - sourceImageReference, - destinationImageReference, - telemetry, - cancellationToken).ConfigureAwait(false); - break; - case DestinationImageReferenceKind.RemoteRegistry: - await PushToRemoteRegistryAsync(builtImage, - sourceImageReference, - destinationImageReference, - cancellationToken).ConfigureAwait(false); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - telemetry.LogPublishSuccess(); + await ImagePublisher.PublishImage(builtImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) + .ConfigureAwait(false); return !Log.HasLoggedErrors; } - private async Task PushToLocalRegistryAsync(BuiltImage builtImage, SourceImageReference sourceImageReference, - DestinationImageReference destinationImageReference, - Telemetry telemetry, - CancellationToken cancellationToken) - { - ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; - if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) - { - telemetry.LogMissingLocalBinary(); - Log.LogErrorWithCodeFromResources(nameof(Strings.LocalRegistryNotAvailable)); - return; - } - try - { - await localRegistry.LoadAsync(builtImage, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); - SafeLog(Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); - - if (localRegistry is ArchiveFileRegistry archive) - { - GeneratedArchiveOutputPath = archive.ArchiveOutputPath; - } - } - catch (ContainerHttpException e) - { - if (BuildEngine != null) - { - Log.LogErrorFromException(e, true); - } - } - catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle) - { - telemetry.LogLocalLoadError(); - Log.LogErrorFromException(dle, showStackTrace: false); - } - catch (ArgumentException argEx) - { - Log.LogErrorFromException(argEx, showStackTrace: false); - } - } - - private async Task PushToRemoteRegistryAsync(BuiltImage builtImage, SourceImageReference sourceImageReference, - DestinationImageReference destinationImageReference, - CancellationToken cancellationToken) - { - try - { - await destinationImageReference.RemoteRegistry!.PushAsync( - builtImage, - sourceImageReference, - destinationImageReference, - cancellationToken).ConfigureAwait(false); - SafeLog(Strings.ContainerBuilder_ImageUploadedToRegistry, destinationImageReference, OutputRegistry); - } - catch (UnableToAccessRepositoryException) - { - if (BuildEngine != null) - { - Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); - } - } - catch (ContainerHttpException e) - { - if (BuildEngine != null) - { - Log.LogErrorFromException(e, true); - } - } - catch (Exception e) - { - if (BuildEngine != null) - { - Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); - Log.LogMessage(MessageImportance.Low, "Details: {0}", e); - } - } - } - private void SetPorts(ImageBuilder image, ITaskItem[] exposedPorts) { foreach (var port in exposedPorts) @@ -324,11 +230,6 @@ private void SetEnvironmentVariables(ImageBuilder img, ITaskItem[] envVars) } } - private void SafeLog(string message, params object[] formatParams) - { - if (BuildEngine != null) Log.LogMessage(MessageImportance.High, message, formatParams); - } - public void Dispose() { _cancellationTokenSource.Dispose(); From 7acc5e91ae0c98cd3b29b99f89a038d29c7dbe06 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Fri, 31 Jan 2025 23:43:21 +0100 Subject: [PATCH 09/74] refactor ImagePublisher --- .../ImagePublisher.cs | 153 +++++------------- .../Tasks/CreateImageIndex.cs | 2 +- .../Tasks/CreateNewImage.cs | 2 +- 3 files changed, 45 insertions(+), 112 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs index e2b76e461867..960a0b0c6027 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs @@ -8,8 +8,8 @@ namespace Microsoft.NET.Build.Containers; internal static class ImagePublisher { - public static async Task PublishImage( - BuiltImage image, + public static async Task PublishImageAsync( + BuiltImage singleArchImage, SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, @@ -23,22 +23,26 @@ public static async Task PublishImage( { case DestinationImageReferenceKind.LocalRegistry: await PushToLocalRegistryAsync( - image, + singleArchImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, - cancellationToken).ConfigureAwait(false); + cancellationToken, + destinationImageReference.LocalRegistry!.LoadAsync, + Strings.ContainerBuilder_ImageUploadedToLocalDaemon).ConfigureAwait(false); break; case DestinationImageReferenceKind.RemoteRegistry: await PushToRemoteRegistryAsync( - image, + singleArchImage, sourceImageReference, destinationImageReference, Log, BuildEngine, - cancellationToken).ConfigureAwait(false); + cancellationToken, + destinationImageReference.RemoteRegistry!.PushAsync, + Strings.ContainerBuilder_ImageUploadedToRegistry).ConfigureAwait(false); break; default: throw new ArgumentOutOfRangeException(); @@ -47,8 +51,8 @@ await PushToRemoteRegistryAsync( telemetry.LogPublishSuccess(); } - public static async Task PublishImage( - BuiltImage[] images, + public static async Task PublishImageAsync( + BuiltImage[] multiArchImage, SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, @@ -62,22 +66,35 @@ public static async Task PublishImage( { case DestinationImageReferenceKind.LocalRegistry: await PushToLocalRegistryAsync( - images, + multiArchImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, - cancellationToken).ConfigureAwait(false); + cancellationToken, + destinationImageReference.LocalRegistry!.LoadAsync, + Strings.ContainerBuilder_ImageUploadedToLocalDaemon).ConfigureAwait(false); break; case DestinationImageReferenceKind.RemoteRegistry: await PushToRemoteRegistryAsync( - images, + multiArchImage, sourceImageReference, destinationImageReference, Log, BuildEngine, - cancellationToken).ConfigureAwait(false); + cancellationToken, + async (images, source, destination, token) => + { + (string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images); + await destinationImageReference.RemoteRegistry!.PushManifestListAsync( + destinationImageReference.Repository, + destinationImageReference.Tags, + imageIndex, + mediaType, + cancellationToken).ConfigureAwait(false); + }, + Strings.ImageIndexUploadedToRegistry).ConfigureAwait(false); break; default: throw new ArgumentOutOfRangeException(); @@ -86,14 +103,16 @@ await PushToRemoteRegistryAsync( telemetry.LogPublishSuccess(); } - private static async Task PushToLocalRegistryAsync( - BuiltImage image, + private static async Task PushToLocalRegistryAsync( + T image, SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, IBuildEngine? BuildEngine, Telemetry telemetry, - CancellationToken cancellationToken) + CancellationToken cancellationToken, + Func loadFunc, + string successMessage) { ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) @@ -104,10 +123,10 @@ private static async Task PushToLocalRegistryAsync( } try { - await localRegistry.LoadAsync(image, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); + await loadFunc(image, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); if (BuildEngine != null) { - Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); + Log.LogMessage(MessageImportance.High, successMessage, destinationImageReference, localRegistry); } } catch (ContainerHttpException e) @@ -128,112 +147,26 @@ private static async Task PushToLocalRegistryAsync( } } - private static async Task PushToLocalRegistryAsync( - BuiltImage[] images, + private static async Task PushToRemoteRegistryAsync( + T image, SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, IBuildEngine? BuildEngine, - Telemetry telemetry, - CancellationToken cancellationToken) + CancellationToken cancellationToken, + Func pushFunc, + string successMessage) { - ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; - if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) - { - telemetry.LogMissingLocalBinary(); - Log.LogErrorWithCodeFromResources(nameof(Strings.LocalRegistryNotAvailable)); - return; - } try { - await localRegistry.LoadAsync(images, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); - if (BuildEngine != null) - { - Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); - } - } - catch (ContainerHttpException e) - { - if (BuildEngine != null) - { - Log.LogErrorFromException(e, true); - } - } - catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle) - { - telemetry.LogLocalLoadError(); - Log.LogErrorFromException(dle, showStackTrace: false); - } - catch (ArgumentException argEx) - { - Log.LogErrorFromException(argEx, showStackTrace: false); - } - } - - private static async Task PushToRemoteRegistryAsync( - BuiltImage image, - SourceImageReference sourceImageReference, - DestinationImageReference destinationImageReference, - Microsoft.Build.Utilities.TaskLoggingHelper Log, - IBuildEngine? BuildEngine, - CancellationToken cancellationToken) - { - try - { - await destinationImageReference.RemoteRegistry!.PushAsync( + await pushFunc( image, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); if (BuildEngine != null) { - Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToRegistry, destinationImageReference, destinationImageReference.RemoteRegistry!.RegistryName); - } - } - catch (UnableToAccessRepositoryException) - { - if (BuildEngine != null) - { - Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); - } - } - catch (ContainerHttpException e) - { - if (BuildEngine != null) - { - Log.LogErrorFromException(e, true); - } - } - catch (Exception e) - { - if (BuildEngine != null) - { - Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); - Log.LogMessage(MessageImportance.Low, "Details: {0}", e); - } - } - } - - private static async Task PushToRemoteRegistryAsync( - BuiltImage[] images, - SourceImageReference sourceImageReference, - DestinationImageReference destinationImageReference, - Microsoft.Build.Utilities.TaskLoggingHelper Log, - IBuildEngine? BuildEngine, - CancellationToken cancellationToken) - { - try - { - (string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images); - await destinationImageReference.RemoteRegistry!.PushManifestListAsync( - destinationImageReference.Repository, - destinationImageReference.Tags, - imageIndex, - mediaType, - cancellationToken).ConfigureAwait(false); - if (BuildEngine != null) - { - Log.LogMessage(MessageImportance.High, Strings.ImageIndexUploadedToRegistry, destinationImageReference, destinationImageReference.RemoteRegistry!.RegistryName); + Log.LogMessage(MessageImportance.High, successMessage, destinationImageReference, destinationImageReference.RemoteRegistry!.RegistryName); } } catch (UnableToAccessRepositoryException) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 49326cd20cf9..69340f2642ac 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -155,7 +155,7 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log); - await ImagePublisher.PublishImage(images, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) + await ImagePublisher.PublishImageAsync(images, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) .ConfigureAwait(false); return !Log.HasLoggedErrors; diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index ba9eea570872..64a298b83e53 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -178,7 +178,7 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) GeneratedContainerMediaType = builtImage.ManifestMediaType; GeneratedContainerNames = destinationImageReference.FullyQualifiedImageNames().Select(name => new Microsoft.Build.Utilities.TaskItem(name)).ToArray(); - await ImagePublisher.PublishImage(builtImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) + await ImagePublisher.PublishImageAsync(builtImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) .ConfigureAwait(false); return !Log.HasLoggedErrors; From 181b6fa27377cc7d6132d8a9617f5c5cd9dd9080 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sat, 1 Feb 2025 11:18:16 +0100 Subject: [PATCH 10/74] skip publishing individual images in case of tarball and local daemon docker publishing --- .../Tasks/CreateNewImage.Interface.cs | 5 +++++ .../Tasks/CreateNewImage.cs | 9 ++++++--- .../build/Microsoft.NET.Build.Containers.targets | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs index 3da5135b9b07..5e9c01de4e45 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs @@ -161,6 +161,11 @@ partial class CreateNewImage [Required] public bool GenerateDigestLabel { get; set; } + /// + /// If true, the tooling will skip the publishing step. + /// + public bool SkipPublishing { get; set; } + [Output] public string GeneratedContainerManifest { get; set; } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index 64a298b83e53..bcc429acf343 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -178,9 +178,12 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) GeneratedContainerMediaType = builtImage.ManifestMediaType; GeneratedContainerNames = destinationImageReference.FullyQualifiedImageNames().Select(name => new Microsoft.Build.Utilities.TaskItem(name)).ToArray(); - await ImagePublisher.PublishImageAsync(builtImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) - .ConfigureAwait(false); - + if (!SkipPublishing) + { + await ImagePublisher.PublishImageAsync(builtImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) + .ConfigureAwait(false); + } + return !Log.HasLoggedErrors; } diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets index 5309bdbeaf8b..8817641c790a 100644 --- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets +++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets @@ -274,6 +274,7 @@ ContainerRuntimeIdentifier="$(ContainerRuntimeIdentifier)" ContainerUser="$(ContainerUser)" RuntimeIdentifierGraphPath="$(RuntimeIdentifierGraphPath)" + SkipPublishing="$(_SkipContainerPublishing)" GenerateLabels="$(ContainerGenerateLabels)" GenerateDigestLabel="$(ContainerGenerateLabelsImageBaseDigest)"> @@ -302,6 +303,17 @@ !$(ContainerArchiveOutputPath.EndsWith('\\')) and $(ContainerArchiveOutputPath.EndsWith('.tar.gz'))" /> + + + + + <_IsTarballPublishing Condition="'$(ContainerArchiveOutputPath)' != ''">true + <_IsLocalDockerPublishing Condition="$(ContainerRegistry) == '' and ($(LocalRegistry) == '' or $(LocalRegistry) == 'Docker')">true + + <_SkipContainerPublishing>false + <_SkipContainerPublishing Condition="'$(_IsTarballPublishing)' == 'true' or '$(_IsLocalDockerPublishing)' == 'true'">true + + <_rids Include="$(ContainerRuntimeIdentifiers)" Condition="'$(ContainerRuntimeIdentifiers)' != ''" /> <_rids Include="$(RuntimeIdentifiers)" Condition="'$(ContainerRuntimeIdentifiers)' == '' and '$(RuntimeIdentifiers)' != ''" /> @@ -328,7 +340,8 @@ _ContainerEnvironmentVariables=@(ContainerEnvironmentVariable->'%(Identity):%(Value)'); ContainerUser=$(ContainerUser); ContainerGenerateLabels=$(ContainerGenerateLabels); - ContainerGenerateLabelsImageBaseDigest=$(ContainerGenerateLabelsImageBaseDigest) + ContainerGenerateLabelsImageBaseDigest=$(ContainerGenerateLabelsImageBaseDigest); + _SkipContainerPublishing=$(_SkipContainerPublishing) "/> <_rids Remove ="$(_rids)" /> From ec09d0501edad23835ba7d62768ee52ccaf4a3d1 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sat, 1 Feb 2025 11:19:04 +0100 Subject: [PATCH 11/74] update public api --- .../PublicAPI/net472/PublicAPI.Unshipped.txt | 2 ++ .../PublicAPI/net8.0/PublicAPI.Unshipped.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt index 43526e9c3ab4..9e6b0de71dc0 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -86,6 +86,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateLabels.get -> bool Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateLabels.set -> void Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.get -> bool Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.set -> void +Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.get -> bool +Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.set -> void Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.get -> Microsoft.Build.Framework.ITaskItem![]! Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.set -> void override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ToolName.get -> string! diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt index f7e2955bd01f..5c07a3871ceb 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -251,6 +251,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateLabels.get -> bool Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateLabels.set -> void Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.get -> bool Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.set -> void +Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.get -> bool +Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.set -> void Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.get -> Microsoft.Build.Framework.ITaskItem![]! Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.set -> void Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties From 5e38e739a16069ebea234e47c509ba7a81536783 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sat, 1 Feb 2025 13:37:57 +0100 Subject: [PATCH 12/74] skip CreateImageIndex task in case of local damon podman publishing as it is not supported --- .../Resources/Strings.Designer.cs | 9 +++++++++ .../Resources/Strings.resx | 4 ++++ .../Resources/xlf/Strings.cs.xlf | 5 +++++ .../Resources/xlf/Strings.de.xlf | 5 +++++ .../Resources/xlf/Strings.es.xlf | 5 +++++ .../Resources/xlf/Strings.fr.xlf | 5 +++++ .../Resources/xlf/Strings.it.xlf | 5 +++++ .../Resources/xlf/Strings.ja.xlf | 5 +++++ .../Resources/xlf/Strings.ko.xlf | 5 +++++ .../Resources/xlf/Strings.pl.xlf | 5 +++++ .../Resources/xlf/Strings.pt-BR.xlf | 5 +++++ .../Resources/xlf/Strings.ru.xlf | 5 +++++ .../Resources/xlf/Strings.tr.xlf | 5 +++++ .../Resources/xlf/Strings.zh-Hans.xlf | 5 +++++ .../Resources/xlf/Strings.zh-Hant.xlf | 5 +++++ .../Tasks/CreateImageIndex.cs | 8 ++++++-- .../build/Microsoft.NET.Build.Containers.targets | 12 +++++++----- 17 files changed, 91 insertions(+), 7 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs index aff92942f408..c2b9b565298b 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs @@ -402,6 +402,15 @@ internal static string InvalidImageMetadata { } } + /// + /// Looks up a localized string similar to Image index creation for Podman is not supported.. + /// + internal static string ImageIndex_PodmanNotSupported { + get { + return ResourceManager.GetString("ImageIndex_PodmanNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to CONTAINER2005: The inferred image name '{0}' contains entirely invalid characters. The valid characters for an image name are alphanumeric characters, -, /, or _, and the image name must start with an alphanumeric character.. /// diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx index 6337f4861b8e..40e57e54665b 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx @@ -380,6 +380,10 @@ Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + + Image index creation for Podman is not supported. + + Error while reading daemon config: {0} {0} is the exception message that ends with period diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf index f0d7173a9cea..8e8ff66ce2e9 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: Nepodařilo se načíst bitovou kopii z místního registru. stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf index ea8fc611e0a0..c7be81fff503 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: Fehler beim Laden des Images aus der lokalen Registrierung. stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf index b4921dcbdf47..cb3170c2954d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: no se pudo cargar la imagen desde el registro local. Stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf index b945393a8398..cab3d8d82aca 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: Échec du chargement de l'image à partir du registre local. sortie standard : {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf index b42327bbfb94..a5f41cf91d2e 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: non è stato possibile caricare l'immagine dal registro locale. stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf index 5d2cf22f0f8c..cd52c02f2459 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: ローカル レジストリからイメージを読み込めませんでした。stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf index 2ff624cec676..8f5d7f791e13 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: 로컬 레지스트리에서 이미지를 로드하지 못했습니다. stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf index 86288597ee7c..1cbda11cd891 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: Nie można załadować obrazu z rejestru lokalnego. stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf index 69fcf3548d8d..9467c4090ed2 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: falha ao carregar a imagem do registro local. stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf index 2989bc8e9687..295593dc4fa5 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: не удалось загрузить образ из локального реестра. stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf index 42fcb52485c7..49ce6f3ce305 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: Görüntü yerel kayıt defterinden yüklenemedi. stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf index 8eb6429f7db6..dd9faa355ab4 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: 未能从本地注册表加载映像。stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf index 892b174bbe97..d7e262e3310a 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf @@ -164,6 +164,11 @@ Pushed image index '{0}' to registry '{1}'. + + Image index creation for Podman is not supported. + Image index creation for Podman is not supported. + + CONTAINER1009: Failed to load image from local registry. stdout: {0} CONTAINER1009: 無法從本機登錄載入映像。stdout: {0} diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 69340f2642ac..655b0e52a9d4 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -1,12 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; using System.Text.Json; using System.Text.Json.Nodes; using Microsoft.Build.Framework; using Microsoft.Extensions.Logging; -using Microsoft.NET.Build.Containers.LocalDaemons; using Microsoft.NET.Build.Containers.Logging; using Microsoft.NET.Build.Containers.Resources; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -128,6 +126,12 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); + if (LocalRegistry == "Podman") + { + Log.LogError(Strings.ImageIndex_PodmanNotSupported); + return false; + } + using MSBuildLoggerProvider loggerProvider = new(Log); ILoggerFactory msbuildLoggerFactory = new LoggerFactory(new[] { loggerProvider }); ILogger logger = msbuildLoggerFactory.CreateLogger(); diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets index 8817641c790a..d092e5951cd4 100644 --- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets +++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets @@ -307,11 +307,12 @@ - <_IsTarballPublishing Condition="'$(ContainerArchiveOutputPath)' != ''">true - <_IsLocalDockerPublishing Condition="$(ContainerRegistry) == '' and ($(LocalRegistry) == '' or $(LocalRegistry) == 'Docker')">true - <_SkipContainerPublishing>false - <_SkipContainerPublishing Condition="'$(_IsTarballPublishing)' == 'true' or '$(_IsLocalDockerPublishing)' == 'true'">true + <_SkipContainerPublishing Condition="$(ContainerArchiveOutputPath) != '' or ( $(ContainerRegistry) == '' and ( $(LocalRegistry) == '' or $(LocalRegistry) == 'Docker' ) )">true + + + <_SkipCreateImageIndex>false + <_SkipCreateImageIndex Condition="$(ContainerArchiveOutputPath) == '' and $(ContainerRegistry) == '' and $(LocalRegistry) == 'Podman'">true @@ -353,7 +354,8 @@ - Date: Sat, 1 Feb 2025 16:46:10 +0100 Subject: [PATCH 13/74] refactor CreateImageIndex task; improve logging; log tip for enabling conrainerd --- .../ImagePublisher.cs | 15 +++- .../PublicAPI/net8.0/PublicAPI.Unshipped.txt | 2 - .../Resources/Strings.Designer.cs | 26 +++++- .../Resources/Strings.resx | 14 ++- .../Resources/xlf/Strings.cs.xlf | 22 +++-- .../Resources/xlf/Strings.de.xlf | 22 +++-- .../Resources/xlf/Strings.es.xlf | 22 +++-- .../Resources/xlf/Strings.fr.xlf | 22 +++-- .../Resources/xlf/Strings.it.xlf | 22 +++-- .../Resources/xlf/Strings.ja.xlf | 22 +++-- .../Resources/xlf/Strings.ko.xlf | 22 +++-- .../Resources/xlf/Strings.pl.xlf | 22 +++-- .../Resources/xlf/Strings.pt-BR.xlf | 22 +++-- .../Resources/xlf/Strings.ru.xlf | 22 +++-- .../Resources/xlf/Strings.tr.xlf | 22 +++-- .../Resources/xlf/Strings.zh-Hans.xlf | 22 +++-- .../Resources/xlf/Strings.zh-Hant.xlf | 22 +++-- .../Tasks/CreateImageIndex.Interface.cs | 80 +++++++++++++++++ .../Tasks/CreateImageIndex.cs | 90 +------------------ .../Tasks/CreateNewImage.cs | 5 +- .../Microsoft.NET.Build.Containers.targets | 4 +- .../CreateImageIndexTests.cs | 24 ++--- 22 files changed, 355 insertions(+), 191 deletions(-) create mode 100644 src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs index 960a0b0c6027..fa890b82506a 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs @@ -74,7 +74,8 @@ await PushToLocalRegistryAsync( telemetry, cancellationToken, destinationImageReference.LocalRegistry!.LoadAsync, - Strings.ContainerBuilder_ImageUploadedToLocalDaemon).ConfigureAwait(false); + Strings.ContainerBuilder_ImageUploadedToLocalDaemon, + logWarningForMultiArch : true).ConfigureAwait(false); break; case DestinationImageReferenceKind.RemoteRegistry: await PushToRemoteRegistryAsync( @@ -112,7 +113,8 @@ private static async Task PushToLocalRegistryAsync( Telemetry telemetry, CancellationToken cancellationToken, Func loadFunc, - string successMessage) + string successMessage, + bool logWarningForMultiArch = false) { ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) @@ -145,6 +147,15 @@ private static async Task PushToLocalRegistryAsync( { Log.LogErrorFromException(argEx, showStackTrace: false); } + catch (DockerLoadException dle) + { + telemetry.LogLocalLoadError(); + Log.LogErrorFromException(dle, showStackTrace: false); + if (logWarningForMultiArch && dle.Message.Contains("no such file or directory")) + { + Log.LogMessage(MessageImportance.High, "Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings."); + } + } } private static async Task PushToRemoteRegistryAsync( diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt index 5c07a3871ceb..e5efa4bed6c1 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -34,8 +34,6 @@ Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedArchiveOutputPath Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Cancel() -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.CreateImageIndex() -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Dispose() -> void -Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedImageIndex.get -> string! -Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedImageIndex.set -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ImageTags.get -> string![]! Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ImageTags.set -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedContainers.get -> Microsoft.Build.Framework.ITaskItem![]! diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs index c2b9b565298b..5750e050ba0e 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs @@ -204,6 +204,15 @@ internal static string ContainerBuilder_StartBuildingImage { } } + /// + /// Looks up a localized string similar to Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.. + /// + internal static string ContainerBuilder_StartBuildingImageForRid { + get { + return ResourceManager.GetString("ContainerBuilder_StartBuildingImageForRid", resourceCulture); + } + } + /// /// Looks up a localized string similar to CONTAINER3001: Failed creating {0} process.. /// @@ -376,7 +385,7 @@ internal static string ImagePullNotSupported { } /// - /// Looks up a localized string similar to Cannot create manifest list (image index) because no images were provided.. + /// Looks up a localized string similar to Cannot create image index because no images were provided.. /// internal static string ImagesEmpty { get { @@ -394,7 +403,7 @@ internal static string InvalidEnvVar { } /// - /// Looks up a localized string similar to Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.. + /// Looks up a localized string similar to Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.. /// internal static string InvalidImageMetadata { get { @@ -402,6 +411,15 @@ internal static string InvalidImageMetadata { } } + /// + /// Looks up a localized string similar to Cannot create image index because at least one of the provided images manifest is invalid.. + /// + internal static string InvalidImageManifest { + get { + return ResourceManager.GetString("InvalidImageManifest", resourceCulture); + } + } + /// /// Looks up a localized string similar to Image index creation for Podman is not supported.. /// @@ -565,7 +583,7 @@ internal static string MissingPortNumber { } /// - /// Looks up a localized string similar to 'mediaType' of manifests should be the same in manifest list (image index).. + /// Looks up a localized string similar to 'mediaType' of manifests should be the same in image index.. /// internal static string MixedMediaTypes { get { @@ -817,7 +835,7 @@ internal static string UnrecognizedMediaType { } /// - /// Looks up a localized string similar to Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.. + /// Looks up a localized string similar to Cannot create image index for the provided 'mediaType' = '{0}'.. /// internal static string UnsupportedMediaType { get { diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx index 40e57e54665b..a27e84824210 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx @@ -358,6 +358,10 @@ Building image '{0}' with tags '{1}' on top of base image '{2}'. + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + Building image index '{0}' on top of manifests {1}. @@ -369,15 +373,19 @@ - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + + + + Cannot create image index because at least one of the provided images manifest is invalid. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf index 8e8ff66ce2e9..3d6e472adb12 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf @@ -79,6 +79,11 @@ Sestavení image {0} se značkami {1} nad základní imagí {2} + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: Nepovedlo se deserializovat token z JSON. @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: '{1}' není platná proměnná prostředí. Ignorování. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf index c7be81fff503..a9298e5a40de 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf @@ -79,6 +79,11 @@ Das Image „{0}“ mit den Tags „{1}“ wird auf dem Basisimage „{2}“ erstellt. + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: Das Token konnte nicht aus JSON deserialisiert werden. @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: „{1}“ war keine gültige Umgebungsvariable. Sie wird ignoriert. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf index cb3170c2954d..0e6827c5b8dd 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf @@ -79,6 +79,11 @@ Compilando imagen "{0}" con etiquetas "{1}" encima de la imagen base "{2}". + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: No se pudo deserializar el token de JSON. @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: "{1}" no era una variable de entorno válida. Ignorando. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf index cab3d8d82aca..d9fd1c30d615 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf @@ -79,6 +79,11 @@ Génération de l’image «{0}» avec des balises «{1}» au-dessus de l’image de base «{2}». + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: impossible de désérialiser le jeton à partir de JSON. @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0} : '{1}' n’était pas une variable d’environnement valide. Ignorant. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf index a5f41cf91d2e..8fb75366363b 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf @@ -79,6 +79,11 @@ Compilazione dell'immagine '{0}' con i tag '{1}' sopra l'immagine di base '{2}'. + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: non è stato possibile deserializzare il token da JSON. @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: '{1}' non è una variabile di ambiente valida. Il valore verrà ignorato. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf index cd52c02f2459..00aeda5edf91 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf @@ -79,6 +79,11 @@ 基本イメージ '{0}' の上にタグ '{1}' 付きのイメージ '{2}' をビルドしています。 + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: JSON からトークンを逆シリアル化できませんでした。 @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: '{1}' は有効な環境変数ではありませんでした。無視しています。 {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf index 8f5d7f791e13..0adadf299470 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf @@ -79,6 +79,11 @@ 기본 이미지 '{2}' 위에 '{1}' 태그가 있는 '{0}' 이미지를 빌드하는 중입니다. + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: JSON에서 토큰을 역직렬화할 수 없습니다. @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: '{1}'은(는) 유효한 환경 변수가 아닙니다. 무시 중. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf index 1cbda11cd891..1fc1770a7181 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf @@ -79,6 +79,11 @@ Tworzenie obrazu „{0}” z tagami „{1}” nad obrazem podstawowym „{2}”. + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: nie można zdeserializować tokenu z pliku JSON. @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: „{1}” nie jest prawidłową zmienną środowiskową. Ignorowanie. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf index 9467c4090ed2..8e6ce774b100 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf @@ -79,6 +79,11 @@ Construindo imagem '{0}' com tags '{1}' em cima da imagem base '{2}'. + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: não foi possível desserializar o token do JSON. @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: '{1}' não era uma variável de ambiente válida. Ignorando. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf index 295593dc4fa5..0d0d9417f2ca 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf @@ -79,6 +79,11 @@ Выполняется сборка образа "{0}" с тегами "{1}" поверх базового образа "{2}". + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: не удалось десериализовать токен из JSON. @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: "{1}" не является допустимой переменной среды. Пропуск. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf index 49ce6f3ce305..9f65b5082fba 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf @@ -79,6 +79,11 @@ '{1}' etiketli '{0}' resmi, '{2}' temel görüntüsünün üzerinde oluşturuluyor. + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: Belirteç, JSON'dan seri durumdan çıkarılamadı. @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: '{1}' geçerli bir Ortam Değişkeni değildi. Görmezden geliniyor. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf index dd9faa355ab4..0f5ad6a2d7a4 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf @@ -79,6 +79,11 @@ 在基本映像“{2}”顶部构建标记为“{1}”的映像“{0}”。 + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: 无法从 JSON 反序列化令牌。 @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: "{1}" 不是有效的环境变量。忽略。 {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf index d7e262e3310a..1287300e7b18 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf @@ -79,6 +79,11 @@ 在基礎映像 '{2}' 上建立具有標記 '{1}' 的映像 '{0}'。 + + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'. + + CONTAINER1007: Could not deserialize token from JSON. CONTAINER1007: 無法從 JSON 還原序列化權杖。 @@ -180,8 +185,8 @@ {StrBegin="CONTAINER1010: "} - Cannot create manifest list (image index) because no images were provided. - Cannot create manifest list (image index) because no images were provided. + Cannot create image index because no images were provided. + Cannot create image index because no images were provided. @@ -189,9 +194,14 @@ CONTAINER2015: {0}: '{1}' 不是有效的環境變數。正在忽略。 {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images manifest is invalid. + + - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. - Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. + Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata. @@ -420,8 +430,8 @@ {StrBegin="CONTAINER2001: "} - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. - Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. + Cannot create image index for the provided 'mediaType' = '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs new file mode 100644 index 000000000000..128657b0a897 --- /dev/null +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Framework; + +namespace Microsoft.NET.Build.Containers.Tasks; + +partial class CreateImageIndex +{ + /// + /// The base registry to pull from. + /// Ex: mcr.microsoft.com + /// + [Required] + public string BaseRegistry { get; set; } + + /// + /// The base image to pull. + /// Ex: dotnet/runtime + /// + [Required] + public string BaseImageName { get; set; } + + /// + /// The base image tag. + /// Ex: 6.0 + /// + [Required] + public string BaseImageTag { get; set; } + + /// + /// Manifests to include in the image index. + /// + [Required] + public ITaskItem[] GeneratedContainers { get; set; } + + /// + /// The registry to push the image index to. + /// + public string OutputRegistry { get; set; } + + /// + /// The file path to which to write a tar.gz archive of the container image. + /// + public string ArchiveOutputPath { get; set; } + + /// + /// The kind of local registry to use, if any. + /// + public string LocalRegistry { get; set; } + + /// + /// The name of the output image index (manifest list) that will be pushed to the registry. + /// + [Required] + public string Repository { get; set; } + + /// + /// The tag to associate with the new image index (manifest list). + /// + [Required] + public string[] ImageTags { get; set; } + + [Output] + public string GeneratedArchiveOutputPath { get; set; } + + public CreateImageIndex() + { + BaseRegistry = string.Empty; + BaseImageName = string.Empty; + BaseImageTag = string.Empty; + GeneratedContainers = Array.Empty(); + OutputRegistry = string.Empty; + ArchiveOutputPath = string.Empty; + LocalRegistry = string.Empty; + Repository = string.Empty; + ImageTags = Array.Empty(); + GeneratedArchiveOutputPath = string.Empty; + } +} \ No newline at end of file diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 655b0e52a9d4..eb5743d81846 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -12,88 +12,8 @@ namespace Microsoft.NET.Build.Containers.Tasks; -public sealed class CreateImageIndex : Microsoft.Build.Utilities.Task, ICancelableTask, IDisposable +public sealed partial class CreateImageIndex : Microsoft.Build.Utilities.Task, ICancelableTask, IDisposable { - #region Parameters - /// - /// The base registry to pull from. - /// Ex: mcr.microsoft.com - /// - [Required] - public string BaseRegistry { get; set; } - - /// - /// The base image to pull. - /// Ex: dotnet/runtime - /// - [Required] - public string BaseImageName { get; set; } - - /// - /// The base image tag. - /// Ex: 6.0 - /// - [Required] - public string BaseImageTag { get; set; } - - /// - /// Manifests to include in the image index. - /// - [Required] - public ITaskItem[] GeneratedContainers { get; set; } - - /// - /// The registry to push the image index to. - /// - public string OutputRegistry { get; set; } - - /// - /// The file path to which to write a tar.gz archive of the container image. - /// - public string ArchiveOutputPath { get; set; } - - /// - /// The kind of local registry to use, if any. - /// - public string LocalRegistry { get; set; } - - /// - /// The name of the output image index (manifest list) that will be pushed to the registry. - /// - [Required] - public string Repository { get; set; } - - /// - /// The tag to associate with the new image index (manifest list). - /// - [Required] - public string[] ImageTags { get; set; } - - [Output] - public string GeneratedArchiveOutputPath { get; set; } - - /// - /// The generated image index (manifest list) in JSON format. - /// - [Output] - public string GeneratedImageIndex { get; set; } - - public CreateImageIndex() - { - BaseRegistry = string.Empty; - BaseImageName = string.Empty; - BaseImageTag = string.Empty; - GeneratedContainers = Array.Empty(); - OutputRegistry = string.Empty; - ArchiveOutputPath = string.Empty; - LocalRegistry = string.Empty; - Repository = string.Empty; - ImageTags = Array.Empty(); - GeneratedArchiveOutputPath = string.Empty; - GeneratedImageIndex = string.Empty; - } - #endregion - private readonly CancellationTokenSource _cancellationTokenSource = new(); public void Cancel() => _cancellationTokenSource.Cancel(); @@ -178,18 +98,16 @@ private BuiltImage[] ParseImages(DestinationImageReferenceKind destinationKind) string manifest = unparsedImage.GetMetadata("Manifest"); string manifestMediaType = unparsedImage.GetMetadata("ManifestMediaType"); - //TODO: add manifestmedia type to the error message - if (string.IsNullOrEmpty(config) || string.IsNullOrEmpty(manifestDigest) || string.IsNullOrEmpty(manifest)) + if (string.IsNullOrEmpty(config) || string.IsNullOrEmpty(manifestDigest) || string.IsNullOrEmpty(manifest) || string.IsNullOrEmpty(manifestMediaType)) { - Log.LogError(Strings.InvalidImageMetadata, unparsedImage.ItemSpec); + Log.LogError(Strings.InvalidImageMetadata); break; } var manifestV2 = JsonSerializer.Deserialize(manifest); if (manifestV2 == null) { - //TODO: log new error about manifest not deserealized - Log.LogError(Strings.InvalidImageMetadata, unparsedImage.ItemSpec); + Log.LogError(Strings.InvalidImageManifest); break; } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index bcc429acf343..997ca7b74973 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -122,7 +122,10 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) if (BuildEngine != null) { - Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_StartBuildingImage, Repository, String.Join(",", ImageTags), sourceImageReference); + (string message, object[] parameters) = SkipPublishing ? + ( Strings.ContainerBuilder_StartBuildingImageForRid, new object[] { Repository, ContainerRuntimeIdentifier, sourceImageReference }) : + ( Strings.ContainerBuilder_StartBuildingImage, new object[] { Repository, String.Join(",", ImageTags), sourceImageReference }); + Log.LogMessage(MessageImportance.High, message, parameters); } Layer newLayer = Layer.FromDirectory(PublishDirectory, WorkingDirectory, imageBuilder.IsWindows, imageBuilder.ManifestMediaType); diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets index d092e5951cd4..3854f88ef29f 100644 --- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets +++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets @@ -363,9 +363,7 @@ ImageTags="@(ContainerImageTags)" BaseRegistry="$(ContainerBaseRegistry)" BaseImageName="$(ContainerBaseName)" - BaseImageTag="$(ContainerBaseTag)"> - - + BaseImageTag="$(ContainerBaseTag)" /> diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs index 2615b2e178ee..18f0580c6935 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs @@ -44,18 +44,18 @@ public async Task CreateImageIndex_Baseline() cii.GeneratedContainers = [image1, image2]; Assert.True(cii.Execute(), FormatBuildMessages(errors)); - // Assert that the image index is created correctly - cii.GeneratedImageIndex.Should().NotBeNullOrEmpty(); - var imageIndex = cii.GeneratedImageIndex.FromJson(); - imageIndex.manifests.Should().HaveCount(2); - - imageIndex.manifests[0].digest.Should().Be(image1.GetMetadata("ManifestDigest")); - imageIndex.manifests[0].platform.os.Should().Be("linux"); - imageIndex.manifests[0].platform.architecture.Should().Be("amd64"); - - imageIndex.manifests[1].digest.Should().Be(image2.GetMetadata("ManifestDigest")); - imageIndex.manifests[1].platform.os.Should().Be("linux"); - imageIndex.manifests[1].platform.architecture.Should().Be("arm64"); + // // Assert that the image index is created correctly + // cii.GeneratedImageIndex.Should().NotBeNullOrEmpty(); + // var imageIndex = cii.GeneratedImageIndex.FromJson(); + // imageIndex.manifests.Should().HaveCount(2); + + // imageIndex.manifests[0].digest.Should().Be(image1.GetMetadata("ManifestDigest")); + // imageIndex.manifests[0].platform.os.Should().Be("linux"); + // imageIndex.manifests[0].platform.architecture.Should().Be("amd64"); + + // imageIndex.manifests[1].digest.Should().Be(image2.GetMetadata("ManifestDigest")); + // imageIndex.manifests[1].platform.os.Should().Be("linux"); + // imageIndex.manifests[1].platform.architecture.Should().Be("arm64"); // Assert that the image index is pushed to the registry var loggerFactory = new TestLoggerFactory(_testOutput); From 6b147e192ca5e17d5b35f0d58d29d1d84dab0113 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sat, 1 Feb 2025 21:44:28 +0100 Subject: [PATCH 14/74] add support for multi tagoci tarball --- .../LocalDaemons/DockerCli.cs | 77 +++++++++---------- .../Resources/Strings.Designer.cs | 11 +-- .../Resources/Strings.resx | 3 - .../Resources/xlf/Strings.cs.xlf | 5 -- .../Resources/xlf/Strings.de.xlf | 5 -- .../Resources/xlf/Strings.es.xlf | 5 -- .../Resources/xlf/Strings.fr.xlf | 5 -- .../Resources/xlf/Strings.it.xlf | 5 -- .../Resources/xlf/Strings.ja.xlf | 5 -- .../Resources/xlf/Strings.ko.xlf | 5 -- .../Resources/xlf/Strings.pl.xlf | 5 -- .../Resources/xlf/Strings.pt-BR.xlf | 5 -- .../Resources/xlf/Strings.ru.xlf | 5 -- .../Resources/xlf/Strings.tr.xlf | 5 -- .../Resources/xlf/Strings.zh-Hans.xlf | 5 -- .../Resources/xlf/Strings.zh-Hant.xlf | 5 -- 16 files changed, 37 insertions(+), 119 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 112887aa5a6e..2e9d41ff9e74 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -122,6 +122,7 @@ await writeStreamFunc(image, sourceReference, destinationReference, loadProcess. } public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken) + // 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. => await LoadAsync(image, sourceReference, destinationReference, WriteDockerImageToStreamAsync, cancellationToken); public async Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken) @@ -406,11 +407,6 @@ public static async Task WriteMultiArchOciImageToStreamAsync( { cancellationToken.ThrowIfCancellationRequested(); - if (destinationReference.Tags.Length > 1) - { - throw new ArgumentException(Resource.FormatString(nameof(Strings.OciImageMultipleTagsNotSupported))); - } - using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true); foreach (var image in images) @@ -435,11 +431,6 @@ private static async Task WriteOciImageToStreamAsync( { cancellationToken.ThrowIfCancellationRequested(); - if (destinationReference.Tags.Length > 1) - { - throw new ArgumentException(Resource.FormatString(nameof(Strings.OciImageMultipleTagsNotSupported))); - } - using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true); await WriteOciImageToBlobs(writer, image, sourceReference, cancellationToken) @@ -511,8 +502,6 @@ private static async Task WriteIndexJsonForMultiArchOciImage( // 1. create manifest list for the blobs cancellationToken.ThrowIfCancellationRequested(); - string tag = destinationReference.Tags[0]; - var manifests = new PlatformSpecificOciManifest[images.Length]; for (int i = 0; i < images.Length; i++) { @@ -554,24 +543,28 @@ private static async Task WriteIndexJsonForMultiArchOciImage( // 2. create index json that points to manifest list in the blobs cancellationToken.ThrowIfCancellationRequested(); + var manifestsIndexJson = new PlatformSpecificOciManifest[destinationReference.Tags.Length]; + for (int i = 0; i < destinationReference.Tags.Length; i++) + { + var tag = destinationReference.Tags[i]; + manifestsIndexJson[i] = new PlatformSpecificOciManifest + { + mediaType = SchemaTypes.OciImageIndexV1, + size = manifestListJson.Length, + digest = manifestListDigest, + annotations = new Dictionary + { + { "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" }, + { "org.opencontainers.image.ref.name", tag } + } + }; + } + var index = new ImageIndexV1 { schemaVersion = 2, mediaType = SchemaTypes.OciImageIndexV1, - manifests = - [ - new PlatformSpecificOciManifest - { - mediaType = SchemaTypes.OciImageIndexV1, - size = manifestListJson.Length, - digest = manifestListDigest, - annotations = new Dictionary - { - { "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" }, - { "org.opencontainers.image.ref.name", tag } - }, - } - ] + manifests = manifestsIndexJson }; using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index, options)!.ToJsonString()))) @@ -592,26 +585,28 @@ private static async Task WriteIndexJsonForOciImage( { cancellationToken.ThrowIfCancellationRequested(); - string tag = destinationReference.Tags[0]; + var manifests = new PlatformSpecificOciManifest[destinationReference.Tags.Length]; + for (int i = 0; i < destinationReference.Tags.Length; i++) + { + var tag = destinationReference.Tags[i]; + manifests[i] = new PlatformSpecificOciManifest + { + mediaType = SchemaTypes.OciManifestV1, + size = image.Manifest.Length, + digest = image.ManifestDigest, + annotations = new Dictionary + { + { "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" }, + { "org.opencontainers.image.ref.name", tag } + } + }; + } var index = new ImageIndexV1 { schemaVersion = 2, mediaType = SchemaTypes.OciImageIndexV1, - manifests = - [ - new PlatformSpecificOciManifest - { - mediaType = SchemaTypes.OciManifestV1, - size = image.Manifest.Length, - digest = image.ManifestDigest, - annotations = new Dictionary - { - { "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" }, - { "org.opencontainers.image.ref.name", tag } - } - } - ] + manifests = manifests }; var options = new JsonSerializerOptions diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs index 5750e050ba0e..7ad9616ae2c1 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs @@ -608,16 +608,7 @@ internal static string NormalizedContainerName { return ResourceManager.GetString("NormalizedContainerName", resourceCulture); } } - - /// - /// Looks up a localized string similar to Unable to create tarball for oci image with multiple tags.. - /// - internal static string OciImageMultipleTagsNotSupported { - get { - return ResourceManager.GetString("OciImageMultipleTagsNotSupported", resourceCulture); - } - } - + /// /// Looks up a localized string similar to CONTAINER2011: {0} '{1}' does not exist. /// diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx index a27e84824210..15f377704a50 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx @@ -244,9 +244,6 @@ CONTAINER2004: Unable to download layer with descriptor '{0}' from registry '{1}' because it does not exist. {StrBegin="CONTAINER2004: "} - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for mediaType '{0}'. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf index 3d6e472adb12..bc89aedef99e 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf @@ -304,11 +304,6 @@ '{0}' nebyl platný název image kontejneru, byl normalizován na '{1}' - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} '{1}' neexistuje. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf index a9298e5a40de..23f4eeafe797 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf @@ -304,11 +304,6 @@ „{0}“ war kein gültiger Containerimagename, er wurde zu „{1}“ normalisiert. - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} „{1}“ ist nicht vorhanden. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf index 0e6827c5b8dd..8533ff49f64d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf @@ -304,11 +304,6 @@ "{0}" no era un nombre de imagen de contenedor válido, se normalizó a "{1}" - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} "{1}" no existe diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf index d9fd1c30d615..127a97b70635 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf @@ -304,11 +304,6 @@ '{0}' n’était pas un nom d’image conteneur valide, il a été normalisé pour '{1}' - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} '{1}' n’existe pas diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf index 8fb75366363b..2c6f97c87e93 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf @@ -304,11 +304,6 @@ '{0}' non è un nome di immagine contenitore valido, è stato normalizzato in '{1}' - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} '{1}' non esiste diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf index 00aeda5edf91..feb83548f3c1 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf @@ -304,11 +304,6 @@ '{0}' は有効なコンテナー イメージ名ではありませんでした。'{1}' に正規化されました - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} '{1}' が存在しません diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf index 0adadf299470..c696a7cb649c 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf @@ -304,11 +304,6 @@ '{0}'은(는) 유효한 컨테이너 이미지 이름이 아닙니다. '{1}'(으)로 정규화되었습니다. - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} '{1}'이(가) 존재하지 않습니다. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf index 1fc1770a7181..165f11851003 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf @@ -304,11 +304,6 @@ Nazwa „{0}” nie była prawidłową nazwą obrazu kontenera, została znormalizowana do „{1}” - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} „{1}” nie istnieje diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf index 8e6ce774b100..82b3f1b1a646 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf @@ -304,11 +304,6 @@ '{0}' não era um nome de imagem de contêiner válido, foi normalizado para '{1}' - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} '{1}' não existe diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf index 0d0d9417f2ca..8a4e2d1ba8f0 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf @@ -304,11 +304,6 @@ "{0}" не является допустимым именем образа контейнера, оно нормализовано до "{1}" - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} "{1}" не существует diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf index 9f65b5082fba..4b2b0147c029 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf @@ -304,11 +304,6 @@ '{0}', geçerli bir kapsayıcı görüntüsü adı değildi, '{1}' olarak normalleştirildi - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} '{1}' yok diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf index 0f5ad6a2d7a4..a02595dc1a7b 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf @@ -304,11 +304,6 @@ “{0}”不是有效的容器映像名称,已规范化为“{1}” - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} {1} 不存在 diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf index 1287300e7b18..e1e859d58559 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf @@ -304,11 +304,6 @@ '{0}' 不是有效的容器映像名稱,已標準化為 '{1}' - - Unable to create tarball for oci image with multiple tags. - Unable to create tarball for oci image with multiple tags. - - CONTAINER2011: {0} '{1}' does not exist CONTAINER2011: {0} '{1}' 不存在 From 999368fd3332cedf47bfde4e018eb40ce0b3b095 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sun, 2 Feb 2025 16:17:42 +0100 Subject: [PATCH 15/74] fix test EndToEndMultiArch_LocalRegistry --- .../ContainerCli.cs | 3 ++ .../EndToEndTests.cs | 37 ++++++++++++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs index 82ccc3d4a2c9..a7de99fc1faf 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs @@ -37,6 +37,9 @@ public static RunExeCommand LoadCommand(ITestOutputHelper log, params string[] a public static RunExeCommand PortCommand(ITestOutputHelper log, string containerName, int port) => CreateCommand(log, "port", containerName, port.ToString()); + public static RunExeCommand ImagesCommand(ITestOutputHelper log, params string[] args) + => CreateCommand(log, "images", args); + private static RunExeCommand CreateCommand(ITestOutputHelper log, string command, params string[] args) { string commandPath = IsPodman ? "podman" : "docker"; diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index 40083f8fc58e..306b35858372 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -726,9 +726,8 @@ public void EndToEnd_SingleArch_NoRid() public void EndToEndMultiArch_LocalRegistry() { string imageName = NewImageName(); - string imageTag = "1.0"; - string imageX64 = $"{imageName}:{imageTag}-linux-x64"; - string imageArm64 = $"{imageName}:{imageTag}-linux-arm64"; + string tag = "1.0"; + string image = $"{imageName}:{tag}"; // Create a new console project DirectoryInfo newProjectDir = CreateNewProject("console"); @@ -741,7 +740,7 @@ public void EndToEndMultiArch_LocalRegistry() "/p:RuntimeIdentifiers=\"linux-x64;linux-arm64\"", $"/p:ContainerBaseImage={DockerRegistryManager.FullyQualifiedBaseImageAspNet}", $"/p:ContainerRepository={imageName}", - $"/p:ContainerImageTag={imageTag}", + $"/p:ContainerImageTag={tag}", "/p:EnableSdkContainerSupport=true") .WithWorkingDirectory(newProjectDir.FullName) .Execute(); @@ -752,26 +751,33 @@ public void EndToEndMultiArch_LocalRegistry() commandResult.Should().Pass() .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-x64")) .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-arm64")) - .And.HaveStdOutContaining($"Pushed image '{imageX64}' to local registry") - .And.HaveStdOutContaining($"Pushed image '{imageArm64}' to local registry") - .And.NotHaveStdOutContaining("Pushed image index"); + .And.HaveStdOutContaining($"Building image '{imageName}' for runtime identifier 'linux-x64'") + .And.HaveStdOutContaining($"Building image '{imageName}' for runtime identifier 'linux-arm64'") + .And.HaveStdOutContaining($"Pushed image '{image}' to local registry"); + + //Multi-arch oci images that are loaded to docker can only be run by their image id + string imageId = GetImageId(image); // Check that the containers can be run CommandResult processResultX64 = ContainerCli.RunCommand( _testOutput, "--rm", + "--platform", + "linux/amd64", "--name", $"test-container-{imageName}-x64", - imageX64) + imageId) .Execute(); processResultX64.Should().Pass().And.HaveStdOut("Hello, World!"); CommandResult processResultArm64 = ContainerCli.RunCommand( _testOutput, "--rm", + "--platform", + "linux/arm64", "--name", $"test-container-{imageName}-arm64", - imageArm64) + imageId) .Execute(); processResultArm64.Should().Pass().And.HaveStdOut("Hello, World!"); @@ -779,6 +785,19 @@ public void EndToEndMultiArch_LocalRegistry() newProjectDir.Delete(true); } + private string GetImageId(string image) + { + CommandResult commandResult = ContainerCli.ImagesCommand(_testOutput, "--format", "\"{{.ID}}\"", image) + .Execute(); + commandResult.Should().Pass(); + + var output = commandResult.StdOut.Split("\n").Select(s => s.Trim('"')).ToList(); + + output.Should().NotBeNullOrEmpty().And.OnlyContain(s => s == output[0]); + + return output[0]; + } + [DockerAvailableFact] public void MultiArchStillAllowsSingleRID() { From 0c9bdd1eb0167f127317c1953230f4f8572e4b8e Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 4 Dec 2024 14:32:25 +0100 Subject: [PATCH 16/74] check docker availability before arch support in tests --- .../ContainerCli.cs | 5 +++++ .../DockerSupportsArchFact.cs | 16 ++++++++-------- .../DockerSupportsArchInlineData.cs | 2 ++ .../EndToEndTests.cs | 10 +++++----- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs index a7de99fc1faf..1315d341f94f 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs @@ -7,6 +7,8 @@ static class ContainerCli { public static bool IsPodman => _isPodman.Value; + public static bool IsAvailable => _isAvailable.Value; + public static RunExeCommand PullCommand(ITestOutputHelper log, params string[] args) => CreateCommand(log, "pull", args); @@ -63,4 +65,7 @@ private static RunExeCommand CreateCommand(ITestOutputHelper log, string command private static readonly Lazy _isPodman = new(() => new DockerCli(loggerFactory: new TestLoggerFactory()).GetCommand() == DockerCli.PodmanCommand); + + private static readonly Lazy _isAvailable = + new(() => new DockerCli(loggerFactory: new TestLoggerFactory()).IsAvailable()); } diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs index 984bff2c0b37..36ad9fbc0c9a 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs @@ -3,17 +3,17 @@ namespace Microsoft.NET.Build.Containers.IntegrationTests; -public class DockerSupportsArchFactAttribute : FactAttribute +public class DockerIsAvailableAndSupportsArchFactAttribute : FactAttribute { - private readonly string _arch; - - public DockerSupportsArchFactAttribute(string arch) + public DockerIsAvailableAndSupportsArchFactAttribute(string arch) { - _arch = arch; - - if (!DockerSupportsArchHelper.DaemonSupportsArch(_arch)) + if (!DockerSupportsArchHelper.DaemonIsAvailable) + { + base.Skip = "Skipping test because Docker is not available on this host."; + } + else if (!DockerSupportsArchHelper.DaemonSupportsArch(arch)) { - base.Skip = $"Skipping test because Docker daemon does not support {_arch}."; + base.Skip = $"Skipping test because Docker daemon does not support {arch}."; } } } diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs index 022a8e90c16a..caafbaea4b45 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs @@ -34,6 +34,8 @@ public override IEnumerable GetData(MethodInfo testMethod) internal static class DockerSupportsArchHelper { + internal static bool DaemonIsAvailable => ContainerCli.IsAvailable; + internal static bool DaemonSupportsArch(string arch) { // an optimization - this doesn't change over time so we can compute it once diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index 306b35858372..a9d694adaddc 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -722,7 +722,7 @@ public void EndToEnd_SingleArch_NoRid() processResultX64.Should().Pass().And.HaveStdOut("Hello, World!"); } - [DockerSupportsArchFact("linux/arm64")] + [DockerIsAvailableAndSupportsArchFact("linux/arm64")] public void EndToEndMultiArch_LocalRegistry() { string imageName = NewImageName(); @@ -923,7 +923,7 @@ private DirectoryInfo CreateNewProject(string template, [CallerMemberName] strin private string GetPublishArtifactsPath(string projectDir, string rid, string configuration = "Debug") => Path.Combine(projectDir, "bin", configuration, ToolsetInfo.CurrentTargetFramework, rid, "publish"); - [DockerSupportsArchFact("linux/arm64")] + [DockerIsAvailableAndSupportsArchFact("linux/arm64")] public void EndToEndMultiArch_ArchivePublishing() { string imageName = NewImageName(); @@ -996,7 +996,7 @@ public void EndToEndMultiArch_ArchivePublishing() newProjectDir.Delete(true); } - [DockerSupportsArchFact("linux/arm64")] + [DockerIsAvailableAndSupportsArchFact("linux/arm64")] public void EndToEndMultiArch_RemoteRegistry() { string imageName = NewImageName(); @@ -1125,7 +1125,7 @@ public void EndToEndMultiArch_ContainerRuntimeIdentifiersOverridesRuntimeIdentif newProjectDir.Delete(true); } - [DockerSupportsArchFact("linux/arm64")] + [DockerIsAvailableAndSupportsArchFact("linux/arm64")] public void EndToEndMultiArch_EnvVariables() { string imageName = NewImageName(); @@ -1192,7 +1192,7 @@ public void EndToEndMultiArch_EnvVariables() newProjectDir.Delete(true); } - [DockerSupportsArchFact("linux/arm64")] + [DockerIsAvailableAndSupportsArchFact("linux/arm64")] public void EndToEndMultiArch_Ports() { string imageName = NewImageName(); From 7b09eb9675ce92d6da17b5c8b7a373522816b55a Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sun, 2 Feb 2025 18:42:01 +0100 Subject: [PATCH 17/74] refactoring --- .../Microsoft.NET.Build.Containers/ImagePublisher.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs index fa890b82506a..163c5e5efb26 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs @@ -30,8 +30,7 @@ await PushToLocalRegistryAsync( BuildEngine, telemetry, cancellationToken, - destinationImageReference.LocalRegistry!.LoadAsync, - Strings.ContainerBuilder_ImageUploadedToLocalDaemon).ConfigureAwait(false); + destinationImageReference.LocalRegistry!.LoadAsync).ConfigureAwait(false); break; case DestinationImageReferenceKind.RemoteRegistry: await PushToRemoteRegistryAsync( @@ -74,7 +73,6 @@ await PushToLocalRegistryAsync( telemetry, cancellationToken, destinationImageReference.LocalRegistry!.LoadAsync, - Strings.ContainerBuilder_ImageUploadedToLocalDaemon, logWarningForMultiArch : true).ConfigureAwait(false); break; case DestinationImageReferenceKind.RemoteRegistry: @@ -113,7 +111,6 @@ private static async Task PushToLocalRegistryAsync( Telemetry telemetry, CancellationToken cancellationToken, Func loadFunc, - string successMessage, bool logWarningForMultiArch = false) { ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; @@ -128,7 +125,7 @@ private static async Task PushToLocalRegistryAsync( await loadFunc(image, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); if (BuildEngine != null) { - Log.LogMessage(MessageImportance.High, successMessage, destinationImageReference, localRegistry); + Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); } } catch (ContainerHttpException e) From 6e1035ba2621c7ad7e3c415dbdffa52015956c74 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sun, 2 Feb 2025 18:42:28 +0100 Subject: [PATCH 18/74] fixed multi-arch e2e tests --- .../EndToEndTests.cs | 101 ++++++++++-------- 1 file changed, 56 insertions(+), 45 deletions(-) diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index a9d694adaddc..2881ae159aa6 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -746,8 +746,7 @@ public void EndToEndMultiArch_LocalRegistry() .Execute(); // Check that the app was published for each RID, - // images were created locally for each RID - // and image index was NOT created + // one image was created locally commandResult.Should().Pass() .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-x64")) .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-arm64")) @@ -755,7 +754,7 @@ public void EndToEndMultiArch_LocalRegistry() .And.HaveStdOutContaining($"Building image '{imageName}' for runtime identifier 'linux-arm64'") .And.HaveStdOutContaining($"Pushed image '{image}' to local registry"); - //Multi-arch oci images that are loaded to docker can only be run by their image id + //Multi-arch oci tarballs that are loaded to docker can only be run by their image id string imageId = GetImageId(image); // Check that the containers can be run @@ -927,12 +926,10 @@ private string GetPublishArtifactsPath(string projectDir, string rid, string con public void EndToEndMultiArch_ArchivePublishing() { string imageName = NewImageName(); - string imageTag = "1.0"; - string imageX64 = $"{imageName}:{imageTag}-linux-x64"; - string imageArm64 = $"{imageName}:{imageTag}-linux-arm64"; + string tag = "1.0"; + string image = $"{imageName}:{tag}"; string archiveOutput = Path.Combine(TestSettings.TestArtifactsDirectory, "tarballs-output"); - string imageX64Tarball = Path.Combine(archiveOutput, $"{imageName}-linux-x64.tar.gz"); - string imageArm64Tarball = Path.Combine(archiveOutput, $"{imageName}-linux-arm64.tar.gz"); + string imageTarball = Path.Combine(archiveOutput, $"{imageName}.tar.gz"); // Create a new console project DirectoryInfo newProjectDir = CreateNewProject("console"); @@ -946,49 +943,51 @@ public void EndToEndMultiArch_ArchivePublishing() $"/p:ContainerArchiveOutputPath={archiveOutput}", $"/p:ContainerBaseImage={DockerRegistryManager.FullyQualifiedBaseImageAspNet}", $"/p:ContainerRepository={imageName}", - $"/p:ContainerImageTag={imageTag}", + $"/p:ContainerImageTag={tag}", "/p:EnableSdkContainerSupport=true") .WithWorkingDirectory(newProjectDir.FullName) .Execute(); // Check that the app was published for each RID, - // images were created locally for each RID - // and image index was NOT created + // one image was created in local archive commandResult.Should().Pass() .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-x64")) .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-arm64")) - .And.HaveStdOutContaining($"Pushed image '{imageX64}' to local archive at '{imageX64Tarball}'") - .And.HaveStdOutContaining($"Pushed image '{imageArm64}' to local archive at '{imageArm64Tarball}'") - .And.NotHaveStdOutContaining("Pushed image index"); + .And.HaveStdOutContaining($"Building image '{imageName}' for runtime identifier 'linux-x64'") + .And.HaveStdOutContaining($"Building image '{imageName}' for runtime identifier 'linux-arm64'") + .And.HaveStdOutContaining($"Pushed image '{image}' to local archive at '{imageTarball}'"); // Check that tarballs were created - File.Exists(imageX64Tarball).Should().BeTrue(); - File.Exists(imageArm64Tarball).Should().BeTrue(); + File.Exists(imageTarball).Should().BeTrue(); - // Load the images from the tarballs - ContainerCli.LoadCommand(_testOutput, "--input", imageX64Tarball) - .Execute() - .Should().Pass(); - ContainerCli.LoadCommand(_testOutput, "--input", imageArm64Tarball) + // Load the multi-arch image from the tarball + ContainerCli.LoadCommand(_testOutput, "--input", imageTarball) .Execute() .Should().Pass(); + //Multi-arch oci tarballs that are loaded to docker can only be run by their image id + string imageId = GetImageId(image); + // Check that the containers can be run CommandResult processResultX64 = ContainerCli.RunCommand( _testOutput, "--rm", + "--platform", + "linux/amd64", "--name", $"test-container-{imageName}-x64", - imageX64) + imageId) .Execute(); processResultX64.Should().Pass().And.HaveStdOut("Hello, World!"); CommandResult processResultArm64 = ContainerCli.RunCommand( _testOutput, "--rm", + "--platform", + "linux/arm64", "--name", $"test-container-{imageName}-arm64", - imageArm64) + imageId) .Execute(); processResultArm64.Should().Pass().And.HaveStdOut("Hello, World!"); @@ -1118,8 +1117,8 @@ public void EndToEndMultiArch_ContainerRuntimeIdentifiersOverridesRuntimeIdentif commandResult.Should().Pass() .And.NotHaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-x64")) .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-arm64")) - .And.NotHaveStdOutContaining($"Pushed image '{imageName}:{imageTag}-linux-x64' to local registry") - .And.HaveStdOutContaining($"Pushed image '{imageName}:{imageTag}-linux-arm64' to local registry"); + .And.NotHaveStdOutContaining($"Building image '{imageName}' for runtime identifier 'linux-x64'") + .And.HaveStdOutContaining($"Building image '{imageName}' for runtime identifier 'linux-arm64'"); // Cleanup newProjectDir.Delete(true); @@ -1129,9 +1128,8 @@ public void EndToEndMultiArch_ContainerRuntimeIdentifiersOverridesRuntimeIdentif public void EndToEndMultiArch_EnvVariables() { string imageName = NewImageName(); - string imageTag = "1.0"; - string imageX64 = $"{imageName}:{imageTag}-linux-x64"; - string imageArm64 = $"{imageName}:{imageTag}-linux-arm64"; + string tag = "1.0"; + string image = $"{imageName}:{tag}"; // Create new console app, set ContainerEnvironmentVariables, and set to output env variable DirectoryInfo newProjectDir = CreateNewProject("console"); @@ -1160,31 +1158,37 @@ public void EndToEndMultiArch_EnvVariables() "/p:RuntimeIdentifiers=\"linux-x64;linux-arm64\"", $"/p:ContainerBaseImage={DockerRegistryManager.FullyQualifiedBaseImageAspNet}", $"/p:ContainerRepository={imageName}", - $"/p:ContainerImageTag={imageTag}", + $"/p:ContainerImageTag={tag}", "/p:EnableSdkContainerSupport=true") .WithWorkingDirectory(newProjectDir.FullName) .Execute() .Should().Pass(); - // Check that the env var is printed + string imageId = GetImageId(image); + + // Check that the env var is printed for linux/amd64 platform string containerNameX64 = $"test-container-{imageName}-x64"; CommandResult processResultX64 = ContainerCli.RunCommand( _testOutput, "--rm", + "--platform", + "linux/amd64", "--name", containerNameX64, - imageX64) + imageId) .Execute(); processResultX64.Should().Pass().And.HaveStdOut("FooBar"); - // Check that the env var is printed + // Check that the env var is printed for linux/arm64 platform string containerNameArm64 = $"test-container-{imageName}-arm64"; CommandResult processResultArm64 = ContainerCli.RunCommand( _testOutput, "--rm", + "--platform", + "linux/arm64", "--name", containerNameArm64, - imageArm64) + imageId) .Execute(); processResultArm64.Should().Pass().And.HaveStdOut("FooBar"); @@ -1196,9 +1200,8 @@ public void EndToEndMultiArch_EnvVariables() public void EndToEndMultiArch_Ports() { string imageName = NewImageName(); - string imageTag = "1.0"; - string imageX64 = $"{imageName}:{imageTag}-linux-x64"; - string imageArm64 = $"{imageName}:{imageTag}-linux-arm64"; + string tag = "1.0"; + string image = $"{imageName}:{tag}"; // Create new web app, set ContainerPort DirectoryInfo newProjectDir = CreateNewProject("webapp"); @@ -1222,38 +1225,44 @@ public void EndToEndMultiArch_Ports() "/p:RuntimeIdentifiers=\"linux-x64;linux-arm64\"", $"/p:ContainerBaseImage={DockerRegistryManager.FullyQualifiedBaseImageAspNet}", $"/p:ContainerRepository={imageName}", - $"/p:ContainerImageTag={imageTag}", + $"/p:ContainerImageTag={tag}", "/p:EnableSdkContainerSupport=true") .WithWorkingDirectory(newProjectDir.FullName) .Execute() .Should().Pass(); - // Check that the ports are correct + string imageId = GetImageId(image); + + // Check that the ports are correct for linux/amd64 platform var containerNameX64 = $"test-container-{imageName}-x64"; CommandResult processResultX64 = ContainerCli.RunCommand( _testOutput, "--rm", + "--platform", + "linux/amd64", "--name", containerNameX64, "-P", "--detach", - imageX64) + imageId) .Execute(); processResultX64.Should().Pass(); // 8080 is the default port CheckPorts(containerNameX64, [8080, 8082, 8083], [8081]); - // Check that the ports are correct + // Check that the ports are correct for linux/arm64 platform var containerNameArm64 = $"test-container-{imageName}-arm64"; CommandResult processResultArm64 = ContainerCli.RunCommand( _testOutput, "--rm", + "--platform", + "linux/arm64", "--name", containerNameArm64, "-P", "--detach", - imageArm64) + imageId) .Execute(); processResultArm64.Should().Pass(); @@ -1291,8 +1300,8 @@ private void CheckPorts(string containerName, int[] correctPorts, int[] incorrec public void EndToEndMultiArch_Labels() { string imageName = NewImageName(); - string imageTag = "1.0"; - string imageX64 = $"{imageName}:{imageTag}-linux-x64"; + string tag = "1.0"; + string image = $"{imageName}:{tag}"; // Create new console app DirectoryInfo newProjectDir = CreateNewProject("webapp"); @@ -1305,17 +1314,19 @@ public void EndToEndMultiArch_Labels() "/p:RuntimeIdentifiers=\"linux-x64;linux-arm64\"", $"/p:ContainerBaseImage={DockerRegistryManager.FullyQualifiedBaseImageAspNet}", $"/p:ContainerRepository={imageName}", - $"/p:ContainerImageTag={imageTag}", + $"/p:ContainerImageTag={tag}", "/p:EnableSdkContainerSupport=true") .WithWorkingDirectory(newProjectDir.FullName) .Execute() .Should().Pass(); + string imageId = GetImageId(image); + // Check that labels are set CommandResult inspectResult = ContainerCli.InspectCommand( _testOutput, "--format={{json .Config.Labels}}", - imageX64) + imageId) .Execute(); inspectResult.Should().Pass(); var labels = JsonSerializer.Deserialize>(inspectResult.StdOut); From 9b3fd7e61177fd8e59531377b16e1c2ad25aca1e Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sun, 2 Feb 2025 19:19:42 +0100 Subject: [PATCH 19/74] cleanup --- .../CreateImageIndexTests.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs index 18f0580c6935..24703040cda6 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs @@ -44,19 +44,6 @@ public async Task CreateImageIndex_Baseline() cii.GeneratedContainers = [image1, image2]; Assert.True(cii.Execute(), FormatBuildMessages(errors)); - // // Assert that the image index is created correctly - // cii.GeneratedImageIndex.Should().NotBeNullOrEmpty(); - // var imageIndex = cii.GeneratedImageIndex.FromJson(); - // imageIndex.manifests.Should().HaveCount(2); - - // imageIndex.manifests[0].digest.Should().Be(image1.GetMetadata("ManifestDigest")); - // imageIndex.manifests[0].platform.os.Should().Be("linux"); - // imageIndex.manifests[0].platform.architecture.Should().Be("amd64"); - - // imageIndex.manifests[1].digest.Should().Be(image2.GetMetadata("ManifestDigest")); - // imageIndex.manifests[1].platform.os.Should().Be("linux"); - // imageIndex.manifests[1].platform.architecture.Should().Be("arm64"); - // Assert that the image index is pushed to the registry var loggerFactory = new TestLoggerFactory(_testOutput); var logger = loggerFactory.CreateLogger(nameof(CreateImageIndex_Baseline)); From fb049829dd90854b59f6e1a455f80b86d7943fea Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sun, 2 Feb 2025 19:38:08 +0100 Subject: [PATCH 20/74] refactoring: make BuiltImage a class instead of a struct --- src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs | 6 +++--- .../Microsoft.NET.Build.Containers/ImageIndexGenerator.cs | 4 ++-- .../LocalDaemons/DockerCli.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs index 0a76191db727..bd31ccc8cbf5 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs @@ -6,7 +6,7 @@ namespace Microsoft.NET.Build.Containers; /// /// Represents constructed image ready for further processing. /// -internal readonly struct BuiltImage +internal sealed class BuiltImage { /// /// Gets image configuration in JSON format. @@ -46,12 +46,12 @@ internal readonly struct BuiltImage /// /// Gets image OS. /// - internal string OS { get; init; } + internal string? OS { get; init; } /// /// Gets image architecture. /// - internal string Architecture { get; init; } + internal string? Architecture { get; init; } /// /// Gets layers descriptors. diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs index b0cedc4a60af..413fa5e75591 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs @@ -71,8 +71,8 @@ private static (string, string) GenerateImageIndex(BuiltImage[] images, string m digest = image.ManifestDigest, platform = new PlatformInformation { - architecture = image.Architecture, - os = image.OS + architecture = image.Architecture!, + os = image.OS! } }; manifests[i] = manifest; diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 2e9d41ff9e74..7f79aab2ddf2 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -510,7 +510,7 @@ private static async Task WriteIndexJsonForMultiArchOciImage( mediaType = SchemaTypes.OciManifestV1, size = images[i].Manifest.Length, digest = images[i].ManifestDigest, - platform = new PlatformInformation { architecture = images[i].Architecture, os = images[i].OS } + platform = new PlatformInformation { architecture = images[i].Architecture!, os = images[i].OS! } }; manifests[i] = manifest; } From 73b120d080b6f25074cadd6583a594ce7df6b111 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sun, 2 Feb 2025 20:36:25 +0100 Subject: [PATCH 21/74] rearrange functions order in DockerCli for easier review --- .../LocalDaemons/DockerCli.cs | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 7f79aab2ddf2..820c129672e2 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -398,30 +398,6 @@ private static async Task WriteManifestForDockerImage( } } - public static async Task WriteMultiArchOciImageToStreamAsync( - BuiltImage[] images, - SourceImageReference sourceReference, - DestinationImageReference destinationReference, - Stream imageStream, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true); - - foreach (var image in images) - { - await WriteOciImageToBlobs(writer, image, sourceReference, cancellationToken) - .ConfigureAwait(false); - } - - await WriteIndexJsonForMultiArchOciImage(writer, images, destinationReference, cancellationToken) - .ConfigureAwait(false); - - await WriteOciLayout(writer, cancellationToken) - .ConfigureAwait(false); - } - private static async Task WriteOciImageToStreamAsync( BuiltImage image, SourceImageReference sourceReference, @@ -443,22 +419,6 @@ await WriteOciLayout(writer, cancellationToken) .ConfigureAwait(false); } - private static async Task WriteOciImageToBlobs( - TarWriter writer, - BuiltImage image, - SourceImageReference sourceReference, - CancellationToken cancellationToken) - { - await WriteImageLayers(writer, image, sourceReference, d => $"{_blobsPath}/{d.Substring("sha256:".Length)}", cancellationToken) - .ConfigureAwait(false); - - await WriteImageConfig(writer, image, $"{_blobsPath}/{image.ImageSha}", cancellationToken) - .ConfigureAwait(false); - - await WriteManifestForOciImage(writer, image, cancellationToken) - .ConfigureAwait(false); - } - private static async Task WriteOciLayout(TarWriter writer, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -493,6 +453,46 @@ private static async Task WriteManifestForOciImage( } } + private static async Task WriteOciImageToBlobs( + TarWriter writer, + BuiltImage image, + SourceImageReference sourceReference, + CancellationToken cancellationToken) + { + await WriteImageLayers(writer, image, sourceReference, d => $"{_blobsPath}/{d.Substring("sha256:".Length)}", cancellationToken) + .ConfigureAwait(false); + + await WriteImageConfig(writer, image, $"{_blobsPath}/{image.ImageSha}", cancellationToken) + .ConfigureAwait(false); + + await WriteManifestForOciImage(writer, image, cancellationToken) + .ConfigureAwait(false); + } + + public static async Task WriteMultiArchOciImageToStreamAsync( + BuiltImage[] images, + SourceImageReference sourceReference, + DestinationImageReference destinationReference, + Stream imageStream, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true); + + foreach (var image in images) + { + await WriteOciImageToBlobs(writer, image, sourceReference, cancellationToken) + .ConfigureAwait(false); + } + + await WriteIndexJsonForMultiArchOciImage(writer, images, destinationReference, cancellationToken) + .ConfigureAwait(false); + + await WriteOciLayout(writer, cancellationToken) + .ConfigureAwait(false); + } + private static async Task WriteIndexJsonForMultiArchOciImage( TarWriter writer, BuiltImage[] images, From c8d4e091b88502ff287c5c93ce7c5384ce875e0e Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sun, 2 Feb 2025 20:39:43 +0100 Subject: [PATCH 22/74] rearrange functions again --- .../LocalDaemons/DockerCli.cs | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 820c129672e2..208a10c24725 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -453,6 +453,52 @@ private static async Task WriteManifestForOciImage( } } + private static async Task WriteIndexJsonForOciImage( + TarWriter writer, + BuiltImage image, + DestinationImageReference destinationReference, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var manifests = new PlatformSpecificOciManifest[destinationReference.Tags.Length]; + for (int i = 0; i < destinationReference.Tags.Length; i++) + { + var tag = destinationReference.Tags[i]; + manifests[i] = new PlatformSpecificOciManifest + { + mediaType = SchemaTypes.OciManifestV1, + size = image.Manifest.Length, + digest = image.ManifestDigest, + annotations = new Dictionary + { + { "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" }, + { "org.opencontainers.image.ref.name", tag } + } + }; + } + + var index = new ImageIndexV1 + { + schemaVersion = 2, + mediaType = SchemaTypes.OciImageIndexV1, + manifests = manifests + }; + + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index, options)!.ToJsonString()))) + { + PaxTarEntry indexEntry = new(TarEntryType.RegularFile, "index.json") + { + DataStream = indexStream + }; + await writer.WriteEntryAsync(indexEntry, cancellationToken).ConfigureAwait(false); + } + } + private static async Task WriteOciImageToBlobs( TarWriter writer, BuiltImage image, @@ -577,52 +623,6 @@ private static async Task WriteIndexJsonForMultiArchOciImage( } } - private static async Task WriteIndexJsonForOciImage( - TarWriter writer, - BuiltImage image, - DestinationImageReference destinationReference, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var manifests = new PlatformSpecificOciManifest[destinationReference.Tags.Length]; - for (int i = 0; i < destinationReference.Tags.Length; i++) - { - var tag = destinationReference.Tags[i]; - manifests[i] = new PlatformSpecificOciManifest - { - mediaType = SchemaTypes.OciManifestV1, - size = image.Manifest.Length, - digest = image.ManifestDigest, - annotations = new Dictionary - { - { "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" }, - { "org.opencontainers.image.ref.name", tag } - } - }; - } - - var index = new ImageIndexV1 - { - schemaVersion = 2, - mediaType = SchemaTypes.OciImageIndexV1, - manifests = manifests - }; - - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index, options)!.ToJsonString()))) - { - PaxTarEntry indexEntry = new(TarEntryType.RegularFile, "index.json") - { - DataStream = indexStream - }; - await writer.WriteEntryAsync(indexEntry, cancellationToken).ConfigureAwait(false); - } - } - private async ValueTask GetCommandAsync(CancellationToken cancellationToken) { if (_command != null) From f4f1754153b0a0496e9c270999498ff1478800b3 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sun, 2 Feb 2025 21:23:50 +0100 Subject: [PATCH 23/74] add back log message about building image index --- .../Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index eb5743d81846..60d868554100 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -69,19 +69,20 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) LocalRegistry); var images = ParseImages(destinationImageReference.Kind); - if (Log.HasLoggedErrors) { return false; } - GeneratedArchiveOutputPath = ArchiveOutputPath; + logger.LogInformation(Strings.BuildingImageIndex, destinationImageReference, string.Join(", ", images.Select(i => i.ManifestDigest))); var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log); await ImagePublisher.PublishImageAsync(images, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) .ConfigureAwait(false); + GeneratedArchiveOutputPath = ArchiveOutputPath; + return !Log.HasLoggedErrors; } From fc80348f5979836e47fbba1689dd3665faba61f2 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 3 Feb 2025 00:01:41 +0100 Subject: [PATCH 24/74] extract error messages into Strings --- .../ImagePublisher.cs | 8 ++--- .../Resources/Strings.Designer.cs | 36 +++++++++++++++++++ .../Resources/Strings.resx | 20 +++++++++-- .../Resources/xlf/Strings.cs.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.de.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.es.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.fr.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.it.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.ja.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.ko.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.pl.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.pt-BR.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.ru.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.tr.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.zh-Hans.xlf | 28 ++++++++++++--- .../Resources/xlf/Strings.zh-Hant.xlf | 28 ++++++++++++--- .../Tasks/CreateImageIndex.cs | 29 +++++++++------ 17 files changed, 389 insertions(+), 68 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs index 163c5e5efb26..cd6a6a15ebd8 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs @@ -73,7 +73,7 @@ await PushToLocalRegistryAsync( telemetry, cancellationToken, destinationImageReference.LocalRegistry!.LoadAsync, - logWarningForMultiArch : true).ConfigureAwait(false); + logTipAboutContainerd : true).ConfigureAwait(false); break; case DestinationImageReferenceKind.RemoteRegistry: await PushToRemoteRegistryAsync( @@ -111,7 +111,7 @@ private static async Task PushToLocalRegistryAsync( Telemetry telemetry, CancellationToken cancellationToken, Func loadFunc, - bool logWarningForMultiArch = false) + bool logTipAboutContainerd = false) { ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) @@ -148,9 +148,9 @@ private static async Task PushToLocalRegistryAsync( { telemetry.LogLocalLoadError(); Log.LogErrorFromException(dle, showStackTrace: false); - if (logWarningForMultiArch && dle.Message.Contains("no such file or directory")) + if (logTipAboutContainerd && dle.Message.Contains("no such file or directory")) { - Log.LogMessage(MessageImportance.High, "Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings."); + Log.LogMessage(MessageImportance.High, Strings.TipToEnableContainerdForMultiArch); } } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs index 7ad9616ae2c1..07beb07321fd 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs @@ -420,6 +420,33 @@ internal static string InvalidImageManifest { } } + /// + /// Looks up a localized string similar to Cannot create image index because at least one of the provided images config is invalid.. + /// + internal static string InvalidImageConfig { + get { + return ResourceManager.GetString("InvalidImageConfig", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot create image index because at least one of the provided images' config is missing 'architecture'.. + /// + internal static string ImageConfigMissingArchitecture { + get { + return ResourceManager.GetString("ImageConfigMissingArchitecture", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot create image index because at least one of the provided images' config is missing 'os'.. + /// + internal static string ImageConfigMissingOs { + get { + return ResourceManager.GetString("ImageConfigMissingOs", resourceCulture); + } + } + /// /// Looks up a localized string similar to Image index creation for Podman is not supported.. /// @@ -429,6 +456,15 @@ internal static string ImageIndex_PodmanNotSupported { } } + /// + /// Looks up a localized string similar to Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings.. + /// + internal static string TipToEnableContainerdForMultiArch { + get { + return ResourceManager.GetString("TipToEnableContainerdForMultiArch", resourceCulture); + } + } + /// /// Looks up a localized string similar to CONTAINER2005: The inferred image name '{0}' contains entirely invalid characters. The valid characters for an image name are alphanumeric characters, -, /, or _, and the image name must start with an alphanumeric character.. /// diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx index 15f377704a50..753d0ba091f4 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx @@ -366,7 +366,7 @@ - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. @@ -382,13 +382,29 @@ - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + + + + Cannot create image index because at least one of the provided images' config is invalid. + + + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. Image index creation for Podman is not supported. + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + Error while reading daemon config: {0} {0} is the exception message that ends with period diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf index bc89aedef99e..930f2cd9500f 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf @@ -164,6 +164,16 @@ Nebyl zjištěn žádný objekt hostitele. + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: '{1}' není platná proměnná prostředí. Ignorování. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: Požadovaná vlastnost '{0}' nebyla nastavena nebo je prázdná. {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: Příliš mnoho opakovaných pokusů, zastavuje se. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf index 23f4eeafe797..f851c41fbf92 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf @@ -164,6 +164,16 @@ Es wurde kein Hostobjekt erkannt. + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: „{1}“ war keine gültige Umgebungsvariable. Sie wird ignoriert. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: Die erforderliche Eigenschaft „{0}“ wurde nicht festgelegt oder ist leer. {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: Zu viele Wiederholungsversuche, Vorgang wird beendet. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf index 8533ff49f64d..9f36072ccbe2 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf @@ -164,6 +164,16 @@ No se detectó ningún objeto host. + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: "{1}" no era una variable de entorno válida. Ignorando. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: La propiedad necesaria "{0}" no se estableció o estaba vacía. {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: Demasiados reintentos, deteniendo. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf index 127a97b70635..50def6bb6b9d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf @@ -164,6 +164,16 @@ Aucun objet hôte détecté. + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0} : '{1}' n’était pas une variable d’environnement valide. Ignorant. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: la propriété requise '{0}' n’a pas été définie ou vide. {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: trop de tentatives, arrêt. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf index 2c6f97c87e93..c32f91206de4 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf @@ -164,6 +164,16 @@ Nessun oggetto host rilevato. + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: '{1}' non è una variabile di ambiente valida. Il valore verrà ignorato. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: la proprietà obbligatoria '{0}' non è stata impostata o è vuota. {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: troppi tentativi, arresto. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf index feb83548f3c1..74b016f07d27 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf @@ -164,6 +164,16 @@ ホスト オブジェクトが検出されませんでした。 + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: '{1}' は有効な環境変数ではありませんでした。無視しています。 {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: 必要なプロパティ '{0}' が設定されていなかったか、空でした。 {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: 再試行回数が多すぎます。停止しています。 diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf index c696a7cb649c..0ab031e4eb14 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf @@ -164,6 +164,16 @@ 호스트 개체가 검색되지 않았습니다. + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: '{1}'은(는) 유효한 환경 변수가 아닙니다. 무시 중. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: 필수 속성 '{0}'이(가) 설정되지 않았거나 비어 있습니다. {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: 다시 시도가 너무 많아 중지 중입니다. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf index 165f11851003..6f7cadee6322 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf @@ -164,6 +164,16 @@ Nie wykryto obiektu hosta. + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: „{1}” nie jest prawidłową zmienną środowiskową. Ignorowanie. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: wymagana właściwość „{0}” nie została ustawiona lub jest pusta. {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: zbyt wiele ponownych prób, zatrzymywanie. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf index 82b3f1b1a646..bd00f8b3f7f3 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf @@ -164,6 +164,16 @@ Nenhum objeto de host detectado. + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: '{1}' não era uma variável de ambiente válida. Ignorando. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: A propriedade obrigatória '{0}' não foi definida ou está vazia. {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: Muitas tentativas, parando. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf index 8a4e2d1ba8f0..932f51f124fa 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf @@ -164,6 +164,16 @@ Объект узла не обнаружен. + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: "{1}" не является допустимой переменной среды. Пропуск. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: обязательное свойство "{0}" не установлено или пусто. {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: слишком много повторных попыток, остановка. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf index 4b2b0147c029..dab91d854f1d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf @@ -164,6 +164,16 @@ Ana bilgisayar nesnesi algılanmadı. + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: '{1}' geçerli bir Ortam Değişkeni değildi. Görmezden geliniyor. {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: Gerekli '{0}' özelliği ayarlanmadı veya boş değil. {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: Çok fazla yeniden deneme, durduruluyor. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf index a02595dc1a7b..ebf98c8f1540 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf @@ -164,6 +164,16 @@ 未检测到主机对象。 + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: "{1}" 不是有效的环境变量。忽略。 {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: 未设置必需属性 "{0}" 或为空。 {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: 重试次数过多,正在停止。 diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf index e1e859d58559..01de44e08b8b 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf @@ -164,6 +164,16 @@ 未偵測到主機物件。 + + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + Cannot create image index because at least one of the provided images' config is missing 'architecture'. + + + + Cannot create image index because at least one of the provided images' config is missing 'os'. + Cannot create image index because at least one of the provided images' config is missing 'os'. + + Pushed image index '{0}' to registry '{1}'. Pushed image index '{0}' to registry '{1}'. @@ -194,9 +204,14 @@ CONTAINER2015: {0}: '{1}' 不是有效的環境變數。正在忽略。 {StrBegin="CONTAINER2015: "} + + Cannot create image index because at least one of the provided images' config is invalid. + Cannot create image index because at least one of the provided images' config is invalid. + + - Cannot create image index because at least one of the provided images manifest is invalid. - Cannot create image index because at least one of the provided images manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. + Cannot create image index because at least one of the provided images' manifest is invalid. @@ -290,8 +305,8 @@ {StrBegin="CONTAINER2016: "} - 'mediaType' of manifests should be the same in manifest list (image index). - 'mediaType' of manifests should be the same in manifest list (image index). + 'mediaType' of manifests should be the same in image index. + 'mediaType' of manifests should be the same in image index. @@ -394,6 +409,11 @@ CONTAINER4001: 必要的屬性 '{0}' 未設定或是空的。 {StrBegin="CONTAINER4001: "} + + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + + CONTAINER1006: Too many retries, stopping. CONTAINER1006: 重試太多次,正在停止。 diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 60d868554100..d56ae92d9b62 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -135,17 +135,26 @@ private BuiltImage[] ParseImages(DestinationImageReferenceKind destinationKind) return images; } - private static (string, string) GetArchitectureAndOsFromConfig(string config) + private (string, string) GetArchitectureAndOsFromConfig(string config) { - var configJson = JsonNode.Parse(config) as JsonObject ?? - throw new ArgumentException("Image config should be a JSON object."); - - var architecture = configJson["architecture"]?.ToString() ?? - throw new ArgumentException("Image config should contain 'architecture'."); - - var os = configJson["os"]?.ToString() ?? - throw new ArgumentException("Image config should contain 'os'."); - + var configJson = JsonNode.Parse(config) as JsonObject; + if (configJson is null) + { + Log.LogError(Strings.InvalidImageConfig); + return (string.Empty, string.Empty); + } + var architecture = configJson["architecture"]?.ToString(); + if (architecture is null) + { + Log.LogError(Strings.ImageConfigMissingArchitecture); + return (string.Empty, string.Empty); + } + var os = configJson["os"]?.ToString(); + if (os is null) + { + Log.LogError(Strings.ImageConfigMissingOs); + return (string.Empty, string.Empty); + } return (architecture, os); } } From f2909c14d9dcb11dd3e1f6613b7fad3eb64b2513 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 3 Feb 2025 09:59:10 +0100 Subject: [PATCH 25/74] fix typo in log message --- .../Resources/Strings.Designer.cs | 2 +- .../Microsoft.NET.Build.Containers/Resources/Strings.resx | 2 +- .../Resources/xlf/Strings.cs.xlf | 4 ++-- .../Resources/xlf/Strings.de.xlf | 4 ++-- .../Resources/xlf/Strings.es.xlf | 4 ++-- .../Resources/xlf/Strings.fr.xlf | 4 ++-- .../Resources/xlf/Strings.it.xlf | 4 ++-- .../Resources/xlf/Strings.ja.xlf | 4 ++-- .../Resources/xlf/Strings.ko.xlf | 4 ++-- .../Resources/xlf/Strings.pl.xlf | 4 ++-- .../Resources/xlf/Strings.pt-BR.xlf | 4 ++-- .../Resources/xlf/Strings.ru.xlf | 4 ++-- .../Resources/xlf/Strings.tr.xlf | 4 ++-- .../Resources/xlf/Strings.zh-Hans.xlf | 4 ++-- .../Resources/xlf/Strings.zh-Hant.xlf | 4 ++-- .../Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs | 2 ++ 16 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs index 07beb07321fd..4f2dee24ba99 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs @@ -457,7 +457,7 @@ internal static string ImageIndex_PodmanNotSupported { } /// - /// Looks up a localized string similar to Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings.. + /// Looks up a localized string similar to Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings.. /// internal static string TipToEnableContainerdForMultiArch { get { diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx index 753d0ba091f4..42d9dddffea2 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx @@ -402,7 +402,7 @@ - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf index 930f2cd9500f..20499b00e426 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf index f851c41fbf92..313ffe15f9a1 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf index 9f36072ccbe2..a1c4e1a7107f 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf index 50def6bb6b9d..705c8e48ae85 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf index c32f91206de4..22089bdb50c6 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf index 74b016f07d27..3efb830d3e14 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf index 0ab031e4eb14..75380d38355f 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf index 6f7cadee6322..f34e4c1e4b31 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf index bd00f8b3f7f3..6b5da780f9fb 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf index 932f51f124fa..e9d2290783c0 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf index dab91d854f1d..fac7e81d6444 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf index ebf98c8f1540..411f5221a963 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf index 01de44e08b8b..580bb931de64 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf @@ -410,8 +410,8 @@ {StrBegin="CONTAINER4001: "} - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensude that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. + Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index d56ae92d9b62..217f2909fffe 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -52,6 +52,8 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) return false; } + + using MSBuildLoggerProvider loggerProvider = new(Log); ILoggerFactory msbuildLoggerFactory = new LoggerFactory(new[] { loggerProvider }); ILogger logger = msbuildLoggerFactory.CreateLogger(); From a61aad5637c4dbbedde843777ee827260ed1d4e7 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 3 Feb 2025 11:45:37 +0100 Subject: [PATCH 26/74] extract image index creation from DockerCli into ImageIndexGenerator --- .../ImageIndexGenerator.cs | 66 ++++++++++--- .../LocalDaemons/DockerCli.cs | 95 ++++--------------- .../ImageIndexGeneratorTests.cs | 4 +- 3 files changed, 70 insertions(+), 95 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs index 413fa5e75591..e4d167a007df 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs @@ -2,9 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text.Json; -using System.Text.Json.Nodes; +using System.Text.Json.Serialization; using Microsoft.NET.Build.Containers.Resources; -using Microsoft.NET.Build.Containers.Tasks; namespace Microsoft.NET.Build.Containers; @@ -43,11 +42,11 @@ internal static (string, string) GenerateImageIndex(BuiltImage[] images) if (manifestMediaType == SchemaTypes.DockerManifestV2) { - return GenerateImageIndex(images, SchemaTypes.DockerManifestV2, SchemaTypes.DockerManifestListV2); + return (GenerateImageIndex(images, SchemaTypes.DockerManifestV2, SchemaTypes.DockerManifestListV2), SchemaTypes.DockerManifestListV2); } else if (manifestMediaType == SchemaTypes.OciManifestV1) { - return GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1); + return (GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1), SchemaTypes.OciImageIndexV1); } else { @@ -55,36 +54,73 @@ internal static (string, string) GenerateImageIndex(BuiltImage[] images) } } - private static (string, string) GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType) + internal static string GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType) { // Here we are using ManifestListV2 struct, but we could use ImageIndexV1 struct as well. // We are filling the same fields, so we can use the same struct. var manifests = new PlatformSpecificManifest[images.Length]; + for (int i = 0; i < images.Length; i++) { - var image = images[i]; - - var manifest = new PlatformSpecificManifest + manifests[i] = new PlatformSpecificManifest { mediaType = manifestMediaType, - size = image.Manifest.Length, - digest = image.ManifestDigest, + size = images[i].Manifest.Length, + digest = images[i].ManifestDigest, platform = new PlatformInformation { - architecture = image.Architecture!, - os = image.OS! + architecture = images[i].Architecture!, + os = images[i].OS! } }; - manifests[i] = manifest; } - var manifestList = new ManifestListV2 + var imageIndex = new ManifestListV2 { schemaVersion = 2, mediaType = imageIndexMediaType, manifests = manifests }; - return (JsonSerializer.SerializeToNode(manifestList)?.ToJsonString() ?? "", manifestList.mediaType); + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + return JsonSerializer.SerializeToNode(imageIndex, options)?.ToJsonString() ?? ""; + } + + internal static string GenerateImageIndexWithAnnotations(string manifestMediaType, string manifestDigest, long manifestSize, string repository, string[] tags) + { + var manifests = new PlatformSpecificOciManifest[tags.Length]; + for (int i = 0; i < tags.Length; i++) + { + var tag = tags[i]; + manifests[i] = new PlatformSpecificOciManifest + { + mediaType = manifestMediaType, + size = manifestSize, + digest = manifestDigest, + annotations = new Dictionary + { + { "io.containerd.image.name", $"{repository}:{tag}" }, + { "org.opencontainers.image.ref.name", tag } + } + }; + } + + var index = new ImageIndexV1 + { + schemaVersion = 2, + mediaType = SchemaTypes.OciImageIndexV1, + manifests = manifests + }; + + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + return JsonSerializer.SerializeToNode(index, options)?.ToJsonString() ?? ""; } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 208a10c24725..01bb7bb3bda4 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -7,7 +7,6 @@ #endif using System.Text.Json; using System.Text.Json.Nodes; -using System.Text.Json.Serialization; using Microsoft.DotNet.Cli.Utils; using Microsoft.Extensions.Logging; using Microsoft.NET.Build.Containers.Resources; @@ -461,35 +460,14 @@ private static async Task WriteIndexJsonForOciImage( { cancellationToken.ThrowIfCancellationRequested(); - var manifests = new PlatformSpecificOciManifest[destinationReference.Tags.Length]; - for (int i = 0; i < destinationReference.Tags.Length; i++) - { - var tag = destinationReference.Tags[i]; - manifests[i] = new PlatformSpecificOciManifest - { - mediaType = SchemaTypes.OciManifestV1, - size = image.Manifest.Length, - digest = image.ManifestDigest, - annotations = new Dictionary - { - { "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" }, - { "org.opencontainers.image.ref.name", tag } - } - }; - } + string indexJson = ImageIndexGenerator.GenerateImageIndexWithAnnotations( + SchemaTypes.OciManifestV1, + image.ManifestDigest, + image.Manifest.Length, + destinationReference.Repository, + destinationReference.Tags); - var index = new ImageIndexV1 - { - schemaVersion = 2, - mediaType = SchemaTypes.OciImageIndexV1, - manifests = manifests - }; - - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index, options)!.ToJsonString()))) + using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(indexJson))) { PaxTarEntry indexEntry = new(TarEntryType.RegularFile, "index.json") { @@ -548,31 +526,9 @@ private static async Task WriteIndexJsonForMultiArchOciImage( // 1. create manifest list for the blobs cancellationToken.ThrowIfCancellationRequested(); - var manifests = new PlatformSpecificOciManifest[images.Length]; - for (int i = 0; i < images.Length; i++) - { - var manifest = new PlatformSpecificOciManifest - { - mediaType = SchemaTypes.OciManifestV1, - size = images[i].Manifest.Length, - digest = images[i].ManifestDigest, - platform = new PlatformInformation { architecture = images[i].Architecture!, os = images[i].OS! } - }; - manifests[i] = manifest; - } + // For multi-arch we publish only oci-formatted image tarballs. + string manifestListJson = ImageIndexGenerator.GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1); - var manifestList = new ImageIndexV1 - { - schemaVersion = 2, - mediaType = SchemaTypes.OciImageIndexV1, - manifests = manifests - }; - - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - var manifestListJson = JsonSerializer.SerializeToNode(manifestList, options)!.ToJsonString(); var manifestListDigest = DigestUtils.GetDigest(manifestListJson); var manifestListSha = DigestUtils.GetShaFromDigest(manifestListDigest); var manifestListPath = $"{_blobsPath}/{manifestListSha}"; @@ -586,34 +542,17 @@ private static async Task WriteIndexJsonForMultiArchOciImage( await writer.WriteEntryAsync(indexEntry, cancellationToken).ConfigureAwait(false); } - // 2. create index json that points to manifest list in the blobs + // 2. create index.json that points to manifest list in the blobs cancellationToken.ThrowIfCancellationRequested(); - var manifestsIndexJson = new PlatformSpecificOciManifest[destinationReference.Tags.Length]; - for (int i = 0; i < destinationReference.Tags.Length; i++) - { - var tag = destinationReference.Tags[i]; - manifestsIndexJson[i] = new PlatformSpecificOciManifest - { - mediaType = SchemaTypes.OciImageIndexV1, - size = manifestListJson.Length, - digest = manifestListDigest, - annotations = new Dictionary - { - { "io.containerd.image.name", $"{destinationReference.Repository}:{tag}" }, - { "org.opencontainers.image.ref.name", tag } - } - }; - } - - var index = new ImageIndexV1 - { - schemaVersion = 2, - mediaType = SchemaTypes.OciImageIndexV1, - manifests = manifestsIndexJson - }; + string indexJson = ImageIndexGenerator.GenerateImageIndexWithAnnotations( + SchemaTypes.OciImageIndexV1, + manifestListDigest, + manifestListJson.Length, + destinationReference.Repository, + destinationReference.Tags); - using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index, options)!.ToJsonString()))) + using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(indexJson))) { PaxTarEntry indexEntry = new(TarEntryType.RegularFile, "index.json") { diff --git a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs index 9bc573b5b0e7..3d6e54969aeb 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs @@ -96,7 +96,7 @@ public void GenerateDockerManifestList() ]; var (imageIndex, mediaType) = ImageIndexGenerator.GenerateImageIndex(images); - 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); + 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); Assert.Equal(SchemaTypes.DockerManifestListV2, mediaType); } @@ -130,7 +130,7 @@ public void GenerateOciImageIndex() ]; var (imageIndex, mediaType) = ImageIndexGenerator.GenerateImageIndex(images); - 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); + 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); Assert.Equal(SchemaTypes.OciImageIndexV1, mediaType); } } From 51d9cc80aa044ab9f579c367eaff17daad4a550c Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 3 Feb 2025 12:03:30 +0100 Subject: [PATCH 27/74] cleanup --- .../ImageIndexGenerator.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs index e4d167a007df..27f7fbe9cd5f 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs @@ -7,16 +7,6 @@ namespace Microsoft.NET.Build.Containers; -internal readonly struct ImageInfo -{ - internal string Config { get; init; } - internal string ManifestDigest { get; init; } - internal string Manifest { get; init; } - internal string ManifestMediaType { get; init; } - - public override string ToString() => ManifestDigest; -} - internal static class ImageIndexGenerator { /// From a0c3a5673449c9a812a689dd1bb9af6594f457b3 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 3 Feb 2025 12:26:35 +0100 Subject: [PATCH 28/74] add more ImageIndexGenertorTests --- .../ImageIndexGenerator.cs | 5 +++++ .../ImageIndexGeneratorTests.cs | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs index 27f7fbe9cd5f..cc9b32ec4e9d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs @@ -46,6 +46,11 @@ internal static (string, string) GenerateImageIndex(BuiltImage[] images) internal static string GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType) { + if (images.Length == 0) + { + throw new ArgumentException(string.Format(Strings.ImagesEmpty)); + } + // Here we are using ManifestListV2 struct, but we could use ImageIndexV1 struct as well. // We are filling the same fields, so we can use the same struct. var manifests = new PlatformSpecificManifest[images.Length]; diff --git a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs index 3d6e54969aeb..f89d2601b9b8 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs @@ -15,6 +15,14 @@ public void ImagesCannotBeEmpty() Assert.Equal(Strings.ImagesEmpty, ex.Message); } + [Fact] + public void ImagesCannotBeEmpty_SpecifiedMediaType() + { + BuiltImage[] images = Array.Empty(); + var ex = Assert.Throws(() => ImageIndexGenerator.GenerateImageIndex(images, "manifestMediaType", "imageIndexMediaType")); + Assert.Equal(Strings.ImagesEmpty, ex.Message); + } + [Fact] public void UnsupportedMediaTypeThrows() { @@ -133,4 +141,11 @@ public void GenerateOciImageIndex() 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); Assert.Equal(SchemaTypes.OciImageIndexV1, mediaType); } + + [Fact] + public void GenerateImageIndexWithAnnotations() + { + string imageIndex = ImageIndexGenerator.GenerateImageIndexWithAnnotations("mediaType", "sha256:digest", 3, "repository", ["1.0", "2.0"]); + Assert.Equal("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.index.v1\\u002Bjson\",\"manifests\":[{\"mediaType\":\"mediaType\",\"size\":3,\"digest\":\"sha256:digest\",\"platform\":{},\"annotations\":{\"io.containerd.image.name\":\"repository:1.0\",\"org.opencontainers.image.ref.name\":\"1.0\"}},{\"mediaType\":\"mediaType\",\"size\":3,\"digest\":\"sha256:digest\",\"platform\":{},\"annotations\":{\"io.containerd.image.name\":\"repository:2.0\",\"org.opencontainers.image.ref.name\":\"2.0\"}}]}", imageIndex); + } } From 3078ea8728d9035756c101cfbc73d0381e4a3098 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 3 Feb 2025 14:38:05 +0100 Subject: [PATCH 29/74] check if containerd store is enabled before loading --- .../ImagePublisher.cs | 10 +---- .../LocalDaemons/DockerCli.cs | 41 ++++++++++++++++++- .../Resources/Strings.Designer.cs | 6 +-- .../Resources/Strings.resx | 8 ++-- .../Resources/xlf/Strings.cs.xlf | 10 ++--- .../Resources/xlf/Strings.de.xlf | 10 ++--- .../Resources/xlf/Strings.es.xlf | 10 ++--- .../Resources/xlf/Strings.fr.xlf | 10 ++--- .../Resources/xlf/Strings.it.xlf | 10 ++--- .../Resources/xlf/Strings.ja.xlf | 10 ++--- .../Resources/xlf/Strings.ko.xlf | 10 ++--- .../Resources/xlf/Strings.pl.xlf | 10 ++--- .../Resources/xlf/Strings.pt-BR.xlf | 10 ++--- .../Resources/xlf/Strings.ru.xlf | 10 ++--- .../Resources/xlf/Strings.tr.xlf | 10 ++--- .../Resources/xlf/Strings.zh-Hans.xlf | 10 ++--- .../Resources/xlf/Strings.zh-Hant.xlf | 10 ++--- 17 files changed, 113 insertions(+), 82 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs index cd6a6a15ebd8..bc3e330441af 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs @@ -72,8 +72,7 @@ await PushToLocalRegistryAsync( BuildEngine, telemetry, cancellationToken, - destinationImageReference.LocalRegistry!.LoadAsync, - logTipAboutContainerd : true).ConfigureAwait(false); + destinationImageReference.LocalRegistry!.LoadAsync).ConfigureAwait(false); break; case DestinationImageReferenceKind.RemoteRegistry: await PushToRemoteRegistryAsync( @@ -110,8 +109,7 @@ private static async Task PushToLocalRegistryAsync( IBuildEngine? BuildEngine, Telemetry telemetry, CancellationToken cancellationToken, - Func loadFunc, - bool logTipAboutContainerd = false) + Func loadFunc) { ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!; if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false))) @@ -148,10 +146,6 @@ private static async Task PushToLocalRegistryAsync( { telemetry.LogLocalLoadError(); Log.LogErrorFromException(dle, showStackTrace: false); - if (logTipAboutContainerd && dle.Message.Contains("no such file or directory")) - { - Log.LogMessage(MessageImportance.High, Strings.TipToEnableContainerdForMultiArch); - } } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 01bb7bb3bda4..6b7471cafb76 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -87,10 +87,16 @@ private async Task LoadAsync( SourceImageReference sourceReference, DestinationImageReference destinationReference, Func writeStreamFunc, - CancellationToken cancellationToken) + CancellationToken cancellationToken, + bool checkContainerdStore = false) { cancellationToken.ThrowIfCancellationRequested(); + if (checkContainerdStore && !IsContainerdStoreEnabledForDocker()) + { + throw new DockerLoadException(Strings.ImageLoadFailed_ContainerdStoreDisabled); + } + string commandPath = await FindFullCommandPath(cancellationToken); // call `docker load` and get it ready to receive input @@ -125,7 +131,7 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen => await LoadAsync(image, sourceReference, destinationReference, WriteDockerImageToStreamAsync, cancellationToken); public async Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken) - => await LoadAsync(images, sourceReference, destinationReference, WriteMultiArchOciImageToStreamAsync, cancellationToken); + => await LoadAsync(images, sourceReference, destinationReference, WriteMultiArchOciImageToStreamAsync, cancellationToken, checkContainerdStore: true); public async Task IsAvailableAsync(CancellationToken cancellationToken) { @@ -619,6 +625,37 @@ private static bool IsPodmanAlias() } } + private static bool IsContainerdStoreEnabledForDocker() + { + try + { + // We don't need to check if this is docker, because there is no "DriverStatus" for podman + if (!GetDockerConfig().RootElement.TryGetProperty("DriverStatus", out var driverStatus) || driverStatus.ValueKind != JsonValueKind.Array) + { + return false; + } + + foreach (var item in driverStatus.EnumerateArray()) + { + if (item.ValueKind != JsonValueKind.Array || item.GetArrayLength() != 2) continue; + + var array = item.EnumerateArray().ToArray(); + // The usual output is [driver-type io.containerd.snapshotter.v1] + if (array[0].GetString() == "driver-type" && array[1].GetString()!.StartsWith("io.containerd.snapshotter")) + { + return true; + } + } + + return false; + } + catch + { + return false; + } + } + + #if NET private async Task TryRunVersionCommandAsync(string command, CancellationToken cancellationToken) { diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs index 4f2dee24ba99..59a9f28b0b83 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs @@ -457,11 +457,11 @@ internal static string ImageIndex_PodmanNotSupported { } /// - /// Looks up a localized string similar to Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings.. + /// Looks up a localized string similar to CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.. /// - internal static string TipToEnableContainerdForMultiArch { + internal static string ImageLoadFailed_ContainerdStoreDisabled { get { - return ResourceManager.GetString("TipToEnableContainerdForMultiArch", resourceCulture); + return ResourceManager.GetString("ImageLoadFailed_ContainerdStoreDisabled", resourceCulture); } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx index 42d9dddffea2..bac283c4ac0c 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx @@ -189,6 +189,10 @@ CONTAINER1009: Failed to load image from local registry. stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. {StrBegin="CONTAINER2015: "} @@ -401,10 +405,6 @@ Image index creation for Podman is not supported. - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - Error while reading daemon config: {0} {0} is the exception message that ends with period diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf index 20499b00e426..f916d5f3d916 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf @@ -189,6 +189,11 @@ CONTAINER1009: Nepodařilo se načíst bitovou kopii z místního registru. stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: Načítání imagí z místního registru se nepodporuje. @@ -409,11 +414,6 @@ CONTAINER4001: Požadovaná vlastnost '{0}' nebyla nastavena nebo je prázdná. {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: Příliš mnoho opakovaných pokusů, zastavuje se. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf index 313ffe15f9a1..d0ac67812ab7 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf @@ -189,6 +189,11 @@ CONTAINER1009: Fehler beim Laden des Images aus der lokalen Registrierung. stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: Das Pullen von Images aus der lokalen Registrierung wird nicht unterstützt. @@ -409,11 +414,6 @@ CONTAINER4001: Die erforderliche Eigenschaft „{0}“ wurde nicht festgelegt oder ist leer. {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: Zu viele Wiederholungsversuche, Vorgang wird beendet. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf index a1c4e1a7107f..b1febfedfbc6 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf @@ -189,6 +189,11 @@ CONTAINER1009: no se pudo cargar la imagen desde el registro local. Stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: No se admite la extracción de imágenes del registro local. @@ -409,11 +414,6 @@ CONTAINER4001: La propiedad necesaria "{0}" no se estableció o estaba vacía. {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: Demasiados reintentos, deteniendo. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf index 705c8e48ae85..2566778c2a07 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf @@ -189,6 +189,11 @@ CONTAINER1009: Échec du chargement de l'image à partir du registre local. sortie standard : {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: L'extraction d'images à partir du registre local n'est pas prise en charge. @@ -409,11 +414,6 @@ CONTAINER4001: la propriété requise '{0}' n’a pas été définie ou vide. {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: trop de tentatives, arrêt. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf index 22089bdb50c6..e6b319944c11 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf @@ -189,6 +189,11 @@ CONTAINER1009: non è stato possibile caricare l'immagine dal registro locale. stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: il pull di immagini dal registro locale non è supportato. @@ -409,11 +414,6 @@ CONTAINER4001: la proprietà obbligatoria '{0}' non è stata impostata o è vuota. {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: troppi tentativi, arresto. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf index 3efb830d3e14..16e01793da1d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf @@ -189,6 +189,11 @@ CONTAINER1009: ローカル レジストリからイメージを読み込めませんでした。stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: ローカル レジストリからのイメージのプルはサポートされていません。 @@ -409,11 +414,6 @@ CONTAINER4001: 必要なプロパティ '{0}' が設定されていなかったか、空でした。 {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: 再試行回数が多すぎます。停止しています。 diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf index 75380d38355f..370181275c81 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf @@ -189,6 +189,11 @@ CONTAINER1009: 로컬 레지스트리에서 이미지를 로드하지 못했습니다. stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: 로컬 레지스트리에서 이미지 끌어오기가 지원되지 않습니다. @@ -409,11 +414,6 @@ CONTAINER4001: 필수 속성 '{0}'이(가) 설정되지 않았거나 비어 있습니다. {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: 다시 시도가 너무 많아 중지 중입니다. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf index f34e4c1e4b31..d92686d7d371 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf @@ -189,6 +189,11 @@ CONTAINER1009: Nie można załadować obrazu z rejestru lokalnego. stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: Ściąganie obrazów z rejestru lokalnego nie jest obsługiwane. @@ -409,11 +414,6 @@ CONTAINER4001: wymagana właściwość „{0}” nie została ustawiona lub jest pusta. {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: zbyt wiele ponownych prób, zatrzymywanie. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf index 6b5da780f9fb..651ea8dd65b6 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf @@ -189,6 +189,11 @@ CONTAINER1009: falha ao carregar a imagem do registro local. stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: A extração de imagens do registro local não é suportada. @@ -409,11 +414,6 @@ CONTAINER4001: A propriedade obrigatória '{0}' não foi definida ou está vazia. {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: Muitas tentativas, parando. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf index e9d2290783c0..a52774d219e5 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf @@ -189,6 +189,11 @@ CONTAINER1009: не удалось загрузить образ из локального реестра. stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: извлечение образов из локального реестра не поддерживается. @@ -409,11 +414,6 @@ CONTAINER4001: обязательное свойство "{0}" не установлено или пусто. {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: слишком много повторных попыток, остановка. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf index fac7e81d6444..f513dfc84940 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf @@ -189,6 +189,11 @@ CONTAINER1009: Görüntü yerel kayıt defterinden yüklenemedi. stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: Yerel kayıt defterinden görüntü çekme desteklenmiyor. @@ -409,11 +414,6 @@ CONTAINER4001: Gerekli '{0}' özelliği ayarlanmadı veya boş değil. {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: Çok fazla yeniden deneme, durduruluyor. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf index 411f5221a963..82b415641aa9 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf @@ -189,6 +189,11 @@ CONTAINER1009: 未能从本地注册表加载映像。stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: 不支持从本地注册表拉取映像。 @@ -409,11 +414,6 @@ CONTAINER4001: 未设置必需属性 "{0}" 或为空。 {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: 重试次数过多,正在停止。 diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf index 580bb931de64..ef91ab2848e8 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf @@ -189,6 +189,11 @@ CONTAINER1009: 無法從本機登錄載入映像。stdout: {0} {StrBegin="CONTAINER1009: "} + + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings. + {StrBegin="CONTAINER1020: "} + CONTAINER1010: Pulling images from local registry is not supported. CONTAINER1010: 不支援從本機登錄提取映像。 @@ -409,11 +414,6 @@ CONTAINER4001: 必要的屬性 '{0}' 未設定或是空的。 {StrBegin="CONTAINER4001: "} - - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - Tip: For multi-arch image publishing, ensure that 'Use containerd for pulling and storing images' is checked in Docker Desktop settings. - - CONTAINER1006: Too many retries, stopping. CONTAINER1006: 重試太多次,正在停止。 From adadcc27c020db4838be80e487c9c82aa51f344b Mon Sep 17 00:00:00 2001 From: Forgind <12969783+Forgind@users.noreply.github.com> Date: Tue, 4 Feb 2025 17:02:16 -0800 Subject: [PATCH 30/74] Delete spurious error --- src/Cli/dotnet/commands/InstallingWorkloadCommand.cs | 2 +- .../SdkDirectoryWorkloadManifestProvider.cs | 6 +++++- .../Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs index 8a0ae321c37f..9232b9a3790c 100644 --- a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs +++ b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs @@ -254,7 +254,7 @@ private IEnumerable InstallWorkloadSet(ITransactionContex PrintWorkloadSetTransition(workloadSetVersion); var workloadSet = _workloadInstaller.InstallWorkloadSet(context, workloadSetVersion); - return _workloadManifestUpdater.CalculateManifestUpdatesForWorkloadSet(workloadSet); + return workloadSet is null ? Enumerable.Empty() : _workloadManifestUpdater.CalculateManifestUpdatesForWorkloadSet(workloadSet); } private void PrintWorkloadSetTransition(string newVersion) diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs index 2aca0c68dbd5..e24daeb6c321 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs @@ -539,7 +539,11 @@ static void AddWorkloadSetsForFeatureBand(Dictionary availa { var workloadSetVersion = Path.GetFileName(workloadSetDirectory); var workloadSet = WorkloadSet.FromWorkloadSetFolder(workloadSetDirectory, workloadSetVersion, featureBand); - availableWorkloadSets[workloadSet.Version!] = workloadSet; + + if (workloadSet is not null) + { + availableWorkloadSets[workloadSet.Version!] = workloadSet; + } } } } diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs index eecb89c38f06..18adbfab7d13 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs @@ -81,7 +81,7 @@ public static WorkloadSet FromJson(string json, SdkFeatureBand defaultFeatureBan #endif } - public static WorkloadSet FromWorkloadSetFolder(string path, string workloadSetVersion, SdkFeatureBand defaultFeatureBand) + public static WorkloadSet? FromWorkloadSetFolder(string path, string workloadSetVersion, SdkFeatureBand defaultFeatureBand) { WorkloadSet? workloadSet = null; foreach (var jsonFile in Directory.GetFiles(path, "*.workloadset.json")) @@ -107,7 +107,7 @@ public static WorkloadSet FromWorkloadSetFolder(string path, string workloadSetV if (workloadSet == null) { - throw new InvalidOperationException("No workload set information found in: " + path); + return null; } if (File.Exists(Path.Combine(path, "baseline.workloadset.json"))) From af3a5482eb2e11b585e8da28b394bc05861bacef Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 5 Feb 2025 14:36:06 +0100 Subject: [PATCH 31/74] cleanup targets --- .../build/Microsoft.NET.Build.Containers.targets | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets index 3854f88ef29f..51b28a1db5f6 100644 --- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets +++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets @@ -297,12 +297,6 @@ - - @@ -400,10 +394,6 @@ Include="$([System.String]::Copy('%(_ParsedContainerEnvironmentVariables.Identity)').Split(':')[0])" Value="$([System.String]::Copy('%(_ParsedContainerEnvironmentVariables.Identity)').Split(':')[1])" /> - - - $([System.IO.Path]::Combine($(ContainerArchiveOutputPath), $(ContainerRepository)-$(ContainerRuntimeIdentifier).tar.gz)) - Date: Wed, 5 Feb 2025 14:39:06 +0100 Subject: [PATCH 32/74] add docker.io/library prefix to image name annotation; fix json serialization symbol escaping --- .../ImageIndexGenerator.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs index cc9b32ec4e9d..8abbe315d0c8 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.NET.Build.Containers.Resources; @@ -46,11 +47,6 @@ internal static (string, string) GenerateImageIndex(BuiltImage[] images) internal static string GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType) { - if (images.Length == 0) - { - throw new ArgumentException(string.Format(Strings.ImagesEmpty)); - } - // Here we are using ManifestListV2 struct, but we could use ImageIndexV1 struct as well. // We are filling the same fields, so we can use the same struct. var manifests = new PlatformSpecificManifest[images.Length]; @@ -77,12 +73,7 @@ internal static string GenerateImageIndex(BuiltImage[] images, string manifestMe manifests = manifests }; - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - - return JsonSerializer.SerializeToNode(imageIndex, options)?.ToJsonString() ?? ""; + return GetJsonStringFromImageIndex(imageIndex); } internal static string GenerateImageIndexWithAnnotations(string manifestMediaType, string manifestDigest, long manifestSize, string repository, string[] tags) @@ -98,7 +89,7 @@ internal static string GenerateImageIndexWithAnnotations(string manifestMediaTyp digest = manifestDigest, annotations = new Dictionary { - { "io.containerd.image.name", $"{repository}:{tag}" }, + { "io.containerd.image.name", $"docker.io/library/{repository}:{tag}" }, { "org.opencontainers.image.ref.name", tag } } }; @@ -111,11 +102,20 @@ internal static string GenerateImageIndexWithAnnotations(string manifestMediaTyp manifests = manifests }; - var options = new JsonSerializerOptions + return GetJsonStringFromImageIndex(index); + } + + private static string GetJsonStringFromImageIndex(T imageIndex) + { + var nullIgnoreOptions = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; + var escapeOptions = new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; - return JsonSerializer.SerializeToNode(index, options)?.ToJsonString() ?? ""; + return JsonSerializer.SerializeToNode(imageIndex, nullIgnoreOptions)?.ToJsonString(escapeOptions) ?? ""; } } From a821ac2cc768396ea97c0d54bd329d030ec9e26e Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 5 Feb 2025 14:39:42 +0100 Subject: [PATCH 33/74] delete empty lines --- .../Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 217f2909fffe..d56ae92d9b62 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -52,8 +52,6 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) return false; } - - using MSBuildLoggerProvider loggerProvider = new(Log); ILoggerFactory msbuildLoggerFactory = new LoggerFactory(new[] { loggerProvider }); ILogger logger = msbuildLoggerFactory.CreateLogger(); From 8bea2ec53b15a3a33b49b971916f27e532c7bd1e Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 5 Feb 2025 15:39:14 +0100 Subject: [PATCH 34/74] fix EndToEndMultiArch_ArchivePublishing test --- .../EndToEndTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index 2881ae159aa6..19e04dd0f132 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -928,7 +928,7 @@ public void EndToEndMultiArch_ArchivePublishing() string imageName = NewImageName(); string tag = "1.0"; string image = $"{imageName}:{tag}"; - string archiveOutput = Path.Combine(TestSettings.TestArtifactsDirectory, "tarballs-output"); + string archiveOutput = TestSettings.TestArtifactsDirectory; string imageTarball = Path.Combine(archiveOutput, $"{imageName}.tar.gz"); // Create a new console project From 2e6adabb97b8db0450bf9a67295607b6998027ac Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 5 Feb 2025 15:54:15 +0100 Subject: [PATCH 35/74] update EndToEndMultiArch_RemoteRegistry test since containerd store is enabled --- .../ContainerCli.cs | 3 -- .../EndToEndTests.cs | 48 +++++++------------ 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs index 1315d341f94f..13aa9928633d 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs @@ -12,9 +12,6 @@ static class ContainerCli public static RunExeCommand PullCommand(ITestOutputHelper log, params string[] args) => CreateCommand(log, "pull", args); - public static RunExeCommand TagCommand(ITestOutputHelper log, params string[] args) - => CreateCommand(log, "tag", args); - public static RunExeCommand PushCommand(ITestOutputHelper log, params string[] args) => CreateCommand(log, "push", args); diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index 19e04dd0f132..ea6135c5bcbe 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -1004,6 +1004,7 @@ public void EndToEndMultiArch_RemoteRegistry() string imageX64 = $"{imageName}:{imageTag}-linux-x64"; string imageArm64 = $"{imageName}:{imageTag}-linux-arm64"; string imageIndex = $"{imageName}:{imageTag}"; + string imageFromRegistry = $"{registry}/{imageIndex}"; // Create a new console project DirectoryInfo newProjectDir = CreateNewProject("console"); @@ -1031,59 +1032,42 @@ public void EndToEndMultiArch_RemoteRegistry() .And.HaveStdOutContaining($"Pushed image '{imageX64}' to registry '{registry}'.") .And.HaveStdOutContaining($"Pushed image '{imageArm64}' to registry '{registry}'.") .And.HaveStdOutContaining($"Pushed image index '{imageIndex}' to registry '{registry}'."); - - - // Check that the containers can be run - // First pull the image from the registry, then tag so the image won't be overwritten - string imageX64Tagged = $"{registry}/test-image-{imageName}-x64"; + + // First pull the image from the registry for each platform ContainerCli.PullCommand( _testOutput, "--platform", "linux/amd64", - $"{registry}/{imageIndex}") + imageFromRegistry) .Execute() .Should().Pass(); - ContainerCli.TagCommand( + ContainerCli.PullCommand( _testOutput, - $"{registry}/{imageIndex}", - imageX64Tagged) + "--platform", + "linux/arm64", + imageFromRegistry) .Execute() .Should().Pass(); - CommandResult processResultX64 = ContainerCli.RunCommand( + + // Check that the containers can be run + ContainerCli.RunCommand( _testOutput, "--rm", "--platform", "linux/amd64", "--name", $"test-container-{imageName}-x64", - imageX64Tagged) - .Execute(); - processResultX64.Should().Pass().And.HaveStdOut("Hello, World!"); - - string imageArm64Tagged = $"{registry}/test-image-{imageName}-arm64"; - ContainerCli.PullCommand( - _testOutput, - "--platform", - "linux/arm64", - $"{registry}/{imageIndex}") - .Execute() - .Should().Pass(); - ContainerCli.TagCommand( - _testOutput, - $"{registry}/{imageIndex}", - imageArm64Tagged) - .Execute() - .Should().Pass(); - CommandResult processResultArm64 = ContainerCli.RunCommand( + imageFromRegistry) + .Execute().Should().Pass().And.HaveStdOut("Hello, World!"); + ContainerCli.RunCommand( _testOutput, "--rm", "--platform", "linux/arm64", "--name", $"test-container-{imageName}-arm64", - imageArm64Tagged) - .Execute(); - processResultArm64.Should().Pass().And.HaveStdOut("Hello, World!"); + imageFromRegistry) + .Execute().Should().Pass().And.HaveStdOut("Hello, World!"); // Cleanup newProjectDir.Delete(true); From 929c2b327de5ddacc50c36dccd3375d7b445acc4 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 5 Feb 2025 16:51:17 +0100 Subject: [PATCH 36/74] go back to running containers by image and not imageId for multi-arch tests --- .../ContainerCli.cs | 3 - .../EndToEndTests.cs | 62 ++++++------------- 2 files changed, 18 insertions(+), 47 deletions(-) diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs index 13aa9928633d..b69f070da2e2 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs @@ -36,9 +36,6 @@ public static RunExeCommand LoadCommand(ITestOutputHelper log, params string[] a public static RunExeCommand PortCommand(ITestOutputHelper log, string containerName, int port) => CreateCommand(log, "port", containerName, port.ToString()); - public static RunExeCommand ImagesCommand(ITestOutputHelper log, params string[] args) - => CreateCommand(log, "images", args); - private static RunExeCommand CreateCommand(ITestOutputHelper log, string command, params string[] args) { string commandPath = IsPodman ? "podman" : "docker"; diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index ea6135c5bcbe..888d8f48cfc1 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -745,8 +745,7 @@ public void EndToEndMultiArch_LocalRegistry() .WithWorkingDirectory(newProjectDir.FullName) .Execute(); - // Check that the app was published for each RID, - // one image was created locally + // Check that the app was published for each RID, one image was created locally commandResult.Should().Pass() .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-x64")) .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-arm64")) @@ -754,9 +753,6 @@ public void EndToEndMultiArch_LocalRegistry() .And.HaveStdOutContaining($"Building image '{imageName}' for runtime identifier 'linux-arm64'") .And.HaveStdOutContaining($"Pushed image '{image}' to local registry"); - //Multi-arch oci tarballs that are loaded to docker can only be run by their image id - string imageId = GetImageId(image); - // Check that the containers can be run CommandResult processResultX64 = ContainerCli.RunCommand( _testOutput, @@ -765,7 +761,7 @@ public void EndToEndMultiArch_LocalRegistry() "linux/amd64", "--name", $"test-container-{imageName}-x64", - imageId) + image) .Execute(); processResultX64.Should().Pass().And.HaveStdOut("Hello, World!"); @@ -776,7 +772,7 @@ public void EndToEndMultiArch_LocalRegistry() "linux/arm64", "--name", $"test-container-{imageName}-arm64", - imageId) + image) .Execute(); processResultArm64.Should().Pass().And.HaveStdOut("Hello, World!"); @@ -784,19 +780,6 @@ public void EndToEndMultiArch_LocalRegistry() newProjectDir.Delete(true); } - private string GetImageId(string image) - { - CommandResult commandResult = ContainerCli.ImagesCommand(_testOutput, "--format", "\"{{.ID}}\"", image) - .Execute(); - commandResult.Should().Pass(); - - var output = commandResult.StdOut.Split("\n").Select(s => s.Trim('"')).ToList(); - - output.Should().NotBeNullOrEmpty().And.OnlyContain(s => s == output[0]); - - return output[0]; - } - [DockerAvailableFact] public void MultiArchStillAllowsSingleRID() { @@ -948,8 +931,7 @@ public void EndToEndMultiArch_ArchivePublishing() .WithWorkingDirectory(newProjectDir.FullName) .Execute(); - // Check that the app was published for each RID, - // one image was created in local archive + // Check that the app was published for each RID, one image was created in local archive commandResult.Should().Pass() .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-x64")) .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-arm64")) @@ -957,7 +939,7 @@ public void EndToEndMultiArch_ArchivePublishing() .And.HaveStdOutContaining($"Building image '{imageName}' for runtime identifier 'linux-arm64'") .And.HaveStdOutContaining($"Pushed image '{image}' to local archive at '{imageTarball}'"); - // Check that tarballs were created + // Check that tarball were created File.Exists(imageTarball).Should().BeTrue(); // Load the multi-arch image from the tarball @@ -965,9 +947,6 @@ public void EndToEndMultiArch_ArchivePublishing() .Execute() .Should().Pass(); - //Multi-arch oci tarballs that are loaded to docker can only be run by their image id - string imageId = GetImageId(image); - // Check that the containers can be run CommandResult processResultX64 = ContainerCli.RunCommand( _testOutput, @@ -976,7 +955,7 @@ public void EndToEndMultiArch_ArchivePublishing() "linux/amd64", "--name", $"test-container-{imageName}-x64", - imageId) + image) .Execute(); processResultX64.Should().Pass().And.HaveStdOut("Hello, World!"); @@ -987,7 +966,7 @@ public void EndToEndMultiArch_ArchivePublishing() "linux/arm64", "--name", $"test-container-{imageName}-arm64", - imageId) + image) .Execute(); processResultArm64.Should().Pass().And.HaveStdOut("Hello, World!"); @@ -1024,8 +1003,8 @@ public void EndToEndMultiArch_RemoteRegistry() .Execute(); // Check that the app was published for each RID, - // images were created locally for each RID - // and image index was created + // images for each RID were pushed to remote registry + // and image index was pushed to remote registry commandResult.Should().Pass() .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-x64")) .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-arm64")) @@ -1033,6 +1012,7 @@ public void EndToEndMultiArch_RemoteRegistry() .And.HaveStdOutContaining($"Pushed image '{imageArm64}' to registry '{registry}'.") .And.HaveStdOutContaining($"Pushed image index '{imageIndex}' to registry '{registry}'."); + // Check that the containers can be run // First pull the image from the registry for each platform ContainerCli.PullCommand( _testOutput, @@ -1048,8 +1028,8 @@ public void EndToEndMultiArch_RemoteRegistry() imageFromRegistry) .Execute() .Should().Pass(); - - // Check that the containers can be run + + // Run the containers ContainerCli.RunCommand( _testOutput, "--rm", @@ -1097,7 +1077,7 @@ public void EndToEndMultiArch_ContainerRuntimeIdentifiersOverridesRuntimeIdentif .Execute(); // Check that the app was published only for RID from ContainerRuntimeIdentifiers - // images were created locally only for RID for from ContainerRuntimeIdentifiers + // images were built only for RID for from ContainerRuntimeIdentifiers commandResult.Should().Pass() .And.NotHaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-x64")) .And.HaveStdOutContaining(GetPublishArtifactsPath(newProjectDir.FullName, "linux-arm64")) @@ -1148,8 +1128,6 @@ public void EndToEndMultiArch_EnvVariables() .Execute() .Should().Pass(); - string imageId = GetImageId(image); - // Check that the env var is printed for linux/amd64 platform string containerNameX64 = $"test-container-{imageName}-x64"; CommandResult processResultX64 = ContainerCli.RunCommand( @@ -1159,7 +1137,7 @@ public void EndToEndMultiArch_EnvVariables() "linux/amd64", "--name", containerNameX64, - imageId) + image) .Execute(); processResultX64.Should().Pass().And.HaveStdOut("FooBar"); @@ -1172,7 +1150,7 @@ public void EndToEndMultiArch_EnvVariables() "linux/arm64", "--name", containerNameArm64, - imageId) + image) .Execute(); processResultArm64.Should().Pass().And.HaveStdOut("FooBar"); @@ -1215,8 +1193,6 @@ public void EndToEndMultiArch_Ports() .Execute() .Should().Pass(); - string imageId = GetImageId(image); - // Check that the ports are correct for linux/amd64 platform var containerNameX64 = $"test-container-{imageName}-x64"; CommandResult processResultX64 = ContainerCli.RunCommand( @@ -1228,7 +1204,7 @@ public void EndToEndMultiArch_Ports() containerNameX64, "-P", "--detach", - imageId) + image) .Execute(); processResultX64.Should().Pass(); @@ -1246,7 +1222,7 @@ public void EndToEndMultiArch_Ports() containerNameArm64, "-P", "--detach", - imageId) + image) .Execute(); processResultArm64.Should().Pass(); @@ -1304,13 +1280,11 @@ public void EndToEndMultiArch_Labels() .Execute() .Should().Pass(); - string imageId = GetImageId(image); - // Check that labels are set CommandResult inspectResult = ContainerCli.InspectCommand( _testOutput, "--format={{json .Config.Labels}}", - imageId) + image) .Execute(); inspectResult.Should().Pass(); var labels = JsonSerializer.Deserialize>(inspectResult.StdOut); From e46389b934c41a357ca7f82f7446786b0b1b295e Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 5 Feb 2025 18:01:17 +0100 Subject: [PATCH 37/74] implement skipping tests if containerd image store is not enabled --- .../LocalDaemons/DockerCli.cs | 2 +- .../ContainerCli.cs | 2 ++ .../DockerSupportsArchFact.cs | 6 +++++- .../DockerSupportsArchInlineData.cs | 2 ++ .../EndToEndTests.cs | 14 +++++++------- .../DockerAvailableUtils.cs | 11 +++++++---- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 6b7471cafb76..12ac89441ea7 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -625,7 +625,7 @@ private static bool IsPodmanAlias() } } - private static bool IsContainerdStoreEnabledForDocker() + internal static bool IsContainerdStoreEnabledForDocker() { try { diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs index b69f070da2e2..690cb28fe1e3 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs @@ -9,6 +9,8 @@ static class ContainerCli public static bool IsAvailable => _isAvailable.Value; + public static bool IsContainerdStoreEnabledForDocker => DockerCli.IsContainerdStoreEnabledForDocker(); + public static RunExeCommand PullCommand(ITestOutputHelper log, params string[] args) => CreateCommand(log, "pull", args); diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs index 36ad9fbc0c9a..ae0e76f3c153 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs @@ -5,12 +5,16 @@ namespace Microsoft.NET.Build.Containers.IntegrationTests; public class DockerIsAvailableAndSupportsArchFactAttribute : FactAttribute { - public DockerIsAvailableAndSupportsArchFactAttribute(string arch) + public DockerIsAvailableAndSupportsArchFactAttribute(string arch, bool checkContainerdStoreAvailability = false) { if (!DockerSupportsArchHelper.DaemonIsAvailable) { base.Skip = "Skipping test because Docker is not available on this host."; } + else if (checkContainerdStoreAvailability && !DockerSupportsArchHelper.IsContainerdStoreEnabledForDocker) + { + base.Skip = "Skipping test because Docker daemon is not using containerd as the storage driver."; + } else if (!DockerSupportsArchHelper.DaemonSupportsArch(arch)) { base.Skip = $"Skipping test because Docker daemon does not support {arch}."; diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs index caafbaea4b45..7944436b39c9 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs @@ -36,6 +36,8 @@ internal static class DockerSupportsArchHelper { internal static bool DaemonIsAvailable => ContainerCli.IsAvailable; + internal static bool IsContainerdStoreEnabledForDocker => ContainerCli.IsContainerdStoreEnabledForDocker; + internal static bool DaemonSupportsArch(string arch) { // an optimization - this doesn't change over time so we can compute it once diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index 888d8f48cfc1..49b8ffec61f6 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -722,7 +722,7 @@ public void EndToEnd_SingleArch_NoRid() processResultX64.Should().Pass().And.HaveStdOut("Hello, World!"); } - [DockerIsAvailableAndSupportsArchFact("linux/arm64")] + [DockerIsAvailableAndSupportsArchFact("linux/arm64", checkContainerdStoreAvailability: true)] public void EndToEndMultiArch_LocalRegistry() { string imageName = NewImageName(); @@ -905,7 +905,7 @@ private DirectoryInfo CreateNewProject(string template, [CallerMemberName] strin private string GetPublishArtifactsPath(string projectDir, string rid, string configuration = "Debug") => Path.Combine(projectDir, "bin", configuration, ToolsetInfo.CurrentTargetFramework, rid, "publish"); - [DockerIsAvailableAndSupportsArchFact("linux/arm64")] + [DockerIsAvailableAndSupportsArchFact("linux/arm64", checkContainerdStoreAvailability: true)] public void EndToEndMultiArch_ArchivePublishing() { string imageName = NewImageName(); @@ -974,7 +974,7 @@ public void EndToEndMultiArch_ArchivePublishing() newProjectDir.Delete(true); } - [DockerIsAvailableAndSupportsArchFact("linux/arm64")] + [DockerIsAvailableAndSupportsArchFact("linux/arm64", checkContainerdStoreAvailability: true)] public void EndToEndMultiArch_RemoteRegistry() { string imageName = NewImageName(); @@ -1053,7 +1053,7 @@ public void EndToEndMultiArch_RemoteRegistry() newProjectDir.Delete(true); } - [DockerAvailableFact] + [DockerAvailableFact(checkContainerdStoreAvailability: true)] public void EndToEndMultiArch_ContainerRuntimeIdentifiersOverridesRuntimeIdentifiers() { // Create a new console project @@ -1088,7 +1088,7 @@ public void EndToEndMultiArch_ContainerRuntimeIdentifiersOverridesRuntimeIdentif newProjectDir.Delete(true); } - [DockerIsAvailableAndSupportsArchFact("linux/arm64")] + [DockerIsAvailableAndSupportsArchFact("linux/arm64", checkContainerdStoreAvailability: true)] public void EndToEndMultiArch_EnvVariables() { string imageName = NewImageName(); @@ -1158,7 +1158,7 @@ public void EndToEndMultiArch_EnvVariables() newProjectDir.Delete(true); } - [DockerIsAvailableAndSupportsArchFact("linux/arm64")] + [DockerIsAvailableAndSupportsArchFact("linux/arm64", checkContainerdStoreAvailability: true)] public void EndToEndMultiArch_Ports() { string imageName = NewImageName(); @@ -1256,7 +1256,7 @@ private void CheckPorts(string containerName, int[] correctPorts, int[] incorrec } } - [DockerAvailableFact] + [DockerAvailableFact(checkContainerdStoreAvailability: true)] public void EndToEndMultiArch_Labels() { string imageName = NewImageName(); diff --git a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/DockerAvailableUtils.cs b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/DockerAvailableUtils.cs index 74184ec675b1..61c5f2dde3bf 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/DockerAvailableUtils.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/DockerAvailableUtils.cs @@ -25,17 +25,20 @@ public class DockerAvailableFactAttribute : FactAttribute { public static string LocalRegistry => DockerCliStatus.LocalRegistry; - public DockerAvailableFactAttribute(bool skipPodman = false) + public DockerAvailableFactAttribute(bool skipPodman = false, bool checkContainerdStoreAvailability = false) { if (!DockerCliStatus.IsAvailable) { base.Skip = "Skipping test because Docker is not available on this host."; } - - if (skipPodman && DockerCliStatus.Command == DockerCli.PodmanCommand) + else if (checkContainerdStoreAvailability && !DockerCli.IsContainerdStoreEnabledForDocker()) { - base.Skip = $"Skipping test with {DockerCliStatus.Command} cli."; + base.Skip = "Skipping test because Docker daemon is not using containerd as the storage driver."; } + else if (skipPodman && DockerCliStatus.Command == DockerCli.PodmanCommand) + { + base.Skip = $"Skipping test with {DockerCliStatus.Command} cli."; + } } } From 299e6d107182a4b63761220de8d0dda4dc0a1541 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 5 Feb 2025 18:19:27 +0100 Subject: [PATCH 38/74] remove using --- .../EndToEndTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index 49b8ffec61f6..ccef1a3b41c4 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -4,7 +4,6 @@ using System.Formats.Tar; using System.Runtime.CompilerServices; using System.Text.Json; -using Microsoft.Build.Logging; using Microsoft.DotNet.Cli.Utils; using Microsoft.NET.Build.Containers.LocalDaemons; using Microsoft.NET.Build.Containers.Resources; From 674ee0ef816f9234760d03b60050abc3f041f909 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 5 Feb 2025 21:34:46 +0100 Subject: [PATCH 39/74] refactor to bring back GeneratedImageIndex; fix ImageIndexGeneratorTests --- .../ImageIndexGenerator.cs | 5 +++ .../ImagePublisher.cs | 13 ++----- .../LocalDaemons/ArchiveFileRegistry.cs | 4 +-- .../LocalDaemons/DockerCli.cs | 23 ++++++------ .../LocalDaemons/ILocalRegistry.cs | 2 +- .../MultiArchImage.cs | 16 +++++++++ .../PublicAPI/net8.0/PublicAPI.Unshipped.txt | 2 ++ .../Registry/Registry.cs | 10 ++++-- .../Tasks/CreateImageIndex.Interface.cs | 10 ++++++ .../Tasks/CreateImageIndex.cs | 36 ++++++++++++++++--- .../Microsoft.NET.Build.Containers.targets | 5 ++- .../CreateImageIndexTests.cs | 13 +++++++ .../ImageIndexGeneratorTests.cs | 6 ++-- 13 files changed, 107 insertions(+), 38 deletions(-) create mode 100644 src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs index 8abbe315d0c8..d2bc53a5900d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs @@ -47,6 +47,11 @@ internal static (string, string) GenerateImageIndex(BuiltImage[] images) internal static string GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType) { + if (images.Length == 0) + { + throw new ArgumentException(string.Format(Strings.ImagesEmpty)); + } + // Here we are using ManifestListV2 struct, but we could use ImageIndexV1 struct as well. // We are filling the same fields, so we can use the same struct. var manifests = new PlatformSpecificManifest[images.Length]; diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs index bc3e330441af..8dc164f41f49 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs @@ -51,7 +51,7 @@ await PushToRemoteRegistryAsync( } public static async Task PublishImageAsync( - BuiltImage[] multiArchImage, + MultiArchImage multiArchImage, SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, @@ -82,16 +82,7 @@ await PushToRemoteRegistryAsync( Log, BuildEngine, cancellationToken, - async (images, source, destination, token) => - { - (string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images); - await destinationImageReference.RemoteRegistry!.PushManifestListAsync( - destinationImageReference.Repository, - destinationImageReference.Tags, - imageIndex, - mediaType, - cancellationToken).ConfigureAwait(false); - }, + destinationImageReference.RemoteRegistry!.PushManifestListAsync, Strings.ImageIndexUploadedToRegistry).ConfigureAwait(false); break; default: diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs index 5af3edd5a546..c23e22d8b9aa 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs @@ -46,10 +46,10 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen => await LoadAsync(image, sourceReference, destinationReference, cancellationToken, DockerCli.WriteImageToStreamAsync); - public async Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference, + public async Task LoadAsync(MultiArchImage multiArchImage, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken) - => await LoadAsync(images, sourceReference, destinationReference, cancellationToken, + => await LoadAsync(multiArchImage, sourceReference, destinationReference, cancellationToken, DockerCli.WriteMultiArchOciImageToStreamAsync); public Task IsAvailableAsync(CancellationToken cancellationToken) => Task.FromResult(true); diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 12ac89441ea7..d5b8fb2b3c5f 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -130,8 +130,8 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen // 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. => await LoadAsync(image, sourceReference, destinationReference, WriteDockerImageToStreamAsync, cancellationToken); - public async Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken) - => await LoadAsync(images, sourceReference, destinationReference, WriteMultiArchOciImageToStreamAsync, cancellationToken, checkContainerdStore: true); + public async Task LoadAsync(MultiArchImage multiArchImage, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken) + => await LoadAsync(multiArchImage, sourceReference, destinationReference, WriteMultiArchOciImageToStreamAsync, cancellationToken, checkContainerdStore: true); public async Task IsAvailableAsync(CancellationToken cancellationToken) { @@ -500,7 +500,7 @@ await WriteManifestForOciImage(writer, image, cancellationToken) } public static async Task WriteMultiArchOciImageToStreamAsync( - BuiltImage[] images, + MultiArchImage multiArchImage, SourceImageReference sourceReference, DestinationImageReference destinationReference, Stream imageStream, @@ -510,13 +510,13 @@ public static async Task WriteMultiArchOciImageToStreamAsync( using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true); - foreach (var image in images) + foreach (var image in multiArchImage.Images!) { await WriteOciImageToBlobs(writer, image, sourceReference, cancellationToken) .ConfigureAwait(false); } - await WriteIndexJsonForMultiArchOciImage(writer, images, destinationReference, cancellationToken) + await WriteIndexJsonForMultiArchOciImage(writer, multiArchImage, destinationReference, cancellationToken) .ConfigureAwait(false); await WriteOciLayout(writer, cancellationToken) @@ -525,21 +525,18 @@ await WriteOciLayout(writer, cancellationToken) private static async Task WriteIndexJsonForMultiArchOciImage( TarWriter writer, - BuiltImage[] images, + MultiArchImage multiArchImage, DestinationImageReference destinationReference, CancellationToken cancellationToken) { // 1. create manifest list for the blobs cancellationToken.ThrowIfCancellationRequested(); - // For multi-arch we publish only oci-formatted image tarballs. - string manifestListJson = ImageIndexGenerator.GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1); - - var manifestListDigest = DigestUtils.GetDigest(manifestListJson); + var manifestListDigest = DigestUtils.GetDigest(multiArchImage.ImageIndex); var manifestListSha = DigestUtils.GetShaFromDigest(manifestListDigest); var manifestListPath = $"{_blobsPath}/{manifestListSha}"; - using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(manifestListJson))) + using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(multiArchImage.ImageIndex))) { PaxTarEntry indexEntry = new(TarEntryType.RegularFile, manifestListPath) { @@ -552,9 +549,9 @@ private static async Task WriteIndexJsonForMultiArchOciImage( cancellationToken.ThrowIfCancellationRequested(); string indexJson = ImageIndexGenerator.GenerateImageIndexWithAnnotations( - SchemaTypes.OciImageIndexV1, + multiArchImage.ImageIndexMediaType, manifestListDigest, - manifestListJson.Length, + multiArchImage.ImageIndex.Length, destinationReference.Repository, destinationReference.Tags); diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs index 34ccb13d093d..c4ee04a7f5d6 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs @@ -16,7 +16,7 @@ internal interface ILocalRegistry { /// /// Loads a multi-arch image (presumably from a tarball) into the local registry. /// - public Task LoadAsync(BuiltImage[] images, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken); + public Task LoadAsync(MultiArchImage multiArchImage, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken); /// /// Checks to see if the local registry is available. This is used to give nice errors to the user. diff --git a/src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs b/src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs new file mode 100644 index 000000000000..2e671b186d9e --- /dev/null +++ b/src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.NET.Build.Containers; + +/// +/// Represents constructed image ready for further processing. +/// +internal sealed class MultiArchImage +{ + internal required string ImageIndex { get; init; } + + internal required string ImageIndexMediaType { get; init; } + + internal BuiltImage[]? Images { get; init; } +} \ No newline at end of file diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt index e5efa4bed6c1..5c07a3871ceb 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -34,6 +34,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedArchiveOutputPath Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Cancel() -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.CreateImageIndex() -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Dispose() -> void +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedImageIndex.get -> string! +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedImageIndex.set -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ImageTags.get -> string![]! Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ImageTags.set -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedContainers.get -> Microsoft.Build.Framework.ITaskItem![]! diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs index 2e754986334a..87671575f34a 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs @@ -525,13 +525,17 @@ private async Task UploadBlobAsync(string repository, string digest, Stream cont } - public async Task PushManifestListAsync(string repositoryName, string[] tags, string manifestListJson, string mediaType, CancellationToken cancellationToken) + public async Task PushManifestListAsync( + MultiArchImage multiArchImage, + SourceImageReference sourceImageReference, + DestinationImageReference destinationImageReference, + CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - foreach (var tag in tags) + foreach (var tag in destinationImageReference.Tags) { _logger.LogInformation(Strings.Registry_TagUploadStarted, tag, RegistryName); - await _registryAPI.Manifest.PutAsync(repositoryName, tag, manifestListJson, mediaType, cancellationToken).ConfigureAwait(false); + await _registryAPI.Manifest.PutAsync(destinationImageReference.Repository, tag, multiArchImage.ImageIndex, multiArchImage.ImageIndexMediaType, cancellationToken).ConfigureAwait(false); _logger.LogInformation(Strings.Registry_TagUploaded, tag, RegistryName); } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs index 128657b0a897..2751eb5ebbc3 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs @@ -61,9 +61,18 @@ partial class CreateImageIndex [Required] public string[] ImageTags { get; set; } + /// + /// The generated archive output path. + /// [Output] public string GeneratedArchiveOutputPath { get; set; } + /// + /// The generated image index (manifest list) in JSON format. + /// + [Output] + public string GeneratedImageIndex { get; set; } + public CreateImageIndex() { BaseRegistry = string.Empty; @@ -76,5 +85,6 @@ public CreateImageIndex() Repository = string.Empty; ImageTags = Array.Empty(); GeneratedArchiveOutputPath = string.Empty; + GeneratedImageIndex = string.Empty; } } \ No newline at end of file diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index d56ae92d9b62..65443e816fd6 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -74,14 +74,17 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) return false; } + var multiArchImage = CreateMultiArchImage(images, destinationImageReference.Kind); + + GeneratedImageIndex = multiArchImage.ImageIndex; + GeneratedArchiveOutputPath = ArchiveOutputPath; + logger.LogInformation(Strings.BuildingImageIndex, destinationImageReference, string.Join(", ", images.Select(i => i.ManifestDigest))); var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log); - await ImagePublisher.PublishImageAsync(images, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) - .ConfigureAwait(false); - - GeneratedArchiveOutputPath = ArchiveOutputPath; + await ImagePublisher.PublishImageAsync(multiArchImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) + .ConfigureAwait(false); return !Log.HasLoggedErrors; } @@ -157,4 +160,29 @@ private BuiltImage[] ParseImages(DestinationImageReferenceKind destinationKind) } return (architecture, os); } + + private static MultiArchImage CreateMultiArchImage(BuiltImage[] images, DestinationImageReferenceKind destinationImageKind) + { + switch (destinationImageKind) + { + case DestinationImageReferenceKind.LocalRegistry: + return new MultiArchImage() + { + // For multi-arch we publish only oci-formatted image tarballs. + ImageIndex = ImageIndexGenerator.GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1), + ImageIndexMediaType = SchemaTypes.OciImageIndexV1, + Images = images + }; + case DestinationImageReferenceKind.RemoteRegistry: + (string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images); + return new MultiArchImage() + { + ImageIndex = imageIndex, + ImageIndexMediaType = mediaType, + // For remote registry we don't need individual images, as they should be pushed already + }; + default: + throw new ArgumentOutOfRangeException(); + } + } } diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets index 51b28a1db5f6..d7cf4bc5ecbf 100644 --- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets +++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets @@ -357,7 +357,10 @@ ImageTags="@(ContainerImageTags)" BaseRegistry="$(ContainerBaseRegistry)" BaseImageName="$(ContainerBaseName)" - BaseImageTag="$(ContainerBaseTag)" /> + BaseImageTag="$(ContainerBaseTag)"> + + + diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs index 24703040cda6..2615b2e178ee 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateImageIndexTests.cs @@ -44,6 +44,19 @@ public async Task CreateImageIndex_Baseline() cii.GeneratedContainers = [image1, image2]; Assert.True(cii.Execute(), FormatBuildMessages(errors)); + // Assert that the image index is created correctly + cii.GeneratedImageIndex.Should().NotBeNullOrEmpty(); + var imageIndex = cii.GeneratedImageIndex.FromJson(); + imageIndex.manifests.Should().HaveCount(2); + + imageIndex.manifests[0].digest.Should().Be(image1.GetMetadata("ManifestDigest")); + imageIndex.manifests[0].platform.os.Should().Be("linux"); + imageIndex.manifests[0].platform.architecture.Should().Be("amd64"); + + imageIndex.manifests[1].digest.Should().Be(image2.GetMetadata("ManifestDigest")); + imageIndex.manifests[1].platform.os.Should().Be("linux"); + imageIndex.manifests[1].platform.architecture.Should().Be("arm64"); + // Assert that the image index is pushed to the registry var loggerFactory = new TestLoggerFactory(_testOutput); var logger = loggerFactory.CreateLogger(nameof(CreateImageIndex_Baseline)); diff --git a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs index f89d2601b9b8..6b87a502d6a6 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs @@ -104,7 +104,7 @@ public void GenerateDockerManifestList() ]; var (imageIndex, mediaType) = ImageIndexGenerator.GenerateImageIndex(images); - 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); + Assert.Equal("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.docker.distribution.manifest.list.v2+json\",\"manifests\":[{\"mediaType\":\"application/vnd.docker.distribution.manifest.v2+json\",\"size\":3,\"digest\":\"sha256:digest1\",\"platform\":{\"architecture\":\"arch1\",\"os\":\"os1\"}},{\"mediaType\":\"application/vnd.docker.distribution.manifest.v2+json\",\"size\":3,\"digest\":\"sha256:digest2\",\"platform\":{\"architecture\":\"arch2\",\"os\":\"os2\"}}]}", imageIndex); Assert.Equal(SchemaTypes.DockerManifestListV2, mediaType); } @@ -138,7 +138,7 @@ public void GenerateOciImageIndex() ]; var (imageIndex, mediaType) = ImageIndexGenerator.GenerateImageIndex(images); - 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); + Assert.Equal("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.index.v1+json\",\"manifests\":[{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"size\":3,\"digest\":\"sha256:digest1\",\"platform\":{\"architecture\":\"arch1\",\"os\":\"os1\"}},{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"size\":3,\"digest\":\"sha256:digest2\",\"platform\":{\"architecture\":\"arch2\",\"os\":\"os2\"}}]}", imageIndex); Assert.Equal(SchemaTypes.OciImageIndexV1, mediaType); } @@ -146,6 +146,6 @@ public void GenerateOciImageIndex() public void GenerateImageIndexWithAnnotations() { string imageIndex = ImageIndexGenerator.GenerateImageIndexWithAnnotations("mediaType", "sha256:digest", 3, "repository", ["1.0", "2.0"]); - Assert.Equal("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.index.v1\\u002Bjson\",\"manifests\":[{\"mediaType\":\"mediaType\",\"size\":3,\"digest\":\"sha256:digest\",\"platform\":{},\"annotations\":{\"io.containerd.image.name\":\"repository:1.0\",\"org.opencontainers.image.ref.name\":\"1.0\"}},{\"mediaType\":\"mediaType\",\"size\":3,\"digest\":\"sha256:digest\",\"platform\":{},\"annotations\":{\"io.containerd.image.name\":\"repository:2.0\",\"org.opencontainers.image.ref.name\":\"2.0\"}}]}", imageIndex); + Assert.Equal("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.index.v1+json\",\"manifests\":[{\"mediaType\":\"mediaType\",\"size\":3,\"digest\":\"sha256:digest\",\"platform\":{},\"annotations\":{\"io.containerd.image.name\":\"docker.io/library/repository:1.0\",\"org.opencontainers.image.ref.name\":\"1.0\"}},{\"mediaType\":\"mediaType\",\"size\":3,\"digest\":\"sha256:digest\",\"platform\":{},\"annotations\":{\"io.containerd.image.name\":\"docker.io/library/repository:2.0\",\"org.opencontainers.image.ref.name\":\"2.0\"}}]}", imageIndex); } } From adaffdd48928a927d3cf8cb2bdb7a381cf34c67b Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 5 Feb 2025 21:36:58 +0100 Subject: [PATCH 40/74] small fix --- .../Microsoft.NET.Build.Containers/ImageIndexGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs index d2bc53a5900d..ce6d91c1edbf 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs @@ -21,7 +21,7 @@ internal static (string, string) GenerateImageIndex(BuiltImage[] images) { if (images.Length == 0) { - throw new ArgumentException(string.Format(Strings.ImagesEmpty)); + throw new ArgumentException(Strings.ImagesEmpty); } string manifestMediaType = images[0].ManifestMediaType; @@ -49,9 +49,9 @@ internal static string GenerateImageIndex(BuiltImage[] images, string manifestMe { if (images.Length == 0) { - throw new ArgumentException(string.Format(Strings.ImagesEmpty)); + throw new ArgumentException(Strings.ImagesEmpty); } - + // Here we are using ManifestListV2 struct, but we could use ImageIndexV1 struct as well. // We are filling the same fields, so we can use the same struct. var manifests = new PlatformSpecificManifest[images.Length]; From c6c0826a8a755d86e825f5336efdcd32d7fda3a4 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 6 Feb 2025 05:02:16 +0000 Subject: [PATCH 41/74] Update dependencies from https://github.com/dotnet/templating build 20250205.2 Microsoft.SourceBuild.Intermediate.templating , Microsoft.TemplateEngine.Abstractions , Microsoft.TemplateEngine.Mocks From Version 8.0.406-servicing.25066.12 -> To Version 8.0.407-servicing.25105.2 --- NuGet.config | 2 +- eng/Version.Details.xml | 12 ++++++------ eng/Versions.props | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/NuGet.config b/NuGet.config index 71e8d20ed936..d397f5b4bfba 100644 --- a/NuGet.config +++ b/NuGet.config @@ -37,7 +37,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index cbbf1c10d46e..7fb7e371f2b5 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,17 +1,17 @@ - + https://github.com/dotnet/templating - 40256cc3ba6c47902104c959770822e91642f4a7 + 43dbf476bd7c34c753d5cb5520c09e56947b49be - + https://github.com/dotnet/templating - 40256cc3ba6c47902104c959770822e91642f4a7 + 43dbf476bd7c34c753d5cb5520c09e56947b49be - + https://github.com/dotnet/templating - 40256cc3ba6c47902104c959770822e91642f4a7 + 43dbf476bd7c34c753d5cb5520c09e56947b49be diff --git a/eng/Versions.props b/eng/Versions.props index 757009a7cbc1..87334e56b414 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -141,13 +141,13 @@ - 8.0.406 + 8.0.407 $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) - 8.0.406-servicing.25066.12 + 8.0.407-servicing.25105.2 $(MicrosoftTemplateEngineMocksPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineMocksPackageVersion) From 3285270e2870a1a0025b9530bb669abb6ba74a9e Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Thu, 6 Feb 2025 15:47:25 +0100 Subject: [PATCH 42/74] make BuiltImage and MultiArchImage readonly structs --- src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs | 2 +- src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs index bd31ccc8cbf5..fd14bce11c58 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs @@ -6,7 +6,7 @@ namespace Microsoft.NET.Build.Containers; /// /// Represents constructed image ready for further processing. /// -internal sealed class BuiltImage +internal readonly struct BuiltImage { /// /// Gets image configuration in JSON format. diff --git a/src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs b/src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs index 2e671b186d9e..b24fbf65e87f 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs @@ -6,7 +6,7 @@ namespace Microsoft.NET.Build.Containers; /// /// Represents constructed image ready for further processing. /// -internal sealed class MultiArchImage +internal readonly struct MultiArchImage { internal required string ImageIndex { get; init; } From 9cea231503a99fddc5eacacca09ec102d0260690 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Thu, 6 Feb 2025 21:00:16 +0100 Subject: [PATCH 43/74] if ArchiveOutputPath does't have file extension then treat it as directory; add ArchiveFileRegistryTests --- .../LocalDaemons/ArchiveFileRegistry.cs | 12 ++- .../ArchiveFileRegistryTests.cs | 73 +++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs index c23e22d8b9aa..86c170fcb1f9 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs @@ -14,14 +14,22 @@ public ArchiveFileRegistry(string archiveOutputPath) ArchiveOutputPath = archiveOutputPath; } - private async Task LoadAsync(T image, SourceImageReference sourceReference, + internal async Task LoadAsync(T image, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken, Func writeStreamFunc) { var fullPath = Path.GetFullPath(ArchiveOutputPath); + var directorySeparatorChar = Path.DirectorySeparatorChar; + + // if doesn't end with a file extension, assume it's a directory + if (!fullPath.Split(directorySeparatorChar).Last().Contains('.')) + { + fullPath += Path.DirectorySeparatorChar; + } + // pointing to a directory? -> append default name - if (Directory.Exists(fullPath) || ArchiveOutputPath.EndsWith("/") || ArchiveOutputPath.EndsWith("\\")) + if (fullPath.EndsWith(directorySeparatorChar)) { fullPath = Path.Combine(fullPath, destinationReference.Repository + ".tar.gz"); } diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs new file mode 100644 index 000000000000..e5bfc54711d0 --- /dev/null +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.NET.Build.Containers.LocalDaemons; + +namespace Microsoft.NET.Build.Containers.IntegrationTests; + +public class ArchiveFileRegistryTests +{ + [Fact] + public async Task ArchiveOutputPathIsExistingDirectory_CreatesFileWithRepositoryNameAndTarGz() + { + string archiveOutputPath = TestSettings.TestArtifactsDirectory; + string expectedCreatedFilePath = Path.Combine(TestSettings.TestArtifactsDirectory, "repository.tar.gz"); + + await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false); + + Assert.True(File.Exists(expectedCreatedFilePath)); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ArchiveOutputPathIsNonExistingDirectory_CreatesDirectoryAndFileWithRepositoryNameAndTarGz(bool includeDirectorySeperatorAtTheEnd) + { + string archiveOutputPath = Path.Combine( + TestSettings.TestArtifactsDirectory, + "nonexisting" + (includeDirectorySeperatorAtTheEnd ? Path.DirectorySeparatorChar : "")); + string expectedCreatedFilePath = Path.Combine(archiveOutputPath, "repository.tar.gz"); + + await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false); + + Assert.True(File.Exists(expectedCreatedFilePath)); + } + + [Fact] + public async Task ArchiveOutputPathIsCustomFileNameInExistingDirectory_CreatesFileWithThatName() + { + string archiveOutputPath = Path.Combine(TestSettings.TestArtifactsDirectory, "custom-name.withextension"); + string expectedCreatedFilePath = archiveOutputPath; + + await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false); + + Assert.True(File.Exists(expectedCreatedFilePath)); + } + + [Fact] + public async Task ArchiveOutputPathIsCustomFileNameInNonExistingDirectory_CreatesDirectoryAndFileWithThatName() + { + string archiveOutputPath = Path.Combine(TestSettings.TestArtifactsDirectory, $"nonexisting-directory{Path.AltDirectorySeparatorChar}custom-name.withextension"); + string expectedCreatedFilePath = archiveOutputPath; + + await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false); + + Assert.True(File.Exists(expectedCreatedFilePath)); + } + + private async Task CreateRegistryAndCallLoadAsync(string archiveOutputPath) + { + var registry = new ArchiveFileRegistry(archiveOutputPath); + var destinationImageReference = new DestinationImageReference(registry, "repository", ["tag"]); + + await registry.LoadAsync( + "test image", + new SourceImageReference(), + destinationImageReference, + CancellationToken.None, + async (img, srcRef, destRef, stream, token) => + { + await Task.CompletedTask; + }).ConfigureAwait(false); + } +} \ No newline at end of file From d9985105ecd7f4730bbcc6a921269a17f88ea06a Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Thu, 6 Feb 2025 21:09:17 +0100 Subject: [PATCH 44/74] trim BaseRegistry before checking null or empty --- .../Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs | 2 +- .../Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 65443e816fd6..6382a642cc1b 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -23,7 +23,7 @@ public void Dispose() _cancellationTokenSource.Dispose(); } - private bool IsLocalPull => string.IsNullOrEmpty(BaseRegistry); + private bool IsLocalPull => string.IsNullOrEmpty(BaseRegistry.Trim()); public override bool Execute() { diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index 997ca7b74973..a5e98431d802 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -23,7 +23,7 @@ public sealed partial class CreateNewImage : Microsoft.Build.Utilities.Task, ICa /// public string ToolPath { get; set; } - private bool IsLocalPull => string.IsNullOrEmpty(BaseRegistry); + private bool IsLocalPull => string.IsNullOrEmpty(BaseRegistry.Trim()); public void Cancel() => _cancellationTokenSource.Cancel(); From 5094a413f5ea7e1dc5db83ab73cb495fcb6fb2d7 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Thu, 6 Feb 2025 21:25:40 +0100 Subject: [PATCH 45/74] BaseRegistry shouldn't be required parameter --- .../Tasks/CreateImageIndex.Interface.cs | 1 - .../Tasks/CreateNewImage.Interface.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs index 2751eb5ebbc3..6e64ede53469 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs @@ -11,7 +11,6 @@ partial class CreateImageIndex /// The base registry to pull from. /// Ex: mcr.microsoft.com /// - [Required] public string BaseRegistry { get; set; } /// diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs index 5e9c01de4e45..b23a8482f3cf 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs @@ -20,7 +20,6 @@ partial class CreateNewImage /// The base registry to pull from. /// Ex: mcr.microsoft.com /// - [Required] public string BaseRegistry { get; set; } /// From b9a63cad3ad0b63db2170f1d1e883b8b0eb978e4 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Fri, 7 Feb 2025 05:01:50 +0000 Subject: [PATCH 46/74] Update dependencies from https://github.com/dotnet/templating build 20250205.9 Microsoft.SourceBuild.Intermediate.templating , Microsoft.TemplateEngine.Abstractions , Microsoft.TemplateEngine.Mocks From Version 8.0.406-servicing.25066.12 -> To Version 8.0.407-servicing.25105.9 --- NuGet.config | 11 +---------- eng/Version.Details.xml | 10 +++++----- eng/Versions.props | 2 +- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/NuGet.config b/NuGet.config index 737ba553a018..4a316fe10e42 100644 --- a/NuGet.config +++ b/NuGet.config @@ -37,16 +37,7 @@ - - - - - - - - - - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ce2f1aafa3f4..ed9f2abd0c35 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -3,15 +3,15 @@ https://github.com/dotnet/templating - 43dbf476bd7c34c753d5cb5520c09e56947b49be + 5d99405601e2672cd496087efc51611f1fc0d860 - + https://github.com/dotnet/templating - 43dbf476bd7c34c753d5cb5520c09e56947b49be + 5d99405601e2672cd496087efc51611f1fc0d860 - + https://github.com/dotnet/templating - 43dbf476bd7c34c753d5cb5520c09e56947b49be + 5d99405601e2672cd496087efc51611f1fc0d860 diff --git a/eng/Versions.props b/eng/Versions.props index 5da129246917..2ee109077d54 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -147,7 +147,7 @@ $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) - 8.0.407-servicing.25105.2 + 8.0.407-servicing.25105.9 $(MicrosoftTemplateEngineMocksPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineMocksPackageVersion) From e9f4af3870ee2346e1ec0036a16c5252ed28337b Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sat, 8 Feb 2025 09:59:15 +0100 Subject: [PATCH 47/74] address comments --- .../Microsoft.NET.Build.Containers/DigestUtils.cs | 2 +- .../ImageIndexGenerator.cs | 12 +++++++++++- .../LocalDaemons/ArchiveFileRegistry.cs | 2 +- .../Tasks/CreateImageIndex.cs | 2 +- .../Tasks/CreateNewImage.cs | 2 +- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs b/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs index 4171589c025d..a6f680436c39 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs @@ -21,7 +21,7 @@ internal static string GetShaFromDigest(string digest) { if (!digest.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase)) { - throw new ArgumentException("Invalid digest format. Digest must start with 'sha256:'."); + throw new ArgumentException($"Invalid digest '{digest}'. Digest must start with 'sha256:'."); } return digest.Substring("sha256:".Length); diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs index ce6d91c1edbf..86e8d0a5c0c5 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs @@ -13,7 +13,7 @@ internal static class ImageIndexGenerator /// /// Generates an image index from the given images. /// - /// + /// Images to generate image index from. /// Returns json string of image index and image index mediaType. /// /// @@ -45,6 +45,15 @@ internal static (string, string) GenerateImageIndex(BuiltImage[] images) } } + /// + /// Generates an image index from the given images. + /// + /// Images to generate image index from. + /// Media type of the manifest. + /// Media type of the produced image index. + /// Returns json string of image index and image index mediaType. + /// + /// internal static string GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType) { if (images.Length == 0) @@ -116,6 +125,7 @@ private static string GetJsonStringFromImageIndex(T imageIndex) { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; + // To avoid things like \u002B for '+' especially in media types ("application/vnd.oci.image.manifest.v1\u002Bjson"), we use UnsafeRelaxedJsonEscaping. var escapeOptions = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs index 86c170fcb1f9..d127635ba584 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs @@ -23,7 +23,7 @@ internal async Task LoadAsync(T image, SourceImageReference sourceReference, var directorySeparatorChar = Path.DirectorySeparatorChar; // if doesn't end with a file extension, assume it's a directory - if (!fullPath.Split(directorySeparatorChar).Last().Contains('.')) + if (!Path.HasExtension(fullPath)) { fullPath += Path.DirectorySeparatorChar; } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 6382a642cc1b..918cd60394ac 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -23,7 +23,7 @@ public void Dispose() _cancellationTokenSource.Dispose(); } - private bool IsLocalPull => string.IsNullOrEmpty(BaseRegistry.Trim()); + private bool IsLocalPull => string.IsNullOrWhiteSpace(BaseRegistry); public override bool Execute() { diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index a5e98431d802..6f3dbbdf8530 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -23,7 +23,7 @@ public sealed partial class CreateNewImage : Microsoft.Build.Utilities.Task, ICa /// public string ToolPath { get; set; } - private bool IsLocalPull => string.IsNullOrEmpty(BaseRegistry.Trim()); + private bool IsLocalPull => string.IsNullOrWhiteSpace(BaseRegistry); public void Cancel() => _cancellationTokenSource.Cancel(); From 65837f687913ac2d3f8f8e796bccb42eae04be84 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sat, 8 Feb 2025 10:34:08 +0100 Subject: [PATCH 48/74] refactor ImagePublisher --- .../ImagePublisher.cs | 62 +++++++++---------- .../Tasks/CreateImageIndex.cs | 2 +- .../Tasks/CreateNewImage.cs | 2 +- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs index 8dc164f41f49..c44d7fec09fc 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs @@ -13,7 +13,7 @@ public static async Task PublishImageAsync( SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, - IBuildEngine? BuildEngine, + bool isSafeLog, Telemetry telemetry, CancellationToken cancellationToken) { @@ -27,7 +27,7 @@ await PushToLocalRegistryAsync( sourceImageReference, destinationImageReference, Log, - BuildEngine, + isSafeLog, telemetry, cancellationToken, destinationImageReference.LocalRegistry!.LoadAsync).ConfigureAwait(false); @@ -38,7 +38,7 @@ await PushToRemoteRegistryAsync( sourceImageReference, destinationImageReference, Log, - BuildEngine, + isSafeLog, cancellationToken, destinationImageReference.RemoteRegistry!.PushAsync, Strings.ContainerBuilder_ImageUploadedToRegistry).ConfigureAwait(false); @@ -55,7 +55,7 @@ public static async Task PublishImageAsync( SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, - IBuildEngine? BuildEngine, + bool isSafeLog, Telemetry telemetry, CancellationToken cancellationToken) { @@ -69,7 +69,7 @@ await PushToLocalRegistryAsync( sourceImageReference, destinationImageReference, Log, - BuildEngine, + isSafeLog, telemetry, cancellationToken, destinationImageReference.LocalRegistry!.LoadAsync).ConfigureAwait(false); @@ -80,7 +80,7 @@ await PushToRemoteRegistryAsync( sourceImageReference, destinationImageReference, Log, - BuildEngine, + isSafeLog, cancellationToken, destinationImageReference.RemoteRegistry!.PushManifestListAsync, Strings.ImageIndexUploadedToRegistry).ConfigureAwait(false); @@ -97,7 +97,7 @@ private static async Task PushToLocalRegistryAsync( SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, - IBuildEngine? BuildEngine, + bool isSafeLog, Telemetry telemetry, CancellationToken cancellationToken, Func loadFunc) @@ -112,31 +112,34 @@ private static async Task PushToLocalRegistryAsync( try { await loadFunc(image, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); - if (BuildEngine != null) + if (isSafeLog) { Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); } } - catch (ContainerHttpException e) + catch (ContainerHttpException e) when (isSafeLog) { - if (BuildEngine != null) - { - Log.LogErrorFromException(e, true); - } + Log.LogErrorFromException(e, true); } catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle) { telemetry.LogLocalLoadError(); - Log.LogErrorFromException(dle, showStackTrace: false); + if (isSafeLog) + { + Log.LogErrorFromException(dle, showStackTrace: false); + } } - catch (ArgumentException argEx) + catch (ArgumentException argEx) when (isSafeLog) { Log.LogErrorFromException(argEx, showStackTrace: false); } catch (DockerLoadException dle) { telemetry.LogLocalLoadError(); - Log.LogErrorFromException(dle, showStackTrace: false); + if (isSafeLog) + { + Log.LogErrorFromException(dle, showStackTrace: false); + } } } @@ -145,7 +148,7 @@ private static async Task PushToRemoteRegistryAsync( SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, - IBuildEngine? BuildEngine, + bool isSafeLog, CancellationToken cancellationToken, Func pushFunc, string successMessage) @@ -157,32 +160,23 @@ await pushFunc( sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); - if (BuildEngine != null) + if (isSafeLog) { Log.LogMessage(MessageImportance.High, successMessage, destinationImageReference, destinationImageReference.RemoteRegistry!.RegistryName); } } - catch (UnableToAccessRepositoryException) + catch (UnableToAccessRepositoryException) when (isSafeLog) { - if (BuildEngine != null) - { - Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); - } + Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); } - catch (ContainerHttpException e) + catch (ContainerHttpException e) when (isSafeLog) { - if (BuildEngine != null) - { - Log.LogErrorFromException(e, true); - } + Log.LogErrorFromException(e, true); } - catch (Exception e) + catch (Exception e) when (isSafeLog) { - if (BuildEngine != null) - { - Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); - Log.LogMessage(MessageImportance.Low, "Details: {0}", e); - } + Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); + Log.LogMessage(MessageImportance.Low, "Details: {0}", e); } } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 918cd60394ac..64ca1939bdab 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -83,7 +83,7 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log); - await ImagePublisher.PublishImageAsync(multiArchImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) + await ImagePublisher.PublishImageAsync(multiArchImage, sourceImageReference, destinationImageReference, Log, BuildEngine != null, telemetry, cancellationToken) .ConfigureAwait(false); return !Log.HasLoggedErrors; diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index 6f3dbbdf8530..c6e1fba6ce43 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -183,7 +183,7 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) if (!SkipPublishing) { - await ImagePublisher.PublishImageAsync(builtImage, sourceImageReference, destinationImageReference, Log, BuildEngine, telemetry, cancellationToken) + await ImagePublisher.PublishImageAsync(builtImage, sourceImageReference, destinationImageReference, Log, BuildEngine != null, telemetry, cancellationToken) .ConfigureAwait(false); } From 960bb7d19ddfd923270d8f9e17066330c8c098d6 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sat, 8 Feb 2025 10:57:41 +0100 Subject: [PATCH 49/74] remove BuildEngine null check before logging --- .../ImagePublisher.cs | 38 +++++-------------- .../Tasks/CreateImageIndex.cs | 2 +- .../Tasks/CreateNewImage.cs | 13 +++---- 3 files changed, 15 insertions(+), 38 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs index c44d7fec09fc..1a16cbbad500 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs @@ -13,7 +13,6 @@ public static async Task PublishImageAsync( SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, - bool isSafeLog, Telemetry telemetry, CancellationToken cancellationToken) { @@ -27,7 +26,6 @@ await PushToLocalRegistryAsync( sourceImageReference, destinationImageReference, Log, - isSafeLog, telemetry, cancellationToken, destinationImageReference.LocalRegistry!.LoadAsync).ConfigureAwait(false); @@ -38,7 +36,6 @@ await PushToRemoteRegistryAsync( sourceImageReference, destinationImageReference, Log, - isSafeLog, cancellationToken, destinationImageReference.RemoteRegistry!.PushAsync, Strings.ContainerBuilder_ImageUploadedToRegistry).ConfigureAwait(false); @@ -55,7 +52,6 @@ public static async Task PublishImageAsync( SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, - bool isSafeLog, Telemetry telemetry, CancellationToken cancellationToken) { @@ -69,7 +65,6 @@ await PushToLocalRegistryAsync( sourceImageReference, destinationImageReference, Log, - isSafeLog, telemetry, cancellationToken, destinationImageReference.LocalRegistry!.LoadAsync).ConfigureAwait(false); @@ -80,7 +75,6 @@ await PushToRemoteRegistryAsync( sourceImageReference, destinationImageReference, Log, - isSafeLog, cancellationToken, destinationImageReference.RemoteRegistry!.PushManifestListAsync, Strings.ImageIndexUploadedToRegistry).ConfigureAwait(false); @@ -97,7 +91,6 @@ private static async Task PushToLocalRegistryAsync( SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, - bool isSafeLog, Telemetry telemetry, CancellationToken cancellationToken, Func loadFunc) @@ -112,34 +105,25 @@ private static async Task PushToLocalRegistryAsync( try { await loadFunc(image, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); - if (isSafeLog) - { - Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); - } + Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry); } - catch (ContainerHttpException e) when (isSafeLog) + catch (ContainerHttpException e) { Log.LogErrorFromException(e, true); } catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle) { telemetry.LogLocalLoadError(); - if (isSafeLog) - { - Log.LogErrorFromException(dle, showStackTrace: false); - } + Log.LogErrorFromException(dle, showStackTrace: false); } - catch (ArgumentException argEx) when (isSafeLog) + catch (ArgumentException argEx) { Log.LogErrorFromException(argEx, showStackTrace: false); } catch (DockerLoadException dle) { telemetry.LogLocalLoadError(); - if (isSafeLog) - { - Log.LogErrorFromException(dle, showStackTrace: false); - } + Log.LogErrorFromException(dle, showStackTrace: false); } } @@ -148,7 +132,6 @@ private static async Task PushToRemoteRegistryAsync( SourceImageReference sourceImageReference, DestinationImageReference destinationImageReference, Microsoft.Build.Utilities.TaskLoggingHelper Log, - bool isSafeLog, CancellationToken cancellationToken, Func pushFunc, string successMessage) @@ -160,20 +143,17 @@ await pushFunc( sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false); - if (isSafeLog) - { - Log.LogMessage(MessageImportance.High, successMessage, destinationImageReference, destinationImageReference.RemoteRegistry!.RegistryName); - } + Log.LogMessage(MessageImportance.High, successMessage, destinationImageReference, destinationImageReference.RemoteRegistry!.RegistryName); } - catch (UnableToAccessRepositoryException) when (isSafeLog) + catch (UnableToAccessRepositoryException) { Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName); } - catch (ContainerHttpException e) when (isSafeLog) + catch (ContainerHttpException e) { Log.LogErrorFromException(e, true); } - catch (Exception e) when (isSafeLog) + catch (Exception e) { Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message); Log.LogMessage(MessageImportance.Low, "Details: {0}", e); diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 64ca1939bdab..f168e157ddc7 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -83,7 +83,7 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log); - await ImagePublisher.PublishImageAsync(multiArchImage, sourceImageReference, destinationImageReference, Log, BuildEngine != null, telemetry, cancellationToken) + await ImagePublisher.PublishImageAsync(multiArchImage, sourceImageReference, destinationImageReference, Log, telemetry, cancellationToken) .ConfigureAwait(false); return !Log.HasLoggedErrors; diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index c6e1fba6ce43..df46fd354709 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -120,13 +120,10 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) return !Log.HasLoggedErrors; } - if (BuildEngine != null) - { - (string message, object[] parameters) = SkipPublishing ? - ( Strings.ContainerBuilder_StartBuildingImageForRid, new object[] { Repository, ContainerRuntimeIdentifier, sourceImageReference }) : - ( Strings.ContainerBuilder_StartBuildingImage, new object[] { Repository, String.Join(",", ImageTags), sourceImageReference }); - Log.LogMessage(MessageImportance.High, message, parameters); - } + (string message, object[] parameters) = SkipPublishing ? + ( Strings.ContainerBuilder_StartBuildingImageForRid, new object[] { Repository, ContainerRuntimeIdentifier, sourceImageReference }) : + ( Strings.ContainerBuilder_StartBuildingImage, new object[] { Repository, String.Join(",", ImageTags), sourceImageReference }); + Log.LogMessage(MessageImportance.High, message, parameters); Layer newLayer = Layer.FromDirectory(PublishDirectory, WorkingDirectory, imageBuilder.IsWindows, imageBuilder.ManifestMediaType); imageBuilder.AddLayer(newLayer); @@ -183,7 +180,7 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) if (!SkipPublishing) { - await ImagePublisher.PublishImageAsync(builtImage, sourceImageReference, destinationImageReference, Log, BuildEngine != null, telemetry, cancellationToken) + await ImagePublisher.PublishImageAsync(builtImage, sourceImageReference, destinationImageReference, Log, telemetry, cancellationToken) .ConfigureAwait(false); } From 63847bab1e80d361f1e8fe28cd5cd2ecc3258a66 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Sat, 8 Feb 2025 12:15:53 +0100 Subject: [PATCH 50/74] register task resources for CreateImageIndex --- .../Tasks/CreateImageIndex.Interface.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs index 6e64ede53469..bb69651add0e 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.Framework; +using Microsoft.NET.Build.Containers.Resources; namespace Microsoft.NET.Build.Containers.Tasks; @@ -85,5 +86,7 @@ public CreateImageIndex() ImageTags = Array.Empty(); GeneratedArchiveOutputPath = string.Empty; GeneratedImageIndex = string.Empty; + + TaskResources = Resource.Manager; } } \ No newline at end of file From 72b4bf04d2a987b1c6c7fdbc331b36e53ab2ada7 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 10 Feb 2025 09:17:24 +0100 Subject: [PATCH 51/74] optimize ParseImages in CreateImageIndex --- .../BuiltImage.cs | 4 +-- .../LocalDaemons/DockerCli.cs | 4 +-- .../Registry/Registry.cs | 2 +- .../Tasks/CreateImageIndex.cs | 30 ++++++++++++------- .../ImageIndexGeneratorTests.cs | 14 --------- 5 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs index fd14bce11c58..91f47193ce66 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs @@ -16,12 +16,12 @@ internal readonly struct BuiltImage /// /// Gets image digest. /// - internal required string ImageDigest { get; init; } + internal string? ImageDigest { get; init; } /// /// Gets image SHA. /// - internal required string ImageSha { get; init; } + internal string? ImageSha { get; init; } /// /// Gets image manifest. diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index d5b8fb2b3c5f..5bdb7f40c8a4 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -311,7 +311,7 @@ private static async Task WriteDockerImageToStreamAsync( await WriteImageLayers(writer, image, sourceReference, d => $"{d.Substring("sha256:".Length)}/layer.tar", cancellationToken, layerTarballPaths) .ConfigureAwait(false); - string configTarballPath = $"{image.ImageSha}.json"; + string configTarballPath = $"{image.ImageSha!}.json"; await WriteImageConfig(writer, image, configTarballPath, cancellationToken) .ConfigureAwait(false); @@ -492,7 +492,7 @@ private static async Task WriteOciImageToBlobs( await WriteImageLayers(writer, image, sourceReference, d => $"{_blobsPath}/{d.Substring("sha256:".Length)}", cancellationToken) .ConfigureAwait(false); - await WriteImageConfig(writer, image, $"{_blobsPath}/{image.ImageSha}", cancellationToken) + await WriteImageConfig(writer, image, $"{_blobsPath}/{image.ImageSha!}", cancellationToken) .ConfigureAwait(false); await WriteManifestForOciImage(writer, image, cancellationToken) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs index 87671575f34a..1b01ebd5bdc4 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs @@ -595,7 +595,7 @@ private async Task PushAsync(BuiltImage builtImage, SourceImageReference source, cancellationToken.ThrowIfCancellationRequested(); using (MemoryStream stringStream = new MemoryStream(Encoding.UTF8.GetBytes(builtImage.Config))) { - var configDigest = builtImage.ImageDigest; + var configDigest = builtImage.ImageDigest!; _logger.LogInformation(Strings.Registry_ConfigUploadStarted, configDigest); await UploadBlobAsync(destination.Repository, configDigest, stringStream, cancellationToken).ConfigureAwait(false); _logger.LogInformation(Strings.Registry_ConfigUploaded); diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index f168e157ddc7..25658f612e06 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -108,19 +108,27 @@ private BuiltImage[] ParseImages(DestinationImageReferenceKind destinationKind) break; } - var manifestV2 = JsonSerializer.Deserialize(manifest); - if (manifestV2 == null) - { - Log.LogError(Strings.InvalidImageManifest); - break; - } - - string imageDigest = manifestV2.Config.digest; - string imageSha = DigestUtils.GetShaFromDigest(imageDigest); - // We don't need layers for remote registry, as the individual images should be pushed already - var layers = destinationKind == DestinationImageReferenceKind.RemoteRegistry ? null : manifestV2.Layers; (string architecture, string os) = GetArchitectureAndOsFromConfig(config); + // We don't need ImageDigest, ImageSha, Layers for remote registry, as the individual images should be pushed already + string? imageDigest = null; + string? imageSha = null; + List? layers = null; + + if (destinationKind == DestinationImageReferenceKind.LocalRegistry) + { + var manifestV2 = JsonSerializer.Deserialize(manifest); + if (manifestV2 == null) + { + Log.LogError(Strings.InvalidImageManifest); + break; + } + + imageDigest = manifestV2.Config.digest; + imageSha = DigestUtils.GetShaFromDigest(imageDigest); + layers = manifestV2.Layers; + } + images[i] = new BuiltImage() { Config = config, diff --git a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs index 6b87a502d6a6..1acc846dcdcd 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageIndexGeneratorTests.cs @@ -31,8 +31,6 @@ public void UnsupportedMediaTypeThrows() new BuiltImage { Config = "", - ImageDigest = "", - ImageSha = "", Manifest = "", ManifestDigest = "", ManifestMediaType = "unsupported" @@ -53,8 +51,6 @@ public void ImagesWithMixedMediaTypes(string supportedMediaType) new BuiltImage { Config = "", - ImageDigest = "", - ImageSha = "", Manifest = "", ManifestDigest = "", ManifestMediaType = supportedMediaType, @@ -62,8 +58,6 @@ public void ImagesWithMixedMediaTypes(string supportedMediaType) new BuiltImage { Config = "", - ImageDigest = "", - ImageSha = "", Manifest = "", ManifestDigest = "", ManifestMediaType = "anotherMediaType" @@ -82,8 +76,6 @@ public void GenerateDockerManifestList() new BuiltImage { Config = "", - ImageDigest = "", - ImageSha = "", Manifest = "123", ManifestDigest = "sha256:digest1", ManifestMediaType = SchemaTypes.DockerManifestV2, @@ -93,8 +85,6 @@ public void GenerateDockerManifestList() new BuiltImage { Config = "", - ImageDigest = "", - ImageSha = "", Manifest = "123", ManifestDigest = "sha256:digest2", ManifestMediaType = SchemaTypes.DockerManifestV2, @@ -116,8 +106,6 @@ public void GenerateOciImageIndex() new BuiltImage { Config = "", - ImageDigest = "", - ImageSha = "", Manifest = "123", ManifestDigest = "sha256:digest1", ManifestMediaType = SchemaTypes.OciManifestV1, @@ -127,8 +115,6 @@ public void GenerateOciImageIndex() new BuiltImage { Config = "", - ImageDigest = "", - ImageSha = "", Manifest = "123", ManifestDigest = "sha256:digest2", ManifestMediaType = SchemaTypes.OciManifestV1, From 0f5e6c80c92e78d028be4a8a172f84d9a4559d2f Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 10 Feb 2025 13:07:00 +0100 Subject: [PATCH 52/74] fix setting annotation image name prefix: set to 'docker.io/' when repostory contains /, and 'docker.io/library/' when it doesn't; add tests --- .../ImageIndexGenerator.cs | 4 +++- .../DockerSupportsArchFact.cs | 19 ++++++++++++++++ .../EndToEndTests.cs | 22 ++++++++++--------- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs index 86e8d0a5c0c5..64a00b4236cb 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs @@ -92,6 +92,8 @@ internal static string GenerateImageIndex(BuiltImage[] images, string manifestMe internal static string GenerateImageIndexWithAnnotations(string manifestMediaType, string manifestDigest, long manifestSize, string repository, string[] tags) { + string containerdImageNamePrefix = repository.Contains('/') ? "docker.io/" : "docker.io/library/"; + var manifests = new PlatformSpecificOciManifest[tags.Length]; for (int i = 0; i < tags.Length; i++) { @@ -103,7 +105,7 @@ internal static string GenerateImageIndexWithAnnotations(string manifestMediaTyp digest = manifestDigest, annotations = new Dictionary { - { "io.containerd.image.name", $"docker.io/library/{repository}:{tag}" }, + { "io.containerd.image.name", $"{containerdImageNamePrefix}{repository}:{tag}" }, { "org.opencontainers.image.ref.name", tag } } }; diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs index ae0e76f3c153..e7744d8882d4 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs @@ -21,3 +21,22 @@ public DockerIsAvailableAndSupportsArchFactAttribute(string arch, bool checkCont } } } + +public class DockerIsAvailableAndSupportsArchTheoryAttribute : TheoryAttribute +{ + public DockerIsAvailableAndSupportsArchTheoryAttribute(string arch, bool checkContainerdStoreAvailability = false) + { + if (!DockerSupportsArchHelper.DaemonIsAvailable) + { + base.Skip = "Skipping test because Docker is not available on this host."; + } + else if (checkContainerdStoreAvailability && !DockerSupportsArchHelper.IsContainerdStoreEnabledForDocker) + { + base.Skip = "Skipping test because Docker daemon is not using containerd as the storage driver."; + } + else if (!DockerSupportsArchHelper.DaemonSupportsArch(arch)) + { + base.Skip = $"Skipping test because Docker daemon does not support {arch}."; + } + } +} diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index ccef1a3b41c4..836022f30f3b 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -721,10 +721,11 @@ public void EndToEnd_SingleArch_NoRid() processResultX64.Should().Pass().And.HaveStdOut("Hello, World!"); } - [DockerIsAvailableAndSupportsArchFact("linux/arm64", checkContainerdStoreAvailability: true)] - public void EndToEndMultiArch_LocalRegistry() + [InlineData("endtoendmultiarch-localregisty")] + [InlineData("myteam/endtoendmultiarch-localregisty")] + [DockerIsAvailableAndSupportsArchTheory("linux/arm64", checkContainerdStoreAvailability: true)] + public void EndToEndMultiArch_LocalRegistry(string imageName) { - string imageName = NewImageName(); string tag = "1.0"; string image = $"{imageName}:{tag}"; @@ -759,7 +760,7 @@ public void EndToEndMultiArch_LocalRegistry() "--platform", "linux/amd64", "--name", - $"test-container-{imageName}-x64", + $"test-container-{imageName.Replace('/', '-')}-x64", image) .Execute(); processResultX64.Should().Pass().And.HaveStdOut("Hello, World!"); @@ -770,7 +771,7 @@ public void EndToEndMultiArch_LocalRegistry() "--platform", "linux/arm64", "--name", - $"test-container-{imageName}-arm64", + $"test-container-{imageName.Replace('/', '-')}-arm64", image) .Execute(); processResultArm64.Should().Pass().And.HaveStdOut("Hello, World!"); @@ -904,10 +905,11 @@ private DirectoryInfo CreateNewProject(string template, [CallerMemberName] strin private string GetPublishArtifactsPath(string projectDir, string rid, string configuration = "Debug") => Path.Combine(projectDir, "bin", configuration, ToolsetInfo.CurrentTargetFramework, rid, "publish"); - [DockerIsAvailableAndSupportsArchFact("linux/arm64", checkContainerdStoreAvailability: true)] - public void EndToEndMultiArch_ArchivePublishing() + [InlineData("endtoendmultiarch-archivepublishing")] + [InlineData("myteam/endtoendmultiarch-archivepublishing")] + [DockerIsAvailableAndSupportsArchTheory("linux/arm64", checkContainerdStoreAvailability: true)] + public void EndToEndMultiArch_ArchivePublishing(string imageName) { - string imageName = NewImageName(); string tag = "1.0"; string image = $"{imageName}:{tag}"; string archiveOutput = TestSettings.TestArtifactsDirectory; @@ -953,7 +955,7 @@ public void EndToEndMultiArch_ArchivePublishing() "--platform", "linux/amd64", "--name", - $"test-container-{imageName}-x64", + $"test-container-{imageName.Replace('/', '-')}-x64", image) .Execute(); processResultX64.Should().Pass().And.HaveStdOut("Hello, World!"); @@ -964,7 +966,7 @@ public void EndToEndMultiArch_ArchivePublishing() "--platform", "linux/arm64", "--name", - $"test-container-{imageName}-arm64", + $"test-container-{imageName.Replace('/', '-')}-arm64", image) .Execute(); processResultArm64.Should().Pass().And.HaveStdOut("Hello, World!"); From f09c5c41b50cff4cadb36b73912687ab0fa44bb7 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 10 Feb 2025 13:44:33 +0100 Subject: [PATCH 53/74] move DockerIsAvailableAndSupportsArchTheoryAttribute to its own file; rename DockerIsAvailableAndSupportsArchFactAttribute file --- ...> DockerIsAvailableAndSupportsArchFact.cs} | 19 --------------- .../DockerIsAvailableAndSupportsArchTheory.cs | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+), 19 deletions(-) rename src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/{DockerSupportsArchFact.cs => DockerIsAvailableAndSupportsArchFact.cs} (55%) create mode 100644 src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchFact.cs similarity index 55% rename from src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs rename to src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchFact.cs index e7744d8882d4..ae0e76f3c153 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchFact.cs @@ -21,22 +21,3 @@ public DockerIsAvailableAndSupportsArchFactAttribute(string arch, bool checkCont } } } - -public class DockerIsAvailableAndSupportsArchTheoryAttribute : TheoryAttribute -{ - public DockerIsAvailableAndSupportsArchTheoryAttribute(string arch, bool checkContainerdStoreAvailability = false) - { - if (!DockerSupportsArchHelper.DaemonIsAvailable) - { - base.Skip = "Skipping test because Docker is not available on this host."; - } - else if (checkContainerdStoreAvailability && !DockerSupportsArchHelper.IsContainerdStoreEnabledForDocker) - { - base.Skip = "Skipping test because Docker daemon is not using containerd as the storage driver."; - } - else if (!DockerSupportsArchHelper.DaemonSupportsArch(arch)) - { - base.Skip = $"Skipping test because Docker daemon does not support {arch}."; - } - } -} diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs new file mode 100644 index 000000000000..382e57604fab --- /dev/null +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.NET.Build.Containers.IntegrationTests; + +public class DockerIsAvailableAndSupportsArchTheoryAttribute : TheoryAttribute +{ + public DockerIsAvailableAndSupportsArchTheoryAttribute(string arch, bool checkContainerdStoreAvailability = false) + { + if (!DockerSupportsArchHelper.DaemonIsAvailable) + { + base.Skip = "Skipping test because Docker is not available on this host."; + } + else if (checkContainerdStoreAvailability && !DockerSupportsArchHelper.IsContainerdStoreEnabledForDocker) + { + base.Skip = "Skipping test because Docker daemon is not using containerd as the storage driver."; + } + else if (!DockerSupportsArchHelper.DaemonSupportsArch(arch)) + { + base.Skip = $"Skipping test because Docker daemon does not support {arch}."; + } + } +} \ No newline at end of file From bca34b9b6edd70f26b2cf7f5c38f63bf5e4a099c Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Tue, 11 Feb 2025 17:48:15 -0600 Subject: [PATCH 54/74] [release/8.0.4xx] Manual Backport of #40611 to 8.0.4xx - Allow users to set a specific ContainerImageFormat to force OCI support (#46140) --- .../ContainerBuilder.cs | 16 ++ .../ImageBuilder.cs | 17 +- .../PublicAPI/net472/PublicAPI.Unshipped.txt | 2 + .../PublicAPI/net8.0/PublicAPI.Unshipped.txt | 2 + .../Registry/SchemaTypes.cs | 3 + .../Resources/Strings.Designer.cs | 199 ++++++++++-------- .../Resources/Strings.resx | 4 + .../Resources/xlf/Strings.cs.xlf | 5 + .../Resources/xlf/Strings.de.xlf | 5 + .../Resources/xlf/Strings.es.xlf | 5 + .../Resources/xlf/Strings.fr.xlf | 5 + .../Resources/xlf/Strings.it.xlf | 5 + .../Resources/xlf/Strings.ja.xlf | 5 + .../Resources/xlf/Strings.ko.xlf | 5 + .../Resources/xlf/Strings.pl.xlf | 5 + .../Resources/xlf/Strings.pt-BR.xlf | 5 + .../Resources/xlf/Strings.ru.xlf | 5 + .../Resources/xlf/Strings.tr.xlf | 5 + .../Resources/xlf/Strings.zh-Hans.xlf | 5 + .../Resources/xlf/Strings.zh-Hant.xlf | 5 + .../Tasks/CreateNewImage.Interface.cs | 4 + .../Tasks/CreateNewImage.cs | 18 ++ .../Tasks/CreateNewImageToolTask.cs | 4 + .../containerize/ContainerizeCommand.cs | 8 + .../Microsoft.NET.Build.Containers.targets | 1 + .../CreateNewImageTests.cs | 66 ++++++ 26 files changed, 313 insertions(+), 96 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs b/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs index 48867a93de6b..665869e4a7a0 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs @@ -6,6 +6,12 @@ namespace Microsoft.NET.Build.Containers; +internal enum KnownImageFormats +{ + OCI, + Docker +} + internal static class ContainerBuilder { internal static async Task ContainerizeAsync( @@ -33,6 +39,7 @@ internal static async Task ContainerizeAsync( string? archiveOutputPath, bool generateLabels, bool generateDigestLabel, + KnownImageFormats? imageFormat, ILoggerFactory loggerFactory, CancellationToken cancellationToken) { @@ -98,6 +105,15 @@ internal static async Task ContainerizeAsync( logger.LogInformation(Strings.ContainerBuilder_StartBuildingImage, imageName, string.Join(",", imageName), sourceImageReference); cancellationToken.ThrowIfCancellationRequested(); + // forcibly change the media type if required + imageBuilder.ManifestMediaType = imageFormat switch + { + null => imageBuilder.ManifestMediaType, + KnownImageFormats.Docker => SchemaTypes.DockerManifestV2, + KnownImageFormats.OCI => SchemaTypes.OciManifestV1, + _ => imageBuilder.ManifestMediaType // should be impossible unless we add to the enum + }; + Layer newLayer = Layer.FromDirectory(publishDirectory.FullName, workingDir, imageBuilder.IsWindows, imageBuilder.ManifestMediaType); imageBuilder.AddLayer(newLayer); imageBuilder.SetWorkingDirectory(workingDir); diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs index 633ca8e98860..9a41e432c6a9 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs @@ -20,7 +20,6 @@ internal sealed class ImageBuilder // the mutable internal manifest that we're building by modifying the base and applying customizations private readonly ManifestV2 _manifest; - private readonly string _manifestMediaType; private readonly ImageConfig _baseImageConfig; private readonly ILogger _logger; @@ -33,15 +32,15 @@ internal sealed class ImageBuilder public ImageConfig BaseImageConfig => _baseImageConfig; /// - /// MediaType of the output manifest. + /// MediaType of the output manifest. By default, this will be the same as the base image manifest. /// - public string ManifestMediaType => _manifestMediaType; // output the same media type as the base image manifest. + public string ManifestMediaType { get; set; } internal ImageBuilder(ManifestV2 manifest, string manifestMediaType, ImageConfig baseImageConfig, ILogger logger) { _baseImageManifest = manifest; _manifest = new ManifestV2() { SchemaVersion = manifest.SchemaVersion, Config = manifest.Config, Layers = new(manifest.Layers), MediaType = manifest.MediaType }; - _manifestMediaType = manifestMediaType; + ManifestMediaType = manifestMediaType; _baseImageConfig = baseImageConfig; _logger = logger; } @@ -71,14 +70,20 @@ internal BuiltImage Build() ManifestConfig newManifestConfig = _manifest.Config with { digest = imageDigest, - size = imageSize + size = imageSize, + mediaType = ManifestMediaType switch + { + SchemaTypes.OciManifestV1 => SchemaTypes.OciImageConfigV1, + SchemaTypes.DockerManifestV2 => SchemaTypes.DockerContainerV1, + _ => SchemaTypes.OciImageConfigV1 // opinion - defaulting to modern here, but really this should never happen + } }; ManifestV2 newManifest = new ManifestV2() { Config = newManifestConfig, SchemaVersion = _manifest.SchemaVersion, - MediaType = _manifest.MediaType, + MediaType = ManifestMediaType, Layers = _manifest.Layers }; diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt index 9e6b0de71dc0..4da41db6b2dc 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -90,6 +90,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.get -> bool Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.set -> void Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.get -> Microsoft.Build.Framework.ITaskItem![]! Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.set -> void +Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageFormat.get -> string? +Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageFormat.set -> void override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ToolName.get -> string! override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateCommandLineCommands() -> string! override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateFullPathToTool() -> string! diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt index 5c07a3871ceb..3370734680b1 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -255,6 +255,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.get -> bool Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.set -> void Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.get -> Microsoft.Build.Framework.ITaskItem![]! Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.set -> void +Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageFormat.get -> string? +Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageFormat.set -> void Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ContainerEnvironmentVariables.get -> Microsoft.Build.Framework.ITaskItem![]! Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ContainerEnvironmentVariables.set -> void diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/SchemaTypes.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/SchemaTypes.cs index 8021f652f72d..f7ae91a19e8b 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Registry/SchemaTypes.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/SchemaTypes.cs @@ -9,8 +9,11 @@ internal class SchemaTypes internal const string DockerContainerV1 = "application/vnd.docker.container.image.v1+json"; internal const string DockerManifestListV2 = "application/vnd.docker.distribution.manifest.list.v2+json"; internal const string DockerManifestV2 = "application/vnd.docker.distribution.manifest.v2+json"; + internal const string OciManifestV1 = "application/vnd.oci.image.manifest.v1+json"; // https://containers.gitbook.io/build-containers-the-hard-way/#registry-format-oci-image-manifest internal const string OciImageIndexV1 = "application/vnd.oci.image.index.v1+json"; + internal const string OciImageConfigV1 = "application/vnd.oci.image.config.v1+json"; + internal const string DockerLayerGzip = "application/vnd.docker.image.rootfs.diff.tar.gzip"; internal const string OciLayerGzipV1 = "application/vnd.oci.image.layer.v1.tar+gzip"; } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs index 59a9f28b0b83..ec4966caaa27 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs @@ -10,8 +10,8 @@ namespace Microsoft.NET.Build.Containers.Resources { using System; - - + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -23,15 +23,15 @@ namespace Microsoft.NET.Build.Containers.Resources { [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Strings { - + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Strings() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// @@ -45,7 +45,7 @@ internal Strings() { return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. @@ -59,7 +59,7 @@ internal Strings() { resourceCulture = value; } } - + /// /// Looks up a localized string similar to CONTAINER0000: Value for unit test {0}. /// @@ -68,7 +68,7 @@ internal static string _Test { return ResourceManager.GetString("_Test", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1002: Request to Amazon Elastic Container Registry failed prematurely. This is often caused when the target repository does not exist in the registry.. /// @@ -77,7 +77,7 @@ internal static string AmazonRegistryFailed { return ResourceManager.GetString("AmazonRegistryFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2008: Both {0} and {1} were provided, but only one or the other is allowed.. /// @@ -86,7 +86,7 @@ internal static string AmbiguousTags { return ResourceManager.GetString("AmbiguousTags", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2025: ContainerAppCommandArgs are provided without specifying a ContainerAppCommand.. /// @@ -95,7 +95,7 @@ internal static string AppCommandArgsSetNoAppCommand { return ResourceManager.GetString("AppCommandArgsSetNoAppCommand", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2026: ContainerAppCommand and ContainerAppCommandArgs must be empty when ContainerAppCommandInstruction is '{0}'.. /// @@ -104,7 +104,7 @@ internal static string AppCommandSetNotUsed { return ResourceManager.GetString("AppCommandSetNotUsed", resourceCulture); } } - + /// /// Looks up a localized string similar to local archive at '{0}'. /// @@ -113,7 +113,7 @@ internal static string ArchiveRegistry_PushInfo { return ResourceManager.GetString("ArchiveRegistry_PushInfo", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2022: The base image has an entrypoint that will be overwritten to start the application. Set ContainerAppCommandInstruction to 'Entrypoint' if this is desired. To preserve the base image entrypoint, set ContainerAppCommandInstruction to 'DefaultArgs'.. /// @@ -122,7 +122,7 @@ internal static string BaseEntrypointOverwritten { return ResourceManager.GetString("BaseEntrypointOverwritten", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2009: Could not parse {0}: {1}. /// @@ -131,7 +131,7 @@ internal static string BaseImageNameParsingFailed { return ResourceManager.GetString("BaseImageNameParsingFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2020: {0} does not specify a registry and will be pulled from Docker Hub. Please prefix the name with the image registry, for example: '{1}/<image>'.. /// @@ -140,7 +140,7 @@ internal static string BaseImageNameRegistryFallback { return ResourceManager.GetString("BaseImageNameRegistryFallback", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2013: {0} had spaces in it, replacing with dashes.. /// @@ -149,7 +149,7 @@ internal static string BaseImageNameWithSpaces { return ResourceManager.GetString("BaseImageNameWithSpaces", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1011: Couldn't find matching base image for {0} that matches RuntimeIdentifier {1}.. /// @@ -158,7 +158,7 @@ internal static string BaseImageNotFound { return ResourceManager.GetString("BaseImageNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1001: Failed to upload blob using {0}; received status code '{1}'.. /// @@ -167,7 +167,7 @@ internal static string BlobUploadFailed { return ResourceManager.GetString("BlobUploadFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to Building image index '{0}' on top of manifests {1}.. /// @@ -176,7 +176,7 @@ internal static string BuildingImageIndex { return ResourceManager.GetString("BuildingImageIndex", resourceCulture); } } - + /// /// Looks up a localized string similar to Pushed image '{0}' to {1}.. /// @@ -185,7 +185,7 @@ internal static string ContainerBuilder_ImageUploadedToLocalDaemon { return ResourceManager.GetString("ContainerBuilder_ImageUploadedToLocalDaemon", resourceCulture); } } - + /// /// Looks up a localized string similar to Pushed image '{0}' to registry '{1}'.. /// @@ -194,7 +194,7 @@ internal static string ContainerBuilder_ImageUploadedToRegistry { return ResourceManager.GetString("ContainerBuilder_ImageUploadedToRegistry", resourceCulture); } } - + /// /// Looks up a localized string similar to Building image '{0}' with tags '{1}' on top of base image '{2}'.. /// @@ -203,7 +203,7 @@ internal static string ContainerBuilder_StartBuildingImage { return ResourceManager.GetString("ContainerBuilder_StartBuildingImage", resourceCulture); } } - + /// /// Looks up a localized string similar to Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.. /// @@ -221,7 +221,7 @@ internal static string ContainerRuntimeProcessCreationFailed { return ResourceManager.GetString("ContainerRuntimeProcessCreationFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1007: Could not deserialize token from JSON.. /// @@ -230,7 +230,7 @@ internal static string CouldntDeserializeJsonToken { return ResourceManager.GetString("CouldntDeserializeJsonToken", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2012: Could not recognize registry '{0}'.. /// @@ -239,7 +239,7 @@ internal static string CouldntRecognizeRegistry { return ResourceManager.GetString("CouldntRecognizeRegistry", resourceCulture); } } - + /// /// Looks up a localized string similar to local registry via '{0}'. /// @@ -248,7 +248,7 @@ internal static string DockerCli_PushInfo { return ResourceManager.GetString("DockerCli_PushInfo", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER3002: Failed to get docker info({0})\n{1}\n{2}. /// @@ -257,7 +257,7 @@ internal static string DockerInfoFailed { return ResourceManager.GetString("DockerInfoFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER3002: Failed to get docker info: {0}. /// @@ -266,7 +266,7 @@ internal static string DockerInfoFailed_Ex { return ResourceManager.GetString("DockerInfoFailed_Ex", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER4006: Property '{0}' is empty or contains whitespace and will be ignored.. /// @@ -275,7 +275,7 @@ internal static string EmptyOrWhitespacePropertyIgnored { return ResourceManager.GetString("EmptyOrWhitespacePropertyIgnored", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER4004: Items '{0}' contain empty item(s) which will be ignored.. /// @@ -284,7 +284,7 @@ internal static string EmptyValuesIgnored { return ResourceManager.GetString("EmptyValuesIgnored", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2023: A ContainerEntrypoint and ContainerAppCommandArgs are provided. ContainerAppInstruction must be set to configure how the application is started. Valid instructions are {0}.. /// @@ -293,7 +293,7 @@ internal static string EntrypointAndAppCommandArgsSetNoAppCommandInstruction { return ResourceManager.GetString("EntrypointAndAppCommandArgsSetNoAppCommandInstruction", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2024: ContainerEntrypointArgs are provided without specifying a ContainerEntrypoint.. /// @@ -302,7 +302,7 @@ internal static string EntrypointArgsSetNoEntrypoint { return ResourceManager.GetString("EntrypointArgsSetNoEntrypoint", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2029: ContainerEntrypointArgsSet are provided. Change to use ContainerAppCommandArgs for arguments that must always be set, or ContainerDefaultArgs for arguments that can be overridden when the container is created.. /// @@ -311,7 +311,7 @@ internal static string EntrypointArgsSetPreferAppCommandArgs { return ResourceManager.GetString("EntrypointArgsSetPreferAppCommandArgs", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2028: ContainerEntrypoint can not be combined with ContainerAppCommandInstruction '{0}'.. /// @@ -320,7 +320,7 @@ internal static string EntrypointConflictAppCommand { return ResourceManager.GetString("EntrypointConflictAppCommand", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2027: A ContainerEntrypoint is provided. ContainerAppInstruction must be set to configure how the application is started. Valid instructions are {0}.. /// @@ -329,7 +329,7 @@ internal static string EntrypointSetNoAppCommandInstruction { return ResourceManager.GetString("EntrypointSetNoAppCommandInstruction", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1008: Failed retrieving credentials for "{0}": {1}. /// @@ -338,7 +338,7 @@ internal static string FailedRetrievingCredentials { return ResourceManager.GetString("FailedRetrievingCredentials", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2030: GenerateLabels was disabled but GenerateDigestLabel was enabled - no digest label will be created.. /// @@ -347,7 +347,7 @@ internal static string GenerateDigestLabelWithoutGenerateLabels { return ResourceManager.GetString("GenerateDigestLabelWithoutGenerateLabels", resourceCulture); } } - + /// /// Looks up a localized string similar to No host object detected.. /// @@ -356,7 +356,7 @@ internal static string HostObjectNotDetected { return ResourceManager.GetString("HostObjectNotDetected", resourceCulture); } } - + /// /// Looks up a localized string similar to Pushed image index '{0}' to registry '{1}'.. /// @@ -365,7 +365,7 @@ internal static string ImageIndexUploadedToRegistry { return ResourceManager.GetString("ImageIndexUploadedToRegistry", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1009: Failed to load image from local registry. stdout: {0}. /// @@ -374,7 +374,7 @@ internal static string ImageLoadFailed { return ResourceManager.GetString("ImageLoadFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1010: Pulling images from local registry is not supported.. /// @@ -383,7 +383,7 @@ internal static string ImagePullNotSupported { return ResourceManager.GetString("ImagePullNotSupported", resourceCulture); } } - + /// /// Looks up a localized string similar to Cannot create image index because no images were provided.. /// @@ -392,7 +392,16 @@ internal static string ImagesEmpty { return ResourceManager.GetString("ImagesEmpty", resourceCulture); } } - + + /// + /// Looks up a localized string similar to CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.. + /// + internal static string InvalidContainerImageFormat { + get { + return ResourceManager.GetString("InvalidContainerImageFormat", resourceCulture); + } + } + /// /// Looks up a localized string similar to CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.. /// @@ -401,7 +410,7 @@ internal static string InvalidEnvVar { return ResourceManager.GetString("InvalidEnvVar", resourceCulture); } } - + /// /// Looks up a localized string similar to Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.. /// @@ -410,7 +419,7 @@ internal static string InvalidImageMetadata { return ResourceManager.GetString("InvalidImageMetadata", resourceCulture); } } - + /// /// Looks up a localized string similar to Cannot create image index because at least one of the provided images manifest is invalid.. /// @@ -473,7 +482,7 @@ internal static string InvalidImageName_EntireNameIsInvalidCharacters { return ResourceManager.GetString("InvalidImageName_EntireNameIsInvalidCharacters", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2005: The first character of the image name '{0}' must be a lowercase letter or a digit and all characters in the name must be an alphanumeric character, -, /, or _.. /// @@ -482,7 +491,7 @@ internal static string InvalidImageName_NonAlphanumericStartCharacter { return ResourceManager.GetString("InvalidImageName_NonAlphanumericStartCharacter", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2017: A ContainerPort item was provided with an invalid port number '{0}'. ContainerPort items must have an Include value that is an integer, and a Type value that is either 'tcp' or 'udp'.. /// @@ -491,7 +500,7 @@ internal static string InvalidPort_Number { return ResourceManager.GetString("InvalidPort_Number", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2017: A ContainerPort item was provided with an invalid port number '{0}' and an invalid port type '{1}'. ContainerPort items must have an Include value that is an integer, and a Type value that is either 'tcp' or 'udp'.. /// @@ -500,7 +509,7 @@ internal static string InvalidPort_NumberAndType { return ResourceManager.GetString("InvalidPort_NumberAndType", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2017: A ContainerPort item was provided with an invalid port type '{0}'. ContainerPort items must have an Include value that is an integer, and a Type value that is either 'tcp' or 'udp'.. /// @@ -509,7 +518,7 @@ internal static string InvalidPort_Type { return ResourceManager.GetString("InvalidPort_Type", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2018: Invalid SDK prerelease version '{0}' - only 'rc' and 'preview' are supported.. /// @@ -518,7 +527,7 @@ internal static string InvalidSdkPrereleaseVersion { return ResourceManager.GetString("InvalidSdkPrereleaseVersion", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2019: Invalid SDK semantic version '{0}'.. /// @@ -527,7 +536,7 @@ internal static string InvalidSdkVersion { return ResourceManager.GetString("InvalidSdkVersion", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2007: Invalid {0} provided: {1}. Image tags must be alphanumeric, underscore, hyphen, or period.. /// @@ -536,7 +545,7 @@ internal static string InvalidTag { return ResourceManager.GetString("InvalidTag", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2010: Invalid {0} provided: {1}. {0} must be a semicolon-delimited list of valid image tags. Image tags must be alphanumeric, underscore, hyphen, or period.. /// @@ -545,7 +554,7 @@ internal static string InvalidTags { return ResourceManager.GetString("InvalidTags", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid string[] TargetRuntimeIdentifiers. Either all should be 'linux-musl' or none.. /// @@ -554,7 +563,7 @@ internal static string InvalidTargetRuntimeIdentifiers { return ResourceManager.GetString("InvalidTargetRuntimeIdentifiers", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1003: Token response had neither token nor access_token.. /// @@ -563,7 +572,7 @@ internal static string InvalidTokenResponse { return ResourceManager.GetString("InvalidTokenResponse", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER4005: Item '{0}' contains items without metadata 'Value', and they will be ignored.. /// @@ -572,7 +581,7 @@ internal static string ItemsWithoutMetadata { return ResourceManager.GetString("ItemsWithoutMetadata", resourceCulture); } } - + /// /// Looks up a localized string similar to Error while reading daemon config: {0}. /// @@ -581,7 +590,7 @@ internal static string LocalDocker_FailedToGetConfig { return ResourceManager.GetString("LocalDocker_FailedToGetConfig", resourceCulture); } } - + /// /// Looks up a localized string similar to The daemon server reported errors: {0}. /// @@ -590,7 +599,7 @@ internal static string LocalDocker_LocalDaemonErrors { return ResourceManager.GetString("LocalDocker_LocalDaemonErrors", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1012: The local registry is not available, but pushing to a local registry was requested.. /// @@ -599,7 +608,7 @@ internal static string LocalRegistryNotAvailable { return ResourceManager.GetString("LocalRegistryNotAvailable", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2004: Unable to download layer with descriptor '{0}' from registry '{1}' because it does not exist.. /// @@ -608,7 +617,7 @@ internal static string MissingLinkToRegistry { return ResourceManager.GetString("MissingLinkToRegistry", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2016: ContainerPort item '{0}' does not specify the port number. Please ensure the item's Include is a port number, for example '<ContainerPort Include="80" />'. /// @@ -617,7 +626,7 @@ internal static string MissingPortNumber { return ResourceManager.GetString("MissingPortNumber", resourceCulture); } } - + /// /// Looks up a localized string similar to 'mediaType' of manifests should be the same in image index.. /// @@ -626,7 +635,7 @@ internal static string MixedMediaTypes { return ResourceManager.GetString("MixedMediaTypes", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1004: No RequestUri specified.. /// @@ -635,7 +644,7 @@ internal static string NoRequestUriSpecified { return ResourceManager.GetString("NoRequestUriSpecified", resourceCulture); } } - + /// /// Looks up a localized string similar to '{0}' was not a valid container image name, it was normalized to '{1}'. /// @@ -645,6 +654,16 @@ internal static string NormalizedContainerName { } } + + /// + /// Looks up a localized string similar to Unable to create tarball for oci image with multiple tags.. + /// + internal static string OciImageMultipleTagsNotSupported { + get { + return ResourceManager.GetString("OciImageMultipleTagsNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to CONTAINER2011: {0} '{1}' does not exist. /// @@ -653,7 +672,7 @@ internal static string PublishDirectoryDoesntExist { return ResourceManager.GetString("PublishDirectoryDoesntExist", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploaded config to registry.. /// @@ -662,7 +681,7 @@ internal static string Registry_ConfigUploaded { return ResourceManager.GetString("Registry_ConfigUploaded", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploading config to registry at blob '{0}',. /// @@ -671,7 +690,7 @@ internal static string Registry_ConfigUploadStarted { return ResourceManager.GetString("Registry_ConfigUploadStarted", resourceCulture); } } - + /// /// Looks up a localized string similar to Layer '{0}' already exists.. /// @@ -680,7 +699,7 @@ internal static string Registry_LayerExists { return ResourceManager.GetString("Registry_LayerExists", resourceCulture); } } - + /// /// Looks up a localized string similar to Finished uploading layer '{0}' to '{1}'.. /// @@ -689,7 +708,7 @@ internal static string Registry_LayerUploaded { return ResourceManager.GetString("Registry_LayerUploaded", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploading layer '{0}' to '{1}'.. /// @@ -698,7 +717,7 @@ internal static string Registry_LayerUploadStarted { return ResourceManager.GetString("Registry_LayerUploadStarted", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploaded manifest to '{0}'.. /// @@ -707,7 +726,7 @@ internal static string Registry_ManifestUploaded { return ResourceManager.GetString("Registry_ManifestUploaded", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploading manifest to registry '{0}' as blob '{1}'.. /// @@ -716,7 +735,7 @@ internal static string Registry_ManifestUploadStarted { return ResourceManager.GetString("Registry_ManifestUploadStarted", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploaded tag '{0}' to '{1}'.. /// @@ -725,7 +744,7 @@ internal static string Registry_TagUploaded { return ResourceManager.GetString("Registry_TagUploaded", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploading tag '{0}' to '{1}'.. /// @@ -734,7 +753,7 @@ internal static string Registry_TagUploadStarted { return ResourceManager.GetString("Registry_TagUploadStarted", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1017: Unable to communicate with the registry '{0}'.. /// @@ -743,7 +762,7 @@ internal static string RegistryOperationFailed { return ResourceManager.GetString("RegistryOperationFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1013: Failed to push to the output registry: {0}. /// @@ -752,7 +771,7 @@ internal static string RegistryOutputPushFailed { return ResourceManager.GetString("RegistryOutputPushFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1014: Manifest pull failed.. /// @@ -761,7 +780,7 @@ internal static string RegistryPullFailed { return ResourceManager.GetString("RegistryPullFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1005: Registry push failed; received status code '{0}'.. /// @@ -770,7 +789,7 @@ internal static string RegistryPushFailed { return ResourceManager.GetString("RegistryPushFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1015: Unable to access the repository '{0}' at tag '{1}' in the registry '{2}'. Please confirm that this name and tag are present in the registry.. /// @@ -779,7 +798,7 @@ internal static string RepositoryNotFound { return ResourceManager.GetString("RepositoryNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER4003: Required '{0}' items contain empty items.. /// @@ -788,7 +807,7 @@ internal static string RequiredItemsContainsEmptyItems { return ResourceManager.GetString("RequiredItemsContainsEmptyItems", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER4002: Required '{0}' items were not set.. /// @@ -797,7 +816,7 @@ internal static string RequiredItemsNotSet { return ResourceManager.GetString("RequiredItemsNotSet", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER4001: Required property '{0}' was not set or empty.. /// @@ -806,7 +825,7 @@ internal static string RequiredPropertyNotSetOrEmpty { return ResourceManager.GetString("RequiredPropertyNotSetOrEmpty", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1006: Too many retries, stopping.. /// @@ -815,7 +834,7 @@ internal static string TooManyRetries { return ResourceManager.GetString("TooManyRetries", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1016: Unable to access the repository '{0}' in the registry '{1}'. Please confirm your credentials are correct and that you have access to this repository and registry.. /// @@ -824,7 +843,7 @@ internal static string UnableToAccessRepository { return ResourceManager.GetString("UnableToAccessRepository", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2021: Unknown AppCommandInstruction '{0}'. Valid instructions are {1}.. /// @@ -833,7 +852,7 @@ internal static string UnknownAppCommandInstruction { return ResourceManager.GetString("UnknownAppCommandInstruction", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2002: Unknown local registry type '{0}'. Valid local container registry types are {1}.. /// @@ -842,7 +861,7 @@ internal static string UnknownLocalRegistryType { return ResourceManager.GetString("UnknownLocalRegistryType", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2003: The manifest for {0}:{1} from registry {2} was an unknown type: {3}. Please raise an issue at https://github.com/dotnet/sdk-container-builds/issues with this message.. /// @@ -851,7 +870,7 @@ internal static string UnknownMediaType { return ResourceManager.GetString("UnknownMediaType", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2001: Unrecognized mediaType '{0}'.. /// @@ -860,7 +879,7 @@ internal static string UnrecognizedMediaType { return ResourceManager.GetString("UnrecognizedMediaType", resourceCulture); } } - + /// /// Looks up a localized string similar to Cannot create image index for the provided 'mediaType' = '{0}'.. /// @@ -869,7 +888,7 @@ internal static string UnsupportedMediaType { return ResourceManager.GetString("UnsupportedMediaType", resourceCulture); } } - + /// /// Looks up a localized string similar to Unable to create tarball for mediaType '{0}'.. /// diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx index bac283c4ac0c..b0e7350da710 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx @@ -481,4 +481,8 @@ CONTAINER2030: GenerateLabels was disabled but GenerateDigestLabel was enabled - no digest label will be created. {StrBegin="CONTAINER2030: "} + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf index f916d5f3d916..383c7346ddca 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: '{1}' není platná proměnná prostředí. Ignorování. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf index d0ac67812ab7..f273669dca6c 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: „{1}“ war keine gültige Umgebungsvariable. Sie wird ignoriert. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf index b1febfedfbc6..fb785d4e4f7d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: "{1}" no era una variable de entorno válida. Ignorando. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf index 2566778c2a07..ff593f5316cb 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0} : '{1}' n’était pas une variable d’environnement valide. Ignorant. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf index e6b319944c11..0a8f8a977150 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: '{1}' non è una variabile di ambiente valida. Il valore verrà ignorato. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf index 16e01793da1d..6cf8d131368c 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: '{1}' は有効な環境変数ではありませんでした。無視しています。 diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf index 370181275c81..77de4d142dd3 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: '{1}'은(는) 유효한 환경 변수가 아닙니다. 무시 중. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf index d92686d7d371..6eeb96f72d6d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: „{1}” nie jest prawidłową zmienną środowiskową. Ignorowanie. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf index 651ea8dd65b6..9686e1444a08 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: '{1}' não era uma variável de ambiente válida. Ignorando. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf index a52774d219e5..8b942db5bfef 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: "{1}" не является допустимой переменной среды. Пропуск. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf index f513dfc84940..146196fba1bc 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: '{1}' geçerli bir Ortam Değişkeni değildi. Görmezden geliniyor. diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf index 82b415641aa9..ab3a5f1b5993 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: "{1}" 不是有效的环境变量。忽略。 diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf index ef91ab2848e8..9a144ff7f80b 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf @@ -204,6 +204,11 @@ Cannot create image index because no images were provided. + + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. + {StrBegins="CONTAINER2031: "} + CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring. CONTAINER2015: {0}: '{1}' 不是有效的環境變數。正在忽略。 diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs index b23a8482f3cf..5ce5a7286fce 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs @@ -161,6 +161,10 @@ partial class CreateNewImage public bool GenerateDigestLabel { get; set; } /// + /// Set to either 'OCI', 'Docker', or null. If unset, the generated images' mediaType will be that of the base image. If set, the generated image will be given the specified media type. + /// + public string? ImageFormat { get; set; } + /// If true, the tooling will skip the publishing step. /// public bool SkipPublishing { get; set; } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index df46fd354709..b0f4899f75e8 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -125,6 +125,24 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) ( Strings.ContainerBuilder_StartBuildingImage, new object[] { Repository, String.Join(",", ImageTags), sourceImageReference }); Log.LogMessage(MessageImportance.High, message, parameters); + // forcibly change the media type if required + if (ImageFormat is not null) + { + if (Enum.TryParse(ImageFormat, out var imageFormat)) + { + imageBuilder.ManifestMediaType = imageFormat switch + { + KnownImageFormats.Docker => SchemaTypes.DockerManifestV2, + KnownImageFormats.OCI => SchemaTypes.OciManifestV1, + _ => imageBuilder.ManifestMediaType // should be impossible unless we add to the enum + }; + } + else + { + Log.LogErrorWithCodeFromResources(nameof(Strings.InvalidContainerImageFormat), ImageFormat, string.Join(",", Enum.GetValues())); + } + } + Layer newLayer = Layer.FromDirectory(PublishDirectory, WorkingDirectory, imageBuilder.IsWindows, imageBuilder.ManifestMediaType); imageBuilder.AddLayer(newLayer); imageBuilder.SetWorkingDirectory(WorkingDirectory); diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs index 51869ccb6c54..d2ac3dbe0313 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs @@ -119,6 +119,10 @@ internal string GenerateCommandLineCommandsInt() { builder.AppendSwitchIfNotNull("--appcommandinstruction ", AppCommandInstruction); } + if (!string.IsNullOrWhiteSpace(ImageFormat)) + { + builder.AppendSwitchIfNotNull("--image-format ", ImageFormat); + } AppendSwitchIfNotNullSanitized(builder, "--entrypoint ", nameof(Entrypoint), Entrypoint); AppendSwitchIfNotNullSanitized(builder, "--entrypointargs ", nameof(EntrypointArgs), EntrypointArgs); diff --git a/src/Containers/containerize/ContainerizeCommand.cs b/src/Containers/containerize/ContainerizeCommand.cs index 52b7e390b503..d611fb3e154d 100644 --- a/src/Containers/containerize/ContainerizeCommand.cs +++ b/src/Containers/containerize/ContainerizeCommand.cs @@ -197,6 +197,11 @@ internal class ContainerizeCommand : CliRootCommand Arity = ArgumentArity.Zero }; + internal CliOption ImageFormatOption { get; } = new("--image-format") + { + Description = "If set to OCI or Docker will force the generated image to be that format. If unset, the base images format will be used." + }; + internal ContainerizeCommand() : base("Containerize an application without Docker.") { PublishDirectoryArgument.AcceptLegalFilePathsOnly(); @@ -225,6 +230,7 @@ internal ContainerizeCommand() : base("Containerize an application without Docke this.Options.Add(ContainerUserOption); this.Options.Add(GenerateLabelsOption); this.Options.Add(GenerateDigestLabelOption); + this.Options.Add(ImageFormatOption); this.SetAction(async (parseResult, cancellationToken) => { @@ -252,6 +258,7 @@ internal ContainerizeCommand() : base("Containerize an application without Docke string? _containerUser = parseResult.GetValue(ContainerUserOption); bool _generateLabels = parseResult.GetValue(GenerateLabelsOption); bool _generateDigestLabel = parseResult.GetValue(GenerateDigestLabelOption); + KnownImageFormats? _imageFormat = parseResult.GetValue(ImageFormatOption); //setup basic logging bool traceEnabled = Env.GetEnvironmentVariableAsBool("CONTAINERIZE_TRACE_LOGGING_ENABLED"); @@ -283,6 +290,7 @@ await ContainerBuilder.ContainerizeAsync( _archiveOutputPath, _generateLabels, _generateDigestLabel, + _imageFormat, loggerFactory, cancellationToken).ConfigureAwait(false); }); diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets index d7cf4bc5ecbf..ff22ed811e83 100644 --- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets +++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets @@ -255,6 +255,7 @@ BaseRegistry="$(ContainerBaseRegistry)" BaseImageName="$(ContainerBaseName)" BaseImageTag="$(ContainerBaseTag)" + ImageFormat="$(ContainerImageFormat)" LocalRegistry="$(LocalRegistry)" OutputRegistry="$(ContainerRegistry)" ArchiveOutputPath="$(ContainerArchiveOutputPath)" diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs index cf25db4b0309..f6a8e0823117 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs @@ -295,6 +295,72 @@ public async System.Threading.Tasks.Task CreateNewImage_RootlessBaseImage() Assert.Equal(RootlessUser, imageBuilder.BaseImageConfig.GetUser()); } + + [DockerAvailableFact] + public void CanOverrideContainerImageFormat() + { + DirectoryInfo newProjectDir = new(GetTestDirectoryName()); + + if (newProjectDir.Exists) + { + newProjectDir.Delete(recursive: true); + } + + newProjectDir.Create(); + + new DotnetNewCommand(_testOutput, "console", "-f", ToolsetInfo.CurrentTargetFramework) + .WithVirtualHive() + .WithWorkingDirectory(newProjectDir.FullName) + .Execute() + .Should().Pass(); + + new DotnetCommand(_testOutput, "build", "--configuration", "release") + .WithWorkingDirectory(newProjectDir.FullName) + .Execute() + .Should().Pass(); + + ParseContainerProperties pcp = new(); + (IBuildEngine buildEngine, List errors) = SetupBuildEngine(); + pcp.BuildEngine = buildEngine; + + pcp.FullyQualifiedBaseImageName = "mcr.microsoft.com/dotnet/runtime:9.0"; + pcp.ContainerRegistry = "localhost:5010"; + pcp.ContainerRepository = "dotnet/testimage"; + pcp.ContainerImageTags = new[] { "5.0", "latest" }; + + Assert.True(pcp.Execute(), FormatBuildMessages(errors)); + Assert.Equal("mcr.microsoft.com", pcp.ParsedContainerRegistry); + Assert.Equal("dotnet/runtime", pcp.ParsedContainerImage); + Assert.Equal("9.0", pcp.ParsedContainerTag); + + Assert.Equal("dotnet/testimage", pcp.NewContainerRepository); + pcp.NewContainerTags.Should().BeEquivalentTo(new[] { "5.0", "latest" }); + + CreateNewImage cni = new(); + (buildEngine, errors) = SetupBuildEngine(); + cni.BuildEngine = buildEngine; + + cni.BaseRegistry = pcp.ParsedContainerRegistry; + cni.BaseImageName = pcp.ParsedContainerImage; + cni.BaseImageTag = pcp.ParsedContainerTag; + cni.Repository = pcp.NewContainerRepository; + cni.OutputRegistry = "localhost:5010"; + cni.PublishDirectory = Path.Combine(newProjectDir.FullName, "bin", "release", ToolsetInfo.CurrentTargetFramework); + cni.WorkingDirectory = "app/"; + cni.Entrypoint = new TaskItem[] { new(newProjectDir.Name) }; + cni.ImageTags = pcp.NewContainerTags; + cni.ContainerRuntimeIdentifier = "linux-x64"; + cni.RuntimeIdentifierGraphPath = ToolsetUtils.GetRuntimeGraphFilePath(); + + cni.ImageFormat = KnownImageFormats.OCI.ToString(); + + Assert.True(cni.Execute(), FormatBuildMessages(errors)); + + cni.GeneratedContainerMediaType.Should().Be(SchemaTypes.OciManifestV1); + newProjectDir.Delete(true); + } + + private static (IBuildEngine buildEngine, List errors) SetupBuildEngine() { List errors = new(); From 6b995c01bf6bb03315e2c3493d228fb06ac2eb86 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Feb 2025 05:01:47 +0000 Subject: [PATCH 55/74] Update dependencies from https://github.com/dotnet/msbuild build 20250211.6 Microsoft.SourceBuild.Intermediate.msbuild , Microsoft.Build , Microsoft.Build.Localization From Version 17.12.27-preview-25106-01 -> To Version 17.12.29-preview-25111-06 --- NuGet.config | 109 +++++++++++++++++++++++++++++++--------- eng/Version.Details.xml | 12 ++--- eng/Versions.props | 4 +- 3 files changed, 94 insertions(+), 31 deletions(-) diff --git a/NuGet.config b/NuGet.config index b0d7c59274f0..b678a7b62dab 100644 --- a/NuGet.config +++ b/NuGet.config @@ -5,9 +5,6 @@ - - - @@ -28,42 +25,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -90,12 +118,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2b0e64c87e63..8a1c90878a62 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -73,18 +73,18 @@ 2c27e405e17595694d91892159593d6dd10e61e2 - + https://github.com/dotnet/msbuild - e0b90a9a8ad3d8fdd186566b33859450c9e4e06c + 990a07ed6852e478b0c05599154c3a28c124cdeb - + https://github.com/dotnet/msbuild - e0b90a9a8ad3d8fdd186566b33859450c9e4e06c + 990a07ed6852e478b0c05599154c3a28c124cdeb - + https://github.com/dotnet/msbuild - e0b90a9a8ad3d8fdd186566b33859450c9e4e06c + 990a07ed6852e478b0c05599154c3a28c124cdeb diff --git a/eng/Versions.props b/eng/Versions.props index 4bc5ca94dec1..91c2be5b0b30 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -180,8 +180,8 @@ At usage sites, either we use MicrosoftBuildMinimumVersion, or MicrosoftBuildVersion in source-only modes. Additionally, set the MinimumVSVersion for the installer UI that's required for targeting NetCurrent --> - 17.12.27 - 17.12.27-preview-25106-01 + 17.12.29 + 17.12.29-preview-25111-06 17.11.4 17.12 From fbe3b3112551911639e127c298c055df862de3a7 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Feb 2025 05:01:54 +0000 Subject: [PATCH 56/74] Update dependencies from https://github.com/dotnet/arcade build 20250211.5 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SignTool , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.25077.4 -> To Version 9.0.0-beta.25111.5 --- NuGet.config | 107 +++++++++++++++++++++++++++++++--------- eng/Version.Details.xml | 28 +++++------ eng/Versions.props | 8 +-- global.json | 6 +-- 4 files changed, 106 insertions(+), 43 deletions(-) diff --git a/NuGet.config b/NuGet.config index b0d7c59274f0..6a0e0a4288f6 100644 --- a/NuGet.config +++ b/NuGet.config @@ -5,9 +5,6 @@ - - - @@ -28,9 +25,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -39,31 +66,32 @@ + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -90,12 +118,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2b0e64c87e63..70902bf7e830 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -589,34 +589,34 @@ - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 diff --git a/eng/Versions.props b/eng/Versions.props index 4bc5ca94dec1..e98afbf7cd49 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -270,10 +270,10 @@ - 9.0.0-beta.25077.4 - 9.0.0-beta.25077.4 - 9.0.0-beta.25077.4 - 9.0.0-beta.25077.4 + 9.0.0-beta.25111.5 + 9.0.0-beta.25111.5 + 9.0.0-beta.25111.5 + 9.0.0-beta.25111.5 diff --git a/global.json b/global.json index c827278fb4cf..8156db5f7137 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "tools": { - "dotnet": "9.0.102", + "dotnet": "9.0.103", "runtimes": { "dotnet": [ "$(VSRedistCommonNetCoreSharedFrameworkx6490PackageVersion)" @@ -17,8 +17,8 @@ "cmake": "latest" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25077.4", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25077.4", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25111.5", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25111.5", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.DotNet.CMake.Sdk": "9.0.0-beta.24217.1" } From 97260ce7cc6f645e1972e78e7347ebece33d3406 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Feb 2025 07:48:54 +0000 Subject: [PATCH 57/74] Update dependencies from https://github.com/dotnet/templating build 20250211.2 Microsoft.SourceBuild.Intermediate.templating , Microsoft.TemplateEngine.Abstractions , Microsoft.TemplateEngine.Mocks From Version 9.0.104-servicing.25105.3 -> To Version 9.0.104-servicing.25111.2 --- NuGet.config | 109 +++++++++++++++++++++++++++++++--------- eng/Version.Details.xml | 10 ++-- eng/Versions.props | 2 +- 3 files changed, 92 insertions(+), 29 deletions(-) diff --git a/NuGet.config b/NuGet.config index b0d7c59274f0..83b70706b891 100644 --- a/NuGet.config +++ b/NuGet.config @@ -5,9 +5,6 @@ - - - @@ -28,9 +25,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -39,31 +66,32 @@ + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -90,12 +118,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2b0e64c87e63..1e0487833c71 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -3,16 +3,16 @@ https://github.com/dotnet/templating - 5936acfcfd47d64eaadd59a15c33d0de07db171b + 35611eac8e7421e41f201af4c95817e8be90e3f6 - + https://github.com/dotnet/templating - 5936acfcfd47d64eaadd59a15c33d0de07db171b + 35611eac8e7421e41f201af4c95817e8be90e3f6 - + https://github.com/dotnet/templating - 5936acfcfd47d64eaadd59a15c33d0de07db171b + 35611eac8e7421e41f201af4c95817e8be90e3f6 diff --git a/eng/Versions.props b/eng/Versions.props index 4bc5ca94dec1..aa6ade4602df 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -193,7 +193,7 @@ $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) - 9.0.104-servicing.25105.3 + 9.0.104-servicing.25111.2 $(MicrosoftTemplateEngineMocksPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineMocksPackageVersion) From affb75100e53d280f47a607075fc5970bc9c5077 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Feb 2025 10:08:23 +0000 Subject: [PATCH 58/74] Update dependencies from https://github.com/dotnet/templating build 20250212.3 Microsoft.SourceBuild.Intermediate.templating , Microsoft.TemplateEngine.Abstractions , Microsoft.TemplateEngine.Mocks From Version 9.0.104-servicing.25105.3 -> To Version 9.0.104-servicing.25112.3 --- NuGet.config | 2 +- eng/Version.Details.xml | 10 +++++----- eng/Versions.props | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/NuGet.config b/NuGet.config index 83b70706b891..9269934b756a 100644 --- a/NuGet.config +++ b/NuGet.config @@ -73,7 +73,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1e0487833c71..7ce5ea7211fe 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -3,16 +3,16 @@ https://github.com/dotnet/templating - 35611eac8e7421e41f201af4c95817e8be90e3f6 + 77f6dd4f65d0f835a49b7f8375f9667933bb93d6 - + https://github.com/dotnet/templating - 35611eac8e7421e41f201af4c95817e8be90e3f6 + 77f6dd4f65d0f835a49b7f8375f9667933bb93d6 - + https://github.com/dotnet/templating - 35611eac8e7421e41f201af4c95817e8be90e3f6 + 77f6dd4f65d0f835a49b7f8375f9667933bb93d6 diff --git a/eng/Versions.props b/eng/Versions.props index aa6ade4602df..5ef57870e6ab 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -193,7 +193,7 @@ $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) - 9.0.104-servicing.25111.2 + 9.0.104-servicing.25112.3 $(MicrosoftTemplateEngineMocksPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineMocksPackageVersion) From a77f8db3ac08c99ef81a69faaddb4fb5a339d3c0 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Feb 2025 10:24:08 +0000 Subject: [PATCH 59/74] Update dependencies from https://github.com/dotnet/templating build 20250212.5 Microsoft.SourceBuild.Intermediate.templating , Microsoft.TemplateEngine.Abstractions , Microsoft.TemplateEngine.Mocks From Version 9.0.201-servicing.25111.3 -> To Version 9.0.201-servicing.25112.5 --- NuGet.config | 2 +- eng/Version.Details.xml | 10 +++++----- eng/Versions.props | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/NuGet.config b/NuGet.config index cd8a12b0557f..0a37349401bd 100644 --- a/NuGet.config +++ b/NuGet.config @@ -73,7 +73,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b2176337f7b2..54ab2996828f 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -3,16 +3,16 @@ https://github.com/dotnet/templating - 18284132b745ea1dddd65889bfd45ea97872322e + a2862fdcaf86b098da8025439e8317417290e610 - + https://github.com/dotnet/templating - 18284132b745ea1dddd65889bfd45ea97872322e + a2862fdcaf86b098da8025439e8317417290e610 - + https://github.com/dotnet/templating - 18284132b745ea1dddd65889bfd45ea97872322e + a2862fdcaf86b098da8025439e8317417290e610 diff --git a/eng/Versions.props b/eng/Versions.props index d2cb5866c2d0..d7e67342f223 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -191,7 +191,7 @@ $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) - 9.0.201-servicing.25111.3 + 9.0.201-servicing.25112.5 $(MicrosoftTemplateEngineMocksPackageVersion) $(MicrosoftTemplateEngineAbstractionsPackageVersion) $(MicrosoftTemplateEngineMocksPackageVersion) From da94f40be9bd4c498070d31a5ba9a1255cf8dbd5 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 12 Feb 2025 13:04:56 +0100 Subject: [PATCH 60/74] add BaseImageDigest param to CreateImageIndex task --- .../PublicAPI/net9.0/PublicAPI.Unshipped.txt | 2 ++ .../Tasks/CreateImageIndex.Interface.cs | 8 +++++++- .../Tasks/CreateImageIndex.cs | 2 +- .../build/Microsoft.NET.Build.Containers.targets | 4 +++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net9.0/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net9.0/PublicAPI.Unshipped.txt index b07fea9db116..021c60e82a7c 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net9.0/PublicAPI.Unshipped.txt +++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net9.0/PublicAPI.Unshipped.txt @@ -25,6 +25,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageName.get -> strin Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageName.set -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageTag.get -> string! Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageTag.set -> void +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageDigest.get -> string! +Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageDigest.set -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ArchiveOutputPath.get -> string! Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ArchiveOutputPath.set -> void Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.LocalRegistry.get -> string! diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs index bb69651add0e..538240841c6b 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs @@ -25,9 +25,14 @@ partial class CreateImageIndex /// The base image tag. /// Ex: 6.0 /// - [Required] public string BaseImageTag { get; set; } + /// + /// The base image digest. + /// Ex: sha256:12345... + /// + public string BaseImageDigest { get; set; } + /// /// Manifests to include in the image index. /// @@ -78,6 +83,7 @@ public CreateImageIndex() BaseRegistry = string.Empty; BaseImageName = string.Empty; BaseImageTag = string.Empty; + BaseImageDigest = string.Empty; GeneratedContainers = Array.Empty(); OutputRegistry = string.Empty; ArchiveOutputPath = string.Empty; diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs index 25658f612e06..16e5b8392372 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs @@ -58,7 +58,7 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) RegistryMode sourceRegistryMode = BaseRegistry.Equals(OutputRegistry, StringComparison.InvariantCultureIgnoreCase) ? RegistryMode.PullFromOutput : RegistryMode.Pull; Registry? sourceRegistry = IsLocalPull ? null : new Registry(BaseRegistry, logger, sourceRegistryMode); - SourceImageReference sourceImageReference = new(sourceRegistry, BaseImageName, BaseImageTag); + SourceImageReference sourceImageReference = new(sourceRegistry, BaseImageName, BaseImageTag, BaseImageDigest); DestinationImageReference destinationImageReference = DestinationImageReference.CreateFromSettings( Repository, diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets index 1a4dedebdcff..694e1dc8372f 100644 --- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets +++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets @@ -322,6 +322,7 @@ ContainerBaseRegistry=$(ContainerBaseRegistry); ContainerBaseName=$(ContainerBaseName); ContainerBaseTag=$(ContainerBaseTag); + ContainerBaseDigest=$(ContainerBaseDigest); ContainerRegistry=$(ContainerRegistry); _ContainerImageTags=@(ContainerImageTags, ';'); ContainerRepository=$(ContainerRepository); @@ -359,7 +360,8 @@ ImageTags="@(ContainerImageTags)" BaseRegistry="$(ContainerBaseRegistry)" BaseImageName="$(ContainerBaseName)" - BaseImageTag="$(ContainerBaseTag)"> + BaseImageTag="$(ContainerBaseTag)" + BaseImageDigest="$(ContainerBaseDigest)"> From e796fde44c4fc8ed7a8ada7c12646385b078884b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Feb 2025 12:07:21 +0000 Subject: [PATCH 61/74] Update dependencies from https://github.com/dotnet/msbuild build 20250212.7 Microsoft.SourceBuild.Intermediate.msbuild , Microsoft.Build , Microsoft.Build.Localization From Version 17.13.12-preview-25111-07 -> To Version 17.13.13-preview-25112-07 --- NuGet.config | 2 +- eng/Version.Details.xml | 12 ++++++------ eng/Versions.props | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/NuGet.config b/NuGet.config index cd8a12b0557f..a8d256578c86 100644 --- a/NuGet.config +++ b/NuGet.config @@ -60,7 +60,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b2176337f7b2..ef542b5b820c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -69,18 +69,18 @@ 2c27e405e17595694d91892159593d6dd10e61e2 - + https://github.com/dotnet/msbuild - 1ea16607ac1c0646d3f309d9457a4d2b75517de1 + 1c202646288e077c489d41e00e67c458cf9245f7 - + https://github.com/dotnet/msbuild - 1ea16607ac1c0646d3f309d9457a4d2b75517de1 + 1c202646288e077c489d41e00e67c458cf9245f7 - + https://github.com/dotnet/msbuild - 1ea16607ac1c0646d3f309d9457a4d2b75517de1 + 1c202646288e077c489d41e00e67c458cf9245f7 diff --git a/eng/Versions.props b/eng/Versions.props index d2cb5866c2d0..065f01841984 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -178,8 +178,8 @@ At usage sites, either we use MicrosoftBuildMinimumVersion, or MicrosoftBuildVersion in source-only modes. Additionally, set the MinimumVSVersion for the installer UI that's required for targeting NetCurrent --> - 17.13.12 - 17.13.12-preview-25111-07 + 17.13.13 + 17.13.13-preview-25112-07 17.11.4 17.12 From 70393ec28787350e885a5acd196e94318f9edfd6 Mon Sep 17 00:00:00 2001 From: ".NET Source-Build Bot" <102560831+dotnet-sb-bot@users.noreply.github.com> Date: Wed, 12 Feb 2025 07:32:28 -0600 Subject: [PATCH 62/74] .NET Source-Build 9.0.103 February 2025 Updates (#46729) --- src/SourceBuild/content/eng/Version.Details.xml | 4 ++-- src/SourceBuild/content/eng/Versions.props | 4 ++-- src/SourceBuild/content/global.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SourceBuild/content/eng/Version.Details.xml b/src/SourceBuild/content/eng/Version.Details.xml index 0a6f513a34a1..816645ba6ca6 100644 --- a/src/SourceBuild/content/eng/Version.Details.xml +++ b/src/SourceBuild/content/eng/Version.Details.xml @@ -2,9 +2,9 @@ - + https://github.com/dotnet/arcade - b41381d5cd633471265e9cd72e933a7048e03062 + c4bbc67763bf0c5a868862df874079380e647d61 diff --git a/src/SourceBuild/content/eng/Versions.props b/src/SourceBuild/content/eng/Versions.props index 777ee8f64980..054cb887516a 100644 --- a/src/SourceBuild/content/eng/Versions.props +++ b/src/SourceBuild/content/eng/Versions.props @@ -23,8 +23,8 @@ of a .NET major or minor release, prebuilts may be needed. When the release is mature, prebuilts are not necessary, and this property is removed from the file. --> - 9.0.102 - 9.0.102-servicing.24611.1 + 9.0.103 + 9.0.103-servicing.25071.1 2.0.0-beta4.24126.1 diff --git a/src/SourceBuild/content/global.json b/src/SourceBuild/content/global.json index fd25153f4451..459f62242541 100644 --- a/src/SourceBuild/content/global.json +++ b/src/SourceBuild/content/global.json @@ -1,10 +1,10 @@ { "tools": { - "dotnet": "9.0.102" + "dotnet": "9.0.103" }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24572.2" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25065.2" } } From 5c29eb79e3f3e7e0a46a0a410776ca3ffd087668 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 12 Feb 2025 15:39:28 +0100 Subject: [PATCH 63/74] delete file that was renamed --- .../DockerSupportsArchInlineData.cs | 115 ------------------ 1 file changed, 115 deletions(-) delete mode 100644 test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs diff --git a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs deleted file mode 100644 index 7944436b39c9..000000000000 --- a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Reflection; -using System.Text.Json; -using Xunit.Sdk; - -namespace Microsoft.NET.Build.Containers.IntegrationTests; - -public class DockerSupportsArchInlineData : DataAttribute -{ - private readonly string _arch; - private readonly object[] _data; - - public DockerSupportsArchInlineData(string arch, params object[] data) - { - _arch = arch; - _data = data; - } - - public override IEnumerable GetData(MethodInfo testMethod) - { - if (DockerSupportsArchHelper.DaemonSupportsArch(_arch)) - { - return new object[][] { _data.Prepend(_arch).ToArray() }; - } - else - { - base.Skip = $"Skipping test because Docker daemon does not support {_arch}."; - } - return Array.Empty(); - } -} - -internal static class DockerSupportsArchHelper -{ - internal static bool DaemonIsAvailable => ContainerCli.IsAvailable; - - internal static bool IsContainerdStoreEnabledForDocker => ContainerCli.IsContainerdStoreEnabledForDocker; - - internal static bool DaemonSupportsArch(string arch) - { - // an optimization - this doesn't change over time so we can compute it once - string[] LinuxPlatforms = GetSupportedLinuxPlatforms(); - - if (LinuxPlatforms.Contains(arch)) - { - return true; - } - else - { - // another optimization - daemons don't switch types easily or quickly, so this is as good as static - bool IsWindowsDockerDaemon = GetIsWindowsDockerDaemon(); - - if (IsWindowsDockerDaemon && arch.StartsWith("windows", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - return false; - } - } - - private static string[] GetSupportedLinuxPlatforms() - { - if (ContainerCli.IsPodman) - { - var inspectResult = new RunExeCommand(NullLogger.Instance, "podman", "info").Execute(); - inspectResult.Should().Pass(); - var platformsLine = inspectResult.StdOut!.Split(Environment.NewLine).First(x => x.Contains("OsArch:", StringComparison.OrdinalIgnoreCase)); - return new[] { platformsLine.Trim().Substring("OsArch: ".Length) }; - } - else - { - var inspectResult = new RunExeCommand(NullLogger.Instance, "docker", "buildx", "inspect", "default").Execute(); - inspectResult.Should().Pass(); - var platformsLine = inspectResult.StdOut!.Split(Environment.NewLine).First(x => x.StartsWith("Platforms:", StringComparison.OrdinalIgnoreCase)); - return platformsLine.Substring("Platforms: ".Length).Split(",", StringSplitOptions.TrimEntries); - } - } - - private static bool GetIsWindowsDockerDaemon() - { - if (ContainerCli.IsPodman) - { - return false; - } - // the config json has an OSType property that is either "linux" or "windows" - - // we can't use this for linux arch detection because that isn't enough information. - var config = DockerCli.GetDockerConfig(); - if (config.RootElement.TryGetProperty("OSType", out JsonElement osTypeProperty)) - { - return osTypeProperty.GetString() == "windows"; - } - else - { - return false; - } - } - - private class NullLogger : ITestOutputHelper - { - private NullLogger() { } - - public static NullLogger Instance { get; } = new NullLogger(); - - public void WriteLine(string message) - { - //do nothing - } - public void WriteLine(string format, params object[] args) - { - //do nothing - } - } -} From 702936d085a2d3f2af148e8966d5a19ae64b814f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Feb 2025 14:51:55 +0000 Subject: [PATCH 64/74] Update dependencies from https://github.com/dotnet/scenario-tests build 20250212.2 Microsoft.SourceBuild.Intermediate.scenario-tests , Microsoft.DotNet.ScenarioTests.SdkTemplateTests From Version 9.0.0-preview.25102.1 -> To Version 9.0.0-preview.25112.2 --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b2176337f7b2..280469181c3d 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -597,14 +597,14 @@ https://github.com/dotnet/arcade-services 47e3672c762970073e4282bd563233da86bcca3e - + https://github.com/dotnet/scenario-tests - 008b3a357044b102ec30e15ac381c2e880f573af + f219fd635f701e3142be92cb0bb4039cadb39d4d - + https://github.com/dotnet/scenario-tests - 008b3a357044b102ec30e15ac381c2e880f573af + f219fd635f701e3142be92cb0bb4039cadb39d4d From 6728bc42394613cf509c41ec2f0b4540a9a34d3c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Feb 2025 14:51:56 +0000 Subject: [PATCH 65/74] Update dependencies from https://github.com/dotnet/scenario-tests build 20250212.2 Microsoft.SourceBuild.Intermediate.scenario-tests , Microsoft.DotNet.ScenarioTests.SdkTemplateTests From Version 9.0.0-preview.25102.1 -> To Version 9.0.0-preview.25112.2 --- NuGet.config | 107 +++++++++++++++++++++++++++++++--------- eng/Version.Details.xml | 8 +-- eng/Versions.props | 2 +- 3 files changed, 90 insertions(+), 27 deletions(-) diff --git a/NuGet.config b/NuGet.config index b0d7c59274f0..6a0e0a4288f6 100644 --- a/NuGet.config +++ b/NuGet.config @@ -5,9 +5,6 @@ - - - @@ -28,9 +25,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -39,31 +66,32 @@ + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -90,12 +118,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2b0e64c87e63..3b83e3802ef7 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -631,14 +631,14 @@ https://github.com/dotnet/arcade-services 47e3672c762970073e4282bd563233da86bcca3e - + https://github.com/dotnet/scenario-tests - 008b3a357044b102ec30e15ac381c2e880f573af + f219fd635f701e3142be92cb0bb4039cadb39d4d - + https://github.com/dotnet/scenario-tests - 008b3a357044b102ec30e15ac381c2e880f573af + f219fd635f701e3142be92cb0bb4039cadb39d4d From e2db10d3e3ed9f7d027f66a2dcc5e749fa996f6a Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 12 Feb 2025 16:05:20 +0100 Subject: [PATCH 66/74] delete file that was renamed --- .../DockerSupportsArchFact.cs | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs diff --git a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs deleted file mode 100644 index 36ad9fbc0c9a..000000000000 --- a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.NET.Build.Containers.IntegrationTests; - -public class DockerIsAvailableAndSupportsArchFactAttribute : FactAttribute -{ - public DockerIsAvailableAndSupportsArchFactAttribute(string arch) - { - if (!DockerSupportsArchHelper.DaemonIsAvailable) - { - base.Skip = "Skipping test because Docker is not available on this host."; - } - else if (!DockerSupportsArchHelper.DaemonSupportsArch(arch)) - { - base.Skip = $"Skipping test because Docker daemon does not support {arch}."; - } - } -} From 07e7dfee116b4dab82b62a252ecbbc56fbbb3c02 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 12 Feb 2025 16:09:49 +0100 Subject: [PATCH 67/74] Revert "delete file that was renamed" This reverts commit 5c29eb79e3f3e7e0a46a0a410776ca3ffd087668. --- .../DockerSupportsArchInlineData.cs | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs diff --git a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs new file mode 100644 index 000000000000..7944436b39c9 --- /dev/null +++ b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Text.Json; +using Xunit.Sdk; + +namespace Microsoft.NET.Build.Containers.IntegrationTests; + +public class DockerSupportsArchInlineData : DataAttribute +{ + private readonly string _arch; + private readonly object[] _data; + + public DockerSupportsArchInlineData(string arch, params object[] data) + { + _arch = arch; + _data = data; + } + + public override IEnumerable GetData(MethodInfo testMethod) + { + if (DockerSupportsArchHelper.DaemonSupportsArch(_arch)) + { + return new object[][] { _data.Prepend(_arch).ToArray() }; + } + else + { + base.Skip = $"Skipping test because Docker daemon does not support {_arch}."; + } + return Array.Empty(); + } +} + +internal static class DockerSupportsArchHelper +{ + internal static bool DaemonIsAvailable => ContainerCli.IsAvailable; + + internal static bool IsContainerdStoreEnabledForDocker => ContainerCli.IsContainerdStoreEnabledForDocker; + + internal static bool DaemonSupportsArch(string arch) + { + // an optimization - this doesn't change over time so we can compute it once + string[] LinuxPlatforms = GetSupportedLinuxPlatforms(); + + if (LinuxPlatforms.Contains(arch)) + { + return true; + } + else + { + // another optimization - daemons don't switch types easily or quickly, so this is as good as static + bool IsWindowsDockerDaemon = GetIsWindowsDockerDaemon(); + + if (IsWindowsDockerDaemon && arch.StartsWith("windows", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + return false; + } + } + + private static string[] GetSupportedLinuxPlatforms() + { + if (ContainerCli.IsPodman) + { + var inspectResult = new RunExeCommand(NullLogger.Instance, "podman", "info").Execute(); + inspectResult.Should().Pass(); + var platformsLine = inspectResult.StdOut!.Split(Environment.NewLine).First(x => x.Contains("OsArch:", StringComparison.OrdinalIgnoreCase)); + return new[] { platformsLine.Trim().Substring("OsArch: ".Length) }; + } + else + { + var inspectResult = new RunExeCommand(NullLogger.Instance, "docker", "buildx", "inspect", "default").Execute(); + inspectResult.Should().Pass(); + var platformsLine = inspectResult.StdOut!.Split(Environment.NewLine).First(x => x.StartsWith("Platforms:", StringComparison.OrdinalIgnoreCase)); + return platformsLine.Substring("Platforms: ".Length).Split(",", StringSplitOptions.TrimEntries); + } + } + + private static bool GetIsWindowsDockerDaemon() + { + if (ContainerCli.IsPodman) + { + return false; + } + // the config json has an OSType property that is either "linux" or "windows" - + // we can't use this for linux arch detection because that isn't enough information. + var config = DockerCli.GetDockerConfig(); + if (config.RootElement.TryGetProperty("OSType", out JsonElement osTypeProperty)) + { + return osTypeProperty.GetString() == "windows"; + } + else + { + return false; + } + } + + private class NullLogger : ITestOutputHelper + { + private NullLogger() { } + + public static NullLogger Instance { get; } = new NullLogger(); + + public void WriteLine(string message) + { + //do nothing + } + public void WriteLine(string format, params object[] args) + { + //do nothing + } + } +} From 426c92d470dd8b4b86026944ad583b39a06d697b Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Wed, 12 Feb 2025 16:29:03 +0100 Subject: [PATCH 68/74] remove ConfigureAwait in ArchiveFileRegistryTests --- .../ArchiveFileRegistryTests.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs b/test/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs index e5bfc54711d0..e78fb6854005 100644 --- a/test/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs +++ b/test/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs @@ -13,7 +13,7 @@ public async Task ArchiveOutputPathIsExistingDirectory_CreatesFileWithRepository string archiveOutputPath = TestSettings.TestArtifactsDirectory; string expectedCreatedFilePath = Path.Combine(TestSettings.TestArtifactsDirectory, "repository.tar.gz"); - await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false); + await CreateRegistryAndCallLoadAsync(archiveOutputPath); Assert.True(File.Exists(expectedCreatedFilePath)); } @@ -28,7 +28,7 @@ public async Task ArchiveOutputPathIsNonExistingDirectory_CreatesDirectoryAndFil "nonexisting" + (includeDirectorySeperatorAtTheEnd ? Path.DirectorySeparatorChar : "")); string expectedCreatedFilePath = Path.Combine(archiveOutputPath, "repository.tar.gz"); - await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false); + await CreateRegistryAndCallLoadAsync(archiveOutputPath); Assert.True(File.Exists(expectedCreatedFilePath)); } @@ -39,7 +39,7 @@ public async Task ArchiveOutputPathIsCustomFileNameInExistingDirectory_CreatesFi string archiveOutputPath = Path.Combine(TestSettings.TestArtifactsDirectory, "custom-name.withextension"); string expectedCreatedFilePath = archiveOutputPath; - await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false); + await CreateRegistryAndCallLoadAsync(archiveOutputPath); Assert.True(File.Exists(expectedCreatedFilePath)); } @@ -50,7 +50,7 @@ public async Task ArchiveOutputPathIsCustomFileNameInNonExistingDirectory_Create string archiveOutputPath = Path.Combine(TestSettings.TestArtifactsDirectory, $"nonexisting-directory{Path.AltDirectorySeparatorChar}custom-name.withextension"); string expectedCreatedFilePath = archiveOutputPath; - await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false); + await CreateRegistryAndCallLoadAsync(archiveOutputPath); Assert.True(File.Exists(expectedCreatedFilePath)); } @@ -68,6 +68,6 @@ await registry.LoadAsync( async (img, srcRef, destRef, stream, token) => { await Task.CompletedTask; - }).ConfigureAwait(false); + }); } } \ No newline at end of file From 08f9eb5ac6aa7e8ba5932cd83f6404aebf9052a6 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 13 Feb 2025 02:46:37 +0000 Subject: [PATCH 69/74] Update dependencies from https://github.com/dotnet/roslyn build 20250212.7 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.CodeAnalysis.CSharp.CodeStyle , Microsoft.CodeAnalysis.CSharp.Features , Microsoft.CodeAnalysis.CSharp.Workspaces , Microsoft.CodeAnalysis.Workspaces.MSBuild , Microsoft.Net.Compilers.Toolset , Microsoft.Net.Compilers.Toolset.Framework From Version 4.13.0-3.25105.14 -> To Version 4.13.0-3.25112.7 --- eng/Version.Details.xml | 36 ++++++++++++++++++------------------ eng/Versions.props | 16 ++++++++-------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7148e3211739..3e74c319300d 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -93,43 +93,43 @@ 3d780e06fbae1bc106cb90088fa9605ea4a42eed - + https://github.com/dotnet/roslyn - 398fa9b7175f642be13efcda93c6ab1187934a6a + afe5bc4a827030229ab79ec596b3cb91d4e87872 - + https://github.com/dotnet/roslyn - 398fa9b7175f642be13efcda93c6ab1187934a6a + afe5bc4a827030229ab79ec596b3cb91d4e87872 - + https://github.com/dotnet/roslyn - 398fa9b7175f642be13efcda93c6ab1187934a6a + afe5bc4a827030229ab79ec596b3cb91d4e87872 - + https://github.com/dotnet/roslyn - 398fa9b7175f642be13efcda93c6ab1187934a6a + afe5bc4a827030229ab79ec596b3cb91d4e87872 - + https://github.com/dotnet/roslyn - 398fa9b7175f642be13efcda93c6ab1187934a6a + afe5bc4a827030229ab79ec596b3cb91d4e87872 - + https://github.com/dotnet/roslyn - 398fa9b7175f642be13efcda93c6ab1187934a6a + afe5bc4a827030229ab79ec596b3cb91d4e87872 - + https://github.com/dotnet/roslyn - 398fa9b7175f642be13efcda93c6ab1187934a6a + afe5bc4a827030229ab79ec596b3cb91d4e87872 - + https://github.com/dotnet/roslyn - 398fa9b7175f642be13efcda93c6ab1187934a6a + afe5bc4a827030229ab79ec596b3cb91d4e87872 - + https://github.com/dotnet/roslyn - 398fa9b7175f642be13efcda93c6ab1187934a6a + afe5bc4a827030229ab79ec596b3cb91d4e87872 https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore diff --git a/eng/Versions.props b/eng/Versions.props index 25b8d4ec479f..21ff24a33862 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -202,14 +202,14 @@ - 4.13.0-3.25105.14 - 4.13.0-3.25105.14 - 4.13.0-3.25105.14 - 4.13.0-3.25105.14 - 4.13.0-3.25105.14 - 4.13.0-3.25105.14 - 4.13.0-3.25105.14 - 4.13.0-3.25105.14 + 4.13.0-3.25112.7 + 4.13.0-3.25112.7 + 4.13.0-3.25112.7 + 4.13.0-3.25112.7 + 4.13.0-3.25112.7 + 4.13.0-3.25112.7 + 4.13.0-3.25112.7 + 4.13.0-3.25112.7 From 4e9565d6aa738b03ceff979ac40b5a441bd67686 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Fri, 14 Feb 2025 16:10:58 -0600 Subject: [PATCH 70/74] Add test case for adding projects outside solution dir descendants (#46835) --- .../Dir/App.sln | 5 ++++ .../Dir/App.slnx | 1 + .../Lib/Lib.csproj | 7 ++++++ .../Lib/Library.cs | 15 ++++++++++++ test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs | 23 +++++++++++++++++++ 5 files changed, 51 insertions(+) create mode 100644 test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Dir/App.sln create mode 100644 test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Dir/App.slnx create mode 100644 test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Lib/Lib.csproj create mode 100644 test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Lib/Library.cs diff --git a/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Dir/App.sln b/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Dir/App.sln new file mode 100644 index 000000000000..8eca2536691f --- /dev/null +++ b/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Dir/App.sln @@ -0,0 +1,5 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 diff --git a/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Dir/App.slnx b/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Dir/App.slnx new file mode 100644 index 000000000000..4e2253ddceed --- /dev/null +++ b/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Dir/App.slnx @@ -0,0 +1 @@ + diff --git a/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Lib/Lib.csproj b/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Lib/Lib.csproj new file mode 100644 index 000000000000..c350d4032840 --- /dev/null +++ b/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Lib/Lib.csproj @@ -0,0 +1,7 @@ + + + + netstandard1.4 + + + diff --git a/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Lib/Library.cs b/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Lib/Library.cs new file mode 100644 index 000000000000..71a4d48322c7 --- /dev/null +++ b/test/TestAssets/TestProjects/TestAppWithSlnAndCsprojInParentDir/Lib/Library.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace Lib +{ + public class Library + { + public static string GetMessage() + { + return "Message from Lib"; + } + } +} diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs b/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs index 6009f9c04b97..e22f2f979cda 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs @@ -1096,6 +1096,29 @@ public void WhenSolutionFolderIsPassedWithDirectorySeparatorFolderStructureIsCor .Should().BeVisuallyEquivalentTo(expectedSlnContents); } + [Theory] + [InlineData("sln", ".sln")] + [InlineData("sln", ".slnx")] + [InlineData("solution", ".sln")] + [InlineData("solution", ".slnx")] + public async Task WhenAddingProjectOutsideDirectoryItShouldNotAddSolutionFolders(string solutionCommand, string solutionExtension) + { + var projectDirectory = _testAssetsManager + .CopyTestAsset("TestAppWithSlnAndCsprojInParentDir", identifier: $"GivenDotnetSlnAdd-{solutionCommand}{solutionExtension}") + .WithSource() + .Path; + var projectToAdd = Path.Combine("..", "Lib", "Lib.csproj"); + var cmd = new DotnetCommand(Log) + .WithWorkingDirectory(Path.Join(projectDirectory, "Dir")) + .Execute(solutionCommand, $"App{solutionExtension}", "add", projectToAdd); + cmd.Should().Pass(); + // Should have no solution folders + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(Path.Join(projectDirectory, "Dir", $"App{solutionExtension}")); + SolutionModel solution = await serializer.OpenAsync(Path.Join(projectDirectory, "Dir", $"App{solutionExtension}"), CancellationToken.None); + solution.SolutionProjects.Count.Should().Be(1); + solution.SolutionFolders.Count.Should().Be(0); + } + private string GetExpectedSlnContents( string slnPath, string slnTemplateName, From c3f86d6bde68b8f105f423938eedce72d2303fb2 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Tue, 18 Feb 2025 05:02:14 +0000 Subject: [PATCH 71/74] Update dependencies from https://github.com/microsoft/vstest build 20250217.1 Microsoft.SourceBuild.Intermediate.vstest , Microsoft.NET.Test.Sdk , Microsoft.TestPlatform.Build , Microsoft.TestPlatform.CLI From Version 17.14.0-preview-25115-01 -> To Version 17.14.0-preview-25117-01 --- eng/Version.Details.xml | 16 ++++++++-------- eng/Versions.props | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a7b2643ed958..3e6734a665ec 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -208,22 +208,22 @@ https://github.com/nuget/nuget.client c4b26195ee5a77e70b2ea5fd50db87d6a9194c24 - + https://github.com/microsoft/vstest - 483a29c033b2b1a59f61f03ff1d3ee2c25995ead + fa1172341736fdcab08a591fe4e5a6fb71117ad2 - + https://github.com/microsoft/vstest - 483a29c033b2b1a59f61f03ff1d3ee2c25995ead + fa1172341736fdcab08a591fe4e5a6fb71117ad2 - + https://github.com/microsoft/vstest - 483a29c033b2b1a59f61f03ff1d3ee2c25995ead + fa1172341736fdcab08a591fe4e5a6fb71117ad2 - + https://github.com/microsoft/vstest - 483a29c033b2b1a59f61f03ff1d3ee2c25995ead + fa1172341736fdcab08a591fe4e5a6fb71117ad2 diff --git a/eng/Versions.props b/eng/Versions.props index 7a5f7c4a8faa..dcee4041d73d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -150,9 +150,9 @@ - 17.14.0-preview-25115-01 - 17.14.0-preview-25115-01 - 17.14.0-preview-25115-01 + 17.14.0-preview-25117-01 + 17.14.0-preview-25117-01 + 17.14.0-preview-25117-01 From 857d4548b2bf03d357b7bb94743172ab4af91ed4 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Tue, 18 Feb 2025 05:02:16 +0000 Subject: [PATCH 72/74] Update dependencies from https://github.com/dotnet/roslyn build 20250217.2 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.CodeAnalysis.CSharp.CodeStyle , Microsoft.CodeAnalysis.CSharp.Features , Microsoft.CodeAnalysis.CSharp.Workspaces , Microsoft.CodeAnalysis.Workspaces.MSBuild , Microsoft.Net.Compilers.Toolset , Microsoft.Net.Compilers.Toolset.Framework From Version 4.14.0-2.25115.2 -> To Version 4.14.0-2.25117.2 --- eng/Version.Details.xml | 36 ++++++++++++++++++------------------ eng/Versions.props | 16 ++++++++-------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a7b2643ed958..a79638d6d4d2 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -93,43 +93,43 @@ 387f04cb6675c984036a64c9bb768ac6f00e06f9 - + https://github.com/dotnet/roslyn - d862b947d551d420a2363e4c7d457e768b2fa22c + 7561e17a55b6515c0ba7e1da576aab82c2caec87 - + https://github.com/dotnet/roslyn - d862b947d551d420a2363e4c7d457e768b2fa22c + 7561e17a55b6515c0ba7e1da576aab82c2caec87 - + https://github.com/dotnet/roslyn - d862b947d551d420a2363e4c7d457e768b2fa22c + 7561e17a55b6515c0ba7e1da576aab82c2caec87 - + https://github.com/dotnet/roslyn - d862b947d551d420a2363e4c7d457e768b2fa22c + 7561e17a55b6515c0ba7e1da576aab82c2caec87 - + https://github.com/dotnet/roslyn - d862b947d551d420a2363e4c7d457e768b2fa22c + 7561e17a55b6515c0ba7e1da576aab82c2caec87 - + https://github.com/dotnet/roslyn - d862b947d551d420a2363e4c7d457e768b2fa22c + 7561e17a55b6515c0ba7e1da576aab82c2caec87 - + https://github.com/dotnet/roslyn - d862b947d551d420a2363e4c7d457e768b2fa22c + 7561e17a55b6515c0ba7e1da576aab82c2caec87 - + https://github.com/dotnet/roslyn - d862b947d551d420a2363e4c7d457e768b2fa22c + 7561e17a55b6515c0ba7e1da576aab82c2caec87 - + https://github.com/dotnet/roslyn - d862b947d551d420a2363e4c7d457e768b2fa22c + 7561e17a55b6515c0ba7e1da576aab82c2caec87 https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore diff --git a/eng/Versions.props b/eng/Versions.props index 7a5f7c4a8faa..126b6cdd2e88 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -202,14 +202,14 @@ - 4.14.0-2.25115.2 - 4.14.0-2.25115.2 - 4.14.0-2.25115.2 - 4.14.0-2.25115.2 - 4.14.0-2.25115.2 - 4.14.0-2.25115.2 - 4.14.0-2.25115.2 - 4.14.0-2.25115.2 + 4.14.0-2.25117.2 + 4.14.0-2.25117.2 + 4.14.0-2.25117.2 + 4.14.0-2.25117.2 + 4.14.0-2.25117.2 + 4.14.0-2.25117.2 + 4.14.0-2.25117.2 + 4.14.0-2.25117.2 From cbb033cd711e4258f3186d95a1d0489cdb6e7bbe Mon Sep 17 00:00:00 2001 From: Jason Zhai Date: Tue, 18 Feb 2025 01:38:58 -0800 Subject: [PATCH 73/74] Remove duplicate node --- .../Resources/Strings.Designer.cs | 9 --------- .../Resources/Strings.resx | 4 ---- .../Tasks/CreateNewImage.cs | 18 ------------------ 3 files changed, 31 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs index ec448c987e9b..4830ae754c41 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs @@ -393,15 +393,6 @@ internal static string ImagesEmpty { } } - /// - /// Looks up a localized string similar to CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.. - /// - internal static string InvalidContainerImageFormat { - get { - return ResourceManager.GetString("InvalidContainerImageFormat", resourceCulture); - } - } - /// /// Looks up a localized string similar to CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.. /// diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx index 973354faffe8..05e8baba0154 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx @@ -489,8 +489,4 @@ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. {StrBegins="CONTAINER2031: "} - - CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'. - {StrBegins="CONTAINER2031: "} - diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index f981281448c7..4c01694e7604 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -143,24 +143,6 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) } } - // forcibly change the media type if required - if (ImageFormat is not null) - { - if (Enum.TryParse(ImageFormat, out var imageFormat)) - { - imageBuilder.ManifestMediaType = imageFormat switch - { - KnownImageFormats.Docker => SchemaTypes.DockerManifestV2, - KnownImageFormats.OCI => SchemaTypes.OciManifestV1, - _ => imageBuilder.ManifestMediaType // should be impossible unless we add to the enum - }; - } - else - { - Log.LogErrorWithCodeFromResources(nameof(Strings.InvalidContainerImageFormat), ImageFormat, string.Join(",", Enum.GetValues())); - } - } - Layer newLayer = Layer.FromDirectory(PublishDirectory, WorkingDirectory, imageBuilder.IsWindows, imageBuilder.ManifestMediaType); imageBuilder.AddLayer(newLayer); imageBuilder.SetWorkingDirectory(WorkingDirectory); From 8f39695024abf46abf2551e038057d2eb70f26a2 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Tue, 18 Feb 2025 13:29:55 +0100 Subject: [PATCH 74/74] fix Microsoft.NET.Build.Containers.targets --- .../packaging/build/Microsoft.NET.Build.Containers.targets | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets index c91134a3545b..694e1dc8372f 100644 --- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets +++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets @@ -257,7 +257,6 @@ BaseImageTag="$(ContainerBaseTag)" ImageFormat="$(ContainerImageFormat)" BaseImageDigest="$(ContainerBaseDigest)" - ImageFormat="$(ContainerImageFormat)" LocalRegistry="$(LocalRegistry)" OutputRegistry="$(ContainerRegistry)" ArchiveOutputPath="$(ContainerArchiveOutputPath)"