Skip to content

Commit 7b1edda

Browse files
authored
Replace transport version utils with a build service (#133034)
Several transport version build tasks have a need to access the repository-wide transport version resources. Thus far it has been done through several utility methods. This commit moves these utility methods into an encapsulated build service that is shared across the transport version tasks. The advantage is that no paths are used, the build service encapsulates access to the resources and understands internally how to find the correct filesystem path and load it.
1 parent 70f2742 commit 7b1edda

8 files changed

+359
-278
lines changed

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

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

1212
import org.gradle.api.DefaultTask;
13-
import org.gradle.api.file.DirectoryProperty;
1413
import org.gradle.api.file.RegularFileProperty;
14+
import org.gradle.api.provider.Property;
15+
import org.gradle.api.services.ServiceReference;
1516
import org.gradle.api.tasks.InputDirectory;
17+
import org.gradle.api.tasks.Optional;
1618
import org.gradle.api.tasks.OutputFile;
19+
import org.gradle.api.tasks.PathSensitive;
20+
import org.gradle.api.tasks.PathSensitivity;
1721
import org.gradle.api.tasks.TaskAction;
1822

1923
import java.io.IOException;
@@ -24,15 +28,24 @@
2428
import java.nio.file.attribute.BasicFileAttributes;
2529

2630
public abstract class GenerateTransportVersionManifestTask extends DefaultTask {
31+
32+
@ServiceReference("transportVersionResources")
33+
abstract Property<TransportVersionResourcesService> getTransportResources();
34+
2735
@InputDirectory
28-
public abstract DirectoryProperty getDefinitionsDirectory();
36+
@Optional
37+
@PathSensitive(PathSensitivity.RELATIVE)
38+
public Path getDefinitionsDirectory() {
39+
return getTransportResources().get().getDefinitionsDir();
40+
}
2941

3042
@OutputFile
3143
public abstract RegularFileProperty getManifestFile();
3244

3345
@TaskAction
3446
public void generateTransportVersionManifest() throws IOException {
35-
Path definitionsDir = getDefinitionsDirectory().get().getAsFile().toPath();
47+
48+
Path definitionsDir = getDefinitionsDirectory();
3649
Path manifestFile = getManifestFile().get().getAsFile().toPath();
3750
try (var writer = Files.newBufferedWriter(manifestFile)) {
3851
Files.walkFileTree(definitionsDir, new SimpleFileVisitor<>() {

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@
1212
import org.gradle.api.attributes.Attribute;
1313
import org.gradle.api.attributes.AttributeContainer;
1414

15+
import java.io.File;
1516
import java.io.IOException;
1617
import java.nio.charset.StandardCharsets;
1718
import java.nio.file.Files;
1819
import java.nio.file.Path;
1920
import java.util.ArrayList;
21+
import java.util.HashSet;
2022
import java.util.List;
23+
import java.util.Set;
2124

2225
import static org.gradle.api.artifacts.type.ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE;
2326

@@ -43,6 +46,14 @@ static void addArtifactAttribute(AttributeContainer attributes) {
4346
attributes.attribute(REFERENCES_ATTRIBUTE, true);
4447
}
4548

49+
static Set<String> collectNames(Iterable<File> referencesFiles) throws IOException {
50+
Set<String> names = new HashSet<>();
51+
for (var referencesFile : referencesFiles) {
52+
listFromFile(referencesFile.toPath()).stream().map(TransportVersionReference::name).forEach(names::add);
53+
}
54+
return names;
55+
}
56+
4657
@Override
4758
public String toString() {
4859
return name + "," + location;

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,9 @@
1313
import org.gradle.api.Plugin;
1414
import org.gradle.api.Project;
1515
import org.gradle.api.artifacts.Configuration;
16-
import org.gradle.api.file.Directory;
1716
import org.gradle.api.tasks.SourceSet;
1817
import org.gradle.language.base.plugins.LifecycleBasePlugin;
1918

20-
import static org.elasticsearch.gradle.internal.transport.TransportVersionUtils.getDefinitionsDirectory;
21-
import static org.elasticsearch.gradle.internal.transport.TransportVersionUtils.getResourcesDirectory;
22-
2319
public class TransportVersionReferencesPlugin implements Plugin<Project> {
2420

2521
@Override
@@ -46,10 +42,6 @@ public void apply(Project project) {
4642
.register("validateTransportVersionReferences", ValidateTransportVersionReferencesTask.class, t -> {
4743
t.setGroup("Transport Versions");
4844
t.setDescription("Validates that all TransportVersion references used in the project have an associated definition file");
49-
Directory definitionsDir = getDefinitionsDirectory(getResourcesDirectory(project));
50-
if (definitionsDir.getAsFile().exists()) {
51-
t.getDefinitionsDirectory().set(definitionsDir);
52-
}
5345
t.getReferencesFile().set(collectTask.get().getOutputFile());
5446
});
5547
project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME).configure(t -> t.dependsOn(validateTask));

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,22 @@
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-
2623
public class TransportVersionResourcesPlugin implements Plugin<Project> {
2724

2825
@Override
2926
public void apply(Project project) {
3027
project.getPluginManager().apply(LifecycleBasePlugin.class);
3128

29+
String resourceRoot = getResourceRoot(project);
30+
31+
project.getGradle()
32+
.getSharedServices()
33+
.registerIfAbsent("transportVersionResources", TransportVersionResourcesService.class, spec -> {
34+
Directory transportResources = project.getLayout().getProjectDirectory().dir("src/main/resources/" + resourceRoot);
35+
spec.getParameters().getTransportResourcesDirectory().set(transportResources);
36+
spec.getParameters().getRootDirectory().set(project.getRootProject().getRootDir());
37+
});
38+
3239
DependencyHandler depsHandler = project.getDependencies();
3340
Configuration tvReferencesConfig = project.getConfigurations().create("globalTvReferences");
3441
tvReferencesConfig.setCanBeConsumed(false);
@@ -46,10 +53,6 @@ public void apply(Project project) {
4653
.register("validateTransportVersionDefinitions", ValidateTransportVersionResourcesTask.class, t -> {
4754
t.setGroup("Transport Versions");
4855
t.setDescription("Validates that all defined TransportVersion constants are used in at least one project");
49-
Directory resourcesDir = getResourcesDirectory(project);
50-
if (resourcesDir.getAsFile().exists()) {
51-
t.getResourcesDirectory().set(resourcesDir);
52-
}
5356
t.getReferencesFiles().setFrom(tvReferencesConfig);
5457
});
5558
project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME).configure(t -> t.dependsOn(validateTask));
@@ -58,12 +61,18 @@ public void apply(Project project) {
5861
.register("generateTransportVersionManifest", GenerateTransportVersionManifestTask.class, t -> {
5962
t.setGroup("Transport Versions");
6063
t.setDescription("Generate a manifest resource for all the known transport version definitions");
61-
t.getDefinitionsDirectory().set(getDefinitionsDirectory(getResourcesDirectory(project)));
6264
t.getManifestFile().set(project.getLayout().getBuildDirectory().file("generated-resources/manifest.txt"));
6365
});
64-
String resourceRoot = TransportVersionUtils.getResourceRoot(project);
6566
project.getTasks().named(JavaPlugin.PROCESS_RESOURCES_TASK_NAME, Copy.class).configure(t -> {
6667
t.into(resourceRoot + "/definitions", c -> c.from(generateManifestTask));
6768
});
6869
}
70+
71+
private static String getResourceRoot(Project project) {
72+
var resourceRoot = project.findProperty("org.elasticsearch.transport.resourceRoot");
73+
if (resourceRoot == null) {
74+
resourceRoot = "transport";
75+
}
76+
return resourceRoot.toString();
77+
}
6978
}
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
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 org.gradle.api.file.DirectoryProperty;
13+
import org.gradle.api.services.BuildService;
14+
import org.gradle.api.services.BuildServiceParameters;
15+
import org.gradle.process.ExecOperations;
16+
import org.gradle.process.ExecResult;
17+
18+
import java.io.ByteArrayOutputStream;
19+
import java.io.IOException;
20+
import java.nio.charset.StandardCharsets;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.util.ArrayList;
24+
import java.util.Collections;
25+
import java.util.HashMap;
26+
import java.util.HashSet;
27+
import java.util.List;
28+
import java.util.Map;
29+
import java.util.Set;
30+
import java.util.concurrent.atomic.AtomicReference;
31+
import java.util.function.BiFunction;
32+
33+
import javax.inject.Inject;
34+
35+
/**
36+
* An encapsulation of operations on transport version resources.
37+
*
38+
* <p>These are resource files to describe transport versions that will be loaded at Elasticsearch runtime. They exist
39+
* as jar resource files at runtime, and as a directory of resources at build time.
40+
*
41+
* <p>The layout of the transport version resources are as follows:
42+
* <ul>
43+
* <li><b>/transport/definitions/named/</b>
44+
* - Definitions that can be looked up by name. The name is the filename before the .csv suffix.</li>
45+
* <li><b>/transport/definitions/unreferenced/</b>
46+
* - Definitions which contain ids that are known at runtime, but cannot be looked up by name.</li>
47+
* <li><b>/transport/latest/</b>
48+
* - The latest transport version definition for each release branch.</li>
49+
* </ul>
50+
*/
51+
public abstract class TransportVersionResourcesService implements BuildService<TransportVersionResourcesService.Parameters> {
52+
53+
public interface Parameters extends BuildServiceParameters {
54+
DirectoryProperty getTransportResourcesDirectory();
55+
56+
DirectoryProperty getRootDirectory();
57+
}
58+
59+
@Inject
60+
public abstract ExecOperations getExecOperations();
61+
62+
private static final Path DEFINITIONS_DIR = Path.of("definitions");
63+
private static final Path NAMED_DIR = DEFINITIONS_DIR.resolve("named");
64+
private static final Path UNREFERENCED_DIR = DEFINITIONS_DIR.resolve("unreferenced");
65+
private static final Path LATEST_DIR = Path.of("latest");
66+
67+
private final Path transportResourcesDir;
68+
private final Path rootDir;
69+
private final AtomicReference<Set<String>> mainResources = new AtomicReference<>(null);
70+
private final AtomicReference<Set<String>> changedResources = new AtomicReference<>(null);
71+
72+
@Inject
73+
public TransportVersionResourcesService(Parameters params) {
74+
this.transportResourcesDir = params.getTransportResourcesDirectory().get().getAsFile().toPath();
75+
this.rootDir = params.getRootDirectory().get().getAsFile().toPath();
76+
}
77+
78+
/**
79+
* Return the directory for this repository which contains transport version resources.
80+
* This should be an input to any tasks reading resources from this service.
81+
*/
82+
Path getTransportResourcesDir() {
83+
return transportResourcesDir;
84+
}
85+
86+
/**
87+
* Return the transport version definitions directory for this repository.
88+
* This should be an input to any tasks that only read definitions from this service.
89+
*/
90+
Path getDefinitionsDir() {
91+
return transportResourcesDir.resolve(DEFINITIONS_DIR);
92+
}
93+
94+
// return the path, relative to the resources dir, of a named definition
95+
private Path getNamedDefinitionRelativePath(String name) {
96+
return NAMED_DIR.resolve(name + ".csv");
97+
}
98+
99+
/** Return all named definitions, mapped by their name. */
100+
Map<String, TransportVersionDefinition> getNamedDefinitions() throws IOException {
101+
Map<String, TransportVersionDefinition> definitions = new HashMap<>();
102+
// temporarily include unreferenced in named until validation understands the distinction
103+
for (var dir : List.of(NAMED_DIR, UNREFERENCED_DIR)) {
104+
Path path = transportResourcesDir.resolve(dir);
105+
if (Files.isDirectory(path) == false) {
106+
continue;
107+
}
108+
try (var definitionsStream = Files.list(path)) {
109+
for (var definitionFile : definitionsStream.toList()) {
110+
String contents = Files.readString(definitionFile, StandardCharsets.UTF_8).strip();
111+
var definition = TransportVersionDefinition.fromString(definitionFile.getFileName().toString(), contents);
112+
definitions.put(definition.name(), definition);
113+
}
114+
}
115+
}
116+
return definitions;
117+
}
118+
119+
/** Test whether the given named definition exists */
120+
TransportVersionDefinition getNamedDefinitionFromMain(String name) {
121+
String resourcePath = getNamedDefinitionRelativePath(name).toString();
122+
return getMainFile(resourcePath, TransportVersionDefinition::fromString);
123+
}
124+
125+
/** Test whether the given named definition exists */
126+
boolean namedDefinitionExists(String name) {
127+
return Files.exists(transportResourcesDir.resolve(getNamedDefinitionRelativePath(name)));
128+
}
129+
130+
/** Return the path within the repository of the given named definition */
131+
Path getRepositoryPath(TransportVersionDefinition definition) {
132+
return rootDir.relativize(transportResourcesDir.resolve(getNamedDefinitionRelativePath(definition.name())));
133+
}
134+
135+
/** Read all latest files and return them mapped by their release branch */
136+
Map<String, TransportVersionLatest> getLatestByReleaseBranch() throws IOException {
137+
Map<String, TransportVersionLatest> latests = new HashMap<>();
138+
try (var stream = Files.list(transportResourcesDir.resolve(LATEST_DIR))) {
139+
for (var latestFile : stream.toList()) {
140+
String contents = Files.readString(latestFile, StandardCharsets.UTF_8).strip();
141+
var latest = TransportVersionLatest.fromString(latestFile.getFileName().toString(), contents);
142+
latests.put(latest.name(), latest);
143+
}
144+
}
145+
return latests;
146+
}
147+
148+
/** Retrieve the latest transport version for the given release branch on main */
149+
TransportVersionLatest getLatestFromMain(String releaseBranch) {
150+
String resourcePath = getLatestRelativePath(releaseBranch).toString();
151+
return getMainFile(resourcePath, TransportVersionLatest::fromString);
152+
}
153+
154+
/** Return the path within the repository of the given latest */
155+
Path getRepositoryPath(TransportVersionLatest latest) {
156+
return rootDir.relativize(transportResourcesDir.resolve(getLatestRelativePath(latest.branch())));
157+
}
158+
159+
private Path getLatestRelativePath(String releaseBranch) {
160+
return LATEST_DIR.resolve(releaseBranch + ".csv");
161+
}
162+
163+
// Return the transport version resources paths that exist in main
164+
private Set<String> getMainResources() {
165+
if (mainResources.get() == null) {
166+
synchronized (mainResources) {
167+
String output = gitCommand("ls-tree", "--name-only", "-r", "main", ".");
168+
169+
HashSet<String> resources = new HashSet<>();
170+
Collections.addAll(resources, output.split(System.lineSeparator()));
171+
mainResources.set(resources);
172+
}
173+
}
174+
return mainResources.get();
175+
}
176+
177+
// Return the transport version resources paths that have been changed relative to main
178+
private Set<String> getChangedResources() {
179+
if (changedResources.get() == null) {
180+
synchronized (changedResources) {
181+
String output = gitCommand("diff", "--name-only", "main", ".");
182+
183+
HashSet<String> resources = new HashSet<>();
184+
Collections.addAll(resources, output.split(System.lineSeparator()));
185+
changedResources.set(resources);
186+
}
187+
}
188+
return changedResources.get();
189+
}
190+
191+
// Read a transport version resource from the main branch, or return null if it doesn't exist on main
192+
private <T> T getMainFile(String resourcePath, BiFunction<String, String, T> parser) {
193+
if (getMainResources().contains(resourcePath) == false) {
194+
return null;
195+
}
196+
String content = gitCommand("show", "main:./" + resourcePath).strip();
197+
return parser.apply(resourcePath, content);
198+
}
199+
200+
// run a git command, relative to the transport version resources directory
201+
private String gitCommand(String... args) {
202+
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
203+
204+
List<String> command = new ArrayList<>();
205+
Collections.addAll(command, "git", "-C", getTransportResourcesDir().toString());
206+
Collections.addAll(command, args);
207+
208+
ExecResult result = getExecOperations().exec(spec -> {
209+
spec.setCommandLine(command);
210+
spec.setStandardOutput(stdout);
211+
spec.setErrorOutput(stdout);
212+
spec.setIgnoreExitValue(true);
213+
});
214+
215+
if (result.getExitValue() != 0) {
216+
throw new RuntimeException(
217+
"git command failed with exit code "
218+
+ result.getExitValue()
219+
+ System.lineSeparator()
220+
+ "command: "
221+
+ String.join(" ", command)
222+
+ System.lineSeparator()
223+
+ "output:"
224+
+ System.lineSeparator()
225+
+ stdout.toString(StandardCharsets.UTF_8)
226+
);
227+
}
228+
229+
return stdout.toString(StandardCharsets.UTF_8);
230+
}
231+
}

0 commit comments

Comments
 (0)