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
3 changes: 3 additions & 0 deletions localization/strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -2565,6 +2565,9 @@ On first run, creates the file with all settings commented out at their defaults
<data name="WSLCCLI_RemoveArgDescription" xml:space="preserve">
<value>Remove the container after it stops</value>
</data>
<data name="WSLCCLI_NetworkArgDescription" xml:space="preserve">
<value>Connect a container to a network (none, host, bridge)</value>
</data>
<data name="WSLCCLI_SessionIdPositionalArgDescription" xml:space="preserve">
<value>Session ID</value>
</data>
Expand Down
5 changes: 3 additions & 2 deletions src/windows/wslc/arguments/ArgumentDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()) \
Expand All @@ -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()) \
Expand Down
32 changes: 32 additions & 0 deletions src/windows/wslc/arguments/ArgumentValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ void Argument::Validate(const ArgMap& execArgs) const
validation::ValidateWSLCSignalFromString(execArgs.GetAll<ArgType::Signal>(), m_name);
break;

case ArgType::Network:
validation::ValidateNetworkType(execArgs.GetAll<ArgType::Network>(), m_name);
break;

case ArgType::Time:
validation::ValidateIntegerFromString<LONGLONG>(execArgs.GetAll<ArgType::Time>(), m_name);
break;
Expand Down Expand Up @@ -191,4 +195,32 @@ InspectType GetInspectTypeFromString(const std::wstring& input, const std::wstri
}
}

void ValidateNetworkType(const std::vector<std::wstring>& 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
3 changes: 3 additions & 0 deletions src/windows/wslc/arguments/ArgumentValidation.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ T GetIntegerFromString(const std::wstring& value, const std::wstring& argName =
void ValidateWSLCSignalFromString(const std::vector<std::wstring>& values, const std::wstring& argName);
WSLCSignal GetWSLCSignalFromString(const std::wstring& input, const std::wstring& argName = {});

void ValidateNetworkType(const std::vector<std::wstring>& values, const std::wstring& argName);
WSLCContainerNetworkType GetNetworkTypeFromString(const std::wstring& input, const std::wstring& argName = {});

void ValidateFormatTypeFromString(const std::vector<std::wstring>& values, const std::wstring& argName);
FormatType GetFormatTypeFromString(const std::wstring& input, const std::wstring& argName = {});

Expand Down
1 change: 1 addition & 0 deletions src/windows/wslc/commands/ContainerCreateCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ std::vector<Argument> 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),
Expand Down
1 change: 1 addition & 0 deletions src/windows/wslc/commands/ContainerRunCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ std::vector<Argument> 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),
Expand Down
1 change: 1 addition & 0 deletions src/windows/wslc/services/ContainerModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct ContainerOptions
bool Remove = false;
bool TTY = false;
bool PublishAll = false;
WSLCContainerNetworkType NetworkType = WSLCContainerNetworkTypeBridged;
std::vector<std::string> Ports;
std::vector<std::wstring> Volumes;
std::string WorkingDirectory;
Expand Down
2 changes: 1 addition & 1 deletion src/windows/wslc/services/ContainerService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions src/windows/wslc/tasks/ContainerTasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ void SetContainerOptionsFromArgs(CLIExecutionContext& context)
options.Remove = true;
}

if (context.Args.Contains(ArgType::Network))
{
options.NetworkType = validation::GetNetworkTypeFromString(context.Args.Get<ArgType::Network>());
}

if (context.Args.Contains(ArgType::Command))
{
options.Arguments.emplace_back(WideToMultiByte(context.Args.Get<ArgType::Command>()));
Expand Down
27 changes: 27 additions & 0 deletions test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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"
Expand Down
22 changes: 22 additions & 0 deletions test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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"
Expand Down
Loading