429 lines
21 KiB
Java
Raw Normal View History

2010-02-08 15:30:06 +02:00
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
2010-02-08 15:30:06 +02:00
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch;
2010-02-08 15:30:06 +02:00
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.VersionUtils;
import org.hamcrest.Matchers;
import java.lang.reflect.Modifier;
Validate a joining node's version with version of existing cluster nodes (#25808) When a node tries to join a cluster, it goes through a validation step to make sure the node is compatible with the cluster. Currently we validation that the node can read the cluster state and that it is compatible with the indexes of the cluster. This PR adds validation that the joining node's version is compatible with the versions of existing nodes. Concretely we check that: 1) The node's min compatible version is higher or equal to any node in the cluster (this prevents a too-new node from joining) 2) The node's version is higher or equal to the min compat version of all cluster nodes (this prevents a too old join where, for example, the master is on 5.6, there's another 6.0 node in the cluster and a 5.4 node tries to join). 3) The node's major version is at least as higher as the lowest node in the cluster. This is important as we use the minimum version in the cluster to stop executing bwc code for operations that require multiple nodes. If the nodes are already operating in "new cluster mode", we should prevent nodes from the previous major to join (even if they are wire level compatible). This does mean that if you have a very unlucky partition during the upgrade which partitions all old nodes which are also a minority / data nodes only, the may not be able to re-join the cluster. We feel this edge case risk is well worth the simplification it brings to BWC layers only going one way. This restriction only holds if the cluster state has been recovered (i.e., the cluster has properly formed). Also, the node join validation can now selectively fail specific nodes (previously the entire batch was failed). This is an important preparation for a follow up PR where we plan to have a rejected joining node die with dignity.
2017-07-20 20:11:29 +02:00
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
Validate a joining node's version with version of existing cluster nodes (#25808) When a node tries to join a cluster, it goes through a validation step to make sure the node is compatible with the cluster. Currently we validation that the node can read the cluster state and that it is compatible with the indexes of the cluster. This PR adds validation that the joining node's version is compatible with the versions of existing nodes. Concretely we check that: 1) The node's min compatible version is higher or equal to any node in the cluster (this prevents a too-new node from joining) 2) The node's version is higher or equal to the min compat version of all cluster nodes (this prevents a too old join where, for example, the master is on 5.6, there's another 6.0 node in the cluster and a 5.4 node tries to join). 3) The node's major version is at least as higher as the lowest node in the cluster. This is important as we use the minimum version in the cluster to stop executing bwc code for operations that require multiple nodes. If the nodes are already operating in "new cluster mode", we should prevent nodes from the previous major to join (even if they are wire level compatible). This does mean that if you have a very unlucky partition during the upgrade which partitions all old nodes which are also a minority / data nodes only, the may not be able to re-join the cluster. We feel this edge case risk is well worth the simplification it brings to BWC layers only going one way. This restriction only holds if the cluster state has been recovered (i.e., the cluster has properly formed). Also, the node join validation can now selectively fail specific nodes (previously the entire batch was failed). This is an important preparation for a follow up PR where we plan to have a rejected joining node die with dignity.
2017-07-20 20:11:29 +02:00
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import static org.elasticsearch.Version.V_6_3_0;
import static org.elasticsearch.Version.V_7_0_0_alpha1;
Validate a joining node's version with version of existing cluster nodes (#25808) When a node tries to join a cluster, it goes through a validation step to make sure the node is compatible with the cluster. Currently we validation that the node can read the cluster state and that it is compatible with the indexes of the cluster. This PR adds validation that the joining node's version is compatible with the versions of existing nodes. Concretely we check that: 1) The node's min compatible version is higher or equal to any node in the cluster (this prevents a too-new node from joining) 2) The node's version is higher or equal to the min compat version of all cluster nodes (this prevents a too old join where, for example, the master is on 5.6, there's another 6.0 node in the cluster and a 5.4 node tries to join). 3) The node's major version is at least as higher as the lowest node in the cluster. This is important as we use the minimum version in the cluster to stop executing bwc code for operations that require multiple nodes. If the nodes are already operating in "new cluster mode", we should prevent nodes from the previous major to join (even if they are wire level compatible). This does mean that if you have a very unlucky partition during the upgrade which partitions all old nodes which are also a minority / data nodes only, the may not be able to re-join the cluster. We feel this edge case risk is well worth the simplification it brings to BWC layers only going one way. This restriction only holds if the cluster state has been recovered (i.e., the cluster has properly formed). Also, the node join validation can now selectively fail specific nodes (previously the entire batch was failed). This is an important preparation for a follow up PR where we plan to have a rejected joining node die with dignity.
2017-07-20 20:11:29 +02:00
import static org.elasticsearch.test.VersionUtils.allVersions;
import static org.elasticsearch.test.VersionUtils.randomVersion;
import static org.hamcrest.CoreMatchers.equalTo;
Remove and ban @Test There are three ways `@Test` was used. Way one: ```java @Test public void flubTheBlort() { ``` This way was always replaced with: ```java public void testFlubTheBlort() { ``` Or, maybe with a better method name if I was feeling generous. Way two: ```java @Test(throws=IllegalArgumentException.class) public void testFoo() { methodThatThrows(); } ``` This way of using `@Test` is actually pretty OK, but to get the tools to ban `@Test` entirely it can't be used. Instead: ```java public void testFoo() { try { methodThatThrows(); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e ) { assertThat(e.getMessage(), containsString("something")); } } ``` This is longer but tests more than the old ways and is much more precise. Compare: ```java @Test(throws=IllegalArgumentException.class) public void testFoo() { some(); copy(); and(); pasted(); methodThatThrows(); code(); // <---- This was left here by mistake and is never called } ``` to: ```java @Test(throws=IllegalArgumentException.class) public void testFoo() { some(); copy(); and(); pasted(); try { methodThatThrows(); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e ) { assertThat(e.getMessage(), containsString("something")); } } ``` The final use of test is: ```java @Test(timeout=1000) public void testFoo() { methodThatWasSlow(); } ``` This is the most insidious use of `@Test` because its tempting but tragically flawed. Its flaws are: 1. Hard and fast timeouts can look like they are asserting that something is faster and even do an ok job of it when you compare the timings on the same machine but as soon as you take them to another machine they start to be invalid. On a slow VM both the new and old methods fail. On a super-fast machine the slower and faster ways succeed. 2. Tests often contain slow `assert` calls so the performance of tests isn't sure to predict the performance of non-test code. 3. These timeouts are rude to debuggers because the test just drops out from under it after the timeout. Confusingly, timeouts are useful in tests because it'd be rude for a broken test to cause CI to abort the whole build after it hits a global timeout. But those timeouts should be very very long "backstop" timeouts and aren't useful assertions about speed. For all its flaws `@Test(timeout=1000)` doesn't have a good replacement __in__ __tests__. Nightly benchmarks like http://benchmarks.elasticsearch.org/ are useful here because they run on the same machine but they aren't quick to check and it takes lots of time to figure out the regressions. Sometimes its useful to compare dueling implementations but that requires keeping both implementations around. All and all we don't have a satisfactory answer to the question "what do you replace `@Test(timeout=1000)`" with. So we handle each occurrence on a case by case basis. For files with `@Test` this also: 1. Removes excess blank lines. They don't help anything. 2. Removes underscores from method names. Those would fail any code style checks we ever care to run and don't add to readability. Since I did this manually I didn't do it consistently. 3. Make sure all test method names start with `test`. Some used to end in `Test` or start with `verify` or `check` and they were picked up using the annotation. Without the annotation they always need to start with `test`. 4. Organizes imports using the rules we generate for Eclipse. For the most part this just removes `*` imports which is a win all on its own. It was "required" to quickly remove `@Test`. 5. Removes unneeded casts. This is just a setting I have enabled in Eclipse and forgot to turn off before I did this work. It probably isn't hurting anything. 6. Removes trailing whitespace. Again, another Eclipse setting I forgot to turn off that doesn't hurt anything. Hopefully. 7. Swaps some tests override superclass tests to make them empty with `assumeTrue` so that the reasoning for the skips is logged in the test run and it doesn't "look like" that thing is being tested when it isn't. 8. Adds an oxford comma to an error message. The total test count doesn't change. I know. I counted. ```bash git checkout master && mvn clean && mvn install | tee with_test git no_test_annotation master && mvn clean && mvn install | tee not_test grep 'Tests summary' with_test > with_test_summary grep 'Tests summary' not_test > not_test_summary diff with_test_summary not_test_summary ``` These differ somewhat because some tests are skipped based on the random seed. The total shouldn't differ. But it does! ``` 1c1 < [INFO] Tests summary: 564 suites (1 ignored), 3171 tests, 31 ignored (31 assumptions) --- > [INFO] Tests summary: 564 suites (1 ignored), 3167 tests, 17 ignored (17 assumptions) ``` These are the core unit tests. So we dig further: ```bash cat with_test | perl -pe 's/\n// if /^Suite/;s/.*\n// if /IGNOR/;s/.*\n// if /Assumption #/;s/.*\n// if /HEARTBEAT/;s/Completed .+?,//' | grep Suite > with_test_suites cat not_test | perl -pe 's/\n// if /^Suite/;s/.*\n// if /IGNOR/;s/.*\n// if /Assumption #/;s/.*\n// if /HEARTBEAT/;s/Completed .+?,//' | grep Suite > not_test_suites diff <(sort with_test_suites) <(sort not_test_suites) ``` The four tests with lower test numbers are all extend `AbstractQueryTestCase` and all have a method that looks like this: ```java @Override public void testToQuery() throws IOException { assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0); super.testToQuery(); } ``` It looks like this method was being double counted on master and isn't anymore. Closes #14028
2015-10-09 13:15:22 -04:00
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.sameInstance;
public class VersionTests extends ESTestCase {
public void testVersionComparison() throws Exception {
assertThat(V_6_3_0.before(V_7_0_0_alpha1), is(true));
assertThat(V_6_3_0.before(V_6_3_0), is(false));
assertThat(V_7_0_0_alpha1.before(V_6_3_0), is(false));
assertThat(V_6_3_0.onOrBefore(V_7_0_0_alpha1), is(true));
assertThat(V_6_3_0.onOrBefore(V_6_3_0), is(true));
assertThat(V_7_0_0_alpha1.onOrBefore(V_6_3_0), is(false));
assertThat(V_6_3_0.after(V_7_0_0_alpha1), is(false));
assertThat(V_6_3_0.after(V_6_3_0), is(false));
assertThat(V_7_0_0_alpha1.after(V_6_3_0), is(true));
assertThat(V_6_3_0.onOrAfter(V_7_0_0_alpha1), is(false));
assertThat(V_6_3_0.onOrAfter(V_6_3_0), is(true));
assertThat(V_7_0_0_alpha1.onOrAfter(V_6_3_0), is(true));
assertTrue(Version.fromString("5.0.0-alpha2").onOrAfter(Version.fromString("5.0.0-alpha1")));
assertTrue(Version.fromString("5.0.0").onOrAfter(Version.fromString("5.0.0-beta2")));
assertTrue(Version.fromString("5.0.0-rc1").onOrAfter(Version.fromString("5.0.0-beta24")));
assertTrue(Version.fromString("5.0.0-alpha24").before(Version.fromString("5.0.0-beta0")));
assertThat(V_6_3_0, is(lessThan(V_7_0_0_alpha1)));
assertThat(V_6_3_0.compareTo(V_6_3_0), is(0));
assertThat(V_7_0_0_alpha1, is(greaterThan(V_6_3_0)));
}
public void testMin() {
assertEquals(VersionUtils.getPreviousVersion(), Version.min(Version.CURRENT, VersionUtils.getPreviousVersion()));
assertEquals(Version.fromString("1.0.1"), Version.min(Version.fromString("1.0.1"), Version.CURRENT));
Version version = VersionUtils.randomVersion(random());
Version version1 = VersionUtils.randomVersion(random());
if (version.id <= version1.id) {
assertEquals(version, Version.min(version1, version));
} else {
assertEquals(version1, Version.min(version1, version));
}
}
public void testMax() {
assertEquals(Version.CURRENT, Version.max(Version.CURRENT, VersionUtils.getPreviousVersion()));
assertEquals(Version.CURRENT, Version.max(Version.fromString("1.0.1"), Version.CURRENT));
Version version = VersionUtils.randomVersion(random());
Version version1 = VersionUtils.randomVersion(random());
if (version.id >= version1.id) {
assertEquals(version, Version.max(version1, version));
} else {
assertEquals(version1, Version.max(version1, version));
}
}
public void testMinimumIndexCompatibilityVersion() {
assertEquals(Version.fromId(5000099), Version.V_6_0_0_beta1.minimumIndexCompatibilityVersion());
assertEquals(Version.fromId(2000099), Version.fromId(5000099).minimumIndexCompatibilityVersion());
assertEquals(Version.fromId(2000099),
Version.fromId(5010000).minimumIndexCompatibilityVersion());
assertEquals(Version.fromId(2000099),
Version.fromId(5000001).minimumIndexCompatibilityVersion());
}
public void testVersionConstantPresent() {
assertThat(Version.CURRENT, sameInstance(Version.fromId(Version.CURRENT.id)));
assertThat(Version.CURRENT.luceneVersion, equalTo(org.apache.lucene.util.Version.LATEST));
final int iters = scaledRandomIntBetween(20, 100);
for (int i = 0; i < iters; i++) {
Version version = randomVersion(random());
assertThat(version, sameInstance(Version.fromId(version.id)));
assertThat(version.luceneVersion, sameInstance(Version.fromId(version.id).luceneVersion));
}
}
2014-11-24 13:40:26 +02:00
public void testCURRENTIsLatest() {
final int iters = scaledRandomIntBetween(100, 1000);
for (int i = 0; i < iters; i++) {
Version version = randomVersion(random());
if (version != Version.CURRENT) {
assertThat("Version: " + version + " should be before: " + Version.CURRENT + " but wasn't", version.before(Version.CURRENT), is(true));
}
}
}
public void testVersionFromString() {
final int iters = scaledRandomIntBetween(100, 1000);
for (int i = 0; i < iters; i++) {
Version version = randomVersion(random());
2016-02-20 17:12:48 -08:00
assertThat(Version.fromString(version.toString()), sameInstance(version));
}
}
public void testTooLongVersionFromString() {
Exception e = expectThrows(IllegalArgumentException.class, () -> Version.fromString("1.0.0.1.3"));
assertThat(e.getMessage(), containsString("needs to contain major, minor, and revision"));
}
public void testTooShortVersionFromString() {
Exception e = expectThrows(IllegalArgumentException.class, () -> Version.fromString("1.0"));
assertThat(e.getMessage(), containsString("needs to contain major, minor, and revision"));
}
public void testWrongVersionFromString() {
Exception e = expectThrows(IllegalArgumentException.class, () -> Version.fromString("WRONG.VERSION"));
assertThat(e.getMessage(), containsString("needs to contain major, minor, and revision"));
}
public void testVersionNoPresentInSettings() {
Exception e = expectThrows(IllegalStateException.class, () -> Version.indexCreated(Settings.builder().build()));
assertThat(e.getMessage(), containsString("[index.version.created] is not present"));
}
public void testIndexCreatedVersion() {
// an actual index has a IndexMetaData.SETTING_INDEX_UUID
final Version version = Version.V_6_0_0_beta1;
assertEquals(version, Version.indexCreated(Settings.builder().put(IndexMetaData.SETTING_INDEX_UUID, "foo").put(IndexMetaData.SETTING_VERSION_CREATED, version).build()));
}
public void testMinCompatVersion() {
Version major = Version.fromString("2.0.0");
assertThat(Version.fromString("2.0.0").minimumCompatibilityVersion(), equalTo(major));
assertThat(Version.fromString("2.2.0").minimumCompatibilityVersion(), equalTo(major));
assertThat(Version.fromString("2.3.0").minimumCompatibilityVersion(), equalTo(major));
Version major5x = Version.fromString("5.0.0");
assertThat(Version.fromString("5.0.0").minimumCompatibilityVersion(), equalTo(major5x));
assertThat(Version.fromString("5.2.0").minimumCompatibilityVersion(), equalTo(major5x));
assertThat(Version.fromString("5.3.0").minimumCompatibilityVersion(), equalTo(major5x));
Version major56x = Version.fromString("5.6.0");
assertThat(Version.V_6_5_0.minimumCompatibilityVersion(), equalTo(major56x));
assertThat(Version.V_6_3_1.minimumCompatibilityVersion(), equalTo(major56x));
// from 7.0 on we are supporting the latest minor of the previous major... this might fail once we add a new version ie. 5.x is
// released since we need to bump the supported minor in Version#minimumCompatibilityVersion()
Version lastVersion = Version.V_6_6_0; // TODO: remove this once min compat version is a constant instead of method
assertEquals(lastVersion.major, Version.V_7_0_0_alpha1.minimumCompatibilityVersion().major);
assertEquals("did you miss to bump the minor in Version#minimumCompatibilityVersion()",
lastVersion.minor, Version.V_7_0_0_alpha1.minimumCompatibilityVersion().minor);
assertEquals(0, Version.V_7_0_0_alpha1.minimumCompatibilityVersion().revision);
}
public void testToString() {
// with 2.0.beta we lowercase
assertEquals("2.0.0-beta1", Version.fromString("2.0.0-beta1").toString());
assertEquals("5.0.0-alpha1", Version.fromId(5000001).toString());
assertEquals("2.3.0", Version.fromString("2.3.0").toString());
assertEquals("0.90.0.Beta1", Version.fromString("0.90.0.Beta1").toString());
assertEquals("1.0.0.Beta1", Version.fromString("1.0.0.Beta1").toString());
assertEquals("2.0.0-beta1", Version.fromString("2.0.0-beta1").toString());
assertEquals("5.0.0-beta1", Version.fromString("5.0.0-beta1").toString());
assertEquals("5.0.0-alpha1", Version.fromString("5.0.0-alpha1").toString());
}
public void testIsBeta() {
assertTrue(Version.fromString("2.0.0-beta1").isBeta());
assertTrue(Version.fromString("1.0.0.Beta1").isBeta());
assertTrue(Version.fromString("0.90.0.Beta1").isBeta());
}
public void testIsAlpha() {
assertTrue(new Version(5000001, org.apache.lucene.util.Version.LUCENE_7_0_0).isAlpha());
assertFalse(new Version(4000002, org.apache.lucene.util.Version.LUCENE_7_0_0).isAlpha());
assertTrue(new Version(4000002, org.apache.lucene.util.Version.LUCENE_7_0_0).isBeta());
assertTrue(Version.fromString("5.0.0-alpha14").isAlpha());
assertEquals(5000014, Version.fromString("5.0.0-alpha14").id);
assertTrue(Version.fromId(5000015).isAlpha());
for (int i = 0 ; i < 25; i++) {
assertEquals(Version.fromString("5.0.0-alpha" + i).id, Version.fromId(5000000 + i).id);
assertEquals("5.0.0-alpha" + i, Version.fromId(5000000 + i).toString());
}
for (int i = 0 ; i < 25; i++) {
assertEquals(Version.fromString("5.0.0-beta" + i).id, Version.fromId(5000000 + i + 25).id);
assertEquals("5.0.0-beta" + i, Version.fromId(5000000 + i + 25).toString());
}
}
public void testParseVersion() {
final int iters = scaledRandomIntBetween(100, 1000);
for (int i = 0; i < iters; i++) {
Version version = randomVersion(random());
if (random().nextBoolean()) {
version = new Version(version.id, version.luceneVersion);
}
Version parsedVersion = Version.fromString(version.toString());
assertEquals(version, parsedVersion);
}
expectThrows(IllegalArgumentException.class, () -> {
Version.fromString("5.0.0-alph2");
});
assertSame(Version.CURRENT, Version.fromString(Version.CURRENT.toString()));
assertEquals(Version.fromString("2.0.0-SNAPSHOT"), Version.fromId(2000099));
expectThrows(IllegalArgumentException.class, () -> {
Version.fromString("5.0.0-SNAPSHOT");
});
}
public void testParseLenient() {
// note this is just a silly sanity check, we test it in lucene
for (Version version : VersionUtils.allReleasedVersions()) {
org.apache.lucene.util.Version luceneVersion = version.luceneVersion;
String string = luceneVersion.toString().toUpperCase(Locale.ROOT)
.replaceFirst("^LUCENE_(\\d+)_(\\d+)$", "$1.$2");
assertThat(luceneVersion, Matchers.equalTo(Lucene.parseVersionLenient(string, null)));
}
}
public void testAllVersionsMatchId() throws Exception {
final Set<Version> releasedVersions = new HashSet<>(VersionUtils.allReleasedVersions());
final Set<Version> unreleasedVersions = new HashSet<>(VersionUtils.allUnreleasedVersions());
Map<String, Version> maxBranchVersions = new HashMap<>();
for (java.lang.reflect.Field field : Version.class.getFields()) {
if (field.getName().matches("_ID")) {
assertTrue(field.getName() + " should be static", Modifier.isStatic(field.getModifiers()));
assertTrue(field.getName() + " should be final", Modifier.isFinal(field.getModifiers()));
int versionId = (Integer)field.get(Version.class);
String constantName = field.getName().substring(0, field.getName().indexOf("_ID"));
java.lang.reflect.Field versionConstant = Version.class.getField(constantName);
assertTrue(constantName + " should be static", Modifier.isStatic(versionConstant.getModifiers()));
assertTrue(constantName + " should be final", Modifier.isFinal(versionConstant.getModifiers()));
Version v = (Version) versionConstant.get(null);
logger.debug("Checking {}", v);
if (field.getName().endsWith("_UNRELEASED")) {
assertTrue(unreleasedVersions.contains(v));
} else {
assertTrue(releasedVersions.contains(v));
}
assertEquals("Version id " + field.getName() + " does not point to " + constantName, v, Version.fromId(versionId));
assertEquals("Version " + constantName + " does not have correct id", versionId, v.id);
if (v.major >= 2) {
2016-02-20 17:12:48 -08:00
String number = v.toString();
if (v.isBeta()) {
number = number.replace("-beta", "_beta");
} else if (v.isRC()) {
number = number.replace("-rc", "_rc");
} else if (v.isAlpha()) {
number = number.replace("-alpha", "_alpha");
}
assertEquals("V_" + number.replace('.', '_'), constantName);
} else {
2016-02-20 17:12:48 -08:00
assertEquals("V_" + v.toString().replace('.', '_'), constantName);
}
// only the latest version for a branch should be a snapshot (ie unreleased)
String branchName = "" + v.major + "." + v.minor;
Version maxBranchVersion = maxBranchVersions.get(branchName);
if (maxBranchVersion == null) {
maxBranchVersions.put(branchName, v);
} else if (v.after(maxBranchVersion)) {
if (v == Version.CURRENT) {
// Current is weird - it counts as released even though it shouldn't.
continue;
}
assertFalse("Version " + maxBranchVersion + " cannot be a snapshot because version " + v + " exists",
VersionUtils.allUnreleasedVersions().contains(maxBranchVersion));
maxBranchVersions.put(branchName, v);
}
}
}
}
// this test ensures we never bump the lucene version in a bugfix release
public void testLuceneVersionIsSameOnMinorRelease() {
for (Version version : VersionUtils.allReleasedVersions()) {
for (Version other : VersionUtils.allReleasedVersions()) {
if (other.onOrAfter(version)) {
assertTrue("lucene versions must be " + other + " >= " + version,
other.luceneVersion.onOrAfter(version.luceneVersion));
}
if (other.isAlpha() == false && version.isAlpha() == false
&& other.major == version.major && other.minor == version.minor) {
assertEquals(version + " vs. " + other, other.luceneVersion.major, version.luceneVersion.major);
assertEquals(version + " vs. " + other, other.luceneVersion.minor, version.luceneVersion.minor);
// should we also assert the lucene bugfix version?
}
}
}
}
public static void assertUnknownVersion(Version version) {
assertFalse("Version " + version + " has been releaed don't use a new instance of this version",
VersionUtils.allReleasedVersions().contains(version));
}
public void testIsCompatible() {
assertTrue(isCompatible(Version.CURRENT, Version.CURRENT.minimumCompatibilityVersion()));
assertFalse(isCompatible(Version.V_6_5_0, Version.V_7_0_0_alpha1));
assertTrue(isCompatible(Version.V_6_6_0, Version.V_7_0_0_alpha1));
assertFalse(isCompatible(Version.fromId(2000099), Version.V_7_0_0_alpha1));
assertFalse(isCompatible(Version.fromId(2000099), Version.V_6_5_0));
assertFalse(isCompatible(Version.fromString("7.0.0"), Version.fromString("8.0.0")));
assertFalse(isCompatible(Version.fromString("7.0.0-alpha1"), Version.fromString("8.0.0")));
final Version currentMajorVersion = Version.fromId(Version.CURRENT.major * 1000000 + 99);
final Version currentOrNextMajorVersion;
if (Version.CURRENT.minor > 0) {
currentOrNextMajorVersion = Version.fromId((Version.CURRENT.major + 1) * 1000000 + 99);
} else {
currentOrNextMajorVersion = currentMajorVersion;
}
final Version lastMinorFromPreviousMajor =
VersionUtils
.allVersions()
.stream()
.filter(v -> v.major == currentOrNextMajorVersion.major - 1)
.max(Version::compareTo)
.orElseThrow(
() -> new IllegalStateException("expected previous minor version for [" + currentOrNextMajorVersion + "]"));
final Version previousMinorVersion = VersionUtils.getPreviousMinorVersion();
assert previousMinorVersion.major == currentOrNextMajorVersion.major
|| previousMinorVersion.major == lastMinorFromPreviousMajor.major;
boolean isCompatible = previousMinorVersion.major == currentOrNextMajorVersion.major
|| previousMinorVersion.minor == lastMinorFromPreviousMajor.minor;
final String message = String.format(
Locale.ROOT,
"[%s] should %s be compatible with [%s]",
previousMinorVersion,
isCompatible ? "" : " not",
currentOrNextMajorVersion);
assertThat(
message,
isCompatible(VersionUtils.getPreviousMinorVersion(), currentOrNextMajorVersion),
equalTo(isCompatible));
assertFalse(isCompatible(Version.fromId(5000099), Version.fromString("6.0.0")));
assertFalse(isCompatible(Version.fromId(5000099), Version.fromString("7.0.0")));
Validate a joining node's version with version of existing cluster nodes (#25808) When a node tries to join a cluster, it goes through a validation step to make sure the node is compatible with the cluster. Currently we validation that the node can read the cluster state and that it is compatible with the indexes of the cluster. This PR adds validation that the joining node's version is compatible with the versions of existing nodes. Concretely we check that: 1) The node's min compatible version is higher or equal to any node in the cluster (this prevents a too-new node from joining) 2) The node's version is higher or equal to the min compat version of all cluster nodes (this prevents a too old join where, for example, the master is on 5.6, there's another 6.0 node in the cluster and a 5.4 node tries to join). 3) The node's major version is at least as higher as the lowest node in the cluster. This is important as we use the minimum version in the cluster to stop executing bwc code for operations that require multiple nodes. If the nodes are already operating in "new cluster mode", we should prevent nodes from the previous major to join (even if they are wire level compatible). This does mean that if you have a very unlucky partition during the upgrade which partitions all old nodes which are also a minority / data nodes only, the may not be able to re-join the cluster. We feel this edge case risk is well worth the simplification it brings to BWC layers only going one way. This restriction only holds if the cluster state has been recovered (i.e., the cluster has properly formed). Also, the node join validation can now selectively fail specific nodes (previously the entire batch was failed). This is an important preparation for a follow up PR where we plan to have a rejected joining node die with dignity.
2017-07-20 20:11:29 +02:00
Version a = randomVersion(random());
Version b = randomVersion(random());
assertThat(a.isCompatible(b), equalTo(b.isCompatible(a)));
}
/* tests that if a new version's minCompatVersion is always equal or higher to any older version */
public void testMinCompatVersionOrderRespectsVersionOrder() {
List<Version> versionsByMinCompat = new ArrayList<>(allVersions());
versionsByMinCompat.sort(Comparator.comparing(Version::minimumCompatibilityVersion));
assertThat(versionsByMinCompat, equalTo(allVersions()));
versionsByMinCompat.sort(Comparator.comparing(Version::minimumIndexCompatibilityVersion));
assertThat(versionsByMinCompat, equalTo(allVersions()));
}
public boolean isCompatible(Version left, Version right) {
boolean result = left.isCompatible(right);
assert result == right.isCompatible(left);
return result;
}
// This exists because 5.1.0 was never released due to a mistake in the release process.
// This verifies that we never declare the version as "released" accidentally.
// It would never pass qa tests later on, but those come very far in the build and this is quick to check now.
public void testUnreleasedVersion() {
Version VERSION_5_1_0_UNRELEASED = Version.fromString("5.1.0");
VersionTests.assertUnknownVersion(VERSION_5_1_0_UNRELEASED);
}
public void testDisplayVersion() {
final Version version = randomVersion(random());
{
final String displayVersion = Version.displayVersion(version, true);
assertThat(displayVersion, equalTo(version.toString() + "-SNAPSHOT"));
}
{
final String displayVersion = Version.displayVersion(version, false);
assertThat(displayVersion, equalTo(version.toString()));
}
}
}