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);