From 60b317caa48f0ab8aebd9bc153b9d8206bdbc072 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Wed, 2 Jul 2014 12:42:39 -0400 Subject: [PATCH] Snapshot/Restore: Add ability to restore indices without their aliases Closes #6457 --- docs/reference/modules/snapshots.asciidoc | 3 +- .../restore/RestoreSnapshotRequest.java | 26 +++++++++ .../RestoreSnapshotRequestBuilder.java | 19 +++++-- .../TransportRestoreSnapshotAction.java | 3 +- .../cluster/metadata/IndexMetaData.java | 5 ++ .../snapshots/RestoreService.java | 28 ++++++++- .../SharedClusterSnapshotRestoreTests.java | 57 +++++++++++++++++++ .../hamcrest/ElasticsearchAssertions.java | 15 +++++ 8 files changed, 149 insertions(+), 7 deletions(-) diff --git a/docs/reference/modules/snapshots.asciidoc b/docs/reference/modules/snapshots.asciidoc index 71837db0acd..2a6b1e40213 100644 --- a/docs/reference/modules/snapshots.asciidoc +++ b/docs/reference/modules/snapshots.asciidoc @@ -189,6 +189,7 @@ should be restored as well as prevent global cluster state from being restored b <>. The `rename_pattern` and `rename_replacement` options can be also used to rename index on restore using regular expression that supports referencing the original text as explained http://docs.oracle.com/javase/6/docs/api/java/util/regex/Matcher.html#appendReplacement(java.lang.StringBuffer,%20java.lang.String)[here]. +Set `include_aliases` to `false` to prevent aliases from being restored together with associated indices coming[1.3.0]. [source,js] ----------------------------------- @@ -210,7 +211,7 @@ persistent settings are added to the existing persistent settings. [float] === Partial restore -added[1.3.0] +coming[1.3.0] By default, entire restore operation will fail if one or more indices participating in the operation don't have snapshots of all shards available. It can occur if some shards failed to snapshot for example. It is still possible to diff --git a/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java b/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java index e03c962d30a..42fe0d08cd1 100644 --- a/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java +++ b/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java @@ -70,6 +70,8 @@ public class RestoreSnapshotRequest extends MasterNodeOperationRequest) entry.getValue()); } else if (name.equals("include_global_state")) { includeGlobalState = nodeBooleanValue(entry.getValue()); + } else if (name.equals("include_aliases")) { + includeAliases = nodeBooleanValue(entry.getValue()); } else if (name.equals("rename_pattern")) { if (entry.getValue() instanceof String) { renamePattern((String) entry.getValue()); @@ -538,6 +562,7 @@ public class RestoreSnapshotRequest extends MasterNodeOperationRequest listener) { client.restoreSnapshot(request, listener); diff --git a/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java b/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java index a24a882a6c5..580832437ef 100644 --- a/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java +++ b/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java @@ -78,7 +78,8 @@ public class TransportRestoreSnapshotAction extends TransportMasterNodeOperation RestoreService.RestoreRequest restoreRequest = new RestoreService.RestoreRequest( "restore_snapshot[" + request.snapshot() + "]", request.repository(), request.snapshot(), request.indices(), request.indicesOptions(), request.renamePattern(), request.renameReplacement(), - request.settings(), request.masterNodeTimeout(), request.includeGlobalState(), request.partial()); + request.settings(), request.masterNodeTimeout(), request.includeGlobalState(), request.partial(), request.includeAliases()); + restoreService.restoreSnapshot(restoreRequest, new RestoreSnapshotListener() { @Override public void onResponse(RestoreInfo restoreInfo) { diff --git a/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java b/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java index 4aecfe07b9a..b1c9f83eecf 100644 --- a/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java +++ b/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java @@ -509,6 +509,11 @@ public class IndexMetaData { return this; } + public Builder removeAllAliases() { + aliases.clear(); + return this; + } + public Builder putCustom(String type, Custom customIndexMetaData) { this.customs.put(type, customIndexMetaData); return this; diff --git a/src/main/java/org/elasticsearch/snapshots/RestoreService.java b/src/main/java/org/elasticsearch/snapshots/RestoreService.java index e572ba9d7d0..8047081c3c5 100644 --- a/src/main/java/org/elasticsearch/snapshots/RestoreService.java +++ b/src/main/java/org/elasticsearch/snapshots/RestoreService.java @@ -161,6 +161,10 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis // Make sure that the index we are about to create has a validate name createIndexService.validateIndexName(renamedIndex, currentState); IndexMetaData.Builder indexMdBuilder = IndexMetaData.builder(snapshotIndexMetaData).state(IndexMetaData.State.OPEN).index(renamedIndex); + if (!request.includeAliases() && !snapshotIndexMetaData.aliases().isEmpty()) { + // Remove all aliases - they shouldn't be restored + indexMdBuilder.removeAllAliases(); + } IndexMetaData updatedIndexMetaData = indexMdBuilder.build(); if (partial) { populateIgnoredShards(index, ignoreShards); @@ -172,6 +176,16 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis // Index exists and it's closed - open it in metadata and start recovery IndexMetaData.Builder indexMdBuilder = IndexMetaData.builder(snapshotIndexMetaData).state(IndexMetaData.State.OPEN); indexMdBuilder.version(Math.max(snapshotIndexMetaData.version(), currentIndexMetaData.version() + 1)); + if (!request.includeAliases()) { + // Remove all snapshot aliases + if (!snapshotIndexMetaData.aliases().isEmpty()) { + indexMdBuilder.removeAllAliases(); + } + /// Add existing aliases + for (ObjectCursor alias : currentIndexMetaData.aliases().values()) { + indexMdBuilder.putAlias(alias.value); + } + } IndexMetaData updatedIndexMetaData = indexMdBuilder.index(renamedIndex).build(); rtBuilder.addAsRestore(updatedIndexMetaData, restoreSource); blocks.removeIndexBlock(renamedIndex, INDEX_CLOSED_BLOCK); @@ -553,6 +567,8 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis final private boolean partial; + final private boolean includeAliases; + /** * Constructs new restore request * @@ -570,7 +586,7 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis */ public RestoreRequest(String cause, String repository, String name, String[] indices, IndicesOptions indicesOptions, String renamePattern, String renameReplacement, Settings settings, - TimeValue masterNodeTimeout, boolean includeGlobalState, boolean partial) { + TimeValue masterNodeTimeout, boolean includeGlobalState, boolean partial, boolean includeAliases) { this.cause = cause; this.name = name; this.repository = repository; @@ -582,6 +598,7 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis this.masterNodeTimeout = masterNodeTimeout; this.includeGlobalState = includeGlobalState; this.partial = partial; + this.includeAliases = includeAliases; } /** @@ -674,6 +691,15 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis return partial; } + /** + * Returns true if aliases should be restore during this restore operation + * + * @return restore aliases state flag + */ + public boolean includeAliases() { + return includeAliases; + } + /** * Return master node timeout * diff --git a/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreTests.java b/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreTests.java index 22507ea0869..1085d2d57da 100644 --- a/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreTests.java +++ b/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreTests.java @@ -200,6 +200,63 @@ public class SharedClusterSnapshotRestoreTests extends AbstractSnapshotTests { assertThat(client.admin().cluster().prepareGetSnapshots("test-repo").setSnapshots("test-snap").get().getSnapshots().get(0).state(), equalTo(SnapshotState.SUCCESS)); } + @Test + public void restoreAliasesTest() throws Exception { + Client client = client(); + + logger.info("--> creating repository"); + assertAcked(client.admin().cluster().preparePutRepository("test-repo") + .setType("fs").setSettings(ImmutableSettings.settingsBuilder().put("location", newTempDir()))); + + 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()); + assertAliasesExist(client.admin().indices().prepareAliasesExist("alias-123").get()); + + logger.info("--> snapshot"); + assertThat(client.admin().cluster().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"); + assertAliasesMissing(client.admin().indices().prepareAliasesExist("alias-123", "alias-1").get()); + + logger.info("--> restore snapshot with aliases"); + RestoreSnapshotResponse restoreSnapshotResponse = client.admin().cluster().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"); + assertAliasesExist(client.admin().indices().prepareAliasesExist("alias-123", "alias-1").get()); + + + 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")); + assertAliasesMissing(client.admin().indices().prepareAliasesExist("alias-123", "alias-1").get()); + + logger.info("--> restore snapshot without aliases"); + restoreSnapshotResponse = client.admin().cluster().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"); + assertAliasesMissing(client.admin().indices().prepareAliasesExist("alias-123", "alias-1").get()); + assertAliasesExist(client.admin().indices().prepareAliasesExist("alias-3").get()); + + } + @Test public void restoreTemplatesTest() throws Exception { Client client = client(); diff --git a/src/test/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java b/src/test/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java index b59855b1736..02f597b0721 100644 --- a/src/test/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java +++ b/src/test/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java @@ -38,6 +38,7 @@ import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.cluster.node.info.PluginInfo; import org.elasticsearch.action.admin.cluster.node.info.PluginsInfo; +import org.elasticsearch.action.admin.indices.alias.exists.AliasesExistResponse; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequestBuilder; import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; @@ -364,6 +365,20 @@ public class ElasticsearchAssertions { assertThat(templateNames, hasItem(name)); } + /** + * Assert that aliases are missing + */ + public static void assertAliasesMissing(AliasesExistResponse aliasesExistResponse) { + assertFalse("Aliases shouldn't exist", aliasesExistResponse.exists()); + } + + /** + * Assert that aliases exist + */ + public static void assertAliasesExist(AliasesExistResponse aliasesExistResponse) { + assertTrue("Aliases should exist", aliasesExistResponse.exists()); + } + /* * matchers */