diff --git a/platform_windows_compat.go b/platform_windows_compat.go index ef21a29..4aa162a 100644 --- a/platform_windows_compat.go +++ b/platform_windows_compat.go @@ -87,19 +87,26 @@ func checkWindowsHostAndContainerCompat(host, ctr windowsOSVersion) bool { return host.Build == ctr.Build } - // Find the latest LTSC version that is earlier than the host version. - // This is the earliest version of container that the host can run. + // Find the floor of the compatible container range. Per the Windows stable + // ABI policy, every host from LTSC N up to (but not including) LTSC N+1 can + // run containers from LTSC N-1 up to the host build. // - // If the host version is an LTSC, then it supports compatibility with - // everything from the previous LTSC up to itself, so we want supportedLTSCRelease - // to be the previous entry. + // So we find the largest LTSC <= host.Build, then step one entry back to + // get the floor. If host.Build is past the latest LTSC in the list + // (e.g. a 26200 host, which is in the WS2025 generation), the floor is + // still the previous LTSC (20348), not the latest LTSC itself. // - // If no match is found, then we know that the host is LTSC2022 exactly, - // since we already checked that it's not less than LTSC2022. + // If host is the very first LTSC (or no entry matches, which is impossible + // here since we already checked host.Build >= ltsc2022), use that LTSC as + // the floor. var supportedLTSCRelease uint16 = ltsc2022 for i := len(compatLTSCReleases) - 1; i >= 0; i-- { - if host.Build > compatLTSCReleases[i] { - supportedLTSCRelease = compatLTSCReleases[i] + if host.Build >= compatLTSCReleases[i] { + if i == 0 { + supportedLTSCRelease = compatLTSCReleases[i] + } else { + supportedLTSCRelease = compatLTSCReleases[i-1] + } break } } diff --git a/platform_windows_compat_test.go b/platform_windows_compat_test.go index 1a05075..94b2e17 100644 --- a/platform_windows_compat_test.go +++ b/platform_windows_compat_test.go @@ -91,6 +91,28 @@ func Test_PlatformCompat(t *testing.T) { ctrOS: v22H2Win11, shouldRun: true, }, + // A WS2025-generation host with a build past the latest LTSC (e.g. + // 26200) must still accept WS2022 containers per the stable ABI policy. + { + hostOS: 26200, + ctrOS: ltsc2022, + shouldRun: true, + }, + { + hostOS: 26200, + ctrOS: ltsc2025, + shouldRun: true, + }, + { + hostOS: 26200, + ctrOS: v22H2Win11, + shouldRun: true, + }, + { + hostOS: 26200, + ctrOS: ltsc2019, + shouldRun: false, + }, } { t.Run(fmt.Sprintf("Host_%d_Ctr_%d", tc.hostOS, tc.ctrOS), func(t *testing.T) { hostOSVersion := windowsOSVersion{ @@ -143,6 +165,13 @@ func Test_PlatformOrder(t *testing.T) { OSFeatures: nil, Variant: "", } + ws2025PatchedPlatform := specs.Platform{ + Architecture: "amd64", + OS: "windows", + OSVersion: "10.0.26200.0", + OSFeatures: nil, + Variant: "", + } tt := []struct { name string @@ -168,6 +197,12 @@ func Test_PlatformOrder(t *testing.T) { platforms: []specs.Platform{linuxPlatform, ws2022Platform, ws2025Rev3000Platform}, wantPlatform: ws2025Rev3000Platform, }, + { + name: "Windows Server 2025 host past latest LTSC should still accept 2022", + hostPlatform: ws2025PatchedPlatform, + platforms: []specs.Platform{linuxPlatform, ws2022Platform}, + wantPlatform: ws2022Platform, + }, } for _, tc := range tt {