|
|
@@ -28,10 +28,16 @@ import org.elasticsearch.cluster.ClusterStateUpdateTask;
|
|
|
import org.elasticsearch.cluster.metadata.Metadata;
|
|
|
import org.elasticsearch.cluster.metadata.RepositoriesMetadata;
|
|
|
import org.elasticsearch.cluster.service.ClusterService;
|
|
|
+import org.elasticsearch.common.Strings;
|
|
|
import org.elasticsearch.common.bytes.BytesReference;
|
|
|
import org.elasticsearch.common.settings.Settings;
|
|
|
import org.elasticsearch.common.unit.ByteSizeUnit;
|
|
|
+import org.elasticsearch.common.xcontent.DeprecationHandler;
|
|
|
+import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
|
|
+import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
|
|
+import org.elasticsearch.common.xcontent.json.JsonXContent;
|
|
|
+import org.elasticsearch.repositories.IndexId;
|
|
|
import org.elasticsearch.repositories.IndexMetaDataGenerations;
|
|
|
import org.elasticsearch.repositories.RepositoriesService;
|
|
|
import org.elasticsearch.repositories.Repository;
|
|
|
@@ -41,6 +47,7 @@ import org.elasticsearch.repositories.ShardGenerations;
|
|
|
import org.elasticsearch.repositories.blobstore.BlobStoreRepository;
|
|
|
import org.elasticsearch.threadpool.ThreadPool;
|
|
|
|
|
|
+import java.io.IOException;
|
|
|
import java.nio.file.Files;
|
|
|
import java.nio.file.Path;
|
|
|
import java.nio.file.StandardOpenOption;
|
|
|
@@ -50,6 +57,7 @@ import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
|
|
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFileExists;
|
|
|
import static org.hamcrest.Matchers.containsString;
|
|
|
import static org.hamcrest.Matchers.equalTo;
|
|
|
import static org.hamcrest.Matchers.greaterThan;
|
|
|
@@ -304,6 +312,66 @@ public class CorruptedBlobStoreRepositoryIT extends AbstractSnapshotIntegTestCas
|
|
|
expectThrows(RepositoryException.class, () -> getRepositoryData(otherRepo));
|
|
|
}
|
|
|
|
|
|
+ public void testHandleSnapshotErrorWithBwCFormat() throws IOException {
|
|
|
+ final String repoName = "test-repo";
|
|
|
+ final Path repoPath = randomRepoPath();
|
|
|
+ createRepository(repoName, "fs", repoPath);
|
|
|
+
|
|
|
+ // Workaround to simulate BwC situation: taking a snapshot without indices here so that we don't create any new version shard
|
|
|
+ // generations (the existence of which would short-circuit checks for the repo containing old version snapshots)
|
|
|
+ final String oldVersionSnapshot = "old-version-snapshot";
|
|
|
+ final CreateSnapshotResponse createSnapshotResponse = client().admin().cluster()
|
|
|
+ .prepareCreateSnapshot(repoName, oldVersionSnapshot).setIndices().setWaitForCompletion(true).get();
|
|
|
+ assertThat(createSnapshotResponse.getSnapshotInfo().totalShards(), is(0));
|
|
|
+
|
|
|
+ logger.info("--> writing downgraded RepositoryData");
|
|
|
+ final RepositoryData repositoryData = getRepositoryData(repoName);
|
|
|
+ final XContentBuilder jsonBuilder = JsonXContent.contentBuilder();
|
|
|
+ repositoryData.snapshotsToXContent(jsonBuilder, SnapshotsService.OLD_SNAPSHOT_FORMAT);
|
|
|
+ final RepositoryData downgradedRepoData = RepositoryData.snapshotsFromXContent(JsonXContent.jsonXContent.createParser(
|
|
|
+ NamedXContentRegistry.EMPTY,
|
|
|
+ DeprecationHandler.THROW_UNSUPPORTED_OPERATION,
|
|
|
+ Strings.toString(jsonBuilder).replace(Version.CURRENT.toString(), SnapshotsService.OLD_SNAPSHOT_FORMAT.toString())),
|
|
|
+ repositoryData.getGenId());
|
|
|
+ Files.write(repoPath.resolve(BlobStoreRepository.INDEX_FILE_PREFIX + repositoryData.getGenId()),
|
|
|
+ BytesReference.toBytes(BytesReference.bytes(
|
|
|
+ downgradedRepoData.snapshotsToXContent(XContentFactory.jsonBuilder(), SnapshotsService.OLD_SNAPSHOT_FORMAT))),
|
|
|
+ StandardOpenOption.TRUNCATE_EXISTING);
|
|
|
+
|
|
|
+ logger.info("--> recreating repository to clear caches");
|
|
|
+ client().admin().cluster().prepareDeleteRepository(repoName).get();
|
|
|
+ createRepository(repoName, "fs", repoPath);
|
|
|
+
|
|
|
+ final String indexName = "test-index";
|
|
|
+ createIndex(indexName);
|
|
|
+
|
|
|
+ assertCreateSnapshotSuccess(repoName, "snapshot-1");
|
|
|
+
|
|
|
+ // In the old metadata version the shard level metadata could be moved to the next generation for all sorts of reasons, this should
|
|
|
+ // not break subsequent repository operations
|
|
|
+ logger.info("--> move shard level metadata to new generation");
|
|
|
+ final IndexId indexId = getRepositoryData(repoName).resolveIndexId(indexName);
|
|
|
+ final Path shardPath = repoPath.resolve("indices").resolve(indexId.getId()).resolve("0");
|
|
|
+ final Path initialShardMetaPath = shardPath.resolve(BlobStoreRepository.INDEX_FILE_PREFIX + "0");
|
|
|
+ assertFileExists(initialShardMetaPath);
|
|
|
+ Files.move(initialShardMetaPath, shardPath.resolve(BlobStoreRepository.INDEX_FILE_PREFIX + "1"));
|
|
|
+
|
|
|
+ logger.info("--> delete old version snapshot");
|
|
|
+ client().admin().cluster().prepareDeleteSnapshot(repoName, oldVersionSnapshot).get();
|
|
|
+
|
|
|
+ assertCreateSnapshotSuccess(repoName, "snapshot-2");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void assertCreateSnapshotSuccess(String repoName, String snapshotName) {
|
|
|
+ logger.info("--> create another snapshot");
|
|
|
+ final SnapshotInfo snapshotInfo = client().admin().cluster().prepareCreateSnapshot(repoName, snapshotName)
|
|
|
+ .setWaitForCompletion(true).get().getSnapshotInfo();
|
|
|
+ assertThat(snapshotInfo.state(), is(SnapshotState.SUCCESS));
|
|
|
+ final int successfulShards = snapshotInfo.successfulShards();
|
|
|
+ assertThat(successfulShards, greaterThan(0));
|
|
|
+ assertThat(successfulShards, equalTo(snapshotInfo.totalShards()));
|
|
|
+ }
|
|
|
+
|
|
|
private void assertRepositoryBlocked(Client client, String repo, String existingSnapshot) {
|
|
|
logger.info("--> try to delete snapshot");
|
|
|
final RepositoryException repositoryException3 = expectThrows(RepositoryException.class,
|