Match docker's keyset for wslc volume list --format json (#41413)
* Match docker's keyset for wslc volume list --format json Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ggarzia-MSFT committed
Aug 24, 2026 at 13:31 UTC
6a19064c08f109a4e280322ae07d043416ae987b
20 files changed
+171
-86
src/shared/inc/JsonUtils.h
-20
@@ -185,24 +185,4 @@ struct adl_serializer<wsl::shared::string::MacAddress>
185
}
186
};
187
188
-#ifdef WIN32
189
-template <>
190
-struct adl_serializer<WSLCVolumeInformation>
191
-{
192
- static void to_json(json& j, const WSLCVolumeInformation& volume)
193
- {
194
- j = json{{"Name", std::string(volume.Name)}, {"Driver", std::string(volume.Driver)}};
195
- }
196
-
197
- static void from_json(const json& j, WSLCVolumeInformation& volume)
198
- {
199
- std::string name = j.at("Name").get<std::string>();
200
- std::string driver = j.at("Driver").get<std::string>();
201
-
202
- strncpy_s(volume.Name, sizeof(volume.Name), name.c_str(), _TRUNCATE);
203
- strncpy_s(volume.Driver, sizeof(volume.Driver), driver.c_str(), _TRUNCATE);
204
- }
205
-};
206
-#endif
207
-
188
} // namespace nlohmann
\ No newline at end of file
src/windows/inc/wslc_schema.h
+13
@@ -335,4 +335,17 @@ struct NetworkListEntry
335
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(NetworkListEntry, Id, Name, Driver, Scope, Created, EnableIPv4, EnableIPv6, Internal, Labels);
336
};
337
338
+// The volume properties carried from the session to the CLI for "volume list". Values keep their
339
+// native types; the CLI renders the string output.
340
+struct VolumeListEntry
341
+{
342
+ std::string Name;
343
+ std::string Driver;
344
+ std::string Mountpoint;
345
+ std::string Scope;
346
+ std::map<std::string, std::string> Labels;
347
+
348
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VolumeListEntry, Name, Driver, Mountpoint, Scope, Labels);
349
+};
350
+
351
} // namespace wsl::windows::common::wslc_schema
src/windows/service/inc/wslc.idl
+1
-1
@@ -758,7 +758,7 @@ interface IWSLCSession : IUnknown
758
// Volume management.
759
HRESULT CreateVolume([in] const WSLCVolumeOptions* Options, [out] WSLCVolumeInformation* VolumeInfo);
760
HRESULT DeleteVolume([in] LPCSTR Name);
761
- HRESULT ListVolumes([in, unique, size_is(FiltersCount)] const WSLCFilter* Filters, [in] ULONG FiltersCount, [out, size_is(, *Count)] WSLCVolumeInformation** Volumes, [out] ULONG* Count);
761
+ HRESULT ListVolumes([in, unique, size_is(FiltersCount)] const WSLCFilter* Filters, [in] ULONG FiltersCount, [out] LPSTR* Output);
762
HRESULT InspectVolume([in] LPCSTR Name, [out] LPSTR* Output);
763
764
HRESULT Authenticate([in] LPCSTR ServerAddress, [in] LPCSTR Username, [in] LPCSTR Password, [out] LPSTR* IdentityToken);
src/windows/wslc/core/ExecutionContextData.h
+1
-1
@@ -56,7 +56,7 @@ namespace details {
56
DEFINE_DATA_MAPPING(Containers, std::vector<wsl::windows::wslc::models::ContainerInformation>);
57
DEFINE_DATA_MAPPING(ContainerOptions, wsl::windows::wslc::models::ContainerOptions);
58
DEFINE_DATA_MAPPING(Images, std::vector<wsl::windows::wslc::models::ImageInformation>);
59
- DEFINE_DATA_MAPPING(Volumes, std::vector<WSLCVolumeInformation>);
59
+ DEFINE_DATA_MAPPING(Volumes, std::vector<wsl::windows::common::wslc_schema::VolumeListEntry>);
60
DEFINE_DATA_MAPPING(Networks, std::vector<wsl::windows::common::wslc_schema::NetworkListEntry>);
61
DEFINE_DATA_MAPPING(NetworkEndpointOptions, wsl::windows::wslc::models::NetworkEndpointOptions);
62
} // namespace details
src/windows/wslc/services/VolumeModel.h
+17
@@ -33,4 +33,21 @@ struct PruneVolumesResult
33
ULONGLONG SpaceReclaimed{};
34
};
35
36
+// The shape emitted by "volume list --format json"; every value is reported as a string.
37
+struct VolumeOutputInformation
38
+{
39
+ std::string Availability;
40
+ std::string Driver;
41
+ std::string Group;
42
+ std::string Labels;
43
+ std::string Links;
44
+ std::string Mountpoint;
45
+ std::string Name;
46
+ std::string Scope;
47
+ std::string Size;
48
+ std::string Status;
49
+
50
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VolumeOutputInformation, Availability, Driver, Group, Labels, Links, Mountpoint, Name, Scope, Size, Status);
51
+};
52
+
53
} // namespace wsl::windows::wslc::models
src/windows/wslc/services/VolumeService.cpp
+5
-12
@@ -60,7 +60,8 @@ void VolumeService::Delete(models::Session& session, const std::string& name)
60
THROW_IF_FAILED(session.Get()->DeleteVolume(name.c_str()));
61
}
62
63
-std::vector<WSLCVolumeInformation> VolumeService::List(models::Session& session, const std::vector<std::pair<std::string, std::string>>& filters)
63
+std::vector<wsl::windows::common::wslc_schema::VolumeListEntry> VolumeService::List(
64
+ models::Session& session, const std::vector<std::pair<std::string, std::string>>& filters)
65
{
66
std::vector<WSLCFilter> filterEntries;
67
filterEntries.reserve(filters.size());
@@ -69,19 +70,11 @@ std::vector<WSLCVolumeInformation> VolumeService::List(models::Session& session,
70
filterEntries.push_back({.Key = key.c_str(), .Value = value.c_str()});
71
}
72
72
- wil::unique_cotaskmem_array_ptr<WSLCVolumeInformation> rawVolumes;
73
- ULONG count = 0;
73
+ wil::unique_cotaskmem_ansistring output;
74
THROW_IF_FAILED(session.Get()->ListVolumes(
75
- filterEntries.empty() ? nullptr : filterEntries.data(), static_cast<ULONG>(filterEntries.size()), &rawVolumes, &count));
76
-
77
- std::vector<WSLCVolumeInformation> volumes;
78
- volumes.reserve(count);
79
- for (auto ptr = rawVolumes.get(), end = rawVolumes.get() + count; ptr != end; ++ptr)
80
- {
81
- volumes.push_back(*ptr);
82
- }
75
+ filterEntries.empty() ? nullptr : filterEntries.data(), static_cast<ULONG>(filterEntries.size()), &output));
76
84
- return volumes;
77
+ return FromJson<std::vector<wsl::windows::common::wslc_schema::VolumeListEntry>>(output.get());
78
}
79
80
wsl::windows::common::wslc_schema::InspectVolume VolumeService::Inspect(models::Session& session, const std::string& name)
src/windows/wslc/services/VolumeService.h
+2
-1
@@ -24,7 +24,8 @@ struct VolumeService
24
{
25
static WSLCVolumeInformation Create(models::Session& session, const models::CreateVolumeOptions& createOptions);
26
static void Delete(models::Session& session, const std::string& name);
27
- static std::vector<WSLCVolumeInformation> List(models::Session& session, const std::vector<std::pair<std::string, std::string>>& filters = {});
27
+ static std::vector<wsl::windows::common::wslc_schema::VolumeListEntry> List(
28
+ models::Session& session, const std::vector<std::pair<std::string, std::string>>& filters = {});
29
static wsl::windows::common::wslc_schema::InspectVolume Inspect(models::Session& session, const std::string& name);
30
static models::PruneVolumesResult Prune(
31
Terminal& terminal, models::Session& session, bool all, const std::vector<std::pair<std::string, std::string>>& filters = {});
src/windows/wslc/tasks/VolumeTasks.cpp
+36
-1
@@ -33,6 +33,41 @@ namespace wsl::windows::wslc::task {
33
34
constexpr uint32_t c_reclaimedSpacePrecision = 4;
35
36
+namespace {
37
+
38
+ // Reported for the fields that only carry a value when volume usage data or swarm cluster
39
+ // information is available, neither of which applies here.
40
+ constexpr std::string_view c_notAvailable = "N/A";
41
+
42
+ // Converts session volume entries into the all-string shape used for "volume list --format json".
43
+ VolumeOutputInformation ToVolumeOutput(const wslc_schema::VolumeListEntry& volume)
44
+ {
45
+ VolumeOutputInformation entry;
46
+ entry.Availability = c_notAvailable;
47
+ entry.Driver = volume.Driver;
48
+ entry.Group = c_notAvailable;
49
+ entry.Links = c_notAvailable;
50
+ entry.Mountpoint = volume.Mountpoint;
51
+ entry.Name = volume.Name;
52
+ entry.Scope = volume.Scope;
53
+ entry.Size = c_notAvailable;
54
+ entry.Status = c_notAvailable;
55
+
56
+ for (const auto& [key, value] : volume.Labels)
57
+ {
58
+ if (!entry.Labels.empty())
59
+ {
60
+ entry.Labels += ",";
61
+ }
62
+
63
+ entry.Labels += std::format("{}={}", key, value);
64
+ }
65
+
66
+ return entry;
67
+ }
68
+
69
+} // namespace
70
+
71
static bool TryInspectVolume(Terminal& terminal, Session& session, const std::string& volumeName, std::optional<wslc_schema::InspectVolume>& inspectData)
72
{
73
try
@@ -177,7 +212,7 @@ void ListVolumes(CLIExecutionContext& context)
212
{
213
for (const auto& volume : volumes)
214
{
180
- context.Terminal.Output(L"{}\n", ToJsonW(volume, c_jsonCompactIndent));
215
+ context.Terminal.Output(L"{}\n", ToJsonW(ToVolumeOutput(volume), c_jsonCompactIndent));
216
}
217
218
break;
src/windows/wslcsession/IWSLCVolume.h
+3
@@ -40,6 +40,9 @@ public:
40
// The user-specified labels on this volume (excludes the WSLC metadata label).
41
virtual const std::map<std::string, std::string>& Labels() const noexcept = 0;
42
43
+ // The path at which the volume is mounted inside the utility VM.
44
+ virtual const std::string& Mountpoint() const noexcept = 0;
45
+
46
// The status of the volume as {Code, Message}: S_OK with an empty message when the volume
47
// opened successfully and is usable, otherwise a failure HRESULT and a human-readable reason
48
// (e.g. the backing VHD is missing).
src/windows/wslcsession/WSLCGuestVolume.h
+4
@@ -69,6 +69,10 @@ public:
69
{
70
return m_labels;
71
}
72
+ const std::string& Mountpoint() const noexcept override
73
+ {
74
+ return m_mountpoint;
75
+ }
76
77
void Delete() override;
78
std::string Inspect() const override;
src/windows/wslcsession/WSLCSession.cpp
+5
-14
@@ -2776,16 +2776,14 @@ try
2776
}
2777
CATCH_RETURN();
2778
2779
-HRESULT WSLCSession::ListVolumes(const WSLCFilter* Filters, ULONG FiltersCount, WSLCVolumeInformation** Volumes, ULONG* Count)
2779
+HRESULT WSLCSession::ListVolumes(const WSLCFilter* Filters, ULONG FiltersCount, LPSTR* Output)
2780
try
2781
{
2782
WSLCExecutionContext context(this);
2783
2784
- RETURN_HR_IF_NULL(E_POINTER, Volumes);
2785
- RETURN_HR_IF_NULL(E_POINTER, Count);
2784
+ RETURN_HR_IF_NULL(E_POINTER, Output);
2785
2787
- *Volumes = nullptr;
2788
- *Count = 0;
2786
+ *Output = nullptr;
2787
2788
auto filters = wsl::windows::common::wslutil::ParseKeyMultiValuePairs(Filters, FiltersCount);
2789
@@ -2794,16 +2792,9 @@ try
2792
2793
auto volumeList = m_runtime.Volumes().ListVolumes(std::move(filters));
2794
2797
- if (volumeList.empty())
2798
- {
2799
- return S_OK;
2800
- }
2801
-
2802
- auto output = wil::make_unique_cotaskmem<WSLCVolumeInformation[]>(volumeList.size());
2803
- memcpy(output.get(), volumeList.data(), volumeList.size() * sizeof(WSLCVolumeInformation));
2795
+ std::string json = wsl::shared::ToJson(volumeList);
2796
+ *Output = wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(json.c_str()).release();
2797
2805
- *Count = static_cast<ULONG>(volumeList.size());
2806
- *Volumes = output.release();
2798
return S_OK;
2799
}
2800
CATCH_RETURN();
src/windows/wslcsession/WSLCSession.h
+1
-2
@@ -180,8 +180,7 @@ public:
180
IFACEMETHOD(CreateVolume)(_In_ const WSLCVolumeOptions* Options, _Out_ WSLCVolumeInformation* VolumeInfo) override;
181
IFACEMETHOD(DeleteVolume)(_In_ LPCSTR Name) override;
182
IFACEMETHOD(ListVolumes)
183
- (_In_reads_opt_(FiltersCount) const WSLCFilter* Filters, _In_ ULONG FiltersCount, _Out_ WSLCVolumeInformation** Volumes, _Out_ ULONG* Count)
184
- override;
183
+ (_In_reads_opt_(FiltersCount) const WSLCFilter* Filters, _In_ ULONG FiltersCount, _Out_ LPSTR* Output) override;
184
IFACEMETHOD(InspectVolume)(_In_ LPCSTR Name, _Out_ LPSTR* Output) override;
185
IFACEMETHOD(PruneVolumes)
186
(_In_reads_opt_(FiltersCount) const WSLCFilter* Filters,
src/windows/wslcsession/WSLCVhdVolume.h
+4
@@ -77,6 +77,10 @@ public:
77
{
78
return m_labels;
79
}
80
+ const std::string& Mountpoint() const noexcept override
81
+ {
82
+ return m_mountpoint;
83
+ }
84
85
std::pair<HRESULT, std::string> Status() const override
86
{
src/windows/wslcsession/WSLCVolumes.cpp
+10
-3
@@ -141,7 +141,7 @@ void WSLCVolumes::DeleteVolume(LPCSTR Name)
141
m_expectedEvents.emplace_back(Name, VolumeEvent::Destroy);
142
}
143
144
-std::vector<WSLCVolumeInformation> WSLCVolumes::ListVolumes(std::map<std::string, std::vector<std::string>>&& Filters) const
144
+std::vector<wsl::windows::common::wslc_schema::VolumeListEntry> WSLCVolumes::ListVolumes(std::map<std::string, std::vector<std::string>>&& Filters) const
145
{
146
// Pull the driver filter out and forward everything else to docker for filtering.
147
// Driver filter is special-cased because our driver concept doesn't map 1:1 to docker's.
@@ -169,7 +169,7 @@ std::vector<WSLCVolumeInformation> WSLCVolumes::ListVolumes(std::map<std::string
169
170
auto lock = m_lock.lock_shared();
171
172
- std::vector<WSLCVolumeInformation> result;
172
+ std::vector<wsl::windows::common::wslc_schema::VolumeListEntry> result;
173
result.reserve(dockerVolumeNames.size());
174
175
for (const auto& [name, vol] : m_volumes)
@@ -186,7 +186,14 @@ std::vector<WSLCVolumeInformation> WSLCVolumes::ListVolumes(std::map<std::string
186
continue;
187
}
188
189
- result.push_back(vol->GetVolumeInformation());
189
+ wsl::windows::common::wslc_schema::VolumeListEntry entry;
190
+ entry.Name = vol->Name();
191
+ entry.Driver = vol->Driver();
192
+ entry.Mountpoint = vol->Mountpoint();
193
+ entry.Scope = WSLCVolumeScope;
194
+ entry.Labels = vol->Labels();
195
+
196
+ result.push_back(std::move(entry));
197
}
198
199
return result;
src/windows/wslcsession/WSLCVolumes.h
+2
-1
@@ -18,6 +18,7 @@ Abstract:
18
#include "WSLCVolumeMetadata.h"
19
#include "DockerHTTPClient.h"
20
#include "DockerEventTracker.h"
21
+#include <wslc_schema.h>
22
23
namespace wsl::windows::service::wslc {
24
@@ -40,7 +41,7 @@ public:
41
42
void DeleteVolume(_In_ LPCSTR Name);
43
43
- std::vector<WSLCVolumeInformation> ListVolumes(std::map<std::string, std::vector<std::string>>&& Filters) const;
44
+ std::vector<wsl::windows::common::wslc_schema::VolumeListEntry> ListVolumes(std::map<std::string, std::vector<std::string>>&& Filters) const;
45
46
struct PruneVolumesResult
47
{
test/windows/WSLCTests.cpp
+20
-24
@@ -654,16 +654,18 @@ class WSLCTests
654
return std::move(deletedImages);
655
}
656
657
- std::set<std::string> ListVolumes(const std::vector<WSLCFilter>& Filters = {})
657
+ std::vector<wsl::windows::common::wslc_schema::VolumeListEntry> ListVolumeEntries(const std::vector<WSLCFilter>& Filters = {})
658
{
659
- const WSLCFilter* filtersPtr = Filters.empty() ? nullptr : Filters.data();
660
- const ULONG filtersCount = static_cast<ULONG>(Filters.size());
659
+ wil::unique_cotaskmem_ansistring output;
660
+ VERIFY_SUCCEEDED(m_defaultSession->ListVolumes(Filters.empty() ? nullptr : Filters.data(), static_cast<ULONG>(Filters.size()), &output));
661
662
- wil::unique_cotaskmem_array_ptr<WSLCVolumeInformation> volumes;
663
- VERIFY_SUCCEEDED(m_defaultSession->ListVolumes(filtersPtr, filtersCount, volumes.addressof(), volumes.size_address<ULONG>()));
662
+ return wsl::shared::FromJson<std::vector<wsl::windows::common::wslc_schema::VolumeListEntry>>(output.get());
663
+ }
664
665
+ std::set<std::string> ListVolumes(const std::vector<WSLCFilter>& Filters = {})
666
+ {
667
std::set<std::string> names;
666
- for (const auto& v : volumes)
668
+ for (const auto& v : ListVolumeEntries(Filters))
669
{
670
names.insert(v.Name);
671
}
@@ -5266,11 +5268,12 @@ class WSLCTests
5268
WSLCVolumeInformation volInfo{};
5269
VERIFY_SUCCEEDED(m_defaultSession->CreateVolume(&vhdOptions, &volInfo));
5270
5269
- wil::unique_cotaskmem_array_ptr<WSLCVolumeInformation> volumes;
5270
- VERIFY_SUCCEEDED(m_defaultSession->ListVolumes(nullptr, 0, volumes.addressof(), volumes.size_address<ULONG>()));
5271
+ auto volumes = ListVolumeEntries();
5272
VERIFY_ARE_EQUAL(1u, volumes.size());
5272
- VERIFY_ARE_EQUAL(std::string(volumes[0].Name), vhdVolumeName);
5273
- VERIFY_ARE_EQUAL(std::string(volumes[0].Driver), std::string("vhd"));
5273
+ VERIFY_ARE_EQUAL(volumes[0].Name, vhdVolumeName);
5274
+ VERIFY_ARE_EQUAL(volumes[0].Driver, std::string("vhd"));
5275
+ VERIFY_IS_FALSE(volumes[0].Mountpoint.empty());
5276
+ VERIFY_ARE_EQUAL(volumes[0].Scope, std::string("local"));
5277
5278
// Verify that a guest volume cannot be created with the same name as an existing vhd volume.
5279
WSLCVolumeOptions duplicateGuestOptions{};
@@ -5292,7 +5295,7 @@ class WSLCTests
5295
duplicateVhdOptions.DriverOptsCount = ARRAYSIZE(driverOpts);
5296
VERIFY_ARE_EQUAL(m_defaultSession->CreateVolume(&duplicateVhdOptions, &volInfo), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
5297
5295
- VERIFY_SUCCEEDED(m_defaultSession->ListVolumes(nullptr, 0, volumes.addressof(), volumes.size_address<ULONG>()));
5298
+ volumes = ListVolumeEntries();
5299
VERIFY_ARE_EQUAL(2u, volumes.size());
5300
5301
std::map<std::string, std::string> namesToDrivers;
@@ -5335,10 +5338,10 @@ class WSLCTests
5338
5339
// Delete the VHD volume and verify only the guest volume remains.
5340
VERIFY_SUCCEEDED(m_defaultSession->DeleteVolume(vhdVolumeName.c_str()));
5338
- VERIFY_SUCCEEDED(m_defaultSession->ListVolumes(nullptr, 0, volumes.addressof(), volumes.size_address<ULONG>()));
5341
+ volumes = ListVolumeEntries();
5342
VERIFY_ARE_EQUAL(1u, volumes.size());
5340
- VERIFY_ARE_EQUAL(std::string(volumes[0].Name), guestVolumeName);
5341
- VERIFY_ARE_EQUAL(std::string(volumes[0].Driver), std::string("guest"));
5343
+ VERIFY_ARE_EQUAL(volumes[0].Name, guestVolumeName);
5344
+ VERIFY_ARE_EQUAL(volumes[0].Driver, std::string("guest"));
5345
}
5346
5347
WSLC_TEST_METHOD(ListVolumesFilters)
@@ -5370,22 +5373,15 @@ class WSLCTests
5373
const WSLCFilter* filtersPtr = filters.empty() ? nullptr : filters.data();
5374
const ULONG filtersCount = static_cast<ULONG>(filters.size());
5375
5373
- wil::unique_cotaskmem_array_ptr<WSLCVolumeInformation> volumes;
5374
- VERIFY_ARE_EQUAL(
5375
- expected, m_defaultSession->ListVolumes(filtersPtr, filtersCount, volumes.addressof(), volumes.size_address<ULONG>()));
5376
+ wil::unique_cotaskmem_ansistring output;
5377
+ VERIFY_ARE_EQUAL(expected, m_defaultSession->ListVolumes(filtersPtr, filtersCount, &output));
5378
};
5379
5380
auto expectList = [&](const std::vector<std::string>& expected,
5381
const std::vector<WSLCFilter>& filters = {},
5382
const std::source_location& source = std::source_location::current()) {
5381
- const WSLCFilter* filtersPtr = filters.empty() ? nullptr : filters.data();
5382
- const ULONG filtersCount = static_cast<ULONG>(filters.size());
5383
-
5384
- wil::unique_cotaskmem_array_ptr<WSLCVolumeInformation> volumes;
5385
- VERIFY_SUCCEEDED(m_defaultSession->ListVolumes(filtersPtr, filtersCount, volumes.addressof(), volumes.size_address<ULONG>()));
5386
-
5383
std::vector<std::string> names;
5388
- for (const auto& v : volumes)
5384
+ for (const auto& v : ListVolumeEntries(filters))
5385
{
5386
names.emplace_back(v.Name);
5387
}
test/windows/wslc/WSLCCLIExecutionUnitTests.cpp
+1
-1
@@ -144,7 +144,7 @@ class WSLCCLIExecutionUnitTests
144
}
145
else if (dataType == Data::Volumes)
146
{
147
- std::vector<WSLCVolumeInformation> volumes;
147
+ std::vector<wsl::windows::common::wslc_schema::VolumeListEntry> volumes;
148
dataMap.Add<Data::Volumes>(std::move(volumes));
149
handled = true;
150
}
test/windows/wslc/e2e/WSLCE2EHelpers.cpp
+3
-3
@@ -228,7 +228,7 @@ void VerifyVolumeIsListed(const std::wstring& volumeName)
228
{
229
auto result = RunWslc(L"volume list --format json");
230
result.Verify({.Stderr = L"", .ExitCode = 0});
231
- auto volumes = ParseNdjsonOutputAs<WSLCVolumeInformation>(result);
231
+ auto volumes = ParseNdjsonOutputAs<VolumeListOutput>(result);
232
for (const auto& vol : volumes)
233
{
234
if (vol.Name == wsl::shared::string::WideToMultiByte(volumeName))
@@ -244,7 +244,7 @@ void VerifyVolumeIsNotListed(const std::wstring& volumeName)
244
{
245
auto result = RunWslc(L"volume list --format json");
246
result.Verify({.Stderr = L"", .ExitCode = 0});
247
- auto volumes = ParseNdjsonOutputAs<WSLCVolumeInformation>(result);
247
+ auto volumes = ParseNdjsonOutputAs<VolumeListOutput>(result);
248
for (const auto& vol : volumes)
249
{
250
if (vol.Name == wsl::shared::string::WideToMultiByte(volumeName))
@@ -460,7 +460,7 @@ void EnsureVolumeDoesNotExist(const std::wstring& volumeName)
460
{
461
auto result = RunWslc(L"volume list --format json");
462
result.Verify({.Stderr = L"", .ExitCode = 0});
463
- auto volumes = ParseNdjsonOutputAs<WSLCVolumeInformation>(result);
463
+ auto volumes = ParseNdjsonOutputAs<VolumeListOutput>(result);
464
for (const auto& vol : volumes)
465
{
466
if (vol.Name == wsl::shared::string::WideToMultiByte(volumeName))
test/windows/wslc/e2e/WSLCE2EHelpers.h
+16
@@ -78,6 +78,22 @@ struct NetworkListOutput
78
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(NetworkListOutput, CreatedAt, Driver, ID, IPv4, IPv6, Internal, Labels, Name, Scope);
79
};
80
81
+struct VolumeListOutput
82
+{
83
+ std::string Availability;
84
+ std::string Driver;
85
+ std::string Group;
86
+ std::string Labels;
87
+ std::string Links;
88
+ std::string Mountpoint;
89
+ std::string Name;
90
+ std::string Scope;
91
+ std::string Size;
92
+ std::string Status;
93
+
94
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VolumeListOutput, Availability, Driver, Group, Labels, Links, Mountpoint, Name, Scope, Size, Status);
95
+};
96
+
97
struct TestImage
98
{
99
std::wstring Name;
test/windows/wslc/e2e/WSLCE2EVolumeListTests.cpp
+27
-2
@@ -88,7 +88,7 @@ class WSLCE2EVolumeListTests
88
result = RunWslc(L"volume list --format json");
89
result.Verify({.Stderr = L"", .ExitCode = 0});
90
91
- auto volumes = ParseNdjsonOutputAs<WSLCVolumeInformation>(result);
91
+ auto volumes = ParseNdjsonOutputAs<VolumeListOutput>(result);
92
VERIFY_ARE_EQUAL(2U, volumes.size());
93
94
std::vector<std::string> names;
@@ -102,6 +102,31 @@ class WSLCE2EVolumeListTests
102
VERIFY_ARE_NOT_EQUAL(names.end(), std::find(names.begin(), names.end(), WideToMultiByte(TestVolumeName2)));
103
}
104
105
+ WSLC_TEST_METHOD(WSLCE2E_Volume_List_ReportsFullFieldSet)
106
+ {
107
+ auto result = RunWslc(std::format(L"volume create --label env=prod {}", TestVolumeName));
108
+ result.Verify({.Stderr = L"", .ExitCode = 0});
109
+
110
+ result = RunWslc(L"volume list --format json");
111
+ result.Verify({.Stderr = L"", .ExitCode = 0});
112
+
113
+ const auto entries = ParseNdjsonOutput(result);
114
+ VERIFY_ARE_EQUAL(1u, entries.size());
115
+ const auto& volume = entries[0];
116
+
117
+ VERIFY_ARE_EQUAL(10u, volume.size());
118
+ VERIFY_ARE_EQUAL("N/A", volume["Availability"].get<std::string>());
119
+ VERIFY_ARE_EQUAL("guest", volume["Driver"].get<std::string>());
120
+ VERIFY_ARE_EQUAL("N/A", volume["Group"].get<std::string>());
121
+ VERIFY_ARE_EQUAL("env=prod", volume["Labels"].get<std::string>());
122
+ VERIFY_ARE_EQUAL("N/A", volume["Links"].get<std::string>());
123
+ VERIFY_IS_FALSE(volume["Mountpoint"].get<std::string>().empty());
124
+ VERIFY_ARE_EQUAL(WideToMultiByte(TestVolumeName), volume["Name"].get<std::string>());
125
+ VERIFY_ARE_EQUAL("local", volume["Scope"].get<std::string>());
126
+ VERIFY_ARE_EQUAL("N/A", volume["Size"].get<std::string>());
127
+ VERIFY_ARE_EQUAL("N/A", volume["Status"].get<std::string>());
128
+ }
129
+
130
WSLC_TEST_METHOD(WSLCE2E_Volume_List_Filter_MalformedValue)
131
{
132
const auto result = RunWslc(L"volume list --filter label");
@@ -243,7 +268,7 @@ private:
268
{
269
auto result = RunWslc(std::format(L"volume list --format json {}", filterArgs));
270
result.Verify({.Stderr = L"", .ExitCode = 0});
246
- const auto volumes = ParseNdjsonOutputAs<WSLCVolumeInformation>(result);
271
+ const auto volumes = ParseNdjsonOutputAs<VolumeListOutput>(result);
272
std::set<std::string> names;
273
for (const auto& v : volumes)
274
{