Skip to content

Commit b303ab2

Browse files
committed
System properties: testOnlyClasspath and enableForTests
1 parent bc88a95 commit b303ab2

File tree

6 files changed

+90
-16
lines changed

6 files changed

+90
-16
lines changed

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
import javax.inject.Inject;
3939

40+
import static java.util.Objects.requireNonNull;
4041
import static org.elasticsearch.gradle.internal.util.ParamsUtils.loadBuildParams;
4142
import static org.elasticsearch.gradle.util.FileUtils.mkdirs;
4243
import static org.elasticsearch.gradle.util.GradleUtils.maybeConfigure;
@@ -173,6 +174,16 @@ public void execute(Task t) {
173174
// we use 'temp' relative to CWD since this is per JVM and tests are forbidden from writing to CWD
174175
nonInputProperties.systemProperty("java.io.tmpdir", test.getWorkingDir().toPath().resolve("temp"));
175176

177+
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
178+
SourceSet mainSourceSet = sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME);
179+
SourceSet testSourceSet = sourceSets.findByName(SourceSet.TEST_SOURCE_SET_NAME);
180+
if (mainSourceSet != null && testSourceSet != null) {
181+
FileCollection mainRuntime = mainSourceSet.getRuntimeClasspath();
182+
FileCollection testRuntime = testSourceSet.getRuntimeClasspath();
183+
FileCollection testOnlyFiles = testRuntime.minus(mainRuntime);
184+
nonInputProperties.systemProperty("es.entitlement.testOnlyPath", testOnlyFiles::getAsPath);
185+
}
186+
176187
test.systemProperties(getProviderFactory().systemPropertiesPrefixedBy("tests.").get());
177188
test.systemProperties(getProviderFactory().systemPropertiesPrefixedBy("es.").get());
178189

@@ -211,13 +222,12 @@ public void execute(Task t) {
211222
project.getPluginManager().withPlugin("com.gradleup.shadow", p -> {
212223
if (test.getName().equals(JavaPlugin.TEST_TASK_NAME)) {
213224
// Remove output class files and any other dependencies from the test classpath, since the shadow JAR includes these
214-
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
215-
FileCollection mainRuntime = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath();
216225
// Add any "shadow" dependencies. These are dependencies that are *not* bundled into the shadow JAR
217226
Configuration shadowConfig = project.getConfigurations().getByName(ShadowBasePlugin.CONFIGURATION_NAME);
218227
// Add the shadow JAR artifact itself
219228
FileCollection shadowJar = project.files(project.getTasks().named("shadowJar"));
220-
FileCollection testRuntime = sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME).getRuntimeClasspath();
229+
FileCollection mainRuntime = requireNonNull(mainSourceSet).getRuntimeClasspath();
230+
FileCollection testRuntime = requireNonNull(testSourceSet).getRuntimeClasspath();
221231
test.setClasspath(testRuntime.minus(mainRuntime).plus(shadowConfig).plus(shadowJar));
222232
}
223233
});

build-tools/src/main/java/org/elasticsearch/gradle/test/TestBuildInfoPlugin.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818
import org.gradle.api.provider.ProviderFactory;
1919
import org.gradle.api.tasks.SourceSet;
2020
import org.gradle.api.tasks.SourceSetContainer;
21+
import org.gradle.api.tasks.testing.Test;
2122
import org.gradle.language.jvm.tasks.ProcessResources;
2223

24+
import java.util.List;
25+
2326
import javax.inject.Inject;
2427

2528
/**
@@ -53,5 +56,11 @@ public void apply(Project project) {
5356
project.getTasks().withType(ProcessResources.class).named("processResources").configure(task -> {
5457
task.into("META-INF", copy -> copy.from(testBuildInfoTask));
5558
});
59+
60+
project.getTasks().withType(Test.class).configureEach(test -> {
61+
if (List.of("test", "internalClusterTest").contains(test.getName())) {
62+
test.systemProperty("es.entitlement.enableForTests", "true");
63+
}
64+
});
5665
}
5766
}

test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@
2929
import java.net.URL;
3030
import java.nio.file.Path;
3131
import java.util.ArrayList;
32+
import java.util.Arrays;
3233
import java.util.Collection;
3334
import java.util.HashMap;
3435
import java.util.List;
3536
import java.util.Map;
3637
import java.util.Set;
38+
import java.util.TreeSet;
39+
import java.util.stream.Collectors;
3740

3841
public class TestEntitlementBootstrap {
3942

@@ -45,13 +48,20 @@ public class TestEntitlementBootstrap {
4548
* Activates entitlement checking in tests.
4649
*/
4750
public static void bootstrap(Path tempDir) throws IOException {
51+
if (isEnabledForTest() == false) {
52+
return;
53+
}
4854
TestPathLookup pathLookup = new TestPathLookup(List.of(tempDir));
4955
policyManager = createPolicyManager(pathLookup);
5056
EntitlementInitialization.initializeArgs = new EntitlementInitialization.InitializeArgs(pathLookup, Set.of(), policyManager);
5157
logger.debug("Loading entitlement agent");
5258
EntitlementBootstrap.loadAgent(EntitlementBootstrap.findAgentJar(), EntitlementInitialization.class.getName());
5359
}
5460

61+
public static boolean isEnabledForTest() {
62+
return Boolean.getBoolean("es.entitlement.enableForTests");
63+
}
64+
5565
public static void setActive(boolean newValue) {
5666
policyManager.setActive(newValue);
5767
}
@@ -61,7 +71,9 @@ public static void setTriviallyAllowingTestCode(boolean newValue) {
6171
}
6272

6373
public static void reset() {
64-
policyManager.reset();
74+
if (policyManager != null) {
75+
policyManager.reset();
76+
}
6577
}
6678

6779
private static TestPolicyManager createPolicyManager(PathLookup pathLookup) throws IOException {
@@ -79,13 +91,22 @@ private static TestPolicyManager createPolicyManager(PathLookup pathLookup) thro
7991

8092
FilesEntitlementsValidation.validate(pluginPolicies, pathLookup);
8193

94+
String testOnlyPathProperty = System.getProperty("es.entitlement.testOnlyPath");
95+
Set<String> testOnlyClassPath;
96+
if (testOnlyPathProperty == null) {
97+
testOnlyClassPath = Set.of();
98+
} else {
99+
testOnlyClassPath = Arrays.stream(testOnlyPathProperty.split(":")).collect(Collectors.toCollection(TreeSet::new));
100+
}
101+
82102
return new TestPolicyManager(
83103
HardcodedEntitlements.serverPolicy(null, null),
84104
HardcodedEntitlements.agentEntitlements(),
85105
pluginPolicies,
86106
scopeResolver,
87107
pluginSourcePaths,
88-
pathLookup
108+
pathLookup,
109+
testOnlyClassPath
89110
);
90111
}
91112

test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@
1313
import org.elasticsearch.test.ESTestCase;
1414

1515
import java.nio.file.Path;
16+
import java.security.CodeSource;
17+
import java.security.ProtectionDomain;
1618
import java.util.Collection;
1719
import java.util.List;
1820
import java.util.Map;
1921
import java.util.concurrent.ConcurrentHashMap;
2022
import java.util.function.Function;
2123

24+
import static java.util.Objects.requireNonNull;
25+
2226
public class TestPolicyManager extends PolicyManager {
27+
2328
boolean isActive;
2429
boolean isTriviallyAllowingTestCode;
2530

@@ -29,15 +34,19 @@ public class TestPolicyManager extends PolicyManager {
2934
*/
3035
final Map<Class<?>, ModuleEntitlements> classEntitlementsMap = new ConcurrentHashMap<>();
3136

37+
final Collection<String> testOnlyClasspath;
38+
3239
public TestPolicyManager(
3340
Policy serverPolicy,
3441
List<Entitlement> apmAgentEntitlements,
3542
Map<String, Policy> pluginPolicies,
3643
Function<Class<?>, PolicyScope> scopeResolver,
3744
Map<String, Collection<Path>> pluginSourcePaths,
38-
PathLookup pathLookup
45+
PathLookup pathLookup,
46+
Collection<String> testOnlyClasspath
3947
) {
4048
super(serverPolicy, apmAgentEntitlements, pluginPolicies, scopeResolver, pluginSourcePaths, pathLookup);
49+
this.testOnlyClasspath = testOnlyClasspath;
4150
reset();
4251
}
4352

@@ -70,10 +79,13 @@ boolean isTriviallyAllowed(Class<?> requestingClass) {
7079
if (isActive == false) {
7180
return true;
7281
}
73-
if (isTriviallyAllowingTestCode && isTestCaseClass(requestingClass)) {
82+
if (isEntitlementClass(requestingClass)) {
83+
return true;
84+
}
85+
if (isTestFrameworkClass(requestingClass)) {
7486
return true;
7587
}
76-
if (isTestFrameworkClass(requestingClass) || isEntitlementClass(requestingClass)) {
88+
if (isTriviallyAllowingTestCode && isTestCode(requestingClass)) {
7789
return true;
7890
}
7991
return super.isTriviallyAllowed(requestingClass);
@@ -84,6 +96,9 @@ private boolean isEntitlementClass(Class<?> requestingClass) {
8496
&& (requestingClass.getName().contains("Test") == false);
8597
}
8698

99+
@Deprecated // TODO: reevaluate whether we want this.
100+
// If we can simply check for dependencies the gradle worker has that aren't
101+
// declared in the gradle config (namely org.gradle) that would be simpler.
87102
private boolean isTestFrameworkClass(Class<?> requestingClass) {
88103
String packageName = requestingClass.getPackageName();
89104
for (String prefix : TEST_FRAMEWORK_PACKAGE_PREFIXES) {
@@ -94,13 +109,25 @@ private boolean isTestFrameworkClass(Class<?> requestingClass) {
94109
return false;
95110
}
96111

97-
private boolean isTestCaseClass(Class<?> requestingClass) {
98-
for (Class<?> candidate = requestingClass; candidate != null; candidate = candidate.getDeclaringClass()) {
112+
private boolean isTestCode(Class<?> requestingClass) {
113+
// TODO: Cache this? It's expensive
114+
for (Class<?> candidate = requireNonNull(requestingClass); candidate != null; candidate = candidate.getDeclaringClass()) {
99115
if (ESTestCase.class.isAssignableFrom(candidate)) {
100116
return true;
101117
}
102118
}
103-
return false;
119+
ProtectionDomain protectionDomain = requestingClass.getProtectionDomain();
120+
CodeSource codeSource = protectionDomain.getCodeSource();
121+
if (codeSource == null) {
122+
// This can happen for JDK classes
123+
return false;
124+
}
125+
String needle = codeSource.getLocation().getPath();
126+
if (needle.endsWith("/")) {
127+
needle = needle.substring(0, needle.length() - 1);
128+
}
129+
boolean result = testOnlyClasspath.contains(needle);
130+
return result;
104131
}
105132

106133
private static final String[] TEST_FRAMEWORK_PACKAGE_PREFIXES = {

test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -514,10 +514,16 @@ protected void afterIfSuccessful() throws Exception {}
514514

515515
@BeforeClass
516516
public static void setupEntitlementsForClass() {
517-
TestEntitlementBootstrap.setActive(false == getTestClass().isAnnotationPresent(WithoutEntitlements.class));
518-
TestEntitlementBootstrap.setTriviallyAllowingTestCode(
519-
false == getTestClass().isAnnotationPresent(WithEntitlementsOnTestCode.class)
520-
);
517+
boolean withoutEntitlements = getTestClass().isAnnotationPresent(WithoutEntitlements.class);
518+
boolean withEntitlementsOnTestCode = getTestClass().isAnnotationPresent(WithEntitlementsOnTestCode.class);
519+
if (TestEntitlementBootstrap.isEnabledForTest()) {
520+
TestEntitlementBootstrap.setActive(false == withoutEntitlements);
521+
TestEntitlementBootstrap.setTriviallyAllowingTestCode(false == withEntitlementsOnTestCode);
522+
} else if (withEntitlementsOnTestCode) {
523+
throw new AssertionError(
524+
"Cannot use WithEntitlementsOnTestCode on tests that are not configured to use entitlements for testing"
525+
);
526+
}
521527
}
522528

523529
@AfterClass

test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public void setupPolicyManager() {
3131
Map.of(),
3232
c -> new PolicyScope(PLUGIN, "example-plugin" + scopeCounter.incrementAndGet(), "org.example.module"),
3333
Map.of(),
34-
new TestPathLookup(List.of())
34+
new TestPathLookup(List.of()),
35+
List.of()
3536
);
3637
policyManager.setActive(true);
3738
}

0 commit comments

Comments
 (0)