Skip to content
Draft
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
43 changes: 43 additions & 0 deletions localization/strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -2560,6 +2560,49 @@ For privacy information about this product please visit https://aka.ms/privacy.<
<value>Manage the lifecycle of WSL containers, including creating, starting, stopping, and removing them.</value>
<comment>{Locked="WSL"}Product names should not be translated</comment>
</data>
<data name="WSLCCLI_ComposeCommandDesc" xml:space="preserve">
<value>Manage compose sessions.</value>
</data>
<data name="WSLCCLI_ComposeCommandLongDesc" xml:space="preserve">
<value>Create and manage containers declared by a compose file.</value>
</data>
<data name="WSLCCLI_ComposeCreateDesc" xml:space="preserve">
<value>Create a compose session.</value>
</data>
<data name="WSLCCLI_ComposeCreateLongDesc" xml:space="preserve">
<value>Create the containers declared by a compose file.</value>
</data>
<data name="WSLCCLI_ComposeUpDesc" xml:space="preserve">
<value>Start and attach to a compose session.</value>
</data>
<data name="WSLCCLI_ComposeUpLongDesc" xml:space="preserve">
<value>Start all containers in a compose session, then attach interactively and display their output.</value>
</data>
<data name="WSLCCLI_ComposeStartDesc" xml:space="preserve">
<value>Start a compose session.</value>
</data>
<data name="WSLCCLI_ComposeStartLongDesc" xml:space="preserve">
<value>Start all containers in a compose session.</value>
</data>
<data name="WSLCCLI_ComposeAttachDesc" xml:space="preserve">
<value>Attach to a compose session.</value>
</data>
<data name="WSLCCLI_ComposeAttachLongDesc" xml:space="preserve">
<value>Attach interactively and display output from all containers in a compose session.</value>
</data>
<data name="WSLCCLI_ComposeStopDesc" xml:space="preserve">
<value>Stop a compose session.</value>
</data>
<data name="WSLCCLI_ComposeStopLongDesc" xml:space="preserve">
<value>Stop all containers in a compose session.</value>
</data>
<data name="WSLCCLI_ComposePathArgDescription" xml:space="preserve">
<value>Path to the compose YAML file</value>
</data>
<data name="MessageWslcComposeFileInvalid" xml:space="preserve">
<value>The compose file '{0}' is invalid: {1}.</value>
<comment>{Locked="{0}"}{Locked="{1}"}File paths, property names, and error details should not be translated</comment>
</data>
<data name="WSLCCLI_ContainerAttachDesc" xml:space="preserve">
<value>Attach to a container.</value>
</data>
Expand Down
8 changes: 8 additions & 0 deletions msipackage/package.wix.in
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,14 @@
</RegistryKey>
</RegistryKey>

<!-- IWSLCComposeSession-->
<RegistryKey Root="HKCR" Key="Interface\{96DFF26B-422A-44F3-B675-23DA2BA32C77}">
<RegistryValue Value="IWSLCComposeSession" Type="string" />
<RegistryKey Key="ProxyStubClsid32">
<RegistryValue Value="{4EA0C6DD-E9FF-48E7-994E-13A31D10DC60}" Type="string" />
</RegistryKey>
</RegistryKey>

<!-- IWSLCProcess-->
<RegistryKey Root="HKCR" Key="Interface\{1AD163CD-393D-4B33-83A2-8A3F3F23E608}">
<RegistryValue Value="IWSLCProcess" Type="string" />
Expand Down
30 changes: 19 additions & 11 deletions src/windows/common/WSLCContainerLauncher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,24 +313,27 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::L
return std::make_pair(result, std::move(container));
}

std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::CreateNoThrow(IWSLCSession& Session, IWarningCallback* WarningCallback)
WSLCContainerLauncher::ContainerOptionsStorage WSLCContainerLauncher::CreateOptions()
{
WSLCContainerOptions options{};
ContainerOptionsStorage storage;
auto& options = storage.Options;
options.Image = m_image.c_str();

if (!m_name.empty())
{
options.Name = m_name.c_str();
}

std::vector<const char*> entrypointStorage;
auto& entrypointStorage = storage.EntrypointStorage;

for (const auto& e : m_entrypoint)
{
entrypointStorage.push_back(e.c_str());
}

auto [processOptions, commandLinePtrs, environmentPtrs] = CreateProcessOptions();
storage.CommandLineStorage = std::move(commandLinePtrs);
storage.EnvironmentStorage = std::move(environmentPtrs);
options.InitProcessOptions = processOptions;
options.Ports = m_ports.data();
options.PortsCount = static_cast<ULONG>(m_ports.size());
Expand Down Expand Up @@ -374,7 +377,7 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::C
options.DomainName = m_domainname.c_str();
}

std::vector<const char*> dnsServersStorage;
auto& dnsServersStorage = storage.DnsServersStorage;
for (const auto& e : m_dnsServers)
{
dnsServersStorage.push_back(e.c_str());
Expand All @@ -385,7 +388,7 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::C
options.DnsServers = {dnsServersStorage.data(), static_cast<ULONG>(dnsServersStorage.size())};
}

std::vector<const char*> dnsSearchDomainsStorage;
auto& dnsSearchDomainsStorage = storage.DnsSearchDomainsStorage;
for (const auto& e : m_dnsSearchDomains)
{
dnsSearchDomainsStorage.push_back(e.c_str());
Expand All @@ -396,7 +399,7 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::C
options.DnsSearchDomains = {dnsSearchDomainsStorage.data(), static_cast<ULONG>(dnsSearchDomainsStorage.size())};
}

std::vector<const char*> dnsOptionsStorage;
auto& dnsOptionsStorage = storage.DnsOptionsStorage;
for (const auto& e : m_dnsOptions)
{
dnsOptionsStorage.push_back(e.c_str());
Expand Down Expand Up @@ -427,9 +430,9 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::C
options.ContainerNetwork.NetworkMode = m_networkMode.c_str();

// Each additional network becomes an entry in NetworkingConfig.EndpointsConfig.
std::vector<WSLCNetworkConnection> connections;
auto& connections = storage.NetworkConnections;
connections.reserve(m_additionalNetworks.size());
std::vector<std::vector<KeyValuePair>> connectionSettings;
auto& connectionSettings = storage.NetworkConnectionSettings;
connectionSettings.reserve(m_additionalNetworks.size());
for (const auto& e : m_additionalNetworks)
{
Expand All @@ -451,7 +454,7 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::C
options.ContainerNetwork.NetworksCount = static_cast<ULONG>(connections.size());

// Aliases for the primary endpoint.
std::vector<KeyValuePair> primarySettings;
auto& primarySettings = storage.PrimaryNetworkSettings;
primarySettings.reserve(m_primaryNetworkAliases.size());
for (const auto& alias : m_primaryNetworkAliases)
{
Expand All @@ -466,9 +469,14 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::C
options.UlimitsCount = static_cast<ULONG>(m_ulimits.size());
options.Ulimits = m_ulimits.size() > 0 ? m_ulimits.data() : nullptr;

// TODO: Support volumes, ports, flags, container networking mode, etc.
return storage;
}

std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::CreateNoThrow(IWSLCSession& Session, IWarningCallback* WarningCallback)
{
auto storage = CreateOptions();
wil::com_ptr<IWSLCContainer> container;
auto result = Session.CreateContainer(&options, WarningCallback, &container);
auto result = Session.CreateContainer(&storage.Options, WarningCallback, &container);
if (FAILED(result))
{
return std::pair<HRESULT, std::optional<RunningWSLCContainer>>(result, std::optional<RunningWSLCContainer>{});
Expand Down
21 changes: 21 additions & 0 deletions src/windows/common/WSLCContainerLauncher.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,27 @@ class WSLCContainerLauncher : private WSLCProcessLauncher
using WSLCProcessLauncher::SetUser;
using WSLCProcessLauncher::SetWorkingDirectory;

protected:
struct ContainerOptionsStorage
{
ContainerOptionsStorage() = default;
NON_COPYABLE(ContainerOptionsStorage);
DEFAULT_MOVABLE(ContainerOptionsStorage);

WSLCContainerOptions Options{};
std::vector<const char*> EntrypointStorage;
std::vector<const char*> CommandLineStorage;
std::vector<const char*> EnvironmentStorage;
std::vector<const char*> DnsServersStorage;
std::vector<const char*> DnsSearchDomainsStorage;
std::vector<const char*> DnsOptionsStorage;
std::vector<WSLCNetworkConnection> NetworkConnections;
std::vector<std::vector<KeyValuePair>> NetworkConnectionSettings;
std::vector<KeyValuePair> PrimaryNetworkSettings;
};

ContainerOptionsStorage CreateOptions();

private:
struct NetworkConnection
{
Expand Down
16 changes: 16 additions & 0 deletions src/windows/service/inc/wslc.idl
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,20 @@ interface IWSLCContainer : IUnknown
HRESULT DownloadArchive([in, string] LPCSTR SrcPath, [in] WSLCHandle OutHandle);
}

[
uuid(96DFF26B-422A-44F3-B675-23DA2BA32C77),
pointer_default(unique),
object
]
interface IWSLCComposeSession : IUnknown
{
HRESULT GetConfigPath([out] LPWSTR* Path);
HRESULT ListContainers([out, size_is(, *Count)] WSLCContainerEntry** Containers, [out] ULONG* Count);
HRESULT Start();
HRESULT Attach();
HRESULT Stop([in] ULONG Timeout);
}

typedef struct _WSLCDeletedImageInformation
{
char Image[WSLC_MAX_IMAGE_NAME_LENGTH + 1];
Expand Down Expand Up @@ -757,6 +771,8 @@ interface IWSLCSession : IUnknown
// RPC_E_DISCONNECTED. The client holds the returned token for the whole operation; releasing
// it (or the client exiting) lets the VM idle-terminate again.
HRESULT BeginContainerOperation([out] IUnknown** Operation);

HRESULT CreateComposeSession([in] LPCWSTR Path, [out] IWSLCComposeSession** ComposeSession);
}

//
Expand Down
Loading