diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/AbstractVersionsTask.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/AbstractVersionsTask.java new file mode 100644 index 0000000000000..0ab3a9b917d65 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/AbstractVersionsTask.java @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.release; + +import org.gradle.api.DefaultTask; +import org.gradle.initialization.layout.BuildLayout; + +import java.nio.file.Path; + +public abstract class AbstractVersionsTask extends DefaultTask { + + static final String TRANSPORT_VERSION_TYPE = "TransportVersion"; + static final String INDEX_VERSION_TYPE = "IndexVersion"; + + static final String SERVER_MODULE_PATH = "server/src/main/java/"; + static final String TRANSPORT_VERSION_FILE_PATH = SERVER_MODULE_PATH + "org/elasticsearch/TransportVersions.java"; + static final String INDEX_VERSION_FILE_PATH = SERVER_MODULE_PATH + "org/elasticsearch/index/IndexVersions.java"; + + static final String SERVER_RESOURCES_PATH = "server/src/main/resources/"; + static final String TRANSPORT_VERSIONS_RECORD = SERVER_RESOURCES_PATH + "org/elasticsearch/TransportVersions.csv"; + static final String INDEX_VERSIONS_RECORD = SERVER_RESOURCES_PATH + "org/elasticsearch/index/IndexVersions.csv"; + + final Path rootDir; + + protected AbstractVersionsTask(BuildLayout layout) { + rootDir = layout.getRootDirectory().toPath(); + } + +} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ExtractCurrentVersionsTask.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ExtractCurrentVersionsTask.java new file mode 100644 index 0000000000000..3530d7ef9e807 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ExtractCurrentVersionsTask.java @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.release; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.expr.IntegerLiteralExpr; + +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.options.Option; +import org.gradle.initialization.layout.BuildLayout; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import javax.inject.Inject; + +public class ExtractCurrentVersionsTask extends AbstractVersionsTask { + private static final Logger LOGGER = Logging.getLogger(ExtractCurrentVersionsTask.class); + + private Path outputFile; + + @Inject + public ExtractCurrentVersionsTask(BuildLayout layout) { + super(layout); + } + + @Option(option = "output-file", description = "File to output tag information to") + public void outputFile(String file) { + this.outputFile = Path.of(file); + } + + @TaskAction + public void executeTask() throws IOException { + if (outputFile == null) { + throw new IllegalArgumentException("Output file not specified"); + } + + LOGGER.lifecycle("Extracting latest version information"); + + List output = new ArrayList<>(); + int transportVersion = readLatestVersion(rootDir.resolve(TRANSPORT_VERSION_FILE_PATH)); + LOGGER.lifecycle("Transport version: {}", transportVersion); + output.add(TRANSPORT_VERSION_TYPE + ":" + transportVersion); + + int indexVersion = readLatestVersion(rootDir.resolve(INDEX_VERSION_FILE_PATH)); + LOGGER.lifecycle("Index version: {}", indexVersion); + output.add(INDEX_VERSION_TYPE + ":" + indexVersion); + + LOGGER.lifecycle("Writing version information to {}", outputFile); + Files.write(outputFile, output, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); + } + + static class FieldIdExtractor implements Consumer { + private Integer highestVersionId; + + Integer highestVersionId() { + return highestVersionId; + } + + @Override + public void accept(FieldDeclaration fieldDeclaration) { + var ints = fieldDeclaration.findAll(IntegerLiteralExpr.class); + switch (ints.size()) { + case 0 -> { + // No ints in the field declaration, ignore + } + case 1 -> { + int id = ints.get(0).asNumber().intValue(); + if (highestVersionId != null && highestVersionId > id) { + LOGGER.warn("Version ids [{}, {}] out of order", highestVersionId, id); + } else { + highestVersionId = id; + } + } + default -> LOGGER.warn("Multiple integers found in version field declaration [{}]", fieldDeclaration); // and ignore it + } + } + } + + private static int readLatestVersion(Path javaVersionsFile) throws IOException { + CompilationUnit java = StaticJavaParser.parse(javaVersionsFile); + + FieldIdExtractor extractor = new FieldIdExtractor(); + java.walk(FieldDeclaration.class, extractor); // walks in code file order + if (extractor.highestVersionId == null) { + throw new IllegalArgumentException("No version ids found in " + javaVersionsFile); + } + return extractor.highestVersionId; + } +} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java index 6c978edd48c29..8001b82797557 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java @@ -50,6 +50,9 @@ public void apply(Project project) { project.getTasks() .register("updateVersions", UpdateVersionsTask.class, t -> project.getTasks().named("spotlessApply").get().mustRunAfter(t)); + project.getTasks().register("extractCurrentVersions", ExtractCurrentVersionsTask.class); + project.getTasks().register("tagVersions", TagVersionsTask.class); + final FileTree yamlFiles = projectDirectory.dir("docs/changelog") .getAsFileTree() .matching(new PatternSet().include("**/*.yml", "**/*.yaml")); diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/TagVersionsTask.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/TagVersionsTask.java new file mode 100644 index 0000000000000..fa11746543e82 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/TagVersionsTask.java @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.release; + +import org.elasticsearch.gradle.Version; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.options.Option; +import org.gradle.initialization.layout.BuildLayout; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +public class TagVersionsTask extends AbstractVersionsTask { + private static final Logger LOGGER = Logging.getLogger(TagVersionsTask.class); + + private Version releaseVersion; + + private Map tagVersions = Map.of(); + + @Inject + public TagVersionsTask(BuildLayout layout) { + super(layout); + } + + @Option(option = "release", description = "The release version to be tagged") + public void release(String version) { + releaseVersion = Version.fromString(version); + } + + @Option(option = "tag-version", description = "Version id to tag. Of the form :.") + public void tagVersions(List version) { + this.tagVersions = version.stream().map(l -> { + var split = l.split(":"); + if (split.length != 2) throw new IllegalArgumentException("Invalid tag format [" + l + "]"); + return split; + }).collect(Collectors.toMap(l -> l[0], l -> Integer.parseInt(l[1]))); + } + + @TaskAction + public void executeTask() throws IOException { + if (releaseVersion == null) { + throw new IllegalArgumentException("Release version not specified"); + } + if (tagVersions.isEmpty()) { + throw new IllegalArgumentException("No version tags specified"); + } + + LOGGER.lifecycle("Tagging version {} component ids", releaseVersion); + + var versions = expandV7Version(tagVersions); + + for (var v : versions.entrySet()) { + Path recordFile = switch (v.getKey()) { + case TRANSPORT_VERSION_TYPE -> rootDir.resolve(TRANSPORT_VERSIONS_RECORD); + case INDEX_VERSION_TYPE -> rootDir.resolve(INDEX_VERSIONS_RECORD); + default -> throw new IllegalArgumentException("Unknown version type " + v.getKey()); + }; + + LOGGER.lifecycle("Adding version record for {} to [{}]: [{},{}]", v.getKey(), recordFile, releaseVersion, v.getValue()); + + Path file = rootDir.resolve(recordFile); + List versionRecords = Files.readAllLines(file); + var modified = addVersionRecord(versionRecords, releaseVersion, v.getValue()); + if (modified.isPresent()) { + Files.write( + file, + modified.get(), + StandardOpenOption.CREATE, + StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING + ); + } + } + } + + /* + * V7 just extracts a single Version. If so, this version needs to be applied to transport and index versions. + */ + private static Map expandV7Version(Map tagVersions) { + Integer v7Version = tagVersions.get("Version"); + if (v7Version == null) return tagVersions; + + return Map.of(TRANSPORT_VERSION_TYPE, v7Version, INDEX_VERSION_TYPE, v7Version); + } + + private static final Pattern VERSION_LINE = Pattern.compile("(\\d+\\.\\d+\\.\\d+),(\\d+)"); + + static Optional> addVersionRecord(List versionRecordLines, Version release, int id) { + Map versions = versionRecordLines.stream().map(l -> { + Matcher m = VERSION_LINE.matcher(l); + if (m.matches() == false) throw new IllegalArgumentException(String.format("Incorrect format for line [%s]", l)); + return m; + }).collect(Collectors.toMap(m -> Version.fromString(m.group(1)), m -> Integer.parseInt(m.group(2)))); + + Integer existing = versions.putIfAbsent(release, id); + if (existing != null) { + if (existing.equals(id)) { + LOGGER.lifecycle("Version id [{}] for release [{}] already recorded", id, release); + return Optional.empty(); + } else { + throw new IllegalArgumentException( + String.format( + "Release [%s] already recorded with version id [%s], cannot update to version [%s]", + release, + existing, + id + ) + ); + } + } + + return Optional.of( + versions.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(e -> e.getKey() + "," + e.getValue()).toList() + ); + } +} diff --git a/build-tools-internal/src/test/java/org/elasticsearch/gradle/internal/release/ExtractCurrentVersionsTaskTests.java b/build-tools-internal/src/test/java/org/elasticsearch/gradle/internal/release/ExtractCurrentVersionsTaskTests.java new file mode 100644 index 0000000000000..1dd4675756f94 --- /dev/null +++ b/build-tools-internal/src/test/java/org/elasticsearch/gradle/internal/release/ExtractCurrentVersionsTaskTests.java @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.release; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.body.FieldDeclaration; + +import org.elasticsearch.gradle.internal.release.ExtractCurrentVersionsTask.FieldIdExtractor; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class ExtractCurrentVersionsTaskTests { + + @Test + public void testFieldExtractor() { + var unit = StaticJavaParser.parse(""" + public class Version { + public static final Version V_1 = def(1); + public static final Version V_2 = def(2); + public static final Version V_3 = def(3); + + // ignore fields with no or more than one int + public static final Version REF = V_3; + public static final Version COMPUTED = compute(100, 200); + }"""); + + FieldIdExtractor extractor = new FieldIdExtractor(); + unit.walk(FieldDeclaration.class, extractor); + assertThat(extractor.highestVersionId(), is(3)); + } +} diff --git a/build-tools-internal/src/test/java/org/elasticsearch/gradle/internal/release/TagVersionsTaskTests.java b/build-tools-internal/src/test/java/org/elasticsearch/gradle/internal/release/TagVersionsTaskTests.java new file mode 100644 index 0000000000000..28f92faf49b06 --- /dev/null +++ b/build-tools-internal/src/test/java/org/elasticsearch/gradle/internal/release/TagVersionsTaskTests.java @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.release; + +import org.elasticsearch.gradle.Version; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThrows; + +public class TagVersionsTaskTests { + + @Test + public void testAddLastRecord() { + List startingLines = List.of( + "8.0.0,6100", + "8.0.1,6100", + "8.1.0,6204", + "8.2.0,6234", + "8.3.0,6239", + "8.3.1,6260", + "8.4.0,6301" + ); + + var modified = TagVersionsTask.addVersionRecord(new ArrayList<>(startingLines), Version.fromString("8.4.1"), 6305); + assertThat(modified.isPresent(), is(true)); + + List expected = new ArrayList<>(startingLines); + expected.add("8.4.1,6305"); + expected.sort(Comparator.naturalOrder()); + assertThat(modified.get(), contains(expected.toArray())); + } + + @Test + public void testAddMiddleRecord() { + List startingLines = List.of( + "8.0.0,6100", + "8.0.1,6100", + "8.1.0,6204", + "8.2.0,6234", + "8.3.0,6239", + "8.3.1,6260", + "8.4.0,6301" + ); + + var modified = TagVersionsTask.addVersionRecord(new ArrayList<>(startingLines), Version.fromString("8.3.2"), 6280); + assertThat(modified.isPresent(), is(true)); + + List expected = new ArrayList<>(startingLines); + expected.add("8.3.2,6280"); + expected.sort(Comparator.naturalOrder()); + assertThat(modified.get(), contains(expected.toArray())); + } + + @Test + public void testIdempotent() { + List startingLines = List.of( + "8.0.0,6100", + "8.0.1,6100", + "8.1.0,6204", + "8.2.0,6234", + "8.3.1,6260", + "8.3.0,6239", + "8.4.0,6301" + ); + + var modified = TagVersionsTask.addVersionRecord(new ArrayList<>(startingLines), Version.fromString("8.4.0"), 6301); + assertThat(modified.isPresent(), is(false)); + } + + @Test + public void testFailsConflictingId() { + List startingLines = List.of( + "8.0.0,6100", + "8.0.1,6100", + "8.1.0,6204", + "8.2.0,6234", + "8.3.1,6260", + "8.3.0,6239", + "8.4.0,6301" + ); + + var ex = assertThrows( + IllegalArgumentException.class, + () -> TagVersionsTask.addVersionRecord(new ArrayList<>(startingLines), Version.fromString("8.4.0"), 6302) + ); + assertThat(ex.getMessage(), is("Release [8.4.0] already recorded with version id [6301], cannot update to version [6302]")); + } + + @Test + public void testFailsIncorrectFormat() { + List lines = List.of("8.0.,f4d2"); + + var ex = assertThrows( + IllegalArgumentException.class, + () -> TagVersionsTask.addVersionRecord(new ArrayList<>(lines), Version.fromString("1.0.0"), 1) + ); + assertThat(ex.getMessage(), is("Incorrect format for line [8.0.,f4d2]")); + } + + @Test + public void testFailsDuplicateVersions() { + List lines = List.of("8.0.0,100", "8.0.0,101"); + + var ex = assertThrows( + IllegalStateException.class, + () -> TagVersionsTask.addVersionRecord(new ArrayList<>(lines), Version.fromString("8.0.1"), 102) + ); + assertThat(ex.getMessage(), is("Duplicate key 8.0.0 (attempted merging values 100 and 101)")); + } +} diff --git a/docs/changelog/103627.yaml b/docs/changelog/103627.yaml new file mode 100644 index 0000000000000..4b0d9e937542e --- /dev/null +++ b/docs/changelog/103627.yaml @@ -0,0 +1,5 @@ +pr: 103627 +summary: Add gradle tasks and code to modify and access mappings between version ids and release versions +area: Infra/Core +type: feature +issues: [] diff --git a/server/src/main/java/org/elasticsearch/ReleaseVersions.java b/server/src/main/java/org/elasticsearch/ReleaseVersions.java new file mode 100644 index 0000000000000..440603cf10ae5 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/ReleaseVersions.java @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch; + +import org.elasticsearch.common.Strings; +import org.elasticsearch.internal.BuildExtension; +import org.elasticsearch.plugins.ExtensionLoader; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.NavigableMap; +import java.util.ServiceLoader; +import java.util.TreeMap; +import java.util.function.IntFunction; +import java.util.regex.Pattern; + +public class ReleaseVersions { + + private static final boolean USES_VERSIONS; + + static { + USES_VERSIONS = ExtensionLoader.loadSingleton(ServiceLoader.load(BuildExtension.class)) + .map(BuildExtension::hasReleaseVersioning) + .orElse(true); + } + + private static final Pattern VERSION_LINE = Pattern.compile("(\\d+\\.\\d+\\.\\d+),(\\d+)"); + + public static IntFunction generateVersionsLookup(Class versionContainer) { + if (USES_VERSIONS == false) return Integer::toString; + + try { + String versionsFileName = versionContainer.getSimpleName() + ".csv"; + InputStream versionsFile = versionContainer.getResourceAsStream(versionsFileName); + if (versionsFile == null) { + throw new FileNotFoundException(Strings.format("Could not find versions file for class [%s]", versionContainer)); + } + + NavigableMap> versions = new TreeMap<>(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(versionsFile, StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + var matcher = VERSION_LINE.matcher(line); + if (matcher.matches() == false) { + throw new IOException(Strings.format("Incorrect format for line [%s] in [%s]", line, versionsFileName)); + } + try { + Integer id = Integer.valueOf(matcher.group(2)); + Version version = Version.fromString(matcher.group(1)); + versions.computeIfAbsent(id, k -> new ArrayList<>()).add(version); + } catch (IllegalArgumentException e) { + // cannot happen??? regex is wrong... + assert false : "Regex allowed non-integer id or incorrect version through: " + e; + throw new IOException(Strings.format("Incorrect format for line [%s] in [%s]", line, versionsFileName), e); + } + } + } + + // replace all version lists with the smallest & greatest versions + versions.replaceAll((k, v) -> { + if (v.size() == 1) { + return List.of(v.get(0)); + } else { + v.sort(Comparator.naturalOrder()); + return List.of(v.get(0), v.get(v.size() - 1)); + } + }); + + return lookupFunction(versions); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static IntFunction lookupFunction(NavigableMap> versions) { + assert versions.values().stream().allMatch(vs -> vs.size() == 1 || vs.size() == 2) + : "Version ranges have not been properly processed: " + versions; + + return id -> { + List versionRange = versions.get(id); + + String lowerBound, upperBound; + if (versionRange != null) { + lowerBound = versionRange.get(0).toString(); + upperBound = lastItem(versionRange).toString(); + } else { + // infer the bounds from the surrounding entries + var lowerRange = versions.lowerEntry(id); + if (lowerRange != null) { + // the next version is just a guess - might be a newer revision, might be a newer minor or major... + lowerBound = nextVersion(lastItem(lowerRange.getValue())).toString(); + } else { + // we know about all preceding versions - how can this version be less than everything else we know about??? + assert false : "Could not find preceding version for id " + id; + lowerBound = "snapshot[" + id + "]"; + } + + var upperRange = versions.higherEntry(id); + if (upperRange != null) { + // too hard to guess what version this id might be for using the next version - just use it directly + upperBound = upperRange.getValue().get(0).toString(); + } else { + // likely a version created after the last release tagged version - ok + upperBound = "snapshot[" + id + "]"; + } + } + + return lowerBound.equals(upperBound) ? lowerBound : lowerBound + "-" + upperBound; + }; + } + + private static T lastItem(List list) { + return list.get(list.size() - 1); + } + + private static Version nextVersion(Version version) { + return new Version(version.id + 100); // +1 to revision + } +} diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 823ade37fe87c..03345ca0a369e 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -20,6 +20,7 @@ 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 @@ -310,6 +311,12 @@ static Collection getAllVersions() { return VERSION_IDS.values(); } + private static final IntFunction VERSION_LOOKUP = ReleaseVersions.generateVersionsLookup(TransportVersions.class); + + public static String toReleaseVersion(TransportVersion version) { + return VERSION_LOOKUP.apply(version.id()); + } + // no instance private TransportVersions() {} } diff --git a/server/src/main/java/org/elasticsearch/index/IndexVersions.java b/server/src/main/java/org/elasticsearch/index/IndexVersions.java index 4419abba73c1b..ba990d5b0c244 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexVersions.java +++ b/server/src/main/java/org/elasticsearch/index/IndexVersions.java @@ -9,6 +9,7 @@ package org.elasticsearch.index; import org.apache.lucene.util.Version; +import org.elasticsearch.ReleaseVersions; import org.elasticsearch.core.Assertions; import org.elasticsearch.core.UpdateForV9; @@ -21,6 +22,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import java.util.function.IntFunction; @SuppressWarnings("deprecation") public class IndexVersions { @@ -204,4 +206,10 @@ static NavigableMap getAllVersionIds(Class cls) { static Collection getAllVersions() { return VERSION_IDS.values(); } + + private static final IntFunction VERSION_LOOKUP = ReleaseVersions.generateVersionsLookup(IndexVersions.class); + + public static String toReleaseVersion(IndexVersion version) { + return VERSION_LOOKUP.apply(version.id()); + } } diff --git a/server/src/main/java/org/elasticsearch/internal/BuildExtension.java b/server/src/main/java/org/elasticsearch/internal/BuildExtension.java index 0d1a9880f8d32..921577317604a 100644 --- a/server/src/main/java/org/elasticsearch/internal/BuildExtension.java +++ b/server/src/main/java/org/elasticsearch/internal/BuildExtension.java @@ -19,4 +19,11 @@ public interface BuildExtension { * Returns the {@link Build} that represents the running Elasticsearch code. */ Build getCurrentBuild(); + + /** + * {@code true} if this build uses release versions. + */ + default boolean hasReleaseVersioning() { + return true; + } } diff --git a/server/src/main/resources/org/elasticsearch/TransportVersions.csv b/server/src/main/resources/org/elasticsearch/TransportVersions.csv new file mode 100644 index 0000000000000..0af8fa5641297 --- /dev/null +++ b/server/src/main/resources/org/elasticsearch/TransportVersions.csv @@ -0,0 +1,52 @@ +7.0.0,7000099 +7.0.1,7000199 +7.1.0,7010099 +7.2.0,7020099 +7.2.1,7020199 +7.3.0,7030099 +7.3.2,7030299 +7.4.0,7040099 +7.5.0,7050099 +7.6.0,7060099 +7.7.0,7070099 +7.8.0,7080099 +7.8.1,7080199 +7.9.0,7090099 +7.10.0,7100099 +7.10.1,7100199 +7.11.0,7110099 +7.12.0,7120099 +7.13.0,7130099 +7.14.0,7140099 +7.15.0,7150099 +7.15.1,7150199 +7.16.0,7160099 +7.17.0,7170099 +7.17.1,7170199 +7.17.8,7170899 +8.0.0,8000099 +8.1.0,8010099 +8.2.0,8020099 +8.3.0,8030099 +8.4.0,8040099 +8.5.0,8050099 +8.6.0,8060099 +8.6.1,8060199 +8.7.0,8070099 +8.7.1,8070199 +8.8.0,8080099 +8.8.1,8080199 +8.9.0,8500020 +8.9.1,8500020 +8.9.2,8500020 +8.10.0,8500061 +8.10.1,8500061 +8.10.2,8500061 +8.10.3,8500061 +8.10.4,8500061 +8.11.0,8512001 +8.11.1,8512001 +8.11.2,8512001 +8.11.3,8512001 +8.11.4,8512001 +8.12.0,8560000 diff --git a/server/src/main/resources/org/elasticsearch/index/IndexVersions.csv b/server/src/main/resources/org/elasticsearch/index/IndexVersions.csv new file mode 100644 index 0000000000000..c3a09e141f2d5 --- /dev/null +++ b/server/src/main/resources/org/elasticsearch/index/IndexVersions.csv @@ -0,0 +1,53 @@ +7.0.0,7000099 +7.0.1,7000199 +7.1.0,7010099 +7.2.0,7020099 +7.2.1,7020199 +7.3.0,7030099 +7.3.2,7030299 +7.4.0,7040099 +7.5.0,7050099 +7.6.0,7060099 +7.7.0,7070099 +7.8.0,7080099 +7.8.1,7080199 +7.9.0,7090099 +7.10.0,7100099 +7.10.1,7100199 +7.11.0,7110099 +7.12.0,7120099 +7.13.0,7130099 +7.14.0,7140099 +7.15.0,7150099 +7.15.1,7150199 +7.16.0,7160099 +7.17.0,7170099 +7.17.1,7170199 +7.17.8,7170899 +8.0.0,8000099 +8.1.0,8010099 +8.2.0,8020099 +8.3.0,8030099 +8.4.0,8040099 +8.5.0,8050099 +8.6.0,8060099 +8.6.1,8060199 +8.7.0,8070099 +8.7.1,8070199 +8.8.0,8080099 +8.8.1,8080199 +8.8.2,8080299 +8.9.0,8090099 +8.9.1,8090199 +8.9.2,8090299 +8.10.0,8100099 +8.10.1,8100199 +8.10.2,8100299 +8.10.3,8100399 +8.10.4,8100499 +8.11.0,8500003 +8.11.1,8500003 +8.11.2,8500003 +8.11.3,8500003 +8.11.4,8500003 +8.12.0,8500008 diff --git a/server/src/test/java/org/elasticsearch/ReleaseVersionsTests.java b/server/src/test/java/org/elasticsearch/ReleaseVersionsTests.java new file mode 100644 index 0000000000000..4ed262da07407 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/ReleaseVersionsTests.java @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch; + +import org.elasticsearch.test.ESTestCase; + +import java.util.function.IntFunction; + +import static org.hamcrest.Matchers.equalTo; + +public class ReleaseVersionsTests extends ESTestCase { + + public void testReleaseVersions() { + IntFunction versions = ReleaseVersions.generateVersionsLookup(ReleaseVersionsTests.class); + + assertThat(versions.apply(10), equalTo("8.0.0")); + assertThat(versions.apply(14), equalTo("8.1.0-8.1.1")); + assertThat(versions.apply(21), equalTo("8.2.0")); + assertThat(versions.apply(22), equalTo("8.2.1")); + } + + public void testReturnsRange() { + IntFunction versions = ReleaseVersions.generateVersionsLookup(ReleaseVersionsTests.class); + + assertThat(versions.apply(17), equalTo("8.1.2-8.2.0")); + expectThrows(AssertionError.class, () -> versions.apply(9)); + assertThat(versions.apply(24), equalTo("8.2.2-snapshot[24]")); + } +} diff --git a/server/src/test/resources/org/elasticsearch/ReleaseVersionsTests.csv b/server/src/test/resources/org/elasticsearch/ReleaseVersionsTests.csv new file mode 100644 index 0000000000000..33d58941baa44 --- /dev/null +++ b/server/src/test/resources/org/elasticsearch/ReleaseVersionsTests.csv @@ -0,0 +1,5 @@ +8.0.0,10 +8.1.0,14 +8.1.1,14 +8.2.0,21 +8.2.1,22