Generate custom container name on empty input (#40432)
yao-msft committed
May 11, 2026 at 18:51 UTC
4ee78818dbef570a8191e3dbbe36631ec2a8d1bb
5 files changed
+188
-6
src/windows/common/ContainerNameGenerator.h
new
+96
@@ -0,0 +1,96 @@
1
+/*++
2
+
3
+Copyright (c) Microsoft. All rights reserved.
4
+
5
+Module Name:
6
+
7
+ ContainerNameGenerator.h
8
+
9
+Abstract:
10
+
11
+ Constants for auto generated container names.
12
+
13
+--*/
14
+
15
+#pragma once
16
+
17
+#include <array>
18
+
19
+namespace wsl::windows::service::wslc {
20
+
21
+constexpr std::array c_descriptors = {
22
+ "swift", "bold", "misty", "golden", "rugged", "serene", "mighty", "noble", "silent", "ancient",
23
+ "bright", "calm", "crisp", "daring", "eager", "fierce", "gentle", "hidden", "icy", "jade",
24
+ "keen", "lofty", "majestic", "nimble", "proud", "quiet", "radiant", "snowy", "stellar", "tranquil",
25
+ "untamed", "vast", "wandering", "wild", "azure", "blazing", "cloudy", "dusty", "emerald", "frosty",
26
+ "gleaming", "hazy", "ivory", "jovial", "luminous", "mossy", "northern", "onyx", "peaceful", "rustic",
27
+ "shimmering", "twilight", "verdant", "whispering", "zephyr",
28
+};
29
+
30
+constexpr std::array c_mountains = {
31
+ // Asia
32
+ "himalaya",
33
+ "karakoram",
34
+ "hindukush",
35
+ "pamirs",
36
+ "tienshan",
37
+ "kunlun",
38
+ "altai",
39
+ "zagros",
40
+ "elburz",
41
+ "caucasus",
42
+ "annamite",
43
+ // Europe
44
+ "alps",
45
+ "pyrenees",
46
+ "carpathian",
47
+ "apennine",
48
+ "balkan",
49
+ "dinaric",
50
+ "scandinavian",
51
+ "dolomites",
52
+ "urals",
53
+ // North America
54
+ "rockies",
55
+ "appalachian",
56
+ "sierra",
57
+ "cascade",
58
+ "olympic",
59
+ "brooks",
60
+ "alaska",
61
+ "ozark",
62
+ "adirondack",
63
+ "catskill",
64
+ "bighorn",
65
+ "bitterroot",
66
+ "sawtooth",
67
+ "teton",
68
+ "wasatch",
69
+ "sangre",
70
+ "uinta",
71
+ "absaroka",
72
+ "beartooth",
73
+ "laramie",
74
+ "medicine",
75
+ // South America
76
+ "andes",
77
+ "cordillera",
78
+ // Africa
79
+ "atlas",
80
+ "drakensberg",
81
+ "rwenzori",
82
+ "simien",
83
+ "virunga",
84
+ "tibesti",
85
+ "ahaggar",
86
+ // Oceania
87
+ "dividing",
88
+ "macdonnell",
89
+ "flinders",
90
+ "stirling",
91
+ // Antarctica
92
+ "transantarctic",
93
+ "ellsworth",
94
+};
95
+
96
+} // namespace wsl::windows::service::wslc
src/windows/wslcsession/WSLCContainer.cpp
+2
-2
@@ -1219,6 +1219,7 @@ WslcInspectContainer WSLCContainerImpl::BuildInspectContainer(const DockerInspec
1219
1220
std::unique_ptr<WSLCContainerImpl> WSLCContainerImpl::Create(
1221
const WSLCContainerOptions& containerOptions,
1222
+ const std::string& containerName,
1223
WSLCSession& wslcSession,
1224
WSLCVirtualMachine& virtualMachine,
1225
const std::unordered_map<std::string, NetworkEntry>& sessionNetworks,
@@ -1530,8 +1531,7 @@ std::unique_ptr<WSLCContainerImpl> WSLCContainerImpl::Create(
1531
request.Labels.insert(labels.begin(), labels.end());
1532
1533
// Send the request to docker.
1533
- auto result =
1534
- DockerClient.CreateContainer(request, containerOptions.Name != nullptr ? containerOptions.Name : std::optional<std::string>{});
1534
+ auto result = DockerClient.CreateContainer(request, containerName);
1535
1536
// Clean up the Docker container if anything below fails.
1537
// N.B. The container ID is captured by value since it is moved into the WSLCContainerImpl constructor below.
src/windows/wslcsession/WSLCContainer.h
+1
@@ -127,6 +127,7 @@ public:
127
128
static std::unique_ptr<WSLCContainerImpl> Create(
129
const WSLCContainerOptions& Options,
130
+ const std::string& Name,
131
WSLCSession& wslcSession,
132
WSLCVirtualMachine& virtualMachine,
133
const std::unordered_map<std::string, NetworkEntry>& SessionNetworks,
src/windows/wslcsession/WSLCSession.cpp
+55
-1
@@ -16,6 +16,7 @@ Abstract:
16
#include "WSLCSession.h"
17
#include "WSLCContainer.h"
18
#include "WSLCNetworkMetadata.h"
19
+#include "ContainerNameGenerator.h"
20
#include "ServiceProcessLauncher.h"
21
#include "WslCoreFilesystem.h"
22
@@ -110,6 +111,29 @@ wslc_schema::InspectImage ConvertInspectImage(const docker_schema::InspectImage&
111
return wslcInspect;
112
}
113
114
+using wsl::windows::service::wslc::c_descriptors;
115
+using wsl::windows::service::wslc::c_mountains;
116
+
117
+// Generate a random container name in the format "descriptor_mountain".
118
+// When retry > 0, appends a random digit (0-9) to reduce collisions.
119
+std::string GenerateContainerName(int retry)
120
+{
121
+ std::mt19937 gen(std::random_device{}());
122
+
123
+ std::uniform_int_distribution<size_t> leftDist(0, c_descriptors.size() - 1);
124
+ std::uniform_int_distribution<size_t> rightDist(0, c_mountains.size() - 1);
125
+
126
+ auto name = std::format("{}_{}", c_descriptors[leftDist(gen)], c_mountains[rightDist(gen)]);
127
+
128
+ if (retry > 0)
129
+ {
130
+ std::uniform_int_distribution<int> digitDist(0, 9);
131
+ name += std::to_string(digitDist(gen));
132
+ }
133
+
134
+ return name;
135
+}
136
+
137
} // namespace
138
139
namespace wsl::windows::service::wslc {
@@ -1623,7 +1647,7 @@ try
1647
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_dockerClient);
1648
1649
// Validate that name & images are valid.
1626
- if (containerOptions->Name != nullptr)
1650
+ if (containerOptions->Name != nullptr && containerOptions->Name[0] != '\0')
1651
{
1652
ValidateName(containerOptions->Name, WSLC_MAX_CONTAINER_NAME_LENGTH);
1653
}
@@ -1636,8 +1660,38 @@ try
1660
{
1661
std::scoped_lock lock(m_containersLock, m_networksLock);
1662
1663
+ // Generate a unique container name if the user didn't provide one.
1664
+ std::string containerName;
1665
+ if (containerOptions->Name != nullptr && containerOptions->Name[0] != '\0')
1666
+ {
1667
+ containerName = containerOptions->Name;
1668
+ }
1669
+ else
1670
+ {
1671
+ constexpr int c_maxNameRetries = 6;
1672
+ for (int attempt = 0; attempt < c_maxNameRetries; attempt++)
1673
+ {
1674
+ auto randomName = GenerateContainerName(attempt);
1675
+ if (std::ranges::none_of(m_containers, [&](const auto& c) { return c->Name() == randomName; }))
1676
+ {
1677
+ containerName = randomName;
1678
+ break;
1679
+ }
1680
+ }
1681
+
1682
+ // Fallback to a GUID name.
1683
+ if (containerName.empty())
1684
+ {
1685
+ WSL_LOG("GenerateGuidContainerName");
1686
+ GUID guid{};
1687
+ THROW_IF_FAILED(CoCreateGuid(&guid));
1688
+ containerName = wsl::shared::string::GuidToString<char>(guid, wsl::shared::string::GuidToStringFlags::None);
1689
+ }
1690
+ }
1691
+
1692
auto& it = m_containers.emplace_back(WSLCContainerImpl::Create(
1693
*containerOptions,
1694
+ containerName,
1695
*this,
1696
m_virtualMachine.value(),
1697
m_networks,
test/windows/WSLCTests.cpp
+34
-3
@@ -19,6 +19,7 @@ Abstract:
19
#include "WSLCContainerLauncher.h"
20
#include "WslCoreFilesystem.h"
21
#include "hcs.hpp"
22
+#include "ContainerNameGenerator.h"
23
#include <nlohmann/json.hpp>
24
25
using namespace std::literals::chrono_literals;
@@ -8148,7 +8149,7 @@ class WSLCTests
8149
WSLC_TEST_METHOD(ContainerNameGeneration)
8150
{
8151
{
8151
- // Create a container with a specific name
8152
+ // Create a container with a specific name.
8153
auto container = WSLCContainerLauncher("debian:latest", "test-container-name").Create(*m_defaultSession.get());
8154
8155
// Validate that the container name is correct.
@@ -8159,8 +8160,38 @@ class WSLCTests
8160
// Create a container without name.
8161
auto container = WSLCContainerLauncher("debian:latest").Create(*m_defaultSession.get());
8162
8162
- // Validate that the service generates a name for the container.
8163
- VERIFY_ARE_NOT_EQUAL(container.Name(), "");
8163
+ // Validate that the service generates a name in the format "descriptor_mountain[digit]".
8164
+ auto name = container.Name();
8165
+ VERIFY_ARE_NOT_EQUAL(name, "");
8166
+
8167
+ auto underscore = name.find('_');
8168
+ VERIFY_ARE_NOT_EQUAL(underscore, std::string::npos);
8169
+
8170
+ auto descriptor = name.substr(0, underscore);
8171
+ auto mountain = name.substr(underscore + 1);
8172
+
8173
+ // Strip trailing retry digit if present.
8174
+ if (!mountain.empty() && std::isdigit(mountain.back()))
8175
+ {
8176
+ mountain.pop_back();
8177
+ }
8178
+
8179
+ using wsl::windows::service::wslc::c_descriptors;
8180
+ using wsl::windows::service::wslc::c_mountains;
8181
+
8182
+ VERIFY_IS_TRUE(std::ranges::find(c_descriptors, descriptor) != c_descriptors.end());
8183
+ VERIFY_IS_TRUE(std::ranges::find(c_mountains, mountain) != c_mountains.end());
8184
+ }
8185
+
8186
+ {
8187
+ // Create multiple containers without names and verify they get unique names.
8188
+ auto container1 = WSLCContainerLauncher("debian:latest").Create(*m_defaultSession.get());
8189
+ auto container2 = WSLCContainerLauncher("debian:latest").Create(*m_defaultSession.get());
8190
+ auto container3 = WSLCContainerLauncher("debian:latest").Create(*m_defaultSession.get());
8191
+
8192
+ VERIFY_ARE_NOT_EQUAL(container1.Name(), container2.Name());
8193
+ VERIFY_ARE_NOT_EQUAL(container1.Name(), container3.Name());
8194
+ VERIFY_ARE_NOT_EQUAL(container2.Name(), container3.Name());
8195
}
8196
}
8197