Skip to content

Commit 832b595

Browse files
committed
First draft
1 parent 97503ab commit 832b595

File tree

3 files changed

+204
-15
lines changed

3 files changed

+204
-15
lines changed
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.gradle.internal.transport;
11+
12+
import com.google.common.collect.Streams;
13+
import org.elasticsearch.gradle.Version;
14+
import org.elasticsearch.gradle.VersionProperties;
15+
import org.elasticsearch.gradle.internal.transport.TransportVersionUtils.TransportVersionSetData;
16+
import org.gradle.api.DefaultTask;
17+
import org.gradle.api.GradleException;
18+
import org.gradle.api.file.RegularFileProperty;
19+
import org.gradle.api.provider.Property;
20+
import org.gradle.api.tasks.Input;
21+
import org.gradle.api.tasks.InputDirectory;
22+
import org.gradle.api.tasks.TaskAction;
23+
import org.jetbrains.annotations.NotNull;
24+
25+
import java.io.IOException;
26+
import java.util.List;
27+
import java.util.Objects;
28+
import java.util.stream.Stream;
29+
30+
import static org.elasticsearch.gradle.internal.transport.TransportVersionUtils.LATEST_SUFFIX;
31+
import static org.elasticsearch.gradle.internal.transport.TransportVersionUtils.getTVSetDataFilePath;
32+
33+
/**
34+
* This task generates TransportVersionSetData data files that contain information about transport versions. These files
35+
* are added to the server project's resource directory at `server/src/main/resources/org/elasticsearch/transport/`.
36+
* They have the following format:
37+
* <pre>
38+
* Filename: my-transport-version-set.json // Must be the same as the name of the transport version set.
39+
* {
40+
* "name": "my-transport-version-set", // The name of the transport version set used for reference in the code.
41+
* "ids": [
42+
* 9109000, // The transport version introduced to the main branch.
43+
* 8841059 // The transport version backported to a previous release branch.
44+
* ]
45+
* }
46+
* </pre>
47+
*/
48+
public abstract class GenerateTransportVersionDataTask extends DefaultTask {
49+
50+
/**
51+
* Specifies the directory in which contains all TransportVersionSet data files.
52+
*
53+
* @return
54+
*/
55+
@InputDirectory
56+
public abstract RegularFileProperty getDataFileDirectory();
57+
58+
/**
59+
* Used to set the name of the TransportVersionSet for which a data file will be generated.
60+
*/
61+
@Input
62+
public abstract Property<String> getTVName();
63+
64+
/**
65+
* Used to set the `major.minor` release version for which the specific TransportVersion ID will be generated.
66+
* E.g.: "9.2", "8.18", etc.
67+
*/
68+
@Input
69+
public abstract Property<String> getMinorVersionForTV();
70+
71+
@TaskAction
72+
public void generateTransportVersionData() throws IOException {
73+
final var tvDataDir = Objects.requireNonNull(getDataFileDirectory().getAsFile().get());
74+
final var tvSetName = Objects.requireNonNull(getTVName().get());
75+
final var minorVersion = MinorVersion.fromString(Objects.requireNonNull(getMinorVersionForTV().get()));
76+
77+
// Get the latest transport version data for the specified minor version.
78+
final var latestTV = TransportVersionUtils.getLatestFile(tvDataDir.toPath(), minorVersion.toString());
79+
80+
// Create the new version
81+
final var mainReleaseVersion = MinorVersion.of(VersionProperties.getElasticsearchVersion());
82+
final var isReleaseVersionMain = minorVersion.equals(mainReleaseVersion);
83+
int newVersion = bumpVersionNumber(latestTV.ids().getFirst(), minorVersion, isReleaseVersionMain);
84+
85+
// Load the tvSetData for the specified name, if it exists
86+
final var tvSetDataFromFile = TransportVersionUtils.readDefinitionFile(tvDataDir.toPath(), tvSetName);
87+
final var tvSetFileExists = tvSetDataFromFile != null;
88+
89+
// Create/update the data file
90+
if (tvSetFileExists) {
91+
// This is not a new TVSet. We are creating a backport version for an existing TVSet.
92+
// Check to ensure that there isn't already a TV id for this release version (e.g., if this task has been run twice).
93+
var existingIDsForReleaseVersion = tvSetDataFromFile.ids().stream().filter(id -> {
94+
var priorLatestID = priorLatestTVSetData.ids().getFirst();
95+
return priorLatestID < id && id <= newVersion;
96+
}).toList();
97+
if (existingIDsForReleaseVersion.isEmpty() == false) {
98+
throw new GradleException(
99+
"A transport version could not be created because a preexisting one was found for this name & release."
100+
+ " This could be due to another pre-existing TV with the same name, or a result of running this"
101+
+ " task twice:"
102+
+ " Release version: "
103+
+ minorVersion
104+
+ " TransportVersion Id: "
105+
+ existingIDsForReleaseVersion.getFirst()
106+
+ " File: "
107+
+ getTVSetDataFilePath(tvDataDir, tvSetName)
108+
);
109+
}
110+
// 9.2:123 | 9.1:456 | 9.0:789 |
111+
112+
// Update the existing data file for the backport.
113+
new TransportVersionSetData(
114+
tvSetName,
115+
Streams.concat(tvSetDataFromFile.ids().stream(), Stream.of(newVersion)).sorted().toList().reversed()
116+
).writeToDataDir(tvDataDir);
117+
} else {
118+
// Create a new data file for the case where this is a new TV
119+
new TransportVersionSetData(tvSetName, List.of(newVersion)).writeToDataDir(tvDataDir);
120+
}
121+
122+
// Update the LATEST file.
123+
TransportVersionUtils.writeTVSetData(
124+
tvDataDir,
125+
formatLatestTVSetFilename(minorVersion),
126+
new TransportVersionSetData(tvSetName, List.of(newVersion))
127+
);
128+
}
129+
130+
// TODO Do I need to remove the patch when updating the server portion? NO, but probably need some additional checks
131+
private static int bumpVersionNumber(
132+
int tvIDToBump,
133+
MinorVersion minorVersion,
134+
boolean majorVersionBump,
135+
boolean isTVReleaseVersionMain
136+
) {
137+
138+
/* The TV format:
139+
*
140+
* MM_NNN_S_PP
141+
*
142+
* M - The major version of Elasticsearch
143+
* NNN - The server version part
144+
* S - The subsidiary version part. It should always be 0 here, it is only used in subsidiary repositories.
145+
* PP - The patch version part
146+
*/
147+
if (isTVReleaseVersionMain) {
148+
if (majorVersionBump) {
149+
// Bump the major version part, set all other parts to zero.
150+
return minorVersion.major * 1_000_000; // TODO add check that this doesn't cause overflow out of server versions
151+
} else {
152+
// Bump the server version part if not a major bump.
153+
// TODO add check that this doesn't cause overflow out of server versions
154+
// TODO Do we need to assert on the shape of the number? e.g. no patch version.
155+
return tvIDToBump + 1000;
156+
}
157+
} else {
158+
// bump the patch version part
159+
return tvIDToBump + 1; // TODO add check that this doesn't cause overflow out of patch versions
160+
}
161+
}
162+
163+
164+
private static String formatLatestTVSetFilename(MinorVersion minorVersion) {
165+
return minorVersion.toString() + LATEST_SUFFIX;
166+
}
167+
168+
private record MinorVersion(int major, int minor) {
169+
public static MinorVersion fromString(String string) {
170+
String[] versionParts = string.split("\\.");
171+
assert versionParts.length == 2;
172+
return new MinorVersion(Integer.parseInt(versionParts[0]), Integer.parseInt(versionParts[1]));
173+
}
174+
175+
public static MinorVersion of(Version version) {
176+
return new MinorVersion(version.getMajor(), version.getMinor());
177+
}
178+
179+
@Override
180+
public @NotNull String toString() {
181+
return major + "." + minor;
182+
}
183+
}
184+
}

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/transport/TransportVersionUtils.java

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
package org.elasticsearch.gradle.internal.transport;
1111

1212
import com.google.common.collect.Comparators;
13-
1413
import org.gradle.api.Project;
1514
import org.gradle.api.attributes.Attribute;
1615
import org.gradle.api.attributes.AttributeContainer;
@@ -27,10 +26,10 @@
2726
import static org.gradle.api.artifacts.type.ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE;
2827

2928
class TransportVersionUtils {
30-
3129
static final Attribute<Boolean> TRANSPORT_VERSION_REFERENCES_ATTRIBUTE = Attribute.of("transport-version-references", Boolean.class);
3230

33-
record TransportVersionConstant(String name, List<Integer> ids) {}
31+
record TransportVersionDefinition(String name, List<Integer> ids) {
32+
}
3433

3534
record TransportVersionReference(String name, String location) {
3635
@Override
@@ -39,24 +38,29 @@ public String toString() {
3938
}
4039
}
4140

42-
static TransportVersionConstant readDefinitionFile(Path file) throws IOException {
41+
static TransportVersionDefinition getLatestFile(Path latestDataDir, String majorMinor) throws IOException {
42+
return readDefinitionFile(latestDataDir.resolve(majorMinor + ".csv"), true);
43+
}
44+
45+
static TransportVersionDefinition readDefinitionFile(Path file, boolean nameInFile) throws IOException {
4346
assert file.endsWith(".csv");
4447
String rawName = file.getFileName().toString();
45-
String name = rawName.substring(0, rawName.length() - 4);
46-
List<Integer> ids = new ArrayList<>();
4748

48-
for (String rawId : Files.readString(file, StandardCharsets.UTF_8).split(",")) {
49+
String[] parts = Files.readString(file, StandardCharsets.UTF_8).split(",");
50+
String name = nameInFile ? parts[0] : rawName.substring(0, rawName.length() - 4);
51+
List<Integer> ids = new ArrayList<>();
52+
for (int i = nameInFile ? 1 : 0; i < parts.length; ++i) {
4953
try {
50-
ids.add(Integer.parseInt(rawId.strip()));
51-
} catch (NumberFormatException e) {
52-
throw new IOException("Failed to parse id " + rawId + " in " + file, e);
54+
ids.add(Integer.parseInt(parts[i]));
55+
} catch (NumberFormatException nfe) {
56+
throw new IllegalStateException("invalid transport version file format [" + file + "]", nfe);
5357
}
5458
}
5559

5660
if (Comparators.isInOrder(ids, Comparator.reverseOrder()) == false) {
5761
throw new IOException("invalid transport version data file [" + file + "], ids are not in sorted");
5862
}
59-
return new TransportVersionConstant(name, ids);
63+
return new TransportVersionDefinition(name, ids);
6064
}
6165

6266
static List<TransportVersionReference> readReferencesFile(Path file) throws IOException {
@@ -82,4 +86,5 @@ static void addTransportVersionReferencesAttribute(AttributeContainer attributes
8286
attributes.attribute(TransportVersionUtils.TRANSPORT_VERSION_REFERENCES_ATTRIBUTE, true);
8387
}
8488

89+
8590
}

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/transport/ValidateTransportVersionDefinitionsTask.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@ public abstract class ValidateTransportVersionDefinitionsTask extends DefaultTas
3939

4040
@TaskAction
4141
public void validateTransportVersions() throws IOException {
42-
Path constantsDir = getDefinitionsDirectory().getAsFile().get().toPath();
42+
Path definitionsDir = getDefinitionsDirectory().getAsFile().get().toPath();
4343

4444
Set<String> allTvNames = new HashSet<>();
4545
for (var tvReferencesFile : getReferencesFiles()) {
4646
readReferencesFile(tvReferencesFile.toPath()).stream().map(TransportVersionReference::name).forEach(allTvNames::add);
4747
}
4848

49-
try (var constantsStream = Files.list(constantsDir)) {
50-
for (var constantsFile : constantsStream.toList()) {
51-
var tv = readDefinitionFile(constantsFile);
49+
try (var definitionsStream = Files.list(definitionsDir)) {
50+
for (var constantsFile : definitionsStream.toList()) {
51+
var tv = readDefinitionFile(constantsFile, false);
5252
if (allTvNames.contains(tv.name()) == false) {
5353
throw new IllegalStateException("Transport version constant " + tv.name() + " is not referenced");
5454
}

0 commit comments

Comments
 (0)