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