From 5e2df93efdde1984e47b8f80128a3ffaa06770f5 Mon Sep 17 00:00:00 2001 From: jaymode Date: Mon, 6 Apr 2020 22:18:15 -0600 Subject: [PATCH] Reintroduce system index APIs for Kibana This change reintroduces the system index APIs for Kibana wihtout the changes made for marking what system indices could be accessed using these APIs. In essence, this is a partial revert of #53912. The changes for marking what system indices should be allowed access will be handled in a separate change. The APIs introduced here are wrapped versions of the existing REST endpoints. A new setting is also introduced since the Kibana system indices' names are allowed to be changed by a user in case multiple instances of Kibana use the same instance of Elasticsearch. Relates #52385 --- modules/kibana/build.gradle | 31 +++ .../elasticsearch/kibana/KibanaPlugin.java | 143 ++++++++++ .../kibana/KibanaPluginTests.java | 49 ++++ .../kibana/KibanaSystemIndexIT.java | 260 ++++++++++++++++++ .../tasksplugin/TasksPlugin.java | 3 +- .../tasksplugin/TasksPluginTests.java | 3 +- .../elasticsearch/action/ActionModule.java | 2 +- .../java/org/elasticsearch/node/Node.java | 2 +- .../plugins/SystemIndexPlugin.java | 4 +- .../elasticsearch/rest/BaseRestHandler.java | 53 ++++ .../rest/action/document/RestIndexAction.java | 11 +- .../action/document/RestIndexActionTests.java | 7 +- .../xpack/enrich/EnrichPlugin.java | 2 +- .../xpack/logstash/Logstash.java | 3 +- .../xpack/ml/MachineLearning.java | 2 +- .../xpack/security/Security.java | 2 +- .../xpack/transform/Transform.java | 2 +- .../elasticsearch/xpack/watcher/Watcher.java | 2 +- 18 files changed, 559 insertions(+), 22 deletions(-) create mode 100644 modules/kibana/build.gradle create mode 100644 modules/kibana/src/main/java/org/elasticsearch/kibana/KibanaPlugin.java create mode 100644 modules/kibana/src/test/java/org/elasticsearch/kibana/KibanaPluginTests.java create mode 100644 modules/kibana/src/test/java/org/elasticsearch/kibana/KibanaSystemIndexIT.java diff --git a/modules/kibana/build.gradle b/modules/kibana/build.gradle new file mode 100644 index 0000000000000..818f555be1b9c --- /dev/null +++ b/modules/kibana/build.gradle @@ -0,0 +1,31 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +esplugin { + description 'Plugin exposing APIs for Kibana system indices' + classname 'org.elasticsearch.kibana.KibanaPlugin' +} + +dependencies { + compile project(path: ':modules:reindex', configuration: 'runtime') +} + +testClusters.integTest { + module file(project(':modules:reindex').tasks.bundlePlugin.archiveFile) +} diff --git a/modules/kibana/src/main/java/org/elasticsearch/kibana/KibanaPlugin.java b/modules/kibana/src/main/java/org/elasticsearch/kibana/KibanaPlugin.java new file mode 100644 index 0000000000000..2d3b13989ffeb --- /dev/null +++ b/modules/kibana/src/main/java/org/elasticsearch/kibana/KibanaPlugin.java @@ -0,0 +1,143 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.kibana; + +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.IndexScopedSettings; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Setting.Property; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.settings.SettingsFilter; +import org.elasticsearch.index.reindex.RestDeleteByQueryAction; +import org.elasticsearch.indices.SystemIndexDescriptor; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.SystemIndexPlugin; +import org.elasticsearch.rest.BaseRestHandler; +import org.elasticsearch.rest.RestController; +import org.elasticsearch.rest.RestHandler; +import org.elasticsearch.rest.action.admin.indices.RestCreateIndexAction; +import org.elasticsearch.rest.action.admin.indices.RestGetAliasesAction; +import org.elasticsearch.rest.action.admin.indices.RestGetIndicesAction; +import org.elasticsearch.rest.action.admin.indices.RestIndexPutAliasAction; +import org.elasticsearch.rest.action.admin.indices.RestRefreshAction; +import org.elasticsearch.rest.action.admin.indices.RestUpdateSettingsAction; +import org.elasticsearch.rest.action.document.RestBulkAction; +import org.elasticsearch.rest.action.document.RestDeleteAction; +import org.elasticsearch.rest.action.document.RestGetAction; +import org.elasticsearch.rest.action.document.RestIndexAction; +import org.elasticsearch.rest.action.document.RestIndexAction.AutoIdHandler; +import org.elasticsearch.rest.action.document.RestIndexAction.CreateHandler; +import org.elasticsearch.rest.action.document.RestMultiGetAction; +import org.elasticsearch.rest.action.document.RestUpdateAction; +import org.elasticsearch.rest.action.search.RestClearScrollAction; +import org.elasticsearch.rest.action.search.RestSearchAction; +import org.elasticsearch.rest.action.search.RestSearchScrollAction; + +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class KibanaPlugin extends Plugin implements SystemIndexPlugin { + + public static final Setting> KIBANA_INDEX_NAMES_SETTING = Setting.listSetting( + "kibana.system_indices", + List.of(".kibana*", ".reporting"), + Function.identity(), + Property.NodeScope + ); + + @Override + public Collection getSystemIndexDescriptors(Settings settings) { + return KIBANA_INDEX_NAMES_SETTING.get(settings) + .stream() + .map(pattern -> new SystemIndexDescriptor(pattern, "System index used by kibana")) + .collect(Collectors.toUnmodifiableList()); + } + + @Override + public List getRestHandlers( + Settings settings, + RestController restController, + ClusterSettings clusterSettings, + IndexScopedSettings indexScopedSettings, + SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier nodesInCluster + ) { + // TODO need to figure out what subset of system indices Kibana should have access to via these APIs + return List.of( + // Based on https://github.com/elastic/kibana/issues/49764 + // apis needed to perform migrations... ideally these will go away + new KibanaWrappedRestHandler(new RestCreateIndexAction()), + new KibanaWrappedRestHandler(new RestGetAliasesAction()), + new KibanaWrappedRestHandler(new RestIndexPutAliasAction()), + new KibanaWrappedRestHandler(new RestRefreshAction()), + + // apis needed to access saved objects + new KibanaWrappedRestHandler(new RestGetAction()), + new KibanaWrappedRestHandler(new RestMultiGetAction(settings)), + new KibanaWrappedRestHandler(new RestSearchAction()), + new KibanaWrappedRestHandler(new RestBulkAction(settings)), + new KibanaWrappedRestHandler(new RestDeleteAction()), + new KibanaWrappedRestHandler(new RestDeleteByQueryAction()), + + // api used for testing + new KibanaWrappedRestHandler(new RestUpdateSettingsAction()), + + // apis used specifically by reporting + new KibanaWrappedRestHandler(new RestGetIndicesAction()), + new KibanaWrappedRestHandler(new RestIndexAction()), + new KibanaWrappedRestHandler(new CreateHandler()), + new KibanaWrappedRestHandler(new AutoIdHandler(nodesInCluster)), + new KibanaWrappedRestHandler(new RestUpdateAction()), + new KibanaWrappedRestHandler(new RestSearchScrollAction()), + new KibanaWrappedRestHandler(new RestClearScrollAction()) + ); + + } + + @Override + public List> getSettings() { + return List.of(KIBANA_INDEX_NAMES_SETTING); + } + + static class KibanaWrappedRestHandler extends BaseRestHandler.Wrapper { + + KibanaWrappedRestHandler(BaseRestHandler delegate) { + super(delegate); + } + + @Override + public String getName() { + return "kibana_" + super.getName(); + } + + @Override + public List routes() { + return super.routes().stream() + .map(route -> new Route(route.getMethod(), "/_kibana" + route.getPath())) + .collect(Collectors.toUnmodifiableList()); + } + } +} diff --git a/modules/kibana/src/test/java/org/elasticsearch/kibana/KibanaPluginTests.java b/modules/kibana/src/test/java/org/elasticsearch/kibana/KibanaPluginTests.java new file mode 100644 index 0000000000000..b1551b5a4874e --- /dev/null +++ b/modules/kibana/src/test/java/org/elasticsearch/kibana/KibanaPluginTests.java @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.kibana; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.indices.SystemIndexDescriptor; +import org.elasticsearch.test.ESTestCase; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; + +public class KibanaPluginTests extends ESTestCase { + + public void testKibanaIndexNames() { + assertThat(new KibanaPlugin().getSettings(), contains(KibanaPlugin.KIBANA_INDEX_NAMES_SETTING)); + assertThat( + new KibanaPlugin().getSystemIndexDescriptors(Settings.EMPTY) + .stream() + .map(SystemIndexDescriptor::getIndexPattern) + .collect(Collectors.toUnmodifiableList()), + contains(".kibana*", ".reporting") + ); + final List names = List.of("." + randomAlphaOfLength(4), "." + randomAlphaOfLength(6)); + final List namesFromDescriptors = new KibanaPlugin().getSystemIndexDescriptors( + Settings.builder().putList(KibanaPlugin.KIBANA_INDEX_NAMES_SETTING.getKey(), names).build() + ).stream().map(SystemIndexDescriptor::getIndexPattern).collect(Collectors.toUnmodifiableList()); + assertThat(namesFromDescriptors, is(names)); + } +} diff --git a/modules/kibana/src/test/java/org/elasticsearch/kibana/KibanaSystemIndexIT.java b/modules/kibana/src/test/java/org/elasticsearch/kibana/KibanaSystemIndexIT.java new file mode 100644 index 0000000000000..df6e894db2776 --- /dev/null +++ b/modules/kibana/src/test/java/org/elasticsearch/kibana/KibanaSystemIndexIT.java @@ -0,0 +1,260 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.kibana; + +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.test.rest.ESRestTestCase; + +import java.io.IOException; +import java.util.Map; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class KibanaSystemIndexIT extends ESRestTestCase { + + public void testCreateIndex() throws IOException { + Request request = new Request("PUT", "/_kibana/.kibana-1"); + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + } + + public void testAliases() throws IOException { + Request request = new Request("PUT", "/_kibana/.kibana-1"); + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + request = new Request("PUT", "/_kibana/.kibana-1/_alias/.kibana"); + response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + request = new Request("GET", "/_kibana/_aliases"); + response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString(".kibana")); + } + + public void testBulkToKibanaIndex() throws IOException { + Request request = new Request("POST", "/_kibana/_bulk"); + request.setJsonEntity("{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"1\" } }\n{ \"foo\" : \"bar\" }\n"); + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + } + + public void testRefresh() throws IOException { + Request request = new Request("POST", "/_kibana/_bulk"); + request.setJsonEntity("{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"1\" } }\n{ \"foo\" : \"bar\" }\n"); + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + request = new Request("GET", "/_kibana/.kibana/_refresh"); + response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + Request getRequest = new Request("GET", "/_kibana/.kibana/_doc/1"); + Response getResponse = client().performRequest(getRequest); + assertThat(getResponse.getStatusLine().getStatusCode(), is(200)); + String responseBody = EntityUtils.toString(getResponse.getEntity()); + assertThat(responseBody, containsString("foo")); + assertThat(responseBody, containsString("bar")); + } + + public void testGetFromKibanaIndex() throws IOException { + Request request = new Request("POST", "/_kibana/_bulk"); + request.setJsonEntity("{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"1\" } }\n{ \"foo\" : \"bar\" }\n"); + request.addParameter("refresh", "true"); + + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + Request getRequest = new Request("GET", "/_kibana/.kibana/_doc/1"); + Response getResponse = client().performRequest(getRequest); + assertThat(getResponse.getStatusLine().getStatusCode(), is(200)); + String responseBody = EntityUtils.toString(getResponse.getEntity()); + assertThat(responseBody, containsString("foo")); + assertThat(responseBody, containsString("bar")); + } + + public void testMultiGetFromKibanaIndex() throws IOException { + Request request = new Request("POST", "/_kibana/_bulk"); + request.setJsonEntity( + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"1\" } }\n{ \"foo\" : \"bar\" }\n" + + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"2\" } }\n{ \"baz\" : \"tag\" }\n" + ); + request.addParameter("refresh", "true"); + + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + Request getRequest = new Request("GET", "/_kibana/_mget"); + getRequest.setJsonEntity( + "{ \"docs\" : [ { \"_index\" : \".kibana\", \"_id\" : \"1\" }, " + "{ \"_index\" : \".kibana\", \"_id\" : \"2\" } ] }\n" + ); + Response getResponse = client().performRequest(getRequest); + assertThat(getResponse.getStatusLine().getStatusCode(), is(200)); + String responseBody = EntityUtils.toString(getResponse.getEntity()); + assertThat(responseBody, containsString("foo")); + assertThat(responseBody, containsString("bar")); + assertThat(responseBody, containsString("baz")); + assertThat(responseBody, containsString("tag")); + } + + public void testSearchFromKibanaIndex() throws IOException { + Request request = new Request("POST", "/_kibana/_bulk"); + request.setJsonEntity( + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"1\" } }\n{ \"foo\" : \"bar\" }\n" + + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"2\" } }\n{ \"baz\" : \"tag\" }\n" + ); + request.addParameter("refresh", "true"); + + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + Request searchRequest = new Request("GET", "/_kibana/.kibana/_search"); + searchRequest.setJsonEntity("{ \"query\" : { \"match_all\" : {} } }\n"); + Response getResponse = client().performRequest(searchRequest); + assertThat(getResponse.getStatusLine().getStatusCode(), is(200)); + String responseBody = EntityUtils.toString(getResponse.getEntity()); + assertThat(responseBody, containsString("foo")); + assertThat(responseBody, containsString("bar")); + assertThat(responseBody, containsString("baz")); + assertThat(responseBody, containsString("tag")); + } + + public void testDeleteFromKibanaIndex() throws IOException { + Request request = new Request("POST", "/_kibana/_bulk"); + request.setJsonEntity( + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"1\" } }\n{ \"foo\" : \"bar\" }\n" + + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"2\" } }\n{ \"baz\" : \"tag\" }\n" + ); + request.addParameter("refresh", "true"); + + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + Request deleteRequest = new Request("DELETE", "/_kibana/.kibana/_doc/1"); + Response deleteResponse = client().performRequest(deleteRequest); + assertThat(deleteResponse.getStatusLine().getStatusCode(), is(200)); + } + + public void testDeleteByQueryFromKibanaIndex() throws IOException { + Request request = new Request("POST", "/_kibana/_bulk"); + request.setJsonEntity( + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"1\" } }\n{ \"foo\" : \"bar\" }\n" + + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"2\" } }\n{ \"baz\" : \"tag\" }\n" + ); + request.addParameter("refresh", "true"); + + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + Request dbqRequest = new Request("POST", "/_kibana/.kibana/_delete_by_query"); + dbqRequest.setJsonEntity("{ \"query\" : { \"match_all\" : {} } }\n"); + Response dbqResponse = client().performRequest(dbqRequest); + assertThat(dbqResponse.getStatusLine().getStatusCode(), is(200)); + } + + public void testUpdateIndexSettings() throws IOException { + Request request = new Request("PUT", "/_kibana/.kibana-1"); + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + request = new Request("PUT", "/_kibana/.kibana-1/_settings"); + request.setJsonEntity("{ \"index.blocks.read_only\" : false }"); + response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + } + + public void testGetIndex() throws IOException { + Request request = new Request("PUT", "/_kibana/.kibana-1"); + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + request = new Request("GET", "/_kibana/.kibana-1"); + response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString(".kibana-1")); + } + + public void testIndexingAndUpdatingDocs() throws IOException { + Request request = new Request("PUT", "/_kibana/.kibana-1/_doc/1"); + request.setJsonEntity("{ \"foo\" : \"bar\" }"); + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(201)); + + request = new Request("PUT", "/_kibana/.kibana-1/_create/2"); + request.setJsonEntity("{ \"foo\" : \"bar\" }"); + response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(201)); + + request = new Request("POST", "/_kibana/.kibana-1/_doc"); + request.setJsonEntity("{ \"foo\" : \"bar\" }"); + response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(201)); + + request = new Request("GET", "/_kibana/.kibana-1/_refresh"); + response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + request = new Request("POST", "/_kibana/.kibana-1/_update/1"); + request.setJsonEntity("{ \"doc\" : { \"foo\" : \"baz\" } }"); + response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + } + + public void testScrollingDocs() throws IOException { + Request request = new Request("POST", "/_kibana/_bulk"); + request.setJsonEntity( + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"1\" } }\n{ \"foo\" : \"bar\" }\n" + + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"2\" } }\n{ \"baz\" : \"tag\" }\n" + + "{ \"index\" : { \"_index\" : \".kibana\", \"_id\" : \"3\" } }\n{ \"baz\" : \"tag\" }\n" + ); + request.addParameter("refresh", "true"); + Response response = client().performRequest(request); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + Request searchRequest = new Request("GET", "/_kibana/.kibana/_search"); + searchRequest.setJsonEntity("{ \"size\" : 1,\n\"query\" : { \"match_all\" : {} } }\n"); + searchRequest.addParameter("scroll", "1m"); + response = client().performRequest(searchRequest); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + Map map = XContentHelper.convertToMap(JsonXContent.jsonXContent, EntityUtils.toString(response.getEntity()), false); + assertNotNull(map.get("_scroll_id")); + String scrollId = (String) map.get("_scroll_id"); + + Request scrollRequest = new Request("POST", "/_kibana/_search/scroll"); + scrollRequest.addParameter("scroll_id", scrollId); + scrollRequest.addParameter("scroll", "1m"); + response = client().performRequest(scrollRequest); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + map = XContentHelper.convertToMap(JsonXContent.jsonXContent, EntityUtils.toString(response.getEntity()), false); + assertNotNull(map.get("_scroll_id")); + scrollId = (String) map.get("_scroll_id"); + + Request clearScrollRequest = new Request("DELETE", "/_kibana/_search/scroll"); + clearScrollRequest.addParameter("scroll_id", scrollId); + response = client().performRequest(clearScrollRequest); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + } +} diff --git a/modules/tasks/src/main/java/org/elasticsearch/tasksplugin/TasksPlugin.java b/modules/tasks/src/main/java/org/elasticsearch/tasksplugin/TasksPlugin.java index b7d63991877db..0467b9419c778 100644 --- a/modules/tasks/src/main/java/org/elasticsearch/tasksplugin/TasksPlugin.java +++ b/modules/tasks/src/main/java/org/elasticsearch/tasksplugin/TasksPlugin.java @@ -19,6 +19,7 @@ package org.elasticsearch.tasksplugin; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SystemIndexPlugin; @@ -34,7 +35,7 @@ public class TasksPlugin extends Plugin implements SystemIndexPlugin { @Override - public Collection getSystemIndexDescriptors() { + public Collection getSystemIndexDescriptors(Settings settings) { return Collections.singletonList(new SystemIndexDescriptor(TASK_INDEX, this.getClass().getSimpleName())); } } diff --git a/modules/tasks/src/test/java/org/elasticsearch/tasksplugin/TasksPluginTests.java b/modules/tasks/src/test/java/org/elasticsearch/tasksplugin/TasksPluginTests.java index 48ec1e06098f3..23b873e377eb3 100644 --- a/modules/tasks/src/test/java/org/elasticsearch/tasksplugin/TasksPluginTests.java +++ b/modules/tasks/src/test/java/org/elasticsearch/tasksplugin/TasksPluginTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.tasksplugin; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; import org.hamcrest.Matchers; @@ -27,6 +28,6 @@ public class TasksPluginTests extends ESTestCase { public void testDummy() { // This is a dummy test case to satisfy the conventions TasksPlugin plugin = new TasksPlugin(); - assertThat(plugin.getSystemIndexDescriptors(), Matchers.hasSize(1)); + assertThat(plugin.getSystemIndexDescriptors(Settings.EMPTY), Matchers.hasSize(1)); } } diff --git a/server/src/main/java/org/elasticsearch/action/ActionModule.java b/server/src/main/java/org/elasticsearch/action/ActionModule.java index d0f82f138205f..cb52f610607f8 100644 --- a/server/src/main/java/org/elasticsearch/action/ActionModule.java +++ b/server/src/main/java/org/elasticsearch/action/ActionModule.java @@ -716,7 +716,7 @@ public void initRestHandlers(Supplier nodesInCluster) { registerHandler.accept(new RestIndexAction()); registerHandler.accept(new CreateHandler()); - registerHandler.accept(new AutoIdHandler(clusterService)); + registerHandler.accept(new AutoIdHandler(nodesInCluster)); registerHandler.accept(new RestGetAction()); registerHandler.accept(new RestGetSourceAction()); registerHandler.accept(new RestMultiGetAction(settings)); diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index 91f75aaa0e0c8..ec0e86505bf1e 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -434,7 +434,7 @@ protected Node(final Environment initialEnvironment, .stream() .collect(Collectors.toUnmodifiableMap( plugin -> plugin.getClass().getSimpleName(), - plugin -> plugin.getSystemIndexDescriptors())); + plugin -> plugin.getSystemIndexDescriptors(settings))); SystemIndexDescriptor.checkForOverlappingPatterns(systemIndexDescriptorMap); final List systemIndexDescriptors = systemIndexDescriptorMap.values().stream() diff --git a/server/src/main/java/org/elasticsearch/plugins/SystemIndexPlugin.java b/server/src/main/java/org/elasticsearch/plugins/SystemIndexPlugin.java index a4d7a36f38670..821f141b3eabc 100644 --- a/server/src/main/java/org/elasticsearch/plugins/SystemIndexPlugin.java +++ b/server/src/main/java/org/elasticsearch/plugins/SystemIndexPlugin.java @@ -19,6 +19,7 @@ package org.elasticsearch.plugins; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.indices.SystemIndexDescriptor; import java.util.Collection; @@ -33,9 +34,10 @@ public interface SystemIndexPlugin extends ActionPlugin { /** * Returns a {@link Collection} of {@link SystemIndexDescriptor}s that describe this plugin's system indices, including * name, mapping, and settings. + * @param settings The node's settings * @return Descriptions of the system indices managed by this plugin. */ - default Collection getSystemIndexDescriptors() { + default Collection getSystemIndexDescriptors(Settings settings) { return Collections.emptyList(); } } diff --git a/server/src/main/java/org/elasticsearch/rest/BaseRestHandler.java b/server/src/main/java/org/elasticsearch/rest/BaseRestHandler.java index f2fd12bfa79c2..101c3182fbb62 100644 --- a/server/src/main/java/org/elasticsearch/rest/BaseRestHandler.java +++ b/server/src/main/java/org/elasticsearch/rest/BaseRestHandler.java @@ -183,4 +183,57 @@ protected Set responseParams() { return Collections.emptySet(); } + public static class Wrapper extends BaseRestHandler { + + protected final BaseRestHandler delegate; + + public Wrapper(BaseRestHandler delegate) { + this.delegate = delegate; + } + + @Override + public String getName() { + return delegate.getName(); + } + + @Override + public List routes() { + return delegate.routes(); + } + + @Override + public List deprecatedRoutes() { + return delegate.deprecatedRoutes(); + } + + @Override + public List replacedRoutes() { + return delegate.replacedRoutes(); + } + + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + return delegate.prepareRequest(request, client); + } + + @Override + protected Set responseParams() { + return delegate.responseParams(); + } + + @Override + public boolean canTripCircuitBreaker() { + return delegate.canTripCircuitBreaker(); + } + + @Override + public boolean supportsContentStream() { + return delegate.supportsContentStream(); + } + + @Override + public boolean allowsUnsafeBuffers() { + return delegate.allowsUnsafeBuffers(); + } + } } diff --git a/server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java b/server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java index 350490c370860..6c54285d67dad 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java @@ -23,7 +23,7 @@ import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.client.node.NodeClient; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.index.VersionType; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; @@ -33,6 +33,7 @@ import java.io.IOException; import java.util.List; import java.util.Locale; +import java.util.function.Supplier; import static org.elasticsearch.rest.RestRequest.Method.POST; import static org.elasticsearch.rest.RestRequest.Method.PUT; @@ -81,10 +82,10 @@ void validateOpType(String opType) { public static final class AutoIdHandler extends RestIndexAction { - private final ClusterService clusterService; + private final Supplier nodesInCluster; - public AutoIdHandler(ClusterService clusterService) { - this.clusterService = clusterService; + public AutoIdHandler(Supplier nodesInCluster) { + this.nodesInCluster = nodesInCluster; } @Override @@ -100,7 +101,7 @@ public List routes() { @Override public RestChannelConsumer prepareRequest(RestRequest request, final NodeClient client) throws IOException { assert request.params().get("id") == null : "non-null id: " + request.params().get("id"); - if (request.params().get("op_type") == null && clusterService.state().nodes().getMinNodeVersion().onOrAfter(Version.V_7_5_0)) { + if (request.params().get("op_type") == null && nodesInCluster.get().getMinNodeVersion().onOrAfter(Version.V_7_5_0)) { // default to op_type create request.params().put("op_type", "create"); } diff --git a/server/src/test/java/org/elasticsearch/rest/action/document/RestIndexActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/document/RestIndexActionTests.java index 84bc5f3bef340..bb549b64c724f 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/document/RestIndexActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/document/RestIndexActionTests.java @@ -27,7 +27,6 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.RestRequest; @@ -43,9 +42,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; public class RestIndexActionTests extends RestActionTestCase { @@ -53,11 +50,9 @@ public class RestIndexActionTests extends RestActionTestCase { @Before public void setUpAction() { - ClusterService clusterService = mock(ClusterService.class); - when(clusterService.state()).thenAnswer(invocationOnMock -> clusterStateSupplier.get()); controller().registerHandler(new RestIndexAction()); controller().registerHandler(new CreateHandler()); - controller().registerHandler(new AutoIdHandler(clusterService)); + controller().registerHandler(new AutoIdHandler(() -> clusterStateSupplier.get().nodes())); } public void testCreateOpTypeValidation() { diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java index f50251ec6edc9..be804788c0038 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java @@ -246,7 +246,7 @@ public List> getSettings() { } @Override - public Collection getSystemIndexDescriptors() { + public Collection getSystemIndexDescriptors(Settings settings) { return Collections.singletonList( new SystemIndexDescriptor(ENRICH_INDEX_PATTERN, "Contains data to support enrich ingest processors.") ); diff --git a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/Logstash.java b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/Logstash.java index f42bfa1a82176..919799e044e68 100644 --- a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/Logstash.java +++ b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/Logstash.java @@ -10,6 +10,7 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.cluster.metadata.IndexTemplateMetadata; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SystemIndexPlugin; @@ -62,7 +63,7 @@ public UnaryOperator> getIndexTemplateMetadat } @Override - public Collection getSystemIndexDescriptors() { + public Collection getSystemIndexDescriptors(Settings settings) { return Collections.singletonList( new SystemIndexDescriptor(LOGSTASH_CONCRETE_INDEX_NAME, "Contains data for Logstash Central Management") ); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java index 96b0c488fb05b..e5b80ec84ecda 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java @@ -992,7 +992,7 @@ public List getNamedWriteables() { } @Override - public Collection getSystemIndexDescriptors() { + public Collection getSystemIndexDescriptors(Settings settings) { return List.of( new SystemIndexDescriptor(MlMetaIndex.INDEX_NAME, "Contains scheduling and anomaly tracking metadata"), new SystemIndexDescriptor(AnomalyDetectorsIndexFields.CONFIG_INDEX, "Contains ML configuration data"), diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 56d29623c19c6..722d96d65e9a1 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -1087,7 +1087,7 @@ private synchronized NioGroupFactory getNioGroupFactory(Settings settings) { } @Override - public Collection getSystemIndexDescriptors() { + public Collection getSystemIndexDescriptors(Settings settings) { return List.of( new SystemIndexDescriptor(SECURITY_MAIN_ALIAS, "Contains Security configuration"), new SystemIndexDescriptor(RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_6, "Contains Security configuration"), diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java index f4aa0607afa66..becbbe2e92ecc 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java @@ -389,7 +389,7 @@ public List getNamedXContent() { } @Override - public Collection getSystemIndexDescriptors() { + public Collection getSystemIndexDescriptors(Settings settings) { return Collections.singletonList( new SystemIndexDescriptor(TransformInternalIndexConstants.INDEX_NAME_PATTERN, "Contains Transform configuration data") ); diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java index ad7ffaa6f88e2..e6511f73b6a2d 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java @@ -690,7 +690,7 @@ public void reload(Settings settings) { } @Override - public Collection getSystemIndexDescriptors() { + public Collection getSystemIndexDescriptors(Settings settings) { return List.of( new SystemIndexDescriptor(Watch.INDEX, "Contains Watch definitions"), new SystemIndexDescriptor(TriggeredWatchStoreField.INDEX_NAME, "Used to track current and queued Watch execution")