mirror of
https://github.com/microsoft/WSL.git
synced 2026-05-31 05:47:06 -05:00
CLI: Initialize cidfile option (#40455)
* Initialize cidfile option Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -2678,7 +2678,7 @@ On first run, creates the file with all settings commented out at their defaults
|
||||
<value>Working directory inside the container</value>
|
||||
</data>
|
||||
<data name="WSLCCLI_CIDFileArgDescription" xml:space="preserve">
|
||||
<value>Write the container ID to the provided path.</value>
|
||||
<value>Write the container ID to the provided path</value>
|
||||
</data>
|
||||
<data name="WSLCCLI_DNSArgDescription" xml:space="preserve">
|
||||
<value>IP address of the DNS nameserver in resolv.conf</value>
|
||||
@@ -2758,6 +2758,10 @@ On first run, creates the file with all settings commented out at their defaults
|
||||
<value>Invalid {} argument value: '{}'. Expected a memory size (e.g. 256M, 1G)</value>
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated{Locked="256M"}{Locked="1G"}</comment>
|
||||
</data>
|
||||
<data name="WSLCCLI_CIDFileAlreadyExistsError" xml:space="preserve">
|
||||
<value>CID file '{}' already exists</value>
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="WSLCCLI_ImageNotFoundPulling" xml:space="preserve">
|
||||
<value>Image '{}' not found, pulling</value>
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
|
||||
@@ -38,7 +38,7 @@ _(Attach, "attach", L"a", Kind::Flag, L
|
||||
_(BuildArg, "build-arg", NO_ALIAS, Kind::Value, Localization::WSLCCLI_BuildArgDescription()) \
|
||||
_(BuildPull, "pull", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_BuildPullArgDescription()) \
|
||||
_(BuildTarget, "target", NO_ALIAS, Kind::Value, Localization::WSLCCLI_BuildTargetArgDescription()) \
|
||||
/*_(CIDFile, "cidfile", NO_ALIAS, Kind::Value, Localization::WSLCCLI_CIDFileArgDescription())*/ \
|
||||
_(CIDFile, "cidfile", NO_ALIAS, Kind::Value, Localization::WSLCCLI_CIDFileArgDescription()) \
|
||||
_(Command, "command", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_CommandArgDescription()) \
|
||||
_(ContainerId, "container-id", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_ContainerIdArgDescription()) \
|
||||
_(Force, "force", L"f", Kind::Flag, Localization::WSLCCLI_ForceArgDescription()) \
|
||||
|
||||
@@ -31,7 +31,7 @@ std::vector<Argument> ContainerCreateCommand::GetArguments() const
|
||||
Argument::Create(ArgType::ImageId, true),
|
||||
Argument::Create(ArgType::Command),
|
||||
Argument::Create(ArgType::ForwardArgs),
|
||||
// Argument::Create(ArgType::CIDFile),
|
||||
Argument::Create(ArgType::CIDFile),
|
||||
Argument::Create(ArgType::DNS, false, NO_LIMIT),
|
||||
// Argument::Create(ArgType::DNSDomain),
|
||||
Argument::Create(ArgType::DNSOption, false, NO_LIMIT),
|
||||
|
||||
@@ -31,7 +31,7 @@ std::vector<Argument> ContainerRunCommand::GetArguments() const
|
||||
Argument::Create(ArgType::ImageId, true),
|
||||
Argument::Create(ArgType::Command),
|
||||
Argument::Create(ArgType::ForwardArgs),
|
||||
// Argument::Create(ArgType::CIDFile),
|
||||
Argument::Create(ArgType::CIDFile),
|
||||
Argument::Create(ArgType::Detach),
|
||||
Argument::Create(ArgType::DNS, false, NO_LIMIT),
|
||||
// Argument::Create(ArgType::DNSDomain),
|
||||
|
||||
@@ -332,4 +332,50 @@ TmpfsMount TmpfsMount::Parse(const std::string& value)
|
||||
result.m_options = value.substr(colonPos + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
CidFile::CidFile(const std::optional<std::wstring>& path)
|
||||
{
|
||||
if (!path.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_path = *path;
|
||||
auto [file, openError] = wil::try_create_new_file(std::filesystem::path(*m_path).c_str(), GENERIC_WRITE);
|
||||
if (!file.is_valid())
|
||||
{
|
||||
if (openError == ERROR_FILE_EXISTS || openError == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
THROW_HR_WITH_USER_ERROR(HRESULT_FROM_WIN32(openError), Localization::WSLCCLI_CIDFileAlreadyExistsError(*m_path));
|
||||
}
|
||||
|
||||
const auto errorMessage = wsl::windows::common::wslutil::GetSystemErrorString(HRESULT_FROM_WIN32(openError));
|
||||
THROW_HR_WITH_USER_ERROR(HRESULT_FROM_WIN32(openError), Localization::MessageWslcFailedToOpenFile(*m_path, errorMessage));
|
||||
}
|
||||
|
||||
m_file = std::move(file);
|
||||
}
|
||||
|
||||
CidFile::~CidFile()
|
||||
{
|
||||
if (m_committed || !m_path.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_file.reset();
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(std::filesystem::path(*m_path), ec);
|
||||
}
|
||||
|
||||
void CidFile::Commit(const std::string& containerId)
|
||||
{
|
||||
if (m_file)
|
||||
{
|
||||
DWORD bytesWritten{};
|
||||
THROW_IF_WIN32_BOOL_FALSE(::WriteFile(m_file.get(), containerId.data(), static_cast<DWORD>(containerId.size()), &bytesWritten, nullptr));
|
||||
}
|
||||
|
||||
m_committed = true;
|
||||
}
|
||||
} // namespace wsl::windows::wslc::models
|
||||
|
||||
@@ -52,6 +52,7 @@ struct ContainerOptions
|
||||
std::vector<std::string> DnsOptions;
|
||||
std::vector<std::string> Tmpfs;
|
||||
std::vector<std::pair<std::string, std::string>> Labels;
|
||||
std::optional<std::wstring> CidFile{};
|
||||
};
|
||||
|
||||
struct CreateContainerResult
|
||||
@@ -290,4 +291,21 @@ private:
|
||||
std::string m_containerPath;
|
||||
std::string m_options;
|
||||
};
|
||||
|
||||
class CidFile
|
||||
{
|
||||
public:
|
||||
explicit CidFile(const std::optional<std::wstring>& path);
|
||||
~CidFile();
|
||||
|
||||
NON_COPYABLE(CidFile);
|
||||
NON_MOVABLE(CidFile);
|
||||
|
||||
void Commit(const std::string& containerId);
|
||||
|
||||
private:
|
||||
std::optional<std::wstring> m_path{};
|
||||
wil::unique_hfile m_file;
|
||||
bool m_committed = false;
|
||||
};
|
||||
} // namespace wsl::windows::wslc::models
|
||||
|
||||
@@ -20,6 +20,7 @@ Abstract:
|
||||
#include <wslutil.h>
|
||||
#include <WSLCProcessLauncher.h>
|
||||
#include <CommandLine.h>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
#include <wslc.h>
|
||||
|
||||
@@ -341,10 +342,18 @@ std::wstring ContainerService::FormatPorts(WSLCContainerState state, const std::
|
||||
|
||||
int ContainerService::Run(Session& session, const std::string& image, ContainerOptions runOptions)
|
||||
{
|
||||
// Reserve the CID file (fails if it already exists) before creating the container so a
|
||||
// container isn't created when the caller-requested path can't be written. The file is
|
||||
// removed automatically if we don't reach Commit() below.
|
||||
CidFile cidFile(runOptions.CidFile);
|
||||
|
||||
// Create the container
|
||||
auto runningContainer = CreateInternal(session, image, runOptions);
|
||||
auto& container = runningContainer.Get();
|
||||
|
||||
WSLCContainerId containerId{};
|
||||
THROW_IF_FAILED(container.GetId(containerId));
|
||||
|
||||
// Start the created container
|
||||
WSLCContainerStartFlags startFlags{};
|
||||
WI_SetFlagIf(startFlags, WSLCContainerStartFlagsAttach, !runOptions.Detach);
|
||||
@@ -352,6 +361,7 @@ int ContainerService::Run(Session& session, const std::string& image, ContainerO
|
||||
|
||||
// Disable auto-delete only after successful start
|
||||
runningContainer.SetDeleteOnClose(false);
|
||||
cidFile.Commit(containerId);
|
||||
|
||||
// Handle attach if requested
|
||||
if (WI_IsFlagSet(startFlags, WSLCContainerStartFlagsAttach))
|
||||
@@ -360,19 +370,19 @@ int ContainerService::Run(Session& session, const std::string& image, ContainerO
|
||||
return consoleService.AttachToCurrentConsole(runningContainer.GetInitProcess());
|
||||
}
|
||||
|
||||
WSLCContainerId containerId{};
|
||||
THROW_IF_FAILED(container.GetId(containerId));
|
||||
PrintMessage(L"%hs", stdout, containerId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CreateContainerResult ContainerService::Create(Session& session, const std::string& image, ContainerOptions runOptions)
|
||||
{
|
||||
CidFile cidFile(runOptions.CidFile);
|
||||
auto runningContainer = CreateInternal(session, image, runOptions);
|
||||
runningContainer.SetDeleteOnClose(false);
|
||||
auto& container = runningContainer.Get();
|
||||
WSLCContainerId id{};
|
||||
THROW_IF_FAILED(container.GetId(id));
|
||||
cidFile.Commit(id);
|
||||
return {.Id = id};
|
||||
}
|
||||
|
||||
|
||||
@@ -226,6 +226,11 @@ void SetContainerOptionsFromArgs(CLIExecutionContext& context)
|
||||
{
|
||||
ContainerOptions options;
|
||||
|
||||
if (context.Args.Contains(ArgType::CIDFile))
|
||||
{
|
||||
options.CidFile = context.Args.Get<ArgType::CIDFile>();
|
||||
}
|
||||
|
||||
if (context.Args.Contains(ArgType::Name))
|
||||
{
|
||||
options.Name = WideToMultiByte(context.Args.Get<ArgType::Name>());
|
||||
|
||||
@@ -71,6 +71,7 @@ COMMAND_LINE_TEST_CASE(
|
||||
true)
|
||||
COMMAND_LINE_TEST_CASE(L"container run ubuntu bash -c 'echo Hello World'", L"run", true)
|
||||
COMMAND_LINE_TEST_CASE(L"container run ubuntu", L"run", true)
|
||||
COMMAND_LINE_TEST_CASE(L"container run --cidfile C:\\temp\\cidfile ubuntu", L"run", true)
|
||||
COMMAND_LINE_TEST_CASE(L"container run -it --name foo ubuntu", L"run", true)
|
||||
COMMAND_LINE_TEST_CASE(L"container run --rm -it --name foo ubuntu", L"run", true)
|
||||
COMMAND_LINE_TEST_CASE(L"stop", L"stop", true)
|
||||
@@ -84,6 +85,7 @@ COMMAND_LINE_TEST_CASE(L"container start --attach cont", L"start", true)
|
||||
COMMAND_LINE_TEST_CASE(L"container start -a cont", L"start", true)
|
||||
COMMAND_LINE_TEST_CASE(L"create ubuntu:latest", L"create", true)
|
||||
COMMAND_LINE_TEST_CASE(L"container create --name foo ubuntu", L"create", true)
|
||||
COMMAND_LINE_TEST_CASE(L"container create --cidfile C:\\temp\\cidfile --name foo ubuntu", L"create", true)
|
||||
COMMAND_LINE_TEST_CASE(L"create --workdir /app ubuntu", L"create", true)
|
||||
COMMAND_LINE_TEST_CASE(L"create -w /app ubuntu", L"create", true)
|
||||
COMMAND_LINE_TEST_CASE(L"container create --workdir /app ubuntu sh", L"create", true)
|
||||
|
||||
@@ -106,6 +106,36 @@ class WSLCE2EContainerCreateTests
|
||||
VerifyContainerIsListed(containerId, L"created");
|
||||
}
|
||||
|
||||
WSLC_TEST_METHOD(WSLCE2E_Container_Create_CIDFile_Valid)
|
||||
{
|
||||
// Prepare a CID file path that does not exist
|
||||
const auto cidFilePath = wsl::windows::common::filesystem::GetTempFilename();
|
||||
VERIFY_IS_TRUE(DeleteFileW(cidFilePath.c_str()));
|
||||
auto deleteCidFile = wil::scope_exit([&]() { VERIFY_IS_TRUE(DeleteFileW(cidFilePath.c_str())); });
|
||||
|
||||
auto result = RunWslc(std::format(
|
||||
L"container create --cidfile \"{}\" --name {} {}", EscapePath(cidFilePath.wstring()), WslcContainerName, DebianImage.NameAndTag()));
|
||||
result.Verify({.Stderr = L"", .ExitCode = 0});
|
||||
|
||||
const auto containerId = result.GetStdoutOneLine();
|
||||
VERIFY_IS_TRUE(std::filesystem::exists(cidFilePath));
|
||||
VERIFY_ARE_EQUAL(containerId, ReadFileContent(cidFilePath.wstring()));
|
||||
}
|
||||
|
||||
WSLC_TEST_METHOD(WSLCE2E_Container_Create_CIDFile_AlreadyExists)
|
||||
{
|
||||
const auto cidFilePath = wsl::windows::common::filesystem::GetTempFilename();
|
||||
auto deleteCidFile = wil::scope_exit([&]() { VERIFY_IS_TRUE(DeleteFileW(cidFilePath.c_str())); });
|
||||
|
||||
auto result = RunWslc(std::format(
|
||||
L"container create --cidfile \"{}\" --name {} {}", EscapePath(cidFilePath.wstring()), WslcContainerName, DebianImage.NameAndTag()));
|
||||
result.Verify(
|
||||
{.Stderr = std::format(L"CID file '{}' already exists\r\nError code: ERROR_FILE_EXISTS\r\n", EscapePath(cidFilePath.wstring())),
|
||||
.ExitCode = 1});
|
||||
|
||||
VerifyContainerIsNotListed(WslcContainerName);
|
||||
}
|
||||
|
||||
WSLC_TEST_METHOD(WSLCE2E_Container_Create_DuplicateContainerName)
|
||||
{
|
||||
VerifyContainerIsNotListed(WslcContainerName);
|
||||
@@ -797,6 +827,7 @@ private:
|
||||
{
|
||||
std::wstringstream options;
|
||||
options << L"The following options are available:\r\n" //
|
||||
<< L" --cidfile Write the container ID to the provided path\r\n"
|
||||
<< L" --dns IP address of the DNS nameserver in resolv.conf\r\n"
|
||||
<< L" --dns-option Set DNS options\r\n"
|
||||
<< L" --dns-search Set DNS search domains\r\n"
|
||||
|
||||
@@ -88,6 +88,39 @@ class WSLCE2EContainerRunTests
|
||||
VerifyContainerIsListed(WslcContainerName, L"exited");
|
||||
}
|
||||
|
||||
WSLC_TEST_METHOD(WSLCE2E_Container_Run_CIDFile_Valid)
|
||||
{
|
||||
// Prepare a CID file path that does not exist
|
||||
const auto cidFilePath = wsl::windows::common::filesystem::GetTempFilename();
|
||||
VERIFY_IS_TRUE(DeleteFileW(cidFilePath.c_str()));
|
||||
auto deleteCidFile = wil::scope_exit([&]() { VERIFY_IS_TRUE(DeleteFileW(cidFilePath.c_str())); });
|
||||
|
||||
auto result = RunWslc(std::format(
|
||||
L"container run -d --cidfile \"{}\" --name {} {} sleep infinity",
|
||||
EscapePath(cidFilePath.wstring()),
|
||||
WslcContainerName,
|
||||
DebianImage.NameAndTag()));
|
||||
result.Verify({.Stderr = L"", .ExitCode = 0});
|
||||
|
||||
const auto containerId = result.GetStdoutOneLine();
|
||||
VERIFY_IS_TRUE(std::filesystem::exists(cidFilePath));
|
||||
VERIFY_ARE_EQUAL(containerId, ReadFileContent(cidFilePath.wstring()));
|
||||
}
|
||||
|
||||
WSLC_TEST_METHOD(WSLCE2E_Container_Run_CIDFile_AlreadyExists)
|
||||
{
|
||||
const auto cidFilePath = wsl::windows::common::filesystem::GetTempFilename();
|
||||
auto deleteCidFile = wil::scope_exit([&]() { VERIFY_IS_TRUE(DeleteFileW(cidFilePath.c_str())); });
|
||||
|
||||
auto result = RunWslc(std::format(
|
||||
L"container run --cidfile \"{}\" --name {} {}", EscapePath(cidFilePath.wstring()), WslcContainerName, DebianImage.NameAndTag()));
|
||||
result.Verify(
|
||||
{.Stderr = std::format(L"CID file '{}' already exists\r\nError code: ERROR_FILE_EXISTS\r\n", EscapePath(cidFilePath.wstring())),
|
||||
.ExitCode = 1});
|
||||
|
||||
VerifyContainerIsNotListed(WslcContainerName);
|
||||
}
|
||||
|
||||
WSLC_TEST_METHOD(WSLCE2E_Container_Run_Entrypoint)
|
||||
{
|
||||
auto result = RunWslc(std::format(L"container run --rm --entrypoint /bin/whoami {}", DebianImage.NameAndTag()));
|
||||
@@ -786,6 +819,7 @@ private:
|
||||
{
|
||||
std::wstringstream options;
|
||||
options << L"The following options are available:\r\n"
|
||||
<< L" --cidfile Write the container ID to the provided path\r\n"
|
||||
<< L" -d,--detach Run container in detached mode\r\n"
|
||||
<< L" --dns IP address of the DNS nameserver in resolv.conf\r\n"
|
||||
<< L" --dns-option Set DNS options\r\n"
|
||||
|
||||
Reference in New Issue
Block a user