From 726e425b9433286beb80a2b8655a38aa66c852f6 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:19:34 -0700 Subject: [PATCH] Init container remove volumes option --- localization/strings/en-US/Resources.resw | 3 + .../wslc/arguments/ArgumentDefinitions.h | 1 + .../wslc/commands/ContainerRemoveCommand.cpp | 1 + .../wslc/services/ContainerService.cpp | 16 +++++- src/windows/wslc/services/ContainerService.h | 2 +- src/windows/wslc/tasks/ContainerTasks.cpp | 3 +- .../wslc/e2e/WSLCE2EContainerRemoveTests.cpp | 55 +++++++++++++++++++ 7 files changed, 77 insertions(+), 4 deletions(-) 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";