Skip to content

Commit

Permalink
Add more integ tests for query insights (opensearch-project#71) (open…
Browse files Browse the repository at this point in the history
…search-project#87)

(cherry picked from commit 2c02a2d)

Signed-off-by: Chenyang Ji <[email protected]>
  • Loading branch information
ansjcy authored Sep 4, 2024
1 parent 9c142a7 commit ae269c5
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 172 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ integTest {
if (System.getProperty("security.enabled") == "true" || System.getProperty("https") == "true") {
// Exclude this IT, because they executed in another task (:integTestWithSecurity)
exclude 'org/opensearch/plugin/insights/rules/resthandler/top_queries/TopQueriesRestIT.class'
exclude 'org/opensearch/plugin/insights/core/exporter/QueryInsightsExporterIT.class'
}
}

Expand Down Expand Up @@ -333,6 +334,8 @@ task integTestWithSecurity(type: RestIntegTestTask) {
jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005'
}

// NOTE: this IT config discovers only junit5 (jupiter) tests.
// https://github.com/opensearch-project/sql/issues/1974
filter {
includeTestsMatching 'org.opensearch.plugin.insights.rules.resthandler.top_queries.TopQueriesRestIT'
}
Expand Down
3 changes: 1 addition & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionSha256Sum=f8b4f4772d302c8ff580bc40d0f56e715de69b163546944f787c87abf209c961
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.plugin.insights;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContextBuilder;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.opensearch.client.Request;
import org.opensearch.client.Response;
import org.opensearch.client.RestClient;
import org.opensearch.client.RestClientBuilder;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.unit.TimeValue;
import org.opensearch.common.util.concurrent.ThreadContext;
import org.opensearch.core.xcontent.DeprecationHandler;
import org.opensearch.core.xcontent.MediaType;
import org.opensearch.core.xcontent.NamedXContentRegistry;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.test.rest.OpenSearchRestTestCase;

public abstract class QueryInsightsRestTestCase extends OpenSearchRestTestCase {
protected static final String QUERY_INSIGHTS_INDICES_PREFIX = "top_queries";

protected boolean isHttps() {
return Optional.ofNullable(System.getProperty("https")).map("true"::equalsIgnoreCase).orElse(false);
}

@Override
protected String getProtocol() {
return isHttps() ? "https" : "http";
}

@Override
protected RestClient buildClient(Settings settings, HttpHost[] hosts) throws IOException {
RestClientBuilder builder = RestClient.builder(hosts);
if (isHttps()) {
configureHttpsClient(builder, settings);
} else {
configureClient(builder, settings);
}

builder.setStrictDeprecationMode(false);
return builder.build();
}

protected static void configureClient(RestClientBuilder builder, Settings settings) throws IOException {
String userName = System.getProperty("user");
String password = System.getProperty("password");
if (userName != null && password != null) {
builder.setHttpClientConfigCallback(httpClientBuilder -> {
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(null, -1), new UsernamePasswordCredentials(userName, password));
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
});
}
OpenSearchRestTestCase.configureClient(builder, settings);
}

protected static void configureHttpsClient(RestClientBuilder builder, Settings settings) throws IOException {
// Similar to client configuration with OpenSearch:
// https://github.com/opensearch-project/OpenSearch/blob/2.15.1/test/framework/src/main/java/org/opensearch/test/rest/OpenSearchRestTestCase.java#L841-L863
builder.setHttpClientConfigCallback(httpClientBuilder -> {
String userName = Optional.ofNullable(System.getProperty("user"))
.orElseThrow(() -> new RuntimeException("user name is missing"));
String password = Optional.ofNullable(System.getProperty("password"))
.orElseThrow(() -> new RuntimeException("password is missing"));
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final AuthScope anyScope = new AuthScope(null, -1);
credentialsProvider.setCredentials(anyScope, new UsernamePasswordCredentials(userName, password));
try {
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
// disable the certificate since our testing cluster just uses the default security configuration
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.setSSLContext(SSLContextBuilder.create().loadTrustMaterial(null, (chains, authType) -> true).build());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
Map<String, String> headers = ThreadContext.buildDefaultHeaders(settings);
Header[] defaultHeaders = new Header[headers.size()];
int i = 0;
for (Map.Entry<String, String> entry : headers.entrySet()) {
defaultHeaders[i++] = new BasicHeader(entry.getKey(), entry.getValue());
}
builder.setDefaultHeaders(defaultHeaders);
final String socketTimeoutString = settings.get(CLIENT_SOCKET_TIMEOUT);
final TimeValue socketTimeout = TimeValue.parseTimeValue(
socketTimeoutString == null ? "60s" : socketTimeoutString,
CLIENT_SOCKET_TIMEOUT
);
builder.setRequestConfigCallback(conf -> conf.setSocketTimeout(Math.toIntExact(socketTimeout.getMillis())));
if (settings.hasValue(CLIENT_PATH_PREFIX)) {
builder.setPathPrefix(settings.get(CLIENT_PATH_PREFIX));
}
}

/**
* wipeAllIndices won't work since it cannot delete security index. Use
* wipeAllQueryInsightsIndices instead.
*/
@Override
protected boolean preserveIndicesUponCompletion() {
return true;
}

@Before
public void runBeforeEachTest() throws IOException {
// Create documents for search
Request request = new Request("POST", "/my-index-0/_doc");
request.setJsonEntity(createDocumentsBody());
Response response = client().performRequest(request);

Assert.assertEquals(201, response.getStatusLine().getStatusCode());
}

@SuppressWarnings("unchecked")
@After
public void wipeAllQueryInsightsIndices() throws Exception {
Response response = adminClient().performRequest(new Request("GET", "/_cat/indices?format=json&expand_wildcards=all"));
MediaType xContentType = MediaType.fromMediaType(response.getEntity().getContentType().getValue());
try (
XContentParser parser = xContentType.xContent()
.createParser(
NamedXContentRegistry.EMPTY,
DeprecationHandler.THROW_UNSUPPORTED_OPERATION,
response.getEntity().getContent()
)
) {
XContentParser.Token token = parser.nextToken();
List<Map<String, Object>> parserList = null;
if (token == XContentParser.Token.START_ARRAY) {
parserList = parser.listOrderedMap().stream().map(obj -> (Map<String, Object>) obj).collect(Collectors.toList());
} else {
parserList = Collections.singletonList(parser.mapOrdered());
}

for (Map<String, Object> index : parserList) {
final String indexName = (String) index.get("index");
if (indexName.startsWith(QUERY_INSIGHTS_INDICES_PREFIX)) {
adminClient().performRequest(new Request("DELETE", "/" + indexName));
}
}
}
}

protected String defaultTopQueriesSettings() {
return "{\n"
+ " \"persistent\" : {\n"
+ " \"search.insights.top_queries.latency.enabled\" : \"true\",\n"
+ " \"search.insights.top_queries.latency.window_size\" : \"600s\",\n"
+ " \"search.insights.top_queries.latency.top_n_size\" : 5\n"
+ " }\n"
+ "}";
}

protected String createDocumentsBody() {
return "{\n"
+ " \"@timestamp\": \"2099-11-15T13:12:00\",\n"
+ " \"message\": \"this is document 1\",\n"
+ " \"user\": {\n"
+ " \"id\": \"cyji\"\n"
+ " }\n"
+ "}";
}

protected String searchBody() {
return "{}";
}

protected void doSearch(int times) throws IOException {
for (int i = 0; i < times; i++) {
// Do Search
Request request = new Request("GET", "/my-index-0/_search?size=20&pretty");
request.setJsonEntity(searchBody());
Response response = client().performRequest(request);
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.plugin.insights.core.exporter;

import java.io.IOException;
import org.junit.Assert;
import org.opensearch.client.Request;
import org.opensearch.client.Response;
import org.opensearch.client.ResponseException;
import org.opensearch.plugin.insights.QueryInsightsRestTestCase;

/** Rest Action tests for query */
public class QueryInsightsExporterIT extends QueryInsightsRestTestCase {
/**
* Test Top Queries setting endpoints
*
* @throws IOException IOException
*/
public void testQueryInsightsExporterSettings() throws IOException {
// test invalid settings
for (String setting : invalidExporterSettings()) {
Request request = new Request("PUT", "/_cluster/settings");
request.setJsonEntity(setting);
try {
client().performRequest(request);
fail("Should not succeed with invalid exporter settings");
} catch (ResponseException e) {
assertEquals(400, e.getResponse().getStatusLine().getStatusCode());
}
}

// Test enable Top N Queries feature
Request request = new Request("PUT", "/_cluster/settings");
request.setJsonEntity(defaultExporterSettings());
Response response = client().performRequest(request);
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
}

private String defaultExporterSettings() {
return "{\n"
+ " \"persistent\" : {\n"
+ " \"search.insights.top_queries.latency.exporter.config.index\" : \"YYYY.MM.dd\",\n"
+ " \"search.insights.top_queries.latency.exporter.type\" : \"local_index\"\n"
+ " }\n"
+ "}";
}

private String[] invalidExporterSettings() {
return new String[] {
"{\n"
+ " \"persistent\" : {\n"
+ " \"search.insights.top_queries.latency.exporter.type\" : invalid_type\n"
+ " }\n"
+ "}",
"{\n"
+ " \"persistent\" : {\n"
+ " \"search.insights.top_queries.latency.exporter.type\" : local_index,\n"
+ " \"search.insights.top_queries.latency.exporter.config.index\" : \"1a2b\"\n"
+ " }\n"
+ "}" };
}
}
Loading

0 comments on commit ae269c5

Please sign in to comment.