Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions src/windows/common/ContainerNameGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*++

Copyright (c) Microsoft. All rights reserved.

Module Name:

ContainerNameGenerator.h

Abstract:

Constants for auto generated container names.

Comment thread
yao-msft marked this conversation as resolved.
--*/

#pragma once

#include <array>

namespace wsl::windows::service::wslc {

constexpr std::array c_descriptors = {
"swift", "bold", "misty", "golden", "rugged", "serene", "mighty", "noble", "silent", "ancient",
"bright", "calm", "crisp", "daring", "eager", "fierce", "gentle", "hidden", "icy", "jade",
"keen", "lofty", "majestic", "nimble", "proud", "quiet", "radiant", "snowy", "stellar", "tranquil",
"untamed", "vast", "wandering", "wild", "azure", "blazing", "cloudy", "dusty", "emerald", "frosty",
"gleaming", "hazy", "ivory", "jovial", "luminous", "mossy", "northern", "onyx", "peaceful", "rustic",
"shimmering", "twilight", "verdant", "whispering", "zephyr",
};

constexpr std::array c_mountains = {
// Asia
"himalaya",
"karakoram",
"hindukush",
"pamirs",
"tienshan",
"kunlun",
"altai",
"zagros",
"elburz",
"caucasus",
"annamite",
// Europe
"alps",
"pyrenees",
"carpathian",
"apennine",
"balkan",
"dinaric",
"scandinavian",
"dolomites",
"urals",
// North America
"rockies",
"appalachian",
"sierra",
"cascade",
"olympic",
"brooks",
"alaska",
"ozark",
"adirondack",
"catskill",
"bighorn",
"bitterroot",
"sawtooth",
"teton",
"wasatch",
"sangre",
"uinta",
"absaroka",
"beartooth",
"laramie",
"medicine",
// South America
"andes",
"cordillera",
// Africa
"atlas",
"drakensberg",
"rwenzori",
"simien",
"virunga",
"tibesti",
"ahaggar",
// Oceania
"dividing",
"macdonnell",
"flinders",
"stirling",
// Antarctica
"transantarctic",
"ellsworth",
};

} // namespace wsl::windows::service::wslc
4 changes: 2 additions & 2 deletions src/windows/wslcsession/WSLCContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,7 @@ WslcInspectContainer WSLCContainerImpl::BuildInspectContainer(const DockerInspec

std::unique_ptr<WSLCContainerImpl> WSLCContainerImpl::Create(
const WSLCContainerOptions& containerOptions,
const std::string& containerName,
WSLCSession& wslcSession,
WSLCVirtualMachine& virtualMachine,
const std::unordered_map<std::string, std::unique_ptr<IWSLCVolume>>& sessionVolumes,
Expand Down Expand Up @@ -1489,8 +1490,7 @@ std::unique_ptr<WSLCContainerImpl> WSLCContainerImpl::Create(
request.Labels.insert(labels.begin(), labels.end());

// Send the request to docker.
auto result =
DockerClient.CreateContainer(request, containerOptions.Name != nullptr ? containerOptions.Name : std::optional<std::string>{});
auto result = DockerClient.CreateContainer(request, containerName);

Comment thread
yao-msft marked this conversation as resolved.
// Clean up the Docker container if anything below fails.
// N.B. The container ID is captured by value since it is moved into the WSLCContainerImpl constructor below.
Expand Down
1 change: 1 addition & 0 deletions src/windows/wslcsession/WSLCContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class WSLCContainerImpl

static std::unique_ptr<WSLCContainerImpl> Create(
const WSLCContainerOptions& Options,
const std::string& Name,
WSLCSession& wslcSession,
WSLCVirtualMachine& virtualMachine,
const std::unordered_map<std::string, std::unique_ptr<IWSLCVolume>>& SessionVolumes,
Expand Down
56 changes: 55 additions & 1 deletion src/windows/wslcsession/WSLCSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Module Name:
#include "WSLCSession.h"
#include "WSLCContainer.h"
#include "WSLCNetworkMetadata.h"
#include "ContainerNameGenerator.h"
#include "ServiceProcessLauncher.h"
#include "WslCoreFilesystem.h"

Expand Down Expand Up @@ -109,6 +110,29 @@ wslc_schema::InspectImage ConvertInspectImage(const docker_schema::InspectImage&
return wslcInspect;
}

using wsl::windows::service::wslc::c_descriptors;
using wsl::windows::service::wslc::c_mountains;

// Generate a random container name in the format "descriptor_mountain".
// When retry > 0, appends a random digit (0-9) to reduce collisions.
std::string GenerateContainerName(int retry)
{
std::mt19937 gen(std::random_device{}());

std::uniform_int_distribution<size_t> leftDist(0, c_descriptors.size() - 1);
std::uniform_int_distribution<size_t> rightDist(0, c_mountains.size() - 1);

auto name = std::format("{}_{}", c_descriptors[leftDist(gen)], c_mountains[rightDist(gen)]);

if (retry > 0)
{
std::uniform_int_distribution<int> digitDist(0, 9);
name += std::to_string(digitDist(gen));
}
Comment thread
yao-msft marked this conversation as resolved.

return name;
}

} // namespace

namespace wsl::windows::service::wslc {
Expand Down Expand Up @@ -1623,7 +1647,7 @@ try
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_dockerClient);

// Validate that name & images are valid.
if (containerOptions->Name != nullptr)
if (containerOptions->Name != nullptr && containerOptions->Name[0] != '\0')
{
ValidateName(containerOptions->Name, WSLC_MAX_CONTAINER_NAME_LENGTH);
}
Expand All @@ -1636,8 +1660,38 @@ try
{
std::scoped_lock lock(m_containersLock, m_volumesLock, m_networksLock);

// Generate a unique container name if the user didn't provide one.
std::string containerName;
if (containerOptions->Name != nullptr && containerOptions->Name[0] != '\0')
{
containerName = containerOptions->Name;
}
else
{
constexpr int c_maxNameRetries = 6;
for (int attempt = 0; attempt < c_maxNameRetries; attempt++)
{
auto randomName = GenerateContainerName(attempt);
if (std::ranges::none_of(m_containers, [&](const auto& c) { return c->Name() == randomName; }))
{
containerName = randomName;
break;
}
}
Comment thread
yao-msft marked this conversation as resolved.

// Fallback to a GUID name.
if (containerName.empty())
{
WSL_LOG("GenerateGuidContainerName");
GUID guid{};
Comment thread
yao-msft marked this conversation as resolved.
THROW_IF_FAILED(CoCreateGuid(&guid));
containerName = wsl::shared::string::GuidToString<char>(guid, wsl::shared::string::GuidToStringFlags::None);
}
}
Comment thread
yao-msft marked this conversation as resolved.

auto& it = m_containers.emplace_back(WSLCContainerImpl::Create(
*containerOptions,
containerName,
*this,
m_virtualMachine.value(),
m_volumes,
Expand Down
37 changes: 34 additions & 3 deletions test/windows/WSLCTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Module Name:
#include "WSLCContainerLauncher.h"
#include "WslCoreFilesystem.h"
#include "hcs.hpp"
#include "ContainerNameGenerator.h"
#include <nlohmann/json.hpp>

using namespace std::literals::chrono_literals;
Expand Down Expand Up @@ -7809,7 +7810,7 @@ class WSLCTests
WSLC_TEST_METHOD(ContainerNameGeneration)
{
{
// Create a container with a specific name
// Create a container with a specific name.
auto container = WSLCContainerLauncher("debian:latest", "test-container-name").Create(*m_defaultSession.get());

// Validate that the container name is correct.
Expand All @@ -7820,8 +7821,38 @@ class WSLCTests
// Create a container without name.
auto container = WSLCContainerLauncher("debian:latest").Create(*m_defaultSession.get());

// Validate that the service generates a name for the container.
VERIFY_ARE_NOT_EQUAL(container.Name(), "");
// Validate that the service generates a name in the format "descriptor_mountain[digit]".
auto name = container.Name();
VERIFY_ARE_NOT_EQUAL(name, "");

auto underscore = name.find('_');
VERIFY_ARE_NOT_EQUAL(underscore, std::string::npos);

auto descriptor = name.substr(0, underscore);
auto mountain = name.substr(underscore + 1);

// Strip trailing retry digit if present.
if (!mountain.empty() && std::isdigit(mountain.back()))
Comment thread
yao-msft marked this conversation as resolved.
{
mountain.pop_back();
}

using wsl::windows::service::wslc::c_descriptors;
using wsl::windows::service::wslc::c_mountains;

VERIFY_IS_TRUE(std::ranges::find(c_descriptors, descriptor) != c_descriptors.end());
VERIFY_IS_TRUE(std::ranges::find(c_mountains, mountain) != c_mountains.end());
}

{
// Create multiple containers without names and verify they get unique names.
auto container1 = WSLCContainerLauncher("debian:latest").Create(*m_defaultSession.get());
auto container2 = WSLCContainerLauncher("debian:latest").Create(*m_defaultSession.get());
auto container3 = WSLCContainerLauncher("debian:latest").Create(*m_defaultSession.get());

VERIFY_ARE_NOT_EQUAL(container1.Name(), container2.Name());
VERIFY_ARE_NOT_EQUAL(container1.Name(), container3.Name());
VERIFY_ARE_NOT_EQUAL(container2.Name(), container3.Name());
Comment thread
yao-msft marked this conversation as resolved.
}
}

Expand Down