Skip to content

Commit

Permalink
Stop relying on ReleaseId/DisplayVersion registry keys
Browse files Browse the repository at this point in the history
Instead, we now use Windows build numbers to identify host system.

This is a preparation step to add support for Windows Server 2022 LTSC,
 whose DisplayVersion (21H2) collides with Windows 10 21H2 and Windows 11 21H2.

This commit also removes EOLed Windows 1903 and 1909 in order to reduce lookup tables that we have to hardcode.
  • Loading branch information
slonopotamus committed Aug 21, 2021
1 parent b52b9cb commit 158975d
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 68 deletions.
10 changes: 1 addition & 9 deletions ue4docker/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,8 @@ def build():
logger.info('Detected max image size: {:.0f}GB'.format(DockerUtils.maxsize()), False)
logger.info('Visual Studio: {}'.format(config.visualStudio), False)

# Verify that the specified base image tag is not a release that has reached End Of Life (EOL)
if not config.ignoreEOL and WindowsUtils.isEndOfLifeWindowsVersion(config.basetag):
logger.error('Error: detected EOL base OS image tag: {}'.format(config.basetag), False)
logger.error('This version of Windows has reached End Of Life (EOL), which means', False)
logger.error('Microsoft no longer supports or maintains container base images for it.', False)
logger.error('You will need to use a base image tag for a supported version of Windows.', False)
sys.exit(1)

# Verify that the host OS is not a release that is blacklisted due to critical bugs
if config.ignoreBlacklist == False and WindowsUtils.isBlacklistedWindowsVersion() == True:
if config.ignoreBlacklist == False and WindowsUtils.isBlacklistedWindowsHost() == True:
logger.error('Error: detected blacklisted host OS version: {}'.format(WindowsUtils.systemString()), False)
logger.error('', False)
logger.error('This version of Windows contains one or more critical bugs that', False)
Expand Down
6 changes: 3 additions & 3 deletions ue4docker/diagnostics/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ def _generateWindowsBuildArgs(self, logger, basetagOverride=None, isolationOverr

# Determine the appropriate container image base tag for the host system release unless the user specified a base tag
buildArgs = []
defaultBaseTag = WindowsUtils.getReleaseBaseTag(WindowsUtils.getWindowsRelease())
baseTag = basetagOverride if basetagOverride is not None else defaultBaseTag
hostBaseTag = WindowsUtils.getHostBaseTag()
baseTag = basetagOverride if basetagOverride is not None else hostBaseTag
buildArgs = ['--build-arg', 'BASETAG={}'.format(baseTag)]

# Use the default isolation mode unless requested otherwise
Expand All @@ -72,7 +72,7 @@ def _generateWindowsBuildArgs(self, logger, basetagOverride=None, isolationOverr

# If the user specified process isolation mode and a different base tag to the host system then warn them
prefix = self.getPrefix()
if isolation == 'process' and baseTag != defaultBaseTag:
if isolation == 'process' and baseTag != hostBaseTag:
logger.info('[{}] Warning: attempting to use different Windows container/host versions'.format(prefix), False)
logger.info('[{}] when running in process isolation mode, this will usually break!'.format(prefix), False)

Expand Down
7 changes: 2 additions & 5 deletions ue4docker/infrastructure/BuildConfiguration.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ def addArguments(parser):
parser.add_argument('--combine', action='store_true', help='Combine generated Dockerfiles into a single multi-stage build Dockerfile')
parser.add_argument('--monitor', action='store_true', help='Monitor resource usage during builds (useful for debugging)')
parser.add_argument('-interval', type=float, default=20.0, help='Sampling interval in seconds when resource monitoring has been enabled using --monitor (default is 20 seconds)')
parser.add_argument('--ignore-eol', action='store_true', help='Run builds even on EOL versions of Windows (advanced use only)')
parser.add_argument('--ignore-blacklist', action='store_true', help='Run builds even on blacklisted versions of Windows (advanced use only)')
parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose output during builds (useful for debugging)')

Expand Down Expand Up @@ -166,7 +165,6 @@ def __init__(self, parser, argv):
self.excludedComponents = set(self.args.exclude)
self.baseImage = None
self.prereqsTag = None
self.ignoreEOL = self.args.ignore_eol
self.ignoreBlacklist = self.args.ignore_blacklist
self.verbose = self.args.verbose
self.layoutDir = self.args.layout
Expand Down Expand Up @@ -250,8 +248,7 @@ def _generateWindowsConfig(self):
self.opts['buildgraph_args'] = self.opts.get('buildgraph_args', '') + f' -set:VS{self.visualStudio}=true'

# Determine base tag for the Windows release of the host system
self.hostRelease = WindowsUtils.getWindowsRelease()
self.hostBasetag = WindowsUtils.getReleaseBaseTag(self.hostRelease)
self.hostBasetag = WindowsUtils.getHostBaseTag()

# Store the tag for the base Windows Server Core image
self.basetag = self.args.basetag if self.args.basetag is not None else self.hostBasetag
Expand All @@ -273,7 +270,7 @@ def _generateWindowsConfig(self):
else:

# If we are able to use process isolation mode then use it, otherwise fallback to the Docker daemon's default isolation mode
differentKernels = WindowsUtils.isInsiderPreview() or self.basetag != self.hostBasetag
differentKernels = self.basetag != self.hostBasetag
dockerSupportsProcess = parse_version(DockerUtils.version()['Version']) >= parse_version('18.09.0')
if not differentKernels and dockerSupportsProcess:
self.isolation = 'process'
Expand Down
73 changes: 22 additions & 51 deletions ue4docker/infrastructure/WindowsUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ class WindowsUtils(object):
# The oldest Windows build we support
_minimumRequiredBuild = 17763

# The latest Windows build version we recognise as a non-Insider build
_latestReleaseBuild = 19042
# This lookup table is based on the list of valid tags from <https://hub.docker.com/r/microsoft/windowsservercore/>
# and list of build-to-release mapping from https://docs.microsoft.com/en-us/windows/release-health/release-information
_tagsByBuildNumber = {
17763: 'ltsc2019',
19041: '2004',
19042: '20H2',
}

# The list of Windows Server Core base image tags that we recognise, in ascending version number order
_validTags = ['ltsc2019', '1903', '1909', '2004', '20H2']
_validTags = list(_tagsByBuildNumber.values())

# The list of Windows Server and Windows 10 host OS releases that are blacklisted due to critical bugs
# (See: <https://unrealcontainers.com/docs/concepts/windows-containers>)
_blacklistedReleases = ['1903', '1909']

# The list of Windows Server Core container image releases that are unsupported due to having reached EOL
_eolReleases = ['1903', '1909']
_blacklistedBuilds = [18362, 18363]

@staticmethod
def _getVersionRegKey(subkey : str) -> str:
Expand Down Expand Up @@ -54,26 +56,12 @@ def systemString() -> str:
'''
Generates a verbose human-readable version string for the Windows host system
'''
return '{} Version {} (Build {}.{})'.format(
return '{} (Build {}.{})'.format(
WindowsUtils._getVersionRegKey('ProductName'),
WindowsUtils.getWindowsRelease(),
WindowsUtils.getWindowsBuild(),
WindowsUtils._getVersionRegKey('UBR')
)

@staticmethod
def getWindowsRelease() -> str:
'''
Determines the Windows 10 / Windows Server release (1607, 1709, 1803, etc.) of the Windows host system
'''
try:
# Starting with Windows 20H2 (also known as 2009), Microsoft stopped updating ReleaseId
# and instead updates DisplayVersion
return WindowsUtils._getVersionRegKey('DisplayVersion')
except FileNotFoundError:
# Fallback to ReleaseId for pre-20H2 releases that didn't have DisplayVersion
return WindowsUtils._getVersionRegKey('ReleaseId')

@staticmethod
def getWindowsBuild() -> int:
'''
Expand All @@ -82,23 +70,14 @@ def getWindowsBuild() -> int:
return sys.getwindowsversion().build

@staticmethod
def isBlacklistedWindowsVersion(release=None):
def isBlacklistedWindowsHost() -> bool:
'''
Determines if the specified Windows release is one with bugs that make it unsuitable for use
Determines if host Windows version is one with bugs that make it unsuitable for use
(defaults to checking the host OS release if one is not specified)
'''
dockerVersion = parse_version(DockerUtils.version()['Version'])
release = WindowsUtils.getWindowsRelease() if release is None else release
return release in WindowsUtils._blacklistedReleases and dockerVersion < parse_version('19.03.6')

@staticmethod
def isEndOfLifeWindowsVersion(release=None):
'''
Determines if the specified Windows release is one that has reached End Of Life (EOL)
(defaults to checking the host OS release if one is not specified)
'''
release = WindowsUtils.getWindowsRelease() if release is None else release
return release in WindowsUtils._eolReleases
build = WindowsUtils.getWindowsBuild()
return build in WindowsUtils._blacklistedBuilds and dockerVersion < parse_version('19.03.6')

@staticmethod
def isWindowsServer() -> bool:
Expand All @@ -109,26 +88,18 @@ def isWindowsServer() -> bool:
return 'Windows Server' in WindowsUtils._getVersionRegKey('ProductName')

@staticmethod
def isInsiderPreview() -> bool:
'''
Determines if the Windows host system is a Windows Insider preview build
'''
return WindowsUtils.getWindowsBuild() > WindowsUtils._latestReleaseBuild

@staticmethod
def getReleaseBaseTag(release: str) -> str:
def getHostBaseTag() -> str:
'''
Retrieves the tag for the Windows Server Core base image matching the specified Windows 10 / Windows Server release
Retrieves the tag for the Windows Server Core base image matching the host Windows system
'''

# For Windows Insider preview builds, build the latest release tag
if WindowsUtils.isInsiderPreview():
return WindowsUtils._validTags[-1]
hostBuild = WindowsUtils.getWindowsBuild()

# This lookup table is based on the list of valid tags from <https://hub.docker.com/r/microsoft/windowsservercore/>
return {
'1809': 'ltsc2019',
}.get(release, release)
# Note that we fallback to hostBuild to avoid pretending that we understood what the host version is
# when we're running on an unsupported Windows build (like, EOLed or Insider Preview).
# This fallback will later be rejected when ue4-docker checks whether it is valid.
# Users of unsupported Windows builds can still use ue4-docker by passing -basetag option with a known value.
return WindowsUtils._tagsByBuildNumber.get(hostBuild, str(hostBuild))

@staticmethod
def getDllSrcImage(basetag: str) -> str:
Expand Down

0 comments on commit 158975d

Please sign in to comment.