Fix race condition when starting a local registry (#40797)

Blue committed Jun 15, 2026 at 01:26 UTC 5b3faba836484eca3eeb4e2aa97cbde4a82fde50
3 files changed +11 -47
test/windows/WslcSdkTests.cpp
+3 -23
@@ -19,6 +19,7 @@ Abstract:
19 #include "WSLCContainerLauncher.h"
20 #include "WSLCProcessLauncher.h"
21 #include "wslc_schema.h"
22 +#include "wslc/e2e/WSLCE2EHelpers.h"
23 #include <optional>
24
25 extern std::wstring g_testDataPath;
@@ -2225,30 +2226,9 @@ class WslcSdkTests
2226 std::pair<wsl::windows::common::RunningWSLCContainer, std::string> StartLocalRegistry(
2227 const std::string& username = {}, const std::string& password = {}, uint16_t port = 5000)
2228 {
2228 - VERIFY_IS_TRUE(HasImage("wslc-registry:latest"));
2229 -
2230 - std::vector<std::string> env = {std::format("REGISTRY_HTTP_ADDR=0.0.0.0:{}", port)};
2231 - if (!username.empty())
2232 - {
2233 - env.push_back(std::format("USERNAME={}", username));
2234 - env.push_back(std::format("PASSWORD={}", password));
2235 - }
2236 -
2237 - wsl::windows::common::WSLCContainerLauncher launcher("wslc-registry:latest", {}, {}, env);
2238 - launcher.SetEntrypoint({"/entrypoint.sh"});
2239 - launcher.AddPort(port, port, AF_INET);
2240 -
2241 - // Get the IWSLCSession COM object from the SDK session handle.
2229 + // Get the IWSLCSession COM object from the SDK session handle and delegate to the shared helper.
2230 auto& session = *reinterpret_cast<WslcSessionImpl*>(m_defaultSession)->session;
2243 - auto container = launcher.Launch(session, WSLCContainerStartFlagsNone);
2244 -
2245 - auto registryAddress = std::format("127.0.0.1:{}", port);
2246 -
2247 - // Wait for the registry to be ready by probing from the host.
2248 - auto hostUrl = std::format(L"http://{}", registryAddress);
2249 - ExpectHttpResponse(hostUrl.c_str(), 200, true);
2250 -
2251 - return {std::move(container), registryAddress};
2231 + return WSLCE2ETests::StartLocalRegistry(session, username, password, port);
2232 }
2233
2234 // Tags and pushes an image to a local registry via the SDK APIs.
test/windows/WslcSdkWinRTTests.cpp
+3 -23
@@ -18,6 +18,7 @@ Abstract:
18 #include "WslcsdkPrivate.h"
19 #include "WSLCContainerLauncher.h"
20 #include "wslutil.h"
21 +#include "wslc/e2e/WSLCE2EHelpers.h"
22
23 #include "winrt/Session.h"
24 #include "winrt/Helpers.h"
@@ -171,30 +172,9 @@ class WslcSdkWinRtTests
172 std::pair<wsl::windows::common::RunningWSLCContainer, std::string> StartLocalRegistry(
173 const std::string& username = {}, const std::string& password = {}, uint16_t port = 5000)
174 {
174 - VERIFY_IS_TRUE(HasImage(L"wslc-registry:latest"));
175 -
176 - std::vector<std::string> env = {std::format("REGISTRY_HTTP_ADDR=0.0.0.0:{}", port)};
177 - if (!username.empty())
178 - {
179 - env.push_back(std::format("USERNAME={}", username));
180 - env.push_back(std::format("PASSWORD={}", password));
181 - }
182 -
183 - wsl::windows::common::WSLCContainerLauncher launcher("wslc-registry:latest", {}, {}, env);
184 - launcher.SetEntrypoint({"/entrypoint.sh"});
185 - launcher.AddPort(port, port, AF_INET);
186 -
187 - // Get the IWSLCSession COM object from the SDK session handle.
175 + // Get the IWSLCSession COM object from the SDK session handle and delegate to the shared helper.
176 auto& comSession = *reinterpret_cast<WslcSessionImpl*>(WSLCSDK::implementation::GetHandle(m_defaultSession))->session;
189 - auto container = launcher.Launch(comSession, WSLCContainerStartFlagsNone);
190 -
191 - auto registryAddress = std::format("127.0.0.1:{}", port);
192 -
193 - // Wait for the registry to be ready by probing from the host.
194 - auto hostUrl = std::format(L"http://{}", registryAddress);
195 - ExpectHttpResponse(hostUrl.c_str(), 200, true);
196 -
197 - return {std::move(container), registryAddress};
177 + return WSLCE2ETests::StartLocalRegistry(comSession, username, password, port);
178 }
179
180 // Tags and pushes an image to a local registry via the SDK APIs.
test/windows/wslc/e2e/WSLCE2EHelpers.cpp
+5 -1
@@ -519,7 +519,11 @@ std::pair<RunningWSLCContainer, std::string> StartLocalRegistry(IWSLCSession& se
519 launcher.SetEntrypoint({"/entrypoint.sh"});
520 launcher.AddPort(port, port, AF_INET);
521
522 - auto container = launcher.Launch(session, WSLCContainerStartFlagsNone);
522 + auto container = launcher.Launch(session);
523 +
524 + // Wait for the registry to bind the port before continuing.
525 + auto initProcess = container.GetInitProcess();
526 + WaitForOutput(initProcess.GetStdHandle(2), std::format("listening on [::]:{}", port));
527
528 auto address = std::format("127.0.0.1:{}", port);
529 auto url = std::format(L"http://{}/v2/", wsl::shared::string::MultiByteToWide(address));