From f720e6e038b38c9d717fc9b982536db40d42f011 Mon Sep 17 00:00:00 2001 From: Logan Bussell Date: Thu, 21 Sep 2023 13:01:14 -0700 Subject: [PATCH] Add native AOT image tests (#4859) --- .../DotNetImageVariant.cs | 5 +- .../ImageData.cs | 4 +- .../ImageScenarioVerifier.cs | 73 +++++++++++++------ .../ProductImageData.cs | 63 ++++++---------- .../RuntimeDepsImageTests.cs | 46 +++++++++--- .../TestAppArtifacts/Dockerfile.linux | 14 ++++ .../Microsoft.DotNet.Docker.Tests/TestData.cs | 44 +++++------ 7 files changed, 148 insertions(+), 101 deletions(-) diff --git a/tests/Microsoft.DotNet.Docker.Tests/DotNetImageVariant.cs b/tests/Microsoft.DotNet.Docker.Tests/DotNetImageVariant.cs index 32f37bfe4..31366df68 100644 --- a/tests/Microsoft.DotNet.Docker.Tests/DotNetImageVariant.cs +++ b/tests/Microsoft.DotNet.Docker.Tests/DotNetImageVariant.cs @@ -10,7 +10,8 @@ namespace Microsoft.DotNet.Docker.Tests public enum DotNetImageVariant { None = 0, - Composite = 1 << 0, - Extra = 1 << 1 + AOT = 1 << 0, + Composite = 1 << 1, + Extra = 1 << 2, } } diff --git a/tests/Microsoft.DotNet.Docker.Tests/ImageData.cs b/tests/Microsoft.DotNet.Docker.Tests/ImageData.cs index 1761019cc..5624e92a8 100644 --- a/tests/Microsoft.DotNet.Docker.Tests/ImageData.cs +++ b/tests/Microsoft.DotNet.Docker.Tests/ImageData.cs @@ -134,9 +134,9 @@ namespace Microsoft.DotNet.Docker.Tests public static string GetRepoNameModifier() => $"{(Config.IsNightlyRepo ? "/nightly" : string.Empty)}"; - public static string GetImageName(string tag, string variantName, string repoNameModifier = null) + public static string GetImageName(string tag, string repoName, string repoNameModifier = null) { - string repo = $"dotnet{repoNameModifier ?? GetRepoNameModifier()}/{variantName}"; + string repo = $"dotnet{repoNameModifier ?? GetRepoNameModifier()}/{repoName}"; string registry = GetRegistryName(repo, tag); return $"{registry}{repo}:{tag}"; diff --git a/tests/Microsoft.DotNet.Docker.Tests/ImageScenarioVerifier.cs b/tests/Microsoft.DotNet.Docker.Tests/ImageScenarioVerifier.cs index 028ea714e..d41b73c6f 100644 --- a/tests/Microsoft.DotNet.Docker.Tests/ImageScenarioVerifier.cs +++ b/tests/Microsoft.DotNet.Docker.Tests/ImageScenarioVerifier.cs @@ -26,6 +26,7 @@ namespace Microsoft.DotNet.Docker.Tests private readonly string _adminUser = DockerHelper.IsLinuxContainerModeEnabled ? "root" : "ContainerAdministrator"; private readonly string _nonRootUser = DockerHelper.IsLinuxContainerModeEnabled ? "app" : "ContainerUser"; private readonly bool _nonRootUserSupported; + private readonly bool _isAot; public ImageScenarioVerifier( ProductImageData imageData, @@ -38,11 +39,18 @@ namespace Microsoft.DotNet.Docker.Tests _isWeb = isWeb; _outputHelper = outputHelper; _nonRootUserSupported = DockerHelper.IsLinuxContainerModeEnabled && _imageData.Version.Major > 7; + _isAot = _imageData.ImageVariant.HasFlag(DotNetImageVariant.AOT); } public async Task Execute() { - string solutionDir = CreateTestSolutionWithSdkImage(_isWeb ? "web" : "console"); + string appType = _isAot && _isWeb + ? "webapiaot" + : _isWeb + ? "web" + : "console"; + + string solutionDir = CreateTestSolutionWithSdkImage(appType); List tags = new List(); InjectCustomTestCode(Path.Combine(solutionDir, "app")); @@ -55,10 +63,13 @@ namespace Microsoft.DotNet.Docker.Tests // the password isn't necessarily provided in that stage. string customBuildArgs = $"rid={_imageData.Rid}"; + string buildStageTarget = _isAot ? "publish_aot" : "build"; + string appStageTarget = _isAot ? "aot_app" : "self_contained_app"; + if (!_imageData.HasCustomSdk) { // Use `sdk` image to build and run test app - string buildTag = BuildTestAppImage("build", solutionDir, customBuildArgs); + string buildTag = BuildTestAppImage(buildStageTarget, solutionDir, customBuildArgs); tags.Add(buildTag); string dotnetRunArgs = _isWeb && _imageData.Version.Major <= 7 ? $" --urls http://0.0.0.0:{_imageData.DefaultPort}" : string.Empty; await RunTestAppImage(buildTag, command: $"dotnet run ${dotnetRunArgs}"); @@ -73,32 +84,36 @@ namespace Microsoft.DotNet.Docker.Tests await RunTestAppImage(unitTestTag); } - // Use `sdk` image to publish FX dependent app and run with `runtime` or `aspnet` image - string fxDepTag = BuildTestAppImage("fx_dependent_app", solutionDir, customBuildArgs); - tags.Add(fxDepTag); - // If we're a web app on Windows, use the ContainerAdministrator account - string fxDepUser = (_isWeb && !DockerHelper.IsLinuxContainerModeEnabled) ? _adminUser : null; - await RunTestAppImage(fxDepTag, user: fxDepUser); + // AOT compiled apps don't need the .NET runtime. + if (!_isAot) + { + // Use `sdk` image to publish FX dependent app and run with `runtime` or `aspnet` image + string fxDepTag = BuildTestAppImage("fx_dependent_app", solutionDir, customBuildArgs); + tags.Add(fxDepTag); + // If we're a web app on Windows, use the ContainerAdministrator account + string fxDepUser = (_isWeb && !DockerHelper.IsLinuxContainerModeEnabled) ? _adminUser : null; + await RunTestAppImage(fxDepTag, user: fxDepUser); - // For distroless, run another test that explicitly runs the container as a root user to verify - // the root user is defined. - if (!_isWeb && DockerHelper.IsLinuxContainerModeEnabled && _imageData.IsDistroless && - (!_imageData.OS.StartsWith(OS.Mariner) || _imageData.Version.Major > 6)) - { - await RunTestAppImage(fxDepTag, user: _adminUser); - } - // For non-distroless, which uses the root user by default, run the test as the non-root user - else if (_nonRootUserSupported && !_imageData.IsDistroless) - { - await RunTestAppImage(fxDepTag, user: _nonRootUser); + // For distroless, run another test that explicitly runs the container as a root user to verify + // the root user is defined. + if (!_isWeb && DockerHelper.IsLinuxContainerModeEnabled && _imageData.IsDistroless && + (!_imageData.OS.StartsWith(OS.Mariner) || _imageData.Version.Major > 6)) + { + await RunTestAppImage(fxDepTag, user: _adminUser); + } + // For non-distroless, which uses the root user by default, run the test as the non-root user + else if (_nonRootUserSupported && !_imageData.IsDistroless) + { + await RunTestAppImage(fxDepTag, user: _nonRootUser); + } } // There is no point in testing composite images here because there are no composite-specific // runtime-deps or SDK images - if (DockerHelper.IsLinuxContainerModeEnabled && _imageData.ImageVariant != DotNetImageVariant.Composite) + if (DockerHelper.IsLinuxContainerModeEnabled && !_imageData.ImageVariant.HasFlag(DotNetImageVariant.Composite)) { // Use `sdk` image to publish self contained app and run with `runtime-deps` image - string selfContainedTag = BuildTestAppImage("self_contained_app", solutionDir, customBuildArgs); + string selfContainedTag = BuildTestAppImage(appStageTarget, solutionDir, customBuildArgs); tags.Add(selfContainedTag); await RunTestAppImage(selfContainedTag, user: _adminUser); @@ -197,7 +212,12 @@ namespace Microsoft.DotNet.Docker.Tests { string tag = _imageData.GetIdentifier(stageTarget); - DotNetImageRepo runtimeImageRepo = _isWeb ? DotNetImageRepo.Aspnet : DotNetImageRepo.Runtime; + DotNetImageRepo runtimeImageRepo = _isAot + ? DotNetImageRepo.Runtime_Deps + : _isWeb + ? DotNetImageRepo.Aspnet + : DotNetImageRepo.Runtime; + List buildArgs = new() { $"sdk_image={_imageData.GetImage(DotNetImageRepo.SDK, _dockerHelper)}", @@ -298,7 +318,7 @@ namespace Microsoft.DotNet.Docker.Tests "--no-restore" }; - if (templateName == "web") + if (templateName.Contains("web")) { args = args.Append("--exclude-launch-settings"); } @@ -331,7 +351,12 @@ namespace Microsoft.DotNet.Docker.Tests if (_isWeb && !Config.IsHttpVerificationDisabled) { - await VerifyHttpResponseFromContainerAsync(containerName, _dockerHelper, _outputHelper, _imageData.DefaultPort); + await VerifyHttpResponseFromContainerAsync( + containerName, + _dockerHelper, + _outputHelper, + _imageData.DefaultPort, + pathAndQuery: _isAot ? "todos" : null); } } finally diff --git a/tests/Microsoft.DotNet.Docker.Tests/ProductImageData.cs b/tests/Microsoft.DotNet.Docker.Tests/ProductImageData.cs index 7aaea3fef..502f8abe3 100644 --- a/tests/Microsoft.DotNet.Docker.Tests/ProductImageData.cs +++ b/tests/Microsoft.DotNet.Docker.Tests/ProductImageData.cs @@ -13,14 +13,9 @@ namespace Microsoft.DotNet.Docker.Tests private string _sdkOS; private string _osTag; private ImageVersion? _versionFamily; - private DotNetImageVariant _imageVariant = DotNetImageVariant.None; - private DotNetImageRepo _supportedImageRepos = - DotNetImageRepo.Runtime_Deps - | DotNetImageRepo.Runtime - | DotNetImageRepo.Aspnet - | DotNetImageRepo.SDK; public bool HasCustomSdk => _sdkOS != null; + public bool GlobalizationInvariantMode => (!ImageVariant.HasFlag(DotNetImageVariant.Extra) || Version.Major == 6 || Version.Major == 7) @@ -28,51 +23,32 @@ namespace Microsoft.DotNet.Docker.Tests public string SdkOS { - get - { - if (_sdkOS != null) - { - return _sdkOS; - } - - return OS; - } - set { _sdkOS = value; } + get => HasCustomSdk ? _sdkOS : OS; + init => _sdkOS = value; } + public DotNetImageVariant SdkImageVariant { get; init; } = DotNetImageVariant.None; + public string OSTag { - get - { - if (_osTag is not null) - { - return _osTag; - } - - return OS; - } - set { _osTag = value; } + get => _osTag != null ? _osTag : OS; + init => _osTag = value; } - public ImageVersion Version { get; set; } + public ImageVersion Version { get; init; } public ImageVersion VersionFamily { get { return _versionFamily.GetValueOrDefault(Version); } - set { _versionFamily = value; } + init { _versionFamily = value; } } - public DotNetImageVariant ImageVariant - { - get => _imageVariant; - set => _imageVariant = value; - } + public DotNetImageVariant ImageVariant { get; init;} - public DotNetImageRepo SupportedImageRepos - { - get => _supportedImageRepos; - set => _supportedImageRepos = value; - } + public DotNetImageRepo SupportedImageRepos { get; init; } = DotNetImageRepo.Runtime_Deps + | DotNetImageRepo.Runtime + | DotNetImageRepo.Aspnet + | DotNetImageRepo.SDK; public string VersionString => Version.ToString(); @@ -97,12 +73,18 @@ namespace Microsoft.DotNet.Docker.Tests public string GetImage(DotNetImageRepo imageRepo, DockerHelper dockerHelper) { - // ASP.NET composite includes its own runtime that we want to test - if (ImageVariant == DotNetImageVariant.Composite && imageRepo == DotNetImageRepo.Runtime) + // ASP.NET composite includes its own runtime that we want to test. + if (ImageVariant.HasFlag(DotNetImageVariant.Composite) && imageRepo == DotNetImageRepo.Runtime) { imageRepo = DotNetImageRepo.Aspnet; } + if (imageRepo != DotNetImageRepo.SDK && !SupportedImageRepos.HasFlag(imageRepo)) + { + throw new ArgumentOutOfRangeException(nameof(imageRepo), + $"Unsupported image type '{imageRepo}' for Image Variant '{ImageVariant}'"); + } + string tag = GetTagName(imageRepo); string imageName = GetImageName(tag, GetImageRepoName(imageRepo)); @@ -170,6 +152,7 @@ namespace Microsoft.DotNet.Docker.Tests case DotNetImageRepo.SDK: imageVersion = Version; os = SdkOS; + variant = GetImageVariantName(SdkImageVariant); break; default: throw new NotSupportedException($"Unsupported image type '{imageRepo}'"); diff --git a/tests/Microsoft.DotNet.Docker.Tests/RuntimeDepsImageTests.cs b/tests/Microsoft.DotNet.Docker.Tests/RuntimeDepsImageTests.cs index fea959573..3de46cf61 100644 --- a/tests/Microsoft.DotNet.Docker.Tests/RuntimeDepsImageTests.cs +++ b/tests/Microsoft.DotNet.Docker.Tests/RuntimeDepsImageTests.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -25,6 +26,28 @@ namespace Microsoft.DotNet.Docker.Tests public static IEnumerable GetImageData() => GetImageData(DotNetImageRepo.Runtime_Deps); + [LinuxImageTheory] + [MemberData(nameof(GetImageData))] + public async Task VerifyAotAppScenario(ProductImageData imageData) + { + if (!imageData.ImageVariant.HasFlag(DotNetImageVariant.AOT)) + { + OutputHelper.WriteLine("Test is only relevant to AOT images."); + return; + } + + if (imageData.Arch == Arch.Arm) + { + OutputHelper.WriteLine("Skipping test due to https://github.com/dotnet/docker-tools/issues/1177. " + + "ImageBuilder is unable to queue arm32 AOT images together with the arm64 AOT SDKs. " + + "Re-enable once fixed."); + return; + } + + ImageScenarioVerifier verifier = new(imageData, DockerHelper, OutputHelper, isWeb: true); + await verifier.Execute(); + } + [LinuxImageTheory] [MemberData(nameof(GetImageData))] public void VerifyEnvironmentVariables(ProductImageData imageData) @@ -189,7 +212,9 @@ namespace Microsoft.DotNet.Docker.Tests private static IEnumerable GetExpectedPackages(ProductImageData imageData, DotNetImageRepo imageRepo) { - IEnumerable expectedPackages = GetRuntimeDepsPackages(imageData); + IEnumerable expectedPackages = imageData.ImageVariant.HasFlag(DotNetImageVariant.AOT) + ? GetAotDepsPackages(imageData) + : GetRuntimeDepsPackages(imageData); if (imageData.IsDistroless) { @@ -220,7 +245,7 @@ namespace Microsoft.DotNet.Docker.Tests _ => throw new NotSupportedException() }; - private static IEnumerable GetRuntimeDepsPackages(ProductImageData imageData) => imageData switch + private static IEnumerable GetAotDepsPackages(ProductImageData imageData) => imageData switch { { OS: OS.Mariner20Distroless, Version: ImageVersion version } when version.Major == 6 || version.Major == 7 => new[] @@ -229,7 +254,6 @@ namespace Microsoft.DotNet.Docker.Tests "glibc", "krb5", "libgcc", - "libstdc++", "openssl", "openssl-libs", "prebuilt-ca-certificates", @@ -242,7 +266,6 @@ namespace Microsoft.DotNet.Docker.Tests "icu", "krb5", "libgcc", - "libstdc++", "openssl-libs", "zlib" }, @@ -250,7 +273,6 @@ namespace Microsoft.DotNet.Docker.Tests { "glibc", "libgcc", - "libstdc++", "openssl-libs", "zlib" }, @@ -260,7 +282,6 @@ namespace Microsoft.DotNet.Docker.Tests "libc6", "libgcc-s1", "libssl3", - "libstdc++6", "zlib1g" }, { OS: OS.Focal } => new[] @@ -271,7 +292,6 @@ namespace Microsoft.DotNet.Docker.Tests "libgssapi-krb5-2", "libicu66", "libssl1.1", - "libstdc++6", "zlib1g" }, { OS: string os } when os.Contains(OS.Alpine) => new[] @@ -279,17 +299,15 @@ namespace Microsoft.DotNet.Docker.Tests "ca-certificates-bundle", "libgcc", "libssl3", - "libstdc++", "zlib" }, { OS: OS.BookwormSlim } => new[] { "ca-certificates", "libc6", - "libgcc-s1", + "libgcc-s1", "libicu72", "libssl3", - "libstdc++6", "tzdata", "zlib1g" }, @@ -301,12 +319,18 @@ namespace Microsoft.DotNet.Docker.Tests "libgssapi-krb5-2", "libicu67", "libssl1.1", - "libstdc++6", "zlib1g" }, _ => throw new NotSupportedException() }; + private static IEnumerable GetRuntimeDepsPackages(ProductImageData imageData) { + string libstdcppPkgName = imageData.OS.Contains(OS.Mariner) || imageData.OS.Contains(OS.Alpine) + ? "libstdc++" + : "libstdc++6"; + return GetAotDepsPackages(imageData).Append(libstdcppPkgName); + } + internal static IEnumerable GetExtraPackages(ProductImageData imageData) => imageData switch { { IsDistroless: true, OS: string os } when os.Contains(OS.Mariner) => new[] diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/Dockerfile.linux b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/Dockerfile.linux index a0f8e4aa4..766d5d045 100644 --- a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/Dockerfile.linux +++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/Dockerfile.linux @@ -2,6 +2,7 @@ ARG sdk_image ARG runtime_image ARG runtime_deps_image + FROM $sdk_image as build ARG rid @@ -61,3 +62,16 @@ EXPOSE $port WORKDIR /app COPY --from=publish_self_contained /source/app/out ./ ENTRYPOINT ["./app"] + + +FROM build as publish_aot + +RUN dotnet publish -r $rid --no-restore -o /app + + +FROM $runtime_deps_image AS aot_app + +WORKDIR /app +COPY --from=publish_aot /app . +USER $APP_UID +ENTRYPOINT ["./app"] diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestData.cs b/tests/Microsoft.DotNet.Docker.Tests/TestData.cs index 6595428cd..baa25105c 100644 --- a/tests/Microsoft.DotNet.Docker.Tests/TestData.cs +++ b/tests/Microsoft.DotNet.Docker.Tests/TestData.cs @@ -165,28 +165,28 @@ namespace Microsoft.DotNet.Docker.Tests private static readonly ProductImageData[] s_linuxMonitorTestData = { - new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Amd64 }, - new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Arm64 }, - new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Amd64 }, - new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Arm64 }, - new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Amd64 }, - new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Arm64 }, - new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Amd64 }, - new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Arm64 }, - new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Amd64 }, - new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Arm64 }, - new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Amd64 }, - new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Arm64 }, - new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Amd64 }, - new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Arm64 }, - new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Amd64 }, - new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Arm64 }, - new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Amd64 }, - new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Arm64 }, - new ProductImageData { Version = V8_0, VersionFamily = V8_0, OS = OS.JammyChiseled, OSTag = OS.UbuntuChiseled, Arch = Arch.Amd64 }, - new ProductImageData { Version = V8_0, VersionFamily = V8_0, OS = OS.JammyChiseled, OSTag = OS.UbuntuChiseled, Arch = Arch.Arm64 }, - new ProductImageData { Version = V8_0, VersionFamily = V8_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Amd64 }, - new ProductImageData { Version = V8_0, VersionFamily = V8_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Arm64 }, + new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V6_3, VersionFamily = V6_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_2, VersionFamily = V7_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Alpine318, OSTag = OS.Alpine, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Mariner20, OSTag = OS.Mariner, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V7_3, VersionFamily = V7_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V8_0, VersionFamily = V8_0, OS = OS.JammyChiseled, OSTag = OS.UbuntuChiseled, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V8_0, VersionFamily = V8_0, OS = OS.JammyChiseled, OSTag = OS.UbuntuChiseled, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V8_0, VersionFamily = V8_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Amd64, SupportedImageRepos = DotNetImageRepo.Monitor }, + new ProductImageData { Version = V8_0, VersionFamily = V8_0, OS = OS.Mariner20Distroless, OSTag = OS.MarinerDistroless, Arch = Arch.Arm64, SupportedImageRepos = DotNetImageRepo.Monitor } }; private static readonly ProductImageData[] s_windowsMonitorTestData =