From 901ec06ae8e1cda57d4223365c5c1e3f8e7bdee3 Mon Sep 17 00:00:00 2001 From: Pooja Trivedi Date: Mon, 8 Dec 2025 19:46:23 -0500 Subject: [PATCH] Implement Stop Container --- src/windows/wslaservice/exe/WSLAContainer.cpp | 33 ++++++++++++++++++- test/windows/WSLATests.cpp | 33 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/windows/wslaservice/exe/WSLAContainer.cpp b/src/windows/wslaservice/exe/WSLAContainer.cpp index 3b770e9..c82cf78 100644 --- a/src/windows/wslaservice/exe/WSLAContainer.cpp +++ b/src/windows/wslaservice/exe/WSLAContainer.cpp @@ -69,9 +69,40 @@ HRESULT WSLAContainer::Start() } HRESULT WSLAContainer::Stop(int Signal, ULONG TimeoutMs) +try { - return E_NOTIMPL; + std::lock_guard lock{m_lock}; + + /* 'nerdctl stop ...' + * returns success and on stdout if the container is running or already stopped + * returns error "No such container: " on stderr if the container is in 'Created' state or does not exist + * + * For our case, we consider stopping a container that is not running or does not exist as a no-op and return success. + * TODO: Discuss and return stdout/stderr or corresponding HRESULT from nerdctl stop for better diagnostics. + */ + + if (m_state == WslaContainerStateExited) + { + return S_OK; + } + // Validate that the container is in the exited state. + RETURN_HR_IF_MSG( + HRESULT_FROM_WIN32(ERROR_INVALID_STATE), + m_state != WslaContainerStateRunning, + "No such running or exited container '%hs', state: %i", + m_name.c_str(), + m_state); + ServiceProcessLauncher launcher(nerdctlPath, {nerdctlPath, "stop", m_name}); + // TODO: Figure out how we want to handle custom signals and timeout values. + // nerdctl stop has a --time and a --signal option that can be used + // By default, it uses SIGTERM and a default timeout of 10 seconds. + auto result = launcher.Launch(*m_parentVM).Wait(); + THROW_HR_IF(E_FAIL, result.first != 0); + + m_state = WslaContainerStateExited; + return S_OK; } +CATCH_RETURN(); HRESULT WSLAContainer::Delete() try diff --git a/test/windows/WSLATests.cpp b/test/windows/WSLATests.cpp index 031611c..1af0543 100644 --- a/test/windows/WSLATests.cpp +++ b/test/windows/WSLATests.cpp @@ -1233,6 +1233,32 @@ class WSLATests VERIFY_SUCCEEDED(container.Get().Delete()); } + // Test StopContainer + { + // Create a container + WSLAContainerLauncher launcher( + "debian:latest", "test-container-2", "/bin/cat", {}, {}, ProcessFlags::Stdin | ProcessFlags::Stdout | ProcessFlags::Stderr); + + auto container = launcher.Launch(*session); + + // Verify that the container is in running state. + VERIFY_ARE_EQUAL(container.State(), WslaContainerStateRunning); + + VERIFY_SUCCEEDED(container.Get().Stop(9, 10000)); + + expectContainerList( + {{"exited-container", "debian:latest", WslaContainerStateExited}, + {"test-container-2", "debian:latest", WslaContainerStateExited}}); + + // Verify that the container is in exited state. + VERIFY_ARE_EQUAL(container.State(), WslaContainerStateExited); + + // Verify that deleting a container stopped via Stop() works. + VERIFY_SUCCEEDED(container.Get().Delete()); + + expectContainerList({{"exited-container", "debian:latest", WslaContainerStateExited}}); + } + // Verify that trying to open a non existing container fails. { wil::com_ptr sameContainer; @@ -1274,9 +1300,16 @@ class WSLATests {{"exited-container", "debian:latest", WslaContainerStateExited}, {"test-unique-name", "debian:latest", WslaContainerStateExited}}); + // Verify that calling Stop() on exited containers is a no-op and state remains as WslaContainerStateExited. + VERIFY_SUCCEEDED(container.Get().Stop(15, 1000)); + VERIFY_ARE_EQUAL(container.State(), WslaContainerStateExited); + // Verify that stopped containers can be deleted. VERIFY_SUCCEEDED(container.Get().Delete()); + // Verify that stopping a deleted container returns ERROR_INVALID_STATE. + VERIFY_ARE_EQUAL(container.Get().Stop(15, 1000), HRESULT_FROM_WIN32(ERROR_INVALID_STATE)); + // Verify that deleted containers can't be deleted again. VERIFY_ARE_EQUAL(container.Get().Delete(), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));