diff --git a/server/src/main/java/org/elasticsearch/TransportVersion.java b/server/src/main/java/org/elasticsearch/TransportVersion.java
index 7437985e49636..848edb692bb30 100644
--- a/server/src/main/java/org/elasticsearch/TransportVersion.java
+++ b/server/src/main/java/org/elasticsearch/TransportVersion.java
@@ -12,15 +12,35 @@
import org.elasticsearch.common.VersionId;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
-import org.elasticsearch.internal.VersionExtension;
-import org.elasticsearch.plugins.ExtensionLoader;
+import java.io.BufferedReader;
import java.io.IOException;
-import java.util.ServiceLoader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+import java.util.stream.Collectors;
/**
* Represents the version of the wire protocol used to communicate between a pair of ES nodes.
*
+ * Note: We are currently transitioning to a file-based system to load and maintain transport versions. These file-based transport
+ * versions are named and are referred to as named transport versions. Named transport versions also maintain a linked list of their
+ * own patch versions to simplify transport version compatibility checks. Transport versions that continue to be loaded through
+ * {@link TransportVersions} are referred to as unnamed transport versions. Unnamed transport versions will continue being used
+ * over the wire as we only need the id for compatibility checks even against named transport versions. There are changes
+ * throughout {@link TransportVersion} that are for this transition. For now, continue to use the existing system of adding unnamed
+ * transport versions to {@link TransportVersions}.
+ *
* Prior to 8.8.0, the release {@link Version} was used everywhere. This class separates the wire protocol version from the release version.
*
* Each transport version constant has an id number, which for versions prior to 8.9.0 is the same as the release version for backwards
@@ -50,14 +70,62 @@
* different version value. If you need to know whether the cluster as a whole speaks a new enough {@link TransportVersion} to understand a
* newly-added feature, use {@link org.elasticsearch.cluster.ClusterState#getMinTransportVersion}.
*/
-public record TransportVersion(int id) implements VersionId {
+public record TransportVersion(String name, int id, TransportVersion nextPatchVersion) implements VersionId {
+
+ /**
+ * Constructs an unnamed transport version.
+ */
+ public TransportVersion(int id) {
+ this(null, id, null);
+ }
+
+ /**
+ * Constructs a named transport version along with its set of compatible patch versions from x-content.
+ * This method takes in the parameter {@code latest} which is the highest valid transport version id
+ * supported by this node. Versions newer than the current transport version id for this node are discarded.
+ */
+ public static TransportVersion fromInputStream(String path, boolean nameInFile, InputStream stream, Integer latest) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
+ String line = reader.readLine();
+ String[] parts = line.replaceAll("\\s+", "").split(",");
+ String check;
+ while ((check = reader.readLine()) != null) {
+ if (check.replaceAll("\\s+", "").isEmpty() == false) {
+ throw new IllegalArgumentException("invalid transport version file format [" + path + "]");
+ }
+ }
+ if (parts.length < (nameInFile ? 2 : 1)) {
+ throw new IllegalStateException("invalid transport version file format [" + path + "]");
+ }
+ String name = nameInFile ? parts[0] : path.substring(path.lastIndexOf('/') + 1, path.length() - 4);
+ List ids = new ArrayList<>();
+ for (int i = nameInFile ? 1 : 0; i < parts.length; ++i) {
+ try {
+ ids.add(Integer.parseInt(parts[i]));
+ } catch (NumberFormatException nfe) {
+ throw new IllegalStateException("invalid transport version file format [" + path + "]", nfe);
+ }
+ }
+ ids.sort(Integer::compareTo);
+ TransportVersion transportVersion = null;
+ for (int idIndex = 0; idIndex < ids.size(); ++idIndex) {
+ if (ids.get(idIndex) > latest) {
+ break;
+ }
+ transportVersion = new TransportVersion(name, ids.get(idIndex), transportVersion);
+ }
+ return transportVersion;
+ } catch (IOException ioe) {
+ throw new UncheckedIOException("cannot parse transport version [" + path + "]", ioe);
+ }
+ }
public static TransportVersion readVersion(StreamInput in) throws IOException {
return fromId(in.readVInt());
}
public static TransportVersion fromId(int id) {
- TransportVersion known = TransportVersions.VERSION_IDS.get(id);
+ TransportVersion known = VersionsHolder.ALL_VERSIONS_BY_ID.get(id);
if (known != null) {
return known;
}
@@ -65,6 +133,23 @@ public static TransportVersion fromId(int id) {
return new TransportVersion(id);
}
+ /**
+ * Finds a {@link TransportVersion} by its name. The parameter {@code name} must be a {@link String}
+ * direct value or validation checks will fail. {@code TransportVersion.fromName("direct_value")}.
+ *
+ * This will only return the latest known named transport version for a given name and not its
+ * patch versions. Patch versions are constructed as a linked list internally and may be found by
+ * cycling through them in a loop using {@link TransportVersion#nextPatchVersion()}.
+ *
+ */
+ public static TransportVersion fromName(String name) {
+ TransportVersion known = VersionsHolder.ALL_VERSIONS_BY_NAME.get(name);
+ if (known == null) {
+ throw new IllegalStateException("unknown transport version [" + name + "]");
+ }
+ return known;
+ }
+
public static void writeVersion(TransportVersion version, StreamOutput out) throws IOException {
out.writeVInt(version.id);
}
@@ -95,7 +180,14 @@ public static boolean isCompatible(TransportVersion version) {
* This should be the transport version with the highest id.
*/
public static TransportVersion current() {
- return CurrentHolder.CURRENT;
+ return VersionsHolder.CURRENT;
+ }
+
+ /**
+ * Sorted list of all defined transport versions
+ */
+ public static List getAllVersions() {
+ return VersionsHolder.ALL_VERSIONS;
}
/**
@@ -104,7 +196,7 @@ public static TransportVersion current() {
* in the wild (they're sent over the wire by numeric ID) but we don't know how to communicate using such versions.
*/
public boolean isKnown() {
- return before(TransportVersions.V_8_9_X) || TransportVersions.VERSION_IDS.containsKey(id);
+ return before(TransportVersions.V_8_9_X) || VersionsHolder.ALL_VERSIONS_BY_ID.containsKey(id);
}
/**
@@ -116,7 +208,7 @@ public TransportVersion bestKnownVersion() {
return this;
}
TransportVersion bestSoFar = TransportVersions.ZERO;
- for (final var knownVersion : TransportVersions.VERSION_IDS.values()) {
+ for (final var knownVersion : VersionsHolder.ALL_VERSIONS_BY_ID.values()) {
if (knownVersion.after(bestSoFar) && knownVersion.before(this)) {
bestSoFar = knownVersion;
}
@@ -152,12 +244,75 @@ public boolean isPatchFrom(TransportVersion version) {
return onOrAfter(version) && id < version.id + 100 - (version.id % 100);
}
+ /**
+ * Supports is used to determine if a named transport version is supported
+ * by a caller transport version. This will check both the latest id
+ * and all of its patch ids for compatibility. This replaces the pattern
+ * of {@code wireTV.onOrAfter(TV_FEATURE) || wireTV.isPatchFrom(TV_FEATURE_BACKPORT) || ...}
+ * for unnamed transport versions with {@code wireTV.supports(TV_FEATURE)} for named
+ * transport versions (since named versions know about their own patch versions).
+ *
+ * The recommended use of this method is to declare a static final {@link TransportVersion}
+ * as part of the file that it's used in. This constant is then used in conjunction with
+ * this method to check transport version compatability.
+ *
+ * An example:
+ * {@code
+ * public class ExampleClass {
+ * ...
+ * TransportVersion TV_FEATURE = TransportVersion.fromName("tv_feature");
+ * ...
+ * public static ExampleClass readFrom(InputStream in) {
+ * ...
+ * if (in.getTransportVersion().supports(TV_FEATURE) {
+ * // read newer values
+ * }
+ * ...
+ * }
+ * ...
+ * public void writeTo(OutputStream out) {
+ * ...
+ * if (out.getTransportVersion().supports(TV_FEATURE) {
+ * // write newer values
+ * }
+ * ...
+ * }
+ * ...
+ * }
+ * }
+ */
+ public boolean supports(TransportVersion version) {
+ if (onOrAfter(version)) {
+ return true;
+ }
+ TransportVersion nextPatchVersion = version.nextPatchVersion;
+ while (nextPatchVersion != null) {
+ if (isPatchFrom(nextPatchVersion)) {
+ return true;
+ }
+ nextPatchVersion = nextPatchVersion.nextPatchVersion;
+ }
+ return false;
+ }
+
/**
* Returns a string representing the Elasticsearch release version of this transport version,
* if applicable for this deployment, otherwise the raw version number.
*/
public String toReleaseVersion() {
- return TransportVersions.VERSION_LOOKUP.apply(id);
+ return VersionsHolder.VERSION_LOOKUP_BY_RELEASE.apply(id);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) return false;
+ TransportVersion that = (TransportVersion) o;
+ return id == that.id;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(id);
}
@Override
@@ -165,16 +320,106 @@ public String toString() {
return Integer.toString(id);
}
- private static class CurrentHolder {
- private static final TransportVersion CURRENT = findCurrent();
+ /**
+ * This class holds various data structures for looking up known transport versions both
+ * named and unnamed. While we transition to named transport versions, this class will
+ * load and merge unnamed transport versions from {@link TransportVersions} along with
+ * named transport versions specified in a manifest file in resources.
+ */
+ private static class VersionsHolder {
+
+ private static final List ALL_VERSIONS;
+ private static final Map ALL_VERSIONS_BY_ID;
+ private static final Map ALL_VERSIONS_BY_NAME;
+ private static final IntFunction VERSION_LOOKUP_BY_RELEASE;
+ private static final TransportVersion CURRENT;
- // finds the pluggable current version
- private static TransportVersion findCurrent() {
- var version = ExtensionLoader.loadSingleton(ServiceLoader.load(VersionExtension.class))
- .map(e -> e.getCurrentTransportVersion(TransportVersions.LATEST_DEFINED))
- .orElse(TransportVersions.LATEST_DEFINED);
- assert version.onOrAfter(TransportVersions.LATEST_DEFINED);
- return version;
+ static {
+ // collect all the transport versions from server and es modules/plugins (defined in server)
+ List allVersions = new ArrayList<>(TransportVersions.DEFINED_VERSIONS);
+ Map allVersionsByName = loadTransportVersionsByName();
+ addTransportVersions(allVersionsByName.values(), allVersions).sort(TransportVersion::compareTo);
+
+ // set the transport version lookups
+ ALL_VERSIONS = Collections.unmodifiableList(allVersions);
+ ALL_VERSIONS_BY_ID = ALL_VERSIONS.stream().collect(Collectors.toUnmodifiableMap(TransportVersion::id, Function.identity()));
+ ALL_VERSIONS_BY_NAME = Collections.unmodifiableMap(allVersionsByName);
+ VERSION_LOOKUP_BY_RELEASE = ReleaseVersions.generateVersionsLookup(
+ TransportVersions.class,
+ allVersions.get(allVersions.size() - 1).id()
+ );
+ CURRENT = ALL_VERSIONS.get(ALL_VERSIONS.size() - 1);
+ }
+
+ private static Map loadTransportVersionsByName() {
+ Map transportVersions = new HashMap<>();
+
+ String latestLocation = "/transport/latest/" + Version.CURRENT.major + "." + Version.CURRENT.minor + ".csv";
+ int latestId = -1;
+ try (InputStream inputStream = TransportVersion.class.getResourceAsStream(latestLocation)) {
+ // this check is required until bootstrapping for the new transport versions format is completed;
+ // when load is false, we will only use the transport versions in the legacy format;
+ // load becomes false if we don't find the latest or manifest files required for the new format
+ if (inputStream != null) {
+ TransportVersion latest = fromInputStream(latestLocation, true, inputStream, Integer.MAX_VALUE);
+ if (latest == null) {
+ throw new IllegalStateException(
+ "invalid latest transport version for minor version ["
+ + Version.CURRENT.major
+ + "."
+ + Version.CURRENT.minor
+ + "]"
+ );
+ }
+ latestId = latest.id();
+ }
+ } catch (IOException ioe) {
+ throw new UncheckedIOException("latest transport version file not found at [" + latestLocation + "]", ioe);
+ }
+
+ String manifestLocation = "/transport/constant/manifest.txt";
+ List versionFileNames = null;
+ if (latestId > -1) {
+ try (InputStream inputStream = TransportVersion.class.getResourceAsStream(manifestLocation)) {
+ if (inputStream != null) {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ versionFileNames = reader.lines().filter(line -> line.isBlank() == false).toList();
+ }
+ } catch (IOException ioe) {
+ throw new UncheckedIOException("transport version manifest file not found at [" + manifestLocation + "]", ioe);
+ }
+ }
+
+ if (versionFileNames != null) {
+ for (String name : versionFileNames) {
+ String versionLocation = "/transport/constant/" + name;
+ try (InputStream inputStream = TransportVersion.class.getResourceAsStream(versionLocation)) {
+ if (inputStream == null) {
+ throw new IllegalStateException("transport version file not found at [" + versionLocation + "]");
+ }
+ TransportVersion transportVersion = TransportVersion.fromInputStream(versionLocation, false, inputStream, latestId);
+ if (transportVersion != null) {
+ transportVersions.put(transportVersion.name(), transportVersion);
+ }
+ } catch (IOException ioe) {
+ throw new UncheckedIOException("transport version file not found at [ " + versionLocation + "]", ioe);
+ }
+ }
+ }
+
+ return transportVersions;
+ }
+
+ private static List addTransportVersions(Collection addFrom, List addTo) {
+ for (TransportVersion transportVersion : addFrom) {
+ addTo.add(transportVersion);
+ TransportVersion patchVersion = transportVersion.nextPatchVersion();
+ while (patchVersion != null) {
+ addTo.add(patchVersion);
+ patchVersion = patchVersion.nextPatchVersion();
+ }
+ }
+ return addTo;
}
}
}
diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java
index 6fb74ed0ed109..8e942d634b61a 100644
--- a/server/src/main/java/org/elasticsearch/TransportVersions.java
+++ b/server/src/main/java/org/elasticsearch/TransportVersions.java
@@ -12,15 +12,13 @@
import org.elasticsearch.core.Assertions;
import java.lang.reflect.Field;
-import java.util.Collection;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import java.util.NavigableMap;
import java.util.Set;
-import java.util.TreeMap;
import java.util.TreeSet;
-import java.util.function.IntFunction;
/**
* Transport version is used to coordinate compatible wire protocol communication between nodes, at a fine-grained level. This replaces
@@ -325,21 +323,14 @@ static TransportVersion def(int id) {
*/
public static final TransportVersion MINIMUM_CCS_VERSION = RETRY_ILM_ASYNC_ACTION_REQUIRE_ERROR_8_18;
- static final NavigableMap VERSION_IDS = getAllVersionIds(TransportVersions.class);
-
- // the highest transport version constant defined in this file, used as a fallback for TransportVersion.current()
- static final TransportVersion LATEST_DEFINED;
- static {
- LATEST_DEFINED = VERSION_IDS.lastEntry().getValue();
-
- // see comment on IDS field
- // now we're registered all the transport versions, we can clear the map
- IDS = null;
- }
+ /**
+ * Sorted list of all versions defined in this class
+ */
+ static final List DEFINED_VERSIONS = collectAllVersionIdsDefinedInClass(TransportVersions.class);
- public static NavigableMap getAllVersionIds(Class> cls) {
+ public static List collectAllVersionIdsDefinedInClass(Class> cls) {
Map versionIdFields = new HashMap<>();
- NavigableMap builder = new TreeMap<>();
+ List definedTransportVersions = new ArrayList<>();
Set ignore = Set.of("ZERO", "CURRENT", "MINIMUM_COMPATIBLE", "MINIMUM_CCS_VERSION");
@@ -356,7 +347,7 @@ public static NavigableMap getAllVersionIds(Class>
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
- builder.put(version.id(), version);
+ definedTransportVersions.add(version);
if (Assertions.ENABLED) {
// check the version number is unique
@@ -373,15 +364,11 @@ public static NavigableMap getAllVersionIds(Class>
}
}
- return Collections.unmodifiableNavigableMap(builder);
- }
+ Collections.sort(definedTransportVersions);
- static Collection getAllVersions() {
- return VERSION_IDS.values();
+ return List.copyOf(definedTransportVersions);
}
- static final IntFunction VERSION_LOOKUP = ReleaseVersions.generateVersionsLookup(TransportVersions.class, LATEST_DEFINED.id());
-
// no instance
private TransportVersions() {}
}
diff --git a/server/src/main/resources/transport/latest/8.18.csv b/server/src/main/resources/transport/latest/8.18.csv
new file mode 100644
index 0000000000000..987d72e2aaeae
--- /dev/null
+++ b/server/src/main/resources/transport/latest/8.18.csv
@@ -0,0 +1 @@
+placeholder,8840007
diff --git a/server/src/main/resources/transport/latest/8.19.csv b/server/src/main/resources/transport/latest/8.19.csv
new file mode 100644
index 0000000000000..2480f207cc6e4
--- /dev/null
+++ b/server/src/main/resources/transport/latest/8.19.csv
@@ -0,0 +1 @@
+placeholder,8841064
diff --git a/server/src/main/resources/transport/latest/9.0.csv b/server/src/main/resources/transport/latest/9.0.csv
new file mode 100644
index 0000000000000..478f07788af87
--- /dev/null
+++ b/server/src/main/resources/transport/latest/9.0.csv
@@ -0,0 +1 @@
+placeholder,9000014
diff --git a/server/src/main/resources/transport/latest/9.1.csv b/server/src/main/resources/transport/latest/9.1.csv
new file mode 100644
index 0000000000000..21304ce07f713
--- /dev/null
+++ b/server/src/main/resources/transport/latest/9.1.csv
@@ -0,0 +1 @@
+placeholder,9112003
diff --git a/server/src/main/resources/transport/latest/9.2.csv b/server/src/main/resources/transport/latest/9.2.csv
new file mode 100644
index 0000000000000..5db8e8fb48f39
--- /dev/null
+++ b/server/src/main/resources/transport/latest/9.2.csv
@@ -0,0 +1 @@
+placeholder,9130000
diff --git a/server/src/test/java/org/elasticsearch/TransportVersionTests.java b/server/src/test/java/org/elasticsearch/TransportVersionTests.java
index 74d2c07af34bb..ee2aa713181c9 100644
--- a/server/src/test/java/org/elasticsearch/TransportVersionTests.java
+++ b/server/src/test/java/org/elasticsearch/TransportVersionTests.java
@@ -15,15 +15,14 @@
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
-import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThan;
@@ -37,7 +36,7 @@ public class TransportVersionTests extends ESTestCase {
* If the test fails, there is something wrong with your backport PR.
*/
public void testMaximumAllowedTransportVersion() {
- assertThat(TransportVersions.LATEST_DEFINED.isPatchFrom(TransportVersions.INITIAL_ELASTICSEARCH_8_19), is(true));
+ assertThat(TransportVersion.current().isPatchFrom(TransportVersions.INITIAL_ELASTICSEARCH_8_19), is(true));
}
public void testVersionComparison() {
@@ -79,21 +78,18 @@ public static class DuplicatedIdFakeVersion {
public void testStaticTransportVersionChecks() {
assertThat(
- TransportVersions.getAllVersionIds(CorrectFakeVersion.class),
- equalTo(
- Map.of(
- 199,
- CorrectFakeVersion.V_0_00_01,
- 2,
- CorrectFakeVersion.V_0_000_002,
- 3,
- CorrectFakeVersion.V_0_000_003,
- 4,
- CorrectFakeVersion.V_0_000_004
- )
+ TransportVersions.collectAllVersionIdsDefinedInClass(CorrectFakeVersion.class),
+ contains(
+ CorrectFakeVersion.V_0_000_002,
+ CorrectFakeVersion.V_0_000_003,
+ CorrectFakeVersion.V_0_000_004,
+ CorrectFakeVersion.V_0_00_01
)
);
- AssertionError e = expectThrows(AssertionError.class, () -> TransportVersions.getAllVersionIds(DuplicatedIdFakeVersion.class));
+ AssertionError e = expectThrows(
+ AssertionError.class,
+ () -> TransportVersions.collectAllVersionIdsDefinedInClass(DuplicatedIdFakeVersion.class)
+ );
assertThat(e.getMessage(), containsString("have the same version number"));
}
@@ -196,7 +192,7 @@ public void testVersionConstantPresent() {
}
public void testCURRENTIsLatest() {
- assertThat(Collections.max(TransportVersions.getAllVersions()), is(TransportVersion.current()));
+ assertThat(Collections.max(TransportVersion.getAllVersions()), is(TransportVersion.current()));
}
public void testPatchVersionsStillAvailable() {
@@ -233,7 +229,7 @@ public void testToString() {
public void testDenseTransportVersions() {
Set missingVersions = new TreeSet<>();
TransportVersion previous = null;
- for (var tv : TransportVersions.getAllVersions()) {
+ for (var tv : TransportVersion.getAllVersions()) {
if (tv.before(TransportVersions.V_8_16_0)) {
continue;
}
@@ -261,7 +257,7 @@ public void testDenseTransportVersions() {
}
public void testDuplicateConstants() {
- List tvs = TransportVersions.getAllVersions().stream().sorted().toList();
+ List tvs = TransportVersion.getAllVersions().stream().sorted().toList();
TransportVersion previous = tvs.get(0);
for (int i = 1; i < tvs.size(); i++) {
TransportVersion next = tvs.get(i);
@@ -271,4 +267,112 @@ public void testDuplicateConstants() {
previous = next;
}
}
+
+ public void testFromName() {
+ assertThat(TransportVersion.fromName("test_0"), is(new TransportVersion("test_0", 3001000, null)));
+ assertThat(TransportVersion.fromName("test_1"), is(new TransportVersion("test_1", 3002000, null)));
+ assertThat(
+ TransportVersion.fromName("test_2"),
+ is(
+ new TransportVersion(
+ "test_2",
+ 3003000,
+ new TransportVersion("test_2", 2001001, new TransportVersion("test_2", 1001001, null))
+ )
+ )
+ );
+ assertThat(
+ TransportVersion.fromName("test_3"),
+ is(new TransportVersion("test_3", 3003001, new TransportVersion("test_3", 2001002, null)))
+ );
+ assertThat(
+ TransportVersion.fromName("test_4"),
+ is(
+ new TransportVersion(
+ "test_4",
+ 3003002,
+ new TransportVersion("test_4", 2001003, new TransportVersion("test_4", 1001002, null))
+ )
+ )
+ );
+ }
+
+ public void testSupports() {
+ TransportVersion test0 = TransportVersion.fromName("test_0");
+ assertThat(new TransportVersion(null, 2003000, null).supports(test0), is(false));
+ assertThat(new TransportVersion(null, 3001000, null).supports(test0), is(true));
+ assertThat(new TransportVersion(null, 100001001, null).supports(test0), is(true));
+
+ TransportVersion test1 = TransportVersion.fromName("test_1");
+ assertThat(new TransportVersion(null, 2003000, null).supports(test1), is(false));
+ assertThat(new TransportVersion(null, 3001000, null).supports(test1), is(false));
+ assertThat(new TransportVersion(null, 3001001, null).supports(test1), is(false));
+ assertThat(new TransportVersion(null, 3002000, null).supports(test1), is(true));
+ assertThat(new TransportVersion(null, 100001000, null).supports(test1), is(true));
+ assertThat(new TransportVersion(null, 100001001, null).supports(test1), is(true));
+
+ TransportVersion test2 = TransportVersion.fromName("test_2");
+ assertThat(new TransportVersion(null, 1001000, null).supports(test2), is(false));
+ assertThat(new TransportVersion(null, 1001001, null).supports(test2), is(true));
+ assertThat(new TransportVersion(null, 1001002, null).supports(test2), is(true));
+ assertThat(new TransportVersion(null, 1002000, null).supports(test2), is(false));
+ assertThat(new TransportVersion(null, 1002001, null).supports(test2), is(false));
+ assertThat(new TransportVersion(null, 2001000, null).supports(test2), is(false));
+ assertThat(new TransportVersion(null, 2001001, null).supports(test2), is(true));
+ assertThat(new TransportVersion(null, 2001002, null).supports(test2), is(true));
+ assertThat(new TransportVersion(null, 2003000, null).supports(test2), is(false));
+ assertThat(new TransportVersion(null, 2003001, null).supports(test2), is(false));
+ assertThat(new TransportVersion(null, 3001000, null).supports(test2), is(false));
+ assertThat(new TransportVersion(null, 3001001, null).supports(test2), is(false));
+ assertThat(new TransportVersion(null, 3003000, null).supports(test2), is(true));
+ assertThat(new TransportVersion(null, 3003001, null).supports(test2), is(true));
+ assertThat(new TransportVersion(null, 3003002, null).supports(test2), is(true));
+ assertThat(new TransportVersion(null, 3003003, null).supports(test2), is(true));
+ assertThat(new TransportVersion(null, 100001000, null).supports(test2), is(true));
+ assertThat(new TransportVersion(null, 100001001, null).supports(test2), is(true));
+
+ TransportVersion test3 = TransportVersion.fromName("test_3");
+ assertThat(new TransportVersion(null, 1001001, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 1001002, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 1001003, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 1002001, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 1002002, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 2001001, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 2001002, null).supports(test3), is(true));
+ assertThat(new TransportVersion(null, 2001003, null).supports(test3), is(true));
+ assertThat(new TransportVersion(null, 2003000, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 2003001, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 3001000, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 3001001, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 3003000, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 3003001, null).supports(test3), is(true));
+ assertThat(new TransportVersion(null, 3003002, null).supports(test3), is(true));
+ assertThat(new TransportVersion(null, 3003003, null).supports(test3), is(true));
+ assertThat(new TransportVersion(null, 3004000, null).supports(test3), is(true));
+ assertThat(new TransportVersion(null, 100001000, null).supports(test3), is(true));
+ assertThat(new TransportVersion(null, 100001001, null).supports(test3), is(true));
+
+ TransportVersion test4 = TransportVersion.fromName("test_4");
+ assertThat(new TransportVersion(null, 1001001, null).supports(test4), is(false));
+ assertThat(new TransportVersion(null, 1001002, null).supports(test4), is(true));
+ assertThat(new TransportVersion(null, 1001003, null).supports(test4), is(true));
+ assertThat(new TransportVersion(null, 1002001, null).supports(test4), is(false));
+ assertThat(new TransportVersion(null, 1002002, null).supports(test4), is(false));
+ assertThat(new TransportVersion(null, 1002003, null).supports(test3), is(false));
+ assertThat(new TransportVersion(null, 2001002, null).supports(test4), is(false));
+ assertThat(new TransportVersion(null, 2001003, null).supports(test4), is(true));
+ assertThat(new TransportVersion(null, 2001004, null).supports(test4), is(true));
+ assertThat(new TransportVersion(null, 2003000, null).supports(test4), is(false));
+ assertThat(new TransportVersion(null, 2003001, null).supports(test4), is(false));
+ assertThat(new TransportVersion(null, 3001000, null).supports(test4), is(false));
+ assertThat(new TransportVersion(null, 3001001, null).supports(test4), is(false));
+ assertThat(new TransportVersion(null, 3003000, null).supports(test4), is(false));
+ assertThat(new TransportVersion(null, 3003001, null).supports(test4), is(false));
+ assertThat(new TransportVersion(null, 3003002, null).supports(test4), is(true));
+ assertThat(new TransportVersion(null, 3003003, null).supports(test4), is(true));
+ assertThat(new TransportVersion(null, 3003004, null).supports(test4), is(true));
+ assertThat(new TransportVersion(null, 3004000, null).supports(test4), is(true));
+ assertThat(new TransportVersion(null, 100001000, null).supports(test4), is(true));
+ assertThat(new TransportVersion(null, 100001001, null).supports(test4), is(true));
+ }
}
diff --git a/server/src/test/resources/transport/constant/manifest.txt b/server/src/test/resources/transport/constant/manifest.txt
new file mode 100644
index 0000000000000..b158f9910edf4
--- /dev/null
+++ b/server/src/test/resources/transport/constant/manifest.txt
@@ -0,0 +1,5 @@
+test_0.csv
+test_1.csv
+test_2.csv
+test_3.csv
+test_4.csv
diff --git a/server/src/test/resources/transport/constant/test_0.csv b/server/src/test/resources/transport/constant/test_0.csv
new file mode 100644
index 0000000000000..46b80e0a7f735
--- /dev/null
+++ b/server/src/test/resources/transport/constant/test_0.csv
@@ -0,0 +1 @@
+100001000,3001000
diff --git a/server/src/test/resources/transport/constant/test_1.csv b/server/src/test/resources/transport/constant/test_1.csv
new file mode 100644
index 0000000000000..68f67c2ab7884
--- /dev/null
+++ b/server/src/test/resources/transport/constant/test_1.csv
@@ -0,0 +1,2 @@
+3002000
+
diff --git a/server/src/test/resources/transport/constant/test_2.csv b/server/src/test/resources/transport/constant/test_2.csv
new file mode 100644
index 0000000000000..5db5b13038410
--- /dev/null
+++ b/server/src/test/resources/transport/constant/test_2.csv
@@ -0,0 +1 @@
+3003000,2001001,1001001
diff --git a/server/src/test/resources/transport/constant/test_3.csv b/server/src/test/resources/transport/constant/test_3.csv
new file mode 100644
index 0000000000000..b9dd0509e1364
--- /dev/null
+++ b/server/src/test/resources/transport/constant/test_3.csv
@@ -0,0 +1 @@
+100002000,3003001,2001002
diff --git a/server/src/test/resources/transport/constant/test_4.csv b/server/src/test/resources/transport/constant/test_4.csv
new file mode 100644
index 0000000000000..55c482a68ee7f
--- /dev/null
+++ b/server/src/test/resources/transport/constant/test_4.csv
@@ -0,0 +1 @@
+100002000,3003002,2001003,1001002
diff --git a/test/framework/src/main/java/org/elasticsearch/KnownTransportVersions.java b/test/framework/src/main/java/org/elasticsearch/KnownTransportVersions.java
index a9981d6951400..24bd492fdf11c 100644
--- a/test/framework/src/main/java/org/elasticsearch/KnownTransportVersions.java
+++ b/test/framework/src/main/java/org/elasticsearch/KnownTransportVersions.java
@@ -18,5 +18,5 @@ public class KnownTransportVersions {
/**
* A sorted list of all known transport versions
*/
- public static final List ALL_VERSIONS = List.copyOf(TransportVersions.getAllVersions());
+ public static final List ALL_VERSIONS = List.copyOf(TransportVersion.getAllVersions());
}