Skip to content

Commit da69e11

Browse files
committed
re-add runtime inheritance for manifest list runtimes
1 parent 08c09df commit da69e11

File tree

10 files changed

+51
-22
lines changed

10 files changed

+51
-22
lines changed

Microsoft.NET.Build.Containers/ContainerBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ namespace Microsoft.NET.Build.Containers;
77

88
public static class ContainerBuilder
99
{
10-
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)
10+
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)
1111
{
1212
var isDockerPull = String.IsNullOrEmpty(registryName);
1313
if (isDockerPull) {
1414
throw new ArgumentException("Don't know how to pull images from local daemons at the moment");
1515
}
1616
Registry baseRegistry = new Registry(ContainerHelpers.TryExpandRegistryToUri(registryName));
1717

18-
var img = await baseRegistry.GetImageManifest(baseName, baseTag, containerRuntimeIdentifier);
18+
var img = await baseRegistry.GetImageManifest(baseName, baseTag, containerRuntimeIdentifier, ridGraphPath);
1919
if (img is null) {
2020
throw new ArgumentException($"Could not find image {baseName}:{baseTag} in registry {registryName} matching RuntimeIdentifier {containerRuntimeIdentifier}");
2121
}

Microsoft.NET.Build.Containers/ContentStore.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ public static string PathForDescriptor(Descriptor descriptor)
3434
{
3535
"application/vnd.docker.image.rootfs.diff.tar.gzip"
3636
or "application/vnd.oci.image.layer.v1.tar+gzip"
37+
or "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip"
3738
=> ".tar.gz",
3839
"application/vnd.docker.image.rootfs.diff.tar"
3940
or "application/vnd.oci.image.layer.v1.tar"
41+
or ":application/vnd.docker.image.rootfs.foreign.diff.tar"
4042
=> ".tar",
4143
_ => throw new ArgumentException($"Unrecognized mediaType '{descriptor.MediaType}'")
4244
};

Microsoft.NET.Build.Containers/CreateNewImage.Interface.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,19 @@ partial class CreateNewImage
9999
[Required]
100100
public string ContainerRuntimeIdentifier { get; set; }
101101

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

103108
[Output]
104109
public string GeneratedContainerManifest { get; set; }
105110

106111
[Output]
107112
public string GeneratedContainerConfiguration { get; set; }
108113

114+
109115
public CreateNewImage()
110116
{
111117
ContainerizeDirectory = "";
@@ -125,6 +131,7 @@ public CreateNewImage()
125131
ExposedPorts = Array.Empty<ITaskItem>();
126132
ContainerEnvironmentVariables = Array.Empty<ITaskItem>();
127133
ContainerRuntimeIdentifier = "";
134+
RuntimeIdentifierGraphPath = "";
128135

129136
GeneratedContainerConfiguration = "";
130137
GeneratedContainerManifest = "";

Microsoft.NET.Build.Containers/CreateNewImage.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ private void SetEnvironmentVariables(Image img, ITaskItem[] envVars)
7878
throw new ArgumentException("Don't know how to pull images from local daemons at the moment");
7979
} else {
8080
var reg = new Registry(ContainerHelpers.TryExpandRegistryToUri(BaseRegistry));
81-
return reg.GetImageManifest(BaseImageName, BaseImageTag, ContainerRuntimeIdentifier).Result;
81+
return reg.GetImageManifest(BaseImageName, BaseImageTag, ContainerRuntimeIdentifier, RuntimeIdentifierGraphPath).Result;
8282
}
8383
}
8484

@@ -88,6 +88,7 @@ private void SafeLog(string message, params object[] formatParams) {
8888

8989
public override bool Execute()
9090
{
91+
System.Diagnostics.Debugger.Launch();
9192
if (!Directory.Exists(PublishDirectory))
9293
{
9394
Log.LogError("{0} '{1}' does not exist", nameof(PublishDirectory), PublishDirectory);

Microsoft.NET.Build.Containers/CreateNewImageToolTask.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ protected override string GenerateCommandLineCommands()
8383
(ImageTags.Length > 0 ? " --imagetags " + String.Join(" ", ImageTags.Select((i) => Quote(i))) : "") +
8484
(EntrypointArgs.Length > 0 ? " --entrypointargs " + String.Join(" ", EntrypointArgs.Select((i) => i.ItemSpec)) : "") +
8585
(ExposedPorts.Length > 0 ? " --ports " + String.Join(" ", ExposedPorts.Select((i) => i.ItemSpec + "/" + i.GetMetadata("Type"))) : "") +
86-
(ContainerEnvironmentVariables.Length > 0 ? " --environmentvariables " + String.Join(" ", ContainerEnvironmentVariables.Select((i) => i.ItemSpec + "=" + Quote(i.GetMetadata("Value")))) : "");
86+
(ContainerEnvironmentVariables.Length > 0 ? " --environmentvariables " + String.Join(" ", ContainerEnvironmentVariables.Select((i) => i.ItemSpec + "=" + Quote(i.GetMetadata("Value")))) : "") +
87+
$" --ridgraphpath {Quote(RuntimeIdentifierGraphPath)}";
8788
}
8889

8990
private string Quote(string path)

Microsoft.NET.Build.Containers/Registry.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public record Registry(Uri BaseUri)
3838

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

41-
public async Task<Image?> GetImageManifest(string name, string reference, string runtimeIdentifier)
41+
public async Task<Image?> GetImageManifest(string name, string reference, string runtimeIdentifier, string runtimeIdentifierGraphPath)
4242
{
4343
var client = GetClient();
4444
var initialManifestResponse = await GetManifest(reference);
@@ -62,7 +62,8 @@ public record Registry(Uri BaseUri)
6262
}
6363

6464
async Task<Image?> TryPickBestImageFromManifestList(ManifestListV2 manifestList, string runtimeIdentifier) {
65-
var (ridDict, graphForManifestList) = ConstructRuntimeGraphForManifestList(manifestList);
65+
var runtimeGraph = GetRuntimeGraphForDotNet(runtimeIdentifierGraphPath);
66+
var (ridDict, graphForManifestList) = ConstructRuntimeGraphForManifestList(manifestList, runtimeGraph);
6667
var bestManifestRid = CheckIfRidExistsInGraph(graphForManifestList, runtimeIdentifier);
6768
if (bestManifestRid is null) {
6869
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)
8990
}
9091
}
9192

92-
private string? CheckIfRidExistsInGraph(RuntimeGraph graphForManifestList, string userRid)
93-
{
94-
graphForManifestList.Runtimes.TryGetValue(userRid, out var runtimeInfo);
95-
return runtimeInfo?.RuntimeIdentifier;
96-
}
93+
private string? CheckIfRidExistsInGraph(RuntimeGraph graphForManifestList, string userRid) => graphForManifestList.Runtimes.FirstOrDefault(kvp => graphForManifestList.AreCompatible(kvp.Key, userRid)).Key;
9794

98-
private (IReadOnlyDictionary<string, PlatformSpecificManifest>, RuntimeGraph) ConstructRuntimeGraphForManifestList(ManifestListV2 manifestList)
95+
private (IReadOnlyDictionary<string, PlatformSpecificManifest>, RuntimeGraph) ConstructRuntimeGraphForManifestList(ManifestListV2 manifestList, RuntimeGraph dotnetRuntimeGraph)
9996
{
10097
var ridDict = new Dictionary<string, PlatformSpecificManifest>();
10198
var runtimeDescriptionSet = new HashSet<RuntimeDescription>();
10299
foreach (var manifest in manifestList.manifests) {
103100
if (CreateRidForPlatform(manifest.platform) is { } rid)
104101
{
105-
ridDict.TryAdd(rid, manifest);
106-
runtimeDescriptionSet.Add(new RuntimeDescription(rid));
102+
if (ridDict.TryAdd(rid, manifest)) {
103+
AddRidAndDescendantsToSet(runtimeDescriptionSet, rid, dotnetRuntimeGraph);
104+
}
107105
}
108106
}
109107

@@ -139,6 +137,15 @@ async Task<HttpResponseMessage> GetBlob(string digest)
139137
return $"{osPart}{versionPart ?? ""}-{platformPart}";
140138
}
141139

140+
private RuntimeGraph GetRuntimeGraphForDotNet(string ridGraphPath) => JsonRuntimeFormat.ReadRuntimeGraph(ridGraphPath);
141+
142+
private void AddRidAndDescendantsToSet(HashSet<RuntimeDescription> runtimeDescriptionSet, string rid, RuntimeGraph dotnetRuntimeGraph)
143+
{
144+
var R = dotnetRuntimeGraph.Runtimes[rid];
145+
runtimeDescriptionSet.Add(R);
146+
foreach (var r in R.InheritedRuntimes) AddRidAndDescendantsToSet(runtimeDescriptionSet, r, dotnetRuntimeGraph);
147+
}
148+
142149
/// <summary>
143150
/// Ensure a blob associated with <paramref name="name"/> from the registry is available locally.
144151
/// </summary>

Test.Microsoft.NET.Build.Containers.Filesystem/EndToEnd.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ namespace Test.Microsoft.NET.Build.Containers.Filesystem;
1010
[TestClass]
1111
public class EndToEnd
1212
{
13+
public static string RuntimeGraphFilePath() =>
14+
// TODO: The DOTNET_ROOT comes from the test host, but we have no idea what the SDK version is.
15+
Path.Combine(Environment.GetEnvironmentVariable("DOTNET_ROOT"), "sdk", "7.0.100", "RuntimeIdentifierGraph.json");
16+
1317
public static string NewImageName([CallerMemberName] string callerMemberName = "")
1418
{
1519
bool normalized = ContainerHelpers.NormalizeImageName(callerMemberName, out string normalizedName);
@@ -31,7 +35,7 @@ public async Task ApiEndToEndWithRegistryPushAndPull()
3135

3236
Registry registry = new Registry(ContainerHelpers.TryExpandRegistryToUri(DockerRegistryManager.LocalRegistry));
3337

34-
Image x = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag, "linux-x64");
38+
Image x = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag, "linux-x64", RuntimeGraphFilePath());
3539

3640
Layer l = Layer.FromDirectory(publishDirectory, "/app");
3741

@@ -69,7 +73,7 @@ public async Task ApiEndToEndWithLocalLoad()
6973

7074
Registry registry = new Registry(ContainerHelpers.TryExpandRegistryToUri(DockerRegistryManager.LocalRegistry));
7175

72-
Image x = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag, "linux-x64");
76+
Image x = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag, "linux-x64", RuntimeGraphFilePath());
7377

7478
Layer l = Layer.FromDirectory(publishDirectory, "/app");
7579

@@ -298,7 +302,7 @@ public async Task CanPackageForAllSupportedContainerRIDs(string rid, bool isRIDS
298302
// Build the image
299303
Registry registry = new Registry(ContainerHelpers.TryExpandRegistryToUri(DockerRegistryManager.BaseImageSource));
300304

301-
Image x = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net7ImageTag, rid);
305+
Image x = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net7ImageTag, rid, RuntimeGraphFilePath());
302306

303307
Layer l = Layer.FromDirectory(publishDirectory, "/app");
304308

Test.Microsoft.NET.Build.Containers.Filesystem/RegistryTests.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ public async Task GetFromRegistry()
1515
{
1616
Registry registry = new Registry(ContainerHelpers.TryExpandRegistryToUri(DockerRegistryManager.LocalRegistry));
1717

18-
Image downloadedImage = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag, "linux-x64");
18+
// TODO: The DOTNET_ROOT comes from the test host, but we have no idea what the SDK version is.
19+
var ridgraphfile = Path.Combine(Environment.GetEnvironmentVariable("DOTNET_ROOT"), "sdk", "7.0.100", "RuntimeIdentifierGraph.json");
20+
21+
Image downloadedImage = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag, "linux-x64", ridgraphfile); // don't need rid graph for local registry
1922

2023
Assert.IsNotNull(downloadedImage);
2124
}

containerize/Program.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153

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

156+
var ridGraphPathOpt = new Option<string>(name: "--ridgraphpath", description: "Path to the RID graph file.");
156157

157158
RootCommand root = new RootCommand("Containerize an application without Docker.")
158159
{
@@ -169,7 +170,8 @@
169170
labelsOpt,
170171
portsOpt,
171172
envVarsOpt,
172-
ridOpt
173+
ridOpt,
174+
ridGraphPathOpt
173175
};
174176

175177
root.SetHandler(async (context) =>
@@ -188,7 +190,8 @@
188190
Port[] _ports = context.ParseResult.GetValueForOption(portsOpt) ?? Array.Empty<Port>();
189191
string[] _envVars = context.ParseResult.GetValueForOption(envVarsOpt) ?? Array.Empty<string>();
190192
string _rid = context.ParseResult.GetValueForOption(ridOpt) ?? "";
191-
await ContainerBuilder.Containerize(_publishDir, _workingDir, _baseReg, _baseName, _baseTag, _entrypoint, _entrypointArgs, _name, _tags, _outputReg, _labels, _ports, _envVars, _rid);
193+
string _ridGraphPath = context.ParseResult.GetValueForOption(ridGraphPathOpt) ?? "";
194+
await ContainerBuilder.Containerize(_publishDir, _workingDir, _baseReg, _baseName, _baseTag, _entrypoint, _entrypointArgs, _name, _tags, _outputReg, _labels, _ports, _envVars, _rid, _ridGraphPath);
192195
});
193196

194197
return await root.InvokeAsync(args);

packaging/build/Microsoft.NET.Build.Containers.targets

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
<ContainerWorkingDirectory Condition="'$(ContainerWorkingDirectory)' == ''">/app</ContainerWorkingDirectory>
6363
<!-- 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.
6464
For builds that have a RID, we default to that RID. Otherwise, we default to the RID of the currently-executing SDK. -->
65-
<ContainerRuntimeIdentifier Condition="'$(ContainerRuntimeIdentifier)' == '' and '$(IsRidAgnostic)' == 'true'">$(RuntimeIdentifier)</ContainerRuntimeIdentifier>
65+
<ContainerRuntimeIdentifier Condition="'$(ContainerRuntimeIdentifier)' == '' and '$(IsRidAgnostic)' != 'true'">$(RuntimeIdentifier)</ContainerRuntimeIdentifier>
6666
<ContainerRuntimeIdentifier Condition="'$(ContainerRuntimeIdentifier)' == '' ">$(NETCoreSdkPortableRuntimeIdentifier)</ContainerRuntimeIdentifier>
6767
</PropertyGroup>
6868

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

0 commit comments

Comments
 (0)