Add REST Test for Snapshot Clone API (#63863) (#63881)

Adds snapshot clone REST tests and HLRC support for the API.
This commit is contained in:
Armin Braun 2020-10-20 09:48:03 +02:00 committed by GitHub
parent 1287df4074
commit 1880bcdc09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 194 additions and 1 deletions

View File

@ -28,6 +28,7 @@ import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRe
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse; import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
import org.elasticsearch.action.admin.cluster.snapshots.clone.CloneSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest; 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.create.CreateSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest; import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
@ -236,6 +237,32 @@ public final class SnapshotClient {
CreateSnapshotResponse::fromXContent, listener, emptySet()); CreateSnapshotResponse::fromXContent, listener, emptySet());
} }
/**
* Clones a snapshot.
* <p>
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
* API on elastic.co</a>
*/
public AcknowledgedResponse clone(CloneSnapshotRequest cloneSnapshotRequest, RequestOptions options)
throws IOException {
return restHighLevelClient.performRequestAndParseEntity(cloneSnapshotRequest, SnapshotRequestConverters::cloneSnapshot, options,
AcknowledgedResponse::fromXContent, emptySet());
}
/**
* Asynchronously clones a snapshot.
* <p>
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
* API on elastic.co</a>
* @return cancellable that may be used to cancel the request
*/
public Cancellable cloneAsync(CloneSnapshotRequest cloneSnapshotRequest, RequestOptions options,
ActionListener<AcknowledgedResponse> listener) {
return restHighLevelClient.performRequestAsyncAndParseEntity(cloneSnapshotRequest,
SnapshotRequestConverters::cloneSnapshot, options,
AcknowledgedResponse::fromXContent, listener, emptySet());
}
/** /**
* Get snapshots. * Get snapshots.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore

View File

@ -28,6 +28,7 @@ import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteReposito
import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest;
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
import org.elasticsearch.action.admin.cluster.snapshots.clone.CloneSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest; 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.delete.DeleteSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest; import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
@ -123,6 +124,21 @@ final class SnapshotRequestConverters {
return request; return request;
} }
static Request cloneSnapshot(CloneSnapshotRequest cloneSnapshotRequest) throws IOException {
String endpoint = new RequestConverters.EndpointBuilder().addPathPart("_snapshot")
.addPathPart(cloneSnapshotRequest.repository())
.addPathPart(cloneSnapshotRequest.source())
.addPathPart("_clone")
.addPathPart(cloneSnapshotRequest.target())
.build();
Request request = new Request(HttpPut.METHOD_NAME, endpoint);
RequestConverters.Params params = new RequestConverters.Params();
params.withMasterTimeout(cloneSnapshotRequest.masterNodeTimeout());
request.addParameters(params.asMap());
request.setEntity(RequestConverters.createEntity(cloneSnapshotRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE));
return request;
}
static Request getSnapshots(GetSnapshotsRequest getSnapshotsRequest) { static Request getSnapshots(GetSnapshotsRequest getSnapshotsRequest) {
RequestConverters.EndpointBuilder endpointBuilder = new RequestConverters.EndpointBuilder().addPathPartAsIs("_snapshot") RequestConverters.EndpointBuilder endpointBuilder = new RequestConverters.EndpointBuilder().addPathPartAsIs("_snapshot")
.addPathPart(getSnapshotsRequest.repository()); .addPathPart(getSnapshotsRequest.repository());

View File

@ -28,6 +28,7 @@ import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRe
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse; import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
import org.elasticsearch.action.admin.cluster.snapshots.clone.CloneSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest; 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.create.CreateSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest; import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
@ -351,6 +352,30 @@ public class SnapshotIT extends ESRestHighLevelClientTestCase {
assertTrue(response.isAcknowledged()); assertTrue(response.isAcknowledged());
} }
public void testCloneSnapshot() throws IOException {
String repository = "test_repository";
String snapshot = "source_snapshot";
String targetSnapshot = "target_snapshot";
final String testIndex = "test_idx";
createIndex(testIndex, Settings.EMPTY);
assertTrue("index [" + testIndex + "] should have been created", indexExists(testIndex));
AcknowledgedResponse putRepositoryResponse = createTestRepository(repository, FsRepository.TYPE, "{\"location\": \".\"}");
assertTrue(putRepositoryResponse.isAcknowledged());
CreateSnapshotRequest createSnapshotRequest = new CreateSnapshotRequest(repository, snapshot);
createSnapshotRequest.waitForCompletion(true);
CreateSnapshotResponse createSnapshotResponse = createTestSnapshot(createSnapshotRequest);
assertEquals(RestStatus.OK, createSnapshotResponse.status());
CloneSnapshotRequest request = new CloneSnapshotRequest(repository, snapshot, targetSnapshot, new String[]{testIndex});
AcknowledgedResponse response = execute(request, highLevelClient().snapshot()::clone, highLevelClient().snapshot()::cloneAsync);
assertTrue(response.isAcknowledged());
}
private static Map<String, Object> randomUserMetadata() { private static Map<String, Object> randomUserMetadata() {
if (randomBoolean()) { if (randomBoolean()) {
return null; return null;

View File

@ -0,0 +1,43 @@
{
"snapshot.clone":{
"documentation":{
"url":"https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-snapshots.html",
"description":"Clones indices from one snapshot into another snapshot in the same repository."
},
"stability":"stable",
"url":{
"paths":[
{
"path":"/_snapshot/{repository}/{snapshot}/_clone/{target_snapshot}",
"methods":[
"PUT"
],
"parts":{
"repository":{
"type":"string",
"description":"A repository name"
},
"snapshot":{
"type":"string",
"description":"The name of the snapshot to clone from"
},
"target_snapshot":{
"type":"string",
"description":"The name of the cloned snapshot to create"
}
}
}
]
},
"params":{
"master_timeout":{
"type":"time",
"description":"Explicit operation timeout for connection to master node"
}
},
"body":{
"description":"The snapshot clone definition",
"required":true
}
}
}

View File

@ -0,0 +1,54 @@
---
setup:
- do:
snapshot.create_repository:
repository: test_repo_create_1
body:
type: fs
settings:
location: "test_repo_create_1_loc"
- do:
indices.create:
index: test_index_1
body:
settings:
number_of_shards: 1
number_of_replicas: 1
- do:
indices.create:
index: test_index_2
body:
settings:
number_of_shards: 1
number_of_replicas: 1
- do:
snapshot.create:
repository: test_repo_create_1
snapshot: test_snapshot
wait_for_completion: true
---
"Clone a snapshot":
- skip:
version: " - 7.9.99"
reason: "Clone snapshot functionality was introduced in 7.10"
- do:
snapshot.clone:
repository: test_repo_create_1
snapshot: test_snapshot
target_snapshot: target_snapshot_1
body:
"indices": test_index_2
- match: { acknowledged: true }
- do:
snapshot.delete:
repository: test_repo_create_1
snapshot: target_snapshot_1
- match: { acknowledged: true }

View File

@ -23,14 +23,17 @@ import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.IndicesRequest; import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.MasterNodeRequest; import org.elasticsearch.action.support.master.MasterNodeRequest;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException; import java.io.IOException;
import static org.elasticsearch.action.ValidateActions.addValidationError; import static org.elasticsearch.action.ValidateActions.addValidationError;
public class CloneSnapshotRequest extends MasterNodeRequest<CloneSnapshotRequest> implements IndicesRequest.Replaceable{ public class CloneSnapshotRequest extends MasterNodeRequest<CloneSnapshotRequest> implements IndicesRequest.Replaceable, ToXContentObject {
private final String repository; private final String repository;
@ -139,4 +142,29 @@ public class CloneSnapshotRequest extends MasterNodeRequest<CloneSnapshotRequest
public String source() { public String source() {
return this.source; return this.source;
} }
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field("repository", repository);
builder.field("source", source);
builder.field("target", target);
if (indices != null) {
builder.startArray("indices");
for (String index : indices) {
builder.value(index);
}
builder.endArray();
}
if (indicesOptions != null) {
indicesOptions.toXContent(builder, params);
}
builder.endObject();
return builder;
}
@Override
public String toString() {
return Strings.toString(this);
}
} }