Skip to content

Commit de9839b

Browse files
rjernstjdconrad
andauthored
Add more transport version files validation (#132373) (#132773)
This commit adds additional validation cases to the new transport version resource files. It also adds gradle tests for the validation cases. Co-authored-by: Jack Conradson <[email protected]>
1 parent 3ca8029 commit de9839b

File tree

15 files changed

+693
-50
lines changed

15 files changed

+693
-50
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
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+
13+
import org.elasticsearch.gradle.fixtures.AbstractGradleFuncTest
14+
import org.gradle.testkit.runner.BuildResult
15+
import org.gradle.testkit.runner.TaskOutcome
16+
17+
class TransportVersionManagementPluginFuncTest extends AbstractGradleFuncTest {
18+
19+
/**
20+
*
21+
* @param project
22+
* @param path
23+
* @param content
24+
* @return
25+
*/
26+
def javaResource(String project, String path, String content) {
27+
file("${project}/src/main/resources/${path}").withWriter { writer ->
28+
writer << content
29+
}
30+
}
31+
32+
def javaSource(String project, String packageName, String className, String imports, String content) {
33+
String packageSlashes = packageName.replace('.', '/')
34+
file("${project}/src/main/java/${packageSlashes}/${className}.java").withWriter { writer ->
35+
writer << """
36+
package ${packageName};
37+
${imports}
38+
public class ${className} {
39+
${content}
40+
}
41+
"""
42+
}
43+
}
44+
45+
def definedTransportVersion(String name, String ids) {
46+
javaResource("myserver", "transport/defined/" + name + ".csv", ids)
47+
}
48+
49+
def definedAndUsedTransportVersion(String name, String ids) {
50+
return definedAndUsedTransportVersion(name, ids, "Test${name.capitalize()}")
51+
}
52+
53+
def definedAndUsedTransportVersion(String name, String ids, String classname) {
54+
javaSource("myserver", "org.elasticsearch", classname, "", """
55+
static final TransportVersion usage = TransportVersion.fromName("${name}");
56+
""")
57+
definedTransportVersion(name, ids)
58+
}
59+
60+
def latestTransportVersion(String branch, String name, String id) {
61+
javaResource("myserver", "transport/latest/" + branch + ".csv","${name},${id}")
62+
}
63+
64+
def validateReferencesFails(String project) {
65+
return gradleRunner(":${project}:validateTransportVersionReferences").buildAndFail()
66+
}
67+
68+
def validateDefinitionsFails() {
69+
return gradleRunner(":myserver:validateTransportVersionDefinitions").buildAndFail()
70+
}
71+
72+
def assertReferencesFailure(BuildResult result, String project, String expectedOutput) {
73+
result.task(":${project}:validateTransportVersionReferences").outcome == TaskOutcome.FAILED
74+
assertOutputContains(result.output, expectedOutput)
75+
}
76+
77+
def assertDefinitionsFailure(BuildResult result, String expectedOutput) {
78+
result.task(":myserver:validateTransportVersionDefinitions").outcome == TaskOutcome.FAILED
79+
assertOutputContains(result.output, expectedOutput)
80+
}
81+
82+
def setup() {
83+
configurationCacheCompatible = false
84+
internalBuild()
85+
settingsFile << """
86+
include ':myserver'
87+
include ':myplugin'
88+
"""
89+
file("gradle.properties") << """
90+
org.elasticsearch.transport.definitionsProject=:myserver
91+
"""
92+
93+
file("myserver/build.gradle") << """
94+
apply plugin: 'java-library'
95+
apply plugin: 'elasticsearch.transport-version-management'
96+
apply plugin: 'elasticsearch.global-transport-version-management'
97+
"""
98+
definedTransportVersion("existing_91", "8012000")
99+
definedTransportVersion("existing_92", "8123000,8012001")
100+
latestTransportVersion("9.2", "existing_92", "8123000")
101+
latestTransportVersion("9.1", "existing_92", "8012001")
102+
// a mock version of TransportVersion, just here so we can compile Dummy.java et al
103+
javaSource("myserver", "org.elasticsearch", "TransportVersion", "", """
104+
public static TransportVersion fromName(String name) {
105+
return null;
106+
}
107+
""")
108+
javaSource("myserver", "org.elasticsearch", "Dummy", "", """
109+
static final TransportVersion existing91 = TransportVersion.fromName("existing_91");
110+
static final TransportVersion existing92 = TransportVersion.fromName("existing_92");
111+
""")
112+
113+
file("myplugin/build.gradle") << """
114+
apply plugin: 'java-library'
115+
apply plugin: 'elasticsearch.transport-version-management'
116+
117+
dependencies {
118+
implementation project(":myserver")
119+
}
120+
"""
121+
122+
setupLocalGitRepo()
123+
execute("git checkout -b main")
124+
execute("git checkout -b test")
125+
}
126+
127+
def "test setup works"() {
128+
when:
129+
def result = gradleRunner("validateTransportVersionDefinitions", "validateTransportVersionReferences").build()
130+
then:
131+
result.task(":myserver:validateTransportVersionDefinitions").outcome == TaskOutcome.SUCCESS
132+
result.task(":myserver:validateTransportVersionReferences").outcome == TaskOutcome.SUCCESS
133+
result.task(":myplugin:validateTransportVersionReferences").outcome == TaskOutcome.SUCCESS
134+
}
135+
136+
def "definitions must be referenced"() {
137+
given:
138+
javaSource("myplugin", "org.elasticsearch.plugin", "MyPlugin",
139+
"import org.elasticsearch.TransportVersion;", """
140+
static final TransportVersion dne = TransportVersion.fromName("dne");
141+
""")
142+
when:
143+
def result = validateReferencesFails("myplugin")
144+
then:
145+
assertReferencesFailure(result, "myplugin", "TransportVersion.fromName(\"dne\") was used at " +
146+
"org.elasticsearch.plugin.MyPlugin line 6, but lacks a transport version definition.")
147+
}
148+
149+
def "references must be defined"() {
150+
given:
151+
definedTransportVersion("not_used", "1000000")
152+
when:
153+
def result = validateDefinitionsFails()
154+
then:
155+
assertDefinitionsFailure(result, "Transport version definition file " +
156+
"[myserver/src/main/resources/transport/defined/not_used.csv] is not referenced")
157+
}
158+
159+
def "names must be lowercase alphanum or underscore"() {
160+
given:
161+
definedAndUsedTransportVersion("${name}", "8100000", "TestNames")
162+
when:
163+
def result = validateDefinitionsFails()
164+
then:
165+
assertDefinitionsFailure(result, "Transport version definition file " +
166+
"[myserver/src/main/resources/transport/defined/${name}.csv] does not have a valid name, " +
167+
"must be lowercase alphanumeric and underscore")
168+
169+
where:
170+
name << ["CapitalTV", "spaces tv", "trailing_spaces_tv ", "hyphen-tv", "period.tv"]
171+
}
172+
173+
def "definitions contain at least one id"() {
174+
given:
175+
definedAndUsedTransportVersion("empty", "")
176+
when:
177+
def result = validateDefinitionsFails()
178+
then:
179+
assertDefinitionsFailure(result, "Transport version definition file " +
180+
"[myserver/src/main/resources/transport/defined/empty.csv] does not contain any ids")
181+
}
182+
183+
def "definitions have ids in descending order"() {
184+
given:
185+
definedAndUsedTransportVersion("out_of_order", "8100000,8200000")
186+
when:
187+
def result = validateDefinitionsFails()
188+
then:
189+
assertDefinitionsFailure(result, "Transport version definition file " +
190+
"[myserver/src/main/resources/transport/defined/out_of_order.csv] does not have ordered ids")
191+
}
192+
193+
def "definition ids are unique"() {
194+
given:
195+
definedAndUsedTransportVersion("duplicate", "8123000")
196+
when:
197+
def result = validateDefinitionsFails()
198+
then:
199+
assertDefinitionsFailure(result, "Transport version definition file " +
200+
"[myserver/src/main/resources/transport/defined/existing_92.csv] contains id 8123000 already defined in " +
201+
"[myserver/src/main/resources/transport/defined/duplicate.csv]")
202+
}
203+
204+
def "definitions have bwc ids with non-zero patch part"() {
205+
given:
206+
definedAndUsedTransportVersion("patched", "8200000,8100000")
207+
when:
208+
def result = validateDefinitionsFails()
209+
then:
210+
assertDefinitionsFailure(result, "Transport version definition file " +
211+
"[myserver/src/main/resources/transport/defined/patched.csv] contains bwc id [8100000] with a patch part of 0")
212+
}
213+
214+
def "definitions have primary ids which cannot change"() {
215+
given:
216+
definedTransportVersion("existing_92", "8500000")
217+
when:
218+
def result = validateDefinitionsFails()
219+
then:
220+
assertDefinitionsFailure(result, "Transport version definition file " +
221+
"[myserver/src/main/resources/transport/defined/existing_92.csv] has modified primary id from 8123000 to 8500000")
222+
}
223+
224+
def "cannot change committed ids to a branch"() {
225+
given:
226+
definedTransportVersion("existing_92", "8123000,8012002")
227+
when:
228+
def result = validateDefinitionsFails()
229+
then:
230+
assertDefinitionsFailure(result, "Transport version definition file " +
231+
"[myserver/src/main/resources/transport/defined/existing_92.csv] modifies existing patch id from 8012001 to 8012002")
232+
}
233+
234+
def "latest files must reference defined name"() {
235+
given:
236+
latestTransportVersion("9.2", "dne", "8123000")
237+
when:
238+
def result = validateDefinitionsFails()
239+
then:
240+
assertDefinitionsFailure(result, "Latest transport version file " +
241+
"[myserver/src/main/resources/transport/latest/9.2.csv] contains transport version name [dne] which is not defined")
242+
}
243+
244+
def "latest files id must exist in definition"() {
245+
given:
246+
latestTransportVersion("9.2", "existing_92", "8124000")
247+
when:
248+
def result = validateDefinitionsFails()
249+
then:
250+
assertDefinitionsFailure(result, "Latest transport version file " +
251+
"[myserver/src/main/resources/transport/latest/9.2.csv] has id 8124000 which is not in definition " +
252+
"[myserver/src/main/resources/transport/defined/existing_92.csv]")
253+
}
254+
255+
def "latest files have latest id within base"() {
256+
given:
257+
latestTransportVersion("9.0", "seemingly_latest", "8110001")
258+
definedAndUsedTransportVersion("original", "8110000")
259+
definedAndUsedTransportVersion("seemingly_latest", "8111000,8110001")
260+
definedAndUsedTransportVersion("actual_latest", "8112000,8110002")
261+
when:
262+
def result = validateDefinitionsFails()
263+
then:
264+
assertDefinitionsFailure(result, "Latest transport version file " +
265+
"[myserver/src/main/resources/transport/latest/9.0.csv] has id 8110001 from [seemingly_latest] with base 8110000 " +
266+
"but another id 8110002 from [actual_latest] is later for that base")
267+
}
268+
269+
def "latest files cannot change base id"() {
270+
given:
271+
definedAndUsedTransportVersion("original", "8013000")
272+
definedAndUsedTransportVersion("patch", "8015000,8013001")
273+
latestTransportVersion("9.1", "patch", "8013001")
274+
when:
275+
def result = validateDefinitionsFails()
276+
then:
277+
assertDefinitionsFailure(result, "Latest transport version file " +
278+
"[myserver/src/main/resources/transport/latest/9.1.csv] modifies base id from 8012000 to 8013000")
279+
}
280+
281+
def "ids must be dense"() {
282+
given:
283+
definedAndUsedTransportVersion("original", "8013000")
284+
definedAndUsedTransportVersion("patch1", "8015000,8013002")
285+
latestTransportVersion("9.0", "patch1", "8013002")
286+
when:
287+
def result = validateDefinitionsFails()
288+
then:
289+
assertDefinitionsFailure(result, "Transport version base id 8013000 is missing patch ids between 8013000 and 8013002")
290+
}
291+
292+
def "primary id must not be patch version"() {
293+
given:
294+
definedAndUsedTransportVersion("patch", "8015001")
295+
when:
296+
def result = validateDefinitionsFails()
297+
then:
298+
assertDefinitionsFailure(result, "Transport version definition file " +
299+
"[myserver/src/main/resources/transport/defined/patch.csv] has patch version 8015001 as primary id")
300+
}
301+
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,15 @@ public void checkTransportVersion() throws IOException {
7474
Files.writeString(outputFile, String.join("\n", results.stream().map(Object::toString).sorted().toList()));
7575
}
7676

77-
private void addNamesFromClassesDirectory(Set<TransportVersionUtils.TransportVersionReference> results, Path file) throws IOException {
78-
Files.walkFileTree(file, new SimpleFileVisitor<>() {
77+
private void addNamesFromClassesDirectory(Set<TransportVersionUtils.TransportVersionReference> results, Path basePath)
78+
throws IOException {
79+
Files.walkFileTree(basePath, new SimpleFileVisitor<>() {
7980
@Override
8081
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
8182
String filename = file.getFileName().toString();
8283
if (filename.endsWith(CLASS_EXTENSION) && filename.endsWith(MODULE_INFO) == false) {
8384
try (var inputStream = Files.newInputStream(file)) {
84-
addNamesFromClass(results, inputStream, classname(file.toString()));
85+
addNamesFromClass(results, inputStream, classname(basePath.relativize(file).toString()));
8586
}
8687
}
8788
return FileVisitResult.CONTINUE;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ public abstract class GenerateTransportVersionManifestTask extends DefaultTask {
2929

3030
@TaskAction
3131
public void generateTransportVersionManifest() throws IOException {
32-
Path constantsDir = getDefinitionsDirectory().get().getAsFile().toPath();
32+
Path definitionsDir = getDefinitionsDirectory().get().getAsFile().toPath();
3333
Path manifestFile = getManifestFile().get().getAsFile().toPath();
3434
try (var writer = Files.newBufferedWriter(manifestFile)) {
35-
try (var stream = Files.list(constantsDir)) {
35+
try (var stream = Files.list(definitionsDir)) {
3636
for (String filename : stream.map(p -> p.getFileName().toString()).toList()) {
3737
if (filename.equals(manifestFile.getFileName().toString())) {
3838
// don't list self

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020

2121
import java.util.Map;
2222

23+
import static org.elasticsearch.gradle.internal.transport.TransportVersionUtils.getDefinitionsDirectory;
24+
import static org.elasticsearch.gradle.internal.transport.TransportVersionUtils.getResourcesDirectory;
25+
2326
public class GlobalTransportVersionManagementPlugin implements Plugin<Project> {
2427

2528
@Override
@@ -43,9 +46,9 @@ public void apply(Project project) {
4346
.register("validateTransportVersionDefinitions", ValidateTransportVersionDefinitionsTask.class, t -> {
4447
t.setGroup("Transport Versions");
4548
t.setDescription("Validates that all defined TransportVersion constants are used in at least one project");
46-
Directory definitionsDir = TransportVersionUtils.getDefinitionsDirectory(project);
47-
if (definitionsDir.getAsFile().exists()) {
48-
t.getDefinitionsDirectory().set(definitionsDir);
49+
Directory resourcesDir = getResourcesDirectory(project);
50+
if (resourcesDir.getAsFile().exists()) {
51+
t.getResourcesDirectory().set(resourcesDir);
4952
}
5053
t.getReferencesFiles().setFrom(tvReferencesConfig);
5154
});
@@ -55,7 +58,7 @@ public void apply(Project project) {
5558
.register("generateTransportVersionManifest", GenerateTransportVersionManifestTask.class, t -> {
5659
t.setGroup("Transport Versions");
5760
t.setDescription("Generate a manifest resource for all the known transport version definitions");
58-
t.getDefinitionsDirectory().set(TransportVersionUtils.getDefinitionsDirectory(project));
61+
t.getDefinitionsDirectory().set(getDefinitionsDirectory(getResourcesDirectory(project)));
5962
t.getManifestFile().set(project.getLayout().getBuildDirectory().file("generated-resources/manifest.txt"));
6063
});
6164
project.getTasks().named(JavaPlugin.PROCESS_RESOURCES_TASK_NAME, Copy.class).configure(t -> {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import org.gradle.api.tasks.SourceSet;
1818
import org.gradle.language.base.plugins.LifecycleBasePlugin;
1919

20+
import static org.elasticsearch.gradle.internal.transport.TransportVersionUtils.getDefinitionsDirectory;
21+
import static org.elasticsearch.gradle.internal.transport.TransportVersionUtils.getResourcesDirectory;
22+
2023
public class TransportVersionManagementPlugin implements Plugin<Project> {
2124

2225
@Override
@@ -43,7 +46,7 @@ public void apply(Project project) {
4346
.register("validateTransportVersionReferences", ValidateTransportVersionReferencesTask.class, t -> {
4447
t.setGroup("Transport Versions");
4548
t.setDescription("Validates that all TransportVersion references used in the project have an associated definition file");
46-
Directory definitionsDir = TransportVersionUtils.getDefinitionsDirectory(project);
49+
Directory definitionsDir = getDefinitionsDirectory(getResourcesDirectory(project));
4750
if (definitionsDir.getAsFile().exists()) {
4851
t.getDefinitionsDirectory().set(definitionsDir);
4952
}

0 commit comments

Comments
 (0)