Skip to content

Commit ef188e8

Browse files
Add IsValidRegistry, Ensure ports flow with registries (#55)
* Add IsValidRegistry and tests * Ensure port tags along during registry parsing
1 parent d088c89 commit ef188e8

File tree

5 files changed

+99
-5
lines changed

5 files changed

+99
-5
lines changed

Microsoft.NET.Build.Containers/ContainerHelpers.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,34 @@ public static class ContainerHelpers
3030
}
3131
}
3232

33+
/// <summary>
34+
/// Ensures the given registry is valid.
35+
/// </summary>
36+
/// <param name="imageName"></param>
37+
/// <returns></returns>
38+
public static bool IsValidRegistry(string registryName)
39+
{
40+
// No scheme prefixed onto the registry
41+
if (string.IsNullOrEmpty(registryName) ||
42+
(!registryName.StartsWith("http://") &&
43+
!registryName.StartsWith("https://") &&
44+
!registryName.StartsWith("docker://")))
45+
{
46+
return false;
47+
}
48+
49+
try
50+
{
51+
UriBuilder uri = new UriBuilder(registryName);
52+
}
53+
catch
54+
{
55+
return false;
56+
}
57+
58+
return true;
59+
}
60+
3361
/// <summary>
3462
/// Ensures the given image name is valid.
3563
/// Spec: https://github.com/opencontainers/distribution-spec/blob/4ab4752c3b86a926d7e5da84de64cbbdcc18d313/spec.md#pulling-manifests
@@ -82,7 +110,7 @@ public static bool TryParseFullyQualifiedContainerName(string fullyQualifiedCont
82110
// If the image has a ':', there's a tag we need to parse.
83111
int indexOfColon = image.IndexOf(':');
84112

85-
containerRegistry = uri.Scheme + "://" + uri.Host;
113+
containerRegistry = uri.Scheme + "://" + uri.Host + (uri.Port > 0 && !uri.IsDefaultPort ? ":" + uri.Port : "");
86114
containerName = indexOfColon == -1 ? image : image.Substring(0, indexOfColon);
87115
containerTag = indexOfColon == -1 ? "" : image.Substring(indexOfColon + 1);
88116
return true;

Microsoft.NET.Build.Containers/ParseContainerProperties.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,24 @@ public override bool Execute()
7474
return !Log.HasLoggedErrors;
7575
}
7676

77+
string registryToUse = string.Empty;
78+
79+
if (!ContainerRegistry.StartsWith("http://") &&
80+
!ContainerRegistry.StartsWith("https://") &&
81+
!ContainerRegistry.StartsWith("docker://"))
82+
{
83+
// Default to https when no scheme is present: https://github.com/distribution/distribution/blob/26163d82560f4dda94bd7b87d587f94644c5af79/reference/normalize.go#L88
84+
registryToUse = "https://";
85+
}
86+
87+
registryToUse += ContainerRegistry;
88+
89+
if (!ContainerHelpers.IsValidRegistry(registryToUse))
90+
{
91+
Log.LogError("Could not recognize registry '{0}'. Does your registry need a scheme, like 'https://'?", ContainerRegistry);
92+
return !Log.HasLoggedErrors;
93+
}
94+
7795
if (FullyQualifiedBaseImageName.Contains(' ') && BuildEngine != null)
7896
{
7997
Log.LogWarning($"{nameof(FullyQualifiedBaseImageName)} had spaces in it, replacing with dashes.");
@@ -91,7 +109,7 @@ public override bool Execute()
91109
ParsedContainerRegistry = outputReg;
92110
ParsedContainerImage = outputImage;
93111
ParsedContainerTag = outputTag;
94-
NewContainerRegistry = ContainerRegistry;
112+
NewContainerRegistry = registryToUse;
95113
NewContainerImageName = ContainerImageName;
96114
NewContainerTag = ContainerImageTag;
97115

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ public void ParseContainerProperties_EndToEnd()
9191

9292
ParseContainerProperties pcp = new ParseContainerProperties();
9393
pcp.FullyQualifiedBaseImageName = "https://mcr.microsoft.com/dotnet/runtime:6.0";
94+
pcp.ContainerRegistry = "http://localhost:5010";
9495
pcp.ContainerImageName = "dotnet/testimage";
9596
pcp.ContainerImageTag = "5.0";
9697

@@ -107,7 +108,6 @@ public void ParseContainerProperties_EndToEnd()
107108
cni.BaseImageName = pcp.ParsedContainerImage;
108109
cni.BaseImageTag = pcp.ParsedContainerTag;
109110
cni.ImageName = pcp.NewContainerImageName;
110-
111111
cni.OutputRegistry = "http://localhost:5010";
112112
cni.PublishDirectory = Path.Combine(newProjectDir.FullName, "bin", "release", "net7.0");
113113
cni.WorkingDirectory = "app/";

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public void Baseline()
1313
{
1414
ParseContainerProperties task = new ParseContainerProperties();
1515
task.FullyQualifiedBaseImageName = "https://mcr.microsoft.com/dotnet/runtime:6.0";
16+
task.ContainerRegistry = "http://localhost:5010";
1617
task.ContainerImageName = "dotnet/testimage";
1718
task.ContainerImageTag = "5.0";
1819

@@ -26,10 +27,11 @@ public void Baseline()
2627
}
2728

2829
[TestMethod]
29-
public void RegistriesWithNoHttpGetHttp()
30+
public void BaseRegistriesWithNoSchemeGetHttps()
3031
{
3132
ParseContainerProperties task = new ParseContainerProperties();
3233
task.FullyQualifiedBaseImageName = "mcr.microsoft.com/dotnet/runtime:6.0";
34+
task.ContainerRegistry = "http://localhost:5010";
3335
task.ContainerImageName = "dotnet/testimage";
3436
task.ContainerImageTag = "5.0";
3537

@@ -38,6 +40,26 @@ public void RegistriesWithNoHttpGetHttp()
3840
Assert.AreEqual("dotnet/runtime", task.ParsedContainerImage);
3941
Assert.AreEqual("6.0", task.ParsedContainerTag);
4042

43+
Assert.AreEqual("http://localhost:5010", task.NewContainerRegistry);
44+
Assert.AreEqual("dotnet/testimage", task.NewContainerImageName);
45+
Assert.AreEqual("5.0", task.NewContainerTag);
46+
}
47+
48+
[TestMethod]
49+
public void UserRegistriesWithNoSchemeGetHttps()
50+
{
51+
ParseContainerProperties task = new ParseContainerProperties();
52+
task.FullyQualifiedBaseImageName = "mcr.microsoft.com/dotnet/runtime:6.0";
53+
task.ContainerRegistry = "localhost:5010";
54+
task.ContainerImageName = "dotnet/testimage";
55+
task.ContainerImageTag = "5.0";
56+
57+
Assert.IsTrue(task.Execute());
58+
Assert.AreEqual("https://mcr.microsoft.com", task.ParsedContainerRegistry);
59+
Assert.AreEqual("dotnet/runtime", task.ParsedContainerImage);
60+
Assert.AreEqual("6.0", task.ParsedContainerTag);
61+
62+
Assert.AreEqual("https://localhost:5010", task.NewContainerRegistry);
4163
Assert.AreEqual("dotnet/testimage", task.NewContainerImageName);
4264
Assert.AreEqual("5.0", task.NewContainerTag);
4365
}
@@ -47,6 +69,7 @@ public void SpacesGetReplacedWithDashes()
4769
{
4870
ParseContainerProperties task = new ParseContainerProperties();
4971
task.FullyQualifiedBaseImageName = "mcr microsoft com/dotnet runtime:6 0";
72+
task.ContainerRegistry = "http://localhost:5010";
5073

5174
// Spaces in the "new" container info don't pass the regex.
5275
task.ContainerImageName = "dotnet/testimage";
@@ -67,6 +90,7 @@ public void RegexCatchesInvalidContainerNames()
6790
{
6891
ParseContainerProperties task = new ParseContainerProperties();
6992
task.FullyQualifiedBaseImageName = "mcr.microsoft.com/dotnet/runtime:6 0";
93+
task.ContainerRegistry = "http://localhost:5010";
7094

7195
// Spaces in the "new" container info don't pass the regex.
7296
task.ContainerImageName = "dotnet testimage";
@@ -82,7 +106,7 @@ public void RegexCatchesInvalidContainerTags()
82106
{
83107
ParseContainerProperties task = new ParseContainerProperties();
84108
task.FullyQualifiedBaseImageName = "mcr.microsoft.com/dotnet/runtime:6 0";
85-
109+
task.ContainerRegistry = "http://localhost:5010";
86110
// Spaces in the "new" container info don't pass the regex.
87111
task.ContainerImageName = "dotnet/testimage";
88112
task.ContainerImageTag = "5 0";

Test.Microsoft.NET.Build.Containers/ContainerHelpersTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,36 @@ namespace Test.Microsoft.NET.Build.Containers;
66
[TestClass]
77
public class ContainerHelpersTests
88
{
9+
[TestMethod]
10+
// Valid Tests
11+
[DataRow("https://mcr.microsoft.com", true)]
12+
[DataRow("https://mcr.microsoft.com/", true)]
13+
[DataRow("http://mcr.microsoft.com:5001", true)] // Registries can have ports
14+
[DataRow("docker://mcr.microsoft.com:5001", true)] // docker:// is considered valid
15+
16+
// // Invalid tests
17+
[DataRow("docker://mcr.microsoft.com:xyz/dotnet/runtime:6.0", false)] // invalid port
18+
[DataRow("httpz://mcr.microsoft.com", false)] // invalid scheme
19+
[DataRow("https://mcr.mi-=crosoft.com", false)] // invalid url
20+
[DataRow("mcr.microsoft.com/", false)] // Missing scheme
21+
public void IsValidRegistry(string registry, bool expectedReturn)
22+
{
23+
Assert.AreEqual(expectedReturn, ContainerHelpers.IsValidRegistry(registry));
24+
}
925

1026
[TestMethod]
1127
[DataRow("https://mcr.microsoft.com/dotnet/runtime:6.0", true, "https://mcr.microsoft.com", "dotnet/runtime", "6.0")]
1228
[DataRow("https://mcr.microsoft.com/dotnet/runtime", true, "https://mcr.microsoft.com", "dotnet/runtime", "")]
1329
[DataRow("docker://mcr.microsoft.com/dotnet/runtime", true, "docker://mcr.microsoft.com", "dotnet/runtime", "")]
1430
[DataRow("https://mcr.microsoft.com/", false, null, null, null)] // no image = nothing resolves
31+
// Ports tag along
32+
[DataRow("docker://mcr.microsoft.com:54/dotnet/runtime", true, "docker://mcr.microsoft.com:54", "dotnet/runtime", "")]
33+
// Unless they're invalid
34+
[DataRow("docker://mcr.microsoft.com:0/dotnet/runtime", true, "docker://mcr.microsoft.com", "dotnet/runtime", "")]
35+
// Strip the ':' in an unspecified port
36+
[DataRow("docker://mcr.microsoft.com:/dotnet/runtime", true, "docker://mcr.microsoft.com", "dotnet/runtime", "")]
37+
// no image = nothing resolves
38+
[DataRow("https://mcr.microsoft.com/", false, null, null, null)]
1539
public void TryParseFullyQualifiedContainerName(string fullyQualifiedName, bool expectedReturn, string expectedRegistry, string expectedImage, string expectedTag)
1640
{
1741
Assert.AreEqual(expectedReturn, ContainerHelpers.TryParseFullyQualifiedContainerName(fullyQualifiedName, out string? containerReg, out string? containerName, out string? containerTag));

0 commit comments

Comments
 (0)