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