master
cpp 340 lines 14.9 KB
Raw
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 "TestImageRegistry.h"
19 #include "Argument.h"
20
21 namespace WSLCE2ETests {
22 using namespace wsl::shared;
23
24 class WSLCE2ENetworkTests
25 {
26 WSLC_TEST_CLASS(WSLCE2ENetworkTests)
27
28 TEST_CLASS_SETUP(ClassSetup)
29 {
30 TestImageRegistry::Instance().EnsureLoaded(DebianImage);
31 return true;
32 }
33
34 TEST_CLASS_CLEANUP(ClassCleanup)
35 {
36 EnsureContainerDoesNotExist(WslcContainerName);
37 EnsureContainerDoesNotExist(WslcTargetContainerName);
38 EnsureNetworkDoesNotExist(TestNetworkName);
39 return true;
40 }
41
42 TEST_METHOD_SETUP(TestMethodSetup)
43 {
44 EnsureContainerDoesNotExist(WslcContainerName);
45 EnsureContainerDoesNotExist(WslcTargetContainerName);
46 EnsureNetworkDoesNotExist(TestNetworkName);
47 return true;
48 }
49
50 WSLC_TEST_METHOD(WSLCE2E_Network_HelpCommand)
51 {
52 auto result = RunWslc(L"network --help");
53 result.Verify({.Stderr = L"", .ExitCode = 0});
54 VERIFY_IS_FALSE(result.Stdout.value().empty());
55 }
56
57 WSLC_TEST_METHOD(WSLCE2E_Network_NoSubcommand_ShowsHelp)
58 {
59 auto result = RunWslc(L"network");
60 result.Verify({.Stderr = L"", .ExitCode = 0});
61 VERIFY_IS_FALSE(result.Stdout.value().empty());
62 }
63
64 WSLC_TEST_METHOD(WSLCE2E_Network_InvalidCommand_DisplaysErrorMessage)
65 {
66 auto result = RunWslc(L"network INVALID_CMD");
67 result.Verify({.Stdout = L"", .ExitCode = 1});
68 VERIFY_IS_TRUE(result.StderrContainsSubstring(L"Unrecognized command: 'INVALID_CMD'"));
69 }
70
71 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_HelpCommand)
72 {
73 auto result = RunWslc(L"network connect --help");
74 result.Verify({.Stderr = L"", .ExitCode = 0});
75 VERIFY_IS_FALSE(result.Stdout.value().empty());
76 }
77
78 WSLC_TEST_METHOD(WSLCE2E_Network_Disconnect_HelpCommand)
79 {
80 auto result = RunWslc(L"network disconnect --help");
81 result.Verify({.Stderr = L"", .ExitCode = 0});
82 VERIFY_IS_FALSE(result.Stdout.value().empty());
83 }
84
85 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_MissingNetworkName)
86 {
87 auto result = RunWslc(L"network connect");
88 result.Verify({.Stdout = L"", .ExitCode = 1});
89 VERIFY_IS_TRUE(result.StderrContainsSubstring(L"Required argument not provided: 'network-name'"));
90 }
91
92 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_MissingContainerId)
93 {
94 auto result = RunWslc(std::format(L"network connect {}", TestNetworkName));
95 result.Verify({.Stdout = L"", .ExitCode = 1});
96 VERIFY_IS_TRUE(result.StderrContainsSubstring(L"Required argument not provided: 'container-id'"));
97 }
98
99 WSLC_TEST_METHOD(WSLCE2E_Network_Disconnect_MissingNetworkName)
100 {
101 auto result = RunWslc(L"network disconnect");
102 result.Verify({.Stdout = L"", .ExitCode = 1});
103 VERIFY_IS_TRUE(result.StderrContainsSubstring(L"Required argument not provided: 'network-name'"));
104 }
105
106 WSLC_TEST_METHOD(WSLCE2E_Network_Disconnect_MissingContainerId)
107 {
108 auto result = RunWslc(std::format(L"network disconnect {}", TestNetworkName));
109 result.Verify({.Stdout = L"", .ExitCode = 1});
110 VERIFY_IS_TRUE(result.StderrContainsSubstring(L"Required argument not provided: 'container-id'"));
111 }
112
113 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_Valid)
114 {
115 auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
116 result.Verify({.Stderr = L"", .ExitCode = 0});
117
118 result = RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
119 result.Verify({.Stderr = L"", .ExitCode = 0});
120
121 result = RunWslc(std::format(L"network connect {} {}", TestNetworkName, WslcContainerName));
122 result.Verify({.Stderr = L"", .ExitCode = 0});
123
124 const auto inspect = InspectContainer(WslcContainerName);
125 VERIFY_IS_TRUE(inspect.NetworkSettings.Networks.contains(string::WideToMultiByte(TestNetworkName)));
126 }
127
128 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_UnknownNetwork)
129 {
130 auto result = RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
131 result.Verify({.Stderr = L"", .ExitCode = 0});
132
133 result = RunWslc(std::format(L"network connect {} {}", TestNetworkName, WslcContainerName));
134 result.Verify(
135 {.Stderr = FormatErrorMessage(std::format(L"Network not found: '{}'", TestNetworkName), L"WSLC_E_NETWORK_NOT_FOUND"),
136 .ExitCode = 1});
137 }
138
139 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_UnknownContainer)
140 {
141 auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
142 result.Verify({.Stderr = L"", .ExitCode = 0});
143
144 result = RunWslc(std::format(L"network connect {} {}", TestNetworkName, WslcContainerName));
145 result.Verify(
146 {.Stderr =
147 FormatErrorMessage(std::format(L"Container '{}' not found.", WslcContainerName), L"WSLC_E_CONTAINER_NOT_FOUND"),
148 .ExitCode = 1});
149 }
150
151 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_NoneMode_Rejected)
152 {
153 auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
154 result.Verify({.Stderr = L"", .ExitCode = 0});
155
156 result = RunWslc(
157 std::format(L"container run -d --network none --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
158 result.Verify({.Stderr = L"", .ExitCode = 0});
159
160 result = RunWslc(std::format(L"network connect {} {}", TestNetworkName, WslcContainerName));
161 VERIFY_ARE_EQUAL(1u, result.ExitCode.value());
162 VERIFY_IS_TRUE(result.Stderr.has_value());
163 VerifyPatternMatch(
164 string::WideToMultiByte(result.Stderr.value()),
165 string::WideToMultiByte(
166 FormatErrorMessage(L"*does not support connecting or disconnecting additional networks*", L"*")));
167 }
168
169 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_AlreadyConnected_DockerErrorPropagated)
170 {
171 auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
172 result.Verify({.Stderr = L"", .ExitCode = 0});
173
174 result = RunWslc(std::format(
175 L"container run -d --network {} --name {} {} sleep infinity", TestNetworkName, WslcContainerName, DebianImage.NameAndTag()));
176 result.Verify({.Stderr = L"", .ExitCode = 0});
177
178 result = RunWslc(std::format(L"network connect {} {}", TestNetworkName, WslcContainerName));
179 VERIFY_ARE_EQUAL(1u, result.ExitCode.value());
180 VERIFY_IS_TRUE(result.Stderr.has_value());
181 VerifyPatternMatch(
182 string::WideToMultiByte(result.Stderr.value()),
183 string::WideToMultiByte(FormatErrorMessage(L"*already exists*", L"*")));
184 }
185
186 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_WithEndpointFlags_RoundTrips)
187 {
188 const std::wstring subnet = L"172.72.0.0/16";
189 const std::wstring ipAddress = L"172.72.0.42";
190 const std::wstring alias1 = L"primary-alias";
191 const std::wstring alias2 = L"secondary-alias";
192 const std::wstring linkLocal = L"169.254.11.5";
193
194 auto result = RunWslc(std::format(L"network create --driver bridge --subnet {} {}", subnet, TestNetworkName));
195 result.Verify({.Stderr = L"", .ExitCode = 0});
196
197 result = RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
198 result.Verify({.Stderr = L"", .ExitCode = 0});
199
200 result = RunWslc(std::format(
201 L"network connect --network-alias {} --network-alias {} --ip {} --link-local-ip {} {} {}", alias1, alias2, ipAddress, linkLocal, TestNetworkName, WslcContainerName));
202 result.Verify({.Stderr = L"", .ExitCode = 0});
203
204 const auto inspect = InspectContainer(WslcContainerName);
205 const auto networkKey = string::WideToMultiByte(TestNetworkName);
206 VERIFY_IS_TRUE(inspect.NetworkSettings.Networks.contains(networkKey));
207 const auto& endpoint = inspect.NetworkSettings.Networks.at(networkKey);
208 VERIFY_ARE_EQUAL(string::WideToMultiByte(ipAddress), endpoint.IPAddress);
209 VERIFY_IS_TRUE(endpoint.IPAMConfig.has_value());
210 VERIFY_ARE_EQUAL(string::WideToMultiByte(ipAddress), endpoint.IPAMConfig->IPv4Address);
211 VERIFY_IS_TRUE(std::ranges::find(endpoint.Aliases, string::WideToMultiByte(alias1)) != endpoint.Aliases.end());
212 VERIFY_IS_TRUE(std::ranges::find(endpoint.Aliases, string::WideToMultiByte(alias2)) != endpoint.Aliases.end());
213 VERIFY_IS_TRUE(
214 std::ranges::find(endpoint.IPAMConfig->LinkLocalIPs, string::WideToMultiByte(linkLocal)) !=
215 endpoint.IPAMConfig->LinkLocalIPs.end());
216 }
217
218 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_InvalidIp_Rejected)
219 {
220 const std::wstring badIp = L"not-an-ip";
221
222 auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
223 result.Verify({.Stderr = L"", .ExitCode = 0});
224
225 result = RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
226 result.Verify({.Stderr = L"", .ExitCode = 0});
227
228 result = RunWslc(std::format(L"network connect --ip {} {} {}", badIp, TestNetworkName, WslcContainerName));
229 VERIFY_ARE_EQUAL(1u, result.ExitCode.value());
230 VERIFY_IS_TRUE(result.Stderr.has_value());
231 VerifyPatternMatch(
232 string::WideToMultiByte(result.Stderr.value()), std::format("*Invalid IP address '{}'*", string::WideToMultiByte(badIp)));
233 }
234
235 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_DriverOpt_RoundTrips)
236 {
237 const std::wstring driverOptKey = L"com.docker.network.endpoint.custom";
238 const std::wstring driverOptValue = L"verify";
239
240 auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
241 result.Verify({.Stderr = L"", .ExitCode = 0});
242
243 result = RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
244 result.Verify({.Stderr = L"", .ExitCode = 0});
245
246 result = RunWslc(std::format(L"network connect --driver-opt {}={} {} {}", driverOptKey, driverOptValue, TestNetworkName, WslcContainerName));
247 result.Verify({.Stderr = L"", .ExitCode = 0});
248
249 const auto inspect = InspectContainer(WslcContainerName);
250 const auto networkKey = string::WideToMultiByte(TestNetworkName);
251 VERIFY_IS_TRUE(inspect.NetworkSettings.Networks.contains(networkKey));
252 const auto& endpoint = inspect.NetworkSettings.Networks.at(networkKey);
253 const auto keyUtf8 = string::WideToMultiByte(driverOptKey);
254 const auto valueUtf8 = string::WideToMultiByte(driverOptValue);
255 const auto driverOptIt = endpoint.DriverOpts.find(keyUtf8);
256 VERIFY_IS_TRUE(driverOptIt != endpoint.DriverOpts.end());
257 VERIFY_ARE_EQUAL(valueUtf8, driverOptIt->second);
258 }
259
260 WSLC_TEST_METHOD(WSLCE2E_Network_Connect_Link_RoundTrips)
261 {
262 const std::wstring targetAlias = L"db";
263
264 auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
265 result.Verify({.Stderr = L"", .ExitCode = 0});
266
267 result = RunWslc(std::format(
268 L"container run -d --network {} --network-alias {} --name {} {} sleep infinity",
269 TestNetworkName,
270 targetAlias,
271 WslcTargetContainerName,
272 DebianImage.NameAndTag()));
273 result.Verify({.Stderr = L"", .ExitCode = 0});
274
275 result = RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
276 result.Verify({.Stderr = L"", .ExitCode = 0});
277
278 const std::wstring linkEntry = std::format(L"{}:{}", WslcTargetContainerName, targetAlias);
279 result = RunWslc(std::format(L"network connect --link {} {} {}", linkEntry, TestNetworkName, WslcContainerName));
280 result.Verify({.Stderr = L"", .ExitCode = 0});
281
282 const auto inspect = InspectContainer(WslcContainerName);
283 const auto networkKey = string::WideToMultiByte(TestNetworkName);
284 VERIFY_IS_TRUE(inspect.NetworkSettings.Networks.contains(networkKey));
285 const auto& endpoint = inspect.NetworkSettings.Networks.at(networkKey);
286 const auto linkEntryUtf8 = string::WideToMultiByte(linkEntry);
287 VERIFY_IS_TRUE(std::ranges::find(endpoint.Links, linkEntryUtf8) != endpoint.Links.end());
288 }
289
290 WSLC_TEST_METHOD(WSLCE2E_Network_Disconnect_Valid)
291 {
292 auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
293 result.Verify({.Stderr = L"", .ExitCode = 0});
294
295 result = RunWslc(std::format(
296 L"container run -d --network {} --name {} {} sleep infinity", TestNetworkName, WslcContainerName, DebianImage.NameAndTag()));
297 result.Verify({.Stderr = L"", .ExitCode = 0});
298
299 {
300 const auto inspect = InspectContainer(WslcContainerName);
301 VERIFY_IS_TRUE(inspect.NetworkSettings.Networks.contains(string::WideToMultiByte(TestNetworkName)));
302 }
303
304 result = RunWslc(std::format(L"network disconnect {} {}", TestNetworkName, WslcContainerName));
305 result.Verify({.Stderr = L"", .ExitCode = 0});
306
307 const auto inspect = InspectContainer(WslcContainerName);
308 VERIFY_IS_FALSE(inspect.NetworkSettings.Networks.contains(string::WideToMultiByte(TestNetworkName)));
309 }
310
311 WSLC_TEST_METHOD(WSLCE2E_Network_Disconnect_UnknownNetwork)
312 {
313 auto result = RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
314 result.Verify({.Stderr = L"", .ExitCode = 0});
315
316 result = RunWslc(std::format(L"network disconnect {} {}", TestNetworkName, WslcContainerName));
317 result.Verify(
318 {.Stderr = FormatErrorMessage(std::format(L"Network not found: '{}'", TestNetworkName), L"WSLC_E_NETWORK_NOT_FOUND"),
319 .ExitCode = 1});
320 }
321
322 WSLC_TEST_METHOD(WSLCE2E_Network_Disconnect_UnknownContainer)
323 {
324 auto result = RunWslc(std::format(L"network create --driver bridge {}", TestNetworkName));
325 result.Verify({.Stderr = L"", .ExitCode = 0});
326
327 result = RunWslc(std::format(L"network disconnect {} {}", TestNetworkName, WslcContainerName));
328 result.Verify(
329 {.Stderr =
330 FormatErrorMessage(std::format(L"Container '{}' not found.", WslcContainerName), L"WSLC_E_CONTAINER_NOT_FOUND"),
331 .ExitCode = 1});
332 }
333
334 private:
335 const std::wstring WslcContainerName = L"wslc-e2e-network-connect-container";
336 const std::wstring WslcTargetContainerName = L"wslc-e2e-network-connect-target";
337 const std::wstring TestNetworkName = L"wslc-e2e-network-connect";
338 const TestImage& DebianImage = DebianTestImage();
339 };
340 } // namespace WSLCE2ETests