Skip to content

Commit 8a19711

Browse files
prdoyleldematteelasticsearchmachine
authored andcommitted
[Entitlements] Add test entitlement bootstrap and initialization classes (elastic#128625)
* Initialization class as argument to EntitlementAgent * visibility changes * WIP: test entitlement bootstrap and initialization classes * Simplify * Moving packages to reduce visibility * adjust visibility * add plugins descriptor + policy parsing * PR comments * update visibility, uncomment TestBuildInfoParser usage * [CI] Auto commit changes from spotless * Factor out createPolicyManager to help merge * TestEntitlementInitialization is not yet implemented * Respond to PR comments --------- Co-authored-by: Lorenzo Dematte <[email protected]> Co-authored-by: elasticsearchmachine <[email protected]>
1 parent 67ce338 commit 8a19711

File tree

10 files changed

+235
-47
lines changed

10 files changed

+235
-47
lines changed

libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public static void bootstrap(
122122
suppressFailureLogPackages
123123
);
124124
exportInitializationToAgent();
125-
loadAgent(findAgentJar());
125+
loadAgent(findAgentJar(), EntitlementInitialization.class.getName());
126126
}
127127

128128
private static Path getUserHome() {
@@ -134,11 +134,11 @@ private static Path getUserHome() {
134134
}
135135

136136
@SuppressForbidden(reason = "The VirtualMachine API is the only way to attach a java agent dynamically")
137-
private static void loadAgent(String agentPath) {
137+
static void loadAgent(String agentPath, String entitlementInitializationClassName) {
138138
try {
139139
VirtualMachine vm = VirtualMachine.attach(Long.toString(ProcessHandle.current().pid()));
140140
try {
141-
vm.loadAgent(agentPath, EntitlementInitialization.class.getName());
141+
vm.loadAgent(agentPath, entitlementInitializationClassName);
142142
} finally {
143143
vm.detach();
144144
}
@@ -154,7 +154,7 @@ private static void exportInitializationToAgent() {
154154
EntitlementInitialization.class.getModule().addExports(initPkg, unnamedModule);
155155
}
156156

157-
public static String findAgentJar() {
157+
static String findAgentJar() {
158158
String propertyName = "es.entitlement.agentJar";
159159
String propertyValue = System.getProperty(propertyName);
160160
if (propertyValue != null) {

libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ public class EntitlementInitialization {
3636

3737
private static final Module ENTITLEMENTS_MODULE = PolicyManager.class.getModule();
3838

39-
private static ElasticsearchEntitlementChecker manager;
39+
private static ElasticsearchEntitlementChecker checker;
4040

4141
// Note: referenced by bridge reflectively
4242
public static EntitlementChecker checker() {
43-
return manager;
43+
return checker;
4444
}
4545

4646
/**
@@ -63,17 +63,33 @@ public static EntitlementChecker checker() {
6363
* @param inst the JVM instrumentation class instance
6464
*/
6565
public static void initialize(Instrumentation inst) throws Exception {
66-
manager = initChecker();
66+
checker = initChecker(inst, createPolicyManager());
67+
}
6768

68-
var verifyBytecode = Booleans.parseBoolean(System.getProperty("es.entitlements.verify_bytecode", "false"));
69-
if (verifyBytecode) {
70-
ensureClassesSensitiveToVerificationAreInitialized();
71-
}
69+
private static PolicyCheckerImpl createPolicyChecker(PolicyManager policyManager) {
70+
EntitlementBootstrap.BootstrapArgs bootstrapArgs = EntitlementBootstrap.bootstrapArgs();
71+
return new PolicyCheckerImpl(
72+
bootstrapArgs.suppressFailureLogPackages(),
73+
ENTITLEMENTS_MODULE,
74+
policyManager,
75+
bootstrapArgs.pathLookup()
76+
);
77+
}
7278

73-
DynamicInstrumentation.initialize(
74-
inst,
75-
EntitlementCheckerUtils.getVersionSpecificCheckerClass(EntitlementChecker.class, Runtime.version().feature()),
76-
verifyBytecode
79+
private static PolicyManager createPolicyManager() {
80+
EntitlementBootstrap.BootstrapArgs bootstrapArgs = EntitlementBootstrap.bootstrapArgs();
81+
Map<String, Policy> pluginPolicies = bootstrapArgs.pluginPolicies();
82+
PathLookup pathLookup = bootstrapArgs.pathLookup();
83+
84+
FilesEntitlementsValidation.validate(pluginPolicies, pathLookup);
85+
86+
return new PolicyManager(
87+
HardcodedEntitlements.serverPolicy(pathLookup.pidFile(), bootstrapArgs.serverPolicyPatch()),
88+
HardcodedEntitlements.agentEntitlements(),
89+
pluginPolicies,
90+
EntitlementBootstrap.bootstrapArgs().scopeResolver(),
91+
EntitlementBootstrap.bootstrapArgs().sourcePaths(),
92+
pathLookup
7793
);
7894
}
7995

@@ -99,9 +115,8 @@ private static void ensureClassesSensitiveToVerificationAreInitialized() {
99115
}
100116
}
101117

102-
private static ElasticsearchEntitlementChecker initChecker() {
103-
final PolicyChecker policyChecker = createPolicyChecker();
104-
118+
static ElasticsearchEntitlementChecker initChecker(Instrumentation inst, PolicyManager policyManager) throws Exception {
119+
final PolicyChecker policyChecker = createPolicyChecker(policyManager);
105120
final Class<?> clazz = EntitlementCheckerUtils.getVersionSpecificCheckerClass(
106121
ElasticsearchEntitlementChecker.class,
107122
Runtime.version().feature()
@@ -113,34 +128,25 @@ private static ElasticsearchEntitlementChecker initChecker() {
113128
} catch (NoSuchMethodException e) {
114129
throw new AssertionError("entitlement impl is missing required constructor: [" + clazz.getName() + "]", e);
115130
}
131+
132+
ElasticsearchEntitlementChecker checker;
116133
try {
117-
return (ElasticsearchEntitlementChecker) constructor.newInstance(policyChecker);
134+
checker = (ElasticsearchEntitlementChecker) constructor.newInstance(policyChecker);
118135
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
119136
throw new AssertionError(e);
120137
}
121-
}
122-
123-
private static PolicyCheckerImpl createPolicyChecker() {
124-
EntitlementBootstrap.BootstrapArgs bootstrapArgs = EntitlementBootstrap.bootstrapArgs();
125-
Map<String, Policy> pluginPolicies = bootstrapArgs.pluginPolicies();
126-
PathLookup pathLookup = bootstrapArgs.pathLookup();
127138

128-
FilesEntitlementsValidation.validate(pluginPolicies, pathLookup);
139+
var verifyBytecode = Booleans.parseBoolean(System.getProperty("es.entitlements.verify_bytecode", "false"));
140+
if (verifyBytecode) {
141+
ensureClassesSensitiveToVerificationAreInitialized();
142+
}
129143

130-
PolicyManager policyManager = new PolicyManager(
131-
HardcodedEntitlements.serverPolicy(pathLookup.pidFile(), bootstrapArgs.serverPolicyPatch()),
132-
HardcodedEntitlements.agentEntitlements(),
133-
pluginPolicies,
134-
EntitlementBootstrap.bootstrapArgs().scopeResolver(),
135-
EntitlementBootstrap.bootstrapArgs().sourcePaths(),
136-
pathLookup
137-
);
138-
return new PolicyCheckerImpl(
139-
bootstrapArgs.suppressFailureLogPackages(),
140-
ENTITLEMENTS_MODULE,
141-
policyManager,
142-
bootstrapArgs.pathLookup()
144+
DynamicInstrumentation.initialize(
145+
inst,
146+
EntitlementCheckerUtils.getVersionSpecificCheckerClass(EntitlementChecker.class, Runtime.version().feature()),
147+
verifyBytecode
143148
);
144-
}
145149

150+
return checker;
151+
}
146152
}

server/src/main/java/org/elasticsearch/plugins/PluginDescriptor.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,31 @@ public static PluginDescriptor readFromProperties(final Path pluginDir) throws I
250250
return descriptor;
251251
}
252252

253+
/**
254+
* Reads the internal descriptor for a classic plugin.
255+
*
256+
* @param stream the InputStream from which to read the plugin data
257+
* @return the plugin info
258+
* @throws IOException if an I/O exception occurred reading the plugin descriptor
259+
*/
260+
public static PluginDescriptor readInternalDescriptorFromStream(InputStream stream) throws IOException {
261+
final Map<String, String> propsMap;
262+
{
263+
final Properties props = new Properties();
264+
props.load(stream);
265+
propsMap = props.stringPropertyNames().stream().collect(Collectors.toMap(Function.identity(), props::getProperty));
266+
}
267+
268+
PluginDescriptor descriptor = readerInternalDescriptor(propsMap, INTERNAL_DESCRIPTOR_FILENAME);
269+
String name = descriptor.getName();
270+
271+
if (propsMap.isEmpty() == false) {
272+
throw new IllegalArgumentException("Unknown properties for plugin [" + name + "] in plugin descriptor: " + propsMap.keySet());
273+
}
274+
275+
return descriptor;
276+
}
277+
253278
private static PluginDescriptor readerInternalDescriptor(Map<String, String> propsMap, String filename) {
254279
String name = readNonEmptyString(propsMap, filename, "name");
255280
String desc = readString(propsMap, name, "description");

server/src/test/java/org/elasticsearch/plugins/PluginDescriptorTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ void assertBothDescriptors(Consumer<DescriptorWriter> assertions) {
107107
assertions.accept(PluginDescriptorTests::mockStableDescriptor);
108108
}
109109

110-
public void testReadInternalDescriptor() throws Exception {
110+
public void testReadInternalDescriptorFromStream() throws Exception {
111111
PluginDescriptor info = mockInternalDescriptor();
112112
assertEquals("my_plugin", info.getName());
113113
assertEquals("fake desc", info.getDescription());

test/framework/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ dependencies {
1616
api project(':libs:ssl-config')
1717
api project(":server")
1818
api project(":libs:cli")
19+
api project(":libs:entitlement:bridge")
1920
api "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}"
2021
api "junit:junit:${versions.junit}"
2122
api "org.hamcrest:hamcrest:${versions.hamcrest}"

test/framework/src/main/java/org/elasticsearch/bootstrap/TestBuildInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@
1111

1212
import java.util.List;
1313

14-
record TestBuildInfo(String component, List<TestBuildInfoLocation> locations) {}
14+
public record TestBuildInfo(String component, List<TestBuildInfoLocation> locations) {}

test/framework/src/main/java/org/elasticsearch/bootstrap/TestBuildInfoParser.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import java.util.ArrayList;
2424
import java.util.List;
2525

26-
class TestBuildInfoParser {
26+
public class TestBuildInfoParser {
2727

2828
private static final String PLUGIN_TEST_BUILD_INFO_RESOURCES = "META-INF/plugin-test-build-info.json";
2929
private static final String SERVER_TEST_BUILD_INFO_RESOURCE = "META-INF/server-test-build-info.json";
@@ -75,7 +75,7 @@ static TestBuildInfo fromXContent(final XContentParser parser) throws IOExceptio
7575
return PARSER.parse(parser, null).build();
7676
}
7777

78-
static List<TestBuildInfo> parseAllPluginTestBuildInfo() throws IOException {
78+
public static List<TestBuildInfo> parseAllPluginTestBuildInfo() throws IOException {
7979
var xContent = XContentFactory.xContent(XContentType.JSON);
8080
List<TestBuildInfo> pluginsTestBuildInfos = new ArrayList<>();
8181
var resources = TestBuildInfoParser.class.getClassLoader().getResources(PLUGIN_TEST_BUILD_INFO_RESOURCES);
@@ -88,7 +88,7 @@ static List<TestBuildInfo> parseAllPluginTestBuildInfo() throws IOException {
8888
return pluginsTestBuildInfos;
8989
}
9090

91-
static TestBuildInfo parseServerTestBuildInfo() throws IOException {
91+
public static TestBuildInfo parseServerTestBuildInfo() throws IOException {
9292
var xContent = XContentFactory.xContent(XContentType.JSON);
9393
var resource = TestBuildInfoParser.class.getClassLoader().getResource(SERVER_TEST_BUILD_INFO_RESOURCE);
9494
// No test-build-info for server: this might be a non-gradle build. Proceed without TestBuildInfo

test/framework/src/main/java/org/elasticsearch/bootstrap/TestScopeResolver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import java.util.Map;
2222
import java.util.function.Function;
2323

24-
record TestScopeResolver(Map<String, PolicyManager.PolicyScope> scopeMap) {
24+
public record TestScopeResolver(Map<String, PolicyManager.PolicyScope> scopeMap) {
2525

2626
private static final Logger logger = LogManager.getLogger(TestScopeResolver.class);
2727

@@ -38,7 +38,7 @@ PolicyManager.PolicyScope getScope(Class<?> callerClass) {
3838
return scope;
3939
}
4040

41-
static Function<Class<?>, PolicyManager.PolicyScope> createScopeResolver(
41+
public static Function<Class<?>, PolicyManager.PolicyScope> createScopeResolver(
4242
TestBuildInfo serverBuildInfo,
4343
List<TestBuildInfo> pluginsBuildInfo
4444
) {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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.entitlement.bootstrap;
11+
12+
import org.elasticsearch.entitlement.initialization.TestEntitlementInitialization;
13+
import org.elasticsearch.entitlement.runtime.policy.PathLookup;
14+
import org.elasticsearch.logging.LogManager;
15+
import org.elasticsearch.logging.Logger;
16+
17+
public class TestEntitlementBootstrap {
18+
19+
private static final Logger logger = LogManager.getLogger(TestEntitlementBootstrap.class);
20+
private static BootstrapArgs bootstrapArgs;
21+
22+
/**
23+
* Activates entitlement checking in tests.
24+
* @param bootstrapArgs arguments used for and passed to entitlement initialization
25+
*/
26+
public static void bootstrap(BootstrapArgs bootstrapArgs) {
27+
assert bootstrapArgs != null;
28+
TestEntitlementBootstrap.bootstrapArgs = bootstrapArgs;
29+
logger.debug("Loading entitlement agent");
30+
EntitlementBootstrap.loadAgent(EntitlementBootstrap.findAgentJar(), TestEntitlementInitialization.class.getName());
31+
}
32+
33+
public static BootstrapArgs bootstrapArgs() {
34+
return bootstrapArgs;
35+
}
36+
37+
public record BootstrapArgs(PathLookup pathLookup) {}
38+
}

0 commit comments

Comments
 (0)