Skip to content

Commit

Permalink
re-add runtime inheritance for manifest list runtimes
Browse files Browse the repository at this point in the history
  • Loading branch information
baronfel committed Dec 21, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 08c09df commit d1036ee
Showing 8 changed files with 40 additions and 18 deletions.
4 changes: 2 additions & 2 deletions Microsoft.NET.Build.Containers/ContainerBuilder.cs
Original file line number Diff line number Diff line change
@@ -7,15 +7,15 @@ 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) {
throw new ArgumentException("Don't know how to pull images from local daemons at the moment");
}
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}");
}
2 changes: 2 additions & 0 deletions Microsoft.NET.Build.Containers/ContentStore.cs
Original file line number Diff line number Diff line change
@@ -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}'")
};
7 changes: 7 additions & 0 deletions Microsoft.NET.Build.Containers/CreateNewImage.Interface.cs
Original file line number Diff line number Diff line change
@@ -99,13 +99,19 @@ partial class CreateNewImage
[Required]
public string ContainerRuntimeIdentifier { get; set; }

/// <summary>
/// The path to the runtime identifier graph file. This is used to compute RID compatibility for Image Manifest List entries.
/// </summary>
[Required]
public string RuntimeIdentifierGraphPath { get; set; }

[Output]
public string GeneratedContainerManifest { get; set; }

[Output]
public string GeneratedContainerConfiguration { get; set; }


public CreateNewImage()
{
ContainerizeDirectory = "";
@@ -125,6 +131,7 @@ public CreateNewImage()
ExposedPorts = Array.Empty<ITaskItem>();
ContainerEnvironmentVariables = Array.Empty<ITaskItem>();
ContainerRuntimeIdentifier = "";
RuntimeIdentifierGraphPath = "";

GeneratedContainerConfiguration = "";
GeneratedContainerManifest = "";
3 changes: 2 additions & 1 deletion Microsoft.NET.Build.Containers/CreateNewImage.cs
Original file line number Diff line number Diff line change
@@ -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);
3 changes: 2 additions & 1 deletion Microsoft.NET.Build.Containers/CreateNewImageToolTask.cs
Original file line number Diff line number Diff line change
@@ -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)
27 changes: 17 additions & 10 deletions Microsoft.NET.Build.Containers/Registry.cs
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ public record Registry(Uri BaseUri)

private string RegistryName { get; } = BaseUri.Host;

public async Task<Image?> GetImageManifest(string name, string reference, string runtimeIdentifier)
public async Task<Image?> 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<Image?> 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<HttpResponseMessage> 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<string, PlatformSpecificManifest>, RuntimeGraph) ConstructRuntimeGraphForManifestList(ManifestListV2 manifestList)
private (IReadOnlyDictionary<string, PlatformSpecificManifest>, RuntimeGraph) ConstructRuntimeGraphForManifestList(ManifestListV2 manifestList, RuntimeGraph dotnetRuntimeGraph)
{
var ridDict = new Dictionary<string, PlatformSpecificManifest>();
var runtimeDescriptionSet = new HashSet<RuntimeDescription>();
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<HttpResponseMessage> GetBlob(string digest)
return $"{osPart}{versionPart ?? ""}-{platformPart}";
}

private RuntimeGraph GetRuntimeGraphForDotNet(string ridGraphPath) => JsonRuntimeFormat.ReadRuntimeGraph(ridGraphPath);

private void AddRidAndDescendantsToSet(HashSet<RuntimeDescription> runtimeDescriptionSet, string rid, RuntimeGraph dotnetRuntimeGraph)
{
var R = dotnetRuntimeGraph.Runtimes[rid];
runtimeDescriptionSet.Add(R);
foreach (var r in R.InheritedRuntimes) AddRidAndDescendantsToSet(runtimeDescriptionSet, r, dotnetRuntimeGraph);
}

/// <summary>
/// Ensure a blob associated with <paramref name="name"/> from the registry is available locally.
/// </summary>
7 changes: 5 additions & 2 deletions containerize/Program.cs
Original file line number Diff line number Diff line change
@@ -153,6 +153,7 @@

var ridOpt = new Option<string>(name: "--rid", description: "Runtime Identifier of the generated container.");

var ridGraphPathOpt = new Option<string>(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<Port>();
string[] _envVars = context.ParseResult.GetValueForOption(envVarsOpt) ?? Array.Empty<string>();
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);
5 changes: 3 additions & 2 deletions packaging/build/Microsoft.NET.Build.Containers.targets
Original file line number Diff line number Diff line change
@@ -62,7 +62,7 @@
<ContainerWorkingDirectory Condition="'$(ContainerWorkingDirectory)' == ''">/app</ContainerWorkingDirectory>
<!-- The Container RID should default to the RID used for the entire build (to ensure things run on the platform they are built for), but the user knows best and so should be able to set it explicitly.
For builds that have a RID, we default to that RID. Otherwise, we default to the RID of the currently-executing SDK. -->
<ContainerRuntimeIdentifier Condition="'$(ContainerRuntimeIdentifier)' == '' and '$(IsRidAgnostic)' == 'true'">$(RuntimeIdentifier)</ContainerRuntimeIdentifier>
<ContainerRuntimeIdentifier Condition="'$(ContainerRuntimeIdentifier)' == '' and '$(IsRidAgnostic)' != 'true'">$(RuntimeIdentifier)</ContainerRuntimeIdentifier>
<ContainerRuntimeIdentifier Condition="'$(ContainerRuntimeIdentifier)' == '' ">$(NETCoreSdkPortableRuntimeIdentifier)</ContainerRuntimeIdentifier>
</PropertyGroup>

@@ -134,7 +134,8 @@
Labels="@(ContainerLabel)"
ExposedPorts="@(ContainerPort)"
ContainerEnvironmentVariables="@(ContainerEnvironmentVariables)"
ContainerRuntimeIdentifier="$(ContainerRuntimeIdentifier)">
ContainerRuntimeIdentifier="$(ContainerRuntimeIdentifier)"
RuntimeIdentifierGraphPath="$(RuntimeIdentifierGraphPath)"> <!-- The RID graph path is provided as a property by the SDK. -->
<Output TaskParameter="GeneratedContainerManifest" PropertyName="GeneratedContainerManifest" />
<Output TaskParameter="GeneratedContainerConfiguration" PropertyName="GeneratedContainerConfiguration" />
</CreateNewImage>

0 comments on commit d1036ee

Please sign in to comment.