Initial support for wslc network commands (#40564)

* Init network commands Co-authored-by: AmelBawa-msft <104940545+AmelBawa-msft@users.noreply.github.com> * Integrate network inspect Co-authored-by: AmelBawa-msft <104940545+AmelBawa-msft@users.noreply.github.com> * Clang format Co-authored-by: AmelBawa-msft <104940545+AmelBawa-msft@users.noreply.github.com> * fix: address review feedback in network port to master Agent-Logs-Url: https://github.com/microsoft/WSL/sessions/db7c0afc-316b-46bf-aae1-71244fd15e16 Co-authored-by: AmelBawa-msft <104940545+AmelBawa-msft@users.noreply.github.com> * Sync with latest * Clang format * Added test * Update test * Update string * Promote parsers * Clang format * Remove comments * Fix test --------- Co-authored-by: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>

Copilot committed Jun 4, 2026 at 19:44 UTC 8cd5ef1ff94502a5827c8d1aceda6e0a9cb82a87
36 files changed +1681 -85
localization/strings/en-US/Resources.resw
+44
@@ -2958,6 +2958,50 @@ On first run, creates the file with all settings commented out at their defaults
2958 <data name="WSLCCLI_ImageLoadNoInputError" xml:space="preserve">
2959 <value>Requested load but no input provided.</value>
2960 </data>
2961 + <data name="WSLCCLI_NetworkCommandDesc" xml:space="preserve">
2962 + <value>Manage networks.</value>
2963 + </data>
2964 + <data name="WSLCCLI_NetworkCommandLongDesc" xml:space="preserve">
2965 + <value>Manage the lifecycle of WSL networks, including creating, inspecting, listing, and deleting them.</value>
2966 + <comment>{Locked="WSL"}Product names should not be translated</comment>
2967 + </data>
2968 + <data name="WSLCCLI_NetworkCreateDesc" xml:space="preserve">
2969 + <value>Create a network.</value>
2970 + </data>
2971 + <data name="WSLCCLI_NetworkCreateLongDesc" xml:space="preserve">
2972 + <value>Creates a new network.</value>
2973 + </data>
2974 + <data name="WSLCCLI_NetworkDriverOptionDescription" xml:space="preserve">
2975 + <value>Specify network driver name (default: bridge)</value>
2976 + <comment>{Locked="bridge"}Command line arguments, file names and string inserts should not be translated</comment>
2977 + </data>
2978 + <data name="WSLCCLI_NetworkLabelArgDescription" xml:space="preserve">
2979 + <value>Network metadata setting</value>
2980 + </data>
2981 + <data name="WSLCCLI_NetworkRemoveDesc" xml:space="preserve">
2982 + <value>Remove one or more networks.</value>
2983 + </data>
2984 + <data name="WSLCCLI_NetworkRemoveLongDesc" xml:space="preserve">
2985 + <value>Removes one or more networks.</value>
2986 + </data>
2987 + <data name="WSLCCLI_NetworkInspectDesc" xml:space="preserve">
2988 + <value>Display detailed information on one or more networks.</value>
2989 + </data>
2990 + <data name="WSLCCLI_NetworkInspectLongDesc" xml:space="preserve">
2991 + <value>Display detailed information on one or more networks.</value>
2992 + </data>
2993 + <data name="WSLCCLI_NetworkListDesc" xml:space="preserve">
2994 + <value>List networks.</value>
2995 + </data>
2996 + <data name="WSLCCLI_NetworkListLongDesc" xml:space="preserve">
2997 + <value>Lists all networks in the session.</value>
2998 + </data>
2999 + <data name="WSLCCLI_NetworkNameArgDescription" xml:space="preserve">
3000 + <value>Network name</value>
3001 + </data>
3002 + <data name="WSLCCLI_NetworkListQuietArgDesc" xml:space="preserve">
3003 + <value>Outputs the network names only</value>
3004 + </data>
3005 <data name="WSLCCLI_VolumeCommandDesc" xml:space="preserve">
3006 <value>Manage volumes.</value>
3007 </data>
src/shared/inc/JsonUtils.h
+20
@@ -190,6 +190,26 @@ struct adl_serializer<WSLCVolumeInformation>
190 strncpy_s(volume.Driver, sizeof(volume.Driver), driver.c_str(), _TRUNCATE);
191 }
192 };
193 +
194 +template <>
195 +struct adl_serializer<WSLCNetworkInformation>
196 +{
197 + static void to_json(json& j, const WSLCNetworkInformation& network)
198 + {
199 + j = json{{"Name", std::string(network.Name)}, {"Id", std::string(network.Id)}, {"Driver", std::string(network.Driver)}};
200 + }
201 +
202 + static void from_json(const json& j, WSLCNetworkInformation& network)
203 + {
204 + std::string name = j.at("Name").get<std::string>();
205 + std::string id = j.at("Id").get<std::string>();
206 + std::string driver = j.at("Driver").get<std::string>();
207 +
208 + strncpy_s(network.Name, sizeof(network.Name), name.c_str(), _TRUNCATE);
209 + strncpy_s(network.Id, sizeof(network.Id), id.c_str(), _TRUNCATE);
210 + strncpy_s(network.Driver, sizeof(network.Driver), driver.c_str(), _TRUNCATE);
211 + }
212 +};
213 #endif
214
215 } // namespace nlohmann
\ No newline at end of file
src/windows/wslc/arguments/ArgumentDefinitions.h
+1
@@ -73,6 +73,7 @@ _(Label, "label", L"l", Kind::Value, L
73 _(Last, "last", L"n", Kind::Value, Localization::WSLCCLI_LastArgDescription()) \
74 _(Latest, "latest", L"l", Kind::Flag, Localization::WSLCCLI_LatestArgDescription()) \
75 _(Name, "name", NO_ALIAS, Kind::Value, Localization::WSLCCLI_NameArgDescription()) \
76 +_(NetworkName, "network-name", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_NetworkNameArgDescription()) \
77 /*_(NoDNS, "no-dns", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoDNSArgDescription())*/ \
78 _(NoCache, "no-cache", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoCacheArgDescription()) \
79 _(NoPrune, "no-prune", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoPruneArgDescription()) \
src/windows/wslc/arguments/ArgumentValidation.cpp
+34 -1
@@ -226,13 +226,17 @@ InspectType GetInspectTypeFromString(const std::wstring& input, const std::wstri
226 {
227 return InspectType::Container;
228 }
229 + else if (IsEqual(input, L"network"))
230 + {
231 + return InspectType::Network;
232 + }
233 else if (IsEqual(input, L"volume"))
234 {
235 return InspectType::Volume;
236 }
237 else
238 {
235 - constexpr std::wstring_view supportedValues = L"image, container, volume";
239 + constexpr std::wstring_view supportedValues = L"image, container, network, volume";
240 throw ArgumentException(Localization::WSLCCLI_InvalidInspectError(argName, input, supportedValues));
241 }
242 }
@@ -267,4 +271,33 @@ int64_t GetMemorySizeFromString(const std::wstring& input, const std::wstring& a
271 return static_cast<int64_t>(parsed.value());
272 }
273
274 +std::pair<std::string, std::string> ParseLabel(const std::wstring& value)
275 +{
276 + std::pair<std::string, std::string> result{};
277 + auto pos = value.find('=');
278 + if (pos == std::wstring::npos)
279 + {
280 + result.first = WideToMultiByte(value);
281 + }
282 + else
283 + {
284 + result.first = WideToMultiByte(value.substr(0, pos));
285 + result.second = WideToMultiByte(value.substr(pos + 1));
286 + }
287 +
288 + THROW_HR_WITH_USER_ERROR_IF(E_INVALIDARG, Localization::WSLCCLI_LabelKeyEmptyError(), result.first.empty());
289 + return result;
290 +}
291 +
292 +std::pair<std::string, std::string> ParseDriverOption(const std::wstring& value)
293 +{
294 + auto pos = value.find('=');
295 + if (pos == std::wstring::npos)
296 + {
297 + return {WideToMultiByte(value), std::string{}};
298 + }
299 +
300 + return {WideToMultiByte(value.substr(0, pos)), WideToMultiByte(value.substr(pos + 1))};
301 +}
302 +
303 } // namespace wsl::windows::wslc::validation
src/windows/wslc/arguments/ArgumentValidation.h
+3
@@ -74,4 +74,7 @@ void ValidateGpus(const std::vector<std::wstring>& values, const std::wstring& a
74 void ValidateVolumeMount(const std::vector<std::wstring>& values);
75 void ValidateFilter(const std::vector<std::wstring>& values);
76
77 +std::pair<std::string, std::string> ParseLabel(const std::wstring& value);
78 +std::pair<std::string, std::string> ParseDriverOption(const std::wstring& value);
79 +
80 } // namespace wsl::windows::wslc::validation
src/windows/wslc/commands/NetworkCommand.cpp new
+51
@@ -0,0 +1,51 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkCommand.cpp
8 +
9 +Abstract:
10 +
11 + Implementation of command execution logic.
12 +
13 +--*/
14 +#include "CLIExecutionContext.h"
15 +#include "NetworkCommand.h"
16 +
17 +using namespace wsl::windows::wslc::execution;
18 +using namespace wsl::shared;
19 +
20 +namespace wsl::windows::wslc {
21 +// Network Root Command
22 +std::vector<std::unique_ptr<Command>> NetworkCommand::GetCommands() const
23 +{
24 + std::vector<std::unique_ptr<Command>> commands;
25 + commands.push_back(std::make_unique<NetworkCreateCommand>(FullName()));
26 + commands.push_back(std::make_unique<NetworkRemoveCommand>(FullName()));
27 + commands.push_back(std::make_unique<NetworkInspectCommand>(FullName()));
28 + commands.push_back(std::make_unique<NetworkListCommand>(FullName()));
29 + return commands;
30 +}
31 +
32 +std::vector<Argument> NetworkCommand::GetArguments() const
33 +{
34 + return {};
35 +}
36 +
37 +std::wstring NetworkCommand::ShortDescription() const
38 +{
39 + return Localization::WSLCCLI_NetworkCommandDesc();
40 +}
41 +
42 +std::wstring NetworkCommand::LongDescription() const
43 +{
44 + return Localization::WSLCCLI_NetworkCommandLongDesc();
45 +}
46 +
47 +void NetworkCommand::ExecuteInternal(CLIExecutionContext& context) const
48 +{
49 + OutputHelp();
50 +}
51 +} // namespace wsl::windows::wslc
src/windows/wslc/commands/NetworkCommand.h new
+95
@@ -0,0 +1,95 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkCommand.h
8 +
9 +Abstract:
10 +
11 + Declaration of command classes and interfaces.
12 +
13 +--*/
14 +#pragma once
15 +#include "Command.h"
16 +
17 +namespace wsl::windows::wslc {
18 +// Root Network Command
19 +struct NetworkCommand final : public Command
20 +{
21 + constexpr static std::wstring_view CommandName = L"network";
22 + NetworkCommand(const std::wstring& parent) : Command(CommandName, parent)
23 + {
24 + }
25 + std::vector<Argument> GetArguments() const override;
26 + std::wstring ShortDescription() const override;
27 + std::wstring LongDescription() const override;
28 +
29 + std::vector<std::unique_ptr<Command>> GetCommands() const override;
30 +
31 +protected:
32 + void ExecuteInternal(CLIExecutionContext& context) const override;
33 +};
34 +
35 +// Create Command
36 +struct NetworkCreateCommand final : public Command
37 +{
38 + constexpr static std::wstring_view CommandName = L"create";
39 + NetworkCreateCommand(const std::wstring& parent) : Command(CommandName, parent)
40 + {
41 + }
42 + std::vector<Argument> GetArguments() const override;
43 + std::wstring ShortDescription() const override;
44 + std::wstring LongDescription() const override;
45 +
46 +protected:
47 + void ExecuteInternal(CLIExecutionContext& context) const override;
48 +};
49 +
50 +// Remove Command
51 +struct NetworkRemoveCommand final : public Command
52 +{
53 + constexpr static std::wstring_view CommandName = L"remove";
54 + NetworkRemoveCommand(const std::wstring& parent) : Command(CommandName, {L"delete", L"rm"}, parent)
55 + {
56 + }
57 + std::vector<Argument> GetArguments() const override;
58 + std::wstring ShortDescription() const override;
59 + std::wstring LongDescription() const override;
60 +
61 +protected:
62 + void ExecuteInternal(CLIExecutionContext& context) const override;
63 +};
64 +
65 +// Inspect Command
66 +struct NetworkInspectCommand final : public Command
67 +{
68 + constexpr static std::wstring_view CommandName = L"inspect";
69 + NetworkInspectCommand(const std::wstring& parent) : Command(CommandName, parent)
70 + {
71 + }
72 + std::vector<Argument> GetArguments() const override;
73 + std::wstring ShortDescription() const override;
74 + std::wstring LongDescription() const override;
75 +
76 +protected:
77 + void ExecuteInternal(CLIExecutionContext& context) const override;
78 +};
79 +
80 +// List Command
81 +struct NetworkListCommand final : public Command
82 +{
83 + constexpr static std::wstring_view CommandName = L"list";
84 + NetworkListCommand(const std::wstring& parent) : Command(CommandName, {L"ls"}, parent)
85 + {
86 + }
87 + std::vector<Argument> GetArguments() const override;
88 + std::wstring ShortDescription() const override;
89 + std::wstring LongDescription() const override;
90 +
91 +protected:
92 + void ValidateArgumentsInternal(const ArgMap& execArgs) const override;
93 + void ExecuteInternal(CLIExecutionContext& context) const override;
94 +};
95 +} // namespace wsl::windows::wslc
src/windows/wslc/commands/NetworkCreateCommand.cpp new
+53
@@ -0,0 +1,53 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkCreateCommand.cpp
8 +
9 +Abstract:
10 +
11 + Implementation of command execution logic.
12 +
13 +--*/
14 +
15 +#include "NetworkCommand.h"
16 +#include "CLIExecutionContext.h"
17 +#include "SessionTasks.h"
18 +#include "NetworkTasks.h"
19 +#include "Task.h"
20 +
21 +using namespace wsl::windows::wslc::execution;
22 +using namespace wsl::windows::wslc::task;
23 +using namespace wsl::shared;
24 +
25 +namespace wsl::windows::wslc {
26 +// Network Create Command
27 +std::vector<Argument> NetworkCreateCommand::GetArguments() const
28 +{
29 + return {
30 + Argument::Create(ArgType::NetworkName, true),
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::Session),
35 + };
36 +}
37 +
38 +std::wstring NetworkCreateCommand::ShortDescription() const
39 +{
40 + return Localization::WSLCCLI_NetworkCreateDesc();
41 +}
42 +
43 +std::wstring NetworkCreateCommand::LongDescription() const
44 +{
45 + return Localization::WSLCCLI_NetworkCreateLongDesc();
46 +}
47 +
48 +void NetworkCreateCommand::ExecuteInternal(CLIExecutionContext& context) const
49 +{
50 + context << CreateSession //
51 + << CreateNetwork;
52 +}
53 +} // namespace wsl::windows::wslc
src/windows/wslc/commands/NetworkInspectCommand.cpp new
+50
@@ -0,0 +1,50 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkInspectCommand.cpp
8 +
9 +Abstract:
10 +
11 + Implementation of command execution logic.
12 +
13 +--*/
14 +
15 +#include "NetworkCommand.h"
16 +#include "CLIExecutionContext.h"
17 +#include "SessionTasks.h"
18 +#include "NetworkTasks.h"
19 +#include "Task.h"
20 +
21 +using namespace wsl::windows::wslc::execution;
22 +using namespace wsl::windows::wslc::task;
23 +using namespace wsl::shared;
24 +
25 +namespace wsl::windows::wslc {
26 +// Network Inspect Command
27 +std::vector<Argument> NetworkInspectCommand::GetArguments() const
28 +{
29 + return {
30 + Argument::Create(ArgType::NetworkName, true, NO_LIMIT),
31 + Argument::Create(ArgType::Session),
32 + };
33 +}
34 +
35 +std::wstring NetworkInspectCommand::ShortDescription() const
36 +{
37 + return Localization::WSLCCLI_NetworkInspectDesc();
38 +}
39 +
40 +std::wstring NetworkInspectCommand::LongDescription() const
41 +{
42 + return Localization::WSLCCLI_NetworkInspectLongDesc();
43 +}
44 +
45 +void NetworkInspectCommand::ExecuteInternal(CLIExecutionContext& context) const
46 +{
47 + context << CreateSession //
48 + << InspectNetworks;
49 +}
50 +} // namespace wsl::windows::wslc
src/windows/wslc/commands/NetworkListCommand.cpp new
+65
@@ -0,0 +1,65 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkListCommand.cpp
8 +
9 +Abstract:
10 +
11 + Implementation of command execution logic.
12 +
13 +--*/
14 +
15 +#include "NetworkCommand.h"
16 +#include "CLIExecutionContext.h"
17 +#include "SessionTasks.h"
18 +#include "NetworkTasks.h"
19 +#include "Task.h"
20 +
21 +using namespace wsl::windows::wslc::execution;
22 +using namespace wsl::windows::wslc::task;
23 +using namespace wsl::shared;
24 +using namespace wsl::shared::string;
25 +
26 +namespace wsl::windows::wslc {
27 +// Network List Command
28 +std::vector<Argument> NetworkListCommand::GetArguments() const
29 +{
30 + return {
31 + Argument::Create(ArgType::Format),
32 + Argument::Create(ArgType::Quiet, false, std::nullopt, Localization::WSLCCLI_NetworkListQuietArgDesc()),
33 + Argument::Create(ArgType::Session),
34 + };
35 +}
36 +
37 +std::wstring NetworkListCommand::ShortDescription() const
38 +{
39 + return Localization::WSLCCLI_NetworkListDesc();
40 +}
41 +
42 +std::wstring NetworkListCommand::LongDescription() const
43 +{
44 + return Localization::WSLCCLI_NetworkListLongDesc();
45 +}
46 +
47 +void NetworkListCommand::ValidateArgumentsInternal(const ArgMap& execArgs) const
48 +{
49 + if (execArgs.Contains(ArgType::Format))
50 + {
51 + auto format = execArgs.Get<ArgType::Format>();
52 + if (!IsEqual(format, L"json") && !IsEqual(format, L"table"))
53 + {
54 + throw CommandException(Localization::WSLCCLI_InvalidFormatError());
55 + }
56 + }
57 +}
58 +
59 +void NetworkListCommand::ExecuteInternal(CLIExecutionContext& context) const
60 +{
61 + context << CreateSession //
62 + << GetNetworks //
63 + << ListNetworks;
64 +}
65 +} // namespace wsl::windows::wslc
src/windows/wslc/commands/NetworkRemoveCommand.cpp new
+50
@@ -0,0 +1,50 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkRemoveCommand.cpp
8 +
9 +Abstract:
10 +
11 + Implementation of command execution logic.
12 +
13 +--*/
14 +
15 +#include "NetworkCommand.h"
16 +#include "CLIExecutionContext.h"
17 +#include "SessionTasks.h"
18 +#include "NetworkTasks.h"
19 +#include "Task.h"
20 +
21 +using namespace wsl::windows::wslc::execution;
22 +using namespace wsl::windows::wslc::task;
23 +using namespace wsl::shared;
24 +
25 +namespace wsl::windows::wslc {
26 +// Network Remove Command
27 +std::vector<Argument> NetworkRemoveCommand::GetArguments() const
28 +{
29 + return {
30 + Argument::Create(ArgType::NetworkName, true, NO_LIMIT),
31 + Argument::Create(ArgType::Session),
32 + };
33 +}
34 +
35 +std::wstring NetworkRemoveCommand::ShortDescription() const
36 +{
37 + return Localization::WSLCCLI_NetworkRemoveDesc();
38 +}
39 +
40 +std::wstring NetworkRemoveCommand::LongDescription() const
41 +{
42 + return Localization::WSLCCLI_NetworkRemoveLongDesc();
43 +}
44 +
45 +void NetworkRemoveCommand::ExecuteInternal(CLIExecutionContext& context) const
46 +{
47 + context << CreateSession //
48 + << DeleteNetworks;
49 +}
50 +} // namespace wsl::windows::wslc
src/windows/wslc/commands/RootCommand.cpp
+2
@@ -16,6 +16,7 @@ Abstract:
16 // Include all commands that parent to the root.
17 #include "ContainerCommand.h"
18 #include "ImageCommand.h"
19 +#include "NetworkCommand.h"
20 #include "RegistryCommand.h"
21 #include "SettingsCommand.h"
22 #include "SystemCommand.h"
@@ -32,6 +33,7 @@ std::vector<std::unique_ptr<Command>> RootCommand::GetCommands() const
33 std::vector<std::unique_ptr<Command>> commands;
34 commands.push_back(std::make_unique<ContainerCommand>(FullName()));
35 commands.push_back(std::make_unique<ImageCommand>(FullName()));
36 + commands.push_back(std::make_unique<NetworkCommand>(FullName()));
37 commands.push_back(std::make_unique<RegistryCommand>(FullName()));
38 commands.push_back(std::make_unique<SettingsCommand>(FullName()));
39 commands.push_back(std::make_unique<SystemCommand>(FullName()));
src/windows/wslc/core/ExecutionContextData.h
+2
@@ -38,6 +38,7 @@ enum class Data : size_t
38 ContainerOptions,
39 Images,
40 Volumes,
41 + Networks,
42
43 Max
44 };
@@ -53,6 +54,7 @@ namespace details {
54 DEFINE_DATA_MAPPING(ContainerOptions, wsl::windows::wslc::models::ContainerOptions);
55 DEFINE_DATA_MAPPING(Images, std::vector<wsl::windows::wslc::models::ImageInformation>);
56 DEFINE_DATA_MAPPING(Volumes, std::vector<WSLCVolumeInformation>);
57 + DEFINE_DATA_MAPPING(Networks, std::vector<WSLCNetworkInformation>);
58 } // namespace details
59
60 struct DataMap : wsl::windows::wslc::EnumBasedVariantMap<Data, wsl::windows::wslc::execution::details::DataMapping>
src/windows/wslc/services/InspectModel.h
+2 -1
@@ -19,7 +19,8 @@ typedef enum _InspectType
19 Container = 1,
20 Image = 2,
21 Volume = 4,
22 + Network = 8,
23
23 - All = Container | Image | Volume,
24 + All = Container | Image | Network | Volume,
25 } InspectType;
26 } // namespace wsl::windows::wslc::models
src/windows/wslc/services/NetworkModel.h new
+30
@@ -0,0 +1,30 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkModel.h
8 +
9 +Abstract:
10 +
11 + This file contains the NetworkModel definitions
12 +
13 +--*/
14 +
15 +#pragma once
16 +
17 +#include "JsonUtils.h"
18 +#include <string>
19 +
20 +namespace wsl::windows::wslc::models {
21 +
22 +struct CreateNetworkOptions
23 +{
24 + std::string Name;
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 +};
29 +
30 +} // namespace wsl::windows::wslc::models
src/windows/wslc/services/NetworkService.cpp new
+84
@@ -0,0 +1,84 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkService.cpp
8 +
9 +Abstract:
10 +
11 + This file contains the NetworkService implementation
12 +
13 +--*/
14 +#include "NetworkService.h"
15 +#include "WarningCallback.h"
16 +#include <wslutil.h>
17 +#include <wslc.h>
18 +
19 +using namespace wsl::shared;
20 +using namespace wsl::shared::string;
21 +using namespace wsl::windows::common::wslutil;
22 +
23 +namespace wsl::windows::wslc::services {
24 +
25 +void NetworkService::Create(models::Session& session, const models::CreateNetworkOptions& createOptions)
26 +{
27 + WSLCNetworkOptions options{};
28 + options.Name = createOptions.Name.c_str();
29 + if (createOptions.Driver.has_value())
30 + {
31 + options.Driver = createOptions.Driver->c_str();
32 + }
33 +
34 + // Set driver options
35 + std::vector<KeyValuePair> driverOpts;
36 + for (const auto& option : createOptions.DriverOpts)
37 + {
38 + driverOpts.push_back({.Key = option.first.c_str(), .Value = option.second.c_str()});
39 + }
40 +
41 + // Set labels
42 + std::vector<KeyValuePair> labels;
43 + for (const auto& label : createOptions.Labels)
44 + {
45 + labels.push_back({.Key = label.first.c_str(), .Value = label.second.c_str()});
46 + }
47 +
48 + options.DriverOpts = driverOpts.data();
49 + options.DriverOptsCount = static_cast<ULONG>(driverOpts.size());
50 + options.Labels = labels.data();
51 + options.LabelsCount = static_cast<ULONG>(labels.size());
52 +
53 + auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
54 + THROW_IF_FAILED(session.Get()->CreateNetwork(&options, warningCallback.Get()));
55 +}
56 +
57 +void NetworkService::Delete(models::Session& session, const std::string& name)
58 +{
59 + THROW_IF_FAILED(session.Get()->DeleteNetwork(name.c_str()));
60 +}
61 +
62 +std::vector<WSLCNetworkInformation> NetworkService::List(models::Session& session)
63 +{
64 + wil::unique_cotaskmem_array_ptr<WSLCNetworkInformation> rawNetworks;
65 + ULONG count = 0;
66 + THROW_IF_FAILED(session.Get()->ListNetworks(&rawNetworks, &count));
67 +
68 + std::vector<WSLCNetworkInformation> networks;
69 + networks.reserve(count);
70 + for (auto ptr = rawNetworks.get(), end = rawNetworks.get() + count; ptr != end; ++ptr)
71 + {
72 + networks.push_back(*ptr);
73 + }
74 +
75 + return networks;
76 +}
77 +
78 +wsl::windows::common::wslc_schema::Network NetworkService::Inspect(models::Session& session, const std::string& name)
79 +{
80 + wil::unique_cotaskmem_ansistring output;
81 + THROW_IF_FAILED(session.Get()->InspectNetwork(name.c_str(), &output));
82 + return FromJson<wsl::windows::common::wslc_schema::Network>(output.get());
83 +}
84 +} // namespace wsl::windows::wslc::services
src/windows/wslc/services/NetworkService.h new
+28
@@ -0,0 +1,28 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkService.h
8 +
9 +Abstract:
10 +
11 + This file contains the NetworkService definition
12 +
13 +--*/
14 +#pragma once
15 +
16 +#include "SessionModel.h"
17 +#include "NetworkModel.h"
18 +#include <wslc_schema.h>
19 +
20 +namespace wsl::windows::wslc::services {
21 +struct NetworkService
22 +{
23 + static void Create(models::Session& session, const models::CreateNetworkOptions& createOptions);
24 + static void Delete(models::Session& session, const std::string& name);
25 + static std::vector<WSLCNetworkInformation> List(models::Session& session);
26 + static wsl::windows::common::wslc_schema::Network Inspect(models::Session& session, const std::string& name);
27 +};
28 +} // namespace wsl::windows::wslc::services
src/windows/wslc/services/VolumeModel.cpp deleted
-55
@@ -1,55 +0,0 @@
1 -/*++
2 -
3 -Copyright (c) Microsoft. All rights reserved.
4 -
5 -Module Name:
6 -
7 - VolumeModel.cpp
8 -
9 -Abstract:
10 -
11 - This file contains the VolumeModel implementations
12 -
13 ---*/
14 -#include "precomp.h"
15 -#include "VolumeModel.h"
16 -
17 -using namespace wsl::shared;
18 -using namespace wsl::windows::common::string;
19 -
20 -namespace wsl::windows::wslc::models {
21 -
22 -std::pair<std::string, std::string> Label::Parse(const std::wstring& value)
23 -{
24 - std::pair<std::string, std::string> result{};
25 - auto pos = value.find('=');
26 - if (pos == std::wstring::npos)
27 - {
28 - result.first = WideToMultiByte(value);
29 - }
30 - else
31 - {
32 - result.first = WideToMultiByte(value.substr(0, pos));
33 - result.second = WideToMultiByte(value.substr(pos + 1));
34 - }
35 -
36 - THROW_HR_WITH_USER_ERROR_IF(E_INVALIDARG, Localization::WSLCCLI_LabelKeyEmptyError(), result.first.empty());
37 - return result;
38 -}
39 -
40 -std::pair<std::string, std::string> DriverOption::Parse(const std::wstring& value)
41 -{
42 - std::pair<std::string, std::string> result{};
43 - auto pos = value.find('=');
44 - if (pos == std::wstring::npos)
45 - {
46 - result.first = WideToMultiByte(value);
47 - return result;
48 - }
49 -
50 - result.first = WideToMultiByte(value.substr(0, pos));
51 - result.second = WideToMultiByte(value.substr(pos + 1));
52 - return result;
53 -}
54 -
55 -} // namespace wsl::windows::wslc::models
src/windows/wslc/services/VolumeModel.h
-10
@@ -19,16 +19,6 @@ Abstract:
19
20 namespace wsl::windows::wslc::models {
21
22 -struct Label
23 -{
24 - static std::pair<std::string, std::string> Parse(const std::wstring& value);
25 -};
26 -
27 -struct DriverOption
28 -{
29 - static std::pair<std::string, std::string> Parse(const std::wstring& value);
30 -};
31 -
22 struct CreateVolumeOptions
23 {
24 std::string Name;
src/windows/wslc/tasks/ContainerTasks.cpp
+1 -2
@@ -22,7 +22,6 @@ Abstract:
22 #include "SessionModel.h"
23 #include "SessionService.h"
24 #include "TableOutput.h"
25 -#include "VolumeModel.h"
25 #include <wil/result_macros.h>
26 #include <wslc_schema.h>
27
@@ -518,7 +517,7 @@ void SetContainerOptionsFromArgs(CLIExecutionContext& context)
517 {
518 for (const auto& label : context.Args.GetAll<ArgType::Label>())
519 {
521 - auto parsed = Label::Parse(label);
520 + auto parsed = validation::ParseLabel(label);
521 options.Labels.emplace_back(parsed.first, parsed.second);
522 }
523 }
src/windows/wslc/tasks/InspectTasks.cpp
+11
@@ -16,6 +16,7 @@ Abstract:
16 #include "InspectTasks.h"
17 #include "InspectModel.h"
18 #include "ImageService.h"
19 +#include "NetworkService.h"
20 #include "VolumeService.h"
21 #include "ContainerService.h"
22
@@ -57,6 +58,11 @@ static bool TryInspectContainer(wsl::windows::wslc::models::Session& session, co
58 return TryInspect([&]() { result = services::ContainerService::Inspect(session, containerId); }, WSLC_E_CONTAINER_NOT_FOUND);
59 }
60
61 +static bool TryInspectNetwork(wsl::windows::wslc::models::Session& session, const std::string& networkName, std::optional<wslc_schema::Network>& result)
62 +{
63 + return TryInspect([&]() { result = services::NetworkService::Inspect(session, networkName); }, WSLC_E_NETWORK_NOT_FOUND);
64 +}
65 +
66 static bool TryInspectVolume(wsl::windows::wslc::models::Session& session, const std::string& volumeId, std::optional<wslc_schema::InspectVolume>& result)
67 {
68 return TryInspect([&]() { result = services::VolumeService::Inspect(session, volumeId); }, WSLC_E_VOLUME_NOT_FOUND);
@@ -80,6 +86,7 @@ void Inspect(CLIExecutionContext& context)
86 auto id = WideToMultiByte(objectId);
87 std::optional<wslc_schema::InspectContainer> container;
88 std::optional<wslc_schema::InspectImage> image;
89 + std::optional<wslc_schema::Network> network;
90 std::optional<wslc_schema::InspectVolume> volume;
91
92 if (WI_IsFlagSet(type, InspectType::Container) && TryInspectContainer(session, id, container))
@@ -90,6 +97,10 @@ void Inspect(CLIExecutionContext& context)
97 {
98 array.push_back(std::move(*image));
99 }
100 + else if (WI_IsFlagSet(type, InspectType::Network) && TryInspectNetwork(session, id, network))
101 + {
102 + array.push_back(std::move(*network));
103 + }
104 else if (WI_IsFlagSet(type, InspectType::Volume) && TryInspectVolume(session, id, volume))
105 {
106 array.push_back(std::move(*volume));
src/windows/wslc/tasks/NetworkTasks.cpp new
+194
@@ -0,0 +1,194 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkTasks.cpp
8 +
9 +Abstract:
10 +
11 + Implementation of network command related execution logic.
12 +
13 +--*/
14 +#include "Argument.h"
15 +#include "ArgumentValidation.h"
16 +#include "CLIExecutionContext.h"
17 +#include "NetworkModel.h"
18 +#include "NetworkService.h"
19 +#include "NetworkTasks.h"
20 +#include "TableOutput.h"
21 +#include <wslc_schema.h>
22 +
23 +using namespace wsl::shared;
24 +using namespace wsl::windows::common;
25 +using namespace wsl::windows::common::string;
26 +using namespace wsl::windows::common::wslutil;
27 +using namespace wsl::windows::wslc::execution;
28 +using namespace wsl::windows::wslc::models;
29 +using namespace wsl::windows::wslc::services;
30 +
31 +namespace wsl::windows::wslc::task {
32 +
33 +static bool TryInspectNetwork(Session& session, const std::string& networkName, std::optional<wslc_schema::Network>& inspectData)
34 +{
35 + try
36 + {
37 + inspectData = NetworkService::Inspect(session, networkName);
38 + return true;
39 + }
40 + catch (const wil::ResultException& ex)
41 + {
42 + if (ex.GetErrorCode() == WSLC_E_NETWORK_NOT_FOUND)
43 + {
44 + PrintMessage(Localization::MessageWslcNetworkNotFound(networkName.c_str()), stderr);
45 + return false;
46 + }
47 +
48 + throw;
49 + }
50 +}
51 +
52 +static bool TryDeleteNetwork(Session& session, const std::string& networkName)
53 +{
54 + try
55 + {
56 + NetworkService::Delete(session, networkName);
57 + return true;
58 + }
59 + catch (const wil::ResultException& ex)
60 + {
61 + if (ex.GetErrorCode() == WSLC_E_NETWORK_NOT_FOUND)
62 + {
63 + PrintMessage(Localization::MessageWslcNetworkNotFound(networkName.c_str()), stderr);
64 + return false;
65 + }
66 +
67 + throw;
68 + }
69 +}
70 +
71 +void CreateNetwork(CLIExecutionContext& context)
72 +{
73 + WI_ASSERT(context.Data.Contains(Data::Session));
74 + WI_ASSERT(context.Args.Contains(ArgType::NetworkName));
75 +
76 + models::CreateNetworkOptions options{};
77 + options.Name = WideToMultiByte(context.Args.Get<ArgType::NetworkName>());
78 +
79 + for (const auto& option : context.Args.GetAll<ArgType::Options>())
80 + {
81 + options.DriverOpts.push_back(validation::ParseDriverOption(option));
82 + }
83 +
84 + for (const auto& label : context.Args.GetAll<ArgType::Label>())
85 + {
86 + options.Labels.push_back(validation::ParseLabel(label));
87 + }
88 +
89 + if (context.Args.Contains(ArgType::Driver))
90 + {
91 + options.Driver = WideToMultiByte(context.Args.Get<ArgType::Driver>());
92 + }
93 +
94 + NetworkService::Create(context.Data.Get<Data::Session>(), options);
95 + PrintMessage(MultiByteToWide(options.Name));
96 +}
97 +
98 +void DeleteNetworks(CLIExecutionContext& context)
99 +{
100 + WI_ASSERT(context.Data.Contains(Data::Session));
101 + auto& session = context.Data.Get<Data::Session>();
102 + auto networkNames = context.Args.GetAll<ArgType::NetworkName>();
103 + for (const auto& name : networkNames)
104 + {
105 + if (TryDeleteNetwork(session, WideToMultiByte(name)))
106 + {
107 + PrintMessage(name);
108 + }
109 + else
110 + {
111 + context.ExitCode = 1;
112 + }
113 + }
114 +}
115 +
116 +void GetNetworks(CLIExecutionContext& context)
117 +{
118 + WI_ASSERT(context.Data.Contains(Data::Session));
119 + auto& session = context.Data.Get<Data::Session>();
120 + context.Data.Add<Data::Networks>(NetworkService::List(session));
121 +}
122 +
123 +void InspectNetworks(CLIExecutionContext& context)
124 +{
125 + WI_ASSERT(context.Data.Contains(Data::Session));
126 + auto& session = context.Data.Get<Data::Session>();
127 + auto networkNames = context.Args.GetAll<ArgType::NetworkName>();
128 + std::vector<wsl::windows::common::wslc_schema::Network> result;
129 + for (const auto& name : networkNames)
130 + {
131 + std::optional<wslc_schema::Network> inspectData;
132 + if (TryInspectNetwork(session, WideToMultiByte(name), inspectData))
133 + {
134 + result.push_back(*inspectData);
135 + }
136 + else
137 + {
138 + context.ExitCode = 1;
139 + }
140 + }
141 +
142 + auto json = ToJson(result, c_jsonPrettyPrintIndent);
143 + PrintMessage(MultiByteToWide(json));
144 +}
145 +
146 +void ListNetworks(CLIExecutionContext& context)
147 +{
148 + WI_ASSERT(context.Data.Contains(Data::Networks));
149 + auto& networks = context.Data.Get<Data::Networks>();
150 +
151 + if (context.Args.Contains(ArgType::Quiet))
152 + {
153 + for (const auto& network : networks)
154 + {
155 + PrintMessage(MultiByteToWide(network.Name));
156 + }
157 +
158 + return;
159 + }
160 +
161 + FormatType format = FormatType::Table;
162 + if (context.Args.Contains(ArgType::Format))
163 + {
164 + format = validation::GetFormatTypeFromString(context.Args.Get<ArgType::Format>());
165 + }
166 +
167 + switch (format)
168 + {
169 + case FormatType::Json:
170 + {
171 + auto json = ToJson(networks, c_jsonPrettyPrintIndent);
172 + PrintMessage(MultiByteToWide(json));
173 + break;
174 + }
175 + case FormatType::Table:
176 + {
177 + auto table = wsl::windows::wslc::TableOutput<3>({L"NETWORK ID", L"NAME", L"DRIVER"});
178 + for (const auto& network : networks)
179 + {
180 + table.OutputLine({
181 + MultiByteToWide(TruncateId(network.Id)),
182 + MultiByteToWide(network.Name),
183 + MultiByteToWide(network.Driver),
184 + });
185 + }
186 +
187 + table.Complete();
188 + break;
189 + }
190 + default:
191 + THROW_HR(E_UNEXPECTED);
192 + }
193 +}
194 +} // namespace wsl::windows::wslc::task
src/windows/wslc/tasks/NetworkTasks.h new
+24
@@ -0,0 +1,24 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + NetworkTasks.h
8 +
9 +Abstract:
10 +
11 + Declaration of network command execution tasks.
12 +
13 +--*/
14 +#pragma once
15 +#include "CLIExecutionContext.h"
16 +
17 +namespace wsl::windows::wslc::task {
18 +
19 +void CreateNetwork(wsl::windows::wslc::execution::CLIExecutionContext& context);
20 +void DeleteNetworks(wsl::windows::wslc::execution::CLIExecutionContext& context);
21 +void GetNetworks(wsl::windows::wslc::execution::CLIExecutionContext& context);
22 +void InspectNetworks(wsl::windows::wslc::execution::CLIExecutionContext& context);
23 +void ListNetworks(wsl::windows::wslc::execution::CLIExecutionContext& context);
24 +} // namespace wsl::windows::wslc::task
src/windows/wslc/tasks/VolumeTasks.cpp
+2 -2
@@ -80,13 +80,13 @@ void CreateVolume(CLIExecutionContext& context)
80
81 for (const auto& option : context.Args.GetAll<ArgType::Options>())
82 {
83 - auto parsed = DriverOption::Parse(option);
83 + auto parsed = validation::ParseDriverOption(option);
84 options.DriverOpts.emplace_back(parsed.first, parsed.second);
85 }
86
87 for (const auto& label : context.Args.GetAll<ArgType::Label>())
88 {
89 - auto parsed = Label::Parse(label);
89 + auto parsed = validation::ParseLabel(label);
90 options.Labels.emplace_back(parsed.first, parsed.second);
91 }
92
test/windows/wslc/WSLCCLIExecutionUnitTests.cpp
+6
@@ -104,6 +104,12 @@ class WSLCCLIExecutionUnitTests
104 dataMap.Add<Data::Volumes>(std::move(volumes));
105 handled = true;
106 }
107 + else if (dataType == Data::Networks)
108 + {
109 + std::vector<WSLCNetworkInformation> networks;
110 + dataMap.Add<Data::Networks>(std::move(networks));
111 + handled = true;
112 + }
113
114 if (!handled)
115 {
test/windows/wslc/WSLCCLILabelParserUnitTests.cpp
+3 -3
@@ -14,7 +14,7 @@ Abstract:
14 #include "precomp.h"
15 #include "windows/Common.h"
16 #include "WSLCCLITestHelpers.h"
17 -#include "VolumeModel.h"
17 +#include "ArgumentValidation.h"
18
19 using namespace wsl::windows::wslc;
20
@@ -35,7 +35,7 @@ class WSLCCLILabelParserUnitTests
35
36 for (const auto& [input, expectedKey, expectedValue] : validLabels)
37 {
38 - auto result = models::Label::Parse(input);
38 + auto result = validation::ParseLabel(input);
39 VERIFY_ARE_EQUAL(expectedKey, result.first);
40 VERIFY_ARE_EQUAL(expectedValue, result.second);
41 }
@@ -53,7 +53,7 @@ class WSLCCLILabelParserUnitTests
53 {
54 try
55 {
56 - (void)models::Label::Parse(input);
56 + (void)validation::ParseLabel(input);
57 VERIFY_FAIL(L"Expected exception");
58 }
59 catch (const wil::ResultException& ex)
test/windows/wslc/WSLCCLIOptionsParserUnitTests.cpp
+2 -2
@@ -14,7 +14,7 @@ Abstract:
14 #include "precomp.h"
15 #include "windows/Common.h"
16 #include "WSLCCLITestHelpers.h"
17 -#include "VolumeModel.h"
17 +#include "ArgumentValidation.h"
18
19 using namespace wsl::windows::wslc;
20
@@ -37,7 +37,7 @@ class WSLCCLIOptionsParserUnitTests
37
38 for (const auto& [input, expectedKey, expectedValue] : validOptions)
39 {
40 - auto result = models::DriverOption::Parse(input);
40 + auto result = validation::ParseDriverOption(input);
41 VERIFY_ARE_EQUAL(expectedKey, result.first);
42 VERIFY_ARE_EQUAL(expectedValue, result.second);
43 }
test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp
+1
@@ -525,6 +525,7 @@ private:
525 std::vector<std::pair<std::wstring_view, std::wstring>> entries = {
526 {L"container", Localization::WSLCCLI_ContainerCommandDesc()},
527 {L"image", Localization::WSLCCLI_ImageCommandDesc()},
528 + {L"network", Localization::WSLCCLI_NetworkCommandDesc()},
529 {L"registry", Localization::WSLCCLI_RegistryCommandDesc()},
530 {L"settings", Localization::WSLCCLI_SettingsCommandDesc()},
531 {L"system", Localization::WSLCCLI_SystemCommandDesc()},
test/windows/wslc/e2e/WSLCE2EHelpers.cpp
+55
@@ -244,6 +244,36 @@ void VerifyVolumeIsNotListed(const std::wstring& volumeName)
244 }
245 }
246
247 +void VerifyNetworkIsListed(const std::wstring& networkName)
248 +{
249 + auto result = RunWslc(L"network list --format json");
250 + result.Verify({.Stderr = L"", .ExitCode = 0});
251 + auto networks = wsl::shared::FromJson<std::vector<WSLCNetworkInformation>>(result.Stdout.value().c_str());
252 + for (const auto& net : networks)
253 + {
254 + if (net.Name == wsl::shared::string::WideToMultiByte(networkName))
255 + {
256 + return;
257 + }
258 + }
259 +
260 + VERIFY_FAIL(std::format(L"Network '{}' not found in network list output", networkName).c_str());
261 +}
262 +
263 +void VerifyNetworkIsNotListed(const std::wstring& networkName)
264 +{
265 + auto result = RunWslc(L"network list --format json");
266 + result.Verify({.Stderr = L"", .ExitCode = 0});
267 + auto networks = wsl::shared::FromJson<std::vector<WSLCNetworkInformation>>(result.Stdout.value().c_str());
268 + for (const auto& net : networks)
269 + {
270 + if (net.Name == wsl::shared::string::WideToMultiByte(networkName))
271 + {
272 + VERIFY_FAIL(std::format(L"Network '{}' found in network list output", networkName).c_str());
273 + }
274 + }
275 +}
276 +
277 std::string GetHashId(const std::string& id, bool fullId)
278 {
279 return wsl::windows::common::string::TruncateId(id, !fullId);
@@ -422,6 +452,31 @@ void EnsureVolumeDoesNotExist(const std::wstring& volumeName)
452 }
453 }
454
455 +void EnsureNetworkDoesNotExist(const std::wstring& networkName)
456 +{
457 + auto result = RunWslc(L"network list --format json");
458 + result.Verify({.Stderr = L"", .ExitCode = 0});
459 + auto networks = wsl::shared::FromJson<std::vector<WSLCNetworkInformation>>(result.Stdout.value().c_str());
460 + for (const auto& net : networks)
461 + {
462 + if (net.Name == wsl::shared::string::WideToMultiByte(networkName))
463 + {
464 + auto deleteResult = RunWslc(std::format(L"network rm {}", networkName));
465 + deleteResult.Verify({.Stderr = L"", .ExitCode = 0});
466 + break;
467 + }
468 + }
469 +}
470 +
471 +wslc_schema::Network InspectNetwork(const std::wstring& networkName)
472 +{
473 + auto result = RunWslc(std::format(L"network inspect {}", networkName));
474 + result.Verify({.Stderr = L"", .ExitCode = 0});
475 + auto inspectData = wsl::shared::FromJson<std::vector<wslc_schema::Network>>(result.Stdout.value().c_str());
476 + VERIFY_ARE_EQUAL(1u, inspectData.size());
477 + return inspectData[0];
478 +}
479 +
480 wil::com_ptr<IWSLCSession> OpenDefaultElevatedSession()
481 {
482 // Ensure the default elevated session exists before opening it via COM.
test/windows/wslc/e2e/WSLCE2EHelpers.h
+4
@@ -123,11 +123,14 @@ void VerifyImageIsNotUsed(const TestImage& image);
123 void VerifyImageIsListed(const TestImage& image);
124 void VerifyVolumeIsListed(const std::wstring& volumeName);
125 void VerifyVolumeIsNotListed(const std::wstring& volumeName);
126 +void VerifyNetworkIsListed(const std::wstring& networkName);
127 +void VerifyNetworkIsNotListed(const std::wstring& networkName);
128
129 std::string GetHashId(const std::string& id, bool fullId = false);
130 wsl::windows::common::wslc_schema::InspectContainer InspectContainer(const std::wstring& containerName);
131 wsl::windows::common::wslc_schema::InspectImage InspectImage(const std::wstring& imageName);
132 wsl::windows::common::wslc_schema::InspectVolume InspectVolume(const std::wstring& volumeName);
133 +wsl::windows::common::wslc_schema::Network InspectNetwork(const std::wstring& networkName);
134 std::vector<wsl::windows::wslc::models::ContainerInformation> ListAllContainers();
135
136 void EnsureContainerDoesNotExist(const std::wstring& containerName);
@@ -136,6 +139,7 @@ void EnsureImageIsDeleted(const TestImage& image);
139 void EnsureImageContainersAreDeleted(const TestImage& image);
140 void EnsureSessionIsTerminated(const std::wstring& sessionName = L"");
141 void EnsureVolumeDoesNotExist(const std::wstring& volumeName);
142 +void EnsureNetworkDoesNotExist(const std::wstring& networkName);
143
144 void WriteTestFile(const std::filesystem::path& filePath, const std::vector<std::string>& envVariableLines);
145 std::wstring GetPythonHttpServerScript(uint16_t port);
test/windows/wslc/e2e/WSLCE2EInspectTests.cpp
+59 -9
@@ -35,6 +35,7 @@ class WSLCE2EInspectTests
35 {
36 EnsureContainerDoesNotExist(WslcContainerName);
37 EnsureContainerDoesNotExist(DebianImage.Name);
38 + EnsureNetworkDoesNotExist(WslcNetworkName);
39 EnsureImageIsDeleted(DebianImage);
40 return true;
41 }
@@ -43,6 +44,7 @@ class WSLCE2EInspectTests
44 {
45 EnsureContainerDoesNotExist(WslcContainerName);
46 EnsureContainerDoesNotExist(DebianImage.Name);
47 + EnsureNetworkDoesNotExist(WslcNetworkName);
48 return true;
49 }
50
@@ -165,14 +167,14 @@ class WSLCE2EInspectTests
167 }
168 }
169
168 - WSLC_TEST_METHOD(WSLCE2E_Inspect_Image_PriorityOverVolume)
170 + WSLC_TEST_METHOD(WSLCE2E_Inspect_Image_PriorityOverNetwork)
171 {
170 - // When an image and volume share the same name and no --type is specified,
171 - // the image should be returned (image is checked before volume in InspectTasks).
172 - EnsureVolumeDoesNotExist(DebianImage.Name);
173 - auto createResult = RunWslc(std::format(L"volume create {}", DebianImage.Name));
172 + // When an image and network share the same name and no --type is specified,
173 + // the image should be returned (image is checked before network in InspectTasks).
174 + EnsureNetworkDoesNotExist(DebianImage.Name);
175 + auto createResult = RunWslc(std::format(L"network create --driver bridge {}", DebianImage.Name));
176 createResult.Verify({.Stderr = L"", .ExitCode = 0});
175 - auto deleteVolume = wil::scope_exit([&]() { EnsureVolumeDoesNotExist(DebianImage.Name); });
177 + auto deleteNetwork = wil::scope_exit([&]() { EnsureNetworkDoesNotExist(DebianImage.Name); });
178
179 // No type specified
180 {
@@ -199,14 +201,61 @@ class WSLCE2EInspectTests
201 VERIFY_ARE_EQUAL(DebianImage.NameAndTag(), wsl::shared::string::MultiByteToWide(inspectData[0].RepoTags.value()[0]));
202 }
203
204 + // With --type network
205 + {
206 + auto result = RunWslc(std::format(L"inspect --type network {}", DebianImage.Name));
207 + result.Verify({.Stderr = L"", .ExitCode = 0});
208 + auto inspectData =
209 + wsl::shared::FromJson<std::vector<wsl::windows::common::wslc_schema::Network>>(result.Stdout.value().c_str());
210 + VERIFY_ARE_EQUAL(1u, inspectData.size());
211 + VERIFY_ARE_EQUAL(DebianImage.Name, wsl::shared::string::MultiByteToWide(inspectData[0].Name));
212 + }
213 + }
214 +
215 + WSLC_TEST_METHOD(WSLCE2E_Inspect_Network_PriorityOverVolume)
216 + {
217 + // When a network and volume share the same name and no --type is specified,
218 + // the network should be returned (network is checked before volume in InspectTasks).
219 + EnsureNetworkDoesNotExist(WslcNetworkName);
220 + EnsureVolumeDoesNotExist(WslcNetworkName);
221 +
222 + auto createNetworkResult = RunWslc(std::format(L"network create --driver bridge {}", WslcNetworkName));
223 + createNetworkResult.Verify({.Stderr = L"", .ExitCode = 0});
224 + auto deleteNetwork = wil::scope_exit([&]() { EnsureNetworkDoesNotExist(WslcNetworkName); });
225 +
226 + auto createVolumeResult = RunWslc(std::format(L"volume create {}", WslcNetworkName));
227 + createVolumeResult.Verify({.Stderr = L"", .ExitCode = 0});
228 + auto deleteVolume = wil::scope_exit([&]() { EnsureVolumeDoesNotExist(WslcNetworkName); });
229 +
230 + // No type specified — network takes priority over volume
231 + {
232 + auto result = RunWslc(std::format(L"inspect {}", WslcNetworkName));
233 + result.Verify({.Stderr = L"", .ExitCode = 0});
234 +
235 + auto inspectData =
236 + wsl::shared::FromJson<std::vector<wsl::windows::common::wslc_schema::Network>>(result.Stdout.value().c_str());
237 + VERIFY_ARE_EQUAL(1u, inspectData.size());
238 + VERIFY_ARE_EQUAL(WslcNetworkName, wsl::shared::string::MultiByteToWide(inspectData[0].Name));
239 + }
240 +
241 + // With --type network
242 + {
243 + auto result = RunWslc(std::format(L"inspect --type network {}", WslcNetworkName));
244 + result.Verify({.Stderr = L"", .ExitCode = 0});
245 + auto inspectData =
246 + wsl::shared::FromJson<std::vector<wsl::windows::common::wslc_schema::Network>>(result.Stdout.value().c_str());
247 + VERIFY_ARE_EQUAL(1u, inspectData.size());
248 + VERIFY_ARE_EQUAL(WslcNetworkName, wsl::shared::string::MultiByteToWide(inspectData[0].Name));
249 + }
250 +
251 // With --type volume
252 {
204 - auto result = RunWslc(std::format(L"inspect --type volume {}", DebianImage.Name));
253 + auto result = RunWslc(std::format(L"inspect --type volume {}", WslcNetworkName));
254 result.Verify({.Stderr = L"", .ExitCode = 0});
255 auto inspectData =
256 wsl::shared::FromJson<std::vector<wsl::windows::common::wslc_schema::InspectVolume>>(result.Stdout.value().c_str());
257 VERIFY_ARE_EQUAL(1u, inspectData.size());
209 - VERIFY_ARE_EQUAL(DebianImage.Name, wsl::shared::string::MultiByteToWide(inspectData[0].Name));
258 + VERIFY_ARE_EQUAL(WslcNetworkName, wsl::shared::string::MultiByteToWide(inspectData[0].Name));
259 }
260 }
261
@@ -239,7 +288,7 @@ class WSLCE2EInspectTests
288 WSLC_TEST_METHOD(WSLCE2E_Inspect_InvalidTypeValue)
289 {
290 auto result = RunWslc(std::format(L"inspect --type invalid {}", DebianImage.NameAndTag()));
242 - result.Verify({.Stderr = L"Invalid type value: invalid is not a recognized inspect type. Supported inspect types are: image, container, volume.\r\n", .ExitCode = 1});
291 + result.Verify({.Stderr = L"Invalid type value: invalid is not a recognized inspect type. Supported inspect types are: image, container, network, volume.\r\n", .ExitCode = 1});
292 }
293
294 WSLC_TEST_METHOD(WSLCE2E_Inspect_SkipsInvalidFormatError)
@@ -261,6 +310,7 @@ private:
310 const TestImage& DebianImage = DebianTestImage();
311 const TestImage& InvalidImage = InvalidTestImage();
312 const std::wstring WslcVolumeName = L"wslc-inspect-test-volume";
313 + const std::wstring WslcNetworkName = L"wslc-inspect-test-network";
314 std::wstring GetHelpMessage() const
315 {
316 std::wstringstream output;
test/windows/wslc/e2e/WSLCE2ENetworkCreateTests.cpp new
+161
@@ -0,0 +1,161 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + WSLCE2ENetworkCreateTests.cpp
8 +
9 +Abstract:
10 +
11 + This file contains end-to-end tests for WSLC.
12 +--*/
13 +
14 +#include "precomp.h"
15 +#include "windows/Common.h"
16 +#include "WSLCExecutor.h"
17 +#include "WSLCE2EHelpers.h"
18 +
19 +namespace WSLCE2ETests {
20 +using namespace wsl::shared;
21 +
22 +class WSLCE2ENetworkCreateTests
23 +{
24 + WSLC_TEST_CLASS(WSLCE2ENetworkCreateTests)
25 +
26 + TEST_METHOD_SETUP(MethodSetup)
27 + {
28 + EnsureNetworkDoesNotExist(TestNetworkName);
29 + return true;
30 + }
31 +
32 + TEST_CLASS_CLEANUP(ClassCleanup)
33 + {
34 + EnsureNetworkDoesNotExist(TestNetworkName);
35 + return true;
36 + }
37 +
38 + WSLC_TEST_METHOD(WSLCE2E_Network_Create_HelpCommand)
39 + {
40 + auto result = RunWslc(L"network create --help");
41 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
42 + }
43 +
44 + WSLC_TEST_METHOD(WSLCE2E_Network_Create_MissingName)
45 + {
46 + auto result = RunWslc(L"network create");
47 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Required argument not provided: 'network-name'\r\n", .ExitCode = 1});
48 + }
49 +
50 + WSLC_TEST_METHOD(WSLCE2E_Network_Create_DefaultDriver_Success)
51 + {
52 + // TODO: http://task.ms/62564313
53 + SKIP_TEST_UNSTABLE();
54 +
55 + auto result = RunWslc(std::format(L"network create {}", TestNetworkName));
56 + result.Verify({.Stderr = L"", .ExitCode = 0});
57 + VERIFY_ARE_EQUAL(TestNetworkName, result.GetStdoutOneLine());
58 +
59 + VerifyNetworkIsListed(TestNetworkName);
60 + auto inspect = InspectNetwork(TestNetworkName);
61 + VERIFY_ARE_EQUAL("bridge", inspect.Driver);
62 + }
63 +
64 + WSLC_TEST_METHOD(WSLCE2E_Network_Create_BridgeDriver_Success)
65 + {
66 + auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
67 + result.Verify({.Stderr = L"", .ExitCode = 0});
68 + VERIFY_ARE_EQUAL(TestNetworkName, result.GetStdoutOneLine());
69 +
70 + VerifyNetworkIsListed(TestNetworkName);
71 + auto inspect = InspectNetwork(TestNetworkName);
72 + VERIFY_ARE_EQUAL("bridge", inspect.Driver);
73 + }
74 +
75 + WSLC_TEST_METHOD(WSLCE2E_Network_Create_WithLabels_Success)
76 + {
77 + auto result = RunWslc(std::format(L"network create --driver bridge --label env=test --label app=wslc {}", TestNetworkName));
78 + result.Verify({.Stderr = L"", .ExitCode = 0});
79 + VERIFY_ARE_EQUAL(TestNetworkName, result.GetStdoutOneLine());
80 +
81 + VerifyNetworkIsListed(TestNetworkName);
82 + auto inspect = InspectNetwork(TestNetworkName);
83 + VERIFY_ARE_EQUAL("bridge", inspect.Driver);
84 + VERIFY_ARE_EQUAL("test", inspect.Labels["env"]);
85 + VERIFY_ARE_EQUAL("wslc", inspect.Labels["app"]);
86 + }
87 +
88 + WSLC_TEST_METHOD(WSLCE2E_Network_Create_EmptyLabelKey_Fail)
89 + {
90 + auto result = RunWslc(std::format(L"network create --driver bridge --label =foo {}", TestNetworkName));
91 + result.Verify({.Stdout = L"", .Stderr = L"Label key cannot be empty\r\nError code: E_INVALIDARG\r\n", .ExitCode = 1});
92 +
93 + VerifyNetworkIsNotListed(TestNetworkName);
94 + }
95 +
96 + WSLC_TEST_METHOD(WSLCE2E_Network_Create_InvalidDriver_Fail)
97 + {
98 + auto result = RunWslc(std::format(L"network create --driver invalid_driver {}", TestNetworkName));
99 + result.Verify(
100 + {.Stdout = L"", .Stderr = std::format(L"Unsupported network driver: 'invalid_driver'\r\nError code: E_INVALIDARG\r\n"), .ExitCode = 1});
101 +
102 + VerifyNetworkIsNotListed(TestNetworkName);
103 + }
104 +
105 + WSLC_TEST_METHOD(WSLCE2E_Network_Create_Duplicate_Fail)
106 + {
107 + auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
108 + result.Verify({.Stderr = L"", .ExitCode = 0});
109 +
110 + result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
111 + result.Verify(
112 + {.Stdout = L"", .Stderr = L"Cannot create a file when that file already exists. \r\nError code: ERROR_ALREADY_EXISTS\r\n", .ExitCode = 1});
113 + }
114 +
115 +private:
116 + const std::wstring TestNetworkName = L"wslc-e2e-network-create";
117 +
118 + std::wstring GetHelpMessage() const
119 + {
120 + std::wstringstream output;
121 + output << GetWslcHeader() //
122 + << GetDescription() //
123 + << GetUsage() //
124 + << GetAvailableCommands() //
125 + << GetAvailableOptions();
126 + return output.str();
127 + }
128 +
129 + std::wstring GetDescription() const
130 + {
131 + return std::format(L"{}\r\n\r\n", Localization::WSLCCLI_NetworkCreateLongDesc());
132 + }
133 +
134 + std::wstring GetUsage() const
135 + {
136 + return L"Usage: wslc network create [<options>] <network-name>\r\n\r\n";
137 + }
138 +
139 + std::wstring GetAvailableCommands() const
140 + {
141 + std::wstringstream commands;
142 + commands << L"The following arguments are available:\r\n" //
143 + << L" network-name Network name\r\n" //
144 + << L"\r\n";
145 + return commands.str();
146 + }
147 +
148 + std::wstring GetAvailableOptions() const
149 + {
150 + std::wstringstream options;
151 + options << L"The following options are available:\r\n" //
152 + << L" -d,--driver Specify network driver name (default: bridge)\r\n" //
153 + << L" -o,--opt Set driver specific options\r\n" //
154 + << L" -l,--label Network metadata setting\r\n" //
155 + << L" --session Specify the session to use\r\n" //
156 + << L" -?,--help Shows help about the selected command\r\n" //
157 + << L"\r\n";
158 + return options.str();
159 + }
160 +};
161 +} // namespace WSLCE2ETests
test/windows/wslc/e2e/WSLCE2ENetworkInspectTests.cpp new
+157
@@ -0,0 +1,157 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + WSLCE2ENetworkInspectTests.cpp
8 +
9 +Abstract:
10 +
11 + This file contains end-to-end tests for WSLC.
12 +--*/
13 +
14 +#include "precomp.h"
15 +#include "windows/Common.h"
16 +#include "WSLCExecutor.h"
17 +#include "WSLCE2EHelpers.h"
18 +
19 +namespace WSLCE2ETests {
20 +using namespace wsl::shared;
21 +using namespace wsl::shared::string;
22 +
23 +class WSLCE2ENetworkInspectTests
24 +{
25 + WSLC_TEST_CLASS(WSLCE2ENetworkInspectTests)
26 +
27 + TEST_METHOD_SETUP(MethodSetup)
28 + {
29 + EnsureNetworkDoesNotExist(TestNetworkName1);
30 + EnsureNetworkDoesNotExist(TestNetworkName2);
31 + return true;
32 + }
33 +
34 + TEST_CLASS_CLEANUP(ClassCleanup)
35 + {
36 + EnsureNetworkDoesNotExist(TestNetworkName1);
37 + EnsureNetworkDoesNotExist(TestNetworkName2);
38 + return true;
39 + }
40 +
41 + WSLC_TEST_METHOD(WSLCE2E_Network_Inspect_HelpCommand)
42 + {
43 + auto result = RunWslc(L"network inspect --help");
44 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
45 + }
46 +
47 + WSLC_TEST_METHOD(WSLCE2E_Network_Inspect_MissingNetworkName)
48 + {
49 + auto result = RunWslc(L"network inspect");
50 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Required argument not provided: 'network-name'\r\n", .ExitCode = 1});
51 + }
52 +
53 + WSLC_TEST_METHOD(WSLCE2E_Network_Inspect_Success)
54 + {
55 + auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName1));
56 + result.Verify({.Stderr = L"", .ExitCode = 0});
57 + VERIFY_ARE_EQUAL(TestNetworkName1, result.GetStdoutOneLine());
58 +
59 + result = RunWslc(std::format(L"network inspect {}", TestNetworkName1));
60 + result.Verify({.Stderr = L"", .ExitCode = 0});
61 + auto inspectData = wsl::shared::FromJson<std::vector<wsl::windows::common::wslc_schema::Network>>(result.Stdout.value().c_str());
62 + VERIFY_ARE_EQUAL(1u, inspectData.size());
63 + auto inspect = inspectData[0];
64 +
65 + VERIFY_ARE_EQUAL(WideToMultiByte(TestNetworkName1), inspect.Name);
66 + VERIFY_ARE_EQUAL("bridge", inspect.Driver);
67 + }
68 +
69 + WSLC_TEST_METHOD(WSLCE2E_Network_InspectMultiple_Success)
70 + {
71 + auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName1));
72 + result.Verify({.Stderr = L"", .ExitCode = 0});
73 + VERIFY_ARE_EQUAL(TestNetworkName1, result.GetStdoutOneLine());
74 + result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName2));
75 + result.Verify({.Stderr = L"", .ExitCode = 0});
76 + VERIFY_ARE_EQUAL(TestNetworkName2, result.GetStdoutOneLine());
77 +
78 + result = RunWslc(std::format(L"network inspect {} {}", TestNetworkName1, TestNetworkName2));
79 + result.Verify({.Stderr = L"", .ExitCode = 0});
80 + auto inspectData = wsl::shared::FromJson<std::vector<wsl::windows::common::wslc_schema::Network>>(result.Stdout.value().c_str());
81 + VERIFY_ARE_EQUAL(2u, inspectData.size());
82 +
83 + auto inspect1 = inspectData[0];
84 + VERIFY_ARE_EQUAL(WideToMultiByte(TestNetworkName1), inspect1.Name);
85 + VERIFY_ARE_EQUAL("bridge", inspect1.Driver);
86 +
87 + auto inspect2 = inspectData[1];
88 + VERIFY_ARE_EQUAL(WideToMultiByte(TestNetworkName2), inspect2.Name);
89 + VERIFY_ARE_EQUAL("bridge", inspect2.Driver);
90 + }
91 +
92 + WSLC_TEST_METHOD(WSLCE2E_Network_Inspect_NotFound)
93 + {
94 + auto result = RunWslc(std::format(L"network inspect {}", TestNetworkName1));
95 + result.Verify({.Stdout = L"[]\r\n", .Stderr = std::format(L"Network not found: '{}'\r\n", TestNetworkName1), .ExitCode = 1});
96 + }
97 +
98 + WSLC_TEST_METHOD(WSLCE2E_Network_Inspect_MixedFoundNotFound)
99 + {
100 + auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName1));
101 + result.Verify({.Stderr = L"", .ExitCode = 0});
102 + VERIFY_ARE_EQUAL(TestNetworkName1, result.GetStdoutOneLine());
103 +
104 + result = RunWslc(std::format(L"network inspect {} {}", TestNetworkName1, TestNetworkName2));
105 + result.Verify({.Stderr = std::format(L"Network not found: '{}'\r\n", TestNetworkName2), .ExitCode = 1});
106 +
107 + auto inspectData = wsl::shared::FromJson<std::vector<wsl::windows::common::wslc_schema::Network>>(result.Stdout.value().c_str());
108 + VERIFY_ARE_EQUAL(1u, inspectData.size());
109 + auto inspect = inspectData[0];
110 + VERIFY_ARE_EQUAL(WideToMultiByte(TestNetworkName1), inspect.Name);
111 + }
112 +
113 +private:
114 + const std::wstring TestNetworkName1 = L"wslc-e2e-network-inspect-1";
115 + const std::wstring TestNetworkName2 = L"wslc-e2e-network-inspect-2";
116 +
117 + std::wstring GetHelpMessage() const
118 + {
119 + std::wstringstream output;
120 + output << GetWslcHeader() //
121 + << GetDescription() //
122 + << GetUsage() //
123 + << GetAvailableCommands() //
124 + << GetAvailableOptions();
125 + return output.str();
126 + }
127 +
128 + std::wstring GetDescription() const
129 + {
130 + return std::format(L"{}\r\n\r\n", Localization::WSLCCLI_NetworkInspectLongDesc());
131 + }
132 +
133 + std::wstring GetUsage() const
134 + {
135 + return L"Usage: wslc network inspect [<options>] <network-name>\r\n\r\n";
136 + }
137 +
138 + std::wstring GetAvailableCommands() const
139 + {
140 + std::wstringstream commands;
141 + commands << L"The following arguments are available:\r\n" //
142 + << L" network-name Network name\r\n" //
143 + << L"\r\n";
144 + return commands.str();
145 + }
146 +
147 + std::wstring GetAvailableOptions() const
148 + {
149 + std::wstringstream options;
150 + options << L"The following options are available:\r\n" //
151 + << L" --session Specify the session to use\r\n" //
152 + << L" -?,--help Shows help about the selected command\r\n" //
153 + << L"\r\n";
154 + return options.str();
155 + }
156 +};
157 +} // namespace WSLCE2ETests
test/windows/wslc/e2e/WSLCE2ENetworkListTests.cpp new
+134
@@ -0,0 +1,134 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + WSLCE2ENetworkListTests.cpp
8 +
9 +Abstract:
10 +
11 + This file contains end-to-end tests for WSLC.
12 +--*/
13 +
14 +#include "precomp.h"
15 +#include "windows/Common.h"
16 +#include "WSLCExecutor.h"
17 +#include "WSLCE2EHelpers.h"
18 +
19 +namespace WSLCE2ETests {
20 +using namespace wsl::shared;
21 +using namespace wsl::shared::string;
22 +
23 +class WSLCE2ENetworkListTests
24 +{
25 + WSLC_TEST_CLASS(WSLCE2ENetworkListTests)
26 +
27 + TEST_METHOD_SETUP(MethodSetup)
28 + {
29 + EnsureNetworkDoesNotExist(TestNetworkName);
30 + EnsureNetworkDoesNotExist(TestNetworkName2);
31 + return true;
32 + }
33 +
34 + TEST_CLASS_CLEANUP(ClassCleanup)
35 + {
36 + EnsureNetworkDoesNotExist(TestNetworkName);
37 + EnsureNetworkDoesNotExist(TestNetworkName2);
38 + return true;
39 + }
40 +
41 + WSLC_TEST_METHOD(WSLCE2E_Network_List_HelpCommand)
42 + {
43 + auto result = RunWslc(L"network list --help");
44 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
45 + }
46 +
47 + WSLC_TEST_METHOD(WSLCE2E_Network_List_InvalidFormatOption)
48 + {
49 + auto result = RunWslc(L"network list --format invalid");
50 + result.Verify({.Stderr = L"Invalid format value: invalid is not a recognized format type. Supported format types are: json, table.\r\n", .ExitCode = 1});
51 + }
52 +
53 + WSLC_TEST_METHOD(WSLCE2E_Network_List_QuietOption_OutputsNamesOnly)
54 + {
55 + auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
56 + result.Verify({.Stderr = L"", .ExitCode = 0});
57 + result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName2));
58 + result.Verify({.Stderr = L"", .ExitCode = 0});
59 +
60 + result = RunWslc(L"network list --quiet");
61 + result.Verify({.Stderr = L"", .ExitCode = 0});
62 +
63 + auto lines = result.GetStdoutLines();
64 + VERIFY_ARE_NOT_EQUAL(lines.end(), std::find(lines.begin(), lines.end(), TestNetworkName));
65 + VERIFY_ARE_NOT_EQUAL(lines.end(), std::find(lines.begin(), lines.end(), TestNetworkName2));
66 + }
67 +
68 + WSLC_TEST_METHOD(WSLCE2E_Network_List_JsonFormat)
69 + {
70 + auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
71 + result.Verify({.Stderr = L"", .ExitCode = 0});
72 + result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName2));
73 + result.Verify({.Stderr = L"", .ExitCode = 0});
74 +
75 + result = RunWslc(L"network list --format json");
76 + result.Verify({.Stderr = L"", .ExitCode = 0});
77 +
78 + auto networks = FromJson<std::vector<WSLCNetworkInformation>>(result.Stdout.value().c_str());
79 + VERIFY_ARE_EQUAL(2U, networks.size());
80 +
81 + std::vector<std::string> names;
82 + names.reserve(networks.size());
83 + for (const auto& network : networks)
84 + {
85 + names.push_back(network.Name);
86 + }
87 +
88 + VERIFY_ARE_NOT_EQUAL(names.end(), std::find(names.begin(), names.end(), WideToMultiByte(TestNetworkName)));
89 + VERIFY_ARE_NOT_EQUAL(names.end(), std::find(names.begin(), names.end(), WideToMultiByte(TestNetworkName2)));
90 + }
91 +
92 +private:
93 + const std::wstring TestNetworkName = L"wslc-e2e-network-list";
94 + const std::wstring TestNetworkName2 = L"wslc-e2e-network-list-2";
95 +
96 + std::wstring GetHelpMessage() const
97 + {
98 + std::wstringstream output;
99 + output << GetWslcHeader() //
100 + << GetDescription() //
101 + << GetUsage() //
102 + << GetAvailableCommandAliases() //
103 + << GetAvailableOptions();
104 + return output.str();
105 + }
106 +
107 + std::wstring GetDescription() const
108 + {
109 + return std::format(L"{}\r\n\r\n", Localization::WSLCCLI_NetworkListLongDesc());
110 + }
111 +
112 + std::wstring GetUsage() const
113 + {
114 + return L"Usage: wslc network list [<options>]\r\n\r\n";
115 + }
116 +
117 + std::wstring GetAvailableCommandAliases() const
118 + {
119 + return L"The following command aliases are available: ls\r\n\r\n";
120 + }
121 +
122 + std::wstring GetAvailableOptions() const
123 + {
124 + std::wstringstream options;
125 + options << L"The following options are available:\r\n" //
126 + << L" --format Output formatting (json or table) (Default: table)\r\n" //
127 + << L" -q,--quiet Outputs the network names only\r\n" //
128 + << L" --session Specify the session to use\r\n" //
129 + << L" -?,--help Shows help about the selected command\r\n" //
130 + << L"\r\n";
131 + return options.str();
132 + }
133 +};
134 +} // namespace WSLCE2ETests
test/windows/wslc/e2e/WSLCE2ENetworkRemoveTests.cpp new
+152
@@ -0,0 +1,152 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + WSLCE2ENetworkRemoveTests.cpp
8 +
9 +Abstract:
10 +
11 + This file contains end-to-end tests for WSLC.
12 +--*/
13 +
14 +#include "precomp.h"
15 +#include "windows/Common.h"
16 +#include "WSLCExecutor.h"
17 +#include "WSLCE2EHelpers.h"
18 +
19 +namespace WSLCE2ETests {
20 +using namespace wsl::shared;
21 +
22 +class WSLCE2ENetworkRemoveTests
23 +{
24 + WSLC_TEST_CLASS(WSLCE2ENetworkRemoveTests)
25 +
26 + TEST_METHOD_SETUP(MethodSetup)
27 + {
28 + EnsureNetworkDoesNotExist(TestNetworkName);
29 + EnsureNetworkDoesNotExist(TestNetworkName2);
30 + return true;
31 + }
32 +
33 + TEST_CLASS_CLEANUP(ClassCleanup)
34 + {
35 + EnsureNetworkDoesNotExist(TestNetworkName);
36 + EnsureNetworkDoesNotExist(TestNetworkName2);
37 + return true;
38 + }
39 +
40 + WSLC_TEST_METHOD(WSLCE2E_Network_Remove_HelpCommand)
41 + {
42 + auto result = RunWslc(L"network remove --help");
43 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
44 + }
45 +
46 + WSLC_TEST_METHOD(WSLCE2E_Network_Remove_MissingNetworkName)
47 + {
48 + auto result = RunWslc(L"network remove");
49 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Required argument not provided: 'network-name'\r\n", .ExitCode = 1});
50 + }
51 +
52 + WSLC_TEST_METHOD(WSLCE2E_Network_Remove_Valid)
53 + {
54 + auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
55 + result.Verify({.Stderr = L"", .ExitCode = 0});
56 +
57 + VerifyNetworkIsListed(TestNetworkName);
58 +
59 + result = RunWslc(std::format(L"network remove {}", TestNetworkName));
60 + result.Verify({.Stdout = std::format(L"{}\r\n", TestNetworkName), .Stderr = L"", .ExitCode = 0});
61 +
62 + VerifyNetworkIsNotListed(TestNetworkName);
63 + }
64 +
65 + WSLC_TEST_METHOD(WSLCE2E_Network_Remove_Multiple_Valid)
66 + {
67 + auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
68 + result.Verify({.Stderr = L"", .ExitCode = 0});
69 + result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName2));
70 + result.Verify({.Stderr = L"", .ExitCode = 0});
71 +
72 + VerifyNetworkIsListed(TestNetworkName);
73 + VerifyNetworkIsListed(TestNetworkName2);
74 +
75 + result = RunWslc(std::format(L"network remove {} {}", TestNetworkName, TestNetworkName2));
76 + result.Verify({.Stderr = L"", .ExitCode = 0});
77 +
78 + VerifyNetworkIsNotListed(TestNetworkName);
79 + VerifyNetworkIsNotListed(TestNetworkName2);
80 + }
81 +
82 + WSLC_TEST_METHOD(WSLCE2E_Network_Remove_NotFound)
83 + {
84 + auto result = RunWslc(std::format(L"network remove {}", TestNetworkName));
85 + result.Verify({.Stdout = L"", .Stderr = std::format(L"Network not found: '{}'\r\n", TestNetworkName), .ExitCode = 1});
86 + }
87 +
88 + WSLC_TEST_METHOD(WSLCE2E_Network_Remove_MixedFoundNotFound)
89 + {
90 + auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
91 + result.Verify({.Stderr = L"", .ExitCode = 0});
92 + VerifyNetworkIsListed(TestNetworkName);
93 +
94 + result = RunWslc(std::format(L"network remove {} {}", TestNetworkName, TestNetworkName2));
95 + result.Verify(
96 + {.Stdout = std::format(L"{}\r\n", TestNetworkName),
97 + .Stderr = std::format(L"Network not found: '{}'\r\n", TestNetworkName2),
98 + .ExitCode = 1});
99 + VerifyNetworkIsNotListed(TestNetworkName);
100 + }
101 +
102 +private:
103 + const std::wstring TestNetworkName = L"wslc-e2e-network-remove";
104 + const std::wstring TestNetworkName2 = L"wslc-e2e-network-remove-2";
105 +
106 + std::wstring GetHelpMessage() const
107 + {
108 + std::wstringstream output;
109 + output << GetWslcHeader() //
110 + << GetDescription() //
111 + << GetUsage() //
112 + << GetAvailableCommandAliases() //
113 + << GetAvailableCommands() //
114 + << GetAvailableOptions();
115 + return output.str();
116 + }
117 +
118 + std::wstring GetDescription() const
119 + {
120 + return Localization::WSLCCLI_NetworkRemoveLongDesc() + L"\r\n\r\n";
121 + }
122 +
123 + std::wstring GetUsage() const
124 + {
125 + return L"Usage: wslc network remove [<options>] <network-name>\r\n\r\n";
126 + }
127 +
128 + std::wstring GetAvailableCommandAliases() const
129 + {
130 + return L"The following command aliases are available: delete rm\r\n\r\n";
131 + }
132 +
133 + std::wstring GetAvailableCommands() const
134 + {
135 + std::wstringstream commands;
136 + commands << L"The following arguments are available:\r\n" //
137 + << L" network-name Network name\r\n" //
138 + << L"\r\n";
139 + return commands.str();
140 + }
141 +
142 + std::wstring GetAvailableOptions() const
143 + {
144 + std::wstringstream options;
145 + options << L"The following options are available:\r\n" //
146 + << L" --session Specify the session to use\r\n" //
147 + << L" -?,--help Shows help about the selected command\r\n" //
148 + << L"\r\n";
149 + return options.str();
150 + }
151 +};
152 +} // namespace WSLCE2ETests
test/windows/wslc/e2e/WSLCE2ENetworkTests.cpp new
+101
@@ -0,0 +1,101 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + WSLCE2ENetworkTests.cpp
8 +
9 +Abstract:
10 +
11 + This file contains end-to-end tests for WSLC.
12 +--*/
13 +
14 +#include "precomp.h"
15 +#include "windows/Common.h"
16 +#include "WSLCExecutor.h"
17 +#include "WSLCE2EHelpers.h"
18 +#include "Argument.h"
19 +
20 +namespace WSLCE2ETests {
21 +using namespace wsl::shared;
22 +
23 +class WSLCE2ENetworkTests
24 +{
25 + WSLC_TEST_CLASS(WSLCE2ENetworkTests)
26 +
27 + WSLC_TEST_METHOD(WSLCE2E_Network_HelpCommand)
28 + {
29 + auto result = RunWslc(L"network --help");
30 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
31 + }
32 +
33 + WSLC_TEST_METHOD(WSLCE2E_Network_NoSubcommand_ShowsHelp)
34 + {
35 + auto result = RunWslc(L"network");
36 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
37 + }
38 +
39 + WSLC_TEST_METHOD(WSLCE2E_Network_InvalidCommand_DisplaysErrorMessage)
40 + {
41 + auto result = RunWslc(L"network INVALID_CMD");
42 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Unrecognized command: 'INVALID_CMD'\r\n", .ExitCode = 1});
43 + }
44 +
45 +private:
46 + std::wstring GetHelpMessage() const
47 + {
48 + std::wstringstream output;
49 + output << GetWslcHeader() //
50 + << GetDescription() //
51 + << GetUsage() //
52 + << GetAvailableCommands() //
53 + << GetAvailableOptions();
54 + return output.str();
55 + }
56 +
57 + std::wstring GetDescription() const
58 + {
59 + return Localization::WSLCCLI_NetworkCommandLongDesc() + L"\r\n\r\n";
60 + }
61 +
62 + std::wstring GetUsage() const
63 + {
64 + return L"Usage: wslc network [<command>] [<options>]\r\n\r\n";
65 + }
66 +
67 + std::wstring GetAvailableCommands() const
68 + {
69 + std::vector<std::pair<std::wstring_view, std::wstring>> entries = {
70 + {L"create", Localization::WSLCCLI_NetworkCreateDesc()},
71 + {L"remove", Localization::WSLCCLI_NetworkRemoveDesc()},
72 + {L"inspect", Localization::WSLCCLI_NetworkInspectDesc()},
73 + {L"list", Localization::WSLCCLI_NetworkListDesc()},
74 + };
75 +
76 + size_t maxLen = 0;
77 + for (const auto& [name, _] : entries)
78 + {
79 + maxLen = (std::max)(maxLen, name.size());
80 + }
81 +
82 + std::wstringstream commands;
83 + commands << Localization::WSLCCLI_AvailableSubcommands() << L"\r\n";
84 + for (const auto& [name, desc] : entries)
85 + {
86 + commands << L" " << name << std::wstring(maxLen - name.size() + 2, L' ') << desc << L"\r\n";
87 + }
88 + commands << L"\r\n" << Localization::WSLCCLI_HelpForDetails() << L" [" << WSLC_CLI_HELP_ARG_STRING << L"]\r\n\r\n";
89 + return commands.str();
90 + }
91 +
92 + std::wstring GetAvailableOptions() const
93 + {
94 + std::wstringstream options;
95 + options << L"The following options are available:\r\n"
96 + << L" -?,--help Shows help about the selected command\r\n"
97 + << L"\r\n";
98 + return options.str();
99 + }
100 +};
101 +} // namespace WSLCE2ETests