diff --git a/docs/reference/cluster.asciidoc b/docs/reference/cluster.asciidoc index cfa2d5a6488d7..3c3001fd58e48 100644 --- a/docs/reference/cluster.asciidoc +++ b/docs/reference/cluster.asciidoc @@ -81,6 +81,8 @@ include::cluster/health.asciidoc[] include::cluster/state.asciidoc[] +include::cluster/master.asciidoc[] + include::cluster/stats.asciidoc[] include::cluster/pending.asciidoc[] diff --git a/docs/reference/cluster/master.asciidoc b/docs/reference/cluster/master.asciidoc new file mode 100644 index 0000000000000..c6fb332fc71a9 --- /dev/null +++ b/docs/reference/cluster/master.asciidoc @@ -0,0 +1,16 @@ +[[cluster-master]] +== Cluster master + +The cluster master API returns the identity of the currently elected master +node. + +[source,js] +-------------------------------------------------- +GET /_cluster/master +-------------------------------------------------- +// CONSOLE + +By default the coordinating node asks the master for its own identity to verify +that it is still the master. The timeout for this can be set with the +`?master_timeout` <> query parameter, which defaults to +`30s`. This check can be skipped by setting the `?local` query parameter. diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.master.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.master.json new file mode 100644 index 0000000000000..46f6417d1cfb8 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.master.json @@ -0,0 +1,21 @@ +{ + "cluster.master": { + "documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/master/cluster-master.html", + "methods": ["GET"], + "url": { + "path": "/_cluster/master", + "paths": ["/_cluster/master"], + "params": { + "local": { + "type": "boolean", + "description": "Return local information, do not retrieve the state from master node (default: false)" + }, + "master_timeout": { + "type": "time", + "description": "Specify timeout for connection to master" + } + } + }, + "body": null + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.master/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.master/10_basic.yml new file mode 100644 index 0000000000000..cd42a228dd4b3 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.master/10_basic.yml @@ -0,0 +1,10 @@ +--- +"get cluster master": + - skip: + version: " - 7.99.99" + reason: Get cluster master API was introduced in 8.0 + + - do: + cluster.master: {} + + - is_true: master_node_id diff --git a/server/src/main/java/org/elasticsearch/action/ActionModule.java b/server/src/main/java/org/elasticsearch/action/ActionModule.java index 83e1e01614435..02a23a825a20d 100644 --- a/server/src/main/java/org/elasticsearch/action/ActionModule.java +++ b/server/src/main/java/org/elasticsearch/action/ActionModule.java @@ -227,6 +227,7 @@ import org.elasticsearch.rest.action.admin.cluster.RestClusterAllocationExplainAction; import org.elasticsearch.rest.action.admin.cluster.RestClusterGetSettingsAction; import org.elasticsearch.rest.action.admin.cluster.RestClusterHealthAction; +import org.elasticsearch.rest.action.admin.cluster.RestClusterMasterAction; import org.elasticsearch.rest.action.admin.cluster.RestClusterRerouteAction; import org.elasticsearch.rest.action.admin.cluster.RestClusterSearchShardsAction; import org.elasticsearch.rest.action.admin.cluster.RestClusterStateAction; @@ -561,6 +562,7 @@ public void initRestHandlers(Supplier nodesInCluster) { registerHandler.accept(new RestClusterAllocationExplainAction(settings, restController)); registerHandler.accept(new RestClusterStatsAction(settings, restController)); registerHandler.accept(new RestClusterStateAction(settings, restController, settingsFilter)); + registerHandler.accept(new RestClusterMasterAction(settings, restController)); registerHandler.accept(new RestClusterHealthAction(settings, restController)); registerHandler.accept(new RestClusterUpdateSettingsAction(settings, restController)); registerHandler.accept(new RestClusterGetSettingsAction(settings, restController, clusterSettings, settingsFilter)); diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterMasterAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterMasterAction.java new file mode 100644 index 0000000000000..c686877e1dae4 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterMasterAction.java @@ -0,0 +1,72 @@ +/* + * 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.rest.action.admin.cluster; + +import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; +import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; +import org.elasticsearch.client.Requests; +import org.elasticsearch.client.node.NodeClient; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.discovery.MasterNotDiscoveredException; +import org.elasticsearch.rest.BaseRestHandler; +import org.elasticsearch.rest.BytesRestResponse; +import org.elasticsearch.rest.RestController; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.RestResponse; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.rest.action.RestBuilderListener; + +import java.io.IOException; + +public class RestClusterMasterAction extends BaseRestHandler { + + public RestClusterMasterAction(Settings settings, RestController controller) { + super(settings); + controller.registerHandler(RestRequest.Method.GET, "/_cluster/master", this); + } + + @Override + public String getName() { + return "cluster_master_action"; + } + + @Override + public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { + final ClusterStateRequest clusterStateRequest = Requests.clusterStateRequest(); + clusterStateRequest.clear(); + clusterStateRequest.nodes(true); + clusterStateRequest.local(request.paramAsBoolean("local", clusterStateRequest.local())); + clusterStateRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterStateRequest.masterNodeTimeout())); + return channel -> client.admin().cluster().state(clusterStateRequest, new RestBuilderListener(channel) { + @Override + public RestResponse buildResponse(ClusterStateResponse response, XContentBuilder builder) throws Exception { + final DiscoveryNode masterNode = response.getState().nodes().getMasterNode(); + if (masterNode == null) { + throw new MasterNotDiscoveredException(); + } + builder.startObject(); + builder.field("master_node_id", masterNode.getId()); + builder.endObject(); + return new BytesRestResponse(RestStatus.OK, builder); + } + }); + } +}