From a7a9a5030ada4629310972ba6723353eeb3e0989 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Fri, 24 Apr 2026 01:20:22 -0700
Subject: [PATCH 1/2] Init network option
---
localization/strings/en-US/Resources.resw | 3 ++
.../wslc/arguments/ArgumentDefinitions.h | 5 +--
.../wslc/arguments/ArgumentValidation.cpp | 33 +++++++++++++++++++
.../wslc/arguments/ArgumentValidation.h | 3 ++
.../wslc/commands/ContainerCreateCommand.cpp | 1 +
.../wslc/commands/ContainerRunCommand.cpp | 1 +
src/windows/wslc/services/ContainerModel.h | 1 +
.../wslc/services/ContainerService.cpp | 2 +-
src/windows/wslc/tasks/ContainerTasks.cpp | 5 +++
.../wslc/e2e/WSLCE2EContainerCreateTests.cpp | 27 +++++++++++++++
.../wslc/e2e/WSLCE2EContainerRunTests.cpp | 22 +++++++++++++
11 files changed, 100 insertions(+), 3 deletions(-)
diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index de258d29d..af48af5df 100644
--- a/localization/strings/en-US/Resources.resw
+++ b/localization/strings/en-US/Resources.resw
@@ -2565,6 +2565,9 @@ On first run, creates the file with all settings commented out at their defaults
Remove the container after it stops
+
+ Connect a container to a network (none, host, bridge)
+
Session ID
diff --git a/src/windows/wslc/arguments/ArgumentDefinitions.h b/src/windows/wslc/arguments/ArgumentDefinitions.h
index a7bfe4e9b..bd47f1bcf 100644
--- a/src/windows/wslc/arguments/ArgumentDefinitions.h
+++ b/src/windows/wslc/arguments/ArgumentDefinitions.h
@@ -65,7 +65,8 @@ _(Input, "input", L"i", Kind::Value, L
_(Interactive, "interactive", L"i", Kind::Flag, Localization::WSLCCLI_InteractiveArgDescription()) \
_(Label, "label", NO_ALIAS, Kind::Value, L"Volume metadata setting") \
_(Name, "name", NO_ALIAS, Kind::Value, Localization::WSLCCLI_NameArgDescription()) \
-/*_(NoDNS, "no-dns", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoDNSArgDescription())*/ \
+_(Network, "network", NO_ALIAS, Kind::Value, Localization::WSLCCLI_NetworkArgDescription()) \
+/*_(NoDNS, "no-dns", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoDNSArgDescription())*/\
_(NoCache, "no-cache", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoCacheArgDescription()) \
_(NoPrune, "no-prune", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoPruneArgDescription()) \
_(NoTrunc, "no-trunc", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoTruncArgDescription()) \
@@ -85,9 +86,9 @@ _(Remove, "rm", NO_ALIAS, Kind::Flag, L
_(Server, "server", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_LoginServerArgDescription()) \
_(Session, "session", NO_ALIAS, Kind::Value, Localization::WSLCCLI_SessionIdArgDescription()) \
_(SessionId, "session-id", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_SessionIdPositionalArgDescription()) \
-_(StoragePath, "storage-path", NO_ALIAS, Kind::Positional, L"Path to the session storage directory") \
_(Signal, "signal", L"s", Kind::Value, Localization::WSLCCLI_SignalArgDescription(L"SIGKILL")) \
_(Source, "source", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_SourceArgDescription()) \
+_(StoragePath, "storage-path", NO_ALIAS, Kind::Positional, L"Path to the session storage directory") \
_(Tag, "tag", L"t", Kind::Value, Localization::WSLCCLI_TagArgDescription()) \
_(Target, "target", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_TargetArgDescription()) \
_(Time, "time", L"t", Kind::Value, Localization::WSLCCLI_TimeArgDescription()) \
diff --git a/src/windows/wslc/arguments/ArgumentValidation.cpp b/src/windows/wslc/arguments/ArgumentValidation.cpp
index b96dadc4e..6dc410377 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.cpp
+++ b/src/windows/wslc/arguments/ArgumentValidation.cpp
@@ -40,6 +40,10 @@ void Argument::Validate(const ArgMap& execArgs) const
validation::ValidateWSLCSignalFromString(execArgs.GetAll(), m_name);
break;
+ case ArgType::Network:
+ validation::ValidateNetworkType(execArgs.GetAll(), m_name);
+ break;
+
case ArgType::Time:
validation::ValidateIntegerFromString(execArgs.GetAll(), m_name);
break;
@@ -191,4 +195,33 @@ InspectType GetInspectTypeFromString(const std::wstring& input, const std::wstri
}
}
+void ValidateNetworkType(const std::vector& values, const std::wstring& argName)
+{
+ for (const auto& value : values)
+ {
+ std::ignore = GetNetworkTypeFromString(value, argName);
+ }
+}
+
+WSLCContainerNetworkType GetNetworkTypeFromString(const std::wstring& input, const std::wstring& argName)
+{
+ if (IsEqual(input, L"none"))
+ {
+ return WSLCContainerNetworkTypeNone;
+ }
+ else if (IsEqual(input, L"host"))
+ {
+ return WSLCContainerNetworkTypeHost;
+ }
+ else if (IsEqual(input, L"bridge"))
+ {
+ return WSLCContainerNetworkTypeBridged;
+ }
+ else
+ {
+ throw ArgumentException(
+ std::format(L"Invalid {} argument value: '{}'. Expected one of: none, host, bridge", argName, input));
+ }
+}
+
} // namespace wsl::windows::wslc::validation
diff --git a/src/windows/wslc/arguments/ArgumentValidation.h b/src/windows/wslc/arguments/ArgumentValidation.h
index 879582558..9497b0f20 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.h
+++ b/src/windows/wslc/arguments/ArgumentValidation.h
@@ -58,6 +58,9 @@ T GetIntegerFromString(const std::wstring& value, const std::wstring& argName =
void ValidateWSLCSignalFromString(const std::vector& values, const std::wstring& argName);
WSLCSignal GetWSLCSignalFromString(const std::wstring& input, const std::wstring& argName = {});
+void ValidateNetworkType(const std::vector& values, const std::wstring& argName);
+WSLCContainerNetworkType GetNetworkTypeFromString(const std::wstring& input, const std::wstring& argName = {});
+
void ValidateFormatTypeFromString(const std::vector& values, const std::wstring& argName);
FormatType GetFormatTypeFromString(const std::wstring& input, const std::wstring& argName = {});
diff --git a/src/windows/wslc/commands/ContainerCreateCommand.cpp b/src/windows/wslc/commands/ContainerCreateCommand.cpp
index 2135c0197..c5cbd83ae 100644
--- a/src/windows/wslc/commands/ContainerCreateCommand.cpp
+++ b/src/windows/wslc/commands/ContainerCreateCommand.cpp
@@ -44,6 +44,7 @@ std::vector ContainerCreateCommand::GetArguments() const
Argument::Create(ArgType::Hostname),
Argument::Create(ArgType::Interactive),
Argument::Create(ArgType::Name),
+ Argument::Create(ArgType::Network),
// Argument::Create(ArgType::NoDNS),
// Argument::Create(ArgType::Progress),
Argument::Create(ArgType::Publish, false, NO_LIMIT),
diff --git a/src/windows/wslc/commands/ContainerRunCommand.cpp b/src/windows/wslc/commands/ContainerRunCommand.cpp
index 7899d3f79..a5484a48d 100644
--- a/src/windows/wslc/commands/ContainerRunCommand.cpp
+++ b/src/windows/wslc/commands/ContainerRunCommand.cpp
@@ -44,6 +44,7 @@ std::vector ContainerRunCommand::GetArguments() const
Argument::Create(ArgType::Hostname),
Argument::Create(ArgType::Interactive),
Argument::Create(ArgType::Name),
+ Argument::Create(ArgType::Network),
// Argument::Create(ArgType::NoDNS),
// Argument::Create(ArgType::Progress),
Argument::Create(ArgType::Publish, false, NO_LIMIT),
diff --git a/src/windows/wslc/services/ContainerModel.h b/src/windows/wslc/services/ContainerModel.h
index 5997a2736..9aa72bbb6 100644
--- a/src/windows/wslc/services/ContainerModel.h
+++ b/src/windows/wslc/services/ContainerModel.h
@@ -37,6 +37,7 @@ struct ContainerOptions
bool Remove = false;
bool TTY = false;
bool PublishAll = false;
+ WSLCContainerNetworkType NetworkType = WSLCContainerNetworkTypeBridged;
std::vector Ports;
std::vector Volumes;
std::string WorkingDirectory;
diff --git a/src/windows/wslc/services/ContainerService.cpp b/src/windows/wslc/services/ContainerService.cpp
index 90af89e87..888cf4e8f 100644
--- a/src/windows/wslc/services/ContainerService.cpp
+++ b/src/windows/wslc/services/ContainerService.cpp
@@ -48,7 +48,7 @@ static wsl::windows::common::RunningWSLCContainer CreateInternal(Session& sessio
WI_SetFlagIf(containerFlags, WSLCContainerFlagsPublishAll, options.PublishAll);
wsl::windows::common::WSLCContainerLauncher containerLauncher(
- image, options.Name, options.Arguments, options.EnvironmentVariables, WSLCContainerNetworkTypeBridged, processFlags);
+ image, options.Name, options.Arguments, options.EnvironmentVariables, options.NetworkType, processFlags);
// Set port options if provided
for (const auto& port : options.Ports)
diff --git a/src/windows/wslc/tasks/ContainerTasks.cpp b/src/windows/wslc/tasks/ContainerTasks.cpp
index 804f54282..faf186891 100644
--- a/src/windows/wslc/tasks/ContainerTasks.cpp
+++ b/src/windows/wslc/tasks/ContainerTasks.cpp
@@ -247,6 +247,11 @@ void SetContainerOptionsFromArgs(CLIExecutionContext& context)
options.Remove = true;
}
+ if (context.Args.Contains(ArgType::Network))
+ {
+ options.NetworkType = validation::GetNetworkTypeFromString(context.Args.Get());
+ }
+
if (context.Args.Contains(ArgType::Command))
{
options.Arguments.emplace_back(WideToMultiByte(context.Args.Get()));
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
index aa80ddd8e..9ded9a228 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
@@ -651,6 +651,32 @@ class WSLCE2EContainerCreateTests
VERIFY_IS_TRUE(result.Stdout->find(L"options ndots:5 timeout:3") != std::wstring::npos);
}
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_Network_None)
+ {
+ auto result = RunWslc(std::format(
+ L"container create --network none --name {} {} cat /etc/resolv.conf", WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ std::wstring containerId = result.GetStdoutOneLine();
+ VerifyContainerIsListed(containerId, L"created");
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_Network_Host)
+ {
+ auto result = RunWslc(std::format(
+ L"container create --network host --name {} {} echo hello", WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ std::wstring containerId = result.GetStdoutOneLine();
+ VerifyContainerIsListed(containerId, L"created");
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_Network_Invalid)
+ {
+ auto result = RunWslc(std::format(
+ L"container create --network invalid --name {} {} echo hello", WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.ExitCode = 1});
+ VERIFY_IS_TRUE(result.Stderr->find(L"Invalid") != std::wstring::npos);
+ }
+
private:
// Test container name
const std::wstring WslcContainerName = L"wslc-test-container";
@@ -720,6 +746,7 @@ class WSLCE2EContainerCreateTests
<< L" -h,--hostname Container host name\r\n"
<< L" -i,--interactive Attach to stdin and keep it open\r\n"
<< L" --name Name of the container\r\n"
+ << L" --network Connect a container to a network (none, host, bridge)\r\n"
<< L" -p,--publish Publish a port from a container to host\r\n"
<< L" -P,--publish-all Publish all exposed ports to random host ports\r\n"
<< L" --rm Remove the container after it stops\r\n"
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
index 506c2e81b..8f4d5a473 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
@@ -641,6 +641,27 @@ class WSLCE2EContainerRunTests
result.Verify({.Stderr = std::format(L"Volume not found: '{}'\r\nError code: WSLC_E_VOLUME_NOT_FOUND\r\n", WslcVolumeName), .ExitCode = 1});
}
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_Network_None)
+ {
+ auto result = RunWslc(std::format(L"container run --rm --network none {} echo hello", DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_ARE_EQUAL(*result.Stdout, L"hello\r\n");
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_Network_Host)
+ {
+ auto result = RunWslc(std::format(L"container run --rm --network host {} echo hello", DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_ARE_EQUAL(*result.Stdout, L"hello\r\n");
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_Network_Invalid)
+ {
+ auto result = RunWslc(std::format(L"container run --rm --network invalid {} echo hello", DebianImage.NameAndTag()));
+ result.Verify({.ExitCode = 1});
+ VERIFY_IS_TRUE(result.Stderr->find(L"Invalid") != std::wstring::npos);
+ }
+
private:
// Test container name
const std::wstring WslcContainerName = L"wslc-test-container";
@@ -716,6 +737,7 @@ class WSLCE2EContainerRunTests
<< L" -h,--hostname Container host name\r\n"
<< L" -i,--interactive Attach to stdin and keep it open\r\n"
<< L" --name Name of the container\r\n"
+ << L" --network Connect a container to a network (none, host, bridge)\r\n"
<< L" -p,--publish Publish a port from a container to host\r\n"
<< L" -P,--publish-all Publish all exposed ports to random host ports\r\n"
<< L" --rm Remove the container after it stops\r\n"
From 6cf64dca8dafe35493ff4c2ab057f828946a378f Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Fri, 24 Apr 2026 15:46:10 -0700
Subject: [PATCH 2/2] Clang format
---
src/windows/wslc/arguments/ArgumentValidation.cpp | 3 +--
test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp | 8 ++++----
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/src/windows/wslc/arguments/ArgumentValidation.cpp b/src/windows/wslc/arguments/ArgumentValidation.cpp
index 6dc410377..f2a53bb9d 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.cpp
+++ b/src/windows/wslc/arguments/ArgumentValidation.cpp
@@ -219,8 +219,7 @@ WSLCContainerNetworkType GetNetworkTypeFromString(const std::wstring& input, con
}
else
{
- throw ArgumentException(
- std::format(L"Invalid {} argument value: '{}'. Expected one of: none, host, bridge", argName, input));
+ throw ArgumentException(std::format(L"Invalid {} argument value: '{}'. Expected one of: none, host, bridge", argName, input));
}
}
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
index 9ded9a228..0dc24e325 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
@@ -662,8 +662,8 @@ class WSLCE2EContainerCreateTests
WSLC_TEST_METHOD(WSLCE2E_Container_Create_Network_Host)
{
- auto result = RunWslc(std::format(
- L"container create --network host --name {} {} echo hello", WslcContainerName, DebianImage.NameAndTag()));
+ auto result =
+ RunWslc(std::format(L"container create --network host --name {} {} echo hello", WslcContainerName, DebianImage.NameAndTag()));
result.Verify({.Stderr = L"", .ExitCode = 0});
std::wstring containerId = result.GetStdoutOneLine();
VerifyContainerIsListed(containerId, L"created");
@@ -671,8 +671,8 @@ class WSLCE2EContainerCreateTests
WSLC_TEST_METHOD(WSLCE2E_Container_Create_Network_Invalid)
{
- auto result = RunWslc(std::format(
- L"container create --network invalid --name {} {} echo hello", WslcContainerName, DebianImage.NameAndTag()));
+ auto result = RunWslc(
+ std::format(L"container create --network invalid --name {} {} echo hello", WslcContainerName, DebianImage.NameAndTag()));
result.Verify({.ExitCode = 1});
VERIFY_IS_TRUE(result.Stderr->find(L"Invalid") != std::wstring::npos);
}