diff options
author | David Bradford <david.bradford@mongodb.com> | 2022-05-17 01:04:31 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-05-17 12:57:34 +0000 |
commit | 5c8894d8e0fb53d556675b3eb7b3b434e2e21448 (patch) | |
tree | 2823c936952e5848d87a3ed2098f07b4c0cf5eaf | |
parent | 1a44e197ee6d2e8ebdec97f8fd817619e84aacaa (diff) | |
download | mongo-5c8894d8e0fb53d556675b3eb7b3b434e2e21448.tar.gz |
SERVER-65937: Support different fcv tags for lts and continuous multiversion tests
(cherry picked from commit 3b4067a108295b079c3d43027caffe2e2c66d7d0)
7 files changed, 252 insertions, 76 deletions
diff --git a/buildscripts/resmokelib/discovery/__init__.py b/buildscripts/resmokelib/discovery/__init__.py index 02c6a206564..617eec6655e 100644 --- a/buildscripts/resmokelib/discovery/__init__.py +++ b/buildscripts/resmokelib/discovery/__init__.py @@ -6,6 +6,7 @@ from pydantic import BaseModel from buildscripts.resmokelib import configure_resmoke from buildscripts.resmokelib import suitesconfig +from buildscripts.resmokelib.multiversion.multiversion_service import MultiversionService, MongoReleases, MongoVersion from buildscripts.resmokelib.plugin import PluginInterface, Subcommand from buildscripts.resmokelib.testing.suite import Suite @@ -85,6 +86,8 @@ class MultiversionConfig(BaseModel): last_versions: List[str] requires_fcv_tag: str + requires_fcv_tag_lts: str + requires_fcv_tag_continuous: str class MultiversionConfigSubcommand(Subcommand): @@ -99,9 +102,16 @@ class MultiversionConfigSubcommand(Subcommand): def determine_multiversion_config() -> MultiversionConfig: """Discover the current multiversion configuration.""" from buildscripts.resmokelib import multiversionconstants + multiversion_service = MultiversionService( + mongo_version=MongoVersion.from_yaml_file(multiversionconstants.MONGO_VERSION_YAML), + mongo_releases=MongoReleases.from_yaml_file(multiversionconstants.RELEASES_YAML), + ) + fcv_constants = multiversion_service.calculate_fcv_constants() return MultiversionConfig( last_versions=multiversionconstants.OLD_VERSIONS, - requires_fcv_tag=multiversionconstants.REQUIRES_FCV_TAG, + requires_fcv_tag=fcv_constants.get_fcv_tag_list(), + requires_fcv_tag_lts=fcv_constants.get_lts_fcv_tag_list(), + requires_fcv_tag_continuous=fcv_constants.get_continuous_fcv_tag_list(), ) diff --git a/buildscripts/resmokelib/multiversion/__init__.py b/buildscripts/resmokelib/multiversion/__init__.py new file mode 100644 index 00000000000..4b7a2bb941b --- /dev/null +++ b/buildscripts/resmokelib/multiversion/__init__.py @@ -0,0 +1 @@ +"""Empty.""" diff --git a/buildscripts/resmokelib/multiversion/multiversion_service.py b/buildscripts/resmokelib/multiversion/multiversion_service.py new file mode 100644 index 00000000000..0f8ff484159 --- /dev/null +++ b/buildscripts/resmokelib/multiversion/multiversion_service.py @@ -0,0 +1,169 @@ +"""A service for working with multiversion testing.""" +from __future__ import annotations + +import re +from bisect import bisect_left, bisect_right +from typing import List, NamedTuple, Optional + +from packaging.version import Version +from pydantic import BaseModel, Field +import yaml + +# These values must match the include paths for artifacts.tgz in evergreen.yml. +MONGO_VERSION_YAML = ".resmoke_mongo_version.yml" +RELEASES_YAML = ".resmoke_mongo_release_values.yml" +VERSION_RE = re.compile(r'^[0-9]+\.[0-9]+') + + +def tag_str(version: Version) -> str: + """Return a tag for the given version.""" + return 'requires_fcv_{}{}'.format(version.major, version.minor) + + +class FcvConstantValues(NamedTuple): + """ + Object to hold the calculated FCV constants. + + * latest: Latest FCV. + * last_continuous: Last continuous FCV. + * last_lts: Last LTS FCV. + * requires_fcv_tag_list: List of FCVs that we need to generate a tag for against LTS versions. + * requires_fcv_tag_list_continuous: List of FCVs that we need to generate a tag for against + continuous versions. + * fcvs_less_than_latest: List of all FCVs that are less than latest, starting from v4.0. + """ + + latest: Version + last_continuous: Version + last_lts: Version + requires_fcv_tag_list: List[Version] + requires_fcv_tag_list_continuous: List[Version] + fcvs_less_than_latest: List[Version] + + def get_fcv_tag_list(self) -> str: + """Get a comma joined string of all the fcv tags.""" + return ",".join([tag_str(tag) for tag in self.requires_fcv_tag_list]) + + def get_lts_fcv_tag_list(self) -> str: + """Get a comma joined string of all the LTS fcv tags.""" + # Note: the LTS tag list is the default used above, so this function is the same as + # `get_fcv_tag_list`. This function was added to make it explicit when we want to use + # the LTS version vs the default. + return ",".join([tag_str(tag) for tag in self.requires_fcv_tag_list]) + + def get_continuous_fcv_tag_list(self) -> str: + """Get a comma joined string of all the continuous fcv tags.""" + return ",".join([tag_str(tag) for tag in self.requires_fcv_tag_list_continuous]) + + def get_latest_tag(self) -> str: + """Get a string version of the latest FCV.""" + return tag_str(self.latest) + + +class MongoVersion(BaseModel): + """ + The mongo version being tested. + + * mongo_version: The mongo version being tested. + """ + + mongo_version: str + + @classmethod + def from_yaml_file(cls, yaml_file: str) -> MongoVersion: + """ + Read the mongo version from the given yaml file. + + :param yaml_file: Path to yaml file. + :return: MongoVersion read from file. + """ + mongo_version_yml_file = open(yaml_file, 'r') + return cls(**yaml.safe_load(mongo_version_yml_file)) + + def get_version(self) -> Version: + """Get the Version representation of the mongo version being tested.""" + version_match = VERSION_RE.match(self.mongo_version) + if version_match is None: + raise ValueError( + f"Could not determine version from mongo version string '{self.mongo_version}'") + return Version(version_match.group(0)) + + +class MongoReleases(BaseModel): + """ + Information about the FCVs and LTS release version since v4.0. + + * feature_compatibility_version: All FCVs starting with 4.0. + * long_term_support_releases: All LTS releases starting with 4.0. + * generate_fcv_lower_bound_override: Extend FCV generation down to the previous value of last + LTS. + """ + + feature_compatibility_versions: List[str] = Field(alias="featureCompatibilityVersions") + long_term_support_releases: List[str] = Field(alias="longTermSupportReleases") + generate_fcv_lower_bound_override: Optional[str] = Field(None, + alias="generateFCVLowerBoundOverride") + + @classmethod + def from_yaml_file(cls, yaml_file: str) -> MongoReleases: + """ + Read the mongo release information from the given yaml file. + + :param yaml_file: Path to yaml file. + :return: MongoReleases read from file. + """ + + mongo_releases_file = open(yaml_file, 'r') + return cls(**yaml.safe_load(mongo_releases_file)) + + def get_fcv_versions(self) -> List[Version]: + """Get the Version representation of all fcv versions.""" + return [Version(fcv) for fcv in self.feature_compatibility_versions] + + def get_lts_versions(self) -> List[Version]: + """Get the Version representation of the lts versions.""" + return [Version(fcv) for fcv in self.long_term_support_releases] + + +class MultiversionService: + """A service for working with multiversion information.""" + + def __init__(self, mongo_version: MongoVersion, mongo_releases: MongoReleases) -> None: + """ + Initialize the service. + + :param mongo_version: Contents of the Mongo Version file. + :param mongo_releases: Contents of the Mongo Releases file. + """ + self.mongo_version = mongo_version + self.mongo_releases = mongo_releases + + def calculate_fcv_constants(self) -> FcvConstantValues: + """Calculate multiversion constants from data files.""" + latest = self.mongo_version.get_version() + fcvs = self.mongo_releases.get_fcv_versions() + lts = self.mongo_releases.get_lts_versions() + lower_bound_override = self.mongo_releases.generate_fcv_lower_bound_override + + # Highest release less than latest. + last_continuous = fcvs[bisect_left(fcvs, latest) - 1] + + # Highest LTS release less than latest. + last_lts = lts[bisect_left(lts, latest) - 1] + + # Normally, this list includes all FCVs greater than last LTS, up to latest. + # However, if we have 'generateFCVLowerBoundOverride' set in releases.yml, we will + # extend the lower bound to also include the previous value of lastLTS. + lts_cutoff = last_lts + if lower_bound_override is not None: + lts_cutoff = Version(lower_bound_override) + requires_fcv_tag_list = fcvs[bisect_right(fcvs, lts_cutoff):bisect_right(fcvs, latest)] + requires_fcv_tag_list_continuous = [latest] + + # All FCVs less than latest. + fcvs_less_than_latest = fcvs[:bisect_left(fcvs, latest)] + + return FcvConstantValues(latest=latest, last_continuous=last_continuous, last_lts=last_lts, + requires_fcv_tag_list=requires_fcv_tag_list, + requires_fcv_tag_list_continuous=requires_fcv_tag_list_continuous, + fcvs_less_than_latest=fcvs_less_than_latest) diff --git a/buildscripts/resmokelib/multiversionconstants.py b/buildscripts/resmokelib/multiversionconstants.py index 0ff3b42b56f..5cf8df73aa0 100644 --- a/buildscripts/resmokelib/multiversionconstants.py +++ b/buildscripts/resmokelib/multiversionconstants.py @@ -1,20 +1,17 @@ """FCV and Server binary version constants used for multiversion testing.""" - -from bisect import bisect_left, bisect_right import os -import re import shutil from subprocess import call, CalledProcessError, check_output, STDOUT, DEVNULL import structlog -import yaml -from packaging.version import Version try: # when running resmoke + from buildscripts.resmokelib.multiversion.multiversion_service import MongoReleases, MongoVersion, MultiversionService from buildscripts.resmokelib.multiversionsetupconstants import USE_EXISTING_RELEASES_FILE except ImportError: # when running db-contrib-tool from multiversionsetupconstants import USE_EXISTING_RELEASES_FILE + from multiversion.multiversion_service import MongoReleases, MongoVersion, MultiversionService LOGGER = structlog.getLogger(__name__) @@ -76,83 +73,22 @@ else: "Skipping generating releases file since the --useExistingReleasesFile flag has been set") -class FCVConstantValues(object): - """Object to hold the calculated FCV constants.""" - - def __init__(self, latest, last_continuous, last_lts, requires_fcv_tag_list, - fcvs_less_than_latest): - """ - Initialize the object. - - :param latest: Latest FCV. - :param last_continuous: Last continuous FCV. - :param last_lts: Last LTS FCV. - :param requires_fcv_tag_list: List of FCVs that we need to generate a tag for. - :param fcvs_less_than_latest: List of all FCVs that are less than latest, starting from v4.0. - """ - self.latest = latest - self.last_continuous = last_continuous - self.last_lts = last_lts - self.requires_fcv_tag_list = requires_fcv_tag_list - self.fcvs_less_than_latest = fcvs_less_than_latest - - -def calculate_fcv_constants(): - """Calculate multiversion constants from data files.""" - mongo_version_yml_file = open(MONGO_VERSION_YAML, 'r') - mongo_version_yml = yaml.safe_load(mongo_version_yml_file) - mongo_version = mongo_version_yml['mongo_version'] - latest = Version(re.match(r'^[0-9]+\.[0-9]+', mongo_version).group(0)) - - releases_yml_file = open(RELEASES_YAML, 'r') - releases_yml = yaml.safe_load(releases_yml_file) - - fcvs = releases_yml['featureCompatibilityVersions'] - fcvs = list(map(Version, fcvs)) - lts = releases_yml['longTermSupportReleases'] - lts = list(map(Version, lts)) - lower_bound_override = releases_yml.get('generateFCVLowerBoundOverride') - - mongo_version_yml_file.close() - releases_yml_file.close() - - # Highest release less than latest. - last_continuous = fcvs[bisect_left(fcvs, latest) - 1] - - # Highest LTS release less than latest. - last_lts = lts[bisect_left(lts, latest) - 1] - - # Normally, this list includes all FCVs greater than last LTS, up to latest. - # However, if we have 'generateFCVLowerBoundOverride' set in releases.yml, we will - # extend the lower bound to also include the prevous value of lastLTS. - lts_cutoff = last_lts - if lower_bound_override is not None: - lts_cutoff = Version(lower_bound_override) - requires_fcv_tag_list = fcvs[bisect_right(fcvs, lts_cutoff):bisect_right(fcvs, latest)] - - # All FCVs less than latest. - fcvs_less_than_latest = fcvs[:bisect_left(fcvs, latest)] - - return FCVConstantValues(latest, last_continuous, last_lts, requires_fcv_tag_list, - fcvs_less_than_latest) - - def version_str(version): """Return a string of the given version in 'MAJOR.MINOR' form.""" return '{}.{}'.format(version.major, version.minor) -def tag_str(version): - """Return a tag for the given version.""" - return 'requires_fcv_{}{}'.format(version.major, version.minor) - - def evg_project_str(version): """Return the evergreen project name for the given version.""" return 'mongodb-mongo-v{}.{}'.format(version.major, version.minor) -fcv_constants = calculate_fcv_constants() +multiversion_service = MultiversionService( + mongo_version=MongoVersion.from_yaml_file(MONGO_VERSION_YAML), + mongo_releases=MongoReleases.from_yaml_file(RELEASES_YAML), +) + +fcv_constants = multiversion_service.calculate_fcv_constants() LAST_LTS_BIN_VERSION = version_str(fcv_constants.last_lts) LAST_CONTINUOUS_BIN_VERSION = version_str(fcv_constants.last_continuous) @@ -169,11 +105,11 @@ LAST_LTS_MONGO_BINARY = "mongo-" + LAST_LTS_BIN_VERSION LAST_LTS_MONGOD_BINARY = "mongod-" + LAST_LTS_BIN_VERSION LAST_LTS_MONGOS_BINARY = "mongos-" + LAST_LTS_BIN_VERSION -REQUIRES_FCV_TAG_LATEST = tag_str(fcv_constants.latest) +REQUIRES_FCV_TAG_LATEST = fcv_constants.get_latest_tag() # Generate tags for all FCVS in (lastLTS, latest], or (lowerBoundOverride, latest] if requested. # All multiversion tests should be run with these tags excluded. -REQUIRES_FCV_TAG = ",".join([tag_str(fcv) for fcv in fcv_constants.requires_fcv_tag_list]) +REQUIRES_FCV_TAG = fcv_constants.get_fcv_tag_list() # Generate evergreen project names for all FCVs less than latest. EVERGREEN_PROJECTS = ['mongodb-mongo-master'] diff --git a/buildscripts/tests/resmokelib/multiversion/__init__.py b/buildscripts/tests/resmokelib/multiversion/__init__.py new file mode 100644 index 00000000000..4b7a2bb941b --- /dev/null +++ b/buildscripts/tests/resmokelib/multiversion/__init__.py @@ -0,0 +1 @@ +"""Empty.""" diff --git a/buildscripts/tests/resmokelib/multiversion/test_multiversion_service.py b/buildscripts/tests/resmokelib/multiversion/test_multiversion_service.py new file mode 100644 index 00000000000..1a067f1ff15 --- /dev/null +++ b/buildscripts/tests/resmokelib/multiversion/test_multiversion_service.py @@ -0,0 +1,59 @@ +"""Unit tests for multiversion_service.py.""" +from unittest import TestCase + +from packaging.version import Version + +import buildscripts.resmokelib.multiversion.multiversion_service as under_test + +# pylint: disable=missing-docstring,invalid-name + + +class TestTagStr(TestCase): + def test_require_fcv_tag_should_be_returned(self): + self.assertEqual(under_test.tag_str(Version("6.0")), "requires_fcv_60") + self.assertEqual(under_test.tag_str(Version("5.3")), "requires_fcv_53") + self.assertEqual(under_test.tag_str(Version("31.41")), "requires_fcv_3141") + + +class TestGetVersion(TestCase): + def test_version_should_be_extracted(self): + mongo_version = under_test.MongoVersion(mongo_version="6.0.0-rc5-18-gbcdfaa9035b") + + self.assertEqual(mongo_version.get_version(), Version("6.0")) + + def test_incompatible_version_should_raise_an_exception(self): + mongo_version = under_test.MongoVersion(mongo_version="not_a_version") + + with self.assertRaises(ValueError): + mongo_version.get_version() + + +class TestCalculateFcvConstants(TestCase): + def test_fcv_constants_should_be_accurate_for_lts_testing(self): + mongo_version = under_test.MongoVersion(mongo_version="6.0") + mongo_releases = under_test.MongoReleases( + **{ + "featureCompatibilityVersions": [ + "4.0", "4.2", "4.4", "4.7", "4.8", "4.9", "5.0", "5.1", "5.2", "5.3", "6.0", + "100.0" + ], + "longTermSupportReleases": ["4.0", "4.2", "4.4", "5.0"], + }) + + multiversion_service = under_test.MultiversionService( + mongo_version=mongo_version, + mongo_releases=mongo_releases, + ) + + fcv_constants = multiversion_service.calculate_fcv_constants() + + self.assertEqual(fcv_constants.latest, Version("6.0")) + self.assertEqual(fcv_constants.last_continuous, Version("5.3")) + self.assertEqual(fcv_constants.last_lts, Version("5.0")) + self.assertEqual(fcv_constants.requires_fcv_tag_list, + [Version(v) for v in ["5.1", "5.2", "5.3", "6.0"]]) + self.assertEqual(fcv_constants.requires_fcv_tag_list_continuous, [Version("6.0")]) + self.assertEqual(fcv_constants.fcvs_less_than_latest, [ + Version(v) + for v in ["4.0", "4.2", "4.4", "4.7", "4.8", "4.9", "5.0", "5.1", "5.2", "5.3"] + ]) diff --git a/evergreen/generate_version.sh b/evergreen/generate_version.sh index 028b7ad923b..db38741fbbd 100644 --- a/evergreen/generate_version.sh +++ b/evergreen/generate_version.sh @@ -6,7 +6,7 @@ cd src set -o errexit set -o verbose -curl -L https://github.com/mongodb/mongo-task-generator/releases/download/v0.3.4/mongo-task-generator --output mongo-task-generator +curl -L https://github.com/mongodb/mongo-task-generator/releases/download/v0.3.6/mongo-task-generator --output mongo-task-generator chmod +x mongo-task-generator activate_venv |