Added action.destructive_requires_name that controls whether wildcard expressions and _all is allowed to be used for destructive operat Also the delete index api requires always an index to be specified (either concrete index, alias or wildcard expression)

Closes  
This commit is contained in:
Martijn van Groningen 2014-01-04 18:54:17 +01:00
parent 7042a9aa65
commit eb63bb259d
18 changed files with 400 additions and 127 deletions

@ -8,10 +8,12 @@ The delete index API allows to delete an existing index.
$ curl -XDELETE 'http://localhost:9200/twitter/'
--------------------------------------------------
The above example deletes an index called `twitter`.
The above example deletes an index called `twitter`. Specifying an index,
alias or wildcard expression is required.
The delete index API can also be applied to more than one index, or on
`_all` indices (be careful!). All indices will also be deleted when no
specific index is provided. In order to disable allowing to delete all
indices, set `action.disable_delete_all_indices` setting in the config
to `true`.
all indices (be careful!) by using `_all` or `*` as index.
In order to disable allowing to delete indices via wildcards or `_all`,
set `action.destructive_requires_name` setting in the config to `true`.
This setting can also be changed via the cluster update settings api.

@ -23,6 +23,7 @@ disabled using the `ignore_unavailable=true` parameter.
All indices can be opened or closed at once using `_all` as the index name
or specifying patterns that identify them all (e.g. `*`).
Closing all indices can be disabled by setting the `action.disable_close_all_indices`
flag in the config file to `true`.
Identifying indices via wildcards or `_all` can be disabled by setting the
`action.destructive_requires_name` flag in the config file to `true`.
This setting can also be changed via the cluster update settings api.

@ -8,7 +8,7 @@
"parts": {
"index": {
"type" : "list",
"description" : "A comma-separated list of indices to delete; use `_all` or empty string to delete all indices"
"description" : "A comma-separated list of indices to delete; use `_all` or `*` string to delete all indices"
}
},
"params": {

@ -20,8 +20,8 @@
package org.elasticsearch.action.admin.indices.close;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.action.support.master.TransportMasterNodeOperationAction;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
@ -32,6 +32,7 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.MetaDataIndexStateService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.node.settings.NodeSettingsService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -41,15 +42,14 @@ import org.elasticsearch.transport.TransportService;
public class TransportCloseIndexAction extends TransportMasterNodeOperationAction<CloseIndexRequest, CloseIndexResponse> {
private final MetaDataIndexStateService indexStateService;
private final boolean disableCloseAllIndices;
private final DestructiveOperations destructiveOperations;
@Inject
public TransportCloseIndexAction(Settings settings, TransportService transportService, ClusterService clusterService,
ThreadPool threadPool, MetaDataIndexStateService indexStateService) {
ThreadPool threadPool, MetaDataIndexStateService indexStateService, NodeSettingsService nodeSettingsService) {
super(settings, transportService, clusterService, threadPool);
this.indexStateService = indexStateService;
this.disableCloseAllIndices = settings.getAsBoolean("action.disable_close_all_indices", false);
this.destructiveOperations = new DestructiveOperations(logger, settings, nodeSettingsService);
}
@Override
@ -75,17 +75,7 @@ public class TransportCloseIndexAction extends TransportMasterNodeOperationActio
@Override
protected void doExecute(CloseIndexRequest request, ActionListener<CloseIndexResponse> listener) {
ClusterState state = clusterService.state();
String[] indicesOrAliases = request.indices();
request.indices(state.metaData().concreteIndices(indicesOrAliases, request.indicesOptions()));
if (disableCloseAllIndices) {
if (state.metaData().isExplicitAllIndices(indicesOrAliases) ||
state.metaData().isPatternMatchingAllIndices(indicesOrAliases, request.indices())) {
throw new ElasticsearchIllegalArgumentException("closing all indices is disabled");
}
}
destructiveOperations.failDestructive(request.indices());
super.doExecute(request, listener);
}
@ -96,7 +86,7 @@ public class TransportCloseIndexAction extends TransportMasterNodeOperationActio
@Override
protected void masterOperation(final CloseIndexRequest request, final ClusterState state, final ActionListener<CloseIndexResponse> listener) throws ElasticsearchException {
request.indices(state.metaData().concreteIndices(request.indices(), request.indicesOptions()));
CloseIndexClusterStateUpdateRequest updateRequest = new CloseIndexClusterStateUpdateRequest()
.ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout())
.indices(request.indices());

@ -68,7 +68,7 @@ public class DeleteIndexRequest extends MasterNodeOperationRequest<DeleteIndexRe
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (indices == null) {
if (indices == null || indices.length == 0) {
validationException = addValidationError("index / indices is missing", validationException);
}
return validationException;
@ -114,10 +114,7 @@ public class DeleteIndexRequest extends MasterNodeOperationRequest<DeleteIndexRe
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
indices = new String[in.readVInt()];
for (int i = 0; i < indices.length; i++) {
indices[i] = in.readString();
}
indices = in.readStringArray();
indicesOptions = IndicesOptions.readIndicesOptions(in);
timeout = readTimeValue(in);
}
@ -125,14 +122,7 @@ public class DeleteIndexRequest extends MasterNodeOperationRequest<DeleteIndexRe
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
if (indices == null) {
out.writeVInt(0);
} else {
out.writeVInt(indices.length);
for (String index : indices) {
out.writeString(index);
}
}
out.writeStringArray(indices);
indicesOptions.writeIndicesOptions(out);
timeout.writeTo(out);
}

@ -20,9 +20,8 @@
package org.elasticsearch.action.admin.indices.delete;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.mapping.delete.TransportDeleteMappingAction;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.action.support.master.TransportMasterNodeOperationAction;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
@ -32,6 +31,7 @@ import org.elasticsearch.cluster.metadata.MetaDataDeleteIndexService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.CountDown;
import org.elasticsearch.node.settings.NodeSettingsService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -41,19 +41,15 @@ import org.elasticsearch.transport.TransportService;
public class TransportDeleteIndexAction extends TransportMasterNodeOperationAction<DeleteIndexRequest, DeleteIndexResponse> {
private final MetaDataDeleteIndexService deleteIndexService;
private final TransportDeleteMappingAction deleteMappingAction;
private final boolean disableDeleteAllIndices;
private final DestructiveOperations destructiveOperations;
@Inject
public TransportDeleteIndexAction(Settings settings, TransportService transportService, ClusterService clusterService,
ThreadPool threadPool, MetaDataDeleteIndexService deleteIndexService, TransportDeleteMappingAction deleteMappingAction) {
ThreadPool threadPool, MetaDataDeleteIndexService deleteIndexService,
NodeSettingsService nodeSettingsService) {
super(settings, transportService, clusterService, threadPool);
this.deleteIndexService = deleteIndexService;
this.deleteMappingAction = deleteMappingAction;
this.disableDeleteAllIndices = settings.getAsBoolean("action.disable_delete_all_indices", false);
this.destructiveOperations = new DestructiveOperations(logger, settings, nodeSettingsService);
}
@Override
@ -78,17 +74,7 @@ public class TransportDeleteIndexAction extends TransportMasterNodeOperationActi
@Override
protected void doExecute(DeleteIndexRequest request, ActionListener<DeleteIndexResponse> listener) {
ClusterState state = clusterService.state();
String[] indicesOrAliases = request.indices();
request.indices(state.metaData().concreteIndices(request.indices(), request.indicesOptions()));
if (disableDeleteAllIndices) {
if (state.metaData().isAllIndices(indicesOrAliases) ||
state.metaData().isPatternMatchingAllIndices(indicesOrAliases, request.indices())) {
throw new ElasticsearchIllegalArgumentException("deleting all indices is disabled");
}
}
destructiveOperations.failDestructive(request.indices());
super.doExecute(request, listener);
}
@ -99,6 +85,7 @@ public class TransportDeleteIndexAction extends TransportMasterNodeOperationActi
@Override
protected void masterOperation(final DeleteIndexRequest request, final ClusterState state, final ActionListener<DeleteIndexResponse> listener) throws ElasticsearchException {
request.indices(state.metaData().concreteIndices(request.indices(), request.indicesOptions()));
if (request.indices().length == 0) {
listener.onResponse(new DeleteIndexResponse(true));
return;

@ -27,6 +27,7 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
import org.elasticsearch.action.admin.indices.refresh.TransportRefreshAction;
import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse;
import org.elasticsearch.action.deletebyquery.TransportDeleteByQueryAction;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.action.support.QuerySourceBuilder;
import org.elasticsearch.action.support.master.TransportMasterNodeOperationAction;
import org.elasticsearch.client.Requests;
@ -41,6 +42,7 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.node.settings.NodeSettingsService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -53,16 +55,19 @@ public class TransportDeleteMappingAction extends TransportMasterNodeOperationAc
private final TransportFlushAction flushAction;
private final TransportDeleteByQueryAction deleteByQueryAction;
private final TransportRefreshAction refreshAction;
private final DestructiveOperations destructiveOperations;
@Inject
public TransportDeleteMappingAction(Settings settings, TransportService transportService, ClusterService clusterService,
ThreadPool threadPool, MetaDataMappingService metaDataMappingService,
TransportDeleteByQueryAction deleteByQueryAction, TransportRefreshAction refreshAction, TransportFlushAction flushAction) {
TransportDeleteByQueryAction deleteByQueryAction, TransportRefreshAction refreshAction,
TransportFlushAction flushAction, NodeSettingsService nodeSettingsService) {
super(settings, transportService, clusterService, threadPool);
this.metaDataMappingService = metaDataMappingService;
this.deleteByQueryAction = deleteByQueryAction;
this.refreshAction = refreshAction;
this.flushAction = flushAction;
this.destructiveOperations = new DestructiveOperations(logger, settings, nodeSettingsService);
}
@Override
@ -88,7 +93,7 @@ public class TransportDeleteMappingAction extends TransportMasterNodeOperationAc
@Override
protected void doExecute(DeleteMappingRequest request, ActionListener<DeleteMappingResponse> listener) {
request.indices(clusterService.state().metaData().concreteIndices(request.indices(), request.indicesOptions()));
destructiveOperations.failDestructive(request.indices());
super.doExecute(request, listener);
}
@ -99,6 +104,7 @@ public class TransportDeleteMappingAction extends TransportMasterNodeOperationAc
@Override
protected void masterOperation(final DeleteMappingRequest request, final ClusterState state, final ActionListener<DeleteMappingResponse> listener) throws ElasticsearchException {
request.indices(state.metaData().concreteIndices(request.indices(), request.indicesOptions()));
flushAction.execute(Requests.flushRequest(request.indices()), new ActionListener<FlushResponse>() {
@Override
public void onResponse(FlushResponse flushResponse) {

@ -21,6 +21,7 @@ package org.elasticsearch.action.admin.indices.open;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.action.support.master.TransportMasterNodeOperationAction;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
@ -31,6 +32,7 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.MetaDataIndexStateService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.node.settings.NodeSettingsService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -40,12 +42,14 @@ import org.elasticsearch.transport.TransportService;
public class TransportOpenIndexAction extends TransportMasterNodeOperationAction<OpenIndexRequest, OpenIndexResponse> {
private final MetaDataIndexStateService indexStateService;
private final DestructiveOperations destructiveOperations;
@Inject
public TransportOpenIndexAction(Settings settings, TransportService transportService, ClusterService clusterService,
ThreadPool threadPool, MetaDataIndexStateService indexStateService) {
ThreadPool threadPool, MetaDataIndexStateService indexStateService, NodeSettingsService nodeSettingsService) {
super(settings, transportService, clusterService, threadPool);
this.indexStateService = indexStateService;
this.destructiveOperations = new DestructiveOperations(logger, settings, nodeSettingsService);
}
@Override
@ -71,7 +75,7 @@ public class TransportOpenIndexAction extends TransportMasterNodeOperationAction
@Override
protected void doExecute(OpenIndexRequest request, ActionListener<OpenIndexResponse> listener) {
request.indices(clusterService.state().metaData().concreteIndices(request.indices(), request.indicesOptions()));
destructiveOperations.failDestructive(request.indices());
super.doExecute(request, listener);
}
@ -82,7 +86,7 @@ public class TransportOpenIndexAction extends TransportMasterNodeOperationAction
@Override
protected void masterOperation(final OpenIndexRequest request, final ClusterState state, final ActionListener<OpenIndexResponse> listener) throws ElasticsearchException {
request.indices(state.metaData().concreteIndices(request.indices(), request.indicesOptions()));
OpenIndexClusterStateUpdateRequest updateRequest = new OpenIndexClusterStateUpdateRequest()
.ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout())
.indices(request.indices());

@ -20,6 +20,8 @@
package org.elasticsearch.action.deletebyquery;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.action.support.replication.TransportIndicesReplicationOperationAction;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
@ -27,6 +29,7 @@ import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.node.settings.NodeSettingsService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -38,10 +41,20 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
*/
public class TransportDeleteByQueryAction extends TransportIndicesReplicationOperationAction<DeleteByQueryRequest, DeleteByQueryResponse, IndexDeleteByQueryRequest, IndexDeleteByQueryResponse, ShardDeleteByQueryRequest, ShardDeleteByQueryRequest, ShardDeleteByQueryResponse> {
private final DestructiveOperations destructiveOperations;
@Inject
public TransportDeleteByQueryAction(Settings settings, ClusterService clusterService, TransportService transportService,
ThreadPool threadPool, TransportIndexDeleteByQueryAction indexDeleteByQueryAction) {
ThreadPool threadPool, TransportIndexDeleteByQueryAction indexDeleteByQueryAction,
NodeSettingsService nodeSettingsService) {
super(settings, transportService, clusterService, threadPool, indexDeleteByQueryAction);
this.destructiveOperations = new DestructiveOperations(logger, settings, nodeSettingsService);
}
@Override
protected void doExecute(DeleteByQueryRequest request, ActionListener<DeleteByQueryResponse> listener) {
destructiveOperations.failDestructive(request.indices());
super.doExecute(request, listener);
}
@Override
@ -82,7 +95,7 @@ public class TransportDeleteByQueryAction extends TransportIndicesReplicationOpe
}
@Override
protected ClusterBlockException checkRequestBlock(ClusterState state, DeleteByQueryRequest replicationPingRequest, String[] concreteIndices) {
protected ClusterBlockException checkRequestBlock(ClusterState state, DeleteByQueryRequest request, String[] concreteIndices) {
return state.blocks().indicesBlockedException(ClusterBlockLevel.WRITE, concreteIndices);
}

@ -0,0 +1,85 @@
/*
* 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.support;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.node.settings.NodeSettingsService;
/**
* Helper for dealing with destructive operations and wildcard usage.
*/
public final class DestructiveOperations implements NodeSettingsService.Listener {
/**
* Setting which controls whether wildcard usage (*, prefix*, _all) is allowed.
*/
public static final String REQUIRES_NAME = "action.destructive_requires_name";
private final ESLogger logger;
private volatile boolean destructiveRequiresName;
// TODO: Turn into a component that can be reused and wired up into all the transport actions where
// this helper logic is required. Note: also added the logger as argument, otherwise the same log
// statement is printed several times, this can removed once this becomes a component.
public DestructiveOperations(ESLogger logger, Settings settings, NodeSettingsService nodeSettingsService) {
this.logger = logger;
destructiveRequiresName = settings.getAsBoolean(DestructiveOperations.REQUIRES_NAME, false);
nodeSettingsService.addListener(this);
}
/**
* Fail if there is wildcard usage in indices and the named is required for destructive operations.
*/
public void failDestructive(String[] aliasesOrIndices) {
if (!destructiveRequiresName) {
return;
}
if (aliasesOrIndices == null || aliasesOrIndices.length == 0) {
throw new ElasticsearchIllegalArgumentException("Wildcard expressions or all indices are not allowed");
} else if (aliasesOrIndices.length == 1) {
if (hasWildcardUsage(aliasesOrIndices[0])) {
throw new ElasticsearchIllegalArgumentException("Wildcard expressions or all indices are not allowed");
}
} else {
for (String aliasesOrIndex : aliasesOrIndices) {
if (hasWildcardUsage(aliasesOrIndex)) {
throw new ElasticsearchIllegalArgumentException("Wildcard expressions or all indices are not allowed");
}
}
}
}
@Override
public void onRefreshSettings(Settings settings) {
boolean newValue = settings.getAsBoolean("action.destructive_requires_name", destructiveRequiresName);
if (destructiveRequiresName != newValue) {
logger.info("updating [action.operate_all_indices] from [{}] to [{}]", destructiveRequiresName, newValue);
this.destructiveRequiresName = newValue;
}
}
private static boolean hasWildcardUsage(String aliasOrIndex) {
return "_all".equals(aliasOrIndex) || aliasOrIndex.indexOf('*') != -1;
}
}

@ -19,6 +19,7 @@
package org.elasticsearch.cluster.settings;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.cluster.InternalClusterInfoService;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
@ -79,6 +80,7 @@ public class ClusterDynamicSettingsModule extends AbstractModule {
clusterDynamicSettings.addDynamicSetting(SnapshotInProgressAllocationDecider.CLUSTER_ROUTING_ALLOCATION_SNAPSHOT_RELOCATION_ENABLED);
clusterDynamicSettings.addDynamicSetting(InternalCircuitBreakerService.CIRCUIT_BREAKER_MAX_BYTES_SETTING, Validator.BYTES_SIZE);
clusterDynamicSettings.addDynamicSetting(InternalCircuitBreakerService.CIRCUIT_BREAKER_OVERHEAD_SETTING, Validator.NON_NEGATIVE_DOUBLE);
clusterDynamicSettings.addDynamicSetting(DestructiveOperations.REQUIRES_NAME);
}
public void addDynamicSettings(String... settings) {

@ -22,6 +22,7 @@ import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
import org.elasticsearch.action.admin.indices.close.CloseIndexResponse;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
@ -30,70 +31,65 @@ import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
import org.junit.Test;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
@ClusterScope(scope=Scope.SUITE, numNodes=2)
@ClusterScope(scope=Scope.TEST, numNodes=2)
public class CloseIndexDisableCloseAllTests extends ElasticsearchIntegrationTest {
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder().put("action.disable_close_all_indices", true).put(super.nodeSettings(nodeOrdinal)).build();
}
@Test(expected = ElasticsearchIllegalArgumentException.class)
public void testCloseAllExplicitly() {
createIndex("test1", "test2", "test3");
ClusterHealthResponse healthResponse = client().admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
assertThat(healthResponse.isTimedOut(), equalTo(false));
client().admin().indices().prepareClose("_all").execute().actionGet();
}
@Test(expected = ElasticsearchIllegalArgumentException.class)
public void testCloseAllWildcard() {
createIndex("test1", "test2", "test3");
ClusterHealthResponse healthResponse = client().admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
assertThat(healthResponse.isTimedOut(), equalTo(false));
client().admin().indices().prepareClose("*").execute().actionGet();
}
@Test(expected = ElasticsearchIllegalArgumentException.class)
public void testCloseAllWildcard2() {
createIndex("test1", "test2", "test3");
ClusterHealthResponse healthResponse = client().admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
assertThat(healthResponse.isTimedOut(), equalTo(false));
client().admin().indices().prepareClose("test*").execute().actionGet();
}
@Test
public void testCloseWildcardNonMatchingAll() {
// Combined multiple tests into one, because cluster scope is test.
// The cluster scope is test b/c we can't clear cluster settings.
public void testCloseAllRequiresName() {
Settings clusterSettings = ImmutableSettings.builder()
.put(DestructiveOperations.REQUIRES_NAME, true)
.build();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(clusterSettings));
createIndex("test1", "test2", "test3");
ClusterHealthResponse healthResponse = client().admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
assertThat(healthResponse.isTimedOut(), equalTo(false));
CloseIndexResponse closeIndexResponse = client().admin().indices().prepareClose("*", "-test1").execute().actionGet();
// Close all explicitly
try {
client().admin().indices().prepareClose("_all").execute().actionGet();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {
}
// Close all wildcard
try {
client().admin().indices().prepareClose("*").execute().actionGet();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {
}
// Close all wildcard
try {
client().admin().indices().prepareClose("test*").execute().actionGet();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {
}
// Close all wildcard
try {
client().admin().indices().prepareClose("*", "-test1").execute().actionGet();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {
}
// Close all wildcard
try {
client().admin().indices().prepareClose("*", "-test1", "+test1").execute().actionGet();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {
}
CloseIndexResponse closeIndexResponse = client().admin().indices().prepareClose("test3", "test2").execute().actionGet();
assertThat(closeIndexResponse.isAcknowledged(), equalTo(true));
assertIndexIsClosed("test2", "test3");
}
@Test(expected = ElasticsearchIllegalArgumentException.class)
public void testCloseWildcardMatchingAll() {
createIndex("test1", "test2", "test3");
ClusterHealthResponse healthResponse = client().admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
assertThat(healthResponse.isTimedOut(), equalTo(false));
client().admin().indices().prepareClose("*", "-test1", "+test1").execute().actionGet();
}
@Test
public void testCloseWildcardNonMatchingAll2() {
createIndex( "test1", "test2", "test3", "a");
ClusterHealthResponse healthResponse = client().admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
assertThat(healthResponse.isTimedOut(), equalTo(false));
CloseIndexResponse closeIndexResponse = client().admin().indices().prepareClose("test*").execute().actionGet();
assertThat(closeIndexResponse.isAcknowledged(), equalTo(true));
assertIndexIsClosed("test1", "test2", "test3");
}
private void assertIndexIsClosed(String... indices) {
checkIndexState(IndexMetaData.State.CLOSE, indices);
}

@ -0,0 +1,179 @@
/*
* 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.operateAllIndices;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.equalTo;
/**
*/
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.TEST)
public class DestructiveOperationsIntegrationTests extends ElasticsearchIntegrationTest {
@Test
// One test for test performance, since cluster scope is test
// The cluster scope is test b/c we can't clear cluster settings.
public void testDestructiveOperations() throws Exception {
Settings settings = ImmutableSettings.builder()
.put(DestructiveOperations.REQUIRES_NAME, true)
.build();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
assertAcked(client().admin().indices().prepareCreate("index1").get());
assertAcked(client().admin().indices().prepareCreate("1index").get());
// Should succeed, since no wildcards
assertAcked(client().admin().indices().prepareDelete("1index").get());
try {
// should fail since index1 is the only index.
client().admin().indices().prepareDelete("i*").get();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {}
try {
client().admin().indices().prepareDelete("_all").get();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {}
settings = ImmutableSettings.builder()
.put(DestructiveOperations.REQUIRES_NAME, false)
.build();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
assertAcked(client().admin().indices().prepareDelete("_all").get());
assertThat(client().admin().indices().prepareExists("_all").get().isExists(), equalTo(false));
// end delete index:
// close index:
settings = ImmutableSettings.builder()
.put(DestructiveOperations.REQUIRES_NAME, true)
.build();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
assertAcked(client().admin().indices().prepareCreate("index1").get());
assertAcked(client().admin().indices().prepareCreate("1index").get());
// Should succeed, since no wildcards
assertAcked(client().admin().indices().prepareClose("1index").get());
try {
client().admin().indices().prepareClose("_all").get();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {}
try {
assertAcked(client().admin().indices().prepareOpen("_all").get());
assert false;
} catch (ElasticsearchIllegalArgumentException e) {
}
try {
client().admin().indices().prepareClose("*").get();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {}
try {
assertAcked(client().admin().indices().prepareOpen("*").get());
assert false;
} catch (ElasticsearchIllegalArgumentException e) {
}
settings = ImmutableSettings.builder()
.put(DestructiveOperations.REQUIRES_NAME, false)
.build();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
assertAcked(client().admin().indices().prepareClose("_all").get());
assertAcked(client().admin().indices().prepareOpen("_all").get());
// end close index:
client().admin().indices().prepareDelete("_all").get();
// delete_by_query:
settings = ImmutableSettings.builder()
.put(DestructiveOperations.REQUIRES_NAME, true)
.build();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
assertAcked(client().admin().indices().prepareCreate("index1").get());
assertAcked(client().admin().indices().prepareCreate("1index").get());
// Should succeed, since no wildcards
client().prepareDeleteByQuery("1index").setQuery(QueryBuilders.matchAllQuery()).get();
try {
client().prepareDeleteByQuery("_all").setQuery(QueryBuilders.matchAllQuery()).get();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {}
try {
client().prepareDeleteByQuery().setQuery(QueryBuilders.matchAllQuery()).get();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {}
settings = ImmutableSettings.builder()
.put(DestructiveOperations.REQUIRES_NAME, false)
.build();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
client().prepareDeleteByQuery().setQuery(QueryBuilders.matchAllQuery()).get();
client().prepareDeleteByQuery("_all").setQuery(QueryBuilders.matchAllQuery()).get();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
client().prepareDeleteByQuery().setQuery(QueryBuilders.matchAllQuery()).get();
// end delete_by_query:
client().admin().indices().prepareDelete("_all").get();
// delete mapping:
settings = ImmutableSettings.builder()
.put(DestructiveOperations.REQUIRES_NAME, true)
.build();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
assertAcked(client().admin().indices().prepareCreate("index1").addMapping("1", "field1", "type=string").get());
assertAcked(client().admin().indices().prepareCreate("1index").addMapping("1", "field1", "type=string").get());
// Should succeed, since no wildcards
client().admin().indices().prepareDeleteMapping("1index").setType("1").get();
try {
client().admin().indices().prepareDeleteMapping("_all").setType("1").get();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {}
try {
client().admin().indices().prepareDeleteMapping().setType("1").get();
assert false;
} catch (ElasticsearchIllegalArgumentException e) {}
settings = ImmutableSettings.builder()
.put(DestructiveOperations.REQUIRES_NAME, false)
.build();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
client().admin().indices().preparePutMapping("1index").setType("1").setSource("field1", "type=string").get();
client().admin().indices().prepareDeleteMapping().setType("1").get();
client().admin().indices().preparePutMapping("1index").setType("1").setSource("field1", "type=string").get();
client().admin().indices().prepareDeleteMapping("_all").setType("1").get();
}
}

@ -1504,7 +1504,7 @@ public class PercolatorTests extends ElasticsearchIntegrationTest {
@Test
public void testDeletePercolatorType() throws Exception {
DeleteIndexResponse deleteIndexResponse = client().admin().indices().prepareDelete().execute().actionGet();
DeleteIndexResponse deleteIndexResponse = client().admin().indices().prepareDelete("_all").execute().actionGet();
assertThat("Delete Index failed - not acked", deleteIndexResponse.isAcknowledged(), equalTo(true));
ensureGreen();

@ -196,7 +196,7 @@ public class RecoveryPercolatorTests extends ElasticsearchIntegrationTest {
cluster().startNode(settings);
cluster().startNode(settings);
client().admin().indices().prepareDelete().execute().actionGet();
client().admin().indices().prepareDelete("_all").execute().actionGet();
ensureGreen();
client().admin().indices().prepareCreate("test")
@ -265,7 +265,7 @@ public class RecoveryPercolatorTests extends ElasticsearchIntegrationTest {
logger.info("--> Adding 3th node");
cluster().startNode(settingsBuilder().put("node.stay", true));
client().admin().indices().prepareDelete().execute().actionGet();
client().admin().indices().prepareDelete("_all").execute().actionGet();
ensureGreen();
client().admin().indices().prepareCreate("test")

@ -55,7 +55,7 @@ public class TTLPercolatorTests extends ElasticsearchIntegrationTest {
@Test
public void testPercolatingWithTimeToLive() throws Exception {
Client client = client();
client.admin().indices().prepareDelete().execute().actionGet();
client.admin().indices().prepareDelete("_all").execute().actionGet();
ensureGreen();
String precolatorMapping = XContentFactory.jsonBuilder().startObject().startObject(PercolatorService.TYPE_NAME)

@ -610,7 +610,7 @@ public class CompletionSuggestSearchTests extends ElasticsearchIntegrationTest {
public void testThatStatsAreWorking() throws Exception {
String otherField = "testOtherField";
client().admin().indices().prepareDelete().get();
client().admin().indices().prepareDelete("_all").get();
client().admin().indices().prepareCreate(INDEX)
.setSettings(createDefaultSettings())
.get();

@ -18,14 +18,17 @@
*/
package org.elasticsearch.test;
import com.carrotsearch.hppc.ObjectArrayList;
import com.google.common.base.Joiner;
import org.apache.lucene.util.AbstractRandomizedTest;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.admin.indices.flush.FlushResponse;
@ -42,6 +45,7 @@ import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.Priority;
import org.elasticsearch.common.collect.Tuple;
@ -182,7 +186,7 @@ public abstract class ElasticsearchIntegrationTest extends ElasticsearchTestCase
assert false : "Unknown Scope: [" + currentClusterScope + "]";
}
currentCluster.beforeTest(getRandom(), getPerTestTransportClientRatio());
wipeIndices();
wipeIndices("_all");
wipeTemplates();
randomIndexTemplate();
logger.info("[{}#{}]: before test", getTestClass().getSimpleName(), getTestName());
@ -230,7 +234,7 @@ public abstract class ElasticsearchIntegrationTest extends ElasticsearchTestCase
.transientSettings().getAsMap().size(), equalTo(0));
}
wipeIndices(); // wipe after to make sure we fail in the test that
wipeIndices("_all"); // wipe after to make sure we fail in the test that
// didn't ack the delete
wipeTemplates();
ensureAllSearchersClosed();
@ -319,12 +323,26 @@ public abstract class ElasticsearchIntegrationTest extends ElasticsearchTestCase
* Deletes the given indices from the tests cluster. If no index name is passed to this method
* all indices are removed.
*/
public static void wipeIndices(String... names) {
public static void wipeIndices(String... indices) {
assert indices != null && indices.length > 0;
if (cluster().size() > 0) {
try {
assertAcked(client().admin().indices().prepareDelete(names));
assertAcked(client().admin().indices().prepareDelete(indices));
} catch (IndexMissingException e) {
// ignore
} catch (ElasticsearchIllegalArgumentException e) {
// Happens if `action.destructive_requires_name` is set to true
// which is the case in the CloseIndexDisableCloseAllTests
if ("_all".equals(indices[0])) {
ClusterStateResponse clusterStateResponse = client().admin().cluster().prepareState().execute().actionGet();
ObjectArrayList<String> concreteIndices = new ObjectArrayList<String>();
for (IndexMetaData indexMetaData : clusterStateResponse.getState().metaData()) {
concreteIndices.add(indexMetaData.getIndex());
}
if (!concreteIndices.isEmpty()) {
assertAcked(client().admin().indices().prepareDelete(concreteIndices.toArray(String.class)));
}
}
}
}
}
@ -363,7 +381,7 @@ public abstract class ElasticsearchIntegrationTest extends ElasticsearchTestCase
created.add(name);
success = true;
} finally {
if (!success) {
if (!success && !created.isEmpty()) {
wipeIndices(created.toArray(new String[created.size()]));
}
}