Skip to content

Commit 4f223be

Browse files
committed
Add RemoveBlock transport layer and client APIs
- Add TransportRemoveIndexBlockAction and RemoveIndexBlockRequestBuilder - Add prepareRemoveBlock() and removeBlock() methods to IndicesAdminClient - Register RemoveBlock actions in ActionModule - Add integration tests and security permissions
1 parent 20e57fe commit 4f223be

File tree

7 files changed

+369
-0
lines changed

7 files changed

+369
-0
lines changed

server/src/internalClusterTest/java/org/elasticsearch/blocks/SimpleBlocksIT.java

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.action.DocWriteResponse;
1515
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
1616
import org.elasticsearch.action.admin.indices.readonly.AddIndexBlockResponse;
17+
import org.elasticsearch.action.admin.indices.readonly.RemoveIndexBlockResponse;
1718
import org.elasticsearch.action.index.IndexRequestBuilder;
1819
import org.elasticsearch.action.support.ActiveShardCount;
1920
import org.elasticsearch.action.support.PlainActionFuture;
@@ -549,6 +550,143 @@ public static void disableIndexBlock(String index, APIBlock block) {
549550
disableIndexBlock(index, block.settingName());
550551
}
551552

553+
public void testRemoveBlockToMissingIndex() {
554+
IndexNotFoundException e = expectThrows(
555+
IndexNotFoundException.class,
556+
indicesAdmin().prepareRemoveBlock(randomAddableBlock(), "test")
557+
);
558+
assertThat(e.getMessage(), is("no such index [test]"));
559+
}
560+
561+
public void testRemoveBlockToOneMissingIndex() {
562+
createIndex("test1");
563+
final IndexNotFoundException e = expectThrows(
564+
IndexNotFoundException.class,
565+
indicesAdmin().prepareRemoveBlock(randomAddableBlock(), "test1", "test2")
566+
);
567+
assertThat(e.getMessage(), is("no such index [test2]"));
568+
}
569+
570+
public void testRemoveBlockNoIndex() {
571+
final ActionRequestValidationException e = expectThrows(
572+
ActionRequestValidationException.class,
573+
indicesAdmin().prepareRemoveBlock(randomAddableBlock())
574+
);
575+
assertThat(e.getMessage(), containsString("index is missing"));
576+
}
577+
578+
public void testRemoveBlockNullIndex() {
579+
expectThrows(NullPointerException.class, () -> indicesAdmin().prepareRemoveBlock(randomAddableBlock(), (String[]) null));
580+
}
581+
582+
public void testCannotRemoveReadOnlyAllowDeleteBlock() {
583+
createIndex("test1");
584+
final ActionRequestValidationException e = expectThrows(
585+
ActionRequestValidationException.class,
586+
indicesAdmin().prepareRemoveBlock(APIBlock.READ_ONLY_ALLOW_DELETE, "test1")
587+
);
588+
assertThat(e.getMessage(), containsString("read_only_allow_delete block is for internal use only"));
589+
}
590+
591+
public void testRemoveIndexBlock() throws Exception {
592+
final String indexName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT);
593+
createIndex(indexName);
594+
ensureGreen(indexName);
595+
596+
final int nbDocs = randomIntBetween(0, 50);
597+
indexRandom(
598+
randomBoolean(),
599+
false,
600+
randomBoolean(),
601+
IntStream.range(0, nbDocs).mapToObj(i -> prepareIndex(indexName).setId(String.valueOf(i)).setSource("num", i)).collect(toList())
602+
);
603+
604+
final APIBlock block = randomAddableBlock();
605+
try {
606+
// First add the block
607+
AddIndexBlockResponse addResponse = indicesAdmin().prepareAddBlock(block, indexName).get();
608+
assertTrue(
609+
"Add block [" + block + "] to index [" + indexName + "] not acknowledged: " + addResponse,
610+
addResponse.isAcknowledged()
611+
);
612+
assertIndexHasBlock(block, indexName);
613+
614+
// Then remove the block
615+
RemoveIndexBlockResponse removeResponse = indicesAdmin().prepareRemoveBlock(block, indexName).get();
616+
assertTrue(
617+
"Remove block [" + block + "] from index [" + indexName + "] not acknowledged: " + removeResponse,
618+
removeResponse.isAcknowledged()
619+
);
620+
assertIndexDoesNotHaveBlock(block, indexName);
621+
} finally {
622+
// Ensure cleanup
623+
disableIndexBlock(indexName, block);
624+
}
625+
626+
indicesAdmin().prepareRefresh(indexName).get();
627+
assertHitCount(prepareSearch(indexName).setSize(0), nbDocs);
628+
}
629+
630+
public void testRemoveBlockIdempotent() throws Exception {
631+
final String indexName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT);
632+
createIndex(indexName);
633+
ensureGreen(indexName);
634+
635+
final APIBlock block = randomAddableBlock();
636+
try {
637+
// First add the block
638+
assertAcked(indicesAdmin().prepareAddBlock(block, indexName));
639+
assertIndexHasBlock(block, indexName);
640+
641+
// Remove the block
642+
assertAcked(indicesAdmin().prepareRemoveBlock(block, indexName));
643+
assertIndexDoesNotHaveBlock(block, indexName);
644+
645+
// Second remove should be acked too (idempotent behavior)
646+
assertAcked(indicesAdmin().prepareRemoveBlock(block, indexName));
647+
assertIndexDoesNotHaveBlock(block, indexName);
648+
} finally {
649+
disableIndexBlock(indexName, block);
650+
}
651+
}
652+
653+
public void testRemoveBlockOneMissingIndexIgnoreMissing() throws Exception {
654+
createIndex("test1");
655+
final APIBlock block = randomAddableBlock();
656+
try {
657+
// First add the block to test1
658+
assertAcked(indicesAdmin().prepareAddBlock(block, "test1"));
659+
assertIndexHasBlock(block, "test1");
660+
661+
// Remove from both test1 and test2 (missing), with lenient options
662+
assertBusy(
663+
() -> assertAcked(indicesAdmin().prepareRemoveBlock(block, "test1", "test2").setIndicesOptions(lenientExpandOpen()))
664+
);
665+
assertIndexDoesNotHaveBlock(block, "test1");
666+
} finally {
667+
disableIndexBlock("test1", block);
668+
}
669+
}
670+
671+
static void assertIndexDoesNotHaveBlock(APIBlock block, final String... indices) {
672+
final ClusterState clusterState = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState();
673+
final ProjectId projectId = Metadata.DEFAULT_PROJECT_ID;
674+
for (String index : indices) {
675+
final IndexMetadata indexMetadata = clusterState.metadata().getProject(projectId).indices().get(index);
676+
final Settings indexSettings = indexMetadata.getSettings();
677+
assertThat(
678+
"Index " + index + " should not have block setting [" + block.settingName() + "]",
679+
indexSettings.getAsBoolean(block.settingName(), false),
680+
is(false)
681+
);
682+
assertThat(
683+
"Index " + index + " should not have block [" + block.getBlock() + "]",
684+
clusterState.blocks().hasIndexBlock(projectId, index, block.getBlock()),
685+
is(false)
686+
);
687+
}
688+
}
689+
552690
/**
553691
* The read-only-allow-delete block cannot be added via the add index block API; this method chooses randomly from the values that
554692
* the add index block API does support.

server/src/internalClusterTest/java/org/elasticsearch/operateAllIndices/DestructiveOperationsIT.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,40 @@ public void testAddIndexBlockDefaultBehaviour() throws Exception {
155155
assertTrue("write block is set on index1", state.getBlocks().hasIndexBlock("index1", IndexMetadata.INDEX_WRITE_BLOCK));
156156
assertTrue("write block is set on 1index", state.getBlocks().hasIndexBlock("1index", IndexMetadata.INDEX_WRITE_BLOCK));
157157
}
158+
159+
public void testRemoveIndexBlockIsRejected() throws Exception {
160+
updateClusterSettings(Settings.builder().put(DestructiveOperations.REQUIRES_NAME_SETTING.getKey(), true));
161+
162+
createIndex("index1", "1index");
163+
// Add blocks first
164+
assertAcked(indicesAdmin().prepareAddBlock(WRITE, "index1", "1index").get());
165+
166+
// Should succeed, since no wildcards
167+
assertAcked(indicesAdmin().prepareRemoveBlock(WRITE, "1index").get());
168+
// Special "match none" pattern succeeds, since non-destructive
169+
assertAcked(indicesAdmin().prepareRemoveBlock(WRITE, "*", "-*").get());
170+
171+
expectThrows(IllegalArgumentException.class, indicesAdmin().prepareRemoveBlock(WRITE, "i*"));
172+
expectThrows(IllegalArgumentException.class, indicesAdmin().prepareRemoveBlock(WRITE, "_all"));
173+
}
174+
175+
public void testRemoveIndexBlockDefaultBehaviour() throws Exception {
176+
if (randomBoolean()) {
177+
updateClusterSettings(Settings.builder().put(DestructiveOperations.REQUIRES_NAME_SETTING.getKey(), false));
178+
}
179+
180+
createIndex("index1", "1index");
181+
// Add blocks first
182+
assertAcked(indicesAdmin().prepareAddBlock(WRITE, "index1", "1index").get());
183+
184+
if (randomBoolean()) {
185+
assertAcked(indicesAdmin().prepareRemoveBlock(WRITE, "_all").get());
186+
} else {
187+
assertAcked(indicesAdmin().prepareRemoveBlock(WRITE, "*").get());
188+
}
189+
190+
ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState();
191+
assertFalse("write block is removed from index1", state.getBlocks().hasIndexBlock("index1", IndexMetadata.INDEX_WRITE_BLOCK));
192+
assertFalse("write block is removed from 1index", state.getBlocks().hasIndexBlock("1index", IndexMetadata.INDEX_WRITE_BLOCK));
193+
}
158194
}

server/src/main/java/org/elasticsearch/action/ActionModule.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
import org.elasticsearch.action.admin.indices.open.OpenIndexAction;
115115
import org.elasticsearch.action.admin.indices.open.TransportOpenIndexAction;
116116
import org.elasticsearch.action.admin.indices.readonly.TransportAddIndexBlockAction;
117+
import org.elasticsearch.action.admin.indices.readonly.TransportRemoveIndexBlockAction;
117118
import org.elasticsearch.action.admin.indices.readonly.TransportVerifyShardIndexBlockAction;
118119
import org.elasticsearch.action.admin.indices.recovery.RecoveryAction;
119120
import org.elasticsearch.action.admin.indices.recovery.TransportRecoveryAction;
@@ -343,6 +344,7 @@
343344
import org.elasticsearch.rest.action.admin.indices.RestRecoveryAction;
344345
import org.elasticsearch.rest.action.admin.indices.RestRefreshAction;
345346
import org.elasticsearch.rest.action.admin.indices.RestReloadAnalyzersAction;
347+
import org.elasticsearch.rest.action.admin.indices.RestRemoveIndexBlockAction;
346348
import org.elasticsearch.rest.action.admin.indices.RestResizeHandler;
347349
import org.elasticsearch.rest.action.admin.indices.RestResolveClusterAction;
348350
import org.elasticsearch.rest.action.admin.indices.RestResolveIndexAction;
@@ -687,6 +689,7 @@ public <Request extends ActionRequest, Response extends ActionResponse> void reg
687689
actions.register(OpenIndexAction.INSTANCE, TransportOpenIndexAction.class);
688690
actions.register(TransportCloseIndexAction.TYPE, TransportCloseIndexAction.class);
689691
actions.register(TransportAddIndexBlockAction.TYPE, TransportAddIndexBlockAction.class);
692+
actions.register(TransportRemoveIndexBlockAction.TYPE, TransportRemoveIndexBlockAction.class);
690693
actions.register(GetMappingsAction.INSTANCE, TransportGetMappingsAction.class);
691694
actions.register(GetFieldMappingsAction.INSTANCE, TransportGetFieldMappingsAction.class);
692695
actions.register(TransportGetFieldMappingsIndexAction.TYPE, TransportGetFieldMappingsIndexAction.class);
@@ -889,6 +892,7 @@ public void initRestHandlers(Supplier<DiscoveryNodes> nodesInCluster, Predicate<
889892
registerHandler.accept(new RestCloseIndexAction());
890893
registerHandler.accept(new RestOpenIndexAction());
891894
registerHandler.accept(new RestAddIndexBlockAction());
895+
registerHandler.accept(new RestRemoveIndexBlockAction());
892896
registerHandler.accept(new RestGetHealthAction());
893897
registerHandler.accept(new RestPrevalidateNodeRemovalAction());
894898

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.action.admin.indices.readonly;
11+
12+
import org.elasticsearch.action.support.IndicesOptions;
13+
import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
14+
import org.elasticsearch.client.internal.ElasticsearchClient;
15+
import org.elasticsearch.cluster.metadata.IndexMetadata.APIBlock;
16+
17+
/**
18+
* Builder for remove index block request
19+
*/
20+
public class RemoveIndexBlockRequestBuilder extends AcknowledgedRequestBuilder<
21+
RemoveIndexBlockRequest,
22+
RemoveIndexBlockResponse,
23+
RemoveIndexBlockRequestBuilder> {
24+
25+
public RemoveIndexBlockRequestBuilder(ElasticsearchClient client, APIBlock block, String... indices) {
26+
super(client, TransportRemoveIndexBlockAction.TYPE, new RemoveIndexBlockRequest(block, indices));
27+
}
28+
29+
/**
30+
* Sets the indices to be unblocked
31+
*
32+
* @param indices the indices to be unblocked
33+
* @return the request itself
34+
*/
35+
public RemoveIndexBlockRequestBuilder setIndices(String... indices) {
36+
request.indices(indices);
37+
return this;
38+
}
39+
40+
/**
41+
* Specifies what type of requested indices to ignore and how to deal with wildcard expressions.
42+
* For example indices that don't exist.
43+
*
44+
* @param indicesOptions the desired behaviour regarding indices to ignore and wildcard indices expressions
45+
* @return the request itself
46+
*/
47+
public RemoveIndexBlockRequestBuilder setIndicesOptions(IndicesOptions indicesOptions) {
48+
request.indicesOptions(indicesOptions);
49+
return this;
50+
}
51+
}

0 commit comments

Comments
 (0)