瀏覽代碼

Add cross cluster search test for mvt end point (#78054)

This commit adds a test to check that it is supported and document it.

Co-authored-by: James Rodewig <40268737+jrodewig@users.noreply.github.com>
Ignacio Vera 4 年之前
父節點
當前提交
9033faffff

+ 4 - 1
docs/reference/search/search-vector-tile-api.asciidoc

@@ -51,7 +51,7 @@ https://github.com/mapbox/vector-tile-spec[Mapbox vector tile specification].
 
 * If the {es} {security-features} are enabled, you must have the `read`
 <<privileges-list-indices,index privilege>> for the target data stream, index,
-or alias.
+or alias. For cross-cluster search, see <<cross-cluster-configuring>>.
 
 [[search-vector-tile-api-path-params]]
 ==== {api-path-parms-title}
@@ -60,6 +60,9 @@ or alias.
 (Required, string) Comma-separated list of data streams, indices, or aliases to
 search. Supports wildcards (`*`). To search all data streams and indices, omit
 this parameter or use `*` or `_all`.
++
+To search a remote cluster, use the `<cluster>:<target>` syntax. See
+<<modules-cross-cluster-search>>.
 
 `<field>`::
 (Required, string) Field containing geospatial values to return. Must be a

+ 4 - 0
docs/reference/search/search-your-data/search-across-clusters.asciidoc

@@ -20,6 +20,7 @@ The following APIs support {ccs}:
 * <<multi-search-template,Multi search template>>
 * <<search-field-caps,Field capabilities>>
 * experimental:[] <<eql-search-api,EQL search>>
+* experimental:[] <<search-vector-tile-api,Vector tile search>>
 
 [discrete]
 [[ccs-example]]
@@ -336,6 +337,9 @@ low latency.
 +
 See <<ccs-unmin-roundtrips>> to learn how this option works.
 
+NOTE: The <<search-vector-tile-api,vector tile search API>> always minimizes
+network roundtrips and doesn't include the `ccs_minimize_roundtrips` parameter.
+
 [discrete]
 [[ccs-min-roundtrips]]
 ==== Minimize network roundtrips

+ 0 - 0
x-pack/plugin/vector-tile/qa/build.gradle


+ 52 - 0
x-pack/plugin/vector-tile/qa/multi-cluster/build.gradle

@@ -0,0 +1,52 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import org.elasticsearch.gradle.testclusters.DefaultTestClustersTask
+
+apply plugin: 'elasticsearch.internal-testclusters'
+apply plugin: 'elasticsearch.standalone-rest-test'
+apply plugin: 'elasticsearch.rest-test'
+
+dependencies {
+  testImplementation project(':x-pack:plugin:vector-tile')
+}
+
+testClusters {
+  'local' {
+    testDistribution = 'DEFAULT'
+    setting 'xpack.license.self_generated.type', 'trial'
+    setting 'xpack.security.enabled', 'true'
+    user username: 'admin', password: 'admin-password', role: 'superuser'
+    setting 'cluster.remote.other.seeds',
+      { "\"${testClusters.named('remote').get().getAllTransportPortURI().join(",")}\"" }
+  }
+  'remote' {
+    testDistribution = 'DEFAULT'
+    setting 'xpack.license.self_generated.type', 'trial'
+    setting 'xpack.security.enabled', 'true'
+    user username: 'admin', password: 'admin-password', role: 'superuser'
+  }
+}
+
+tasks.register("startRemoteCluster", DefaultTestClustersTask.class) {
+  useCluster testClusters.'remote'
+  doLast {
+    "Starting remote cluster before integ tests and integTest cluster is started"
+  }
+}
+
+tasks.named("integTest").configure {
+  dependsOn 'startRemoteCluster'
+  useCluster testClusters.'remote'
+  useCluster testClusters.'local'
+  doFirst {
+    nonInputProperties.systemProperty 'tests.local',
+    "${-> testClusters.named('local').get().getAllHttpSocketURI().get(0)}"
+    nonInputProperties.systemProperty 'tests.remote',
+    "${-> testClusters.named('remote').get().getAllHttpSocketURI().get(0)}"
+  }
+}

+ 120 - 0
x-pack/plugin/vector-tile/qa/multi-cluster/src/test/java/org/elasticsearch/vectortile/VectorTileCCSIT.java

@@ -0,0 +1,120 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.vectortile;
+
+import com.wdtinc.mapbox_vector_tile.VectorTile;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.elasticsearch.client.Request;
+import org.elasticsearch.client.Response;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.common.settings.SecureString;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.test.rest.ESRestTestCase;
+import org.hamcrest.Matchers;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class VectorTileCCSIT extends ESRestTestCase {
+
+    @Override
+    protected Settings restClientSettings() {
+        final String token = basicAuthHeaderValue("admin", new SecureString("admin-password".toCharArray()));
+        return Settings.builder()
+            .put(ThreadContext.PREFIX + ".Authorization", token)
+            .build();
+    }
+
+    private int createIndex(RestClient client, String indexName) throws IOException {
+        final Request createRequest = new Request(HttpPut.METHOD_NAME, indexName);
+        Response response = client.performRequest(createRequest);
+        assertThat(response.getStatusLine().getStatusCode(), Matchers.equalTo(HttpStatus.SC_OK));
+        final Request mappingRequest = new Request(HttpPut.METHOD_NAME, indexName + "/_mapping");
+        mappingRequest.setJsonEntity(
+            "{\n"
+                + "  \"properties\": {\n"
+                + "    \"location\": {\n"
+                + "      \"type\": \"geo_shape\"\n"
+                + "    }\n"
+                + "  }\n"
+                + "}"
+        );
+        response = client.performRequest(mappingRequest);
+        assertThat(response.getStatusLine().getStatusCode(), Matchers.equalTo(HttpStatus.SC_OK));
+
+        final Request putRequest = new Request(HttpPost.METHOD_NAME, indexName + "/_doc");
+        putRequest.setJsonEntity("{\"location\": \"POINT(0 0)\"}");
+
+        // just add the shape geometry n times
+        final int numGeometries = randomIntBetween(1, 10);
+        for (int i = 0; i < numGeometries; i++) {
+            response = client.performRequest(putRequest);
+            assertThat(response.getStatusLine().getStatusCode(), Matchers.equalTo(HttpStatus.SC_CREATED));
+        }
+
+        final Request flushRequest = new Request(HttpPost.METHOD_NAME, indexName + "/_refresh");
+        response = client.performRequest(flushRequest);
+        assertThat(response.getStatusLine().getStatusCode(), Matchers.equalTo(HttpStatus.SC_OK));
+        return numGeometries;
+    }
+
+    public void testBasic() throws IOException {
+        try (RestClient local = buildLocalClusterClient(); RestClient remote = buildRemoteClusterClient()) {
+            final int localGeometries = createIndex(local, "test");
+            final int remoteGeometries = createIndex(remote, "test");
+            // check call in each cluster
+            final Request mvtRequest = new Request(HttpPost.METHOD_NAME, "test/_mvt/location/0/0/0");
+            final VectorTile.Tile localTile = execute(local, mvtRequest);
+            assertThat(getLayer(localTile, "hits").getFeaturesCount(), Matchers.equalTo(localGeometries));
+            final VectorTile.Tile remoteTile = execute(remote, mvtRequest);
+            assertThat(getLayer(remoteTile, "hits").getFeaturesCount(), Matchers.equalTo(remoteGeometries));
+            // call to both clusters
+            final Request mvtCCSRequest = new Request(HttpPost.METHOD_NAME, "/test,other:test/_mvt/location/0/0/0");
+            final VectorTile.Tile ccsTile = execute(local, mvtCCSRequest);
+            assertThat(getLayer(ccsTile, "hits").getFeaturesCount(), Matchers.equalTo(localGeometries + remoteGeometries));
+        }
+    }
+
+    private VectorTile.Tile.Layer getLayer(VectorTile.Tile tile, String layerName) {
+        for (int i = 0; i < tile.getLayersCount(); i++) {
+            final VectorTile.Tile.Layer layer = tile.getLayers(i);
+            if (layerName.equals(layer.getName())) {
+                return layer;
+            }
+        }
+        fail("Could not find layer " + layerName);
+        return null;
+    }
+
+    private VectorTile.Tile execute(RestClient client, Request mvtRequest) throws IOException {
+        final Response response = client.performRequest(mvtRequest);
+        final InputStream inputStream = response.getEntity().getContent();
+        assertThat(response.getStatusLine().getStatusCode(), Matchers.equalTo(HttpStatus.SC_OK));
+        return VectorTile.Tile.parseFrom(inputStream);
+    }
+
+    private RestClient buildLocalClusterClient() throws IOException {
+        return buildClient(System.getProperty("tests.local"));
+    }
+
+    private RestClient buildRemoteClusterClient() throws IOException {
+        return buildClient(System.getProperty("tests.remote"));
+    }
+
+    private RestClient buildClient(final String url) throws IOException {
+        final int portSeparator = url.lastIndexOf(':');
+        final HttpHost httpHost = new HttpHost(url.substring(0, portSeparator),
+            Integer.parseInt(url.substring(portSeparator + 1)), getProtocol());
+        return buildClient(restAdminSettings(), new HttpHost[]{httpHost});
+    }
+}