Skip to content

Commit 6715978

Browse files
authored
Merge pull request #247 from dotnet/support-other-arch-and-os
2 parents cb86a38 + 81c9768 commit 6715978

21 files changed

+333
-106
lines changed

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<PackageVersion Include="MSTest.TestAdapter" Version="2.3.0-preview-20220810-02" />
1414
<PackageVersion Include="MSTest.TestFramework" Version="2.2.10" />
1515
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.5.109" />
16+
<PackageVersion Include="NuGet.Packaging" Version="6.3.1" />
1617
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
1718
<PackageVersion Include="Valleysoft.DockerCredsProvider" Version="2.1.0" />
1819
</ItemGroup>

Microsoft.NET.Build.Containers/ContainerBuilder.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@ 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)
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-
Image img = await baseRegistry.GetImageManifest(baseName, baseTag);
18+
var img = await baseRegistry.GetImageManifest(baseName, baseTag, containerRuntimeIdentifier, ridGraphPath);
19+
if (img is null) {
20+
throw new ArgumentException($"Could not find image {baseName}:{baseTag} in registry {registryName} matching RuntimeIdentifier {containerRuntimeIdentifier}");
21+
}
22+
1923
img.WorkingDirectory = workingDir;
2024

2125
JsonSerializerOptions options = new()
@@ -82,6 +86,5 @@ public static async Task Containerize(DirectoryInfo folder, string workingDir, s
8286
}
8387
}
8488
}
85-
8689
}
8790
}

Microsoft.NET.Build.Containers/ContentStore.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ 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"

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,25 @@ partial class CreateNewImage
9393
/// </summary>
9494
public ITaskItem[] ContainerEnvironmentVariables { get; set; }
9595

96+
/// <summary>
97+
/// The RID to use to determine the host manifest if the parent container is a manifest list
98+
/// </summary>
99+
[Required]
100+
public string ContainerRuntimeIdentifier { get; set; }
101+
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; }
107+
96108
[Output]
97109
public string GeneratedContainerManifest { get; set; }
98110

99111
[Output]
100112
public string GeneratedContainerConfiguration { get; set; }
101113

114+
102115
public CreateNewImage()
103116
{
104117
ContainerizeDirectory = "";
@@ -117,6 +130,9 @@ public CreateNewImage()
117130
Labels = Array.Empty<ITaskItem>();
118131
ExposedPorts = Array.Empty<ITaskItem>();
119132
ContainerEnvironmentVariables = Array.Empty<ITaskItem>();
133+
ContainerRuntimeIdentifier = "";
134+
RuntimeIdentifierGraphPath = "";
135+
120136
GeneratedContainerConfiguration = "";
121137
GeneratedContainerManifest = "";
122138
}

Microsoft.NET.Build.Containers/CreateNewImage.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.Build.Framework;
1+
using System.Text.Json;
2+
using Microsoft.Build.Framework;
23

34
namespace Microsoft.NET.Build.Containers.Tasks;
45

@@ -72,12 +73,12 @@ private void SetEnvironmentVariables(Image img, ITaskItem[] envVars)
7273
}
7374
}
7475

75-
private Image GetBaseImage() {
76+
private Image? GetBaseImage() {
7677
if (IsDockerPull) {
7778
throw new ArgumentException("Don't know how to pull images from local daemons at the moment");
7879
} else {
7980
var reg = new Registry(ContainerHelpers.TryExpandRegistryToUri(BaseRegistry));
80-
return reg.GetImageManifest(BaseImageName, BaseImageTag).Result;
81+
return reg.GetImageManifest(BaseImageName, BaseImageTag, ContainerRuntimeIdentifier, RuntimeIdentifierGraphPath).Result;
8182
}
8283
}
8384

@@ -95,6 +96,11 @@ public override bool Execute()
9596

9697
var image = GetBaseImage();
9798

99+
if (image is null) {
100+
Log.LogError($"Couldn't find matching base image for {0}:{1} that matches RuntimeIdentifier {2}", BaseImageName, BaseImageTag, ContainerRuntimeIdentifier);
101+
return !Log.HasLoggedErrors;
102+
}
103+
98104
SafeLog("Building image '{0}' with tags {1} on top of base image {2}/{3}:{4}", ImageName, String.Join(",", ImageTags), BaseRegistry, BaseImageName, BaseImageTag);
99105

100106
Layer newLayer = Layer.FromDirectory(PublishDirectory, WorkingDirectory);
@@ -118,7 +124,7 @@ public override bool Execute()
118124
}
119125

120126
// at this point we're done with modifications and are just pushing the data other places
121-
GeneratedContainerManifest = image.manifest.ToJsonString();
127+
GeneratedContainerManifest = JsonSerializer.Serialize(image.manifest);
122128
GeneratedContainerConfiguration = image.config.ToJsonString();
123129

124130
Registry? outputReg = IsDockerPush ? null : new Registry(ContainerHelpers.TryExpandRegistryToUri(OutputRegistry));

Microsoft.NET.Build.Containers/CreateNewImageToolTask.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ 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+
$" --rid {Quote(ContainerRuntimeIdentifier)}" +
88+
$" --ridgraphpath {Quote(RuntimeIdentifierGraphPath)}";
8789
}
8890

8991
private string Quote(string path)

Microsoft.NET.Build.Containers/Image.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Microsoft.NET.Build.Containers;
88

99
public class Image
1010
{
11-
public JsonNode manifest;
11+
public ManifestV2 manifest;
1212
public JsonNode config;
1313

1414
public readonly string OriginatingName;
@@ -22,7 +22,7 @@ public class Image
2222

2323
internal Dictionary<string, string> environmentVariables;
2424

25-
public Image(JsonNode manifest, JsonNode config, string name, Registry? registry)
25+
public Image(ManifestV2 manifest, JsonNode config, string name, Registry? registry)
2626
{
2727
this.manifest = manifest;
2828
this.config = config;
@@ -38,39 +38,38 @@ public IEnumerable<Descriptor> LayerDescriptors
3838
{
3939
get
4040
{
41-
JsonNode? layersNode = manifest["layers"];
41+
var layersNode = manifest.layers;
4242

4343
if (layersNode is null)
4444
{
4545
throw new NotImplementedException("Tried to get layer information but there is no layer node?");
4646
}
4747

48-
foreach (JsonNode? descriptorJson in layersNode.AsArray())
48+
foreach (var layer in layersNode)
4949
{
50-
if (descriptorJson is null)
51-
{
52-
throw new NotImplementedException("Null layer descriptor in the list?");
53-
}
54-
55-
yield return descriptorJson.Deserialize<Descriptor>();
50+
yield return new(layer.mediaType, layer.digest, layer.size);
5651
}
5752
}
5853
}
5954

6055
public void AddLayer(Layer l)
6156
{
6257
newLayers.Add(l);
63-
manifest["layers"]!.AsArray().Add(l.Descriptor);
58+
59+
manifest.layers.Add(new(l.Descriptor.MediaType, l.Descriptor.Size, l.Descriptor.Digest, l.Descriptor.Urls));
6460
config["rootfs"]!["diff_ids"]!.AsArray().Add(l.Descriptor.UncompressedDigest);
6561
RecalculateDigest();
6662
}
6763

6864
private void RecalculateDigest()
6965
{
7066
config["created"] = DateTime.UtcNow;
71-
72-
manifest["config"]!["digest"] = GetDigest(config);
73-
manifest["config"]!["size"] = Encoding.UTF8.GetBytes(config.ToJsonString()).Length;
67+
var newManifestConfig = manifest.config with
68+
{
69+
digest = GetDigest(config),
70+
size = Encoding.UTF8.GetBytes(config.ToJsonString()).Length
71+
};
72+
manifest.config = newManifestConfig;
7473
}
7574

7675
private JsonObject CreatePortMap()
@@ -249,6 +248,13 @@ public string GetDigest(JsonNode json)
249248
return $"sha256:{hashString}";
250249
}
251250

251+
public string GetDigest<T>(T item)
252+
{
253+
var node = JsonSerializer.SerializeToNode(item);
254+
if (node is not null) return GetDigest(node);
255+
else return String.Empty;
256+
}
257+
252258
public static string GetSha(JsonNode json)
253259
{
254260
Span<byte> hash = stackalloc byte[SHA256.HashSizeInBytes];

Microsoft.NET.Build.Containers/LocalDocker.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,20 @@ public static async Task WriteImageToStream(Image x, string name, string tag, St
4747

4848
foreach (var d in x.LayerDescriptors)
4949
{
50-
if (!x.originatingRegistry.HasValue)
50+
if (x.originatingRegistry is {} registry)
5151
{
52-
throw new NotImplementedException("Need a good error for 'couldn't download a thing because no link to registry'");
53-
}
52+
string localPath = await registry.DownloadBlob(x.OriginatingName, d);
5453

55-
string localPath = await x.originatingRegistry.Value.DownloadBlob(x.OriginatingName, d);
56-
57-
// Stuff that (uncompressed) tarball into the image tar stream
58-
// TODO uncompress!!
59-
string layerTarballPath = $"{d.Digest.Substring("sha256:".Length)}/layer.tar";
60-
await writer.WriteEntryAsync(localPath, layerTarballPath);
61-
layerTarballPaths.Add(layerTarballPath);
62-
}
54+
// Stuff that (uncompressed) tarball into the image tar stream
55+
// TODO uncompress!!
56+
string layerTarballPath = $"{d.Digest.Substring("sha256:".Length)}/layer.tar";
57+
await writer.WriteEntryAsync(localPath, layerTarballPath);
58+
layerTarballPaths.Add(layerTarballPath);
59+
}
60+
else
61+
{
62+
throw new NotImplementedException("Need a good error for 'couldn't download a thing because no link to registry'");
63+
} }
6364

6465
// add config
6566
string configTarballPath = $"{Image.GetSha(x.config)}.json";

Microsoft.NET.Build.Containers/Microsoft.NET.Build.Containers.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
<ItemGroup>
2424
<PackageReference Include="Microsoft.Build.Utilities.Core" PrivateAssets="all" ExcludeAssets="runtime" />
25+
<PackageReference Include="Nuget.Packaging" />
2526
<PackageReference Include="Valleysoft.DockerCredsProvider" />
2627
</ItemGroup>
2728

0 commit comments

Comments
 (0)