From d1036ee09fa79b4f42ad94b7084e74eda7c1d65c Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Wed, 21 Dec 2022 13:51:14 -0600 Subject: [PATCH] re-add runtime inheritance for manifest list runtimes --- .../ContainerBuilder.cs | 4 +-- .../ContentStore.cs | 2 ++ .../CreateNewImage.Interface.cs | 7 +++++ .../CreateNewImage.cs | 3 ++- .../CreateNewImageToolTask.cs | 3 ++- Microsoft.NET.Build.Containers/Registry.cs | 27 ++++++++++++------- containerize/Program.cs | 7 +++-- .../Microsoft.NET.Build.Containers.targets | 5 ++-- 8 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Microsoft.NET.Build.Containers/ContainerBuilder.cs b/Microsoft.NET.Build.Containers/ContainerBuilder.cs index 488f5607..381ffb58 100644 --- a/Microsoft.NET.Build.Containers/ContainerBuilder.cs +++ b/Microsoft.NET.Build.Containers/ContainerBuilder.cs @@ -7,7 +7,7 @@ namespace Microsoft.NET.Build.Containers; public static class ContainerBuilder { - public static async Task Containerize(DirectoryInfo folder, string workingDir, string registryName, string baseName, string baseTag, string[] entrypoint, string[] entrypointArgs, string imageName, string[] imageTags, string outputRegistry, string[] labels, Port[] exposedPorts, string[] envVars, string containerRuntimeIdentifier) + public static async Task Containerize(DirectoryInfo folder, string workingDir, string registryName, string baseName, string baseTag, string[] entrypoint, string[] entrypointArgs, string imageName, string[] imageTags, string outputRegistry, string[] labels, Port[] exposedPorts, string[] envVars, string containerRuntimeIdentifier, string ridGraphPath) { var isDockerPull = String.IsNullOrEmpty(registryName); if (isDockerPull) { @@ -15,7 +15,7 @@ public static async Task Containerize(DirectoryInfo folder, string workingDir, s } Registry baseRegistry = new Registry(ContainerHelpers.TryExpandRegistryToUri(registryName)); - var img = await baseRegistry.GetImageManifest(baseName, baseTag, containerRuntimeIdentifier); + var img = await baseRegistry.GetImageManifest(baseName, baseTag, containerRuntimeIdentifier, ridGraphPath); if (img is null) { throw new ArgumentException($"Could not find image {baseName}:{baseTag} in registry {registryName} matching RuntimeIdentifier {containerRuntimeIdentifier}"); } diff --git a/Microsoft.NET.Build.Containers/ContentStore.cs b/Microsoft.NET.Build.Containers/ContentStore.cs index 76771900..7c901d28 100644 --- a/Microsoft.NET.Build.Containers/ContentStore.cs +++ b/Microsoft.NET.Build.Containers/ContentStore.cs @@ -34,9 +34,11 @@ public static string PathForDescriptor(Descriptor descriptor) { "application/vnd.docker.image.rootfs.diff.tar.gzip" or "application/vnd.oci.image.layer.v1.tar+gzip" + or "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip" => ".tar.gz", "application/vnd.docker.image.rootfs.diff.tar" or "application/vnd.oci.image.layer.v1.tar" + or ":application/vnd.docker.image.rootfs.foreign.diff.tar" => ".tar", _ => throw new ArgumentException($"Unrecognized mediaType '{descriptor.MediaType}'") }; diff --git a/Microsoft.NET.Build.Containers/CreateNewImage.Interface.cs b/Microsoft.NET.Build.Containers/CreateNewImage.Interface.cs index cf366d05..2a394cd7 100644 --- a/Microsoft.NET.Build.Containers/CreateNewImage.Interface.cs +++ b/Microsoft.NET.Build.Containers/CreateNewImage.Interface.cs @@ -99,6 +99,11 @@ partial class CreateNewImage [Required] public string ContainerRuntimeIdentifier { get; set; } + /// + /// The path to the runtime identifier graph file. This is used to compute RID compatibility for Image Manifest List entries. + /// + [Required] + public string RuntimeIdentifierGraphPath { get; set; } [Output] public string GeneratedContainerManifest { get; set; } @@ -106,6 +111,7 @@ partial class CreateNewImage [Output] public string GeneratedContainerConfiguration { get; set; } + public CreateNewImage() { ContainerizeDirectory = ""; @@ -125,6 +131,7 @@ public CreateNewImage() ExposedPorts = Array.Empty(); ContainerEnvironmentVariables = Array.Empty(); ContainerRuntimeIdentifier = ""; + RuntimeIdentifierGraphPath = ""; GeneratedContainerConfiguration = ""; GeneratedContainerManifest = ""; diff --git a/Microsoft.NET.Build.Containers/CreateNewImage.cs b/Microsoft.NET.Build.Containers/CreateNewImage.cs index 9b305510..abbd3ba3 100644 --- a/Microsoft.NET.Build.Containers/CreateNewImage.cs +++ b/Microsoft.NET.Build.Containers/CreateNewImage.cs @@ -78,7 +78,7 @@ private void SetEnvironmentVariables(Image img, ITaskItem[] envVars) throw new ArgumentException("Don't know how to pull images from local daemons at the moment"); } else { var reg = new Registry(ContainerHelpers.TryExpandRegistryToUri(BaseRegistry)); - return reg.GetImageManifest(BaseImageName, BaseImageTag, ContainerRuntimeIdentifier).Result; + return reg.GetImageManifest(BaseImageName, BaseImageTag, ContainerRuntimeIdentifier, RuntimeIdentifierGraphPath).Result; } } @@ -88,6 +88,7 @@ private void SafeLog(string message, params object[] formatParams) { public override bool Execute() { + System.Diagnostics.Debugger.Launch(); if (!Directory.Exists(PublishDirectory)) { Log.LogError("{0} '{1}' does not exist", nameof(PublishDirectory), PublishDirectory); diff --git a/Microsoft.NET.Build.Containers/CreateNewImageToolTask.cs b/Microsoft.NET.Build.Containers/CreateNewImageToolTask.cs index b77276ec..95dcccf9 100644 --- a/Microsoft.NET.Build.Containers/CreateNewImageToolTask.cs +++ b/Microsoft.NET.Build.Containers/CreateNewImageToolTask.cs @@ -83,7 +83,8 @@ protected override string GenerateCommandLineCommands() (ImageTags.Length > 0 ? " --imagetags " + String.Join(" ", ImageTags.Select((i) => Quote(i))) : "") + (EntrypointArgs.Length > 0 ? " --entrypointargs " + String.Join(" ", EntrypointArgs.Select((i) => i.ItemSpec)) : "") + (ExposedPorts.Length > 0 ? " --ports " + String.Join(" ", ExposedPorts.Select((i) => i.ItemSpec + "/" + i.GetMetadata("Type"))) : "") + - (ContainerEnvironmentVariables.Length > 0 ? " --environmentvariables " + String.Join(" ", ContainerEnvironmentVariables.Select((i) => i.ItemSpec + "=" + Quote(i.GetMetadata("Value")))) : ""); + (ContainerEnvironmentVariables.Length > 0 ? " --environmentvariables " + String.Join(" ", ContainerEnvironmentVariables.Select((i) => i.ItemSpec + "=" + Quote(i.GetMetadata("Value")))) : "") + + $" --ridgraphpath {Quote(RuntimeIdentifierGraphPath)}"; } private string Quote(string path) diff --git a/Microsoft.NET.Build.Containers/Registry.cs b/Microsoft.NET.Build.Containers/Registry.cs index ed8b1bf9..4279fb09 100644 --- a/Microsoft.NET.Build.Containers/Registry.cs +++ b/Microsoft.NET.Build.Containers/Registry.cs @@ -38,7 +38,7 @@ public record Registry(Uri BaseUri) private string RegistryName { get; } = BaseUri.Host; - public async Task GetImageManifest(string name, string reference, string runtimeIdentifier) + public async Task GetImageManifest(string name, string reference, string runtimeIdentifier, string runtimeIdentifierGraphPath) { var client = GetClient(); var initialManifestResponse = await GetManifest(reference); @@ -62,7 +62,8 @@ public record Registry(Uri BaseUri) } async Task TryPickBestImageFromManifestList(ManifestListV2 manifestList, string runtimeIdentifier) { - var (ridDict, graphForManifestList) = ConstructRuntimeGraphForManifestList(manifestList); + var runtimeGraph = GetRuntimeGraphForDotNet(runtimeIdentifierGraphPath); + var (ridDict, graphForManifestList) = ConstructRuntimeGraphForManifestList(manifestList, runtimeGraph); var bestManifestRid = CheckIfRidExistsInGraph(graphForManifestList, runtimeIdentifier); if (bestManifestRid is null) { throw new ArgumentException($"The runtimeIdentifier '{runtimeIdentifier}' is not supported. The supported RuntimeIdentifiers for the base image {name}:{reference} are {String.Join(",", graphForManifestList.Runtimes.Keys)}"); @@ -89,21 +90,18 @@ async Task GetBlob(string digest) } } - private string? CheckIfRidExistsInGraph(RuntimeGraph graphForManifestList, string userRid) - { - graphForManifestList.Runtimes.TryGetValue(userRid, out var runtimeInfo); - return runtimeInfo?.RuntimeIdentifier; - } + private string? CheckIfRidExistsInGraph(RuntimeGraph graphForManifestList, string userRid) => graphForManifestList.Runtimes.FirstOrDefault(kvp => graphForManifestList.AreCompatible(kvp.Key, userRid)).Key; - private (IReadOnlyDictionary, RuntimeGraph) ConstructRuntimeGraphForManifestList(ManifestListV2 manifestList) + private (IReadOnlyDictionary, RuntimeGraph) ConstructRuntimeGraphForManifestList(ManifestListV2 manifestList, RuntimeGraph dotnetRuntimeGraph) { var ridDict = new Dictionary(); var runtimeDescriptionSet = new HashSet(); foreach (var manifest in manifestList.manifests) { if (CreateRidForPlatform(manifest.platform) is { } rid) { - ridDict.TryAdd(rid, manifest); - runtimeDescriptionSet.Add(new RuntimeDescription(rid)); + if (ridDict.TryAdd(rid, manifest)) { + AddRidAndDescendantsToSet(runtimeDescriptionSet, rid, dotnetRuntimeGraph); + } } } @@ -139,6 +137,15 @@ async Task GetBlob(string digest) return $"{osPart}{versionPart ?? ""}-{platformPart}"; } + private RuntimeGraph GetRuntimeGraphForDotNet(string ridGraphPath) => JsonRuntimeFormat.ReadRuntimeGraph(ridGraphPath); + + private void AddRidAndDescendantsToSet(HashSet runtimeDescriptionSet, string rid, RuntimeGraph dotnetRuntimeGraph) + { + var R = dotnetRuntimeGraph.Runtimes[rid]; + runtimeDescriptionSet.Add(R); + foreach (var r in R.InheritedRuntimes) AddRidAndDescendantsToSet(runtimeDescriptionSet, r, dotnetRuntimeGraph); + } + /// /// Ensure a blob associated with from the registry is available locally. /// diff --git a/containerize/Program.cs b/containerize/Program.cs index 71a9de35..3e775c6c 100644 --- a/containerize/Program.cs +++ b/containerize/Program.cs @@ -153,6 +153,7 @@ var ridOpt = new Option(name: "--rid", description: "Runtime Identifier of the generated container."); +var ridGraphPathOpt = new Option(name: "--ridgraphpath", description: "Path to the RID graph file."); RootCommand root = new RootCommand("Containerize an application without Docker.") { @@ -169,7 +170,8 @@ labelsOpt, portsOpt, envVarsOpt, - ridOpt + ridOpt, + ridGraphPathOpt }; root.SetHandler(async (context) => @@ -188,7 +190,8 @@ Port[] _ports = context.ParseResult.GetValueForOption(portsOpt) ?? Array.Empty(); string[] _envVars = context.ParseResult.GetValueForOption(envVarsOpt) ?? Array.Empty(); string _rid = context.ParseResult.GetValueForOption(ridOpt) ?? ""; - await ContainerBuilder.Containerize(_publishDir, _workingDir, _baseReg, _baseName, _baseTag, _entrypoint, _entrypointArgs, _name, _tags, _outputReg, _labels, _ports, _envVars, _rid); + string _ridGraphPath = context.ParseResult.GetValueForOption(ridGraphPathOpt) ?? ""; + await ContainerBuilder.Containerize(_publishDir, _workingDir, _baseReg, _baseName, _baseTag, _entrypoint, _entrypointArgs, _name, _tags, _outputReg, _labels, _ports, _envVars, _rid, _ridGraphPath); }); return await root.InvokeAsync(args); diff --git a/packaging/build/Microsoft.NET.Build.Containers.targets b/packaging/build/Microsoft.NET.Build.Containers.targets index 46d4d9e9..ae838494 100644 --- a/packaging/build/Microsoft.NET.Build.Containers.targets +++ b/packaging/build/Microsoft.NET.Build.Containers.targets @@ -62,7 +62,7 @@ /app - $(RuntimeIdentifier) + $(RuntimeIdentifier) $(NETCoreSdkPortableRuntimeIdentifier) @@ -134,7 +134,8 @@ Labels="@(ContainerLabel)" ExposedPorts="@(ContainerPort)" ContainerEnvironmentVariables="@(ContainerEnvironmentVariables)" - ContainerRuntimeIdentifier="$(ContainerRuntimeIdentifier)"> + ContainerRuntimeIdentifier="$(ContainerRuntimeIdentifier)" + RuntimeIdentifierGraphPath="$(RuntimeIdentifierGraphPath)">