Support arbitrary network driver options in wslc network create (#40859)
beena352 committed
Jul 1, 2026 at 17:16 UTC
a22a4c94590ff0ef457f4a7be8a5dec8dd61e3a5
13 files changed
+261
-143
localization/strings/en-US/Resources.resw
+13
-2
@@ -2340,8 +2340,8 @@ For privacy information about this product please visit https://aka.ms/privacy.<
2340
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
2341
</data>
2342
<data name="MessageWslcGatewayRequiresSubnet" xml:space="preserve">
2343
- <value>Network driver option 'Gateway' requires 'Subnet' to also be specified.</value>
2344
- <comment>{Locked="Gateway"}{Locked="Subnet"}Command line arguments, file names and string inserts should not be translated</comment>
2343
+ <value>The '--gateway' option requires '--subnet' to also be specified.</value>
2344
+ <comment>{Locked="--gateway'"}{Locked="--subnet'"}Command line arguments, file names and string inserts should not be translated</comment>
2345
</data>
2346
<data name = "MessageWslcNetworkInUse" xml:space = "preserve" >
2347
<value>Network '{}' has active endpoints.</value>
@@ -3115,6 +3115,17 @@ On first run, creates the file with all settings commented out at their defaults
3115
<data name="WSLCCLI_NetworkLabelArgDescription" xml:space="preserve">
3116
<value>Network metadata setting</value>
3117
</data>
3118
+ <data name="WSLCCLI_NetworkInternalArgDescription" xml:space="preserve">
3119
+ <value>Restrict external access to the network</value>
3120
+ </data>
3121
+ <data name="WSLCCLI_NetworkSubnetArgDescription" xml:space="preserve">
3122
+ <value>Subnet in CIDR format that represents a network segment</value>
3123
+ <comment>{Locked="CIDR"}Command line arguments, file names and string inserts should not be translated</comment>
3124
+ </data>
3125
+ <data name="WSLCCLI_NetworkGatewayArgDescription" xml:space="preserve">
3126
+ <value>IPv4 or IPv6 gateway for the subnet</value>
3127
+ <comment>{Locked="IPv4"}{Locked="IPv6"}Command line arguments, file names and string inserts should not be translated</comment>
3128
+ </data>
3129
<data name="WSLCCLI_NetworkRemoveDesc" xml:space="preserve">
3130
<value>Remove one or more networks.</value>
3131
</data>
src/windows/inc/docker_schema.h
+4
-2
@@ -146,9 +146,10 @@ struct CreateNetwork
146
std::string Driver;
147
bool Internal{};
148
std::optional<IPAM> IPAM;
149
+ std::optional<std::map<std::string, std::string>> Options;
150
std::map<std::string, std::string> Labels;
151
151
- NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(CreateNetwork, Name, Driver, Internal, IPAM, Labels);
152
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(CreateNetwork, Name, Driver, Internal, IPAM, Options, Labels);
153
};
154
155
struct Network
@@ -159,9 +160,10 @@ struct Network
160
std::string Scope;
161
bool Internal{};
162
IPAM IPAM;
163
+ std::optional<std::map<std::string, std::string>> Options;
164
std::map<std::string, std::string> Labels;
165
164
- NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Network, Id, Name, Driver, Scope, Internal, IPAM, Labels);
166
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Network, Id, Name, Driver, Scope, Internal, IPAM, Options, Labels);
167
};
168
169
struct ContainerNetworkRequest
src/windows/inc/wslc_schema.h
+2
-1
@@ -195,9 +195,10 @@ struct Network
195
std::string Scope;
196
bool Internal{};
197
IPAM IPAM;
198
+ std::optional<std::map<std::string, std::string>> Options;
199
std::map<std::string, std::string> Labels;
200
200
- NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Network, Id, Name, Driver, Scope, Internal, IPAM, Labels);
201
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Network, Id, Name, Driver, Scope, Internal, IPAM, Options, Labels);
202
};
203
204
} // namespace wsl::windows::common::wslc_schema
src/windows/service/inc/wslc.idl
+3
@@ -534,6 +534,9 @@ typedef struct _WSLCNetworkOptions
534
ULONG DriverOptsCount;
535
[unique, size_is(LabelsCount)] const WSLCLabel* Labels;
536
ULONG LabelsCount;
537
+ BOOL Internal;
538
+ [unique] LPCSTR Subnet;
539
+ [unique] LPCSTR Gateway;
540
} WSLCNetworkOptions;
541
542
typedef char WSLCNetworkName[WSLC_MAX_NETWORK_NAME_LENGTH + 1];
src/windows/wslc/arguments/ArgumentDefinitions.h
+3
@@ -61,6 +61,7 @@ _(Since, "since", NO_ALIAS, Kind::Value, L
61
_(Until, "until", NO_ALIAS, Kind::Value, Localization::WSLCCLI_UntilArgDescription()) \
62
_(Format, "format", NO_ALIAS, Kind::Value, Localization::WSLCCLI_FormatArgDescription()) \
63
_(ForwardArgs, "arguments", NO_ALIAS, Kind::Forward, Localization::WSLCCLI_ForwardArgsDescription()) \
64
+_(Gateway, "gateway", NO_ALIAS, Kind::Value, Localization::WSLCCLI_NetworkGatewayArgDescription()) \
65
_(Gpus, "gpus", NO_ALIAS, Kind::Value, Localization::WSLCCLI_GpusArgDescription()) \
66
/*_(GroupId, "groupid", NO_ALIAS, Kind::Value, Localization::WSLCCLI_GroupIdArgDescription())*/ \
67
_(Help, "help", WSLC_CLI_HELP_ARG, Kind::Flag, Localization::WSLCCLI_HelpArgDescription()) \
@@ -70,6 +71,7 @@ _(ImageId, "image", NO_ALIAS, Kind::Positional, L
71
_(ImportFile, "file", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_ImportFileArgDescription()) \
72
_(Input, "input", L"i", Kind::Value, Localization::WSLCCLI_InputArgDescription()) \
73
_(Interactive, "interactive", L"i", Kind::Flag, Localization::WSLCCLI_InteractiveArgDescription()) \
74
+_(Internal, "internal", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NetworkInternalArgDescription()) \
75
_(Label, "label", L"l", Kind::Value, Localization::WSLCCLI_LabelArgDescription()) \
76
_(Last, "last", L"n", Kind::Value, Localization::WSLCCLI_LastArgDescription()) \
77
_(Latest, "latest", L"l", Kind::Flag, Localization::WSLCCLI_LatestArgDescription()) \
@@ -104,6 +106,7 @@ _(Signal, "signal", L"s", Kind::Value, L
106
_(Source, "source", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_SourceArgDescription()) \
107
_(StopSignal, "stop-signal", NO_ALIAS, Kind::Value, Localization::WSLCCLI_StopSignalArgDescription()) \
108
_(StopTimeout, "stop-timeout", NO_ALIAS, Kind::Value, Localization::WSLCCLI_StopTimeoutArgDescription()) \
109
+_(Subnet, "subnet", NO_ALIAS, Kind::Value, Localization::WSLCCLI_NetworkSubnetArgDescription()) \
110
_(Tail, "tail", L"n", Kind::Value, Localization::WSLCCLI_TailArgDescription()) \
111
_(Tag, "tag", L"t", Kind::Value, Localization::WSLCCLI_TagArgDescription()) \
112
_(Target, "target", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_TargetArgDescription()) \
src/windows/wslc/commands/NetworkCreateCommand.cpp
+3
@@ -31,6 +31,9 @@ std::vector<Argument> NetworkCreateCommand::GetArguments() const
31
Argument::Create(ArgType::Driver, std::nullopt, std::nullopt, Localization::WSLCCLI_NetworkDriverOptionDescription()),
32
Argument::Create(ArgType::Options, false, NO_LIMIT),
33
Argument::Create(ArgType::Label, false, NO_LIMIT, Localization::WSLCCLI_NetworkLabelArgDescription()),
34
+ Argument::Create(ArgType::Gateway),
35
+ Argument::Create(ArgType::Internal),
36
+ Argument::Create(ArgType::Subnet),
37
};
38
}
39
src/windows/wslc/services/NetworkModel.h
+3
@@ -25,6 +25,9 @@ struct CreateNetworkOptions
25
std::optional<std::string> Driver;
26
std::vector<std::pair<std::string, std::string>> DriverOpts{};
27
std::vector<std::pair<std::string, std::string>> Labels{};
28
+ bool Internal{false};
29
+ std::optional<std::string> Subnet;
30
+ std::optional<std::string> Gateway;
31
};
32
33
struct PruneNetworksResult
src/windows/wslc/services/NetworkService.cpp
+11
@@ -50,6 +50,17 @@ void NetworkService::Create(models::Session& session, const models::CreateNetwor
50
options.Labels = labels.data();
51
options.LabelsCount = static_cast<ULONG>(labels.size());
52
53
+ options.Internal = createOptions.Internal ? TRUE : FALSE;
54
+ if (createOptions.Subnet.has_value())
55
+ {
56
+ options.Subnet = createOptions.Subnet->c_str();
57
+ }
58
+
59
+ if (createOptions.Gateway.has_value())
60
+ {
61
+ options.Gateway = createOptions.Gateway->c_str();
62
+ }
63
+
64
auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
65
THROW_IF_FAILED(session.Get()->CreateNetwork(&options, warningCallback.Get()));
66
}
src/windows/wslc/tasks/NetworkTasks.cpp
+12
@@ -95,6 +95,18 @@ void CreateNetwork(CLIExecutionContext& context)
95
options.Driver = WideToMultiByte(context.Args.Get<ArgType::Driver>());
96
}
97
98
+ options.Internal = context.Args.Contains(ArgType::Internal);
99
+
100
+ if (context.Args.Contains(ArgType::Subnet))
101
+ {
102
+ options.Subnet = WideToMultiByte(context.Args.Get<ArgType::Subnet>());
103
+ }
104
+
105
+ if (context.Args.Contains(ArgType::Gateway))
106
+ {
107
+ options.Gateway = WideToMultiByte(context.Args.Get<ArgType::Gateway>());
108
+ }
109
+
110
NetworkService::Create(context.Data.Get<Data::Session>(), options);
111
PrintMessage(MultiByteToWide(options.Name));
112
}
src/windows/wslcsession/WSLCNetworkMetadata.h
+1
@@ -45,6 +45,7 @@ struct NetworkEntry
45
std::string Scope;
46
bool Internal{false};
47
std::map<std::string, std::string> Labels;
48
+ std::map<std::string, std::string> Options;
49
NetworkIPAM IPAM;
50
};
51
src/windows/wslcsession/WSLCSession.cpp
+26
-21
@@ -2443,7 +2443,7 @@ try
2443
RETURN_HR_IF_NULL(E_POINTER, Options->Name);
2444
2445
std::string name = Options->Name;
2446
- std::string driver = (Options->Driver != nullptr) ? Options->Driver : WSLCBridgeNetworkDriver;
2446
+ std::string driver = Options->Driver != nullptr ? Options->Driver : WSLCBridgeNetworkDriver;
2447
2448
ValidateName(name.c_str(), WSLC_MAX_NETWORK_NAME_LENGTH);
2449
@@ -2453,17 +2453,6 @@ try
2453
auto driverOpts = wslutil::ParseKeyValuePairs(Options->DriverOpts, Options->DriverOptsCount);
2454
auto labels = wslutil::ParseKeyValuePairs(Options->Labels, Options->LabelsCount, WSLCNetworkManagedLabel);
2455
2456
- static constexpr std::array<std::string_view, 3> c_supportedDriverOpts{"Internal", "Subnet", "Gateway"};
2457
- for (const auto& [key, _] : driverOpts)
2458
- {
2459
- const bool supported = std::any_of(
2460
- c_supportedDriverOpts.begin(), c_supportedDriverOpts.end(), [&](std::string_view opt) { return key == opt; });
2461
- THROW_HR_WITH_USER_ERROR_IF(E_INVALIDARG, Localization::MessageWslcInvalidNetworkDriverOption(key), !supported);
2462
- }
2463
-
2464
- THROW_HR_WITH_USER_ERROR_IF(
2465
- E_INVALIDARG, Localization::MessageWslcGatewayRequiresSubnet(), driverOpts.contains("Gateway") && !driverOpts.contains("Subnet"));
2466
-
2456
auto lock = m_lock.lock_shared();
2457
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_dockerClient);
2458
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
@@ -2477,20 +2466,19 @@ try
2466
request.Labels = labels;
2467
request.Labels[WSLCNetworkManagedLabel] = "true";
2468
2480
- if (auto it = driverOpts.find("Internal"); it != driverOpts.end())
2481
- {
2482
- request.Internal = (it->second == "true");
2483
- }
2469
+ request.Internal = static_cast<bool>(Options->Internal);
2470
+
2471
+ THROW_HR_WITH_USER_ERROR_IF(
2472
+ E_INVALIDARG, Localization::MessageWslcGatewayRequiresSubnet(), Options->Gateway != nullptr && Options->Subnet == nullptr);
2473
2485
- if (auto it = driverOpts.find("Subnet"); it != driverOpts.end())
2474
+ if (Options->Subnet != nullptr)
2475
{
2476
docker_schema::IPAMConfig ipamConfig;
2488
- ipamConfig.Subnet = it->second;
2477
+ ipamConfig.Subnet = Options->Subnet;
2478
2490
- auto gatewayIt = driverOpts.find("Gateway");
2491
- if (gatewayIt != driverOpts.end())
2479
+ if (Options->Gateway != nullptr)
2480
{
2493
- ipamConfig.Gateway = gatewayIt->second;
2481
+ ipamConfig.Gateway = Options->Gateway;
2482
}
2483
2484
auto& ipam = request.IPAM.emplace();
@@ -2498,6 +2486,11 @@ try
2486
ipam.Config.emplace().push_back(std::move(ipamConfig));
2487
}
2488
2489
+ if (!driverOpts.empty())
2490
+ {
2491
+ request.Options = std::move(driverOpts);
2492
+ }
2493
+
2494
docker_schema::CreateNetworkResponse createResult;
2495
try
2496
{
@@ -2535,6 +2528,10 @@ try
2528
entry.Scope = full.Scope;
2529
entry.Internal = full.Internal;
2530
entry.Labels = full.Labels;
2531
+ if (full.Options)
2532
+ {
2533
+ entry.Options = *full.Options;
2534
+ }
2535
entry.IPAM.Driver = full.IPAM.Driver;
2536
if (full.IPAM.Config)
2537
{
@@ -2659,6 +2656,10 @@ try
2656
result.Scope = entry.Scope;
2657
result.Internal = entry.Internal;
2658
result.Labels = entry.Labels;
2659
+ if (!entry.Options.empty())
2660
+ {
2661
+ result.Options = entry.Options;
2662
+ }
2663
2664
result.IPAM.Driver = entry.IPAM.Driver;
2665
if (entry.IPAM.Config)
@@ -3489,6 +3490,10 @@ void WSLCSession::RecoverExistingNetworks()
3490
entry.Scope = network.Scope;
3491
entry.Internal = network.Internal;
3492
entry.Labels = network.Labels;
3493
+ if (network.Options)
3494
+ {
3495
+ entry.Options = *network.Options;
3496
+ }
3497
entry.IPAM.Driver = network.IPAM.Driver;
3498
if (network.IPAM.Config)
3499
{
test/windows/WSLCTests.cpp
+102
-112
@@ -5166,25 +5166,27 @@ class WSLCTests
5166
WSLC_TEST_METHOD(NetworkCreateWithSubnetTest)
5167
{
5168
const std::string networkName = "subnet-test-net";
5169
+ const std::string subnet = "172.28.0.0/16";
5170
5171
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
5172
5172
- WSLCDriverOption subnetOpt[] = {{"Subnet", "172.28.0.0/16"}};
5173
-
5173
WSLCNetworkOptions options{};
5174
options.Name = networkName.c_str();
5175
options.Driver = "bridge";
5177
- options.DriverOpts = subnetOpt;
5178
- options.DriverOptsCount = ARRAYSIZE(subnetOpt);
5176
+ options.Subnet = subnet.c_str();
5177
5178
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
5179
5180
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
5181
5184
- wil::unique_cotaskmem_array_ptr<WSLCNetworkInformation> networks;
5185
- VERIFY_SUCCEEDED(m_defaultSession->ListNetworks(networks.addressof(), networks.size_address<ULONG>()));
5186
- VERIFY_ARE_EQUAL(1u, networks.size());
5187
- VERIFY_ARE_EQUAL(networkName, std::string(networks[0].Name));
5182
+ wil::unique_cotaskmem_ansistring output;
5183
+ VERIFY_SUCCEEDED(m_defaultSession->InspectNetwork(networkName.c_str(), &output));
5184
+ VERIFY_IS_NOT_NULL(output.get());
5185
+
5186
+ auto inspect = wsl::shared::FromJson<wsl::windows::common::wslc_schema::Network>(output.get());
5187
+ VERIFY_IS_TRUE(inspect.IPAM.Config.has_value());
5188
+ VERIFY_ARE_EQUAL(1u, inspect.IPAM.Config->size());
5189
+ VERIFY_ARE_EQUAL(subnet, inspect.IPAM.Config->at(0).Subnet);
5190
}
5191
5192
WSLC_TEST_METHOD(NetworkCreateInternalTest)
@@ -5193,22 +5195,22 @@ class WSLCTests
5195
5196
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
5197
5196
- WSLCDriverOption internalOpt[] = {{"Internal", "true"}};
5197
-
5198
WSLCNetworkOptions options{};
5199
options.Name = networkName.c_str();
5200
options.Driver = "bridge";
5201
- options.DriverOpts = internalOpt;
5202
- options.DriverOptsCount = ARRAYSIZE(internalOpt);
5201
+ options.Internal = TRUE;
5202
5203
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
5204
5205
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
5206
5208
- wil::unique_cotaskmem_array_ptr<WSLCNetworkInformation> networks;
5209
- VERIFY_SUCCEEDED(m_defaultSession->ListNetworks(networks.addressof(), networks.size_address<ULONG>()));
5210
- VERIFY_ARE_EQUAL(1u, networks.size());
5211
- VERIFY_ARE_EQUAL(networkName, std::string(networks[0].Name));
5207
+ wil::unique_cotaskmem_ansistring output;
5208
+ VERIFY_SUCCEEDED(m_defaultSession->InspectNetwork(networkName.c_str(), &output));
5209
+ VERIFY_IS_NOT_NULL(output.get());
5210
+
5211
+ auto inspect = wsl::shared::FromJson<wsl::windows::common::wslc_schema::Network>(output.get());
5212
+ VERIFY_ARE_EQUAL(networkName, inspect.Name);
5213
+ VERIFY_IS_TRUE(inspect.Internal);
5214
}
5215
5216
WSLC_TEST_METHOD(NetworkCreateWithLabelsTest)
@@ -5242,11 +5244,14 @@ class WSLCTests
5244
5245
WSLC_TEST_METHOD(NetworkCreateInvalidDriverAndOptionTest)
5246
{
5247
+ const std::string networkName = "bad-network-create-input";
5248
+
5249
+ LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
5250
+ auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
5251
+
5252
WSLCNetworkOptions options{};
5246
- options.Name = "bad-network-create-input";
5253
+ options.Name = networkName.c_str();
5254
options.Driver = "bridge";
5248
- options.DriverOpts = nullptr;
5249
- options.DriverOptsCount = 0;
5255
5256
auto verifyInvalid = [&](PCWSTR expectedMessage) {
5257
VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options, nullptr));
@@ -5254,35 +5259,18 @@ class WSLCTests
5259
};
5260
5261
// Invalid drivers (unknown, wrong case, empty)
5262
+ for (const char* driver : {"overlay", "Bridge", ""})
5263
{
5258
- options.DriverOpts = nullptr;
5259
- options.DriverOptsCount = 0;
5260
- for (const char* driver : {"overlay", "Bridge", ""})
5261
- {
5262
- options.Driver = driver;
5263
- verifyInvalid(L"Unsupported network driver:");
5264
- }
5265
- }
5266
-
5267
- // Invalid driver options (wrong case and unknown keys)
5268
- {
5269
- options.Driver = "bridge";
5270
- for (const char* key : {"internal", "subnet", "gateway", "foo"})
5271
- {
5272
- WSLCDriverOption opt{key, "true"};
5273
- options.DriverOpts = &opt;
5274
- options.DriverOptsCount = 1;
5275
- verifyInvalid(wsl::shared::string::MultiByteToWide(key).c_str());
5276
- }
5264
+ options.Driver = driver;
5265
+ verifyInvalid(L"Unsupported network driver:");
5266
}
5267
5268
// Gateway specified without Subnet
5269
{
5270
options.Driver = "bridge";
5282
- WSLCDriverOption opt{"Gateway", "172.44.0.1"};
5283
- options.DriverOpts = &opt;
5284
- options.DriverOptsCount = 1;
5285
- verifyInvalid(L"requires 'Subnet'");
5271
+ options.Subnet = nullptr;
5272
+ options.Gateway = "172.44.0.1";
5273
+ verifyInvalid(L"--subnet");
5274
}
5275
}
5276
@@ -5346,14 +5334,12 @@ class WSLCTests
5334
const std::string networkName = "bad-subnet-net";
5335
5336
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
5349
-
5350
- WSLCDriverOption opts[] = {{"Subnet", "not-a-cidr"}};
5337
+ auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
5338
5339
WSLCNetworkOptions options{};
5340
options.Name = networkName.c_str();
5341
options.Driver = "bridge";
5355
- options.DriverOpts = opts;
5356
- options.DriverOptsCount = ARRAYSIZE(opts);
5342
+ options.Subnet = "not-a-cidr";
5343
5344
VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options, nullptr));
5345
ValidateCOMErrorMessageContains(L"invalid subnet");
@@ -5367,14 +5353,13 @@ class WSLCTests
5353
const std::string networkName = "bad-gateway-net";
5354
5355
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
5370
-
5371
- WSLCDriverOption opts[] = {{"Subnet", "172.27.0.0/16"}, {"Gateway", "999.999.999.999"}};
5356
+ auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
5357
5358
WSLCNetworkOptions options{};
5359
options.Name = networkName.c_str();
5360
options.Driver = "bridge";
5376
- options.DriverOpts = opts;
5377
- options.DriverOptsCount = ARRAYSIZE(opts);
5361
+ options.Subnet = "172.27.0.0/16";
5362
+ options.Gateway = "999.999.999.999";
5363
5364
VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options, nullptr));
5365
ValidateCOMErrorMessageContains(L"invalid gateway");
@@ -5386,10 +5371,39 @@ class WSLCTests
5371
WSLC_TEST_METHOD(NetworkCreateWithGatewayTest)
5372
{
5373
const std::string networkName = "gateway-test-net";
5374
+ const std::string subnet = "172.31.0.0/16";
5375
+ const std::string gateway = "172.31.0.1";
5376
5377
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
5378
5392
- WSLCDriverOption opts[] = {{"Subnet", "172.31.0.0/16"}, {"Gateway", "172.31.0.1"}};
5379
+ WSLCNetworkOptions options{};
5380
+ options.Name = networkName.c_str();
5381
+ options.Driver = "bridge";
5382
+ options.Subnet = subnet.c_str();
5383
+ options.Gateway = gateway.c_str();
5384
+
5385
+ auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
5386
+
5387
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
5388
+
5389
+ wil::unique_cotaskmem_ansistring output;
5390
+ VERIFY_SUCCEEDED(m_defaultSession->InspectNetwork(networkName.c_str(), &output));
5391
+ VERIFY_IS_NOT_NULL(output.get());
5392
+
5393
+ auto inspect = wsl::shared::FromJson<wsl::windows::common::wslc_schema::Network>(output.get());
5394
+ VERIFY_IS_TRUE(inspect.IPAM.Config.has_value());
5395
+ VERIFY_ARE_EQUAL(1u, inspect.IPAM.Config->size());
5396
+ VERIFY_ARE_EQUAL(subnet, inspect.IPAM.Config->at(0).Subnet);
5397
+ VERIFY_ARE_EQUAL(gateway, inspect.IPAM.Config->at(0).Gateway);
5398
+ }
5399
+
5400
+ WSLC_TEST_METHOD(NetworkCreateWithArbitraryDriverOptsTest)
5401
+ {
5402
+ const std::string networkName = "arbitrary-opts-test-net";
5403
+
5404
+ LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
5405
+
5406
+ WSLCDriverOption opts[] = {{"my.abc.key", "mygod"}, {"com.example.flag", "1"}};
5407
5408
WSLCNetworkOptions options{};
5409
options.Name = networkName.c_str();
@@ -5406,10 +5420,11 @@ class WSLCTests
5420
VERIFY_IS_NOT_NULL(output.get());
5421
5422
auto inspect = wsl::shared::FromJson<wsl::windows::common::wslc_schema::Network>(output.get());
5409
- VERIFY_IS_TRUE(inspect.IPAM.Config.has_value());
5410
- VERIFY_ARE_EQUAL(1u, inspect.IPAM.Config->size());
5411
- VERIFY_ARE_EQUAL(std::string("172.31.0.0/16"), inspect.IPAM.Config->at(0).Subnet);
5412
- VERIFY_ARE_EQUAL(std::string("172.31.0.1"), inspect.IPAM.Config->at(0).Gateway);
5423
+ VERIFY_IS_TRUE(inspect.Options.has_value());
5424
+ VERIFY_IS_TRUE(inspect.Options->contains("my.abc.key"));
5425
+ VERIFY_IS_TRUE(inspect.Options->contains("com.example.flag"));
5426
+ VERIFY_ARE_EQUAL(std::string("mygod"), inspect.Options->at("my.abc.key"));
5427
+ VERIFY_ARE_EQUAL(std::string("1"), inspect.Options->at("com.example.flag"));
5428
}
5429
5430
WSLC_TEST_METHOD(NetworkSessionRecoveryTest)
@@ -5418,11 +5433,13 @@ class WSLCTests
5433
5434
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
5435
5436
+ WSLCDriverOption recoveryOpts[] = {{"recovery.test.key", "preserved"}};
5437
+
5438
WSLCNetworkOptions options{};
5439
options.Name = networkName.c_str();
5440
options.Driver = "bridge";
5424
- options.DriverOpts = nullptr;
5425
- options.DriverOptsCount = 0;
5441
+ options.DriverOpts = recoveryOpts;
5442
+ options.DriverOptsCount = ARRAYSIZE(recoveryOpts);
5443
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
5444
5445
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
@@ -5436,6 +5453,16 @@ class WSLCTests
5453
VERIFY_ARE_EQUAL(networkName, std::string(networks[0].Name));
5454
VERIFY_ARE_EQUAL(std::string("bridge"), std::string(networks[0].Driver));
5455
VERIFY_IS_TRUE(strlen(networks[0].Id) > 0);
5456
+
5457
+ // Verify arbitrary driver options survive session recovery.
5458
+ wil::unique_cotaskmem_ansistring output;
5459
+ VERIFY_SUCCEEDED(m_defaultSession->InspectNetwork(networkName.c_str(), &output));
5460
+ VERIFY_IS_NOT_NULL(output.get());
5461
+
5462
+ auto inspect = wsl::shared::FromJson<wsl::windows::common::wslc_schema::Network>(output.get());
5463
+ VERIFY_IS_TRUE(inspect.Options.has_value());
5464
+ VERIFY_IS_TRUE(inspect.Options->contains("recovery.test.key"));
5465
+ VERIFY_ARE_EQUAL(std::string("preserved"), inspect.Options->at("recovery.test.key"));
5466
}
5467
5468
WSLC_TEST_METHOD(NetworkMultipleCreateListDeleteTest)
@@ -5461,20 +5488,16 @@ class WSLCTests
5488
optionsA.DriverOptsCount = 0;
5489
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&optionsA, nullptr));
5490
5464
- WSLCDriverOption subnetOpt[] = {{"Subnet", "172.29.0.0/16"}};
5491
WSLCNetworkOptions optionsB{};
5492
optionsB.Name = networkNameB.c_str();
5493
optionsB.Driver = "bridge";
5468
- optionsB.DriverOpts = subnetOpt;
5469
- optionsB.DriverOptsCount = ARRAYSIZE(subnetOpt);
5494
+ optionsB.Subnet = "172.29.0.0/16";
5495
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&optionsB, nullptr));
5496
5472
- WSLCDriverOption internalOpt[] = {{"Internal", "true"}};
5497
WSLCNetworkOptions optionsC{};
5498
optionsC.Name = networkNameC.c_str();
5499
optionsC.Driver = "bridge";
5476
- optionsC.DriverOpts = internalOpt;
5477
- optionsC.DriverOptsCount = ARRAYSIZE(internalOpt);
5500
+ optionsC.Internal = TRUE;
5501
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&optionsC, nullptr));
5502
5503
wil::unique_cotaskmem_array_ptr<WSLCNetworkInformation> networks;
@@ -5515,18 +5538,16 @@ class WSLCTests
5538
WSLC_TEST_METHOD(NetworkInspectWithSubnetTest)
5539
{
5540
const std::string networkName = "test-inspect-subnet-net";
5541
+ const std::string subnet = "172.30.0.0/16";
5542
5543
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
5544
5545
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
5546
5523
- WSLCDriverOption subnetOpt[] = {{"Subnet", "172.30.0.0/16"}};
5524
-
5547
WSLCNetworkOptions options{};
5548
options.Name = networkName.c_str();
5549
options.Driver = "bridge";
5528
- options.DriverOpts = subnetOpt;
5529
- options.DriverOptsCount = ARRAYSIZE(subnetOpt);
5550
+ options.Subnet = subnet.c_str();
5551
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
5552
5553
wil::unique_cotaskmem_ansistring output;
@@ -5538,7 +5559,7 @@ class WSLCTests
5559
VERIFY_ARE_EQUAL(inspect.Driver, std::string("bridge"));
5560
VERIFY_IS_TRUE(inspect.IPAM.Config.has_value());
5561
VERIFY_ARE_EQUAL(1u, inspect.IPAM.Config->size());
5541
- VERIFY_ARE_EQUAL(std::string("172.30.0.0/16"), inspect.IPAM.Config->at(0).Subnet);
5562
+ VERIFY_ARE_EQUAL(subnet, inspect.IPAM.Config->at(0).Subnet);
5563
}
5564
5565
WSLC_TEST_METHOD(NetworkInspectNotFoundTest)
@@ -6693,13 +6714,10 @@ class WSLCTests
6714
6715
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
6716
6696
- WSLCDriverOption opts[] = {{"Subnet", "172.35.0.0/16"}};
6697
-
6717
WSLCNetworkOptions networkOptions{};
6718
networkOptions.Name = networkName.c_str();
6719
networkOptions.Driver = "bridge";
6701
- networkOptions.DriverOpts = opts;
6702
- networkOptions.DriverOptsCount = ARRAYSIZE(opts);
6720
+ networkOptions.Subnet = "172.35.0.0/16";
6721
6722
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6723
@@ -6794,13 +6812,10 @@ class WSLCTests
6812
6813
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
6814
6797
- WSLCDriverOption opts[] = {{"Subnet", "172.36.0.0/16"}};
6798
-
6815
WSLCNetworkOptions networkOptions{};
6816
networkOptions.Name = networkName.c_str();
6817
networkOptions.Driver = "bridge";
6802
- networkOptions.DriverOpts = opts;
6803
- networkOptions.DriverOptsCount = ARRAYSIZE(opts);
6818
+ networkOptions.Subnet = "172.36.0.0/16";
6819
6820
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6821
@@ -6825,13 +6840,10 @@ class WSLCTests
6840
6841
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
6842
6828
- WSLCDriverOption opts[] = {{"Subnet", "172.37.0.0/16"}};
6829
-
6843
WSLCNetworkOptions networkOptions{};
6844
networkOptions.Name = networkName.c_str();
6845
networkOptions.Driver = "bridge";
6833
- networkOptions.DriverOpts = opts;
6834
- networkOptions.DriverOptsCount = ARRAYSIZE(opts);
6846
+ networkOptions.Subnet = "172.37.0.0/16";
6847
6848
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6849
@@ -6851,13 +6863,10 @@ class WSLCTests
6863
6864
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
6865
6854
- WSLCDriverOption opts[] = {{"Subnet", "172.38.0.0/16"}};
6855
-
6866
WSLCNetworkOptions networkOptions{};
6867
networkOptions.Name = networkName.c_str();
6868
networkOptions.Driver = "bridge";
6859
- networkOptions.DriverOpts = opts;
6860
- networkOptions.DriverOptsCount = ARRAYSIZE(opts);
6869
+ networkOptions.Subnet = "172.38.0.0/16";
6870
6871
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6872
@@ -6881,13 +6890,10 @@ class WSLCTests
6890
6891
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str()));
6892
6884
- WSLCDriverOption opts[] = {{"Subnet", "172.39.0.0/16"}};
6885
-
6893
WSLCNetworkOptions networkOptions{};
6894
networkOptions.Name = networkName.c_str();
6895
networkOptions.Driver = "bridge";
6889
- networkOptions.DriverOpts = opts;
6890
- networkOptions.DriverOptsCount = ARRAYSIZE(opts);
6896
+ networkOptions.Subnet = "172.39.0.0/16";
6897
6898
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6899
@@ -6922,21 +6928,17 @@ class WSLCTests
6928
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str()));
6929
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(additionalNetwork.c_str()));
6930
6925
- WSLCDriverOption primaryOpts[] = {{"Subnet", "172.40.0.0/16"}};
6931
WSLCNetworkOptions primaryNetOpts{};
6932
primaryNetOpts.Name = primaryNetwork.c_str();
6933
primaryNetOpts.Driver = "bridge";
6929
- primaryNetOpts.DriverOpts = primaryOpts;
6930
- primaryNetOpts.DriverOptsCount = ARRAYSIZE(primaryOpts);
6934
+ primaryNetOpts.Subnet = "172.40.0.0/16";
6935
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&primaryNetOpts, nullptr));
6936
auto primaryCleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str())); });
6937
6934
- WSLCDriverOption additionalOpts[] = {{"Subnet", "172.41.0.0/16"}};
6938
WSLCNetworkOptions additionalNetOpts{};
6939
additionalNetOpts.Name = additionalNetwork.c_str();
6940
additionalNetOpts.Driver = "bridge";
6938
- additionalNetOpts.DriverOpts = additionalOpts;
6939
- additionalNetOpts.DriverOptsCount = ARRAYSIZE(additionalOpts);
6941
+ additionalNetOpts.Subnet = "172.41.0.0/16";
6942
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&additionalNetOpts, nullptr));
6943
auto additionalCleanup =
6944
wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(additionalNetwork.c_str())); });
@@ -6961,12 +6963,10 @@ class WSLCTests
6963
6964
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str()));
6965
6964
- WSLCDriverOption opts[] = {{"Subnet", "172.48.0.0/16"}};
6966
WSLCNetworkOptions netOpts{};
6967
netOpts.Name = primaryNetwork.c_str();
6968
netOpts.Driver = "bridge";
6968
- netOpts.DriverOpts = opts;
6969
- netOpts.DriverOptsCount = ARRAYSIZE(opts);
6969
+ netOpts.Subnet = "172.48.0.0/16";
6970
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&netOpts, nullptr));
6971
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str())); });
6972
@@ -6987,12 +6987,10 @@ class WSLCTests
6987
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str()));
6988
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(missingNetwork.c_str()));
6989
6990
- WSLCDriverOption opts[] = {{"Subnet", "172.49.0.0/16"}};
6990
WSLCNetworkOptions netOpts{};
6991
netOpts.Name = primaryNetwork.c_str();
6992
netOpts.Driver = "bridge";
6994
- netOpts.DriverOpts = opts;
6995
- netOpts.DriverOptsCount = ARRAYSIZE(opts);
6993
+ netOpts.Subnet = "172.49.0.0/16";
6994
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&netOpts, nullptr));
6995
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str())); });
6996
@@ -7061,21 +7059,17 @@ class WSLCTests
7059
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str()));
7060
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(additionalNetwork.c_str()));
7061
7064
- WSLCDriverOption primaryOpts[] = {{"Subnet", "172.50.0.0/16"}};
7062
WSLCNetworkOptions primaryNetOpts{};
7063
primaryNetOpts.Name = primaryNetwork.c_str();
7064
primaryNetOpts.Driver = "bridge";
7068
- primaryNetOpts.DriverOpts = primaryOpts;
7069
- primaryNetOpts.DriverOptsCount = ARRAYSIZE(primaryOpts);
7065
+ primaryNetOpts.Subnet = "172.50.0.0/16";
7066
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&primaryNetOpts, nullptr));
7067
auto primaryCleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str())); });
7068
7073
- WSLCDriverOption additionalOpts[] = {{"Subnet", "172.51.0.0/16"}};
7069
WSLCNetworkOptions additionalNetOpts{};
7070
additionalNetOpts.Name = additionalNetwork.c_str();
7071
additionalNetOpts.Driver = "bridge";
7077
- additionalNetOpts.DriverOpts = additionalOpts;
7078
- additionalNetOpts.DriverOptsCount = ARRAYSIZE(additionalOpts);
7072
+ additionalNetOpts.Subnet = "172.51.0.0/16";
7073
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&additionalNetOpts, nullptr));
7074
auto additionalCleanup =
7075
wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(additionalNetwork.c_str())); });
@@ -7098,12 +7092,10 @@ class WSLCTests
7092
7093
auto createNetwork = [&](const std::string& name, const char* subnet) {
7094
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(name.c_str()));
7101
- WSLCDriverOption opts[] = {{"Subnet", subnet}};
7095
WSLCNetworkOptions netOpts{};
7096
netOpts.Name = name.c_str();
7097
netOpts.Driver = "bridge";
7105
- netOpts.DriverOpts = opts;
7106
- netOpts.DriverOptsCount = ARRAYSIZE(opts);
7098
+ netOpts.Subnet = subnet;
7099
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&netOpts, nullptr));
7100
};
7101
@@ -7229,12 +7221,10 @@ class WSLCTests
7221
{
7222
auto createNetwork = [&](const std::string& name, const char* subnet) {
7223
LOG_IF_FAILED(m_defaultSession->DeleteNetwork(name.c_str()));
7232
- WSLCDriverOption opts[] = {{"Subnet", subnet}};
7224
WSLCNetworkOptions netOpts{};
7225
netOpts.Name = name.c_str();
7226
netOpts.Driver = "bridge";
7236
- netOpts.DriverOpts = opts;
7237
- netOpts.DriverOptsCount = ARRAYSIZE(opts);
7227
+ netOpts.Subnet = subnet;
7228
VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&netOpts, nullptr));
7229
};
7230
test/windows/wslc/e2e/WSLCE2ENetworkCreateTests.cpp
+78
-5
@@ -109,6 +109,76 @@ class WSLCE2ENetworkCreateTests
109
{.Stdout = L"", .Stderr = L"Cannot create a file when that file already exists. \r\nError code: ERROR_ALREADY_EXISTS\r\n", .ExitCode = 1});
110
}
111
112
+ WSLC_TEST_METHOD(WSLCE2E_Network_Create_Internal_Success)
113
+ {
114
+ auto result = RunWslc(std::format(L"network create --internal {}", TestNetworkName));
115
+ result.Verify({.Stderr = L"", .ExitCode = 0});
116
+ VERIFY_ARE_EQUAL(TestNetworkName, result.GetStdoutOneLine());
117
+
118
+ VerifyNetworkIsListed(TestNetworkName);
119
+ auto inspect = InspectNetwork(TestNetworkName);
120
+ VERIFY_ARE_EQUAL("bridge", inspect.Driver);
121
+ VERIFY_IS_TRUE(inspect.Internal);
122
+ }
123
+
124
+ WSLC_TEST_METHOD(WSLCE2E_Network_Create_Subnet_Success)
125
+ {
126
+ const std::wstring subnet = L"172.45.0.0/16";
127
+ auto result = RunWslc(std::format(L"network create --subnet {} {}", subnet, TestNetworkName));
128
+ result.Verify({.Stderr = L"", .ExitCode = 0});
129
+ VERIFY_ARE_EQUAL(TestNetworkName, result.GetStdoutOneLine());
130
+
131
+ VerifyNetworkIsListed(TestNetworkName);
132
+ auto inspect = InspectNetwork(TestNetworkName);
133
+ VERIFY_ARE_EQUAL("bridge", inspect.Driver);
134
+ VERIFY_IS_TRUE(inspect.IPAM.Config.has_value());
135
+ VERIFY_ARE_EQUAL(1u, inspect.IPAM.Config->size());
136
+ VERIFY_ARE_EQUAL(wsl::shared::string::WideToMultiByte(subnet), (*inspect.IPAM.Config)[0].Subnet);
137
+ }
138
+
139
+ WSLC_TEST_METHOD(WSLCE2E_Network_Create_SubnetAndGateway_Success)
140
+ {
141
+ const std::wstring subnet = L"172.46.0.0/16";
142
+ const std::wstring gateway = L"172.46.0.1";
143
+ auto result = RunWslc(std::format(L"network create --subnet {} --gateway {} {}", subnet, gateway, TestNetworkName));
144
+ result.Verify({.Stderr = L"", .ExitCode = 0});
145
+ VERIFY_ARE_EQUAL(TestNetworkName, result.GetStdoutOneLine());
146
+
147
+ VerifyNetworkIsListed(TestNetworkName);
148
+ auto inspect = InspectNetwork(TestNetworkName);
149
+ VERIFY_ARE_EQUAL("bridge", inspect.Driver);
150
+ VERIFY_IS_TRUE(inspect.IPAM.Config.has_value());
151
+ VERIFY_ARE_EQUAL(1u, inspect.IPAM.Config->size());
152
+ VERIFY_ARE_EQUAL(wsl::shared::string::WideToMultiByte(subnet), (*inspect.IPAM.Config)[0].Subnet);
153
+ VERIFY_ARE_EQUAL(wsl::shared::string::WideToMultiByte(gateway), (*inspect.IPAM.Config)[0].Gateway);
154
+ }
155
+
156
+ WSLC_TEST_METHOD(WSLCE2E_Network_Create_GatewayWithoutSubnet_Fail)
157
+ {
158
+ auto result = RunWslc(std::format(L"network create --gateway 172.47.0.1 {}", TestNetworkName));
159
+ result.Verify(
160
+ {.Stdout = L"",
161
+ .Stderr = L"The '--gateway' option requires '--subnet' to also be specified.\r\nError code: E_INVALIDARG\r\n",
162
+ .ExitCode = 1});
163
+
164
+ VerifyNetworkIsNotListed(TestNetworkName);
165
+ }
166
+
167
+ WSLC_TEST_METHOD(WSLCE2E_Network_Create_WithOpt_Success)
168
+ {
169
+ constexpr auto c_opts = L"--opt com.docker.network.bridge.enable_icc=true --opt com.docker.network.driver.mtu=1450";
170
+ auto result = RunWslc(std::format(L"network create {} {}", c_opts, TestNetworkName));
171
+ result.Verify({.Stderr = L"", .ExitCode = 0});
172
+ VERIFY_ARE_EQUAL(TestNetworkName, result.GetStdoutOneLine());
173
+
174
+ VerifyNetworkIsListed(TestNetworkName);
175
+ auto inspect = InspectNetwork(TestNetworkName);
176
+ VERIFY_ARE_EQUAL("bridge", inspect.Driver);
177
+ VERIFY_IS_TRUE(inspect.Options.has_value());
178
+ VERIFY_ARE_EQUAL("true", (*inspect.Options)["com.docker.network.bridge.enable_icc"]);
179
+ VERIFY_ARE_EQUAL("1450", (*inspect.Options)["com.docker.network.driver.mtu"]);
180
+ }
181
+
182
private:
183
const std::wstring TestNetworkName = L"wslc-e2e-network-create";
184
@@ -145,11 +215,14 @@ private:
215
std::wstring GetAvailableOptions() const
216
{
217
std::wstringstream options;
148
- options << L"The following options are available:\r\n" //
149
- << L" -d,--driver Specify network driver name (default: bridge)\r\n" //
150
- << L" -o,--opt Set driver specific options\r\n" //
151
- << L" -l,--label Network metadata setting\r\n" //
152
- << L" -?,--help Shows help about the selected command\r\n" //
218
+ options << L"The following options are available:\r\n" //
219
+ << L" -d,--driver Specify network driver name (default: bridge)\r\n" //
220
+ << L" -o,--opt Set driver specific options\r\n" //
221
+ << L" -l,--label Network metadata setting\r\n" //
222
+ << L" --gateway IPv4 or IPv6 gateway for the subnet\r\n" //
223
+ << L" --internal Restrict external access to the network\r\n" //
224
+ << L" --subnet Subnet in CIDR format that represents a network segment\r\n" //
225
+ << L" -?,--help Shows help about the selected command\r\n" //
226
<< L"\r\n";
227
return options.str();
228
}