| import logging |
| |
| from collections import namedtuple |
| |
| from dtest import RUN_STATIC_UPGRADE_MATRIX |
| |
| logger = logging.getLogger(__name__) |
| |
| # UpgradePath's contain data about upgrade paths we wish to test |
| # They also contain VersionMeta's for each version the path is testing |
| UpgradePath = namedtuple('UpgradePath', ('name', 'starting_version', 'upgrade_version', 'starting_meta', 'upgrade_meta')) |
| |
| |
| def _get_version_family(): |
| """ |
| Detects the version family (line) using dtest.py:CASSANDRA_VERSION_FROM_BUILD |
| """ |
| # todo CASSANDRA-14421 |
| # current_version = CASSANDRA_VERSION_FROM_BUILD |
| current_version = '4.0' |
| |
| version_family = 'unknown' |
| if current_version.vstring.startswith('2.0'): |
| version_family = '2.0.x' |
| elif current_version.vstring.startswith('2.1'): |
| version_family = '2.1.x' |
| elif current_version.vstring.startswith('2.2'): |
| version_family = '2.2.x' |
| elif current_version.vstring.startswith('3.0'): |
| version_family = '3.0.x' |
| elif '3.1' <= current_version < '4.0': |
| version_family = '3.x' |
| elif '4.0' <= current_version < '4.1': |
| version_family = 'trunk' |
| else: |
| # when this occurs, it's time to update this manifest a bit! |
| raise RuntimeError("4.1+ not yet supported on upgrade tests!") |
| |
| return version_family |
| |
| |
| VERSION_FAMILY = _get_version_family() |
| |
| |
| class VersionMeta(namedtuple('_VersionMeta', ('name', 'family', 'variant', 'version', 'min_proto_v', 'max_proto_v', 'java_versions'))): |
| """ |
| VersionMeta's are namedtuples that capture data about version family, protocols supported, and current version identifiers |
| they must have a 'variant' value of 'current' or 'indev', where 'current' means most recent released version, |
| 'indev' means where changing code is found. |
| """ |
| @property |
| def java_version(self): |
| return max(self.java_versions) |
| |
| @property |
| def matches_current_env_version_family(self): |
| """ |
| Returns boolean indicating whether this meta matches the current version family of the environment. |
| |
| e.g. Returns true if the current env version family is 3.x and the meta's family attribute is a match. |
| """ |
| return self.family == VERSION_FAMILY |
| |
| def clone_with_local_env_version(self): |
| """ |
| Returns a new object cloned from this one, with the version replaced with the local env version. |
| """ |
| # todo CASSANDRA-14421 |
| # return self._replace(version=CASSANDRA_GITREF or CASSANDRA_VERSION_FROM_BUILD) |
| return self |
| |
| |
| indev_2_0_x = None # None if release not likely |
| current_2_0_x = VersionMeta(name='current_2_0_x', family='2.0.x', variant='current', version='2.0.17', min_proto_v=1, max_proto_v=2, java_versions=(7,)) |
| |
| indev_2_1_x = VersionMeta(name='indev_2_1_x', family='2.1.x', variant='indev', version='github:apache/cassandra-2.1', min_proto_v=1, max_proto_v=3, java_versions=(7, 8)) |
| current_2_1_x = VersionMeta(name='current_2_1_x', family='2.1.x', variant='current', version='2.1.17', min_proto_v=1, max_proto_v=3, java_versions=(7, 8)) |
| |
| indev_2_2_x = VersionMeta(name='indev_2_2_x', family='2.2.x', variant='indev', version='github:apache/cassandra-2.2', min_proto_v=1, max_proto_v=4, java_versions=(7, 8)) |
| current_2_2_x = VersionMeta(name='current_2_2_x', family='2.2.x', variant='current', version='2.2.9', min_proto_v=1, max_proto_v=4, java_versions=(7, 8)) |
| |
| indev_3_0_x = VersionMeta(name='indev_3_0_x', family='3.0.x', variant='indev', version='github:apache/cassandra-3.0', min_proto_v=3, max_proto_v=4, java_versions=(8,)) |
| current_3_0_x = VersionMeta(name='current_3_0_x', family='3.0.x', variant='current', version='3.0.12', min_proto_v=3, max_proto_v=4, java_versions=(8,)) |
| |
| indev_3_x = VersionMeta(name='indev_3_x', family='3.x', variant='indev', version='github:apache/cassandra-3.11', min_proto_v=3, max_proto_v=4, java_versions=(8,)) |
| current_3_x = VersionMeta(name='current_3_x', family='3.x', variant='current', version='3.10', min_proto_v=3, max_proto_v=4, java_versions=(8,)) |
| |
| indev_trunk = VersionMeta(name='indev_trunk', family='trunk', variant='indev', version='github:apache/trunk', min_proto_v=3, max_proto_v=4, java_versions=(8,)) |
| |
| |
| # MANIFEST maps a VersionMeta representing a line/variant to a list of other VersionMeta's representing supported upgrades |
| # Note on versions: 2.0 must upgrade to 2.1. Once at 2.1 or newer, upgrade is supported to any later version, including trunk (for now). |
| # "supported upgrade" means a few basic things, for an upgrade of version 'A' to higher version 'B': |
| # 1) The cluster will function in a mixed-version state, with some nodes on version A and some nodes on version B. Schema modifications are not supported on mixed-version clusters. |
| # 2) Features exclusive to version B may not work until all nodes are running version B. |
| # 3) Nodes upgraded to version B can read data stored by the predecessor version A, and from a data standpoint will function the same as if they always ran version B. |
| # 4) If a new sstable format is present in version B, writes will occur in that format after upgrade. Running sstableupgrade on version B will proactively convert version A sstables to version B. |
| MANIFEST = { |
| indev_2_0_x: [indev_2_1_x, current_2_1_x], |
| current_2_0_x: [indev_2_0_x, indev_2_1_x, current_2_1_x], |
| |
| indev_2_1_x: [indev_2_2_x, current_2_2_x, indev_3_0_x, current_3_0_x, indev_3_x, current_3_x], |
| current_2_1_x: [indev_2_1_x, indev_2_2_x, current_2_2_x, indev_3_0_x, current_3_0_x, indev_3_x, current_3_x], |
| |
| indev_2_2_x: [indev_3_0_x, current_3_0_x, indev_3_x, current_3_x], |
| current_2_2_x: [indev_2_2_x, indev_3_0_x, current_3_0_x, indev_3_x, current_3_x], |
| |
| indev_3_0_x: [indev_3_x, current_3_x], |
| current_3_0_x: [indev_3_0_x, indev_3_x, current_3_x, indev_trunk], |
| |
| current_3_x: [indev_3_x, indev_trunk], |
| indev_3_x: [indev_trunk] |
| } |
| |
| # Local env and custom path testing instructions. Use these steps to REPLACE the normal upgrade test cases with your own. |
| # 1) Add a VersionMeta for each version you wish to test (see examples below). Update the name, family, version, and protocol restrictions as needed. Use a unique name for each VersionMeta. |
| # 2) Update OVERRIDE_MANIFEST (see example below). |
| # 3) If you want to test using local code, set the version attribute using local slugs in the format 'local:/path/to/cassandra/:branch_name' |
| # 4) Run the tests! |
| # To run all, use 'nosetests -v upgrade_tests/'. To run specific tests, use 'nosetests -vs --collect-only' to preview the test names, then run nosetests using the desired test name. |
| # Note that nosetests outputs test names in a format that needs to be tweaked a bit before they will run from the command line. |
| custom_1 = VersionMeta(name='custom_branch_1', family='2.1.x', variant='indev', version='local:some_branch', min_proto_v=3, max_proto_v=4, java_versions=(7, 8)) |
| custom_2 = VersionMeta(name='custom_branch_2', family='2.2.x', variant='indev', version='git:trunk', min_proto_v=3, max_proto_v=4, java_versions=(7, 8)) |
| custom_3 = VersionMeta(name='custom_branch_3', family='3.0.x', variant='indev', version='git:cassandra-3.5', min_proto_v=3, max_proto_v=4, java_versions=(7, 8)) |
| custom_4 = VersionMeta(name='custom_branch_4', family='3.x', variant='indev', version='git:cassandra-3.6', min_proto_v=3, max_proto_v=4, java_versions=(7, 8)) |
| OVERRIDE_MANIFEST = { |
| # EXAMPLE: |
| # custom_1: [custom_2, custom_3], # creates a test of custom_1 -> custom_2, and another test from custom_1 -> custom_3 |
| # custom_3: [custom_4] # creates a test of custom_3 -> custom_4 |
| } |
| |
| |
| def _have_common_proto(origin_meta, destination_meta): |
| """ |
| Takes two VersionMeta objects, in order of test from start version to next version. |
| Returns a boolean indicating if the given VersionMetas have a common protocol version. |
| """ |
| return origin_meta.max_proto_v >= destination_meta.min_proto_v |
| |
| |
| def _is_targeted_variant_combo(origin_meta, destination_meta): |
| """ |
| Takes two VersionMeta objects, in order of test from start version to next version. |
| Returns a boolean indicating if this is a test pair we care about. |
| |
| for now we only test upgrades of these types: |
| current -> in-dev (aka: released -> branch) |
| """ |
| # if we're overriding the test manifest, we don't want to filter anything out |
| if OVERRIDE_MANIFEST: |
| return True |
| |
| # is this an upgrade variant combination we care about? |
| return (origin_meta.variant == 'current' and destination_meta.variant == 'indev') |
| |
| |
| def build_upgrade_pairs(): |
| """ |
| Using the manifest (above), builds a set of valid upgrades, according to current testing practices. |
| |
| Returns a list of UpgradePath's. |
| """ |
| valid_upgrade_pairs = [] |
| manifest = OVERRIDE_MANIFEST or MANIFEST |
| |
| for origin_meta, destination_metas in list(manifest.items()): |
| for destination_meta in destination_metas: |
| if not (origin_meta and destination_meta): # None means we don't care about that version, which means we don't care about iterations involving it either |
| logger.debug("skipping class creation as a version is undefined (this is normal), versions: {} and {}".format(origin_meta, destination_meta)) |
| continue |
| |
| if not _is_targeted_variant_combo(origin_meta, destination_meta): |
| logger.debug("skipping class creation, no testing of '{}' to '{}' (for {} upgrade to {})".format(origin_meta.variant, destination_meta.variant, origin_meta.name, destination_meta.name)) |
| continue |
| |
| if not _have_common_proto(origin_meta, destination_meta): |
| logger.debug("skipping class creation, no compatible protocol version between {} and {}".format(origin_meta.name, destination_meta.name)) |
| continue |
| |
| path_name = 'Upgrade_' + origin_meta.name + '_To_' + destination_meta.name |
| |
| if not (RUN_STATIC_UPGRADE_MATRIX or OVERRIDE_MANIFEST): |
| if destination_meta.matches_current_env_version_family: |
| # looks like this test should actually run in the current env, so let's set the final version to match the env exactly |
| oldmeta = destination_meta |
| newmeta = destination_meta.clone_with_local_env_version() |
| logger.debug("{} appears applicable to current env. Overriding final test version from {} to {}".format(path_name, oldmeta.version, newmeta.version)) |
| destination_meta = newmeta |
| |
| valid_upgrade_pairs.append( |
| UpgradePath( |
| name=path_name, |
| starting_version=origin_meta.version, |
| upgrade_version=destination_meta.version, |
| starting_meta=origin_meta, |
| upgrade_meta=destination_meta |
| ) |
| ) |
| |
| return valid_upgrade_pairs |