Skip to content

Commit fb38b5c

Browse files
Add VS (non-core) Compatibility (#164)
* Add new net472 tooltask for CreateNewImage * Modify build to support multi-targeting net472/net7.0
1 parent d65bbc0 commit fb38b5c

15 files changed

+352
-110
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
namespace Microsoft.NET.Build.Containers;
2+
3+
using System;
4+
using System.IO;
5+
using System.Text.Json;
6+
using System.Threading.Tasks;
7+
8+
public static class ContainerBuilder
9+
{
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)
11+
{
12+
Registry baseRegistry = new Registry(new Uri(registryName));
13+
14+
Image img = await baseRegistry.GetImageManifest(baseName, baseTag);
15+
img.WorkingDirectory = workingDir;
16+
17+
JsonSerializerOptions options = new()
18+
{
19+
WriteIndented = true,
20+
};
21+
22+
Layer l = Layer.FromDirectory(folder.FullName, workingDir);
23+
24+
img.AddLayer(l);
25+
26+
img.SetEntrypoint(entrypoint, entrypointArgs);
27+
28+
var isDockerPush = outputRegistry.StartsWith("docker://");
29+
Registry? outputReg = isDockerPush ? null : new Registry(new Uri(outputRegistry));
30+
31+
foreach (var label in labels)
32+
{
33+
string[] labelPieces = label.Split('=');
34+
35+
// labels are validated by System.CommandLine API
36+
img.Label(labelPieces[0], labelPieces[1]);
37+
}
38+
39+
foreach (var (number, type) in exposedPorts)
40+
{
41+
// ports are validated by System.CommandLine API
42+
img.ExposePort(number, type);
43+
}
44+
45+
foreach (var tag in imageTags)
46+
{
47+
if (isDockerPush)
48+
{
49+
try
50+
{
51+
LocalDocker.Load(img, imageName, tag, baseName).Wait();
52+
Console.WriteLine("Containerize: Pushed container '{0}:{1}' to Docker daemon", imageName, tag);
53+
}
54+
catch (Exception e)
55+
{
56+
Console.WriteLine($"Containerize: error CONTAINER001: Failed to push to local docker registry: {e}");
57+
Environment.ExitCode = -1;
58+
}
59+
}
60+
else
61+
{
62+
try
63+
{
64+
outputReg?.Push(img, imageName, tag, imageName).Wait();
65+
Console.WriteLine($"Containerize: Pushed container '{imageName}:{tag}' to registry '{outputRegistry}'");
66+
}
67+
catch (Exception e)
68+
{
69+
Console.WriteLine($"Containerize: error CONTAINER001: Failed to push to output registry: {e}");
70+
Environment.ExitCode = -1;
71+
}
72+
}
73+
}
74+
75+
}
76+
}

Microsoft.NET.Build.Containers/ContainerHelpers.cs

Lines changed: 23 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
namespace Microsoft.NET.Build.Containers;
22

3+
using System;
34
using System.Diagnostics.CodeAnalysis;
4-
using System.Text.Json;
55
using System.Text.RegularExpressions;
66

7+
record Label(string name, string value);
8+
9+
// Explicitly lowercase to ease parsing - the incoming values are
10+
// lowercased by spec
11+
public enum PortType
12+
{
13+
tcp,
14+
udp
15+
}
16+
17+
public record Port(int number, PortType type);
18+
719
public static class ContainerHelpers
820
{
921
private static Regex imageTagRegex = new Regex(@"^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$");
@@ -13,7 +25,8 @@ public static class ContainerHelpers
1325
/// <summary>
1426
/// Matches if the string is not lowercase or numeric, or ., _, or -.
1527
/// </summary>
16-
private static Regex imageNameCharacters = new Regex(@"[^a-z0-9._\-/]");
28+
/// <remarks>Technically the period should be allowed as well, but due to inconsistent support between cloud providers we're removing it.</remarks>
29+
private static Regex imageNameCharacters = new Regex(@"[^a-z0-9_\-/]");
1730

1831
/// <summary>
1932
/// Given some "fully qualified" image name (e.g. mcr.microsoft.com/dotnet/runtime), return
@@ -125,7 +138,8 @@ public static bool TryParseFullyQualifiedContainerName(string fullyQualifiedCont
125138
/// <summary>
126139
/// Checks if a given container image name adheres to the image name spec. If not, and recoverable, then normalizes invalid characters.
127140
/// </summary>
128-
public static bool NormalizeImageName(string containerImageName, [NotNullWhen(false)] out string? normalizedImageName)
141+
public static bool NormalizeImageName(string containerImageName,
142+
[NotNullWhen(false)] out string? normalizedImageName)
129143
{
130144
if (IsValidImageName(containerImageName))
131145
{
@@ -194,13 +208,16 @@ public static bool TryParsePort(string? portNumber, string? portType, [NotNullWh
194208
public static bool TryParsePort(string input, [NotNullWhen(true)] out Port? port, [NotNullWhen(false)] out ParsePortError? error)
195209
{
196210
var parts = input.Split('/');
197-
if (parts is [var portNumber, var type])
211+
if (parts.Length == 2)
198212
{
213+
string portNumber = parts[0];
214+
string type = parts[1];
199215
return TryParsePort(portNumber, type, out port, out error);
200216
}
201-
else if (parts is [var portNo])
217+
else if (parts.Length == 1)
202218
{
203-
return TryParsePort(portNo, null, out port, out error);
219+
string portNum = parts[0];
220+
return TryParsePort(portNum, null, out port, out error);
204221
}
205222
else
206223
{
@@ -209,74 +226,4 @@ public static bool TryParsePort(string input, [NotNullWhen(true)] out Port? port
209226
return false;
210227
}
211228
}
212-
213-
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)
214-
{
215-
Registry baseRegistry = new Registry(new Uri(registryName));
216-
217-
Console.WriteLine($"Reading from {baseRegistry.BaseUri}");
218-
219-
Image img = await baseRegistry.GetImageManifest(baseName, baseTag);
220-
img.WorkingDirectory = workingDir;
221-
222-
JsonSerializerOptions options = new()
223-
{
224-
WriteIndented = true,
225-
};
226-
227-
Console.WriteLine($"Copying from {folder.FullName} to {workingDir}");
228-
Layer l = Layer.FromDirectory(folder.FullName, workingDir);
229-
230-
img.AddLayer(l);
231-
232-
img.SetEntrypoint(entrypoint, entrypointArgs);
233-
234-
var isDockerPush = outputRegistry.StartsWith("docker://");
235-
Registry? outputReg = isDockerPush ? null : new Registry(new Uri(outputRegistry));
236-
237-
foreach (var label in labels)
238-
{
239-
string[] labelPieces = label.Split('=');
240-
241-
// labels are validated by System.CommandLine API
242-
img.Label(labelPieces[0], labelPieces[1]);
243-
}
244-
245-
foreach (var (number, type) in exposedPorts)
246-
{
247-
// ports are validated by System.CommandLine API
248-
img.ExposePort(number, type);
249-
}
250-
251-
foreach (var tag in imageTags)
252-
{
253-
if (isDockerPush)
254-
{
255-
try
256-
{
257-
LocalDocker.Load(img, imageName, tag, baseName).Wait();
258-
Console.WriteLine("Pushed container '{0}:{1}' to Docker daemon", imageName, tag);
259-
}
260-
catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle)
261-
{
262-
Console.WriteLine(dle);
263-
Environment.ExitCode = -1;
264-
}
265-
}
266-
else
267-
{
268-
try
269-
{
270-
Console.WriteLine($"Trying to push container '{imageName}:{tag}' to registry '{outputRegistry}'");
271-
outputReg?.Push(img, imageName, tag, imageName).Wait();
272-
Console.WriteLine($"Pushed container '{imageName}:{tag}' to registry '{outputRegistry}'");
273-
}
274-
catch (Exception e)
275-
{
276-
Console.WriteLine("Failed to push to output registry: {0}", e);
277-
Environment.ExitCode = -1;
278-
}
279-
}
280-
}
281-
}
282229
}

Microsoft.NET.Build.Containers/CreateNewImage.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@ namespace Microsoft.NET.Build.Containers.Tasks;
44

55
public class CreateNewImage : Microsoft.Build.Utilities.Task
66
{
7+
8+
/// <summary>
9+
/// Unused. This exists so we can call a single task invocation in PublishContainer.
10+
/// </summary>
11+
public string ContainerizeDirectory { get; set; }
12+
13+
/// <summary>
14+
/// Unused. See above.
15+
/// </summary>
16+
public string ToolExe { get; set; }
17+
18+
/// <summary>
19+
/// Unused. See above.
20+
/// </summary>
21+
public string ToolPath { get; set; }
22+
723
/// <summary>
824
/// The base registry to pull from.
925
/// Ex: https://mcr.microsoft.com
@@ -82,6 +98,9 @@ public class CreateNewImage : Microsoft.Build.Utilities.Task
8298

8399
public CreateNewImage()
84100
{
101+
ContainerizeDirectory = "";
102+
ToolExe = "";
103+
ToolPath = "";
85104
BaseRegistry = "";
86105
BaseImageName = "";
87106
BaseImageTag = "";

0 commit comments

Comments
 (0)