Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions tests/Microsoft.DotNet.Docker.Tests/ImageData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public abstract record ImageData

public Arch Arch { get; set; }
public bool IsArm => Arch == Arch.Arm || Arch == Arch.Arm64;
public string OS { get; set; }
public bool IsDistroless => OS.Contains(Tests.OS.DistrolessSuffix) || OS.Contains(Tests.OS.ChiseledSuffix);
public OSInfo OS { get; set; }
public bool IsDistroless => OS.IsDistroless;
public virtual int DefaultPort => 8080;
public virtual int? NonRootUID => IsWindows ? null : 1654;

Expand Down Expand Up @@ -66,7 +66,7 @@ public string Platform
_ => throw new NotImplementedException()
};

public bool IsWindows => OS.StartsWith(Tests.OS.NanoServer) || OS.StartsWith(Tests.OS.ServerCore);
public bool IsWindows => OS.IsWindows;

public string Rid
{
Expand All @@ -87,7 +87,7 @@ public string Rid
Arch.Amd64 => "x64",
_ => throw new NotImplementedException()
};
string modifier = OS.StartsWith(Tests.OS.Alpine) ? "musl-" : "";
string modifier = OS.Family == OSFamily.Alpine ? "musl-" : "";
rid = $"linux-{modifier}{arch}";
}

Expand Down
93 changes: 44 additions & 49 deletions tests/Microsoft.DotNet.Docker.Tests/OS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,49 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.DotNet.Docker.Tests
#nullable enable

namespace Microsoft.DotNet.Docker.Tests;

internal static class OS
{
public static class OS
{
// Alpine
public const string Alpine = "alpine";
public const string Alpine321 = $"{Alpine}3.21";
public const string Alpine322 = $"{Alpine}3.22";
public const string Alpine323 = $"{Alpine}3.23";

// AzureLinux
public const string AzureLinux = "azurelinux";
public const string AzureLinuxDistroless = $"{AzureLinux}-distroless";
public const string AzureLinux30 = $"{AzureLinux}3.0";
public const string AzureLinux30Distroless = $"{AzureLinux30}-distroless";

// Debian
public const string Bookworm = "bookworm";
public const string BookwormSlim = $"{Bookworm}{SlimSuffix}";

// Mariner
public const string Mariner = "cbl-mariner";
public const string MarinerDistroless = $"{Mariner}-distroless";
public const string Mariner20 = $"{Mariner}2.0";
public const string Mariner20Distroless = $"{Mariner20}-distroless";

// Ubuntu
public const string Jammy = "jammy";
public const string JammyChiseled = $"{Jammy}{ChiseledSuffix}";
public const string Noble = "noble";
public const string NobleChiseled = $"{Noble}{ChiseledSuffix}";
public const string Resolute = "resolute";
public const string ResoluteChiseled = $"{Resolute}{ChiseledSuffix}";
public const string UbuntuChiseled = $"ubuntu{ChiseledSuffix}";

// Windows
public const string NanoServer = "nanoserver";
public const string NanoServer1809 = $"{NanoServer}-1809";
public const string NanoServerLtsc2022 = $"{NanoServer}-ltsc2022";
public const string NanoServerLtsc2025 = $"{NanoServer}-ltsc2025";
public const string ServerCore = "windowsservercore";
public const string ServerCoreLtsc2019 = $"{ServerCore}-ltsc2019";
public const string ServerCoreLtsc2022 = $"{ServerCore}-ltsc2022";
public const string ServerCoreLtsc2025 = $"{ServerCore}-ltsc2025";

// Helpers
public const string DistrolessSuffix = "-distroless";
public const string ChiseledSuffix = "-chiseled";
public const string SlimSuffix = "-slim";
}
// String constants for special tagging cases (appliance images)
public const string Alpine = "alpine";
public const string AzureLinuxDistroless = "azurelinux-distroless";
public const string MarinerDistroless = "cbl-mariner-distroless";
public const string UbuntuChiseled = "ubuntu-chiseled";

// Alpine
public static OSInfo AlpineFloating { get; } = new(OSType.Linux, OSFamily.Alpine, "");
public static OSInfo Alpine322 { get; } = AlpineFloating with { Version = "3.22" };
public static OSInfo Alpine323 { get; } = AlpineFloating with { Version = "3.23" };

// Azure Linux
public static OSInfo AzureLinux30 { get; } = new(OSType.Linux, OSFamily.AzureLinux, "3.0");
public static OSInfo AzureLinux30Distroless { get; } = AzureLinux30 with { IsDistroless = true };

// Debian
public static OSInfo BookwormSlim { get; } = new(OSType.Linux, OSFamily.Debian, "12");

// Mariner (CBL-Mariner)
public static OSInfo Mariner20 { get; } = new(OSType.Linux, OSFamily.Mariner, "2.0");
public static OSInfo Mariner20Distroless { get; } = Mariner20 with { IsDistroless = true };

// Ubuntu
public static OSInfo Jammy { get; } = new(OSType.Linux, OSFamily.Ubuntu, "22.04");
public static OSInfo JammyChiseled { get; } = Jammy with { IsDistroless = true };
public static OSInfo Noble { get; } = new(OSType.Linux, OSFamily.Ubuntu, "24.04");
public static OSInfo NobleChiseled { get; } = Noble with { IsDistroless = true };
public static OSInfo Resolute { get; } = new(OSType.Linux, OSFamily.Ubuntu, "26.04");
public static OSInfo ResoluteChiseled { get; } = Resolute with { IsDistroless = true };

// Windows - Nano Server
public static OSInfo NanoServer1809 { get; } = new(OSType.Windows, OSFamily.NanoServer, "1809");
public static OSInfo NanoServerLtsc2022 { get; } = NanoServer1809 with { Version = "LTSC 2022" };
public static OSInfo NanoServerLtsc2025 { get; } = NanoServer1809 with { Version = "LTSC 2025" };

// Windows - Server Core
public static OSInfo ServerCoreLtsc2019 { get; } = new(OSType.Windows, OSFamily.WindowsServerCore, "LTSC 2019");
public static OSInfo ServerCoreLtsc2022 { get; } = ServerCoreLtsc2019 with { Version = "LTSC 2022" };
public static OSInfo ServerCoreLtsc2025 { get; } = ServerCoreLtsc2019 with { Version = "LTSC 2025" };
}
134 changes: 134 additions & 0 deletions tests/Microsoft.DotNet.Docker.Tests/OSInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;

#nullable enable

namespace Microsoft.DotNet.Docker.Tests;

/// <summary>
/// Represents the type of operating system.
/// </summary>
public enum OSType
{
Linux,
Windows
}

/// <summary>
/// Represents the family or distribution of an operating system.
/// </summary>
public enum OSFamily
{
Alpine,
AzureLinux,
Debian,
Mariner,
Ubuntu,
NanoServer,
WindowsServerCore
}

/// <summary>
/// Operating system used in .NET container images.
/// </summary>
/// <param name="Type">The type of operating system (Linux or Windows).</param>
/// <param name="Family">The family or distribution of the operating system.</param>
/// <param name="Version">
/// The version of the operating system. Do not use codenames here. Use the
/// version exactly as it appears in the OS's own documentation. Examples:
/// "3.23" for Alpine, "26.04" for Ubuntu, "LTSC 2025" for Windows Server Core.
/// </param>
/// <param name="IsDistroless">Whether the OS is distroless.</param>
public sealed record OSInfo(
OSType Type,
OSFamily Family,
string Version,
bool IsDistroless = false)
{
/// <summary>
/// Gets the tag name for this OS, used in Docker image tags.
/// </summary>
public string TagName
{
get
{
static string GetCodename(OSFamily family, string version) => (family, version) switch
{
(OSFamily.Debian, "12") => "bookworm",
(OSFamily.Ubuntu, "22.04") => "jammy",
(OSFamily.Ubuntu, "24.04") => "noble",
(OSFamily.Ubuntu, "26.04") => "resolute",
_ => throw new InvalidOperationException($"Unknown {family} version: {version}")
};

string baseTag = Family switch
{
OSFamily.Alpine => string.IsNullOrEmpty(Version) ? "alpine" : $"alpine{Version}",
OSFamily.AzureLinux => $"azurelinux{Version}",
OSFamily.Debian => $"{GetCodename(Family, Version)}-slim",
OSFamily.Mariner => $"cbl-mariner{Version}",
OSFamily.Ubuntu => GetCodename(Family, Version),
OSFamily.NanoServer => $"nanoserver-{Version.ToLowerInvariant().Replace(" ", "")}",
OSFamily.WindowsServerCore => $"windowsservercore-{Version.ToLowerInvariant().Replace(" ", "")}",
_ => throw new InvalidOperationException($"Unknown OS family: {Family}")
};

return (Family, IsDistroless) switch
{
(OSFamily.Ubuntu, true) => $"{baseTag}-chiseled",
(_, true) => $"{baseTag}-distroless",
_ => baseTag
};
}
}

/// <summary>
/// Gets the display name for this OS, combining family and version.
/// </summary>
public string DisplayName
{
get
{
string familyName = Family switch
{
OSFamily.Alpine => "Alpine",
OSFamily.AzureLinux => "Azure Linux",
OSFamily.Debian => "Debian",
OSFamily.Mariner => "CBL-Mariner",
OSFamily.Ubuntu => "Ubuntu",
OSFamily.NanoServer => "Nano Server",
OSFamily.WindowsServerCore => "Windows Server Core",
_ => Family.ToString()
};

return string.IsNullOrEmpty(Version) ? familyName : $"{familyName} {Version}";
}
}

/// <summary>
/// Gets a value indicating whether this OS is Windows-based.
/// </summary>
public bool IsWindows => Type == OSType.Windows;

/// <summary>
/// Checks if the TagName contains the specified value.
/// Provides backward compatibility with string-based OS checks.
/// </summary>
public bool Contains(string value) => TagName.Contains(value);

/// <summary>
/// Checks if the TagName starts with the specified value.
/// Provides backward compatibility with string-based OS checks.
/// </summary>
public bool StartsWith(string value) => TagName.StartsWith(value);

/// <summary>
/// Implicit conversion to string for backward compatibility with existing code.
/// </summary>
public static implicit operator string(OSInfo os) => os.TagName;

public override string ToString() => TagName;
}
10 changes: 5 additions & 5 deletions tests/Microsoft.DotNet.Docker.Tests/ProductImageData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,25 @@ namespace Microsoft.DotNet.Docker.Tests
{
public record ProductImageData : ImageData
{
private string _sdkOS;
private OSInfo _sdkOS;
private string _osTag;
private string _osDir;
private ImageVersion? _versionFamily;

public bool HasCustomSdk => _sdkOS != null;
public bool HasCustomSdk => _sdkOS is not null;

public bool GlobalizationInvariantMode => !SupportsGlobalization;

// PowerShell does not support Arm-based Alpine
public bool SupportsPowerShell => !(OS.Contains("alpine") && IsArm);
public bool SupportsPowerShell => !(OS.Family == OSFamily.Alpine && IsArm);

/// <summary>
/// Indicates whether the SDK version of the image supports `dnx` and
/// should have it installed.
/// </summary>
public bool SupportsDnx => VersionFamily != ImageVersion.V8_0 && VersionFamily != ImageVersion.V9_0;

public string SdkOS
public OSInfo SdkOS
{
get => HasCustomSdk ? _sdkOS : OS;
init => _sdkOS = value;
Expand Down Expand Up @@ -70,7 +70,7 @@ private bool SupportsGlobalization
{
get
{
bool isSizeFocusedImage = IsDistroless || OS.Contains(Tests.OS.Alpine);
bool isSizeFocusedImage = IsDistroless || OS.Family == OSFamily.Alpine;
return ImageVariant.HasFlag(DotNetImageVariant.Extra) || !isSizeFocusedImage;
}
}
Expand Down
Loading
Loading