Skip to content

Commit

Permalink
Forbid read-only-allow-delete block in blocks API
Browse files Browse the repository at this point in the history
The read-only-allow-delete block is not really under the user's control
since Elasticsearch adds/removes it automatically. This commit removes
support for it from the new API for adding blocks to indices that was
introduced in elastic#58094.
  • Loading branch information
DaveCTurner committed Jun 30, 2020
1 parent 7816c10 commit 1cc5bc1
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 28 deletions.
33 changes: 18 additions & 15 deletions docs/reference/index-modules/blocks.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,6 @@ index:
Set to `true` to make the index and index metadata read only, `false` to
allow writes and metadata changes.

`index.blocks.read_only_allow_delete`::

Similar to `index.blocks.read_only`, but also allows deleting the index to
make more resources available. The <<disk-based-shard-allocation,disk-based shard
allocator>> may add and remove this block automatically.

Deleting documents from an index to release resources - rather than deleting the index itself - can increase the index size over time. When `index.blocks.read_only_allow_delete` is set to `true`, deleting documents is not permitted. However, deleting the index itself releases the read-only index block and makes resources available almost immediately.

IMPORTANT: {es} adds and removes the read-only index block automatically when the disk utilization falls below the high watermark, controlled by <<cluster-routing-flood_stage,cluster.routing.allocation.disk.watermark.flood_stage>>.

`index.blocks.read`::

Set to `true` to disable read operations against the index.
Expand All @@ -46,6 +36,24 @@ IMPORTANT: {es} adds and removes the read-only index block automatically when th

Set to `true` to disable index metadata reads and writes.

`index.blocks.read_only_allow_delete`::

Similar to `index.blocks.read_only`, but also allows deleting the index to
make more resources available. The <<disk-based-shard-allocation,disk-based shard
allocator>> adds and removes this block automatically.

Deleting documents from an index to release resources - rather than deleting
the index itself - can increase the index size over time. When
`index.blocks.read_only_allow_delete` is set to `true`, deleting documents is
not permitted. However, deleting the index itself releases the read-only index
block and makes resources available almost immediately.

IMPORTANT: {es} adds the read-only-allow-delete index block automatically when
disk utilisation exceeds the <<cluster-routing-flood_stage,flood-stage
watermark>> and removes it again when disk utilisation is below the
<<cluster-routing-high,high watermark>>. You should not apply this block
yourself.

[discrete]
[[add-index-block]]
=== Add index block API
Expand Down Expand Up @@ -94,11 +102,6 @@ Disable read operations.
`read_only`::
Disable write operations and metadata changes.
`read_only_allow_delete`::
Disable write operations and metadata changes.
Document deletion is disabled.
However, index deletion is still allowed.
`write`::
Disable write operations. However, metadata changes are still allowed.
====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
"block":{
"type":"string",
"description":"The block to add (one of read, write, read_only, metadata, read_only_allow_delete)"
"description":"The block to add (one of read, write, read_only or metadata)"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.readonly.AddIndexBlockRequestBuilder;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequestBuilder;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.index.IndexResponse;
Expand Down Expand Up @@ -177,6 +178,9 @@ public void testAddBlocksWhileExistingBlocks() {
ensureGreen("test");

for (APIBlock otherBlock : APIBlock.values()) {
if (otherBlock == APIBlock.READ_ONLY_ALLOW_DELETE) {
continue;
}

for (APIBlock block : Arrays.asList(APIBlock.READ, APIBlock.WRITE)) {
try {
Expand Down Expand Up @@ -216,20 +220,20 @@ public void testAddBlocksWhileExistingBlocks() {

public void testAddBlockToMissingIndex() {
IndexNotFoundException e = expectThrows(IndexNotFoundException.class, () -> client().admin().indices()
.prepareAddBlock(randomFrom(APIBlock.values()),"test").get());
.prepareAddBlock(randomAddableBlock(), "test").get());
assertThat(e.getMessage(), is("no such index [test]"));
}

public void testAddBlockToOneMissingIndex() {
createIndex("test1");
final IndexNotFoundException e = expectThrows(IndexNotFoundException.class,
() -> client().admin().indices().prepareAddBlock(randomFrom(APIBlock.values()),"test1", "test2").get());
() -> client().admin().indices().prepareAddBlock(randomAddableBlock(), "test1", "test2").get());
assertThat(e.getMessage(), is("no such index [test2]"));
}

public void testCloseOneMissingIndexIgnoreMissing() throws Exception {
createIndex("test1");
final APIBlock block = randomFrom(APIBlock.values());
final APIBlock block = randomAddableBlock();
try {
assertBusy(() -> assertAcked(client().admin().indices().prepareAddBlock(block, "test1", "test2")
.setIndicesOptions(lenientExpandOpen())));
Expand All @@ -241,13 +245,20 @@ public void testCloseOneMissingIndexIgnoreMissing() throws Exception {

public void testAddBlockNoIndex() {
final ActionRequestValidationException e = expectThrows(ActionRequestValidationException.class,
() -> client().admin().indices().prepareAddBlock(randomFrom(APIBlock.values())).get());
() -> client().admin().indices().prepareAddBlock(randomAddableBlock()).get());
assertThat(e.getMessage(), containsString("index is missing"));
}

public void testAddBlockNullIndex() {
expectThrows(NullPointerException.class,
() -> client().admin().indices().prepareAddBlock(randomFrom(APIBlock.values()), (String[])null));
() -> client().admin().indices().prepareAddBlock(randomAddableBlock(), (String[])null));
}

public void testCannotAddReadOnlyAllowDeleteBlock() {
createIndex("test1");
final AddIndexBlockRequestBuilder request = client().admin().indices().prepareAddBlock(APIBlock.READ_ONLY_ALLOW_DELETE, "test1");
final ActionRequestValidationException e = expectThrows(ActionRequestValidationException.class, request::get);
assertThat(e.getMessage(), containsString("read_only_allow_delete block is for internal use only"));
}

public void testAddIndexBlock() throws Exception {
Expand All @@ -258,7 +269,7 @@ public void testAddIndexBlock() throws Exception {
indexRandom(randomBoolean(), false, randomBoolean(), IntStream.range(0, nbDocs)
.mapToObj(i -> client().prepareIndex(indexName).setId(String.valueOf(i)).setSource("num", i)).collect(toList()));

final APIBlock block = randomFrom(APIBlock.values());
final APIBlock block = randomAddableBlock();
try {
assertAcked(client().admin().indices().prepareAddBlock(block, indexName));
assertIndexHasBlock(block, indexName);
Expand All @@ -278,7 +289,7 @@ public void testSameBlockTwice() throws Exception {
indexRandom(randomBoolean(), false, randomBoolean(), IntStream.range(0, randomIntBetween(1, 10))
.mapToObj(i -> client().prepareIndex(indexName).setId(String.valueOf(i)).setSource("num", i)).collect(toList()));
}
final APIBlock block = randomFrom(APIBlock.values());
final APIBlock block = randomAddableBlock();
try {
assertAcked(client().admin().indices().prepareAddBlock(block, indexName));
assertIndexHasBlock(block, indexName);
Expand All @@ -300,7 +311,7 @@ public void testAddBlockToUnassignedIndex() throws Exception {
assertThat(clusterState.metadata().indices().get(indexName).getState(), is(IndexMetadata.State.OPEN));
assertThat(clusterState.routingTable().allShards().stream().allMatch(ShardRouting::unassigned), is(true));

final APIBlock block = randomFrom(APIBlock.values());
final APIBlock block = randomAddableBlock();
try {
assertAcked(client().admin().indices().prepareAddBlock(block, indexName));
assertIndexHasBlock(block, indexName);
Expand All @@ -321,7 +332,7 @@ public void testConcurrentAddBlock() throws InterruptedException {
final CountDownLatch startClosing = new CountDownLatch(1);
final Thread[] threads = new Thread[randomIntBetween(2, 5)];

final APIBlock block = randomFrom(APIBlock.values());
final APIBlock block = randomAddableBlock();

try {
for (int i = 0; i < threads.length; i++) {
Expand Down Expand Up @@ -356,7 +367,7 @@ public void testAddBlockWhileIndexingDocuments() throws Exception {
final String indexName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT);
createIndex(indexName);

final APIBlock block = randomFrom(APIBlock.values());
final APIBlock block = randomAddableBlock();

int nbDocs = 0;

Expand Down Expand Up @@ -400,7 +411,7 @@ public void testAddBlockWhileDeletingIndices() throws Exception {
final List<Thread> threads = new ArrayList<>();
final CountDownLatch latch = new CountDownLatch(1);

final APIBlock block = randomFrom(APIBlock.values());
final APIBlock block = randomAddableBlock();

Consumer<Exception> exceptionConsumer = t -> {
Throwable cause = ExceptionsHelper.unwrapCause(t);
Expand Down Expand Up @@ -478,4 +489,12 @@ static void assertIndexHasBlock(APIBlock block, final String... indices) {
public static void disableIndexBlock(String index, APIBlock block) {
disableIndexBlock(index, block.settingName());
}

/**
* The read-only-allow-delete block cannot be added via the add index block API; this method chooses randomly from the values that
* the add index block API does support.
*/
private static APIBlock randomAddableBlock() {
return randomValueOtherThan(APIBlock.READ_ONLY_ALLOW_DELETE, () -> randomFrom(APIBlock.values()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public ActionRequestValidationException validate() {
if (CollectionUtils.isEmpty(indices)) {
validationException = addValidationError("index is missing", validationException);
}
if (block == APIBlock.READ_ONLY_ALLOW_DELETE) {
validationException = addValidationError("read_only_allow_delete block is for internal use only", validationException);
}
return validationException;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1470,7 +1470,8 @@ public static void disableIndexBlock(String index, String block) {

/** Enables an index block for the specified index */
public static void enableIndexBlock(String index, String block) {
if (randomBoolean()) {
if (IndexMetadata.APIBlock.fromSetting(block) == IndexMetadata.APIBlock.READ_ONLY_ALLOW_DELETE || randomBoolean()) {
// the read-only-allow-delete block isn't supported by the add block API so we must use the update settings API here.
Settings settings = Settings.builder().put(block, true).build();
client().admin().indices().prepareUpdateSettings(index).setSettings(settings).get();
} else {
Expand Down

0 comments on commit 1cc5bc1

Please sign in to comment.