diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index de258d29d..5744ee0de 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -2124,6 +2124,10 @@ For privacy information about this product please visit https://aka.ms/privacy.< Volume not found: '{}' {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated + + Image '{}' not found. + {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated + Missing required option: '{}' {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated diff --git a/src/windows/wslc/tasks/ContainerTasks.cpp b/src/windows/wslc/tasks/ContainerTasks.cpp index 804f54282..336daadac 100644 --- a/src/windows/wslc/tasks/ContainerTasks.cpp +++ b/src/windows/wslc/tasks/ContainerTasks.cpp @@ -24,6 +24,7 @@ Module Name: #include using namespace wsl::shared; +using namespace wsl::windows::common; using namespace wsl::windows::common::string; using namespace wsl::windows::common::wslutil; using namespace wsl::windows::wslc::execution; @@ -31,6 +32,26 @@ using namespace wsl::windows::wslc::models; using namespace wsl::windows::wslc::services; namespace wsl::windows::wslc::task { + +static bool TryInspectContainer(Session& session, const std::string& containerId, std::optional& inspectData) +{ + try + { + inspectData = ContainerService::Inspect(session, containerId); + return true; + } + catch (const wil::ResultException& ex) + { + if (ex.GetErrorCode() == WSLC_E_CONTAINER_NOT_FOUND) + { + PrintMessage(Localization::MessageWslcContainerNotFound(containerId.c_str()), stderr); + return false; + } + + throw; + } +} + void AttachContainer::operator()(CLIExecutionContext& context) const { WI_ASSERT(context.Data.Contains(Data::Session)); @@ -71,8 +92,15 @@ void InspectContainers(CLIExecutionContext& context) std::vector result; for (const auto& id : containerIds) { - auto inspectData = ContainerService::Inspect(session, WideToMultiByte(id)); - result.push_back(inspectData); + std::optional inspectData; + if (TryInspectContainer(session, WideToMultiByte(id), inspectData)) + { + result.push_back(*inspectData); + } + else + { + context.ExitCode = 1; + } } auto json = ToJson(result, c_jsonPrettyPrintIndent); diff --git a/src/windows/wslc/tasks/ImageTasks.cpp b/src/windows/wslc/tasks/ImageTasks.cpp index bba509ee4..e177241a9 100644 --- a/src/windows/wslc/tasks/ImageTasks.cpp +++ b/src/windows/wslc/tasks/ImageTasks.cpp @@ -26,12 +26,34 @@ Module Name: #include using namespace wsl::shared; +using namespace wsl::windows::common; using namespace wsl::windows::common::string; using namespace wsl::windows::common::wslutil; using namespace wsl::windows::wslc::execution; +using namespace wsl::windows::wslc::models; using namespace wsl::windows::wslc::services; namespace wsl::windows::wslc::task { + +static bool TryInspectImage(Session& session, const std::string& imageId, std::optional& inspectData) +{ + try + { + inspectData = ImageService::Inspect(session, imageId); + return true; + } + catch (const wil::ResultException& ex) + { + if (ex.GetErrorCode() == WSLC_E_IMAGE_NOT_FOUND) + { + PrintMessage(Localization::MessageWslcImageNotFound(imageId.c_str()), stderr); + return false; + } + + throw; + } +} + void BuildImage(CLIExecutionContext& context) { WI_ASSERT(context.Data.Contains(Data::Session)); @@ -197,8 +219,15 @@ void InspectImages(CLIExecutionContext& context) std::vector result; for (const auto& id : imageIds) { - auto inspectData = ImageService::Inspect(session, WideToMultiByte(id)); - result.push_back(inspectData); + std::optional inspectData; + if (TryInspectImage(session, WideToMultiByte(id), inspectData)) + { + result.push_back(*inspectData); + } + else + { + context.ExitCode = 1; + } } auto json = ToJson(result, c_jsonPrettyPrintIndent); diff --git a/test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp new file mode 100644 index 000000000..f804004c1 --- /dev/null +++ b/test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp @@ -0,0 +1,160 @@ +/*++ + +Copyright (c) Microsoft. All rights reserved. + +Module Name: + + WSLCE2EContainerInspectTests.cpp + +Abstract: + + This file contains end-to-end tests for WSLC. +--*/ + +#include "precomp.h" +#include "windows/Common.h" +#include "WSLCExecutor.h" +#include "WSLCE2EHelpers.h" +#include + +namespace WSLCE2ETests { +using namespace wsl::shared; +using namespace wsl::shared::string; + +class WSLCE2EContainerInspectTests +{ + WSLC_TEST_CLASS(WSLCE2EContainerInspectTests) + + TEST_CLASS_SETUP(ClassSetup) + { + EnsureImageIsLoaded(DebianImage); + return true; + } + + TEST_CLASS_CLEANUP(ClassCleanup) + { + EnsureContainerDoesNotExist(TestContainerName1); + EnsureContainerDoesNotExist(TestContainerName2); + EnsureImageIsDeleted(DebianImage); + return true; + } + + TEST_METHOD_SETUP(MethodSetup) + { + EnsureContainerDoesNotExist(TestContainerName1); + EnsureContainerDoesNotExist(TestContainerName2); + return true; + } + + WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_HelpCommand) + { + auto result = RunWslc(L"container inspect --help"); + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0}); + } + + WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_MissingContainerId) + { + auto result = RunWslc(L"container inspect"); + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Required argument not provided: 'container-id'\r\n", .ExitCode = 1}); + } + + WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_ContainerNotFound) + { + auto result = RunWslc(std::format(L"container inspect {}", TestContainerName1)); + result.Verify({.Stdout = L"[]\r\n", .Stderr = std::format(L"Container '{}' not found.\r\n", TestContainerName1), .ExitCode = 1}); + } + + WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_Success) + { + auto createResult = RunWslc(std::format(L"container create --name {} {}", TestContainerName1, DebianImage.NameAndTag())); + createResult.Verify({.Stderr = L"", .ExitCode = 0}); + + auto result = RunWslc(std::format(L"container inspect {}", TestContainerName1)); + result.Verify({.Stderr = L"", .ExitCode = 0}); + auto inspectData = + wsl::shared::FromJson>(result.Stdout.value().c_str()); + VERIFY_ARE_EQUAL(1u, inspectData.size()); + VERIFY_ARE_EQUAL(WideToMultiByte(TestContainerName1), inspectData[0].Name); + } + + WSLC_TEST_METHOD(WSLCE2E_Container_InspectMultiple_Success) + { + // Create two containers to inspect at the same time + auto result = RunWslc(std::format(L"container create --name {} {}", TestContainerName1, DebianImage.NameAndTag())); + result.Verify({.Stderr = L"", .ExitCode = 0}); + result = RunWslc(std::format(L"container create --name {} {}", TestContainerName2, DebianImage.NameAndTag())); + result.Verify({.Stderr = L"", .ExitCode = 0}); + + // Inspect both containers in the same command + result = RunWslc(std::format(L"container inspect {} {}", TestContainerName1, TestContainerName2)); + result.Verify({.Stderr = L"", .ExitCode = 0}); + auto inspectData = + wsl::shared::FromJson>(result.Stdout.value().c_str()); + VERIFY_ARE_EQUAL(2u, inspectData.size()); + VERIFY_ARE_EQUAL(WideToMultiByte(TestContainerName1), inspectData[0].Name); + VERIFY_ARE_EQUAL(WideToMultiByte(TestContainerName2), inspectData[1].Name); + } + + WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_MixedFoundNotFound) + { + // Create one container but not the other + auto result = RunWslc(std::format(L"container create --name {} {}", TestContainerName1, DebianImage.NameAndTag())); + result.Verify({.Stderr = L"", .ExitCode = 0}); + + // Inspect both containers in the same command, expecting one to be found and the other to not be found + result = RunWslc(std::format(L"container inspect {} {}", TestContainerName1, TestContainerName2)); + result.Verify({.Stderr = std::format(L"Container '{}' not found.\r\n", TestContainerName2), .ExitCode = 1}); + + // Verify found container + auto inspectData = + wsl::shared::FromJson>(result.Stdout.value().c_str()); + VERIFY_ARE_EQUAL(1u, inspectData.size()); + VERIFY_ARE_EQUAL(WideToMultiByte(TestContainerName1), inspectData[0].Name); + } + +private: + const std::wstring TestContainerName1 = L"wslc-e2e-container-inspect-1"; + const std::wstring TestContainerName2 = L"wslc-e2e-container-inspect-2"; + const TestImage& DebianImage = DebianTestImage(); + + std::wstring GetHelpMessage() const + { + std::wstringstream output; + output << GetWslcHeader() // + << GetDescription() // + << GetUsage() // + << GetAvailableCommands() // + << GetAvailableOptions(); + return output.str(); + } + + std::wstring GetDescription() const + { + return Localization::WSLCCLI_ContainerInspectLongDesc() + L"\r\n\r\n"; + } + + std::wstring GetUsage() const + { + return L"Usage: wslc container inspect [] \r\n\r\n"; + } + + std::wstring GetAvailableCommands() const + { + std::wstringstream commands; + commands << L"The following arguments are available:\r\n" // + << L" container-id Container ID\r\n" // + << L"\r\n"; + return commands.str(); + } + + std::wstring GetAvailableOptions() const + { + std::wstringstream options; + options << L"The following options are available:\r\n" // + << L" --session Specify the session to use\r\n" // + << L" -?,--help Shows help about the selected command\r\n" // + << L"\r\n"; + return options.str(); + } +}; +} // namespace WSLCE2ETests diff --git a/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp b/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp index 4601aa423..caeb451c9 100644 --- a/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp +++ b/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp @@ -51,8 +51,7 @@ class WSLCE2EImageInspectTests WSLC_TEST_METHOD(WSLCE2E_Image_Inspect_ImageNotFound) { auto result = RunWslc(std::format(L"image inspect {}", InvalidImage.NameAndTag())); - auto errorMessage = std::format(L"No such image: {}\r\nError code: WSLC_E_IMAGE_NOT_FOUND\r\n", InvalidImage.NameAndTag()); - result.Verify({.Stdout = L"", .Stderr = errorMessage, .ExitCode = 1}); + result.Verify({.Stdout = L"[]\r\n", .Stderr = std::format(L"Image '{}' not found.\r\n", InvalidImage.NameAndTag()), .ExitCode = 1}); } WSLC_TEST_METHOD(WSLCE2E_Image_Inspect_Success)