Implement Stop Container

This commit is contained in:
Pooja Trivedi 2025-12-08 19:46:23 -05:00
parent c7f915e0fd
commit 901ec06ae8
2 changed files with 65 additions and 1 deletions

View File

@ -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 <containerId> on stdout if the container is running or already stopped
* returns error "No such container: <containerId>" 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

View File

@ -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<IWSLAContainer> 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));