mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-09 14:34:43 +00:00
Splitting some tests out of this class that has become a catch-all for random snapshot related tests into either existing suits that fit better for these tests or one of two new suits to prevent timeouts in extreme cases (e.g. `WindowsFS` + many nodes + multiple data paths per node). No other changes to tests were made whatsoever. Closes #61541
This commit is contained in:
parent
7d74fb8577
commit
022a3ef831
@ -1319,12 +1319,6 @@ public class ConcurrentSnapshotsIT extends AbstractSnapshotIntegTestCase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SnapshotInfo assertSuccessful(ActionFuture<CreateSnapshotResponse> future) throws Exception {
|
|
||||||
final SnapshotInfo snapshotInfo = future.get().getSnapshotInfo();
|
|
||||||
assertThat(snapshotInfo.state(), is(SnapshotState.SUCCESS));
|
|
||||||
return snapshotInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void corruptIndexN(Path repoPath, long generation) throws IOException {
|
private void corruptIndexN(Path repoPath, long generation) throws IOException {
|
||||||
logger.info("--> corrupting [index-{}] in [{}]", generation, repoPath);
|
logger.info("--> corrupting [index-{}] in [{}]", generation, repoPath);
|
||||||
Path indexNBlob = repoPath.resolve(BlobStoreRepository.INDEX_FILE_PREFIX + generation);
|
Path indexNBlob = repoPath.resolve(BlobStoreRepository.INDEX_FILE_PREFIX + generation);
|
||||||
|
@ -21,6 +21,9 @@ package org.elasticsearch.snapshots;
|
|||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionRunnable;
|
import org.elasticsearch.action.ActionRunnable;
|
||||||
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
||||||
|
import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
|
||||||
|
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
|
||||||
|
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
@ -32,6 +35,7 @@ import org.elasticsearch.common.bytes.BytesReference;
|
|||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.ByteSizeUnit;
|
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
|
import org.elasticsearch.core.internal.io.IOUtils;
|
||||||
import org.elasticsearch.repositories.IndexId;
|
import org.elasticsearch.repositories.IndexId;
|
||||||
import org.elasticsearch.repositories.IndexMetaDataGenerations;
|
import org.elasticsearch.repositories.IndexMetaDataGenerations;
|
||||||
import org.elasticsearch.repositories.RepositoriesService;
|
import org.elasticsearch.repositories.RepositoriesService;
|
||||||
@ -43,20 +47,25 @@ import org.elasticsearch.repositories.blobstore.BlobStoreRepository;
|
|||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.SeekableByteChannel;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFileExists;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFileExists;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertRequestBuilderThrows;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.greaterThan;
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
public class CorruptedBlobStoreRepositoryIT extends AbstractSnapshotIntegTestCase {
|
public class CorruptedBlobStoreRepositoryIT extends AbstractSnapshotIntegTestCase {
|
||||||
@ -424,6 +433,285 @@ public class CorruptedBlobStoreRepositoryIT extends AbstractSnapshotIntegTestCas
|
|||||||
createFullSnapshot(repoName, "snapshot-2");
|
createFullSnapshot(repoName, "snapshot-2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that a shard snapshot with a corrupted shard index file can still be used for restore and incremental snapshots.
|
||||||
|
*/
|
||||||
|
public void testSnapshotWithCorruptedShardIndexFile() throws Exception {
|
||||||
|
final Client client = client();
|
||||||
|
final Path repo = randomRepoPath();
|
||||||
|
final String indexName = "test-idx";
|
||||||
|
final int nDocs = randomIntBetween(1, 10);
|
||||||
|
|
||||||
|
logger.info("--> creating index [{}] with [{}] documents in it", indexName, nDocs);
|
||||||
|
assertAcked(prepareCreate(indexName).setSettings(indexSettingsNoReplicas(1)));
|
||||||
|
|
||||||
|
final IndexRequestBuilder[] documents = new IndexRequestBuilder[nDocs];
|
||||||
|
for (int j = 0; j < nDocs; j++) {
|
||||||
|
documents[j] = client.prepareIndex(indexName, "_doc").setSource("foo", "bar");
|
||||||
|
}
|
||||||
|
indexRandom(true, documents);
|
||||||
|
flushAndRefresh();
|
||||||
|
|
||||||
|
createRepository("test-repo", "fs", repo);
|
||||||
|
|
||||||
|
final String snapshot1 = "test-snap-1";
|
||||||
|
final SnapshotInfo snapshotInfo = createFullSnapshot("test-repo", snapshot1);
|
||||||
|
assertThat(snapshotInfo.indices(), hasSize(1));
|
||||||
|
|
||||||
|
final RepositoryData repositoryData = getRepositoryData("test-repo");
|
||||||
|
final Map<String, IndexId> indexIds = repositoryData.getIndices();
|
||||||
|
assertThat(indexIds.size(), equalTo(1));
|
||||||
|
|
||||||
|
final IndexId corruptedIndex = indexIds.get(indexName);
|
||||||
|
final Path shardIndexFile = repo.resolve("indices")
|
||||||
|
.resolve(corruptedIndex.getId()).resolve("0")
|
||||||
|
.resolve("index-" + repositoryData.shardGenerations().getShardGen(corruptedIndex, 0));
|
||||||
|
|
||||||
|
logger.info("--> truncating shard index file [{}]", shardIndexFile);
|
||||||
|
try (SeekableByteChannel outChan = Files.newByteChannel(shardIndexFile, StandardOpenOption.WRITE)) {
|
||||||
|
outChan.truncate(randomInt(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> verifying snapshot state for [{}]", snapshot1);
|
||||||
|
List<SnapshotInfo> snapshotInfos = clusterAdmin().prepareGetSnapshots("test-repo").get().getSnapshots();
|
||||||
|
assertThat(snapshotInfos.size(), equalTo(1));
|
||||||
|
assertThat(snapshotInfos.get(0).state(), equalTo(SnapshotState.SUCCESS));
|
||||||
|
assertThat(snapshotInfos.get(0).snapshotId().getName(), equalTo(snapshot1));
|
||||||
|
|
||||||
|
logger.info("--> deleting index [{}]", indexName);
|
||||||
|
assertAcked(client().admin().indices().prepareDelete(indexName));
|
||||||
|
|
||||||
|
logger.info("--> restoring snapshot [{}]", snapshot1);
|
||||||
|
clusterAdmin().prepareRestoreSnapshot("test-repo", snapshot1)
|
||||||
|
.setRestoreGlobalState(randomBoolean())
|
||||||
|
.setWaitForCompletion(true)
|
||||||
|
.get();
|
||||||
|
ensureGreen();
|
||||||
|
|
||||||
|
assertDocCount(indexName, nDocs);
|
||||||
|
|
||||||
|
logger.info("--> indexing [{}] more documents into [{}]", nDocs, indexName);
|
||||||
|
for (int j = 0; j < nDocs; j++) {
|
||||||
|
documents[j] = client.prepareIndex(indexName, "_doc").setSource("foo2", "bar2");
|
||||||
|
}
|
||||||
|
indexRandom(true, documents);
|
||||||
|
|
||||||
|
final String snapshot2 = "test-snap-2";
|
||||||
|
logger.info("--> creating snapshot [{}]", snapshot2);
|
||||||
|
final SnapshotInfo snapshotInfo2 = clusterAdmin().prepareCreateSnapshot("test-repo", snapshot2)
|
||||||
|
.setWaitForCompletion(true)
|
||||||
|
.get()
|
||||||
|
.getSnapshotInfo();
|
||||||
|
assertThat(snapshotInfo2.state(), equalTo(SnapshotState.PARTIAL));
|
||||||
|
assertThat(snapshotInfo2.failedShards(), equalTo(1));
|
||||||
|
assertThat(snapshotInfo2.successfulShards(), equalTo(snapshotInfo.totalShards() - 1));
|
||||||
|
assertThat(snapshotInfo2.indices(), hasSize(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeleteSnapshotWithMissingIndexAndShardMetadata() throws Exception {
|
||||||
|
Client client = client();
|
||||||
|
|
||||||
|
Path repo = randomRepoPath();
|
||||||
|
createRepository("test-repo", "fs", repo);
|
||||||
|
|
||||||
|
final String[] indices = {"test-idx-1", "test-idx-2"};
|
||||||
|
createIndex(indices);
|
||||||
|
logger.info("--> indexing some data");
|
||||||
|
indexRandom(true,
|
||||||
|
client().prepareIndex("test-idx-1", "_doc").setSource("foo", "bar"),
|
||||||
|
client().prepareIndex("test-idx-2", "_doc").setSource("foo", "bar"));
|
||||||
|
|
||||||
|
logger.info("--> creating snapshot");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap-1")
|
||||||
|
.setWaitForCompletion(true).setIndices(indices).get();
|
||||||
|
final SnapshotInfo snapshotInfo = createSnapshotResponse.getSnapshotInfo();
|
||||||
|
assertThat(snapshotInfo.successfulShards(), greaterThan(0));
|
||||||
|
assertThat(snapshotInfo.successfulShards(), equalTo(snapshotInfo.totalShards()));
|
||||||
|
|
||||||
|
final Map<String, IndexId> indexIds = getRepositoryData("test-repo").getIndices();
|
||||||
|
final Path indicesPath = repo.resolve("indices");
|
||||||
|
|
||||||
|
logger.info("--> delete index metadata and shard metadata");
|
||||||
|
for (String index : indices) {
|
||||||
|
Path shardZero = indicesPath.resolve(indexIds.get(index).getId()).resolve("0");
|
||||||
|
if (randomBoolean()) {
|
||||||
|
Files.delete(shardZero.resolve("index-" + getRepositoryData("test-repo").shardGenerations()
|
||||||
|
.getShardGen(indexIds.get(index), 0)));
|
||||||
|
}
|
||||||
|
Files.delete(shardZero.resolve("snap-" + snapshotInfo.snapshotId().getUUID() + ".dat"));
|
||||||
|
}
|
||||||
|
|
||||||
|
startDeleteSnapshot("test-repo", "test-snap-1").get();
|
||||||
|
|
||||||
|
logger.info("--> make sure snapshot doesn't exist");
|
||||||
|
|
||||||
|
expectThrows(SnapshotMissingException.class, () -> client.admin().cluster().prepareGetSnapshots("test-repo")
|
||||||
|
.addSnapshots("test-snap-1").get());
|
||||||
|
|
||||||
|
for (String index : indices) {
|
||||||
|
assertTrue(Files.notExists(indicesPath.resolve(indexIds.get(index).getId())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeleteSnapshotWithMissingMetadata() throws Exception {
|
||||||
|
Client client = client();
|
||||||
|
|
||||||
|
Path repo = randomRepoPath();
|
||||||
|
createRepository("test-repo", "fs", repo);
|
||||||
|
|
||||||
|
createIndex("test-idx-1", "test-idx-2");
|
||||||
|
logger.info("--> indexing some data");
|
||||||
|
indexRandom(true,
|
||||||
|
client().prepareIndex("test-idx-1", "_doc").setSource("foo", "bar"),
|
||||||
|
client().prepareIndex("test-idx-2", "_doc").setSource("foo", "bar"));
|
||||||
|
|
||||||
|
logger.info("--> creating snapshot");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap-1")
|
||||||
|
.setWaitForCompletion(true).setIndices("test-idx-*").get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
|
|
||||||
|
logger.info("--> delete global state metadata");
|
||||||
|
Path metadata = repo.resolve("meta-" + createSnapshotResponse.getSnapshotInfo().snapshotId().getUUID() + ".dat");
|
||||||
|
Files.delete(metadata);
|
||||||
|
|
||||||
|
startDeleteSnapshot("test-repo", "test-snap-1").get();
|
||||||
|
|
||||||
|
logger.info("--> make sure snapshot doesn't exist");
|
||||||
|
expectThrows(SnapshotMissingException.class, () -> client.admin().cluster().prepareGetSnapshots("test-repo")
|
||||||
|
.addSnapshots("test-snap-1").get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeleteSnapshotWithCorruptedSnapshotFile() throws Exception {
|
||||||
|
Client client = client();
|
||||||
|
|
||||||
|
Path repo = randomRepoPath();
|
||||||
|
createRepository("test-repo", "fs", Settings.builder()
|
||||||
|
.put("location", repo).put("compress", false)
|
||||||
|
.put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES));
|
||||||
|
|
||||||
|
createIndex("test-idx-1", "test-idx-2");
|
||||||
|
logger.info("--> indexing some data");
|
||||||
|
indexRandom(true,
|
||||||
|
client().prepareIndex("test-idx-1", "_doc").setSource("foo", "bar"),
|
||||||
|
client().prepareIndex("test-idx-2", "_doc").setSource("foo", "bar"));
|
||||||
|
|
||||||
|
logger.info("--> creating snapshot");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap-1")
|
||||||
|
.setWaitForCompletion(true).setIndices("test-idx-*").get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
|
|
||||||
|
logger.info("--> truncate snapshot file to make it unreadable");
|
||||||
|
Path snapshotPath = repo.resolve("snap-" + createSnapshotResponse.getSnapshotInfo().snapshotId().getUUID() + ".dat");
|
||||||
|
try(SeekableByteChannel outChan = Files.newByteChannel(snapshotPath, StandardOpenOption.WRITE)) {
|
||||||
|
outChan.truncate(randomInt(10));
|
||||||
|
}
|
||||||
|
startDeleteSnapshot("test-repo", "test-snap-1").get();
|
||||||
|
|
||||||
|
logger.info("--> make sure snapshot doesn't exist");
|
||||||
|
expectThrows(SnapshotMissingException.class,
|
||||||
|
() -> client.admin().cluster().prepareGetSnapshots("test-repo").addSnapshots("test-snap-1").get().getSnapshots());
|
||||||
|
|
||||||
|
logger.info("--> make sure that we can create the snapshot again");
|
||||||
|
createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap-1")
|
||||||
|
.setWaitForCompletion(true).setIndices("test-idx-*").get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests that a snapshot with a corrupted global state file can still be deleted */
|
||||||
|
public void testDeleteSnapshotWithCorruptedGlobalState() throws Exception {
|
||||||
|
final Path repo = randomRepoPath();
|
||||||
|
|
||||||
|
createRepository("test-repo", "fs", Settings.builder()
|
||||||
|
.put("location", repo)
|
||||||
|
.put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES));
|
||||||
|
|
||||||
|
createIndex("test-idx-1", "test-idx-2");
|
||||||
|
indexRandom(true,
|
||||||
|
client().prepareIndex("test-idx-1", "_doc").setSource("foo", "bar"),
|
||||||
|
client().prepareIndex("test-idx-2", "_doc").setSource("foo", "bar"),
|
||||||
|
client().prepareIndex("test-idx-2", "_doc").setSource("foo", "bar"));
|
||||||
|
flushAndRefresh("test-idx-1", "test-idx-2");
|
||||||
|
|
||||||
|
SnapshotInfo snapshotInfo = createFullSnapshot("test-repo", "test-snap");
|
||||||
|
|
||||||
|
final Path globalStatePath = repo.resolve("meta-" + snapshotInfo.snapshotId().getUUID() + ".dat");
|
||||||
|
if (randomBoolean()) {
|
||||||
|
// Delete the global state metadata file
|
||||||
|
IOUtils.deleteFilesIgnoringExceptions(globalStatePath);
|
||||||
|
} else {
|
||||||
|
// Truncate the global state metadata file
|
||||||
|
try (SeekableByteChannel outChan = Files.newByteChannel(globalStatePath, StandardOpenOption.WRITE)) {
|
||||||
|
outChan.truncate(randomInt(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SnapshotInfo> snapshotInfos = clusterAdmin().prepareGetSnapshots("test-repo").get().getSnapshots();
|
||||||
|
assertThat(snapshotInfos.size(), equalTo(1));
|
||||||
|
assertThat(snapshotInfos.get(0).state(), equalTo(SnapshotState.SUCCESS));
|
||||||
|
assertThat(snapshotInfos.get(0).snapshotId().getName(), equalTo("test-snap"));
|
||||||
|
|
||||||
|
SnapshotsStatusResponse snapshotStatusResponse =
|
||||||
|
clusterAdmin().prepareSnapshotStatus("test-repo").setSnapshots("test-snap").get();
|
||||||
|
assertThat(snapshotStatusResponse.getSnapshots(), hasSize(1));
|
||||||
|
assertThat(snapshotStatusResponse.getSnapshots().get(0).getSnapshot().getSnapshotId().getName(), equalTo("test-snap"));
|
||||||
|
|
||||||
|
assertAcked(startDeleteSnapshot("test-repo", "test-snap").get());
|
||||||
|
expectThrows(SnapshotMissingException.class, () -> clusterAdmin()
|
||||||
|
.prepareGetSnapshots("test-repo").addSnapshots("test-snap").get());
|
||||||
|
assertRequestBuilderThrows(clusterAdmin().prepareSnapshotStatus("test-repo").addSnapshots("test-snap"),
|
||||||
|
SnapshotMissingException.class);
|
||||||
|
|
||||||
|
createFullSnapshot("test-repo", "test-snap");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSnapshotWithMissingShardLevelIndexFile() throws Exception {
|
||||||
|
disableRepoConsistencyCheck("This test uses a purposely broken repository so it would fail consistency checks");
|
||||||
|
|
||||||
|
Path repo = randomRepoPath();
|
||||||
|
createRepository("test-repo", "fs", repo);
|
||||||
|
|
||||||
|
createIndex("test-idx-1", "test-idx-2");
|
||||||
|
logger.info("--> indexing some data");
|
||||||
|
indexRandom(true,
|
||||||
|
client().prepareIndex("test-idx-1", "_doc").setSource("foo", "bar"),
|
||||||
|
client().prepareIndex("test-idx-2", "_doc").setSource("foo", "bar"));
|
||||||
|
|
||||||
|
logger.info("--> creating snapshot");
|
||||||
|
clusterAdmin().prepareCreateSnapshot("test-repo", "test-snap-1")
|
||||||
|
.setWaitForCompletion(true).setIndices("test-idx-*").get();
|
||||||
|
|
||||||
|
logger.info("--> deleting shard level index file");
|
||||||
|
final Path indicesPath = repo.resolve("indices");
|
||||||
|
for (IndexId indexId : getRepositoryData("test-repo").getIndices().values()) {
|
||||||
|
final Path shardGen;
|
||||||
|
try (Stream<Path> shardFiles = Files.list(indicesPath.resolve(indexId.getId()).resolve("0"))) {
|
||||||
|
shardGen = shardFiles.filter(file -> file.getFileName().toString().startsWith(BlobStoreRepository.INDEX_FILE_PREFIX))
|
||||||
|
.findFirst().orElseThrow(() -> new AssertionError("Failed to find shard index blob"));
|
||||||
|
}
|
||||||
|
Files.delete(shardGen);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> creating another snapshot");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse =
|
||||||
|
clusterAdmin().prepareCreateSnapshot("test-repo", "test-snap-2")
|
||||||
|
.setWaitForCompletion(true).setIndices("test-idx-1").get();
|
||||||
|
assertEquals(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
createSnapshotResponse.getSnapshotInfo().totalShards() - 1);
|
||||||
|
|
||||||
|
logger.info("--> restoring the first snapshot, the repository should not have lost any shard data despite deleting index-N, " +
|
||||||
|
"because it uses snap-*.data files and not the index-N to determine what files to restore");
|
||||||
|
client().admin().indices().prepareDelete("test-idx-1", "test-idx-2").get();
|
||||||
|
RestoreSnapshotResponse restoreSnapshotResponse =
|
||||||
|
clusterAdmin().prepareRestoreSnapshot("test-repo", "test-snap-1").setWaitForCompletion(true).get();
|
||||||
|
assertEquals(0, restoreSnapshotResponse.getRestoreInfo().failedShards());
|
||||||
|
}
|
||||||
|
|
||||||
private void assertRepositoryBlocked(Client client, String repo, String existingSnapshot) {
|
private void assertRepositoryBlocked(Client client, String repo, String existingSnapshot) {
|
||||||
logger.info("--> try to delete snapshot");
|
logger.info("--> try to delete snapshot");
|
||||||
final RepositoryException repositoryException3 = expectThrows(RepositoryException.class,
|
final RepositoryException repositoryException3 = expectThrows(RepositoryException.class,
|
||||||
|
@ -0,0 +1,750 @@
|
|||||||
|
/*
|
||||||
|
* 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.snapshots;
|
||||||
|
|
||||||
|
import org.elasticsearch.action.ActionFuture;
|
||||||
|
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
||||||
|
import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
|
||||||
|
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
|
||||||
|
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
|
||||||
|
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||||
|
import org.elasticsearch.client.Client;
|
||||||
|
import org.elasticsearch.cluster.block.ClusterBlocks;
|
||||||
|
import org.elasticsearch.cluster.metadata.IndexMetadata;
|
||||||
|
import org.elasticsearch.cluster.metadata.MappingMetadata;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||||
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
|
import org.elasticsearch.indices.InvalidIndexNameException;
|
||||||
|
import org.elasticsearch.repositories.RepositoriesService;
|
||||||
|
import org.elasticsearch.rest.RestStatus;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS;
|
||||||
|
import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS;
|
||||||
|
import static org.elasticsearch.index.IndexSettings.INDEX_REFRESH_INTERVAL_SETTING;
|
||||||
|
import static org.elasticsearch.index.IndexSettings.INDEX_SOFT_DELETES_SETTING;
|
||||||
|
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
||||||
|
import static org.elasticsearch.indices.recovery.RecoverySettings.INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertIndexTemplateExists;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertIndexTemplateMissing;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertRequestBuilderThrows;
|
||||||
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
|
||||||
|
public class RestoreSnapshotIT extends AbstractSnapshotIntegTestCase {
|
||||||
|
|
||||||
|
public void testParallelRestoreOperations() {
|
||||||
|
String indexName1 = "testindex1";
|
||||||
|
String indexName2 = "testindex2";
|
||||||
|
String repoName = "test-restore-snapshot-repo";
|
||||||
|
String snapshotName1 = "test-restore-snapshot1";
|
||||||
|
String snapshotName2 = "test-restore-snapshot2";
|
||||||
|
Path absolutePath = randomRepoPath().toAbsolutePath();
|
||||||
|
logger.info("Path [{}]", absolutePath);
|
||||||
|
String restoredIndexName1 = indexName1 + "-restored";
|
||||||
|
String restoredIndexName2 = indexName2 + "-restored";
|
||||||
|
String expectedValue = "expected";
|
||||||
|
|
||||||
|
Client client = client();
|
||||||
|
// Write a document
|
||||||
|
String docId = Integer.toString(randomInt());
|
||||||
|
index(indexName1, "_doc", docId, "value", expectedValue);
|
||||||
|
|
||||||
|
String docId2 = Integer.toString(randomInt());
|
||||||
|
index(indexName2, "_doc", docId2, "value", expectedValue);
|
||||||
|
|
||||||
|
createRepository(repoName, "fs", absolutePath);
|
||||||
|
|
||||||
|
logger.info("--> snapshot");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot(repoName, snapshotName1)
|
||||||
|
.setWaitForCompletion(true)
|
||||||
|
.setIndices(indexName1)
|
||||||
|
.get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().state(), equalTo(SnapshotState.SUCCESS));
|
||||||
|
|
||||||
|
CreateSnapshotResponse createSnapshotResponse2 = client.admin().cluster().prepareCreateSnapshot(repoName, snapshotName2)
|
||||||
|
.setWaitForCompletion(true)
|
||||||
|
.setIndices(indexName2)
|
||||||
|
.get();
|
||||||
|
assertThat(createSnapshotResponse2.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse2.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse2.getSnapshotInfo().totalShards()));
|
||||||
|
assertThat(createSnapshotResponse2.getSnapshotInfo().state(), equalTo(SnapshotState.SUCCESS));
|
||||||
|
|
||||||
|
RestoreSnapshotResponse restoreSnapshotResponse1 = client.admin().cluster().prepareRestoreSnapshot(repoName, snapshotName1)
|
||||||
|
.setWaitForCompletion(false)
|
||||||
|
.setRenamePattern(indexName1)
|
||||||
|
.setRenameReplacement(restoredIndexName1)
|
||||||
|
.get();
|
||||||
|
RestoreSnapshotResponse restoreSnapshotResponse2 = client.admin().cluster().prepareRestoreSnapshot(repoName, snapshotName2)
|
||||||
|
.setWaitForCompletion(false)
|
||||||
|
.setRenamePattern(indexName2)
|
||||||
|
.setRenameReplacement(restoredIndexName2)
|
||||||
|
.get();
|
||||||
|
assertThat(restoreSnapshotResponse1.status(), equalTo(RestStatus.ACCEPTED));
|
||||||
|
assertThat(restoreSnapshotResponse2.status(), equalTo(RestStatus.ACCEPTED));
|
||||||
|
ensureGreen(restoredIndexName1, restoredIndexName2);
|
||||||
|
assertThat(client.prepareGet(restoredIndexName1, "_doc", docId).get().isExists(), equalTo(true));
|
||||||
|
assertThat(client.prepareGet(restoredIndexName2, "_doc", docId2).get().isExists(), equalTo(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParallelRestoreOperationsFromSingleSnapshot() throws Exception {
|
||||||
|
String indexName1 = "testindex1";
|
||||||
|
String indexName2 = "testindex2";
|
||||||
|
String repoName = "test-restore-snapshot-repo";
|
||||||
|
String snapshotName = "test-restore-snapshot";
|
||||||
|
Path absolutePath = randomRepoPath().toAbsolutePath();
|
||||||
|
logger.info("Path [{}]", absolutePath);
|
||||||
|
String restoredIndexName1 = indexName1 + "-restored";
|
||||||
|
String restoredIndexName2 = indexName2 + "-restored";
|
||||||
|
String expectedValue = "expected";
|
||||||
|
|
||||||
|
Client client = client();
|
||||||
|
// Write a document
|
||||||
|
String docId = Integer.toString(randomInt());
|
||||||
|
index(indexName1, "_doc", docId, "value", expectedValue);
|
||||||
|
|
||||||
|
String docId2 = Integer.toString(randomInt());
|
||||||
|
index(indexName2, "_doc", docId2, "value", expectedValue);
|
||||||
|
|
||||||
|
createRepository(repoName, "fs", absolutePath);
|
||||||
|
|
||||||
|
logger.info("--> snapshot");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot(repoName, snapshotName)
|
||||||
|
.setWaitForCompletion(true)
|
||||||
|
.setIndices(indexName1, indexName2)
|
||||||
|
.get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().state(), equalTo(SnapshotState.SUCCESS));
|
||||||
|
|
||||||
|
ActionFuture<RestoreSnapshotResponse> restoreSnapshotResponse1 = client.admin().cluster()
|
||||||
|
.prepareRestoreSnapshot(repoName, snapshotName)
|
||||||
|
.setIndices(indexName1)
|
||||||
|
.setRenamePattern(indexName1)
|
||||||
|
.setRenameReplacement(restoredIndexName1)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
boolean sameSourceIndex = randomBoolean();
|
||||||
|
|
||||||
|
ActionFuture<RestoreSnapshotResponse> restoreSnapshotResponse2 = client.admin().cluster()
|
||||||
|
.prepareRestoreSnapshot(repoName, snapshotName)
|
||||||
|
.setIndices(sameSourceIndex ? indexName1 : indexName2)
|
||||||
|
.setRenamePattern(sameSourceIndex ? indexName1 : indexName2)
|
||||||
|
.setRenameReplacement(restoredIndexName2)
|
||||||
|
.execute();
|
||||||
|
assertThat(restoreSnapshotResponse1.get().status(), equalTo(RestStatus.ACCEPTED));
|
||||||
|
assertThat(restoreSnapshotResponse2.get().status(), equalTo(RestStatus.ACCEPTED));
|
||||||
|
ensureGreen(restoredIndexName1, restoredIndexName2);
|
||||||
|
assertThat(client.prepareGet(restoredIndexName1, "_doc", docId).get().isExists(), equalTo(true));
|
||||||
|
assertThat(client.prepareGet(restoredIndexName2, "_doc", sameSourceIndex ? docId : docId2).get().isExists(), equalTo(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRestoreIncreasesPrimaryTerms() {
|
||||||
|
final String indexName = randomAlphaOfLengthBetween(5, 10).toLowerCase(Locale.ROOT);
|
||||||
|
createIndex(indexName, indexSettingsNoReplicas(2).build());
|
||||||
|
ensureGreen(indexName);
|
||||||
|
|
||||||
|
if (randomBoolean()) {
|
||||||
|
// open and close the index to increase the primary terms
|
||||||
|
for (int i = 0; i < randomInt(3); i++) {
|
||||||
|
assertAcked(client().admin().indices().prepareClose(indexName));
|
||||||
|
assertAcked(client().admin().indices().prepareOpen(indexName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final IndexMetadata indexMetadata = clusterAdmin().prepareState().clear().setIndices(indexName)
|
||||||
|
.setMetadata(true).get().getState().metadata().index(indexName);
|
||||||
|
assertThat(indexMetadata.getSettings().get(IndexMetadata.SETTING_HISTORY_UUID), nullValue());
|
||||||
|
final int numPrimaries = getNumShards(indexName).numPrimaries;
|
||||||
|
final Map<Integer, Long> primaryTerms = IntStream.range(0, numPrimaries)
|
||||||
|
.boxed().collect(Collectors.toMap(shardId -> shardId, indexMetadata::primaryTerm));
|
||||||
|
|
||||||
|
createRepository("test-repo", "fs");
|
||||||
|
final CreateSnapshotResponse createSnapshotResponse = clusterAdmin().prepareCreateSnapshot("test-repo", "test-snap")
|
||||||
|
.setWaitForCompletion(true).setIndices(indexName).get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), equalTo(numPrimaries));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().failedShards(), equalTo(0));
|
||||||
|
|
||||||
|
assertAcked(client().admin().indices().prepareClose(indexName));
|
||||||
|
|
||||||
|
final RestoreSnapshotResponse restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setWaitForCompletion(true).get();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().successfulShards(), equalTo(numPrimaries));
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().failedShards(), equalTo(0));
|
||||||
|
|
||||||
|
final IndexMetadata restoredIndexMetadata = clusterAdmin().prepareState().clear().setIndices(indexName)
|
||||||
|
.setMetadata(true).get().getState().metadata().index(indexName);
|
||||||
|
for (int shardId = 0; shardId < numPrimaries; shardId++) {
|
||||||
|
assertThat(restoredIndexMetadata.primaryTerm(shardId), greaterThan(primaryTerms.get(shardId)));
|
||||||
|
}
|
||||||
|
assertThat(restoredIndexMetadata.getSettings().get(IndexMetadata.SETTING_HISTORY_UUID), notNullValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRestoreWithDifferentMappingsAndSettings() throws Exception {
|
||||||
|
createRepository("test-repo", "fs");
|
||||||
|
|
||||||
|
logger.info("--> create index with baz field");
|
||||||
|
assertAcked(prepareCreate("test-idx", 2, Settings.builder()
|
||||||
|
.put(indexSettings()).put(SETTING_NUMBER_OF_REPLICAS, between(0, 1)).put("refresh_interval", 10, TimeUnit.SECONDS)));
|
||||||
|
|
||||||
|
NumShards numShards = getNumShards("test-idx");
|
||||||
|
|
||||||
|
assertAcked(client().admin().indices().preparePutMapping("test-idx").setType("_doc").setSource("baz", "type=text"));
|
||||||
|
ensureGreen();
|
||||||
|
|
||||||
|
logger.info("--> snapshot it");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = clusterAdmin().prepareCreateSnapshot("test-repo", "test-snap")
|
||||||
|
.setWaitForCompletion(true).setIndices("test-idx").get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
|
|
||||||
|
logger.info("--> delete the index and recreate it with foo field");
|
||||||
|
cluster().wipeIndices("test-idx");
|
||||||
|
assertAcked(prepareCreate("test-idx", 2, Settings.builder()
|
||||||
|
.put(SETTING_NUMBER_OF_SHARDS, numShards.numPrimaries).put(SETTING_NUMBER_OF_REPLICAS, between(0, 1))
|
||||||
|
.put("refresh_interval", 5, TimeUnit.SECONDS)));
|
||||||
|
assertAcked(client().admin().indices().preparePutMapping("test-idx").setType("_doc").setSource("foo", "type=text"));
|
||||||
|
ensureGreen();
|
||||||
|
|
||||||
|
logger.info("--> close index");
|
||||||
|
client().admin().indices().prepareClose("test-idx").get();
|
||||||
|
|
||||||
|
logger.info("--> restore all indices from the snapshot");
|
||||||
|
RestoreSnapshotResponse restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setWaitForCompletion(true).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0));
|
||||||
|
|
||||||
|
logger.info("--> assert that old mapping is restored");
|
||||||
|
MappingMetadata mappings = clusterAdmin().prepareState().get().getState().getMetadata()
|
||||||
|
.getIndices().get("test-idx").mapping();
|
||||||
|
assertThat(mappings.sourceAsMap().toString(), containsString("baz"));
|
||||||
|
assertThat(mappings.sourceAsMap().toString(), not(containsString("foo")));
|
||||||
|
|
||||||
|
logger.info("--> assert that old settings are restored");
|
||||||
|
GetSettingsResponse getSettingsResponse = client().admin().indices().prepareGetSettings("test-idx").execute().actionGet();
|
||||||
|
assertThat(getSettingsResponse.getSetting("test-idx", "index.refresh_interval"), equalTo("10s"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRestoreAliases() throws Exception {
|
||||||
|
createRepository("test-repo", "fs");
|
||||||
|
|
||||||
|
logger.info("--> create test indices");
|
||||||
|
createIndex("test-idx-1", "test-idx-2", "test-idx-3");
|
||||||
|
ensureGreen();
|
||||||
|
|
||||||
|
logger.info("--> create aliases");
|
||||||
|
assertAcked(client().admin().indices().prepareAliases()
|
||||||
|
.addAlias("test-idx-1", "alias-123")
|
||||||
|
.addAlias("test-idx-2", "alias-123")
|
||||||
|
.addAlias("test-idx-3", "alias-123")
|
||||||
|
.addAlias("test-idx-1", "alias-1")
|
||||||
|
.get());
|
||||||
|
|
||||||
|
assertFalse(client().admin().indices().prepareGetAliases("alias-123").get().getAliases().isEmpty());
|
||||||
|
|
||||||
|
logger.info("--> snapshot");
|
||||||
|
assertThat(clusterAdmin().prepareCreateSnapshot("test-repo", "test-snap")
|
||||||
|
.setIndices().setWaitForCompletion(true).get().getSnapshotInfo().state(),
|
||||||
|
equalTo(SnapshotState.SUCCESS));
|
||||||
|
|
||||||
|
logger.info("--> delete all indices");
|
||||||
|
cluster().wipeIndices("test-idx-1", "test-idx-2", "test-idx-3");
|
||||||
|
assertTrue(client().admin().indices().prepareGetAliases("alias-123").get().getAliases().isEmpty());
|
||||||
|
assertTrue(client().admin().indices().prepareGetAliases("alias-1").get().getAliases().isEmpty());
|
||||||
|
|
||||||
|
logger.info("--> restore snapshot with aliases");
|
||||||
|
RestoreSnapshotResponse restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setWaitForCompletion(true).setRestoreGlobalState(true).execute().actionGet();
|
||||||
|
// We don't restore any indices here
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().successfulShards(), allOf(greaterThan(0),
|
||||||
|
equalTo(restoreSnapshotResponse.getRestoreInfo().totalShards())));
|
||||||
|
|
||||||
|
logger.info("--> check that aliases are restored");
|
||||||
|
assertFalse(client().admin().indices().prepareGetAliases("alias-123").get().getAliases().isEmpty());
|
||||||
|
assertFalse(client().admin().indices().prepareGetAliases("alias-1").get().getAliases().isEmpty());
|
||||||
|
|
||||||
|
logger.info("--> update aliases");
|
||||||
|
assertAcked(client().admin().indices().prepareAliases().removeAlias("test-idx-3", "alias-123"));
|
||||||
|
assertAcked(client().admin().indices().prepareAliases().addAlias("test-idx-3", "alias-3"));
|
||||||
|
|
||||||
|
logger.info("--> delete and close indices");
|
||||||
|
cluster().wipeIndices("test-idx-1", "test-idx-2");
|
||||||
|
assertAcked(client().admin().indices().prepareClose("test-idx-3"));
|
||||||
|
assertTrue(client().admin().indices().prepareGetAliases("alias-123").get().getAliases().isEmpty());
|
||||||
|
assertTrue(client().admin().indices().prepareGetAliases("alias-1").get().getAliases().isEmpty());
|
||||||
|
|
||||||
|
logger.info("--> restore snapshot without aliases");
|
||||||
|
restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "test-snap").setWaitForCompletion(true)
|
||||||
|
.setRestoreGlobalState(true).setIncludeAliases(false).execute().actionGet();
|
||||||
|
// We don't restore any indices here
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().successfulShards(), allOf(greaterThan(0),
|
||||||
|
equalTo(restoreSnapshotResponse.getRestoreInfo().totalShards())));
|
||||||
|
|
||||||
|
logger.info("--> check that aliases are not restored and existing aliases still exist");
|
||||||
|
assertTrue(client().admin().indices().prepareGetAliases("alias-123").get().getAliases().isEmpty());
|
||||||
|
assertTrue(client().admin().indices().prepareGetAliases("alias-1").get().getAliases().isEmpty());
|
||||||
|
assertFalse(client().admin().indices().prepareGetAliases("alias-3").get().getAliases().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRestoreTemplates() throws Exception {
|
||||||
|
createRepository("test-repo", "fs");
|
||||||
|
|
||||||
|
logger.info("--> creating test template");
|
||||||
|
assertThat(client().admin().indices()
|
||||||
|
.preparePutTemplate("test-template")
|
||||||
|
.setPatterns(Collections.singletonList("te*"))
|
||||||
|
.addMapping("_doc", XContentFactory.jsonBuilder()
|
||||||
|
.startObject()
|
||||||
|
.startObject("_doc")
|
||||||
|
.startObject("properties")
|
||||||
|
.startObject("field1")
|
||||||
|
.field("type", "text")
|
||||||
|
.field("store", true)
|
||||||
|
.endObject()
|
||||||
|
.startObject("field2")
|
||||||
|
.field("type", "keyword")
|
||||||
|
.field("store", true)
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endObject())
|
||||||
|
.get().isAcknowledged(), equalTo(true));
|
||||||
|
|
||||||
|
logger.info("--> snapshot");
|
||||||
|
final SnapshotInfo snapshotInfo = assertSuccessful(clusterAdmin().prepareCreateSnapshot("test-repo", "test-snap")
|
||||||
|
.setIndices().setWaitForCompletion(true).execute());
|
||||||
|
assertThat(snapshotInfo.totalShards(), equalTo(0));
|
||||||
|
assertThat(snapshotInfo.successfulShards(), equalTo(0));
|
||||||
|
assertThat(getSnapshot("test-repo", "test-snap").state(), equalTo(SnapshotState.SUCCESS));
|
||||||
|
|
||||||
|
logger.info("--> delete test template");
|
||||||
|
assertThat(client().admin().indices().prepareDeleteTemplate("test-template").get().isAcknowledged(), equalTo(true));
|
||||||
|
GetIndexTemplatesResponse getIndexTemplatesResponse = client().admin().indices().prepareGetTemplates().get();
|
||||||
|
assertIndexTemplateMissing(getIndexTemplatesResponse, "test-template");
|
||||||
|
|
||||||
|
logger.info("--> restore cluster state");
|
||||||
|
RestoreSnapshotResponse restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setWaitForCompletion(true).setRestoreGlobalState(true).execute().actionGet();
|
||||||
|
// We don't restore any indices here
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), equalTo(0));
|
||||||
|
|
||||||
|
logger.info("--> check that template is restored");
|
||||||
|
getIndexTemplatesResponse = client().admin().indices().prepareGetTemplates().get();
|
||||||
|
assertIndexTemplateExists(getIndexTemplatesResponse, "test-template");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testRenameOnRestore() throws Exception {
|
||||||
|
Client client = client();
|
||||||
|
|
||||||
|
createRepository("test-repo", "fs");
|
||||||
|
|
||||||
|
createIndex("test-idx-1", "test-idx-2", "test-idx-3");
|
||||||
|
ensureGreen();
|
||||||
|
|
||||||
|
assertAcked(client.admin().indices().prepareAliases()
|
||||||
|
.addAlias("test-idx-1", "alias-1", false)
|
||||||
|
.addAlias("test-idx-2", "alias-2", false)
|
||||||
|
.addAlias("test-idx-3", "alias-3", false)
|
||||||
|
);
|
||||||
|
|
||||||
|
indexRandomDocs("test-idx-1", 100);
|
||||||
|
indexRandomDocs("test-idx-2", 100);
|
||||||
|
|
||||||
|
logger.info("--> snapshot");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap")
|
||||||
|
.setWaitForCompletion(true).setIndices("test-idx-1", "test-idx-2").get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
|
|
||||||
|
logger.info("--> restore indices with different names");
|
||||||
|
RestoreSnapshotResponse restoreSnapshotResponse = client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setRenamePattern("(.+)").setRenameReplacement("$1-copy").setWaitForCompletion(true).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0));
|
||||||
|
|
||||||
|
assertDocCount("test-idx-1-copy", 100L);
|
||||||
|
assertDocCount("test-idx-2-copy", 100L);
|
||||||
|
|
||||||
|
logger.info("--> close just restored indices");
|
||||||
|
client.admin().indices().prepareClose("test-idx-1-copy", "test-idx-2-copy").get();
|
||||||
|
|
||||||
|
logger.info("--> and try to restore these indices again");
|
||||||
|
restoreSnapshotResponse = client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setRenamePattern("(.+)").setRenameReplacement("$1-copy").setWaitForCompletion(true).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0));
|
||||||
|
|
||||||
|
assertDocCount("test-idx-1-copy", 100L);
|
||||||
|
assertDocCount("test-idx-2-copy", 100L);
|
||||||
|
|
||||||
|
logger.info("--> close indices");
|
||||||
|
assertAcked(client.admin().indices().prepareClose("test-idx-1", "test-idx-2-copy"));
|
||||||
|
|
||||||
|
logger.info("--> restore indices with different names");
|
||||||
|
restoreSnapshotResponse = client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setRenamePattern("(.+-2)").setRenameReplacement("$1-copy").setWaitForCompletion(true).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0));
|
||||||
|
|
||||||
|
logger.info("--> delete indices");
|
||||||
|
cluster().wipeIndices("test-idx-1", "test-idx-1-copy", "test-idx-2", "test-idx-2-copy");
|
||||||
|
|
||||||
|
logger.info("--> try renaming indices using the same name");
|
||||||
|
try {
|
||||||
|
client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap").setRenamePattern("(.+)")
|
||||||
|
.setRenameReplacement("same-name").setWaitForCompletion(true).execute().actionGet();
|
||||||
|
fail("Shouldn't be here");
|
||||||
|
} catch (SnapshotRestoreException ex) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> try renaming indices using the same name");
|
||||||
|
try {
|
||||||
|
client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap").setRenamePattern("test-idx-2")
|
||||||
|
.setRenameReplacement("test-idx-1").setWaitForCompletion(true).execute().actionGet();
|
||||||
|
fail("Shouldn't be here");
|
||||||
|
} catch (SnapshotRestoreException ex) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> try renaming indices using invalid index name");
|
||||||
|
try {
|
||||||
|
client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap").setIndices("test-idx-1").setRenamePattern(".+")
|
||||||
|
.setRenameReplacement("__WRONG__").setWaitForCompletion(true).execute().actionGet();
|
||||||
|
fail("Shouldn't be here");
|
||||||
|
} catch (InvalidIndexNameException ex) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> try renaming indices into existing alias name");
|
||||||
|
try {
|
||||||
|
client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap").setIndices("test-idx-1").setRenamePattern(".+")
|
||||||
|
.setRenameReplacement("alias-3").setWaitForCompletion(true).execute().actionGet();
|
||||||
|
fail("Shouldn't be here");
|
||||||
|
} catch (InvalidIndexNameException ex) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> try renaming indices into existing alias of itself");
|
||||||
|
try {
|
||||||
|
client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap").setIndices("test-idx-1").setRenamePattern("test-idx")
|
||||||
|
.setRenameReplacement("alias").setWaitForCompletion(true).execute().actionGet();
|
||||||
|
fail("Shouldn't be here");
|
||||||
|
} catch (SnapshotRestoreException ex) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> try renaming indices into existing alias of another restored index");
|
||||||
|
try {
|
||||||
|
client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap").setIndices("test-idx-1", "test-idx-2")
|
||||||
|
.setRenamePattern("test-idx-1").setRenameReplacement("alias-2").setWaitForCompletion(true).execute().actionGet();
|
||||||
|
fail("Shouldn't be here");
|
||||||
|
} catch (SnapshotRestoreException ex) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> try renaming indices into existing alias of itself, but don't restore aliases ");
|
||||||
|
restoreSnapshotResponse = client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setIndices("test-idx-1").setRenamePattern("test-idx").setRenameReplacement("alias")
|
||||||
|
.setWaitForCompletion(true).setIncludeAliases(false).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDynamicRestoreThrottling() throws Exception {
|
||||||
|
Client client = client();
|
||||||
|
|
||||||
|
createRepository("test-repo", "fs", Settings.builder()
|
||||||
|
.put("location", randomRepoPath()).put("compress", randomBoolean())
|
||||||
|
.put("chunk_size", 100, ByteSizeUnit.BYTES));
|
||||||
|
|
||||||
|
createIndexWithRandomDocs("test-idx", 100);
|
||||||
|
|
||||||
|
logger.info("--> snapshot");
|
||||||
|
client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap")
|
||||||
|
.setWaitForCompletion(true).setIndices("test-idx").get();
|
||||||
|
|
||||||
|
logger.info("--> delete index");
|
||||||
|
cluster().wipeIndices("test-idx");
|
||||||
|
|
||||||
|
logger.info("--> restore index");
|
||||||
|
client.admin().cluster().prepareUpdateSettings().setTransientSettings(Settings.builder()
|
||||||
|
.put(INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey(), "100b").build()).get();
|
||||||
|
ActionFuture<RestoreSnapshotResponse> restoreSnapshotResponse = client.admin().cluster()
|
||||||
|
.prepareRestoreSnapshot("test-repo", "test-snap").setWaitForCompletion(true).execute();
|
||||||
|
|
||||||
|
// check if throttling is active
|
||||||
|
assertBusy(() -> {
|
||||||
|
long restorePause = 0L;
|
||||||
|
for (RepositoriesService repositoriesService : internalCluster().getDataNodeInstances(RepositoriesService.class)) {
|
||||||
|
restorePause += repositoriesService.repository("test-repo").getRestoreThrottleTimeInNanos();
|
||||||
|
}
|
||||||
|
assertThat(restorePause, greaterThan(TimeValue.timeValueSeconds(randomIntBetween(1, 5)).nanos()));
|
||||||
|
assertFalse(restoreSnapshotResponse.isDone());
|
||||||
|
}, 30, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
// run at full speed again
|
||||||
|
client.admin().cluster().prepareUpdateSettings().setTransientSettings(Settings.builder()
|
||||||
|
.putNull(INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey()).build()).get();
|
||||||
|
|
||||||
|
// check that restore now completes quickly (i.e. within 10 seconds)
|
||||||
|
assertBusy(() -> assertTrue(restoreSnapshotResponse.isDone()));
|
||||||
|
|
||||||
|
assertThat(restoreSnapshotResponse.get().getRestoreInfo().totalShards(), greaterThan(0));
|
||||||
|
assertDocCount("test-idx", 100L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testChangeSettingsOnRestore() throws Exception {
|
||||||
|
Client client = client();
|
||||||
|
|
||||||
|
createRepository("test-repo", "fs");
|
||||||
|
|
||||||
|
logger.info("--> create test index with case-preserving search analyzer");
|
||||||
|
|
||||||
|
Settings.Builder indexSettings = Settings.builder()
|
||||||
|
.put(indexSettings())
|
||||||
|
.put(SETTING_NUMBER_OF_REPLICAS, between(0, 1))
|
||||||
|
.put(INDEX_REFRESH_INTERVAL_SETTING.getKey(), "10s")
|
||||||
|
.put("index.analysis.analyzer.my_analyzer.type", "custom")
|
||||||
|
.put("index.analysis.analyzer.my_analyzer.tokenizer", "standard");
|
||||||
|
|
||||||
|
assertAcked(prepareCreate("test-idx", 2, indexSettings));
|
||||||
|
|
||||||
|
int numberOfShards = getNumShards("test-idx").numPrimaries;
|
||||||
|
assertAcked(client().admin().indices().preparePutMapping("test-idx").setType("_doc")
|
||||||
|
.setSource("field1", "type=text,analyzer=standard,search_analyzer=my_analyzer"));
|
||||||
|
final int numdocs = randomIntBetween(10, 100);
|
||||||
|
IndexRequestBuilder[] builders = new IndexRequestBuilder[numdocs];
|
||||||
|
for (int i = 0; i < builders.length; i++) {
|
||||||
|
builders[i] = client().prepareIndex("test-idx", "_doc").setId(Integer.toString(i)).setSource("field1", "Foo bar " + i);
|
||||||
|
}
|
||||||
|
indexRandom(true, builders);
|
||||||
|
flushAndRefresh();
|
||||||
|
|
||||||
|
assertHitCount(client.prepareSearch("test-idx").setSize(0).setQuery(matchQuery("field1", "foo")).get(), numdocs);
|
||||||
|
assertHitCount(client.prepareSearch("test-idx").setSize(0).setQuery(matchQuery("field1", "Foo")).get(), 0);
|
||||||
|
assertHitCount(client.prepareSearch("test-idx").setSize(0).setQuery(matchQuery("field1", "bar")).get(), numdocs);
|
||||||
|
|
||||||
|
logger.info("--> snapshot it");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap")
|
||||||
|
.setWaitForCompletion(true).setIndices("test-idx").get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
|
|
||||||
|
logger.info("--> delete the index and recreate it while changing refresh interval and analyzer");
|
||||||
|
cluster().wipeIndices("test-idx");
|
||||||
|
|
||||||
|
Settings newIndexSettings = Settings.builder()
|
||||||
|
.put("refresh_interval", "5s")
|
||||||
|
.put("index.analysis.analyzer.my_analyzer.type", "standard")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Settings newIncorrectIndexSettings = Settings.builder()
|
||||||
|
.put(newIndexSettings)
|
||||||
|
.put(SETTING_NUMBER_OF_SHARDS, numberOfShards + 100)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
logger.info("--> try restoring while changing the number of shards - should fail");
|
||||||
|
assertRequestBuilderThrows(client.admin().cluster()
|
||||||
|
.prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setIgnoreIndexSettings("index.analysis.*")
|
||||||
|
.setIndexSettings(newIncorrectIndexSettings)
|
||||||
|
.setWaitForCompletion(true), SnapshotRestoreException.class);
|
||||||
|
|
||||||
|
logger.info("--> try restoring while changing the number of replicas to a negative number - should fail");
|
||||||
|
Settings newIncorrectReplicasIndexSettings = Settings.builder()
|
||||||
|
.put(newIndexSettings)
|
||||||
|
.put(SETTING_NUMBER_OF_REPLICAS.substring(IndexMetadata.INDEX_SETTING_PREFIX.length()), randomIntBetween(-10, -1))
|
||||||
|
.build();
|
||||||
|
assertRequestBuilderThrows(client.admin().cluster()
|
||||||
|
.prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setIgnoreIndexSettings("index.analysis.*")
|
||||||
|
.setIndexSettings(newIncorrectReplicasIndexSettings)
|
||||||
|
.setWaitForCompletion(true), IllegalArgumentException.class);
|
||||||
|
|
||||||
|
logger.info("--> restore index with correct settings from the snapshot");
|
||||||
|
RestoreSnapshotResponse restoreSnapshotResponse = client.admin().cluster()
|
||||||
|
.prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setIgnoreIndexSettings("index.analysis.*")
|
||||||
|
.setIndexSettings(newIndexSettings)
|
||||||
|
.setWaitForCompletion(true).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0));
|
||||||
|
|
||||||
|
logger.info("--> assert that correct settings are restored");
|
||||||
|
GetSettingsResponse getSettingsResponse = client.admin().indices().prepareGetSettings("test-idx").execute().actionGet();
|
||||||
|
assertThat(getSettingsResponse.getSetting("test-idx", INDEX_REFRESH_INTERVAL_SETTING.getKey()), equalTo("5s"));
|
||||||
|
// Make sure that number of shards didn't change
|
||||||
|
assertThat(getSettingsResponse.getSetting("test-idx", SETTING_NUMBER_OF_SHARDS), equalTo("" + numberOfShards));
|
||||||
|
assertThat(getSettingsResponse.getSetting("test-idx", "index.analysis.analyzer.my_analyzer.type"), equalTo("standard"));
|
||||||
|
|
||||||
|
assertHitCount(client.prepareSearch("test-idx").setSize(0).setQuery(matchQuery("field1", "Foo")).get(), numdocs);
|
||||||
|
assertHitCount(client.prepareSearch("test-idx").setSize(0).setQuery(matchQuery("field1", "bar")).get(), numdocs);
|
||||||
|
|
||||||
|
logger.info("--> delete the index and recreate it while deleting all index settings");
|
||||||
|
cluster().wipeIndices("test-idx");
|
||||||
|
|
||||||
|
logger.info("--> restore index with correct settings from the snapshot");
|
||||||
|
restoreSnapshotResponse = client.admin().cluster()
|
||||||
|
.prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setIgnoreIndexSettings("*") // delete everything we can delete
|
||||||
|
.setIndexSettings(newIndexSettings)
|
||||||
|
.setWaitForCompletion(true).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0));
|
||||||
|
|
||||||
|
logger.info("--> assert that correct settings are restored and index is still functional");
|
||||||
|
getSettingsResponse = client.admin().indices().prepareGetSettings("test-idx").execute().actionGet();
|
||||||
|
assertThat(getSettingsResponse.getSetting("test-idx", INDEX_REFRESH_INTERVAL_SETTING.getKey()), equalTo("5s"));
|
||||||
|
// Make sure that number of shards didn't change
|
||||||
|
assertThat(getSettingsResponse.getSetting("test-idx", SETTING_NUMBER_OF_SHARDS), equalTo("" + numberOfShards));
|
||||||
|
|
||||||
|
assertHitCount(client.prepareSearch("test-idx").setSize(0).setQuery(matchQuery("field1", "Foo")).get(), numdocs);
|
||||||
|
assertHitCount(client.prepareSearch("test-idx").setSize(0).setQuery(matchQuery("field1", "bar")).get(), numdocs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRecreateBlocksOnRestore() throws Exception {
|
||||||
|
Client client = client();
|
||||||
|
|
||||||
|
createRepository("test-repo", "fs");
|
||||||
|
|
||||||
|
Settings.Builder indexSettings = Settings.builder()
|
||||||
|
.put(indexSettings())
|
||||||
|
.put(SETTING_NUMBER_OF_REPLICAS, between(0, 1))
|
||||||
|
.put(INDEX_REFRESH_INTERVAL_SETTING.getKey(), "10s");
|
||||||
|
|
||||||
|
logger.info("--> create index");
|
||||||
|
assertAcked(prepareCreate("test-idx", 2, indexSettings));
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<String> initialBlockSettings = randomSubsetOf(randomInt(3),
|
||||||
|
IndexMetadata.SETTING_BLOCKS_WRITE, IndexMetadata.SETTING_BLOCKS_METADATA, IndexMetadata.SETTING_READ_ONLY);
|
||||||
|
Settings.Builder initialSettingsBuilder = Settings.builder();
|
||||||
|
for (String blockSetting : initialBlockSettings) {
|
||||||
|
initialSettingsBuilder.put(blockSetting, true);
|
||||||
|
}
|
||||||
|
Settings initialSettings = initialSettingsBuilder.build();
|
||||||
|
logger.info("--> using initial block settings {}", initialSettings);
|
||||||
|
|
||||||
|
if (!initialSettings.isEmpty()) {
|
||||||
|
logger.info("--> apply initial blocks to index");
|
||||||
|
client().admin().indices().prepareUpdateSettings("test-idx").setSettings(initialSettingsBuilder).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> snapshot index");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap")
|
||||||
|
.setWaitForCompletion(true).setIndices("test-idx").get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
|
|
||||||
|
logger.info("--> remove blocks and delete index");
|
||||||
|
disableIndexBlock("test-idx", IndexMetadata.SETTING_BLOCKS_METADATA);
|
||||||
|
disableIndexBlock("test-idx", IndexMetadata.SETTING_READ_ONLY);
|
||||||
|
disableIndexBlock("test-idx", IndexMetadata.SETTING_BLOCKS_WRITE);
|
||||||
|
disableIndexBlock("test-idx", IndexMetadata.SETTING_BLOCKS_READ);
|
||||||
|
cluster().wipeIndices("test-idx");
|
||||||
|
|
||||||
|
logger.info("--> restore index with additional block changes");
|
||||||
|
List<String> changeBlockSettings = randomSubsetOf(randomInt(4),
|
||||||
|
IndexMetadata.SETTING_BLOCKS_METADATA, IndexMetadata.SETTING_BLOCKS_WRITE,
|
||||||
|
IndexMetadata.SETTING_READ_ONLY, IndexMetadata.SETTING_BLOCKS_READ);
|
||||||
|
Settings.Builder changedSettingsBuilder = Settings.builder();
|
||||||
|
for (String blockSetting : changeBlockSettings) {
|
||||||
|
changedSettingsBuilder.put(blockSetting, randomBoolean());
|
||||||
|
}
|
||||||
|
Settings changedSettings = changedSettingsBuilder.build();
|
||||||
|
logger.info("--> applying changed block settings {}", changedSettings);
|
||||||
|
|
||||||
|
RestoreSnapshotResponse restoreSnapshotResponse = client.admin().cluster()
|
||||||
|
.prepareRestoreSnapshot("test-repo", "test-snap")
|
||||||
|
.setIndexSettings(changedSettings)
|
||||||
|
.setWaitForCompletion(true).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0));
|
||||||
|
|
||||||
|
ClusterBlocks blocks = client.admin().cluster().prepareState().clear().setBlocks(true).get().getState().blocks();
|
||||||
|
// compute current index settings (as we cannot query them if they contain SETTING_BLOCKS_METADATA)
|
||||||
|
Settings mergedSettings = Settings.builder()
|
||||||
|
.put(initialSettings)
|
||||||
|
.put(changedSettings)
|
||||||
|
.build();
|
||||||
|
logger.info("--> merged block settings {}", mergedSettings);
|
||||||
|
|
||||||
|
logger.info("--> checking consistency between settings and blocks");
|
||||||
|
assertThat(mergedSettings.getAsBoolean(IndexMetadata.SETTING_BLOCKS_METADATA, false),
|
||||||
|
is(blocks.hasIndexBlock("test-idx", IndexMetadata.INDEX_METADATA_BLOCK)));
|
||||||
|
assertThat(mergedSettings.getAsBoolean(IndexMetadata.SETTING_BLOCKS_READ, false),
|
||||||
|
is(blocks.hasIndexBlock("test-idx", IndexMetadata.INDEX_READ_BLOCK)));
|
||||||
|
assertThat(mergedSettings.getAsBoolean(IndexMetadata.SETTING_BLOCKS_WRITE, false),
|
||||||
|
is(blocks.hasIndexBlock("test-idx", IndexMetadata.INDEX_WRITE_BLOCK)));
|
||||||
|
assertThat(mergedSettings.getAsBoolean(IndexMetadata.SETTING_READ_ONLY, false),
|
||||||
|
is(blocks.hasIndexBlock("test-idx", IndexMetadata.INDEX_READ_ONLY_BLOCK)));
|
||||||
|
} finally {
|
||||||
|
logger.info("--> cleaning up blocks");
|
||||||
|
disableIndexBlock("test-idx", IndexMetadata.SETTING_BLOCKS_METADATA);
|
||||||
|
disableIndexBlock("test-idx", IndexMetadata.SETTING_READ_ONLY);
|
||||||
|
disableIndexBlock("test-idx", IndexMetadata.SETTING_BLOCKS_WRITE);
|
||||||
|
disableIndexBlock("test-idx", IndexMetadata.SETTING_BLOCKS_READ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testForbidDisableSoftDeletesDuringRestore() throws Exception {
|
||||||
|
createRepository("test-repo", "fs");
|
||||||
|
final Settings.Builder settings = Settings.builder();
|
||||||
|
if (randomBoolean()) {
|
||||||
|
settings.put(INDEX_SOFT_DELETES_SETTING.getKey(), true);
|
||||||
|
}
|
||||||
|
createIndex("test-index", settings.build());
|
||||||
|
ensureGreen();
|
||||||
|
if (randomBoolean()) {
|
||||||
|
indexRandomDocs("test-index", between(0, 100));
|
||||||
|
flush("test-index");
|
||||||
|
}
|
||||||
|
clusterAdmin().prepareCreateSnapshot("test-repo", "snapshot-0")
|
||||||
|
.setIndices("test-index").setWaitForCompletion(true).get();
|
||||||
|
final SnapshotRestoreException restoreError = expectThrows(SnapshotRestoreException.class,
|
||||||
|
() -> clusterAdmin().prepareRestoreSnapshot("test-repo", "snapshot-0")
|
||||||
|
.setIndexSettings(Settings.builder().put(INDEX_SOFT_DELETES_SETTING.getKey(), false))
|
||||||
|
.setRenamePattern("test-index").setRenameReplacement("new-index")
|
||||||
|
.get());
|
||||||
|
assertThat(restoreError.getMessage(), containsString("cannot disable setting [index.soft_deletes.enabled] on restore"));
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* 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.snapshots;
|
||||||
|
|
||||||
|
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
||||||
|
import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
|
||||||
|
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStatus;
|
||||||
|
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
|
||||||
|
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse;
|
||||||
|
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
|
||||||
|
import org.elasticsearch.action.ingest.DeletePipelineRequest;
|
||||||
|
import org.elasticsearch.action.ingest.GetPipelineResponse;
|
||||||
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
|
import org.elasticsearch.ingest.IngestTestPlugin;
|
||||||
|
import org.elasticsearch.plugins.Plugin;
|
||||||
|
import org.elasticsearch.script.MockScriptEngine;
|
||||||
|
import org.elasticsearch.script.StoredScriptsIT;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertIndexTemplateExists;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertIndexTemplateMissing;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
|
||||||
|
public class SnapshotCustomPluginStateIT extends AbstractSnapshotIntegTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Class<? extends Plugin>> nodePlugins() {
|
||||||
|
return Arrays.asList(IngestTestPlugin.class, StoredScriptsIT.CustomScriptPlugin.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIncludeGlobalState() throws Exception {
|
||||||
|
createRepository("test-repo", "fs");
|
||||||
|
|
||||||
|
boolean testTemplate = randomBoolean();
|
||||||
|
boolean testPipeline = randomBoolean();
|
||||||
|
boolean testScript = (testTemplate == false && testPipeline == false) || randomBoolean(); // At least something should be stored
|
||||||
|
|
||||||
|
if(testTemplate) {
|
||||||
|
logger.info("--> creating test template");
|
||||||
|
assertThat(client().admin().indices()
|
||||||
|
.preparePutTemplate("test-template")
|
||||||
|
.setPatterns(Collections.singletonList("te*"))
|
||||||
|
.addMapping("_doc", XContentFactory.jsonBuilder()
|
||||||
|
.startObject()
|
||||||
|
.startObject("_doc")
|
||||||
|
.startObject("properties")
|
||||||
|
.startObject("field1")
|
||||||
|
.field("type", "text")
|
||||||
|
.field("store", true)
|
||||||
|
.endObject()
|
||||||
|
.startObject("field2")
|
||||||
|
.field("type", "keyword")
|
||||||
|
.field("store", true)
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endObject())
|
||||||
|
.get().isAcknowledged(), equalTo(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(testPipeline) {
|
||||||
|
logger.info("--> creating test pipeline");
|
||||||
|
BytesReference pipelineSource = BytesReference.bytes(jsonBuilder().startObject()
|
||||||
|
.field("description", "my_pipeline")
|
||||||
|
.startArray("processors")
|
||||||
|
.startObject()
|
||||||
|
.startObject("test")
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endArray()
|
||||||
|
.endObject());
|
||||||
|
assertAcked(clusterAdmin().preparePutPipeline("barbaz", pipelineSource, XContentType.JSON).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(testScript) {
|
||||||
|
logger.info("--> creating test script");
|
||||||
|
assertAcked(clusterAdmin().preparePutStoredScript()
|
||||||
|
.setId("foobar")
|
||||||
|
.setContent(new BytesArray(
|
||||||
|
"{\"script\": { \"lang\": \"" + MockScriptEngine.NAME + "\", \"source\": \"1\"} }"), XContentType.JSON));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> snapshot without global state");
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = clusterAdmin()
|
||||||
|
.prepareCreateSnapshot("test-repo", "test-snap-no-global-state").setIndices().setIncludeGlobalState(false)
|
||||||
|
.setWaitForCompletion(true).get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().totalShards(), equalTo(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), equalTo(0));
|
||||||
|
assertThat(getSnapshot("test-repo", "test-snap-no-global-state").state(), equalTo(SnapshotState.SUCCESS));
|
||||||
|
SnapshotsStatusResponse snapshotsStatusResponse = clusterAdmin().prepareSnapshotStatus("test-repo")
|
||||||
|
.addSnapshots("test-snap-no-global-state").get();
|
||||||
|
assertThat(snapshotsStatusResponse.getSnapshots().size(), equalTo(1));
|
||||||
|
SnapshotStatus snapshotStatus = snapshotsStatusResponse.getSnapshots().get(0);
|
||||||
|
assertThat(snapshotStatus.includeGlobalState(), equalTo(false));
|
||||||
|
|
||||||
|
logger.info("--> snapshot with global state");
|
||||||
|
createSnapshotResponse = clusterAdmin().prepareCreateSnapshot("test-repo", "test-snap-with-global-state")
|
||||||
|
.setIndices().setIncludeGlobalState(true).setWaitForCompletion(true).get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().totalShards(), equalTo(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), equalTo(0));
|
||||||
|
assertThat(getSnapshot("test-repo", "test-snap-with-global-state").state(), equalTo(SnapshotState.SUCCESS));
|
||||||
|
snapshotsStatusResponse = clusterAdmin().prepareSnapshotStatus("test-repo")
|
||||||
|
.addSnapshots("test-snap-with-global-state").get();
|
||||||
|
assertThat(snapshotsStatusResponse.getSnapshots().size(), equalTo(1));
|
||||||
|
snapshotStatus = snapshotsStatusResponse.getSnapshots().get(0);
|
||||||
|
assertThat(snapshotStatus.includeGlobalState(), equalTo(true));
|
||||||
|
|
||||||
|
if (testTemplate) {
|
||||||
|
logger.info("--> delete test template");
|
||||||
|
cluster().wipeTemplates("test-template");
|
||||||
|
GetIndexTemplatesResponse getIndexTemplatesResponse = client().admin().indices().prepareGetTemplates().get();
|
||||||
|
assertIndexTemplateMissing(getIndexTemplatesResponse, "test-template");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testPipeline) {
|
||||||
|
logger.info("--> delete test pipeline");
|
||||||
|
assertAcked(clusterAdmin().deletePipeline(new DeletePipelineRequest("barbaz")).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testScript) {
|
||||||
|
logger.info("--> delete test script");
|
||||||
|
assertAcked(clusterAdmin().prepareDeleteStoredScript("foobar").get());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("--> try restoring cluster state from snapshot without global state");
|
||||||
|
RestoreSnapshotResponse restoreSnapshotResponse = clusterAdmin()
|
||||||
|
.prepareRestoreSnapshot("test-repo", "test-snap-no-global-state")
|
||||||
|
.setWaitForCompletion(true).setRestoreGlobalState(true).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), equalTo(0));
|
||||||
|
|
||||||
|
logger.info("--> check that template wasn't restored");
|
||||||
|
GetIndexTemplatesResponse getIndexTemplatesResponse = client().admin().indices().prepareGetTemplates().get();
|
||||||
|
assertIndexTemplateMissing(getIndexTemplatesResponse, "test-template");
|
||||||
|
|
||||||
|
logger.info("--> restore cluster state");
|
||||||
|
restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "test-snap-with-global-state")
|
||||||
|
.setWaitForCompletion(true).setRestoreGlobalState(true).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), equalTo(0));
|
||||||
|
|
||||||
|
if (testTemplate) {
|
||||||
|
logger.info("--> check that template is restored");
|
||||||
|
getIndexTemplatesResponse = client().admin().indices().prepareGetTemplates().get();
|
||||||
|
assertIndexTemplateExists(getIndexTemplatesResponse, "test-template");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testPipeline) {
|
||||||
|
logger.info("--> check that pipeline is restored");
|
||||||
|
GetPipelineResponse getPipelineResponse = clusterAdmin().prepareGetPipeline("barbaz").get();
|
||||||
|
assertTrue(getPipelineResponse.isFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testScript) {
|
||||||
|
logger.info("--> check that script is restored");
|
||||||
|
GetStoredScriptResponse getStoredScriptResponse = clusterAdmin().prepareGetStoredScript("foobar").get();
|
||||||
|
assertNotNull(getStoredScriptResponse.getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
createIndexWithRandomDocs("test-idx", 100);
|
||||||
|
|
||||||
|
logger.info("--> snapshot without global state but with indices");
|
||||||
|
createSnapshotResponse = clusterAdmin().prepareCreateSnapshot("test-repo", "test-snap-no-global-state-with-index")
|
||||||
|
.setIndices("test-idx").setIncludeGlobalState(false).setWaitForCompletion(true).get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().totalShards(), greaterThan(0));
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
|
||||||
|
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
|
assertThat(getSnapshot("test-repo", "test-snap-no-global-state-with-index").state(), equalTo(SnapshotState.SUCCESS));
|
||||||
|
|
||||||
|
logger.info("--> delete global state and index ");
|
||||||
|
cluster().wipeIndices("test-idx");
|
||||||
|
if (testTemplate) {
|
||||||
|
cluster().wipeTemplates("test-template");
|
||||||
|
}
|
||||||
|
if (testPipeline) {
|
||||||
|
assertAcked(clusterAdmin().deletePipeline(new DeletePipelineRequest("barbaz")).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testScript) {
|
||||||
|
assertAcked(clusterAdmin().prepareDeleteStoredScript("foobar").get());
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndexTemplatesResponse = client().admin().indices().prepareGetTemplates().get();
|
||||||
|
assertIndexTemplateMissing(getIndexTemplatesResponse, "test-template");
|
||||||
|
|
||||||
|
logger.info("--> try restoring index and cluster state from snapshot without global state");
|
||||||
|
restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "test-snap-no-global-state-with-index")
|
||||||
|
.setWaitForCompletion(true).setRestoreGlobalState(true).execute().actionGet();
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0));
|
||||||
|
assertThat(restoreSnapshotResponse.getRestoreInfo().failedShards(), equalTo(0));
|
||||||
|
|
||||||
|
logger.info("--> check that global state wasn't restored but index was");
|
||||||
|
getIndexTemplatesResponse = client().admin().indices().prepareGetTemplates().get();
|
||||||
|
assertIndexTemplateMissing(getIndexTemplatesResponse, "test-template");
|
||||||
|
assertFalse(clusterAdmin().prepareGetPipeline("barbaz").get().isFound());
|
||||||
|
assertNull(clusterAdmin().prepareGetStoredScript("foobar").get().getSource());
|
||||||
|
assertDocCount("test-idx", 100L);
|
||||||
|
}
|
||||||
|
}
|
@ -22,23 +22,34 @@ import org.elasticsearch.Version;
|
|||||||
import org.elasticsearch.action.ActionFuture;
|
import org.elasticsearch.action.ActionFuture;
|
||||||
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
||||||
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
|
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
|
||||||
|
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse;
|
||||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotIndexShardStage;
|
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotIndexShardStage;
|
||||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotIndexShardStatus;
|
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotIndexShardStatus;
|
||||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStats;
|
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStats;
|
||||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStatus;
|
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStatus;
|
||||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
|
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
|
||||||
|
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.cluster.SnapshotsInProgress;
|
import org.elasticsearch.cluster.SnapshotsInProgress;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.core.internal.io.IOUtils;
|
import org.elasticsearch.core.internal.io.IOUtils;
|
||||||
import org.elasticsearch.repositories.blobstore.BlobStoreRepository;
|
import org.elasticsearch.repositories.blobstore.BlobStoreRepository;
|
||||||
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.greaterThan;
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
@ -46,6 +57,13 @@ import static org.hamcrest.Matchers.is;
|
|||||||
|
|
||||||
public class SnapshotStatusApisIT extends AbstractSnapshotIntegTestCase {
|
public class SnapshotStatusApisIT extends AbstractSnapshotIntegTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Settings nodeSettings(int nodeOrdinal) {
|
||||||
|
return Settings.builder().put(super.nodeSettings(nodeOrdinal))
|
||||||
|
.put(ThreadPool.ESTIMATED_TIME_INTERVAL_SETTING.getKey(), 0) // We have tests that check by-timestamp order
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
public void testStatusApiConsistency() {
|
public void testStatusApiConsistency() {
|
||||||
Client client = client();
|
Client client = client();
|
||||||
|
|
||||||
@ -264,6 +282,158 @@ public class SnapshotStatusApisIT extends AbstractSnapshotIntegTestCase {
|
|||||||
assertThat(responseSnapshotTwo.get().getSnapshotInfo().state(), is(SnapshotState.SUCCESS));
|
assertThat(responseSnapshotTwo.get().getSnapshotInfo().state(), is(SnapshotState.SUCCESS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSnapshotStatusOnFailedSnapshot() throws Exception {
|
||||||
|
String repoName = "test-repo";
|
||||||
|
createRepository(repoName, "fs");
|
||||||
|
final String snapshot = "test-snap-1";
|
||||||
|
addBwCFailedSnapshot(repoName, snapshot, Collections.emptyMap());
|
||||||
|
|
||||||
|
logger.info("--> creating good index");
|
||||||
|
assertAcked(prepareCreate("test-idx-good").setSettings(indexSettingsNoReplicas(1)));
|
||||||
|
ensureGreen();
|
||||||
|
indexRandomDocs("test-idx-good", randomIntBetween(1, 5));
|
||||||
|
|
||||||
|
final SnapshotsStatusResponse snapshotsStatusResponse =
|
||||||
|
client().admin().cluster().prepareSnapshotStatus(repoName).setSnapshots(snapshot).get();
|
||||||
|
assertEquals(1, snapshotsStatusResponse.getSnapshots().size());
|
||||||
|
assertEquals(SnapshotsInProgress.State.FAILED, snapshotsStatusResponse.getSnapshots().get(0).getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetSnapshotsRequest() throws Exception {
|
||||||
|
final String repositoryName = "test-repo";
|
||||||
|
final String indexName = "test-idx";
|
||||||
|
final Client client = client();
|
||||||
|
|
||||||
|
createRepository(repositoryName, "mock", Settings.builder()
|
||||||
|
.put("location", randomRepoPath()).put("compress", false)
|
||||||
|
.put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES).put("wait_after_unblock", 200));
|
||||||
|
|
||||||
|
logger.info("--> get snapshots on an empty repository");
|
||||||
|
expectThrows(SnapshotMissingException.class, () -> client.admin()
|
||||||
|
.cluster()
|
||||||
|
.prepareGetSnapshots(repositoryName)
|
||||||
|
.addSnapshots("non-existent-snapshot")
|
||||||
|
.get());
|
||||||
|
// with ignore unavailable set to true, should not throw an exception
|
||||||
|
GetSnapshotsResponse getSnapshotsResponse = client.admin()
|
||||||
|
.cluster()
|
||||||
|
.prepareGetSnapshots(repositoryName)
|
||||||
|
.setIgnoreUnavailable(true)
|
||||||
|
.addSnapshots("non-existent-snapshot")
|
||||||
|
.get();
|
||||||
|
assertThat(getSnapshotsResponse.getSnapshots().size(), equalTo(0));
|
||||||
|
|
||||||
|
logger.info("--> creating an index and indexing documents");
|
||||||
|
// Create index on 2 nodes and make sure each node has a primary by setting no replicas
|
||||||
|
assertAcked(prepareCreate(indexName, 1, Settings.builder().put("number_of_replicas", 0)));
|
||||||
|
ensureGreen();
|
||||||
|
indexRandomDocs(indexName, 10);
|
||||||
|
|
||||||
|
// make sure we return only the in-progress snapshot when taking the first snapshot on a clean repository
|
||||||
|
// take initial snapshot with a block, making sure we only get 1 in-progress snapshot returned
|
||||||
|
// block a node so the create snapshot operation can remain in progress
|
||||||
|
final String initialBlockedNode = blockNodeWithIndex(repositoryName, indexName);
|
||||||
|
client.admin().cluster().prepareCreateSnapshot(repositoryName, "snap-on-empty-repo")
|
||||||
|
.setWaitForCompletion(false)
|
||||||
|
.setIndices(indexName)
|
||||||
|
.get();
|
||||||
|
waitForBlock(initialBlockedNode, repositoryName, TimeValue.timeValueSeconds(60)); // wait for block to kick in
|
||||||
|
getSnapshotsResponse = client.admin().cluster()
|
||||||
|
.prepareGetSnapshots("test-repo")
|
||||||
|
.setSnapshots(randomFrom("_all", "_current", "snap-on-*", "*-on-empty-repo", "snap-on-empty-repo"))
|
||||||
|
.get();
|
||||||
|
assertEquals(1, getSnapshotsResponse.getSnapshots().size());
|
||||||
|
assertEquals("snap-on-empty-repo", getSnapshotsResponse.getSnapshots().get(0).snapshotId().getName());
|
||||||
|
unblockNode(repositoryName, initialBlockedNode); // unblock node
|
||||||
|
admin().cluster().prepareDeleteSnapshot(repositoryName, "snap-on-empty-repo").get();
|
||||||
|
|
||||||
|
final int numSnapshots = randomIntBetween(1, 3) + 1;
|
||||||
|
logger.info("--> take {} snapshot(s)", numSnapshots - 1);
|
||||||
|
final String[] snapshotNames = new String[numSnapshots];
|
||||||
|
for (int i = 0; i < numSnapshots - 1; i++) {
|
||||||
|
final String snapshotName = randomAlphaOfLength(8).toLowerCase(Locale.ROOT);
|
||||||
|
CreateSnapshotResponse createSnapshotResponse = client.admin()
|
||||||
|
.cluster()
|
||||||
|
.prepareCreateSnapshot(repositoryName, snapshotName)
|
||||||
|
.setWaitForCompletion(true)
|
||||||
|
.setIndices(indexName)
|
||||||
|
.get();
|
||||||
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
|
snapshotNames[i] = snapshotName;
|
||||||
|
}
|
||||||
|
logger.info("--> take another snapshot to be in-progress");
|
||||||
|
// add documents so there are data files to block on
|
||||||
|
for (int i = 10; i < 20; i++) {
|
||||||
|
index(indexName, "_doc", Integer.toString(i), "foo", "bar" + i);
|
||||||
|
}
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
final String inProgressSnapshot = randomAlphaOfLength(8).toLowerCase(Locale.ROOT);
|
||||||
|
snapshotNames[numSnapshots - 1] = inProgressSnapshot;
|
||||||
|
// block a node so the create snapshot operation can remain in progress
|
||||||
|
final String blockedNode = blockNodeWithIndex(repositoryName, indexName);
|
||||||
|
client.admin().cluster().prepareCreateSnapshot(repositoryName, inProgressSnapshot)
|
||||||
|
.setWaitForCompletion(false)
|
||||||
|
.setIndices(indexName)
|
||||||
|
.get();
|
||||||
|
waitForBlock(blockedNode, repositoryName, TimeValue.timeValueSeconds(60)); // wait for block to kick in
|
||||||
|
|
||||||
|
logger.info("--> get all snapshots with a current in-progress");
|
||||||
|
// with ignore unavailable set to true, should not throw an exception
|
||||||
|
final List<String> snapshotsToGet = new ArrayList<>();
|
||||||
|
if (randomBoolean()) {
|
||||||
|
// use _current plus the individual names of the finished snapshots
|
||||||
|
snapshotsToGet.add("_current");
|
||||||
|
for (int i = 0; i < numSnapshots - 1; i++) {
|
||||||
|
snapshotsToGet.add(snapshotNames[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snapshotsToGet.add("_all");
|
||||||
|
}
|
||||||
|
getSnapshotsResponse = client.admin().cluster()
|
||||||
|
.prepareGetSnapshots(repositoryName)
|
||||||
|
.setSnapshots(snapshotsToGet.toArray(Strings.EMPTY_ARRAY))
|
||||||
|
.get();
|
||||||
|
List<String> sortedNames = Arrays.asList(snapshotNames);
|
||||||
|
Collections.sort(sortedNames);
|
||||||
|
assertThat(getSnapshotsResponse.getSnapshots().size(), equalTo(numSnapshots));
|
||||||
|
assertThat(getSnapshotsResponse.getSnapshots().stream()
|
||||||
|
.map(s -> s.snapshotId().getName())
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList()), equalTo(sortedNames));
|
||||||
|
|
||||||
|
getSnapshotsResponse = client.admin().cluster()
|
||||||
|
.prepareGetSnapshots(repositoryName)
|
||||||
|
.addSnapshots(snapshotNames)
|
||||||
|
.get();
|
||||||
|
sortedNames = Arrays.asList(snapshotNames);
|
||||||
|
Collections.sort(sortedNames);
|
||||||
|
assertThat(getSnapshotsResponse.getSnapshots().size(), equalTo(numSnapshots));
|
||||||
|
assertThat(getSnapshotsResponse.getSnapshots().stream()
|
||||||
|
.map(s -> s.snapshotId().getName())
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList()), equalTo(sortedNames));
|
||||||
|
|
||||||
|
logger.info("--> make sure duplicates are not returned in the response");
|
||||||
|
String regexName = snapshotNames[randomIntBetween(0, numSnapshots - 1)];
|
||||||
|
final int splitPos = regexName.length() / 2;
|
||||||
|
final String firstRegex = regexName.substring(0, splitPos) + "*";
|
||||||
|
final String secondRegex = "*" + regexName.substring(splitPos);
|
||||||
|
getSnapshotsResponse = client.admin().cluster()
|
||||||
|
.prepareGetSnapshots(repositoryName)
|
||||||
|
.addSnapshots(snapshotNames)
|
||||||
|
.addSnapshots(firstRegex, secondRegex)
|
||||||
|
.get();
|
||||||
|
assertThat(getSnapshotsResponse.getSnapshots().size(), equalTo(numSnapshots));
|
||||||
|
assertThat(getSnapshotsResponse.getSnapshots().stream()
|
||||||
|
.map(s -> s.snapshotId().getName())
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList()), equalTo(sortedNames));
|
||||||
|
|
||||||
|
unblockNode(repositoryName, blockedNode); // unblock node
|
||||||
|
waitForCompletion(repositoryName, inProgressSnapshot, TimeValue.timeValueSeconds(60));
|
||||||
|
}
|
||||||
|
|
||||||
private static SnapshotIndexShardStatus stateFirstShard(SnapshotStatus snapshotStatus, String indexName) {
|
private static SnapshotIndexShardStatus stateFirstShard(SnapshotStatus snapshotStatus, String indexName) {
|
||||||
return snapshotStatus.getIndices().get(indexName).getShards().get(0);
|
return snapshotStatus.getIndices().get(indexName).getShards().get(0);
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,13 @@
|
|||||||
package org.elasticsearch.snapshots;
|
package org.elasticsearch.snapshots;
|
||||||
|
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.action.ActionFuture;
|
||||||
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
||||||
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
|
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
|
||||||
import org.elasticsearch.action.index.IndexRequestBuilder;
|
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||||
import org.elasticsearch.action.search.SearchRequest;
|
import org.elasticsearch.action.search.SearchRequest;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
|
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.ClusterStateObserver;
|
import org.elasticsearch.cluster.ClusterStateObserver;
|
||||||
import org.elasticsearch.cluster.SnapshotDeletionsInProgress;
|
import org.elasticsearch.cluster.SnapshotDeletionsInProgress;
|
||||||
@ -81,6 +83,7 @@ import java.util.function.Predicate;
|
|||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
public abstract class AbstractSnapshotIntegTestCase extends ESIntegTestCase {
|
public abstract class AbstractSnapshotIntegTestCase extends ESIntegTestCase {
|
||||||
@ -444,6 +447,11 @@ public abstract class AbstractSnapshotIntegTestCase extends ESIntegTestCase {
|
|||||||
awaitClusterState(internalCluster().getMasterName(), statePredicate);
|
awaitClusterState(internalCluster().getMasterName(), statePredicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ActionFuture<AcknowledgedResponse> startDeleteSnapshot(String repoName, String snapshotName) {
|
||||||
|
logger.info("--> deleting snapshot [{}] from repo [{}]", snapshotName, repoName);
|
||||||
|
return clusterAdmin().prepareDeleteSnapshot(repoName, snapshotName).execute();
|
||||||
|
}
|
||||||
|
|
||||||
protected void awaitClusterState(String viaNode, Predicate<ClusterState> statePredicate) throws Exception {
|
protected void awaitClusterState(String viaNode, Predicate<ClusterState> statePredicate) throws Exception {
|
||||||
final ClusterService clusterService = internalCluster().getInstance(ClusterService.class, viaNode);
|
final ClusterService clusterService = internalCluster().getInstance(ClusterService.class, viaNode);
|
||||||
final ThreadPool threadPool = internalCluster().getInstance(ThreadPool.class, viaNode);
|
final ThreadPool threadPool = internalCluster().getInstance(ThreadPool.class, viaNode);
|
||||||
@ -469,4 +477,17 @@ public abstract class AbstractSnapshotIntegTestCase extends ESIntegTestCase {
|
|||||||
future.get(30L, TimeUnit.SECONDS);
|
future.get(30L, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static SnapshotInfo assertSuccessful(ActionFuture<CreateSnapshotResponse> future) throws Exception {
|
||||||
|
final SnapshotInfo snapshotInfo = future.get().getSnapshotInfo();
|
||||||
|
assertThat(snapshotInfo.state(), is(SnapshotState.SUCCESS));
|
||||||
|
return snapshotInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SnapshotInfo getSnapshot(String repository, String snapshot) {
|
||||||
|
final List<SnapshotInfo> snapshotInfos = clusterAdmin().prepareGetSnapshots(repository).setSnapshots(snapshot)
|
||||||
|
.get().getSnapshots();
|
||||||
|
assertThat(snapshotInfos, hasSize(1));
|
||||||
|
return snapshotInfos.get(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ import org.elasticsearch.action.support.DefaultShardOperationFailedException;
|
|||||||
import org.elasticsearch.action.support.IndicesOptions;
|
import org.elasticsearch.action.support.IndicesOptions;
|
||||||
import org.elasticsearch.client.AdminClient;
|
import org.elasticsearch.client.AdminClient;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
|
import org.elasticsearch.client.ClusterAdminClient;
|
||||||
import org.elasticsearch.client.Requests;
|
import org.elasticsearch.client.Requests;
|
||||||
import org.elasticsearch.client.RestClient;
|
import org.elasticsearch.client.RestClient;
|
||||||
import org.elasticsearch.client.RestClientBuilder;
|
import org.elasticsearch.client.RestClientBuilder;
|
||||||
@ -1428,6 +1429,13 @@ public abstract class ESIntegTestCase extends ESTestCase {
|
|||||||
return client().admin();
|
return client().admin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a random cluster admin client. This client can be pointing to any of the nodes in the cluster.
|
||||||
|
*/
|
||||||
|
protected ClusterAdminClient clusterAdmin() {
|
||||||
|
return admin().cluster();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method that forwards to {@link #indexRandom(boolean, List)}.
|
* Convenience method that forwards to {@link #indexRandom(boolean, List)}.
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user