1
+/*++
2
+
3
+Copyright (c) Microsoft. All rights reserved.
4
+
5
+Module Name:
6
+
7
+ TestImageRegistry.cpp
8
+
9
+Abstract:
10
+
11
+ This file contains the implementation of the test image registry.
12
+--*/
13
+
14
+#include "precomp.h"
15
+#include "ImageModel.h"
16
+#include "WSLCExecutor.h"
17
+#include "WSLCE2EHelpers.h"
18
+#include "TestImageRegistry.h"
19
+
20
+namespace WSLCE2ETests {
21
+
22
+std::wstring TestImageRegistry::FormatCommand(const std::wstring& sessionName, const std::wstring& command)
23
+{
24
+ if (sessionName.empty())
25
+ {
26
+ return command;
27
+ }
28
+
29
+ return std::format(L"--session \"{}\" {}", sessionName, command);
30
+}
31
+
32
+TestImageRegistry& TestImageRegistry::Instance()
33
+{
34
+ static TestImageRegistry registry;
35
+ return registry;
36
+}
37
+
38
+TestImageRegistry::ImageKey TestImageRegistry::MakeKey(const TestImage& image, const std::wstring& sessionName)
39
+{
40
+ return ImageKey{sessionName, image.Name, image.Tag};
41
+}
42
+
43
+void TestImageRegistry::EnsureSeeded(const std::wstring& sessionName)
44
+{
45
+ if (m_seededSessions.contains(sessionName))
46
+ {
47
+ return;
48
+ }
49
+
50
+ auto result = RunWslc(FormatCommand(sessionName, L"image list --format json"));
51
+ result.Verify({.Stderr = L"", .ExitCode = 0});
52
+
53
+ const auto images = ParseNdjsonOutputAs<wsl::windows::wslc::models::ImageOutputInformation>(result);
54
+
55
+ for (const auto& image : images)
56
+ {
57
+ if (image.Repository != wsl::windows::wslc::models::c_none && image.Tag != wsl::windows::wslc::models::c_none)
58
+ {
59
+ m_loaded.insert(ImageKey{
60
+ sessionName, wsl::shared::string::MultiByteToWide(image.Repository), wsl::shared::string::MultiByteToWide(image.Tag)});
61
+ }
62
+ }
63
+
64
+ m_seededSessions.insert(sessionName);
65
+}
66
+
67
+void TestImageRegistry::EnsureLoaded(const TestImage& image, const std::wstring& sessionName)
68
+{
69
+ EnsureSeeded(sessionName);
70
+
71
+ if (m_loaded.contains(MakeKey(image, sessionName)))
72
+ {
73
+ return;
74
+ }
75
+
76
+ Load(image, sessionName);
77
+}
78
+
79
+void TestImageRegistry::Restore(const TestImage& image, const std::wstring& sessionName)
80
+{
81
+ EnsureSeeded(sessionName);
82
+ m_loaded.erase(MakeKey(image, sessionName));
83
+ Load(image, sessionName);
84
+}
85
+
86
+void TestImageRegistry::Load(const TestImage& image, const std::wstring& sessionName)
87
+{
88
+ auto result = RunWslc(FormatCommand(sessionName, std::format(L"image load --input \"{}\"", image.Path.wstring())));
89
+ result.Verify({.Stderr = L"", .ExitCode = 0});
90
+
91
+ m_loaded.insert(MakeKey(image, sessionName));
92
+}
93
+
94
+void TestImageRegistry::Delete(const TestImage& image, const std::wstring& sessionName)
95
+{
96
+ auto result = RunWslc(FormatCommand(sessionName, L"image list --format json"));
97
+ result.Verify({.Stderr = L"", .ExitCode = 0});
98
+
99
+ const auto images = ParseNdjsonOutputAs<wsl::windows::wslc::models::ImageOutputInformation>(result);
100
+ const auto name = wsl::shared::string::WideToMultiByte(image.Name);
101
+ const auto tag = wsl::shared::string::WideToMultiByte(image.Tag);
102
+ const bool present =
103
+ std::ranges::any_of(images, [&](const auto& candidate) { return candidate.Repository == name && candidate.Tag == tag; });
104
+
105
+ if (!present)
106
+ {
107
+ m_loaded.erase(MakeKey(image, sessionName));
108
+ return;
109
+ }
110
+
111
+ // Container enumeration is scoped to the default session, so named sessions rely on the
112
+ // force flag to remove the image out from under any containers still referencing it.
113
+ if (sessionName.empty())
114
+ {
115
+ EnsureImageContainersAreDeleted(image);
116
+ }
117
+
118
+ auto deleteResult = RunWslc(FormatCommand(sessionName, std::format(L"image delete --force {}", image.NameAndTag())));
119
+ deleteResult.Verify({.Stderr = L"", .ExitCode = 0});
120
+
121
+ m_loaded.erase(MakeKey(image, sessionName));
122
+}
123
+
124
+} // namespace WSLCE2ETests