diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java
index a6122b0681e..b486d9fce52 100644
--- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java
@@ -40,6 +40,7 @@ import org.elasticsearch.action.admin.cluster.settings.ClusterGetSettingsRequest
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
+import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
@@ -980,6 +981,20 @@ final class RequestConverters {
return request;
}
+ static Request restoreSnapshot(RestoreSnapshotRequest restoreSnapshotRequest) throws IOException {
+ String endpoint = new EndpointBuilder().addPathPartAsIs("_snapshot")
+ .addPathPart(restoreSnapshotRequest.repository())
+ .addPathPart(restoreSnapshotRequest.snapshot())
+ .addPathPartAsIs("_restore")
+ .build();
+ Request request = new Request(HttpPost.METHOD_NAME, endpoint);
+ Params parameters = new Params(request);
+ parameters.withMasterTimeout(restoreSnapshotRequest.masterNodeTimeout());
+ parameters.withWaitForCompletion(restoreSnapshotRequest.waitForCompletion());
+ request.setEntity(createEntity(restoreSnapshotRequest, REQUEST_BODY_CONTENT_TYPE));
+ return request;
+ }
+
static Request deleteSnapshot(DeleteSnapshotRequest deleteSnapshotRequest) {
String endpoint = new EndpointBuilder().addPathPartAsIs("_snapshot")
.addPathPart(deleteSnapshotRequest.repository())
diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java
index ae115839bae..319eb96a9f8 100644
--- a/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java
@@ -30,6 +30,8 @@ import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyReposito
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
+import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
+import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
@@ -252,6 +254,36 @@ public final class SnapshotClient {
SnapshotsStatusResponse::fromXContent, listener, emptySet());
}
+ /**
+ * Restores a snapshot.
+ * See Snapshot and Restore
+ * API on elastic.co
+ *
+ * @param restoreSnapshotRequest the request
+ * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+ * @return the response
+ * @throws IOException in case there is a problem sending the request or parsing back the response
+ */
+ public RestoreSnapshotResponse restore(RestoreSnapshotRequest restoreSnapshotRequest, RequestOptions options) throws IOException {
+ return restHighLevelClient.performRequestAndParseEntity(restoreSnapshotRequest, RequestConverters::restoreSnapshot, options,
+ RestoreSnapshotResponse::fromXContent, emptySet());
+ }
+
+ /**
+ * Asynchronously restores a snapshot.
+ * See Snapshot and Restore
+ * API on elastic.co
+ *
+ * @param restoreSnapshotRequest the request
+ * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+ * @param listener the listener to be notified upon request completion
+ */
+ public void restoreAsync(RestoreSnapshotRequest restoreSnapshotRequest, RequestOptions options,
+ ActionListener listener) {
+ restHighLevelClient.performRequestAsyncAndParseEntity(restoreSnapshotRequest, RequestConverters::restoreSnapshot, options,
+ RestoreSnapshotResponse::fromXContent, listener, emptySet());
+ }
+
/**
* Deletes a snapshot.
* See Snapshot and Restore
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java
index c1f47feb33d..0415d363c54 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java
@@ -41,6 +41,7 @@ import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequ
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
+import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
@@ -2198,6 +2199,31 @@ public class RequestConvertersTests extends ESTestCase {
assertThat(request.getEntity(), is(nullValue()));
}
+ public void testRestoreSnapshot() throws IOException {
+ Map expectedParams = new HashMap<>();
+ String repository = randomIndicesNames(1, 1)[0];
+ String snapshot = "snapshot-" + randomAlphaOfLengthBetween(2, 5).toLowerCase(Locale.ROOT);
+ String endpoint = String.format(Locale.ROOT, "/_snapshot/%s/%s/_restore", repository, snapshot);
+
+ RestoreSnapshotRequest restoreSnapshotRequest = new RestoreSnapshotRequest(repository, snapshot);
+ setRandomMasterTimeout(restoreSnapshotRequest, expectedParams);
+ if (randomBoolean()) {
+ restoreSnapshotRequest.waitForCompletion(true);
+ expectedParams.put("wait_for_completion", "true");
+ }
+ if (randomBoolean()) {
+ String timeout = randomTimeValue();
+ restoreSnapshotRequest.masterNodeTimeout(timeout);
+ expectedParams.put("master_timeout", timeout);
+ }
+
+ Request request = RequestConverters.restoreSnapshot(restoreSnapshotRequest);
+ assertThat(endpoint, equalTo(request.getEndpoint()));
+ assertThat(HttpPost.METHOD_NAME, equalTo(request.getMethod()));
+ assertThat(expectedParams, equalTo(request.getParameters()));
+ assertToXContentBody(restoreSnapshotRequest, request.getEntity());
+ }
+
public void testDeleteSnapshot() {
Map expectedParams = new HashMap<>();
String repository = randomIndicesNames(1, 1)[0];
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java
index 48934a9bed8..5cf3b352756 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java
@@ -665,7 +665,6 @@ public class RestHighLevelClientTests extends ESTestCase {
"reindex_rethrottle",
"render_search_template",
"scripts_painless_execute",
- "snapshot.restore",
"tasks.get",
"termvectors",
"update_by_query"
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java
index 5483f055c2c..06aec70a018 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java
@@ -28,6 +28,8 @@ import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequ
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
+import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
+import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
import org.elasticsearch.common.settings.Settings;
@@ -40,12 +42,15 @@ import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.repositories.fs.FsRepository;
import org.elasticsearch.rest.RestStatus;
+import org.elasticsearch.snapshots.RestoreInfo;
import java.io.IOException;
+import java.util.Collections;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
public class SnapshotIT extends ESRestHighLevelClientTestCase {
@@ -205,6 +210,42 @@ public class SnapshotIT extends ESRestHighLevelClientTestCase {
assertThat(response.getSnapshots().get(0).getIndices().containsKey(testIndex), is(true));
}
+ public void testRestoreSnapshot() throws IOException {
+ String testRepository = "test";
+ String testSnapshot = "snapshot_1";
+ String testIndex = "test_index";
+ String restoredIndex = testIndex + "_restored";
+
+ PutRepositoryResponse putRepositoryResponse = createTestRepository(testRepository, FsRepository.TYPE, "{\"location\": \".\"}");
+ assertTrue(putRepositoryResponse.isAcknowledged());
+
+ createIndex(testIndex, Settings.EMPTY);
+ assertTrue("index [" + testIndex + "] should have been created", indexExists(testIndex));
+
+ CreateSnapshotRequest createSnapshotRequest = new CreateSnapshotRequest(testRepository, testSnapshot);
+ createSnapshotRequest.indices(testIndex);
+ createSnapshotRequest.waitForCompletion(true);
+ CreateSnapshotResponse createSnapshotResponse = createTestSnapshot(createSnapshotRequest);
+ assertEquals(RestStatus.OK, createSnapshotResponse.status());
+
+ deleteIndex(testIndex);
+ assertFalse("index [" + testIndex + "] should have been deleted", indexExists(testIndex));
+
+ RestoreSnapshotRequest request = new RestoreSnapshotRequest(testRepository, testSnapshot);
+ request.waitForCompletion(true);
+ request.renamePattern(testIndex);
+ request.renameReplacement(restoredIndex);
+
+ RestoreSnapshotResponse response = execute(request, highLevelClient().snapshot()::restore,
+ highLevelClient().snapshot()::restoreAsync);
+
+ RestoreInfo restoreInfo = response.getRestoreInfo();
+ assertThat(restoreInfo.name(), equalTo(testSnapshot));
+ assertThat(restoreInfo.indices(), equalTo(Collections.singletonList(restoredIndex)));
+ assertThat(restoreInfo.successfulShards(), greaterThan(0));
+ assertThat(restoreInfo.failedShards(), equalTo(0));
+ }
+
public void testDeleteSnapshot() throws IOException {
String repository = "test_repository";
String snapshot = "test_snapshot";
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java
index fff3e7ece70..922fcb984d9 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java
@@ -33,6 +33,8 @@ import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotReq
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.GetSnapshotsResponse;
+import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
+import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
@@ -53,12 +55,15 @@ import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.repositories.fs.FsRepository;
import org.elasticsearch.rest.RestStatus;
+import org.elasticsearch.snapshots.RestoreInfo;
import org.elasticsearch.snapshots.SnapshotId;
import org.elasticsearch.snapshots.SnapshotInfo;
import org.elasticsearch.snapshots.SnapshotShardFailure;
import org.elasticsearch.snapshots.SnapshotState;
import java.io.IOException;
+import java.util.Collections;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -263,6 +268,107 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
}
}
+ public void testRestoreSnapshot() throws IOException {
+ RestHighLevelClient client = highLevelClient();
+
+ createTestRepositories();
+ createTestIndex();
+ createTestSnapshots();
+
+ // tag::restore-snapshot-request
+ RestoreSnapshotRequest request = new RestoreSnapshotRequest(repositoryName, snapshotName);
+ // end::restore-snapshot-request
+ // we need to restore as a different index name
+
+ // tag::restore-snapshot-request-masterTimeout
+ request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
+ request.masterNodeTimeout("1m"); // <2>
+ // end::restore-snapshot-request-masterTimeout
+
+ // tag::restore-snapshot-request-waitForCompletion
+ request.waitForCompletion(true); // <1>
+ // end::restore-snapshot-request-waitForCompletion
+
+ // tag::restore-snapshot-request-partial
+ request.partial(false); // <1>
+ // end::restore-snapshot-request-partial
+
+ // tag::restore-snapshot-request-include-global-state
+ request.includeGlobalState(false); // <1>
+ // end::restore-snapshot-request-include-global-state
+
+ // tag::restore-snapshot-request-include-aliases
+ request.includeAliases(false); // <1>
+ // end::restore-snapshot-request-include-aliases
+
+
+ // tag::restore-snapshot-request-indices
+ request.indices("test_index");
+ // end::restore-snapshot-request-indices
+
+ String restoredIndexName = "restored_index";
+ // tag::restore-snapshot-request-rename
+ request.renamePattern("test_(.+)"); // <1>
+ request.renameReplacement("restored_$1"); // <2>
+ // end::restore-snapshot-request-rename
+
+ // tag::restore-snapshot-request-index-settings
+ request.indexSettings( // <1>
+ Settings.builder()
+ .put("index.number_of_replicas", 0)
+ .build());
+
+ request.ignoreIndexSettings("index.refresh_interval", "index.search.idle.after"); // <2>
+ request.indicesOptions(new IndicesOptions( // <3>
+ EnumSet.of(IndicesOptions.Option.IGNORE_UNAVAILABLE),
+ EnumSet.of(IndicesOptions.WildcardStates.OPEN)));
+ // end::restore-snapshot-request-index-settings
+
+ // tag::restore-snapshot-execute
+ RestoreSnapshotResponse response = client.snapshot().restore(request, RequestOptions.DEFAULT);
+ // end::restore-snapshot-execute
+
+ // tag::restore-snapshot-response
+ RestoreInfo restoreInfo = response.getRestoreInfo();
+ List indices = restoreInfo.indices(); // <1>
+ // end::restore-snapshot-response
+ assertEquals(Collections.singletonList(restoredIndexName), indices);
+ assertEquals(0, restoreInfo.failedShards());
+ assertTrue(restoreInfo.successfulShards() > 0);
+ }
+
+ public void testRestoreSnapshotAsync() throws InterruptedException {
+ RestHighLevelClient client = highLevelClient();
+ {
+ RestoreSnapshotRequest request = new RestoreSnapshotRequest();
+
+ // tag::restore-snapshot-execute-listener
+ ActionListener listener =
+ new ActionListener() {
+ @Override
+ public void onResponse(RestoreSnapshotResponse restoreSnapshotResponse) {
+ // <1>
+ }
+
+ @Override
+ public void onFailure(Exception e) {
+ // <2>
+ }
+ };
+ // end::restore-snapshot-execute-listener
+
+ // Replace the empty listener by a blocking listener in test
+ final CountDownLatch latch = new CountDownLatch(1);
+ listener = new LatchedActionListener<>(listener, latch);
+
+ // tag::restore-snapshot-execute-async
+ client.snapshot().restoreAsync(request, RequestOptions.DEFAULT, listener); // <1>
+ // end::restore-snapshot-execute-async
+
+ assertTrue(latch.await(30L, TimeUnit.SECONDS));
+ }
+ }
+
public void testSnapshotDeleteRepository() throws IOException {
RestHighLevelClient client = highLevelClient();
diff --git a/docs/java-rest/high-level/snapshot/restore_snapshot.asciidoc b/docs/java-rest/high-level/snapshot/restore_snapshot.asciidoc
new file mode 100644
index 00000000000..a4b83ca419a
--- /dev/null
+++ b/docs/java-rest/high-level/snapshot/restore_snapshot.asciidoc
@@ -0,0 +1,144 @@
+[[java-rest-high-snapshot-restore-snapshot]]
+=== Restore Snapshot API
+
+The Restore Snapshot API allows to restore a snapshot.
+
+[[java-rest-high-snapshot-restore-snapshot-request]]
+==== Restore Snapshot Request
+
+A `RestoreSnapshotRequest`:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-request]
+--------------------------------------------------
+
+==== Limiting Indices to Restore
+
+By default all indices are restored. With the `indices` property you can
+provide a list of indices that should be restored:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-request-indices]
+--------------------------------------------------
+<1> Request that Elasticsearch only restores "test_index".
+
+==== Renaming Indices
+
+You can rename indices using regular expressions when restoring a snapshot:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-request-rename]
+--------------------------------------------------
+<1> A regular expression matching the indices that should be renamed.
+<2> A replacement pattern that references the group from the regular
+ expression as `$1`. "test_index" from the snapshot is restored as
+ "restored_index" in this example.
+
+==== Index Settings and Options
+
+You can also customize index settings and options when restoring:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-request-index-settings]
+--------------------------------------------------
+<1> Use `#indexSettings()` to set any specific index setting for the indices
+ that are restored.
+<2> Use `#ignoreIndexSettings()` to provide index settings that should be
+ ignored from the original indices.
+<3> Set `IndicesOptions.Option.IGNORE_UNAVAILABLE` in `#indicesOptions()` to
+ have the restore succeed even if indices are missing in the snapshot.
+
+==== Further Arguments
+
+The following arguments can optionally be provided:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-request-masterTimeout]
+--------------------------------------------------
+<1> Timeout to connect to the master node as a `TimeValue`
+<2> Timeout to connect to the master node as a `String`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-request-waitForCompletion]
+--------------------------------------------------
+<1> Boolean indicating whether to wait until the snapshot has been restored.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-request-partial]
+--------------------------------------------------
+<1> Boolean indicating whether the entire snapshot should succeed although one
+ or more indices participating in the snapshot don’t have all primary
+ shards available.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-request-include-global-state]
+--------------------------------------------------
+<1> Boolean indicating whether restored templates that don’t currently exist
+ in the cluster are added and existing templates with the same name are
+ replaced by the restored templates. The restored persistent settings are
+ added to the existing persistent settings.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-request-include-aliases]
+--------------------------------------------------
+<1> Boolean to control whether aliases should be restored. Set to `false` to
+ prevent aliases from being restored together with associated indices.
+
+[[java-rest-high-snapshot-restore-snapshot-sync]]
+==== Synchronous Execution
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-execute]
+--------------------------------------------------
+
+[[java-rest-high-snapshot-restore-snapshot-async]]
+==== Asynchronous Execution
+
+The asynchronous execution of a restore snapshot request requires both the
+`RestoreSnapshotRequest` instance and an `ActionListener` instance to be
+passed to the asynchronous method:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-execute-async]
+--------------------------------------------------
+<1> The `RestoreSnapshotRequest` to execute and the `ActionListener`
+to use when the execution completes
+
+The asynchronous method does not block and returns immediately. Once it is
+completed the `ActionListener` is called back using the `onResponse` method
+if the execution successfully completed or using the `onFailure` method if
+it failed.
+
+A typical listener for `RestoreSnapshotResponse` looks like:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-execute-listener]
+--------------------------------------------------
+<1> Called when the execution is successfully completed. The response is
+ provided as an argument.
+<2> Called in case of a failure. The raised exception is provided as an argument.
+
+[[java-rest-high-cluster-restore-snapshot-response]]
+==== Restore Snapshot Response
+
+The returned `RestoreSnapshotResponse` allows to retrieve information about the
+executed operation as follows:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[restore-snapshot-response]
+--------------------------------------------------
+<1> The `RestoreInfo` contains details about the restored snapshot like the indices or
+ the number of successfully restored and failed shards.
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
index c1b8c73c9ef..53aa522772a 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
@@ -27,14 +27,17 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import static org.elasticsearch.action.ValidateActions.addValidationError;
import static org.elasticsearch.common.settings.Settings.readSettingsFromStream;
@@ -45,7 +48,7 @@ import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeBo
/**
* Restore snapshot request
*/
-public class RestoreSnapshotRequest extends MasterNodeRequest {
+public class RestoreSnapshotRequest extends MasterNodeRequest implements ToXContentObject {
private String snapshot;
private String repository;
@@ -563,6 +566,49 @@ public class RestoreSnapshotRequest extends MasterNodeRequest PARSER = new ConstructingObjectParser<>(
+ "restore_snapshot", true, v -> {
+ RestoreInfo restoreInfo = (RestoreInfo) v[0];
+ Boolean accepted = (Boolean) v[1];
+ assert (accepted == null && restoreInfo != null) ||
+ (accepted != null && accepted && restoreInfo == null) :
+ "accepted: [" + accepted + "], restoreInfo: [" + restoreInfo + "]";
+ return new RestoreSnapshotResponse(restoreInfo);
+ });
+
+ static {
+ PARSER.declareObject(optionalConstructorArg(), (parser, context) -> RestoreInfo.fromXContent(parser), new ParseField("snapshot"));
+ PARSER.declareBoolean(optionalConstructorArg(), new ParseField("accepted"));
+ }
+
+
+ public static RestoreSnapshotResponse fromXContent(XContentParser parser) throws IOException {
+ return PARSER.parse(parser, null);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ RestoreSnapshotResponse that = (RestoreSnapshotResponse) o;
+ return Objects.equals(restoreInfo, that.restoreInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(restoreInfo);
+ }
+
+ @Override
+ public String toString() {
+ return "RestoreSnapshotResponse{" + "restoreInfo=" + restoreInfo + '}';
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/snapshots/RestoreInfo.java b/server/src/main/java/org/elasticsearch/snapshots/RestoreInfo.java
index 36e80501fc1..6a58b52f72a 100644
--- a/server/src/main/java/org/elasticsearch/snapshots/RestoreInfo.java
+++ b/server/src/main/java/org/elasticsearch/snapshots/RestoreInfo.java
@@ -18,18 +18,22 @@
*/
package org.elasticsearch.snapshots;
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
-import org.elasticsearch.common.xcontent.ToXContent.Params;
+import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* Information about successfully completed restore operation.
@@ -120,9 +124,6 @@ public class RestoreInfo implements ToXContentObject, Streamable {
static final String SUCCESSFUL = "successful";
}
- /**
- * {@inheritDoc}
- */
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
@@ -141,9 +142,23 @@ public class RestoreInfo implements ToXContentObject, Streamable {
return builder;
}
- /**
- * {@inheritDoc}
- */
+ private static final ObjectParser PARSER = new ObjectParser<>(RestoreInfo.class.getName(), true, RestoreInfo::new);
+
+ static {
+ ObjectParser shardsParser = new ObjectParser<>("shards", true, null);
+ shardsParser.declareInt((r, s) -> r.totalShards = s, new ParseField(Fields.TOTAL));
+ shardsParser.declareInt((r, s) -> { /* only consume, don't set */ }, new ParseField(Fields.FAILED));
+ shardsParser.declareInt((r, s) -> r.successfulShards = s, new ParseField(Fields.SUCCESSFUL));
+
+ PARSER.declareString((r, n) -> r.name = n, new ParseField(Fields.SNAPSHOT));
+ PARSER.declareStringArray((r, i) -> r.indices = i, new ParseField(Fields.INDICES));
+ PARSER.declareField(shardsParser::parse, new ParseField(Fields.SHARDS), ObjectParser.ValueType.OBJECT);
+ }
+
+ public static RestoreInfo fromXContent(XContentParser parser) throws IOException {
+ return PARSER.parse(parser, null);
+ }
+
@Override
public void readFrom(StreamInput in) throws IOException {
name = in.readString();
@@ -157,9 +172,6 @@ public class RestoreInfo implements ToXContentObject, Streamable {
successfulShards = in.readVInt();
}
- /**
- * {@inheritDoc}
- */
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
@@ -193,4 +205,24 @@ public class RestoreInfo implements ToXContentObject, Streamable {
return in.readOptionalStreamable(RestoreInfo::new);
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ RestoreInfo that = (RestoreInfo) o;
+ return totalShards == that.totalShards &&
+ successfulShards == that.successfulShards &&
+ Objects.equals(name, that.name) &&
+ Objects.equals(indices, that.indices);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, indices, totalShards, successfulShards);
+ }
+
+ @Override
+ public String toString() {
+ return Strings.toString(this);
+ }
}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java
new file mode 100644
index 00000000000..fbe8761a07d
--- /dev/null
+++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java
@@ -0,0 +1,141 @@
+/*
+ * 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.action.admin.cluster.snapshots.restore;
+
+import org.elasticsearch.action.support.IndicesOptions;
+import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.io.stream.Writeable;
+import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.common.xcontent.ToXContent;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.common.xcontent.XContentType;
+import org.elasticsearch.test.AbstractWireSerializingTestCase;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RestoreSnapshotRequestTests extends AbstractWireSerializingTestCase {
+ private RestoreSnapshotRequest randomState(RestoreSnapshotRequest instance) {
+ if (randomBoolean()) {
+ List indices = new ArrayList<>();
+ int count = randomInt(3) + 1;
+
+ for (int i = 0; i < count; ++i) {
+ indices.add(randomAlphaOfLength(randomInt(3) + 2));
+ }
+
+ instance.indices(indices);
+ }
+ if (randomBoolean()) {
+ instance.renamePattern(randomUnicodeOfLengthBetween(1, 100));
+ }
+ if (randomBoolean()) {
+ instance.renameReplacement(randomUnicodeOfLengthBetween(1, 100));
+ }
+ instance.partial(randomBoolean());
+ instance.includeAliases(randomBoolean());
+
+ if (randomBoolean()) {
+ Map settings = new HashMap<>();
+ int count = randomInt(3) + 1;
+
+ for (int i = 0; i < count; ++i) {
+ settings.put(randomAlphaOfLengthBetween(2, 5), randomAlphaOfLengthBetween(2, 5));
+ }
+
+ instance.settings(settings);
+ }
+ if (randomBoolean()) {
+ Map indexSettings = new HashMap<>();
+ int count = randomInt(3) + 1;
+
+ for (int i = 0; i < count; ++i) {
+ indexSettings.put(randomAlphaOfLengthBetween(2, 5), randomAlphaOfLengthBetween(2, 5));;
+ }
+ instance.indexSettings(indexSettings);
+ }
+
+ instance.includeGlobalState(randomBoolean());
+
+ if (randomBoolean()) {
+ Collection wildcardStates = randomSubsetOf(
+ Arrays.asList(IndicesOptions.WildcardStates.values()));
+ Collection options = randomSubsetOf(
+ Arrays.asList(IndicesOptions.Option.ALLOW_NO_INDICES, IndicesOptions.Option.IGNORE_UNAVAILABLE));
+
+ instance.indicesOptions(new IndicesOptions(
+ options.isEmpty() ? IndicesOptions.Option.NONE : EnumSet.copyOf(options),
+ wildcardStates.isEmpty() ? IndicesOptions.WildcardStates.NONE : EnumSet.copyOf(wildcardStates)));
+ }
+
+ instance.waitForCompletion(randomBoolean());
+
+ if (randomBoolean()) {
+ instance.masterNodeTimeout(randomTimeValue());
+ }
+ return instance;
+ }
+
+ @Override
+ protected RestoreSnapshotRequest createTestInstance() {
+ return randomState(new RestoreSnapshotRequest(randomAlphaOfLength(5), randomAlphaOfLength(10)));
+ }
+
+ @Override
+ protected Writeable.Reader instanceReader() {
+ return RestoreSnapshotRequest::new;
+ }
+
+ @Override
+ protected RestoreSnapshotRequest mutateInstance(RestoreSnapshotRequest instance) throws IOException {
+ RestoreSnapshotRequest copy = copyInstance(instance);
+ // ensure that at least one property is different
+ copy.repository("copied-" + instance.repository());
+ return randomState(copy);
+ }
+
+ public void testSource() throws IOException {
+ RestoreSnapshotRequest original = createTestInstance();
+ XContentBuilder builder = original.toXContent(XContentFactory.jsonBuilder(), new ToXContent.MapParams(Collections.emptyMap()));
+ XContentParser parser = XContentType.JSON.xContent().createParser(
+ NamedXContentRegistry.EMPTY, null, BytesReference.bytes(builder).streamInput());
+ Map map = parser.mapOrdered();
+
+ // we will only restore properties from the map that are contained in the request body. All other
+ // properties are restored from the original (in the actual REST action this is restored from the
+ // REST path and request parameters).
+ RestoreSnapshotRequest processed = new RestoreSnapshotRequest(original.repository(), original.snapshot());
+ processed.masterNodeTimeout(original.masterNodeTimeout());
+ processed.waitForCompletion(original.waitForCompletion());
+
+ processed.source(map);
+
+ assertEquals(original, processed);
+ }
+}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotResponseTests.java
new file mode 100644
index 00000000000..17d1ecafabd
--- /dev/null
+++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotResponseTests.java
@@ -0,0 +1,56 @@
+/*
+ * 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.action.admin.cluster.snapshots.restore;
+
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.snapshots.RestoreInfo;
+import org.elasticsearch.test.AbstractXContentTestCase;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class RestoreSnapshotResponseTests extends AbstractXContentTestCase {
+
+ @Override
+ protected RestoreSnapshotResponse createTestInstance() {
+ if (randomBoolean()) {
+ String name = randomRealisticUnicodeOfCodepointLengthBetween(1, 30);
+ List indices = new ArrayList<>();
+ indices.add("test0");
+ indices.add("test1");
+ int totalShards = randomIntBetween(1, 1000);
+ int successfulShards = randomIntBetween(0, totalShards);
+ return new RestoreSnapshotResponse(new RestoreInfo(name, indices, totalShards, successfulShards));
+ } else {
+ return new RestoreSnapshotResponse(null);
+ }
+ }
+
+ @Override
+ protected RestoreSnapshotResponse doParseInstance(XContentParser parser) throws IOException {
+ return RestoreSnapshotResponse.fromXContent(parser);
+ }
+
+ @Override
+ protected boolean supportsUnknownFields() {
+ return true;
+ }
+}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java
index 937adddf3a4..e25e8b43d3a 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java
@@ -536,6 +536,11 @@ public abstract class ESRestTestCase extends ESTestCase {
client().performRequest(request);
}
+ protected static void deleteIndex(String name) throws IOException {
+ Request request = new Request("DELETE", "/" + name);
+ client().performRequest(request);
+ }
+
protected static void updateIndexSettings(String index, Settings.Builder settings) throws IOException {
updateIndexSettings(index, settings.build());
}