-
Notifications
You must be signed in to change notification settings - Fork 25.5k
Replace transport version utils with a build service #133034
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
18c548d
303ba81
4523585
67effbd
f1699d8
3f3a7c1
9089ab5
a10d8e3
88c3bcd
052a93d
f371deb
896e383
5a2a931
905372d
b2bdea8
a654a33
e96297a
499bba4
4b10b7a
754696b
110f62d
61112c7
d183b39
190757e
3d141fe
0ba4a31
ca2b79d
09b6d9c
e6cec60
2743bbc
bfd07d4
2874ff0
1d151ea
e5f5101
c67a164
ac9ce1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
/* | ||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
package org.elasticsearch.gradle.internal.transport; | ||
|
||
import org.gradle.api.file.DirectoryProperty; | ||
import org.gradle.api.services.BuildService; | ||
import org.gradle.api.services.BuildServiceParameters; | ||
import org.gradle.process.ExecOperations; | ||
import org.gradle.process.ExecResult; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
import java.util.function.BiFunction; | ||
|
||
import javax.inject.Inject; | ||
|
||
/** | ||
* An encapsulation of operations on transport version resources. | ||
* | ||
* <p>These are resource files to describe transport versions that will be loaded at Elasticsearch runtime. They exist | ||
* as jar resource files at runtime, and as a directory of resources at build time. | ||
* | ||
* <p>The layout of the transport version resources are as follows: | ||
* <ul> | ||
* <li><b>/transport/definitions/named/</b> | ||
* - Definitions that can be looked up by name. The name is the filename before the .csv suffix.</li> | ||
* <li><b>/transport/definitions/unreferenced/</b> | ||
* - Definitions which contain ids that are known at runtime, but cannot be looked up by name.</li> | ||
* <li><b>/transport/latest/</b> | ||
* - The latest transport version definition for each release branch.</li> | ||
* </ul> | ||
*/ | ||
public abstract class TransportVersionResourcesService implements BuildService<TransportVersionResourcesService.Parameters> { | ||
|
||
public interface Parameters extends BuildServiceParameters { | ||
DirectoryProperty getTransportResourcesDirectory(); | ||
|
||
DirectoryProperty getRootDirectory(); | ||
} | ||
|
||
@Inject | ||
public abstract ExecOperations getExecOperations(); | ||
|
||
private static final Path DEFINITIONS_DIR = Path.of("definitions"); | ||
rjernst marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private static final Path NAMED_DIR = DEFINITIONS_DIR.resolve("named"); | ||
private static final Path UNREFERENCED_DIR = DEFINITIONS_DIR.resolve("unreferenced"); | ||
private static final Path LATEST_DIR = Path.of("latest"); | ||
|
||
private final Path transportResourcesDir; | ||
private final Path rootDir; | ||
private final AtomicReference<Set<String>> mainResources = new AtomicReference<>(null); | ||
private final AtomicReference<Set<String>> changedResources = new AtomicReference<>(null); | ||
|
||
@Inject | ||
public TransportVersionResourcesService(Parameters params) { | ||
this.transportResourcesDir = params.getTransportResourcesDirectory().get().getAsFile().toPath(); | ||
this.rootDir = params.getRootDirectory().get().getAsFile().toPath(); | ||
} | ||
|
||
/** | ||
* Return the directory for this repository which contains transport version resources. | ||
* This should be an input to any tasks reading resources from this service. | ||
*/ | ||
Path getTransportResourcesDir() { | ||
return transportResourcesDir; | ||
} | ||
|
||
/** | ||
* Return the transport version definitions directory for this repository. | ||
* This should be an input to any tasks that only read definitions from this service. | ||
*/ | ||
Path getDefinitionsDir() { | ||
return transportResourcesDir.resolve(DEFINITIONS_DIR); | ||
} | ||
|
||
// return the path, relative to the resources dir, of a named definition | ||
private Path getNamedDefinitionRelativePath(String name) { | ||
return NAMED_DIR.resolve(name + ".csv"); | ||
} | ||
|
||
/** Return all named definitions, mapped by their name. */ | ||
Map<String, TransportVersionDefinition> getNamedDefinitions() throws IOException { | ||
Map<String, TransportVersionDefinition> definitions = new HashMap<>(); | ||
// temporarily include unreferenced in named until validation understands the distinction | ||
for (var dir : List.of(NAMED_DIR, UNREFERENCED_DIR)) { | ||
Path path = transportResourcesDir.resolve(dir); | ||
if (Files.isDirectory(path) == false) { | ||
continue; | ||
} | ||
try (var definitionsStream = Files.list(path)) { | ||
for (var definitionFile : definitionsStream.toList()) { | ||
String contents = Files.readString(definitionFile, StandardCharsets.UTF_8).strip(); | ||
var definition = TransportVersionDefinition.fromString(definitionFile.getFileName().toString(), contents); | ||
definitions.put(definition.name(), definition); | ||
} | ||
} | ||
} | ||
return definitions; | ||
} | ||
|
||
/** Test whether the given named definition exists */ | ||
TransportVersionDefinition getNamedDefinitionFromMain(String name) { | ||
String resourcePath = getNamedDefinitionRelativePath(name).toString(); | ||
return getMainFile(resourcePath, TransportVersionDefinition::fromString); | ||
} | ||
|
||
/** Test whether the given named definition exists */ | ||
boolean namedDefinitionExists(String name) { | ||
return Files.exists(transportResourcesDir.resolve(getNamedDefinitionRelativePath(name))); | ||
} | ||
|
||
/** Return the path within the repository of the given named definition */ | ||
Path getRepositoryPath(TransportVersionDefinition definition) { | ||
return rootDir.relativize(transportResourcesDir.resolve(getNamedDefinitionRelativePath(definition.name()))); | ||
} | ||
|
||
/** Read all latest files and return them mapped by their release branch */ | ||
Map<String, TransportVersionLatest> getLatestByReleaseBranch() throws IOException { | ||
Map<String, TransportVersionLatest> latests = new HashMap<>(); | ||
try (var stream = Files.list(transportResourcesDir.resolve(LATEST_DIR))) { | ||
for (var latestFile : stream.toList()) { | ||
String contents = Files.readString(latestFile, StandardCharsets.UTF_8).strip(); | ||
var latest = TransportVersionLatest.fromString(latestFile.getFileName().toString(), contents); | ||
latests.put(latest.name(), latest); | ||
} | ||
} | ||
return latests; | ||
} | ||
|
||
/** Retrieve the latest transport version for the given release branch on main */ | ||
TransportVersionLatest getLatestFromMain(String releaseBranch) { | ||
String resourcePath = getLatestRelativePath(releaseBranch).toString(); | ||
return getMainFile(resourcePath, TransportVersionLatest::fromString); | ||
} | ||
|
||
/** Return the path within the repository of the given latest */ | ||
Path getRepositoryPath(TransportVersionLatest latest) { | ||
return rootDir.relativize(transportResourcesDir.resolve(getLatestRelativePath(latest.branch()))); | ||
} | ||
|
||
private Path getLatestRelativePath(String releaseBranch) { | ||
return LATEST_DIR.resolve(releaseBranch + ".csv"); | ||
} | ||
|
||
// Return the transport version resources paths that exist in main | ||
private Set<String> getMainResources() { | ||
if (mainResources.get() == null) { | ||
synchronized (mainResources) { | ||
String output = gitCommand("ls-tree", "--name-only", "-r", "main", "."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we're going to use relative paths here, then we should explicitly set the working directory when calling exec. There's no real guarantee that this is the workspace directory. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We set the working directory in |
||
|
||
HashSet<String> resources = new HashSet<>(); | ||
Collections.addAll(resources, output.split(System.lineSeparator())); | ||
mainResources.set(resources); | ||
} | ||
} | ||
return mainResources.get(); | ||
} | ||
|
||
// Return the transport version resources paths that have been changed relative to main | ||
private Set<String> getChangedResources() { | ||
if (changedResources.get() == null) { | ||
synchronized (changedResources) { | ||
String output = gitCommand("diff", "--name-only", "main", "."); | ||
|
||
HashSet<String> resources = new HashSet<>(); | ||
Collections.addAll(resources, output.split(System.lineSeparator())); | ||
changedResources.set(resources); | ||
} | ||
} | ||
return changedResources.get(); | ||
} | ||
|
||
// Read a transport version resource from the main branch, or return null if it doesn't exist on main | ||
private <T> T getMainFile(String resourcePath, BiFunction<String, String, T> parser) { | ||
if (getMainResources().contains(resourcePath) == false) { | ||
return null; | ||
} | ||
String content = gitCommand("show", "main:./" + resourcePath).strip(); | ||
return parser.apply(resourcePath, content); | ||
} | ||
|
||
// run a git command, relative to the transport version resources directory | ||
private String gitCommand(String... args) { | ||
ByteArrayOutputStream stdout = new ByteArrayOutputStream(); | ||
|
||
List<String> command = new ArrayList<>(); | ||
Collections.addAll(command, "git", "-C", getTransportResourcesDir().toString()); | ||
Collections.addAll(command, args); | ||
|
||
ExecResult result = getExecOperations().exec(spec -> { | ||
spec.setCommandLine(command); | ||
spec.setStandardOutput(stdout); | ||
spec.setErrorOutput(stdout); | ||
spec.setIgnoreExitValue(true); | ||
}); | ||
|
||
if (result.getExitValue() != 0) { | ||
throw new RuntimeException( | ||
"git command failed with exit code " | ||
+ result.getExitValue() | ||
+ System.lineSeparator() | ||
+ "command: " | ||
+ String.join(" ", command) | ||
+ System.lineSeparator() | ||
+ "output:" | ||
+ System.lineSeparator() | ||
+ stdout.toString(StandardCharsets.UTF_8) | ||
); | ||
} | ||
|
||
return stdout.toString(StandardCharsets.UTF_8); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.