Skip to content

Commit a3ad2b0

Browse files
committed
improved DockerRegistryManager
1 parent e5f5a5b commit a3ad2b0

File tree

1 file changed

+88
-17
lines changed

1 file changed

+88
-17
lines changed

src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs

Lines changed: 88 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Security.Cryptography.Xml;
5+
using System.Threading;
46
using Microsoft.DotNet.Cli.Utils;
57
using Microsoft.Extensions.Logging;
6-
using Microsoft.Extensions.Logging.Abstractions;
78
using Microsoft.NET.TestFramework;
89
using Microsoft.NET.TestFramework.Assertions;
9-
using Microsoft.NET.TestFramework.Commands;
1010
using Xunit.Abstractions;
1111

1212
namespace Microsoft.NET.Build.Containers.IntegrationTests;
@@ -29,29 +29,69 @@ public static void StartAndPopulateDockerRegistry(ITestOutputHelper testOutput)
2929
{
3030
using TestLoggerFactory loggerFactory = new(testOutput);
3131

32-
testOutput.WriteLine("Spawning local registry");
3332
if (!new DockerCli(loggerFactory).IsAvailable()) {
3433
throw new InvalidOperationException("Docker is not available, tests cannot run");
3534
}
36-
CommandResult processResult = ContainerCli.RunCommand(testOutput, "--rm", "--publish", "5010:5000", "--detach", "docker.io/library/registry:2").Execute();
37-
processResult.Should().Pass().And.HaveStdOut();
38-
using var reader = new StringReader(processResult.StdOut!);
39-
s_registryContainerId = reader.ReadLine();
4035

41-
foreach (var tag in new[] { Net6ImageTag, Net7ImageTag, Net8PreviewImageTag })
36+
ILogger logger = loggerFactory.CreateLogger("Docker Registry Init");
37+
38+
const int spawnRegistryMaxRetry = 5;
39+
int spawnRegistryDelay = 1000;
40+
41+
for (int spawnRegistryAttempt = 1; spawnRegistryAttempt <= spawnRegistryMaxRetry; spawnRegistryAttempt++)
4242
{
43-
ContainerCli.PullCommand(testOutput, $"{BaseImageSource}{RuntimeBaseImage}:{tag}")
44-
.Execute()
45-
.Should().Pass();
43+
try
44+
{
45+
logger.LogInformation($"Spawning local registry at '{LocalRegistry}, attempt #{spawnRegistryAttempt}.");
4646

47-
ContainerCli.TagCommand(testOutput, $"{BaseImageSource}{RuntimeBaseImage}:{tag}", $"{LocalRegistry}/{RuntimeBaseImage}:{tag}")
48-
.Execute()
49-
.Should().Pass();
47+
CommandResult processResult = ContainerCli.RunCommand(testOutput, "--rm", "--publish", "5010:5000", "--detach", "docker.io/library/registry:2").Execute();
5048

51-
ContainerCli.PushCommand(testOutput, $"{LocalRegistry}/{RuntimeBaseImage}:{tag}")
52-
.Execute()
53-
.Should().Pass();
49+
processResult.Should().Pass().And.HaveStdOut();
50+
51+
logger.LogInformation($"StdOut: {processResult.StdOut}");
52+
logger.LogInformation($"StdErr: {processResult.StdErr}");
53+
54+
using var reader = new StringReader(processResult.StdOut!);
55+
s_registryContainerId = reader.ReadLine();
56+
57+
EnsureRegistryLoaded(LocalRegistry, logger);
58+
59+
foreach (string? tag in new[] { Net6ImageTag, Net7ImageTag, Net8PreviewImageTag })
60+
{
61+
logger.LogInformation($"Pulling image '{BaseImageSource}{RuntimeBaseImage}:{tag}'.");
62+
ContainerCli.PullCommand(testOutput, $"{BaseImageSource}{RuntimeBaseImage}:{tag}")
63+
.Execute()
64+
.Should().Pass();
65+
66+
logger.LogInformation($"Tagging image '{BaseImageSource}{RuntimeBaseImage}:{tag}' as '{LocalRegistry}/{RuntimeBaseImage}:{tag}'.");
67+
ContainerCli.TagCommand(testOutput, $"{BaseImageSource}{RuntimeBaseImage}:{tag}", $"{LocalRegistry}/{RuntimeBaseImage}:{tag}")
68+
.Execute()
69+
.Should().Pass();
70+
71+
logger.LogInformation($"Pushing image '{LocalRegistry}/{RuntimeBaseImage}:{tag}'.");
72+
ContainerCli.PushCommand(testOutput, $"{LocalRegistry}/{RuntimeBaseImage}:{tag}")
73+
.Execute()
74+
.Should().Pass();
75+
}
76+
return;
77+
}
78+
catch (Exception ex)
79+
{
80+
logger.LogError($"Spawn registry attempt #{spawnRegistryAttempt} failed, {ex.Message}");
81+
if (!string.IsNullOrWhiteSpace(s_registryContainerId))
82+
{
83+
try
84+
{
85+
ContainerCli.StopCommand(testOutput, s_registryContainerId).Execute();
86+
}
87+
catch { }
88+
}
89+
logger.LogInformation($"Retrying after {spawnRegistryDelay} ms.");
90+
Thread.Sleep(spawnRegistryDelay);
91+
spawnRegistryDelay *= 2;
92+
}
5493
}
94+
throw new InvalidOperationException($"The registry was not loaded after {spawnRegistryMaxRetry} retries.");
5595
}
5696

5797
public static void ShutdownDockerRegistry(ITestOutputHelper testOutput)
@@ -63,4 +103,35 @@ public static void ShutdownDockerRegistry(ITestOutputHelper testOutput)
63103
.Should().Pass();
64104
}
65105
}
106+
107+
private static void EnsureRegistryLoaded(string registryBaseUri, ILogger logger)
108+
{
109+
const int registryLoadMaxRetry = 10;
110+
const int registryLoadTimeout = 1000; //ms
111+
112+
using HttpClient client = new();
113+
using HttpRequestMessage request = new(HttpMethod.Get, new Uri(ContainerHelpers.TryExpandRegistryToUri(registryBaseUri), "/v2/"));
114+
115+
logger.LogInformation($"Checking if the registry '{registryBaseUri}' is available.");
116+
117+
int attempt = 1;
118+
while (attempt <= registryLoadMaxRetry)
119+
{
120+
//added an additional delay to allow registry to load
121+
Thread.Sleep(registryLoadTimeout);
122+
HttpResponseMessage response = client.Send(request);
123+
if (response.IsSuccessStatusCode)
124+
{
125+
break;
126+
}
127+
logger.LogWarning($"The registry '{registryBaseUri} is not loaded after {attempt * registryLoadTimeout} ms. Returned status code: {response.StatusCode}.");
128+
attempt++;
129+
}
130+
if (attempt > registryLoadMaxRetry)
131+
{
132+
throw new Exception($"The registry was not loaded after {registryLoadMaxRetry * registryLoadTimeout} ms.");
133+
}
134+
logger.LogInformation($"The registry '{registryBaseUri}' is available after {attempt * registryLoadTimeout} ms.");
135+
}
136+
66137
}

0 commit comments

Comments
 (0)