-
Notifications
You must be signed in to change notification settings - Fork 1.7k
CLI: Add image import command #40309
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/wsl-for-apps
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| /*++ | ||
|
|
||
| Copyright (c) Microsoft. All rights reserved. | ||
|
|
||
| Module Name: | ||
|
|
||
| ImageImportCommand.cpp | ||
|
|
||
| Abstract: | ||
|
|
||
| Implementation of command execution logic. | ||
|
|
||
| --*/ | ||
|
|
||
| #include "ImageCommand.h" | ||
| #include "CLIExecutionContext.h" | ||
| #include "ImageTasks.h" | ||
| #include "SessionTasks.h" | ||
| #include "Task.h" | ||
|
|
||
| using namespace wsl::windows::wslc::execution; | ||
| using namespace wsl::windows::wslc::task; | ||
| using namespace wsl::shared; | ||
|
|
||
| namespace wsl::windows::wslc { | ||
| // Image Import Command | ||
| std::vector<Argument> ImageImportCommand::GetArguments() const | ||
| { | ||
| return { | ||
| Argument::Create(ArgType::ImportFile, true), | ||
| Argument::Create(ArgType::ImageId), | ||
| Argument::Create(ArgType::Session), | ||
| }; | ||
| } | ||
|
|
||
| std::wstring ImageImportCommand::ShortDescription() const | ||
| { | ||
| return Localization::WSLCCLI_ImageImportDesc(); | ||
| } | ||
|
|
||
| std::wstring ImageImportCommand::LongDescription() const | ||
| { | ||
| return Localization::WSLCCLI_ImageImportLongDesc(); | ||
| } | ||
|
|
||
| void ImageImportCommand::ExecuteInternal(CLIExecutionContext& context) const | ||
| { | ||
| context // | ||
| << CreateSession // | ||
| << ImportImage; | ||
| } | ||
| } // namespace wsl::windows::wslc | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -187,6 +187,31 @@ void ImageService::Load(wsl::windows::wslc::models::Session& session, const std: | |
| THROW_IF_FAILED(session.Get()->LoadImage(ToCOMInputHandle(imageFile.get()), nullptr, fileSize.QuadPart)); | ||
| } | ||
|
|
||
| void ImageService::Import(wsl::windows::wslc::models::Session& session, const std::wstring& input, const std::string& imageName) | ||
| { | ||
| HANDLE imageHandle = nullptr; | ||
| wil::unique_hfile imageFile; | ||
| ULONGLONG contentLength = 0; | ||
|
|
||
| if (input == L"-") | ||
| { | ||
| imageHandle = GetStdHandle(STD_INPUT_HANDLE); | ||
| } | ||
| else | ||
| { | ||
| imageFile.reset(CreateFileW(input.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)); | ||
| THROW_LAST_ERROR_IF(!imageFile); | ||
|
|
||
| LARGE_INTEGER fileSize{}; | ||
| THROW_LAST_ERROR_IF(!GetFileSizeEx(imageFile.get(), &fileSize)); | ||
|
|
||
| imageHandle = imageFile.get(); | ||
| contentLength = fileSize.QuadPart; | ||
| } | ||
|
|
||
| THROW_IF_FAILED(session.Get()->ImportImage(ToCOMInputHandle(imageHandle), imageName.c_str(), nullptr, contentLength)); | ||
| } | ||
|
Comment on lines
+196
to
+213
|
||
|
|
||
| void ImageService::Delete(wsl::windows::wslc::models::Session& session, const std::string& image, bool force, bool noPrune) | ||
| { | ||
| WSLCDeleteImageOptions options{}; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -187,6 +187,22 @@ void LoadImage(CLIExecutionContext& context) | |||||||||||||||||||||||||||
| THROW_HR_WITH_USER_ERROR(E_INVALIDARG, Localization::WSLCCLI_ImageLoadNoInputError()); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| void ImportImage(CLIExecutionContext& context) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| WI_ASSERT(context.Data.Contains(Data::Session)); | ||||||||||||||||||||||||||||
| WI_ASSERT(context.Args.Contains(ArgType::ImportFile)); | ||||||||||||||||||||||||||||
| auto& session = context.Data.Get<Data::Session>(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| std::string imageName; | ||||||||||||||||||||||||||||
| if (context.Args.Contains(ArgType::ImageId)) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| imageName = WideToMultiByte(context.Args.Get<ArgType::ImageId>()); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Comment on lines
+196
to
+201
|
||||||||||||||||||||||||||||
| std::string imageName; | |
| if (context.Args.Contains(ArgType::ImageId)) | |
| { | |
| imageName = WideToMultiByte(context.Args.Get<ArgType::ImageId>()); | |
| } | |
| THROW_HR_IF_MSG(E_INVALIDARG, !context.Args.Contains(ArgType::ImageId), "The image name must include a repository and tag."); | |
| auto imageName = WideToMultiByte(context.Args.Get<ArgType::ImageId>()); | |
| const auto tagSeparator = imageName.rfind(':'); | |
| THROW_HR_IF_MSG( | |
| E_INVALIDARG, | |
| (tagSeparator == std::string::npos) || (tagSeparator == 0) || (tagSeparator == imageName.size() - 1), | |
| "The image name must include a repository and tag."); |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,152 @@ | ||||||||||||||
| /*++ | ||||||||||||||
|
|
||||||||||||||
| Copyright (c) Microsoft. All rights reserved. | ||||||||||||||
|
|
||||||||||||||
| Module Name: | ||||||||||||||
|
|
||||||||||||||
| WSLCE2EImageImportTests.cpp | ||||||||||||||
|
|
||||||||||||||
| Abstract: | ||||||||||||||
|
|
||||||||||||||
| This file contains end-to-end tests for WSLC image import. | ||||||||||||||
| --*/ | ||||||||||||||
|
|
||||||||||||||
| #include "precomp.h" | ||||||||||||||
| #include "windows/Common.h" | ||||||||||||||
| #include "WSLCExecutor.h" | ||||||||||||||
| #include "WSLCE2EHelpers.h" | ||||||||||||||
|
|
||||||||||||||
| namespace WSLCE2ETests { | ||||||||||||||
| using namespace wsl::shared; | ||||||||||||||
|
|
||||||||||||||
| class WSLCE2EImageImportTests | ||||||||||||||
| { | ||||||||||||||
| WSLC_TEST_CLASS(WSLCE2EImageImportTests) | ||||||||||||||
|
|
||||||||||||||
| TEST_CLASS_CLEANUP(ClassCleanup) | ||||||||||||||
| { | ||||||||||||||
| EnsureImageIsDeleted(DebianImage); | ||||||||||||||
| EnsureImageIsDeleted(ImportedImage); | ||||||||||||||
| return true; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| TEST_METHOD_SETUP(MethodSetup) | ||||||||||||||
| { | ||||||||||||||
| EnsureImageIsLoaded(DebianImage); | ||||||||||||||
| EnsureImageIsDeleted(ImportedImage); | ||||||||||||||
| SavedArchivePath = wsl::windows::common::filesystem::GetTempFilename(); | ||||||||||||||
| return true; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| TEST_METHOD_CLEANUP(MethodCleanup) | ||||||||||||||
| { | ||||||||||||||
| DeleteFileW(SavedArchivePath.c_str()); | ||||||||||||||
| return true; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| WSLC_TEST_METHOD(WSLCE2E_Image_Import_HelpCommand) | ||||||||||||||
| { | ||||||||||||||
| auto result = RunWslc(L"image import --help"); | ||||||||||||||
| result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0}); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| WSLC_TEST_METHOD(WSLCE2E_Image_Import_MissingFile) | ||||||||||||||
| { | ||||||||||||||
| const auto result = RunWslc(L"image import"); | ||||||||||||||
| result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Required argument not provided: 'file'\r\n", .ExitCode = 1}); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| WSLC_TEST_METHOD(WSLCE2E_Image_Import_Success) | ||||||||||||||
| { | ||||||||||||||
| // Save image as a tarball | ||||||||||||||
| auto saveResult = RunWslc(std::format(L"image save --output \"{}\" {}", SavedArchivePath.wstring(), DebianImage.NameAndTag())); | ||||||||||||||
| saveResult.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0}); | ||||||||||||||
|
|
||||||||||||||
| // Import the tarball as a new image with a tag | ||||||||||||||
| auto importResult = RunWslc(std::format(L"image import \"{}\" {}", SavedArchivePath.wstring(), ImportedImage.NameAndTag())); | ||||||||||||||
| importResult.Verify({.Stderr = L"", .ExitCode = 0}); | ||||||||||||||
|
|
||||||||||||||
| // Verify the imported image is listed | ||||||||||||||
| VerifyImageIsListed(ImportedImage.NameAndTag()); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| WSLC_TEST_METHOD(WSLCE2E_Image_Import_WithoutTag) | ||||||||||||||
| { | ||||||||||||||
| // Save image as a tarball | ||||||||||||||
| auto saveResult = RunWslc(std::format(L"image save --output \"{}\" {}", SavedArchivePath.wstring(), DebianImage.NameAndTag())); | ||||||||||||||
| saveResult.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0}); | ||||||||||||||
|
|
||||||||||||||
| // Import without specifying an image name | ||||||||||||||
| auto importResult = RunWslc(std::format(L"image import \"{}\"", SavedArchivePath.wstring())); | ||||||||||||||
| importResult.Verify({.Stderr = L"", .ExitCode = 0}); | ||||||||||||||
|
Comment on lines
+79
to
+81
|
||||||||||||||
| // Import without specifying an image name | |
| auto importResult = RunWslc(std::format(L"image import \"{}\"", SavedArchivePath.wstring())); | |
| importResult.Verify({.Stderr = L"", .ExitCode = 0}); | |
| // Import without specifying an image name or tag should fail because the current import API requires a repo:tag. | |
| auto importResult = RunWslc(std::format(L"image import \"{}\"", SavedArchivePath.wstring())); | |
| importResult.Verify({.ExitCode = 1}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
imagepositional argument is currently optional, but the underlying import API requires an image name that includes a tag (repo:tag). As-is,wslc image import <file>will parse and execute but fail later in the service layer. Consider makingArgType::ImageIdrequired for this command (and updating help/usage/tests accordingly), or add CLI-side validation/defaulting so the command cannot reach execution with an invalid/missing image reference.