diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index de258d29d..c89b09054 100644
--- a/localization/strings/en-US/Resources.resw
+++ b/localization/strings/en-US/Resources.resw
@@ -2600,6 +2600,9 @@ On first run, creates the file with all settings commented out at their defaults
Bind mount a volume to the container
+
+ Remove anonymous volumes associated with the container
+
Working directory inside the container
diff --git a/src/windows/wslc/arguments/ArgumentDefinitions.h b/src/windows/wslc/arguments/ArgumentDefinitions.h
index a7bfe4e9b..15d3bda94 100644
--- a/src/windows/wslc/arguments/ArgumentDefinitions.h
+++ b/src/windows/wslc/arguments/ArgumentDefinitions.h
@@ -100,6 +100,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()) \
+_(Volumes, "volumes", L"v", Kind::Flag, Localization::WSLCCLI_RemoveVolumesArgDescription()) \
_(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/commands/ContainerRemoveCommand.cpp b/src/windows/wslc/commands/ContainerRemoveCommand.cpp
index 5afa95544..5514e93a3 100644
--- a/src/windows/wslc/commands/ContainerRemoveCommand.cpp
+++ b/src/windows/wslc/commands/ContainerRemoveCommand.cpp
@@ -29,6 +29,7 @@ std::vector ContainerRemoveCommand::GetArguments() const
return {
Argument::Create(ArgType::ContainerId, true, NO_LIMIT),
Argument::Create(ArgType::Force),
+ Argument::Create(ArgType::Volumes),
Argument::Create(ArgType::Session),
};
}
diff --git a/src/windows/wslc/services/ContainerService.cpp b/src/windows/wslc/services/ContainerService.cpp
index 90af89e87..96fbb71eb 100644
--- a/src/windows/wslc/services/ContainerService.cpp
+++ b/src/windows/wslc/services/ContainerService.cpp
@@ -397,11 +397,23 @@ void ContainerService::Kill(Session& session, const std::string& id, WSLCSignal
THROW_IF_FAILED(container->Kill(signal));
}
-void ContainerService::Delete(Session& session, const std::string& id, bool force)
+void ContainerService::Delete(Session& session, const std::string& id, bool force, bool removeVolumes)
{
wil::com_ptr container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
- THROW_IF_FAILED(container->Delete(force ? WSLCDeleteFlagsForce : WSLCDeleteFlagsNone));
+
+ WSLCDeleteFlags flags = WSLCDeleteFlagsNone;
+ if (force)
+ {
+ WI_SetFlag(flags, WSLCDeleteFlagsForce);
+ }
+
+ if (removeVolumes)
+ {
+ WI_SetFlag(flags, WSLCDeleteFlagsDeleteVolumes);
+ }
+
+ THROW_IF_FAILED(container->Delete(flags));
}
std::vector ContainerService::List(Session& session)
diff --git a/src/windows/wslc/services/ContainerService.h b/src/windows/wslc/services/ContainerService.h
index aeddaff9f..9101feb97 100644
--- a/src/windows/wslc/services/ContainerService.h
+++ b/src/windows/wslc/services/ContainerService.h
@@ -28,7 +28,7 @@ struct ContainerService
static int Start(models::Session& session, const std::string& id, bool attach = false);
static void Stop(models::Session& session, const std::string& id, models::StopContainerOptions options);
static void Kill(models::Session& session, const std::string& id, WSLCSignal signal = WSLCSignalSIGKILL);
- static void Delete(models::Session& session, const std::string& id, bool force);
+ static void Delete(models::Session& session, const std::string& id, bool force, bool removeVolumes = false);
static std::vector List(models::Session& session);
static int Exec(models::Session& session, const std::string& id, models::ContainerOptions options);
static wsl::windows::common::wslc_schema::InspectContainer Inspect(models::Session& session, const std::string& id);
diff --git a/src/windows/wslc/tasks/ContainerTasks.cpp b/src/windows/wslc/tasks/ContainerTasks.cpp
index 804f54282..f22e2abdd 100644
--- a/src/windows/wslc/tasks/ContainerTasks.cpp
+++ b/src/windows/wslc/tasks/ContainerTasks.cpp
@@ -178,9 +178,10 @@ void RemoveContainers(CLIExecutionContext& context)
auto& session = context.Data.Get();
auto containerIds = context.Args.GetAll();
bool force = context.Args.Contains(ArgType::Force);
+ bool removeVolumes = context.Args.Contains(ArgType::Volumes);
for (const auto& id : containerIds)
{
- ContainerService::Delete(session, WideToMultiByte(id), force);
+ ContainerService::Delete(session, WideToMultiByte(id), force, removeVolumes);
}
}
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerRemoveTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerRemoveTests.cpp
index 0ec1f1a3b..7f1e7c56f 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerRemoveTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerRemoveTests.cpp
@@ -33,6 +33,7 @@ class WSLCE2EContainerRemoveTests
{
EnsureContainerDoesNotExist(WslcContainerName);
EnsureContainerDoesNotExist(WslcContainerName2);
+ EnsureVolumeDoesNotExist(WslcVolumeName);
EnsureImageIsDeleted(DebianImage);
return true;
}
@@ -41,6 +42,7 @@ class WSLCE2EContainerRemoveTests
{
EnsureContainerDoesNotExist(WslcContainerName);
EnsureContainerDoesNotExist(WslcContainerName2);
+ EnsureVolumeDoesNotExist(WslcVolumeName);
return true;
}
@@ -156,9 +158,61 @@ class WSLCE2EContainerRemoveTests
VerifyContainerIsNotListed(WslcContainerName2);
}
+ WSLC_TEST_METHOD(WSLCE2E_Container_Remove_WithVolumes)
+ {
+ // Create a named volume and a container that uses it
+ RunWslc(std::format(L"volume create --name {}", WslcVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
+
+ auto cleanup = wil::scope_exit([&]() {
+ EnsureContainerDoesNotExist(WslcContainerName);
+ EnsureVolumeDoesNotExist(WslcVolumeName);
+ });
+
+ auto result = RunWslc(
+ std::format(L"container create --name {} -v {}:/data {}", WslcContainerName, WslcVolumeName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ const auto containerId = result.GetStdoutOneLine();
+ VERIFY_IS_FALSE(containerId.empty());
+
+ VerifyContainerIsListed(containerId, L"created");
+ VerifyVolumeIsListed(WslcVolumeName);
+
+ // Remove with --volumes should remove the container and its volumes
+ result = RunWslc(std::format(L"container remove --volumes {}", containerId));
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
+
+ VerifyContainerIsNotListed(containerId);
+ VerifyVolumeIsNotListed(WslcVolumeName);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Remove_WithoutVolumesFlag_KeepsVolumes)
+ {
+ // Create a named volume and a container that uses it
+ RunWslc(std::format(L"volume create --name {}", WslcVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
+
+ auto cleanup = wil::scope_exit([&]() {
+ EnsureContainerDoesNotExist(WslcContainerName);
+ EnsureVolumeDoesNotExist(WslcVolumeName);
+ });
+
+ auto result = RunWslc(
+ std::format(L"container create --name {} -v {}:/data {}", WslcContainerName, WslcVolumeName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ const auto containerId = result.GetStdoutOneLine();
+ VERIFY_IS_FALSE(containerId.empty());
+
+ // Remove without --volumes should keep the volume
+ result = RunWslc(std::format(L"container remove {}", containerId));
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
+
+ VerifyContainerIsNotListed(containerId);
+ VerifyVolumeIsListed(WslcVolumeName);
+ }
+
private:
const std::wstring WslcContainerName = L"wslc-test-container";
const std::wstring WslcContainerName2 = L"wslc-test-container-2";
+ const std::wstring WslcVolumeName = L"wslc-test-rm-volume";
const TestImage& DebianImage = DebianTestImage();
std::wstring GetHelpMessage() const
@@ -200,6 +254,7 @@ class WSLCE2EContainerRemoveTests
std::wstringstream options;
options << L"The following options are available:\r\n" //
<< L" -f,--force Delete containers even if they are running\r\n"
+ << L" -v,--volumes " << Localization::WSLCCLI_RemoveVolumesArgDescription() << L"\r\n"
<< L" --session Specify the session to use\r\n"
<< L" -?,--help Shows help about the selected command\r\n"
<< L"\r\n";