Browse Source

Allow REST tests to run in MP mode (#126906)

Mainly moves project setup from `MultipleProjectsClientYamlSuiteTestCase` to
`ESRestTestCase`. This allows both Java REST tests and YAML tests to be run
in MP mode by passing the system property `-Dtests.multi_project.enabled=true`.
Future work will add the required gradle changes to be able to run any REST test in
MP mode more easily.
Niels Bauman 5 months ago
parent
commit
483a9aee95
16 changed files with 179 additions and 195 deletions
  1. 2 6
      test/external-modules/multi-project/build.gradle
  2. 1 1
      test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/action/admin/indices/IndexMultiProjectCRUDIT.java
  3. 1 1
      test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/action/index/IndexDocumentMultiProjectIT.java
  4. 1 1
      test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/multiproject/MultiProjectClusterStateActionIT.java
  5. 12 57
      test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/multiproject/MultiProjectRestTestCase.java
  6. 1 1
      test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/multiproject/MultiProjectRestartIT.java
  7. 3 3
      test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/multiproject/action/ProjectCrudActionIT.java
  8. 2 2
      test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/xpack/security/SecurityRolesMultiProjectIT.java
  9. 94 26
      test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java
  10. 1 0
      test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/DefaultLocalClusterSpecBuilder.java
  11. 26 0
      test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/MultiProjectEnabledClusterConfigProvider.java
  12. 6 1
      x-pack/plugin/security/qa/multi-project/build.gradle
  13. 26 25
      x-pack/plugin/security/qa/multi-project/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/jwt/JwtMultiProjectIT.java
  14. 1 0
      x-pack/qa/multi-project/core-rest-tests-with-multiple-projects/build.gradle
  15. 1 0
      x-pack/qa/multi-project/xpack-rest-tests-with-multiple-projects/build.gradle
  16. 1 71
      x-pack/qa/multi-project/yaml-test-framework/src/main/java/org/elasticsearch/multiproject/test/MultipleProjectsClientYamlSuiteTestCase.java

+ 2 - 6
test/external-modules/multi-project/build.gradle

@@ -1,5 +1,3 @@
-import org.elasticsearch.gradle.testclusters.StandaloneRestIntegTestTask
-
 apply plugin: 'elasticsearch.internal-es-plugin'
 apply plugin: 'elasticsearch.test-with-dependencies'
 apply plugin: 'elasticsearch.internal-java-rest-test'
@@ -12,12 +10,10 @@ esplugin {
 dependencies {
   testImplementation project(path: ':test:test-clusters')
   clusterModules project(':test:external-modules:test-multi-project')
-}
-
-tasks.withType(StandaloneRestIntegTestTask).configureEach {
-  usesDefaultDistribution("to be triaged")
+  clusterModules project(':modules:analysis-common')
 }
 
 tasks.named("javaRestTest").configure {
   enabled = buildParams.snapshotBuild
+  systemProperty "tests.multi_project.enabled", true
 }

+ 1 - 1
test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/action/admin/indices/IndexMultiProjectCRUDIT.java

@@ -46,7 +46,7 @@ public class IndexMultiProjectCRUDIT extends MultiProjectRestTestCase {
     private static ElasticsearchCluster createCluster() {
         LocalClusterSpecBuilder<ElasticsearchCluster> clusterBuilder = ElasticsearchCluster.local()
             .nodes(NODE_NUM)
-            .distribution(DistributionType.INTEG_TEST) // TODO multi-project: make this test suite work under the default distrib
+            .distribution(DistributionType.INTEG_TEST)
             .module("test-multi-project")
             .setting("test.multi_project.enabled", "true")
             .setting("xpack.security.enabled", "false") // TODO multi-project: make this test suite work with Security enabled

+ 1 - 1
test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/action/index/IndexDocumentMultiProjectIT.java

@@ -42,7 +42,7 @@ public class IndexDocumentMultiProjectIT extends MultiProjectRestTestCase {
     private static ElasticsearchCluster createCluster() {
         LocalClusterSpecBuilder<ElasticsearchCluster> clusterBuilder = ElasticsearchCluster.local()
             .nodes(NODE_NUM)
-            .distribution(DistributionType.INTEG_TEST) // TODO multi-project: make this test suite work under the default distrib
+            .distribution(DistributionType.INTEG_TEST)
             .module("test-multi-project")
             .setting("test.multi_project.enabled", "true")
             .setting("xpack.security.enabled", "false") // TODO multi-project: make this test suite work with Security enabled

+ 1 - 1
test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/multiproject/MultiProjectClusterStateActionIT.java

@@ -25,7 +25,7 @@ public class MultiProjectClusterStateActionIT extends MultiProjectRestTestCase {
 
     @ClassRule
     public static ElasticsearchCluster CLUSTER = ElasticsearchCluster.local()
-        .distribution(DistributionType.DEFAULT)
+        .distribution(DistributionType.INTEG_TEST)
         .setting("test.multi_project.enabled", "true")
         .setting("xpack.security.http.ssl.enabled", "false")
         .setting("xpack.security.enabled", "false")

+ 12 - 57
test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/multiproject/MultiProjectRestTestCase.java

@@ -11,77 +11,32 @@ package org.elasticsearch.multiproject;
 
 import org.elasticsearch.client.Request;
 import org.elasticsearch.client.RequestOptions;
-import org.elasticsearch.client.Response;
-import org.elasticsearch.client.ResponseException;
-import org.elasticsearch.cluster.metadata.Metadata;
 import org.elasticsearch.tasks.Task;
 import org.elasticsearch.test.rest.ESRestTestCase;
-import org.elasticsearch.xcontent.ObjectPath;
 import org.junit.After;
 
 import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
 
 public abstract class MultiProjectRestTestCase extends ESRestTestCase {
-    protected static Request setRequestProjectId(Request request, String projectId) {
-        RequestOptions.Builder options = request.getOptions().toBuilder();
-        options.removeHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER);
-        options.addHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER, projectId);
-        request.setOptions(options);
-        return request;
-    }
-
-    protected static void clearRequestProjectId(Request request) {
-        RequestOptions options = request.getOptions();
-        if (options.containsHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER)) {
-            request.setOptions(options.toBuilder().removeHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER).build());
-        }
-    }
-
-    protected void createProject(String projectId) throws IOException {
-        Request request = new Request("PUT", "/_project/" + projectId);
-        try {
-            Response response = adminClient().performRequest(request);
-            assertOK(response);
-            logger.info("Created project {} : {}", projectId, response.getStatusLine());
-        } catch (ResponseException e) {
-            logger.error("Failed to create project: {}", projectId);
-            throw e;
-        }
-    }
-
-    protected void deleteProject(String projectId) throws IOException {
-        final Request request = new Request("DELETE", "/_project/" + projectId);
-        try {
-            final Response response = adminClient().performRequest(request);
-            logger.info("Deleted project {} : {}", projectId, response.getStatusLine());
-        } catch (ResponseException e) {
-            logger.error("Failed to delete project: {}", projectId);
-            throw e;
-        }
-    }
 
-    protected Set<String> listProjects() throws IOException {
-        final Request request = new Request("GET", "/_cluster/state/metadata?multi_project");
-        final Response response = adminClient().performRequest(request);
-        final List<Map<String, ?>> projects = ObjectPath.eval("metadata.projects", entityAsMap(response));
-        return projects.stream().map(m -> String.valueOf(m.get("id"))).collect(Collectors.toSet());
+    @Override
+    protected boolean shouldConfigureProjects() {
+        return false;
     }
 
     @After
     public void removeNonDefaultProjects() throws IOException {
         if (preserveClusterUponCompletion() == false) {
-            final Set<String> projects = listProjects();
-            logger.info("Removing non-default projects from {}", projects);
-            for (String projectId : projects) {
-                if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id()) == false) {
-                    deleteProject(projectId);
-                }
-            }
+            cleanUpProjects();
         }
     }
 
+    protected static Request setRequestProjectId(Request request, String projectId) {
+        RequestOptions.Builder options = request.getOptions().toBuilder();
+        options.removeHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER);
+        options.addHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER, projectId);
+        request.setOptions(options);
+        return request;
+    }
+
 }

+ 1 - 1
test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/multiproject/MultiProjectRestartIT.java

@@ -23,7 +23,7 @@ public class MultiProjectRestartIT extends MultiProjectRestTestCase {
 
     @ClassRule
     public static ElasticsearchCluster CLUSTER = ElasticsearchCluster.local()
-        .distribution(DistributionType.DEFAULT)
+        .distribution(DistributionType.INTEG_TEST)
         .setting("test.multi_project.enabled", "true")
         .setting("xpack.security.http.ssl.enabled", "false")
         .setting("xpack.security.enabled", "false")

+ 3 - 3
test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/multiproject/action/ProjectCrudActionIT.java

@@ -12,9 +12,9 @@ package org.elasticsearch.multiproject.action;
 import org.elasticsearch.client.Request;
 import org.elasticsearch.client.Response;
 import org.elasticsearch.client.ResponseException;
+import org.elasticsearch.multiproject.MultiProjectRestTestCase;
 import org.elasticsearch.test.cluster.ElasticsearchCluster;
 import org.elasticsearch.test.cluster.local.distribution.DistributionType;
-import org.elasticsearch.test.rest.ESRestTestCase;
 import org.elasticsearch.test.rest.ObjectPath;
 import org.junit.ClassRule;
 
@@ -31,11 +31,11 @@ import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.hasItem;
 import static org.hamcrest.CoreMatchers.not;
 
-public class ProjectCrudActionIT extends ESRestTestCase {
+public class ProjectCrudActionIT extends MultiProjectRestTestCase {
 
     @ClassRule
     public static ElasticsearchCluster CLUSTER = ElasticsearchCluster.local()
-        .distribution(DistributionType.DEFAULT)
+        .distribution(DistributionType.INTEG_TEST)
         .setting("test.multi_project.enabled", "true")
         .setting("xpack.security.enabled", "false")
         .build();

+ 2 - 2
test/external-modules/multi-project/src/javaRestTest/java/org/elasticsearch/xpack/security/SecurityRolesMultiProjectIT.java

@@ -40,8 +40,8 @@ public class SecurityRolesMultiProjectIT extends MultiProjectRestTestCase {
     @ClassRule
     public static ElasticsearchCluster cluster = ElasticsearchCluster.local()
         .nodes(1)
-        .distribution(DistributionType.DEFAULT)
-        .module("test-multi-project")
+        .distribution(DistributionType.INTEG_TEST)
+        .module("analysis-common")
         .setting("test.multi_project.enabled", "true")
         .setting("xpack.security.enabled", "true")
         .user("admin", PASSWORD)

+ 94 - 26
test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java

@@ -49,12 +49,14 @@ import org.elasticsearch.client.RestClient;
 import org.elasticsearch.client.RestClientBuilder;
 import org.elasticsearch.client.WarningsHandler;
 import org.elasticsearch.cluster.metadata.Metadata;
+import org.elasticsearch.cluster.metadata.ProjectId;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.bytes.BytesArray;
 import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.settings.SecureString;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.ssl.PemUtils;
+import org.elasticsearch.common.util.CollectionUtils;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.common.util.set.Sets;
 import org.elasticsearch.common.xcontent.XContentHelper;
@@ -92,6 +94,7 @@ import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -118,6 +121,7 @@ import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -266,6 +270,9 @@ public abstract class ESRestTestCase extends ESTestCase {
     private static RestClient cleanupClient;
 
     private static boolean multiProjectEnabled;
+    private static String activeProject;
+    private static Set<String> extraProjects;
+    private static boolean projectsConfigured = false;
 
     public enum ProductFeature {
         XPACK,
@@ -357,6 +364,14 @@ public abstract class ESRestTestCase extends ESTestCase {
         return testFeatureService != ALL_FEATURES;
     }
 
+    @BeforeClass
+    public static void initializeProjectIds() {
+        // The active project-id is slightly longer, and has a fixed prefix so that it's easier to pick in error messages etc.
+        activeProject = "active00" + randomAlphaOfLength(8).toLowerCase(Locale.ROOT);
+        extraProjects = randomSet(1, 3, () -> randomAlphaOfLength(12).toLowerCase(Locale.ROOT));
+        multiProjectEnabled = Boolean.parseBoolean(System.getProperty("tests.multi_project.enabled"));
+    }
+
     @Before
     public void initClient() throws IOException {
         if (client == null) {
@@ -367,17 +382,19 @@ public abstract class ESRestTestCase extends ESTestCase {
             assert testFeatureServiceInitialized() == false;
             clusterHosts = parseClusterHosts(getTestRestCluster());
             logger.info("initializing REST clients against {}", clusterHosts);
-            var clientSettings = restClientSettings();
+            // We add the project ID to the client settings afterward because a lot of subclasses don't call super.restClientSettings(),
+            // meaning the project ID would be removed from the settings.
+            var clientSettings = addProjectIdToSettings(restClientSettings());
             var adminSettings = restAdminSettings();
+            var cleanupSettings = cleanupClientSettings();
             var hosts = clusterHosts.toArray(new HttpHost[0]);
             client = buildClient(clientSettings, hosts);
             adminClient = clientSettings.equals(adminSettings) ? client : buildClient(adminSettings, hosts);
-            cleanupClient = getCleanupClient();
+            cleanupClient = adminSettings.equals(cleanupSettings) ? adminClient : buildClient(cleanupSettings, hosts);
 
             availableFeatures = EnumSet.of(ProductFeature.LEGACY_TEMPLATES);
             Set<String> versions = new HashSet<>();
             boolean serverless = false;
-            String multiProjectPluginVariant = null;
 
             for (Map<?, ?> nodeInfo : getNodesInfo(adminClient).values()) {
                 var nodeVersion = nodeInfo.get("version").toString();
@@ -407,11 +424,6 @@ public abstract class ESRestTestCase extends ESTestCase {
                     if (moduleName.startsWith("serverless-")) {
                         serverless = true;
                     }
-                    if (moduleName.contains("test-multi-project")) {
-                        multiProjectPluginVariant = "test";
-                    } else if (moduleName.contains("serverless-multi-project")) {
-                        multiProjectPluginVariant = "serverless";
-                    }
                 }
                 if (serverless) {
                     availableFeatures.removeAll(
@@ -432,20 +444,9 @@ public abstract class ESRestTestCase extends ESTestCase {
                 .flatMap(Optional::stream)
                 .collect(Collectors.toSet());
             assert semanticNodeVersions.isEmpty() == false || serverless;
-
-            if (multiProjectPluginVariant != null) {
-                final Request settingRequest = new Request(
-                    "GET",
-                    "/_cluster/settings?include_defaults&filter_path=*." + multiProjectPluginVariant + ".multi_project.enabled"
-                );
-                settingRequest.setOptions(RequestOptions.DEFAULT.toBuilder().setWarningsHandler(WarningsHandler.PERMISSIVE));
-                final var response = entityAsMap(adminClient.performRequest(settingRequest));
-                multiProjectEnabled = Boolean.parseBoolean(
-                    ObjectPath.evaluate(response, "defaults." + multiProjectPluginVariant + ".multi_project.enabled")
-                );
-            }
-
             testFeatureService = createTestFeatureService(getClusterStateFeatures(adminClient), semanticNodeVersions);
+
+            configureProjects();
         }
 
         assert testFeatureServiceInitialized();
@@ -1621,9 +1622,21 @@ public abstract class ESRestTestCase extends ESTestCase {
     /**
      * Returns the REST client used for cleaning up the cluster.
      */
-    protected RestClient getCleanupClient() {
-        assert adminClient != null;
-        return adminClient;
+    protected Settings cleanupClientSettings() {
+        if (multiProjectEnabled == false || shouldConfigureProjects() == false) {
+            return restAdminSettings();
+        }
+        return addProjectIdToSettings(restAdminSettings());
+    }
+
+    private Settings addProjectIdToSettings(Settings settings) {
+        if (multiProjectEnabled == false || shouldConfigureProjects() == false) {
+            return settings;
+        }
+        return Settings.builder()
+            .put(settings)
+            .put(ThreadContext.PREFIX + "." + Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER, activeProject)
+            .build();
     }
 
     /**
@@ -2741,12 +2754,50 @@ public abstract class ESRestTestCase extends ESTestCase {
         assertMap(result, mapMatcher.entry("columns", columnMatcher).entry("values", valuesMatcher));
     }
 
+    /**
+     * Whether the test framework should configure an active projects and some extra projects. This is true by default (when multi-project
+     * is enabled). Subclasses can override this method to avoid configuring projects - e.g. when they configure projects themselves.
+     */
+    protected boolean shouldConfigureProjects() {
+        assert multiProjectEnabled;
+        return true;
+    }
+
+    private void configureProjects() throws IOException {
+        if (projectsConfigured || multiProjectEnabled == false || shouldConfigureProjects() == false) {
+            return;
+        }
+        projectsConfigured = true;
+        createProject(activeProject);
+        for (var project : extraProjects) {
+            createProject(project);
+        }
+
+        // The admin client does not set a project id, and can see all projects
+        assertProjectIds(
+            adminClient(),
+            CollectionUtils.concatLists(List.of(Metadata.DEFAULT_PROJECT_ID.id(), activeProject), extraProjects)
+        );
+        // The test client can only see the project it targets
+        assertProjectIds(client(), List.of(activeProject));
+    }
+
+    @After
+    public final void assertEmptyProjects() throws Exception {
+        if (projectsConfigured == false) {
+            return;
+        }
+        assertEmptyProject(Metadata.DEFAULT_PROJECT_ID.id());
+        for (var project : extraProjects) {
+            assertEmptyProject(project);
+        }
+    }
+
     protected void createProject(String project) throws IOException {
         assert multiProjectEnabled;
-        RestClient client = adminClient();
         final Request request = new Request("PUT", "/_project/" + project);
         try {
-            final Response response = client.performRequest(request);
+            final Response response = adminClient().performRequest(request);
             logger.info("Created project {} : {}", project, response.getStatusLine());
         } catch (ResponseException e) {
             logger.error("Failed to create project: {}", project);
@@ -2777,6 +2828,23 @@ public abstract class ESRestTestCase extends ESTestCase {
         }
     }
 
+    protected void cleanUpProjects() throws IOException {
+        assert multiProjectEnabled;
+        final var projectIds = getProjectIds(adminClient());
+        for (String projectId : projectIds) {
+            if (projectId.equals(ProjectId.DEFAULT.id())) {
+                continue;
+            }
+            deleteProject(projectId);
+        }
+    }
+
+    private void deleteProject(String project) throws IOException {
+        assert multiProjectEnabled;
+        final Request request = new Request("DELETE", "/_project/" + project);
+        cleanupClient().performRequest(request);
+    }
+
     protected void assertEmptyProject(String projectId) throws IOException {
         assert multiProjectEnabled;
         final Request request = new Request("GET", "_cluster/state/metadata,routing_table,customs");

+ 1 - 0
test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/DefaultLocalClusterSpecBuilder.java

@@ -20,6 +20,7 @@ public final class DefaultLocalClusterSpecBuilder extends AbstractLocalClusterSp
     public DefaultLocalClusterSpecBuilder() {
         super();
         this.apply(new FipsEnabledClusterConfigProvider());
+        this.apply(new MultiProjectEnabledClusterConfigProvider());
         this.systemProperties(new DefaultSystemPropertyProvider());
         this.settings(new DefaultSettingsProvider());
         this.environment(new DefaultEnvironmentProvider());

+ 26 - 0
test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/MultiProjectEnabledClusterConfigProvider.java

@@ -0,0 +1,26 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch.test.cluster.local;
+
+public class MultiProjectEnabledClusterConfigProvider implements LocalClusterConfigProvider {
+
+    @Override
+    public void apply(LocalClusterSpecBuilder<?> builder) {
+        if (isMultiProjectEnabled()) {
+            builder.setting("test.multi_project.enabled", "true").module("test-multi-project");
+        }
+    }
+
+    private static boolean isMultiProjectEnabled() {
+        // TODO: we need to use `tests` instead of `test` here to make gradle passes the system property,
+        // but we need `test` in the setting.
+        return Boolean.getBoolean("tests.multi_project.enabled");
+    }
+}

+ 6 - 1
x-pack/plugin/security/qa/multi-project/build.gradle

@@ -2,9 +2,14 @@ apply plugin: 'elasticsearch.internal-java-rest-test'
 
 dependencies {
   javaRestTestImplementation "com.nimbusds:nimbus-jose-jwt:10.0.2"
+  clusterModules project(':test:external-modules:test-multi-project')
+  clusterModules project(':modules:analysis-common')
 }
 
 tasks.named('javaRestTest') {
-  usesDefaultDistribution("to be triaged")
   it.onlyIf("snapshot build") { buildParams.snapshotBuild }
 }
+
+tasks.withType(Test).configureEach {
+  it.systemProperty "tests.multi_project.enabled", true
+}

+ 26 - 25
x-pack/plugin/security/qa/multi-project/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/jwt/JwtMultiProjectIT.java

@@ -29,6 +29,7 @@ import org.elasticsearch.test.cluster.local.distribution.DistributionType;
 import org.elasticsearch.test.cluster.util.resource.Resource;
 import org.elasticsearch.test.rest.ESRestTestCase;
 import org.hamcrest.Matchers;
+import org.junit.After;
 import org.junit.ClassRule;
 
 import java.io.IOException;
@@ -48,8 +49,8 @@ public class JwtMultiProjectIT extends ESRestTestCase {
     @ClassRule
     public static ElasticsearchCluster cluster = ElasticsearchCluster.local()
         .nodes(1)
-        .distribution(DistributionType.DEFAULT)
-        .module("test-multi-project")
+        .distribution(DistributionType.INTEG_TEST)
+        .module("analysis-common")
         .setting("test.multi_project.enabled", "true")
         .configFile("jwkset.json", Resource.fromClasspath("jwk/jwkset.json"))
         .setting("xpack.ml.enabled", "false")
@@ -81,33 +82,38 @@ public class JwtMultiProjectIT extends ESRestTestCase {
         return builder.build();
     }
 
+    @Override
+    protected boolean shouldConfigureProjects() {
+        return false;
+    }
+
+    @After
+    public void cleanup() throws IOException {
+        cleanUpProjects();
+    }
+
     @FixForMultiProject(description = "This should also test role mappings from file-based-settings (when they are project-scoped)")
     public void testSameJwtAuthenticatesToMultipleProjects() throws Exception {
         final String project1 = randomIdentifier();
         final String project2 = randomIdentifier();
 
-        try {
-            createProject(project1);
-            createProject(project2);
+        createProject(project1);
+        createProject(project2);
 
-            final JWTClaimsSet.Builder claims = buildJwtClaims();
-            final JWSHeader jwtHeader = new JWSHeader.Builder(JWSAlgorithm.parse("RS256")).build();
-            final SignedJWT jwt = signJwt(jwtHeader, claims.build());
+        final JWTClaimsSet.Builder claims = buildJwtClaims();
+        final JWSHeader jwtHeader = new JWSHeader.Builder(JWSAlgorithm.parse("RS256")).build();
+        final SignedJWT jwt = signJwt(jwtHeader, claims.build());
 
-            RequestOptions requestOptions = RequestOptions.DEFAULT.toBuilder()
-                .addHeader("Authorization", "Bearer " + jwt.serialize())
-                .addHeader("ES-Client-Authentication", "SharedSecret " + CLIENT_SECRET)
-                .build();
+        RequestOptions requestOptions = RequestOptions.DEFAULT.toBuilder()
+            .addHeader("Authorization", "Bearer " + jwt.serialize())
+            .addHeader("ES-Client-Authentication", "SharedSecret " + CLIENT_SECRET)
+            .build();
 
-            final Map<String, Object> authProject1 = authenticate(project1, requestOptions);
-            assertThat(authProject1, Matchers.hasEntry("username", "tester"));
+        final Map<String, Object> authProject1 = authenticate(project1, requestOptions);
+        assertThat(authProject1, Matchers.hasEntry("username", "tester"));
 
-            final Map<String, Object> authProject2 = authenticate(project2, requestOptions);
-            assertThat(authProject2, Matchers.hasEntry("username", "tester"));
-        } finally {
-            deleteProject(project1);
-            deleteProject(project2);
-        }
+        final Map<String, Object> authProject2 = authenticate(project2, requestOptions);
+        assertThat(authProject2, Matchers.hasEntry("username", "tester"));
     }
 
     private JWTClaimsSet.Builder buildJwtClaims() {
@@ -135,9 +141,4 @@ public class JwtMultiProjectIT extends ESRestTestCase {
         request.setOptions(requestOptions.toBuilder().addHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER, projectId));
         return entityAsMap(client().performRequest(request));
     }
-
-    private void deleteProject(String project) throws IOException {
-        final Request request = new Request("DELETE", "/_project/" + project);
-        client().performRequest(request);
-    }
 }

+ 1 - 0
x-pack/qa/multi-project/core-rest-tests-with-multiple-projects/build.gradle

@@ -82,4 +82,5 @@ tasks.named("yamlRestTest").configure {
     blacklist += [];
   }
   systemProperty 'tests.rest.blacklist', blacklist.join(',')
+  systemProperty "tests.multi_project.enabled", true
 }

+ 1 - 0
x-pack/qa/multi-project/xpack-rest-tests-with-multiple-projects/build.gradle

@@ -165,4 +165,5 @@ tasks.named("yamlRestTest").configure {
     blacklist += [];
   }
   systemProperty 'tests.rest.blacklist', blacklist.join(',')
+  systemProperty "tests.multi_project.enabled", true
 }

+ 1 - 71
x-pack/qa/multi-project/yaml-test-framework/src/main/java/org/elasticsearch/multiproject/test/MultipleProjectsClientYamlSuiteTestCase.java

@@ -7,23 +7,13 @@
 
 package org.elasticsearch.multiproject.test;
 
-import org.elasticsearch.client.RestClient;
-import org.elasticsearch.cluster.metadata.Metadata;
 import org.elasticsearch.common.settings.SecureString;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.util.CollectionUtils;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
-import org.elasticsearch.tasks.Task;
 import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
 import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
 
-import java.util.List;
-import java.util.Locale;
 import java.util.Objects;
-import java.util.Set;
 
 /**
  * Base class for running YAML Rest tests against a cluster with multiple projects
@@ -39,73 +29,13 @@ public abstract class MultipleProjectsClientYamlSuiteTestCase extends ESClientYa
      */
     protected static final String PASS = Objects.requireNonNull(System.getProperty("tests.rest.cluster.password", "test-password"));
 
-    private static String activeProject;
-    private static Set<String> extraProjects;
-    private static boolean projectsConfigured = false;
-
     public MultipleProjectsClientYamlSuiteTestCase(ClientYamlTestCandidate testCandidate) {
         super(testCandidate);
     }
 
-    @BeforeClass
-    public static void initializeProjectIds() {
-        // The active project-id is slightly longer, and has a fixed suffix so that it's easier to pick in error messages etc.
-        activeProject = randomAlphaOfLength(8).toLowerCase(Locale.ROOT) + "00active";
-        extraProjects = randomSet(1, 3, () -> randomAlphaOfLength(12).toLowerCase(Locale.ROOT));
-    }
-
-    @Override
-    protected RestClient getCleanupClient() {
-        return client();
-    }
-
-    @Before
-    public void configureProjects() throws Exception {
-        if (projectsConfigured) {
-            return;
-        }
-        projectsConfigured = true;
-        initClient();
-        createProject(activeProject);
-        for (var project : extraProjects) {
-            createProject(project);
-        }
-
-        // The admin client does not set a project id, and can see all projects
-        assertProjectIds(
-            adminClient(),
-            CollectionUtils.concatLists(List.of(Metadata.DEFAULT_PROJECT_ID.id(), activeProject), extraProjects)
-        );
-        // The test client can only see the project it targets
-        assertProjectIds(client(), List.of(activeProject));
-    }
-
-    @After
-    public final void assertEmptyProjects() throws Exception {
-        assertEmptyProject(Metadata.DEFAULT_PROJECT_ID.id());
-        for (var project : extraProjects) {
-            assertEmptyProject(project);
-        }
-    }
-
     @Override
     protected Settings restClientSettings() {
-        return clientSettings(true);
-    }
-
-    @Override
-    protected Settings restAdminSettings() {
-        return clientSettings(false);
-    }
-
-    private Settings clientSettings(boolean projectScoped) {
         String token = basicAuthHeaderValue(USER, new SecureString(PASS.toCharArray()));
-        final Settings.Builder builder = Settings.builder()
-            .put(super.restClientSettings())
-            .put(ThreadContext.PREFIX + ".Authorization", token);
-        if (projectScoped) {
-            builder.put(ThreadContext.PREFIX + "." + Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER, activeProject);
-        }
-        return builder.build();
+        return Settings.builder().put(super.restClientSettings()).put(ThreadContext.PREFIX + ".Authorization", token).build();
     }
 }