From b851ae906f411dc3057f94b64002a02a49b655d1 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Wed, 8 Apr 2026 16:24:55 -0700
Subject: [PATCH 01/13] Init volume command
---
localization/strings/en-US/Resources.resw | 37 +++++
.../wslc/arguments/ArgumentDefinitions.h | 2 +
.../wslc/arguments/ArgumentValidation.cpp | 27 ++++
.../wslc/arguments/ArgumentValidation.h | 1 +
src/windows/wslc/commands/RootCommand.cpp | 2 +
src/windows/wslc/commands/VolumeCommand.cpp | 51 +++++++
src/windows/wslc/commands/VolumeCommand.h | 95 +++++++++++++
.../wslc/commands/VolumeCreateCommand.cpp | 54 ++++++++
.../wslc/commands/VolumeDeleteCommand.cpp | 53 +++++++
.../wslc/commands/VolumeInspectCommand.cpp | 53 +++++++
.../wslc/commands/VolumeListCommand.cpp | 68 +++++++++
src/windows/wslc/core/ExecutionContextData.h | 3 +
src/windows/wslc/services/VolumeModel.h | 30 ++++
src/windows/wslc/services/VolumeService.cpp | 63 +++++++++
src/windows/wslc/services/VolumeService.h | 28 ++++
src/windows/wslc/tasks/VolumeTasks.cpp | 130 ++++++++++++++++++
src/windows/wslc/tasks/VolumeTasks.h | 28 ++++
.../wslc/e2e/WSLCE2EContainerCreateTests.cpp | 67 +++++++++
.../wslc/e2e/WSLCE2EContainerRunTests.cpp | 67 +++++++++
19 files changed, 859 insertions(+)
create mode 100644 src/windows/wslc/commands/VolumeCommand.cpp
create mode 100644 src/windows/wslc/commands/VolumeCommand.h
create mode 100644 src/windows/wslc/commands/VolumeCreateCommand.cpp
create mode 100644 src/windows/wslc/commands/VolumeDeleteCommand.cpp
create mode 100644 src/windows/wslc/commands/VolumeInspectCommand.cpp
create mode 100644 src/windows/wslc/commands/VolumeListCommand.cpp
create mode 100644 src/windows/wslc/services/VolumeModel.h
create mode 100644 src/windows/wslc/services/VolumeService.cpp
create mode 100644 src/windows/wslc/services/VolumeService.h
create mode 100644 src/windows/wslc/tasks/VolumeTasks.cpp
create mode 100644 src/windows/wslc/tasks/VolumeTasks.h
diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index 462c7b36a..9d2a313a1 100644
--- a/localization/strings/en-US/Resources.resw
+++ b/localization/strings/en-US/Resources.resw
@@ -2505,4 +2505,41 @@ On first run, creates the file with all settings commented out at their defaults
Requested load but no input provided.
+
+ Manage volumes.
+
+
+ Manage the lifecycle of WSL volumes, including creating, inspecting, listing, and deleting them.
+ {Locked="WSL"}Product names should not be translated
+
+
+ Create a volume.
+
+
+ Creates a named volume that can be attached to containers.
+
+
+ Remove one or more volumes.
+
+
+ Removes one or more volumes. A volume cannot be removed if it is in use by a container.
+
+
+ Display detailed information on one or more volumes.
+
+
+ Display detailed information on one or more volumes.
+
+
+ List volumes.
+
+
+ Lists all volumes in the session.
+
+
+ Volume name
+
+
+ Specify volume driver name
+
diff --git a/src/windows/wslc/arguments/ArgumentDefinitions.h b/src/windows/wslc/arguments/ArgumentDefinitions.h
index 3748a95cd..ab9522bdc 100644
--- a/src/windows/wslc/arguments/ArgumentDefinitions.h
+++ b/src/windows/wslc/arguments/ArgumentDefinitions.h
@@ -83,5 +83,7 @@ _(Verbose, "verbose", NO_ALIAS, Kind::Flag, L
_(Version, "version", L"v", Kind::Flag, Localization::WSLCCLI_VersionArgDescription()) \
/*_(Virtual, "virtualization", NO_ALIAS, Kind::Value, Localization::WSLCCLI_VirtualArgDescription())*/ \
_(Volume, "volume", L"v", Kind::Value, Localization::WSLCCLI_VolumeArgDescription()) \
+_(VolumeDriver, "driver", L"d", Kind::Value, Localization::WSLCCLI_VolumeDriverArgDescription()) \
+_(VolumeName, "volume-name", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_VolumeNameArgDescription()) \
_(WorkDir, "workdir", L"w", Kind::Value, Localization::WSLCCLI_WorkingDirArgDescription()) \
// clang-format on
diff --git a/src/windows/wslc/arguments/ArgumentValidation.cpp b/src/windows/wslc/arguments/ArgumentValidation.cpp
index ea47a5f55..35022fbb8 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.cpp
+++ b/src/windows/wslc/arguments/ArgumentValidation.cpp
@@ -47,6 +47,10 @@ void Argument::Validate(const ArgMap& execArgs) const
validation::ValidateVolumeMount(execArgs.GetAll());
break;
+ case ArgType::VolumeDriver:
+ validation::ValidateVolumeDriver(execArgs.GetAll(), m_name);
+ break;
+
case ArgType::WorkDir:
{
const auto& value = execArgs.Get();
@@ -97,6 +101,29 @@ void ValidateVolumeMount(const std::vector& values)
}
}
+void ValidateVolumeDriver(const std::vector& values, const std::wstring& argName)
+{
+ static const std::vector supportedDrivers = {L"vhd"};
+ for (const auto& value : values)
+ {
+ bool found = false;
+ for (const auto& driver : supportedDrivers)
+ {
+ if (IsEqual(value, driver))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ throw ArgumentException(std::format(
+ L"Invalid {} value: {} is not a recognized volume driver. Supported drivers are: vhd.", argName, value));
+ }
+ }
+}
+
// Convert string to WSLCSignal enum - accepts either signal name (e.g., "SIGKILL") or number (e.g., "9")
WSLCSignal GetWSLCSignalFromString(const std::wstring& input, const std::wstring& argName)
{
diff --git a/src/windows/wslc/arguments/ArgumentValidation.h b/src/windows/wslc/arguments/ArgumentValidation.h
index 23208699c..673d63edd 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.h
+++ b/src/windows/wslc/arguments/ArgumentValidation.h
@@ -61,5 +61,6 @@ void ValidateFormatTypeFromString(const std::vector& values, const
FormatType GetFormatTypeFromString(const std::wstring& input, const std::wstring& argName = {});
void ValidateVolumeMount(const std::vector& values);
+void ValidateVolumeDriver(const std::vector& values, const std::wstring& argName);
} // namespace wsl::windows::wslc::validation
\ No newline at end of file
diff --git a/src/windows/wslc/commands/RootCommand.cpp b/src/windows/wslc/commands/RootCommand.cpp
index 732c19404..4d665ee3e 100644
--- a/src/windows/wslc/commands/RootCommand.cpp
+++ b/src/windows/wslc/commands/RootCommand.cpp
@@ -19,6 +19,7 @@ Module Name:
#include "SessionCommand.h"
#include "SettingsCommand.h"
#include "VersionCommand.h"
+#include "VolumeCommand.h"
using namespace wsl::windows::wslc::execution;
using namespace wsl::shared;
@@ -31,6 +32,7 @@ std::vector> RootCommand::GetCommands() const
commands.push_back(std::make_unique(FullName()));
commands.push_back(std::make_unique(FullName()));
commands.push_back(std::make_unique(FullName()));
+ commands.push_back(std::make_unique(FullName()));
commands.push_back(std::make_unique(FullName()));
commands.push_back(std::make_unique(FullName()));
commands.push_back(std::make_unique(FullName()));
diff --git a/src/windows/wslc/commands/VolumeCommand.cpp b/src/windows/wslc/commands/VolumeCommand.cpp
new file mode 100644
index 000000000..4e38ea574
--- /dev/null
+++ b/src/windows/wslc/commands/VolumeCommand.cpp
@@ -0,0 +1,51 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeCommand.cpp
+
+Abstract:
+
+ Implementation of command execution logic.
+
+--*/
+#include "CLIExecutionContext.h"
+#include "VolumeCommand.h"
+
+using namespace wsl::windows::wslc::execution;
+using namespace wsl::shared;
+
+namespace wsl::windows::wslc {
+// Volume Root Command
+std::vector> VolumeCommand::GetCommands() const
+{
+ std::vector> commands;
+ commands.push_back(std::make_unique(FullName()));
+ commands.push_back(std::make_unique(FullName()));
+ commands.push_back(std::make_unique(FullName()));
+ commands.push_back(std::make_unique(FullName()));
+ return commands;
+}
+
+std::vector VolumeCommand::GetArguments() const
+{
+ return {};
+}
+
+std::wstring VolumeCommand::ShortDescription() const
+{
+ return Localization::WSLCCLI_VolumeCommandDesc();
+}
+
+std::wstring VolumeCommand::LongDescription() const
+{
+ return Localization::WSLCCLI_VolumeCommandLongDesc();
+}
+
+void VolumeCommand::ExecuteInternal(CLIExecutionContext& context) const
+{
+ OutputHelp();
+}
+} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/commands/VolumeCommand.h b/src/windows/wslc/commands/VolumeCommand.h
new file mode 100644
index 000000000..82346c760
--- /dev/null
+++ b/src/windows/wslc/commands/VolumeCommand.h
@@ -0,0 +1,95 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeCommand.h
+
+Abstract:
+
+ Declaration of command classes and interfaces.
+
+--*/
+#pragma once
+#include "Command.h"
+
+namespace wsl::windows::wslc {
+// Root Volume Command
+struct VolumeCommand final : public Command
+{
+ constexpr static std::wstring_view CommandName = L"volume";
+ VolumeCommand(const std::wstring& parent) : Command(CommandName, parent)
+ {
+ }
+ std::vector GetArguments() const override;
+ std::wstring ShortDescription() const override;
+ std::wstring LongDescription() const override;
+
+ std::vector> GetCommands() const override;
+
+protected:
+ void ExecuteInternal(CLIExecutionContext& context) const override;
+};
+
+// Create Command
+struct VolumeCreateCommand final : public Command
+{
+ constexpr static std::wstring_view CommandName = L"create";
+ VolumeCreateCommand(const std::wstring& parent) : Command(CommandName, parent)
+ {
+ }
+ std::vector GetArguments() const override;
+ std::wstring ShortDescription() const override;
+ std::wstring LongDescription() const override;
+
+protected:
+ void ExecuteInternal(CLIExecutionContext& context) const override;
+};
+
+// Delete Command
+struct VolumeDeleteCommand final : public Command
+{
+ constexpr static std::wstring_view CommandName = L"remove";
+ VolumeDeleteCommand(const std::wstring& parent) : Command(CommandName, {L"delete", L"rm"}, parent)
+ {
+ }
+ std::vector GetArguments() const override;
+ std::wstring ShortDescription() const override;
+ std::wstring LongDescription() const override;
+
+protected:
+ void ExecuteInternal(CLIExecutionContext& context) const override;
+};
+
+// Inspect Command
+struct VolumeInspectCommand final : public Command
+{
+ constexpr static std::wstring_view CommandName = L"inspect";
+ VolumeInspectCommand(const std::wstring& parent) : Command(CommandName, parent)
+ {
+ }
+ std::vector GetArguments() const override;
+ std::wstring ShortDescription() const override;
+ std::wstring LongDescription() const override;
+
+protected:
+ void ExecuteInternal(CLIExecutionContext& context) const override;
+};
+
+// List Command
+struct VolumeListCommand final : public Command
+{
+ constexpr static std::wstring_view CommandName = L"list";
+ VolumeListCommand(const std::wstring& parent) : Command(CommandName, {L"ls"}, parent)
+ {
+ }
+ std::vector GetArguments() const override;
+ std::wstring ShortDescription() const override;
+ std::wstring LongDescription() const override;
+
+protected:
+ void ValidateArgumentsInternal(const ArgMap& execArgs) const override;
+ void ExecuteInternal(CLIExecutionContext& context) const override;
+};
+} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/commands/VolumeCreateCommand.cpp b/src/windows/wslc/commands/VolumeCreateCommand.cpp
new file mode 100644
index 000000000..4f9b7f29d
--- /dev/null
+++ b/src/windows/wslc/commands/VolumeCreateCommand.cpp
@@ -0,0 +1,54 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeCreateCommand.cpp
+
+Abstract:
+
+ Implementation of command execution logic.
+
+--*/
+
+#include "VolumeCommand.h"
+#include "CLIExecutionContext.h"
+#include "SessionTasks.h"
+#include "VolumeTasks.h"
+#include "Task.h"
+
+using namespace wsl::windows::wslc::execution;
+using namespace wsl::windows::wslc::task;
+using namespace wsl::shared;
+
+namespace wsl::windows::wslc {
+// Volume Create Command
+std::vector VolumeCreateCommand::GetArguments() const
+{
+ return {
+ Argument::Create(ArgType::VolumeName, true),
+ Argument::Create(ArgType::VolumeDriver),
+ Argument::Create(ArgType::Session),
+ };
+}
+
+std::wstring VolumeCreateCommand::ShortDescription() const
+{
+ return Localization::WSLCCLI_VolumeCreateDesc();
+}
+
+std::wstring VolumeCreateCommand::LongDescription() const
+{
+ return Localization::WSLCCLI_VolumeCreateLongDesc();
+}
+
+// clang-format off
+void VolumeCreateCommand::ExecuteInternal(CLIExecutionContext& context) const
+{
+ context
+ << CreateSession
+ << CreateVolume;
+}
+// clang-format on
+} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/commands/VolumeDeleteCommand.cpp b/src/windows/wslc/commands/VolumeDeleteCommand.cpp
new file mode 100644
index 000000000..395fbd0fb
--- /dev/null
+++ b/src/windows/wslc/commands/VolumeDeleteCommand.cpp
@@ -0,0 +1,53 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeDeleteCommand.cpp
+
+Abstract:
+
+ Implementation of command execution logic.
+
+--*/
+
+#include "VolumeCommand.h"
+#include "CLIExecutionContext.h"
+#include "SessionTasks.h"
+#include "VolumeTasks.h"
+#include "Task.h"
+
+using namespace wsl::windows::wslc::execution;
+using namespace wsl::windows::wslc::task;
+using namespace wsl::shared;
+
+namespace wsl::windows::wslc {
+// Volume Delete Command
+std::vector VolumeDeleteCommand::GetArguments() const
+{
+ return {
+ Argument::Create(ArgType::VolumeName, true, NO_LIMIT),
+ Argument::Create(ArgType::Session),
+ };
+}
+
+std::wstring VolumeDeleteCommand::ShortDescription() const
+{
+ return Localization::WSLCCLI_VolumeDeleteDesc();
+}
+
+std::wstring VolumeDeleteCommand::LongDescription() const
+{
+ return Localization::WSLCCLI_VolumeDeleteLongDesc();
+}
+
+// clang-format off
+void VolumeDeleteCommand::ExecuteInternal(CLIExecutionContext& context) const
+{
+ context
+ << CreateSession
+ << DeleteVolumes;
+}
+// clang-format on
+} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/commands/VolumeInspectCommand.cpp b/src/windows/wslc/commands/VolumeInspectCommand.cpp
new file mode 100644
index 000000000..c84c2d4bb
--- /dev/null
+++ b/src/windows/wslc/commands/VolumeInspectCommand.cpp
@@ -0,0 +1,53 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeInspectCommand.cpp
+
+Abstract:
+
+ Implementation of command execution logic.
+
+--*/
+
+#include "VolumeCommand.h"
+#include "CLIExecutionContext.h"
+#include "SessionTasks.h"
+#include "VolumeTasks.h"
+#include "Task.h"
+
+using namespace wsl::windows::wslc::execution;
+using namespace wsl::windows::wslc::task;
+using namespace wsl::shared;
+
+namespace wsl::windows::wslc {
+// Volume Inspect Command
+std::vector VolumeInspectCommand::GetArguments() const
+{
+ return {
+ Argument::Create(ArgType::VolumeName, true, NO_LIMIT),
+ Argument::Create(ArgType::Session),
+ };
+}
+
+std::wstring VolumeInspectCommand::ShortDescription() const
+{
+ return Localization::WSLCCLI_VolumeInspectDesc();
+}
+
+std::wstring VolumeInspectCommand::LongDescription() const
+{
+ return Localization::WSLCCLI_VolumeInspectLongDesc();
+}
+
+// clang-format off
+void VolumeInspectCommand::ExecuteInternal(CLIExecutionContext& context) const
+{
+ context
+ << CreateSession
+ << InspectVolumes;
+}
+// clang-format on
+} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/commands/VolumeListCommand.cpp b/src/windows/wslc/commands/VolumeListCommand.cpp
new file mode 100644
index 000000000..ccbfed72c
--- /dev/null
+++ b/src/windows/wslc/commands/VolumeListCommand.cpp
@@ -0,0 +1,68 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeListCommand.cpp
+
+Abstract:
+
+ Implementation of command execution logic.
+
+--*/
+
+#include "VolumeCommand.h"
+#include "CLIExecutionContext.h"
+#include "SessionTasks.h"
+#include "VolumeTasks.h"
+#include "Task.h"
+
+using namespace wsl::windows::wslc::execution;
+using namespace wsl::windows::wslc::task;
+using namespace wsl::shared;
+using namespace wsl::shared::string;
+
+namespace wsl::windows::wslc {
+// Volume List Command
+std::vector VolumeListCommand::GetArguments() const
+{
+ return {
+ Argument::Create(ArgType::Format),
+ Argument::Create(ArgType::Quiet),
+ Argument::Create(ArgType::Session),
+ };
+}
+
+std::wstring VolumeListCommand::ShortDescription() const
+{
+ return Localization::WSLCCLI_VolumeListDesc();
+}
+
+std::wstring VolumeListCommand::LongDescription() const
+{
+ return Localization::WSLCCLI_VolumeListLongDesc();
+}
+
+void VolumeListCommand::ValidateArgumentsInternal(const ArgMap& execArgs) const
+{
+ if (execArgs.Contains(ArgType::Format))
+ {
+ auto format = execArgs.Get();
+ if (!IsEqual(format, L"json") && !IsEqual(format, L"table"))
+ {
+ throw CommandException(Localization::WSLCCLI_InvalidFormatError());
+ }
+ }
+}
+
+// clang-format off
+void VolumeListCommand::ExecuteInternal(CLIExecutionContext& context) const
+{
+ context
+ << CreateSession
+ << GetVolumes
+ << ListVolumes;
+}
+// clang-format on
+} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/core/ExecutionContextData.h b/src/windows/wslc/core/ExecutionContextData.h
index ca550670c..f48c44951 100644
--- a/src/windows/wslc/core/ExecutionContextData.h
+++ b/src/windows/wslc/core/ExecutionContextData.h
@@ -16,6 +16,7 @@ Module Name:
#include "ContainerModel.h"
#include "ImageModel.h"
#include "SessionModel.h"
+#include "VolumeModel.h"
#include
@@ -36,6 +37,7 @@ enum class Data : size_t
Containers,
ContainerOptions,
Images,
+ Volumes,
Max
};
@@ -50,6 +52,7 @@ namespace details {
DEFINE_DATA_MAPPING(Containers, std::vector);
DEFINE_DATA_MAPPING(ContainerOptions, wsl::windows::wslc::models::ContainerOptions);
DEFINE_DATA_MAPPING(Images, std::vector);
+ DEFINE_DATA_MAPPING(Volumes, std::vector);
} // namespace details
struct DataMap : wsl::windows::wslc::EnumBasedVariantMap
diff --git a/src/windows/wslc/services/VolumeModel.h b/src/windows/wslc/services/VolumeModel.h
new file mode 100644
index 000000000..609eb31cf
--- /dev/null
+++ b/src/windows/wslc/services/VolumeModel.h
@@ -0,0 +1,30 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeModel.h
+
+Abstract:
+
+ This file contains the VolumeModel definitions
+
+--*/
+
+#pragma once
+
+#include
+#include
+
+namespace wsl::windows::wslc::models {
+
+struct VolumeInformation
+{
+ std::string Name;
+ std::string Type;
+
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE(VolumeInformation, Name, Type);
+};
+
+} // namespace wsl::windows::wslc::models
diff --git a/src/windows/wslc/services/VolumeService.cpp b/src/windows/wslc/services/VolumeService.cpp
new file mode 100644
index 000000000..0f4ba13d4
--- /dev/null
+++ b/src/windows/wslc/services/VolumeService.cpp
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeService.cpp
+
+Abstract:
+
+ This file contains the VolumeService implementation
+
+--*/
+#include "VolumeService.h"
+#include
+
+using namespace wsl::shared;
+using namespace wsl::windows::common::wslutil;
+
+namespace wsl::windows::wslc::services {
+
+void VolumeService::Create(models::Session& session, const std::string& name, const std::string& type)
+{
+ WSLCVolumeOptions options{};
+ options.Name = name.c_str();
+ options.Type = type.c_str();
+ options.Options = nullptr;
+
+ THROW_IF_FAILED(session.Get()->CreateVolume(&options));
+}
+
+void VolumeService::Delete(models::Session& session, const std::string& name)
+{
+ THROW_IF_FAILED(session.Get()->DeleteVolume(name.c_str()));
+}
+
+std::vector VolumeService::List(models::Session& session)
+{
+ wil::unique_cotaskmem_array_ptr rawVolumes;
+ ULONG count = 0;
+ THROW_IF_FAILED(session.Get()->ListVolumes(&rawVolumes, &count));
+
+ std::vector volumes;
+ volumes.reserve(count);
+ for (auto ptr = rawVolumes.get(), end = rawVolumes.get() + count; ptr != end; ++ptr)
+ {
+ models::VolumeInformation info;
+ info.Name = ptr->Name;
+ info.Type = ptr->Type;
+ volumes.push_back(std::move(info));
+ }
+
+ return volumes;
+}
+
+wsl::windows::common::wslc_schema::InspectVolume VolumeService::Inspect(models::Session& session, const std::string& name)
+{
+ wil::unique_cotaskmem_ansistring output;
+ THROW_IF_FAILED(session.Get()->InspectVolume(name.c_str(), &output));
+ return FromJson(output.get());
+}
+
+} // namespace wsl::windows::wslc::services
diff --git a/src/windows/wslc/services/VolumeService.h b/src/windows/wslc/services/VolumeService.h
new file mode 100644
index 000000000..41ed222eb
--- /dev/null
+++ b/src/windows/wslc/services/VolumeService.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeService.h
+
+Abstract:
+
+ This file contains the VolumeService definition
+
+--*/
+#pragma once
+
+#include "SessionModel.h"
+#include "VolumeModel.h"
+#include
+
+namespace wsl::windows::wslc::services {
+struct VolumeService
+{
+ static void Create(models::Session& session, const std::string& name, const std::string& type);
+ static void Delete(models::Session& session, const std::string& name);
+ static std::vector List(models::Session& session);
+ static wsl::windows::common::wslc_schema::InspectVolume Inspect(models::Session& session, const std::string& name);
+};
+} // namespace wsl::windows::wslc::services
diff --git a/src/windows/wslc/tasks/VolumeTasks.cpp b/src/windows/wslc/tasks/VolumeTasks.cpp
new file mode 100644
index 000000000..8d39823d6
--- /dev/null
+++ b/src/windows/wslc/tasks/VolumeTasks.cpp
@@ -0,0 +1,130 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeTasks.cpp
+
+Abstract:
+
+ Implementation of volume command related execution logic.
+
+--*/
+#include "Argument.h"
+#include "ArgumentValidation.h"
+#include "CLIExecutionContext.h"
+#include "VolumeModel.h"
+#include "VolumeService.h"
+#include "VolumeTasks.h"
+#include "TableOutput.h"
+#include
+
+using namespace wsl::shared;
+using namespace wsl::windows::common::string;
+using namespace wsl::windows::common::wslutil;
+using namespace wsl::windows::wslc::execution;
+using namespace wsl::windows::wslc::models;
+using namespace wsl::windows::wslc::services;
+
+namespace wsl::windows::wslc::task {
+
+void CreateVolume(CLIExecutionContext& context)
+{
+ WI_ASSERT(context.Data.Contains(Data::Session));
+ WI_ASSERT(context.Args.Contains(ArgType::VolumeName));
+
+ auto name = WideToMultiByte(context.Args.Get());
+ std::string type = "vhd";
+ if (context.Args.Contains(ArgType::VolumeDriver))
+ {
+ type = WideToMultiByte(context.Args.Get());
+ }
+
+ VolumeService::Create(context.Data.Get(), name, type);
+ PrintMessage(MultiByteToWide(name));
+}
+
+void DeleteVolumes(CLIExecutionContext& context)
+{
+ WI_ASSERT(context.Data.Contains(Data::Session));
+ auto& session = context.Data.Get();
+ auto volumeNames = context.Args.GetAll();
+ for (const auto& name : volumeNames)
+ {
+ VolumeService::Delete(session, WideToMultiByte(name));
+ }
+}
+
+void GetVolumes(CLIExecutionContext& context)
+{
+ WI_ASSERT(context.Data.Contains(Data::Session));
+ auto& session = context.Data.Get();
+ context.Data.Add(VolumeService::List(session));
+}
+
+void InspectVolumes(CLIExecutionContext& context)
+{
+ WI_ASSERT(context.Data.Contains(Data::Session));
+ auto& session = context.Data.Get();
+ auto volumeNames = context.Args.GetAll();
+ std::vector result;
+ for (const auto& name : volumeNames)
+ {
+ auto inspectData = VolumeService::Inspect(session, WideToMultiByte(name));
+ result.push_back(inspectData);
+ }
+
+ auto json = ToJson(result, c_jsonPrettyPrintIndent);
+ PrintMessage(MultiByteToWide(json));
+}
+
+void ListVolumes(CLIExecutionContext& context)
+{
+ WI_ASSERT(context.Data.Contains(Data::Volumes));
+ auto& volumes = context.Data.Get();
+
+ if (context.Args.Contains(ArgType::Quiet))
+ {
+ for (const auto& volume : volumes)
+ {
+ PrintMessage(MultiByteToWide(volume.Name));
+ }
+
+ return;
+ }
+
+ FormatType format = FormatType::Table;
+ if (context.Args.Contains(ArgType::Format))
+ {
+ format = validation::GetFormatTypeFromString(context.Args.Get());
+ }
+
+ switch (format)
+ {
+ case FormatType::Json:
+ {
+ auto json = ToJson(volumes, c_jsonPrettyPrintIndent);
+ PrintMessage(MultiByteToWide(json));
+ break;
+ }
+ case FormatType::Table:
+ {
+ auto table = wsl::windows::wslc::TableOutput<2>({L"VOLUME NAME", L"DRIVER"});
+ for (const auto& volume : volumes)
+ {
+ table.OutputLine({
+ MultiByteToWide(volume.Name),
+ MultiByteToWide(volume.Type),
+ });
+ }
+
+ table.Complete();
+ break;
+ }
+ default:
+ THROW_HR(E_UNEXPECTED);
+ }
+}
+
+} // namespace wsl::windows::wslc::task
diff --git a/src/windows/wslc/tasks/VolumeTasks.h b/src/windows/wslc/tasks/VolumeTasks.h
new file mode 100644
index 000000000..b1c49acfd
--- /dev/null
+++ b/src/windows/wslc/tasks/VolumeTasks.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumeTasks.h
+
+Abstract:
+
+ Declaration of volume command execution tasks.
+
+--*/
+#pragma once
+#include "CLIExecutionContext.h"
+#include "Task.h"
+
+using wsl::windows::wslc::execution::CLIExecutionContext;
+
+namespace wsl::windows::wslc::task {
+
+void CreateVolume(CLIExecutionContext& context);
+void DeleteVolumes(CLIExecutionContext& context);
+void GetVolumes(CLIExecutionContext& context);
+void InspectVolumes(CLIExecutionContext& context);
+void ListVolumes(CLIExecutionContext& context);
+
+} // namespace wsl::windows::wslc::task
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
index cda1f24b1..f863c600d 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
@@ -1081,6 +1081,73 @@ class WSLCE2EContainerCreateTests
result.Verify({.Stderr = L"invalid mount path: '' mount path must be absolute\r\nError code: E_FAIL\r\n", .ExitCode = 1});
}
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_NamedVolume_WriteFromContainerReadFromContainer)
+ {
+
+ const std::wstring volumeName = L"wslc-test-vol-create";
+
+ // Cleanup any pre-existing volume
+ RunWslc(std::format(L"volume rm {}", volumeName));
+
+ // Create the named volume
+ auto result = RunWslc(std::format(L"volume create {}", volumeName));
+ result.Verify({.Stderr = L"", .ExitCode = S_OK});
+
+ // Write data to the volume using a container
+ result = RunWslc(std::format(
+ L"container run --rm --volume {}:/data {} sh -c \"echo -n 'named_volume_data' > /data/testfile\"",
+ volumeName,
+ AlpineImage.NameAndTag()));
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = S_OK});
+
+ // Read data back from the volume using another container
+ result = RunWslc(std::format(
+ L"container run --rm --volume {}:/data {} cat /data/testfile",
+ volumeName,
+ AlpineImage.NameAndTag()));
+ result.Verify({.Stdout = L"named_volume_data", .Stderr = L"", .ExitCode = S_OK});
+
+ // Cleanup
+ RunWslc(std::format(L"volume rm {}", volumeName));
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_NamedVolume_PersistsBetweenContainers)
+ {
+
+ const std::wstring volumeName = L"wslc-test-vol-persist";
+
+ // Cleanup any pre-existing volume
+ RunWslc(std::format(L"volume rm {}", volumeName));
+
+ // Create the named volume
+ auto result = RunWslc(std::format(L"volume create {}", volumeName));
+ result.Verify({.Stderr = L"", .ExitCode = S_OK});
+
+ // Write data using first container
+ result = RunWslc(std::format(
+ L"container create --name {} --volume {}:/data {} sh -c \"echo -n 'persist_test' > /data/testfile\"",
+ WslcContainerName,
+ volumeName,
+ AlpineImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = S_OK});
+
+ result = RunWslc(std::format(L"container start -a {}", WslcContainerName));
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = S_OK});
+
+ // Remove first container
+ RunWslc(std::format(L"container rm {}", WslcContainerName));
+
+ // Read data using second container
+ result = RunWslc(std::format(
+ L"container run --rm --volume {}:/data {} cat /data/testfile",
+ volumeName,
+ AlpineImage.NameAndTag()));
+ result.Verify({.Stdout = L"persist_test", .Stderr = L"", .ExitCode = S_OK});
+
+ // Cleanup
+ RunWslc(std::format(L"volume rm {}", volumeName));
+ }
+
private:
// Test container name
const std::wstring WslcContainerName = L"wslc-test-container";
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
index 85f166890..7ef632f85 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
@@ -170,6 +170,73 @@ class WSLCE2EContainerRunTests
result.Verify({.Stderr = L"invalid mount path: '' mount path must be absolute\r\nError code: E_FAIL\r\n", .ExitCode = 1});
}
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_NamedVolume_WriteAndRead)
+ {
+
+ const std::wstring volumeName = L"wslc-test-vol-run";
+
+ // Cleanup any pre-existing volume
+ RunWslc(std::format(L"volume rm {}", volumeName));
+
+ // Create the named volume
+ auto result = RunWslc(std::format(L"volume create {}", volumeName));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ // Write data to the named volume
+ result = RunWslc(std::format(
+ L"container run --rm --volume {}:/data {} sh -c \"echo -n 'run_vol_test' > /data/testfile\"",
+ volumeName,
+ DebianImage.NameAndTag()));
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
+
+ // Read data back from the named volume in a new container
+ result = RunWslc(std::format(
+ L"container run --rm --volume {}:/data {} cat /data/testfile",
+ volumeName,
+ DebianImage.NameAndTag()));
+ result.Verify({.Stdout = L"run_vol_test", .Stderr = L"", .ExitCode = 0});
+
+ // Cleanup
+ RunWslc(std::format(L"volume rm {}", volumeName));
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_NamedVolume_MultipleContainers)
+ {
+
+ const std::wstring volumeName = L"wslc-test-vol-run-multi";
+
+ // Cleanup any pre-existing volume
+ RunWslc(std::format(L"volume rm {}", volumeName));
+
+ // Create the named volume
+ auto result = RunWslc(std::format(L"volume create {}", volumeName));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ // Write data with first container
+ result = RunWslc(std::format(
+ L"container run --rm --volume {}:/data {} sh -c \"echo -n 'first' > /data/testfile\"",
+ volumeName,
+ DebianImage.NameAndTag()));
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
+
+ // Append data with second container
+ result = RunWslc(std::format(
+ L"container run --rm --volume {}:/data {} sh -c \"echo -n '_second' >> /data/testfile\"",
+ volumeName,
+ DebianImage.NameAndTag()));
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
+
+ // Read combined data with third container
+ result = RunWslc(std::format(
+ L"container run --rm --volume {}:/data {} cat /data/testfile",
+ volumeName,
+ DebianImage.NameAndTag()));
+ result.Verify({.Stdout = L"first_second", .Stderr = L"", .ExitCode = 0});
+
+ // Cleanup
+ RunWslc(std::format(L"volume rm {}", volumeName));
+ }
+
private:
const std::wstring WslcContainerName = L"wslc-test-container";
const TestImage& DebianImage = DebianTestImage();
From 89c64d58ffba5fa7cbe1beb6989958dff94ac188 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Fri, 10 Apr 2026 15:19:09 -0700
Subject: [PATCH 02/13] WIP
---
src/windows/wslc/arguments/ArgumentDefinitions.h | 3 ++-
src/windows/wslc/arguments/ArgumentValidation.cpp | 4 ++--
src/windows/wslc/commands/VolumeCreateCommand.cpp | 5 +++--
src/windows/wslc/services/VolumeService.cpp | 5 ++---
src/windows/wslc/services/VolumeService.h | 2 +-
src/windows/wslc/tasks/VolumeTasks.cpp | 13 +++++++++----
6 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/src/windows/wslc/arguments/ArgumentDefinitions.h b/src/windows/wslc/arguments/ArgumentDefinitions.h
index ab9522bdc..5102ee7ef 100644
--- a/src/windows/wslc/arguments/ArgumentDefinitions.h
+++ b/src/windows/wslc/arguments/ArgumentDefinitions.h
@@ -45,6 +45,7 @@ _(Detach, "detach", L"d", Kind::Flag, L
/*_(DNSDomain, "dns-domain", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSDomainArgDescription())*/ \
/*_(DNSOption, "dns-option", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSOptionArgDescription())*/ \
/*_(DNSSearch, "dns-search", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSSearchArgDescription())*/ \
+_(Driver, "driver", L"d", Kind::Value, L"Specify volume driver name (default vhd)") \
_(Entrypoint, "entrypoint", NO_ALIAS, Kind::Value, Localization::WSLCCLI_EntrypointArgDescription()) \
_(Env, "env", L"e", Kind::Value, Localization::WSLCCLI_EnvArgDescription()) \
_(EnvFile, "env-file", NO_ALIAS, Kind::Value, Localization::WSLCCLI_EnvFileArgDescription()) \
@@ -62,6 +63,7 @@ _(Name, "name", NO_ALIAS, Kind::Value, L
/*_(NoDNS, "no-dns", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoDNSArgDescription())*/ \
_(NoPrune, "no-prune", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoPruneArgDescription()) \
_(NoTrunc, "no-trunc", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoTruncArgDescription()) \
+_(Options, "opt", L"o", Kind::Value, L"Set driver specific options") \
_(Output, "output", L"o", Kind::Value, Localization::WSLCCLI_OutputArgDescription()) \
_(Path, "path", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_PathArgDescription()) \
/*_(Progress, "progress", NO_ALIAS, Kind::Value, Localization::WSLCCLI_ProgressArgDescription())*/ \
@@ -83,7 +85,6 @@ _(Verbose, "verbose", NO_ALIAS, Kind::Flag, L
_(Version, "version", L"v", Kind::Flag, Localization::WSLCCLI_VersionArgDescription()) \
/*_(Virtual, "virtualization", NO_ALIAS, Kind::Value, Localization::WSLCCLI_VirtualArgDescription())*/ \
_(Volume, "volume", L"v", Kind::Value, Localization::WSLCCLI_VolumeArgDescription()) \
-_(VolumeDriver, "driver", L"d", Kind::Value, Localization::WSLCCLI_VolumeDriverArgDescription()) \
_(VolumeName, "volume-name", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_VolumeNameArgDescription()) \
_(WorkDir, "workdir", L"w", Kind::Value, Localization::WSLCCLI_WorkingDirArgDescription()) \
// clang-format on
diff --git a/src/windows/wslc/arguments/ArgumentValidation.cpp b/src/windows/wslc/arguments/ArgumentValidation.cpp
index 35022fbb8..2e5eb5378 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.cpp
+++ b/src/windows/wslc/arguments/ArgumentValidation.cpp
@@ -47,8 +47,8 @@ void Argument::Validate(const ArgMap& execArgs) const
validation::ValidateVolumeMount(execArgs.GetAll());
break;
- case ArgType::VolumeDriver:
- validation::ValidateVolumeDriver(execArgs.GetAll(), m_name);
+ case ArgType::Driver:
+ // validation::ValidateVolumeDriver(execArgs.GetAll(), m_name);
break;
case ArgType::WorkDir:
diff --git a/src/windows/wslc/commands/VolumeCreateCommand.cpp b/src/windows/wslc/commands/VolumeCreateCommand.cpp
index 4f9b7f29d..fd029541e 100644
--- a/src/windows/wslc/commands/VolumeCreateCommand.cpp
+++ b/src/windows/wslc/commands/VolumeCreateCommand.cpp
@@ -27,8 +27,9 @@ namespace wsl::windows::wslc {
std::vector VolumeCreateCommand::GetArguments() const
{
return {
- Argument::Create(ArgType::VolumeName, true),
- Argument::Create(ArgType::VolumeDriver),
+ Argument::Create(ArgType::VolumeName),
+ Argument::Create(ArgType::Driver),
+ Argument::Create(ArgType::Options),
Argument::Create(ArgType::Session),
};
}
diff --git a/src/windows/wslc/services/VolumeService.cpp b/src/windows/wslc/services/VolumeService.cpp
index 0f4ba13d4..16c3aad94 100644
--- a/src/windows/wslc/services/VolumeService.cpp
+++ b/src/windows/wslc/services/VolumeService.cpp
@@ -19,13 +19,12 @@ using namespace wsl::windows::common::wslutil;
namespace wsl::windows::wslc::services {
-void VolumeService::Create(models::Session& session, const std::string& name, const std::string& type)
+void VolumeService::Create(models::Session& session, const std::string& name, const std::string& type, const std::string& opt)
{
WSLCVolumeOptions options{};
options.Name = name.c_str();
options.Type = type.c_str();
- options.Options = nullptr;
-
+ options.Options = opt.c_str();
THROW_IF_FAILED(session.Get()->CreateVolume(&options));
}
diff --git a/src/windows/wslc/services/VolumeService.h b/src/windows/wslc/services/VolumeService.h
index 41ed222eb..b9b688c77 100644
--- a/src/windows/wslc/services/VolumeService.h
+++ b/src/windows/wslc/services/VolumeService.h
@@ -20,7 +20,7 @@ Module Name:
namespace wsl::windows::wslc::services {
struct VolumeService
{
- static void Create(models::Session& session, const std::string& name, const std::string& type);
+ static void Create(models::Session& session, const std::string& name, const std::string& type, const std::string& opt);
static void Delete(models::Session& session, const std::string& name);
static std::vector List(models::Session& session);
static wsl::windows::common::wslc_schema::InspectVolume Inspect(models::Session& session, const std::string& name);
diff --git a/src/windows/wslc/tasks/VolumeTasks.cpp b/src/windows/wslc/tasks/VolumeTasks.cpp
index 8d39823d6..370ed9a50 100644
--- a/src/windows/wslc/tasks/VolumeTasks.cpp
+++ b/src/windows/wslc/tasks/VolumeTasks.cpp
@@ -32,16 +32,21 @@ namespace wsl::windows::wslc::task {
void CreateVolume(CLIExecutionContext& context)
{
WI_ASSERT(context.Data.Contains(Data::Session));
- WI_ASSERT(context.Args.Contains(ArgType::VolumeName));
auto name = WideToMultiByte(context.Args.Get());
std::string type = "vhd";
- if (context.Args.Contains(ArgType::VolumeDriver))
+ if (context.Args.Contains(ArgType::Driver))
{
- type = WideToMultiByte(context.Args.Get());
+ type = WideToMultiByte(context.Args.Get());
}
- VolumeService::Create(context.Data.Get(), name, type);
+ std::string options{};
+ if(context.Args.Contains(ArgType::Options))
+ {
+ options = WideToMultiByte(context.Args.Get());
+ }
+
+ VolumeService::Create(context.Data.Get(), name, type, options);
PrintMessage(MultiByteToWide(name));
}
From 2fa9ce80bd4c20548674759d8eaf7106585c5d27 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Mon, 13 Apr 2026 20:53:49 -0700
Subject: [PATCH 03/13] Create
---
.../wslc/arguments/ArgumentValidation.cpp | 27 ---------------
.../wslc/commands/VolumeCreateCommand.cpp | 8 ++---
src/windows/wslc/tasks/VolumeTasks.cpp | 33 +++++++++++++++----
3 files changed, 29 insertions(+), 39 deletions(-)
diff --git a/src/windows/wslc/arguments/ArgumentValidation.cpp b/src/windows/wslc/arguments/ArgumentValidation.cpp
index 2e5eb5378..ea47a5f55 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.cpp
+++ b/src/windows/wslc/arguments/ArgumentValidation.cpp
@@ -47,10 +47,6 @@ void Argument::Validate(const ArgMap& execArgs) const
validation::ValidateVolumeMount(execArgs.GetAll());
break;
- case ArgType::Driver:
- // validation::ValidateVolumeDriver(execArgs.GetAll(), m_name);
- break;
-
case ArgType::WorkDir:
{
const auto& value = execArgs.Get();
@@ -101,29 +97,6 @@ void ValidateVolumeMount(const std::vector& values)
}
}
-void ValidateVolumeDriver(const std::vector& values, const std::wstring& argName)
-{
- static const std::vector supportedDrivers = {L"vhd"};
- for (const auto& value : values)
- {
- bool found = false;
- for (const auto& driver : supportedDrivers)
- {
- if (IsEqual(value, driver))
- {
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- throw ArgumentException(std::format(
- L"Invalid {} value: {} is not a recognized volume driver. Supported drivers are: vhd.", argName, value));
- }
- }
-}
-
// Convert string to WSLCSignal enum - accepts either signal name (e.g., "SIGKILL") or number (e.g., "9")
WSLCSignal GetWSLCSignalFromString(const std::wstring& input, const std::wstring& argName)
{
diff --git a/src/windows/wslc/commands/VolumeCreateCommand.cpp b/src/windows/wslc/commands/VolumeCreateCommand.cpp
index fd029541e..ea49c3142 100644
--- a/src/windows/wslc/commands/VolumeCreateCommand.cpp
+++ b/src/windows/wslc/commands/VolumeCreateCommand.cpp
@@ -27,9 +27,9 @@ namespace wsl::windows::wslc {
std::vector VolumeCreateCommand::GetArguments() const
{
return {
- Argument::Create(ArgType::VolumeName),
+ Argument::Create(ArgType::VolumeName, true),
Argument::Create(ArgType::Driver),
- Argument::Create(ArgType::Options),
+ Argument::Create(ArgType::Options, false, NO_LIMIT),
Argument::Create(ArgType::Session),
};
}
@@ -44,12 +44,10 @@ std::wstring VolumeCreateCommand::LongDescription() const
return Localization::WSLCCLI_VolumeCreateLongDesc();
}
-// clang-format off
void VolumeCreateCommand::ExecuteInternal(CLIExecutionContext& context) const
{
context
- << CreateSession
+ << CreateSession //
<< CreateVolume;
}
-// clang-format on
} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/tasks/VolumeTasks.cpp b/src/windows/wslc/tasks/VolumeTasks.cpp
index 370ed9a50..2710c8501 100644
--- a/src/windows/wslc/tasks/VolumeTasks.cpp
+++ b/src/windows/wslc/tasks/VolumeTasks.cpp
@@ -29,24 +29,43 @@ using namespace wsl::windows::wslc::services;
namespace wsl::windows::wslc::task {
+static std::string OptionsToJson(const std::vector& options)
+{
+ std::map result{};
+ for (const auto& option : options)
+ {
+ auto pos = option.find('=');
+ if (pos == std::wstring::npos)
+ {
+ result[WideToMultiByte(option)] = {};
+ }
+ else
+ {
+ auto key = WideToMultiByte(option.substr(0, pos));
+ auto value = WideToMultiByte(option.substr(pos + 1));
+ result[key] = value;
+ }
+ }
+
+ return ToJson(result);
+}
+
void CreateVolume(CLIExecutionContext& context)
{
WI_ASSERT(context.Data.Contains(Data::Session));
+ WI_ASSERT(context.Args.Contains(ArgType::VolumeName));
auto name = WideToMultiByte(context.Args.Get());
+
+ // Driver option (default "vhd")
std::string type = "vhd";
if (context.Args.Contains(ArgType::Driver))
{
type = WideToMultiByte(context.Args.Get());
}
- std::string options{};
- if(context.Args.Contains(ArgType::Options))
- {
- options = WideToMultiByte(context.Args.Get());
- }
-
- VolumeService::Create(context.Data.Get(), name, type, options);
+ auto optionsJson = OptionsToJson(context.Args.GetAll());
+ VolumeService::Create(context.Data.Get(), name, type, optionsJson);
PrintMessage(MultiByteToWide(name));
}
From 5029cf8d39aa67da7ac1525addad48c565aad961 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Mon, 13 Apr 2026 20:56:13 -0700
Subject: [PATCH 04/13] CLang format
---
src/windows/wslc/commands/VolumeCreateCommand.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/windows/wslc/commands/VolumeCreateCommand.cpp b/src/windows/wslc/commands/VolumeCreateCommand.cpp
index ea49c3142..fcbe26325 100644
--- a/src/windows/wslc/commands/VolumeCreateCommand.cpp
+++ b/src/windows/wslc/commands/VolumeCreateCommand.cpp
@@ -46,8 +46,7 @@ std::wstring VolumeCreateCommand::LongDescription() const
void VolumeCreateCommand::ExecuteInternal(CLIExecutionContext& context) const
{
- context
- << CreateSession //
- << CreateVolume;
+ context << CreateSession //
+ << CreateVolume;
}
} // namespace wsl::windows::wslc
From 506a52c1812fe1befaa7c2d021bf0cec665c2b0e Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 14 Apr 2026 10:59:30 -0700
Subject: [PATCH 05/13] Volume command
---
src/windows/wslc/commands/VolumeInspectCommand.cpp | 7 ++-----
src/windows/wslc/commands/VolumeListCommand.cpp | 9 +++------
src/windows/wslc/services/VolumeService.cpp | 1 -
src/windows/wslc/tasks/VolumeTasks.cpp | 5 ++---
4 files changed, 7 insertions(+), 15 deletions(-)
diff --git a/src/windows/wslc/commands/VolumeInspectCommand.cpp b/src/windows/wslc/commands/VolumeInspectCommand.cpp
index c84c2d4bb..0c0d196b0 100644
--- a/src/windows/wslc/commands/VolumeInspectCommand.cpp
+++ b/src/windows/wslc/commands/VolumeInspectCommand.cpp
@@ -42,12 +42,9 @@ std::wstring VolumeInspectCommand::LongDescription() const
return Localization::WSLCCLI_VolumeInspectLongDesc();
}
-// clang-format off
void VolumeInspectCommand::ExecuteInternal(CLIExecutionContext& context) const
{
- context
- << CreateSession
- << InspectVolumes;
+ context << CreateSession //
+ << InspectVolumes;
}
-// clang-format on
} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/commands/VolumeListCommand.cpp b/src/windows/wslc/commands/VolumeListCommand.cpp
index ccbfed72c..460610fc1 100644
--- a/src/windows/wslc/commands/VolumeListCommand.cpp
+++ b/src/windows/wslc/commands/VolumeListCommand.cpp
@@ -56,13 +56,10 @@ void VolumeListCommand::ValidateArgumentsInternal(const ArgMap& execArgs) const
}
}
-// clang-format off
void VolumeListCommand::ExecuteInternal(CLIExecutionContext& context) const
{
- context
- << CreateSession
- << GetVolumes
- << ListVolumes;
+ context << CreateSession //
+ << GetVolumes //
+ << ListVolumes;
}
-// clang-format on
} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/services/VolumeService.cpp b/src/windows/wslc/services/VolumeService.cpp
index 16c3aad94..305625592 100644
--- a/src/windows/wslc/services/VolumeService.cpp
+++ b/src/windows/wslc/services/VolumeService.cpp
@@ -58,5 +58,4 @@ wsl::windows::common::wslc_schema::InspectVolume VolumeService::Inspect(models::
THROW_IF_FAILED(session.Get()->InspectVolume(name.c_str(), &output));
return FromJson(output.get());
}
-
} // namespace wsl::windows::wslc::services
diff --git a/src/windows/wslc/tasks/VolumeTasks.cpp b/src/windows/wslc/tasks/VolumeTasks.cpp
index 2710c8501..490fff15b 100644
--- a/src/windows/wslc/tasks/VolumeTasks.cpp
+++ b/src/windows/wslc/tasks/VolumeTasks.cpp
@@ -134,12 +134,12 @@ void ListVolumes(CLIExecutionContext& context)
}
case FormatType::Table:
{
- auto table = wsl::windows::wslc::TableOutput<2>({L"VOLUME NAME", L"DRIVER"});
+ auto table = wsl::windows::wslc::TableOutput<2>({L"DRIVER", L"VOLUME NAME"});
for (const auto& volume : volumes)
{
table.OutputLine({
- MultiByteToWide(volume.Name),
MultiByteToWide(volume.Type),
+ MultiByteToWide(volume.Name),
});
}
@@ -150,5 +150,4 @@ void ListVolumes(CLIExecutionContext& context)
THROW_HR(E_UNEXPECTED);
}
}
-
} // namespace wsl::windows::wslc::task
From 02ba8898d8fa41683e0e133c51103ec742db3261 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 14 Apr 2026 13:46:10 -0700
Subject: [PATCH 06/13] Added E2E tests
---
.../wslc/commands/VolumeListCommand.cpp | 4 +-
.../wslc/e2e/WSLCE2EContainerCreateTests.cpp | 10 +-
.../wslc/e2e/WSLCE2EContainerRunTests.cpp | 22 +--
test/windows/wslc/e2e/WSLCE2EHelpers.cpp | 56 +++++++
test/windows/wslc/e2e/WSLCE2EHelpers.h | 4 +
.../wslc/e2e/WSLCE2EVolumeCreateTests.cpp | 127 +++++++++++++++
.../wslc/e2e/WSLCE2EVolumeInspectTests.cpp | 144 ++++++++++++++++++
.../wslc/e2e/WSLCE2EVolumeListTests.cpp | 137 +++++++++++++++++
.../wslc/e2e/WSLCE2EVolumeRemoveTests.cpp | 139 +++++++++++++++++
test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp | 101 ++++++++++++
10 files changed, 717 insertions(+), 27 deletions(-)
create mode 100644 test/windows/wslc/e2e/WSLCE2EVolumeCreateTests.cpp
create mode 100644 test/windows/wslc/e2e/WSLCE2EVolumeInspectTests.cpp
create mode 100644 test/windows/wslc/e2e/WSLCE2EVolumeListTests.cpp
create mode 100644 test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp
create mode 100644 test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp
diff --git a/src/windows/wslc/commands/VolumeListCommand.cpp b/src/windows/wslc/commands/VolumeListCommand.cpp
index 460610fc1..8dafd47c9 100644
--- a/src/windows/wslc/commands/VolumeListCommand.cpp
+++ b/src/windows/wslc/commands/VolumeListCommand.cpp
@@ -29,7 +29,7 @@ std::vector VolumeListCommand::GetArguments() const
{
return {
Argument::Create(ArgType::Format),
- Argument::Create(ArgType::Quiet),
+ Argument::Create(ArgType::Quiet, false, std::nullopt, L"Outputs the volume names only"),
Argument::Create(ArgType::Session),
};
}
@@ -59,7 +59,7 @@ void VolumeListCommand::ValidateArgumentsInternal(const ArgMap& execArgs) const
void VolumeListCommand::ExecuteInternal(CLIExecutionContext& context) const
{
context << CreateSession //
- << GetVolumes //
+ << GetVolumes //
<< ListVolumes;
}
} // namespace wsl::windows::wslc
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
index 271c0b843..70acf7b0a 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
@@ -552,10 +552,7 @@ class WSLCE2EContainerCreateTests
result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = S_OK});
// Read data back from the volume using another container
- result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} cat /data/testfile",
- volumeName,
- AlpineImage.NameAndTag()));
+ result = RunWslc(std::format(L"container run --rm --volume {}:/data {} cat /data/testfile", volumeName, AlpineImage.NameAndTag()));
result.Verify({.Stdout = L"named_volume_data", .Stderr = L"", .ExitCode = S_OK});
// Cleanup
@@ -589,10 +586,7 @@ class WSLCE2EContainerCreateTests
RunWslc(std::format(L"container rm {}", WslcContainerName));
// Read data using second container
- result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} cat /data/testfile",
- volumeName,
- AlpineImage.NameAndTag()));
+ result = RunWslc(std::format(L"container run --rm --volume {}:/data {} cat /data/testfile", volumeName, AlpineImage.NameAndTag()));
result.Verify({.Stdout = L"persist_test", .Stderr = L"", .ExitCode = S_OK});
// Cleanup
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
index 277e6eb23..c9481dcbe 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
@@ -549,16 +549,11 @@ class WSLCE2EContainerRunTests
// Write data to the named volume
result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} sh -c \"echo -n 'run_vol_test' > /data/testfile\"",
- volumeName,
- DebianImage.NameAndTag()));
+ L"container run --rm --volume {}:/data {} sh -c \"echo -n 'run_vol_test' > /data/testfile\"", volumeName, DebianImage.NameAndTag()));
result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
// Read data back from the named volume in a new container
- result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} cat /data/testfile",
- volumeName,
- DebianImage.NameAndTag()));
+ result = RunWslc(std::format(L"container run --rm --volume {}:/data {} cat /data/testfile", volumeName, DebianImage.NameAndTag()));
result.Verify({.Stdout = L"run_vol_test", .Stderr = L"", .ExitCode = 0});
// Cleanup
@@ -579,23 +574,16 @@ class WSLCE2EContainerRunTests
// Write data with first container
result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} sh -c \"echo -n 'first' > /data/testfile\"",
- volumeName,
- DebianImage.NameAndTag()));
+ L"container run --rm --volume {}:/data {} sh -c \"echo -n 'first' > /data/testfile\"", volumeName, DebianImage.NameAndTag()));
result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
// Append data with second container
result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} sh -c \"echo -n '_second' >> /data/testfile\"",
- volumeName,
- DebianImage.NameAndTag()));
+ L"container run --rm --volume {}:/data {} sh -c \"echo -n '_second' >> /data/testfile\"", volumeName, DebianImage.NameAndTag()));
result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
// Read combined data with third container
- result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} cat /data/testfile",
- volumeName,
- DebianImage.NameAndTag()));
+ result = RunWslc(std::format(L"container run --rm --volume {}:/data {} cat /data/testfile", volumeName, DebianImage.NameAndTag()));
result.Verify({.Stdout = L"first_second", .Stderr = L"", .ExitCode = 0});
// Cleanup
diff --git a/test/windows/wslc/e2e/WSLCE2EHelpers.cpp b/test/windows/wslc/e2e/WSLCE2EHelpers.cpp
index f0e0e2420..4f376ad75 100644
--- a/test/windows/wslc/e2e/WSLCE2EHelpers.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EHelpers.cpp
@@ -14,6 +14,7 @@ Module Name:
#include "precomp.h"
#include "SessionModel.h"
#include "ImageModel.h"
+#include "VolumeModel.h"
#include "windows/Common.h"
#include "WSLCExecutor.h"
#include "WSLCE2EHelpers.h"
@@ -212,6 +213,36 @@ void VerifyImageIsListed(const TestImage& image)
VERIFY_FAIL(std::format(L"Image '{}' not found in image list output", image.NameAndTag()).c_str());
}
+void VerifyVolumeIsListed(const std::wstring& volumeName)
+{
+ auto result = RunWslc(L"volume list --format json");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ auto volumes = wsl::shared::FromJson>(result.Stdout.value().c_str());
+ for (const auto& vol : volumes)
+ {
+ if (vol.Name == wsl::shared::string::WideToMultiByte(volumeName))
+ {
+ return;
+ }
+ }
+
+ VERIFY_FAIL(std::format(L"Volume '{}' not found in volume list output", volumeName).c_str());
+}
+
+void VerifyVolumeIsNotListed(const std::wstring& volumeName)
+{
+ auto result = RunWslc(L"volume list --format json");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ auto volumes = wsl::shared::FromJson>(result.Stdout.value().c_str());
+ for (const auto& vol : volumes)
+ {
+ if (vol.Name == wsl::shared::string::WideToMultiByte(volumeName))
+ {
+ VERIFY_FAIL(std::format(L"Volume '{}' found in volume list output", volumeName).c_str());
+ }
+ }
+}
+
std::string GetHashId(const std::string& id, bool fullId)
{
return wsl::windows::common::string::TruncateId(id, !fullId);
@@ -235,6 +266,15 @@ wslc_schema::InspectImage InspectImage(const std::wstring& imageName)
return inspectData[0];
}
+wslc_schema::InspectVolume InspectVolume(const std::wstring& volumeName)
+{
+ auto result = RunWslc(std::format(L"volume inspect {}", volumeName));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ auto inspectData = wsl::shared::FromJson>(result.Stdout.value().c_str());
+ VERIFY_ARE_EQUAL(1u, inspectData.size());
+ return inspectData[0];
+}
+
void EnsureContainerDoesNotExist(const std::wstring& containerName)
{
auto listResult = RunWslc(L"container list --no-trunc --all");
@@ -360,6 +400,22 @@ void EnsureSessionIsTerminated(const std::wstring& sessionName)
}
}
+void EnsureVolumeDoesNotExist(const std::wstring& volumeName)
+{
+ auto result = RunWslc(L"volume list --format json");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ auto volumes = wsl::shared::FromJson>(result.Stdout.value().c_str());
+ for (const auto& vol : volumes)
+ {
+ if (vol.Name == wsl::shared::string::WideToMultiByte(volumeName))
+ {
+ auto deleteResult = RunWslc(std::format(L"volume rm {}", volumeName));
+ deleteResult.Verify({.Stderr = L"", .ExitCode = 0});
+ break;
+ }
+ }
+}
+
void WriteTestFile(const std::filesystem::path& filePath, const std::vector& lines)
{
std::ofstream file(filePath, std::ios::out | std::ios::trunc | std::ios::binary);
diff --git a/test/windows/wslc/e2e/WSLCE2EHelpers.h b/test/windows/wslc/e2e/WSLCE2EHelpers.h
index a09bdefec..06f97177a 100644
--- a/test/windows/wslc/e2e/WSLCE2EHelpers.h
+++ b/test/windows/wslc/e2e/WSLCE2EHelpers.h
@@ -120,10 +120,13 @@ void VerifyContainerIsListed(const std::wstring& containerName, const std::wstri
void VerifyImageIsUsed(const TestImage& image);
void VerifyImageIsNotUsed(const TestImage& image);
void VerifyImageIsListed(const TestImage& image);
+void VerifyVolumeIsListed(const std::wstring& volumeName);
+void VerifyVolumeIsNotListed(const std::wstring& volumeName);
std::string GetHashId(const std::string& id, bool fullId = false);
wsl::windows::common::wslc_schema::InspectContainer InspectContainer(const std::wstring& containerName);
wsl::windows::common::wslc_schema::InspectImage InspectImage(const std::wstring& imageName);
+wsl::windows::common::wslc_schema::InspectVolume InspectVolume(const std::wstring& volumeName);
std::vector ListAllContainers();
void EnsureContainerDoesNotExist(const std::wstring& containerName);
@@ -131,6 +134,7 @@ void EnsureImageIsLoaded(const TestImage& image, const std::wstring& sessionName
void EnsureImageIsDeleted(const TestImage& image);
void EnsureImageContainersAreDeleted(const TestImage& image);
void EnsureSessionIsTerminated(const std::wstring& sessionName = L"");
+void EnsureVolumeDoesNotExist(const std::wstring& volumeName);
void WriteTestFile(const std::filesystem::path& filePath, const std::vector& envVariableLines);
std::wstring GetPythonHttpServerScript(uint16_t port);
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeCreateTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeCreateTests.cpp
new file mode 100644
index 000000000..4aef23da2
--- /dev/null
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeCreateTests.cpp
@@ -0,0 +1,127 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ WSLCE2EVolumeCreateTests.cpp
+
+Abstract:
+
+ This file contains end-to-end tests for WSLC.
+--*/
+
+#include "precomp.h"
+#include "windows/Common.h"
+#include "VolumeModel.h"
+#include "WSLCExecutor.h"
+#include "WSLCE2EHelpers.h"
+
+namespace WSLCE2ETests {
+using namespace wsl::shared;
+using namespace wsl::windows::wslc::models;
+
+class WSLCE2EVolumeCreateTests
+{
+ WSLC_TEST_CLASS(WSLCE2EVolumeCreateTests)
+
+ TEST_METHOD_SETUP(MethodSetup)
+ {
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ return true;
+ }
+
+ TEST_CLASS_CLEANUP(ClassCleanup)
+ {
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ return true;
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Create_HelpCommand)
+ {
+ auto result = RunWslc(L"volume create --help");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Create_MissingVolumeName)
+ {
+ auto result = RunWslc(L"volume create");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Required argument not provided: 'volume-name'\r\n", .ExitCode = 1});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Create_DefaultDriver_Success)
+ {
+ auto result = RunWslc(std::format(L"volume create --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_ARE_EQUAL(TestVolumeName, result.GetStdoutOneLine());
+
+ VerifyVolumeIsListed(TestVolumeName);
+ auto inspect = InspectVolume(TestVolumeName);
+ VERIFY_ARE_EQUAL("vhd", inspect.Type);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Create_ExplicitDriver_Success)
+ {
+ auto result = RunWslc(std::format(L"volume create --driver vhd --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_ARE_EQUAL(TestVolumeName, result.GetStdoutOneLine());
+
+ VerifyVolumeIsListed(TestVolumeName);
+ auto inspect = InspectVolume(TestVolumeName);
+ VERIFY_ARE_EQUAL("vhd", inspect.Type);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Create_InvalidDriver)
+ {
+ auto result = RunWslc(std::format(L"volume create --driver invalid_driver --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName));
+ result.Verify({.Stdout = L"", .Stderr = L"Unsupported volume type: 'invalid_driver'\r\nError code: E_INVALIDARG\r\n", .ExitCode = 1});
+ VerifyVolumeIsNotListed(TestVolumeName);
+ }
+
+private:
+ const std::wstring TestVolumeName = L"wslc-e2e-volume-create";
+ const int DefaultVolumeSizeBytes = 3 * 1024 * 1024;
+
+ std::wstring GetHelpMessage() const
+ {
+ std::wstringstream output;
+ output << GetWslcHeader() //
+ << GetDescription() //
+ << GetUsage() //
+ << GetAvailableCommands() //
+ << GetAvailableOptions();
+ return output.str();
+ }
+
+ std::wstring GetDescription() const
+ {
+ return Localization::WSLCCLI_VolumeCreateLongDesc() + L"\r\n\r\n";
+ }
+
+ std::wstring GetUsage() const
+ {
+ return L"Usage: wslc volume create [] \r\n\r\n";
+ }
+
+ std::wstring GetAvailableCommands() const
+ {
+ std::wstringstream commands;
+ commands << L"The following arguments are available:\r\n" //
+ << L" volume-name Volume name\r\n" //
+ << L"\r\n";
+ return commands.str();
+ }
+
+ std::wstring GetAvailableOptions() const
+ {
+ std::wstringstream options;
+ options << L"The following options are available:\r\n" //
+ << L" -d,--driver Specify volume driver name (default vhd)\r\n" //
+ << L" -o,--opt Set driver specific options\r\n" //
+ << L" --session Specify the session to use\r\n" //
+ << L" -h,--help Shows help about the selected command\r\n" //
+ << L"\r\n";
+ return options.str();
+ }
+};
+} // namespace WSLCE2ETests
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeInspectTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeInspectTests.cpp
new file mode 100644
index 000000000..6ae4040e4
--- /dev/null
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeInspectTests.cpp
@@ -0,0 +1,144 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ WSLCE2EVolumeInspectTests.cpp
+
+Abstract:
+
+ This file contains end-to-end tests for WSLC.
+--*/
+
+#include "precomp.h"
+#include "windows/Common.h"
+#include "WSLCExecutor.h"
+#include "WSLCE2EHelpers.h"
+
+namespace WSLCE2ETests {
+using namespace wsl::shared;
+using namespace wsl::shared::string;
+
+class WSLCE2EVolumeInspectTests
+{
+ WSLC_TEST_CLASS(WSLCE2EVolumeInspectTests)
+
+ TEST_METHOD_SETUP(MethodSetup)
+ {
+ EnsureVolumeDoesNotExist(TestVolumeName1);
+ EnsureVolumeDoesNotExist(TestVolumeName2);
+ return true;
+ }
+
+ TEST_CLASS_CLEANUP(ClassCleanup)
+ {
+ EnsureVolumeDoesNotExist(TestVolumeName1);
+ EnsureVolumeDoesNotExist(TestVolumeName2);
+ return true;
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Inspect_HelpCommand)
+ {
+ auto result = RunWslc(L"volume inspect --help");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Inspect_MissingVolumeName)
+ {
+ auto result = RunWslc(L"volume inspect");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Required argument not provided: 'volume-name'\r\n", .ExitCode = 1});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Inspect_Success)
+ {
+ auto result = RunWslc(std::format(L"volume create --driver vhd --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName1));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_ARE_EQUAL(TestVolumeName1, result.GetStdoutOneLine());
+
+ result = RunWslc(std::format(L"volume inspect {}", TestVolumeName1));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ auto inspectData = wsl::shared::FromJson>(result.Stdout.value().c_str());
+ VERIFY_ARE_EQUAL(1u, inspectData.size());
+ auto inspect = inspectData[0];
+
+ VERIFY_ARE_EQUAL(WideToMultiByte(TestVolumeName1), inspect.Name);
+ VERIFY_ARE_EQUAL("vhd", inspect.Type);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_InspectMultiple_Success)
+ {
+ auto result = RunWslc(std::format(L"volume create --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName1));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_ARE_EQUAL(TestVolumeName1, result.GetStdoutOneLine());
+ result = RunWslc(std::format(L"volume create --driver vhd --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName2));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_ARE_EQUAL(TestVolumeName2, result.GetStdoutOneLine());
+
+ result = RunWslc(std::format(L"volume inspect {} {}", TestVolumeName1, TestVolumeName2));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ auto inspectData = wsl::shared::FromJson>(result.Stdout.value().c_str());
+ VERIFY_ARE_EQUAL(2u, inspectData.size());
+
+ auto inspect1 = inspectData[0];
+ VERIFY_ARE_EQUAL(WideToMultiByte(TestVolumeName1), inspect1.Name);
+ VERIFY_ARE_EQUAL("vhd", inspect1.Type);
+
+ auto inspect2 = inspectData[1];
+ VERIFY_ARE_EQUAL(WideToMultiByte(TestVolumeName2), inspect2.Name);
+ VERIFY_ARE_EQUAL("vhd", inspect2.Type);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Inspect_NotFound)
+ {
+ auto result = RunWslc(std::format(L"volume inspect {}", TestVolumeName1));
+ result.Verify(
+ {.Stdout = L"", .Stderr = std::format(L"Volume not found: '{}'\r\nError code: WSLC_E_VOLUME_NOT_FOUND\r\n", TestVolumeName1), .ExitCode = 1});
+ }
+
+private:
+ const std::wstring TestVolumeName1 = L"wslc-e2e-volume-inspect-1";
+ const std::wstring TestVolumeName2 = L"wslc-e2e-volume-inspect-2";
+ const int DefaultVolumeSizeBytes = 3 * 1024 * 1024;
+
+ std::wstring GetHelpMessage() const
+ {
+ std::wstringstream output;
+ output << GetWslcHeader() //
+ << GetDescription() //
+ << GetUsage() //
+ << GetAvailableCommands() //
+ << GetAvailableOptions();
+ return output.str();
+ }
+
+ std::wstring GetDescription() const
+ {
+ return Localization::WSLCCLI_VolumeInspectLongDesc() + L"\r\n\r\n";
+ }
+
+ std::wstring GetUsage() const
+ {
+ return L"Usage: wslc volume inspect [] \r\n\r\n";
+ }
+
+ std::wstring GetAvailableCommands() const
+ {
+ std::wstringstream commands;
+ commands << L"The following arguments are available:\r\n" //
+ << L" volume-name Volume name\r\n" //
+ << L"\r\n";
+ return commands.str();
+ }
+
+ std::wstring GetAvailableOptions() const
+ {
+ std::wstringstream options;
+ options << L"The following options are available:\r\n" //
+ << L" --session Specify the session to use\r\n" //
+ << L" -h,--help Shows help about the selected command\r\n" //
+ << L"\r\n";
+ return options.str();
+ }
+};
+} // namespace WSLCE2ETests
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeListTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeListTests.cpp
new file mode 100644
index 000000000..3f7efde88
--- /dev/null
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeListTests.cpp
@@ -0,0 +1,137 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ WSLCE2EVolumeListTests.cpp
+
+Abstract:
+
+ This file contains end-to-end tests for WSLC.
+--*/
+
+#include "precomp.h"
+#include "windows/Common.h"
+#include "VolumeModel.h"
+#include "WSLCExecutor.h"
+#include "WSLCE2EHelpers.h"
+
+namespace WSLCE2ETests {
+using namespace wsl::shared;
+using namespace wsl::shared::string;
+using namespace wsl::windows::wslc::models;
+
+class WSLCE2EVolumeListTests
+{
+ WSLC_TEST_CLASS(WSLCE2EVolumeListTests)
+
+ TEST_METHOD_SETUP(MethodSetup)
+ {
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ EnsureVolumeDoesNotExist(TestVolumeName2);
+ return true;
+ }
+
+ TEST_CLASS_CLEANUP(ClassCleanup)
+ {
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ EnsureVolumeDoesNotExist(TestVolumeName2);
+ return true;
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_List_HelpCommand)
+ {
+ auto result = RunWslc(L"volume list --help");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_List_InvalidFormatOption)
+ {
+ auto result = RunWslc(L"volume list --format invalid");
+ result.Verify({.Stderr = L"Invalid format value: invalid is not a recognized format type. Supported format types are: json, table.\r\n", .ExitCode = 1});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_List_QuietOption_OutputsNamesOnly)
+ {
+ auto result = RunWslc(std::format(L"volume create --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ result = RunWslc(std::format(L"volume create --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName2));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ result = RunWslc(L"volume list --quiet");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ auto lines = result.GetStdoutLines();
+ VERIFY_ARE_NOT_EQUAL(lines.end(), std::find(lines.begin(), lines.end(), TestVolumeName));
+ VERIFY_ARE_NOT_EQUAL(lines.end(), std::find(lines.begin(), lines.end(), TestVolumeName2));
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_List_JsonFormat)
+ {
+ auto result = RunWslc(std::format(L"volume create --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ result = RunWslc(std::format(L"volume create --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName2));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ result = RunWslc(L"volume list --format json");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ auto volumes = FromJson>(result.Stdout.value().c_str());
+ VERIFY_ARE_EQUAL(2U, volumes.size());
+
+ std::vector names;
+ names.reserve(volumes.size());
+ for (const auto& volume : volumes)
+ {
+ names.push_back(volume.Name);
+ }
+
+ VERIFY_ARE_NOT_EQUAL(names.end(), std::find(names.begin(), names.end(), WideToMultiByte(TestVolumeName)));
+ VERIFY_ARE_NOT_EQUAL(names.end(), std::find(names.begin(), names.end(), WideToMultiByte(TestVolumeName2)));
+ }
+
+private:
+ const std::wstring TestVolumeName = L"wslc-e2e-volume-list";
+ const std::wstring TestVolumeName2 = L"wslc-e2e-volume-list-2";
+ const int DefaultVolumeSizeBytes = 3 * 1024 * 1024;
+
+ std::wstring GetHelpMessage() const
+ {
+ std::wstringstream output;
+ output << GetWslcHeader() //
+ << GetDescription() //
+ << GetUsage() //
+ << GetAvailableCommandAliases() //
+ << GetAvailableOptions();
+ return output.str();
+ }
+
+ std::wstring GetDescription() const
+ {
+ return Localization::WSLCCLI_VolumeListLongDesc() + L"\r\n\r\n";
+ }
+
+ std::wstring GetUsage() const
+ {
+ return L"Usage: wslc volume list []\r\n\r\n";
+ }
+
+ std::wstring GetAvailableCommandAliases() const
+ {
+ return L"The following command aliases are available: ls\r\n\r\n";
+ }
+
+ std::wstring GetAvailableOptions() const
+ {
+ std::wstringstream options;
+ options << L"The following options are available:\r\n" //
+ << L" --format " << Localization::WSLCCLI_FormatArgDescription() << L"\r\n"
+ << L" -q,--quiet Outputs the volume names only\r\n" //
+ << L" --session Specify the session to use\r\n" //
+ << L" -h,--help Shows help about the selected command\r\n" //
+ << L"\r\n";
+ return options.str();
+ }
+};
+} // namespace WSLCE2ETests
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp
new file mode 100644
index 000000000..1f77c926d
--- /dev/null
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp
@@ -0,0 +1,139 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ WSLCE2EVolumeRemoveTests.cpp
+
+Abstract:
+
+ This file contains end-to-end tests for WSLC.
+--*/
+
+#include "precomp.h"
+#include "windows/Common.h"
+#include "WSLCExecutor.h"
+#include "WSLCE2EHelpers.h"
+
+namespace WSLCE2ETests {
+using namespace wsl::shared;
+
+class WSLCE2EVolumeRemoveTests
+{
+ WSLC_TEST_CLASS(WSLCE2EVolumeRemoveTests)
+
+ TEST_METHOD_SETUP(MethodSetup)
+ {
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ EnsureVolumeDoesNotExist(TestVolumeName2);
+ return true;
+ }
+
+ TEST_CLASS_CLEANUP(ClassCleanup)
+ {
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ EnsureVolumeDoesNotExist(TestVolumeName2);
+ return true;
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Remove_HelpCommand)
+ {
+ auto result = RunWslc(L"volume remove --help");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Remove_MissingVolumeName)
+ {
+ auto result = RunWslc(L"volume remove");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Required argument not provided: 'volume-name'\r\n", .ExitCode = 1});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Remove_Valid)
+ {
+ auto result = RunWslc(std::format(L"volume create --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ VerifyVolumeIsListed(TestVolumeName);
+
+ result = RunWslc(std::format(L"volume remove {}", TestVolumeName));
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
+
+ VerifyVolumeIsNotListed(TestVolumeName);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Remove_Multiple_Valid)
+ {
+ auto result = RunWslc(std::format(L"volume create --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ result = RunWslc(std::format(L"volume create --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName2));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ VerifyVolumeIsListed(TestVolumeName);
+ VerifyVolumeIsListed(TestVolumeName2);
+
+ result = RunWslc(std::format(L"volume remove {} {}", TestVolumeName, TestVolumeName2));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ VerifyVolumeIsNotListed(TestVolumeName);
+ VerifyVolumeIsNotListed(TestVolumeName2);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Remove_NotFound)
+ {
+ auto result = RunWslc(std::format(L"volume remove {}", TestVolumeName));
+ result.Verify({.Stdout = L"", .Stderr = std::format(L"Volume not found: '{}'\r\nError code: WSLC_E_VOLUME_NOT_FOUND\r\n", TestVolumeName), .ExitCode = 1});
+ }
+
+private:
+ const std::wstring TestVolumeName = L"wslc-e2e-volume-remove";
+ const std::wstring TestVolumeName2 = L"wslc-e2e-volume-remove-2";
+ const int DefaultVolumeSizeBytes = 3 * 1024 * 1024;
+
+ std::wstring GetHelpMessage() const
+ {
+ std::wstringstream output;
+ output << GetWslcHeader() //
+ << GetDescription() //
+ << GetUsage() //
+ << GetAvailableCommandAliases() //
+ << GetAvailableCommands() //
+ << GetAvailableOptions();
+ return output.str();
+ }
+
+ std::wstring GetDescription() const
+ {
+ return Localization::WSLCCLI_VolumeDeleteLongDesc() + L"\r\n\r\n";
+ }
+
+ std::wstring GetUsage() const
+ {
+ return L"Usage: wslc volume remove [] \r\n\r\n";
+ }
+
+ std::wstring GetAvailableCommandAliases() const
+ {
+ return L"The following command aliases are available: delete rm\r\n\r\n";
+ }
+
+ std::wstring GetAvailableCommands() const
+ {
+ std::wstringstream commands;
+ commands << L"The following arguments are available:\r\n" //
+ << L" volume-name Volume name\r\n" //
+ << L"\r\n";
+ return commands.str();
+ }
+
+ std::wstring GetAvailableOptions() const
+ {
+ std::wstringstream options;
+ options << L"The following options are available:\r\n" //
+ << L" --session Specify the session to use\r\n" //
+ << L" -h,--help Shows help about the selected command\r\n" //
+ << L"\r\n";
+ return options.str();
+ }
+};
+} // namespace WSLCE2ETests
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp
new file mode 100644
index 000000000..a0b20be68
--- /dev/null
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp
@@ -0,0 +1,101 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ WSLCE2EVolumeTests.cpp
+
+Abstract:
+
+ This file contains end-to-end tests for WSLC.
+--*/
+
+#include "precomp.h"
+#include "windows/Common.h"
+#include "WSLCExecutor.h"
+#include "WSLCE2EHelpers.h"
+#include "Argument.h"
+
+namespace WSLCE2ETests {
+using namespace wsl::shared;
+
+class WSLCE2EVolumeTests
+{
+ WSLC_TEST_CLASS(WSLCE2EVolumeTests)
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_HelpCommand)
+ {
+ auto result = RunWslc(L"volume --help");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_NoSubcommand_ShowsHelp)
+ {
+ auto result = RunWslc(L"volume");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_InvalidCommand_DisplaysErrorMessage)
+ {
+ auto result = RunWslc(L"volume INVALID_CMD");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Unrecognized command: 'INVALID_CMD'\r\n", .ExitCode = 1});
+ }
+
+private:
+ std::wstring GetHelpMessage() const
+ {
+ std::wstringstream output;
+ output << GetWslcHeader() //
+ << GetDescription() //
+ << GetUsage() //
+ << GetAvailableCommands() //
+ << GetAvailableOptions();
+ return output.str();
+ }
+
+ std::wstring GetDescription() const
+ {
+ return Localization::WSLCCLI_VolumeCommandLongDesc() + L"\r\n\r\n";
+ }
+
+ std::wstring GetUsage() const
+ {
+ return L"Usage: wslc volume [] []\r\n\r\n";
+ }
+
+ std::wstring GetAvailableCommands() const
+ {
+ std::vector> entries = {
+ {L"create", Localization::WSLCCLI_VolumeCreateDesc()},
+ {L"remove", Localization::WSLCCLI_VolumeDeleteDesc()},
+ {L"inspect", Localization::WSLCCLI_VolumeInspectDesc()},
+ {L"list", Localization::WSLCCLI_VolumeListDesc()},
+ };
+
+ size_t maxLen = 0;
+ for (const auto& [name, _] : entries)
+ {
+ maxLen = (std::max)(maxLen, name.size());
+ }
+
+ std::wstringstream commands;
+ commands << Localization::WSLCCLI_AvailableSubcommands() << L"\r\n";
+ for (const auto& [name, desc] : entries)
+ {
+ commands << L" " << name << std::wstring(maxLen - name.size() + 2, L' ') << desc << L"\r\n";
+ }
+ commands << L"\r\n" << Localization::WSLCCLI_HelpForDetails() << L" [" << WSLC_CLI_HELP_ARG_STRING << L"]\r\n\r\n";
+ return commands.str();
+ }
+
+ std::wstring GetAvailableOptions() const
+ {
+ std::wstringstream options;
+ options << L"The following options are available:\r\n"
+ << L" -h,--help Shows help about the selected command\r\n"
+ << L"\r\n";
+ return options.str();
+ }
+};
+} // namespace WSLCE2ETests
\ No newline at end of file
From f1f47fca3e1578005d42e07ad419ba22b4933e92 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 14 Apr 2026 13:46:34 -0700
Subject: [PATCH 07/13] Clang format
---
.../wslc/e2e/WSLCE2EVolumeCreateTests.cpp | 9 +++++----
.../wslc/e2e/WSLCE2EVolumeInspectTests.cpp | 16 ++++++++++------
.../wslc/e2e/WSLCE2EVolumeRemoveTests.cpp | 11 +++++++----
3 files changed, 22 insertions(+), 14 deletions(-)
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeCreateTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeCreateTests.cpp
index 4aef23da2..ee0e4b42f 100644
--- a/test/windows/wslc/e2e/WSLCE2EVolumeCreateTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeCreateTests.cpp
@@ -73,7 +73,8 @@ class WSLCE2EVolumeCreateTests
WSLC_TEST_METHOD(WSLCE2E_Volume_Create_InvalidDriver)
{
- auto result = RunWslc(std::format(L"volume create --driver invalid_driver --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName));
+ auto result =
+ RunWslc(std::format(L"volume create --driver invalid_driver --opt SizeBytes={} {}", DefaultVolumeSizeBytes, TestVolumeName));
result.Verify({.Stdout = L"", .Stderr = L"Unsupported volume type: 'invalid_driver'\r\nError code: E_INVALIDARG\r\n", .ExitCode = 1});
VerifyVolumeIsNotListed(TestVolumeName);
}
@@ -106,8 +107,8 @@ class WSLCE2EVolumeCreateTests
std::wstring GetAvailableCommands() const
{
std::wstringstream commands;
- commands << L"The following arguments are available:\r\n" //
- << L" volume-name Volume name\r\n" //
+ commands << L"The following arguments are available:\r\n" //
+ << L" volume-name Volume name\r\n" //
<< L"\r\n";
return commands.str();
}
@@ -115,7 +116,7 @@ class WSLCE2EVolumeCreateTests
std::wstring GetAvailableOptions() const
{
std::wstringstream options;
- options << L"The following options are available:\r\n" //
+ options << L"The following options are available:\r\n" //
<< L" -d,--driver Specify volume driver name (default vhd)\r\n" //
<< L" -o,--opt Set driver specific options\r\n" //
<< L" --session Specify the session to use\r\n" //
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeInspectTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeInspectTests.cpp
index 6ae4040e4..9b31e4320 100644
--- a/test/windows/wslc/e2e/WSLCE2EVolumeInspectTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeInspectTests.cpp
@@ -58,7 +58,8 @@ class WSLCE2EVolumeInspectTests
result = RunWslc(std::format(L"volume inspect {}", TestVolumeName1));
result.Verify({.Stderr = L"", .ExitCode = 0});
- auto inspectData = wsl::shared::FromJson>(result.Stdout.value().c_str());
+ auto inspectData =
+ wsl::shared::FromJson>(result.Stdout.value().c_str());
VERIFY_ARE_EQUAL(1u, inspectData.size());
auto inspect = inspectData[0];
@@ -77,7 +78,8 @@ class WSLCE2EVolumeInspectTests
result = RunWslc(std::format(L"volume inspect {} {}", TestVolumeName1, TestVolumeName2));
result.Verify({.Stderr = L"", .ExitCode = 0});
- auto inspectData = wsl::shared::FromJson>(result.Stdout.value().c_str());
+ auto inspectData =
+ wsl::shared::FromJson>(result.Stdout.value().c_str());
VERIFY_ARE_EQUAL(2u, inspectData.size());
auto inspect1 = inspectData[0];
@@ -93,7 +95,9 @@ class WSLCE2EVolumeInspectTests
{
auto result = RunWslc(std::format(L"volume inspect {}", TestVolumeName1));
result.Verify(
- {.Stdout = L"", .Stderr = std::format(L"Volume not found: '{}'\r\nError code: WSLC_E_VOLUME_NOT_FOUND\r\n", TestVolumeName1), .ExitCode = 1});
+ {.Stdout = L"",
+ .Stderr = std::format(L"Volume not found: '{}'\r\nError code: WSLC_E_VOLUME_NOT_FOUND\r\n", TestVolumeName1),
+ .ExitCode = 1});
}
private:
@@ -125,8 +129,8 @@ class WSLCE2EVolumeInspectTests
std::wstring GetAvailableCommands() const
{
std::wstringstream commands;
- commands << L"The following arguments are available:\r\n" //
- << L" volume-name Volume name\r\n" //
+ commands << L"The following arguments are available:\r\n" //
+ << L" volume-name Volume name\r\n" //
<< L"\r\n";
return commands.str();
}
@@ -134,7 +138,7 @@ class WSLCE2EVolumeInspectTests
std::wstring GetAvailableOptions() const
{
std::wstringstream options;
- options << L"The following options are available:\r\n" //
+ options << L"The following options are available:\r\n" //
<< L" --session Specify the session to use\r\n" //
<< L" -h,--help Shows help about the selected command\r\n" //
<< L"\r\n";
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp
index 1f77c926d..6871ff4fb 100644
--- a/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp
@@ -82,7 +82,10 @@ class WSLCE2EVolumeRemoveTests
WSLC_TEST_METHOD(WSLCE2E_Volume_Remove_NotFound)
{
auto result = RunWslc(std::format(L"volume remove {}", TestVolumeName));
- result.Verify({.Stdout = L"", .Stderr = std::format(L"Volume not found: '{}'\r\nError code: WSLC_E_VOLUME_NOT_FOUND\r\n", TestVolumeName), .ExitCode = 1});
+ result.Verify(
+ {.Stdout = L"",
+ .Stderr = std::format(L"Volume not found: '{}'\r\nError code: WSLC_E_VOLUME_NOT_FOUND\r\n", TestVolumeName),
+ .ExitCode = 1});
}
private:
@@ -120,8 +123,8 @@ class WSLCE2EVolumeRemoveTests
std::wstring GetAvailableCommands() const
{
std::wstringstream commands;
- commands << L"The following arguments are available:\r\n" //
- << L" volume-name Volume name\r\n" //
+ commands << L"The following arguments are available:\r\n" //
+ << L" volume-name Volume name\r\n" //
<< L"\r\n";
return commands.str();
}
@@ -129,7 +132,7 @@ class WSLCE2EVolumeRemoveTests
std::wstring GetAvailableOptions() const
{
std::wstringstream options;
- options << L"The following options are available:\r\n" //
+ options << L"The following options are available:\r\n" //
<< L" --session Specify the session to use\r\n" //
<< L" -h,--help Shows help about the selected command\r\n" //
<< L"\r\n";
From 7917b2ec4fd2d9374d8974cd47e291efabb29918 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 14 Apr 2026 17:10:48 -0700
Subject: [PATCH 08/13] Resolve copilot comment
---
localization/strings/en-US/Resources.resw | 8 ++++++--
src/windows/wslc/arguments/ArgumentDefinitions.h | 4 ++--
src/windows/wslc/services/VolumeModel.h | 1 -
src/windows/wslc/tasks/VolumeTasks.h | 14 +++++---------
.../wslc/e2e/WSLCE2EContainerCreateTests.cpp | 14 +++++++-------
5 files changed, 20 insertions(+), 21 deletions(-)
diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index ac3d3ced1..46a561b5a 100644
--- a/localization/strings/en-US/Resources.resw
+++ b/localization/strings/en-US/Resources.resw
@@ -2571,7 +2571,11 @@ On first run, creates the file with all settings commented out at their defaults
Volume name
-
- Specify volume driver name
+
+ Specify volume driver name (default {})
+ {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated
+
+
+ Set driver specific options
diff --git a/src/windows/wslc/arguments/ArgumentDefinitions.h b/src/windows/wslc/arguments/ArgumentDefinitions.h
index b2b7708d5..dc79e4a6e 100644
--- a/src/windows/wslc/arguments/ArgumentDefinitions.h
+++ b/src/windows/wslc/arguments/ArgumentDefinitions.h
@@ -45,7 +45,7 @@ _(Detach, "detach", L"d", Kind::Flag, L
/*_(DNSDomain, "dns-domain", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSDomainArgDescription())*/ \
/*_(DNSOption, "dns-option", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSOptionArgDescription())*/ \
/*_(DNSSearch, "dns-search", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSSearchArgDescription())*/ \
-_(Driver, "driver", L"d", Kind::Value, L"Specify volume driver name (default vhd)") \
+_(Driver, "driver", L"d", Kind::Value, Localization::WSLCCLI_DriverArgDescription("vhd")) \
_(Entrypoint, "entrypoint", NO_ALIAS, Kind::Value, Localization::WSLCCLI_EntrypointArgDescription()) \
_(Env, "env", L"e", Kind::Value, Localization::WSLCCLI_EnvArgDescription()) \
_(EnvFile, "env-file", NO_ALIAS, Kind::Value, Localization::WSLCCLI_EnvFileArgDescription()) \
@@ -63,7 +63,7 @@ _(Name, "name", NO_ALIAS, Kind::Value, L
/*_(NoDNS, "no-dns", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoDNSArgDescription())*/ \
_(NoPrune, "no-prune", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoPruneArgDescription()) \
_(NoTrunc, "no-trunc", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoTruncArgDescription()) \
-_(Options, "opt", L"o", Kind::Value, L"Set driver specific options") \
+_(Options, "opt", L"o", Kind::Value, Localization::WSLCCLI_OptionsArgDescription()) \
_(Output, "output", L"o", Kind::Value, Localization::WSLCCLI_OutputArgDescription()) \
_(Path, "path", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_PathArgDescription()) \
/*_(Progress, "progress", NO_ALIAS, Kind::Value, Localization::WSLCCLI_ProgressArgDescription())*/ \
diff --git a/src/windows/wslc/services/VolumeModel.h b/src/windows/wslc/services/VolumeModel.h
index 609eb31cf..5995850d8 100644
--- a/src/windows/wslc/services/VolumeModel.h
+++ b/src/windows/wslc/services/VolumeModel.h
@@ -14,7 +14,6 @@ Module Name:
#pragma once
-#include
#include
namespace wsl::windows::wslc::models {
diff --git a/src/windows/wslc/tasks/VolumeTasks.h b/src/windows/wslc/tasks/VolumeTasks.h
index b1c49acfd..d6fe39479 100644
--- a/src/windows/wslc/tasks/VolumeTasks.h
+++ b/src/windows/wslc/tasks/VolumeTasks.h
@@ -13,16 +13,12 @@ Module Name:
--*/
#pragma once
#include "CLIExecutionContext.h"
-#include "Task.h"
-
-using wsl::windows::wslc::execution::CLIExecutionContext;
namespace wsl::windows::wslc::task {
-void CreateVolume(CLIExecutionContext& context);
-void DeleteVolumes(CLIExecutionContext& context);
-void GetVolumes(CLIExecutionContext& context);
-void InspectVolumes(CLIExecutionContext& context);
-void ListVolumes(CLIExecutionContext& context);
-
+void CreateVolume(wsl::windows::wslc::execution::CLIExecutionContext& context);
+void DeleteVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
+void GetVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
+void InspectVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
+void ListVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
} // namespace wsl::windows::wslc::task
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
index 70acf7b0a..e1c06db27 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
@@ -542,18 +542,18 @@ class WSLCE2EContainerCreateTests
// Create the named volume
auto result = RunWslc(std::format(L"volume create {}", volumeName));
- result.Verify({.Stderr = L"", .ExitCode = S_OK});
+ result.Verify({.Stderr = L"", .ExitCode = 0});
// Write data to the volume using a container
result = RunWslc(std::format(
L"container run --rm --volume {}:/data {} sh -c \"echo -n 'named_volume_data' > /data/testfile\"",
volumeName,
AlpineImage.NameAndTag()));
- result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = S_OK});
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
// Read data back from the volume using another container
result = RunWslc(std::format(L"container run --rm --volume {}:/data {} cat /data/testfile", volumeName, AlpineImage.NameAndTag()));
- result.Verify({.Stdout = L"named_volume_data", .Stderr = L"", .ExitCode = S_OK});
+ result.Verify({.Stdout = L"named_volume_data", .Stderr = L"", .ExitCode = 0});
// Cleanup
RunWslc(std::format(L"volume rm {}", volumeName));
@@ -569,7 +569,7 @@ class WSLCE2EContainerCreateTests
// Create the named volume
auto result = RunWslc(std::format(L"volume create {}", volumeName));
- result.Verify({.Stderr = L"", .ExitCode = S_OK});
+ result.Verify({.Stderr = L"", .ExitCode = 0});
// Write data using first container
result = RunWslc(std::format(
@@ -577,17 +577,17 @@ class WSLCE2EContainerCreateTests
WslcContainerName,
volumeName,
AlpineImage.NameAndTag()));
- result.Verify({.Stderr = L"", .ExitCode = S_OK});
+ result.Verify({.Stderr = L"", .ExitCode = 0});
result = RunWslc(std::format(L"container start -a {}", WslcContainerName));
- result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = S_OK});
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
// Remove first container
RunWslc(std::format(L"container rm {}", WslcContainerName));
// Read data using second container
result = RunWslc(std::format(L"container run --rm --volume {}:/data {} cat /data/testfile", volumeName, AlpineImage.NameAndTag()));
- result.Verify({.Stdout = L"persist_test", .Stderr = L"", .ExitCode = S_OK});
+ result.Verify({.Stdout = L"persist_test", .Stderr = L"", .ExitCode = 0});
// Cleanup
RunWslc(std::format(L"volume rm {}", volumeName));
From 87834577776085e85d50567c0eac827eaf891d2d Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 14 Apr 2026 17:20:30 -0700
Subject: [PATCH 09/13] Revert test in run/create
---
.../wslc/e2e/WSLCE2EContainerCreateTests.cpp | 61 -------------------
.../wslc/e2e/WSLCE2EContainerRunTests.cpp | 55 -----------------
2 files changed, 116 deletions(-)
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
index e1c06db27..9497f9c91 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
@@ -532,67 +532,6 @@ class WSLCE2EContainerCreateTests
result.Verify({.Stderr = L"invalid mount path: '' mount path must be absolute\r\nError code: E_FAIL\r\n", .ExitCode = 1});
}
- WSLC_TEST_METHOD(WSLCE2E_Container_Create_NamedVolume_WriteFromContainerReadFromContainer)
- {
-
- const std::wstring volumeName = L"wslc-test-vol-create";
-
- // Cleanup any pre-existing volume
- RunWslc(std::format(L"volume rm {}", volumeName));
-
- // Create the named volume
- auto result = RunWslc(std::format(L"volume create {}", volumeName));
- result.Verify({.Stderr = L"", .ExitCode = 0});
-
- // Write data to the volume using a container
- result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} sh -c \"echo -n 'named_volume_data' > /data/testfile\"",
- volumeName,
- AlpineImage.NameAndTag()));
- result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
-
- // Read data back from the volume using another container
- result = RunWslc(std::format(L"container run --rm --volume {}:/data {} cat /data/testfile", volumeName, AlpineImage.NameAndTag()));
- result.Verify({.Stdout = L"named_volume_data", .Stderr = L"", .ExitCode = 0});
-
- // Cleanup
- RunWslc(std::format(L"volume rm {}", volumeName));
- }
-
- WSLC_TEST_METHOD(WSLCE2E_Container_Create_NamedVolume_PersistsBetweenContainers)
- {
-
- const std::wstring volumeName = L"wslc-test-vol-persist";
-
- // Cleanup any pre-existing volume
- RunWslc(std::format(L"volume rm {}", volumeName));
-
- // Create the named volume
- auto result = RunWslc(std::format(L"volume create {}", volumeName));
- result.Verify({.Stderr = L"", .ExitCode = 0});
-
- // Write data using first container
- result = RunWslc(std::format(
- L"container create --name {} --volume {}:/data {} sh -c \"echo -n 'persist_test' > /data/testfile\"",
- WslcContainerName,
- volumeName,
- AlpineImage.NameAndTag()));
- result.Verify({.Stderr = L"", .ExitCode = 0});
-
- result = RunWslc(std::format(L"container start -a {}", WslcContainerName));
- result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
-
- // Remove first container
- RunWslc(std::format(L"container rm {}", WslcContainerName));
-
- // Read data using second container
- result = RunWslc(std::format(L"container run --rm --volume {}:/data {} cat /data/testfile", volumeName, AlpineImage.NameAndTag()));
- result.Verify({.Stdout = L"persist_test", .Stderr = L"", .ExitCode = 0});
-
- // Cleanup
- RunWslc(std::format(L"volume rm {}", volumeName));
- }
-
private:
// Test container name
const std::wstring WslcContainerName = L"wslc-test-container";
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
index c9481dcbe..8b22bce11 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
@@ -535,61 +535,6 @@ class WSLCE2EContainerRunTests
result.Verify({.Stderr = L"invalid mount path: '' mount path must be absolute\r\nError code: E_FAIL\r\n", .ExitCode = 1});
}
- WSLC_TEST_METHOD(WSLCE2E_Container_Run_NamedVolume_WriteAndRead)
- {
-
- const std::wstring volumeName = L"wslc-test-vol-run";
-
- // Cleanup any pre-existing volume
- RunWslc(std::format(L"volume rm {}", volumeName));
-
- // Create the named volume
- auto result = RunWslc(std::format(L"volume create {}", volumeName));
- result.Verify({.Stderr = L"", .ExitCode = 0});
-
- // Write data to the named volume
- result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} sh -c \"echo -n 'run_vol_test' > /data/testfile\"", volumeName, DebianImage.NameAndTag()));
- result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
-
- // Read data back from the named volume in a new container
- result = RunWslc(std::format(L"container run --rm --volume {}:/data {} cat /data/testfile", volumeName, DebianImage.NameAndTag()));
- result.Verify({.Stdout = L"run_vol_test", .Stderr = L"", .ExitCode = 0});
-
- // Cleanup
- RunWslc(std::format(L"volume rm {}", volumeName));
- }
-
- WSLC_TEST_METHOD(WSLCE2E_Container_Run_NamedVolume_MultipleContainers)
- {
-
- const std::wstring volumeName = L"wslc-test-vol-run-multi";
-
- // Cleanup any pre-existing volume
- RunWslc(std::format(L"volume rm {}", volumeName));
-
- // Create the named volume
- auto result = RunWslc(std::format(L"volume create {}", volumeName));
- result.Verify({.Stderr = L"", .ExitCode = 0});
-
- // Write data with first container
- result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} sh -c \"echo -n 'first' > /data/testfile\"", volumeName, DebianImage.NameAndTag()));
- result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
-
- // Append data with second container
- result = RunWslc(std::format(
- L"container run --rm --volume {}:/data {} sh -c \"echo -n '_second' >> /data/testfile\"", volumeName, DebianImage.NameAndTag()));
- result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
-
- // Read combined data with third container
- result = RunWslc(std::format(L"container run --rm --volume {}:/data {} cat /data/testfile", volumeName, DebianImage.NameAndTag()));
- result.Verify({.Stdout = L"first_second", .Stderr = L"", .ExitCode = 0});
-
- // Cleanup
- RunWslc(std::format(L"volume rm {}", volumeName));
- }
-
private:
// Test container name
const std::wstring WslcContainerName = L"wslc-test-container";
From 49efe4fbc85f23485f3d2ddfa1dbecc9a7dbf973 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 14 Apr 2026 17:31:34 -0700
Subject: [PATCH 10/13] Rename delete to remove
---
localization/strings/en-US/Resources.resw | 4 ++--
.../wslc/arguments/ArgumentValidation.h | 1 -
src/windows/wslc/commands/VolumeCommand.cpp | 2 +-
src/windows/wslc/commands/VolumeCommand.h | 6 +++---
...eteCommand.cpp => VolumeRemoveCommand.cpp} | 21 ++++++++-----------
.../wslc/e2e/WSLCE2EVolumeRemoveTests.cpp | 2 +-
test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp | 2 +-
7 files changed, 17 insertions(+), 21 deletions(-)
rename src/windows/wslc/commands/{VolumeDeleteCommand.cpp => VolumeRemoveCommand.cpp} (56%)
diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index 46a561b5a..0c47c2839 100644
--- a/localization/strings/en-US/Resources.resw
+++ b/localization/strings/en-US/Resources.resw
@@ -2550,10 +2550,10 @@ On first run, creates the file with all settings commented out at their defaults
Creates a named volume that can be attached to containers.
-
+
Remove one or more volumes.
-
+
Removes one or more volumes. A volume cannot be removed if it is in use by a container.
diff --git a/src/windows/wslc/arguments/ArgumentValidation.h b/src/windows/wslc/arguments/ArgumentValidation.h
index 673d63edd..23208699c 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.h
+++ b/src/windows/wslc/arguments/ArgumentValidation.h
@@ -61,6 +61,5 @@ void ValidateFormatTypeFromString(const std::vector& values, const
FormatType GetFormatTypeFromString(const std::wstring& input, const std::wstring& argName = {});
void ValidateVolumeMount(const std::vector& values);
-void ValidateVolumeDriver(const std::vector& values, const std::wstring& argName);
} // namespace wsl::windows::wslc::validation
\ No newline at end of file
diff --git a/src/windows/wslc/commands/VolumeCommand.cpp b/src/windows/wslc/commands/VolumeCommand.cpp
index 4e38ea574..9c171a349 100644
--- a/src/windows/wslc/commands/VolumeCommand.cpp
+++ b/src/windows/wslc/commands/VolumeCommand.cpp
@@ -23,7 +23,7 @@ std::vector> VolumeCommand::GetCommands() const
{
std::vector> commands;
commands.push_back(std::make_unique(FullName()));
- commands.push_back(std::make_unique(FullName()));
+ commands.push_back(std::make_unique(FullName()));
commands.push_back(std::make_unique(FullName()));
commands.push_back(std::make_unique(FullName()));
return commands;
diff --git a/src/windows/wslc/commands/VolumeCommand.h b/src/windows/wslc/commands/VolumeCommand.h
index 82346c760..19a2b0634 100644
--- a/src/windows/wslc/commands/VolumeCommand.h
+++ b/src/windows/wslc/commands/VolumeCommand.h
@@ -47,11 +47,11 @@ struct VolumeCreateCommand final : public Command
void ExecuteInternal(CLIExecutionContext& context) const override;
};
-// Delete Command
-struct VolumeDeleteCommand final : public Command
+// Remove Command
+struct VolumeRemoveCommand final : public Command
{
constexpr static std::wstring_view CommandName = L"remove";
- VolumeDeleteCommand(const std::wstring& parent) : Command(CommandName, {L"delete", L"rm"}, parent)
+ VolumeRemoveCommand(const std::wstring& parent) : Command(CommandName, {L"delete", L"rm"}, parent)
{
}
std::vector GetArguments() const override;
diff --git a/src/windows/wslc/commands/VolumeDeleteCommand.cpp b/src/windows/wslc/commands/VolumeRemoveCommand.cpp
similarity index 56%
rename from src/windows/wslc/commands/VolumeDeleteCommand.cpp
rename to src/windows/wslc/commands/VolumeRemoveCommand.cpp
index 395fbd0fb..59bb57136 100644
--- a/src/windows/wslc/commands/VolumeDeleteCommand.cpp
+++ b/src/windows/wslc/commands/VolumeRemoveCommand.cpp
@@ -4,7 +4,7 @@ Copyright (c) Microsoft. All rights reserved.
Module Name:
- VolumeDeleteCommand.cpp
+ VolumeRemoveCommand.cpp
Abstract:
@@ -24,7 +24,7 @@ using namespace wsl::shared;
namespace wsl::windows::wslc {
// Volume Delete Command
-std::vector VolumeDeleteCommand::GetArguments() const
+std::vector VolumeRemoveCommand::GetArguments() const
{
return {
Argument::Create(ArgType::VolumeName, true, NO_LIMIT),
@@ -32,22 +32,19 @@ std::vector VolumeDeleteCommand::GetArguments() const
};
}
-std::wstring VolumeDeleteCommand::ShortDescription() const
+std::wstring VolumeRemoveCommand::ShortDescription() const
{
- return Localization::WSLCCLI_VolumeDeleteDesc();
+ return Localization::WSLCCLI_VolumeRemoveDesc();
}
-std::wstring VolumeDeleteCommand::LongDescription() const
+std::wstring VolumeRemoveCommand::LongDescription() const
{
- return Localization::WSLCCLI_VolumeDeleteLongDesc();
+ return Localization::WSLCCLI_VolumeRemoveLongDesc();
}
-// clang-format off
-void VolumeDeleteCommand::ExecuteInternal(CLIExecutionContext& context) const
+void VolumeRemoveCommand::ExecuteInternal(CLIExecutionContext& context) const
{
- context
- << CreateSession
- << DeleteVolumes;
+ context << CreateSession //
+ << DeleteVolumes;
}
-// clang-format on
} // namespace wsl::windows::wslc
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp
index 6871ff4fb..abb44a8b6 100644
--- a/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeRemoveTests.cpp
@@ -107,7 +107,7 @@ class WSLCE2EVolumeRemoveTests
std::wstring GetDescription() const
{
- return Localization::WSLCCLI_VolumeDeleteLongDesc() + L"\r\n\r\n";
+ return Localization::WSLCCLI_VolumeRemoveLongDesc() + L"\r\n\r\n";
}
std::wstring GetUsage() const
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp
index a0b20be68..211a7a18a 100644
--- a/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp
@@ -68,7 +68,7 @@ class WSLCE2EVolumeTests
{
std::vector> entries = {
{L"create", Localization::WSLCCLI_VolumeCreateDesc()},
- {L"remove", Localization::WSLCCLI_VolumeDeleteDesc()},
+ {L"remove", Localization::WSLCCLI_VolumeRemoveDesc()},
{L"inspect", Localization::WSLCCLI_VolumeInspectDesc()},
{L"list", Localization::WSLCCLI_VolumeListDesc()},
};
From 21849d3e6601be55194ce01729de89cd455a74b6 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 14 Apr 2026 17:36:04 -0700
Subject: [PATCH 11/13] Resolve copilot comment
---
src/windows/wslc/services/VolumeModel.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/windows/wslc/services/VolumeModel.h b/src/windows/wslc/services/VolumeModel.h
index 5995850d8..308a91561 100644
--- a/src/windows/wslc/services/VolumeModel.h
+++ b/src/windows/wslc/services/VolumeModel.h
@@ -14,6 +14,7 @@ Module Name:
#pragma once
+#include "JsonUtils.h"
#include
namespace wsl::windows::wslc::models {
From a5a3dbf41695154fc2a1cd76c5722db832c9f412 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 14 Apr 2026 17:55:14 -0700
Subject: [PATCH 12/13] Resolve copilot comment
---
localization/strings/en-US/Resources.resw | 4 ++++
src/windows/wslc/commands/VolumeListCommand.cpp | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index 0c47c2839..1ab8a0450 100644
--- a/localization/strings/en-US/Resources.resw
+++ b/localization/strings/en-US/Resources.resw
@@ -2578,4 +2578,8 @@ On first run, creates the file with all settings commented out at their defaults
Set driver specific options
+
+ Outputs the volume names only
+
+
diff --git a/src/windows/wslc/commands/VolumeListCommand.cpp b/src/windows/wslc/commands/VolumeListCommand.cpp
index 8dafd47c9..e8f336b50 100644
--- a/src/windows/wslc/commands/VolumeListCommand.cpp
+++ b/src/windows/wslc/commands/VolumeListCommand.cpp
@@ -29,7 +29,7 @@ std::vector VolumeListCommand::GetArguments() const
{
return {
Argument::Create(ArgType::Format),
- Argument::Create(ArgType::Quiet, false, std::nullopt, L"Outputs the volume names only"),
+ Argument::Create(ArgType::Quiet, false, std::nullopt, Localization::WSLCCLI_VolumeListQuietArgDesc()),
Argument::Create(ArgType::Session),
};
}
From 912a4c546c9dd881b28e0767f0907167891c1454 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 14 Apr 2026 18:56:45 -0700
Subject: [PATCH 13/13] Fix e2e tests
---
test/windows/wslc/WSLCCLIExecutionUnitTests.cpp | 6 ++++++
test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp | 1 +
2 files changed, 7 insertions(+)
diff --git a/test/windows/wslc/WSLCCLIExecutionUnitTests.cpp b/test/windows/wslc/WSLCCLIExecutionUnitTests.cpp
index 83d2d5a32..a5eeb6dd3 100644
--- a/test/windows/wslc/WSLCCLIExecutionUnitTests.cpp
+++ b/test/windows/wslc/WSLCCLIExecutionUnitTests.cpp
@@ -97,6 +97,12 @@ class WSLCCLIExecutionUnitTests
dataMap.Add(std::move(images));
handled = true;
}
+ else if (dataType == Data::Volumes)
+ {
+ std::vector volumes;
+ dataMap.Add(std::move(volumes));
+ handled = true;
+ }
if (!handled)
{
diff --git a/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp b/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp
index 043a02656..4c0352966 100644
--- a/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp
@@ -411,6 +411,7 @@ class WSLCE2EGlobalTests
{L"image", Localization::WSLCCLI_ImageCommandDesc()},
{L"session", Localization::WSLCCLI_SessionCommandDesc()},
{L"settings", Localization::WSLCCLI_SettingsCommandDesc()},
+ {L"volume", Localization::WSLCCLI_VolumeCommandDesc()},
{L"attach", Localization::WSLCCLI_ContainerAttachDesc()},
{L"build", Localization::WSLCCLI_ImageBuildDesc()},
{L"create", Localization::WSLCCLI_ContainerCreateDesc()},