mirror of
https://github.com/microsoft/WSL.git
synced 2025-12-10 00:44:55 -06:00
Implement container state management
This commit is contained in:
parent
c826c20c8d
commit
49462adfb9
@ -95,6 +95,15 @@ std::pair<int, bool> RunningWSLAProcess::GetExitState()
|
||||
return {code, state == WslaProcessStateSignalled};
|
||||
}
|
||||
|
||||
WSLA_PROCESS_STATE RunningWSLAProcess::State()
|
||||
{
|
||||
WSLA_PROCESS_STATE state{};
|
||||
int code{};
|
||||
GetState(&state, &code);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
std::string WSLAProcessLauncher::FormatResult(const RunningWSLAProcess::ProcessResult& result)
|
||||
{
|
||||
auto stdOut = result.Output.find(1);
|
||||
|
||||
@ -50,6 +50,7 @@ public:
|
||||
virtual wil::unique_handle GetStdHandle(int Index) = 0;
|
||||
virtual wil::unique_event GetExitEvent() = 0;
|
||||
std::pair<int, bool> GetExitState();
|
||||
WSLA_PROCESS_STATE State();
|
||||
|
||||
protected:
|
||||
virtual void GetState(WSLA_PROCESS_STATE* State, int* Code) = 0;
|
||||
|
||||
@ -13,7 +13,6 @@ set(SOURCES
|
||||
|
||||
set(HEADERS
|
||||
ServiceProcessLauncher.h
|
||||
WeakRefContainer.h
|
||||
WSLAContainer.h
|
||||
WSLAProcess.h
|
||||
WSLASession.h
|
||||
|
||||
@ -16,36 +16,29 @@ Abstract:
|
||||
#include "WSLAContainer.h"
|
||||
#include "WSLAProcess.h"
|
||||
|
||||
using wsl::windows::service::wsla::WeakReference;
|
||||
using wsl::windows::service::wsla::WSLAContainer;
|
||||
|
||||
constexpr const char* nerdctlPath = "/usr/bin/nerdctl";
|
||||
|
||||
// Constants for required default arguments for "nerdctl run..."
|
||||
static std::vector<std::string> defaultNerdctlRunArgs{
|
||||
//"--pull=never", // TODO: Uncomment once PullImage() is implemented.
|
||||
"--net=host", // TODO: default for now, change later
|
||||
"--ulimit",
|
||||
"nofile=65536:65536"};
|
||||
static std::vector<std::string> defaultNerdctlRunArgs{//"--pull=never", // TODO: Uncomment once PullImage() is implemented.
|
||||
"--net=host", // TODO: default for now, change later
|
||||
"--ulimit",
|
||||
"nofile=65536:65536"};
|
||||
|
||||
WSLAContainer::WSLAContainer(WSLAVirtualMachine* parentVM, ServiceRunningProcess&& containerProcess, const char* name, const char* image) :
|
||||
WeakReference<WSLAContainer>(), m_parentVM(parentVM), m_containerProcess(std::move(containerProcess)), m_name(name), m_image(image)
|
||||
m_parentVM(parentVM), m_containerProcess(std::move(containerProcess)), m_name(name), m_image(image)
|
||||
{
|
||||
}
|
||||
|
||||
WSLAContainer::~WSLAContainer()
|
||||
const std::string& WSLAContainer::Name() const noexcept
|
||||
{
|
||||
OnDestroy();
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void WSLAContainer::GetName(char Name[WSLA_MAX_CONTAINER_NAME_LENGTH + 1]) const noexcept
|
||||
const std::string& WSLAContainer::Image() const noexcept
|
||||
{
|
||||
WI_VERIFY(strcpy_s(Name, sizeof(Name), m_name.c_str()) == 0);
|
||||
}
|
||||
|
||||
void WSLAContainer::GetImage(char Image[WSLA_MAX_IMAGE_NAME_LENGTH + 1]) const noexcept
|
||||
{
|
||||
WI_VERIFY(strcpy_s(Image, WSLA_MAX_IMAGE_NAME_LENGTH + 1, m_image.c_str()) == 0);
|
||||
return m_image;
|
||||
}
|
||||
|
||||
HRESULT WSLAContainer::Start()
|
||||
@ -65,7 +58,17 @@ HRESULT WSLAContainer::Delete()
|
||||
|
||||
HRESULT WSLAContainer::GetState(WSLA_CONTAINER_STATE* State)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
if (m_containerProcess.State() == WSLAProcessStateRunning)
|
||||
{
|
||||
*State = WslaContainerStateRunning;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle failure to start.
|
||||
*State = WslaContainerStateExited;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WSLAContainer::GetInitProcess(IWSLAProcess** Process)
|
||||
@ -110,7 +113,6 @@ Microsoft::WRL::ComPtr<WSLAContainer> WSLAContainer::Create(const WSLA_CONTAINER
|
||||
if (hasStdin)
|
||||
{
|
||||
// For now return a proper error if the caller tries to pass stdin without a TTY to prevent hangs.
|
||||
THROW_WIN32_IF(ERROR_NOT_SUPPORTED, hasTty == false);
|
||||
inputOptions.push_back("-i");
|
||||
}
|
||||
|
||||
@ -127,7 +129,7 @@ Microsoft::WRL::ComPtr<WSLAContainer> WSLAContainer::Create(const WSLA_CONTAINER
|
||||
launcher.AddFd(containerOptions.InitProcessOptions.Fds[i]);
|
||||
}
|
||||
|
||||
return wil::MakeOrThrow<WSLAContainer>(&parentVM, launcher.Launch(parentVM));
|
||||
return wil::MakeOrThrow<WSLAContainer>(&parentVM, launcher.Launch(parentVM), containerOptions.Name, containerOptions.Image);
|
||||
}
|
||||
|
||||
std::vector<std::string> WSLAContainer::PrepareNerdctlRunCommand(const WSLA_CONTAINER_OPTIONS& options, std::vector<std::string>&& inputOptions)
|
||||
|
||||
@ -24,11 +24,10 @@ class DECLSPEC_UUID("B1F1C4E3-C225-4CAE-AD8A-34C004DE1AE4") WSLAContainer
|
||||
: public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IWSLAContainer, IFastRundown>
|
||||
{
|
||||
public:
|
||||
WSLAContainer(WSLAVirtualMachine* parentVM, ServiceRunningProcess&& containerProcess, const char* name, const char* image);
|
||||
~WSLAContainer();
|
||||
|
||||
NON_COPYABLE(WSLAContainer);
|
||||
|
||||
WSLAContainer(WSLAVirtualMachine* parentVM, ServiceRunningProcess&& containerProcess, const char* name, const char* image);
|
||||
|
||||
IFACEMETHOD(Start)() override;
|
||||
IFACEMETHOD(Stop)(_In_ int Signal, _In_ ULONG TimeoutMs) override;
|
||||
IFACEMETHOD(Delete)() override;
|
||||
@ -36,8 +35,8 @@ public:
|
||||
IFACEMETHOD(GetInitProcess)(_Out_ IWSLAProcess** process) override;
|
||||
IFACEMETHOD(Exec)(_In_ const WSLA_PROCESS_OPTIONS* Options, _Out_ IWSLAProcess** Process, _Out_ int* Errno) override;
|
||||
|
||||
void GetName(char Name[WSLA_MAX_IMAGE_NAME_LENGTH + 1]) const noexcept;
|
||||
void GetImage(char Name[WSLA_MAX_CONTAINER_NAME_LENGTH + 1]) const noexcept;
|
||||
const std::string& Name() const noexcept;
|
||||
const std::string& Image() const noexcept;
|
||||
|
||||
static Microsoft::WRL::ComPtr<WSLAContainer> Create(const WSLA_CONTAINER_OPTIONS& Options, WSLAVirtualMachine& parentVM);
|
||||
|
||||
|
||||
@ -226,10 +226,10 @@ try
|
||||
// TODO: Log entrance into the function.
|
||||
auto container = WSLAContainer::Create(*containerOptions, *m_virtualMachine.Get());
|
||||
|
||||
m_containers.Add(container.Get());
|
||||
|
||||
THROW_IF_FAILED(container.CopyTo(__uuidof(IWSLAContainer), (void**)Container));
|
||||
|
||||
m_containers.emplace_back(std::move(container));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
@ -239,19 +239,30 @@ HRESULT WSLASession::OpenContainer(LPCWSTR Name, IWSLAContainer** Container)
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT WSLASession::ListContainers(WSLA_CONTAINER** Images, ULONG* Count)
|
||||
HRESULT WSLASession::ListContainers(WSLA_CONTAINER** Containers, ULONG* Count)
|
||||
try
|
||||
{
|
||||
auto lockedElements = m_containers.Get();
|
||||
*Count = 0;
|
||||
*Containers = nullptr;
|
||||
|
||||
std::lock_guard lock{m_lock};
|
||||
|
||||
auto output = wil::make_unique_cotaskmem<WSLA_CONTAINER[]>(m_containers.size());
|
||||
|
||||
auto output = wil::make_unique_cotaskmem<WSLA_CONTAINER[]>(lockedElements.elements.size());
|
||||
size_t index = 0;
|
||||
for (const auto &e: lockedElements.elements)
|
||||
for (const auto& e : m_containers)
|
||||
{
|
||||
e->GetImage(output[index].Image);
|
||||
e->GetName(output[index].Name);
|
||||
THROW_HR_IF(E_UNEXPECTED, strcpy_s(output[index].Image, e->Image().c_str()) != 0);
|
||||
THROW_HR_IF(E_UNEXPECTED, strcpy_s(output[index].Name, e->Name().c_str()) != 0);
|
||||
THROW_IF_FAILED(e->GetState(&output[index].State));
|
||||
index++;
|
||||
}
|
||||
|
||||
*Count = static_cast<ULONG>(m_containers.size());
|
||||
*Containers = output.release();
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
HRESULT WSLASession::GetVirtualMachine(IWSLAVirtualMachine** VirtualMachine)
|
||||
{
|
||||
|
||||
@ -16,7 +16,6 @@ Abstract:
|
||||
|
||||
#include "wslaservice.h"
|
||||
#include "WSLAVirtualMachine.h"
|
||||
#include "WeakRefContainer.h"
|
||||
#include "WSLAContainer.h"
|
||||
|
||||
namespace wsl::windows::service::wsla {
|
||||
@ -64,7 +63,7 @@ private:
|
||||
Microsoft::WRL::ComPtr<WSLAVirtualMachine> m_virtualMachine;
|
||||
std::wstring m_displayName;
|
||||
std::filesystem::path m_storageVhdPath;
|
||||
WeakRefContainer<WSLAContainer> m_containers;
|
||||
std::vector<Microsoft::WRL::ComPtr<WSLAContainer>> m_containers;
|
||||
std::mutex m_lock;
|
||||
|
||||
// TODO: Add container tracking here. Could reuse m_lock for that.
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "defs.h"
|
||||
#include <unordered_set>
|
||||
#include <wil/com.h>
|
||||
#include <wil/cppwinrt.h>
|
||||
|
||||
namespace wsl::windows::service::wsla {
|
||||
|
||||
template <typename T>
|
||||
class WeakRefContainer
|
||||
{
|
||||
public:
|
||||
struct LockedElements
|
||||
{
|
||||
std::lock_guard<std::mutex> lock;
|
||||
std::unordered_set<T*>& elements;
|
||||
};
|
||||
|
||||
WeakRefContainer(const WeakRefContainer&) = delete;
|
||||
WeakRefContainer(WeakRefContainer&&) = delete;
|
||||
|
||||
WeakRefContainer& operator=(const WeakRefContainer&);
|
||||
WeakRefContainer& operator=(WeakRefContainer&&);
|
||||
|
||||
WeakRefContainer() = default;
|
||||
~WeakRefContainer()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_lock);
|
||||
|
||||
for (const auto& e : m_elements)
|
||||
{
|
||||
e->SetContainer(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Add(T* element)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_lock);
|
||||
|
||||
element->SetContainer(this);
|
||||
m_elements.insert(element);
|
||||
}
|
||||
|
||||
void Remove(T* element)
|
||||
{
|
||||
element->SetContainer(nullptr);
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_lock);
|
||||
m_elements.erase(element);
|
||||
}
|
||||
|
||||
LockedElements Get()
|
||||
{
|
||||
return {std::lock_guard<std::mutex>(m_lock), m_elements};
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_set<T*> m_elements;
|
||||
std::mutex m_lock;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class WeakReference
|
||||
{
|
||||
public:
|
||||
NON_COPYABLE(WeakReference);
|
||||
WeakReference() = default;
|
||||
|
||||
void SetContainer(WeakRefContainer<T>* container) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_lock);
|
||||
m_container = container;
|
||||
}
|
||||
|
||||
protected:
|
||||
WeakRefContainer<T>* m_container = nullptr;
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_lock);
|
||||
if (m_container != nullptr)
|
||||
{
|
||||
m_container->Remove(static_cast<T*>(this));
|
||||
m_container = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_lock;
|
||||
};
|
||||
|
||||
} // namespace wsl::windows::service::wsla
|
||||
@ -162,7 +162,6 @@ enum WSLA_CONTAINER_STATE
|
||||
WslaContainerStateCreated = 1,
|
||||
WslaContainerStateRunning = 2,
|
||||
WslaContainerStateExited = 3,
|
||||
WslaContainerStateFailed = 4,
|
||||
// TODO: More states might be added to reflect all nerdctl's states.
|
||||
};
|
||||
|
||||
|
||||
@ -1129,4 +1129,56 @@ class WSLATests
|
||||
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
TEST_METHOD(ContainerState)
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
SKIP_TEST_ARM64();
|
||||
|
||||
auto settings = GetDefaultSessionSettings();
|
||||
settings.NetworkingMode = WSLANetworkingModeNAT;
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
|
||||
auto expectContainerList = [&](const std::vector<std::tuple<std::string, std::string, WSLA_CONTAINER_STATE>>& expectedContainers) {
|
||||
wil::unique_cotaskmem_array_ptr<WSLA_CONTAINER> containers;
|
||||
|
||||
VERIFY_SUCCEEDED(session->ListContainers(&containers, containers.size_address<ULONG>()));
|
||||
VERIFY_ARE_EQUAL(expectedContainers.size(), containers.size());
|
||||
|
||||
for (size_t i = 0; i < expectedContainers.size(); i++)
|
||||
{
|
||||
const auto& [expectedName, expectedImage, expectedState] = expectedContainers[i];
|
||||
VERIFY_ARE_EQUAL(expectedName, containers[i].Name);
|
||||
VERIFY_ARE_EQUAL(expectedImage, containers[i].Image);
|
||||
VERIFY_ARE_EQUAL(expectedState, containers[i].State);
|
||||
}
|
||||
};
|
||||
|
||||
// Create a stuck container.
|
||||
WSLAContainerLauncher launcher(
|
||||
"debian:latest", "test-container-1", "/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);
|
||||
expectContainerList({{"test-container-1", "debian:latest", WslaContainerStateRunning}});
|
||||
|
||||
// Kill the container init process and expect it to be in exited state.
|
||||
auto initProcess = container.GetInitProcess();
|
||||
initProcess.Get().Signal(9);
|
||||
|
||||
// Wait for the process to actually exit.
|
||||
wsl::shared::retry::RetryWithTimeout<void>(
|
||||
[&]() {
|
||||
initProcess.GetExitState(); // Throw if the process hasn't exited yet.
|
||||
},
|
||||
std::chrono::milliseconds{100},
|
||||
std::chrono::seconds{30});
|
||||
|
||||
// Expect the container to be in exited state.
|
||||
VERIFY_ARE_EQUAL(container.State(), WslaContainerStateExited);
|
||||
expectContainerList({{"test-container-1", "debian:latest", WslaContainerStateExited}});
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user