From c3ba071948f01612f6e6858617fd040603ed3019 Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Fri, 18 Jul 2025 17:11:38 +0200
Subject: [PATCH 01/15] Fix entitlements in internalClusterTest
* Previously, entitlement checks got disabled when resetting the policy
manager (which defaults to inactive).
* The shared data dir is granted as additional data base directory.
* Due to the lack of entitlement delegation and wipePendingDataDirectories using
server's FileSystemUtils, node base directories won't be removed until
after the test.
* Disable entitlement checks for some command tests.
* Disable entitlement checks for some tests requiring entitlement delegation.
---
.../runtime/policy/PolicyManager.java | 3 -
.../action/admin/ReloadSecureSettingsIT.java | 2 +
.../cluster/tasks/PendingTasksBlocksIT.java | 2 +
.../coordination/RemoveCustomsCommandIT.java | 2 +
.../RemoveIndexSettingsCommandIT.java | 2 +
.../coordination/RemoveSettingsCommandIT.java | 2 +
.../RemoveCorruptedShardDataCommandIT.java | 2 +
.../bootstrap/TestEntitlementBootstrap.java | 64 ++++++++++++++-----
.../runtime/policy/TestPolicyManager.java | 16 +++--
.../java/org/elasticsearch/node/MockNode.java | 9 +--
.../org/elasticsearch/test/ESTestCase.java | 2 +-
.../policy/TestPolicyManagerTests.java | 4 +-
12 files changed, 80 insertions(+), 30 deletions(-)
diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
index 7f0541911284c..0021a10b84c86 100644
--- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
+++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
@@ -367,9 +367,6 @@ private ModuleEntitlements getModuleScopeEntitlements(
* @return true if permission is granted regardless of the entitlement
*/
boolean isTriviallyAllowed(Class> requestingClass) {
- if (generalLogger.isTraceEnabled()) {
- generalLogger.trace("Stack trace for upcoming trivially-allowed check", new Exception());
- }
if (requestingClass == null) {
generalLogger.debug("Entitlement trivially allowed: no caller frames outside the entitlement library");
return true;
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/ReloadSecureSettingsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/ReloadSecureSettingsIT.java
index 83e79ff7f45a8..82a6c1f6acf85 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/ReloadSecureSettingsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/ReloadSecureSettingsIT.java
@@ -27,6 +27,7 @@
import org.elasticsearch.plugins.PluginsService;
import org.elasticsearch.plugins.ReloadablePlugin;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import org.junit.BeforeClass;
import java.io.InputStream;
@@ -47,6 +48,7 @@
import static org.hamcrest.Matchers.nullValue;
@ESIntegTestCase.ClusterScope(minNumDataNodes = 2)
+@ESTestCase.WithoutEntitlements // requires entitlement delegation ES-10920
public class ReloadSecureSettingsIT extends ESIntegTestCase {
private static final String VALID_SECURE_SETTING_NAME = "some.setting.that.exists";
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java
index 6475e80901ea7..bb048179a437a 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java
@@ -12,6 +12,7 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.InternalTestCluster;
import java.util.Arrays;
@@ -22,6 +23,7 @@
import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_READ_ONLY;
import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_READ_ONLY_ALLOW_DELETE;
+@ESTestCase.WithoutEntitlements // requires entitlement delegation ES-10920
public class PendingTasksBlocksIT extends ESIntegTestCase {
public void testPendingTasksWithIndexBlocks() {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveCustomsCommandIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveCustomsCommandIT.java
index 2b2dc114e8ffc..e88a08f983b90 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveCustomsCommandIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveCustomsCommandIT.java
@@ -18,12 +18,14 @@
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import java.util.Map;
import static org.hamcrest.Matchers.containsString;
@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false)
+@ESTestCase.WithoutEntitlements // commands don't run with entitlements enforced
public class RemoveCustomsCommandIT extends ESIntegTestCase {
public void testRemoveCustomsAbortedByUser() throws Exception {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveIndexSettingsCommandIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveIndexSettingsCommandIT.java
index 65e325a1291d8..62afa9c57cbaa 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveIndexSettingsCommandIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveIndexSettingsCommandIT.java
@@ -21,6 +21,7 @@
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import java.util.Collection;
import java.util.List;
@@ -31,6 +32,7 @@
import static org.hamcrest.Matchers.not;
@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false)
+@ESTestCase.WithoutEntitlements // commands don't run with entitlements enforced
public class RemoveIndexSettingsCommandIT extends ESIntegTestCase {
static final Setting FOO = Setting.intSetting("index.foo", 1, Setting.Property.IndexScope, Setting.Property.Dynamic);
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveSettingsCommandIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveSettingsCommandIT.java
index d7ed6bb47b98b..1a68211c6ea8e 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveSettingsCommandIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveSettingsCommandIT.java
@@ -19,6 +19,7 @@
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import java.util.Map;
@@ -27,6 +28,7 @@
import static org.hamcrest.Matchers.not;
@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false)
+@ESTestCase.WithoutEntitlements // commands don't run with entitlements enforced
public class RemoveSettingsCommandIT extends ESIntegTestCase {
public void testRemoveSettingsAbortedByUser() throws Exception {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java
index b9513dfb95187..d1cff7b2a30d1 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java
@@ -58,6 +58,7 @@
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.CorruptionUtils;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.InternalSettingsPlugin;
import org.elasticsearch.test.InternalTestCluster;
import org.elasticsearch.test.engine.MockEngineSupport;
@@ -93,6 +94,7 @@
import static org.hamcrest.Matchers.startsWith;
@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0)
+@ESTestCase.WithoutEntitlements // commands don't run with entitlements enforced
public class RemoveCorruptedShardDataCommandIT extends ESIntegTestCase {
@Override
diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java
index 3e6f09915358b..4ce2702dc178c 100644
--- a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java
+++ b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java
@@ -9,6 +9,7 @@
package org.elasticsearch.entitlement.bootstrap;
+import org.apache.lucene.tests.mockfile.FilterPath;
import org.elasticsearch.bootstrap.TestBuildInfo;
import org.elasticsearch.bootstrap.TestBuildInfoParser;
import org.elasticsearch.bootstrap.TestScopeResolver;
@@ -38,6 +39,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -54,6 +56,7 @@
import static org.elasticsearch.env.Environment.PATH_DATA_SETTING;
import static org.elasticsearch.env.Environment.PATH_HOME_SETTING;
import static org.elasticsearch.env.Environment.PATH_REPO_SETTING;
+import static org.elasticsearch.env.Environment.PATH_SHARED_DATA_SETTING;
public class TestEntitlementBootstrap {
@@ -82,30 +85,46 @@ public static void registerNodeBaseDirs(Settings settings, Path configPath) {
if (policyManager == null) {
return;
}
- Path homeDir = absolutePath(PATH_HOME_SETTING.get(settings));
- Path configDir = configPath != null ? configPath : homeDir.resolve("config");
- Collection dataDirs = dataDirs(settings, homeDir);
- Collection repoDirs = repoDirs(settings);
+ var homeDir = homeDir(settings);
+ var configDir = configDir(configPath, homeDir);
+ var sharedDataDir = sharedDataDir(settings);
+ var dataDirs = dataDirs(settings, homeDir);
+ var repoDirs = repoDirs(settings);
logger.debug("Registering node dirs: config [{}], dataDirs [{}], repoDirs [{}]", configDir, dataDirs, repoDirs);
baseDirPaths.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.add(configDir)));
+ if (sharedDataDir != null) {
+ baseDirPaths.compute(BaseDir.DATA, baseDirModifier(paths -> paths.add(sharedDataDir)));
+ }
baseDirPaths.compute(BaseDir.DATA, baseDirModifier(paths -> paths.addAll(dataDirs)));
baseDirPaths.compute(BaseDir.SHARED_REPO, baseDirModifier(paths -> paths.addAll(repoDirs)));
- policyManager.reset();
+ policyManager.clearModuleEntitlementsCache();
}
public static void unregisterNodeBaseDirs(Settings settings, Path configPath) {
if (policyManager == null) {
return;
}
- Path homeDir = absolutePath(PATH_HOME_SETTING.get(settings));
- Path configDir = configPath != null ? configPath : homeDir.resolve("config");
- Collection dataDirs = dataDirs(settings, homeDir);
- Collection repoDirs = repoDirs(settings);
+ var homeDir = homeDir(settings);
+ var configDir = configDir(configPath, homeDir);
+ var sharedDataDir = sharedDataDir(settings);
+ var dataDirs = dataDirs(settings, homeDir);
+ var repoDirs = repoDirs(settings);
logger.debug("Unregistering node dirs: config [{}], dataDirs [{}], repoDirs [{}]", configDir, dataDirs, repoDirs);
baseDirPaths.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.remove(configDir)));
+ if (sharedDataDir != null) {
+ baseDirPaths.compute(BaseDir.DATA, baseDirModifier(paths -> paths.remove(sharedDataDir)));
+ }
baseDirPaths.compute(BaseDir.DATA, baseDirModifier(paths -> paths.removeAll(dataDirs)));
baseDirPaths.compute(BaseDir.SHARED_REPO, baseDirModifier(paths -> paths.removeAll(repoDirs)));
- policyManager.reset();
+ policyManager.clearModuleEntitlementsCache();
+ }
+
+ private static Path homeDir(Settings settings) {
+ return absolutePath(PATH_HOME_SETTING.get(settings));
+ }
+
+ private static Path configDir(Path configDir, Path homeDir) {
+ return configDir != null ? unwrapFilterPath(configDir) : homeDir.resolve("config");
}
private static Collection dataDirs(Settings settings, Path homeDir) {
@@ -115,20 +134,31 @@ private static Collection dataDirs(Settings settings, Path homeDir) {
: dataDirs.stream().map(TestEntitlementBootstrap::absolutePath).toList();
}
+ private static Path sharedDataDir(Settings settings) {
+ String sharedDataDir = PATH_SHARED_DATA_SETTING.get(settings);
+ return sharedDataDir.isEmpty() ? null : TestEntitlementBootstrap.absolutePath(sharedDataDir);
+ }
+
private static Collection repoDirs(Settings settings) {
return PATH_REPO_SETTING.get(settings).stream().map(TestEntitlementBootstrap::absolutePath).toList();
}
private static BiFunction, Collection> baseDirModifier(Consumer> consumer) {
+ // always return a new unmodifiable copy
return (BaseDir baseDir, Collection paths) -> {
- if (paths == null) {
- paths = new HashSet<>();
- }
+ paths = paths == null ? new HashSet<>() : new HashSet<>(paths);
consumer.accept(paths);
- return paths;
+ return Collections.unmodifiableCollection(paths);
};
}
+ private static Path unwrapFilterPath(Path path) {
+ while (path instanceof FilterPath fPath) {
+ path = fPath.getDelegate();
+ }
+ return path;
+ }
+
@SuppressForbidden(reason = "must be resolved using the default file system, rather then the mocked test file system")
private static Path absolutePath(String path) {
return Paths.get(path).toAbsolutePath().normalize();
@@ -158,9 +188,11 @@ public static void setEntitledTestPackages(String[] entitledTestPackages) {
policyManager.setEntitledTestPackages(entitledTestPackages);
}
- public static void reset() {
+ public static void resetAfterTest() {
+ // reset all base dirs except TEMP, which is initialized just once statically
+ baseDirPaths.keySet().retainAll(List.of(TEMP));
if (policyManager != null) {
- policyManager.reset();
+ policyManager.resetAfterTest();
}
}
diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java b/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java
index b504e51e119f7..bd1e6a468de31 100644
--- a/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java
+++ b/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java
@@ -53,7 +53,7 @@ public TestPolicyManager(
super(serverPolicy, apmAgentEntitlements, pluginPolicies, scopeResolver, name -> classpath, pathLookup);
this.classpath = classpath;
this.testOnlyClasspath = testOnlyClasspath;
- reset();
+ resetAfterTest();
}
public void setActive(boolean newValue) {
@@ -77,13 +77,21 @@ public void setEntitledTestPackages(String... entitledTestPackages) {
/**
* Called between tests so each test is not affected by prior tests
*/
- public final void reset() {
- assert moduleEntitlementsMap.isEmpty() : "We're not supposed to be using moduleEntitlementsMap in tests";
- classEntitlementsMap.clear();
+ public final void resetAfterTest() {
+ clearModuleEntitlementsCache();
isActive = false;
isTriviallyAllowingTestCode = true;
}
+ /**
+ * Clear cached module entitlements.
+ * This is required after updating entries in {@link TestPathLookup}.
+ */
+ public final void clearModuleEntitlementsCache() {
+ assert moduleEntitlementsMap.isEmpty() : "We're not supposed to be using moduleEntitlementsMap in tests";
+ classEntitlementsMap.clear();
+ }
+
@Override
protected boolean isTrustedSystemClass(Class> requestingClass) {
ClassLoader loader = requestingClass.getClassLoader();
diff --git a/test/framework/src/main/java/org/elasticsearch/node/MockNode.java b/test/framework/src/main/java/org/elasticsearch/node/MockNode.java
index 3d5229435e729..be21a1c8ab694 100644
--- a/test/framework/src/main/java/org/elasticsearch/node/MockNode.java
+++ b/test/framework/src/main/java/org/elasticsearch/node/MockNode.java
@@ -54,11 +54,11 @@
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.transport.TransportSettings;
-import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.LongSupplier;
@@ -286,11 +286,12 @@ private static Environment prepareEnvironment(final Settings settings, final Pat
}
@Override
- public synchronized void close() throws IOException {
+ public synchronized boolean awaitClose(long timeout, TimeUnit timeUnit) throws InterruptedException {
try {
- super.close();
+ return super.awaitClose(timeout, timeUnit);
} finally {
- TestEntitlementBootstrap.unregisterNodeBaseDirs(getEnvironment().settings(), getEnvironment().configDir());
+ // wipePendingDataDirectories requires entitlement delegation to work due to this using FileSystemUtils ES-10920
+ // TestEntitlementBootstrap.unregisterNodeBaseDirs(settings(), getEnvironment().configDir());
}
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java
index 6ced34ce72759..d3ea21bac9f88 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java
@@ -546,7 +546,7 @@ public static void setupEntitlementsForClass() {
@AfterClass
public static void resetEntitlements() {
- TestEntitlementBootstrap.reset();
+ TestEntitlementBootstrap.resetAfterTest();
}
// setup mock filesystems for this test run. we change PathUtils
diff --git a/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java b/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java
index 4a62355f398d8..c0f1aaa227cdb 100644
--- a/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java
+++ b/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java
@@ -41,13 +41,13 @@ public void setupPolicyManager() {
policyManager.setActive(true);
}
- public void testReset() {
+ public void testResetAfterTest() {
assertTrue(policyManager.classEntitlementsMap.isEmpty());
assertEquals("example-plugin1", policyManager.getEntitlements(getClass()).componentName());
assertEquals("example-plugin1", policyManager.getEntitlements(getClass()).componentName());
assertFalse(policyManager.classEntitlementsMap.isEmpty());
- policyManager.reset();
+ policyManager.resetAfterTest();
assertTrue(policyManager.classEntitlementsMap.isEmpty());
assertEquals("example-plugin2", policyManager.getEntitlements(getClass()).componentName());
From 3bf8256e3169dd5d2b62c29213c2fc4f504f440c Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Mon, 21 Jul 2025 12:36:19 +0200
Subject: [PATCH 02/15] continue logging stacktrace in isTriviallyAllowed
---
.../entitlement/runtime/policy/PolicyManager.java | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
index 0021a10b84c86..4db0c5e7ce126 100644
--- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
+++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
@@ -376,10 +376,13 @@ boolean isTriviallyAllowed(Class> requestingClass) {
return true;
}
if (isTrustedSystemClass(requestingClass)) {
+ // note: logging prior to allowing trusted system classes can fail if loading of new classes is necessary
generalLogger.debug("Entitlement trivially allowed from system module [{}]", requestingClass.getModule().getName());
return true;
}
- generalLogger.trace("Entitlement not trivially allowed");
+ if (generalLogger.isTraceEnabled()) {
+ generalLogger.trace("Stack trace if entitlement not trivially allowed", new Exception());
+ }
return false;
}
From 707e7927f7187de500daa5d769a6fd4e6aefb6b3 Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Tue, 22 Jul 2025 12:23:35 +0200
Subject: [PATCH 03/15] Run Azure tests without entitlements until ES-12435
---
.../repositories/azure/AzureBlobStoreRepositoryTests.java | 2 ++
.../repositories/azure/AzureRepositoryMissingCredentialsIT.java | 2 ++
2 files changed, 4 insertions(+)
diff --git a/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureBlobStoreRepositoryTests.java b/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureBlobStoreRepositoryTests.java
index c6c1868d5bbc1..493f4d10eea6d 100644
--- a/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureBlobStoreRepositoryTests.java
+++ b/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureBlobStoreRepositoryTests.java
@@ -41,6 +41,7 @@
import org.elasticsearch.telemetry.Measurement;
import org.elasticsearch.telemetry.TestTelemetryPlugin;
import org.elasticsearch.test.BackgroundIndexer;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import java.io.ByteArrayInputStream;
@@ -73,6 +74,7 @@
import static org.hamcrest.Matchers.is;
@SuppressForbidden(reason = "this test uses a HttpServer to emulate an Azure endpoint")
+@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435
public class AzureBlobStoreRepositoryTests extends ESMockAPIBasedRepositoryIntegTestCase {
protected static final String DEFAULT_ACCOUNT_NAME = "account";
diff --git a/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureRepositoryMissingCredentialsIT.java b/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureRepositoryMissingCredentialsIT.java
index 947f73c2ce580..5e3cad4534b7c 100644
--- a/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureRepositoryMissingCredentialsIT.java
+++ b/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureRepositoryMissingCredentialsIT.java
@@ -18,12 +18,14 @@
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.repositories.RepositoryVerificationException;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import java.util.Collection;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
+@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435
public class AzureRepositoryMissingCredentialsIT extends ESIntegTestCase {
@Override
From 491f8b625e7d9fb5e445111a54022d08eaae42d5 Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Tue, 22 Jul 2025 12:40:55 +0200
Subject: [PATCH 04/15] Run inference tests without entitlements until ES-12435
---
.../filter/ShardBulkInferenceActionFilterBasicLicenseIT.java | 2 ++
.../action/filter/ShardBulkInferenceActionFilterIT.java | 2 ++
.../integration/InferenceRevokeDefaultEndpointsIT.java | 2 ++
.../xpack/inference/integration/ModelRegistryIT.java | 2 ++
.../xpack/inference/integration/SemanticTextIndexOptionsIT.java | 2 ++
.../xpack/inference/integration/SemanticTextIndexVersionIT.java | 2 ++
.../inference/rest/ServerSentEventsRestActionListenerTests.java | 2 ++
7 files changed, 14 insertions(+)
diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterBasicLicenseIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterBasicLicenseIT.java
index e2d00b8c52781..33b9adb431a0a 100644
--- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterBasicLicenseIT.java
+++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterBasicLicenseIT.java
@@ -23,6 +23,7 @@
import org.elasticsearch.license.LicenseSettings;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.XPackField;
import org.elasticsearch.xpack.inference.LocalStateInferencePlugin;
import org.elasticsearch.xpack.inference.Utils;
@@ -42,6 +43,7 @@
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
+@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435
public class ShardBulkInferenceActionFilterBasicLicenseIT extends ESIntegTestCase {
public static final String INDEX_NAME = "test-index";
diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java
index 8405fba22460f..7ddbf4fc55ffd 100644
--- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java
+++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java
@@ -32,6 +32,7 @@
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.InternalTestCluster;
import org.elasticsearch.xpack.inference.InferenceIndex;
import org.elasticsearch.xpack.inference.LocalStateInferencePlugin;
@@ -56,6 +57,7 @@
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
+@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435
public class ShardBulkInferenceActionFilterIT extends ESIntegTestCase {
public static final String INDEX_NAME = "test-index";
diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/InferenceRevokeDefaultEndpointsIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/InferenceRevokeDefaultEndpointsIT.java
index 1eb530ac1bb9e..a0055332a36c2 100644
--- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/InferenceRevokeDefaultEndpointsIT.java
+++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/InferenceRevokeDefaultEndpointsIT.java
@@ -20,6 +20,7 @@
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.reindex.ReindexPlugin;
import org.elasticsearch.test.ESSingleNodeTestCase;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.http.MockResponse;
import org.elasticsearch.test.http.MockWebServer;
import org.elasticsearch.threadpool.ThreadPool;
@@ -47,6 +48,7 @@
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.mockito.Mockito.mock;
+@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435
public class InferenceRevokeDefaultEndpointsIT extends ESSingleNodeTestCase {
private static final TimeValue TIMEOUT = new TimeValue(30, TimeUnit.SECONDS);
diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java
index e56782bd00ef5..3c09e73c55411 100644
--- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java
+++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java
@@ -34,6 +34,7 @@
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.reindex.ReindexPlugin;
import org.elasticsearch.test.ESSingleNodeTestCase;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xcontent.ToXContentObject;
import org.elasticsearch.xcontent.XContentBuilder;
@@ -74,6 +75,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435
public class ModelRegistryIT extends ESSingleNodeTestCase {
private static final TimeValue TIMEOUT = new TimeValue(30, TimeUnit.SECONDS);
diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexOptionsIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexOptionsIT.java
index 035ec4dc9593d..08d3f0a6a9b9f 100644
--- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexOptionsIT.java
+++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexOptionsIT.java
@@ -30,6 +30,7 @@
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
import org.elasticsearch.reindex.ReindexPlugin;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.InternalTestCluster;
import org.elasticsearch.xcontent.ToXContent;
import org.elasticsearch.xcontent.XContentBuilder;
@@ -55,6 +56,7 @@
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.CoreMatchers.equalTo;
+@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435
public class SemanticTextIndexOptionsIT extends ESIntegTestCase {
private static final String INDEX_NAME = "test-index";
private static final Map BBQ_COMPATIBLE_SERVICE_SETTINGS = Map.of(
diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexVersionIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexVersionIT.java
index 6f8992b5bd200..8986b0a158e9f 100644
--- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexVersionIT.java
+++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexVersionIT.java
@@ -24,6 +24,7 @@
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.index.IndexVersionUtils;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;
@@ -50,6 +51,7 @@
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse;
import static org.hamcrest.Matchers.equalTo;
+@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435
public class SemanticTextIndexVersionIT extends ESIntegTestCase {
private static final int MAXIMUM_NUMBER_OF_VERSIONS_TO_TEST = 25;
private static final String SPARSE_SEMANTIC_FIELD = "sparse_field";
diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/rest/ServerSentEventsRestActionListenerTests.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/rest/ServerSentEventsRestActionListenerTests.java
index 87e14e4e89d88..3287995588d86 100644
--- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/rest/ServerSentEventsRestActionListenerTests.java
+++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/rest/ServerSentEventsRestActionListenerTests.java
@@ -45,6 +45,7 @@
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.ESIntegTestCase;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xcontent.ToXContent;
import org.elasticsearch.xpack.core.inference.action.InferenceAction;
@@ -75,6 +76,7 @@
import static org.hamcrest.Matchers.nullValue;
@ESIntegTestCase.ClusterScope(numDataNodes = 1)
+@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435
public class ServerSentEventsRestActionListenerTests extends ESIntegTestCase {
private static final String INFERENCE_ROUTE = "/_inference";
private static final String REQUEST_COUNT = "request_count";
From 02757bf24bf1237b37e8bf4b8d5dd1a449b6c6c2 Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Tue, 22 Jul 2025 14:55:08 +0200
Subject: [PATCH 05/15] Don't log exception in isTriviallyAllowed
---
.../entitlement/runtime/policy/PolicyManager.java | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
index 4db0c5e7ce126..df68da2e251ac 100644
--- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
+++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
@@ -367,6 +367,7 @@ private ModuleEntitlements getModuleScopeEntitlements(
* @return true if permission is granted regardless of the entitlement
*/
boolean isTriviallyAllowed(Class> requestingClass) {
+ // note: do not log exceptions in here, this could interfere with loading of additionally necessary classes such as ThrowableProxy
if (requestingClass == null) {
generalLogger.debug("Entitlement trivially allowed: no caller frames outside the entitlement library");
return true;
@@ -376,13 +377,10 @@ boolean isTriviallyAllowed(Class> requestingClass) {
return true;
}
if (isTrustedSystemClass(requestingClass)) {
- // note: logging prior to allowing trusted system classes can fail if loading of new classes is necessary
generalLogger.debug("Entitlement trivially allowed from system module [{}]", requestingClass.getModule().getName());
return true;
}
- if (generalLogger.isTraceEnabled()) {
- generalLogger.trace("Stack trace if entitlement not trivially allowed", new Exception());
- }
+ generalLogger.trace("Entitlement not trivially allowed");
return false;
}
From ca4fd4b919d982073bb5642ce6f59fbc3ff356dc Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Wed, 23 Jul 2025 11:59:01 +0200
Subject: [PATCH 06/15] Grant file entitlements for shared data dir to ES base
due to lack of entitlement delegation
---
.../entitlement/bootstrap/HardcodedEntitlements.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
index 278b9e773ae1f..de5d690796b6d 100644
--- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
+++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
@@ -92,8 +92,9 @@ private static List createServerEntitlements(Path pidFile) {
new CreateClassLoaderEntitlement(),
new FilesEntitlement(
List.of(
- // TODO: what in es.base is accessing shared repo?
+ // necessary due to lack of delegation ES-12382
FilesEntitlement.FileData.ofBaseDirPath(SHARED_REPO, READ_WRITE),
+ FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE),
FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE)
)
)
From eac1835c23f52773a8c6238de579c0c086ae6d5d Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Wed, 23 Jul 2025 13:20:12 +0200
Subject: [PATCH 07/15] grant shared_data dir to lucene
---
.../entitlement/bootstrap/HardcodedEntitlements.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
index de5d690796b6d..a1a63020f3231 100644
--- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
+++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
@@ -123,6 +123,7 @@ private static List createServerEntitlements(Path pidFile) {
new FilesEntitlement(
List.of(
FilesEntitlement.FileData.ofBaseDirPath(CONFIG, READ),
+ FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ),
FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE)
)
)
@@ -131,6 +132,7 @@ private static List createServerEntitlements(Path pidFile) {
new Scope(
"org.apache.lucene.misc",
List.of(
+ new FilesEntitlement(List.of(FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE))),
new FilesEntitlement(List.of(FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE))),
new ReadStoreAttributesEntitlement()
)
@@ -146,6 +148,7 @@ private static List createServerEntitlements(Path pidFile) {
"org.elasticsearch.nativeaccess",
List.of(
new LoadNativeLibrariesEntitlement(),
+ new FilesEntitlement(List.of(FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE))),
new FilesEntitlement(List.of(FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE)))
)
)
From b85aff5140c21854250501235c8428cd62089c58 Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Wed, 23 Jul 2025 13:47:26 +0200
Subject: [PATCH 08/15] fix
---
.../bootstrap/HardcodedEntitlements.java | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
index a1a63020f3231..79571482480fc 100644
--- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
+++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
@@ -132,8 +132,12 @@ private static List createServerEntitlements(Path pidFile) {
new Scope(
"org.apache.lucene.misc",
List.of(
- new FilesEntitlement(List.of(FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE))),
- new FilesEntitlement(List.of(FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE))),
+ new FilesEntitlement(
+ List.of(
+ FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE),
+ FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE)
+ )
+ ),
new ReadStoreAttributesEntitlement()
)
),
@@ -148,8 +152,12 @@ private static List createServerEntitlements(Path pidFile) {
"org.elasticsearch.nativeaccess",
List.of(
new LoadNativeLibrariesEntitlement(),
- new FilesEntitlement(List.of(FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE))),
- new FilesEntitlement(List.of(FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE)))
+ new FilesEntitlement(
+ List.of(
+ FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE),
+ FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE)
+ )
+ )
)
)
);
From 7b7a7f3b022bd31c373b30d1a71d2f9b444f1216 Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Wed, 23 Jul 2025 14:22:54 +0200
Subject: [PATCH 09/15] fix
---
.../entitlement/bootstrap/HardcodedEntitlements.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
index 79571482480fc..01e1092f53d00 100644
--- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
+++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java
@@ -123,7 +123,7 @@ private static List createServerEntitlements(Path pidFile) {
new FilesEntitlement(
List.of(
FilesEntitlement.FileData.ofBaseDirPath(CONFIG, READ),
- FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ),
+ FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE),
FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE)
)
)
From 6351ddef892d19d4084bd5adfe5912cec761a3df Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Thu, 24 Jul 2025 09:08:18 +0200
Subject: [PATCH 10/15] logging
---
.../bootstrap/TestEntitlementBootstrap.java | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java
index 2343b283eaec1..d9c10d0d251e5 100644
--- a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java
+++ b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java
@@ -91,7 +91,13 @@ public static void registerNodeBaseDirs(Settings settings, Path configPath) {
Collection dataDirs = dataDirs(settings, homeDir);
Collection sharedDataDir = sharedDataDir(settings);
Collection repoDirs = repoDirs(settings);
- logger.debug("Registering node dirs: config [{}], dataDirs [{}], repoDirs [{}]", configDir, dataDirs, repoDirs);
+ logger.debug(
+ "Registering node dirs: config [{}], dataDirs [{}], sharedDataDir [{}], repoDirs [{}]",
+ configDir,
+ dataDirs,
+ sharedDataDir,
+ repoDirs
+ );
baseDirPaths.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.add(configDir)));
baseDirPaths.compute(BaseDir.DATA, baseDirModifier(paths -> paths.addAll(dataDirs)));
baseDirPaths.compute(BaseDir.SHARED_DATA, baseDirModifier(paths -> paths.addAll(sharedDataDir)));
@@ -109,7 +115,13 @@ public static void unregisterNodeBaseDirs(Settings settings, Path configPath) {
Collection dataDirs = dataDirs(settings, homeDir);
Collection sharedDataDir = sharedDataDir(settings);
Collection repoDirs = repoDirs(settings);
- logger.debug("Unregistering node dirs: config [{}], dataDirs [{}], repoDirs [{}]", configDir, dataDirs, repoDirs);
+ logger.debug(
+ "Unregistering node dirs: config [{}], dataDirs [{}], sharedDataDir [{}], repoDirs [{}]",
+ configDir,
+ dataDirs,
+ sharedDataDir,
+ repoDirs
+ );
baseDirPaths.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.remove(configDir)));
baseDirPaths.compute(BaseDir.DATA, baseDirModifier(paths -> paths.removeAll(dataDirs)));
baseDirPaths.compute(BaseDir.SHARED_DATA, baseDirModifier(paths -> paths.removeAll(sharedDataDir)));
From 43f856e13e449693efa0165c66ac3f8133bcb045 Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Thu, 24 Jul 2025 09:13:15 +0200
Subject: [PATCH 11/15] grant shared_data dir to plugins that have a grant on
data dir indices/
---
.../store-smb/src/main/plugin-metadata/entitlement-policy.yaml | 3 +++
.../src/main/plugin-metadata/entitlement-policy.yaml | 3 +++
2 files changed, 6 insertions(+)
diff --git a/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml b/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml
index 1022253171a11..dbe45c7527967 100644
--- a/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml
+++ b/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml
@@ -3,3 +3,6 @@ ALL-UNNAMED:
- relative_path: "indices/"
relative_to: data
mode: read_write
+ - relative_path: ""
+ relative_to: shared_data
+ mode: read_write
diff --git a/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml b/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml
index 69eead6707114..d21ee299b832d 100644
--- a/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml
+++ b/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml
@@ -6,3 +6,6 @@ org.elasticsearch.searchablesnapshots:
- relative_path: indices
relative_to: data
mode: read_write
+ - relative_path: ""
+ relative_to: shared_data
+ mode: read_write
From 35dfa0d33df21822037433c6391235f0f9ec2ee8 Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Thu, 24 Jul 2025 09:58:27 +0200
Subject: [PATCH 12/15] fix parser
---
.../runtime/policy/entitlements/FilesEntitlement.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java
index 872a083a76ba6..cc9ef9d263dd1 100644
--- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java
+++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java
@@ -182,8 +182,9 @@ private static BaseDir parseBaseDir(String baseDir) {
case "config" -> BaseDir.CONFIG;
case "data" -> BaseDir.DATA;
case "home" -> BaseDir.USER_HOME;
+ case "shared_data" -> BaseDir.SHARED_DATA;
// it would be nice to limit this to just ES modules, but we don't have a way to plumb that through to here
- // however, we still don't document in the error case below that shared_repo is valid
+ // however, we still don't document in the error case below that shared_repo and shared_data is valid
case "shared_repo" -> BaseDir.SHARED_REPO;
default -> throw new PolicyValidationException(
"invalid relative directory: " + baseDir + ", valid values: [config, data, home]"
From d657f0e30269f2702355bd71ea7500d6d7132bf7 Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Mon, 28 Jul 2025 13:35:05 +0200
Subject: [PATCH 13/15] Better isolation using closable node grant
---
.../bootstrap/EntitlementMetaTests.java | 5 +-
.../WithEntitlementsOnTestCodeMetaTests.java | 6 +-
.../indices/IndicesServiceCloseTests.java | 3 +-
.../bootstrap/BootstrapForTesting.java | 4 +-
...otstrap.java => TestEntitlementsRule.java} | 271 ++++++++++--------
.../runtime/policy/TestPolicyManager.java | 21 +-
.../java/org/elasticsearch/node/MockNode.java | 24 +-
.../elasticsearch/test/ESIntegTestCase.java | 4 +-
.../test/ESSingleNodeTestCase.java | 2 +-
.../org/elasticsearch/test/ESTestCase.java | 29 +-
.../test/InternalTestCluster.java | 23 +-
11 files changed, 214 insertions(+), 178 deletions(-)
rename test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/{TestEntitlementBootstrap.java => TestEntitlementsRule.java} (52%)
diff --git a/server/src/test/java/org/elasticsearch/bootstrap/EntitlementMetaTests.java b/server/src/test/java/org/elasticsearch/bootstrap/EntitlementMetaTests.java
index 04e59e5476f3b..230e105feca18 100644
--- a/server/src/test/java/org/elasticsearch/bootstrap/EntitlementMetaTests.java
+++ b/server/src/test/java/org/elasticsearch/bootstrap/EntitlementMetaTests.java
@@ -10,9 +10,8 @@
package org.elasticsearch.bootstrap;
import org.elasticsearch.core.SuppressForbidden;
-import org.elasticsearch.entitlement.bootstrap.TestEntitlementBootstrap;
+import org.elasticsearch.entitlement.bootstrap.TestEntitlementsRule;
import org.elasticsearch.test.ESTestCase;
-import org.elasticsearch.test.ESTestCase.WithEntitlementsOnTestCode;
import java.io.IOException;
import java.nio.file.Path;
@@ -42,7 +41,7 @@
*/
public class EntitlementMetaTests extends ESTestCase {
public void testSelfTestPasses() {
- assumeTrue("Not yet working in serverless", TestEntitlementBootstrap.isEnabledForTest());
+ assumeTrue("Not yet working in serverless", TestEntitlementsRule.isEnabledForTest());
Elasticsearch.entitlementSelfTest();
}
diff --git a/server/src/test/java/org/elasticsearch/bootstrap/WithEntitlementsOnTestCodeMetaTests.java b/server/src/test/java/org/elasticsearch/bootstrap/WithEntitlementsOnTestCodeMetaTests.java
index c126490922e48..14e3803fa6bad 100644
--- a/server/src/test/java/org/elasticsearch/bootstrap/WithEntitlementsOnTestCodeMetaTests.java
+++ b/server/src/test/java/org/elasticsearch/bootstrap/WithEntitlementsOnTestCodeMetaTests.java
@@ -10,7 +10,7 @@
package org.elasticsearch.bootstrap;
import org.elasticsearch.core.SuppressForbidden;
-import org.elasticsearch.entitlement.bootstrap.TestEntitlementBootstrap;
+import org.elasticsearch.entitlement.bootstrap.TestEntitlementsRule;
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.ESTestCase.WithEntitlementsOnTestCode;
@@ -30,13 +30,13 @@ public class WithEntitlementsOnTestCodeMetaTests extends ESTestCase {
* is called from server code. The self-test should pass as usual.
*/
public void testSelfTestPasses() {
- assumeTrue("Not yet working in serverless", TestEntitlementBootstrap.isEnabledForTest());
+ assumeTrue("Not yet working in serverless", TestEntitlementsRule.isEnabledForTest());
Elasticsearch.entitlementSelfTest();
}
@SuppressForbidden(reason = "Testing that a forbidden API is disallowed")
public void testForbiddenActionDenied() {
- assumeTrue("Not yet working in serverless", TestEntitlementBootstrap.isEnabledForTest());
+ assumeTrue("Not yet working in serverless", TestEntitlementsRule.isEnabledForTest());
assertThrows(NotEntitledException.class, () -> Path.of(".").toRealPath());
}
}
diff --git a/server/src/test/java/org/elasticsearch/indices/IndicesServiceCloseTests.java b/server/src/test/java/org/elasticsearch/indices/IndicesServiceCloseTests.java
index a48ba63882734..78df6c9e88c65 100644
--- a/server/src/test/java/org/elasticsearch/indices/IndicesServiceCloseTests.java
+++ b/server/src/test/java/org/elasticsearch/indices/IndicesServiceCloseTests.java
@@ -77,7 +77,8 @@ private Node startNode() throws NodeValidationException {
Node node = new MockNode(
settings,
Arrays.asList(getTestTransportPlugin(), MockHttpTransport.TestPlugin.class, InternalSettingsPlugin.class),
- true
+ true,
+ () -> {}
);
node.start();
return node;
diff --git a/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java b/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java
index bf53f14bc9e46..69dbb7061c948 100644
--- a/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java
+++ b/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java
@@ -15,7 +15,7 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.core.Booleans;
import org.elasticsearch.core.PathUtils;
-import org.elasticsearch.entitlement.bootstrap.TestEntitlementBootstrap;
+import org.elasticsearch.entitlement.bootstrap.TestEntitlementsRule;
import org.elasticsearch.jdk.JarHell;
import java.io.IOException;
@@ -75,7 +75,7 @@ public class BootstrapForTesting {
// Fire up entitlements
try {
- TestEntitlementBootstrap.bootstrap(javaTmpDir);
+ TestEntitlementsRule.initialize(javaTmpDir);
} catch (IOException e) {
throw new IllegalStateException(e.getClass().getSimpleName() + " while initializing entitlements for tests", e);
}
diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java
similarity index 52%
rename from test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java
rename to test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java
index d9c10d0d251e5..579ee9b224dd6 100644
--- a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java
+++ b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java
@@ -9,6 +9,7 @@
package org.elasticsearch.entitlement.bootstrap;
+import org.apache.lucene.search.Multiset;
import org.apache.lucene.tests.mockfile.FilterPath;
import org.elasticsearch.bootstrap.TestBuildInfo;
import org.elasticsearch.bootstrap.TestBuildInfoParser;
@@ -16,20 +17,25 @@
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.core.Booleans;
-import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.PathUtils;
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.entitlement.initialization.EntitlementInitialization;
import org.elasticsearch.entitlement.runtime.policy.PathLookup;
import org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir;
import org.elasticsearch.entitlement.runtime.policy.Policy;
+import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
import org.elasticsearch.entitlement.runtime.policy.PolicyParser;
import org.elasticsearch.entitlement.runtime.policy.TestPathLookup;
import org.elasticsearch.entitlement.runtime.policy.TestPolicyManager;
import org.elasticsearch.logging.LogManager;
import org.elasticsearch.logging.Logger;
import org.elasticsearch.plugins.PluginDescriptor;
+import org.elasticsearch.test.ESTestCase;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
@@ -41,7 +47,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -58,152 +63,180 @@
import static org.elasticsearch.env.Environment.PATH_REPO_SETTING;
import static org.elasticsearch.env.Environment.PATH_SHARED_DATA_SETTING;
-public class TestEntitlementBootstrap {
+public class TestEntitlementsRule implements TestRule {
+ private static final Logger logger = LogManager.getLogger(TestEntitlementsRule.class);
- private static final Logger logger = LogManager.getLogger(TestEntitlementBootstrap.class);
+ private static final Map> BASE_DIR_PATHS = new ConcurrentHashMap<>();
+ private static final TestPolicyManager POLICY_MANAGER;
- private static Map> baseDirPaths = new ConcurrentHashMap<>();
- private static TestPolicyManager policyManager;
-
- /**
- * Activates entitlement checking in tests.
- */
- public static void bootstrap(@Nullable Path tempDir) throws IOException {
- if (isEnabledForTest() == false) {
- return;
+ static {
+ PathLookup pathLookup = new TestPathLookup(BASE_DIR_PATHS);
+ try {
+ if (isEnabledForTest()) {
+ POLICY_MANAGER = createPolicyManager(pathLookup);
+ loadAgent(POLICY_MANAGER, pathLookup);
+ } else {
+ POLICY_MANAGER = null;
+ }
+ } catch (IOException e) {
+ throw new AssertionError(e);
}
- var previousTempDir = baseDirPaths.put(TEMP, zeroOrOne(tempDir));
- assert previousTempDir == null : "Test entitlement bootstrap called multiple times";
- TestPathLookup pathLookup = new TestPathLookup(baseDirPaths);
- policyManager = createPolicyManager(pathLookup);
- EntitlementInitialization.initializeArgs = new EntitlementInitialization.InitializeArgs(pathLookup, Set.of(), policyManager);
- logger.debug("Loading entitlement agent");
- EntitlementBootstrap.loadAgent(EntitlementBootstrap.findAgentJar(), EntitlementInitialization.class.getName());
}
- public static void registerNodeBaseDirs(Settings settings, Path configPath) {
- if (policyManager == null) {
- return;
+ public static void initialize(Path tempDir) throws IOException {
+ if (POLICY_MANAGER != null) {
+ var previousTempDir = BASE_DIR_PATHS.put(TEMP, List.of(tempDir));
+ assert previousTempDir == null : "Test entitlement bootstrap called multiple times";
}
-
- Path homeDir = homeDir(settings);
- Path configDir = configDir(configPath, homeDir);
- Collection dataDirs = dataDirs(settings, homeDir);
- Collection sharedDataDir = sharedDataDir(settings);
- Collection repoDirs = repoDirs(settings);
- logger.debug(
- "Registering node dirs: config [{}], dataDirs [{}], sharedDataDir [{}], repoDirs [{}]",
- configDir,
- dataDirs,
- sharedDataDir,
- repoDirs
- );
- baseDirPaths.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.add(configDir)));
- baseDirPaths.compute(BaseDir.DATA, baseDirModifier(paths -> paths.addAll(dataDirs)));
- baseDirPaths.compute(BaseDir.SHARED_DATA, baseDirModifier(paths -> paths.addAll(sharedDataDir)));
- baseDirPaths.compute(BaseDir.SHARED_REPO, baseDirModifier(paths -> paths.addAll(repoDirs)));
- policyManager.clearModuleEntitlementsCache();
}
- public static void unregisterNodeBaseDirs(Settings settings, Path configPath) {
- if (policyManager == null) {
- return;
+ @Override
+ public Statement apply(Statement base, Description description) {
+ assert description.isSuite() : "must be used as ClassRule";
+
+ // class / suite level
+ boolean withoutEntitlements = description.getAnnotation(ESTestCase.WithoutEntitlements.class) != null;
+ boolean withEntitlementsOnTestCode = description.getAnnotation(ESTestCase.WithEntitlementsOnTestCode.class) != null;
+ var entitledPackages = description.getAnnotation(ESTestCase.EntitledTestPackages.class);
+
+ if (POLICY_MANAGER != null) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ POLICY_MANAGER.setActive(false == withoutEntitlements);
+ POLICY_MANAGER.setTriviallyAllowingTestCode(false == withEntitlementsOnTestCode);
+ if (entitledPackages != null) {
+ assert entitledPackages.value().length > 0 : "No test packages specified in @EntitledTestPackages";
+ POLICY_MANAGER.setEntitledTestPackages(entitledPackages.value());
+ } else {
+ POLICY_MANAGER.setEntitledTestPackages();
+ }
+ BASE_DIR_PATHS.keySet().retainAll(List.of(TEMP));
+ POLICY_MANAGER.clearModuleEntitlementsCache();
+ // evaluate the suite
+ base.evaluate();
+ } finally {
+ POLICY_MANAGER.setActive(false);
+ POLICY_MANAGER.setTriviallyAllowingTestCode(true);
+ POLICY_MANAGER.setEntitledTestPackages();
+ BASE_DIR_PATHS.keySet().retainAll(List.of(TEMP));
+ POLICY_MANAGER.clearModuleEntitlementsCache();
+ }
+ }
+ };
+ } else if (withEntitlementsOnTestCode) {
+ throw new AssertionError(
+ "Cannot use @WithEntitlementsOnTestCode on tests that are not configured to use entitlements for testing"
+ );
+ } else {
+ return base;
}
-
- Path homeDir = homeDir(settings);
- Path configDir = configDir(configPath, homeDir);
- Collection dataDirs = dataDirs(settings, homeDir);
- Collection sharedDataDir = sharedDataDir(settings);
- Collection repoDirs = repoDirs(settings);
- logger.debug(
- "Unregistering node dirs: config [{}], dataDirs [{}], sharedDataDir [{}], repoDirs [{}]",
- configDir,
- dataDirs,
- sharedDataDir,
- repoDirs
- );
- baseDirPaths.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.remove(configDir)));
- baseDirPaths.compute(BaseDir.DATA, baseDirModifier(paths -> paths.removeAll(dataDirs)));
- baseDirPaths.compute(BaseDir.SHARED_DATA, baseDirModifier(paths -> paths.removeAll(sharedDataDir)));
- baseDirPaths.compute(BaseDir.SHARED_REPO, baseDirModifier(paths -> paths.removeAll(repoDirs)));
- policyManager.clearModuleEntitlementsCache();
}
- private static Path homeDir(Settings settings) {
- return absolutePath(PATH_HOME_SETTING.get(settings));
- }
+ /**
+ * Creates a entitlement grant for node specific paths.
+ */
+ public Closeable newNodeGrant(Settings settings, Path configPath) {
+ if (POLICY_MANAGER == null) {
+ return () -> {}; // noop if not running with entitlements
+ }
- private static Path configDir(Path configDir, Path homeDir) {
- return configDir != null ? unwrapFilterPath(configDir) : homeDir.resolve("config");
+ var unwrappedConfigPath = configPath;
+ while (unwrappedConfigPath instanceof FilterPath fPath) {
+ unwrappedConfigPath = fPath.getDelegate();
+ }
+ NodeGrant nodeGrant = new NodeGrant(settings, unwrappedConfigPath, this::revokeGrant);
+ addGrant(nodeGrant);
+ return nodeGrant;
}
- private static Collection dataDirs(Settings settings, Path homeDir) {
- List dataDirs = PATH_DATA_SETTING.get(settings);
- return dataDirs.isEmpty()
- ? List.of(homeDir.resolve("data"))
- : dataDirs.stream().map(TestEntitlementBootstrap::absolutePath).toList();
+ /**
+ * Revoke all open node grants.
+ */
+ public void revokeNodeGrants() {
+ BASE_DIR_PATHS.keySet().retainAll(List.of(TEMP));
+ POLICY_MANAGER.clearModuleEntitlementsCache();
}
- private static Collection sharedDataDir(Settings settings) {
- String sharedDataDir = PATH_SHARED_DATA_SETTING.get(settings);
- return Strings.hasText(sharedDataDir) ? List.of(absolutePath(sharedDataDir)) : List.of();
- }
+ private record NodeGrant(Settings settings, Path configPath, Consumer onClose) implements Closeable {
+ private Path homeDir() {
+ return absolutePath(PATH_HOME_SETTING.get(settings));
+ }
- private static Collection repoDirs(Settings settings) {
- return PATH_REPO_SETTING.get(settings).stream().map(TestEntitlementBootstrap::absolutePath).toList();
- }
+ private Path configDir() {
+ return configPath != null ? configPath : homeDir().resolve("config");
+ }
- private static BiFunction, Collection> baseDirModifier(Consumer> consumer) {
- // always return a new unmodifiable copy
- return (BaseDir baseDir, Collection paths) -> {
- paths = paths == null ? new HashSet<>() : new HashSet<>(paths);
- consumer.accept(paths);
- return Collections.unmodifiableCollection(paths);
- };
- }
+ private Collection dataDirs() {
+ List dataDirs = PATH_DATA_SETTING.get(settings);
+ return dataDirs.isEmpty() ? List.of(homeDir().resolve("data")) : dataDirs.stream().map(NodeGrant::absolutePath).toList();
+ }
- private static Path unwrapFilterPath(Path path) {
- while (path instanceof FilterPath fPath) {
- path = fPath.getDelegate();
+ private Collection sharedDataDir() {
+ String sharedDataDir = PATH_SHARED_DATA_SETTING.get(settings);
+ return Strings.hasText(sharedDataDir) ? List.of(absolutePath(sharedDataDir)) : List.of();
}
- return path;
- }
- @SuppressForbidden(reason = "must be resolved using the default file system, rather then the mocked test file system")
- private static Path absolutePath(String path) {
- return Paths.get(path).toAbsolutePath().normalize();
- }
+ private Collection repoDirs() {
+ return PATH_REPO_SETTING.get(settings).stream().map(NodeGrant::absolutePath).toList();
+ }
- private static List zeroOrOne(T item) {
- if (item == null) {
- return List.of();
- } else {
- return List.of(item);
+ @SuppressForbidden(reason = "must be resolved using the default file system, rather then the mocked test file system")
+ private static Path absolutePath(String path) {
+ return Paths.get(path).toAbsolutePath().normalize();
}
- }
- public static boolean isEnabledForTest() {
- return Booleans.parseBoolean(System.getProperty("es.entitlement.enableForTests", "false"));
+ @Override
+ public void close() {
+ onClose.accept(this);
+ }
+
+ @Override
+ public String toString() {
+ return Strings.format(
+ "NodeGrant[configDir=%s, dataDirs=%s, sharedDataDir=%s, repoDirs=%s]",
+ configDir(),
+ dataDirs(),
+ sharedDataDir(),
+ repoDirs()
+ );
+ }
}
- public static void setActive(boolean newValue) {
- policyManager.setActive(newValue);
+ private void addGrant(NodeGrant nodeGrant) {
+ logger.error("Adding node grant: {}", nodeGrant);
+ BASE_DIR_PATHS.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.add(nodeGrant.configDir())));
+ BASE_DIR_PATHS.compute(BaseDir.DATA, baseDirModifier(paths -> paths.addAll(nodeGrant.dataDirs())));
+ BASE_DIR_PATHS.compute(BaseDir.SHARED_DATA, baseDirModifier(paths -> paths.addAll(nodeGrant.sharedDataDir())));
+ BASE_DIR_PATHS.compute(BaseDir.SHARED_REPO, baseDirModifier(paths -> paths.addAll(nodeGrant.repoDirs())));
+ POLICY_MANAGER.clearModuleEntitlementsCache();
}
- public static void setTriviallyAllowingTestCode(boolean newValue) {
- policyManager.setTriviallyAllowingTestCode(newValue);
+ private void revokeGrant(NodeGrant nodeGrant) {
+ logger.error("Revoking node grant: {}", nodeGrant);
+ BASE_DIR_PATHS.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.remove(nodeGrant.configDir())));
+ BASE_DIR_PATHS.compute(BaseDir.DATA, baseDirModifier(paths -> paths.removeAll(nodeGrant.dataDirs())));
+ BASE_DIR_PATHS.compute(BaseDir.SHARED_DATA, baseDirModifier(paths -> paths.removeAll(nodeGrant.sharedDataDir())));
+ BASE_DIR_PATHS.compute(BaseDir.SHARED_REPO, baseDirModifier(paths -> paths.removeAll(nodeGrant.repoDirs())));
+ POLICY_MANAGER.clearModuleEntitlementsCache();
}
- public static void setEntitledTestPackages(String[] entitledTestPackages) {
- policyManager.setEntitledTestPackages(entitledTestPackages);
+ // this uses a counting multiset to allow duplicates between nodes, e.g. the config dir
+ private static BiFunction, Collection> baseDirModifier(Consumer> consumer) {
+ // always return a new unmodifiable copy
+ return (BaseDir baseDir, Collection paths) -> {
+ Collection newPaths = new Multiset<>();
+ if(paths != null){
+ newPaths.addAll(paths);
+ }
+ consumer.accept(newPaths);
+ return Collections.unmodifiableCollection(newPaths);
+ };
}
- public static void resetAfterTest() {
- // reset all base dirs except TEMP, which is initialized just once statically
- baseDirPaths.keySet().retainAll(List.of(TEMP));
- if (policyManager != null) {
- policyManager.resetAfterTest();
- }
+ public static boolean isEnabledForTest() {
+ return Booleans.parseBoolean(System.getProperty("es.entitlement.enableForTests", "false"));
}
private static TestPolicyManager createPolicyManager(PathLookup pathLookup) throws IOException {
@@ -261,6 +294,12 @@ private static TestPolicyManager createPolicyManager(PathLookup pathLookup) thro
);
}
+ private static void loadAgent(PolicyManager policyManager, PathLookup pathLookup) {
+ logger.debug("Loading entitlement agent");
+ EntitlementInitialization.initializeArgs = new EntitlementInitialization.InitializeArgs(pathLookup, Set.of(), policyManager);
+ EntitlementBootstrap.loadAgent(EntitlementBootstrap.findAgentJar(), EntitlementInitialization.class.getName());
+ }
+
private static Map parsePluginsPolicies(List pluginsData) {
Map policies = new HashMap<>();
for (var pluginData : pluginsData) {
diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java b/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java
index bd1e6a468de31..5eac4bad38bc1 100644
--- a/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java
+++ b/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java
@@ -29,8 +29,8 @@
public class TestPolicyManager extends PolicyManager {
- boolean isActive;
- boolean isTriviallyAllowingTestCode;
+ boolean isActive = true;
+ boolean isTriviallyAllowingTestCode = true;
String[] entitledTestPackages = TEST_FRAMEWORK_PACKAGE_PREFIXES;
/**
@@ -53,7 +53,6 @@ public TestPolicyManager(
super(serverPolicy, apmAgentEntitlements, pluginPolicies, scopeResolver, name -> classpath, pathLookup);
this.classpath = classpath;
this.testOnlyClasspath = testOnlyClasspath;
- resetAfterTest();
}
public void setActive(boolean newValue) {
@@ -65,24 +64,20 @@ public void setTriviallyAllowingTestCode(boolean newValue) {
}
public void setEntitledTestPackages(String... entitledTestPackages) {
+ if (entitledTestPackages == null || entitledTestPackages.length == 0) {
+ this.entitledTestPackages = TEST_FRAMEWORK_PACKAGE_PREFIXES; // already validated and sorted
+ return;
+ }
+
assertNoRedundantPrefixes(TEST_FRAMEWORK_PACKAGE_PREFIXES, entitledTestPackages, false);
if (entitledTestPackages.length > 1) {
assertNoRedundantPrefixes(entitledTestPackages, entitledTestPackages, true);
}
- String[] packages = ArrayUtils.concat(this.entitledTestPackages, entitledTestPackages);
+ String[] packages = ArrayUtils.concat(TEST_FRAMEWORK_PACKAGE_PREFIXES, entitledTestPackages);
Arrays.sort(packages);
this.entitledTestPackages = packages;
}
- /**
- * Called between tests so each test is not affected by prior tests
- */
- public final void resetAfterTest() {
- clearModuleEntitlementsCache();
- isActive = false;
- isTriviallyAllowingTestCode = true;
- }
-
/**
* Clear cached module entitlements.
* This is required after updating entries in {@link TestPathLookup}.
diff --git a/test/framework/src/main/java/org/elasticsearch/node/MockNode.java b/test/framework/src/main/java/org/elasticsearch/node/MockNode.java
index be21a1c8ab694..d3d96b5102479 100644
--- a/test/framework/src/main/java/org/elasticsearch/node/MockNode.java
+++ b/test/framework/src/main/java/org/elasticsearch/node/MockNode.java
@@ -24,7 +24,7 @@
import org.elasticsearch.common.util.MockBigArrays;
import org.elasticsearch.common.util.MockPageCacheRecycler;
import org.elasticsearch.common.util.PageCacheRecycler;
-import org.elasticsearch.entitlement.bootstrap.TestEntitlementBootstrap;
+import org.elasticsearch.core.IOUtils;
import org.elasticsearch.env.Environment;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.indices.ExecutorSelector;
@@ -54,6 +54,7 @@
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.transport.TransportSettings;
+import java.io.Closeable;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
@@ -237,32 +238,37 @@ protected HttpServerTransport newHttpTransport(PluginsService pluginsService, Ne
}
private final Collection> classpathPlugins;
+ // handle for node paths specific entitlement grants
+ private final Closeable entitlementGrant;
public MockNode(final Settings settings, final Collection> classpathPlugins) {
- this(settings, classpathPlugins, true);
+ this(settings, classpathPlugins, true, () -> {});
}
public MockNode(
final Settings settings,
final Collection> classpathPlugins,
- final boolean forbidPrivateIndexSettings
+ final boolean forbidPrivateIndexSettings,
+ final Closeable entitlementGrant
) {
- this(settings, classpathPlugins, null, forbidPrivateIndexSettings);
+ this(settings, classpathPlugins, null, forbidPrivateIndexSettings, entitlementGrant);
}
public MockNode(
final Settings settings,
final Collection> classpathPlugins,
final Path configPath,
- final boolean forbidPrivateIndexSettings
+ final boolean forbidPrivateIndexSettings,
+ final Closeable entitlementGrant
) {
- this(prepareEnvironment(settings, configPath), classpathPlugins, forbidPrivateIndexSettings);
+ this(prepareEnvironment(settings, configPath), classpathPlugins, forbidPrivateIndexSettings, entitlementGrant);
}
private MockNode(
final Environment environment,
final Collection> classpathPlugins,
- final boolean forbidPrivateIndexSettings
+ final boolean forbidPrivateIndexSettings,
+ final Closeable entitlementGrant
) {
super(NodeConstruction.prepareConstruction(environment, null, new MockServiceProvider() {
@@ -273,10 +279,10 @@ PluginsService newPluginService(Environment environment, PluginsLoader pluginsLo
}, forbidPrivateIndexSettings));
this.classpathPlugins = classpathPlugins;
+ this.entitlementGrant = entitlementGrant;
}
private static Environment prepareEnvironment(final Settings settings, final Path configPath) {
- TestEntitlementBootstrap.registerNodeBaseDirs(settings, configPath);
return InternalSettingsPreparer.prepareEnvironment(
Settings.builder().put(TransportSettings.PORT.getKey(), ESTestCase.getPortRange()).put(settings).build(),
Collections.emptyMap(),
@@ -291,7 +297,7 @@ public synchronized boolean awaitClose(long timeout, TimeUnit timeUnit) throws I
return super.awaitClose(timeout, timeUnit);
} finally {
// wipePendingDataDirectories requires entitlement delegation to work due to this using FileSystemUtils ES-10920
- // TestEntitlementBootstrap.unregisterNodeBaseDirs(settings(), getEnvironment().configDir());
+ IOUtils.closeWhileHandlingException(entitlementGrant);
}
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java
index 89099bb7e32ff..52f99ef8fdd1f 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java
@@ -549,6 +549,7 @@ private TestCluster buildAndPutCluster(Scope currentClusterScope, long seed) thr
// close the previous one and create a new one
if (testCluster != null) {
IOUtils.closeWhileHandlingException(testCluster::close);
+ TEST_ENTITLEMENTS.revokeNodeGrants();
}
testCluster = buildTestCluster(currentClusterScope, seed);
}
@@ -2336,7 +2337,8 @@ protected TestCluster buildTestCluster(Scope scope, long seed) throws IOExceptio
getClientWrapper(),
forbidPrivateIndexSettings(),
forceSingleDataPath(),
- autoManageVotingExclusions()
+ autoManageVotingExclusions(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java
index 7ebc5765bda63..66af9e9c56e61 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java
@@ -288,7 +288,7 @@ private Node newNode() {
plugins.add(ConcurrentSearchTestPlugin.class);
}
plugins.add(MockScriptService.TestPlugin.class);
- Node node = new MockNode(settings, plugins, forbidPrivateIndexSettings());
+ Node node = new MockNode(settings, plugins, forbidPrivateIndexSettings(), TEST_ENTITLEMENTS.newNodeGrant(settings, null));
try {
node.start();
} catch (NodeValidationException e) {
diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java
index de74b47e888d6..43243e431db48 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java
@@ -111,7 +111,7 @@
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.core.Tuple;
-import org.elasticsearch.entitlement.bootstrap.TestEntitlementBootstrap;
+import org.elasticsearch.entitlement.bootstrap.TestEntitlementsRule;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.env.TestEnvironment;
@@ -159,6 +159,7 @@
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.internal.AssumptionViolatedException;
import org.junit.rules.RuleChain;
@@ -524,30 +525,8 @@ protected void afterIfSuccessful() throws Exception {}
String[] value();
}
- @BeforeClass
- public static void setupEntitlementsForClass() {
- boolean withoutEntitlements = getTestClass().isAnnotationPresent(WithoutEntitlements.class);
- boolean withEntitlementsOnTestCode = getTestClass().isAnnotationPresent(WithEntitlementsOnTestCode.class);
- EntitledTestPackages entitledPackages = getTestClass().getAnnotation(EntitledTestPackages.class);
-
- if (TestEntitlementBootstrap.isEnabledForTest()) {
- TestEntitlementBootstrap.setActive(false == withoutEntitlements);
- TestEntitlementBootstrap.setTriviallyAllowingTestCode(false == withEntitlementsOnTestCode);
- if (entitledPackages != null) {
- assert entitledPackages.value().length > 0 : "No test packages specified in @EntitledTestPackages";
- TestEntitlementBootstrap.setEntitledTestPackages(entitledPackages.value());
- }
- } else if (withEntitlementsOnTestCode) {
- throw new AssertionError(
- "Cannot use @WithEntitlementsOnTestCode on tests that are not configured to use entitlements for testing"
- );
- }
- }
-
- @AfterClass
- public static void resetEntitlements() {
- TestEntitlementBootstrap.resetAfterTest();
- }
+ @ClassRule
+ public static final TestEntitlementsRule TEST_ENTITLEMENTS = new TestEntitlementsRule();
// setup mock filesystems for this test run. we change PathUtils
// so that all accesses are plumbed thru any mock wrappers
diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java
index cb45797f9b822..46cf1ad9774b6 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java
@@ -178,6 +178,9 @@
*
*/
public final class InternalTestCluster extends TestCluster {
+ public interface NodeGrantProvider {
+ Closeable create(Settings settings, Path configPath);
+ }
private static final Logger logger = LogManager.getLogger(InternalTestCluster.class);
@@ -278,6 +281,8 @@ public String toString() {
// index of node to bootstrap as master, or BOOTSTRAP_MASTER_NODE_INDEX_AUTO or BOOTSTRAP_MASTER_NODE_INDEX_DONE
private int bootstrapMasterNodeIndex = BOOTSTRAP_MASTER_NODE_INDEX_AUTO;
+ private final NodeGrantProvider nodeGrantProvider;
+
public InternalTestCluster(
final long clusterSeed,
final Path baseDir,
@@ -307,7 +312,8 @@ public InternalTestCluster(
clientWrapper,
true,
false,
- true
+ true,
+ (settings, configPath) -> () -> {}
);
}
@@ -326,7 +332,8 @@ public InternalTestCluster(
final Function clientWrapper,
final boolean forbidPrivateIndexSettings,
final boolean forceSingleDataPath,
- final boolean autoManageVotingExclusions
+ final boolean autoManageVotingExclusions,
+ final NodeGrantProvider nodeGrantProvider
) {
super(clusterSeed);
this.autoManageMasterNodes = autoManageMasterNodes;
@@ -335,6 +342,7 @@ public InternalTestCluster(
this.baseDir = baseDir;
this.clusterName = clusterName;
this.autoManageVotingExclusions = autoManageVotingExclusions;
+ this.nodeGrantProvider = nodeGrantProvider;
if (minNumDataNodes < 0 || maxNumDataNodes < 0) {
throw new IllegalArgumentException("minimum and maximum number of data nodes must be >= 0");
}
@@ -783,7 +791,14 @@ private synchronized NodeAndClient buildNode(int nodeId, Settings settings, bool
// we clone this here since in the case of a node restart we might need it again
secureSettings = ((MockSecureSettings) secureSettings).clone();
}
- MockNode node = new MockNode(settings, plugins, nodeConfigurationSource.nodeConfigPath(nodeId), forbidPrivateIndexSettings);
+ Path configPath = nodeConfigurationSource.nodeConfigPath(nodeId);
+ MockNode node = new MockNode(
+ settings,
+ plugins,
+ configPath,
+ forbidPrivateIndexSettings,
+ nodeGrantProvider.create(settings, configPath)
+ );
node.injector().getInstance(TransportService.class).addLifecycleListener(new LifecycleListener() {
@Override
public void afterStart() {
@@ -1058,7 +1073,7 @@ private void recreateNode(final Settings newSettings, final Runnable onTransport
.put(NodeEnvironment.NODE_ID_SEED_SETTING.getKey(), newIdSeed)
.build();
Collection> plugins = node.getClasspathPlugins();
- node = new MockNode(finalSettings, plugins, forbidPrivateIndexSettings);
+ node = new MockNode(finalSettings, plugins, forbidPrivateIndexSettings, nodeGrantProvider.create(finalSettings, null));
node.injector().getInstance(TransportService.class).addLifecycleListener(new LifecycleListener() {
@Override
public void afterStart() {
From 41fe7a50818c19266316a8b393111f1cef4dc8be Mon Sep 17 00:00:00 2001
From: elasticsearchmachine
Date: Mon, 28 Jul 2025 11:45:38 +0000
Subject: [PATCH 14/15] [CI] Auto commit changes from spotless
---
.../entitlement/bootstrap/TestEntitlementsRule.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java
index 579ee9b224dd6..01b3e735d3b45 100644
--- a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java
+++ b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java
@@ -227,7 +227,7 @@ private static BiFunction, Collection> baseDirMo
// always return a new unmodifiable copy
return (BaseDir baseDir, Collection paths) -> {
Collection newPaths = new Multiset<>();
- if(paths != null){
+ if (paths != null) {
newPaths.addAll(paths);
}
consumer.accept(newPaths);
From 01df9c6a2711bf5b64bf0421db6d1e14840bb572 Mon Sep 17 00:00:00 2001
From: Moritz Mack
Date: Tue, 29 Jul 2025 11:29:43 +0200
Subject: [PATCH 15/15] support duplicate paths
---
.../single/SingleNodeDiscoveryIT.java | 6 ++-
.../snapshots/MultiClusterRepoAccessIT.java | 3 +-
.../bootstrap/TestEntitlementsRule.java | 52 ++++++++++---------
.../test/AbstractMultiClustersTestCase.java | 3 +-
.../test/InternalTestCluster.java | 5 +-
.../test/test/InternalTestClusterTests.java | 21 +++++---
.../elasticsearch/xpack/CcrIntegTestCase.java | 6 ++-
7 files changed, 57 insertions(+), 39 deletions(-)
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java
index 10f13f6ab152f..c7780b7b0d2dd 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java
@@ -87,7 +87,8 @@ public Path nodeConfigPath(int nodeOrdinal) {
0,
"other",
Arrays.asList(getTestTransportPlugin(), MockHttpTransport.TestPlugin.class),
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
try {
other.beforeTest(random());
@@ -137,7 +138,8 @@ public Path nodeConfigPath(int nodeOrdinal) {
0,
"other",
Arrays.asList(getTestTransportPlugin(), MockHttpTransport.TestPlugin.class),
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
try (var mockLog = MockLog.capture(JoinHelper.class)) {
mockLog.addExpectation(
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MultiClusterRepoAccessIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MultiClusterRepoAccessIT.java
index c1549c1f3d384..1ee449d8af894 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MultiClusterRepoAccessIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MultiClusterRepoAccessIT.java
@@ -77,7 +77,8 @@ public Path nodeConfigPath(int nodeOrdinal) {
InternalSettingsPlugin.class,
getTestTransportPlugin()
),
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
secondCluster.beforeTest(random());
}
diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java
index 01b3e735d3b45..8b56f59b9ac10 100644
--- a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java
+++ b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java
@@ -9,7 +9,6 @@
package org.elasticsearch.entitlement.bootstrap;
-import org.apache.lucene.search.Multiset;
import org.apache.lucene.tests.mockfile.FilterPath;
import org.elasticsearch.bootstrap.TestBuildInfo;
import org.elasticsearch.bootstrap.TestBuildInfoParser;
@@ -52,6 +51,7 @@
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -168,18 +168,20 @@ private Path configDir() {
return configPath != null ? configPath : homeDir().resolve("config");
}
- private Collection dataDirs() {
+ private Path[] dataDirs() {
List dataDirs = PATH_DATA_SETTING.get(settings);
- return dataDirs.isEmpty() ? List.of(homeDir().resolve("data")) : dataDirs.stream().map(NodeGrant::absolutePath).toList();
+ return dataDirs.isEmpty()
+ ? new Path[] { homeDir().resolve("data") }
+ : dataDirs.stream().map(NodeGrant::absolutePath).toArray(Path[]::new);
}
- private Collection sharedDataDir() {
+ private Path[] sharedDataDir() {
String sharedDataDir = PATH_SHARED_DATA_SETTING.get(settings);
- return Strings.hasText(sharedDataDir) ? List.of(absolutePath(sharedDataDir)) : List.of();
+ return Strings.hasText(sharedDataDir) ? new Path[] { absolutePath(sharedDataDir) } : new Path[0];
}
- private Collection repoDirs() {
- return PATH_REPO_SETTING.get(settings).stream().map(NodeGrant::absolutePath).toList();
+ private Path[] repoDirs() {
+ return PATH_REPO_SETTING.get(settings).stream().map(NodeGrant::absolutePath).toArray(Path[]::new);
}
@SuppressForbidden(reason = "must be resolved using the default file system, rather then the mocked test file system")
@@ -205,33 +207,35 @@ public String toString() {
}
private void addGrant(NodeGrant nodeGrant) {
- logger.error("Adding node grant: {}", nodeGrant);
- BASE_DIR_PATHS.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.add(nodeGrant.configDir())));
- BASE_DIR_PATHS.compute(BaseDir.DATA, baseDirModifier(paths -> paths.addAll(nodeGrant.dataDirs())));
- BASE_DIR_PATHS.compute(BaseDir.SHARED_DATA, baseDirModifier(paths -> paths.addAll(nodeGrant.sharedDataDir())));
- BASE_DIR_PATHS.compute(BaseDir.SHARED_REPO, baseDirModifier(paths -> paths.addAll(nodeGrant.repoDirs())));
+ logger.debug("Adding node grant: {}", nodeGrant);
+ BASE_DIR_PATHS.compute(BaseDir.CONFIG, baseDirModifier(Collection::add, nodeGrant.configDir()));
+ BASE_DIR_PATHS.compute(BaseDir.DATA, baseDirModifier(Collection::add, nodeGrant.dataDirs()));
+ BASE_DIR_PATHS.compute(BaseDir.SHARED_DATA, baseDirModifier(Collection::add, nodeGrant.sharedDataDir()));
+ BASE_DIR_PATHS.compute(BaseDir.SHARED_REPO, baseDirModifier(Collection::add, nodeGrant.repoDirs()));
POLICY_MANAGER.clearModuleEntitlementsCache();
}
private void revokeGrant(NodeGrant nodeGrant) {
- logger.error("Revoking node grant: {}", nodeGrant);
- BASE_DIR_PATHS.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.remove(nodeGrant.configDir())));
- BASE_DIR_PATHS.compute(BaseDir.DATA, baseDirModifier(paths -> paths.removeAll(nodeGrant.dataDirs())));
- BASE_DIR_PATHS.compute(BaseDir.SHARED_DATA, baseDirModifier(paths -> paths.removeAll(nodeGrant.sharedDataDir())));
- BASE_DIR_PATHS.compute(BaseDir.SHARED_REPO, baseDirModifier(paths -> paths.removeAll(nodeGrant.repoDirs())));
+ logger.debug("Revoking node grant: {}", nodeGrant);
+ BASE_DIR_PATHS.compute(BaseDir.CONFIG, baseDirModifier(Collection::remove, nodeGrant.configDir()));
+ BASE_DIR_PATHS.compute(BaseDir.DATA, baseDirModifier(Collection::remove, nodeGrant.dataDirs()));
+ BASE_DIR_PATHS.compute(BaseDir.SHARED_DATA, baseDirModifier(Collection::remove, nodeGrant.sharedDataDir()));
+ BASE_DIR_PATHS.compute(BaseDir.SHARED_REPO, baseDirModifier(Collection::remove, nodeGrant.repoDirs()));
POLICY_MANAGER.clearModuleEntitlementsCache();
}
- // this uses a counting multiset to allow duplicates between nodes, e.g. the config dir
- private static BiFunction, Collection> baseDirModifier(Consumer> consumer) {
+ // This must allow for duplicate paths between nodes, the config dir for instance is shared across all nodes.
+ private static BiFunction, Collection> baseDirModifier(
+ BiConsumer, Path> operation,
+ Path... updates
+ ) {
// always return a new unmodifiable copy
return (BaseDir baseDir, Collection paths) -> {
- Collection newPaths = new Multiset<>();
- if (paths != null) {
- newPaths.addAll(paths);
+ paths = paths == null ? new ArrayList<>() : new ArrayList<>(paths);
+ for (Path update : updates) {
+ operation.accept(paths, update);
}
- consumer.accept(newPaths);
- return Collections.unmodifiableCollection(newPaths);
+ return Collections.unmodifiableCollection(paths);
};
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractMultiClustersTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractMultiClustersTestCase.java
index b4f91f68b8bb7..2b6abb67b4353 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/AbstractMultiClustersTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractMultiClustersTestCase.java
@@ -127,7 +127,8 @@ public final void startClusters() throws Exception {
0,
clusterName + "-",
mockPlugins,
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
try {
cluster.beforeTest(random());
diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java
index 46cf1ad9774b6..0ab12b697801c 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java
@@ -295,7 +295,8 @@ public InternalTestCluster(
final int numClientNodes,
final String nodePrefix,
final Collection> mockPlugins,
- final Function clientWrapper
+ final Function clientWrapper,
+ NodeGrantProvider nodeGrantProvider
) {
this(
clusterSeed,
@@ -313,7 +314,7 @@ public InternalTestCluster(
true,
false,
true,
- (settings, configPath) -> () -> {}
+ nodeGrantProvider
);
}
diff --git a/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java b/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java
index f5c358b3b3301..23282572607c0 100644
--- a/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java
+++ b/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java
@@ -85,7 +85,8 @@ public void testInitializiationIsConsistent() {
numClientNodes,
nodePrefix,
Collections.emptyList(),
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
InternalTestCluster cluster1 = new InternalTestCluster(
clusterSeed,
@@ -99,7 +100,8 @@ public void testInitializiationIsConsistent() {
numClientNodes,
nodePrefix,
Collections.emptyList(),
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
assertClusters(cluster0, cluster1, true);
}
@@ -198,7 +200,8 @@ public Path nodeConfigPath(int nodeOrdinal) {
numClientNodes,
nodePrefix,
mockPlugins(),
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
cluster0.setBootstrapMasterNodeIndex(bootstrapMasterNodeIndex);
@@ -214,7 +217,8 @@ public Path nodeConfigPath(int nodeOrdinal) {
numClientNodes,
nodePrefix,
mockPlugins(),
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
cluster1.setBootstrapMasterNodeIndex(bootstrapMasterNodeIndex);
@@ -280,7 +284,8 @@ public Path nodeConfigPath(int nodeOrdinal) {
numClientNodes,
nodePrefix,
mockPlugins(),
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
try {
cluster.beforeTest(random());
@@ -375,7 +380,8 @@ public Path nodeConfigPath(int nodeOrdinal) {
0,
"",
mockPlugins(),
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
cluster.beforeTest(random());
List roles = new ArrayList<>();
@@ -467,7 +473,8 @@ public Path nodeConfigPath(int nodeOrdinal) {
0,
nodePrefix,
plugins,
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
try {
cluster.beforeTest(random());
diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java
index 644a2a074ac59..bd2f53b238ae7 100644
--- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java
+++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java
@@ -182,7 +182,8 @@ public final void startClusters() throws Exception {
0,
"leader",
mockPlugins,
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
leaderCluster.beforeTest(random());
leaderCluster.ensureAtLeastNumDataNodes(numberOfNodesPerCluster());
@@ -204,7 +205,8 @@ public final void startClusters() throws Exception {
0,
"follower",
mockPlugins,
- Function.identity()
+ Function.identity(),
+ TEST_ENTITLEMENTS::newNodeGrant
);
clusterGroup = new ClusterGroup(leaderCluster, followerCluster);