Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
17 changes: 17 additions & 0 deletions src/windows/inc/ComposeLabels.h

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sold on using the docker scheme for these. I think it would be safer to use "com.microsoft.wslc.compose.*" prefixes for everything, but this is a constant easily changed.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (C) Microsoft Corporation. All rights reserved.

#pragma once

namespace wsl::windows::service::wslc::compose::label {

inline constexpr char c_project[] = "com.docker.compose.project";
inline constexpr char c_service[] = "com.docker.compose.service";
inline constexpr char c_containerNumber[] = "com.docker.compose.container-number";
inline constexpr char c_oneoff[] = "com.docker.compose.oneoff";
inline constexpr char c_network[] = "com.docker.compose.network";
inline constexpr char c_managed[] = "com.microsoft.wslc.compose.managed";
inline constexpr char c_managedValue[] = "true";
inline constexpr char c_metadataVersion[] = "com.microsoft.wslc.compose.metadata-version";
inline constexpr char c_metadataVersionValue[] = "1";

} // namespace wsl::windows::service::wslc::compose::label
4 changes: 4 additions & 0 deletions src/windows/service/inc/wslc.idl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ cpp_quote("#endif")
#define WSLC_MAX_BINDING_ADDRESS_LENGTH 45
#define WSLC_EPHEMERAL_PORT 0
#define WSLC_MAX_SAVE_IMAGES_COUNT 256
#define WSLC_MAX_COMPOSE_PROJECT_NAME_LENGTH 255
#define WSLC_COMPOSE_SCHEMA_VERSION 1

cpp_quote("#define WSLC_MAX_CONTAINER_NAME_LENGTH 255")
cpp_quote("#define WSLC_MAX_IMAGE_NAME_LENGTH 255")
Expand All @@ -45,6 +47,8 @@ cpp_quote("#define WSLC_MAX_IMAGE_ID_LENGTH 255")
cpp_quote("#define WSLC_CONTAINER_ID_LENGTH 64")
cpp_quote("#define WSLC_MAX_BINDING_ADDRESS_LENGTH 45")
cpp_quote("#define WSLC_MAX_SAVE_IMAGES_COUNT 256")
cpp_quote("#define WSLC_MAX_COMPOSE_PROJECT_NAME_LENGTH 255")
cpp_quote("#define WSLC_COMPOSE_SCHEMA_VERSION 1")
cpp_quote("#define WSLC_EPHEMERAL_PORT 0")
cpp_quote("#define WSLC_STOP_TIMEOUT_DEFAULT LONG_MIN") // Pass to Stop() to use the default container stop timeout
cpp_quote("#define WSLC_STOP_TIMEOUT_NONE -1") // Wait forever for the container to stop.
Expand Down
3 changes: 3 additions & 0 deletions src/windows/wslcsession/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set(SOURCES
WSLCSessionReference.cpp

# Session and container implementation
ComposeNormalizer.cpp
ComposeSpec.cpp
ServiceContainerLauncher.cpp
WSLCSession.cpp
Expand Down Expand Up @@ -37,12 +38,14 @@ set(SOURCES
)

set(HEADERS
ComposeNormalizer.h
ComposeSpec.h
DockerEventTracker.h
DockerHTTPClient.h
EventStore.h
IORelay.h
OptionParser.h
ResourceName.h
ServiceProcessLauncher.h
ServiceContainerLauncher.h
WSLCContainer.h
Expand Down
109 changes: 109 additions & 0 deletions src/windows/wslcsession/ComposeNormalizer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (C) Microsoft Corporation. All rights reserved.

#include "precomp.h"
#include "ComposeNormalizer.h"
#include "ResourceName.h"

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

namespace {

std::string NormalizeProjectName(std::string_view name)
{
std::string result{name};
std::ranges::transform(result, result.begin(), [](unsigned char value) { return static_cast<char>(std::tolower(value)); });
std::erase_if(result, [](unsigned char value) { return !std::isalnum(value) && value != '-' && value != '_'; });
while (!result.empty() && !std::isalnum(static_cast<unsigned char>(result.front())))
{
result.erase(result.begin());
}

THROW_HR_IF(E_INVALIDARG, result.empty());
THROW_HR_IF(E_INVALIDARG, result.size() > WSLC_MAX_COMPOSE_PROJECT_NAME_LENGTH);
return result;
}

} // namespace

ComposeSpec ComposeNormalizer::Normalize(const ComposeDocuments& documents, const ComposeProjectSelection& selection)
{
THROW_HR_IF(E_INVALIDARG, documents.SchemaVersion != WSLC_COMPOSE_SCHEMA_VERSION);
THROW_HR_IF(E_INVALIDARG, documents.WorkingDirectory.empty());
THROW_HR_IF(E_INVALIDARG, documents.ProjectDirectory.empty());
THROW_HR_IF(E_INVALIDARG, documents.Documents.size() != 1);
ValidateSelection(selection);

const auto& document = documents.Documents.front();
THROW_HR_IF(E_INVALIDARG, document.SourcePath.empty());
THROW_HR_IF(E_INVALIDARG, document.BaseDirectory.empty());
THROW_HR_IF(E_INVALIDARG, document.Content.empty());

std::string content(reinterpret_cast<const char*>(document.Content.data()), document.Content.size());
THROW_HR_IF(E_INVALIDARG, content.find('\0') != std::string::npos);

auto spec = ComposeSpec::Parse(document.SourcePath, content);

std::string projectName;
if (documents.ExplicitProjectName.has_value())
{
projectName = NormalizeProjectName(*documents.ExplicitProjectName);
}
else
{
auto projectDirectory = documents.ProjectDirectory.lexically_normal();
if (projectDirectory.filename().empty())
{
projectDirectory = projectDirectory.parent_path();
}

projectName = NormalizeProjectName(wsl::shared::string::WideToMultiByte(projectDirectory.filename().wstring()));
}

spec.ProjectName = std::move(projectName);
ValidateName(DefaultNetworkName(spec.ProjectName).c_str(), WSLC_MAX_NETWORK_NAME_LENGTH);
for (auto& container : spec.Containers)
{
THROW_HR_WITH_USER_ERROR_IF(
E_INVALIDARG, wsl::shared::Localization::MessageWslcInvalidName(container.ServiceName.c_str()), container.ServiceName.empty());
Comment thread
dkbennett marked this conversation as resolved.
Resolved

if (container.Name.empty())
Comment thread
Copilot marked this conversation as resolved.
Resolved
{
container.Name = std::format("{}-{}-1", spec.ProjectName, container.ServiceName);
}

ValidateName(container.Name.c_str(), WSLC_MAX_CONTAINER_NAME_LENGTH);
Comment thread
dkbennett marked this conversation as resolved.
Resolved
}
Comment thread
dkbennett marked this conversation as resolved.
Resolved

return spec;
}

std::string ComposeNormalizer::DefaultNetworkName(std::string_view projectName)
{
return std::format("{}_default", projectName);
}

void ComposeNormalizer::ValidateSelection(const ComposeProjectSelection& selection)
{
THROW_HR_IF(E_NOTIMPL, !selection.Profiles.empty() || !selection.Services.empty() || selection.IncludeDependencies);
}

std::string ComposeNormalizer::ValidateProjectKey(std::string_view projectKey)
{
THROW_HR_IF(E_INVALIDARG, !IsValidProjectKey(projectKey));
return std::string{projectKey};
}

bool ComposeNormalizer::IsValidProjectKey(std::string_view projectKey) noexcept
{
if (projectKey.empty() || projectKey.size() > WSLC_MAX_COMPOSE_PROJECT_NAME_LENGTH ||
!std::isalnum(static_cast<unsigned char>(projectKey.front())))
{
return false;
}

return std::ranges::all_of(projectKey, [](unsigned char value) {
return std::isdigit(value) || std::islower(value) || value == '-' || value == '_';
});
}

} // namespace wsl::windows::service::wslc
43 changes: 43 additions & 0 deletions src/windows/wslcsession/ComposeNormalizer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (C) Microsoft Corporation. All rights reserved.

#pragma once

#include "ComposeSpec.h"
#include "wslc.h"
#include <optional>

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

struct ComposeDocument
{
std::filesystem::path SourcePath;
std::filesystem::path BaseDirectory;
std::vector<std::byte> Content;
};

struct ComposeDocuments
{
ULONG SchemaVersion{};
std::filesystem::path WorkingDirectory;
std::filesystem::path ProjectDirectory;
std::optional<std::string> ExplicitProjectName;
std::vector<ComposeDocument> Documents;
};

struct ComposeProjectSelection
{
std::vector<std::string> Profiles;
std::vector<std::string> Services;
bool IncludeDependencies{};
};

struct ComposeNormalizer
{
static ComposeSpec Normalize(const ComposeDocuments& documents, const ComposeProjectSelection& selection);
static std::string DefaultNetworkName(std::string_view projectName);
static bool IsValidProjectKey(std::string_view projectKey) noexcept;
static std::string ValidateProjectKey(std::string_view projectKey);
static void ValidateSelection(const ComposeProjectSelection& selection);
};

} // namespace wsl::windows::service::wslc
Loading
Loading