protected void VerifyCommonEnvironmentVariables( ProductImageData imageData, IEnumerable <EnvironmentVariableInfo> customVariables = null) { List <EnvironmentVariableInfo> variables = new List <EnvironmentVariableInfo>(); variables.AddRange(GetCommonEnvironmentVariables()); variables.Add(new EnvironmentVariableInfo("ASPNETCORE_URLS", "http://+:80")); if (customVariables != null) { variables.AddRange(customVariables); } if (imageData.OS.StartsWith(OS.AlpinePrefix)) { variables.Add(new EnvironmentVariableInfo("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "true")); } string imageTag; if (imageData.IsDistroless) { imageTag = DockerHelper.BuildDistrolessHelper(ImageType, imageData, "bash"); } else { imageTag = imageData.GetImage(ImageType, DockerHelper); } EnvironmentVariableInfo.Validate(variables, imageTag, imageData, DockerHelper); }
private void PowerShellScenario_Execute(ProductImageData imageData, string optionalArgs) { if (imageData.Version.Major < 3) { OutputHelper.WriteLine("PowerShell does not exist in pre-3.0 images, skip testing"); return; } // Disable this test for Arm-based Alpine on 6.0 until PowerShell has support (https://github.com/PowerShell/PowerShell/issues/14667, https://github.com/PowerShell/PowerShell/issues/12937) if (imageData.Version.Major == 6 && imageData.OS.Contains("alpine") && imageData.IsArm) { OutputHelper.WriteLine("PowerShell does not have Alpine arm images, skip testing"); return; } // A basic test which executes an arbitrary command to validate PS is functional string output = DockerHelper.Run( image: imageData.GetImage(DotNetImageType.SDK, DockerHelper), name: imageData.GetIdentifier($"pwsh"), optionalRunArgs: optionalArgs, command: $"pwsh -c (Get-Childitem env:DOTNET_RUNNING_IN_CONTAINER).Value" ); Assert.Equal(output, bool.TrueString, ignoreCase: true); }
internal static string[] GetExpectedRpmPackagesInstalled(ProductImageData imageData) => new string[] { "dotnet-host", $"dotnet-hostfxr-{imageData.VersionString}", $"dotnet-runtime-{imageData.VersionString}", };
private string GetSdkUrl(ProductImageData imageData) { string sdkBuildVersion = Config.GetBuildVersion(ImageType, imageData.VersionString); string sdkFileVersionLabel = sdkBuildVersion; string osType = DockerHelper.IsLinuxContainerModeEnabled ? "linux" : "win"; if (imageData.SdkOS.StartsWith(OS.AlpinePrefix)) { osType += "-musl"; } string architecture = imageData.Arch switch { Arch.Amd64 => "x64", Arch.Arm => "arm", Arch.Arm64 => "arm64", _ => throw new InvalidOperationException($"Unexpected architecture type: '{imageData.Arch}'"), }; string fileType = DockerHelper.IsLinuxContainerModeEnabled ? "tar.gz" : "zip"; string baseUrl = "https://dotnetcli.azureedge.net/dotnet"; if (imageData.Version.Major >= 7) { baseUrl = Config.GetBaseUrl(imageData.VersionString); } return($"{baseUrl}/Sdk/{sdkBuildVersion}/dotnet-sdk-{sdkFileVersionLabel}-{osType}-{architecture}.{fileType}"); }
public void VerifyEnvironmentVariables(ProductImageData imageData) { List <EnvironmentVariableInfo> variables = new List <EnvironmentVariableInfo>(); variables.AddRange(GetCommonEnvironmentVariables()); string aspnetUrlsValue = imageData.Version.Major < 3 ? "http://+:80" : string.Empty; variables.Add(new EnvironmentVariableInfo("ASPNETCORE_URLS", aspnetUrlsValue)); variables.Add(new EnvironmentVariableInfo("DOTNET_USE_POLLING_FILE_WATCHER", "true")); variables.Add(new EnvironmentVariableInfo("NUGET_XMLDOC_MODE", "skip")); if (imageData.Version.Major >= 3 && (DockerHelper.IsLinuxContainerModeEnabled || imageData.Version >= new Version("3.1"))) { variables.Add(new EnvironmentVariableInfo("POWERSHELL_DISTRIBUTION_CHANNEL", allowAnyValue: true)); } if (imageData.SdkOS.StartsWith(Tests.OS.AlpinePrefix)) { variables.Add(new EnvironmentVariableInfo("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "false")); variables.Add(new EnvironmentVariableInfo("LC_ALL", "en_US.UTF-8")); variables.Add(new EnvironmentVariableInfo("LANG", "en_US.UTF-8")); } EnvironmentVariableInfo.Validate(variables, DotNetImageType.SDK, imageData, DockerHelper); }
protected void VerifyCommonInsecureFiles(ProductImageData imageData) { if (imageData.Version < new Version("3.1") || (imageData.OS.Contains("alpine") && imageData.IsArm)) { return; } string worldWritableDirectoriesWithoutStickyBitCmd = @"find / -xdev -type d \( -perm -0002 -a ! -perm -1000 \)"; string worldWritableFilesCmd = "find / -xdev -type f -perm -o+w"; string noUserOrGroupFilesCmd; if (imageData.OS.Contains("alpine")) { // BusyBox in Alpine doesn't support the more convenient -nouser and -nogroup options for the find command noUserOrGroupFilesCmd = @"find / -xdev -exec stat -c %U-%n {} \+ | { grep ^UNKNOWN || true; }"; } else { noUserOrGroupFilesCmd = @"find / -xdev \( -nouser -o -nogroup \)"; } string command = $"/bin/sh -c \"{worldWritableDirectoriesWithoutStickyBitCmd} && {worldWritableFilesCmd} && {noUserOrGroupFilesCmd}\""; string output = DockerHelper.Run( image: imageData.GetImage(ImageType, DockerHelper), name: imageData.GetIdentifier($"InsecureFiles-{ImageType}"), command: command ); Assert.Empty(output); }
public void VerifyPackageCache(ProductImageData imageData) { string verifyCacheCommand = null; if (imageData.Version.Major == 2) { if (DockerHelper.IsLinuxContainerModeEnabled) { verifyCacheCommand = "test -d /usr/share/dotnet/sdk/NuGetFallbackFolder"; } else { verifyCacheCommand = "CMD /S /C PUSHD \"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder\""; } } else { OutputHelper.WriteLine(".NET Core SDK images >= 3.0 don't include a package cache."); } if (verifyCacheCommand != null) { // Simple check to verify the NuGet package cache was created DockerHelper.Run( image: imageData.GetImage(DotNetImageType.SDK, DockerHelper), command: verifyCacheCommand, name: imageData.GetIdentifier("PackageCache")); } }
public static void Validate( IEnumerable <EnvironmentVariableInfo> variables, DotNetImageType imageType, ProductImageData imageData, DockerHelper dockerHelper) { const char delimiter = '|'; IEnumerable <string> echoParts; string invokeCommand; char delimiterEscape; if (DockerHelper.IsLinuxContainerModeEnabled) { echoParts = variables.Select(envVar => $"${envVar.Name}"); invokeCommand = $"/bin/sh -c"; delimiterEscape = '\\'; } else { echoParts = variables.Select(envVar => $"%{envVar.Name}%"); invokeCommand = $"CMD /S /C"; delimiterEscape = '^'; } string combinedValues = dockerHelper.Run( image: imageData.GetImage(imageType, dockerHelper), name: imageData.GetIdentifier($"env"), command: $"{invokeCommand} \"echo {string.Join($"{delimiterEscape}{delimiter}", echoParts)}\""); string[] values = combinedValues.Split(delimiter); Assert.Equal(variables.Count(), values.Count()); for (int i = 0; i < values.Count(); i++) { EnvironmentVariableInfo variable = variables.ElementAt(i); string actualValue; // Process unset variables in Windows if (!DockerHelper.IsLinuxContainerModeEnabled && string.Equals(values[i], $"%{variable.Name}%", StringComparison.Ordinal)) { actualValue = string.Empty; } else { actualValue = values[i]; } if (variable.AllowAnyValue) { Assert.NotEmpty(actualValue); } else { Assert.Equal(variable.ExpectedValue, actualValue); } } }
public async Task VerifyDotnetFolderContents(ProductImageData imageData) { // Disable this test for 5.0 due to https://github.com/dotnet/aspnetcore/issues/27670 if (imageData.Version.Major == 5) { return; } if (!(imageData.Version.Major >= 5 || (imageData.Version.Major >= 3 && (imageData.SdkOS.StartsWith(OS.AlpinePrefix) || !DockerHelper.IsLinuxContainerModeEnabled)))) { return; } IEnumerable <SdkContentFileInfo> actualDotnetFiles = GetActualSdkContents(imageData); IEnumerable <SdkContentFileInfo> expectedDotnetFiles = await GetExpectedSdkContentsAsync(imageData); bool hasCountDifference = expectedDotnetFiles.Count() != actualDotnetFiles.Count(); bool hasFileContentDifference = false; // Skip file comparisons for 3.1 until https://github.com/dotnet/sdk/issues/11327 is fixed. if (imageData.Version.Major != 3) { int fileCount = expectedDotnetFiles.Count(); for (int i = 0; i < fileCount; i++) { if (expectedDotnetFiles.ElementAt(i).CompareTo(actualDotnetFiles.ElementAt(i)) != 0) { hasFileContentDifference = true; break; } } } if (hasCountDifference || hasFileContentDifference) { OutputHelper.WriteLine(string.Empty); OutputHelper.WriteLine("EXPECTED FILES:"); foreach (SdkContentFileInfo file in expectedDotnetFiles) { OutputHelper.WriteLine($"Path: {file.Path}"); OutputHelper.WriteLine($"Checksum: {file.Sha512}"); } OutputHelper.WriteLine(string.Empty); OutputHelper.WriteLine("ACTUAL FILES:"); foreach (SdkContentFileInfo file in actualDotnetFiles) { OutputHelper.WriteLine($"Path: {file.Path}"); OutputHelper.WriteLine($"Checksum: {file.Sha512}"); } } Assert.Equal(expectedDotnetFiles.Count(), actualDotnetFiles.Count()); Assert.False(hasFileContentDifference, "There are file content differences. Check the log output."); }
public static EnvironmentVariableInfo GetAspnetVersionVariableInfo(ProductImageData imageData, DockerHelper dockerHelper) { if (imageData.Version.Major >= 5) { string version = imageData.GetProductVersion(DotNetImageType.Aspnet, dockerHelper); return(new EnvironmentVariableInfo("ASPNET_VERSION", version)); } return(null); }
public async Task VerifyDotnetFolderContents(ProductImageData imageData) { // Disable this test for Arm-based Alpine on 6.0 until PowerShell has support (https://github.com/PowerShell/PowerShell/issues/14667, https://github.com/PowerShell/PowerShell/issues/12937) if (imageData.Version.Major == 6 && imageData.OS.Contains("alpine") && imageData.IsArm) { return; } if (!(imageData.Version.Major >= 5 || (imageData.Version.Major >= 3 && (imageData.SdkOS.StartsWith(OS.AlpinePrefix) || !DockerHelper.IsLinuxContainerModeEnabled)))) { return; } IEnumerable <SdkContentFileInfo> actualDotnetFiles = GetActualSdkContents(imageData); IEnumerable <SdkContentFileInfo> expectedDotnetFiles = await GetExpectedSdkContentsAsync(imageData); bool hasCountDifference = expectedDotnetFiles.Count() != actualDotnetFiles.Count(); bool hasFileContentDifference = false; int fileCount = expectedDotnetFiles.Count(); for (int i = 0; i < fileCount; i++) { if (expectedDotnetFiles.ElementAt(i).CompareTo(actualDotnetFiles.ElementAt(i)) != 0) { hasFileContentDifference = true; break; } } if (hasCountDifference || hasFileContentDifference) { OutputHelper.WriteLine(string.Empty); OutputHelper.WriteLine("EXPECTED FILES:"); foreach (SdkContentFileInfo file in expectedDotnetFiles) { OutputHelper.WriteLine($"Path: {file.Path}"); OutputHelper.WriteLine($"Checksum: {file.Sha512}"); } OutputHelper.WriteLine(string.Empty); OutputHelper.WriteLine("ACTUAL FILES:"); foreach (SdkContentFileInfo file in actualDotnetFiles) { OutputHelper.WriteLine($"Path: {file.Path}"); OutputHelper.WriteLine($"Checksum: {file.Sha512}"); } } Assert.Equal(expectedDotnetFiles.Count(), actualDotnetFiles.Count()); Assert.False(hasFileContentDifference, "There are file content differences. Check the log output."); }
public void VerifyEnvironmentVariables(ProductImageData imageData) { List <EnvironmentVariableInfo> variables = new List <EnvironmentVariableInfo>(); if (imageData.Version.Major >= 5 || (imageData.Version.Major == 2 && DockerHelper.IsLinuxContainerModeEnabled)) { variables.Add(GetRuntimeVersionVariableInfo(imageData, DockerHelper)); } base.VerifyCommonEnvironmentVariables(imageData, variables); }
public async Task VerifyAppScenario(ProductImageData imageData) { // Skip test for .NET 5 on Arm32 Alpine 3.13 due to https://github.com/dotnet/runtime/issues/47423 if (imageData.Version.Major == 5 && imageData.OS == "alpine3.13" && imageData.Arch == Arch.Arm) { return; } ImageScenarioVerifier verifier = new ImageScenarioVerifier(imageData, DockerHelper, OutputHelper, isWeb: true); await verifier.Execute(); }
public void VerifyEnvironmentVariables(ProductImageData imageData) { List <EnvironmentVariableInfo> variables = new List <EnvironmentVariableInfo>(); if (imageData.Version.Major >= 5) { variables.Add(GetRuntimeVersionVariableInfo(imageData, DockerHelper)); } base.VerifyCommonEnvironmentVariables(imageData, variables); }
public void VerifyPackageInstallation(ProductImageData imageData) { if (!imageData.OS.Contains("cbl-mariner") || imageData.IsDistroless) { return; } VerifyExpectedInstalledRpmPackages( imageData, GetExpectedRpmPackagesInstalled(imageData)); }
public ImageScenarioVerifier( ProductImageData imageData, DockerHelper dockerHelper, ITestOutputHelper outputHelper, bool isWeb = false) { _dockerHelper = dockerHelper; _imageData = imageData; _isWeb = isWeb; _outputHelper = outputHelper; }
public void VerifyPackageInstallation(ProductImageData imageData) { if (!imageData.OS.Contains("cbl-mariner")) { return; } VerifyExpectedInstalledRpmPackages( imageData, GetExpectedRpmPackagesInstalled(imageData) .Concat(RuntimeDepsImageTests.GetExpectedRpmPackagesInstalled(imageData))); }
public void VerifyPowerShellScenario_NonDefaultUser(ProductImageData imageData) { string optRunArgs = "-u 12345:12345"; // Linux containers test as non-root user if (!DockerHelper.IsLinuxContainerModeEnabled) { // windows containers test as Admin, default execution is as ContainerUser optRunArgs = "-u ContainerAdministrator "; } PowerShellScenario_Execute(imageData, optRunArgs); }
public async Task VerifyAppScenario(ProductImageData imageData) { if (imageData.IsDistroless) { OutputHelper.WriteLine( "Skipping test for distroless due to https://github.com/dotnet/dotnet-docker/issues/3448. Re-enable when fixed."); return; } ImageScenarioVerifier verifier = new ImageScenarioVerifier(imageData, DockerHelper, OutputHelper, isWeb: true); await verifier.Execute(); }
private IEnumerable <string> GetInstalledRpmPackages(ProductImageData imageData) { // Get list of installed RPM packages string command = $"bash -c \"rpm -qa | sort\""; string installedPackages = DockerHelper.Run( image: imageData.GetImage(ImageType, DockerHelper), command: command, name: imageData.GetIdentifier("PackageInstallation")); return(installedPackages.Split(Environment.NewLine)); }
public void VerifyPowerShellScenario_NonDefaultUser(ProductImageData imageData) { string optRunArgs = "-u 12345:12345"; // Linux containers test as non-root user if (imageData.OS.Contains("nanoserver", StringComparison.OrdinalIgnoreCase)) { // windows containers test as Admin, default execution is as ContainerUser optRunArgs = "-u ContainerAdministrator "; } PowerShellScenario_Execute(imageData, optRunArgs); }
public void VerifyEnvironmentVariables(ProductImageData imageData) { List <EnvironmentVariableInfo> variables = new() { new EnvironmentVariableInfo("ASPNETCORE_URLS", string.Empty), new EnvironmentVariableInfo("DOTNET_GENERATE_ASPNET_CERTIFICATE", "false"), new EnvironmentVariableInfo("DOTNET_USE_POLLING_FILE_WATCHER", "true"), new EnvironmentVariableInfo("NUGET_XMLDOC_MODE", "skip") }; variables.AddRange(GetCommonEnvironmentVariables()); if (imageData.Version.Major >= 3) { variables.Add(new EnvironmentVariableInfo("POWERSHELL_DISTRIBUTION_CHANNEL", allowAnyValue: true)); } if (imageData.Version.Major >= 5) { string version = imageData.GetProductVersion(ImageType, DockerHelper); variables.Add(new EnvironmentVariableInfo("DOTNET_SDK_VERSION", version)); } if (imageData.Version.Major >= 5) { variables.Add(AspnetImageTests.GetAspnetVersionVariableInfo(imageData, DockerHelper)); variables.Add(RuntimeImageTests.GetRuntimeVersionVariableInfo(imageData, DockerHelper)); } if (imageData.Version.Major >= 6) { variables.Add(new EnvironmentVariableInfo("DOTNET_NOLOGO", "true")); } if (imageData.Version.Major == 6) { variables.Add(new EnvironmentVariableInfo("Logging__Console__FormatterName", string.Empty)); } if (imageData.SdkOS.StartsWith(OS.AlpinePrefix)) { variables.Add(new EnvironmentVariableInfo("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "false")); if (imageData.Version.Major < 5) { variables.Add(new EnvironmentVariableInfo("LC_ALL", "en_US.UTF-8")); variables.Add(new EnvironmentVariableInfo("LANG", "en_US.UTF-8")); } } EnvironmentVariableInfo.Validate(variables, imageData.GetImage(DotNetImageType.SDK, DockerHelper), imageData, DockerHelper); }
protected void VerifyCommonEnvironmentVariables(ProductImageData imageData) { List <EnvironmentVariableInfo> variables = new List <EnvironmentVariableInfo>(); variables.AddRange(GetCommonEnvironmentVariables()); variables.Add(new EnvironmentVariableInfo("ASPNETCORE_URLS", "http://+:80")); if (imageData.OS.StartsWith(OS.AlpinePrefix)) { variables.Add(new EnvironmentVariableInfo("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "true")); } EnvironmentVariableInfo.Validate(variables, ImageType, imageData, DockerHelper); }
public void VerifyEnvironmentVariables(ProductImageData imageData) { List <EnvironmentVariableInfo> variables = new List <EnvironmentVariableInfo>(); EnvironmentVariableInfo aspnetVersionVariableInfo = GetAspnetVersionVariableInfo(imageData, DockerHelper); if (aspnetVersionVariableInfo != null) { variables.Add(aspnetVersionVariableInfo); } if (imageData.Version.Major >= 5) { variables.Add(RuntimeImageTests.GetRuntimeVersionVariableInfo(imageData, DockerHelper)); } base.VerifyCommonEnvironmentVariables(imageData, variables); }
private void PowerShellScenario_Execute(ProductImageData imageData, string optionalArgs) { if (imageData.Version.Major < 3) { OutputHelper.WriteLine("PowerShell does not exist in pre-3.0 images, skip testing"); return; } // A basic test which executes an arbitrary command to validate PS is functional string output = DockerHelper.Run( image: imageData.GetImage(DotNetImageType.SDK, DockerHelper), name: imageData.GetIdentifier($"pwsh"), optionalRunArgs: optionalArgs, command: $"pwsh -c (Get-Childitem env:DOTNET_RUNNING_IN_CONTAINER).Value" ); Assert.Equal(output, bool.TrueString, ignoreCase: true); }
public void VerifyDistrolessRunsAsNonRootUser(ProductImageData imageData) { if (!imageData.IsDistroless) { return; } string command = $"bash -c \"echo $EUID\""; string imageTag = DockerHelper.BuildDistrolessHelper(ImageType, imageData, "bash"); string userId = DockerHelper.Run( image: imageTag, command: command, name: imageData.GetIdentifier("NonRootUser")); Assert.NotEqual("0", userId); }
public void VerifyPackageInstallation(ProductImageData imageData) { if (!imageData.OS.Contains("cbl-mariner")) { return; } VerifyExpectedInstalledRpmPackages( imageData, new string[] { $"dotnet-sdk-{imageData.VersionString}", $"dotnet-targeting-pack-{imageData.VersionString}", $"aspnetcore-targeting-pack-{imageData.VersionString}", $"dotnet-apphost-pack-{imageData.VersionString}", $"netstandard-targeting-pack-2.1" } .Concat(AspnetImageTests.GetExpectedRpmPackagesInstalled(imageData))); }
protected void VerifyExpectedInstalledRpmPackages( ProductImageData imageData, IEnumerable <string> expectedPackages) { foreach (string expectedPackage in expectedPackages) { // Example package name: dotnet-runtime-6.0-6.0.0-0.1.preview.5.21270.12.x86_64 string prefix; if (expectedPackage.EndsWith(imageData.VersionString)) { prefix = $"{expectedPackage}-{imageData.VersionString}."; } else { prefix = expectedPackage; } bool installed = GetInstalledRpmPackages(imageData).Any(pkg => pkg.StartsWith(prefix)); Assert.True(installed, $"Package '{expectedPackage}' is not installed."); } }
public void VerifyShellNotInstalledForDistroless(ProductImageData imageData) { if (!imageData.IsDistroless) { OutputHelper.WriteLine("Skipping test for non-distroless platform."); return; } string imageTag = imageData.GetImage(ImageType, DockerHelper); // Attempting to execute the container's shell should result in an exception. // There should be no shell installed in distroless containers. InvalidOperationException ex = Assert.Throws <InvalidOperationException>(() => DockerHelper.Run( image: imageTag, name: imageData.GetIdentifier($"env"), optionalRunArgs: $"--entrypoint /bin/sh") ); Assert.Contains("Exit code: 127", ex.Message); }
public static EnvironmentVariableInfo GetAspnetVersionVariableInfo(ProductImageData imageData, DockerHelper dockerHelper) { string versionEnvName = null; if (imageData.Version.Major == 2 && DockerHelper.IsLinuxContainerModeEnabled) { versionEnvName = "ASPNETCORE_VERSION"; } else if (imageData.Version.Major >= 5) { versionEnvName = "ASPNET_VERSION"; } if (versionEnvName != null) { string version = imageData.GetProductVersion(DotNetImageType.Aspnet, dockerHelper); return(new EnvironmentVariableInfo(versionEnvName, version)); } return(null); }