Skip to content

Commit e939371

Browse files
Add more integ tests for query insights (#71) (#87) (#88)
(cherry picked from commit 2c02a2d) (cherry picked from commit ae269c5) Signed-off-by: Chenyang Ji <cyji@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent f94be16 commit e939371

File tree

5 files changed

+331
-172
lines changed

5 files changed

+331
-172
lines changed

build.gradle

+3
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ integTest {
263263
if (System.getProperty("security.enabled") == "true" || System.getProperty("https") == "true") {
264264
// Exclude this IT, because they executed in another task (:integTestWithSecurity)
265265
exclude 'org/opensearch/plugin/insights/rules/resthandler/top_queries/TopQueriesRestIT.class'
266+
exclude 'org/opensearch/plugin/insights/core/exporter/QueryInsightsExporterIT.class'
266267
}
267268
}
268269

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

337+
// NOTE: this IT config discovers only junit5 (jupiter) tests.
338+
// https://github.com/opensearch-project/sql/issues/1974
336339
filter {
337340
includeTestsMatching 'org.opensearch.plugin.insights.rules.resthandler.top_queries.TopQueriesRestIT'
338341
}

gradle/wrapper/gradle-wrapper.properties

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
distributionBase=GRADLE_USER_HOME
1010
distributionPath=wrapper/dists
11-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
11+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
1212
zipStoreBase=GRADLE_USER_HOME
1313
zipStorePath=wrapper/dists
14-
distributionSha256Sum=f8b4f4772d302c8ff580bc40d0f56e715de69b163546944f787c87abf209c961
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.plugin.insights;
10+
11+
import java.io.IOException;
12+
import java.util.Collections;
13+
import java.util.List;
14+
import java.util.Map;
15+
import java.util.Optional;
16+
import java.util.stream.Collectors;
17+
import org.apache.http.Header;
18+
import org.apache.http.HttpHost;
19+
import org.apache.http.auth.AuthScope;
20+
import org.apache.http.auth.UsernamePasswordCredentials;
21+
import org.apache.http.conn.ssl.NoopHostnameVerifier;
22+
import org.apache.http.impl.client.BasicCredentialsProvider;
23+
import org.apache.http.message.BasicHeader;
24+
import org.apache.http.ssl.SSLContextBuilder;
25+
import org.junit.After;
26+
import org.junit.Assert;
27+
import org.junit.Before;
28+
import org.opensearch.client.Request;
29+
import org.opensearch.client.Response;
30+
import org.opensearch.client.RestClient;
31+
import org.opensearch.client.RestClientBuilder;
32+
import org.opensearch.common.settings.Settings;
33+
import org.opensearch.common.unit.TimeValue;
34+
import org.opensearch.common.util.concurrent.ThreadContext;
35+
import org.opensearch.core.xcontent.DeprecationHandler;
36+
import org.opensearch.core.xcontent.MediaType;
37+
import org.opensearch.core.xcontent.NamedXContentRegistry;
38+
import org.opensearch.core.xcontent.XContentParser;
39+
import org.opensearch.test.rest.OpenSearchRestTestCase;
40+
41+
public abstract class QueryInsightsRestTestCase extends OpenSearchRestTestCase {
42+
protected static final String QUERY_INSIGHTS_INDICES_PREFIX = "top_queries";
43+
44+
protected boolean isHttps() {
45+
return Optional.ofNullable(System.getProperty("https")).map("true"::equalsIgnoreCase).orElse(false);
46+
}
47+
48+
@Override
49+
protected String getProtocol() {
50+
return isHttps() ? "https" : "http";
51+
}
52+
53+
@Override
54+
protected RestClient buildClient(Settings settings, HttpHost[] hosts) throws IOException {
55+
RestClientBuilder builder = RestClient.builder(hosts);
56+
if (isHttps()) {
57+
configureHttpsClient(builder, settings);
58+
} else {
59+
configureClient(builder, settings);
60+
}
61+
62+
builder.setStrictDeprecationMode(false);
63+
return builder.build();
64+
}
65+
66+
protected static void configureClient(RestClientBuilder builder, Settings settings) throws IOException {
67+
String userName = System.getProperty("user");
68+
String password = System.getProperty("password");
69+
if (userName != null && password != null) {
70+
builder.setHttpClientConfigCallback(httpClientBuilder -> {
71+
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
72+
credentialsProvider.setCredentials(new AuthScope(null, -1), new UsernamePasswordCredentials(userName, password));
73+
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
74+
});
75+
}
76+
OpenSearchRestTestCase.configureClient(builder, settings);
77+
}
78+
79+
protected static void configureHttpsClient(RestClientBuilder builder, Settings settings) throws IOException {
80+
// Similar to client configuration with OpenSearch:
81+
// https://github.com/opensearch-project/OpenSearch/blob/2.15.1/test/framework/src/main/java/org/opensearch/test/rest/OpenSearchRestTestCase.java#L841-L863
82+
builder.setHttpClientConfigCallback(httpClientBuilder -> {
83+
String userName = Optional.ofNullable(System.getProperty("user"))
84+
.orElseThrow(() -> new RuntimeException("user name is missing"));
85+
String password = Optional.ofNullable(System.getProperty("password"))
86+
.orElseThrow(() -> new RuntimeException("password is missing"));
87+
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
88+
final AuthScope anyScope = new AuthScope(null, -1);
89+
credentialsProvider.setCredentials(anyScope, new UsernamePasswordCredentials(userName, password));
90+
try {
91+
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
92+
// disable the certificate since our testing cluster just uses the default security configuration
93+
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
94+
.setSSLContext(SSLContextBuilder.create().loadTrustMaterial(null, (chains, authType) -> true).build());
95+
} catch (Exception e) {
96+
throw new RuntimeException(e);
97+
}
98+
});
99+
Map<String, String> headers = ThreadContext.buildDefaultHeaders(settings);
100+
Header[] defaultHeaders = new Header[headers.size()];
101+
int i = 0;
102+
for (Map.Entry<String, String> entry : headers.entrySet()) {
103+
defaultHeaders[i++] = new BasicHeader(entry.getKey(), entry.getValue());
104+
}
105+
builder.setDefaultHeaders(defaultHeaders);
106+
final String socketTimeoutString = settings.get(CLIENT_SOCKET_TIMEOUT);
107+
final TimeValue socketTimeout = TimeValue.parseTimeValue(
108+
socketTimeoutString == null ? "60s" : socketTimeoutString,
109+
CLIENT_SOCKET_TIMEOUT
110+
);
111+
builder.setRequestConfigCallback(conf -> conf.setSocketTimeout(Math.toIntExact(socketTimeout.getMillis())));
112+
if (settings.hasValue(CLIENT_PATH_PREFIX)) {
113+
builder.setPathPrefix(settings.get(CLIENT_PATH_PREFIX));
114+
}
115+
}
116+
117+
/**
118+
* wipeAllIndices won't work since it cannot delete security index. Use
119+
* wipeAllQueryInsightsIndices instead.
120+
*/
121+
@Override
122+
protected boolean preserveIndicesUponCompletion() {
123+
return true;
124+
}
125+
126+
@Before
127+
public void runBeforeEachTest() throws IOException {
128+
// Create documents for search
129+
Request request = new Request("POST", "/my-index-0/_doc");
130+
request.setJsonEntity(createDocumentsBody());
131+
Response response = client().performRequest(request);
132+
133+
Assert.assertEquals(201, response.getStatusLine().getStatusCode());
134+
}
135+
136+
@SuppressWarnings("unchecked")
137+
@After
138+
public void wipeAllQueryInsightsIndices() throws Exception {
139+
Response response = adminClient().performRequest(new Request("GET", "/_cat/indices?format=json&expand_wildcards=all"));
140+
MediaType xContentType = MediaType.fromMediaType(response.getEntity().getContentType().getValue());
141+
try (
142+
XContentParser parser = xContentType.xContent()
143+
.createParser(
144+
NamedXContentRegistry.EMPTY,
145+
DeprecationHandler.THROW_UNSUPPORTED_OPERATION,
146+
response.getEntity().getContent()
147+
)
148+
) {
149+
XContentParser.Token token = parser.nextToken();
150+
List<Map<String, Object>> parserList = null;
151+
if (token == XContentParser.Token.START_ARRAY) {
152+
parserList = parser.listOrderedMap().stream().map(obj -> (Map<String, Object>) obj).collect(Collectors.toList());
153+
} else {
154+
parserList = Collections.singletonList(parser.mapOrdered());
155+
}
156+
157+
for (Map<String, Object> index : parserList) {
158+
final String indexName = (String) index.get("index");
159+
if (indexName.startsWith(QUERY_INSIGHTS_INDICES_PREFIX)) {
160+
adminClient().performRequest(new Request("DELETE", "/" + indexName));
161+
}
162+
}
163+
}
164+
}
165+
166+
protected String defaultTopQueriesSettings() {
167+
return "{\n"
168+
+ " \"persistent\" : {\n"
169+
+ " \"search.insights.top_queries.latency.enabled\" : \"true\",\n"
170+
+ " \"search.insights.top_queries.latency.window_size\" : \"600s\",\n"
171+
+ " \"search.insights.top_queries.latency.top_n_size\" : 5\n"
172+
+ " }\n"
173+
+ "}";
174+
}
175+
176+
protected String createDocumentsBody() {
177+
return "{\n"
178+
+ " \"@timestamp\": \"2099-11-15T13:12:00\",\n"
179+
+ " \"message\": \"this is document 1\",\n"
180+
+ " \"user\": {\n"
181+
+ " \"id\": \"cyji\"\n"
182+
+ " }\n"
183+
+ "}";
184+
}
185+
186+
protected String searchBody() {
187+
return "{}";
188+
}
189+
190+
protected void doSearch(int times) throws IOException {
191+
for (int i = 0; i < times; i++) {
192+
// Do Search
193+
Request request = new Request("GET", "/my-index-0/_search?size=20&pretty");
194+
request.setJsonEntity(searchBody());
195+
Response response = client().performRequest(request);
196+
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
197+
}
198+
}
199+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.plugin.insights.core.exporter;
10+
11+
import java.io.IOException;
12+
import org.junit.Assert;
13+
import org.opensearch.client.Request;
14+
import org.opensearch.client.Response;
15+
import org.opensearch.client.ResponseException;
16+
import org.opensearch.plugin.insights.QueryInsightsRestTestCase;
17+
18+
/** Rest Action tests for query */
19+
public class QueryInsightsExporterIT extends QueryInsightsRestTestCase {
20+
/**
21+
* Test Top Queries setting endpoints
22+
*
23+
* @throws IOException IOException
24+
*/
25+
public void testQueryInsightsExporterSettings() throws IOException {
26+
// test invalid settings
27+
for (String setting : invalidExporterSettings()) {
28+
Request request = new Request("PUT", "/_cluster/settings");
29+
request.setJsonEntity(setting);
30+
try {
31+
client().performRequest(request);
32+
fail("Should not succeed with invalid exporter settings");
33+
} catch (ResponseException e) {
34+
assertEquals(400, e.getResponse().getStatusLine().getStatusCode());
35+
}
36+
}
37+
38+
// Test enable Top N Queries feature
39+
Request request = new Request("PUT", "/_cluster/settings");
40+
request.setJsonEntity(defaultExporterSettings());
41+
Response response = client().performRequest(request);
42+
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
43+
}
44+
45+
private String defaultExporterSettings() {
46+
return "{\n"
47+
+ " \"persistent\" : {\n"
48+
+ " \"search.insights.top_queries.latency.exporter.config.index\" : \"YYYY.MM.dd\",\n"
49+
+ " \"search.insights.top_queries.latency.exporter.type\" : \"local_index\"\n"
50+
+ " }\n"
51+
+ "}";
52+
}
53+
54+
private String[] invalidExporterSettings() {
55+
return new String[] {
56+
"{\n"
57+
+ " \"persistent\" : {\n"
58+
+ " \"search.insights.top_queries.latency.exporter.type\" : invalid_type\n"
59+
+ " }\n"
60+
+ "}",
61+
"{\n"
62+
+ " \"persistent\" : {\n"
63+
+ " \"search.insights.top_queries.latency.exporter.type\" : local_index,\n"
64+
+ " \"search.insights.top_queries.latency.exporter.config.index\" : \"1a2b\"\n"
65+
+ " }\n"
66+
+ "}" };
67+
}
68+
}

0 commit comments

Comments
 (0)