Przeglądaj źródła

[Test] Move helper mthods for multi-project rest test (#124285)

This PR moves the helper methods up to the base ESRestTestCase class so
that they can be reused by other subclasses, e.g. the ones on the
serverless side.

Relates: ES-10292
Yang Wang 7 miesięcy temu
rodzic
commit
207c2df14c

+ 137 - 0
test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java

@@ -48,6 +48,7 @@ import org.elasticsearch.client.ResponseException;
 import org.elasticsearch.client.RestClient;
 import org.elasticsearch.client.RestClientBuilder;
 import org.elasticsearch.client.WarningsHandler;
+import org.elasticsearch.cluster.metadata.Metadata;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.bytes.BytesArray;
 import org.elasticsearch.common.bytes.BytesReference;
@@ -74,6 +75,7 @@ import org.elasticsearch.index.IndexVersions;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.seqno.ReplicationTracker;
 import org.elasticsearch.rest.RestStatus;
+import org.elasticsearch.tasks.Task;
 import org.elasticsearch.test.AbstractBroadcastResponseTestCase;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.MapMatcher;
@@ -142,7 +144,9 @@ import static org.elasticsearch.test.MapMatcher.matchesMap;
 import static org.elasticsearch.test.rest.TestFeatureService.ALL_FEATURES;
 import static org.elasticsearch.xcontent.ToXContent.EMPTY_PARAMS;
 import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.everyItem;
 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
@@ -2702,4 +2706,137 @@ public abstract class ESRestTestCase extends ESTestCase {
     ) {
         assertMap(result, mapMatcher.entry("columns", columnMatcher).entry("values", valuesMatcher));
     }
+
+    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);
+            logger.info("Created project {} : {}", project, response.getStatusLine());
+        } catch (ResponseException e) {
+            logger.error("Failed to create project: {}", project);
+            throw e;
+        }
+    }
+
+    protected void assertProjectIds(RestClient client, List<String> expectedProjects) throws IOException {
+        assert multiProjectEnabled;
+        final Collection<String> actualProjects = getProjectIds(client);
+        assertThat(
+            "Cluster returned project ids: " + actualProjects,
+            actualProjects,
+            containsInAnyOrder(expectedProjects.toArray(String[]::new))
+        );
+    }
+
+    private Collection<String> getProjectIds(RestClient client) throws IOException {
+        assert multiProjectEnabled;
+        final Request request = new Request("GET", "/_cluster/state/routing_table?multi_project=true");
+        try {
+            final ObjectPath response = ObjectPath.createFromResponse(client.performRequest(request));
+            final List<Map<String, Object>> projectRouting = response.evaluate("routing_table.projects");
+            return projectRouting.stream().map(obj -> (String) obj.get("id")).toList();
+        } catch (ResponseException e) {
+            logger.error("Failed to retrieve cluster state");
+            throw e;
+        }
+    }
+
+    protected void assertEmptyProject(String projectId) throws IOException {
+        assert multiProjectEnabled;
+        final Request request = new Request("GET", "_cluster/state/metadata,routing_table,customs");
+        request.setOptions(request.getOptions().toBuilder().addHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER, projectId).build());
+
+        var response = responseAsMap(adminClient().performRequest(request));
+        ObjectPath state = new ObjectPath(response);
+
+        final var indexNames = ((Map<?, ?>) state.evaluate("metadata.indices")).keySet();
+        final var routingTableEntries = ((Map<?, ?>) state.evaluate("routing_table.indices")).keySet();
+        if (indexNames.isEmpty() == false || routingTableEntries.isEmpty() == false) {
+            // Only the default project is allowed to have the security index after tests complete.
+            // The security index could show up in the indices, routing table, or both.
+            // If that happens, we need to check that it hasn't been modified by any leaking API calls.
+            if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id())
+                && (indexNames.isEmpty() || (indexNames.size() == 1 && indexNames.contains(".security-7")))
+                && (routingTableEntries.isEmpty() || (routingTableEntries.size() == 1 && routingTableEntries.contains(".security-7")))) {
+                checkSecurityIndex();
+            } else {
+                // If there are any other indices or if this is for a non-default project, we fail the test.
+                assertThat("Project [" + projectId + "] should not have indices", indexNames, empty());
+                assertThat("Project [" + projectId + "] should not have routing entries", routingTableEntries, empty());
+            }
+        }
+        assertThat(
+            "Project [" + projectId + "] should not have graveyard entries",
+            state.evaluate("metadata.index-graveyard.tombstones"),
+            empty()
+        );
+
+        final Map<String, ?> legacyTemplates = state.evaluate("metadata.templates");
+        if (legacyTemplates != null) {
+            var templateNames = legacyTemplates.keySet().stream().filter(name -> isXPackTemplate(name) == false).toList();
+            assertThat("Project [" + projectId + "] should not have legacy templates", templateNames, empty());
+        }
+
+        final Map<String, Object> indexTemplates = state.evaluate("metadata.index_template.index_template");
+        if (indexTemplates != null) {
+            var templateNames = indexTemplates.keySet().stream().filter(name -> isXPackTemplate(name) == false).toList();
+            assertThat("Project [" + projectId + "] should not have index templates", templateNames, empty());
+        } else if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id())) {
+            fail("Expected default project to have standard templates, but was null");
+        }
+
+        final Map<String, Object> componentTemplates = state.evaluate("metadata.component_template.component_template");
+        if (componentTemplates != null) {
+            var templateNames = componentTemplates.keySet().stream().filter(name -> isXPackTemplate(name) == false).toList();
+            assertThat("Project [" + projectId + "] should not have component templates", templateNames, empty());
+        } else if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id())) {
+            fail("Expected default project to have standard component templates, but was null");
+        }
+
+        final List<Map<String, ?>> pipelines = state.evaluate("metadata.ingest.pipeline");
+        if (pipelines != null) {
+            var pipelineNames = pipelines.stream()
+                .map(pipeline -> String.valueOf(pipeline.get("id")))
+                .filter(id -> isXPackIngestPipeline(id) == false)
+                .toList();
+            assertThat("Project [" + projectId + "] should not have ingest pipelines", pipelineNames, empty());
+        } else if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id())) {
+            fail("Expected default project to have standard ingest pipelines, but was null");
+        }
+
+        if (has(ProductFeature.ILM)) {
+            final Map<String, Object> ilmPolicies = state.evaluate("metadata.index_lifecycle.policies");
+            if (ilmPolicies != null) {
+                var policyNames = new HashSet<>(ilmPolicies.keySet());
+                policyNames.removeAll(preserveILMPolicyIds());
+                assertThat("Project [" + projectId + "] should not have ILM Policies", policyNames, empty());
+            } else if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id())) {
+                fail("Expected default project to have standard ILM policies, but was null");
+            }
+        }
+    }
+
+    private void checkSecurityIndex() throws IOException {
+        assert multiProjectEnabled;
+        final Request request = new Request("GET", "/_security/_query/role");
+        request.setJsonEntity("""
+            {
+              "query": {
+                "bool": {
+                  "must_not": {
+                    "term": {
+                      "metadata._reserved": true
+                    }
+                  }
+                }
+              }
+            }""");
+        request.setOptions(
+            request.getOptions().toBuilder().addHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER, Metadata.DEFAULT_PROJECT_ID.id()).build()
+        );
+        final var response = responseAsMap(adminClient().performRequest(request));
+        assertThat("Security index should not contain any non-reserved roles", (Collection<?>) response.get("roles"), empty());
+    }
 }

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

@@ -136,11 +136,6 @@ public class JwtMultiProjectIT extends ESRestTestCase {
         return entityAsMap(client().performRequest(request));
     }
 
-    private void createProject(String project) throws IOException {
-        final Request request = new Request("PUT", "/_project/" + project);
-        client().performRequest(request);
-    }
-
     private void deleteProject(String project) throws IOException {
         final Request request = new Request("DELETE", "/_project/" + project);
         client().performRequest(request);

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

@@ -7,9 +7,6 @@
 
 package org.elasticsearch.multiproject.test;
 
-import org.elasticsearch.client.Request;
-import org.elasticsearch.client.Response;
-import org.elasticsearch.client.ResponseException;
 import org.elasticsearch.client.RestClient;
 import org.elasticsearch.cluster.metadata.Metadata;
 import org.elasticsearch.common.settings.SecureString;
@@ -17,25 +14,17 @@ 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.ObjectPath;
 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.io.IOException;
-import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.empty;
-
 /**
  * Base class for running YAML Rest tests against a cluster with multiple projects
  */
@@ -99,132 +88,6 @@ public abstract class MultipleProjectsClientYamlSuiteTestCase extends ESClientYa
         }
     }
 
-    private void createProject(String project) throws IOException {
-        RestClient client = adminClient();
-        final Request request = new Request("PUT", "/_project/" + project);
-        try {
-            final Response response = client.performRequest(request);
-            logger.info("Created project {} : {}", project, response.getStatusLine());
-        } catch (ResponseException e) {
-            logger.error("Failed to create project: {}", project);
-            throw e;
-        }
-    }
-
-    private void assertEmptyProject(String projectId) throws IOException {
-        final Request request = new Request("GET", "_cluster/state/metadata,routing_table,customs");
-        request.setOptions(request.getOptions().toBuilder().addHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER, projectId).build());
-
-        var response = responseAsMap(adminClient().performRequest(request));
-        ObjectPath state = new ObjectPath(response);
-
-        final var indexNames = ((Map<?, ?>) state.evaluate("metadata.indices")).keySet();
-        final var routingTableEntries = ((Map<?, ?>) state.evaluate("routing_table.indices")).keySet();
-        if (indexNames.isEmpty() == false || routingTableEntries.isEmpty() == false) {
-            // Only the default project is allowed to have the security index after tests complete.
-            // The security index could show up in the indices, routing table, or both.
-            // If that happens, we need to check that it hasn't been modified by any leaking API calls.
-            if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id())
-                && (indexNames.isEmpty() || (indexNames.size() == 1 && indexNames.contains(".security-7")))
-                && (routingTableEntries.isEmpty() || (routingTableEntries.size() == 1 && routingTableEntries.contains(".security-7")))) {
-                checkSecurityIndex();
-            } else {
-                // If there are any other indices or if this is for a non-default project, we fail the test.
-                assertThat("Project [" + projectId + "] should not have indices", indexNames, empty());
-                assertThat("Project [" + projectId + "] should not have routing entries", routingTableEntries, empty());
-            }
-        }
-        assertThat(
-            "Project [" + projectId + "] should not have graveyard entries",
-            state.evaluate("metadata.index-graveyard.tombstones"),
-            empty()
-        );
-
-        final Map<String, ?> legacyTemplates = state.evaluate("metadata.templates");
-        if (legacyTemplates != null) {
-            var templateNames = legacyTemplates.keySet().stream().filter(name -> isXPackTemplate(name) == false).toList();
-            assertThat("Project [" + projectId + "] should not have legacy templates", templateNames, empty());
-        }
-
-        final Map<String, Object> indexTemplates = state.evaluate("metadata.index_template.index_template");
-        if (indexTemplates != null) {
-            var templateNames = indexTemplates.keySet().stream().filter(name -> isXPackTemplate(name) == false).toList();
-            assertThat("Project [" + projectId + "] should not have index templates", templateNames, empty());
-        } else if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id())) {
-            fail("Expected default project to have standard templates, but was null");
-        }
-
-        final Map<String, Object> componentTemplates = state.evaluate("metadata.component_template.component_template");
-        if (componentTemplates != null) {
-            var templateNames = componentTemplates.keySet().stream().filter(name -> isXPackTemplate(name) == false).toList();
-            assertThat("Project [" + projectId + "] should not have component templates", templateNames, empty());
-        } else if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id())) {
-            fail("Expected default project to have standard component templates, but was null");
-        }
-
-        final List<Map<String, ?>> pipelines = state.evaluate("metadata.ingest.pipeline");
-        if (pipelines != null) {
-            var pipelineNames = pipelines.stream()
-                .map(pipeline -> String.valueOf(pipeline.get("id")))
-                .filter(id -> isXPackIngestPipeline(id) == false)
-                .toList();
-            assertThat("Project [" + projectId + "] should not have ingest pipelines", pipelineNames, empty());
-        } else if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id())) {
-            fail("Expected default project to have standard ingest pipelines, but was null");
-        }
-
-        final Map<String, Object> ilmPolicies = state.evaluate("metadata.index_lifecycle.policies");
-        if (ilmPolicies != null) {
-            var policyNames = new HashSet<>(ilmPolicies.keySet());
-            policyNames.removeAll(preserveILMPolicyIds());
-            assertThat("Project [" + projectId + "] should not have ILM Policies", policyNames, empty());
-        } else if (projectId.equals(Metadata.DEFAULT_PROJECT_ID.id())) {
-            fail("Expected default project to have standard ILM policies, but was null");
-        }
-    }
-
-    private void checkSecurityIndex() throws IOException {
-        final Request request = new Request("GET", "/_security/_query/role");
-        request.setJsonEntity("""
-            {
-              "query": {
-                "bool": {
-                  "must_not": {
-                    "term": {
-                      "metadata._reserved": true
-                    }
-                  }
-                }
-              }
-            }""");
-        request.setOptions(
-            request.getOptions().toBuilder().addHeader(Task.X_ELASTIC_PROJECT_ID_HTTP_HEADER, Metadata.DEFAULT_PROJECT_ID.id()).build()
-        );
-        final var response = responseAsMap(adminClient().performRequest(request));
-        assertThat("Security index should not contain any non-reserved roles", (Collection<?>) response.get("roles"), empty());
-    }
-
-    private void assertProjectIds(RestClient client, List<String> expectedProjects) throws IOException {
-        final Collection<String> actualProjects = getProjectIds(client);
-        assertThat(
-            "Cluster returned project ids: " + actualProjects,
-            actualProjects,
-            containsInAnyOrder(expectedProjects.toArray(String[]::new))
-        );
-    }
-
-    protected Collection<String> getProjectIds(RestClient client) throws IOException {
-        final Request request = new Request("GET", "/_cluster/state/routing_table?multi_project=true");
-        try {
-            final ObjectPath response = ObjectPath.createFromResponse(client.performRequest(request));
-            final List<Map<String, Object>> projectRouting = response.evaluate("routing_table.projects");
-            return projectRouting.stream().map(obj -> (String) obj.get("id")).toList();
-        } catch (ResponseException e) {
-            logger.error("Failed to retrieve cluster state");
-            throw e;
-        }
-    }
-
     @Override
     protected Settings restClientSettings() {
         return clientSettings(true);