-
Notifications
You must be signed in to change notification settings - Fork 25.5k
[Gradle] Fix test seed handling when running with cc #133811
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 5 commits
381cd97
87d1aa8
d9f6c78
769fbb3
4a9f74a
eff11c9
c9ff966
a25ede5
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 |
---|---|---|
|
@@ -29,6 +29,7 @@ | |
import org.gradle.api.NamedDomainObjectContainer; | ||
import org.gradle.api.Plugin; | ||
import org.gradle.api.Project; | ||
import org.gradle.api.configuration.BuildFeatures; | ||
import org.gradle.api.logging.Logger; | ||
import org.gradle.api.logging.Logging; | ||
import org.gradle.api.model.ObjectFactory; | ||
|
@@ -60,7 +61,6 @@ | |
import java.util.List; | ||
import java.util.Locale; | ||
import java.util.Properties; | ||
import java.util.Random; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
@@ -87,7 +87,7 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> { | |
private final JavaInstallationRegistry javaInstallationRegistry; | ||
private final JvmMetadataDetector metadataDetector; | ||
private final ProviderFactory providers; | ||
private final BranchesFileParser branchesFileParser; | ||
private final BuildFeatures buildFeatures; | ||
private JavaToolchainService toolChainService; | ||
private Project project; | ||
|
||
|
@@ -96,13 +96,14 @@ public GlobalBuildInfoPlugin( | |
ObjectFactory objectFactory, | ||
JavaInstallationRegistry javaInstallationRegistry, | ||
JvmMetadataDetector metadataDetector, | ||
ProviderFactory providers | ||
ProviderFactory providers, | ||
BuildFeatures buildFeatures | ||
) { | ||
this.objectFactory = objectFactory; | ||
this.javaInstallationRegistry = javaInstallationRegistry; | ||
this.metadataDetector = new ErrorTraceMetadataDetector(metadataDetector); | ||
this.providers = providers; | ||
this.branchesFileParser = new BranchesFileParser(new ObjectMapper()); | ||
this.buildFeatures = buildFeatures; | ||
} | ||
|
||
@Override | ||
|
@@ -113,6 +114,7 @@ public void apply(Project project) { | |
this.project = project; | ||
project.getPlugins().apply(JvmToolchainsPlugin.class); | ||
project.getPlugins().apply(JdkDownloadPlugin.class); | ||
project.getPlugins().apply(VersionPropertiesPlugin.class); | ||
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. I tend to follow the pattern of applying nested plugins as early as possible (there are edge cases where you might only do it on certain conditions) in the apply method. that makes reading way simpler and I think its common practice |
||
Provider<GitInfo> gitInfo = project.getPlugins().apply(GitInfoPlugin.class).getGitInfo(); | ||
|
||
toolChainService = project.getExtensions().getByType(JavaToolchainService.class); | ||
|
@@ -121,11 +123,9 @@ public void apply(Project project) { | |
throw new GradleException("Gradle " + minimumGradleVersion.getVersion() + "+ is required"); | ||
} | ||
|
||
project.getPlugins().apply(VersionPropertiesPlugin.class); | ||
Properties versionProperties = (Properties) project.getExtensions().getByName(VERSIONS_EXT); | ||
JavaVersion minimumCompilerVersion = JavaVersion.toVersion(versionProperties.get("minimumCompilerJava")); | ||
JavaVersion minimumRuntimeVersion = JavaVersion.toVersion(versionProperties.get("minimumRuntimeJava")); | ||
|
||
Version elasticsearchVersionProperty = Version.fromString(versionProperties.getProperty("elasticsearch")); | ||
|
||
RuntimeJava runtimeJavaHome = findRuntimeJavaHome(); | ||
|
@@ -233,6 +233,7 @@ private List<DevelopmentBranch> getDevelopmentBranches() { | |
} | ||
} | ||
|
||
var branchesFileParser = new BranchesFileParser(new ObjectMapper()); | ||
return branchesFileParser.parse(branchesBytes); | ||
} | ||
|
||
|
@@ -266,7 +267,11 @@ private void logGlobalBuildInfo(BuildParameterExtension buildParams) { | |
if (javaToolchainHome != null) { | ||
LOGGER.quiet(" JAVA_TOOLCHAIN_HOME : " + javaToolchainHome); | ||
} | ||
LOGGER.quiet(" Random Testing Seed : " + buildParams.getTestSeed()); | ||
|
||
if (buildFeatures.getConfigurationCache().getActive().get() == false) { | ||
// if configuration cache is enabled, resolving the test seed early breaks configuration cache reuse | ||
LOGGER.quiet(" Random Testing Seed : " + buildParams.getTestSeed()); | ||
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. the test seed printing resolves the ValueSource early and breaks configuration cache. therefore only print if not running in cc |
||
} | ||
LOGGER.quiet(" In FIPS 140 mode : " + buildParams.getInFipsJvm()); | ||
LOGGER.quiet("======================================="); | ||
} | ||
|
@@ -321,16 +326,8 @@ private Stream<InstallationLocation> getAvailableJavaInstallationLocationSteam() | |
); | ||
} | ||
|
||
private static String getTestSeed() { | ||
String testSeedProperty = System.getProperty("tests.seed"); | ||
final String testSeed; | ||
if (testSeedProperty == null) { | ||
long seed = new Random(System.currentTimeMillis()).nextLong(); | ||
testSeed = Long.toUnsignedString(seed, 16).toUpperCase(Locale.ROOT); | ||
} else { | ||
testSeed = testSeedProperty; | ||
} | ||
return testSeed; | ||
private Provider<String> getTestSeed() { | ||
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. In order to be able to pass a changing value to Test tasks without breaking configuration cache reuse, we have to rely on a ValueSource here that we pass down all the way to "noninputproperties" CommandLineProvider we configure for tests tasks. ValueSource allows being opaque to the configuration cache. Other providers or plain string would always be interpreted as inputs to the configuration cache, resulting in the test seed not changing between multiple build invocation without configuration cache change |
||
return project.getProviders().of(TestSeedValueSource.class, spec -> {}); | ||
} | ||
|
||
private static void throwInvalidJavaHomeException(String description, File javaHome, int expectedVersion, int actualVersion) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* 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.info; | ||
|
||
import org.gradle.api.provider.ValueSource; | ||
import org.gradle.api.provider.ValueSourceParameters; | ||
|
||
import java.util.Locale; | ||
import java.util.Random; | ||
|
||
public abstract class TestSeedValueSource implements ValueSource<String, ValueSourceParameters.None> { | ||
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 need to model the TestSeed resolution as ValueSource as this allows passing the parameter transparently for the configuration cache down to the consumers (test cases, testcluster). Taking this from the according javadoc:
|
||
|
||
@Override | ||
public ValueSourceParameters.None getParameters() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String obtain() { | ||
String testSeedProperty = System.getProperty("tests.seed"); | ||
final String testSeed; | ||
if (testSeedProperty == null) { | ||
long seed = new Random(System.currentTimeMillis()).nextLong(); | ||
testSeed = Long.toUnsignedString(seed, 16).toUpperCase(Locale.ROOT); | ||
} else { | ||
testSeed = testSeedProperty; | ||
} | ||
return testSeed; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,9 +12,7 @@ dependencies { | |
javaRestTestImplementation project(':x-pack:plugin:security') | ||
} | ||
|
||
boolean literalUsername = buildParams.random.nextBoolean() | ||
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. buildParams.random resolves the TestSeed and breaks configuration-cache. by moving this into the task configuration block we defer that. for this particular test cc will always be out of date. Longterm we might wand a more deterministic way of testing this behaviour 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. The altenative would be to split the random from the testseed,, but this would break reproducability. |
||
|
||
tasks.named("javaRestTest").configure { | ||
usesDefaultDistribution("to be triaged") | ||
systemProperty 'test.literalUsername', literalUsername | ||
systemProperty 'test.literalUsername', buildParams.random.map{r -> r.nextBoolean()}.get() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ restResources { | |
} | ||
|
||
// randomise between sniff and proxy modes | ||
boolean proxyMode = buildParams.random.nextBoolean() | ||
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. since we only use this in deprecated TestCluster settings I did not invest to much time to sugar code that api here for now. Ultimately this moves into tests at one point 🤞 |
||
var proxyModeProvider = buildParams.random.map{r -> r.nextBoolean()} | ||
|
||
def fulfillingCluster = testClusters.register('fulfilling-cluster') { | ||
setting 'xpack.security.enabled', 'true' | ||
|
@@ -57,7 +57,7 @@ def queryingCluster = testClusters.register('querying-cluster') { | |
user username: "test_user", password: "x-pack-test-password" | ||
setting 'xpack.ml.enabled', 'false' | ||
setting 'cluster.remote.my_remote_cluster.skip_unavailable', 'false' | ||
if (proxyMode) { | ||
if (proxyModeProvider.get()) { | ||
setting 'cluster.remote.my_remote_cluster.mode', 'proxy' | ||
setting 'cluster.remote.my_remote_cluster.proxy_address', { | ||
"\"${fulfillingCluster.get().getAllTransportPortURI().get(0)}\"" | ||
|
@@ -79,7 +79,7 @@ tasks.register('querying-cluster', RestIntegTestTask) { | |
useCluster fulfillingCluster | ||
useCluster queryingCluster | ||
systemProperty 'tests.rest.suite', 'querying_cluster' | ||
if (proxyMode) { | ||
if (proxyModeProvider.get()) { | ||
systemProperty 'tests.rest.blacklist', [ | ||
'querying_cluster/10_basic/Add persistent remote cluster based on the preset cluster', | ||
'querying_cluster/20_info/Add persistent remote cluster based on the preset cluster and check remote info', | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ restResources { | |
} | ||
|
||
// randomise between sniff and proxy modes | ||
boolean proxyMode = buildParams.random.nextBoolean() | ||
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. this resolves test seed early basically breaking configuration cache reusability every time |
||
var proxyModeProvider = buildParams.random.map{r -> r.nextBoolean()} | ||
|
||
def fulfillingCluster = testClusters.register('fulfilling-cluster') { | ||
setting 'xpack.security.enabled', 'true' | ||
|
@@ -43,7 +43,7 @@ def queryingCluster = testClusters.register('querying-cluster') { | |
setting 'cluster.remote.connections_per_cluster', "1" | ||
user username: "test_user", password: "x-pack-test-password" | ||
|
||
if (proxyMode) { | ||
if (proxyModeProvider.get()) { | ||
setting 'cluster.remote.my_remote_cluster.mode', 'proxy' | ||
setting 'cluster.remote.my_remote_cluster.proxy_address', { | ||
"\"${fulfillingCluster.get().getAllTransportPortURI().get(0)}\"" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems only used at one point. therefore I removed the field property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I got the impression that it belongs in constructor, to avoid creating it on every method call, but the apply method will be called only once anyway.