From 419231f1506dd30dcf448009f5b89079c0050352 Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Mon, 20 Jan 2025 10:28:57 -0600 Subject: [PATCH] Allow users to set a specific ContainerImageFormat to force OCI support (#46011) --- .../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 | 193 +++++++++--------- .../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 | 5 + .../Tasks/CreateNewImage.cs | 18 ++ .../Tasks/CreateNewImageToolTask.cs | 4 + .../containerize/ContainerizeCommand.cs | 8 + .../Microsoft.NET.Build.Containers.targets | 7 +- .../CreateNewImageTests.cs | 66 ++++++ 26 files changed, 309 insertions(+), 101 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 bf0876344f28..ab6ef7b6297a 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs @@ -19,7 +19,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; @@ -32,15 +31,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; } @@ -70,14 +69,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 43526e9c3ab4..e5ae448e2926 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 @@ -88,6 +88,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.get -> b Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.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 b19e4fa81718..0c9a31518b74 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 @@ -241,6 +241,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.get -> b Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.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 aff92942f408..9cf5fb285768 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 CONTAINER3001: Failed creating {0} process.. /// @@ -212,7 +212,7 @@ internal static string ContainerRuntimeProcessCreationFailed { return ResourceManager.GetString("ContainerRuntimeProcessCreationFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1007: Could not deserialize token from JSON.. /// @@ -221,7 +221,7 @@ internal static string CouldntDeserializeJsonToken { return ResourceManager.GetString("CouldntDeserializeJsonToken", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2012: Could not recognize registry '{0}'.. /// @@ -230,7 +230,7 @@ internal static string CouldntRecognizeRegistry { return ResourceManager.GetString("CouldntRecognizeRegistry", resourceCulture); } } - + /// /// Looks up a localized string similar to local registry via '{0}'. /// @@ -239,7 +239,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}. /// @@ -248,7 +248,7 @@ internal static string DockerInfoFailed { return ResourceManager.GetString("DockerInfoFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER3002: Failed to get docker info: {0}. /// @@ -257,7 +257,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.. /// @@ -266,7 +266,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.. /// @@ -275,7 +275,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}.. /// @@ -284,7 +284,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.. /// @@ -293,7 +293,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.. /// @@ -302,7 +302,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}'.. /// @@ -311,7 +311,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}.. /// @@ -320,7 +320,7 @@ internal static string EntrypointSetNoAppCommandInstruction { return ResourceManager.GetString("EntrypointSetNoAppCommandInstruction", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1008: Failed retrieving credentials for "{0}": {1}. /// @@ -329,7 +329,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.. /// @@ -338,7 +338,7 @@ internal static string GenerateDigestLabelWithoutGenerateLabels { return ResourceManager.GetString("GenerateDigestLabelWithoutGenerateLabels", resourceCulture); } } - + /// /// Looks up a localized string similar to No host object detected.. /// @@ -347,7 +347,7 @@ internal static string HostObjectNotDetected { return ResourceManager.GetString("HostObjectNotDetected", resourceCulture); } } - + /// /// Looks up a localized string similar to Pushed image index '{0}' to registry '{1}'.. /// @@ -356,7 +356,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}. /// @@ -365,7 +365,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.. /// @@ -374,7 +374,7 @@ internal static string ImagePullNotSupported { return ResourceManager.GetString("ImagePullNotSupported", resourceCulture); } } - + /// /// Looks up a localized string similar to Cannot create manifest list (image index) because no images were provided.. /// @@ -383,7 +383,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.. /// @@ -392,7 +401,7 @@ internal static string InvalidEnvVar { return ResourceManager.GetString("InvalidEnvVar", resourceCulture); } } - + /// /// 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.. /// @@ -401,7 +410,7 @@ internal static string InvalidImageMetadata { return ResourceManager.GetString("InvalidImageMetadata", 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.. /// @@ -410,7 +419,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 _.. /// @@ -419,7 +428,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'.. /// @@ -428,7 +437,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'.. /// @@ -437,7 +446,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'.. /// @@ -446,7 +455,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.. /// @@ -455,7 +464,7 @@ internal static string InvalidSdkPrereleaseVersion { return ResourceManager.GetString("InvalidSdkPrereleaseVersion", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2019: Invalid SDK semantic version '{0}'.. /// @@ -464,7 +473,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.. /// @@ -473,7 +482,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.. /// @@ -482,7 +491,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.. /// @@ -491,7 +500,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.. /// @@ -500,7 +509,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.. /// @@ -509,7 +518,7 @@ internal static string ItemsWithoutMetadata { return ResourceManager.GetString("ItemsWithoutMetadata", resourceCulture); } } - + /// /// Looks up a localized string similar to Error while reading daemon config: {0}. /// @@ -518,7 +527,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}. /// @@ -527,7 +536,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.. /// @@ -536,7 +545,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.. /// @@ -545,7 +554,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" />'. /// @@ -554,7 +563,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 manifest list (image index).. /// @@ -563,7 +572,7 @@ internal static string MixedMediaTypes { return ResourceManager.GetString("MixedMediaTypes", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1004: No RequestUri specified.. /// @@ -572,7 +581,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}'. /// @@ -581,7 +590,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.. /// @@ -590,7 +599,7 @@ internal static string OciImageMultipleTagsNotSupported { return ResourceManager.GetString("OciImageMultipleTagsNotSupported", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2011: {0} '{1}' does not exist. /// @@ -599,7 +608,7 @@ internal static string PublishDirectoryDoesntExist { return ResourceManager.GetString("PublishDirectoryDoesntExist", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploaded config to registry.. /// @@ -608,7 +617,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}',. /// @@ -617,7 +626,7 @@ internal static string Registry_ConfigUploadStarted { return ResourceManager.GetString("Registry_ConfigUploadStarted", resourceCulture); } } - + /// /// Looks up a localized string similar to Layer '{0}' already exists.. /// @@ -626,7 +635,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}'.. /// @@ -635,7 +644,7 @@ internal static string Registry_LayerUploaded { return ResourceManager.GetString("Registry_LayerUploaded", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploading layer '{0}' to '{1}'.. /// @@ -644,7 +653,7 @@ internal static string Registry_LayerUploadStarted { return ResourceManager.GetString("Registry_LayerUploadStarted", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploaded manifest to '{0}'.. /// @@ -653,7 +662,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}'.. /// @@ -662,7 +671,7 @@ internal static string Registry_ManifestUploadStarted { return ResourceManager.GetString("Registry_ManifestUploadStarted", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploaded tag '{0}' to '{1}'.. /// @@ -671,7 +680,7 @@ internal static string Registry_TagUploaded { return ResourceManager.GetString("Registry_TagUploaded", resourceCulture); } } - + /// /// Looks up a localized string similar to Uploading tag '{0}' to '{1}'.. /// @@ -680,7 +689,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}'.. /// @@ -689,7 +698,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}. /// @@ -698,7 +707,7 @@ internal static string RegistryOutputPushFailed { return ResourceManager.GetString("RegistryOutputPushFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1014: Manifest pull failed.. /// @@ -707,7 +716,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}'.. /// @@ -716,7 +725,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.. /// @@ -725,7 +734,7 @@ internal static string RepositoryNotFound { return ResourceManager.GetString("RepositoryNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER4003: Required '{0}' items contain empty items.. /// @@ -734,7 +743,7 @@ internal static string RequiredItemsContainsEmptyItems { return ResourceManager.GetString("RequiredItemsContainsEmptyItems", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER4002: Required '{0}' items were not set.. /// @@ -743,7 +752,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.. /// @@ -752,7 +761,7 @@ internal static string RequiredPropertyNotSetOrEmpty { return ResourceManager.GetString("RequiredPropertyNotSetOrEmpty", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER1006: Too many retries, stopping.. /// @@ -761,7 +770,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.. /// @@ -770,7 +779,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}.. /// @@ -779,7 +788,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}.. /// @@ -788,7 +797,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.. /// @@ -797,7 +806,7 @@ internal static string UnknownMediaType { return ResourceManager.GetString("UnknownMediaType", resourceCulture); } } - + /// /// Looks up a localized string similar to CONTAINER2001: Unrecognized mediaType '{0}'.. /// @@ -806,7 +815,7 @@ internal static string UnrecognizedMediaType { return ResourceManager.GetString("UnrecognizedMediaType", resourceCulture); } } - + /// /// Looks up a localized string similar to Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.. /// @@ -815,7 +824,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 6337f4861b8e..7c380a773631 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx +++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx @@ -456,4 +456,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 f0d7173a9cea..b7e4f94bf104 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 ea8fc611e0a0..ba06e503ec2f 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 b4921dcbdf47..560e0a1d19ee 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 b945393a8398..025505a858c8 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 b42327bbfb94..26acfa704770 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 5d2cf22f0f8c..7ef77e9c2d8a 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 2ff624cec676..c304b3f32196 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 86288597ee7c..709717fe0ddc 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 69fcf3548d8d..cd79cf7c9874 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 2989bc8e9687..1724284e113f 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 42fcb52485c7..e36b2b182083 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 8eb6429f7db6..b0e7ab261720 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 892b174bbe97..757daf6dcf96 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 @@ -179,6 +179,11 @@ Cannot create manifest list (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 3da5135b9b07..47af9a2aedd9 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; } + /// + /// 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; } + [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 25ba26080af9..285e1de9c8c5 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -124,6 +124,24 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) SafeLog(Strings.ContainerBuilder_StartBuildingImage, Repository, String.Join(",", ImageTags), sourceImageReference); + // 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 1b6ad491c8af..ba1e7c60e6c5 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)" @@ -297,9 +298,9 @@ 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();