Rest: Adding support of multi-index query parameters for _cluster/state

Adding missing support for the multi-index query parameters 'ignore_unavailable',
'allow_no_indices' and 'expand_wildcards' to '_cluster/state' API. These
parameters are supposed to be supported for APIs that work across multiple indices.
So far overwriting the default settings per REST call was not possible which is
fixed here.

Closes #5229
Closes #9295
This commit is contained in:
Christoph Büscher 2015-01-22 16:47:14 +01:00
parent d3e10f9111
commit eeb96db76b
8 changed files with 303 additions and 2 deletions

View File

@ -32,6 +32,18 @@
"flat_settings": {
"type": "boolean",
"description": "Return settings in flat format (default: false)"
},
"ignore_unavailable": {
"type" : "boolean",
"description" : "Whether specified concrete indices should be ignored when unavailable (missing or closed)"
},
"allow_no_indices": {
"type" : "boolean",
"description" : "Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)"
},
"expand_wildcards":{
"type":"list",
"description":"Whether wildcard expressions should get expanded to open or closed indices (default: open)"
}
}
},

View File

@ -0,0 +1,92 @@
setup:
- do:
indices.create:
index: test_close_index
body:
settings:
number_of_shards: "1"
number_of_replicas: "0"
- do:
indices.create:
index: test_open_index
body:
settings:
number_of_shards: "1"
number_of_replicas: "0"
- do:
cluster.health:
wait_for_status: green
# close one index, keep other open for later test
- do:
indices.close:
index: test_close_index
---
"Test expand_wildcards parameter on closed, open indices and both":
- do:
cluster.state:
metric: [ metadata ]
index: test*
expand_wildcards: [ closed ]
- is_false: metadata.indices.test_open_index
- match: {metadata.indices.test_close_index.state: "close"}
- do:
cluster.state:
metric: [ metadata ]
index: test*
expand_wildcards: [ open ]
- match: {metadata.indices.test_open_index.state: "open"}
- is_false: metadata.indices.test_close_index
- do:
cluster.state:
metric: [ metadata ]
index: test*
expand_wildcards: [ open,closed ]
- match: {metadata.indices.test_open_index.state: "open"}
- match: {metadata.indices.test_close_index.state: "close"}
---
"Test ignore_unavailable parameter":
- do:
cluster.state:
metric: [ metadata ]
index: foobla
ignore_unavailable: true
- match: {metadata.indices: {}}
- do:
catch: missing
cluster.state:
metric: [ metadata ]
index: foobla
ignore_unavailable: false
---
"Test allow_no_indices parameter":
- do:
cluster.state:
metric: [ metadata ]
index: not_there*
- match: {metadata.indices: {}}
- do:
catch: missing
cluster.state:
metric: [ metadata ]
index: not_there*
allow_no_indices: false

View File

@ -40,6 +40,7 @@ public class ClusterStateRequest extends MasterNodeReadOperationRequest<ClusterS
private boolean metaData = true;
private boolean blocks = true;
private String[] indices = Strings.EMPTY_ARRAY;
private IndicesOptions indicesOptions = IndicesOptions.lenientExpandOpen();
public ClusterStateRequest() {
}
@ -57,7 +58,7 @@ public class ClusterStateRequest extends MasterNodeReadOperationRequest<ClusterS
indices = Strings.EMPTY_ARRAY;
return this;
}
public ClusterStateRequest clear() {
routingTable = false;
nodes = false;
@ -116,7 +117,12 @@ public class ClusterStateRequest extends MasterNodeReadOperationRequest<ClusterS
@Override
public IndicesOptions indicesOptions() {
return IndicesOptions.lenientExpandOpen();
return this.indicesOptions;
}
public final ClusterStateRequest indicesOptions(IndicesOptions indicesOptions) {
this.indicesOptions = indicesOptions;
return this;
}
@Override
@ -127,6 +133,7 @@ public class ClusterStateRequest extends MasterNodeReadOperationRequest<ClusterS
metaData = in.readBoolean();
blocks = in.readBoolean();
indices = in.readStringArray();
indicesOptions = IndicesOptions.readIndicesOptions(in);
}
@Override
@ -137,5 +144,6 @@ public class ClusterStateRequest extends MasterNodeReadOperationRequest<ClusterS
out.writeBoolean(metaData);
out.writeBoolean(blocks);
out.writeStringArray(indices);
indicesOptions.writeIndicesOptions(out);
}
}

View File

@ -20,6 +20,7 @@
package org.elasticsearch.action.admin.cluster.state;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.MasterNodeReadOperationRequestBuilder;
import org.elasticsearch.client.ClusterAdminClient;
@ -89,6 +90,11 @@ public class ClusterStateRequestBuilder extends MasterNodeReadOperationRequestBu
return this;
}
public ClusterStateRequestBuilder setIndicesOptions(IndicesOptions indicesOptions) {
request.indicesOptions(indicesOptions);
return this;
}
@Override
protected void doExecute(ActionListener<ClusterStateResponse> listener) {
client.state(request, listener);

View File

@ -21,6 +21,7 @@ package org.elasticsearch.rest.action.admin.cluster.state;
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.elasticsearch.cluster.ClusterState;
@ -57,6 +58,7 @@ public class RestClusterStateAction extends BaseRestHandler {
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) {
final ClusterStateRequest clusterStateRequest = Requests.clusterStateRequest();
clusterStateRequest.listenerThreaded(false);
clusterStateRequest.indicesOptions(IndicesOptions.fromRequest(request, clusterStateRequest.indicesOptions()));
clusterStateRequest.local(request.paramAsBoolean("local", clusterStateRequest.local()));
clusterStateRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterStateRequest.masterNodeTimeout()));

View File

@ -0,0 +1,76 @@
/*
* 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.action.admin.cluster.state;
import org.elasticsearch.Version;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.io.stream.BytesStreamInput;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.equalTo;
/**
* Unit tests for the {@link ClusterStateRequest}.
*/
public class ClusterStateRequestTest extends ElasticsearchTestCase {
@Test
public void testSerialization() throws Exception {
int iterations = randomIntBetween(5, 20);
for (int i = 0; i < iterations; i++) {
IndicesOptions indicesOptions = IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean());
ClusterStateRequest clusterStateRequest = new ClusterStateRequest().routingTable(randomBoolean()).metaData(randomBoolean())
.nodes(randomBoolean()).blocks(randomBoolean()).indices("testindex", "testindex2").indicesOptions(indicesOptions);
Version testVersion = randomVersionBetween(Version.CURRENT.minimumCompatibilityVersion(), Version.CURRENT);
BytesStreamOutput output = new BytesStreamOutput();
output.setVersion(testVersion);
clusterStateRequest.writeTo(output);
BytesStreamInput bytesStreamInput = new BytesStreamInput(output.bytes());
bytesStreamInput.setVersion(testVersion);
ClusterStateRequest deserializedCSRequest = new ClusterStateRequest();
deserializedCSRequest.readFrom(bytesStreamInput);
assertThat(deserializedCSRequest.routingTable(), equalTo(clusterStateRequest.routingTable()));
assertThat(deserializedCSRequest.metaData(), equalTo(clusterStateRequest.metaData()));
assertThat(deserializedCSRequest.nodes(), equalTo(clusterStateRequest.nodes()));
assertThat(deserializedCSRequest.blocks(), equalTo(clusterStateRequest.blocks()));
assertThat(deserializedCSRequest.indices(), equalTo(clusterStateRequest.indices()));
if (testVersion.onOrAfter(Version.V_1_5_0)) {
assertOptionsMatch(deserializedCSRequest.indicesOptions(), clusterStateRequest.indicesOptions());
} else {
// versions before V_1_5_0 use IndicesOptions.lenientExpandOpen()
assertOptionsMatch(deserializedCSRequest.indicesOptions(), IndicesOptions.lenientExpandOpen());
}
}
}
private static void assertOptionsMatch(IndicesOptions in, IndicesOptions out) {
assertThat(in.ignoreUnavailable(), equalTo(out.ignoreUnavailable()));
assertThat(in.expandWildcardsClosed(), equalTo(out.expandWildcardsClosed()));
assertThat(in.expandWildcardsOpen(), equalTo(out.expandWildcardsOpen()));
assertThat(in.allowNoIndices(), equalTo(out.allowNoIndices()));
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.bwcompat;
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ElasticsearchBackwardsCompatIntegrationTest;
import org.junit.Test;
import static org.hamcrest.Matchers.*;
public class ClusterStateBackwardsCompat extends ElasticsearchBackwardsCompatIntegrationTest {
@Test
public void testClusterState() throws Exception {
createIndex("test");
NodesInfoResponse nodesInfo = client().admin().cluster().prepareNodesInfo().execute().actionGet();
Settings settings = ImmutableSettings.settingsBuilder().put("client.transport.ignore_cluster_name", true)
.put("node.name", "transport_client_" + getTestName()).build();
// connect to each node with a custom TransportClient, issue a ClusterStateRequest to test serialization
for (NodeInfo n : nodesInfo.getNodes()) {
try (TransportClient tc = new TransportClient(settings)) {
tc.addTransportAddress(n.getNode().address());
ClusterStateResponse response = tc.admin().cluster().prepareState().execute().actionGet();
assertThat(response.getState().status(), equalTo(ClusterState.ClusterStateStatus.UNKNOWN));
assertNotNull(response.getClusterName());
assertTrue(response.getState().getMetaData().hasIndex("test"));
}
}
}
}

View File

@ -21,13 +21,16 @@ package org.elasticsearch.cluster;
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.indices.IndexMissingException;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.hamcrest.CollectionAssertions;
import org.junit.Before;
@ -157,4 +160,51 @@ public class SimpleClusterStateTests extends ElasticsearchIntegrationTest {
assertThat(mappingMetadata, equalTo(masterMappingMetaData));
}
}
@Test
public void testIndicesOptions() throws Exception {
ClusterStateResponse clusterStateResponse = client().admin().cluster().prepareState().clear().setMetaData(true).setIndices("f*")
.get();
assertThat(clusterStateResponse.getState().metaData().indices().size(), is(2));
// close one index
client().admin().indices().close(Requests.closeIndexRequest("fuu")).get();
clusterStateResponse = client().admin().cluster().prepareState().clear().setMetaData(true).setIndices("f*").get();
assertThat(clusterStateResponse.getState().metaData().indices().size(), is(1));
assertThat(clusterStateResponse.getState().metaData().index("foo").state(), equalTo(IndexMetaData.State.OPEN));
// expand_wildcards_closed should toggle return only closed index fuu
IndicesOptions expandCloseOptions = IndicesOptions.fromOptions(false, true, false, true);
clusterStateResponse = client().admin().cluster().prepareState().clear().setMetaData(true).setIndices("f*")
.setIndicesOptions(expandCloseOptions).get();
assertThat(clusterStateResponse.getState().metaData().indices().size(), is(1));
assertThat(clusterStateResponse.getState().metaData().index("fuu").state(), equalTo(IndexMetaData.State.CLOSE));
// ignore_unavailable set to true should not raise exception on fzzbzz
IndicesOptions ignoreUnavailabe = IndicesOptions.fromOptions(true, true, true, false);
clusterStateResponse = client().admin().cluster().prepareState().clear().setMetaData(true).setIndices("fzzbzz")
.setIndicesOptions(ignoreUnavailabe).get();
assertThat(clusterStateResponse.getState().metaData().indices().isEmpty(), is(true));
// empty wildcard expansion result should work when allowNoIndices is
// turned on
IndicesOptions allowNoIndices = IndicesOptions.fromOptions(false, true, true, false);
clusterStateResponse = client().admin().cluster().prepareState().clear().setMetaData(true).setIndices("a*")
.setIndicesOptions(allowNoIndices).get();
assertThat(clusterStateResponse.getState().metaData().indices().isEmpty(), is(true));
}
@Test(expected=IndexMissingException.class)
public void testIndicesOptionsOnAllowNoIndicesFalse() throws Exception {
// empty wildcard expansion throws exception when allowNoIndices is turned off
IndicesOptions allowNoIndices = IndicesOptions.fromOptions(false, false, true, false);
client().admin().cluster().prepareState().clear().setMetaData(true).setIndices("a*").setIndicesOptions(allowNoIndices).get();
}
@Test(expected=IndexMissingException.class)
public void testIndicesIgnoreUnavailableFalse() throws Exception {
// ignore_unavailable set to false throws exception when allowNoIndices is turned off
IndicesOptions allowNoIndices = IndicesOptions.fromOptions(false, true, true, false);
client().admin().cluster().prepareState().clear().setMetaData(true).setIndices("fzzbzz").setIndicesOptions(allowNoIndices).get();
}
}