diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 5e3ebc9a15..57e770f286 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -34,9 +34,12 @@ This package uses the [Gradle](https://docs.gradle.org/current/userguide/usergui 1. `./gradlew build` builds and tests 2. `./gradlew :run` launches a single node cluster with ml-commons plugin installed -3. `./gradlew :integTest` launches a single node cluster with ml-commons plugin installed and runs all integration tests except security -4. ` ./gradlew :integTest --tests="**.test execute foo"` runs a single integration test class or method -5. `./gradlew spotlessApply` formats code. And/or import formatting rules in `.eclipseformat.xml` with IDE. +3. `./gradlew :integTest` launches a single node cluster with ml-commons plugin installed and runs all integration tests except security. Use `./gradlew integTest -PnumNodes=` to launch multi-node cluster. +4. ` ./gradlew :integTest --tests="."` runs a single integration test class or method, for example `./gradlew integTest --tests="org.opensearch.ml.rest.RestMLTrainAndPredictIT.testTrainAndPredictKmeansWithEmptyParam"` or `./gradlew integTest --tests="org.opensearch.ml.rest.RestMLTrainAndPredictIT"` +5. `./gradlew integTest -Dtests.class=""` run specific integ test class, for example `./gradlew integTest -Dtests.class="org.opensearch.ml.rest.RestMLTrainAndPredictIT"` +6. `./gradlew integTest -Dtests.method=""` run specific integ test method, for example `./gradlew integTest -Dtests.method="testTrainAndPredictKmeans"` +7. `./gradlew integTest -Dtests.rest.cluster=localhost:9200 -Dtests.cluster=localhost:9200 -Dtests.clustername="docker-cluster" -Dhttps=true -Duser=admin -Dpassword=admin` launches integration tests against a local cluster and run tests with security. Detail steps: (1)download OpenSearch tarball to local and install by running `opensearch-tar-install.sh`; (2)build ML plugin zip with your change and install ML plugin zip; (3)restart local test cluster; (4) run this gradle command to test. +8. `./gradlew spotlessApply` formats code. And/or import formatting rules in `.eclipseformat.xml` with IDE. When launching a cluster using one of the above commands logs are placed in `/build/cluster/run node0/opensearch-/logs`. Though the logs are teed to the console, in practices it's best to check the actual log file. diff --git a/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/rcf/FixedInTimeRandomCutForestTest.java b/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/rcf/FixedInTimeRandomCutForestTest.java index c68dbcef2d..59069b0516 100644 --- a/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/rcf/FixedInTimeRandomCutForestTest.java +++ b/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/rcf/FixedInTimeRandomCutForestTest.java @@ -50,7 +50,6 @@ public void predict() { int anomalyCount = 0; for (int i = 0 ;i 0.01) { anomalyCount++; } diff --git a/plugin/build.gradle b/plugin/build.gradle index 1edca558b5..bba970f4ab 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -3,7 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +import java.util.concurrent.Callable import org.opensearch.gradle.test.RestIntegTestTask +import org.opensearch.gradle.testclusters.StandaloneRestIntegTestTask plugins { id 'java' @@ -43,24 +45,12 @@ dependencies { compile "org.opensearch:common-utils:${common_utils_version}" compile("com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}") compile("com.fasterxml.jackson.core:jackson-databind:${versions.jackson}") - compile group: 'com.google.guava', name: 'guava', version:'29.0-jre' + implementation group: 'com.google.guava', name: 'guava', version: '31.0.1-jre' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.9.0' checkstyle "com.puppycrawl.tools:checkstyle:${project.checkstyle.toolVersion}" } -test { - include '**/*Test.class' - systemProperty 'tests.security.manager', 'false' - finalizedBy jacocoTestReport -} - -jacocoTestReport { - reports { - xml.enabled true - html.enabled true - csv.enabled true - } -} compileJava { options.compilerArgs.addAll(["-processor", 'lombok.launch.AnnotationProcessorHider$AnnotationProcessor']) @@ -81,14 +71,16 @@ loggerUsageCheck.enabled = false def _numNodes = findProperty('numNodes') as Integer ?: 1 -def opensearch_tmp_dir = rootProject.file('build/private/opensearch_tmp').absoluteFile -opensearch_tmp_dir.mkdirs() test { include '**/*Tests.class' systemProperty 'tests.security.manager', 'false' } +def opensearch_tmp_dir = rootProject.file('build/private/opensearch_tmp').absoluteFile +opensearch_tmp_dir.mkdirs() + + task integTest(type: RestIntegTestTask) { description = "Run tests against a cluster" testClassesDirs = sourceSets.test.output.classesDirs @@ -105,6 +97,13 @@ integTest { systemProperty "user", System.getProperty("user") systemProperty "password", System.getProperty("password") + // Only rest case can run with remote cluster + if (System.getProperty("tests.rest.cluster") != null) { + filter { + includeTestsMatching "org.opensearch.ml.rest.*IT" + } + } + // The 'doFirst' delays till execution time. doFirst { // Tell the test JVM if the cluster JVM is running under a debugger so that tests can @@ -150,6 +149,24 @@ testClusters.integTest { } } +task integTestRemote(type: RestIntegTestTask) { + testClassesDirs = sourceSets.test.output.classesDirs + classpath = sourceSets.test.runtimeClasspath + systemProperty 'tests.security.manager', 'false' + systemProperty 'java.io.tmpdir', opensearch_tmp_dir.absolutePath + + systemProperty "https", System.getProperty("https") + systemProperty "user", System.getProperty("user") + systemProperty "password", System.getProperty("password") + + // Only rest case can run with remote cluster + if (System.getProperty("tests.rest.cluster") != null) { + filter { + includeTestsMatching "org.opensearch.ml.rest.*IT" + } + } +} + run { doFirst { // There seems to be an issue when running multi node run or integ tasks with unicast_hosts @@ -172,6 +189,7 @@ task release(type: Copy, group: 'build') { jacocoTestReport { reports { xml.enabled true + html.enabled true csv.enabled false } diff --git a/plugin/src/test/java/org/opensearch/ml/rest/MLCommonsRestTestCase.java b/plugin/src/test/java/org/opensearch/ml/rest/MLCommonsRestTestCase.java new file mode 100644 index 0000000000..8789dcefe0 --- /dev/null +++ b/plugin/src/test/java/org/opensearch/ml/rest/MLCommonsRestTestCase.java @@ -0,0 +1,236 @@ +package org.opensearch.ml.rest; + +import static org.opensearch.commons.ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_ENABLED; +import static org.opensearch.commons.ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_FILEPATH; +import static org.opensearch.commons.ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_KEYPASSWORD; +import static org.opensearch.commons.ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_PASSWORD; +import static org.opensearch.commons.ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_PEMCERT_FILEPATH; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.apache.http.Header; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +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.apache.http.util.EntityUtils; +import org.junit.After; +import org.opensearch.client.Request; +import org.opensearch.client.Response; +import org.opensearch.client.RestClient; +import org.opensearch.client.RestClientBuilder; +import org.opensearch.common.io.PathUtils; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.common.xcontent.DeprecationHandler; +import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.commons.rest.SecureRestClientBuilder; +import org.opensearch.ml.utils.TestData; +import org.opensearch.ml.utils.TestHelper; +import org.opensearch.rest.RestStatus; +import org.opensearch.test.rest.OpenSearchRestTestCase; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +public abstract class MLCommonsRestTestCase extends OpenSearchRestTestCase { + + protected boolean isHttps() { + boolean isHttps = Optional.ofNullable(System.getProperty("https")).map("true"::equalsIgnoreCase).orElse(false); + if (isHttps) { + // currently only external cluster is supported for security enabled testing + if (!Optional.ofNullable(System.getProperty("tests.rest.cluster")).isPresent()) { + throw new RuntimeException("cluster url should be provided for security enabled testing"); + } + } + + return isHttps; + } + + @Override + protected String getProtocol() { + return isHttps() ? "https" : "http"; + } + + @Override + protected Settings restAdminSettings() { + return Settings + .builder() + // disable the warning exception for admin client since it's only used for cleanup. + .put("strictDeprecationMode", false) + .put("http.port", 9200) + .put(OPENSEARCH_SECURITY_SSL_HTTP_ENABLED, isHttps()) + .put(OPENSEARCH_SECURITY_SSL_HTTP_PEMCERT_FILEPATH, "sample.pem") + .put(OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_FILEPATH, "test-kirk.jks") + .put(OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_PASSWORD, "changeit") + .put(OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_KEYPASSWORD, "changeit") + .build(); + } + + // Utility fn for deleting indices. Should only be used when not allowed in a regular context + // (e.g., deleting system indices) + protected static void deleteIndexWithAdminClient(String name) throws IOException { + Request request = new Request("DELETE", "/" + name); + adminClient().performRequest(request); + } + + // Utility fn for checking if an index exists. Should only be used when not allowed in a regular context + // (e.g., checking existence of system indices) + protected static boolean indexExistsWithAdminClient(String indexName) throws IOException { + Request request = new Request("HEAD", "/" + indexName); + Response response = adminClient().performRequest(request); + return RestStatus.OK.getStatus() == response.getStatusLine().getStatusCode(); + } + + @Override + protected RestClient buildClient(Settings settings, HttpHost[] hosts) throws IOException { + boolean strictDeprecationMode = settings.getAsBoolean("strictDeprecationMode", true); + RestClientBuilder builder = RestClient.builder(hosts); + if (isHttps()) { + String keystore = settings.get(OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_FILEPATH); + if (Objects.nonNull(keystore)) { + URI uri = null; + try { + uri = this.getClass().getClassLoader().getResource("security/sample.pem").toURI(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + Path configPath = PathUtils.get(uri).getParent().toAbsolutePath(); + return new SecureRestClientBuilder(settings, configPath).build(); + } else { + configureHttpsClient(builder, settings); + builder.setStrictDeprecationMode(strictDeprecationMode); + return builder.build(); + } + + } else { + configureClient(builder, settings); + builder.setStrictDeprecationMode(strictDeprecationMode); + return builder.build(); + } + + } + + @SuppressWarnings("unchecked") + @After + protected void wipeAllODFEIndices() throws IOException { + Response response = adminClient().performRequest(new Request("GET", "/_cat/indices?format=json&expand_wildcards=all")); + XContentType xContentType = XContentType.fromMediaTypeOrFormat(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> parserList = null; + if (token == XContentParser.Token.START_ARRAY) { + parserList = parser.listOrderedMap().stream().map(obj -> (Map) obj).collect(Collectors.toList()); + } else { + parserList = Collections.singletonList(parser.mapOrdered()); + } + + for (Map index : parserList) { + String indexName = (String) index.get("index"); + if (indexName != null && !".opendistro_security".equals(indexName)) { + adminClient().performRequest(new Request("DELETE", "/" + indexName)); + } + } + } + } + + protected static void configureHttpsClient(RestClientBuilder builder, Settings settings) throws IOException { + Map headers = ThreadContext.buildDefaultHeaders(settings); + Header[] defaultHeaders = new Header[headers.size()]; + int i = 0; + for (Map.Entry entry : headers.entrySet()) { + defaultHeaders[i++] = new BasicHeader(entry.getKey(), entry.getValue()); + } + builder.setDefaultHeaders(defaultHeaders); + 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")); + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, 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); + } + }); + + 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 wipeAllODFEIndices instead. + */ + @Override + protected boolean preserveIndicesUponCompletion() { + return true; + } + + protected Response ingestIrisData(String indexName) throws IOException { + String irisDataIndexMapping = ""; + TestHelper + .makeRequest( + client(), + "PUT", + indexName, + null, + TestHelper.toHttpEntity(irisDataIndexMapping), + ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, "Kibana")) + ); + + Response statsResponse = TestHelper.makeRequest(client(), "GET", indexName, ImmutableMap.of(), "", null); + assertEquals(RestStatus.OK, TestHelper.restStatus(statsResponse)); + String result = EntityUtils.toString(statsResponse.getEntity()); + assertTrue(result.contains(indexName)); + + Response bulkResponse = TestHelper + .makeRequest( + client(), + "POST", + "_bulk?refresh=true", + null, + TestHelper.toHttpEntity(TestData.IRIS_DATA), + ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, "")) + ); + assertEquals(RestStatus.OK, TestHelper.restStatus(statsResponse)); + return bulkResponse; + } +} diff --git a/plugin/src/test/java/org/opensearch/ml/rest/RestMLTrainAndPredictIT.java b/plugin/src/test/java/org/opensearch/ml/rest/RestMLTrainAndPredictIT.java new file mode 100644 index 0000000000..c22f277a6d --- /dev/null +++ b/plugin/src/test/java/org/opensearch/ml/rest/RestMLTrainAndPredictIT.java @@ -0,0 +1,98 @@ +package org.opensearch.ml.rest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import lombok.NonNull; + +import org.apache.http.HttpEntity; +import org.opensearch.client.Response; +import org.opensearch.index.query.MatchAllQueryBuilder; +import org.opensearch.ml.common.dataset.MLInputDataset; +import org.opensearch.ml.common.dataset.SearchQueryInputDataset; +import org.opensearch.ml.common.parameter.FunctionName; +import org.opensearch.ml.common.parameter.KMeansParams; +import org.opensearch.ml.common.parameter.MLInput; +import org.opensearch.ml.utils.TestHelper; +import org.opensearch.search.builder.SearchSourceBuilder; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; + +public class RestMLTrainAndPredictIT extends MLCommonsRestTestCase { + private String irisIndex = "iris_data"; + private Gson gson = new Gson(); + + public void testTrainAndPredictKmeans() throws IOException { + ingestIrisData(irisIndex); + KMeansParams params = KMeansParams.builder().centroids(3).build(); + @NonNull + SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); + sourceBuilder.query(new MatchAllQueryBuilder()); + sourceBuilder.size(1000); + sourceBuilder.fetchSource(new String[] { "petal_length_in_cm", "petal_width_in_cm" }, null); + MLInputDataset inputData = SearchQueryInputDataset + .builder() + .indices(ImmutableList.of(irisIndex)) + .searchSourceBuilder(sourceBuilder) + .build(); + trainAndPredictKmeansWithIrisData(params, inputData, clusterCount -> { + if (clusterCount.size() == 3) { + for (Map.Entry entry : clusterCount.entrySet()) { + assertEquals(50, entry.getValue(), 5); + } + } + }); + } + + public void testTrainAndPredictKmeansWithEmptyParam() throws IOException { + ingestIrisData(irisIndex); + KMeansParams params = KMeansParams.builder().build(); + @NonNull + SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); + sourceBuilder.query(new MatchAllQueryBuilder()); + sourceBuilder.size(1000); + sourceBuilder.fetchSource(new String[] { "petal_length_in_cm", "petal_width_in_cm" }, null); + MLInputDataset inputData = SearchQueryInputDataset + .builder() + .indices(ImmutableList.of(irisIndex)) + .searchSourceBuilder(sourceBuilder) + .build(); + trainAndPredictKmeansWithIrisData(params, inputData, clusterCount -> { assertEquals(2, clusterCount.size()); }); + } + + private void trainAndPredictKmeansWithIrisData(KMeansParams params, MLInputDataset inputData, Consumer> function) + throws IOException { + MLInput kmeansInput = MLInput.builder().algorithm(FunctionName.KMEANS).parameters(params).inputDataset(inputData).build(); + Response kmeansResponse = TestHelper + .makeRequest( + client(), + "POST", + "/_plugins/_ml/_train_predict/kmeans", + ImmutableMap.of(), + TestHelper.toHttpEntity(kmeansInput), + null + ); + HttpEntity entity = kmeansResponse.getEntity(); + assertNotNull(kmeansResponse); + String entityString = TestHelper.httpEntityToString(entity); + Map map = gson.fromJson(entityString, Map.class); + Map predictionResult = (Map) map.get("prediction_result"); + ArrayList rows = (ArrayList) predictionResult.get("rows"); + Map clusterCount = new HashMap<>(); + for (Object obj : rows) { + Double value = (Double) ((Map) ((ArrayList) ((Map) obj).get("values")).get(0)).get("value"); + if (!clusterCount.containsKey(value)) { + clusterCount.put(value, 1); + } else { + Integer count = clusterCount.get(value); + clusterCount.put(value, ++count); + } + } + function.accept(clusterCount); + } +} diff --git a/plugin/src/test/java/org/opensearch/ml/utils/TestData.java b/plugin/src/test/java/org/opensearch/ml/utils/TestData.java new file mode 100644 index 0000000000..e3b11aa6f4 --- /dev/null +++ b/plugin/src/test/java/org/opensearch/ml/utils/TestData.java @@ -0,0 +1,305 @@ +package org.opensearch.ml.utils; + +public class TestData { + + public static final String IRIS_DATA = "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.1,\"sepal_width_in_cm\":3.5,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.9,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.7,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":1.3,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.6,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.0,\"sepal_width_in_cm\":3.6,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.4,\"sepal_width_in_cm\":3.9,\"petal_length_in_cm\":1.7,\"petal_width_in_cm\":0.4,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.6,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.3,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.0,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.4,\"sepal_width_in_cm\":2.9,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.9,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.1,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.4,\"sepal_width_in_cm\":3.7,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.8,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":1.6,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.8,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.1,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.3,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":1.1,\"petal_width_in_cm\":0.1,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.8,\"sepal_width_in_cm\":4.0,\"petal_length_in_cm\":1.2,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.7,\"sepal_width_in_cm\":4.4,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.4,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.4,\"sepal_width_in_cm\":3.9,\"petal_length_in_cm\":1.3,\"petal_width_in_cm\":0.4,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.1,\"sepal_width_in_cm\":3.5,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.3,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.7,\"sepal_width_in_cm\":3.8,\"petal_length_in_cm\":1.7,\"petal_width_in_cm\":0.3,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.1,\"sepal_width_in_cm\":3.8,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.3,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.4,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":1.7,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.1,\"sepal_width_in_cm\":3.7,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.4,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.6,\"sepal_width_in_cm\":3.6,\"petal_length_in_cm\":1.0,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.1,\"sepal_width_in_cm\":3.3,\"petal_length_in_cm\":1.7,\"petal_width_in_cm\":0.5,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.8,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":1.9,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.0,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":1.6,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.0,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":1.6,\"petal_width_in_cm\":0.4,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.2,\"sepal_width_in_cm\":3.5,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.2,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.7,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":1.6,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.8,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":1.6,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.4,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.4,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.2,\"sepal_width_in_cm\":4.1,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.1,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.5,\"sepal_width_in_cm\":4.2,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.9,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.1,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.0,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":1.2,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.5,\"sepal_width_in_cm\":3.5,\"petal_length_in_cm\":1.3,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.9,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.1,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.4,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":1.3,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.1,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.0,\"sepal_width_in_cm\":3.5,\"petal_length_in_cm\":1.3,\"petal_width_in_cm\":0.3,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.5,\"sepal_width_in_cm\":2.3,\"petal_length_in_cm\":1.3,\"petal_width_in_cm\":0.3,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.4,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":1.3,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.0,\"sepal_width_in_cm\":3.5,\"petal_length_in_cm\":1.6,\"petal_width_in_cm\":0.6,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.1,\"sepal_width_in_cm\":3.8,\"petal_length_in_cm\":1.9,\"petal_width_in_cm\":0.4,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.8,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.3,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.1,\"sepal_width_in_cm\":3.8,\"petal_length_in_cm\":1.6,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.6,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.3,\"sepal_width_in_cm\":3.7,\"petal_length_in_cm\":1.5,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.0,\"sepal_width_in_cm\":3.3,\"petal_length_in_cm\":1.4,\"petal_width_in_cm\":0.2,\"class\":\"Iris-setosa\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.0,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":4.7,\"petal_width_in_cm\":1.4,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.4,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":4.5,\"petal_width_in_cm\":1.5,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.9,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":4.9,\"petal_width_in_cm\":1.5,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.5,\"sepal_width_in_cm\":2.3,\"petal_length_in_cm\":4.0,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.5,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":4.6,\"petal_width_in_cm\":1.5,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.7,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":4.5,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.3,\"sepal_width_in_cm\":3.3,\"petal_length_in_cm\":4.7,\"petal_width_in_cm\":1.6,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.9,\"sepal_width_in_cm\":2.4,\"petal_length_in_cm\":3.3,\"petal_width_in_cm\":1.0,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.6,\"sepal_width_in_cm\":2.9,\"petal_length_in_cm\":4.6,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.2,\"sepal_width_in_cm\":2.7,\"petal_length_in_cm\":3.9,\"petal_width_in_cm\":1.4,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.0,\"sepal_width_in_cm\":2.0,\"petal_length_in_cm\":3.5,\"petal_width_in_cm\":1.0,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.9,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":4.2,\"petal_width_in_cm\":1.5,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.0,\"sepal_width_in_cm\":2.2,\"petal_length_in_cm\":4.0,\"petal_width_in_cm\":1.0,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.1,\"sepal_width_in_cm\":2.9,\"petal_length_in_cm\":4.7,\"petal_width_in_cm\":1.4,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.6,\"sepal_width_in_cm\":2.9,\"petal_length_in_cm\":3.6,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.7,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":4.4,\"petal_width_in_cm\":1.4,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.6,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":4.5,\"petal_width_in_cm\":1.5,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.8,\"sepal_width_in_cm\":2.7,\"petal_length_in_cm\":4.1,\"petal_width_in_cm\":1.0,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.2,\"sepal_width_in_cm\":2.2,\"petal_length_in_cm\":4.5,\"petal_width_in_cm\":1.5,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.6,\"sepal_width_in_cm\":2.5,\"petal_length_in_cm\":3.9,\"petal_width_in_cm\":1.1,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.9,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":4.8,\"petal_width_in_cm\":1.8,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.1,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":4.0,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.3,\"sepal_width_in_cm\":2.5,\"petal_length_in_cm\":4.9,\"petal_width_in_cm\":1.5,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.1,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":4.7,\"petal_width_in_cm\":1.2,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.4,\"sepal_width_in_cm\":2.9,\"petal_length_in_cm\":4.3,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.6,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":4.4,\"petal_width_in_cm\":1.4,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.8,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":4.8,\"petal_width_in_cm\":1.4,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.7,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":5.0,\"petal_width_in_cm\":1.7,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.0,\"sepal_width_in_cm\":2.9,\"petal_length_in_cm\":4.5,\"petal_width_in_cm\":1.5,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.7,\"sepal_width_in_cm\":2.6,\"petal_length_in_cm\":3.5,\"petal_width_in_cm\":1.0,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.5,\"sepal_width_in_cm\":2.4,\"petal_length_in_cm\":3.8,\"petal_width_in_cm\":1.1,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.5,\"sepal_width_in_cm\":2.4,\"petal_length_in_cm\":3.7,\"petal_width_in_cm\":1.0,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.8,\"sepal_width_in_cm\":2.7,\"petal_length_in_cm\":3.9,\"petal_width_in_cm\":1.2,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.0,\"sepal_width_in_cm\":2.7,\"petal_length_in_cm\":5.1,\"petal_width_in_cm\":1.6,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.4,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":4.5,\"petal_width_in_cm\":1.5,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.0,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":4.5,\"petal_width_in_cm\":1.6,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.7,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":4.7,\"petal_width_in_cm\":1.5,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.3,\"sepal_width_in_cm\":2.3,\"petal_length_in_cm\":4.4,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.6,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":4.1,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.5,\"sepal_width_in_cm\":2.5,\"petal_length_in_cm\":4.0,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.5,\"sepal_width_in_cm\":2.6,\"petal_length_in_cm\":4.4,\"petal_width_in_cm\":1.2,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.1,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":4.6,\"petal_width_in_cm\":1.4,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.8,\"sepal_width_in_cm\":2.6,\"petal_length_in_cm\":4.0,\"petal_width_in_cm\":1.2,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.0,\"sepal_width_in_cm\":2.3,\"petal_length_in_cm\":3.3,\"petal_width_in_cm\":1.0,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.6,\"sepal_width_in_cm\":2.7,\"petal_length_in_cm\":4.2,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.7,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":4.2,\"petal_width_in_cm\":1.2,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.7,\"sepal_width_in_cm\":2.9,\"petal_length_in_cm\":4.2,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.2,\"sepal_width_in_cm\":2.9,\"petal_length_in_cm\":4.3,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.1,\"sepal_width_in_cm\":2.5,\"petal_length_in_cm\":3.0,\"petal_width_in_cm\":1.1,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.7,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":4.1,\"petal_width_in_cm\":1.3,\"class\":\"Iris-versicolor\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.3,\"sepal_width_in_cm\":3.3,\"petal_length_in_cm\":6.0,\"petal_width_in_cm\":2.5,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.8,\"sepal_width_in_cm\":2.7,\"petal_length_in_cm\":5.1,\"petal_width_in_cm\":1.9,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.1,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":5.9,\"petal_width_in_cm\":2.1,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.3,\"sepal_width_in_cm\":2.9,\"petal_length_in_cm\":5.6,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.5,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":5.8,\"petal_width_in_cm\":2.2,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.6,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":6.6,\"petal_width_in_cm\":2.1,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":4.9,\"sepal_width_in_cm\":2.5,\"petal_length_in_cm\":4.5,\"petal_width_in_cm\":1.7,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.3,\"sepal_width_in_cm\":2.9,\"petal_length_in_cm\":6.3,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.7,\"sepal_width_in_cm\":2.5,\"petal_length_in_cm\":5.8,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.2,\"sepal_width_in_cm\":3.6,\"petal_length_in_cm\":6.1,\"petal_width_in_cm\":2.5,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.5,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":5.1,\"petal_width_in_cm\":2.0,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.4,\"sepal_width_in_cm\":2.7,\"petal_length_in_cm\":5.3,\"petal_width_in_cm\":1.9,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.8,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":5.5,\"petal_width_in_cm\":2.1,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.7,\"sepal_width_in_cm\":2.5,\"petal_length_in_cm\":5.0,\"petal_width_in_cm\":2.0,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.8,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":5.1,\"petal_width_in_cm\":2.4,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.4,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":5.3,\"petal_width_in_cm\":2.3,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.5,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":5.5,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.7,\"sepal_width_in_cm\":3.8,\"petal_length_in_cm\":6.7,\"petal_width_in_cm\":2.2,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.7,\"sepal_width_in_cm\":2.6,\"petal_length_in_cm\":6.9,\"petal_width_in_cm\":2.3,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.0,\"sepal_width_in_cm\":2.2,\"petal_length_in_cm\":5.0,\"petal_width_in_cm\":1.5,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.9,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":5.7,\"petal_width_in_cm\":2.3,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.6,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":4.9,\"petal_width_in_cm\":2.0,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.7,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":6.7,\"petal_width_in_cm\":2.0,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.3,\"sepal_width_in_cm\":2.7,\"petal_length_in_cm\":4.9,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.7,\"sepal_width_in_cm\":3.3,\"petal_length_in_cm\":5.7,\"petal_width_in_cm\":2.1,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.2,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":6.0,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.2,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":4.8,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.1,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":4.9,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.4,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":5.6,\"petal_width_in_cm\":2.1,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.2,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":5.8,\"petal_width_in_cm\":1.6,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.4,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":6.1,\"petal_width_in_cm\":1.9,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.9,\"sepal_width_in_cm\":3.8,\"petal_length_in_cm\":6.4,\"petal_width_in_cm\":2.0,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.4,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":5.6,\"petal_width_in_cm\":2.2,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.3,\"sepal_width_in_cm\":2.8,\"petal_length_in_cm\":5.1,\"petal_width_in_cm\":1.5,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.1,\"sepal_width_in_cm\":2.6,\"petal_length_in_cm\":5.6,\"petal_width_in_cm\":1.4,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":7.7,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":6.1,\"petal_width_in_cm\":2.3,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.3,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":5.6,\"petal_width_in_cm\":2.4,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.4,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":5.5,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.0,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":4.8,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.9,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":5.4,\"petal_width_in_cm\":2.1,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.7,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":5.6,\"petal_width_in_cm\":2.4,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.9,\"sepal_width_in_cm\":3.1,\"petal_length_in_cm\":5.1,\"petal_width_in_cm\":2.3,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.8,\"sepal_width_in_cm\":2.7,\"petal_length_in_cm\":5.1,\"petal_width_in_cm\":1.9,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.8,\"sepal_width_in_cm\":3.2,\"petal_length_in_cm\":5.9,\"petal_width_in_cm\":2.3,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.7,\"sepal_width_in_cm\":3.3,\"petal_length_in_cm\":5.7,\"petal_width_in_cm\":2.5,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.7,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":5.2,\"petal_width_in_cm\":2.3,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.3,\"sepal_width_in_cm\":2.5,\"petal_length_in_cm\":5.0,\"petal_width_in_cm\":1.9,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.5,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":5.2,\"petal_width_in_cm\":2.0,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":6.2,\"sepal_width_in_cm\":3.4,\"petal_length_in_cm\":5.4,\"petal_width_in_cm\":2.3,\"class\":\"Iris-virginica\"}\n" + + "{ \"index\" : { \"_index\" : \"iris_data\" } }\n" + + "{\"sepal_length_in_cm\":5.9,\"sepal_width_in_cm\":3.0,\"petal_length_in_cm\":5.1,\"petal_width_in_cm\":1.8,\"class\":\"Iris-virginica\"}\n"; +} diff --git a/plugin/src/test/java/org/opensearch/ml/utils/TestHelper.java b/plugin/src/test/java/org/opensearch/ml/utils/TestHelper.java index 718410a0d6..511f803966 100644 --- a/plugin/src/test/java/org/opensearch/ml/utils/TestHelper.java +++ b/plugin/src/test/java/org/opensearch/ml/utils/TestHelper.java @@ -5,9 +5,27 @@ package org.opensearch.ml.utils; +import static org.apache.http.entity.ContentType.APPLICATION_JSON; + +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.nio.entity.NStringEntity; +import org.apache.logging.log4j.util.Strings; +import org.opensearch.client.Request; +import org.opensearch.client.RequestOptions; +import org.opensearch.client.Response; +import org.opensearch.client.RestClient; +import org.opensearch.client.WarningsHandler; import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.LoggingDeprecationHandler; @@ -18,6 +36,7 @@ import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.rest.RestStatus; import org.opensearch.search.SearchModule; public class TestHelper { @@ -46,4 +65,77 @@ public static String toJsonString(ToXContentObject object) throws IOException { public static String xContentBuilderToString(XContentBuilder builder) { return BytesReference.bytes(builder).utf8ToString(); } + + public static Response makeRequest( + RestClient client, + String method, + String endpoint, + Map params, + String jsonEntity, + List
headers + ) throws IOException { + HttpEntity httpEntity = Strings.isBlank(jsonEntity) ? null : new NStringEntity(jsonEntity, ContentType.APPLICATION_JSON); + return makeRequest(client, method, endpoint, params, httpEntity, headers); + } + + public static Response makeRequest( + RestClient client, + String method, + String endpoint, + Map params, + HttpEntity entity, + List
headers + ) throws IOException { + return makeRequest(client, method, endpoint, params, entity, headers, false); + } + + public static Response makeRequest( + RestClient client, + String method, + String endpoint, + Map params, + HttpEntity entity, + List
headers, + boolean strictDeprecationMode + ) throws IOException { + Request request = new Request(method, endpoint); + + RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder(); + if (headers != null) { + headers.forEach(header -> options.addHeader(header.getName(), header.getValue())); + } + options.setWarningsHandler(strictDeprecationMode ? WarningsHandler.STRICT : WarningsHandler.PERMISSIVE); + request.setOptions(options.build()); + + if (params != null) { + params.entrySet().forEach(it -> request.addParameter(it.getKey(), it.getValue())); + } + if (entity != null) { + request.setEntity(entity); + } + return client.performRequest(request); + } + + public static HttpEntity toHttpEntity(ToXContentObject object) throws IOException { + return new StringEntity(toJsonString(object), APPLICATION_JSON); + } + + public static HttpEntity toHttpEntity(String jsonString) throws IOException { + return new StringEntity(jsonString, APPLICATION_JSON); + } + + public static RestStatus restStatus(Response response) { + return RestStatus.fromCode(response.getStatusLine().getStatusCode()); + } + + public static String httpEntityToString(HttpEntity entity) throws IOException { + InputStream inputStream = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1")); + StringBuilder sb = new StringBuilder(); + String line = null; + while ((line = reader.readLine()) != null) { + sb.append(line + "\n"); + } + return sb.toString(); + } } diff --git a/plugin/src/test/resources/security/sample.pem b/plugin/src/test/resources/security/sample.pem new file mode 100644 index 0000000000..fa785ca10f --- /dev/null +++ b/plugin/src/test/resources/security/sample.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEyTCCA7GgAwIBAgIGAWLrc1O2MA0GCSqGSIb3DQEBCwUAMIGPMRMwEQYKCZIm +iZPyLGQBGRYDY29tMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTEZMBcGA1UECgwQ +RXhhbXBsZSBDb20gSW5jLjEhMB8GA1UECwwYRXhhbXBsZSBDb20gSW5jLiBSb290 +IENBMSEwHwYDVQQDDBhFeGFtcGxlIENvbSBJbmMuIFJvb3QgQ0EwHhcNMTgwNDIy +MDM0MzQ3WhcNMjgwNDE5MDM0MzQ3WjBeMRIwEAYKCZImiZPyLGQBGRYCZGUxDTAL +BgNVBAcMBHRlc3QxDTALBgNVBAoMBG5vZGUxDTALBgNVBAsMBG5vZGUxGzAZBgNV +BAMMEm5vZGUtMC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAJa+f476vLB+AwK53biYByUwN+40D8jMIovGXm6wgT8+9Sbs899dDXgt +9CE1Beo65oP1+JUz4c7UHMrCY3ePiDt4cidHVzEQ2g0YoVrQWv0RedS/yx/DKhs8 +Pw1O715oftP53p/2ijD5DifFv1eKfkhFH+lwny/vMSNxellpl6NxJTiJVnQ9HYOL +gf2t971ITJHnAuuxUF48HcuNovW4rhtkXef8kaAN7cE3LU+A9T474ULNCKkEFPIl +ZAKN3iJNFdVsxrTU+CUBHzk73Do1cCkEvJZ0ZFjp0Z3y8wLY/gqWGfGVyA9l2CUq +eIZNf55PNPtGzOrvvONiui48vBKH1LsCAwEAAaOCAVkwggFVMIG8BgNVHSMEgbQw +gbGAFJI1DOAPHitF9k0583tfouYSl0BzoYGVpIGSMIGPMRMwEQYKCZImiZPyLGQB +GRYDY29tMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTEZMBcGA1UECgwQRXhhbXBs +ZSBDb20gSW5jLjEhMB8GA1UECwwYRXhhbXBsZSBDb20gSW5jLiBSb290IENBMSEw +HwYDVQQDDBhFeGFtcGxlIENvbSBJbmMuIFJvb3QgQ0GCAQEwHQYDVR0OBBYEFKyv +78ZmFjVKM9g7pMConYH7FVBHMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXg +MCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA1BgNVHREELjAsiAUq +AwQFBYISbm9kZS0wLmV4YW1wbGUuY29tgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZI +hvcNAQELBQADggEBAIOKuyXsFfGv1hI/Lkpd/73QNqjqJdxQclX57GOMWNbOM5H0 +5/9AOIZ5JQsWULNKN77aHjLRr4owq2jGbpc/Z6kAd+eiatkcpnbtbGrhKpOtoEZy +8KuslwkeixpzLDNISSbkeLpXz4xJI1ETMN/VG8ZZP1bjzlHziHHDu0JNZ6TnNzKr +XzCGMCohFfem8vnKNnKUneMQMvXd3rzUaAgvtf7Hc2LTBlf4fZzZF1EkwdSXhaMA +1lkfHiqOBxtgeDLxCHESZ2fqgVqsWX+t3qHQfivcPW6txtDyrFPRdJOGhiMGzT/t +e/9kkAtQRgpTb3skYdIOOUOV0WGQ60kJlFhAzIs= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/plugin/src/test/resources/security/test-kirk.jks b/plugin/src/test/resources/security/test-kirk.jks new file mode 100644 index 0000000000..174dbda656 Binary files /dev/null and b/plugin/src/test/resources/security/test-kirk.jks differ