This commit address some build failures from the perspective of Intellij. These changes include: * changing an order of a dependency definition that seems to can cause Intellij build to fail. * introduction of an abstract class out of the test source set (seems to be an issue sharing classes cross projects with non-standard source sets. * a couple of missing dependency definitions (not sure how the command line worked prior to this)
This commit is contained in:
parent
b764f8977e
commit
5b7246157f
|
@ -155,7 +155,7 @@ public abstract class GradleUtils {
|
||||||
project.getPluginManager().withPlugin("idea", p -> {
|
project.getPluginManager().withPlugin("idea", p -> {
|
||||||
IdeaModel idea = project.getExtensions().getByType(IdeaModel.class);
|
IdeaModel idea = project.getExtensions().getByType(IdeaModel.class);
|
||||||
idea.getModule().setTestSourceDirs(testSourceSet.getJava().getSrcDirs());
|
idea.getModule().setTestSourceDirs(testSourceSet.getJava().getSrcDirs());
|
||||||
idea.getModule().getScopes().put("TEST", Map.of("plus", List.of(runtimeClasspathConfiguration)));
|
idea.getModule().getScopes().put(testSourceSet.getName(), Map.of("plus", List.of(runtimeClasspathConfiguration)));
|
||||||
});
|
});
|
||||||
project.getPluginManager().withPlugin("eclipse", p -> {
|
project.getPluginManager().withPlugin("eclipse", p -> {
|
||||||
EclipseModel eclipse = project.getExtensions().getByType(EclipseModel.class);
|
EclipseModel eclipse = project.getExtensions().getByType(EclipseModel.class);
|
||||||
|
@ -196,6 +196,19 @@ public abstract class GradleUtils {
|
||||||
child.setRuntimeClasspath(project.getObjects().fileCollection().from(child.getRuntimeClasspath(), parent.getOutput()));
|
child.setRuntimeClasspath(project.getObjects().fileCollection().from(child.getRuntimeClasspath(), parent.getOutput()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends one configuration from another and refreshes the classpath of a provided Test.
|
||||||
|
* The Test parameter is only needed for eagerly defined test tasks.
|
||||||
|
*/
|
||||||
|
public static void extendSourceSet(Project project, String parentSourceSetName, String childSourceSetName, Test test) {
|
||||||
|
extendSourceSet(project, parentSourceSetName, childSourceSetName);
|
||||||
|
if (test != null) {
|
||||||
|
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
|
||||||
|
SourceSet child = sourceSets.getByName(childSourceSetName);
|
||||||
|
test.setClasspath(child.getRuntimeClasspath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Dependency projectDependency(Project project, String projectPath, String projectConfig) {
|
public static Dependency projectDependency(Project project, String projectPath, String projectConfig) {
|
||||||
if (project.findProject(projectPath) == null) {
|
if (project.findProject(projectPath) == null) {
|
||||||
throw new GradleException("no project [" + projectPath + "], project names: " + project.getRootProject().getAllprojects());
|
throw new GradleException("no project [" + projectPath + "], project names: " + project.getRootProject().getAllprojects());
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import org.elasticsearch.gradle.info.BuildParams
|
import org.elasticsearch.gradle.info.BuildParams
|
||||||
|
import org.elasticsearch.gradle.util.GradleUtils
|
||||||
|
|
||||||
apply plugin: 'elasticsearch.java-rest-test'
|
apply plugin: 'elasticsearch.java-rest-test'
|
||||||
apply plugin: 'elasticsearch.esplugin'
|
apply plugin: 'elasticsearch.esplugin'
|
||||||
|
@ -8,10 +9,8 @@ esplugin {
|
||||||
classname 'org.elasticsearch.DieWithDignityPlugin'
|
classname 'org.elasticsearch.DieWithDignityPlugin'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// let the javaRestTest see the classpath of main
|
// let the javaRestTest see the classpath of main
|
||||||
javaRestTestImplementation project.sourceSets.main.runtimeClasspath
|
GradleUtils.extendSourceSet(project, "main", "javaRestTest", javaRestTest)
|
||||||
}
|
|
||||||
|
|
||||||
javaRestTest {
|
javaRestTest {
|
||||||
systemProperty 'tests.security.manager', 'false'
|
systemProperty 'tests.security.manager', 'false'
|
||||||
|
|
|
@ -17,7 +17,7 @@ import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
|
||||||
import org.elasticsearch.test.rest.yaml.ClientYamlTestClient;
|
import org.elasticsearch.test.rest.yaml.ClientYamlTestClient;
|
||||||
import org.elasticsearch.test.rest.yaml.ClientYamlTestResponse;
|
import org.elasticsearch.test.rest.yaml.ClientYamlTestResponse;
|
||||||
import org.elasticsearch.test.rest.yaml.restspec.ClientYamlSuiteRestSpec;
|
import org.elasticsearch.test.rest.yaml.restspec.ClientYamlSuiteRestSpec;
|
||||||
import org.elasticsearch.xpack.test.rest.XPackRestIT;
|
import org.elasticsearch.xpack.test.rest.AbstractXPackRestTest;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -29,7 +29,7 @@ import static java.util.Collections.singletonMap;
|
||||||
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
public class XDocsClientYamlTestSuiteIT extends XPackRestIT {
|
public class XDocsClientYamlTestSuiteIT extends AbstractXPackRestTest {
|
||||||
private static final String USER_TOKEN = basicAuthHeaderValue("test_admin", new SecureString("x-pack-test-password".toCharArray()));
|
private static final String USER_TOKEN = basicAuthHeaderValue("test_admin", new SecureString("x-pack-test-password".toCharArray()));
|
||||||
|
|
||||||
public XDocsClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) {
|
public XDocsClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) {
|
||||||
|
|
|
@ -12,9 +12,11 @@ apply plugin: 'elasticsearch.validate-rest-spec'
|
||||||
archivesBaseName = 'x-pack'
|
archivesBaseName = 'x-pack'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
yamlRestTestImplementation project(xpackModule('core')) // this redundant dependency is here to make IntelliJ happy
|
testImplementation project(xpackModule('core'))
|
||||||
yamlRestTestImplementation project(path: xpackModule('core'), configuration: 'testArtifacts')
|
testImplementation project(path: xpackModule('core'), configuration: 'testArtifacts')
|
||||||
javaRestTestImplementation project(path: xpackModule('core'), configuration: 'testArtifacts')
|
javaRestTestImplementation project(path: xpackModule('core'), configuration: 'testArtifacts')
|
||||||
|
// let the yamlRestTest see the classpath of test
|
||||||
|
yamlRestTestImplementation project.sourceSets.test.runtimeClasspath
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
@ -61,8 +63,6 @@ subprojects {
|
||||||
configurations {
|
configurations {
|
||||||
testArtifacts.extendsFrom testRuntime
|
testArtifacts.extendsFrom testRuntime
|
||||||
testArtifacts.extendsFrom testImplementation
|
testArtifacts.extendsFrom testImplementation
|
||||||
testArtifacts.extendsFrom yamlRestTestImplementation
|
|
||||||
testArtifacts.extendsFrom javaRestTestImplementation
|
|
||||||
}
|
}
|
||||||
|
|
||||||
restResources {
|
restResources {
|
||||||
|
@ -70,9 +70,6 @@ restResources {
|
||||||
includeCore '*'
|
includeCore '*'
|
||||||
includeXpack '*'
|
includeXpack '*'
|
||||||
}
|
}
|
||||||
restTests {
|
|
||||||
includeXpack '*'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//The api and tests need to stay at src/test/... since some external tooling depends on that exact file path.
|
//The api and tests need to stay at src/test/... since some external tooling depends on that exact file path.
|
||||||
|
@ -83,10 +80,8 @@ artifacts {
|
||||||
|
|
||||||
def testJar = tasks.register("testJar", Jar) {
|
def testJar = tasks.register("testJar", Jar) {
|
||||||
appendix 'test'
|
appendix 'test'
|
||||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
|
||||||
from sourceSets.test.output
|
from sourceSets.test.output
|
||||||
from sourceSets.yamlRestTest.output
|
|
||||||
from sourceSets.javaRestTest.output
|
|
||||||
/*
|
/*
|
||||||
* Stick the license and notice file in the jar. This isn't strictly
|
* Stick the license and notice file in the jar. This isn't strictly
|
||||||
* needed because we don't publish it but it makes our super-paranoid
|
* needed because we don't publish it but it makes our super-paranoid
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import org.elasticsearch.gradle.util.GradleUtils
|
||||||
|
|
||||||
apply plugin: 'elasticsearch.esplugin'
|
apply plugin: 'elasticsearch.esplugin'
|
||||||
apply plugin: 'elasticsearch.java-rest-test'
|
apply plugin: 'elasticsearch.java-rest-test'
|
||||||
|
|
||||||
|
@ -9,10 +11,12 @@ esplugin {
|
||||||
dependencies {
|
dependencies {
|
||||||
javaRestTestImplementation("com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}")
|
javaRestTestImplementation("com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}")
|
||||||
javaRestTestImplementation("com.fasterxml.jackson.core:jackson-databind:${versions.jackson}")
|
javaRestTestImplementation("com.fasterxml.jackson.core:jackson-databind:${versions.jackson}")
|
||||||
// let the javaRestTest see the classpath of main
|
|
||||||
javaRestTestImplementation project.sourceSets.main.runtimeClasspath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// let the javaRestTest see the classpath of main
|
||||||
|
GradleUtils.extendSourceSet(project, "main", "javaRestTest", javaRestTest)
|
||||||
|
|
||||||
|
|
||||||
restResources {
|
restResources {
|
||||||
restApi {
|
restApi {
|
||||||
includeCore '_common', 'indices', 'index'
|
includeCore '_common', 'indices', 'index'
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
|
import org.elasticsearch.gradle.util.GradleUtils
|
||||||
|
|
||||||
apply plugin: 'elasticsearch.java-rest-test'
|
apply plugin: 'elasticsearch.java-rest-test'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
javaRestTestImplementation project(path: xpackProject('plugin').path, configuration: 'testArtifacts')
|
javaRestTestImplementation project(path: xpackProject('plugin').path, configuration: 'testArtifacts')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// let the javaRestTest see the classpath of main
|
||||||
|
GradleUtils.extendSourceSet(project, "main", "javaRestTest", javaRestTest)
|
||||||
|
|
||||||
|
|
||||||
File repoDir = file("$buildDir/testclusters/repo")
|
File repoDir = file("$buildDir/testclusters/repo")
|
||||||
|
|
||||||
javaRestTest {
|
javaRestTest {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
apply plugin: 'elasticsearch.java-rest-test'
|
apply plugin: 'elasticsearch.java-rest-test'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
javaRestTestImplementation project(path: xpackModule('core'))
|
||||||
javaRestTestImplementation project(path: xpackProject('plugin').path, configuration: 'testArtifacts')
|
javaRestTestImplementation project(path: xpackProject('plugin').path, configuration: 'testArtifacts')
|
||||||
testImplementation project(":client:rest-high-level")
|
testImplementation project(":client:rest-high-level")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
apply plugin: 'elasticsearch.yaml-rest-test'
|
apply plugin: 'elasticsearch.yaml-rest-test'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
yamlRestTestImplementation project(path: xpackModule('core'), configuration: 'testArtifacts')
|
|
||||||
yamlRestTestImplementation project(path: xpackModule('core'))
|
yamlRestTestImplementation project(path: xpackModule('core'))
|
||||||
|
yamlRestTestImplementation project(path: xpackModule('core'), configuration: 'testArtifacts')
|
||||||
yamlRestTestImplementation project(path: xpackProject('plugin').path, configuration: 'testArtifacts')
|
yamlRestTestImplementation project(path: xpackProject('plugin').path, configuration: 'testArtifacts')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.test.SecuritySettingsSourceField;
|
import org.elasticsearch.test.SecuritySettingsSourceField;
|
||||||
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
|
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
|
||||||
import org.elasticsearch.xpack.test.rest.XPackRestIT;
|
import org.elasticsearch.xpack.test.rest.AbstractXPackRestTest;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -19,7 +19,7 @@ import java.util.Map;
|
||||||
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||||
|
|
||||||
|
|
||||||
public class MlWithSecurityIT extends XPackRestIT {
|
public class MlWithSecurityIT extends AbstractXPackRestTest {
|
||||||
|
|
||||||
private static final String TEST_ADMIN_USERNAME = "x_pack_rest_user";
|
private static final String TEST_ADMIN_USERNAME = "x_pack_rest_user";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
apply plugin: 'elasticsearch.java-rest-test'
|
apply plugin: 'elasticsearch.java-rest-test'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
javaRestTestImplementation project(path: xpackModule('core'))
|
||||||
javaRestTestImplementation project(path: xpackProject('plugin').path, configuration: 'testArtifacts')
|
javaRestTestImplementation project(path: xpackProject('plugin').path, configuration: 'testArtifacts')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,285 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.xpack.test.rest;
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.apache.lucene.util.TimeUnits;
|
||||||
|
import org.elasticsearch.ElasticsearchException;
|
||||||
|
import org.elasticsearch.common.CheckedFunction;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
|
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||||
|
import org.elasticsearch.plugins.MetadataUpgrader;
|
||||||
|
import org.elasticsearch.test.SecuritySettingsSourceField;
|
||||||
|
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||||
|
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
|
||||||
|
import org.elasticsearch.test.rest.yaml.ClientYamlTestResponse;
|
||||||
|
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
|
||||||
|
import org.elasticsearch.xpack.core.ml.MlConfigIndex;
|
||||||
|
import org.elasticsearch.xpack.core.ml.MlMetaIndex;
|
||||||
|
import org.elasticsearch.xpack.core.ml.integration.MlRestTestStateCleaner;
|
||||||
|
import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndex;
|
||||||
|
import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndexFields;
|
||||||
|
import org.elasticsearch.xpack.core.ml.notifications.NotificationsIndex;
|
||||||
|
import org.elasticsearch.xpack.core.rollup.job.RollupJob;
|
||||||
|
import org.elasticsearch.xpack.core.transform.transforms.persistence.TransformInternalIndexConstants;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.Collections.emptyMap;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue;
|
||||||
|
import static org.elasticsearch.rest.action.search.RestSearchAction.TOTAL_HITS_AS_INT_PARAM;
|
||||||
|
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
|
||||||
|
/** Runs rest tests against external cluster */
|
||||||
|
// TODO: Remove this timeout increase once this test suite is broken up
|
||||||
|
@TimeoutSuite(millis = 60 * TimeUnits.MINUTE)
|
||||||
|
public class AbstractXPackRestTest extends ESClientYamlSuiteTestCase {
|
||||||
|
private static final String BASIC_AUTH_VALUE =
|
||||||
|
basicAuthHeaderValue("x_pack_rest_user", SecuritySettingsSourceField.TEST_PASSWORD_SECURE_STRING);
|
||||||
|
|
||||||
|
public AbstractXPackRestTest(ClientYamlTestCandidate testCandidate) {
|
||||||
|
super(testCandidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParametersFactory
|
||||||
|
public static Iterable<Object[]> parameters() throws Exception {
|
||||||
|
return createParameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Settings restClientSettings() {
|
||||||
|
return Settings.builder()
|
||||||
|
.put(ThreadContext.PREFIX + ".Authorization", BASIC_AUTH_VALUE)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setupForTests() throws Exception {
|
||||||
|
waitForTemplates();
|
||||||
|
enableMonitoring();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for Machine Learning and Transform templates to be created by the {@link MetadataUpgrader}
|
||||||
|
*/
|
||||||
|
private void waitForTemplates() throws Exception {
|
||||||
|
if (installTemplates()) {
|
||||||
|
List<String> templates = new ArrayList<>();
|
||||||
|
templates.addAll(
|
||||||
|
Arrays.asList(
|
||||||
|
NotificationsIndex.NOTIFICATIONS_INDEX,
|
||||||
|
MlMetaIndex.indexName(),
|
||||||
|
AnomalyDetectorsIndexFields.STATE_INDEX_PREFIX,
|
||||||
|
AnomalyDetectorsIndex.jobResultsIndexPrefix(),
|
||||||
|
MlConfigIndex.indexName(),
|
||||||
|
TransformInternalIndexConstants.AUDIT_INDEX,
|
||||||
|
TransformInternalIndexConstants.LATEST_INDEX_NAME
|
||||||
|
));
|
||||||
|
|
||||||
|
for (String template : templates) {
|
||||||
|
awaitCallApi("indices.exists_template", singletonMap("name", template), emptyList(),
|
||||||
|
response -> true,
|
||||||
|
() -> "Exception when waiting for [" + template + "] template to be created");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable monitoring and waits for monitoring documents to be collected and indexed in
|
||||||
|
* monitoring indices.This is the signal that the local exporter is started and ready
|
||||||
|
* for the tests.
|
||||||
|
*/
|
||||||
|
private void enableMonitoring() throws Exception {
|
||||||
|
if (isMonitoringTest()) {
|
||||||
|
final ClientYamlTestResponse xpackUsage =
|
||||||
|
callApi("xpack.usage", singletonMap("filter_path", "monitoring.enabled_exporters"), emptyList(), getApiCallHeaders());
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object> exporters = (Map<String, Object>) xpackUsage.evaluate("monitoring.enabled_exporters");
|
||||||
|
assertNotNull("List of monitoring exporters must not be null", exporters);
|
||||||
|
assertThat("List of enabled exporters must be empty before enabling monitoring",
|
||||||
|
XContentMapValues.extractRawValues("monitoring.enabled_exporters", exporters), hasSize(0));
|
||||||
|
|
||||||
|
final Map<String, Object> settings = new HashMap<>();
|
||||||
|
settings.put("xpack.monitoring.collection.enabled", true);
|
||||||
|
settings.put("xpack.monitoring.collection.interval", "1s");
|
||||||
|
settings.put("xpack.monitoring.exporters._local.type", "local");
|
||||||
|
settings.put("xpack.monitoring.exporters._local.enabled", true);
|
||||||
|
|
||||||
|
awaitCallApi("cluster.put_settings", emptyMap(),
|
||||||
|
singletonList(singletonMap("transient", settings)),
|
||||||
|
response -> {
|
||||||
|
Object acknowledged = response.evaluate("acknowledged");
|
||||||
|
return acknowledged != null && (Boolean) acknowledged;
|
||||||
|
},
|
||||||
|
() -> "Exception when enabling monitoring");
|
||||||
|
Map<String, String> searchParams = new HashMap<>();
|
||||||
|
searchParams.put("index", ".monitoring-*");
|
||||||
|
searchParams.put(TOTAL_HITS_AS_INT_PARAM, "true");
|
||||||
|
awaitCallApi("search", searchParams, emptyList(),
|
||||||
|
response -> ((Number) response.evaluate("hits.total")).intValue() > 0,
|
||||||
|
() -> "Exception when waiting for monitoring documents to be indexed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable monitoring
|
||||||
|
*/
|
||||||
|
private void disableMonitoring() throws Exception {
|
||||||
|
if (isMonitoringTest()) {
|
||||||
|
final Map<String, Object> settings = new HashMap<>();
|
||||||
|
settings.put("xpack.monitoring.collection.enabled", null);
|
||||||
|
settings.put("xpack.monitoring.collection.interval", null);
|
||||||
|
settings.put("xpack.monitoring.exporters._local.enabled", null);
|
||||||
|
|
||||||
|
awaitCallApi("cluster.put_settings", emptyMap(),
|
||||||
|
singletonList(singletonMap("transient", settings)),
|
||||||
|
response -> {
|
||||||
|
Object acknowledged = response.evaluate("acknowledged");
|
||||||
|
return acknowledged != null && (Boolean) acknowledged;
|
||||||
|
},
|
||||||
|
() -> "Exception when disabling monitoring");
|
||||||
|
|
||||||
|
assertBusy(() -> {
|
||||||
|
try {
|
||||||
|
ClientYamlTestResponse response =
|
||||||
|
callApi("xpack.usage", singletonMap("filter_path", "monitoring.enabled_exporters"), emptyList(),
|
||||||
|
getApiCallHeaders());
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, ?> exporters = (Map<String, ?>) response.evaluate("monitoring.enabled_exporters");
|
||||||
|
if (exporters.isEmpty() == false) {
|
||||||
|
fail("Exporters were not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("node_id", "_local");
|
||||||
|
params.put("metric", "thread_pool");
|
||||||
|
params.put("filter_path", "nodes.*.thread_pool.write.active");
|
||||||
|
response = callApi("nodes.stats", params, emptyList(), getApiCallHeaders());
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object> nodes = (Map<String, Object>) response.evaluate("nodes");
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object> node = (Map<String, Object>) nodes.values().iterator().next();
|
||||||
|
|
||||||
|
final Number activeWrites = (Number) extractValue("thread_pool.write.active", node);
|
||||||
|
assertNotNull(activeWrites);
|
||||||
|
assertThat(activeWrites, equalTo(0));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ElasticsearchException("Failed to wait for monitoring exporters to stop:", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup after tests.
|
||||||
|
*
|
||||||
|
* Feature-specific cleanup methods should be called from here rather than using
|
||||||
|
* separate @After annotated methods to ensure there is a well-defined cleanup order.
|
||||||
|
*/
|
||||||
|
@After
|
||||||
|
public void cleanup() throws Exception {
|
||||||
|
disableMonitoring();
|
||||||
|
clearMlState();
|
||||||
|
if (isWaitForPendingTasks()) {
|
||||||
|
// This waits for pending tasks to complete, so must go last (otherwise
|
||||||
|
// it could be waiting for pending tasks while monitoring is still running).
|
||||||
|
waitForPendingTasks(adminClient(), task -> {
|
||||||
|
// Don't check rollup jobs because we clear them in the superclass.
|
||||||
|
return task.contains(RollupJob.NAME);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete any left over machine learning datafeeds and jobs.
|
||||||
|
*/
|
||||||
|
private void clearMlState() throws Exception {
|
||||||
|
if (isMachineLearningTest()) {
|
||||||
|
new MlRestTestStateCleaner(logger, adminClient()).clearMlMetadata();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes an API call using the admin context, waiting for it to succeed.
|
||||||
|
*/
|
||||||
|
private void awaitCallApi(String apiName,
|
||||||
|
Map<String, String> params,
|
||||||
|
List<Map<String, Object>> bodies,
|
||||||
|
CheckedFunction<ClientYamlTestResponse, Boolean, IOException> success,
|
||||||
|
Supplier<String> error) {
|
||||||
|
try {
|
||||||
|
final AtomicReference<ClientYamlTestResponse> response = new AtomicReference<>();
|
||||||
|
assertBusy(() -> {
|
||||||
|
// The actual method call that sends the API requests returns a Future, but we immediately
|
||||||
|
// call .get() on it so there's no need for this method to do any other awaiting.
|
||||||
|
response.set(callApi(apiName, params, bodies, getApiCallHeaders()));
|
||||||
|
assertEquals(HttpStatus.SC_OK, response.get().getStatusCode());
|
||||||
|
});
|
||||||
|
success.apply(response.get());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException(error.get(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientYamlTestResponse callApi(String apiName,
|
||||||
|
Map<String, String> params,
|
||||||
|
List<Map<String, Object>> bodies,
|
||||||
|
Map<String, String> headers) throws IOException {
|
||||||
|
return getAdminExecutionContext().callApi(apiName, params, bodies, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map<String, String> getApiCallHeaders() {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean installTemplates() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isMonitoringTest() {
|
||||||
|
String testName = getTestName();
|
||||||
|
return testName != null && (testName.contains("=monitoring/") || testName.contains("=monitoring\\"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isMachineLearningTest() {
|
||||||
|
String testName = getTestName();
|
||||||
|
return testName != null && (testName.contains("=ml/") || testName.contains("=ml\\"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should each test wait for pending tasks to finish after execution?
|
||||||
|
* @return Wait for pending tasks
|
||||||
|
*/
|
||||||
|
protected boolean isWaitForPendingTasks() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,61 +3,13 @@
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.elasticsearch.xpack.test.rest;
|
package org.elasticsearch.xpack.test.rest;
|
||||||
|
|
||||||
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||||
|
|
||||||
import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite;
|
|
||||||
import org.apache.http.HttpStatus;
|
|
||||||
import org.apache.lucene.util.TimeUnits;
|
|
||||||
import org.elasticsearch.ElasticsearchException;
|
|
||||||
import org.elasticsearch.common.CheckedFunction;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
|
||||||
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
|
||||||
import org.elasticsearch.plugins.MetadataUpgrader;
|
|
||||||
import org.elasticsearch.test.SecuritySettingsSourceField;
|
|
||||||
import org.elasticsearch.test.rest.ESRestTestCase;
|
|
||||||
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
|
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
|
||||||
import org.elasticsearch.test.rest.yaml.ClientYamlTestResponse;
|
|
||||||
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
|
|
||||||
import org.elasticsearch.xpack.core.ml.MlConfigIndex;
|
|
||||||
import org.elasticsearch.xpack.core.ml.MlMetaIndex;
|
|
||||||
import org.elasticsearch.xpack.core.ml.integration.MlRestTestStateCleaner;
|
|
||||||
import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndex;
|
|
||||||
import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndexFields;
|
|
||||||
import org.elasticsearch.xpack.core.ml.notifications.NotificationsIndex;
|
|
||||||
import org.elasticsearch.xpack.core.rollup.job.RollupJob;
|
|
||||||
import org.elasticsearch.xpack.core.transform.transforms.persistence.TransformInternalIndexConstants;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
public class XPackRestIT extends AbstractXPackRestTest {
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
import static java.util.Collections.emptyMap;
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.Collections.singletonMap;
|
|
||||||
import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue;
|
|
||||||
import static org.elasticsearch.rest.action.search.RestSearchAction.TOTAL_HITS_AS_INT_PARAM;
|
|
||||||
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
|
||||||
|
|
||||||
/** Runs rest tests against external cluster */
|
|
||||||
// TODO: Remove this timeout increase once this test suite is broken up
|
|
||||||
@TimeoutSuite(millis = 60 * TimeUnits.MINUTE)
|
|
||||||
public class XPackRestIT extends ESClientYamlSuiteTestCase {
|
|
||||||
static final String BASIC_AUTH_VALUE =
|
|
||||||
basicAuthHeaderValue("x_pack_rest_user", SecuritySettingsSourceField.TEST_PASSWORD_SECURE_STRING);
|
|
||||||
|
|
||||||
public XPackRestIT(ClientYamlTestCandidate testCandidate) {
|
public XPackRestIT(ClientYamlTestCandidate testCandidate) {
|
||||||
super(testCandidate);
|
super(testCandidate);
|
||||||
|
@ -67,219 +19,5 @@ public class XPackRestIT extends ESClientYamlSuiteTestCase {
|
||||||
public static Iterable<Object[]> parameters() throws Exception {
|
public static Iterable<Object[]> parameters() throws Exception {
|
||||||
return createParameters();
|
return createParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Settings restClientSettings() {
|
|
||||||
return Settings.builder()
|
|
||||||
.put(ThreadContext.PREFIX + ".Authorization", BASIC_AUTH_VALUE)
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setupForTests() throws Exception {
|
|
||||||
waitForTemplates();
|
|
||||||
enableMonitoring();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits for Machine Learning and Transform templates to be created by the {@link MetadataUpgrader}
|
|
||||||
*/
|
|
||||||
private void waitForTemplates() throws Exception {
|
|
||||||
if (installTemplates()) {
|
|
||||||
List<String> templates = new ArrayList<>();
|
|
||||||
templates.addAll(
|
|
||||||
Arrays.asList(
|
|
||||||
NotificationsIndex.NOTIFICATIONS_INDEX,
|
|
||||||
MlMetaIndex.indexName(),
|
|
||||||
AnomalyDetectorsIndexFields.STATE_INDEX_PREFIX,
|
|
||||||
AnomalyDetectorsIndex.jobResultsIndexPrefix(),
|
|
||||||
MlConfigIndex.indexName(),
|
|
||||||
TransformInternalIndexConstants.AUDIT_INDEX,
|
|
||||||
TransformInternalIndexConstants.LATEST_INDEX_NAME
|
|
||||||
));
|
|
||||||
|
|
||||||
for (String template : templates) {
|
|
||||||
awaitCallApi("indices.exists_template", singletonMap("name", template), emptyList(),
|
|
||||||
response -> true,
|
|
||||||
() -> "Exception when waiting for [" + template + "] template to be created");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable monitoring and waits for monitoring documents to be collected and indexed in
|
|
||||||
* monitoring indices.This is the signal that the local exporter is started and ready
|
|
||||||
* for the tests.
|
|
||||||
*/
|
|
||||||
private void enableMonitoring() throws Exception {
|
|
||||||
if (isMonitoringTest()) {
|
|
||||||
final ClientYamlTestResponse xpackUsage =
|
|
||||||
callApi("xpack.usage", singletonMap("filter_path", "monitoring.enabled_exporters"), emptyList(), getApiCallHeaders());
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Map<String, Object> exporters = (Map<String, Object>) xpackUsage.evaluate("monitoring.enabled_exporters");
|
|
||||||
assertNotNull("List of monitoring exporters must not be null", exporters);
|
|
||||||
assertThat("List of enabled exporters must be empty before enabling monitoring",
|
|
||||||
XContentMapValues.extractRawValues("monitoring.enabled_exporters", exporters), hasSize(0));
|
|
||||||
|
|
||||||
final Map<String, Object> settings = new HashMap<>();
|
|
||||||
settings.put("xpack.monitoring.collection.enabled", true);
|
|
||||||
settings.put("xpack.monitoring.collection.interval", "1s");
|
|
||||||
settings.put("xpack.monitoring.exporters._local.type", "local");
|
|
||||||
settings.put("xpack.monitoring.exporters._local.enabled", true);
|
|
||||||
|
|
||||||
awaitCallApi("cluster.put_settings", emptyMap(),
|
|
||||||
singletonList(singletonMap("transient", settings)),
|
|
||||||
response -> {
|
|
||||||
Object acknowledged = response.evaluate("acknowledged");
|
|
||||||
return acknowledged != null && (Boolean) acknowledged;
|
|
||||||
},
|
|
||||||
() -> "Exception when enabling monitoring");
|
|
||||||
Map<String, String> searchParams = new HashMap<>();
|
|
||||||
searchParams.put("index", ".monitoring-*");
|
|
||||||
searchParams.put(TOTAL_HITS_AS_INT_PARAM, "true");
|
|
||||||
awaitCallApi("search", searchParams, emptyList(),
|
|
||||||
response -> ((Number) response.evaluate("hits.total")).intValue() > 0,
|
|
||||||
() -> "Exception when waiting for monitoring documents to be indexed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable monitoring
|
|
||||||
*/
|
|
||||||
private void disableMonitoring() throws Exception {
|
|
||||||
if (isMonitoringTest()) {
|
|
||||||
final Map<String, Object> settings = new HashMap<>();
|
|
||||||
settings.put("xpack.monitoring.collection.enabled", null);
|
|
||||||
settings.put("xpack.monitoring.collection.interval", null);
|
|
||||||
settings.put("xpack.monitoring.exporters._local.enabled", null);
|
|
||||||
|
|
||||||
awaitCallApi("cluster.put_settings", emptyMap(),
|
|
||||||
singletonList(singletonMap("transient", settings)),
|
|
||||||
response -> {
|
|
||||||
Object acknowledged = response.evaluate("acknowledged");
|
|
||||||
return acknowledged != null && (Boolean) acknowledged;
|
|
||||||
},
|
|
||||||
() -> "Exception when disabling monitoring");
|
|
||||||
|
|
||||||
assertBusy(() -> {
|
|
||||||
try {
|
|
||||||
ClientYamlTestResponse response =
|
|
||||||
callApi("xpack.usage", singletonMap("filter_path", "monitoring.enabled_exporters"), emptyList(),
|
|
||||||
getApiCallHeaders());
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Map<String, ?> exporters = (Map<String, ?>) response.evaluate("monitoring.enabled_exporters");
|
|
||||||
if (exporters.isEmpty() == false) {
|
|
||||||
fail("Exporters were not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, String> params = new HashMap<>();
|
|
||||||
params.put("node_id", "_local");
|
|
||||||
params.put("metric", "thread_pool");
|
|
||||||
params.put("filter_path", "nodes.*.thread_pool.write.active");
|
|
||||||
response = callApi("nodes.stats", params, emptyList(), getApiCallHeaders());
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Map<String, Object> nodes = (Map<String, Object>) response.evaluate("nodes");
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Map<String, Object> node = (Map<String, Object>) nodes.values().iterator().next();
|
|
||||||
|
|
||||||
final Number activeWrites = (Number) extractValue("thread_pool.write.active", node);
|
|
||||||
assertNotNull(activeWrites);
|
|
||||||
assertThat(activeWrites, equalTo(0));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ElasticsearchException("Failed to wait for monitoring exporters to stop:", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleanup after tests.
|
|
||||||
*
|
|
||||||
* Feature-specific cleanup methods should be called from here rather than using
|
|
||||||
* separate @After annotated methods to ensure there is a well-defined cleanup order.
|
|
||||||
*/
|
|
||||||
@After
|
|
||||||
public void cleanup() throws Exception {
|
|
||||||
disableMonitoring();
|
|
||||||
clearMlState();
|
|
||||||
if (isWaitForPendingTasks()) {
|
|
||||||
// This waits for pending tasks to complete, so must go last (otherwise
|
|
||||||
// it could be waiting for pending tasks while monitoring is still running).
|
|
||||||
waitForPendingTasks(adminClient(), task -> {
|
|
||||||
// Don't check rollup jobs because we clear them in the superclass.
|
|
||||||
return task.contains(RollupJob.NAME);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete any left over machine learning datafeeds and jobs.
|
|
||||||
*/
|
|
||||||
private void clearMlState() throws Exception {
|
|
||||||
if (isMachineLearningTest()) {
|
|
||||||
new MlRestTestStateCleaner(logger, adminClient()).clearMlMetadata();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes an API call using the admin context, waiting for it to succeed.
|
|
||||||
*/
|
|
||||||
private void awaitCallApi(String apiName,
|
|
||||||
Map<String, String> params,
|
|
||||||
List<Map<String, Object>> bodies,
|
|
||||||
CheckedFunction<ClientYamlTestResponse, Boolean, IOException> success,
|
|
||||||
Supplier<String> error) {
|
|
||||||
try {
|
|
||||||
final AtomicReference<ClientYamlTestResponse> response = new AtomicReference<>();
|
|
||||||
assertBusy(() -> {
|
|
||||||
// The actual method call that sends the API requests returns a Future, but we immediately
|
|
||||||
// call .get() on it so there's no need for this method to do any other awaiting.
|
|
||||||
response.set(callApi(apiName, params, bodies, getApiCallHeaders()));
|
|
||||||
assertEquals(HttpStatus.SC_OK, response.get().getStatusCode());
|
|
||||||
});
|
|
||||||
success.apply(response.get());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IllegalStateException(error.get(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClientYamlTestResponse callApi(String apiName,
|
|
||||||
Map<String, String> params,
|
|
||||||
List<Map<String, Object>> bodies,
|
|
||||||
Map<String, String> headers) throws IOException {
|
|
||||||
return getAdminExecutionContext().callApi(apiName, params, bodies, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Map<String, String> getApiCallHeaders() {
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean installTemplates() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isMonitoringTest() {
|
|
||||||
String testName = getTestName();
|
|
||||||
return testName != null && (testName.contains("=monitoring/") || testName.contains("=monitoring\\"));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isMachineLearningTest() {
|
|
||||||
String testName = getTestName();
|
|
||||||
return testName != null && (testName.contains("=ml/") || testName.contains("=ml\\"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should each test wait for pending tasks to finish after execution?
|
|
||||||
* @return Wait for pending tasks
|
|
||||||
*/
|
|
||||||
protected boolean isWaitForPendingTasks() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import org.elasticsearch.gradle.util.GradleUtils
|
||||||
|
|
||||||
apply plugin: 'elasticsearch.java-rest-test'
|
apply plugin: 'elasticsearch.java-rest-test'
|
||||||
apply plugin: 'elasticsearch.esplugin'
|
apply plugin: 'elasticsearch.esplugin'
|
||||||
|
|
||||||
|
@ -18,7 +20,6 @@ dependencies {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
javaRestTest {
|
javaRestTest {
|
||||||
systemProperty 'tests.security.manager', 'false'
|
systemProperty 'tests.security.manager', 'false'
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue